本文書は2009年に書いたものですが、現在はSemantic Web関係技術としてmicrodata及びRDFaが活用されており、またHTML5ではprofile属性が廃止されるなどGRDDL及びmicroformatsを敢えて使う場面は極めて少なくなり、筆者自身もGRDDLの使用は止めました。本文書はGRDDLの拡大を意図したものではなく、Bookmarkletに対する考察と記録記事的な内容として公開しています。
W3Cで勧告されたGRDDLは、microformatsとともにSemantic Webの中で重要な技術である一方、RDFaと異なり既存のXHTMLをブラウザ互換性に影響を与えるような大きな変更を伴わずにメタデータを表現し、利用できるという利点があります。そこでGRDDL変換に利用できる3種類のスクリプトを書き、これを基にBookmarkletについて少し考察してみました。なお、に直接変換方式のスクリプトを加えたことを契機に、全体の文書構成を大幅に改稿しました。
下記は変換対象のXHTMLにscript要素で予め紐付けしておくと、変換ボタンを生成します。仕組みは以下の内容ですがFirefoxのみ対象となっており、またXSLTが同じドメインに無いとXSLTを読み込み出来ません。
/*global window, document, XSLTProcessor, XMLSerializer*/
var GRDDL = {};
GRDDL.xslt = function (xsl) {
var mime, xml, proc, frag, newDoc, serializer;
mime = 'application/rdf+xml';
xml = window.document;
proc = new XSLTProcessor();
proc.importStylesheet(xsl);
frag = document.implementation.createDocument('', '', null);
newDoc = proc.transformToFragment(xml, frag);
serializer = new XMLSerializer();
newDoc = serializer.serializeToString(newDoc, mime);
newDoc = encodeURIComponent(newDoc);
window.open('data:' + mime + ';charset=UTF-8,' + newDoc);
};
GRDDL.xslPath = function () {
var lElm, i;
lElm = document.getElementsByTagName('link');
for (i = 0; i < lElm.length; i = i + 1) {
if (lElm.item(i).getAttribute('rel') === 'transformation') {
return lElm.item(i).getAttribute('href');
}
}
};
GRDDL.grddl = function () {
var xmlhttp;
xmlhttp = new XMLHttpRequest();
xmlhttp.onreadystatechange = function () {
if (xmlhttp.readyState === 4 && xmlhttp.status === 200) {
GRDDL.xslt(xmlhttp.responseXML);
}
};
xmlhttp.open('GET', GRDDL.xslPath(), true);
xmlhttp.send(null);
};
GRDDL.set = function () {
var xmlns, pElm, Btn, Frm, aElm;
xmlns = 'http://www.w3.org/1999/xhtml';
pElm = document.createElementNS(xmlns, 'p');
Btn = document.createElementNS(xmlns, 'input');
Btn.setAttribute('type', 'button');
Btn.setAttribute('value', 'GRDDL');
Btn.addEventListener('click', GRDDL.grddl, false);
pElm.appendChild(Btn);
Frm = document.createElementNS(xmlns, 'form');
Frm.setAttribute('action', '');
Frm.appendChild(pElm);
aElm = document.getElementsByTagName('address').item(0);
aElm.parentNode.insertBefore(Frm, aElm.nextSibling);
};
window.addEventListener('load', GRDDL.set, false);
上記の直接変換方式の場合、XSLTが変換するXHTMLと同じドメインに無ければ取得できないことから、profile要素のURIでXSLTを指定する方法の場合、多くは活用出来ません。そこで、変換サービスであるW3C GRDDL Serviceへのリンクを生成する方式を作成してみました。
なお、本項では最初に一般的な外部ファイル形式のスクリプト、次にBookmarklet形式にしたスクリプトを提示します。両スクリプトを通してBookmarkletの課題も併せて考えてみたいと思います。
Bookmarklet(ブックマークレット)とは、JavaScriptについてjavascriptスキームを用いてURI化し、a要素やブックマークにより即時に好きなWebページに対して容易にJavaScriptを起動できるようにしたもので、(X)HTMLとJavaScriptのみで作られる容易さは非常に魅力的な存在です。
その一方で、javascriptスキームはIANAで未だに登録されていないうえ、URIの本質である情報資源の識別子とは言い難いこと、(X)HTMLのa要素はWebリソース間をつなぐリンクの端点と終点を記述する要素であり、アプリケーション起動トリガとして設計されていないなど、Web標準規格とは本質的に相容れないという問題が指摘されています。
実用上の問題点としては、URI化する関係上短いスクリプトに限られ、Internet Explorer6に至っては約500byte以内にする必要があるため、互換性を確保するためのラッパ関数等を書くことも困難となります。また当然、URIとするためJavaScript自体をPercent-Encoding(通称はURLエンコード。%と16進数による文字表記への変換)する必要があります。さらにJavaScriptを使えない閲覧環境であった場合には壊れたリンクと認識されてしまう懸念もあります。
外部ファイルとして使用する場合は下記になります。ページ上部にアンカー文字列GRDDLを生成します。このアンカーをクリックすると変換結果のRDF/XMLが送られてきます。また、profile属性に何も値が設定されていない場合はGRDDLに対応していない旨のアラートを表示します。
/*global window, document */
var GRDDL = {};
GRDDL.createElm = function (elm) {
if (document.createElementNS) {
return document.createElementNS('http://www.w3.org/1999/xhtml', elm);
}
return document.createElement(elm);
};
GRDDL.LinkMaker = function () {
var doc, uri, mTxt, aElm, pElm, bElm;
doc = document;
uri = 'http://www.w3.org/2007/08/grddl/?docAddr=' + doc.URL + '&output=rdfxml';
mTxt = doc.createTextNode('GRDDL');
aElm = GRDDL.createElm('a');
aElm.setAttribute('href', uri);
aElm.appendChild(mTxt);
pElm = GRDDL.createElm('p');
pElm.setAttribute('id', 'GRDDL');
pElm.appendChild(aElm);
bElm = doc.getElementsByTagName('body').item(0);
bElm.insertBefore(pElm, bElm.firstChild);
};
GRDDL.check = function () {
if (document.getElementsByTagName('head').item(0).getAttribute('profile')) {
GRDDL.LinkMaker();
} else {
window.alert('This web page does not support GRDDL.');
}
};
GRDDL.start = function () {
if (window.addEventListener) {
window.addEventListener('load', GRDDL.check, false);
} else {
if (window.attachEvent) {
window.attachEvent('onload', GRDDL.check);
}
}
};
GRDDL.start();
上記の場合、予めscript要素にスクリプトを定めておかなければなりません。そこでこれをBookmarkletに変換してみました。Bookmarklet版のソースは以下です。実際に使用する際は改行記号と空白記号を全て除去したうえで、JavaScriptの部分をPercent-Encodingします。なお、Percent-encodeing後の例も掲載しておきます。
javascript:
(function (A, P, D) {
D = document;
if (D.getElementsByTagName('head').item(0).profile) {
A = D.createElement('a');
A.href = 'http://www.w3.org/2007/08/grddl/?docAddr=' + D.URL + '&output=rdfxml';
A.appendChild(D.createTextNode('GRDDL'));
P = D.createElement('p');
P.appendChild(A);
D.body.insertBefore(P, D.body.firstChild);
} else {
window.alert('NO-GRDDL');
}
})();
javascript:%28function%28A%2CP%2CD%29%7BD%3Ddocument%3Bif%28D.getElementsByTagName%28%27head%27%29.item%280%29.profile%29%7BA%3DD.createElement%28%27a%27%29%3BA.href%3D%27http%3A%2F%2Fwww.w3.org%2F2007%2F08%2Fgrddl%2F%3FdocAddr%3D%27%2BD.URL%2B%27%26amp%3Boutput%3Drdfxml%27%3BA.appendChild%28D.createTextNode%28%27GRDDL%27%29%29%3BP%3DD.createElement%28%27p%27%29%3BP.appendChild%28A%29%3BD.body.insertBefore%28P%2CD.body.firstChild%29%3B%7Delse%7Bwindow.alert%28%27NO-GRDDL%27%29%3B%7D%7D%29%28%29%3B
上記は一般的なJavaScriptと比べて特異な部分があります。まず、全体を無名関数としたうえでその関数を呼び出す形にしている点。これは、単純に値を返す式を使うとページが移動してしまうためです。また、外部から変数に値を代入しないにも関わらず「function(A ,P, D)
」と記述している点。これは文字数を抑制するため、変数宣言(var)を書かずにローカル変数としたものです。さらに元のスクリプトは全てDOM Level2 Coreで統一しており冗長であるため、なるべく文字数の短い命令を選んで組み立てました。
こういった各種の制限によりグローバル変数やセキュリティ関係、その他の細やかな点はどうしても手抜きせざるをえないにも関わらず、Bookmarkletの性質上、広範に利用されるという矛盾があります。
なお、現在策定が進められているHTML5ではjavascriptスキームへの言及がある上、現実に便利なので一律の排除には反対ですが、あくまで臨時的な現実解として使用すべきものだと思います。