diff --git a/release/build.xml b/release/build.xml
new file mode 100644
index 000000000..d655a23da
--- /dev/null
+++ b/release/build.xml
@@ -0,0 +1,144 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/release/build/build/min.js b/release/build/build/min.js
new file mode 100644
index 000000000..5707d4afb
--- /dev/null
+++ b/release/build/build/min.js
@@ -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
index 000000000..f38739201
--- /dev/null
+++ b/release/build/build/pack.js
@@ -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
index 000000000..a76cc7c6b
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
index 000000000..03af07be3
--- /dev/null
+++ b/release/build/js/Packer.js
@@ -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(c35?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
index 000000000..915a8b5db
--- /dev/null
+++ b/release/build/js/ParseMaster.js
@@ -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
index 000000000..cbd9cab45
--- /dev/null
+++ b/release/build/js/Words.js
@@ -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
index 000000000..08f091404
--- /dev/null
+++ b/release/build/js/base2.js
@@ -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
index 000000000..3e1ff0708
--- /dev/null
+++ b/release/build/js/jsmin.js
@@ -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
index 000000000..d59ca0c97
--- /dev/null
+++ b/release/build/js/json.js
@@ -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
index 000000000..56bfdd1a2
--- /dev/null
+++ b/release/build/js/pack.js
@@ -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
index 000000000..48325f636
--- /dev/null
+++ b/release/build/js/pack.wsf
@@ -0,0 +1,68 @@
+
+
+
+
+
+
+
+
+
diff --git a/release/build/js/parse.js b/release/build/js/parse.js
new file mode 100644
index 000000000..211fe6ce0
--- /dev/null
+++ b/release/build/js/parse.js
@@ -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, "&")
+ .replace(//g, ">")
+ //.replace(/\n/g, "
")
+ /*.replace(/(\s\s+)/g, function(a){
+ var ret = "";
+ for ( var i = 0; i < a.length; i++ )
+ ret += " ";
+ 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, ">");
+ //.replace(/\n\n/g, "
")
+ //.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
index 000000000..43b1eb246
--- /dev/null
+++ b/release/build/js/writeFile.js
@@ -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
index 000000000..1d50558e1
--- /dev/null
+++ b/release/build/js/xml.js
@@ -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 ) || /\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
index 000000000..0e363e95e
Binary files /dev/null and b/release/build/yuicompressor.jar differ