不可視点

search guy at cookpad.com

Greasemonkey勉強会

ゼミ用資料 関係のない方はスルーしてください
今回の資料:ダウンロード

Greasemonkeyとは

Firefoxでユーザースクリプトを実現するためのエクステンション(拡張機能)の一つで、読み込んだウェブページをクライアント(ユーザー)側でカスタマイズ可能にするもの。

他のブラウザでは、同様の機能が、Opera8では標準で、IEではTrixieを導入することで、SafariではCreammonkeyを導入することで実現できる。

例えば、特定サイトのフォントを変更したり、広告を排除したり、便利なリンクを追加したりすることができるユーザースクリプトがあります。より詳細な内容:http://diveintogreasemonkey.org/toc/ *1

どんなものがあるの

とりあえず使ってみよう。おもしろいスクリプトがいっぱいある→http://userscripts.org/ /人気エントリ

どうやって作るんだぜ

マニュアル:http://diveintogreasemonkey.org/toc/

書いて*.user.jsと名前をつければおk リンクで踏むかドラッグアンドドロップでインストールできる。

// ==UserScript==
// @name        hello Greasemonkey
// @namespace  http://omedete.na/
// @include     *
// @exclude     http://www.google.com/*
// @exclude     http://www.google.co.jp/*
// @description  ^o^
// ==/UserScript==

alert('Hello world!');

冒頭のコメント行は必須。特殊なのはこれくらい

  • @include:このスクリプトが有効になるURL
  • @exclude:このスクリプトが無効になるURL

日本語はUnicodeエスケープしないといけません。エスケープしてくれるプログラムかエディタを用意するといいでしょう。

初歩

ニコニコ動画の動画リンクがまるで役に立たないので書き換える。
ニコニコは「 短時間での連続アクセスはご遠慮ください」なので自重してsample1.htmlでまず練習(資料

// ==UserScript==
// @name        niconico google
// @namespace  http://omedete.na/
// @include     http://www.nicovideo.jp/watch/*
// @description  ^o^
// ==/UserScript==

var h1 = document.getElementsByTagName('h1')[0];
if(!h1) return;
h1.firstChild.href='http://www.google.co.jp/search?q=' + h1.firstChild.innerHTML;
GM特有の機能

Greasemonkeyには特別な機能をいくつか用意されています。ニコニコ動画にアクセスしている時間をカウントするスクリプトを例にとって説明。

// ==UserScript==
// @name           NicovideoTimer
// @namespace      http://cureblack.com/
// @include        http://www.nicovideo.jp/*
// ==/UserScript==

var field = document.createElement('div');
document.body.appendChild(field);
with(field.style) {
  width = "200px";
  backgroundColor = "#FF0000";
  position = "fixed";
  bottom = 0;
  right = 0;
  opacity = '0.5';
  textAlign = "right";
}
var time = GM_getValue('nicovideotimer_time', 0);

GM_getValue(key,デフォルト値),GM_setValue(key,値)でGreasemonkeyに値を保存したり取り出したり出来ます。*2

if(window.parent == window){
  window.setInterval(function(){
    ++time;
    GM_setValue('nicovideotimer_time', time);

ここもそう。

    var sec = time % 60;
    var min = Math.floor(time / 60) % 60;
    var hour = Math.floor(time / (60 * 60)) % 24;
    var day = Math.floor(time / (60 * 60 * 24));
    var str = "累計 ";
    if(day) str += day + "日";
    if(hour) str += hour + "時間";
    if(min) str += min + "分";
    if(sec) str += sec + "秒";
    field.innerHTML = str;
  }, 1000);
}
GM_registerMenuCommand("Reset Nicovideo timer", function(){
  time = 0;
});

ユーザーメニューに関数を登録できます。*3

ドメイン外との通信にはGM_xmlhttpRequest(details)*4が使えます。
例えばニコニコのコメントXMLにアクセスするのに必要。ニコニコのコメントについては以下を参照

ニコニコ動画を拡張する

ニコニコ動画は動画の長さにより最新のものから250〜1000件のコメントを表示します。短い動画なら250コメント。つまり見えているコメントを書いた人たちとニコニコしているに過ぎない。実際には何人と?
point:

  • GM_xmlhttpRequest(外部ドメインとの通信)
  • 連想配列(ハッシュ→ユニークキーだけに淘汰できるNE)
  • XML解析(.getAttribute())*5

ニコニコは「 短時間での連続アクセスはご遠慮ください」なので自重してsample2.htmlでまず練習(資料

// ==UserScript==
// @name        niconico comment unique user
// @namespace  http://omedete.na/
// @include     http://www.nicovideo.jp/watch/*
// @description  ^o^
// ==/UserScript==

(function(){
  var h1 = document.getElementsByTagName('h1')[0];
  if(!h1) return;

  var info = document.createElement('span');
  info.appendChild(document.createTextNode('unique user...'));

  if(/watch\/([^/]+)$/.test(location.href)){
    var video_id = RegExp.$1;
    GM_xmlhttpRequest({
      method: 'GET',
      url: 'http://www.nicovideo.jp/api/getflv?v=' + video_id,
      onload: function(res){
        if(/thread_id=(.+?)&.+&ms=(.+?)&/.test(res.responseText)){
          var thread_id = decodeURIComponent(RegExp.$1);
          var url = decodeURIComponent(RegExp.$2);
          GM_xmlhttpRequest({
            method: 'POST',
            headers: { 'Content-type': 'text/xml' },
            url: url,
            data: '<thread res_from="-1000" version="20061206" thread="' + thread_id + '" />',
            onload: function(res){
              var responseXML = (new DOMParser).parseFromString(res.responseText, "application/xml");
              var chats = responseXML.getElementsByTagName('chat');
              
              var unique = new Object();

              for(i=0;i<chats.length;i++){
                unique[chats[i].getAttribute('user_id')] = 1;
              }
              var count=0;
              for(s in unique){
                count++;
              }
              
              info.appendChild(document.createTextNode(count));
            },
            onerror: function(res){ GM_log(res.status + ':' + res.statusText); }
          });
        }
      },
      onerror: function(res){ GM_log(res.status + ':' + res.statusText); }
    });
  }
  h1.parentNode.insertBefore(info, h1);
})();

*1:[http://firefox.geckodev.org/index.php?plugin=attach&pcmd=open&file=dive_gm.pdf&refer=Greasemonkey:title=和訳PDF]

*2:http://diveintogreasemonkey.org/api/gm_getvalue.html

*3:http://diveintogreasemonkey.org/api/gm_registermenucommand.html

*4:http://diveintogreasemonkey.org/api/gm_xmlhttprequest.html

*5:ここが参考になった:http://www.w3schools.com/dom/dom_parser.asp