Author: わかば

# JavaScript (ECMAScript) による XPointer の実装

## 概要

この文書では、 JavaScript を使った XPointer の実装を紹介します。

## はじめに

XPointer は XML の素片識別子などとして使うことが想定された指示子です。 XPointer の構文には、識別子を使って要素を識別する速記指示子と、 scheme によって文書の任意の部分資源を識別できる構文の2種類があります。

この文書で紹介するのは、 JavaScript を使った XPointer の実装とその界面です。このスクリプトを使えば、 HTML や XML の文書で XPointer 指示子を評価させることができます。

XPointer では scheme は拡張可能に定義されています。このスクリプトでは簡単に scheme の処理を実装することができます。

## 界面

`xpointer.js` は次の界面により XPointer を実装しています。また、予め速記指示子と XPointer scheme として `element()` および `xmlns()` に対応しています。 `XPointerSchemeProcessor` を使えば更に他の scheme にも対応できます。

この界面やその実装はまだ検討中のもので、 将来変更される可能性があります。必要があれば CVS repository の配布ファイルも参照して下さい。

界面 `XPointerEvaluator`

  界面 `XPointerEvaluator` は XPointer 指示子を評価するためのメソッドを提供します。 この界面は指示子や指示子を評価する文脈となる文書とは独立したものです。

  JavaScript では、構築子を使って次のようにこの界面を実装した物体を取得できます。

      var xp = new XPointerEvaluator ();

  `XPointerEvaluator` 物体の構築
  IDL 定義

        interface XPointerEvaluator {
          XPointerResult     evaluate(in Document contextDocument,
                                      in DOMString xpointer)
                                                raises(XPathException);
          XPointerSchemeProcessor
                             getXPointerSchemeProcessor
                                     (in DOMString namespaceURI,
                                      in DOMString localName);
          void               setXPointerSchemeProcessor
                                     (in DOMString namespaceURI,
                                      in DOMString localName,
                                      in XPointerSchemeProcessor sp);
        };

  メソッド
    `evaluate`

      XPointer 指示子を与えられた文書の元で評価し、 識別された部分資源を返します。

      引数
        `contextDocument` (型 `DOMDocument`)
          XPointer 指示子を評価する文脈となる文書。
        `xpointer` (型 `DOMString`)
          評価する XPointer 指示子。
      返し値
        `XPointerResult`
          指示子を評価して得られた結果。
      例外
        `XPathException`
          `INVALID_EXPRESSION_ERR`: 指示子が不正であるか、 文書内のどの部分資源も識別しない時に発生します。
    `getXPointerSchemeProcessor`

      指定された XPointer scheme の処理器を返します。

      引数
        `namespaceURI` (型 `DOMString`)
          XPointer scheme の名前空間 URI。 名前空間に属さないときは `null` を指定します。 長さ0の文字列は `null` と同じ意味です。
        `localName` (型 `DOMString`)
          XPointer scheme の局所名。
      返し値
        `XPointerSchemeProcessor`
          指定された scheme の処理器。 見つからなければ `null`。 `namespaceURI` と `localName` の両方が `null` の時は、速記指示子用の処理器を返します。
      例外なし
    `setXPointerSchemeProcessor`

      指定された XPointer scheme の処理器を設定します。 既に処理器が登録されているときは置き換えます。

      引数
        `namespaceURI` (型 `DOMString`)
          XPointer scheme の名前空間 URI。 名前空間に属さないときは `null` を指定します。 長さ0の文字列は `null` と同じ意味です。
        `localName` (型 `DOMString`)
          XPointer scheme の局所名。
        `sp` (型 `XPointerSchemeProcessor`)
          設定する XPointer scheme 処理器。
      返し値なし
      例外なし
界面 `XPointerSchemeProcessor`

  界面 `XPointerSchemeProcessor` を実装した物体は、 ``XPointerEvaluator`.`setXPointerSchemeProcessor`` を使って `XPointerEvaluator` に登録して特定の scheme のデータを処理させることができます。

  JavaScript では、構築子を使って次のようにこの界面を実装した物体を作成できます。

      var sp = new XPointerSchemeProcessor (function (doc, context, data) {
        /* XPointerSchemeProcessor.evaluate メソッドの実装 */
      });

  `XPointerSchemeProcessor` 物体の構築
  IDL 定義

        interface XPointerSchemeProcessor {
          XPointerResult     evaluate(in Document contextDocument,
                                      in XPointerSchemeContext schemeContext,
                                      in DOMString schemeData);
        };

  メソッド
    `evaluate`

      与えられた scheme データを評価し、 識別された部分資源を返します。

      引数
        `contextDocument` (型 `Document`)
          評価する文脈となる文書。
        `schemeContext` (型 `XPointerSchemeContext`)
          評価するための XPointer の文脈情報。
        `schemeData` (型 `DOMString`)
          評価する XPointer scheme データ。 文字 `^` による逃避は元に戻された状態で渡されます。
      返し値
        `XPointerResult`
          評価した結果の部分資源。 部分資源が識別された場合には ``XPointerResult`.`hasSubresources`` は `true`、そうでない場合には `false` にします。
      例外なし
界面 `XPointerSchemeContext`

  界面 `XPointerSchemeContext` は、 `XPointerSchemeProcessor` が scheme データを評価するために必要な文脈を提供します。

  現在この界面は名前空間束縛文脈の情報だけを提供していますが、 将来の版では更に他の文脈情報も提供するかもしれません。

  IDL 定義

        interface XPointerSchemeContext {
          DOMString          lookupNamespaceURI(in DOMString prefix);
          void               addNamespaceBinding(in DOMString prefix,
                                                 in DOMString namespaceURI);
        };

  メソッド
    `lookupNamespaceURI`

      与えられた名前空間接頭辞に関連付けられた名前空間 URI を調べます。

      名前空間接頭辞として `null` や空文字列が与えられた時の結果は未定義とします。 将来の版では定義するかもしれません。

      引数
        `prefix` (型 `DOMString`)
          調べる名前空間接頭辞。
        `xpointer` (型 `DOMString`)
          評価する XPointer 指示子。
      返し値
        `DOMString`
          関連付けられている名前空間 URI。 なければ `null`。
      例外なし
    `addNamespaceBinding`

      名前空間接頭辞に名前空間 URI を束縛します。 追加された束縛は、同じ指示子の以後の scheme の評価 (行われる場合。) でも有効です。

      名前空間接頭辞として `null` や空文字列が与えられた時の結果は未定義とします。 将来の版では定義するかもしれません。

      引数
        `prefix` (型 `DOMString`)
          束縛する名前空間接頭辞。
        `namespaceURI` (型 `DOMString`)
          束縛する名前空間 URI。 名前空間なしなら `null`。
      返し値なし
      例外なし
界面 `XPointerResult`

  指示子を評価した結果は `XPointerResult` を実装した物体によって返されます。 この物体をどう実装するかはその実装に依存します。

  結果を返す方法には更に検討が必要です。 将来の版では界面が変更されるかもしれません。

  JavaScript では、便宜上新しい `XPointerResult` を生成するための静的メソッドを用意しています。

      // 空 (一致する部分資源なし) の結果
      result = XPointerResult.createEmptyeXPointerResult ();
      // 節点が1つだけの結果
      // code が null なら createEmptyXPointerResult と同じ
      result = XPointerResult.createNodeXPointerResult (node);

  `XPointerResult` 物体の構築
  IDL 定義

        interface XPointerResult {

          // XPointerResultType
          const unsigned short      UNORDERED_NODE_ITERATOR_TYPE   = 4;
          const unsigned short      ORDERED_NODE_ITERATOR_TYPE     = 5;
          const unsigned short      UNORDERED_NODE_SNAPSHOT_TYPE   = 6;
          const unsigned short      ORDERED_NODE_SNAPSHOT_TYPE     = 7;
          const unsigned short      ANY_UNORDERED_NODE_TYPE        = 8;
          const unsigned short      FIRST_ORDERED_NODE_TYPE        = 9;

          readonly attribute unsigned short  resultType;
          readonly attribute Node            singleNodeValue;
                                                // raises(XPathException) on retrieval
          readonly attribute boolean         hasSubresources;
        };

  属性
    `hasSubresources` (型 `boolean`, 読取専用)

      指示子が文書の部分資源を識別し、 その結果をこの物体が持っているのであれば `true`、そうでなければ `false`。

## 例

### スタイル・シートの変更

HTML にはスタイル・シート (の組) を利用者が選択できる機能が備わっていますが、 これを素片識別子から選択することを試みます。まずは使用例をご覧下さい。

- 使用例
- 使用例へのリンクがある文書
- 使用例で参照している外部 JavaScript のコード

この例では、名前によってスタイル・シートを選択する ``style:`style``()` という scheme などを `XPointerSchemeProcessor` の実装によって導入しています。使用例や使用例へのリンクがある文書にあるリンクは、 この scheme を使った素片識別子を使っています。

スクリプトでは、文書が読み込まれた時とリンクが活性化された時に関数 `reevaluateFragment()` を実行するように指定しています。この関数は素片識別子から XPointer を取り出し、 XPointer 処理器に渡します。

名前空間名
  `<http://suika.fam.cx/~wakaba/archive/2005/3/style/>`
局所名
  `style`
データの構文 (逃避を戻す前)
  `SchemeData`
識別する部分資源
  なし
副作用
  データとして指定された文字列 (0文字以上の任意の文字の列) と一致する名前 (`title` 属性) の代替スタイル・シート (優先スタイル・シートを含みます。) の集合を有効にし、他の代替スタイル・シート (優先スタイル・シートを含みます。) を無効にします。

スタイル選択の XPointer scheme の定義

名前空間名
  `<http://suika.fam.cx/~wakaba/archive/2005/3/window/>`
局所名
  `title`
データの構文 (逃避を戻す前)
  `SchemeData`
識別する部分資源
  なし
副作用
  データとして指定された文字列 (0文字以上の任意の文字の列) を文書の題として使用する。
安全性について
  他の手段とあわせて悪用される虞があり、注意が必要です。

題を変更する XPointer scheme の定義

名前空間名
  `<http://suika.fam.cx/~wakaba/archive/2005/3/xpointer/>`
局所名
  `document`
データの構文 (逃避を戻す前)
  常に空文字列
識別する部分資源
  文書全体
副作用
  なし
メモ
  他の scheme の副作用を利用した後で特に部分資源を識別しない時に、 指示子全体が失敗とならないように利用できます。

常に文書を選択する XPointer scheme の定義

## 素片識別子と XPointer の意味論に関して

URI の素片識別子は資源の一部分や見え方を識別します [RFC 3986]。 XPointer scheme はほとんどどんな指示でも与えることができますが、 この意味から逸脱しない範囲にとどめるべきです。

> The fragment identifier component of a URI allows indirect identification of a secondary resource by reference to a primary resource and additional identifying information. The identified secondary resource may be some portion or subset of the primary resource, some view on representations of the primary resource, or some other resource defined or described by those representations.

RFC 3986 Uniform Resource Identifier (URI): Generic Syntax 3.5. Fragment

また、素片識別子の構文はその資源の媒体型によって定義されることになっています。 使用する文書の媒体型の定義にできるだけ沿った形で利用するべきです。

## 安全性に関して

Web ブラウザにおける表示などを URI の一部により制御可能とすると、なりすましなどに悪用される危険性があります。 XPointer scheme の設計は慎重に行うべきです。

## 制限事項

- HTML や XML の文書で Web ブラウザを通して利用する場合、 スタイル・シートの例で行っているように、 文書の読込み時などに素片識別子を XPointer として解釈します。しかし、一度頁を読んだ後、 頁内のリンクをたどったりブラウザの URI 欄から直接素片識別子だけ修正したりしても、 文書の再読込みが起こらないと XPointer の再解釈は行われません。頁内リンクの場合はスタイル・シートの例のようにリンクをたどる際にスクリプトを実行することで回避できますが、 ブラウザの URI 欄や履歴の移動に対処するよい方法はいまのところありません。 (`setTimeout` で定期的に確認すれば解決できるでしょうが...)
- 現在の実装は XPointer の解釈が正確ではありません。 名前文字の検査が厳密ではないので、 本来認められるべきではないものが誤りにならないことがあります。 また、正規表現を使って実装しているので、括弧の入れ子に完全には対処できません。 現在の実装では三重 (全体で四重) の入れ子までしか正しく扱えません。また、途中の指示子部分まで解釈して部分資源が識別されれば、 残りの部分の評価は行わないので、構文が正しいことも調べません。

## 関連ファイル

この階層にあるファイルが関連する実装や使用例です。

各ファイルは CVS で版管理しています。

## License

関連するファイルは Perl/MPL 1.1/GPL 2.0/LGPL 2.1 で配布しています。詳しくは各ファイルの注釈をご覧下さい。

## 関連する資源

xpointer.js (SuikaWiki)
  ご意見, 不具合情報等はこちらまでお願いします。
XPointer//API (SuikaWiki)
  XPointer を扱うための界面についての話題を扱っています。
CVS log for annozilla/src/content/annozilla/xpointer.js
  古い版の Annozilla は独自の ad hoc な XPointer の JavaScript による実装を行っていました。 (現在は XPointerLib を使用しているようです。)
`location.hash` を使ったセッション復元関連。
  素片識別子に Web 頁のセッション関連の情報を保存しようという試みです。