/*
base2 - copyright 2007-2011, Dean Edwards
http://code.google.com/p/base2/
http://www.opensource.org/licenses/mit-license.php

Contributors:
Doeke Zanstra
*/

var base2 = {
    name: "base2",
    version: "1.0.2",
    exports:
    "Base,Package,Abstract,Module,Enumerable,Map,Collection,Array2,RegGrp," +
    "Undefined,Null,This,True,False,assignID,detect,global",
    namespace: ""
};

new function(_no_shrink_) { ///////////////  BEGIN: CLOSURE  ///////////////

    // =========================================================================
    // base2/header.js
    // =========================================================================

    var Undefined = K(), Null = K(null), True = K(true), False = K(false), This = function() { return this };

    var global = This();
    var base2 = global.base2;

    // private
    var _FORMAT = /%([1-9])/g;
    var _LTRIM = /^\s\s*/;
    var _RTRIM = /\s\s*$/;
    var _RESCAPE = /([\/()[\]{}|*+-.,^$?\\])/g;             // safe regular expressions
    var _BASE = /try/.test(detect) ? /\bbase\b/ : /.*/;     // some platforms don't allow decompilation
    var _HIDDEN = ["constructor", "toString", "valueOf"];   // only override these when prototyping
    var _MSIE_NATIVE_FUNCTION = detect("(jscript)") ?
  new RegExp("^" + rescape(isNaN).replace(/isNaN/, "\\w+") + "$") : { test: False };

    var _counter = 1;
    var _slice = Array.prototype.slice;

    _Function_forEach(); // make sure this is initialised

    function assignID(object) {
        // Assign a unique ID to an object.
        if (!object.base2ID) object.base2ID = "b2_" + _counter++;
        return object.base2ID;
    };

    // =========================================================================
    // base2/Base.js
    // =========================================================================

    // http://dean.edwards.name/weblog/2006/03/base/

    var _subclass = function(_instance, _static) {
        // Build the prototype.
        base2.__prototyping = this.prototype;
        var _prototype = new this;
        if (_instance) extend(_prototype, _instance);
        delete base2.__prototyping;

        // Create the wrapper for the constructor function.
        var _constructor = _prototype.constructor;
        function _class() {
            // Don't call the constructor function when prototyping.
            if (!base2.__prototyping) {
                if (this.constructor == arguments.callee || this.__constructing) {
                    // Instantiation.
                    this.__constructing = true;
                    _constructor.apply(this, arguments);
                    delete this.__constructing;
                } else {
                    // Casting.
                    return extend(arguments[0], _prototype);
                }
            }
            return this;
        };
        _prototype.constructor = _class;

        // Build the static interface.
        for (var i in Base) _class[i] = this[i];
        _class.ancestor = this;
        _class.base = Undefined;
        //_class.init = Undefined;
        if (_static) extend(_class, _static);
        _class.prototype = _prototype;
        if (_class.init) _class.init();

        // introspection (removed when packed)
        ; ; ; _class["#implements"] = [];
        ; ; ; _class["#implemented_by"] = [];

        return _class;
    };

    var Base = _subclass.call(Object, {
        constructor: function() {
            if (arguments.length > 0) {
                this.extend(arguments[0]);
            }
        },

        base: function() {
            // Call this method from any other method to invoke the current method's ancestor (super).
        },

        extend: delegate(extend)
    }, Base = {
        ancestorOf: function(klass) {
            return _ancestorOf(this, klass);
        },

        extend: _subclass,

        forEach: function(object, block, context) {
            _Function_forEach(this, object, block, context);
        },

        implement: function(source) {
            if (typeof source == "function") {
                ; ; ; if (_ancestorOf(Base, source)) {
                    // introspection (removed when packed)
                    ; ; ; this["#implements"].push(source);
                    ; ; ; source["#implemented_by"].push(this);
                    ; ; ;
                }
                source = source.prototype;
            }
            // Add the interface using the extend() function.
            extend(this.prototype, source);
            return this;
        }
    });

    // =========================================================================
    // base2/Package.js
    // =========================================================================

    var Package = Base.extend({
        constructor: function(_private, _public) {
            this.extend(_public);
            if (this.init) this.init();

            if (this.name && this.name != "base2") {
                if (!this.parent) this.parent = base2;
                this.parent.addName(this.name, this);
                this.namespace = format("var %1=%2;", this.name, String2.slice(this, 1, -1));
            }

            if (_private) {
                // This next line gets round a bug in old Mozilla browsers
                var JSNamespace = base2.JavaScript ? base2.JavaScript.namespace : "";
                // This string should be evaluated immediately after creating a Package object.
                _private.imports = Array2.reduce(csv(this.imports), function(namespace, name) {
                    var ns = lookup(name) || lookup("JavaScript." + name);
                    ; ; ; assert(ns, format("Object not found: '%1'.", name), ReferenceError);
                    return namespace += ns.namespace;
                }, "var base2=(function(){return this.base2})();" + base2.namespace + JSNamespace) + lang.namespace;

                // This string should be evaluated after you have created all of the objects
                // that are being exported.
                _private.exports = Array2.reduce(csv(this.exports), function(namespace, name) {
                    var fullName = this.name + "." + name;
                    this.namespace += "var " + name + "=" + fullName + ";";
                    return namespace += "if(!" + fullName + ")" + fullName + "=" + name + ";";
                }, "", this) + "this._label_" + this.name + "();";

                var pkg = this;
                var packageName = String2.slice(this, 1, -1);
                _private["_label_" + this.name] = function() {
                    Package.forEach(pkg, function(object, name) {
                        if (object && object.ancestorOf == Base.ancestorOf) {
                            object.toString = K(format("[%1.%2]", packageName, name));
                            if (object.prototype.toString == Base.prototype.toString) {
                                object.prototype.toString = K(format("[object %1.%2]", packageName, name));
                            }
                        }
                    });
                };
            }

            function lookup(names) {
                names = names.split(".");
                var value = base2, i = 0;
                while (value && names[i] != null) {
                    value = value[names[i++]];
                }
                return value;
            };
        },

        exports: "",
        imports: "",
        name: "",
        namespace: "",
        parent: null,

        addName: function(name, value) {
            if (!this[name]) {
                this[name] = value;
                this.exports += "," + name;
                this.namespace += format("var %1=%2.%1;", name, this.name);
            }
        },

        addPackage: function(name) {
            this.addName(name, new Package(null, { name: name, parent: this }));
        },

        toString: function() {
            return format("[%1]", this.parent ? String2.slice(this.parent, 1, -1) + "." + this.name : this.name);
        }
    });

    // =========================================================================
    // base2/Abstract.js
    // =========================================================================

    var Abstract = Base.extend({
        constructor: function() {
            throw new TypeError("Abstract class cannot be instantiated.");
        }
    });

    // =========================================================================
    // base2/Module.js
    // =========================================================================

    var _moduleCount = 0;

    var Module = Abstract.extend(null, {
        namespace: "",

        extend: function(_interface, _static) {
            // Extend a module to create a new module.
            var module = this.base();
            var index = _moduleCount++;
            module.namespace = "";
            module.partial = this.partial;
            module.toString = K("[base2.Module[" + index + "]]");
            Module[index] = module;
            // Inherit class methods.
            module.implement(this);
            // Implement module (instance AND static) methods.
            if (_interface) module.implement(_interface);
            // Implement static properties and methods.
            if (_static) {
                extend(module, _static);
                if (module.init) module.init();
            }
            return module;
        },

        forEach: function(block, context) {
            _Function_forEach(Module, this.prototype, function(method, name) {
                if (typeOf(method) == "function") {
                    block.call(context, this[name], name, this);
                }
            }, this);
        },

        implement: function(_interface) {
            var module = this;
            var id = module.toString().slice(1, -1);
            if (typeof _interface == "function") {
                if (!_ancestorOf(_interface, module)) {
                    this.base(_interface);
                }
                if (_ancestorOf(Module, _interface)) {
                    // Implement static methods.
                    for (var name in _interface) {
                        if (module[name] === undefined) {
                            var property = _interface[name];
                            if (typeof property == "function" && property.call && _interface.prototype[name]) {
                                property = _staticModuleMethod(_interface, name);
                            }
                            module[name] = property;
                        }
                    }
                    module.namespace += _interface.namespace.replace(/base2\.Module\[\d+\]/g, id);
                }
            } else {
                // Add static interface.
                extend(module, _interface);
                // Add instance interface.
                _extendModule(module, _interface);
            }
            return module;
        },

        partial: function() {
            var module = Module.extend();
            var id = module.toString().slice(1, -1);
            // partial methods are already bound so remove the binding to speed things up
            module.namespace = this.namespace.replace(/(\w+)=b[^\)]+\)/g, "$1=" + id + ".$1");
            this.forEach(function(method, name) {
                module[name] = partial(bind(method, module));
            });
            return module;
        }
    });

    function _extendModule(module, _interface) {
        var proto = module.prototype;
        var id = module.toString().slice(1, -1);
        for (var name in _interface) {
            var property = _interface[name], namespace = "";
            if (name.charAt(0) == "@") { // object detection
                if (detect(name.slice(1))) _extendModule(module, property);
            } else if (!proto[name]) {
                if (name == name.toUpperCase()) {
                    namespace = "var " + name + "=" + id + "." + name + ";";
                } else if (typeof property == "function" && property.call) {
                    namespace = "var " + name + "=base2.lang.bind('" + name + "'," + id + ");";
                    proto[name] = _moduleMethod(module, name);
                    ; ; ; proto[name]._module = module; // introspection
                }
                if (module.namespace.indexOf(namespace) == -1) {
                    module.namespace += namespace;
                }
            }
        }
    };

    function _staticModuleMethod(module, name) {
        return function() {
            return module[name].apply(module, arguments);
        };
    };

    function _moduleMethod(module, name) {
        return function() {
            var args = _slice.call(arguments);
            args.unshift(this);
            return module[name].apply(module, args);
        };
    };

    // =========================================================================
    // base2/Enumerable.js
    // =========================================================================

    var Enumerable = Module.extend({
        every: function(object, test, context) {
            var result = true;
            try {
                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) {
            var i = 0;
            return this.reduce(object, function(result, value, key) {
                if (test.call(context, value, key, object)) {
                    result[i++] = value;
                }
                return result;
            }, []);
        },

        invoke: function(object, method) {
            // Apply a method to each item in the enumerated object.
            var args = _slice.call(arguments, 2);
            return this.map(object, (typeof method == "function") ? function(item) {
                return item == null ? undefined : method.apply(item, args);
            } : function(item) {
                return item == null ? undefined : item[method].apply(item, args);
            });
        },

        map: function(object, block, context) {
            var result = [], i = 0;
            forEach(object, function(value, key) {
                result[i++] = block.call(context, value, key, object);
            });
            return result;
        },

        pluck: function(object, key) {
            return this.map(object, function(item) {
                return item == null ? undefined : item[key];
            });
        },

        reduce: function(object, block, result, context) {
            var initialised = arguments.length > 2;
            forEach(object, function(value, key) {
                if (initialised) {
                    result = block.call(context, result, value, key, object);
                } else {
                    result = value;
                    initialised = true;
                }
            });
            return result;
        },

        some: function(object, test, context) {
            return !this.every(object, not(test), context);
        }
    });

    // =========================================================================
    // base2/Map.js
    // =========================================================================

    // http://wiki.ecmascript.org/doku.php?id=proposals:dictionary

    var _HASH = "#";

    var Map = Base.extend({
        constructor: function(values) {
            if (values) this.merge(values);
        },

        clear: function() {
            for (var key in this) if (key.indexOf(_HASH) == 0) {
                delete this[key];
            }
        },

        copy: function() {
            base2.__prototyping = true; // not really prototyping but it stops [[construct]] being called
            var copy = new this.constructor;
            delete base2.__prototyping;
            for (var i in this) if (this[i] !== copy[i]) {
                copy[i] = this[i];
            }
            return copy;
        },

        forEach: function(block, context) {
            for (var key in this) if (key.indexOf(_HASH) == 0) {
                block.call(context, this[key], key.slice(1), this);
            }
        },

        get: function(key) {
            return this[_HASH + key];
        },

        getKeys: function() {
            return this.map(II);
        },

        getValues: function() {
            return this.map(I);
        },

        // Ancient browsers throw an error if we use "in" as an operator.
        has: function(key) {
            /*@cc_on@*/
            /*@if (@_jscript_version < 5.5)
    return $Legacy.has(this, _HASH + key);
  @else
            @*/
            return _HASH + key in this;
            /*@end@*/
        },

        merge: function(values) {
            var put = flip(this.put);
            forEach(arguments, function(values) {
                forEach(values, put, this);
            }, this);
            return this;
        },

        put: function(key, value) {
            // create the new entry (or overwrite the old entry).
            this[_HASH + key] = value;
        },

        remove: function(key) {
            delete this[_HASH + key];
        },

        size: function() {
            // this is expensive because we are not storing the keys
            var size = 0;
            for (var key in this) if (key.indexOf(_HASH) == 0) size++;
            return size;
        },

        union: function(values) {
            return this.merge.apply(this.copy(), arguments);
        }
    });

    Map.implement(Enumerable);

    Map.prototype.filter = function(test, context) {
        return this.reduce(function(result, value, key) {
            if (!test.call(context, value, key, this)) {
                result.remove(key);
            }
            return result;
        }, this.copy(), this);
    };

    // =========================================================================
    // base2/Collection.js
    // =========================================================================

    // A Map 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.

    // The static create() method is responsible for all construction of collection items.
    // Instance methods that add new items (add, put, insertAt, putAt) 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 _KEYS = "~";

    var Collection = Map.extend({
        constructor: function(values) {
            this[_KEYS] = new Array2;
            this.base(values);
        },

        add: function(key, item) {
            // Duplicates not allowed using add().
            // But you can still overwrite entries using put().
            assert(!this.has(key), "Duplicate key '" + key + "'.");
            this.put.apply(this, arguments);
        },

        clear: function() {
            this.base();
            this[_KEYS].length = 0;
        },

        copy: function() {
            var copy = this.base();
            copy[_KEYS] = this[_KEYS].copy();
            return copy;
        },

        forEach: function(block, context) {
            var keys = this[_KEYS];
            var length = keys.length;
            for (var i = 0; i < length; i++) {
                block.call(context, this[_HASH + keys[i]], keys[i], this);
            }
        },

        getAt: function(index) {
            var key = this[_KEYS].item(index);
            return (key === undefined) ? undefined : this[_HASH + key];
        },

        getKeys: function() {
            return this[_KEYS].copy();
        },

        indexOf: function(key) {
            return this[_KEYS].indexOf(String(key));
        },

        insertAt: function(index, key, item) {
            assert(this[_KEYS].item(index) !== undefined, "Index out of bounds.");
            assert(!this.has(key), "Duplicate key '" + key + "'.");
            this[_KEYS].insertAt(index, String(key));
            this[_HASH + key] = null; // placeholder
            this.put.apply(this, _slice.call(arguments, 1));
        },

        item: function(keyOrIndex) {
            return this[typeof keyOrIndex == "number" ? "getAt" : "get"](keyOrIndex);
        },

        put: function(key, item) {
            if (!this.has(key)) {
                this[_KEYS].push(String(key));
            }
            var klass = this.constructor;
            if (klass.Item && !instanceOf(item, klass.Item)) {
                item = klass.create.apply(klass, arguments);
            }
            this[_HASH + key] = item;
        },

        putAt: function(index, item) {
            arguments[0] = this[_KEYS].item(index);
            assert(arguments[0] !== undefined, "Index out of bounds.");
            this.put.apply(this, arguments);
        },

        remove: function(key) {
            // The remove() method of the Array object can be slow so check if the key exists first.
            if (this.has(key)) {
                this[_KEYS].remove(String(key));
                delete this[_HASH + key];
            }
        },

        removeAt: function(index) {
            var key = this[_KEYS].item(index);
            if (key !== undefined) {
                this[_KEYS].removeAt(index);
                delete this[_HASH + key];
            }
        },

        reverse: function() {
            this[_KEYS].reverse();
            return this;
        },

        size: function() {
            return this[_KEYS].length;
        },

        slice: function(start, end) {
            var sliced = this.copy();
            if (arguments.length > 0) {
                var keys = this[_KEYS], removed = keys;
                sliced[_KEYS] = Array2(_slice.apply(keys, arguments));
                if (sliced[_KEYS].length) {
                    removed = removed.slice(0, start);
                    if (arguments.length > 1) {
                        removed = removed.concat(keys.slice(end));
                    }
                }
                for (var i = 0; i < removed.length; i++) {
                    delete sliced[_HASH + removed[i]];
                }
            }
            return sliced;
        },

        sort: function(compare) { // optimised (refers to _HASH)
            if (compare) {
                this[_KEYS].sort(bind(function(key1, key2) {
                    return compare(this[_HASH + key1], this[_HASH + key2], key1, key2);
                }, this));
            } else this[_KEYS].sort();
            return this;
        },

        toString: function() {
            return "(" + (this[_KEYS] || "") + ")";
        }
    }, {
        Item: null, // If specified, all members of the collection must be instances of Item.

        create: function(key, item) {
            return this.Item ? new this.Item(key, item) : item;
        },

        extend: function(_instance, _static) {
            var klass = this.base(_instance);
            klass.create = this.create;
            if (_static) 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 (klass.init) klass.init();
            return klass;
        }
    });

    // =========================================================================
    // base2/RegGrp.js
    // =========================================================================

    // A collection of regular expressions and their associated replacement values.
    // A Base class for creating parsers.

    var _RG_BACK_REF = /\\(\d+)/g,
    _RG_ESCAPE_CHARS = /\\./g,
    _RG_ESCAPE_BRACKETS = /\(\?[:=!]|\[[^\]]+\]/g,
    _RG_BRACKETS = /\(/g,
    _RG_LOOKUP = /\$(\d+)/,
    _RG_LOOKUP_SIMPLE = /^\$\d+$/;

    var RegGrp = Collection.extend({
        constructor: function(values, ignoreCase) {
            this.base(values);
            this.ignoreCase = !!ignoreCase;
        },

        ignoreCase: false,

        exec: function(string, override) { // optimised (refers to _HASH/_KEYS)
            string += ""; // type-safe
            var items = this, keys = this[_KEYS];
            if (!keys.length) return string;
            if (override == RegGrp.IGNORE) override = 0;
            return string.replace(new RegExp(this, this.ignoreCase ? "gi" : "g"), function(match) {
                var item, offset = 1, i = 0;
                // Loop through the RegGrp items.
                while ((item = items[_HASH + keys[i++]])) {
                    var next = offset + item.length + 1;
                    if (arguments[offset]) { // do we have a result?
                        var replacement = override == null ? item.replacement : override;
                        switch (typeof replacement) {
                            case "function":
                                return replacement.apply(items, _slice.call(arguments, offset, next));
                            case "number":
                                return arguments[offset + replacement];
                            default:
                                return replacement;
                        }
                    }
                    offset = next;
                }
                return match;
            });
        },

        insertAt: function(index, expression, replacement) {
            if (instanceOf(expression, RegExp)) {
                arguments[1] = expression.source;
            }
            return base(this, arguments);
        },

        test: function(string) {
            // The slow way to do it. Hopefully, this isn't called too often. :-)
            return this.exec(string) != string;
        },

        toString: function() {
            var offset = 1;
            return "(" + this.map(function(item) {
                // Fix back references.
                var expression = (item + "").replace(_RG_BACK_REF, function(match, index) {
                    return "\\" + (offset + Number(index));
                });
                offset += item.length + 1;
                return expression;
            }).join(")|(") + ")";
        }
    }, {
        IGNORE: "$0",

        init: function() {
            forEach("add,get,has,put,remove".split(","), function(name) {
                _override(this, name, function(expression) {
                    if (instanceOf(expression, RegExp)) {
                        arguments[0] = expression.source;
                    }
                    return base(this, arguments);
                });
            }, this.prototype);
        },

        Item: {
            constructor: function(expression, replacement) {
                if (replacement == null) replacement = RegGrp.IGNORE;
                else if (replacement.replacement != null) replacement = replacement.replacement;
                else if (typeof replacement != "function") replacement = String(replacement);

                // does the pattern use sub-expressions?
                if (typeof replacement == "string" && _RG_LOOKUP.test(replacement)) {
                    // a simple lookup? (e.g. "$2")
                    if (_RG_LOOKUP_SIMPLE.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
                        // Improved version by Alexei Gorkov:
                        var Q = '"';
                        replacement = replacement
            .replace(/\\/g, "\\\\")
            .replace(/"/g, "\\x22")
            .replace(/\n/g, "\\n")
            .replace(/\r/g, "\\r")
            .replace(/\$(\d+)/g, Q + "+(arguments[$1]||" + Q + Q + ")+" + Q)
            .replace(/(['"])\1\+(.*)\+\1\1$/, "$1");
                        replacement = new Function("return " + Q + replacement + Q);
                    }
                }

                this.length = RegGrp.count(expression);
                this.replacement = replacement;
                this.toString = K(expression + "");
            },

            length: 0,
            replacement: ""
        },

        count: function(expression) {
            // Count the number of sub-expressions in a RegExp/RegGrp.Item.
            expression = (expression + "").replace(_RG_ESCAPE_CHARS, "").replace(_RG_ESCAPE_BRACKETS, "");
            return match(expression, _RG_BRACKETS).length;
        }
    });

    // =========================================================================
    // lang/package.js
    // =========================================================================

    var lang = {
        name: "lang",
        version: base2.version,
        exports: "assert,assertArity,assertType,base,bind,copy,extend,forEach,format,instanceOf,match,pcopy,rescape,trim,typeOf",
        namespace: "" // fixed later
    };

    // =========================================================================
    // lang/assert.js
    // =========================================================================

    function assert(condition, message, ErrorClass) {
        if (!condition) {
            throw new (ErrorClass || Error)(message || "Assertion failed.");
        }
    };

    function assertArity(args, arity, message) {
        if (arity == null) arity = args.callee.length;
        if (args.length < arity) {
            throw new SyntaxError(message || "Not enough arguments.");
        }
    };

    function assertType(object, type, message) {
        if (type && (typeof type == "function" ? !instanceOf(object, type) : typeOf(object) != type)) {
            throw new TypeError(message || "Invalid type.");
        }
    };

    // =========================================================================
    // lang/copy.js
    // =========================================================================

    function copy(object) {
        // a quick copy
        var copy = {};
        for (var i in object) {
            copy[i] = object[i];
        }
        return copy;
    };

    function pcopy(object) {
        // Doug Crockford / Richard Cornford
        _dummy.prototype = object;
        return new _dummy;
    };

    function _dummy() { };

    // =========================================================================
    // lang/extend.js
    // =========================================================================

    function base(object, args) {
        return object.base.apply(object, args);
    };

    function extend(object, source) { // or extend(object, key, value)
        if (object && source) {
            if (arguments.length > 2) { // Extending with a key/value pair.
                var key = source;
                source = {};
                source[key] = arguments[2];
            }
            var proto = global[(typeof source == "function" ? "Function" : "Object")].prototype;
            // Add constructor, toString etc
            if (base2.__prototyping) {
                var i = _HIDDEN.length, key;
                while ((key = _HIDDEN[--i])) {
                    var value = source[key];
                    if (value != proto[key]) {
                        if (_BASE.test(value)) {
                            _override(object, key, value)
                        } else {
                            object[key] = value;
                        }
                    }
                }
            }
            // Copy each of the source object's properties to the target object.
            for (key in source) {
                if (proto[key] === undefined) {
                    var value = source[key];
                    // Object detection.
                    if (key.charAt(0) == "@") {
                        if (detect(key.slice(1))) extend(object, value);
                    } else {
                        // Check for method overriding.
                        var ancestor = object[key];
                        if (ancestor && typeof value == "function") {
                            if (value != ancestor) {
                                if (_BASE.test(value)) {
                                    _override(object, key, value);
                                } else {
                                    value.ancestor = ancestor;
                                    object[key] = value;
                                }
                            }
                        } else {
                            object[key] = value;
                        }
                    }
                }
            }
        }
        return object;
    };

    function _ancestorOf(ancestor, fn) {
        // Check if a function is in another function's inheritance chain.
        while (fn) {
            if (!fn.ancestor) return false;
            fn = fn.ancestor;
            if (fn == ancestor) return true;
        }
        return false;
    };

    function _override(object, name, method) {
        // Override an existing method.
        var ancestor = object[name];
        var superObject = base2.__prototyping; // late binding for prototypes
        if (superObject && ancestor != superObject[name]) superObject = null;
        function _base() {
            var previous = this.base;
            this.base = superObject ? superObject[name] : ancestor;
            var returnValue = method.apply(this, arguments);
            this.base = previous;
            return returnValue;
        };
        _base.method = method;
        _base.ancestor = ancestor;
        object[name] = _base;
        // introspection (removed when packed)
        ; ; ; _base.toString = K(method + "");
    };

    // =========================================================================
    // lang/forEach.js
    // =========================================================================

    // http://dean.edwards.name/weblog/2006/07/enum/

    if (typeof StopIteration == "undefined") {
        StopIteration = new Error("StopIteration");
    }

    function forEach(object, block, context, fn) {
        if (object == null) return;
        if (!fn) {
            if (typeof object == "function" && object.call) {
                // Functions are a special case.
                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.
                _Array_forEach(object, block, context);
                return;
            }
        }
        _Function_forEach(fn || Object, object, block, context);
    };

    forEach.csv = function(string, block, context) {
        forEach(csv(string), block, context);
    };

    forEach.detect = function(object, block, context) {
        forEach(object, function(value, key) {
            if (key.charAt(0) == "@") { // object detection
                if (detect(key.slice(1))) forEach(value, arguments.callee);
            } else block.call(context, value, key, object);
        });
    };

    // These are the two core enumeration methods. All other forEach methods
    //  eventually call one of these two.

    function _Array_forEach(array, block, context) {
        if (array == null) array = global;
        var length = array.length || 0, i; // preserve length
        if (typeof array == "string") {
            for (i = 0; i < length; i++) {
                block.call(context, array.charAt(i), i, array);
            }
        } else { // Cater for sparse arrays.
            for (i = 0; i < length; i++) {
                /*@cc_on@*/
                /*@if (@_jscript_version < 5.2)
      if ($Legacy.has(array, i))
    @else
                @*/
                if (i in array)
                /*@end@*/
                    block.call(context, array[i], i, array);
            }
        }
    };

    function _Function_forEach(fn, object, block, context) {
        // http://code.google.com/p/base2/issues/detail?id=10

        // Run the test for Safari's buggy enumeration.
        var Temp = function() { this.i = 1 };
        Temp.prototype = { i: 1 };
        var count = 0;
        for (var i in new Temp) count++;

        // Overwrite the main function the first time it is called.
        _Function_forEach = (count > 1) ? function(fn, object, block, context) {
            // Safari fix (pre version 3)
            var processed = {};
            for (var key in object) {
                if (!processed[key] && fn.prototype[key] === undefined) {
                    processed[key] = true;
                    block.call(context, object[key], key, object);
                }
            }
        } : 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);
                }
            }
        };

        _Function_forEach(fn, object, block, context);
    };

    // =========================================================================
    // lang/instanceOf.js
    // =========================================================================

    function instanceOf(object, klass) {
        // Handle exceptions where the target object originates from another frame.
        // This is handy for JSON parsing (amongst other things).

        if (typeof klass != "function") {
            throw new TypeError("Invalid 'instanceOf' operand.");
        }

        if (object == null) return false;

        /*@cc_on
        // COM objects don't have a constructor
        if (typeof object.constructor != "function") {
            return typeOf(object) == typeof klass.prototype.valueOf();
        }
        @*/
        if (object.constructor == klass) return true;
        if (klass.ancestorOf) return klass.ancestorOf(object.constructor);
        /*@if (@_jscript_version < 5.1)
    // do nothing
  @else
        @*/
        if (object instanceof klass) return true;
        /*@end@*/

        // If the class is a base2 class then it would have passed the test above.
        if (Base.ancestorOf == klass.ancestorOf) return false;

        // base2 objects can only be instances of Object.
        if (Base.ancestorOf == object.constructor.ancestorOf) return klass == Object;

        switch (klass) {
            case Array: // This is the only troublesome one.
                return !!(typeof object == "object" && object.join && object.splice);
            case Function:
                return typeOf(object) == "function";
            case RegExp:
                return typeof object.constructor.$1 == "string";
            case Date:
                return !!object.getTimezoneOffset;
            case String:
            case Number:
            case Boolean:
                return typeOf(object) == typeof klass.prototype.valueOf();
            case Object:
                return true;
        }

        return false;
    };

    // =========================================================================
    // lang/typeOf.js
    // =========================================================================

    // http://wiki.ecmascript.org/doku.php?id=proposals:typeof

    function typeOf(object) {
        var type = typeof object;
        switch (type) {
            case "object":
                return object == null
        ? "null"
        : typeof object.constructor == "undefined" // COM object
          ? _MSIE_NATIVE_FUNCTION.test(object)
            ? "function"
            : type
          : typeof object.constructor.prototype.valueOf(); // underlying type
            case "function":
                return typeof object.call == "function" ? type : "object";
            default:
                return type;
        }
    };

    // =========================================================================
    // JavaScript/package.js
    // =========================================================================

    var JavaScript = {
        name: "JavaScript",
        version: base2.version,
        exports: "Array2,Date2,Function2,String2",
        namespace: "", // fixed later

        bind: function(host) {
            var top = global;
            global = host;
            forEach.csv(this.exports, function(name2) {
                var name = name2.slice(0, -1);
                extend(host[name], this[name2]);
                this[name2](host[name].prototype); // cast
            }, this);
            global = top;
            return host;
        }
    };

    function _createObject2(Native, constructor, generics, extensions) {
        // Clone native objects and extend them.

        // Create a Module that will contain all the new methods.
        var INative = Module.extend();
        var id = INative.toString().slice(1, -1);
        // http://developer.mozilla.org/en/docs/New_in_JavaScript_1.6#Array_and_String_generics
        forEach.csv(generics, function(name) {
            INative[name] = unbind(Native.prototype[name]);
            INative.namespace += format("var %1=%2.%1;", name, id);
        });
        forEach(_slice.call(arguments, 3), INative.implement, INative);

        // create a faux constructor that augments the native object
        var Native2 = function() {
            return INative(this.constructor == INative ? constructor.apply(null, arguments) : arguments[0]);
        };
        Native2.prototype = INative.prototype;

        // Remove methods that are already implemented.
        for (var name in INative) {
            if (name != "prototype" && Native[name]) {
                delete INative.prototype[name];
            }
            Native2[name] = INative[name];
        }
        Native2.ancestor = Object;
        delete Native2.extend;

        // remove "lang.bind.."
        Native2.namespace = Native2.namespace.replace(/(var (\w+)=)[^,;]+,([^\)]+)\)/g, "$1$3.$2");

        return Native2;
    };

    // =========================================================================
    // JavaScript/~/Date.js
    // =========================================================================

    // Fix Date.get/setYear() (IE5-7)

    if ((new Date).getYear() > 1900) {
        Date.prototype.getYear = function() {
            return this.getFullYear() - 1900;
        };
        Date.prototype.setYear = function(year) {
            return this.setFullYear(year + 1900);
        };
    }

    // https://bugs.webkit.org/show_bug.cgi?id=9532

    var _testDate = new Date(Date.UTC(2006, 1, 20));
    _testDate.setUTCDate(15);
    if (_testDate.getUTCHours() != 0) {
        forEach.csv("FullYear,Month,Date,Hours,Minutes,Seconds,Milliseconds", function(type) {
            extend(Date.prototype, "setUTC" + type, function() {
                var value = base(this, arguments);
                if (value >= 57722401000) {
                    value -= 3600000;
                    this.setTime(value);
                }
                return value;
            });
        });
    }

    // =========================================================================
    // JavaScript/~/Function.js
    // =========================================================================

    // Some browsers don't define this.

    Function.prototype.prototype = {};

    // =========================================================================
    // JavaScript/~/String.js
    // =========================================================================

    // A KHTML bug.

    if ("".replace(/^/, K("$$")) == "$") {
        extend(String.prototype, "replace", function(expression, replacement) {
            if (typeof replacement == "function") {
                var fn = replacement;
                replacement = function() {
                    return String(fn.apply(null, arguments)).split("$").join("$$");
                };
            }
            return this.base(expression, replacement);
        });
    }

    // =========================================================================
    // JavaScript/Array2.js
    // =========================================================================

    var Array2 = _createObject2(
  Array,
  Array,
  "concat,join,pop,push,reverse,shift,slice,sort,splice,unshift", // generics
  Enumerable, {
      combine: function(keys, values) {
          // Combine two arrays to make a hash.
          if (!values) values = keys;
          return Array2.reduce(keys, function(hash, key, index) {
              hash[key] = values[index];
              return hash;
          }, {});
      },

      contains: function(array, item) {
          return Array2.indexOf(array, item) != -1;
      },

      copy: function(array) {
          var copy = _slice.call(array);
          if (!copy.swap) Array2(copy); // cast to Array2
          return copy;
      },

      flatten: function(array) {
          var i = 0;
          return Array2.reduce(array, function(result, item) {
              if (Array2.like(item)) {
                  Array2.reduce(item, arguments.callee, result);
              } else {
                  result[i++] = item;
              }
              return result;
          }, []);
      },

      forEach: _Array_forEach,

      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, index, item) {
          Array2.splice(array, index, 0, item);
          return item;
      },

      item: function(array, index) {
          if (index < 0) index += array.length; // starting from the end
          return array[index];
      },

      lastIndexOf: function(array, item, fromIndex) {
          var length = array.length;
          if (fromIndex == null) {
              fromIndex = length - 1;
          } else if (fromIndex < 0) {
              fromIndex = Math.max(0, length + fromIndex);
          }
          for (var i = fromIndex; i >= 0; i--) {
              if (array[i] === item) return i;
          }
          return -1;
      },

      map: function(array, block, context) {
          var result = [];
          Array2.forEach(array, function(item, index) {
              result[index] = block.call(context, item, index, array);
          });
          return result;
      },

      remove: function(array, item) {
          var index = Array2.indexOf(array, item);
          if (index != -1) Array2.removeAt(array, index);
      },

      removeAt: function(array, index) {
          Array2.splice(array, index, 1);
      },

      swap: function(array, index1, index2) {
          if (index1 < 0) index1 += array.length; // starting from the end
          if (index2 < 0) index2 += array.length;
          var temp = array[index1];
          array[index1] = array[index2];
          array[index2] = temp;
          return array;
      }
  }
);

    Array2.reduce = Enumerable.reduce; // Mozilla does not implement the thisObj argument

    Array2.like = function(object) {
        // is the object like an array?
        return typeOf(object) == "object" && typeof object.length == "number";
    };

    // introspection (removed when packed)
    ; ; ; Enumerable["#implemented_by"].pop();
    ; ; ; Enumerable["#implemented_by"].push(Array2);

    // =========================================================================
    // JavaScript/Date2.js
    // =========================================================================

    // http://developer.mozilla.org/es4/proposals/date_and_time.html

    // big, ugly, regular expression
    var _DATE_PATTERN = /^((-\d+|\d{4,})(-(\d{2})(-(\d{2}))?)?)?T((\d{2})(:(\d{2})(:(\d{2})(\.(\d{1,3})(\d)?\d*)?)?)?)?(([+-])(\d{2})(:(\d{2}))?|Z)?$/;
    var _DATE_PARTS = { // indexes to the sub-expressions of the RegExp above
        FullYear: 2,
        Month: 4,
        Date: 6,
        Hours: 8,
        Minutes: 10,
        Seconds: 12,
        Milliseconds: 14
    };
    var _TIMEZONE_PARTS = { // idem, but without the getter/setter usage on Date object
        Hectomicroseconds: 15, // :-P
        UTC: 16,
        Sign: 17,
        Hours: 18,
        Minutes: 20
    };

    var _TRIM_ZEROES = /(((00)?:0+)?:0+)?\.0+$/;
    var _TRIM_TIMEZONE = /(T[0-9:.]+)$/;

    var Date2 = _createObject2(
  Date,
  function(yy, mm, dd, h, m, s, ms) {
      switch (arguments.length) {
          case 0: return new Date;
          case 1: return typeof yy == "number" ? new Date(yy) : Date2.parse(yy);
          default: return new Date(yy, mm, arguments.length == 2 ? 1 : dd, h || 0, m || 0, s || 0, ms || 0);
      }
  }, "", {
      toISOString: function(date) {
          var string = "####-##-##T##:##:##.###";
          for (var part in _DATE_PARTS) {
              string = string.replace(/#+/, function(digits) {
                  var value = date["getUTC" + part]();
                  if (part == "Month") value++; // js month starts at zero
                  return ("000" + value).slice(-digits.length); // pad
              });
          }
          // remove trailing zeroes, and remove UTC timezone, when time's absent
          return string.replace(_TRIM_ZEROES, "").replace(_TRIM_TIMEZONE, "$1Z");
      }
  }
);

    delete Date2.forEach;

    Date2.now = function() {
        return (new Date).valueOf(); // milliseconds since the epoch
    };

    Date2.parse = function(string, defaultDate) {
        if (arguments.length > 1) {
            assertType(defaultDate, "number", "default date should be of type 'number'.")
        }
        // parse ISO date
        var parts = match(string, _DATE_PATTERN);
        if (parts.length) {
            if (parts[_DATE_PARTS.Month]) parts[_DATE_PARTS.Month]--; // js months start at zero
            // round milliseconds on 3 digits
            if (parts[_TIMEZONE_PARTS.Hectomicroseconds] >= 5) parts[_DATE_PARTS.Milliseconds]++;
            var date = new Date(defaultDate || 0);
            var prefix = parts[_TIMEZONE_PARTS.UTC] || parts[_TIMEZONE_PARTS.Hours] ? "UTC" : "";
            for (var part in _DATE_PARTS) {
                var value = parts[_DATE_PARTS[part]];
                if (!value) continue; // empty value
                // set a date part
                date["set" + prefix + part](value);
                // make sure that this setting does not overflow
                if (date["get" + prefix + part]() != parts[_DATE_PARTS[part]]) {
                    return NaN;
                }
            }
            // timezone can be set, without time being available
            // without a timezone, local timezone is respected
            if (parts[_TIMEZONE_PARTS.Hours]) {
                var hours = Number(parts[_TIMEZONE_PARTS.Sign] + parts[_TIMEZONE_PARTS.Hours]);
                var minutes = Number(parts[_TIMEZONE_PARTS.Sign] + (parts[_TIMEZONE_PARTS.Minutes] || 0));
                date.setUTCMinutes(date.getUTCMinutes() + (hours * 60) + minutes);
            }
            return date.valueOf();
        } else {
            return Date.parse(string);
        }
    };

    // =========================================================================
    // JavaScript/String2.js
    // =========================================================================

    var String2 = _createObject2(
  String,
  function(string) {
      return new String(arguments.length == 0 ? "" : string);
  },
  "charAt,charCodeAt,concat,indexOf,lastIndexOf,match,replace,search,slice,split,substr,substring,toLowerCase,toUpperCase",
  {
      csv: csv,
      format: format,
      rescape: rescape,
      trim: trim
  }
);

    delete String2.forEach;

    // http://blog.stevenlevithan.com/archives/faster-trim-javascript
    function trim(string) {
        return String(string).replace(_LTRIM, "").replace(_RTRIM, "");
    };

    function csv(string) {
        return string ? (string + "").split(/\s*,\s*/) : [];
    };

    function format(string) {
        // Replace %n with arguments[n].
        // e.g. format("%1 %2%3 %2a %1%3", "she", "se", "lls");
        // ==> "she sells sea shells"
        // Only %1 - %9 supported.
        var args = arguments;
        var pattern = new RegExp("%([1-" + (arguments.length - 1) + "])", "g");
        return (string + "").replace(pattern, function(match, index) {
            return args[index];
        });
    };

    function match(string, expression) {
        // Same as String.match() except that this function will return an empty
        // array if there is no match.
        return (string + "").match(expression) || [];
    };

    function rescape(string) {
        // Make a string safe for creating a RegExp.
        return (string + "").replace(_RESCAPE, "\\$1");
    };

    // =========================================================================
    // JavaScript/Function2.js
    // =========================================================================

    var Function2 = _createObject2(
  Function,
  Function,
  "", {
      I: I,
      II: II,
      K: K,
      bind: bind,
      compose: compose,
      delegate: delegate,
      flip: flip,
      not: not,
      partial: partial,
      unbind: unbind
  }
);

    function I(i) { // return first argument
        return i;
    };

    function II(i, ii) { // return second argument
        return ii;
    };

    function K(k) {
        return function() {
            return k;
        };
    };

    function bind(fn, context) {
        var lateBound = typeof fn != "function";
        if (arguments.length > 2) {
            var args = _slice.call(arguments, 2);
            return function() {
                return (lateBound ? context[fn] : fn).apply(context, args.concat.apply(args, arguments));
            };
        } else { // faster if there are no additional arguments
            return function() {
                return (lateBound ? context[fn] : fn).apply(context, arguments);
            };
        }
    };

    function compose() {
        var fns = _slice.call(arguments);
        return function() {
            var i = fns.length, result = fns[--i].apply(this, arguments);
            while (i--) result = fns[i].call(this, result);
            return result;
        };
    };

    function delegate(fn, context) {
        return function() {
            var args = _slice.call(arguments);
            args.unshift(this);
            return fn.apply(context, args);
        };
    };

    function flip(fn) {
        return function() {
            return fn.apply(this, Array2.swap(arguments, 0, 1));
        };
    };

    function not(fn) {
        return function() {
            return !fn.apply(this, arguments);
        };
    };

    function partial(fn) {
        var args = _slice.call(arguments, 1);
        // based on Oliver Steele's version
        return function() {
            var specialised = args.concat(), i = 0, j = 0;
            while (i < args.length && j < arguments.length) {
                if (specialised[i] === undefined) specialised[i] = arguments[j++];
                i++;
            }
            while (j < arguments.length) {
                specialised[i++] = arguments[j++];
            }
            if (Array2.contains(specialised, undefined)) {
                specialised.unshift(fn);
                return partial.apply(null, specialised);
            }
            return fn.apply(this, specialised);
        };
    };

    function unbind(fn) {
        return function(context) {
            return fn.apply(context, _slice.call(arguments, 1));
        };
    };

    // =========================================================================
    // base2/detect.js
    // =========================================================================

    function detect() {
        // Two types of detection:
        //  1. Object detection
        //    e.g. detect("(java)");
        //    e.g. detect("!(document.addEventListener)");
        //  2. Platform detection (browser sniffing)
        //    e.g. detect("MSIE");
        //    e.g. detect("MSIE|opera");

        var jscript = NaN/*@cc_on || @_jscript_version@*/; // http://dean.edwards.name/weblog/2007/03/sniff/#comment85164
        var javaEnabled = global.java ? true : false;
        if (global.navigator) { // browser
            var MSIE = /MSIE[\d.]+/g;
            var element = document.createElement("span");
            // Close up the space between name and version number.
            //  e.g. MSIE 6 -> MSIE6
            var userAgent = navigator.userAgent.replace(/([a-z])[\s\/](\d)/gi, "$1$2");
            // Fix opera's (and others) user agent string.
            if (!jscript) userAgent = userAgent.replace(MSIE, "");
            if (MSIE.test(userAgent)) userAgent = userAgent.match(MSIE)[0] + " " + userAgent.replace(MSIE, "");
            base2.userAgent = navigator.platform + " " + userAgent.replace(/like \w+/gi, "");
            javaEnabled &= navigator.javaEnabled();
            //} else if (java) { // rhino
            //  var System = java.lang.System;
            //  base2.userAgent = "Rhino " + System.getProperty("os.arch") + " " + System.getProperty("os.name") + " " + System.getProperty("os.version");
            //} else if (jscript) { // Windows Scripting Host
            //  base2.userAgent = "WSH";
        }

        var _cache = {};
        detect = function(expression) {
            if (_cache[expression] == null) {
                var returnValue = false, test = expression;
                var not = test.charAt(0) == "!";
                if (not) test = test.slice(1);
                if (test.charAt(0) == "(") {
                    try {
                        returnValue = new Function("element,jscript,java,global", "return !!" + test)(element, jscript, javaEnabled, global);
                    } catch (ex) {
                        // the test failed
                    }
                } else {
                    // Browser sniffing.
                    returnValue = new RegExp("(" + test + ")", "i").test(base2.userAgent);
                }
                _cache[expression] = !!(not ^ returnValue);
            }
            return _cache[expression];
        };

        return detect(arguments[0]);
    };

    // =========================================================================
    // base2/init.js
    // =========================================================================

    base2 = global.base2 = new Package(this, base2);
    var exports = this.exports;

    lang = new Package(this, lang);
    exports += this.exports;

    JavaScript = new Package(this, JavaScript);
    eval(exports + this.exports);

    lang.base = base;
    lang.extend = extend;

}; ////////////////////  END: CLOSURE  /////////////////////////////////////

