JavaDocに勝手にjava.sun.comのAPIドキュメントへのリンクをつけるGreaseMonkey User Script

練習ついでにひとつGreaseMonkey User Scriptを作成。
JDOM APIなど、Web上のJavaDocにはjava.lang.ObjectなどJDKのコアAPIへのリンクをつけていないのが多い。それをリンクさせるスクリプトを書いてみた。

XPathJavaScript正規表現の使い方がわからなくて結構苦労した。

// ==UserScript==
// @name JavaDoc completion
// @namespace http://d.hatena.ne.jp/bellbind/userscript/javadoclink
// @description Browser side link completion to JDK core API.
// @include *
// @exclude *.js
// ==/UserScript==

(function () {
  var texts = document.evaluate(
    "/descendant::*[local-name()!='A']/text()",
    document, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
  
  var packages = new Array(
    "java.applet",
    "java.awt",
    "java.beans",
    "java.io",
    "java.lang",
    "java.math",
    "java.net",
    "java.nio",
    "java.rmi",
    "java.security",
    "java.sql",
    "java.text",
    "java.util",
    "javax.accessibility",
    "javax.crypto",
    "javax.imageio",
    "javax.management",
    "javax.naming",
    "javax.net",
    "javax.print",
    "javax.rmi",
    "javax.security",
    "javax.sound",
    "javax.sql",
    "javax.swing",
    "javax.transaction",
    "javax.xml",
    "org.ietf",
    "org.omg",
    "org.w3c",
    "org.xml",
    "javax.activity");
  var uriBase = "http://java.sun.com/j2se/1.5.0/docs/api/";
  //var pattern = /\b(java\.lang|java\.io)\.[a-zA-Z0-9]+/;
  var pattern = new RegExp('');
  pattern.compile('\\b(' + packages.join('|').replace(/\./g, '\\.') + ')\\.[a-zA-Z0-9.]+');
  
  for (var i = 0; i < texts.snapshotLength; i++) {
    var text = texts.snapshotItem(i);
    var span = document.createElement("span");
    var data = text.data;
    var found = false;
    while (data.match(pattern)) {
      found = true;
      var className = RegExp.lastMatch;
      data = RegExp.rightContext;
      span.appendChild(document.createTextNode(RegExp.leftContext));
      var uri = uriBase + className.replace(/\./g, '/') + '.html';
      var a = document.createElement('a');
      a.setAttribute('href', uri);
      a.appendChild(document.createTextNode(className));
      span.appendChild(a);
    }
    if (found) {
      var parent = text.parentNode;
      span.appendChild(document.createTextNode(data));
      parent.insertBefore(span, text);
      parent.removeChild(text);
    }
  }
})();


躓いた点

  • [local-name() != 'a']でaが小文字ではaタグをはずしてしまう(小文字でもAにするか、もしかしたら両方並べるべき?)
  • ''でも""と同様\のエスケープは必要: 忘れてました。
  • RegExpの使い方: Web上の情報源の情報にある機能には、Firefoxで使えないものがある。結局どの機能が使えるのかトライアンドテストでチェックしてしまった。

一番こまったのは、''で\のエスケープがいるというのに気づくこと。結局、0から構築しなおし、序所に抽象度を上げていく過程でてやっと気がついた。まったく...

あとGoogleで比較的良質のJavaScript情報がかからなくなっているのが痛い。「とほほのJavaScript入門」より前にでてる大半が基本的に屑なのが。。。

あ、あとわざわざspanを作らなくてもよかったかも。

    ...
    var text = texts.snapshotItem(i);
    var parent = text.parentNode;
    var data = text.data;
    var found = false;
    while (data.match(pattern)) {
      found = true;
      var className = RegExp.lastMatch;
      data = RegExp.rightContext;
      parent.insertBefore(document.createTextNode(RegExp.leftContext), text);
      var uri = uriBase + className.replace(/\./g, '/') + '.html';
      var a = document.createElement('a');
      a.setAttribute('href', uri);
      a.appendChild(document.createTextNode(className));
      parent.insertBefore(a, text);
    }
    if (found) {
      parent.insertBefore(document.createTextNode(data), text);
      parent.removeChild(text);
    }
    ...