枕を欹てて聴く

香炉峰の雪は簾を撥げて看る

Greasemonkey 0.8の新機能を使う

今日いきなり更新が来てうきうきだったのですが、結構主だった変更があります。そこで、個人的に気になった。
「新しく追加された機能」
についてご紹介。

response.finalUrl

GM_xmlhttpRequestでonloadに渡されるresponseオブジェクトにfinalUrlプロパティがつきました。

これはリダイレクトなどをされてしまったときに最後に行き着いたURLを格納しています。
使おうと思ったらこんな感じで使えます。

Expand TinyURL for Greasemonkey

// ==UserScript==
// @name           Expand TinyURL
// @namespace      http://d.hatena.ne.jp/Constellation/
// @description    show tooltip on TinyURL and replace href and HTML
// @include        http://*
// @include        https://*
// @author         Constellation
// ==/UserScript==
(function(){
var replaceUrl = function(nodes){
  if (!nodes) return;
  Array.forEach(nodes, function(element){
    var links = element.getElementsByTagName('a');
    if (!links) return;
    Array.forEach(links,function(link){
      if (link.href && /^http:\/\/tinyurl\.com\/[\w]+/.test(link.href)){
        var opt = {
          method:'get',
          url:link.href,
          link:link,
          onload:function(res){
            var url = res.finalUrl;
            this.link.setAttribute('title', url);
            this.link.setAttribute('href', url);
            this.link.innerHTML = url;
          }
        }
      setTimeout(GM_xmlhttpRequest, 0, opt)
      }
    });
  });
}

addFilter(replaceUrl);
replaceUrl([document]);

function addFilter(f, i){
  i = i || 4
  if (window.AutoPagerize && window.AutoPagerize.addFilter) {
    window.AutoPagerize.addFilter(f);
  }else if (i > 1) {
    setTimeout(arguments.callee, 1000, f, i-1);
  }
}
})();

やってることは、TinyURLのURLを元のものに戻す作業です。
TinyURLのリダイレクト先に行って、finalUrlを拾ってきています。

AutoPagerize対応。
従来のほかの人が作ったAPIをたたくのではなく、直接link先をみて、リダイレクト先URLを取得してそれに置き換えています。1から10までGreasemonkeyでできるってなんか素敵じゃないですか。

GM_getResourceURL

なんに使うかっていったら画像とかflashなど、従来userscriptに取り込もうと思ったらbase64 encodeしないといけないものを、もっと簡単に扱おうというものです。
こころなしか「jsとHTMLとstylesheetは分離すべき!」ってのと同じ感じがします。これを使うと画像などとjsを分離できます。

使い方としては、

  1. metadataに "@resource 名前 URL" を書く
  2. GM_getResourceURL(名前)で、そのデータのbase64 encodeした文字列が返ってきます。

it will be base64 encoded into the "data" URL scheme, and returned upon completion of this API method.

GM_getResourceURL - GreaseSpot Wiki

間違っちゃいけないのは、「GM_xmlhttpRequest」とかとどこが違うのか。
Greasemonkey 0.8ならgm_scriptsフォルダのなかにまたフォルダができていると思いますが、ここにそのURL先のファイル自体を格納します。
ダウンロードするのは、インストールした最初の一回だけです。
また、userscriptを新しく更新するときには、一度古いのをUninstallしてからでないと、こういったインストール時にのみダウンロードされるファイルが更新されません。注意してください。

で、ためしに作ったのがこの自分でも何で作ったのかわけわかんないuserscript
ConstellationTwitter.user.js

// ==UserScript==
// @name        Constellation Twitter
// @namespace   http://d.hatena.ne.jp/Constellation/
// @include     http://twitter.com/home
// @description icon to utatane(Constellation)
// @resource    utatane http://s3.amazonaws.com/twitter_production/profile_images/51850588/utatane_normal.jpg
// @author      Constellation
// ==/UserScript==
(function(){

//ここでbase64 encode 結果を受け取っている。
var utatane = GM_getResourceURL('utatane')
var replaceUsers = function(nodes){
  if (!nodes) return;
  Array.forEach(nodes, function(element){
    var imgs = $x(
      'id("timeline")//img[@id="profile-image"]');
    var names = $x(
      'id("timeline")//td[contains(concat(" ",@class," ")," content ")]/strong/a/text()')
    imgs.forEach(function(img){
        img.setAttribute('src', utatane);
    });
    names.forEach(function(name){
      name.nodeValue = 'Constellation'
    });
  });
}

addFilter(replaceUsers);
replaceUsers([document]);

function addFilter(f, i){
  i = i || 4
  if (window.AutoPagerize && window.AutoPagerize.addFilter) {
    window.AutoPagerize.addFilter(f);
  }else if (i > 1) {
    setTimeout(arguments.callee, 1000, f, i-1);
  }
}

function $x(xpath, node){
  var node = node || document;
  var temp = document.evaluate(xpath, node, null,
    XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
  var t = [];
  for (var i = 0; i < temp.snapshotLength; i++) {
      t.push(temp.snapshotItem(i))
  }
  return t;
}
})();

Twitter上のすべての発言をid:hashigotanに変更するGreasemonkey/user.js - TERRAZINE
Twitter上のすべての発言をid:dropdbさんに変更するGreasemonkey - 肉とご飯と甘いもの @ sotarok
なんてのもありましたし。自分でも一度作ってみるべきかと思いまして。
srcをインストール時の一回しかダウンロードしないし、それを使い続けるというserverにやさしい構造。
AutoPagerizeにもバッチリ対応!

gm_scriptsフォルダの中のconstellation_twitterフォルダの中にutataneアカウントのjpgが入っているのを確認してみてください。

GM_getResourceText

今さっきのGM_getResourceURLはbase64 encodeしてくれましたが、こちらはそのままtextを返します。

これも、毎回ダウンロードするのではなく、ダウンロードするのはインストールしたときのみ。以後はそれを読み込みます。

// ==UserScript==
// @name           test
// @namespace      http://d.hatena.ne.jp/Constellation/
// @include        http://www.google.co.jp/*
// @resource       JQuery http://jqueryjs.googlecode.com/files/jquery-1.2.6.min.js
// ==/UserScript==

(function(){
  var myPrototype = GM_getResourceText("JQuery");
  eval(myPrototype);
})();

このように外部のScriptなどを読み込んだりする用途に使えます。最初の一回しかダウンロードしないのでそれほど負荷もかけないでしょうし。
この場合はJQueryをよみこんで、evalで実行しています。
textであれば何でもいけるので、StyleSheetを扱いたいときに分けてみるのもいいかもしれません。

個人的にいいなあと思ったのは、これがインストール後に参照するのがローカルのファイルだということです。
実はダウンロードされたファイルは書き換えることができます。
ですから
CSSなどを別にしておくと気軽にStyleSheetだけ変更できたり、JSONのような形式にしておけば、evalすることにより、設定などを読み込むとができます(iniファイルみたいに)

@require

最後にrequireについて。
さっきのGM_getResourceTextでもいけるのですが、これは外部Script読み込みようです。
ここに指示したものはインストール時にダウンロードされて保存され、以後、そこから読み出されます。
textと違ってScript最初にとして読まれるのでevalとかする必要はありません。
GM_getResourceTextはCSSXML、こちらはScript用として使い分けるといいのかもしれません。

@require URL

という風にメタデータに記述します。
前のもので行くと、

// ==UserScript==
// @name           test
// @namespace      http://d.hatena.ne.jp/Constellation/
// @include        http://www.google.co.jp/*
// @require        http://jqueryjs.googlecode.com/files/jquery-1.2.6.min.js
// ==/UserScript==

(function(){
 alert('jQuery読み込み済み')
})();

一番最初に読み込まれるので、内部で好きに使えます。
ライブラリ読み込み用に利用するのがよさそうです。

補足

finalUrlが使えるようになってやたらめったらテンションがあがったので、そのほかのいろんな新機能についても調べてみました。
参考になればいいですが。

最後に、以上を踏まえてiconをGM_getResourceURLから読み込むタイプのLDRFullFeedをおいておきます。
Greasemonkey 0.8以上でしか動きません。

LDRFullFeed