Using jQuery on WSH

jQueryを使うと、ajax 経由で取得した XML ドキュメントのような、ブラウザ(window,document)と切り離された対象でも簡単に操作することができます。例えば、リモートから取得したRSSであれば、以下のようなコードで各項目(item)のタイトル(title)やリンク先(link)、日時(dc:date)を取得することができます*1

    $.ajax( {
        url : "http://www.example.com/rss.xml",
        dataType : "xml",
        type : "get",
        success : function( xml ){
            var s = "";
            $( xml ).find( "item" ).each( function(){
                s += 
                    $(this).find( "title" ).text() + " <" +
                    $(this).find( "link" ).text() + ">(" +
                    $(this).find( "dc\\:date" ).text() + ")\n";
            } );
            alert( s );
        }
    } );

このように、jQueryを使うと直接DOMを操作するのに比べてXMLでも楽に扱えるので、ブラウザから切り離された環境である WSH / JScript でも jQuery を使ってみたいと思うのは当然でもあります。というわけで、jQueryWSH 環境で使えるのか試してみました。
結論から言うと、簡単な準備をしてくことで、少し制限はあるものの十分実用的に WSH 環境でも jQuery を利用することができました。
以下、jQueryWSHで使うためのコードです。

// グローバル名前空間に window や document などを準備する
(function(){
    if( typeof( document ) == "undefined" ){
        document = new ActiveXObject( "htmlfile" );
        document.write("<html></html>"); // これ重要
    }
    if( typeof( window     ) == "undefined" ) window = document.parentWindow;
    if( typeof( alert      ) == "undefined" ) //alert = window.alert; 
        alert = function(s){ return window.alert( s )};
    if( typeof( confirm       ) == "undefined" ) confirm = function(s){ return window.confirm(s) };
    if( typeof( location      ) == "undefined" ) location = window.location;
    if( typeof( navigator     ) == "undefined" ) navigator = window.navigator;
    _util = {};
    _util.cat = function( filename ){
        var fso = new ActiveXObject( "Scripting.FileSystemObject" );
        var f = fso.OpenTextFile( filename, 1, false );
        return f.ReadAll();
    }
})();

// jQuery の読み込み
eval( _util.cat( "jquery-1.3.2.min.js" ) );

// 簡単な jQuery オブジェクトの操作例
WScript.Echo( $( "<div>" ).attr( "a", "fo" ).get(0).outerHTML );

// もちろん alert も使える。
alert( $( "<div>" ).attr( "a", "fo" ).get(0).outerHTML );

// jQuery.ajax を使ってリモートからXMLを読み込み
$.ajax( {
    url : "http://www.example.com/rss.xml",
    dataType : "xml",
    type : "get",
    async : false, // これ重要
    success : function( xml ){
        var s = "";
        $( xml ).find( "item" ).each( function(){
            s += 
                $(this).find( "title" ).text() + " <" +
                $(this).find( "link" ).text() + "> (" +
                $(this).find( "dc\\:date" ).text() + ")\n";
        } );
        alert( s );
    }
} );

まず、グローバルに document を用意するため、new ActiveXObject( "htmlfile" ) でHTMLDocumentを生成します。これは new ActiveXObject( "Internet Explorer.Application" ) とした場合と異なり、アウトプロセスでIEインスタンスを生成しないため、非常に軽量です。これで、グローバルな document が用意できたのですが、このままだと document.body や document.documentElement が null なままですので、とりあえずダミーで body を用意してやるために document.write( "" ); としてやります。ここまでで document の準備が完了です。
つぎに window ですが、これは document.parentWindow がそのまま利用可能なので、グローバル名前空間に放り出してやります。
通常の JavaScript であればグローバル名前空間は window に支配されているので、例えば alert を呼び出せばそれは暗黙のうちに window.alert の呼び出しとなるわけですが、WSH ではグローバルな名前空間は window とは無関係ですので、window のメンバのうち必要そうなものをグローバルな名前空間に export してやる必要があります。navigator のような単純なプロパティであれば同名のグローバル変数を用意してそこに代入するだけでよいのですが、alert のようなメソッドをexportする場合、alert = window.alert; のような代入では、なぜか呼び出しに失敗するため*2、ラッパ関数を用意してやる必要があります。

window、document をはじめとする一連のグローバルなオブジェクト類の準備ができればあとはjQueryを読み込み、いつもどおりのプログラミングができます。
ただし、本当の window を持っていないため setInterval や setTimeout がうまく扱えません。そのため、jQuery.ajax を呼び出す場合には、async:false が必須となりますので、注意が必要です。(2009-09-30追記: seInterval を誤魔化して async:true で動きました。)もちろん、dataType:"jsonp" などはうまく動きません。

以上、IE8環境でのテストですので他のバージョンのIEでは異なる動作となるかも知れませんが、jQueryが動けばWSHでのちょっとしたツールの作成もとても簡単で楽しいものになります!

追記:激しくガイシュツというのを umq さん、ZIGOROu さんに教えてもらった(´・ω・`)ショボーン

*1:":"を含む要素名のセレクタの書き方は、Wassrでmonmonさんに教えてもらいました。ありがとうございます。

*2:typeof(alert) == "object"になっている