ホーム > タグ > Greasemonkey

Greasemonkey

短縮 URL を展開する Greasemonkey

きっかけ

Twitter など字数制限があるサイトではよく使われている URL の短縮サービス。
フィッシング詐欺に使われることもあるそうなので注意が必要です。
でもいちいちリンクを踏むたびにプレビューするのも煩わしいと思ったので、短縮 URL を展開表示するグリモンを書いてみました。

今回、Long URL Please というところで TinyURL 等の代表的なものだけでなく、55 種類のサービスに対応した API を公開していたので、それを使わせていただきました。

なお、このグリモンは Firefox には対応していません(理由は後述)。対応しました。上記リンクからプラグインを入れると幸せになれると思います。
Safari で動作確認済。Opera でも多分動きます。Opera でも動作確認しました。oAutoPagerize 対応。

インストール

こちらからどうぞ。

Short URL Expander for Greasemonkey

ソース

// ==UserScript==
// @name           Short URL Expander
// @namespace      http://www.sukechan.net/
// @description    Expand shortened URLs.
// @include        *
// @version        1.1
// ==/UserScript==

(function() {
  var apiUrl = 'http://www.longurlplease.com/api/v1.1';
  var urls = new Array();
  var shortUrlsPattern = new RegExp("(^http(s?)://(adjix\.com|b23\.ru|bacn\.me|bit\.ly|bloat\.me|budurl\.com|cli\.gs|clipurl\.us|dwarfurl\.com|ff\.im|fff\.to|href\.in|idek\.net|is\.gd|korta\.nu|lin\.cr|ln\-s\.net|loopt\.us|merky\.de|moourl\.com|nanourl\.se|ow\.ly|peaurl\.com|ping\.fm|piurl\.com|pnt\.me|poprl\.com|reallytinyurl\.com|rubyurl\.com|short\.ie|short\.to|smallr\.com|sn\.vc|snipr\.com|snipurl\.com|snurl\.com|tiny\.cc|tinyurl\.com|tr\.im|tra\.kz|twurl\.cc|twurl\.nl|u\.mavrev\.com|ur1\.ca|url\.az|url\.ie|urlx\.ie|w34\.us|xrl\.us|yep\.it|zi\.ma|zurl\.ws)/[a-zA-Z0-9_-]*)|((^http(s?)://[a-zA-Z0-9_-]+\.notlong\.com)|(^http(s?)://[a-zA-Z0-9_-]+\.qlnk\.net)|(^http(s?)://chilp\.it/[?][a-zA-Z0-9_-]+))[/]?$");

  // JSONP Callback Function
  function createNamespace() {
    window.shortURLExpanderUserJs = {
      json_callback: function(r) {
        for(url in r) {
          for(var i = 0, l = this.items.length; i < l; i++) {
            var item = this.items[i];
            if(item.href == url) {
              item.href = r[url];
              if(r[url].length <= 40) {
                item.textContent = r[url];
              } else {
                item.textContent = r[url].substr(0, 30) + '...';
              }
            }
          }
        }
      },
      items: []
    };
  }

  var f = function() {
    var x = document.evaluate('//a', document, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
    for(var i = 0; i < x.snapshotLength; i++) {
      var url = x.snapshotItem(i).href;
      if(url.match(shortUrlsPattern)) {
        if((urls.length == 0) && (typeof GM_xmlhttpRequest == 'undefined')) {
          createNamespace();
        } else {
          var items = new Array();
        }
        urls.push(url);
        if(typeof GM_xmlhttpRequest == 'undefined') {
          shortURLExpanderUserJs.items.push(x.snapshotItem(i));
        } else {
          items.push(x.snapshotItem(i));
        }
      }
    }
    if(urls.length > 0) {
      var requestUrl = apiUrl + '?q=' + urls.join('&q=');
      if(typeof GM_xmlhttpRequest == 'undefined') {
        jsonp(requestUrl);
      } else {
        GM_xmlhttpRequest({
          method: 'GET',
          url: requestUrl,
          onload: function(x) {
            var r = eval('(' + x.responseText + ')');
            for(var url in r) {
              for(var i = 0, l = items.length; i < l; i++) {
                var item = items[i];
                if(item.href == url) {
                  item.href = r[url];
                  if(r[url].length <= 40) {
                    item.textContent = r[url];
                  } else {
                    item.textContent = r[url].substr(0, 30) + '...';
                  }
                }
              }
            }
          }
        });
      }
    }
  }
  function jsonp(url) {
    var s = document.createElement('script');
    s.src = url + '&callback=shortURLExpanderUserJs.json_callback';
    s.charset = 'UTF-8';
    document.body.appendChild(s);
  }

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

Firefox に対応していない理由

このグリモンでは JSONP を使って API を実行しており、Callback 関数をグローバルに定義する必要がありました。
ただ、Firefox の Greasemonkey からグローバルの変数にアクセスする方法が無いらしいので、対応できませんでした><。
Firefox はプラグインがあるので、まあ要らないんですけどね。

(追記@20090315)
version 1.1 で Firefox / Greasemonkey に対応しました。
それと Firefox / Greasemonkey の場合、グローバル変数には unsafeWindow を使えばアクセスできます。

Greasemonkey で JSONP の Callback 関数を呼び出す方法

とあるグリモンを作っていて、JSONP をサポートしている API にリクエストを投げて、Callback 関数で結果を受け取りたい。例えば以下のような URL。

http://example.com/api?data=hoge&callback=fuga

無名関数内でこの JSONP を呼び出しても fuga は当然実行されないので、グローバルにオブジェクトを 1 つ作り(名前空間的なものです)、その中に Callback 関数を定義してみました。
Safari ではちゃんとグローバルになるんですが、Firefox ではグローバルにならず。
原因が分からずいろいろ調べてみたところ、Greasemonkey として実行される場合、前後に (function() { ~ })(); が付くらしい(Safari では付かない)。

次に、Callback パラメータ内にイベントを作成して、Callback 時にイベントを発生させられないかと思って試してみましたが、こちらは Safari で結果を受け取る方法が分からず断念。

(function() {

  // Callback
  document.addEventListener("onJSONPLoad", function(request) {
    alert('callback');
  });

  // JSONP 実行
  var s = document.createElement('script');
  s.type = 'text/javascript';
  s.charset = 'UTF-8';
  s.src = 'http://example.com/api?data=hoge&callback=(function(result){var%20c=document.createEvent(%22HTMLEvents%22)%3bc.initEvent(%22onJSONPLoad%22,true,true)%3bdocument.dispatchEvent(c)%3b})';
  document.body.appendChild(s);
})();

仕方ないので、script タグを動的に生成して、グローバルに 1 つ名前空間を作り、その中に関数を定義。

(function() {

  // JSONP 実行
  var s = document.createElement('script');
  s.src = param.url + '&callback=my_ns.json_callback';
  s.charset = 'UTF-8';
  document.body.appendChild(s);

  // Callback
  var s = document.createElement('script');
  s.type = 'text/javascript';
  s.charset = 'UTF-8';
  var t = document.createTextNode("var my_ns = { callback: function(r) { alert('callback'); } }");
  s.appendChild(t);
  document.body.appendChild(s);
})();

とりあえずこれで動きますが、もっとスマートな方法ありそうですね・・・。
JavaScript に対する理解がまだまだ浅いことを実感しました><

参考リンク

Google Reader のスターをまとめて開く Greasemonkey

これはなに?

Google Reader でスターを付けた記事をまとめて開くことができる Greasemonkey です。
スター付きアイテムなどを開いた状態で、「o」キーを押すとスター付きの記事を最大 5 件開きます。
開いた記事のスターは消えます。

最近 Google Reader を使うようになったんですが、スターを付けた記事をまとめて開くのがめんどくさかったので作りました。
既に下記リンク先に Greasemonkey はあるのですが、getElementsByTagName などが使われていて Safari では使えなかったので動くようにしました。もちろん Firefox でも動きます。

Google Reader のスターを Livedoor Reader のピンみたいにまとめて開く Greasemonkey - もうゴールしていいブログ

インストール

こちらからインストールしてください。

Google Reader Star Opener – Userscripts.org

ソース

// ==UserScript==
// @name           Google Reader Star Opener
// @namespace      http://www.sukechan.net/
// @description    Starred item open in a lump for Google Reader.
// @include        http://www.google.co.jp/reader/view/*
// @include        http://www.google.com/reader/view/*
// @version        1.0.1
// ==/UserScript==

(function() {
  var MAX_WINDOW_OPEN = 5;

  var onKeyDown = function(event) {
    if(event.keyCode == 79 && !event.shiftKey) {
      var entries = document.evaluate('//div[@id="entries"]//div[@class="entry-container"] | //div[@id="entries"]//div[@class="collapsed"]', document, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
      for(var i = 0, m = MAX_WINDOW_OPEN; i < entries.snapshotLength && m > 0; i++) {
        var entry = entries.snapshotItem(i);
        var stars = document.evaluate('.//div[contains(@class, "item-star-active")]', entry, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
        if(stars.snapshotLength > 0) {
          var star = stars.snapshotItem(0);
          var links = document.evaluate('.//h2[@class="entry-title"]/a | .//div[@class="entry-main"]/a', entry, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
          if(links.snapshotLength > 0) {
            var link = links.snapshotItem(0);
            var win = window.open(link.href);
            if(win && !win.closed) {
              m--;
              var event = document.createEvent('MouseEvents');
              event.initEvent('click', true, true);
              star.dispatchEvent(event);
            }
          }
        }
      }
    }
  }

  document.addEventListener('keydown', onKeyDown, false);
})();

(追記@2008-09-14)
ポップアップブロック等でウィンドウが開かなかった場合にスターを解除しないようにしました。

ポイント

  • Google Reader 側の HTML が変更される可能性があるので、メンテしやすくしておく。今回は全て XPath を使用。
  • document.evaluate の第 2 引数に document 以外のノードを指定した場合は、// を使うとルートノードから走査されることに注意。第 2 引数は単純に初期位置の指定です。ずっとそのノード以下だけが検索されると思ってました。

参考リンク

Twitter の投稿日時のフォーマットを変更するグリモン

Twitter Time Converter

これは何?

Twitter に表示される投稿日時(3 hours ago など)を yyyy/mm/dd hh:mi 形式に変更する Greasemonkey です。

きっかけは自分のログを振り返っている時に、「17 時間前」などと表示されても直感的にいつだったのかが分かりにくかったからです。

対応しているページは、Friends Timeline, Replies, Archive, Public Timeline, Favorites です。Permalink, Direct Messages には対応していません。

使い方

下記リンクからインストールしてください。 Firefox/Greasemonkey または Safari/Greasekit が必要です。

Twitter Time Converter – Userscripts.org

技術的なところ

Twitter のページの HTML に、ISO 8601 形式の日付が abbr タグの title 属性に入っているのですが、その ISO 8601 形式の日付が JavaScript の Date.parse で解釈できないようなので、仕方なく手動で parse しました。

parse については、以下のライブラリを参考にさせていただきました。感謝。
[JSAN] Date.W3CDTF - ISO-8601日時フォーマット対応JavaScriptクラス

関連リンク

ソース

// ==UserScript==
// @name           Twitter Time Converter
// @namespace      http://www.sukechan.net/
// @description    The format of posted date is change.
// @include        http://twitter.com/*
// @version        1.0
// ==/UserScript==

(function() {
  var f = function() {
    var x = document.evaluate('//a[@class="entry-date"]/abbr', document, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
    for (var i = 0; i < x.snapshotLength; i++) {
      var item = x.snapshotItem(i);
      var ds = parseDate(item.getAttribute("title"));
      if (ds) item.parentNode.innerHTML = ds;
    }
  }
  function parseDate(s) {
    var arr = s.split(/[^0-9]/);
    if (arr.length == 8 ) {
      for (var j = 0; j < arr.length; j++) arr[j] = arr[j] - 0;
      var msec = Date.UTC(arr[0], arr[1] - 1, arr[2], arr[3], arr[4], arr[5]);
      if (s.indexOf("+") < 0) arr[6] *= -1;
      msec -= (arr[6] * 60 + arr[7]) * 60 * 1000;
      var dt = new Date(msec);
      return dt.getFullYear() + "/" + padZero(dt.getMonth() + 1) + "/" + padZero(dt.getDate()) + " " + padZero(dt.getHours()) + ":" + padZero(dt.getMinutes());
    } else {
      return;
    }
  }
  function padZero(s) {
    return ("0" + s).slice(-2);
  }
  f();
  addFilter(f);
  function addFilter(filter, i) {
    i = i || 4;
    if (window.AutoPagerize && window.AutoPagerize.addFilter) {
      window.AutoPagerize.addFilter(filter);
    }
    else if (i > 1) {
      setTimeout(arguments.callee, 1000, filter, i -1);
    }
  }
})();

Twitter に Google Maps へのリンクを追加するグリモン

Twitter Google Maps Link

これは何?

Twitter の発言で、L:住所 と表示されているロケーション情報に、Google Maps へのリンクを追加する Greasemonkey です。

使い方

下記リンクからインストールしてください。例によって Greasemonkey が必要です。(Safari の場合は GreaseKit)

インストール後、Twitter の L:住所 という部分が Google Maps へのリンクになります。

twittergooglemapslink.user.js (version 1.0)
Userscripts.org で管理することにしました。
Twitter Google Maps Link – Userscripts.org

その他、技術的なこと

既に同じグリモンがあるかもしれませんが、見つからなかったので作りました。

今回、初めて XPath を使いました。最初 DOM でやろうと思ったんですが、処理が煩雑になるし XPath の方が早いらしいので。以下のリンクを参考にしました。

// ==UserScript==
// @name           Twitter Google Maps Link
// @namespace      http://www.sukechan.net/
// @description    Location is convert into the link to Google Maps.
// @include        http://twitter.com/*
// @version        1.0
// ==/UserScript==

(function() {
  var x = document.evaluate('//*[@class="entry-title entry-content"] | //div[@class="desc"]/descendant::p[1]', document, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
  for (var i = 0; i < x.snapshotLength; i++) {
    var idx = x.snapshotItem(i).textContent.indexOf("L:")
    if (idx >= 0) {
      loc = x.snapshotItem(i).textContent.substr(idx + 2).split(/\s+/)[0].replace(/[\n\r\s]/g, "");
      x.snapshotItem(i).innerHTML = x.snapshotItem(i).innerHTML.replace(loc, "" + loc + "");
    }
  }
})();

 Page 1 of 2  1  2 »

Home > Tags > Greasemonkey

Search
Feeds
Meta
あわせて読みたい
あわせて読みたいブログパーツ
Others...
フィードメーター - sukechan.net
track feed

Return to page top