Home > develop > Javascript Archive

Javascript Archive

$.deferredを利用したflickrのjqueryプラグイン

jQuery1.5から追加されたjquery.deferred。どんなものなのかはなんとなく理解していたのですが、全く触ったことがなかったので練習がてら、このblogのトップページにあるFlickrの最新画像サムネイル表示ライブラリを、jquery.deferredを利用したjquery.pluginとして書きなおしてみました。

利用方法

jquery(1.5)以上と、当jsをロードしておいた状態で、

$('#foo').flickr({ user_name: 'katsuma'});

こんなかんじで最新画像のサムネイルがロードされます(デフォルトでは6枚)。Topページもこのシンプルな方法でロードさせています。

枚数を変更したいときは

$('#foo').flickr({ user_name: 'katsuma', view_num: 3 });

こんなかんじ。

サイズを変更したいときは

$('#foo').flickr({ user_name: 'katsuma', view_num: 3, size: 'small'});

こんなかんじ。詳しくは実際に試せるデモがあるのでこちらをご覧ください。

jquery.deferred

で、肝心のdeferredですが、今回は非同期のjsonpを連続して実行しつづけているのですが、普通に書くとcallbackが激しく入れ子構造になってしまうところ、それらの非同期処理を「割と同期処理っぽく綺麗に書ける」のがいいのかな、、という理解です。 たとえば、今回はユーザIDの逆引き→最新画像情報の取得→実画像のパスを取得のフローになりますが、この流れを次のように書くことができます。(実際のコードを簡略化しています)

$.when(methods.findByUsername(setting.user_name)).then(function(){
  $.when(methods.getPublicPhotos()).then(function(){
    $.when(methods.loadPhoto(setting.photos));
  });
});

こんなかんじで、$.when~thenでchain構造でつなげていけるので、非同期処理ながらも「この処理はこの後で実行」を指示できるのがウリです。よくありがちなのはこんな書き方じゃないでしょうか。

var methods = {
  findByUsername : function(name){
    $.ajax({
      ...
      callback : function(data){
        ...
        methods.getPublicPhotos(); // callback内から次に実行する関数を呼び出し
      }
    });
  },

  getPublicPhotos : function(){
    ...
  }
};

// 最初に呼び出す関数
methods.findByUsername(setting.user_name);

こんな感じでも書けますが、呼び出し元がコード内で散らばるので、可読性が著しく落ちることになります。jquery.deferredを利用すれば、when~thenのchainでまとめられるので、「どういう順番でどの関数が呼び出されているか」を追いかけやすくなります。 まだまだ他にもメリットはあるかと思いますが、ざっと書いてみたところとりあえずこの点が一番のメリットかな、と感じました。

勘違い

最初は結構勘違いしていて、非同期処理を完全に同期処理として書けるものなのかと思ってました。なので、こんなふうに書けちゃうのかな、と。

// findByusernameはjsonpで実行...だけどreturnでidが取れるはず!
var user_id = methods.findByUsername(setting.user_name); 

 // 上記結果を元にして再びjsonpでgetPublicPhotosを呼び出せる!
var photos = methods.getPublicPhotos(user_id);

当然こんなかんじには動きません。非同期処理はあくまでも非同期なので、returnで同期処理っぽく値を取れることはありません。このあたりはやっぱコード書かないとわからないですね。

Hadoop Streamingを利用してJavaScriptでMap Reduce

久々のBlog更新、というわけでリハビリがてらJavaScriptで軽く遊んでみたいと思います。

いま、巷で流行ってるMapReduceのオープンソース実装Hadoopは「Hadoop Streaming」という標準入出力でデータのやりとりができる仕組みを使って、 Hadoopの実装言語であるJavaにとらわれず、RubyやPerlなど他の言語でもMap+Reduceの処理ができることが1つのウリになっています。 で、僕たちwebエンジニアはみんなJavaScript大好きなので、「JavaScriptでもMap Reduceやりたい!」という流れになるのは必然です。 そこで、試行錯誤でいろいろ試してみると割とさっくり出来たのでそのメモを残しておきたいと思います。

環境の整備

Mac OSX上のVMWare FusionにCentOSの仮想マシンを2台立ち上げて、環境セットアップしました。以下のような手順で環境整備しました。

仮想HWの設定

仮想マシンのイメージファイルですが、僕は毎回thoughtpoliceのサイトのものを利用しています。ここからCentOS 5.3のイメージを落としてFusionでロード。

1つハマったのが、同じイメージをコピーして複数台起動すると、MACアドレスがかぶって同時に複数台の仮想マシンがNWに接続できない点。なので、仮想マシンを最初に起動させる前にMACアドレスの設定をしておくことをおすすめします。

MACアドレスは、vmxファイルのuuid.bios値をもとに自動生成されるようで、この値を最初から全仮想マシンでバラバラにしておけばOKです。と、いっても有効な値の範囲があるので、最後の1byte値だけズラしておけばOKだと思います。たとえば、上記サイトからイメージを取得した場合、uuid.bios値の最後は「7d」になっているので、僕はこの値を「7c」「7b」なんかにズラしておきました。(Fusion上から正規の方法?で、マシンをクローンする方法がよくわからなかったので、こういう強引な方法を取っています。綺麗な方法をご存知の方いらしたらぜひ教えてください!)

ネットワーク設定

/etc/hostsを設定して2台のマシンの名前解決。細かな話ですけど、ホスト名には「_(アンダーバー)」は使えないのに要注意。最初「hdp_01」みたいなホスト名にしていて、実行時エラーが出てドはまってたときに全然気づかなかった。

  • 192.168.1.15 hdp-01
  • 192.168.1.16 hdp-02

SSHの設定

マシンはそれぞれ自分自身(localhopst)に対して、パスフレーズなしでSSHでログインできるように公開鍵を設定しておきます。

  • ssh-keygen -t rsa -P ""
  • cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys
  • chmod 600 ~/.ssh/authorized_keys

また、MasterのマシンからSlaveのマシンに対しても同様の設定をしておきます。今回はMasterはhdp-01, Slaveはhdp-02という設定です。hdp-01上で、次の設定をします。

  • cp ~/.ssh/id_rsa.pub ~/.ssh/id_rsa_master.pub
  • scp ~/.ssh/id_rsa_master.pub hdp-02:/home/katsuma/.ssh/
  • chmod 600 ~/.ssh/authorized_keys

また、hdp-02上で、次の設定をします。

  • cat ~/.ssh/id_rsa_master.pub >> ~/.ssh/authorized_keys

これで、Masterからlocalhost, およびSlaveに対してパスフレーズなしでログインできることを確認しておきます。

セキュリティの設定

後でハマるのが面倒なので、iptablesやSELinuxは切っておきます。必要であれば適宜設定してください。

  • sudo /etc/init.d/iptables off
  • sudo /etc/sysconfig iptables off
  • sudo vi /etc/sysconfig/selinux
    • SELINUX=disabled に変更

Shellの設定

.bashrcなんかに以下の設定をしておくと便利です。僕はzsh派なので、.zshrcに記述しました。

# hadoop
alias cdh='cd /home/katsuma/hadoop/latest/'
alias dls='/home/katsuma/hadoop/latest/bin/hadoop dfs -ls'       # ls
alias drm='/home/katsuma/hadoop/latest/bin/hadoop dfs -rm'       # rm
alias dcat='/home/katsuma/hadoop/latest/bin/hadoop dfs -cat'     # cat
alias drmr='/home/katsuma/hadoop/latest/bin/hadoop dfs -rmr'     # rm -r
alias dmkdir='/home/katsuma/hadoop/latest/bin/hadoop dfs -mkdir' # mkdir
alias dput='/home/katsuma/hadoop/latest/bin/hadoop dfs -put'     # HDFS に転送
alias dget='/home/katsuma/hadoop/latest/bin/hadoop dfs -get'     # HDFS から転送
alias dcpfl='/home/katsuma/hadoop/latest/bin/hadoop dfs -copyFromLocal'
alias dcptl='/home/katsuma/hadoop/latest/bin/hadoop dfs -copyToLocal'

Javaのインストール

Sunのサイトに行き、 「Java SE Development Kit (JDK)」を選択して、インストーラをダウンロード。その後、以下の手順でインストールできます。

chmod +x jdk-6u7-linux-i586-rpm.bin
sudo ./jdk-6u7-linux-i586-rpm.bin

Hadoopの整備

Hadoopのインストール

Hadoopは今日時点で最新の0.20.0を利用しました。インストール場所はどこでもいいのですが、僕はホームディレクトリ直下に専用のディレクトリ掘って、いろんなバージョン試せるようにこんな感じで設置してます。ここから最新版をDLが可能です。

  • cd path/to/hadoop-0.20.0.tar.gz
  • tar zxvf hadoop-0.20.0.tar.gz
  • mv hadoop-0.20.0 ~/hadoop/
  • ln -s hadoop-0.20.0 latest

他のバージョンを試したくなったら~/hadoop/以下に展開して、シンボリックリンクを付け直すとOKですね。

Hadoopの設定

特に凝ったことはしていません。全ノードともに同じ設定である必要があるので、Masterで設定しちゃって、それをrsyncで他のSlaveと同期をとるのがよいと思います。

conf/hadoop-env.sh
export JAVA_HOME=/usr/java/latest
conf/core-site.xml

<configuration>
  <property>
    <name>hadoop.tmp.dir</name>
    <value>/home/${user.name}/hadoop/latest/tmp</value>
  </property>
  <property>
    <name>fs.default.name</name>
    <value>hdfs://hdp-01:9000</value>
  </property>
</configuration>

conf/hdfs-site.xml

<configuration>
  <property>
    <name>dfs.replication</name>
    <value>1</value>
  </property>
</configuration>

conf/mapred-site.xml

<configuration>
  <property>
    <name>mapred.job.tracker</name>
    <value>hdp-01:9001</value>
  </property>
</configuration>

conf/masters

      hdp-01

conf/slaves

      hdp-02

ここまで設定できたら、masterのイメージをrsyncしておきましょう。

cd ~/hadoop/
rsync -r hadoop-0.20.0/ hdp-02:/home/katsuma/hadoop/hadoop-0.20.0

SpiderMonkeyの導入

さて、やっとHadoopの設定が終わったので、次にJavaScriptの処理系の導入です。 Hadoop Streamingは前述の通り、標準入出力の仕掛けを使って実現されているので、さすがにブラウザの処理系をそのまま利用することができません。

そこで、FirefoxのJavaScriptのエンジンであるSpiderMonkeyを利用することにします。 SpiderMonkeyはファイルを入力としても処理できるし、irbのように対話型シェルとしても利用できるJavaScriptの処理系です。また、ブラウザを利用しないので、標準出力関数print、標準入力関数readlineが実装されてあるので、今回はこれを利用すればうまくいきそうです。

では、SpiderMonkeyを各ノードに導入します。これも最新版を導入します。あらかじめ、make, gccあたりをyumで入れておきましょう。

参考:SpiderMonkeyのインストール

  • wget http://ftp.mozilla.org/pub/mozilla.org/js/js-1.8.0-rc1.tar.gz
  • tar zxf js-1.8.0-rc1.tar.gz
  • cd js/src
  • BUILD_OPT=1 make -f Makefile.ref
  • sudo install -m 755 Linux_All_OPT.OBJ/js /usr/local/bin

Map, Reduce処理のJavaScriptを記述する。

では、Map, Reduceそれぞれの処理をJavaScriptで書きます。今回はサンプルとしてよくある、ワードカウントの処理を行ってみます。

map.js

map.jsは標準入力された文章を半角スペースごとに分けて、CSVの形式に整形します。JavaScript1.7相当の機能が利用できるので、Array.prototype.forEachが利用できることもポイントです。

#!/usr/local/bin/js

var line="";
while ((line = readline())!= null){
        var words = line.split(" ");
        words.forEach(function(w){
                print(w + "," + 1);
        });
}

reduce.js

reduce.jsは、map処理されたCSV形式の入力に対して、counterオブジェクトで各単語をカウントしていきます。

#!/usr/local/bin/js

var counter = {};
var line = "";
while ((line = readline()) != null) {
        var words = line.split(",");
        var word = words[0]
        if(!counter[word]) counter[word] = 1;
        else counter[word]++;
}

for(var k in counter){
        print(k + ":" + counter[k]);
}

これらのJavaScriptファイルをhadoop/latest/script/あたりに保存しておき、全ノードで同期させておきます。

Hadoopの起動

最初にHDFSをフォーマットしておきます。Masterで次の処理を行います。

cdh
./bin/hadoop namenode -format

MasterでHadoopを起動します。

bin/start-all.sh

MapReduce用の適当な入力ファイルを作成します。こんな内容のファイルを$HADOOP_HOME/input/file1に作成します。

we are the world we change the world

このファイルをHDFSに転送します。dputは bin/hadoop dfs -putのエイリアスです。

dput input/file1 in/count

転送されてあるかどうかは、dls(bin/hadoop dfs -ls)で確認できます。

katsuma@hdp-01 ~/hadoop/latest
$ dls
Found 1 items
drwxr-xr-x   - katsuma supergroup          0 2009-07-31 02:14 /user/katsuma/in

katsuma@hdp-01 ~/hadoop/latest
$ dls in
Found 1 items
drwxr-xr-x   - katsuma supergroup          0 2009-07-31 02:56 /user/katsuma/in/count

入力用ファイルの存在が確認できたので、これでやっとMapReduce処理ができます。

./bin/hadoop jar ./contrib/streaming/hadoop-0.20.0-streaming.jar -input in/count -output out/count -mapper "js /home/katsuma/hadoop/latest/script/map.js" -reducer "js /home/katsuma/hadoop/latest/script/reduce.js"

すると、ゆったりですけど処理が進んでいきます。

packageJobJar: [/home/katsuma/hadoop/latest/tmp/hadoop-unjar4327209233542314881/] [] /tmp/streamjob4726696067913490494.jar tmpDir=null
09/07/31 10:45:05 INFO mapred.FileInputFormat: Total input paths to process : 1
09/07/31 10:45:06 INFO streaming.StreamJob: getLocalDirs(): [/home/katsuma/hadoop/latest/tmp/mapred/local]
09/07/31 10:45:06 INFO streaming.StreamJob: Running job: job_200907310237_0013
09/07/31 10:45:06 INFO streaming.StreamJob: To kill this job, run:
09/07/31 10:45:06 INFO streaming.StreamJob: /home/katsuma/hadoop/latest/bin/../bin/hadoop job  -Dmapred.job.tracker=hdp-01:9001 -kill job_200907310237_0013
09/07/31 10:45:06 INFO streaming.StreamJob: Tracking URL: http://hdp-01:50030/jobdetails.jsp?jobid=job_200907310237_0013
09/07/31 10:45:07 INFO streaming.StreamJob:  map 0%  reduce 0%
09/07/31 10:45:23 INFO streaming.StreamJob:  map 50%  reduce 0%
09/07/31 10:45:45 INFO streaming.StreamJob:  map 50%  reduce 17%
09/07/31 10:48:49 INFO streaming.StreamJob:  map 100%  reduce 17%
09/07/31 10:49:10 INFO streaming.StreamJob:  map 100%  reduce 100%
09/07/31 10:49:15 INFO streaming.StreamJob: Job complete: job_200907310237_0013
09/07/31 10:49:15 INFO streaming.StreamJob: Output: out/count

HDFS上のout/count/以下に結果が格納されたファイルができているので確認してみましょう。

katsuma@hdp-01 ~/hadoop/latest
$ dls out/count
Found 2 items
drwxr-xr-x   - katsuma supergroup          0 2009-07-31 10:45 /user/katsuma/out/count/_logs
-rw-r--r--   1 katsuma supergroup         43 2009-07-31 10:48 /user/katsuma/out/count/part-00000

katsuma@hdp-01 ~/hadoop/latest
$ dcat out/count/part-00000
are:1
change:1
the:2
we:2
world:2

まとめ

SpiderMonkeyのような処理系を用意することで、JavaScriptでもHadoopを使ってMapReduceできることが確認できました。標準入出力さえサポートされてあれば、理屈的にはどんな言語でもMapReduceできるので、MapReduceには興味あるけどJavaということで敬遠していた方は、ぜひいろんな言語で試していただければと思います。

開発のTips

なんだかんだ言って、最初開発にかなり苦労しました。と、いうのもシンタックスエラー以外は、実行しないとどうなるかよくわからないものなので、エラーで落ちたときにどうデバッグすればいいか悩みました。 ただ、よく考えれば、「標準入力→Map処理→Mapの標準出力→Reduceの入力→Reduceの標準出力」という流れになるので、たとえば今回のJavaScript実装の場合、次のように実行することでHadoopを介さなくとも動作確認は可能です。

cat input/file1 | js script/map.js | js script/reduce.js

まずは、このように手元でパイプでつないで結果を調べてみる、というのが手かと思います。実際は手元でうまく動いてもHadoop上でRuntimeエラーが起きる場合も多いので、そのときはlogディレクトリ以下にできるログファイルを調べるのがいいと思います。

また、エラーが発生するとJavaの例外のStackStraceが表示されるので、敬遠せずにそこからHadoopのソースを直接追いかけるのは何だかんだで早い解決法でした。コメントが割と充実しているので、そこからStreaming用のコードのバグを辿るのも難しくはないと思います。

Flash Playerバージョン判定スクリプト

空前のFlash Playerバージョン判定スクリプトブームなわけですが、便乗して僕も乗ってみたいと思います。いろんなところからのものを集めてきたものですけど実際に使っているスクリプトであったりします。結構古い環境もサポートしてます。

FlashPlayer= {
	version : (function(){
		var version='0.0.0';
		if(navigator.plugins && navigator.mimeTypes['application/x-shockwave-flash']){
			var plugin=navigator.mimeTypes['application/x-shockwave-flash'].enabledPlugin;
			if (plugin && plugin.description) {
				version=plugin.description.replace(/^[A-Za-z\s]+/, '').replace(/(\s+r|\s+b[0-9]+)/, ".");
			}
		} else { // Win IE
			var x='';
			try {
				var axo=new ActiveXObject("ShockwaveFlash.ShockwaveFlash.7");
				x=axo.GetVariable("$version");
			} catch(e) {
				try {
					axo=new ActiveXObject("ShockwaveFlash.ShockwaveFlash.6");
					x="WIN 6,0,21,0";
					axo.AllowScriptAccess="always";
					x=axo.GetVariable("$version");
				} catch(e) {
					if (!x.match(/^WIN/)) {
						try {
							axo=null;
							axo=new ActiveXObject("ShockwaveFlash.ShockwaveFlash.3");
							x=axo.GetVariable("$version");
						} catch(e) {
							if (axo) {
								x="WIN 3,0,18,0";
							} else {
								try {
									axo=new ActiveXObject("ShockwaveFlash.ShockwaveFlash");
									x="WIN 2,0,0,11";
								} catch(e) {
									x="WIN 0,0,0,0";
								}
							}
						}
					}
				}
			}
			version=x.replace(/^WIN /,'').replace(/,[0-9]+$/,'').replace(/,/g,'.');
		}
		
		if (version.match(/^[0-9]+\.[0-9]+\.[0-9]+$/)) {
			return version;
		} else {
			return '0.0.0';
		}
	})()
}

使い方

FlashPlayer.version

で値が取れます。また、Flash Playerが未インストール時は0.0.0が返ります。

あと、どうやらIE用のFlash Player10 debugバージョンだとうまく値が取れなかったりするようです。ActiveXObjectの生成でコケるっぽい...んだけども、まだ手を出せていません。これはなんとかしないとなぁ。。

参考

自分自身をロードするときにフレーム解除してロードしようとするサイトをフレームで開くときの抑制方法

タイトルだけ読んだら意味不明かと思います。要はこういうケースです。

  1. 入力フォームとiframeを用意
  2. 入力フォームがURLだったらiframeに指定されたURLのページを読み込む。要するに簡易ブラウザみたいなの。
  3. ここで入力されたURL先のページが自分自身のwindow.selfを確認してwindow.topに描画されていないと分かるや否や、window.topに再描画する。
  4. すると、iframeを表示していた元の親フレームは上書きされて子フレームに乗っ取られる。

たとえばadidasのサイトなんてそういう挙動をします。分かりやすく具体例を示すためにこんなページを用意しました。Googleボタンを押すとiframeの中身がGoogleに入れ替わり、adidasボタンを押すとadidasのサイトに入れ替わります。adidasボタンを押すと親フレームを乗っ取られることが確認できます。ちなみに、一度乗っ取られるとiframeにadidasのキャッシュが残り、「戻る」でテストページに戻っても強制的にフレーム解除でadidasページに再び飛んでしまうこともあるので、ご注意ください。

これ、どういう仕掛けになっているのか調べてみるとadidasのトップページにこんなスクリプトが入っていました。

if (window.self != window.top) window.top.location = window.self.location;

単純だけど強力なスクリプトです。要するに自身の表示フレームを確認して、window.topに強制描画させています。上記のようにフレーム内呼び出しが(なんらかの方法で)行われた場合、結果的には親フレームは壊されてしまい、完全に乗っ取られる形になります。

unloadをフック

最初はwindow.topを上書きしてアクセス不可にすればいい?と考えてみました。が、子フレームから見たwindow.topを書き換えないとそもそも意味が無いので、子フレームの内容はクロスドメインである場合、その内容にアクセスができないのでこの方法は不可能です。

と、なるとwindow.unloadイベントをフックして、想定外のunloadイベントが起きた場合に限り、window.confirmを出してその場に留まらせればOK?と思い、こんなコードを書いてみました。

$(window).unload(function(){
 window.conform("ページが消される!");
});

でも、これじゃダメ。 イベントはフックできても、confirmの内容に関わらずフレームが乗っ取られます。最後にreturn false;とかしても無駄。

beforeunloadをフック

さらに調べてて辿り着いたのがbeforeunloadイベント。Google Docsなんかで文書が保存されていないときにページから立ち去ろうとするときなんかにconfirmが実行されますが、それはこのbeforeunloadイベントをフックしてるみたい。ちなみにこれはunloadよりも先に発生するイベントのようですね。

で、このbeforeunloadイベント、少し変わった仕様になっていてハンドラに渡ってくるイベントオブジェクトのreturnValueに文字列を指定することで、ページ遷移直前にユーザーに確認ダイアログを表示することができます。あくまでもダイアログを表示するだけで、その結果を拾ったり、結果をもとに新しい処理を行うことは不可能です。(もしそんなことができると永久に閉じれないブラクラを作ることができるので、その対応なんでしょうね)

具体的にはこんなコードで確認ダイアログを表示することができます。

window.onbeforeunload = function(event){
  event = event || window.event; 
  return event.returnValue = '移動しようとしてるよ!';
}

で、これを実際に先ほどのテストページに反映させてみました。

ポイントは2点。まず、最後にreturn文として実行しないとSafari3.2ではダイアログが表示されません。つまり、単純に「event.returnValue = '移動しようとしてるよ!';」だけではダメで、return文にしておくのが重要。

あと、Operaの場合はonbeforeunloadが実装されていません。なので、Operaの場合は残念ながら代用としてunload時にalertを出して「立ち去ります!(ごめん)」と謝るしか無さそうです。(confirmで確認画面を出しても、結果で分岐させることができないのでalertを出すくらいしかできない)

この2点に注意すれば、確認ダイアログでページの移動を許可しない場合において、これまでの例のようなフレーム乗っ取りを抑制でき、ちゃんとフレームの中でadidasのサイトを表示することができます。(IE7, Fx3.0.4, Safari3.2.1, Chrome0.4.1で確認)

まとめ

今回はたまたまadidasさんのサイトを例に出しましたが、このようにフレーム解除して表示しているサイトは実は結構あります。比較的まっとうな使い方の中では(今回の例がまっとうかどうかはさておき)、このようなフレーム解除をされると大変です。そんなとき、スマートな解決策とは言えませんが、今回の方法は1つの解として頭に入れておいていいのかな、と思います。あとはOperaがbeforeunload対応してくれればもっといいのですが。。

あと、onbeforeunloadをjQueryでうまく扱う方法が正直よくわからなかったです。いろんなBlogで出てるようにbindメソッドでハンドリングさせる方法もクロスブラウザでうまくハンドリングできなかったり。「これだとクロスブラウザで大丈夫!」な書き方があれば是非教えていただきたいです。

Google ChromeのJavaScriptエンジンV8の性能評価

唐突にリリースされたGoogleブラウザことGoogle Chromeですが、HTMLのレンダリングエンジンこそWebKitながらも、JavaScriptエンジンは自社で作ったもののようです。これはV8という名前のものでオープンソースプロジェクトとしてGoogle Codeにホスティングされています。

そんなV8ですが、ベンチマークを測定できるページが用意されています。

V8 Benchmark Suite - version 1

ざっくり言うとスコアが高ければ高いほうがエンジンとしては性能がよさそう。内容としては

  • OS kernel simulation benchmark(Richards)
  • One-way constraint solver(DeltaBlue)
  • Encryption and decryption benchmark(Crypto)
  • Ray tracer benchmark(RayTrace)
  • Classic Scheme benchmarks(EarleyBoyer)

とのこと。実際のコードは見てないけどとりあえずいろんなブラウザで試してみたらこんな結果でした。おおむね最新ブラウザのみでの計測。IEだけbeta版なのはスミマセン。

Google Chrome

Score: 986

  • Richards: 1270
  • DeltaBlue: 1239
  • Crypto: 766
  • RayTrace: 673
  • EarleyBoyer: 1151

Firefox3.0

Score: 124

  • Richards: 121
  • DeltaBlue: 150
  • Crypto: 70
  • RayTrace: 144
  • EarleyBoyer: 163

Safari3.1.2

Score: 109

  • Richards: 69
  • DeltaBlue: 95
  • Crypto: 86
  • RayTrace: 133
  • EarleyBoyer: 210

Opera9.5

Score: 142

  • Richards: 86
  • DeltaBlue: 108
  • Crypto: 72
  • RayTrace: 218
  • EarleyBoyer: 391

IE8 beta2

Score: 35

  • Richards: 27
  • DeltaBlue: 29
  • Crypto: 21
  • RayTrace: 42
  • EarleyBoyer: 76

と、いうわけでスコアだけ見るとGoogle Chrome(V8)の圧勝すぎで、ちょっと逆に怪しく思っちゃうくらいです。 Fx3, Safari3, Opera9.5あたりの比較はこんなかんじ?思ったよりOpera速いな、という印象もあります。あと、IEに至っては、ベンチとってる最中に「すごく遅くなっちゃうのでスクリプトの実行を停止しますか?」的な(いつもの)ダイアログまで出る始末。IE8がんばれ、もっとがんばれ。

結論

細かな検証はamachangさんやJohnResigさんあたりが行ってくださる?とも思いますが、「V8すげー速いっぽいぞ!」というくらいの感じは持っていてもいいかもしれません。ただGmailやMapsを見ても体感的に劇的に速くなったとも思えないのも実情です。取り急ぎざっくりしたレポートはこんなかんじ。

エントリーにソーシャルブックマークのコメントを追加するJavaScript

(追記 : 09/02/12) ソースはgithubでホスティングすることにしました。

タイトルのようなものを作りました。(SBMComment.js

説明するより見てもらったほうが早いかも。faviconかえるやつのエントリーとかだとわかりやすいと思います。エントリーの下の方にはてブ、del.icio,us、Livedoor Clip、Buzzurlで書かれたコメントを拾ってきて、整形して表示しています。

経緯

masuidriveさんが、「ブログにはてブのコメントを表示するhatana_bookmark_anywhere.js」なんてものを公開されてるのを見て「あーすごくいい!これ欲しかった!むしろやりたかった!」と思っていました。さっそく設置を行おうと思った矢先、どうせだったら他のSBMのコメントも一緒に表示したいなぁ、とも思いました。このあたりのSBMはそれなりのJSON APIを用意してくれているので、ついでに他のSBMのコメントも拾ってくるやつを自作しいてみることにしました。

でも、そもそもこんなこと誰かやってないの??と思って少し調べてみるとmuumoo.jpさんがYahoo PipesでJSON APIを作ってくださっているのを発見。(Pipesのページ)この出来が超よかったので便乗してのっかってみることにしました。このAPI叩くほうがコードもスッキリして書きやすいし。あと自前サーバにAPIを作るんじゃなくてPipes使うという発想がスマートでとても気に入りました。そんなわけで、muumoo.jpさんのラッパーJSといえばそれまでなのですが、そこそこ融通は効きやすいようにしたつもりです。

SBMComment.js(とりあえず導入編)

ページ内のコメントを表示したいところで次のようなコードを追加します。

<div id="sbm-comment"></div>
<script src="http://{どこかのサーバ}/SBMComment.js" type="text/javascript"></script>
<script type="text/javascript">
SBMComment.load({ area : 'sbm-comment'});
</script>

そうすると、次のようなコードがdiv id="sbm-comment"の箇所に挿入されます。

<p id="1206265040">
<span class="sbm-comment-name"><a href="http://hatena.ne.jp/">jkondo</a></span>
from <span class="sbm-comment-type-hatena">hatena</span> at <span class="sbm-comment-date">2008-04-26 22:00</span>
</p>
<p class="sbm-comment-description">これはひどい</p>

要するにareaで指定したidの箇所に内容がロードされる、というわけです。コメントが見つからない場合は何もロードされません。

SBMComment.js(応用編)

書き出されるHTMLにはそれっぽいスタイル属性をつけているので、CSSでも整形できなくないですが、場合によってはニーズにあわない場合もあると思います。そこで、filterパラメータに整形用filter関数を指定することで、各コメント要素に対してfilter関数にかけて、書き出すHTMLを自由に操作することもできます。たとえばこのblogでは次のようなfilter関数を利用してコメントをロードしています。

( 追記:2008-06-12 2:00 ) 時間情報で、1桁の値のものはゼロで埋めて表示させるようにfilter関数をやや修正しました。前から直そうと思いつつ放置してたのをやっと対処。

var f = function(bookmark){
		if(bookmark==null) return '';
		var d = [], published = bookmark.published;
		var fillByZero = function (num){
			return num>10? '0'+num : num;
		};
		d.push('<p id="' + bookmark.id + '">');
		d.push('<span class="sbm-comment-name sbm-comment-type-' + bookmark.type + '">');
		d.push('<a href="' + bookmark.link + '">' + bookmark.author + '</a>');
		d.push('</span>');
		d.push('  <span class="sbm-comment-date">' + published.year + '-' + fillByZero(published.month) + '-' + fillByZero(published.day) + ' ' + fillByZero(published.hour) + ':' + fillByZero(published.minute) + '</span>');
		d.push('</p>');
		d.push('<p class="sbm-comment-description">' + bookmark.comment + '</p>');
		return d.join('');	
};
SBMComment.load( { area : 'sbm-comment-cont', nums : 'sbm-comment-num', filter : f } );

各bookmarkオブジェクトは、いろんなフィールドを持っているのでそれを好みのHTML要素に振り分けをしているfilter関数を用意します。bookmarkオブジェクトは次のようなフィールドを持っています。

  • id : コメントID, 一意な値
  • type : SBMの種類, 「hatena」「delicious」「livedoor」「buzzurl」のいずれかの文字列
  • author : コメントを行った人の名前
  • published : コメントを行ったときの日時オブジェクト, day,day_of_week, hour, minute, month, second. timezone, utime, yearのプロパティをもつ
  • comment : コメントの内容
  • tags : ブックマーク時のタグ
  • link : ブックマークページ

また、総ブックマーク数を表示したい場合は、表示したい場所のIDを引数オブジェクトのnumsプロパティで設定します。上の例だと「sbm-comment-num」で指定しています。

主な使い方はこんなかんじです。特にライブラリ依存はしていない素直なJSONPのコードなので、いろんなライブラリがロードされていても大丈夫なはずです。なんかバグとか不具合とかあったら教えてください。ライセンスはYahoo Pipes使ったもののライセンスがどうなっているのか、元の作者の方がどのようなライセンス形態をとっているのか、なんかが不明なため、とりあえずMITライセンスにしておきます。(これも問題あったら変更します)

MacOSXでTamarinをビルド

なんとなく思いつきでTamarinをビルドしたくなったので挑戦してみると思いのほかすんなりできたのでそのメモです。ちなみにMacOSX(Leopard)でビルドしました。基本的にはMDCのドキュメントの通りなのですが、途中で少し迷った点もあったので、そのあたりも含めて備忘録として残しておきます。

そもそもの環境

MacPortsをインストールしておく必要があります。XCode3.0が入っていれば、pkgからすんなりインストールできるかと思います。

MacPortsが入ったらlibIDLをインストールしておきます。

sudo port sync
sudo port install libidl

また、autoconf213をインストールします。

sudo port install autoconf213

ソースの入手

ソースを入手するためにはhgというコマンドが必要なのですが、これはMercurialがインストールされてある必要があります。ちなみにMercurialとはクロスプラットフォームの分散型バージョン管理システムだそうです。ここで初めて知りました。ほほぅ。

Mercurialはバイナリが用意されてあるので、そこからインストールするのが簡単です。ここからOSX用のMercurial 1.0をDL、解凍して、インストーラを実行します。インストールが終わると、ターミナルで「hg」というコマンドが実行できるようになり、ソースコードを入手することができます。

ソースは次のコマンドで入手することができます。

hg clone http://hg.mozilla.org/tamarin-central

そうすると、ソースコードがカレントディレクトリにDLされます。

ビルド

次のコマンドでビルドできます。

cd tamarin-central
xcodebuild -project platform/mac/shell/shell.xcodeproj

しばらく待つと、tamarin-central/platform/mac/shell/build/Releaseに「shell」の名前でビルドできています。

./shell
avmplus shell 1.0 build cyclone

usage: avmplus
          [-Dinterp]    do not generate machine code, interpret instead
          [-Dforcemir]  use MIR always, never interp
          [-Dnodce]     disable DCE optimization 
          [-Dnocse]     disable CSE optimization 
          [-Dnosse]     use FPU stack instead of SSE2 instructions
          [-Dtimeout]   enforce maximum 15 seconds execution
          [-error]      crash opens debug dialog, instead of dumping
          [-log]
          [-- args]     args passed to AS3 program
          [-jargs ... ;] args passed to Java runtime
          filename.abc ...
          [--] application args

static linkされてるっぽいので、適当な名前にコピーしてどこかに移動してあげると良いでしょう。僕は/opt/local/sbin/avmplus とかに置きました。

で、とりあえずビルド確認したとことまで。こいつでECMAScriptやGCについてちょこちょこ勉強できそうです。

サーバからの通知方法にfaviconの動的変更を利用する

GmailやLDRなどもそうですが、最近はWebアプリケーションでも「起動させっぱなし」を基本路線に置いているものも増えてきています。そういうときにポイントとなるのが「(サーバ側で変更が発生したときに)サーバからの通知をブラウザにどうやって知らせるか?」ということ。ブラウザでページを開かれている場合だと、変更箇所を専用のボックスエリアを設けて、適当に目立たせておけばいいのですが、別タブで開かれている場合などには、タブをユーザが切り替えるまでは、その変更を通知することができません。そんなときに、「差分の大きな複数のfavicon(*)を動的に変更させることで通知と同等の効果が期待できないか?」という話。(全然違うfaviconをアニメーションさせることで目立たせられないか?という狙い)

faviconの変更は割と単純

まずfaviconが動的に変更させることができないか?の検証ページを作ってみました。

「change favicon」ボタンをクリックすることでfaviconのアイコンがサングラスをかけたりメガネをかけたりシャキシャキ入れ替わります。動作確認ブラウザはFirefox2.0.x,3.0b, Opera9.26のみ。Safari3.1やIE7は変化なし。

コードの元ネタは「Dynamic Favicon Library Updated」のページから。作者さんは専用のライブラリを作られているようだけどもjQueryだけでも何とかなるだろうと思ってコードをおっかけてたら何とかなった。仕掛けは単純でlink要素のhref属性で読み込んでいるfaviconのパスを動的に変更させているだけ。でも単純にhrefの値を変更させているだけだとダメで、一度link要素を削除して、その後にfaviconのパスを変更したlink要素を再挿入。これでちゃんと変更が認識されます。そのへんの話がこのあたり。

$('#favicon').remove();
$('meta:last').after($(document.createElement('link')).attr('id', 'favicon').attr('rel', 'shortcut icon').attr('href', icon));

要素の特定が面倒なのでfaviconを読んでいるlink要素には「favicon」IDをあらかじめ振っておいて、meta要素の最後に再挿入。どうでもいいけどjQueryで要素を動的に作成するための関数ってあるのかな?普段って要素特定ばかり使っているから、要素作成についての方法が地味に見つからなかった。

window.blur, window.focusを拾う

通知は他タブに切り替わっている場合のように、フォーカスがあたっていない場合は常に通知をし続けておきたい訳ですから、window.blur, window.focusイベントでうまく処理します。デモは さっきのページで「alert by favicon」ボタンクリックで開始。ウィンドウにfocusがあたっている場合はfaviconが5回変更されて停止、focusがあたっていない場合はfaviconが変更され続けて、focusが当たった瞬間に停止します。説明難しいから実際に試していただいた方が早いか、と。

コードとしては、先に上げた例のものをベースにsetIntervalで回しているだけで、windowイベントをトリガにフラグを切り替えclearIntervalしている単純なものです。読んでみると分かるかな、というものです。そのへんの話がこのあたり。

function alertFavicon(){
	var isFocused=true, favID=0;
	$(window).focus(function(){isFocused = true});
	$(window).blur(function(){isFocused = false});	
	var timer = setInterval(function(){
		if(favID>10 && isFocused){
			clearInterval(timer);
			favID=0;
		}
		var icon = (favID%2==0)? 'http://lab.katsuma.tv/favicon/favicon2.ico': 'http://lab.katsuma.tv/favicon/favicon.ico';
		$('#favicon').remove();
		$('meta:last').after($(document.createElement('link')).attr('id', 'favicon').attr('rel', 'shortcut icon').attr('href', icon));
		favID++;
	}, 500);
}

サーバサイドからの通知方法としては割と有効

ここからは個人的感想にもなりますが、サーバサイドからの通知方法の1つとして、faviconを変更しつづけたアラート方法は割と有効なんじゃないかな、と思います。FxとOperaしか使えないものの、なかなか視覚的に訴えるものは大きいので、ユーザに新着情報をリーチさせる手段として検討してみてもいいんじゃないかな、と思いますよ。もちろん過度に使うとチカチカ動きすぎてうざったい印象をユーザに持たれてしまうので、そのあたりのバランス感覚は言わずもがな重要になってきます。

jQueryでTwitterぽいエフェクトをかける

JavaScriptの小ネタはあまりウケないことを理解しつつのエントリー&&個人的な備忘録です。

TwitterのSettingsで設定変更したときのホワっとメッセージが出て、しばらく表示した後に、スっと消えるあの感じ。あれをjQueryで書く。こんな感じにするとそれっぽくなった。

var t = setTimeout(
 function(){
  $('#fadeout-text').fadeOut(3000);
  clearTimeout(t);
 }, 2000);

fadeOutだけでなんとかなるかなと思ったけど、時間を調整してもそれっぽくならなかった。最初に数秒間、普通に表示させてからスっと消さなきゃそれっぽくならないのでsetTimeoutでfadeOutの実行をズラしてみた。あと、fadeOutじゃなくてhide使うと領域(#fadeout-text)がだんだん小さくなりながら消えていったので、ちょっとイメージとズレてた。setTimeoutのズラした時間はもう少し長くてもいいかもしれない。これ、フォームのPOST実行後の結果表示みたいなときに、さりげなく使うとちょっといい感じ。

mixiの絵文字を消去するbookmarklet

会社で「mixiの日記で文字が多すぎると読みにくくて仕方ない」みたいな話がでて、「それFirebugのコンソールでなんとかなりそうだな」と思ってTwitterにこんなのをつぶやいてみた。

mixiで絵文字OFFるとき⇒Firebugで var e = $x('//img[@class="emoji"]');for(var i=0,l=e.length; i<l i++){ e[i].style.display='none';}

via Twitter

そうしたらkuさんから「map map」なレスが。

@ryo_katsuma mapmap $x('//img[@class="emoji"]').map( function(e){ e.style.display='none' } )

via Twitter

普段mapあまり使わなかったから、こっちの方が1行で書けて確かにいいなーと思いつつ、せっかくなんでBookmarkletにしてみました。xPathどうしようかと思ったけどmixiはprototype.jsロードしてるから$$関数とか使ってあげればCSS記法でノードを抽出できるわけですね。

mixi emoji delete

上記リンクをブックマーク。スッキリ消えて見やすくなっていい感じです。

それにしても最近jQueryばっか使ってたらPrototype.jsの使い方とかすっかり忘れてた。。

プレゼンテーションライブラリS6の使い方(入門編)

身内で勉強会のようなものを行う機会があったので、amachangのJavaScriptによるプレゼンテーションライブラリのS6を利用してみました。 すぐに直感的に利用できる部分もある一方で、スタイルの反映のさせ方でハマった点もあったんで、そのあたりをまとめておこうと思います。

変数の存在を確認する方法と速度比較(その2)

前回に「関数の引数を存在確認するための高速化Tips」と題したエントリーを書きましたが、その後にgakkieからいろいろ意見をいただきました。

@ryo_katsuma 露骨ってほどの差には見えないがw 条件文がtrueでも同じ結果になる?<三項演算子

Twitter / gakkie

@ryo_katsuma ついでに、10万回で50msの違いっていうのはJSにおいて重要なのかがちょっと気になったお。

Twitter / gakkie

あと、その後にチャットで話した結果、自分自身で納得したのですが、前回のエントリーで言いたかったことは「関数の引数うんぬんとかはどうでもよくて、"変数が存在するかどうか" を考えたとき、その確認方法によっては速度が変わることがある」と、いうことが一番言いたかったんだな、、ということに自己完結。また、確認方法についても「関数呼び出しを行う時間を含める/含めない、によっても純粋な判定時間とはずれる?」と、いう話にもなったり。

と、いうことで「関数呼び出しを含まない」「真偽の2パターン」を考慮してもう一度測定し直してみました。検証コードは次のとおり。

SpiderMonkeyのインストール

完全に自分専用の備忘録です。

ちょこっとしたコードやベンチマークとるとき、今までFirebug使ってたんですけども、ファイルにまとまったくらいの規模、100〜200行規模のものだとFirebugだと少し扱い辛いです。で、そんなときにSpiderMonkey。最近よく使ってます。

インストール方法

ソースコードを入手>解凍>makeくらいで簡単。以下はOSXでの方法。Linuxでもほぼ同じです。

wget ftp://ftp.mozilla.org/pub/js/js-1.7.0.tar.gz
tar xvzf js-1.7.0.tar.gz
cd js/src
make -f Makefile.ref
cp Darwin_DBG.OBJ/js /usr/bin/

使い方

ターミナル上で「js」とタイプすると、SpiderMonkeyがインタプリタとして起動するのですが、個人的にはファイルを食わせて実行する場合の方が多いです。適当な実行コードを含むファイルを作っておいて

 

js -f hoge.js

 

こんな感じで実行。document.write()は無いので、代わりにprint()で情報を出力することになるのは注意。

MinibufferがGoogleDocsで干渉する場合がある?

GreasemonkeyのMinibufferがGoogleDocs(docs.google.com)で悪さをすることがあるみたいです。こんな現象は他で聞かないし、僕の環境だけなのかもしれないけど、会社と自宅のiMacで両方再現しました。Windowsでも再現したマシンがあった気がしたけど、そこはちょっとあやふや。とりあえず報告。

現象

上の画像のように、突如モニタのちょうど真ん中に黒い短形が表示されます。スクロールしても座標は不変なのでposition:absoluteで表示されてる感じ。作業するのに相当支障が。。。

しかも、一度この短形が表示されると、GoogleDocsに再ログインしてもずっと変わらないので、状態を引きずってるような感じになります。

対応方法

最初はGoogleDocs単独の不具合かと思っていたのですが、どうもこの「やや黒い」色に見覚えがある気がして、「MiniBufferの背景色?」と当たりをつけてみました。とりあえず個人的にGoogleDocsでMiniBufferは使う場面が無いので、docs.google.comでMiniBufferを無効に。

結果

見事にビンゴ!短形が消滅!!

予想

GoogleDocsの何かのショートカットが影響したのかな、、、というおぼろげな予想。再現方法も今のところ全く分からないので無責任なことも言えないのですが。とりあえず同じような現象を見た人は情報を共有したいものです。

関数の引数を存在確認するための高速化Tips

(2007/12/12 追記)内容をまとめ直し+再考しました。

JavaScriptで関数の引数チェック行う場合に、「どんなチェック方法が速いのか?」な話。

たとえば、引数が存在する場合は「その引数」を、存在しない場合は「bar」を返す、という場面を想定します。単純に考えるとこんな感じ。

function check(arg){
if(arg) {
return arg;
} else {
return 'bar';
}
}

うーん、nullチェックをする、って方法も考えられます。

function check(arg){
if(arg != null){
return arg;
} else {
return 'bar';
}
}

typeof で確認って方法もありますね。

function check(arg){
if(typeof arg != 'undefined'){
return arg;
} else {
return 'bar';
}
}

条件を限定して、引数が「foo」の場合はfooを返す、という場合はこう書けます。

function check(arg){
if(arg=='foo'){
return arg;
} else {
return 'bar';
}
}

と、引数確認だけでもパっと思いついただけでも、これだけ思いつきます。(もっと全然違うパターンがあるよ!な場合はTBかコメントで教えてください!)

あと、もっと突っ込むとif文ではなく、三項演算子を使うとワンライナーでも書けてしまいます。たとえば一番最初の場合だと

function check(arg){
return arg || 'bar';
}

こんな感じに。(2007/12/05 15:00追記:この場合はワンライナーで書いてるけども、三項演算子を利用しているわけではないですね)

どう書けば速いの?

単純に疑問に思ったので、ざっとテストコードを書いてベンチマーク取ってみました。検証コードはこちら。

JavaScriptの定数宣言は高速化につながるか?

JavaScriptでFirefoxがconst(定数宣言)をサポートしていることをフと思い出しました。で、定数宣言って速度向上とかにつながるのかな?思ってついカッとなってベンチマークとるコードをかいてみました。こんな感じ。

<html>
<head><title>const test</title></head>
<body>
<script>
	window.load = monitor();
	
	function monitor(){
		var A = "TestA";
		const B = "TestB";

		var Klass = {
			C : "TestC"
		};

		// 1st test
		var start = new Date();
		for(var i=0; i<10000000; i++){
			var b = A;
		}
		var end = new Date();
		console.log("A:" + (end-start));

		// 2nd test
		var start = new Date();
		for(var i=0; i<10000000; i++){
			var b = B;
		}
		var end = new Date();
		console.log("B:" + (end-start));

		// 3rd test
		var start = new Date();
		for(var i=0; i<10000000; i++){
			var b = Klass.C;
		}
		var end = new Date();
		console.log("C:" + (end-start));	
	}
</script>
</body></html>

1000万回ループで回してグローバル変数、定数宣言した変数、static変数にアクセスして、そのアクセス時間を比較。結果はこんな感じでした。

クロスブラウザ対応したAutopagerizeもどきの実装

Greasemonkeyで「次へ」のリンクがあるようなサイトを無限スクロール可能にさせるAutopagerizeというGMScriptがあります。「次へ」のリンク箇所、次のページ内容、内容を挿入する箇所などをXPathで記述(SITEINFO)し、これらのSITEINFO情報はWikiで管理されてあるので、Autopagerizeが実現できるページはユーザ側でどんどん増やしていくことが可能。なので、気づけば「あ、このサイトも勝手にスクロールされる!!」な発見も多く、非常に素晴らしいGMScriptです。Mozilla24のShibuya.JSで知ったのですが、これ無しじゃもうやってられないくらい愛用しています。

で、ウチでリリースしているChannel.isのサイト、最近ややコンセプトが変わってスタートページは各ビデオ共有サイトの横断検索が可能なサービスになっているのですが、ここの検索結果もAutopagerizeのSITEINFOに登録していました。すると社内で結構評判良かったので、せっかくなんでGMだけにとどめておくのではなく、モダンブラウザ全部対応したスクリプトをスクラッチから書いてみました。

(例)[ 検索 ] video

注意事項として、本家のAutopagerizeのWikiには、channel.isのSITEINFOを削除いただくよう、要請を出しているのですが、コンフリクトを起こす可能性もあるので、Autopagerize導入済みの方は「ユーザスクリプトを実行しないサイト」で「http://channel.is/*」を指定いだければと思います。

以下、実装上のメモです。

FirefoxでVLCを利用する

ブラウザ上でメディアファイルの再生、となると最近ではYouTubeやニコニコ動画に代表されるように、FlashPlayer上でFLVファイルを再生、というのがほぼ等価とされています。実際、FlashPlayerの普及率は95%以上ともされていますし、この流れは当然といえば当然だと言えます。その次にはWindows Media Playerが続くことになるでしょうか。

VLCがアツい!

ただ、忘れちゃならない、というか知らない人も多いかもしれませんが、オープンソース(GPL)のメディアプレーヤーでVLC media player(以下、VLC)というものがあります。これがなかなかアツいのです。

対応プラットフォームが多すぎる

VLCは、スタンドアロンのプレイヤーなのですが、対応プラットフォームがかなり多いです。Windows,Mac, Linuxはもちろん、FreeBSD, Solaris, Zaurusなんかの携帯端末まで、よくもまぁここまで対応させたな、というくらい対応プラットフォームが多くあります。

対応メディアも多すぎる

対応メディアのフォーマットもかなり幅広くサポートしています。ファイル形式だけでも、AVI・DivX・ASF・MP4・MOV・MPEG2・OGG・OGM・Matroska・WAV・FLV・・・などなど。もちろん全てのプラットフォームで全てのフォーマットに対応してはいないのですが、Windows、MacOS、LinuxなんかではQuickTimeやWindows Mediaにも対応しているので、VLCだけでも有名どころの多くのファイル再生が可能なので、万能プレイヤ-として検討の価値があると思います。実際、僕も自宅のPCではVLCをメインのプレイヤーとして利用しています。

Webへの埋め込みもできる!

本題はここから。このVLCですが、フルパッケージでインストールするとFirefox用のプラグインがインストールされます。このプラグインを利用するとJavaScriptだけであたかもFlashで使ったかのような、かなり柔軟なプレイヤーが作れてしまいます。

で、実際にいろいろ触ってみたのですが、なかなか使い勝手はよさそうです。ただ、Firefoxに組み込むにあたって、少しクセもあるのでその辺りをまとめたいと思います。

出張Shibuya.JSにいってきました

Mozill24のイベントの中の「出張 Shibuya.js 24」にいってきました。

Mozilla24はウチの会社がOceanGridで(ほぼ)24時間ライブ中継はしていたのですが、このセッションだけはぜひ生で聞きたかったので、九段下まで出かけて聞いてきました。以下、ざっと感想+メモです。

del.icio.usのh-index値を出力

懲りずにdel.icio.us Reportの改良しています。今回の機能追加は次の2つです。

IE, OperaなどFirefox2以外に対応

Thread.sleepまがいのことをするためにyield使ってましたが、Firefox2しか対応していなかったので、PeriodicalExecuter使うことで他のブラウザにも対応しました。手元の環境だとIE6, Opera9で動作しています。と同時にますます汚いコードになった。。。JSONP使ったときに名前空間を綺麗に処理できないなー。グローバル領域を汚しまくりの突貫コード。

h-index値に対応

はてブで最近h-index値が注目されているようなので、del.icio.usにも対応してみました。作るの簡単なので。

PeriodicalExecuterを停止させる

Prototype.jsで定期実行させるための便利オブジェクトPeriodicalExecuterですが、少し前のバージョンまで停止させる方法がありませんでした。clearIntervalさせるためのsetIntervalの返り値が保存されていなかったのが原因です。実際、少し調べても「停止のためのインターフェースは無いよ」な声ばかり。でも、これウソです。最新のバージョンだと停止させることは可能です。

del.icio.us Report(α版)を作ってみました (3)

またまたdel.icio.us Reportのつづきです。

各エントリーのブックマーク数を表示してからReportとの結果を比較して、あまりに値が異なっていたので「えぇ??」と思ってコードを見直してみると、致命的なバグあったのを発見。de.icio.usからのレスポンス処理がヒドい箇所がありました。なんとレスポンスの配列を先頭要素しか処理してませんでした。いやーほんとひどい。。。

del.icio.us Report(α版)を作ってみました (2)

昨日の続き。

del.icio.us Report

次の機能を追加しました。

del.icio.us Report(α版)を作ってみました

delicious_report.gif

Hatena ReportはUIも使い勝手もいいので、定期的にヒマつぶしに見てしまうのですが、del.icio.us Reportも欲しい!!と思ったので作ってみました。(ただし超α版)。使い方は上の図のとおり。URL入れて1クリックでレポートを作成します。有益な情報が多いサイボウズ・ラボさんを例として利用させていただきました。で、以下、メモです。

del.icio.usからアク禁されました

Error 999.

del.icio.usのブックマーク数取得の(個人的)便利ツールを作ってたら、リクエスト多すぎ!とアク禁食らいました。

Twitterともごもごの同時更新

twitter_mogomogo.gif

もごもごがAPI公開してくれた、ということで、Twitterともごもごを両方同時に更新できるツールを作ってみました。

Twitter Incremental Search

JSONPを利用したTwitter Incremental Searchを作ってみました。

前回(JSONPを利用したTwitter Search )のやや応用。検索をインクリメンタルサーチ対応させました。Win+IE, Firefoxで確認済。今回もTwitter.FMにお世話になっています。いつの間にやら日本語通るようになっていてイィ感じに!

事の発端は嶋くんから「prototype.js使ったインクリメンタルサーチを教えて!」と言われて。「すぐ対応するよ!」と言いつつ、やや放置していたのでサックリ作ってみました。以下、簡単な解説。

JSONPを利用したTwitter Search

JSONPを利用したTwitter Searchを作ってみました。モノを見たほうが早いかも。

(追記:2007/05/03 08:00) Twitter.fmが落ちていることがたびたびあるようです。そのときは検索できません。。

(追記:2007/05/16 03:30) script要素が無限に増殖するのを防ぐために、要素数を1に抑える処理を加えました。

Twitter Search

- powered by Twitter.FM



検索ボックスで適当な単語を入れると非同期通信で検索結果が表示されます。最後にも書いていますが、今のところ日本語は通りません。英単語のみです。(2007.05.16 日本語が通るのも確認しました)あと複数クエリもちょっと怪しいです。とりあえず「hungry」「Twitter」とかで検索かけてみると雰囲気が分かります。

元ネタはつい最近できたTwitter検索サイトのTwitter.FM。ここが検索APIであるTwitter.FM APIを提供してくれているので、これを利用させていただきました。(利用規約とか無かったので勝手に使っちゃったけどマズいかな。。)

APIが提供されてある、、といってもXML/RSSフォーマットしかないので、これをそのまま使うことはできません。JSONP対応してくれていると非常に助かる、、あとJSONフォーマットも対応してくれれば、、と、あれこれ思ったので、思うより動けで次のようなアプローチを。

JSONPのTwitterAPIを試してみました

TwitterAPI

TwitterAPIでJSONがサポートされている、ということで自分の最新ひとことを表示するスクリプトを書いてみました。トップのサイドバーに表示させています。(女の子画像は本家からこっそり拝借)

(2007/04/24 追記)
この後、Flashを使ったガジェットになりました。詳しくはこちら。

コードは次のとおりです。表示のところだけ要prototype.jsですが、結構単純なコードです。実行はloadTwitter()をコール。

イベント発生時に無名関数を実行

HTMLの任意の要素に対して、JavaScriptではonClick, onLoadなどのイベントハンドラに処理を直接結びつけることができます。たとえばこんなかんじ。

<a href="#" onClick="window.alert('Hello, World!');">こんにちわ!</a>

こうすると「こんにちわ!」をクリックすると「Hello, World!」のウィンドウが表示されます。至極簡単。 ただ、HTMLの中にJavaScriptを埋め込む記法はどうなんだ?という意見も当然あるかと思いますが、ここではそれについては割愛。実際、僕も最近はほとんどPrototype.jsのEvent.observe()を使って、イベントに対する処理はまとめて定義しちゃうことが多いです。

さて、話は戻って上記の話をさらにすすめることに。今度は複数の処理を逐次処理させることにします。一番簡単なのは、逐次処理の内容を1つの関数にまとめておいて、その関数をイベント発生時に呼び出す方法。たとえばこんな感じ。

function hello(){
 document.title="こんにちわ!";
 window.alert("こんにちわ!!");
}

こんなのを定義しておいて

<a href="#" onClick="hello();">徹底的に挨拶</a>

こうやって呼び出すとOK。ただし、グローバルな領域にhelloが汚染するのは少し気持ち悪いです。特にイベント発生時に呼び出される処理が単純なものであればあるほど、グローバルな領域は可能な限りクリーンにしておきたい限り。

と、いうわけでこういうときには無名関数を使うのがオススメ。そうするとかなりスッキリと書くことができます。実際のコードはこんな感じ。

Shibuya.js Technical Talk #3のベストスピーカーをFlickrのViewsで測定する

ただのメモの内容の割には、意外にもはてブされてた「Shibuya.js Technical Talk #3 - Shibuya.es」。Flickrに上げさせていただいた写真も結構Viewsの数が増えていて、なかなか興味深い感じです。はてなで「Shibuya.js」なんかで検索する人がいるのは想像つく、、ものの、Flickrでも探し出す人がいるものなんですね。

さて、そのFlickrですが、スピーカーの方それぞれでかなりViewsの数に違いがあることがわかりました。トーク内容とは別に「前からどんな人なのか見たかったんだよ!」という欲望の表れ??

と、いうわけで今日(2007/3/28)時点で、FlickrのViews数だけで独断に決定させていただく、「Shibuya.js Technical Talk #3 ベストスピーカー」こんな結果になりました。

JSONPを定期実行する

XHRはドメインを越えて通信ができないので、ドメインを越えてAJAXを行うときはXHR以外の方法を取る必要があります。JSONP, iframeなんかの解決法があるかと思うのですが、今回はJSONPの話題をば。

JSONPとはJSONデータをcallbackさせたい関数名のパラメータとして受信し、scriptタグを動的生成することでクロスドメインでAJAX(ここまでくると当然XMLとかもはやどうでもいい)を実現する、、、ということなんですけど、文章で書くとワケわかりません。ので、実行コードを書くとこんな感じ。

http://example.com/data.json?callback=view

と、callbackさせたい関数名をパラメータに付けて(*1)リクエストを投げると

view(
 { "state" : "good"}
);

と、「callback_function( JSON data); 」な、形式で返してくれるものがJSONP。こいつのリクエストをXHRでかけるとドメイン越えでデータを受信できないので、scriptタグでJavaScriptをロードさせる要領でリクエストをかけることで、ドメイン越えしてJSONデータを受信する、、というもの。うーん、これ考えた人すごい。(*2)

で、このJSONPを定期実行させたい、ということもあるかと思います。要するに定期的にx秒毎に別ドメインにアクセスして最新の情報をゲットしたい、なんてとき。そこで微妙にハマったことがあったのでメモ。

Shibuya.js Technical Talk #3 - Shibuya.es

Shibuya.js Technical Talk #3 - Shibuya.es

デジタルハリウッド大学秋葉原セカンドキャンパスで開かれた「Shibuya.js Technical Talk #3 - Shibuya.es」に行ってきました。行ってきました、、と、いうか今回はデジハリさんi-revoさんの協力の下、Loocでの配信のため。

当日になってネットワークの調整が非常に難しいことが発覚して「うーん。配信無理っぽいw」と、かなり焦ったのですが、i-revoさんのお力でなんとかLooc側でも配信ができました。(本当にありがとうございます!)

で、以下、配信しつつのメモです。
GoogleDocsでがーーっとメモってただけなので間違いとかあったらゴメンナサイ!

Form.Element.Observerをクリアする

Prototype.jsを使うと、フォームの内容を監視してコールバックを実行させることが簡単に実装できます。 たとえばこんな感じ。

new Form.Element.Observer('input_hoge',1, function(){ window.title=$F('input_hoge');} );

これはid=input_hogeのテキストフィールドの値を1秒間隔で取得して、タイトルに反映させる、というもの。Form.Element.Observerの最後の引数は関数を渡すのですが、無名関数でももちろんOK。ちょこっとしたことならこっちの方が楽かも。

さて、ここで問題は実はこのObserverをクリアする方法が無い、ということ。Prototype.jsは他にもTimerをクリアできない問題があったような。。。(記憶あやふや)なんとなく気持ち悪いのでいろいろ調べていたら、綺麗に解決されてる事例があったのでメモ。

Prototype.js 1.5.1 rc1リリース

CodeZineより。


Javaで手軽にJSON - org.json.simple

Javaでサーバプログラムなんかを書いてクライアントにJSONでレスポンスを返す、なんてケースも最近は増えてきつつあります。小さなJSONなら自前で書いても問題はないのですが、やはりライブラリに頼ったほうがバグも少なくて開発も効率的です。 そんなときorg.json.simpleは、その名の通りシンプルながらもなかなか使えるいい感じです。 使い方は上のリンクにもあるテキストファイルの通りなのですが、簡単にメモっておきます。

ExternalInterfaceを使ったFlashの呼び出し元制御

ActionScriptでExternalInterfaceを利用するとActionScriptからJavaScriptを呼び出せることができるのですが、普通に使うとJavaScript側で関数定義を行っておく必要があります。


たとえばActionScript側で

var ua = ExternalInterface.call("getUserAgent");

のように呼び出し、JavaScript側で

function getUserAgent(){ return navigator.userAgent; }

のような定義を行っておく必要があります。


あるに決まってます。。と今日の今日まで思ってたのですが、よく考えたらcallの引数って無名関数でもOKな、はずなんですよね。実はこれって結構便利。

JavaScriptにおける"this"

仕事で最近マジメにJavaScriptでOOPな開発をしています。
言わずもがなJavaScriptはオブジェクト指向な言語ではありますが、JavaやC#のようなクラスベースなオブジェクト指向ではなく、プロトタイプベースなオブジェクト指向言語です。

このあたりの話は自分の手でコードを書いたことはなくとも、理屈では分かっていた「つもり」だったので「ほうほう」と適当に流していたのですが、いざ自分で書いてみると相当ハマるハメになりました。 ちょっとまだ理解は完璧ではないのですが、備忘録としてここに記しておきたいと思います。


こんなことをしたい、と仮定します。

・./foo.phpにGETでリクエストを投げる
・レスポンスを受け終わるとshowComplete関数をコールバック
・「Complete!!」と表示する


まーよくありがちな流れです。
僕も何度もこんなコードは書いたことはありましたが、ちょっとオブジェクト指向を意識して書いた途端、まったく動作しなくなってしまいました・・・。


さて、次のコードがその動かないコードなのですが、どこが動かないかすぐに分かりますか?
ちなみに要prototype.jsです。


var Foo = Class.create(); Foo.prototype = { url : "./foo.php", initialize : function(){ try{ var ajax = new Ajax.Request( url , { method: 'get', parameters: '', onComplete : showComplete } ); } catch(e){ } }, showComplete : function(){ document.title='Complete!!'; } }; // 実行 Event.observe(window, 'load', function(){ var foo1 = new Foo(); }, false);

ダメな点は2つあります。

JavaScriptによるテーブルの列でソート

Excelなど表で出力されるデータは列の先頭をクリックしてソートしたいことが多々あります。WindowsならExplorerでも標準でありますよね、例のアレ。

Web1.0的考えならクリック毎にサーバで処理してソートされた表のHTMLを生成してブラウザに返して・・・な流れになりますが、Web2.0なこのご時世はJavaScriptだけで解決できます。デモはこちら

利用しているライブラリはprototype.jsorder_by_sort.js
後者は公式サイトがダウンしちゃってるみたいなんで気になる人はデモページからご利用ください

Index of all entries

Home > develop > Javascript Archive

Search
Feeds

Return to page top