KLab若手エンジニアの これなぁに?

KLab株式会社の若手エンジニアによる技術ブログです。phpやRubyなどの汎用LLやJavaScript/actionScript等のクライアントサイドの内容、MySQL等のデータベース、その他フレームワークまで幅広く面白い情報を発信します。

JavaScript/CSS/HTML

IE9を入れたらPandionが動かなくなったでござるの巻

新し物好きのみなさんなら、正式リリース今や遅しと待ちかねているIE9のRelease Candidate版をさっそくいじり倒されていることかと思います。もう、いつRCが取れるかと気になって夜も寝られず、睡眠不足の毎日だったりするのではないでしょうか。 私もその中の一人…なわけではないんですが、ちょっと噂になっていたので、気になって触ってみたんです。IE8が出てそんなにまだしてないように思っていたのに、もう9ですってよ奥さん。はやいもんですわねー。そういえば聞きました?飯田さんとこのお子さんなんですけど。 とにかく、このIE9を入れるとPandionの動作が止まる現象が発生したというお話です。 このPandion、内部は大部分をJavaScriptで記述されていて中を見ることができます。エラーメッセージもこのように出ますので、


どこで問題が起こってるのかを調べることが可能です。

 /* Create the roster tab button
  */
 external.globals( 'ClientPluginContainer' ).Plugins.Add( '/roster', new ClientPluginCore( external.globals( 'ClientPluginContainer' ) ) );
 with ( external.globals( 'ClientPluginContainer' ).Plugins( '/roster' ).ClientPluginTab = new ClientPluginTab( external.globals( 'ClientPluginContainer' ).Plugins( '/roster' ) ) )
 {
  Icon = external.globals( 'cwd' ) + '..\\images\\main\\logo.png';
  Tooltip = external.globals( 'Translator' ).Translate( 'main', 'cl_tooltip', [ external.globals('softwarename' ) ] );
  IsActive = true;
  HTMLArea = document.getElementById( 'content-area' );
  DrawButton();
 }
with (...)の行が該当行です。内容的に目立って怪しいところがあるわけでもないのが悩ましいですが、どこで問題が起こっているかを一か所ずつ区切って確認しました。'.'で何段にもなっているどこかでnullが返っているのではないかと見てみたものの問題は無し。new ClientPluginTab(...)の部分も正常にインスタンス生成している。 となると問題はメソッド呼び出し。直前の行でexternal.globals('ClientPluginContainer')の部分は問題がないので、その次の.Plugins('/roster')の部分が怪しいんじゃないかとにらみました。Pluginsはこれ、他の箇所で下記のようにして生成されるActiveXオブジェクト"Scripting.Dictionary"。

/* This loads and manages the tabs.
 */
function ClientPluginContainer ()
{
	this.Plugins		= new ActiveXObject( 'Scripting.Dictionary' ); // filename->PluginCore
	this.HTMLTabBar		= document.getElementById( 'tab-bar' );
	this.HTMLTabArea	= document.getElementById( 'plugin-tab-area' );
"Scripting.Dictionary"オブジェクトは文字通り辞書オブジェクトを扱うわけなんですが、上記のPlugins( '/roster' )は辞書オブジェクトのkey='/roster'から紐づくvalueを取り出す呼び出しです。これはPlugins.Item('/roster')と書き換えても同じになるんですが。 書き換えたら通ったじゃないですか…。 というわけで、まだRCなので正式版になったら治るかもしれませんが、IE9ではこの"Scripting.Dictionary"オブジェクトの呼び出しに注意しないといけないようです。同様の問題のある箇所はここ以外にもたくさんあったので手元での修正はあきらめて、pandionの作者にこの件を報告しました。Pandionは業務でも大活躍なので、このままだとKLabではIE9が使えないことになってしまいますわん…。 http://getsatisfaction.com/pandion/topics/pandion_stops_working_with_ie9_rc Pandion stops working with IE9 RC

ユーザーのページ遷移をグラフで可視化してみた

takada-atです。 サイトを運営するとき、ユーザーがサイト上でどんな行動をとっているのか知りたくなることがあります。ページからページへリンクをたどって移動する動きは、テキストで表現してもいまいち理解しづらいので、グラフをつかって図にしてみます。 以下のリンクからデモを表示してみてください。 ユーザーがたくさん訪れたページほど、ノードのサイズが大きくなり、またページからページへ移動した人の数が増えるほど、線が太くなります。 HTML5のcanvasをつかって描画しているので、InternetExplorerでは見れません。InternetExplorer以外のブラウザで閲覧してください。 グーグルマップのようにドラッグで移動したり、カーソルキーで移動できます。


■アルゴリズムなど

ノード同士が適切に距離をとりあうようにEadesのバネモデルというアルゴリズムをつかって、ノードを動かしています。 ノード同士のリンクをバネに見たてて、物理シミュレーションしながらアニメーションさせています。 またデータはApache のアクセスログからjson形式のデータを作成しています。データ作成用のPythonスクリプトもgithubにアップロードしてあります。

IRCボットコンテストエントリ: Jsmin ブラウザで動くIRCボット

太平洋高気圧、お前本気出しすぎ。 あとちょっとだけ手加減してもたぶん大丈夫だぞ。 夏いですね。 入社して10日ちょっとのosuga-hです。よろしくお願いします。 締め切りを過ぎてしまいましたが、自分もIRCボットコンテストに向けてブラウザ(Chrome)で動作し(ロジックは)Javascriptで書かれた、Botを作ったので紹介させてください。 普段はBotのような、機能や性格が重要なものは作らないのですが、「そういえばJavascriptで書かれたBotって聞いたことないぞ」と思い立ってしまったので、実装しました。 たまには手段のために目的を選らばなくったっていいじゃない!

■Jsmin

ジャスミンと名づけたこのボットは以下のような機能を持っています。
  1. ハードコーディングされた人口無能 [human]あーなるほどね [Jsmin]本当にわかってるの?
  2. IRC経由で人口無能のルールを追加する機能 [human]add りんご ごりら [human]りんご [Jsmin]ごりら
  3. Javascriptを実行する機能 [human]exp function add( a , b ){ return a + b ; } add( 1 , 2 ) [Jsmin]3
  4. Javascriptをブラウザコンテキストで実行する機能 [human]forceEval alert( "moge" ) 俺の端末に突如alertによるダイアログがポップアップする
4つ目はお遊びですね。 Jsminは以下の2つのポリシーで実装しました。
  • ブラウザで動く
  • ロジックはJSで書く
あとは簡単に実装方法を紹介します。

■IRCのプロトコル部分

ソケット通信を行う必要があり、またIRCのプロトコルも喋らなければならず、ここを1から実装するのはつらかったので その昔所属していた組織でのっぴきならない事情があったためIRCクライアントを自作したときに使った flexircclient というActionScriptのライブラリにJSとのインターフェースを実装しました。 なのでPure Javascriptではありません。 HTML5ならWebSocketあるじゃんという話もあるのですが、Handshakeなどで問題がおきそうだったので今回は完全に選択肢から除外しました。

■人口無能ルールの追加

送られてきたルールはlocalStorageに保存しています。 なので、ブラウザを閉じても学習(?)成果は保存されます。 また、Chromeでは開発者ツールを使うことでlocalStorageの中身をいじくれるので、メンテナンスも簡単です。

■Javascriptコードの実行

危険なAPIの呼び出しなどをしてほしくないので安全なコンテキストで送られてきたJSのコードを評価したいですね。 そのために今回はWebWorkerを使いました。 WebWorkerの中ではdocumentやwindowといったオブジェクトにアクセスできません。 このWebWorkerをサンドボックスとして使うアイデアはこちらで紹介されていました。 ただwhile(1){}とかやられると、困ります。 やるなよ絶対やるなよ。

■ブラウザコンテキストでのコード実行

この機能は、location.href="http://www.google.com"とかやられて、ボットがIRCから出て行ってしまったり、 while(true){alert("\(^o^)/");}とかやられて俺の作業が妨害されたりしたら、面白いんじゃねーの? っていうためだけに存在しています。 実現の方法はただただevalするだけです。 なんのチェックもしてません。

■ソースコード

SWFとかもあるので動作に必要なものはこちらにまとめておきました。 興味のある方はご覧ください。 JSの部分だけですがソースを貼り付けておきます。
//Flash initialization
var flashvars = { };
    params = {
        menu: "false",
        scale: "noScale",
        allowFullscreen: "false",
        allowScriptAccess: "always",
        bgcolor: "#FFFFFF"
    },
    attributes = { id:"FlexIRCClient" };

swfobject.embedSWF("FlexIRCClient.swf", "altContent", "1px", "1px", "9.0.0", "expressInstall.swf", flashvars, params, attributes);

//Util
function log( text ){ document.getElementById("cmd").innerHTML = text + "
" + document.getElementById("cmd").innerHTML  ; }
function $ (){ return document.getElementById.apply( document , arguments ); }

//Interface for swf
//  initialized after onReady event
var client ;

//------------------------------------------------------
// Event handlers of SWF

//SWF is on ready to connect IRC
function onReady(){
    client = document.FlexIRCClient ;
    $("message").innerHTML = "";
    $("form").style.display = "block";

}
function connect(){
    $("message").innerHTML = "connecting..." ;
    $("form").style.display = "none" ;
    client.connect( $("name").value , $("host").value , $("channel").value ) ;
}

//entered to channel
function onEnterChannel( ){
    $("message").innerHTML = $("name").value + "entered to channel '" + $("channel").value + "'" ;
    mergeLocalStorage();
}

//on recieve message
function onMessage( message ){
    setTimeout( function(){
        var i , n , func ;
        for( i = 0 , n = reg.length ; i < n ; i++ ){
            if( message.match( reg[i][0] ) ){
                func = reg[i][1] ;
                var a = reg[i].slice( 2 );
                a.push( message );

                func.apply( this , a );
                break;
            }else{

            }
        }
    },10 );
}

//------------------------------------------------------
// Bot settings
var reg = [
    [ /なるほどね?。?$/ , muno          , "本当にわかってるの?" ] ,
    [ /ってことか。?$/  , muno          , "マジで!?"           ] ,
    [ /^実は/           , muno          , "マジで!?"           ] ,
    [ /^add/            , addRule       ] ,
    [ /^exp/            , exp           ] ,
    [ /^forceEval/      , forceEval     ]
] ,
sandbox = new Worker( "sandbox.worker.js" ); //webworker for sandbox
sandbox.onmessage = function ( event ){      //on receive result from webworker
    data = event.data ;
    client.send( data ); //send result to irc
}

function muno( text ){
    client.send( text );
}

function exp( text ){
    var code = text.replace( "exp" , " "); //parse irc message

    sandbox.postMessage( code ); //pass code to webworker
}

function forceEval( text ){
    var code = text.replace( "forceEval " , " "); //parse irc message

    try{
        var result = eval( code ); //run code
        client.send( result );
    }catch( e ){
        client.send( e.toString() );
    }
}

function addRule( message ){
    var param = message.replace( "add " , "" ).split( " " ); //parse irc message
    if( param.length != 2 ){ return; }

    localStorage[ param[0] ] = param[1] ; // save to localStorage
    reg.push( [ param[0] , muno , param[1] ] );
}

/**
 * load rules from localStorage
 */
function mergeLocalStorage(){
    var key , val ;
    for( key in localStorage ){
        val = localStorage[key];
        reg.push( [ key , muno , val ] );
    }
}

firefoxアドオン「FireMobileSimulator」に追加機能をコミットしました。

はじめまして、nagai-kです。 今回は、携帯サイト開発のサポートツール「FireMobileSimulator」のご紹介とそれをちょっとハックしたよ、ということを書いてみたいと思います。
FireMobileSimulatorは、主要3キャリア(DoCoMo/Au/SoftBank)の携帯端末ブラウザをシミュレートして、モバイルサイト開発を容易にするために作成されたFirefoxのアドオンです。
私は携帯サイトを開発する事がよくありますので、このアドオンは業務をする上で必須のツールになっています。 携帯サイトの開発で大変なことといえば、最終出力であるHTMLの確認です。 プログラムを修正するたびに、ファイルをサーバにアップロードして携帯から確認する・・・・。 そんなことをするのは大変なので、普段は「FireMobileSimulator」を使って、Firefox で確認しています。 ※もちろん最終的は確認は携帯端末の実機で行っていますよ^^ 今回はこの「FireMobileSimulator」を勝手にハックしてみました。 さらに、作者の方に送ってみたら正式に採用されてしまいました! 追加した機能は、
  • 特定のURL(ドメイン)だけで端末選択機能が有効になる機能
  • 端末ごとにUIDなどの個体識別番号を設定する機能
です。内容について詳しくはオフィシャルサイト「1.1.9リリースノート」をご覧ください。
【少しだけ技術的なはなし】 私自身はじめてのFirefoxアドオン開発だったので技術的に詳しくはないですが少し解説したいと思います。 解説の準備としてまず、「FireMobileSimulator」をこちらからダウンロードしてください。 ※Firefoxで左クリックするとインストールしようとしてしまうので、その場合は右クリックで保存してください。 ダウンロードしたxpiファイルを展開します。 (xpiファイルはzipと同じなので、Windowsであれば拡張子を"zip"に変えて展開できます。) 展開したフォルダの中に「/chrome/msim.jar」と言うファイルがあるので、これも展開しておきます。 (xpi同様に拡張子をzipに変えてでOKです。) これでソースを読む準備ができました。 「\components\msimModifyHeaders.js」が今回修正したファイルです。 このファイルに携帯をシミュレートするための端末情報やUIDをHTTPリクエストに付加したりする処理が含まれています。 ファイルを見ると、docomo、SoftBank、au別にHTTPリクエストの処理を変えているのがわかると思います。
if (carrier == "DC") {
  ~ docomo用のHTTPリクエスト処理 ~
		
} else if (carrier == "SB") {
  ~ SoftBank用のHTTPリクエスト処理 ~

} else if (carrier == "AU") {
  ~ au用のHTTPリクエスト処理 ~
}
HTTPリクエストをハックする場合はこの処理を書き換えてみてください。 JavaScriptとHTTPプロトコルがなんとなくわかっていれば、簡単にハックが可能だと思います。 ここでは省略しますが、デバッグメッセージをコンソールに出せるようにしておくと修正作業がはかどります。 とても簡単な説明ですが、大体こんな感じでコードの修正していきました。 このように簡単に手を入れる事ができますので、皆さんも是非Firefoxアドオンの拡張/作成をしてみてはいかがでしょう?

Rhinoを使おう

全国1千万人のJavaScriptユーザーの皆様こんにちは。たかだです。 本日は、拡張性とポータビリティにすぐれた JavaScript 処理系 Rhino(ライノー)をご紹介したいと思います。 JavaScript マニアなら、ブラウザ上だけではなく、もっと汎用的なツールとして JavaScript を利用したいと思いますよね? 日常的に利用するちょっとしたスクリプトはもちろん、できれば サーバーサイドのWebアプリや GUIアプリケーションまで JavaScriptで書きたいですよね?(という人が何人いるのかわかりませんが) RhinoはJavaScriptの実行環境としてだけではなく、Javaをインタラクティブに操る処理系としても役立ちます。 Rhino はオープンソースで開発されている JavaScript の処理系です。Mozilla Foundation によって管理され、Java言語で実装されています。 最大の特徴は、Java のクラスやメソッドを JavaScriptから操作できることです。JavaScriptコードを Javaバイトコードに変換し、classファイルを作成することもできます。 このため、処理系自体を拡張しなくても、Javaのほとんど全機能を JavaScriptから利用することができます。 など、ブラウザから独立した JavaScript(JScript)処理系にはいくつかの選択肢がありますが、拡張性に優れ、Java言語の機能を引き出せる Rhino はもっとも有力な選択肢の1つです。

インストール

さっそく使ってみましょう。 公式サイトより最新版を入手してください。 これ以外にJDK 1.4以降の Java実行環境が必要です。Java実行環境のインストールについてはここでは説明しません。 適当な場所に解凍してください。js.jarというファイルがあるはずです。こちらのファイルに、クラスパスを通せばさっそく実行できます。 以下のコマンドを実行するとJavaScriptのインタラクティブシェルが立ち上がります。 ## 以下 /path/to/rhino は解凍したrhinoのディレクトリに合わせて変更してください。
$ java -classpath /path/to/rhino/js.jar org.mozilla.javascript.tools.shell.Main
Rhino 1.7 release 0 0000 00 00
js> print(1+1);
2
js>
終了には quit() という関数を呼び出します。
js> quit()
ファイルに保存したスクリプトを実行することもできます。
$ echo 'print("hello, world");' > hello.js
$ java -classpath /path/to/rhino/js.jar org.mozilla.javascript.tools.shell.Main hello.js
hello, world

ただし、こんなに長いコマンドを覚えるのは大変なので、呼び出し用の短いエイリアスを作成しておきましょう。 Windowsでは、以下のようなファイルをrhino.batという名前で保存し、パスの通った場所に置いておきます。 Windows環境では、日本語の文字化けを防ぐため、-Dfile.encoding=UTF-8というオプションを付ける必要 あります。
@echo off
set CLASSPATH=/path/to/rhino/js.jar
java -Dfile.encoding=UTF-8 org.mozilla.javascript.tools.shell.Main %1 %2 %3 %4 %5 %6 %7 %8 %9
bash(UnixライクOS)では以下の用なファイルをrhinoという名前で保存し、パスの通った場所に置き、実行権限を与えます。
export CLASSPATH=/path/to/rhino/js.jar
java org.mozilla.javascript.tools.shell.Main $*
以上により、rhino というコマンドで、rhino のJavaScriptシェルを呼び出すことができるようになりました。
$ rhino
Rhino 1.7 release 0 0000 00 00
js> 

Javaの機能を呼び出す

rhinoからJavaの機能を呼ぶのはとても簡単です。
js> java.lang.System.out.println("Hello");
Hello
以上のようにパッケージつきで呼び出せば、まるでJavaScriptの関数を呼ぶように簡単にJavaのメソッドにアクセスできます。 パッケージをタイプするのが面倒な場合は、importPackageという関数で、グローバル環境に読み込むことができます。
js> importPackage(java.lang);
js> System.out.println('hello');
hello
パッケージは、JavaScriptのオブジェクトのような扱いになっているので、変数に代入もできます。
js> var $out = java.lang.System.out;
js> $out.println('Hello');
Hello
Javaクラスのインスタンスを作成するのも簡単です。
js> var f = new java.io.File('.');
js> f.exists();
true

便利な標準関数

Javaの機能を呼びださなくても、いくつかの機能は標準で提供されています。 以下の関数などはかなり使いどころがあるのではないでしょうか。
print()
標準出力に文字列を出力する。
readFile(path)
ファイルを読み込み、内容を文字列として返す。
readUrl(url)
URLを読み込み、内容を文字列として返す。
runCommand(cmd)
外部コマンドを実行する。
Rhinoを使えば汎用的スクリプト言語としてJavaScriptを利用できるのはもちろん、Javaアプリのテストなどにも強力な性能を発揮します。
 KLab若手エンジニアブログのフッター