]> source.dussan.org Git - jquery-ui.git/commitdiff
Deploy release build.xml
authorEduardo Lundgren <eduardolundgren@gmail.com>
Sat, 7 Jun 2008 20:02:49 +0000 (20:02 +0000)
committerEduardo Lundgren <eduardolundgren@gmail.com>
Sat, 7 Jun 2008 20:02:49 +0000 (20:02 +0000)
16 files changed:
release/build.xml [new file with mode: 0644]
release/build/build/min.js [new file with mode: 0644]
release/build/build/pack.js [new file with mode: 0644]
release/build/js.jar [new file with mode: 0644]
release/build/js/Packer.js [new file with mode: 0644]
release/build/js/ParseMaster.js [new file with mode: 0644]
release/build/js/Words.js [new file with mode: 0644]
release/build/js/base2.js [new file with mode: 0644]
release/build/js/jsmin.js [new file with mode: 0644]
release/build/js/json.js [new file with mode: 0644]
release/build/js/pack.js [new file with mode: 0644]
release/build/js/pack.wsf [new file with mode: 0644]
release/build/js/parse.js [new file with mode: 0644]
release/build/js/writeFile.js [new file with mode: 0644]
release/build/js/xml.js [new file with mode: 0644]
release/build/yuicompressor.jar [new file with mode: 0644]

diff --git a/release/build.xml b/release/build.xml
new file mode 100644 (file)
index 0000000..d655a23
--- /dev/null
@@ -0,0 +1,144 @@
+<?xml version="1.0" encoding="iso-8859-1"?>
+
+<!--
+       jQuery UI Release!
+       
+       Call task called 'deploy-release' to build a full release.
+       The release built will be stored on 'dist' dir.
+       
+       @author Eduardo Lundgren (eduardo.lundgren@gmail.com)
+-->
+
+<project name="jquery-ui" default="deploy-release" basedir=".">
+       
+       <target name="deploy-release" depends="load.properties, concatenate, minify, pack, copy" description="Release builder">
+       </target>
+
+       <target name="load.properties">
+               <property file="ant.properties" />
+
+               <property name="dist.dir" value="dist" />
+               <property name="build.dir" value="build" />
+               <property name="ui.dir" value="../" />
+               <property name="src.dir" value="${ui.dir}/ui/" />
+               
+               <property name="min.folder" value="${dist.dir}/ui/minified" />
+               <property name="packed.folder" value="${dist.dir}/ui/packed" />
+               
+               <property name="concatenated" value="jquery-ui-all" />
+               
+               <property name="core.files" value="ui.core.js, ui.draggable.js, ui.droppable.js, ui.resizable.js, ui.selectable.js, ui.sortable.js, effects.core.js" />
+               
+               <property description="YUI Compressor" name="yui-jar" value="${build.dir}/yuicompressor.jar" />
+               <property description="Rhino JS Engine" name="jar" value="${build.dir}/js.jar" />
+       </target>
+
+       <target name="concatenate" depends="load.properties">
+               <echo message="Building concatenated" />
+               <mkdir dir="${dist.dir}/ui/" />
+               <delete file="${dist.dir}/ui/${concatenated}.js" />
+               
+               <concat destfile="${dist.dir}/ui/${concatenated}.js">
+                       <filelist dir="${src.dir}/" files="${core.files}" />
+                       <fileset dir="${src.dir}/" includes="ui.*.js, effects.*.js" excludes="${core.files}" />
+               </concat>
+               <echo message="Concatenated built." />
+       </target>
+
+       <target name="minify" depends="load.properties, concatenate" description="Remove all comments and whitespace, no compression, great in combination with GZip">
+               <echo message="Building minified" />
+               <delete dir="${min.folder}/" />
+               <mkdir dir="${min.folder}" />
+               
+               <apply executable="java" parallel="false">
+                       <filelist dir="${dist.dir}/ui/" files="${concatenated}.js" />
+                       <fileset dir="${src.dir}/" includes="ui.*.js, effects.*.js" />
+                       <arg line="-jar" />
+                       <arg path="${yui-jar}" />
+                       <srcfile />
+                       <arg line="-o" />
+                       <mapper type="glob" from="*.js" to="${min.folder}/*.min.js" />
+                       <targetfile />
+               </apply>
+               <echo message="Minified built." />
+       </target>
+       
+       
+       <target name="pack" depends="load.properties, concatenate, minify" description="Remove all comments and whitespace and compress">
+               <!--
+                       http://dean.edwards.name/download/#packer
+                       http://homepages.nildram.co.uk/~9jack9/download/packer.wsh.zip
+               -->
+               <echo message="Building packed" />
+               <delete dir="${packed.folder}/" />
+               <mkdir dir="${packed.folder}" />
+               
+               <apply executable="cmd" parallel="false">
+                       <filelist dir="${min.folder}" files="${concatenated}.min.js" />
+                       <fileset dir="${min.folder}" includes="ui.*.min.js, effects.*.min.js" />
+                       <arg line="/c CScript /nologo ${build.dir}/js/pack.wsf" />
+                       <srcfile />
+                       <arg line=">>" />
+                       <mapper type="glob" from="*.min.js" to="${packed.folder}/*.packed.js" />
+                       <targetfile />
+               </apply>
+               <echo message="Packed built." />
+       </target>
+               
+       
+       <!--
+       TODO - Using Rhino Javascript Engine
+       <target name="pack" depends="load.properties, concatenate" description="Remove all comments and whitespace and compress">
+               <echo message="Building packed" />
+               <delete dir="${packed.folder}/" />
+               <mkdir dir="${packed.folder}" />
+               
+               <apply executable="java" parallel="false">
+                       <filelist dir="${min.folder}" files="${concatenated}.min.js" />
+                       <fileset dir="${min.folder}" includes="ui.*.min.js, effects.*.min.js" />
+                       <arg line="-jar" />
+                       <arg path="${jar}" />
+                       <arg value="${build.dir}/build/pack.js" />
+                       <srcfile />
+                       <mapper type="glob" from="*.min.js" to="${packed.folder}/*.packed.js" />
+                       <targetfile />
+               </apply>
+               <echo message="Packed built." />
+       </target>
+       -->
+       
+       <target description="Copy needed folders" name="copy" depends="load.properties">
+               <echo message="Copying files" />
+               <mkdir dir="${dist.dir}" />
+               
+               <copy overwrite="true" todir="${dist.dir}/ui/">
+                       <fileset dir="${src.dir}/" includes="ui.*.js, effects.*.js" />
+               </copy>
+               
+               <copy overwrite="true" todir="${dist.dir}/ui/i18n/" >
+                       <fileset dir="${src.dir}/i18n/" />
+               </copy>
+               
+               <copy overwrite="true" todir="${dist.dir}/">
+                       <fileset dir="${ui.dir}/" includes="*.txt" />
+               </copy>
+               
+               <copy overwrite="true" todir="${dist.dir}/demos/" >
+                       <fileset dir="${ui.dir}/demos/" />
+               </copy>
+               
+               <copy overwrite="true" todir="${dist.dir}/tests/" >
+                       <fileset dir="${ui.dir}/tests/" />
+               </copy>
+               
+               <copy overwrite="true" todir="${dist.dir}/themes/" >
+                       <fileset dir="${ui.dir}/themes/" />
+               </copy>
+               <echo message="Files copied." />
+       </target>
+
+        <target depends="load.properties" name="clean">
+               <delete dir="${dist.dir}" />
+    </target>
+       
+</project>
\ No newline at end of file
diff --git a/release/build/build/min.js b/release/build/build/min.js
new file mode 100644 (file)
index 0000000..5707d4a
--- /dev/null
@@ -0,0 +1,11 @@
+load("build/js/jsmin.js", "build/js/writeFile.js");
+
+// arguments
+var inFile = arguments[0];
+var outFile = arguments[1] || inFile.replace(/\.js$/, ".min.js");
+
+var script = readFile(inFile);
+var header = script.match(/\/\*(.|\n)*?\*\//)[0];
+var minifiedScript = jsmin('', script, 3);
+
+writeFile( outFile, header + minifiedScript );
diff --git a/release/build/build/pack.js b/release/build/build/pack.js
new file mode 100644 (file)
index 0000000..f387392
--- /dev/null
@@ -0,0 +1,19 @@
+load("build/js/writeFile.js");
+load("build/js/base2.js");
+load("build/js/Packer.js");
+load("build/js/Words.js");
+
+// arguments
+var inFile = arguments[0];
+var outFile = arguments[1] || inFile.replace(/\.js$/, ".pack.js");
+
+// options
+var base62 = true;
+var shrink = true;
+
+var script = readFile(inFile);
+var header = script.match(/\/\*(.|\n)*?\*\//)[0];
+var packer = new Packer;
+var packedScript = packer.pack(script, base62, shrink);
+
+writeFile(outFile, header + "\n" + packedScript);
diff --git a/release/build/js.jar b/release/build/js.jar
new file mode 100644 (file)
index 0000000..a76cc7c
Binary files /dev/null and b/release/build/js.jar differ
diff --git a/release/build/js/Packer.js b/release/build/js/Packer.js
new file mode 100644 (file)
index 0000000..03af07b
--- /dev/null
@@ -0,0 +1,209 @@
+/*
+       Packer version 3.0 (beta 8) - copyright 2004-2007, Dean Edwards
+       http://www.opensource.org/licenses/mit-license
+*/
+
+eval(base2.namespace);
+
+var IGNORE = RegGrp.IGNORE;
+var REMOVE = "";
+var SPACE = " ";
+var WORDS = /\w+/g;
+
+var Packer = Base.extend({
+       minify: function(script) {
+               script = script.replace(Packer.CONTINUE, "");
+               script = Packer.data.exec(script);
+               script = Packer.whitespace.exec(script);
+               script = Packer.clean.exec(script);
+               return script;
+       },
+       
+       pack: function(script, base62, shrink) {
+               script = this.minify(script + "\n");
+               if (shrink) script = this._shrinkVariables(script);
+               if (base62) script = this._base62Encode(script);        
+               return script;
+       },
+       
+       _base62Encode: function(script) {
+               var words = new Words(script);
+               var encode = function(word) {
+                       return words.fetch(word).encoded;
+               };
+               
+               /* build the packed script */
+               
+               var p = this._escape(script.replace(WORDS, encode));            
+               var a = Math.min(Math.max(words.count(), 2), 62);               
+               var c = words.count();          
+               var k = words;
+               var e = Packer["ENCODE" + (a > 10 ? a > 36 ? 62 : 36 : 10)];
+               var r = a > 10 ? "e(c)" : "c";
+               
+               // the whole thing
+               return format(Packer.UNPACK, p,a,c,k,e,r);
+       },
+       
+       _escape: function(script) {
+               // single quotes wrap the final string so escape them
+               // also escape new lines required by conditional comments
+               return script.replace(/([\\'])/g, "\\$1").replace(/[\r\n]+/g, "\\n");
+       },
+       
+       _shrinkVariables: function(script) {
+               // Windows Scripting Host cannot do regexp.test() on global regexps.
+               var global = function(regexp) {
+                       // This function creates a global version of the passed regexp.
+                       return new RegExp(regexp.source, "g");
+               };
+               
+               var data = []; // encoded strings and regular expressions
+               var REGEXP = /^[^'"]\//;
+               var store = function(string) {
+                       var replacement = "#" + data.length;
+                       if (REGEXP.test(string)) {
+                               replacement = string.charAt(0) + replacement;
+                               string = string.slice(1);
+                       }
+                       data.push(string);
+                       return replacement;
+               };
+               
+               // Base52 encoding (a-Z)
+               var encode52 = function(c) {
+                       return (c < 52 ? '' : arguments.callee(parseInt(c / 52))) +
+                               ((c = c % 52) > 25 ? String.fromCharCode(c + 39) : String.fromCharCode(c + 97));
+               };
+                               
+               // identify blocks, particularly identify function blocks (which define scope)
+               var BLOCK = /(function\s*[\w$]*\s*\(\s*([^\)]*)\s*\)\s*)?(\{([^{}]*)\})/;
+               var VAR_ = /var\s+/g;
+               var VAR_NAME = /var\s+[\w$]+/g;
+               var COMMA = /\s*,\s*/;
+               var blocks = []; // store program blocks (anything between braces {})
+               // encoder for program blocks
+               var encode = function(block, func, args) {
+                       if (func) { // the block is a function block
+                       
+                               // decode the function block (THIS IS THE IMPORTANT BIT)
+                               // We are retrieving all sub-blocks and will re-parse them in light
+                               // of newly shrunk variables
+                               block = decode(block);
+                               
+                               // create the list of variable and argument names 
+                               var vars = match(block, VAR_NAME).join(",").replace(VAR_, "");
+                               var ids = Array2.combine(args.split(COMMA).concat(vars.split(COMMA)));
+                               
+                               // process each identifier
+                               var count = 0, shortId;
+                               forEach (ids, function(id) {
+                                       id = trim(id);
+                                       if (id && id.length > 1) { // > 1 char
+                                               id = rescape(id);
+                                               // find the next free short name (check everything in the current scope)
+                                               do shortId = encode52(count++);
+                                               while (new RegExp("[^\\w$.]" + shortId + "[^\\w$:]").test(block));
+                                               // replace the long name with the short name
+                                               var reg = new RegExp("([^\\w$.])" + id + "([^\\w$:])");
+                                               while (reg.test(block)) block = block.replace(global(reg), "$1" + shortId + "$2");
+                                               var reg = new RegExp("([^{,])" + id + ":", "g");
+                                               block = block.replace(reg, "$1" + shortId + ":");
+                                       }
+                               });
+                       }
+                       var replacement = "~" + blocks.length + "~";
+                       blocks.push(block);
+                       return replacement;
+               };
+               
+               // decoder for program blocks
+               var ENCODED = /~(\d+)~/;
+               var decode = function(script) {
+                       while (ENCODED.test(script)) {
+                               script = script.replace(global(ENCODED), function(match, index) {
+                                       return blocks[index];
+                               });
+                       }
+                       return script;
+               };
+               
+               // encode strings and regular expressions
+               script = Packer.data.exec(script, store);
+               
+               // remove closures (this is for base2 namespaces only)
+               script = script.replace(/new function\(_\)\s*\{/g, "{;#;");
+               
+               // encode blocks, as we encode we replace variable and argument names
+               while (BLOCK.test(script)) {
+                       script = script.replace(global(BLOCK), encode);
+               }
+               
+               // put the blocks back
+               script = decode(script);
+               
+               // put back the closure (for base2 namespaces only)
+               script = script.replace(/\{;#;/g, "new function(_){");
+               
+               // put strings and regular expressions back
+               script = script.replace(/#(\d+)/g, function(match, index) {             
+                       return data[index];
+               });
+               
+               return script;
+       }
+}, {
+       CONTINUE: /\\\r?\n/g,
+       
+       ENCODE10: "String",
+       ENCODE36: "function(c){return c.toString(a)}",
+       ENCODE62: "function(c){return(c<a?'':e(parseInt(c/a)))+((c=c%a)>35?String.fromCharCode(c+29):c.toString(36))}",
+       
+       UNPACK: "eval(function(p,a,c,k,e,r){e=%5;if(!''.replace(/^/,String)){while(c--)r[%6]=k[c]" +
+               "||%6;k=[function(e){return r[e]}];e=function(){return'\\\\w+'};c=1};while(c--)if(k[c])p=p." +
+                       "replace(new RegExp('\\\\b'+e(c)+'\\\\b','g'),k[c]);return p}('%1',%2,%3,'%4'.split('|'),0,{}))",
+       
+       init: function() {
+               this.data = reduce(this.data, function(data, replacement, expression) {
+                       data.store(this.javascript.exec(expression), replacement);
+                       return data;
+               }, new RegGrp, this);
+               this.clean = this.data.union(this.clean);
+               this.whitespace = this.data.union(this.whitespace);
+       },
+       
+       clean: {
+               "\\(\\s*;\\s*;\\s*\\)": "(;;)", // for (;;) loops
+               "throw[^};]+[};]": IGNORE, // a safari 1.3 bug
+               ";+\\s*([};])": "$1"
+       },
+       
+       data: {
+               // strings
+               "STRING1": IGNORE,
+               'STRING2': IGNORE,
+               "CONDITIONAL": IGNORE, // conditional comments
+               "(COMMENT1)\\n\\s*(REGEXP)?": "\n$3",
+               "(COMMENT2)\\s*(REGEXP)?": " $3",
+               "([\\[(\\^=,{}:;&|!*?])\\s*(REGEXP)": "$1$2"
+       },
+       
+       javascript: new RegGrp({
+               COMMENT1:    /(\/\/|;;;)[^\n]*/.source,
+               COMMENT2:    /\/\*[^*]*\*+([^\/][^*]*\*+)*\//.source,
+               CONDITIONAL: /\/\*@|@\*\/|\/\/@[^\n]*\n/.source,
+               REGEXP:      /\/(\\[\/\\]|[^*\/])(\\.|[^\/\n\\])*\/[gim]*/.source,
+               STRING1:     /'(\\.|[^'\\])*'/.source,
+               STRING2:     /"(\\.|[^"\\])*"/.source
+       }),
+       
+       whitespace: {
+               "(\\d)\\s+(\\.\\s*[a-z\\$_\\[(])": "$1 $2", // http://dean.edwards.name/weblog/2007/04/packer3/#comment84066
+               "([+-])\\s+([+-])": "$1 $2", // c = a++ +b;
+               "\\b\\s+\\$\\s+\\b": " $ ", // var $ in
+               "\\$\\s+\\b": "$ ", // object$ in
+               "\\b\\s+\\$": " $", // return $object
+               "\\b\\s+\\b": SPACE,
+               "\\s+": REMOVE
+       }
+});
diff --git a/release/build/js/ParseMaster.js b/release/build/js/ParseMaster.js
new file mode 100644 (file)
index 0000000..915a8b5
--- /dev/null
@@ -0,0 +1,106 @@
+/*
+    ParseMaster, version 1.0.2 (2005-08-19)
+    Copyright 2005, Dean Edwards
+    License: http://creativecommons.org/licenses/LGPL/2.1/
+*/
+
+/* a multi-pattern parser */
+
+// KNOWN BUG: erroneous behavior when using escapeChar with a replacement value that is a function
+
+function ParseMaster() {
+    // constants
+    var $EXPRESSION = 0, $REPLACEMENT = 1, $LENGTH = 2;
+    // used to determine nesting levels
+    var $GROUPS = /\(/g, $SUB_REPLACE = /\$\d/, $INDEXED = /^\$\d+$/,
+        $TRIM = /(['"])\1\+(.*)\+\1\1$/, $$ESCAPE = /\\./g, $QUOTE = /'/,
+        $$DELETED = /\x01[^\x01]*\x01/g;
+    var self = this;
+    // public
+    this.add = function($expression, $replacement) {
+        if (!$replacement) $replacement = "";
+        // count the number of sub-expressions
+        //  - add one because each pattern is itself a sub-expression
+        var $length = (_internalEscape(String($expression)).match($GROUPS) || "").length + 1;
+        // does the pattern deal with sub-expressions?
+        if ($SUB_REPLACE.test($replacement)) {
+            // a simple lookup? (e.g. "$2")
+            if ($INDEXED.test($replacement)) {
+                // store the index (used for fast retrieval of matched strings)
+                $replacement = parseInt($replacement.slice(1)) - 1;
+            } else { // a complicated lookup (e.g. "Hello $2 $1")
+                // build a function to do the lookup
+                var i = $length;
+                var $quote = $QUOTE.test(_internalEscape($replacement)) ? '"' : "'";
+                while (i) $replacement = $replacement.split("$" + i--).join($quote + "+a[o+" + i + "]+" + $quote);
+                $replacement = new Function("a,o", "return" + $quote + $replacement.replace($TRIM, "$1") + $quote);
+            }
+        }
+        // pass the modified arguments
+        _add($expression || "/^$/", $replacement, $length);
+    };
+    // execute the global replacement
+    this.exec = function($string) {
+        _escaped.length = 0;
+        return _unescape(_escape($string, this.escapeChar).replace(
+            new RegExp(_patterns, this.ignoreCase ? "gi" : "g"), _replacement), this.escapeChar).replace($$DELETED, "");
+    };
+    // clear the patterns collection so that this object may be re-used
+    this.reset = function() {
+        _patterns.length = 0;
+    };
+
+    // private
+    var _escaped = [];  // escaped characters
+    var _patterns = []; // patterns stored by index
+    var _toString = function(){return "(" + String(this[$EXPRESSION]).slice(1, -1) + ")"};
+    _patterns.toString = function(){return this.join("|")};
+    // create and add a new pattern to the patterns collection
+    function _add() {
+        arguments.toString = _toString;
+        // store the pattern - as an arguments object (i think this is quicker..?)
+        _patterns[_patterns.length] = arguments;
+    }
+    // this is the global replace function (it's quite complicated)
+    function _replacement() {
+        if (!arguments[0]) return "";
+        var i = 1, j = 0, $pattern;
+        // loop through the patterns
+        while ($pattern = _patterns[j++]) {
+            // do we have a result?
+            if (arguments[i]) {
+                var $replacement = $pattern[$REPLACEMENT];
+                switch (typeof $replacement) {
+                    case "function": return $replacement(arguments, i);
+                    case "number": return arguments[$replacement + i];
+                }
+                var $delete = (arguments[i].indexOf(self.escapeChar) == -1) ? "" :
+                    "\x01" + arguments[i] + "\x01";
+                return $delete + $replacement;
+            // skip over references to sub-expressions
+            } else i += $pattern[$LENGTH];
+        }
+    };
+    // encode escaped characters
+    function _escape($string, $escapeChar) {
+        return $escapeChar ? $string.replace(new RegExp("\\" + $escapeChar + "(.)", "g"), function($match, $char) {
+            _escaped[_escaped.length] = $char;
+            return $escapeChar;
+        }) : $string;
+    };
+    // decode escaped characters
+    function _unescape($string, $escapeChar) {
+        var i = 0;
+        return $escapeChar ? $string.replace(new RegExp("\\" + $escapeChar, "g"), function() {
+            return $escapeChar + (_escaped[i++] || "");
+        }) : $string;
+    };
+    function _internalEscape($string) {
+        return $string.replace($$ESCAPE, "");
+    };
+};
+ParseMaster.prototype = {
+    constructor: ParseMaster,
+    ignoreCase: false,
+    escapeChar: ""
+};
diff --git a/release/build/js/Words.js b/release/build/js/Words.js
new file mode 100644 (file)
index 0000000..cbd9cab
--- /dev/null
@@ -0,0 +1,62 @@
+
+var Words = Collection.extend({
+       constructor: function(script) {
+               this.base();
+               forEach (script.match(WORDS), this.add, this);
+               this.encode();
+       },
+       
+       add: function(word) {
+               if (!this.exists(word)) this.base(word);
+               word = this.fetch(word);
+               word.count++;
+               return word;
+       },
+       
+       encode: function() {
+               // sort by frequency
+               this.sort(function(word1, word2) {
+                       return word2.count - word1.count;
+               });
+               
+               eval("var a=62,e=" + Packer.ENCODE62);
+               var encode = e;         
+               var encoded = new Collection; // a dictionary of base62 -> base10
+               var count = this.count();
+               for (var i = 0; i < count; i++) {
+                       encoded.store(encode(i), i);
+               }
+               
+               var empty = function() {return ""};
+               var index = 0;
+               forEach (this, function(word) {
+                       if (encoded.exists(word)) {
+                               word.index = encoded.fetch(word);
+                               word.toString = empty;
+                       } else {
+                               while (this.exists(encode(index))) index++;
+                               word.index = index++;
+                       }
+                       word.encoded = encode(word.index);
+               }, this);
+               
+               // sort by encoding
+               this.sort(function(word1, word2) {
+                       return word1.index - word2.index;
+               });
+       },
+       
+       toString: function() {
+               return this.values().join("|");
+       }
+}, {
+       Item: {
+               constructor: function(word) {
+                       this.toString = function() {return word};
+               },
+               
+               count: 0,
+               encoded: "",
+               index: -1
+       }
+});
diff --git a/release/build/js/base2.js b/release/build/js/base2.js
new file mode 100644 (file)
index 0000000..08f0914
--- /dev/null
@@ -0,0 +1,978 @@
+// timestamp: Tue, 01 May 2007 19:13:00
+/*
+       base2.js - copyright 2007, Dean Edwards
+       http://www.opensource.org/licenses/mit-license
+*/
+
+var base2 = {};
+
+// You know, writing a javascript library is awfully time consuming.
+
+new function(_) { ////////////////////  BEGIN: CLOSURE  ////////////////////
+
+// =========================================================================
+// base2/Base.js
+// =========================================================================
+
+// version 1.1
+
+var Base = function() {
+       // call this method from any other method to invoke that method's ancestor
+};
+
+Base.prototype = {     
+       extend: function(source) {
+               if (arguments.length > 1) { // extending with a name/value pair
+                       var ancestor = this[source];
+                       var value = arguments[1];
+                       if (typeof value == "function" && ancestor && /\bbase\b/.test(value)) {
+                               var method = value;                             
+                               value = function() { // override
+                                       var previous = this.base;
+                                       this.base = ancestor;
+                                       var returnValue = method.apply(this, arguments);
+                                       this.base = previous;
+                                       return returnValue;
+                               };
+                               value.method = method;
+                               value.ancestor = ancestor;
+                       }
+                       this[source] = value;
+               } else if (source) { // extending with an object literal
+                       var extend = Base.prototype.extend;
+                       if (Base._prototyping) {
+                               var key, i = 0, members = ["constructor", "toString", "valueOf"];
+                               while (key = members[i++]) if (source[key] != Object.prototype[key]) {
+                                       extend.call(this, key, source[key]);
+                               }
+                       } else if (typeof this != "function") {
+                               // if the object has a customised extend() method then use it
+                               extend = this.extend || extend;
+                       }                       
+                       // copy each of the source object's properties to this object
+                       for (key in source) if (!Object.prototype[key]) {
+                               extend.call(this, key, source[key]);
+                       }
+               }
+               return this;
+       },
+
+       base: Base
+};
+
+Base.extend = function(_instance, _static) { // subclass
+       var extend = Base.prototype.extend;
+       
+       // build the prototype
+       Base._prototyping = true;
+       var proto = new this;
+       extend.call(proto, _instance);
+       delete Base._prototyping;
+       
+       // create the wrapper for the constructor function
+       var constructor = proto.constructor;
+       var klass = proto.constructor = function() {
+               if (!Base._prototyping) {
+                       if (this._constructing || this.constructor == klass) { // instantiation
+                               this._constructing = true;
+                               constructor.apply(this, arguments);
+                               delete this._constructing;
+                       } else { // casting
+                               var object = arguments[0];
+                               if (object != null) {
+                                       (object.extend || extend).call(object, proto);
+                               }
+                               return object;
+                       }
+               }
+       };
+       
+       // build the class interface
+       for (var i in Base) klass[i] = this[i];
+       klass.ancestor = this;
+       klass.base = Base.base;
+       klass.prototype = proto;
+       klass.toString = this.toString;
+       extend.call(klass, _static);
+       // class initialisation
+       if (typeof klass.init == "function") klass.init();
+       return klass;
+};
+
+// initialise
+Base = Base.extend({
+       constructor: function() {
+               this.extend(arguments[0]);
+       }
+}, {
+       ancestor: Object,
+       base: Base,
+       
+       implement: function(_interface) {
+               if (typeof _interface == "function") {
+                       // if it's a function, call it
+                       _interface(this.prototype);
+               } else {
+                       // add the interface using the extend() method
+                       this.prototype.extend(_interface);
+               }
+               return this;
+       }
+});
+
+// =========================================================================
+// lang/main.js
+// =========================================================================
+
+var Legacy = typeof $Legacy == "undefined" ? {} : $Legacy;
+
+var K = function(k) {return k};
+
+var assert = function(condition, message, Err) {
+       if (!condition) {
+               throw new (Err || Error)(message || "Assertion failed.");
+       }
+};
+
+var assertType = function(object, type, message) {
+       if (type) {
+               var condition = typeof type == "function" ? instanceOf(object, type) : typeof object == type;
+               assert(condition, message || "Invalid type.", TypeError);
+       }
+};
+
+var copy = function(object) {
+       var fn = new Function;
+       fn.prototype = object;
+       return new fn;
+};
+
+var format = function(string) {
+       // replace %n with arguments[n]
+       // e.g. format("%1 %2%3 %2a %1%3", "she", "se", "lls");
+       // ==> "she sells sea shells"
+       // only supports nine replacements: %1 - %9
+       var args = arguments;
+       return String(string).replace(/%([1-9])/g, function(match, index) {
+               return index < args.length ? args[index] : match;
+       });
+};
+
+var $instanceOf = Legacy.instanceOf || new Function("o,k", "return o instanceof k");
+var instanceOf = function(object, klass) {
+       assertType(klass, "function", "Invalid 'instanceOf' operand.");
+       if ($instanceOf(object, klass)) return true;
+       // handle exceptions where the target object originates from another frame
+       //  this is handy for JSON parsing (amongst other things)
+       if (object != null) switch (klass) {
+               case Object:
+                       return true;
+               case Number:
+               case Boolean:
+               case Function:
+               case String:
+                       return typeof object == typeof klass.prototype.valueOf();
+               case Array:
+                       // this is the only troublesome one
+                       return !!(object.join && object.splice && !arguments.callee(object, Function));
+               case Date:
+                       return !!object.getTimezoneOffset;
+               case RegExp:
+                       return String(object.constructor.prototype) == String(new RegExp);
+       }
+       return false;
+};
+       
+var match = function(string, expression) {
+       // same as String.match() except that this function will return an empty 
+       // array if there is no match
+       return String(string).match(expression) || [];
+};
+
+var RESCAPE = /([\/()[\]{}|*+-.,^$?\\])/g;
+var rescape = function(string) {
+       // make a string safe for creating a RegExp
+       return String(string).replace(RESCAPE, "\\$1");
+};
+
+var $slice = Array.prototype.slice;
+var slice = function(object) {
+       // slice an array-like object
+       return $slice.apply(object, $slice.call(arguments, 1));
+};
+
+var TRIM = /^\s+|\s+$/g;
+var trim = function(string) {
+       return String(string).replace(TRIM, "");        
+};
+
+// =========================================================================
+// lang/extend.js
+// =========================================================================
+
+var base = function(object, args) {
+       // invoke the base method with all supplied arguments
+       return object.base.apply(object, args);
+};
+
+var extend = function(object) {
+       assert(object != Object.prototype, "Object.prototype is verboten!");
+       return Base.prototype.extend.apply(object, slice(arguments, 1));
+};
+
+// =========================================================================
+// lang/assignID.js
+// =========================================================================
+
+var $ID = 1;
+var assignID = function(object) {
+       // assign a unique id
+       if (!object.base2ID) object.base2ID = "b2_" + $ID++;
+       return object.base2ID;
+};
+
+// =========================================================================
+// lang/forEach.js
+// =========================================================================
+
+if (typeof StopIteration == "undefined") {
+       StopIteration = new Error("StopIteration");
+}
+
+var forEach = function(object, block, context) {
+       if (object == null) return;
+       if (typeof object == "function") {
+               // functions are a special case
+               var fn = Function;
+       } else if (typeof object.forEach == "function" && object.forEach != arguments.callee) {
+               // the object implements a custom forEach method
+               object.forEach(block, context);
+               return;
+       } else if (typeof object.length == "number") {
+               // the object is array-like
+               forEach.Array(object, block, context);
+               return;
+       }
+       forEach.Function(fn || Object, object, block, context);
+};
+
+// these are the two core enumeration methods. all other forEach methods
+//  eventually call one of these two.
+
+forEach.Array = function(array, block, context) {
+       var i, length = array.length; // preserve
+       if (typeof array == "string") {
+               for (i = 0; i < length; i++) {
+                       block.call(context, array.charAt(i), i, array);
+               }
+       } else {
+               for (i = 0; i < length; i++) {
+                       block.call(context, array[i], i, array);
+               }
+       }
+};
+
+forEach.Function = Legacy.forEach || function(fn, object, block, context) {
+       // enumerate an object and compare its keys with fn's prototype
+       for (var key in object) {
+               if (fn.prototype[key] === undefined) {
+                       block.call(context, object[key], key, object);
+               }
+       }
+};
+
+// =========================================================================
+// base2/Base/forEach.js
+// =========================================================================
+
+Base.forEach = function(object, block, context) {
+       forEach.Function(this, object, block, context);
+};
+
+// =========================================================================
+// base2/../Function.js
+// =========================================================================
+
+// some browsers don't define this
+
+Function.prototype.prototype = {};
+
+
+// =========================================================================
+// base2/../String.js
+// =========================================================================
+
+// fix String.replace (Safari/IE5.0)
+
+if ("".replace(/^/, String)) {
+       extend(String.prototype, "replace", function(expression, replacement) {
+               if (typeof replacement == "function") { // Safari doesn't like functions
+                       if (instanceOf(expression, RegExp)) {
+                               var regexp = expression;
+                               var global = regexp.global;
+                               if (global == null) global = /(g|gi)$/.test(regexp);
+                               // we have to convert global RexpExps for exec() to work consistently
+                               if (global) regexp = new RegExp(regexp.source); // non-global
+                       } else {
+                               regexp = new RegExp(rescape(expression));
+                       }
+                       var match, string = this, result = "";
+                       while (string && (match = regexp.exec(string))) {
+                               result += string.slice(0, match.index) + replacement.apply(this, match);
+                               string = string.slice(match.index + match[0].length);
+                               if (!global) break;
+                       }
+                       return result + string;
+               } else {
+                       return base(this, arguments);
+               }
+       });
+}
+
+// =========================================================================
+// base2/Abstract.js
+// =========================================================================
+
+var Abstract = Base.extend({
+       constructor: function() {
+               throw new TypeError("Class cannot be instantiated.");
+       }
+});
+
+// =========================================================================
+// base2/Module.js
+// =========================================================================
+
+// based on ruby's Module class and Mozilla's Array generics:
+//   http://www.ruby-doc.org/core/classes/Module.html
+//   http://developer.mozilla.org/en/docs/New_in_JavaScript_1.6#Array_and_String_generics
+
+// A Module is used as the basis for creating interfaces that can be
+// applied to other classes. *All* properties and methods are static.
+// When a module is used as a mixin, methods defined on what would normally be
+// the instance interface become instance methods of the target object.
+
+// Modules cannot be instantiated. Static properties and methods are inherited.
+
+var Module = Abstract.extend(null, {
+       extend: function(_interface, _static) {
+               // extend a module to create a new module
+               var module = this.base();
+               // inherit static methods
+               forEach (this, function(property, name) {
+                       if (!Module[name] && name != "init") {
+                               extend(module, name, property);
+                       }
+               });
+               // implement module (instance AND static) methods
+               module.implement(_interface);
+               // implement static properties and methods
+               extend(module, _static);
+               // Make the submarine noises Larry!
+               if (typeof module.init == "function") module.init();
+               return module;
+       },
+       
+       implement: function(_interface) {
+               // implement an interface on BOTH the instance and static interfaces
+               var module = this;
+               if (typeof _interface == "function") {
+                       module.base(_interface);
+                       forEach (_interface, function(property, name) {
+                               if (!Module[name] && name != "init") {
+                                       extend(module, name, property);
+                               }
+                       });
+               } else {
+                       // create the instance interface
+                       Base.forEach (extend({}, _interface), function(property, name) {
+                               // instance methods call the equivalent static method
+                               if (typeof property == "function") {
+                                       property = function() {
+                                               base; // force inheritance
+                                               return module[name].apply(module, [this].concat(slice(arguments)));
+                                       };
+                               }
+                               if (!Module[name]) extend(this, name, property);
+                       }, module.prototype);
+                       // add the static interface
+                       extend(module, _interface);
+               }
+               return module;
+       }
+});
+
+
+// =========================================================================
+// base2/Enumerable.js
+// =========================================================================
+
+var Enumerable = Module.extend({
+       every: function(object, test, context) {
+               var result = true;
+               try {
+                       this.forEach (object, function(value, key) {
+                               result = test.call(context, value, key, object);
+                               if (!result) throw StopIteration;
+                       });
+               } catch (error) {
+                       if (error != StopIteration) throw error;
+               }
+               return !!result; // cast to boolean
+       },
+       
+       filter: function(object, test, context) {
+               return this.reduce(object, function(result, value, key) {
+                       if (test.call(context, value, key, object)) {
+                               result[result.length] = value;
+                       }
+                       return result;
+               }, new Array2);
+       },
+
+       invoke: function(object, method) {
+               // apply a method to each item in the enumerated object
+               var args = slice(arguments, 2);
+               return this.map(object, (typeof method == "function") ? function(item) {
+                       if (item != null) return method.apply(item, args);
+               } : function(item) {
+                       if (item != null) return item[method].apply(item, args);
+               });
+       },
+       
+       map: function(object, block, context) {
+               var result = new Array2;
+               this.forEach (object, function(value, key) {
+                       result[result.length] = block.call(context, value, key, object);
+               });
+               return result;
+       },
+       
+       pluck: function(object, key) {
+               return this.map(object, function(item) {
+                       if (item != null) return item[key];
+               });
+       },
+       
+       reduce: function(object, block, result, context) {
+               this.forEach (object, function(value, key) {
+                       result = block.call(context, result, value, key, object);
+               });
+               return result;
+       },
+       
+       some: function(object, test, context) {
+               return !this.every(object, function(value, key) {
+                       return !test.call(context, value, key, object);
+               });
+       }
+}, {
+       forEach: forEach
+});
+
+// =========================================================================
+// base2/Array2.js
+// =========================================================================
+
+// The IArray module implements all Array methods.
+// This module is not public but its methods are accessible through the Array2 object (below). 
+
+var IArray = Module.extend({
+       combine: function(keys, values) {
+               // combine two arrays to make a hash
+               if (!values) values = keys;
+               return this.reduce(keys, function(object, key, index) {
+                       object[key] = values[index];
+                       return object;
+               }, {});
+       },
+       
+       copy: function(array) {
+               return this.concat(array);
+       },
+       
+       contains: function(array, item) {
+               return this.indexOf(array, item) != -1;
+       },
+       
+       forEach: forEach.Array,
+       
+       indexOf: function(array, item, fromIndex) {
+               var length = array.length;
+               if (fromIndex == null) {
+                       fromIndex = 0;
+               } else if (fromIndex < 0) {
+                       fromIndex = Math.max(0, length + fromIndex);
+               }
+               for (var i = fromIndex; i < length; i++) {
+                       if (array[i] === item) return i;
+               }
+               return -1;
+       },
+       
+       insertAt: function(array, item, index) {
+               this.splice(array, index, 0, item);
+               return item;
+       },
+       
+       insertBefore: function(array, item, before) {
+               var index = this.indexOf(array, before);
+               if (index == -1) this.push(array, item);
+               else this.splice(array, index, 0, item);
+               return item;
+       },
+       
+       lastIndexOf: function(array, item, fromIndex) {
+               var length = array.length;
+               if (fromIndex == null) {
+                       fromIndex = length - 1;
+               } else if (from < 0) {
+                       fromIndex = Math.max(0, length + fromIndex);
+               }
+               for (var i = fromIndex; i >= 0; i--) {
+                       if (array[i] === item) return i;
+               }
+               return -1;
+       },
+       
+       remove: function(array, item) {
+               var index = this.indexOf(array, item);
+               if (index != -1) this.removeAt(array, index);
+               return item;
+       },
+       
+       removeAt: function(array, index) {
+               var item = array[index];
+               this.splice(array, index, 1);
+               return item;
+       }
+});
+
+IArray.prototype.forEach = function(block, context) {
+       forEach.Array(this, block, context);
+};
+
+IArray.implement(Enumerable);
+
+forEach ("concat,join,pop,push,reverse,shift,slice,sort,splice,unshift".split(","), function(name) {
+       IArray[name] = function(array) {
+               return Array.prototype[name].apply(array, slice(arguments, 1));
+       };
+});
+
+// create a faux constructor that augments the built-in Array object
+var Array2 = function() {
+       return IArray(this.constructor == IArray ? Array.apply(null, arguments) : arguments[0]);
+};
+// expose IArray.prototype so that it can be extended
+Array2.prototype = IArray.prototype;
+
+forEach (IArray, function(method, name, proto) {
+       if (Array[name]) {
+               IArray[name] = Array[name];
+               delete IArray.prototype[name];
+       }
+       Array2[name] = IArray[name];
+});
+
+// =========================================================================
+// base2/Hash.js
+// =========================================================================
+
+var HASH = "#" + Number(new Date);
+var KEYS = HASH + "keys";
+var VALUES = HASH + "values";
+
+var Hash = Base.extend({
+       constructor: function(values) {
+               this[KEYS] = new Array2;
+               this[VALUES] = {};
+               this.merge(values);
+       },
+
+       copy: function() {
+               var copy = new this.constructor(this);
+               Base.forEach (this, function(property, name) {
+                       if (typeof property != "function" && name.charAt(0) != "#") {
+                               copy[name] = property;
+                       }
+               });
+               return copy;
+       },
+
+       // ancient browsers throw an error when we use "in" as an operator 
+       //  so we must create the function dynamically
+       exists: Legacy.exists || new Function("k", format("return('%1'+k)in this['%2']", HASH, VALUES)),
+
+       fetch: function(key) {
+               return this[VALUES][HASH + key];
+       },
+
+       forEach: function(block, context) {
+               forEach (this[KEYS], function(key) {
+                       block.call(context, this.fetch(key), key, this);
+               }, this);
+       },
+
+       keys: function(index, length) {
+               var keys = this[KEYS] || new Array2;
+               switch (arguments.length) {
+                       case 0: return keys.copy();
+                       case 1: return keys[index];
+                       default: return keys.slice(index, length);
+               }
+       },
+
+       merge: function(values) {
+               forEach (arguments, function(values) {
+                       forEach (values, function(value, key) {
+                               this.store(key, value);
+                       }, this);
+               }, this);
+               return this;
+       },
+
+       remove: function(key) {
+               var value = this.fetch(key);
+               this[KEYS].remove(String(key));
+               delete this[VALUES][HASH + key];
+               return value;
+       },
+
+       store: function(key, value) {
+               if (arguments.length == 1) value = key;
+               // only store the key for a new entry
+               if (!this.exists(key)) {
+                       this[KEYS].push(String(key));
+               }
+               // create the new entry (or overwrite the old entry)
+               this[VALUES][HASH + key] = value;
+               return value;
+       },
+
+       toString: function() {
+               return String(this[KEYS]);
+       },
+
+       union: function(values) {
+               return this.merge.apply(this.copy(), arguments);
+       },
+
+       values: function(index, length) {
+               var values = this.map(K);
+               switch (arguments.length) {
+                       case 0: return values;
+                       case 1: return values[index];
+                       default: return values.slice(index, length);
+               }
+       }
+});
+
+Hash.implement(Enumerable);
+
+// =========================================================================
+// base2/Collection.js
+// =========================================================================
+
+// A Hash that is more array-like (accessible by index).
+
+// Collection classes have a special (optional) property: Item
+// The Item property points to a constructor function.
+// Members of the collection must be an instance of Item.
+// e.g.
+//     var Dates = Collection.extend();                 // create a collection class
+//     Dates.Item = Date;                               // only JavaScript Date objects allowed as members
+//     var appointments = new Dates();                  // instantiate the class
+//     appointments.add(appointmentId, new Date);       // add a date
+//     appointments.add(appointmentId, "tomorrow");     // ERROR!
+
+// The static create() method is responsible for all construction of collection items.
+// Instance methods that add new items (add, store, insertAt, replaceAt) pass *all* of their arguments
+// to the static create() method. If you want to modify the way collection items are 
+// created then you only need to override this method for custom collections.
+
+var Collection = Hash.extend({
+       add: function(key, item) {
+               // Duplicates not allowed using add().
+               //  - but you can still overwrite entries using store()
+               assert(!this.exists(key), "Duplicate key.");
+               return this.store.apply(this, arguments);
+       },
+
+       count: function() {
+               return this[KEYS].length;
+       },
+
+       indexOf: function(key) {
+               return this[KEYS].indexOf(String(key));
+       },
+
+       insertAt: function(index, key, item) {
+               assert(!this.exists(key), "Duplicate key.");
+               this[KEYS].insertAt(index, String(key));
+               return this.store.apply(this, slice(arguments, 1));
+       },
+
+       item: function(index) {
+               return this.fetch(this[KEYS][index]);
+       },
+
+       removeAt: function(index) {
+               return this.remove(this[KEYS][index]);
+       },
+
+       reverse: function() {
+               this[KEYS].reverse();
+               return this;
+       },
+
+       sort: function(compare) {
+               if (compare) {
+                       var self = this;
+                       this[KEYS].sort(function(key1, key2) {
+                               return compare(self.fetch(key1), self.fetch(key2), key1, key2);
+                       });
+               } else this[KEYS].sort();
+               return this;
+       },
+
+       store: function(key, item) {
+               if (arguments.length == 1) item = key;
+               item = this.constructor.create.apply(this.constructor, arguments);
+               return this.base(key, item);
+       },
+
+       storeAt: function(index, item) {
+               //-dean: get rid of this?
+               assert(index < this.count(), "Index out of bounds.");
+               arguments[0] = this[KEYS][index];
+               return this.store.apply(this, arguments);
+       }
+}, {
+       Item: null, // if specified, all members of the Collection must be instances of Item
+       
+       create: function(key, item) {
+               if (this.Item && !instanceOf(item, this.Item)) {
+                       item = new this.Item(key, item);
+               }
+               return item;
+       },
+       
+       extend: function(_instance, _static) {
+               var klass = this.base(_instance);
+               klass.create = this.create;
+               extend(klass, _static);
+               if (!klass.Item) {
+                       klass.Item = this.Item;
+               } else if (typeof klass.Item != "function") {
+                       klass.Item = (this.Item || Base).extend(klass.Item);
+               }
+               if (typeof klass.init == "function") klass.init();
+               return klass;
+       }
+});
+
+// =========================================================================
+// base2/RegGrp.js
+// =========================================================================
+
+var RegGrp = Collection.extend({
+       constructor: function(values, flags) {
+               this.base(values);
+               if (typeof flags == "string") {
+                       this.global = /g/.test(flags);
+                       this.ignoreCase = /i/.test(flags);
+               }
+       },
+
+       global: true, // global is the default setting
+       ignoreCase: false,
+
+       exec: function(string, replacement) {
+               if (arguments.length == 1) {
+                       var keys = this[KEYS];
+                       var values = this[VALUES];
+                       replacement = function(match) {
+                               if (!match) return "";
+                               var offset = 1, i = 0;
+                               // loop through the values
+                               while (match = values[HASH + keys[i++]]) {
+                                       // do we have a result?
+                                       if (arguments[offset]) {
+                                               var replacement = match.replacement;
+                                               switch (typeof replacement) {
+                                                       case "function":
+                                                               return replacement.apply(null, slice(arguments, offset));
+                                                       case "number":
+                                                               return arguments[offset + replacement];
+                                                       default:
+                                                               return replacement;
+                                               }
+                                       // no? then skip over references to sub-expressions
+                                       } else offset += match.length + 1;
+                               }
+                       };
+               }
+               var flags = (this.global ? "g" : "") + (this.ignoreCase ? "i" : "");
+               return String(string).replace(new RegExp(this, flags), replacement);
+       },
+
+       test: function(string) {
+               return this.exec(string) != string;
+       },
+       
+       toString: function() {
+               var length = 0;
+               return "(" + this.map(function(item) {
+                       // fix back references
+                       var expression = String(item).replace(/\\(\d+)/g, function($, index) {
+                               return "\\" + (1 + Number(index) + length);
+                       });
+                       length += item.length + 1;
+                       return expression;
+               }).join(")|(") + ")";
+       }
+}, {
+       IGNORE: "$0",
+       
+       init: function() {
+               forEach ("add,exists,fetch,remove,store".split(","), function(name) {
+                       extend(this, name, function(expression) {
+                               if (instanceOf(expression, RegExp)) {
+                                       expression = expression.source;
+                               }
+                               return base(this, arguments);
+                       });
+               }, this.prototype);
+       }
+});
+
+// =========================================================================
+// base2/RegGrp/Item.js
+// =========================================================================
+
+RegGrp.Item = Base.extend({
+       constructor: function(expression, replacement) {
+               var ESCAPE = /\\./g;
+               var STRING = /(['"])\1\+(.*)\+\1\1$/;
+       
+               expression = instanceOf(expression, RegExp) ? expression.source : String(expression);
+               
+               if (typeof replacement == "number") replacement = String(replacement);
+               else if (replacement == null) replacement = "";
+               
+               // count the number of sub-expressions
+               //  - add one because each pattern is itself a sub-expression
+               this.length = match(expression.replace(ESCAPE, "").replace(/\[[^\]]+\]/g, ""), /\(/g).length;
+               
+               // does the pattern use sub-expressions?
+               if (typeof replacement == "string" && /\$(\d+)/.test(replacement)) {
+                       // a simple lookup? (e.g. "$2")
+                       if (/^\$\d+$/.test(replacement)) {
+                               // store the index (used for fast retrieval of matched strings)
+                               replacement = parseInt(replacement.slice(1));
+                       } else { // a complicated lookup (e.g. "Hello $2 $1")
+                               // build a function to do the lookup
+                               var i = this.length + 1;
+                               var Q = /'/.test(replacement.replace(ESCAPE, "")) ? '"' : "'";
+                               replacement = replacement.replace(/\n/g, "\\n").replace(/\r/g, "\\r").replace(/\$(\d+)/g, Q +
+                                       "+(arguments[$1]||" + Q+Q + ")+" + Q);
+                               replacement = new Function("return " + Q + replacement.replace(STRING, "$1") + Q);
+                       }
+               }
+               this.replacement = replacement;
+               this.toString = function() {
+                       return expression || "";
+               };
+       },
+       
+       length: 0,
+       replacement: ""
+});
+
+// =========================================================================
+// base2/Namespace.js
+// =========================================================================
+
+var Namespace = Base.extend({
+       constructor: function(_private, _public) {
+               this.extend(_public);
+               this.toString = function() {
+                       return format("[base2.%1]", this.name);
+               };
+               
+               // initialise
+               if (typeof this.init == "function") this.init();
+               
+               if (this.name != "base2") {
+                       this.namespace = format("var %1=base2.%1;", this.name);
+               }
+               
+               var namespace = "var base=" + base + ";";
+               var imports = ("base2,lang," + this.imports).split(",");
+               _private.imports = Enumerable.reduce(imports, function(namespace, name) {
+                       if (base2[name]) namespace += base2[name].namespace;
+                       return namespace;
+               }, namespace);
+               
+               var namespace = format("base2.%1=%1;", this.name);
+               var exports = this.exports.split(",");
+               _private.exports = Enumerable.reduce(exports, function(namespace, name) {
+                       if (name) {
+                               this.namespace += format("var %2=%1.%2;", this.name, name);
+                               namespace += format("if(!%1.%2)%1.%2=%2;base2.%2=%1.%2;", this.name, name);
+                       }
+                       return namespace;
+               }, namespace, this);
+               
+               if (this.name != "base2") {
+                       base2.namespace += format("var %1=base2.%1;", this.name);
+               }
+       },
+
+       exports: "",
+       imports: "",
+       namespace: "",
+       name: ""
+});
+
+base2 = new Namespace(this, {
+       name:    "base2",
+       version: "0.8 (alpha)",
+       exports: "Base,Abstract,Module,Enumerable,Array2,Hash,Collection,RegGrp,Namespace"
+});
+
+base2.toString = function() {
+       return "[base2]";
+};
+
+eval(this.exports);
+
+// =========================================================================
+// base2/lang/namespace.js
+// =========================================================================
+
+var lang = new Namespace(this, {
+       name:    "lang",
+       version: base2.version,
+       exports: "K,assert,assertType,assignID,copy,instanceOf,extend,format,forEach,match,rescape,slice,trim",
+       
+       init: function() {
+               this.extend = extend;
+               // add the Enumerable methods to the lang object
+               forEach (Enumerable.prototype, function(method, name) {
+                       if (!Module[name]) {
+                               this[name] = function() {
+                                       return Enumerable[name].apply(Enumerable, arguments);
+                               };
+                               this.exports += "," + name;
+                       }
+               }, this);
+       }
+});
+
+eval(this.exports);
+
+base2.namespace += lang.namespace;
+
+}; ////////////////////  END: CLOSURE  /////////////////////////////////////
diff --git a/release/build/js/jsmin.js b/release/build/js/jsmin.js
new file mode 100644 (file)
index 0000000..3e1ff07
--- /dev/null
@@ -0,0 +1,316 @@
+/* jsmin.js - 2006-08-31
+Author: Franck Marcia
+This work is an adaptation of jsminc.c published by Douglas Crockford.
+Permission is hereby granted to use the Javascript version under the same
+conditions as the jsmin.c on which it is based.
+
+jsmin.c
+2006-05-04
+
+Copyright (c) 2002 Douglas Crockford  (www.crockford.com)
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+of the Software, and to permit persons to whom the Software is furnished to do
+so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+The Software shall be used for Good, not Evil.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
+Update:
+       add level:
+               1: minimal, keep linefeeds if single
+               2: normal, the standard algorithm
+               3: agressive, remove any linefeed and doesn't take care of potential
+                  missing semicolons (can be regressive)
+       store stats
+               jsmin.oldSize
+               jsmin.newSize
+*/
+
+String.prototype.has = function(c) {
+       return this.indexOf(c) > -1;
+};
+
+function jsmin(comment, input, level) {
+
+       if (input === undefined) {
+               input = comment;
+               comment = '';
+               level = 2;
+       } else if (level === undefined || level < 1 || level > 3) {
+               level = 2;
+       }
+
+       if (comment.length > 0) {
+               comment += '\n';
+       }
+
+       var a = '',
+               b = '',
+               EOF = -1,
+               LETTERS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz',
+               DIGITS = '0123456789',
+               ALNUM = LETTERS + DIGITS + '_$\\',
+               theLookahead = EOF;
+
+
+       /* isAlphanum -- return true if the character is a letter, digit, underscore,
+                       dollar sign, or non-ASCII character.
+       */
+
+       function isAlphanum(c) {
+               return c != EOF && (ALNUM.has(c) || c.charCodeAt(0) > 126);
+       }
+
+
+       /* get -- return the next character. Watch out for lookahead. If the
+                       character is a control character, translate it to a space or
+                       linefeed.
+       */
+
+       function get() {
+
+               var c = theLookahead;
+               if (get.i == get.l) {
+                       return EOF;
+               }
+               theLookahead = EOF;
+               if (c == EOF) {
+                       c = input.charAt(get.i);
+                       ++get.i;
+               }
+               if (c >= ' ' || c == '\n') {
+                       return c;
+               }
+               if (c == '\r') {
+                       return '\n';
+               }
+               return ' ';
+       }
+
+       get.i = 0;
+       get.l = input.length;
+
+
+       /* peek -- get the next character without getting it.
+       */
+
+       function peek() {
+               theLookahead = get();
+               return theLookahead;
+       }
+
+
+       /* next -- get the next character, excluding comments. peek() is used to see
+                       if a '/' is followed by a '/' or '*'.
+       */
+
+       function next() {
+
+               var c = get();
+               if (c == '/') {
+                       switch (peek()) {
+                       case '/':
+                               for (;;) {
+                                       c = get();
+                                       if (c <= '\n') {
+                                               return c;
+                                       }
+                               }
+                               break;
+                       case '*':
+                               get();
+                               for (;;) {
+                                       switch (get()) {
+                                       case '*':
+                                               if (peek() == '/') {
+                                                       get();
+                                                       return ' ';
+                                               }
+                                               break;
+                                       case EOF:
+                                               throw 'Error: Unterminated comment.';
+                                       }
+                               }
+                               break;
+                       default:
+                               return c;
+                       }
+               }
+               return c;
+       }
+
+
+       /* action -- do something! What you do is determined by the argument:
+                       1   Output A. Copy B to A. Get the next B.
+                       2   Copy B to A. Get the next B. (Delete A).
+                       3   Get the next B. (Delete B).
+          action treats a string as a single character. Wow!
+          action recognizes a regular expression if it is preceded by ( or , or =.
+       */
+
+       function action(d) {
+
+               var r = [];
+
+               if (d == 1) {
+                       r.push(a);
+               }
+
+               if (d < 3) {
+                       a = b;
+                       if (a == '\'' || a == '"') {
+                               for (;;) {
+                                       r.push(a);
+                                       a = get();
+                                       if (a == b) {
+                                               break;
+                                       }
+                                       if (a <= '\n') {
+                                               throw 'Error: unterminated string literal: ' + a;
+                                       }
+                                       if (a == '\\') {
+                                               r.push(a);
+                                               a = get();
+                                       }
+                               }
+                       }
+               }
+
+               b = next();
+
+               if (b == '/' && '(,=:[!&|'.has(a)) {
+                       r.push(a);
+                       r.push(b);
+                       for (;;) {
+                               a = get();
+                               if (a == '/') {
+                                       break;
+                               } else if (a =='\\') {
+                                       r.push(a);
+                                       a = get();
+                               } else if (a <= '\n') {
+                                       throw 'Error: unterminated Regular Expression literal';
+                               }
+                               r.push(a);
+                       }
+                       b = next();
+               }
+
+               return r.join('');
+       }
+
+
+       /* m -- Copy the input to the output, deleting the characters which are
+                       insignificant to JavaScript. Comments will be removed. Tabs will be
+                       replaced with spaces. Carriage returns will be replaced with
+                       linefeeds.
+                       Most spaces and linefeeds will be removed.
+       */
+
+       function m() {
+
+               var r = [];
+               a = '\n';
+
+               r.push(action(3));
+
+               while (a != EOF) {
+                       switch (a) {
+                       case ' ':
+                               if (isAlphanum(b)) {
+                                       r.push(action(1));
+                               } else {
+                                       r.push(action(2));
+                               }
+                               break;
+                       case '\n':
+                               switch (b) {
+                               case '{':
+                               case '[':
+                               case '(':
+                               case '+':
+                               case '-':
+                                       r.push(action(1));
+                                       break;
+                               case ' ':
+                                       r.push(action(3));
+                                       break;
+                               default:
+                                       if (isAlphanum(b)) {
+                                               r.push(action(1));
+                                       } else {
+                                               if (level == 1 && b != '\n') {
+                                                       r.push(action(1));
+                                               } else {
+                                                       r.push(action(2));
+                                               }
+                                       }
+                               }
+                               break;
+                       default:
+                               switch (b) {
+                               case ' ':
+                                       if (isAlphanum(a)) {
+                                               r.push(action(1));
+                                               break;
+                                       }
+                                       r.push(action(3));
+                                       break;
+                               case '\n':
+                                       if (level == 1 && a != '\n') {
+                                               r.push(action(1));
+                                       } else {
+                                               switch (a) {
+                                               case '}':
+                                               case ']':
+                                               case ')':
+                                               case '+':
+                                               case '-':
+                                               case '"':
+                                               case '\'':
+                                                       if (level == 3) {
+                                                               r.push(action(3));
+                                                       } else {
+                                                               r.push(action(1));
+                                                       }
+                                                       break;
+                                               default:
+                                                       if (isAlphanum(a)) {
+                                                               r.push(action(1));
+                                                       } else {
+                                                               r.push(action(3));
+                                                       }
+                                               }
+                                       }
+                                       break;
+                               default:
+                                       r.push(action(1));
+                                       break;
+                               }
+                       }
+               }
+
+               return r.join('');
+       }
+
+       jsmin.oldSize = input.length;
+       var r = m(input);
+       jsmin.newSize = r.length;
+
+       return comment + r;
+
+}
diff --git a/release/build/js/json.js b/release/build/js/json.js
new file mode 100644 (file)
index 0000000..d59ca0c
--- /dev/null
@@ -0,0 +1,117 @@
+/*
+    json.js
+    2006-04-28
+
+    This file adds these methods to JavaScript:
+
+        object.toJSONString()
+
+            This method produces a JSON text from an object. The
+            object must not contain any cyclical references.
+
+        array.toJSONString()
+
+            This method produces a JSON text from an array. The
+            array must not contain any cyclical references.
+
+        string.parseJSON()
+
+            This method parses a JSON text to produce an object or
+            array. It will return false if there is an error.
+*/
+(function () {
+    var m = {
+            '\b': '\\b',
+            '\t': '\\t',
+            '\n': '\\n',
+            '\f': '\\f',
+            '\r': '\\r',
+            '"' : '\\"',
+            '\\': '\\\\'
+        },
+        s = {
+            array: function (x) {
+                var a = ['['], b, f, i, l = x.length, v;
+                for (i = 0; i < l; i += 1) {
+                    v = x[i];
+                    f = s[typeof v];
+                    if (f) {
+                        v = f(v);
+                        if (typeof v == 'string') {
+                            if (b) {
+                                a[a.length] = ',';
+                            }
+                            a[a.length] = v;
+                            b = true;
+                        }
+                    }
+                }
+                a[a.length] = ']';
+                return a.join('');
+            },
+            'boolean': function (x) {
+                return String(x);
+            },
+            'null': function (x) {
+                return "null";
+            },
+            number: function (x) {
+                return isFinite(x) ? String(x) : 'null';
+            },
+            object: function (x) {
+                if (x) {
+                    if (x instanceof Array) {
+                        return s.array(x);
+                    }
+                    var a = ['{'], b, f, i, v;
+                    for (i in x) {
+                        v = x[i];
+                        f = s[typeof v];
+                        if (f) {
+                            v = f(v);
+                            if (typeof v == 'string') {
+                                if (b) {
+                                    a[a.length] = ',';
+                                }
+                                a.push(s.string(i), ':', v);
+                                b = true;
+                            }
+                        }
+                    }
+                    a[a.length] = '}';
+                    return a.join('');
+                }
+                return 'null';
+            },
+            string: function (x) {
+                if (/["\\\x00-\x1f]/.test(x)) {
+                    x = x.replace(/([\x00-\x1f\\"])/g, function(a, b) {
+                        var c = m[b];
+                        if (c) {
+                            return c;
+                        }
+                        c = b.charCodeAt();
+                        return '\\u00' +
+                            Math.floor(c / 16).toString(16) +
+                            (c % 16).toString(16);
+                    });
+                }
+                return '"' + x + '"';
+            }
+        };
+
+    Object.toJSON = function(obj) {
+        return s.object(obj);
+    };
+})();
+
+String.prototype.parseJSON = function () {
+    try {
+        return !(/[^,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]/.test(
+                this.replace(/"(\\.|[^"\\])*"/g, ''))) &&
+            eval('(' + this + ')');
+    } catch (e) {
+        return false;
+    }
+};
+
diff --git a/release/build/js/pack.js b/release/build/js/pack.js
new file mode 100644 (file)
index 0000000..56bfdd1
--- /dev/null
@@ -0,0 +1,316 @@
+/*
+    packer, version 2.0.2 (2005-08-19)
+    Copyright 2004-2005, Dean Edwards
+    License: http://creativecommons.org/licenses/LGPL/2.1/
+*/
+
+function pack(_script, _encoding, _fastDecode, _specialChars) {
+    // constants
+    var $IGNORE = "$1";
+
+    // validate parameters
+    _script += "\n";
+    _encoding = Math.min(parseInt(_encoding), 95);
+
+    // apply all parsing routines
+    function _pack($script) {
+        var i, $parse;
+        for (i = 0; ($parse = _parsers[i]); i++) {
+            $script = $parse($script);
+        }
+        return $script;
+    };
+
+    // unpacking function - this is the boot strap function
+    //  data extracted from this packing routine is passed to
+    //  this function when decoded in the target
+    var _unpack = function($packed, $ascii, $count, $keywords, $encode, $decode) {
+        while ($count--)
+            if ($keywords[$count])
+                $packed = $packed.replace(new RegExp('\\b' + $encode($count) + '\\b', 'g'), $keywords[$count]);
+        return $packed;
+    };
+
+    // code-snippet inserted into the unpacker to speed up decoding
+    var _decode = function() {
+        // does the browser support String.replace where the
+        //  replacement value is a function?
+        if (!''.replace(/^/, String)) {
+            // decode all the values we need
+            while ($count--) $decode[$encode($count)] = $keywords[$count] || $encode($count);
+            // global replacement function
+            $keywords = [function($encoded){return $decode[$encoded]}];
+            // generic match
+            $encode = function(){return'\\w+'};
+            // reset the loop counter -  we are now doing a global replace
+            $count = 1;
+        }
+    };
+
+    // keep a list of parsing functions, they'll be executed all at once
+    var _parsers = [];
+    function _addParser($parser) {
+        _parsers[_parsers.length] = $parser;
+    };
+
+    // zero encoding - just removal of white space and comments
+    function _basicCompression($script) {
+        var $parser = new ParseMaster;
+        // make safe
+        $parser.escapeChar = "\\";
+        // protect strings
+        $parser.add(/'[^'\n\r]*'/, $IGNORE);
+        $parser.add(/"[^"\n\r]*"/, $IGNORE);
+        // remove comments
+        $parser.add(/\/\/[^\n\r]*[\n\r]/, " ");
+        $parser.add(/\/\*[^*]*\*+([^\/][^*]*\*+)*\//, " ");
+        // protect regular expressions
+        $parser.add(/\s+(\/[^\/\n\r\*][^\/\n\r]*\/g?i?)/, "$2"); // IGNORE
+        $parser.add(/[^\w\x24\/'"*)\?:]\/[^\/\n\r\*][^\/\n\r]*\/g?i?/, $IGNORE);
+        // remove: ;;; doSomething();
+        if (_specialChars) $parser.add(/;;;[^\n\r]+[\n\r]/);
+        // remove redundant semi-colons
+        $parser.add(/\(;;\)/, $IGNORE); // protect for (;;) loops
+        $parser.add(/;+\s*([};])/, "$2");
+        // apply the above
+        $script = $parser.exec($script);
+
+        // remove white-space
+        $parser.add(/(\b|\x24)\s+(\b|\x24)/, "$2 $3");
+        $parser.add(/([+\-])\s+([+\-])/, "$2 $3");
+        $parser.add(/\s+/, "");
+        // done
+        return $parser.exec($script);
+    };
+
+    function _encodeSpecialChars($script) {
+        var $parser = new ParseMaster;
+        // replace: $name -> n, $$name -> na
+        $parser.add(/((\x24+)([a-zA-Z$_]+))(\d*)/, function($match, $offset) {
+            var $length = $match[$offset + 2].length;
+            var $start = $length - Math.max($length - $match[$offset + 3].length, 0);
+            return $match[$offset + 1].substr($start, $length) + $match[$offset + 4];
+        });
+        // replace: _name -> _0, double-underscore (__name) is ignored
+        var $regexp = /\b_[A-Za-z\d]\w*/;
+        // build the word list
+        var $keywords = _analyze($script, _globalize($regexp), _encodePrivate);
+        // quick ref
+        var $encoded = $keywords.$encoded;
+        $parser.add($regexp, function($match, $offset) {
+            return $encoded[$match[$offset]];
+        });
+        return $parser.exec($script);
+    };
+
+    function _encodeKeywords($script) {
+        // escape high-ascii values already in the script (i.e. in strings)
+        if (_encoding > 62) $script = _escape95($script);
+        // create the parser
+        var $parser = new ParseMaster;
+        var $encode = _getEncoder(_encoding);
+        // for high-ascii, don't encode single character low-ascii
+        var $regexp = (_encoding > 62) ? /\w\w+/ : /\w+/;
+        // build the word list
+        $keywords = _analyze($script, _globalize($regexp), $encode);
+        var $encoded = $keywords.$encoded;
+        // encode
+        $parser.add($regexp, function($match, $offset) {
+        return $encoded[$match[$offset]];
+        });
+        // if encoded, wrap the script in a decoding function
+        return $script && _bootStrap($parser.exec($script), $keywords);
+    };
+
+    function _analyze($script, $regexp, $encode) {
+        // analyse
+        // retreive all words in the script
+        var $all = $script.match($regexp);
+        var $$sorted = []; // list of words sorted by frequency
+        var $$encoded = {}; // dictionary of word->encoding
+        var $$protected = {}; // instances of "protected" words
+        if ($all) {
+            var $unsorted = []; // same list, not sorted
+            var $protected = {}; // "protected" words (dictionary of word->"word")
+            var $values = {}; // dictionary of charCode->encoding (eg. 256->ff)
+            var $count = {}; // word->count
+            var i = $all.length, j = 0, $word;
+            // count the occurrences - used for sorting later
+            do {
+                $word = "$" + $all[--i];
+                if (!$count[$word]) {
+                    $count[$word] = 0;
+                    $unsorted[j] = $word;
+                    // make a dictionary of all of the protected words in this script
+                    //  these are words that might be mistaken for encoding
+                    $protected["$" + ($values[j] = $encode(j))] = j++;
+                }
+                // increment the word counter
+                $count[$word]++;
+            } while (i);
+            // prepare to sort the word list, first we must protect
+            //  words that are also used as codes. we assign them a code
+            //  equivalent to the word itself.
+            // e.g. if "do" falls within our encoding range
+            //      then we store keywords["do"] = "do";
+            // this avoids problems when decoding
+            i = $unsorted.length;
+            do {
+                $word = $unsorted[--i];
+                if ($protected[$word] != null) {
+                    $$sorted[$protected[$word]] = $word.slice(1);
+                    $$protected[$protected[$word]] = true;
+                    $count[$word] = 0;
+                }
+            } while (i);
+            // sort the words by frequency
+            $unsorted.sort(function($match1, $match2) {
+                return $count[$match2] - $count[$match1];
+            });
+            j = 0;
+            // because there are "protected" words in the list
+            //  we must add the sorted words around them
+            do {
+                if ($$sorted[i] == null) $$sorted[i] = $unsorted[j++].slice(1);
+                $$encoded[$$sorted[i]] = $values[i];
+            } while (++i < $unsorted.length);
+        }
+        return {$sorted: $$sorted, $encoded: $$encoded, $protected: $$protected};
+    };
+
+    // build the boot function used for loading and decoding
+    function _bootStrap($packed, $keywords) {
+        var $ENCODE = _safeRegExp("$encode\\($count\\)", "g");
+
+        // $packed: the packed script
+        $packed = "'" + _escape($packed) + "'";
+
+        // $ascii: base for encoding
+        var $ascii = Math.min($keywords.$sorted.length, _encoding) || 1;
+
+        // $count: number of words contained in the script
+        var $count = $keywords.$sorted.length;
+
+        // $keywords: list of words contained in the script
+        for (var i in $keywords.$protected) $keywords.$sorted[i] = "";
+        // convert from a string to an array
+        $keywords = "'" + $keywords.$sorted.join("|") + "'.split('|')";
+
+        // $encode: encoding function (used for decoding the script)
+        var $encode = _encoding > 62 ? _encode95 : _getEncoder($ascii);
+        $encode = String($encode).replace(/_encoding/g, "$ascii").replace(/arguments\.callee/g, "$encode");
+        var $inline = "$count" + ($ascii > 10 ? ".toString($ascii)" : "");
+
+        // $decode: code snippet to speed up decoding
+        if (_fastDecode) {
+            // create the decoder
+            var $decode = _getFunctionBody(_decode);
+            if (_encoding > 62) $decode = $decode.replace(/\\\\w/g, "[\\xa1-\\xff]");
+            // perform the encoding inline for lower ascii values
+            else if ($ascii < 36) $decode = $decode.replace($ENCODE, $inline);
+            // special case: when $count==0 there are no keywords. I want to keep
+            //  the basic shape of the unpacking funcion so i'll frig the code...
+            if (!$count) $decode = $decode.replace(_safeRegExp("($count)\\s*=\\s*1"), "$1=0");
+        }
+
+        // boot function
+        var $unpack = String(_unpack);
+        if (_fastDecode) {
+            // insert the decoder
+            $unpack = $unpack.replace(/\{/, "{" + $decode + ";");
+        }
+        $unpack = $unpack.replace(/"/g, "'");
+        if (_encoding > 62) { // high-ascii
+            // get rid of the word-boundaries for regexp matches
+            $unpack = $unpack.replace(/'\\\\b'\s*\+|\+\s*'\\\\b'/g, "");
+        }
+        if ($ascii > 36 || _encoding > 62 || _fastDecode) {
+            // insert the encode function
+            $unpack = $unpack.replace(/\{/, "{$encode=" + $encode + ";");
+        } else {
+            // perform the encoding inline
+            $unpack = $unpack.replace($ENCODE, $inline);
+        }
+        // pack the boot function too
+        $unpack = pack($unpack, 0, false, true);
+
+        // arguments
+        var $params = [$packed, $ascii, $count, $keywords];
+        if (_fastDecode) {
+            // insert placeholders for the decoder
+            $params = $params.concat(0, "{}");
+        }
+
+        // the whole thing
+        return "eval(" + $unpack + "(" + $params + "))\n";
+    };
+
+    // mmm.. ..which one do i need ??
+    function _getEncoder($ascii) {
+        return $ascii > 10 ? $ascii > 36 ? $ascii > 62 ? _encode95 : _encode62 : _encode36 : _encode10;
+    };
+
+    // zero encoding
+    // characters: 0123456789
+    var _encode10 = function($charCode) {
+        return $charCode;
+    };
+
+    // inherent base36 support
+    // characters: 0123456789abcdefghijklmnopqrstuvwxyz
+    var _encode36 = function($charCode) {
+        return $charCode.toString(36);
+    };
+
+    // hitch a ride on base36 and add the upper case alpha characters
+    // characters: 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
+    var _encode62 = function($charCode) {
+        return ($charCode < _encoding ? '' : arguments.callee(parseInt($charCode / _encoding))) +
+            (($charCode = $charCode % _encoding) > 35 ? String.fromCharCode($charCode + 29) : $charCode.toString(36));
+    };
+
+    // use high-ascii values
+    var _encode95 = function($charCode) {
+        return ($charCode < _encoding ? '' : arguments.callee($charCode / _encoding)) +
+            String.fromCharCode($charCode % _encoding + 161);
+    };
+
+    // special _chars
+    var _encodePrivate = function($charCode) {
+        return "_" + $charCode;
+    };
+
+    // protect characters used by the parser
+    function _escape($script) {
+        return $script.replace(/([\\'])/g, "\\$1");
+    };
+
+    // protect high-ascii characters already in the script
+    function _escape95($script) {
+        return $script.replace(/[\xa1-\xff]/g, function($match) {
+            return "\\x" + $match.charCodeAt(0).toString(16);
+        });
+    };
+
+    function _safeRegExp($string, $flags) {
+        return new RegExp($string.replace(/\$/g, "\\$"), $flags);
+    };
+
+    // extract the body of a function
+    function _getFunctionBody($function) {
+        with (String($function)) return slice(indexOf("{") + 1, lastIndexOf("}"));
+    };
+
+    // set the global flag on a RegExp (you have to create a new one)
+    function _globalize($regexp) {
+        return new RegExp(String($regexp).slice(1, -1), "g");
+    };
+
+    // build the parsing routine
+    _addParser(_basicCompression);
+    if (_specialChars) _addParser(_encodeSpecialChars);
+    if (_encoding) _addParser(_encodeKeywords);
+
+    // go!
+    return _pack(_script);
+};
diff --git a/release/build/js/pack.wsf b/release/build/js/pack.wsf
new file mode 100644 (file)
index 0000000..48325f6
--- /dev/null
@@ -0,0 +1,68 @@
+<job>
+
+<!-- Windows Scripting Host command line wrapper for Pack.js  -->
+<!-- Run as CScript //nologo infile > outfile                 -->
+
+<script type="text/javascript">
+function ICommon(that){if(that!=null){that.inherit=Common.prototype.inherit;that.specialize=Common.prototype.specialize}return that};ICommon.specialize=function(p,c){if(!p)p={};if(!c)c=p.constructor;if(c=={}.constructor)c=new Function("this.inherit()");c.valueOf=new Function("return this");c.valueOf.prototype=new this.valueOf;c.valueOf.prototype.specialize(p);c.prototype=new c.valueOf;c.valueOf.prototype.constructor=c.prototype.constructor=c;c.ancestor=this;c.specialize=arguments.callee;c.ancestorOf=this.ancestorOf;return c};ICommon.valueOf=new Function("return this");ICommon.valueOf.prototype={constructor:ICommon,inherit:function(){return arguments.callee.caller.ancestor.apply(this,arguments)},specialize:function(that){if(this==this.constructor.prototype&&this.constructor.specialize){return this.constructor.valueOf.prototype.specialize(that)}for(var i in that){switch(i){case"constructor":case"toString":case"valueOf":continue}if(typeof that[i]=="function"&&that[i]!=this[i]){that[i].ancestor=this[i]}this[i]=that[i]}if(that.toString!=this.toString&&that.toString!={}.toString){that.toString.ancestor=this.toString;this.toString=that.toString}return this}};function Common(){};this.Common=ICommon.specialize({constructor:Common,toString:function(){return"[common "+(this.constructor.className||"Object")+"]"},instanceOf:function(klass){return this.constructor==klass||klass.ancestorOf(this.constructor)}});Common.className="Common";Common.ancestor=null;Common.ancestorOf=function(klass){while(klass&&klass.ancestor!=this)klass=klass.ancestor;return Boolean(klass)};Common.valueOf.ancestor=ICommon;function ParseMaster(){var E=0,R=1,L=2;var G=/\(/g,S=/\$\d/,I=/^\$\d+$/,T=/(['"])\1\+(.*)\+\1\1$/,ES=/\\./g,Q=/'/,DE=/\x01[^\x01]*\x01/g;var self=this;this.add=function(e,r){if(!r)r="";var l=(_14(String(e)).match(G)||"").length+1;if(S.test(r)){if(I.test(r)){r=parseInt(r.slice(1))-1}else{var i=l;var q=Q.test(_14(r))?'"':"'";while(i)r=r.split("$"+i--).join(q+"+a[o+"+i+"]+"+q);r=new Function("a,o","return"+q+r.replace(T,"$1")+q)}}_31(e||"/^$/",r,l)};this.exec=function(s){_3.length=0;return _28(_5(s,this.escapeChar).replace(new RegExp(_1,this.ignoreCase?"gi":"g"),_29),this.escapeChar).replace(DE,"")};this.reset=function(){_1.length=0};var _3=[];var _1=[];var _30=function(){return"("+String(this[E]).slice(1,-1)+")"};_1.toString=function(){return this.join("|")};function _31(){arguments.toString=_30;_1[_1.length]=arguments}function _29(){if(!arguments[0])return"";var i=1,j=0,p;while(p=_1[j++]){if(arguments[i]){var r=p[R];switch(typeof r){case"function":return r(arguments,i);case"number":return arguments[r+i]}var d=(arguments[i].indexOf(self.escapeChar)==-1)?"":"\x01"+arguments[i]+"\x01";return d+r}else i+=p[L]}};function _5(s,e){return e?s.replace(new RegExp("\\"+e+"(.)","g"),function(m,c){_3[_3.length]=c;return e}):s};function _28(s,e){var i=0;return e?s.replace(new RegExp("\\"+e,"g"),function(){return e+(_3[i++]||"")}):s};function _14(s){return s.replace(ES,"")}};ParseMaster.prototype={constructor:ParseMaster,ignoreCase:false,escapeChar:""};function pack(_7,_0,_2,_8){var I="$1";_7+="\n";_0=Math.min(parseInt(_0),95);function _15(s){var i,p;for(i=0;(p=_6[i]);i++){s=p(s)}return s};var _25=function(p,a,c,k,e,d){while(c--)if(k[c])p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c]);return p};var _26=function(){if(!''.replace(/^/,String)){while(c--)d[e(c)]=k[c]||e(c);k=[function(e){return d[e]}];e=function(){return'\\w+'};c=1}};var _6=[];function _4(p){_6[_6.length]=p};function _18(s){var p=new ParseMaster;p.escapeChar="\\";p.add(/'[^'\n\r]*'/,I);p.add(/"[^"\n\r]*"/,I);p.add(/\/\/[^\n\r]*[\n\r]/," ");p.add(/\/\*[^*]*\*+([^\/][^*]*\*+)*\//," ");p.add(/\s+(\/[^\/\n\r\*][^\/\n\r]*\/g?i?)/,"$2");p.add(/[^\w\x24\/'"*)\?:]\/[^\/\n\r\*][^\/\n\r]*\/g?i?/,I);if(_8)p.add(/;;;[^\n\r]+[\n\r]/);p.add(/\(;;\)/,I);p.add(/;+\s*([};])/,"$2");s=p.exec(s);p.add(/(\b|\x24)\s+(\b|\x24)/,"$2 $3");p.add(/([+\-])\s+([+\-])/,"$2 $3");p.add(/\s+/,"");return p.exec(s)};function _17(s){var p=new ParseMaster;p.add(/((\x24+)([a-zA-Z_]+))(\d*)/,function(m,o){var l=m[o+2].length;var s=l-Math.max(l-m[o+3].length,0);return m[o+1].substr(s,l)+m[o+4]});var r=/\b_[A-Za-z\d]\w*/;var k=_13(s,_9(r),_21);var e=k.e;p.add(r,function(m,o){return e[m[o]]});return p.exec(s)};function _16(s){if(_0>62)s=_20(s);var p=new ParseMaster;var e=_12(_0);var r=(_0>62)?/\w\w+/ :/\w+/;k=_13(s,_9(r),e);var e=k.e;p.add(r,function(m,o){return e[m[o]]});return s&&_27(p.exec(s),k)};function _13(s,r,e){var a=s.match(r);var so=[];var en={};var pr={};if(a){var u=[];var p={};var v={};var c={};var i=a.length,j=0,w;do{w="$"+a[--i];if(!c[w]){c[w]=0;u[j]=w;p["$"+(v[j]=e(j))]=j++}c[w]++}while(i);i=u.length;do{w=u[--i];if(p[w]!=null){so[p[w]]=w.slice(1);pr[p[w]]=true;c[w]=0}}while(i);u.sort(function(m1,m2){return c[m2]-c[m1]});j=0;do{if(so[i]==null)so[i]=u[j++].slice(1);en[so[i]]=v[i]}while(++i<u.length)}return{s:so,e:en,p:pr}};function _27(p,k){var E=_10("e\\(c\\)","g");p="'"+_5(p)+"'";var a=Math.min(k.s.length,_0)||1;var c=k.s.length;for(var i in k.p)k.s[i]="";k="'"+k.s.join("|")+"'.split('|')";var e=_0>62?_11:_12(a);e=String(e).replace(/_0/g,"a").replace(/arguments\.callee/g,"e");var i="c"+(a>10?".toString(a)":"");if(_2){var d=_19(_26);if(_0>62)d=d.replace(/\\\\w/g,"[\\xa1-\\xff]");else if(a<36)d=d.replace(E,i);if(!c)d=d.replace(_10("(c)\\s*=\\s*1"),"$1=0")}var u=String(_25);if(_2){u=u.replace(/\{/,"{"+d+";")}u=u.replace(/"/g,"'");if(_0>62){u=u.replace(/'\\\\b'\s*\+|\+\s*'\\\\b'/g,"")}if(a>36||_0>62||_2){u=u.replace(/\{/,"{e="+e+";")}else{u=u.replace(E,i)}u=pack(u,0,false,true);var p=[p,a,c,k];if(_2){p=p.concat(0,"{}")}return"eval("+u+"("+p+"))\n"};function _12(a){return a>10?a>36?a>62?_11:_22:_23:_24};var _24=function(c){return c};var _23=function(c){return c.toString(36)};var _22=function(c){return(c<_0?'':arguments.callee(parseInt(c/_0)))+((c=c%_0)>35?String.fromCharCode(c+29):c.toString(36))};var _11=function(c){return(c<_0?'':arguments.callee(c/_0))+String.fromCharCode(c%_0+161)};var _21=function(c){return"_"+c};function _5(s){return s.replace(/([\\'])/g,"\\$1")};function _20(s){return s.replace(/[\xa1-\xff]/g,function(m){return"\\x"+m.charCodeAt(0).toString(16)})};function _10(s,f){return new RegExp(s.replace(/\$/g,"\\$"),f)};function _19(f){with(String(f))return slice(indexOf("{")+1,lastIndexOf("}"))};function _9(r){return new RegExp(String(r).slice(1,-1),"g")};_4(_18);if(_8)_4(_17);if(_0)_4(_16);return _15(_7)};
+</script>
+<script type="text/javascript">
+
+var VERSION = '1.0.0';
+var AUTHOR  = 'Rob Seiler'; /* seiler@elr.com.au */
+
+/* Get command line arguments */
+function JS_getArgs() {
+  var args = [];
+  var objArgs = WScript.Arguments;
+  if (objArgs.length > 0) {
+    for (var i = 0; i < objArgs.length; i++) {
+      args[i] = objArgs(i); /* sic - index in "()" - an object, not an array! */
+    }
+  }
+  return (args);
+}
+
+/* Read the input file */
+function JS_readFile (fname) {
+  var s = '';
+  var ForReading = 1;
+  var fso = new ActiveXObject("Scripting.FileSystemObject");
+  var ts = fso.OpenTextFile(fname, ForReading);
+  while (!ts.AtEndOfStream) {
+    s += ts.ReadLine() + '\n';
+  }
+  ts.Close();
+  return(s);
+}
+
+/* Show help if needed - eg 0 command line arguments */
+function JS_Help () {
+  WScript.Echo ('Compress and encode a Javascript source file using Dean Edwards "Packer"');
+  WScript.Echo ('  Version : ' + VERSION);
+  WScript.Echo ('  Syntax  : program sourcefile [_encoding] [_fastDecode] [_specialChars]\n');
+ }
+
+/* Main program: Get arguments; read input file; output packed string */
+function main() {
+  var params = [];
+  params = JS_getArgs();
+  params[1] = (typeof(params[1]) == 'undefined') ? 62 : params[1]; // -dean : changed defaults
+  params[2] = (typeof(params[2]) == 'undefined') ? 1  : params[2];
+  params[3] = (typeof(params[3]) == 'undefined') ? 0  : params[3];
+  if (params[0] > '') {
+    var $script = JS_readFile(params[0]);
+    if ($script > '') {
+      $script = pack($script, params[1], params[2], params[3]); /* Returns the Dean Edwards "packed" string */
+      WScript.Echo ($script);
+    }
+    else {JS_Help();}
+  }
+  else {JS_Help();}
+}
+
+/* Do the job */
+  main();
+</script>
+
+</job>
diff --git a/release/build/js/parse.js b/release/build/js/parse.js
new file mode 100644 (file)
index 0000000..211fe6c
--- /dev/null
@@ -0,0 +1,105 @@
+function parse( f ) {
+       var c = [], bm, m;
+       var blockMatch = /\/\*\*\s*((.|\n)*?)\s*\*\//g;
+       var paramMatch = /\@(\S+) *((.|\n)*?)(?=\n\@|!!!)/m;
+       
+       while ( bm = blockMatch.exec(f) ) {
+               block = bm[1].replace(/^\s*\* ?/mg,"") + "!!!";
+               var ret = { params: [], examples: [], tests: [], options: [] };
+       
+               while ( m = paramMatch.exec( block ) ) {
+                       block = block.replace( paramMatch, "" );
+
+                       var n = m[1];
+                       var v = m[2]
+                               .replace(/\s*$/g,"")
+                               .replace(/^\s*/g,"")
+                               .replace(/&/g, "&amp;")
+                               .replace(/</g, "&lt;")
+                               .replace(/>/g, "&gt;")
+                               //.replace(/\n/g, "<br/>")
+                               /*.replace(/(\s\s+)/g, function(a){
+                                       var ret = "";
+                                       for ( var i = 0; i < a.length; i++ )
+                                               ret += "&nbsp;";
+                                       return ret;
+                               })*/ || 1;
+       
+                       if ( n == 'param' || n == 'option' ) {
+                               var args = v.split(/\s+/);
+                               v = args.slice( 2, args.length );
+                               v = { type: args[0], name: args[1], desc: v.join(' ') };
+                               n = n + "s";
+                       } else if ( n == 'example' ) {
+                               v = { code: v };
+                               n = "examples";
+                       } else if ( n == 'test' ) {
+                               n = "tests";
+                       }
+       
+                       if ( n == 'desc' || n == 'before' || n == 'after' || n == 'result' ) {
+                               ret.examples[ ret.examples.length - 1 ][ n ] = v;
+                       } else {
+                               if ( ret[ n ] ) {
+                                       if ( ret[ n ].constructor == Array ) {
+                                               ret[ n ].push( v );
+                                       } else {
+                                               ret[ n ] = [ ret[ n ], v ];
+                                       }
+                               } else {
+                                       ret[ n ] = v;
+                               }
+                       }
+               }
+       
+               ret.desc = block.replace(/\s*!!!$/,"")
+                               .replace(/</g, "&lt;")
+                               .replace(/>/g, "&gt;");
+                               //.replace(/\n\n/g, "<br/><br/>")
+                               //.replace(/\n/g, " ");
+       
+               var m = /^((.|\n)*?(\.|$))/.exec( ret.desc );
+               if ( m ) ret['short'] = m[1];
+       
+               if ( ret.name ) c.push( ret );
+       }
+
+       return c;
+}
+
+function categorize( json ) {
+       var obj = { cat: [], method: [] };
+
+       for ( var i = 0; i < json.length; i++ ) {
+               if ( !json[i].cat ) json[i].cat = "";
+
+               var cat = json[i].cat.split("/");
+
+               var pos = obj;
+               for ( var j = 0; j < cat.length; j++ ) {
+                       var c = cat[j];
+                       var curCat = null;
+
+                       // Locate current category
+                       for ( var n = 0; n < pos.cat.length; n++ )
+                               if ( pos.cat[n].value == c )
+                                       curCat = pos.cat[n];
+
+                       // Create current category
+                       if ( !curCat ) {
+                               curCat = { value: c, cat: [], method: [] };
+                               pos.cat.push( curCat )
+                       }
+
+                       // If we're at  the end, add the method
+                       if ( j == cat.length - 1 )
+                               curCat.method.push( json[i] );
+
+                       // Otherwise, traverse deeper
+                       else
+                               pos = curCat;
+               }
+       }
+
+       return obj;
+}
diff --git a/release/build/js/writeFile.js b/release/build/js/writeFile.js
new file mode 100644 (file)
index 0000000..43b1eb2
--- /dev/null
@@ -0,0 +1,19 @@
+importPackage(java.io);
+
+function writeFile( file, stream ) {
+       var buffer = new PrintWriter( new FileWriter( file ) );
+       buffer.print( stream );
+       buffer.close();
+}
+
+function read( file ) {
+       var jq = new File(file);
+       var reader = new BufferedReader(new FileReader(jq));
+       var line = null;
+       var buffer = new java.lang.StringBuffer(jq.length());
+       while( (line = reader.readLine()) != null) {
+               buffer.append(line);
+               buffer.append("\n");
+       }
+       return buffer.toString();
+}
\ No newline at end of file
diff --git a/release/build/js/xml.js b/release/build/js/xml.js
new file mode 100644 (file)
index 0000000..1d50558
--- /dev/null
@@ -0,0 +1,25 @@
+Object.toXML = function( obj, tag ) {
+  if ( obj.constructor == Array ) {
+    var ret = "";
+    for ( var i = 0; i < obj.length; i++ )
+      ret += Object.toXML( obj[i], tag );
+    return ret;
+  } else if ( obj.constructor == Object ) {
+    var tag = tag || "tmp";
+    var p = "", child = "";
+
+    for ( var i in obj )
+      if ( ( obj[i].constructor != String && obj[i].constructor != Number ) || /</.test(obj[i] + "") || Object.toXML.force[i] )
+        child += Object.toXML( obj[i], i );
+      else
+        p += " " + i + "='" + (obj[i] + "").replace(/'/g, "&apos;") + "'";
+
+    return "<" + tag + p + ( child ?  ">\n" + child + "</" + tag + ">\n" : "/>\n" );
+  } else if ( obj.constructor == String || obj.constructor == Number ) {
+    return "<" + tag + ">" + obj + "</" + tag + ">\n";
+  }
+
+  return "";
+};
+
+Object.toXML.force = {};
diff --git a/release/build/yuicompressor.jar b/release/build/yuicompressor.jar
new file mode 100644 (file)
index 0000000..0e363e9
Binary files /dev/null and b/release/build/yuicompressor.jar differ