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 + "\n" : "/>\n" ); + } else if ( obj.constructor == String || obj.constructor == Number ) { + return "<" + tag + ">" + obj + "\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