JavaScript 1.62005年09月09日 12時22分

前の記事で書いた Bug 306664 が修正済みとなり、Firefox 1.5 Beta 1 からは晴れて JavaScript 1.6 に対応するようになった。JavaScript 1.6 の新機能は前の記事に書いたとおり。実際に使用するには、

<script type="application/javascript; version=1.6">
// <![CDATA[

var xml = <element>content</element>;

// ]]>
</script>

という風に type 属性に version=1.6 パラメータを指定する。e4x=1 と指定しなくても E4X は有効になるし、逆に e4x=0 と指定しても無効にはならない。

<script language="JavaScript1.6">

と指定されたスクリプトも動作するが、この場合 E4X は有効にはならない

また、注意すべき点として、script 要素を知らないブラウザのために

<script type="application/javascript; version=1.6">
<!--

...

// -->
</script>

と内容をコメントアウトすると、その内容はまったく実行されない。<!--// のような一行コメントとしてではなく、XML リテラルのコメントの開始として解釈されるからだ。(E4X ではスクリプト中に XML 構文を直接記述できる。)

Bug 309242 の修正によりデフォルトで E4X が有効になった。<!-- から始まる行はこれまでどおり一行コメントとして解釈される。

HTML 4 では script 要素中に </ という文字列が現れてはいけないので、E4X を使う場合は外部スクリプトにしたほうがいいだろう。XHTML では CDATA セクションでスクリプトの内容を囲んでおいたほうがいい。

マークアップビルダ、存在しないメソッド2005年09月16日 15時30分

JavaScript、マークアップビルダ、存在しないメソッド」(Collection & Copy) を読んで。そういえば Firefox 1.5 には XMLContentBuilder というのがあった。XTF の一部として提供されているのだが、これを使うと (といっても XPCOM コンポーネントなので通常の Web 上では使えないが) 例えば table の作成は以下のように書ける、と思う。

var data = [["A1", "A2"], ["B1", "B2"]];
var b = Components.classes["@mozilla.org/xtf/xml-contentbuilder;1"]
                  .createInstance(Components.interfaces.nsIXMLContentBuilder);

b.setDocument(document);
b.setElementNamespace("http://www.w3.org/1999/xhtml");

b.beginElement("table");
  b.attrib("summary", "サンプルの表");
  b.beginElement("tbody");
    for (var i = 0; i < data.length; i++) {
      b.beginElement("tr");
        var cells = data[i];
        for (var j = 0; j < cells.length; j++) {
          b.beginElement("td");
            b.textNode(cells[j]);
          b.endElement(); // td
        }
      b.endElement(); // tr
    }
  b.endElement(); // tbody
b.endElement(); // table

document.body.appendChild(b.root);

beginElementendElement で囲まなければならないっていうのがちょっと泥臭いような気もするが、こうすることで途中に文をいくらでも挟めるのは便利。

それから、実は JavaScript でも存在しない関数を呼ぶことはできる。SpiderMonkey (Firefox などに使われている JavaScript エンジン) でしか利用できないのだが、__noSuchMethod__ というのがそれだ。__noSuchMethod__ というメソッドを定義しておくことによって、存在しないメソッドが呼ばれたときは代わりにそれが呼ばれるようになる。(参考: Bug 196097 Attachment 134673)

Object.prototype.__noSuchMethod__ = function (name, args) {
  alert("存在しないメソッド " + name + " が呼ばれました。");
};

というわけでこれを使えばこのようなマークアップビルダ (Firefox など Gecko 系専用) が可能になる。

var b = new MarkupBuilder();
b.p(null,
    "わからないことを ",
    b.a({href: "http://www.google.co.jp/"}, "Google"),
    " で調べる。");
<p>
  わからないことを
  <a href="http://www.google.co.jp/">Google</a>
  で調べる。
</p>

ブラウザ上でお絵かき2005年09月27日 02時37分

Ajax を使った手書き文字認識」を見て。ここでは線を描くのに絶対配置の span 要素を大量生成しているけど、最近のブラウザってベクター描画エンジン付きのものが多いわけで。WinIE 5 以降の VML 、Opera 8 ・ Firefox 1.5 以降の SVG 、Safari 1.3 ・ Firefox 1.5 以降の canvas 要素といった具合に。

ということで各ブラウザでベクター描画エンジンが使えればそれを使い、なければ絶対配置の HTML 要素を大量生成することで、ブラウザ上でお絵かきできるというものを作ってみた。元に戻す / やり直すこともできる。線を描くだけで塗りつぶしはできないが。

その線を描くために作ったのが DrawingCanvas クラス。以下のようにすることで三角形が描ける。実際の描画部分には SVG Tiny 、HTML Canvas 、CSS Positioning 、VML の 4 つのバックエンドがあり、ブラウザによってそれらを切り替えている。

var canvas = new DrawingCanvas(element, width, height);
canvas.setBgColor("#fff");
canvas.setLineColor("#000");
canvas.setLineWidth(5);

canvas.startLine(10, 10);
canvas.lineTo(50, 50);
canvas.lineTo(50, 10);
canvas.lineTo(10, 10);
canvas.endLine();

CSS Positioning バックエンド (絶対配置の HTML 要素を大量生成) の作成にあたっては線分描画のアルゴリズム (Fussy's HOMEPAGE) を参考にした。Bresenham ね、なるほど。(しかしこうしたアルゴリズムを自分で考えずに、すぐ検索に走ってしまうのは悪い癖か?)

作成にあたってつまずいた点などは以下のとおり。

  • Opera 8 がサポートしているのは SVG Tiny 。よって SVG DOM には対応していない。
  • Opera 8 では SVG 要素を文書に追加 / 削除しただけでは描画に反映されない。そのあと要素の属性値などを変更する必要がある。
  • Canvas の save() / restore() はパスの線の太さ・色といった状態を保存 / 復元するのであって、描画された画像そのものを保存 / 復元するわけではない。

スタイル変更2005年09月27日 04時41分

これまでここのスタイルシートはあらかじめ用意されたものにちょっと手を加えただけだったが、一念発起して一から書き直し。色使いは以前のものとそう変わらず。でもちょっとあっさりした感じになったかと思う。

それにしても IE には散々てこずらされた。そのうち 5 割くらいはボックスが内容の大きさに合わせて拡張されるバグに起因すると思う。IE7 ではこれは是が非でも直してもらいたいところ。その他原因を追究する気にもならない症状も多々あり。バグを使ったブラウザ振り分けはできるだけしたくなかったのだが、何箇所かでアンダースコアハックを使った。

正直今 CSS を組むときの IE の存在というのは (IE6 の標準モードであっても) 、昔 CSS を組んでいたときの NN4 の存在に匹敵するような気がしてきた。(当時は IE5 だか IE5.5 だかに満足していたはずなのに……なぜだろう。)

C MAGAZINE Firefox 特集2005年09月28日 05時50分

C MAGAZINE 2005 年 10 月号の特集「Firefox のすべて」を読みました (ごめんなさい、立ち読みです) 。「Part. 3 ソースからみた Firefox」は私に C++ での GUI プログラミング (というより C++ 自体) の経験がないこともあってざっと目を通すだけだったのですが、ちょっと気になった点が。

「Interface と実装は同じ UUID を持つ」というような記述があったと思うのですが、これは誤りではないでしょうか? 私も以前勘違いしていたのですが、ひとつの「実装」が複数のインターフェースを実装することもあれば、ひとつのインターフェースが複数の「実装」に実装されることもあるので (ややこしい……) 、インターフェースと実装の UUID は独立している必要があるのではないかと。例に出ていた StringBundleService の場合も、

インターフェース
  • IID: nsIStringBundleService
  • UUID: D85A17C0-AA7C-11d2-9B8C-00805F8A16D9
実装
  • Contract ID: "@mozilla.org/intl/stringbundle;1"
  • UUID: D85A17C1-AA7C-11d2-9B8C-00805F8A16D9

1 ビット違いますし。

それから、個人的には、Components.classes[...].createInstance() / getService() が呼び出された時点で実装が作られ、QueryInterface() はその実装の持つインターフェースを実際に使えるようにするもの、とイメージしているので、そのあたりの説明に少し違和感を感じました。

……なんて買ってもいないのにあつかましく書き散らして申し訳ない限りです。ほかの記事も合わせて 1 時間半も立ち読みするくらいならとっとと買えと……。