script.aculo.usを読み解く

第1回scriptaculous.js

はじめに

script.aculo.usは、Web 2.0なサイトを簡単に作れるJavaScriptライブラリです。

Thomas Fuchs氏を中心に、2005年にMITライセンスで公開されてから、現在にいたるまで、活発な開発が続けられています。

AppleGucciといった、テクノロジとデザインに敏感な企業のWebサイトに導入された実績があります。

script.aculo.usというと、Web2.0なデザインを簡単に実現できるライブラリとして、派手なエフェクトにばかり注目が集まりますが、Ajaxを使った入力補完やドラッグアンドドロップなど、ユーザビリティの向上に大きな威力を発揮する点も見逃せません。

prototype.jsに大きく依存しているのも特徴のひとつです。

この連載では、script.aculo.usを読み解き、これらの機能が、いったいどんなコードで実現されているのかを解説していきたいと思います。

script.aculo.usのコードを見ると、ライブラリの機能を拡張するための、たくさんのフックが用意されていることがわかります。これらのフックを活用して機能を拡張していくには、周辺のコードの意味をある程度理解する必要にせまられるのですが、残念ながらそのためのドキュメントは不足しているのが現状です。この連載で、少しでもその不足を補えればと思います。

想定している読者は、script.aculo.usをただ使うだけでなく、自分の手で拡張していくためにコードの意味を理解したいという方、使われているJavaScriptのテクニックに興味がある方、自分で同様のライブラリを作ってみたい方などです。

なお、本稿では2008年1月3日にでた、連載開始時点の最新のバージョン1.8.1に基づいて説明してきます。

8つのライブラリ

script.aculo.usは、8つのライブラリから構成されています。各ライブラリは、次の機能を提供します。

controls.js
Ajaxを活用した、斬新なGUI部品のライブラリです。入力補完つきのテキスト入力ボックスや、Webページを"その場で編集できる"機能を、提供します。
effects.js
script.aculo.usの代名詞ともいえる、フェードアウトやズームインといった、派手なエフェクトの数々を提供するライブラリです。
dragdrop.js
Webページ上でドラッグ&ドロップを実現するためのライブラリです。
slider.js
スライダーというGUI部品のライブラリです。
builder.js
関数型言語のようににDOMを構築できるライブラリです。
sound.js
音声を再生するためのライブラリです。
unittest.js
単体テストを自動化するライブラリです。
scriptaculous.js
上記のライブラリをロードするためのライブラリです。

ソースコードの入手

script.aculo.usのソースコードは、公式サイトからダウンロードできます。ぜひ、お手元にダウンロードして記事と一緒にご覧になってください。

パッケージのtestディレクトリ以下には、ライブラリの単体テスト、機能テストを行うためのファイルがいくつも入っています。これらのテストを動作例として手元で実際に動かしてみると、入力の与えかたや期待されている出力がよくわかり、細かいオプションやフックの意味を知る手がかりになるでしょう。

scriptaculous.js

第1回は、scriptaculous.jsを解説します。

0001: // script.aculo.us scriptaculous.js v1.8.1, Thu Jan 03 22:07:12 -0500 2008
0002: 
0003: // Copyright (c) 2005-2007 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
0004: //
0005: // Permission is hereby granted, free of charge, to any person obtaining
0006: // a copy of this software and associated documentation files (the
0007: // "Software"), to deal in the Software without restriction, including
0008: // without limitation the rights to use, copy, modify, merge, publish,
0009: // distribute, sublicense, and/or sell copies of the Software, and to
0010: // permit persons to whom the Software is furnished to do so, subject to
0011: // the following conditions:
0012: //
0013: // The above copyright notice and this permission notice shall be
0014: // included in all copies or substantial portions of the Software.
0015: //
0016: // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
0017: // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
0018: // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
0019: // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
0020: // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
0021: // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
0022: // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
0023: //
0024: // For details, see the script.aculo.us web site: http://script.aculo.us/
0025: 

Thomas Fuchsによる著作権表示です。MITライセンスなので、オープンソースであるかないかに関わらず再利用が可能です。詳細は以下のリンクをご覧ください。

0026: var Scriptaculous = {
0027:   Version: '1.8.1',
0028:   require: function(libraryName) {
0029:     // inserting via DOM fails in Safari 2.0, so brute force approach
0030:     document.write('<script type="text/javascript" src="'+libraryName+'"><\/script>');
0031:   },
0032:   REQUIRED_PROTOTYPE: '1.6.0',

28行目のScriptaculous.requireは、動的にライブラリのロードを行う関数です。

Safari 2.0ではDOM経由で<script ...>を挿入しても動的ロードできないので、document.writeを使っています。

32行目で、依存するprototype.jsのバージョン(1.6.0)を指定しています。


0033:   load: function() {
0034:     function convertVersionString(versionString){
0035:       var r = versionString.split('.');
0036:       return parseInt(r[0])*100000 + parseInt(r[1])*1000 + parseInt(r[2]);
0037:     }
0038: 
0039:     if((typeof Prototype=='undefined') ||
0040:        (typeof Element == 'undefined') ||
0041:        (typeof Element.Methods=='undefined') ||
0042:        (convertVersionString(Prototype.Version) <
0043:         convertVersionString(Scriptaculous.REQUIRED_PROTOTYPE)))
0044:          throw("script.aculo.us requires the Prototype JavaScript framework >= " +
0045:         Scriptaculous.REQUIRED_PROTOTYPE);
0046:   
0047:     $A(document.getElementsByTagName("script")).findAll( function(s) {
0048:       return (s.src && s.src.match(/scriptaculous\.js(\?.*)?$/))
0049:     }).each( function(s) {
0050:       var path = s.src.replace(/scriptaculous\.js(\?.*)?$/,'');
0051:       var includes = s.src.match(/\?.*load=([a-z,]*)/);
0052:       (includes ? includes[1] : 'builder,effects,dragdrop,controls,slider,sound').split(',').each(
0053:        function(include) { Scriptaculous.require(path+include+'.js') });
0054:     });
0055:   }
0056: }
0057: 
0058: Scriptaculous.load();

33行目のScriptaculous.loadは、ライブラリをロードする関数です。

34~38行目のconvertVersionStringは、'1.6.0'といった文字列を106000と数字にする関数です。これでバージョンの比較を、数字の大小で判断できるようになります。

39~46行目で、prototype.jsが適切にロードされているかチェックしています。

47行目で、タグ名が"script"のDOM要素を集めて、prototype.jsの$A関数を使って配列の形にしてから、配列のfindAllメソッドを使っています。

48行目で、<script ... src="...scriptaculous.js...">となっているDOM要素をfindAllで集めていることがわかります。

9~51行目で、"...scriptaculous.js..."を分解して、pathの部分と、scriptaculous.js?load=以降の部分とに分解してpath/scriptaculous.js?load=effectsやpath/scriptaculous.js?load=effects,dragdropといった指定を扱っています。

52行目で、それらの結果を組み合わせて、load=effects,dragdropとなっていたときはpath/effects.jsやpath/dragdrop.jsを、単にscriptaculous.jsとなっていたときは全てのライブラリを、28行目で定義したrequire関数で動的にロードしています。

おすすめ記事

記事・ニュース一覧