BUNDLE=[["index.html.bundle-1-0.js","index.html.bundle-1-1.js","index.html.bundle-1-2.js","index.html.bundle-1-3.js"]];
;
//*/
/* <copyright>
Copyright (c) 2012, Motorola Mobility LLC.
All Rights Reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

* Redistributions of source code must retain the above copyright notice,
  this list of conditions and the following disclaimer.

* Redistributions in binary form must reproduce the above copyright notice,
  this list of conditions and the following disclaimer in the documentation
  and/or other materials provided with the distribution.

* Neither the name of Motorola Mobility LLC nor the names of its
  contributors may be used to endorse or promote products derived from this
  software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
</copyright> */
/*global BUNDLE */
if (typeof window !== "undefined") {

    // Workaround for window.Touch on desktop browsers
    if (!("ontouchstart" in window)) {
        window.Touch = null;
    }

    document._montageTiming = {}
    document._montageTiming.loadStartTime = Date.now();

    // Give a threshold before we decide we need to show the bootstrapper progress
    // Applications that use our loader will interact with this timeout
    // and class name to coordinate a nice loading experience. Applications that do not will
    // just go about business as usual and draw their content as soon as possible.
    window.addEventListener("DOMContentLoaded", function() {
        var bootstrappingDelay = 1000;
        document._montageStartBootstrappingTimeout = setTimeout(function() {
            document._montageStartBootstrappingTimeout = null;

            var root = document.documentElement;
            if(!!root.classList) {
                root.classList.add("montage-app-bootstrapping");
            } else {
                root.className = root.className + " montage-app-bootstrapping";
            }

            document._montageTiming.bootstrappingStartTime = Date.now();
        }, bootstrappingDelay);
    });

}

(function (definition) {
    if (typeof require !== "undefined") {
        // CommonJS / NodeJS
        definition.call(
            typeof global !== "undefined" ? global : this,
            require,
            exports,
            module
        );
    } else {
        // <script>
        definition({}, {}, {});
    }
})(function (require, exports, module) {

    // The global context object
    global = this;

    /**
     * Initializes Montage and creates the application singleton if
     * necessary.
     */
    exports.initMontage = function () {
        var platform = exports.getPlatform();

        // Platform dependent
        platform.bootstrap(function (Require, Promise, URL) {
            var params = platform.getParams();
            var config = platform.getConfig();

            var montageLocation = URL.resolve(Require.getLocation(), params.montageLocation);

            // setup the reel loader
            config.makeLoader = function (config) {
                return exports.ReelLoader(
                    config,
                    Require.makeLoader(config)
                );
            };

            // setup serialization compiler
            config.makeCompiler = function (config) {
                return exports.MetaCompiler(
                    config,
                    exports.SerializationCompiler(
                        config,
                        exports.TemplateCompiler(
                            config,
                            Require.makeCompiler(config)
                        )
                    )
                );
            };

            var location = URL.resolve(config.location, params["package"] || ".");
            var applicationHash = params.applicationHash;

            if (typeof BUNDLE === "object") {
                var bundleDefinitions = {};
                var getDefinition = function (name) {
                    return bundleDefinitions[name] =
                        bundleDefinitions[name] ||
                            Promise.defer();
                };
                global.bundleLoaded = function (name) {
                    getDefinition(name).resolve();
                };
                var preloading = Promise.defer();
                config.preloaded = preloading.promise;
                // preload bundles sequentially
                var preloaded = Promise.resolve();
                BUNDLE.forEach(function (bundleLocations) {
                    preloaded = preloaded.then(function () {
                        return Promise.all(bundleLocations.map(function (bundleLocation) {
                            browser.load(bundleLocation);
                            return getDefinition(bundleLocation).promise;
                        }));
                    });
                });
                // then release the module loader to run normally
                preloading.resolve(preloaded.then(function () {
                    delete BUNDLE;
                    delete bundleLoaded;
                }));
            }

            var applicationRequirePromise;

            if (!("remoteTrigger" in params)) {
                if ("autoPackage" in params) {
                    Require.injectPackageDescription(location, {
                        dependencies: {
                            montage: "*"
                        }
                    }, config);
                } else {
                    // handle explicit package.json location
                    if (location.slice(location.length - 5) === ".json") {
                        var packageDescriptionLocation = location;
                        location = URL.resolve(location, ".");
                        Require.injectPackageDescriptionLocation(
                            location,
                            packageDescriptionLocation,
                            config
                        );
                    }
                }
                applicationRequirePromise = Require.loadPackage({
                    location: location,
                    hash: applicationHash
                }, config);
            } else {
                // allows the bootstrapping to be remote controlled by the
                // parent window, with a dynamically generated package
                // description
                var trigger = Promise.defer();
                window.postMessage({
                    type: "montageReady"
                }, "*");
                var messageCallback = function (event) {
                    if (
                        params.remoteTrigger === event.origin &&
                        (event.source === window || event.source === window.parent)
                    ) {
                        switch (event.data.type) {
                        case "montageInit":
                            window.removeEventListener("message", messageCallback);
                            trigger.resolve([event.data.location, event.data.injections]);
                            break;
                        case "isMontageReady":
                            // allow the injector to query the state in case
                            // they missed the first message
                            window.postMessage({
                                type: "montageReady"
                            }, "*");
                        }
                    }
                };
                window.addEventListener("message", messageCallback);

                applicationRequirePromise = trigger.promise.spread(function (location, injections) {
                    var promise = Require.loadPackage({
                        location: location,
                        hash: applicationHash
                    }, config);
                    if (injections) {
                        promise = promise.then(function (applicationRequire) {
                            location = URL.resolve(location, ".");
                            var packageDescriptions = injections.packageDescriptions,
                                packageDescriptionLocations = injections.packageDescriptionLocations,
                                mappings = injections.mappings,
                                dependencies = injections.dependencies,
                                index, injectionsLength;

                            if (packageDescriptions) {
                                injectionsLength = packageDescriptions.length;
                                for (index = 0; index < injectionsLength; index++) {
                                    applicationRequire.injectPackageDescription(
                                        packageDescriptions[index].location,
                                        packageDescriptions[index].description);
                                }
                            }

                            if (packageDescriptionLocations) {
                                injectionsLength = packageDescriptionLocations.length;
                                for (index = 0; index < injectionsLength; index++) {
                                    applicationRequire.injectPackageDescriptionLocation(
                                        packageDescriptionLocations[index].location,
                                        packageDescriptionLocations[index].descriptionLocation);
                                }
                            }

                            if (mappings) {
                                injectionsLength = mappings.length;
                                for (index = 0; index < injectionsLength; index++) {
                                    applicationRequire.injectMapping(
                                        mappings[index].dependency,
                                        mappings[index].name);
                                }
                            }

                            if (dependencies) {
                                injectionsLength = dependencies.length;
                                for (index = 0; index < injectionsLength; index++) {
                                    applicationRequire.injectDependency(
                                        dependencies[index].name,
                                        dependencies[index].version);
                                }
                            }

                            return applicationRequire;
                        });
                    }

                    return promise;
                });
            }

            applicationRequirePromise
            .then(function (applicationRequire) {
                applicationRequire.loadPackage({
                    location: montageLocation,
                    hash: params.montageHash
                })
                .then(function (montageRequire) {
                    // load the promise package so we can inject the bootstrapped
                    // promise library back into it
                    var promiseLocation;
                    if (params.promiseLocation) {
                        promiseLocation = URL.resolve(Require.getLocation(), params.promiseLocation);
                    } else {
                        promiseLocation = URL.resolve(montageLocation, "packages/mr/packages/q");
                    }

                    return [
                        montageRequire,
                        montageRequire.loadPackage({
                            location: promiseLocation,
                            hash: params.promiseHash
                        })
                    ];
                })
                .spread(function (montageRequire, promiseRequire) {
                    montageRequire.inject("core/mini-url", URL);
                    montageRequire.inject("core/promise", {Promise: Promise});
                    promiseRequire.inject("q", Promise);

                    // install the linter, which loads on the first error
                    config.lint = function (module) {
                        montageRequire.async("core/jshint")
                        .then(function (JSHINT) {
                            if (!JSHINT.JSHINT(module.text)) {
                                console.warn("JSHint Error: "+module.location);
                                JSHINT.JSHINT.errors.forEach(function(error) {
                                    if (error) {
                                        console.warn("Problem at line "+error.line+" character "+error.character+": "+error.reason);
                                        if (error.evidence) {
                                            console.warn("    " + error.evidence);
                                        }
                                    }
                                });
                            }
                        })
                        .done();
                    };

                    global.require = applicationRequire;
                    global.montageRequire = montageRequire;
                    platform.initMontage(montageRequire, applicationRequire, params);
                });
            })
            .done();

        });

    };

    /**
     Adds "_montage_metadata" property to all objects and function attached to
     the exports object.
     @see Compiler middleware in require/require.js
     @param config
     @param compiler
     */
    var reverseReelExpression = /((.*)\.reel)\/\2$/;
    var reverseReelFunction = function ($0, $1) { return $1 };
    exports.SerializationCompiler = function(config, compile) {
        return function(module) {
            compile(module);
            if (!module.factory)
                return;
            var defaultFactory = module.factory;
            module.factory = function(require, exports, module) {
                defaultFactory.call(this, require, exports, module);
                for (var name in exports) {
                    var object = exports[name];
                    // avoid attempting to initialize a non-object
                    if (!(object instanceof Object)) {
                    // avoid attempting to reinitialize an aliased property
                    } else if (object.hasOwnProperty("_montage_metadata") && !object._montage_metadata.isInstance) {
                        object._montage_metadata.aliases.push(name);
                        object._montage_metadata.objectName = name;
                    } else if (!Object.isSealed(object)) {
                        var id = module.id.replace(
                            reverseReelExpression,
                            reverseReelFunction
                        );
                        Object.defineProperty(
                            object,
                            "_montage_metadata",
                            {
                                value: {
                                    require: require,
                                    module: id,
                                    moduleId: id, // deprecated
                                    property: name,
                                    objectName: name, // deprecated
                                    aliases: [name],
                                    isInstance: false
                                }
                            }
                        );
                    }
                }
            };
            return module;
        };
    };

    /**
     * Allows reel directories to load the contained eponymous JavaScript
     * module.
     * @see Loader middleware in require/require.js
     * @param config
     * @param loader the next loader in the chain
     */
    var reelExpression = /([^\/]+)\.reel$/;
    exports.ReelLoader = function (config, load) {
        return function (id, module) {
            var match = reelExpression.exec(id);
            if (match) {
                module.redirect = id + "/" + match[1];
                return module;
            } else {
                return load(id, module);
            }
        };
    };

    /**
     * Allows the .meta files to be loaded as json
     * @see Compiler middleware in require/require.js
     * @param config
     * @param compile
     */
    var metaExpression = /\.meta/;
    exports.MetaCompiler = function (config, compile) {
        return function (module) {
            var json = (module.location || "").match(metaExpression);
            if (json) {
                module.exports = JSON.parse(module.text);
                return module;
            } else {
                return compile(module);
            }
        };
    };

    /**
     Allows the reel's html file to be loaded via require.
     @see Compiler middleware in require/require.js
     @param config
     @param compiler
     */
    exports.TemplateCompiler = function(config, compile) {
        return function(module) {
            if (!module.location)
                return;
            var match = module.location.match(/(.*\/)?(?=[^\/]+\.html(?:\.load\.js)?$)/);
            if (match) {
                module.dependencies = module.dependencies || [];
                module.exports = {
                    directory: match[1],
                    content: module.text
                };
                // XXX deprecated
                Object.defineProperty(module.exports, "root", {
                    get: function () {
                        if (typeof console === "object") {
                            console.warn("'root' property is deprecated on template modules.  Use 'directory' instead of root[1]");
                        }
                        return match;
                    }
                });
                return module;
            } else {
                compile(module);
            }
        };
    };

    // Bootstrapping for multiple-platforms

    exports.getPlatform = function () {
        if (typeof window !== "undefined" && window && window.document) {
            return browser;
        } else if (typeof process !== "undefined") {
            return require("./node.js");
        } else {
            throw new Error("Platform not supported.");
        }
    };

    var browser = {

        // mini-url library
        makeResolve: function () {
            var head = document.querySelector("head"),
                baseElement = document.createElement("base"),
                relativeElement = document.createElement("a");

            baseElement.href = "";

            return function (base, relative) {
                var currentBaseElement = head.querySelector("base");
                if (!currentBaseElement) {
                    head.appendChild(baseElement);
                    currentBaseElement = baseElement;
                }
                base = String(base);
                if (!/^[\w\-]+:/.test(base)) { // isAbsolute(base)
                    throw new Error("Can't resolve from a relative location: " + JSON.stringify(base) + " " + JSON.stringify(relative));
                }
                var restore = currentBaseElement.href;
                currentBaseElement.href = base;
                relativeElement.href = relative;
                var resolved = relativeElement.href;
                currentBaseElement.href = restore;
                if (currentBaseElement === baseElement) {
                    head.removeChild(currentBaseElement);
                }
                return resolved;
            };
        },

        load: function (location) {
            var script = document.createElement("script");
            script.src = location;
            script.onload = function () {
                // remove clutter
                script.parentNode.removeChild(script);
            };
            document.getElementsByTagName("head")[0].appendChild(script);
        },

        getConfig: function() {
            return {
                location: "" + window.location
            };
        },

        getParams: function() {
            var i, j,
                match,
                script,
                montage,
                attr,
                name;
            if (!this._params) {
                this._params = {};
                // Find the <script> that loads us, so we can divine our
                // parameters from its attributes.
                var scripts = document.getElementsByTagName("script");
                for (i = 0; i < scripts.length; i++) {
                    script = scripts[i];
                    montage = false;
                    if (script.src && (match = script.src.match(/^(.*)montage.js(?:[\?\.]|$)/i))) {
                        this._params.montageLocation = match[1];
                        montage = true;
                    }
                    if (script.hasAttribute("data-montage-location")) {
                        this._params.montageLocation = script.getAttribute("data-montage-location");
                        montage = true;
                    }
                    if (montage) {
                        if (script.dataset) {
                            for (name in script.dataset) {
                                this._params[name] = script.dataset[name];
                            }
                        } else if (script.attributes) {
                            var dataRe = /^data-(.*)$/,
                                letterAfterDash = /-([a-z])/g,
                                upperCaseChar = function (_, c) {
                                    return c.toUpperCase();
                                };

                            for (j = 0; j < script.attributes.length; j++) {
                                attr = script.attributes[j];
                                match = attr.name.match(/^data-(.*)$/);
                                if (match) {
                                    this._params[match[1].replace(letterAfterDash, upperCaseChar)] = attr.value;
                                }
                            }
                        }
                        // Permits multiple montage.js <scripts>; by
                        // removing as they are discovered, next one
                        // finds itself.
                        script.parentNode.removeChild(script);
                        break;
                    }
                }
            }
            return this._params;
        },

        bootstrap: function (callback) {
            var base, Require, DOM, Promise, URL;

            var params = this.getParams();
            var resolve = this.makeResolve();

            // observe dom loading and load scripts in parallel

            // observe dom loaded
            function domLoad() {
                document.removeEventListener("DOMContentLoaded", domLoad, true);
                DOM = true;
                callbackIfReady();
            }

            // this permits montage.js to be injected after DOMContentLoaded
            // http://jsperf.com/readystate-boolean-vs-regex/2
            if (/interactive|complete/.test(document.readyState)) {
                domLoad();
            } else {
                document.addEventListener("DOMContentLoaded", domLoad, true);
            }

            // determine which scripts to load
            var pending = {
                "require": "packages/mr/require.js",
                "require/browser": "packages/mr/browser.js",
                "promise": "packages/mr/packages/q/q.js"
            };

            // load in parallel, but only if we're not using a preloaded cache.
            // otherwise, these scripts will be inlined after already
            if (typeof BUNDLE === "undefined") {
                var montageLocation = resolve(window.location, params.montageLocation);
                for (var id in pending) {
                    browser.load(resolve(montageLocation, pending[id]));
                }
            }

            // register module definitions for deferred,
            // serial execution
            var definitions = {};
            global.bootstrap = function (id, factory) {
                definitions[id] = factory;
                delete pending[id];
                for (var id in pending) {
                    // this causes the function to exit if there are any remaining
                    // scripts loading, on the first iteration.  consider it
                    // equivalent to an array length check
                    return;
                }
                // if we get past the for loop, bootstrapping is complete.  get rid
                // of the bootstrap function and proceed.
                delete global.bootstrap;
                allModulesLoaded();
            };

            // one module loaded for free, for use in require.js, browser.js
            global.bootstrap("mini-url", function (require, exports) {
                exports.resolve = resolve;
            });

            // miniature module system
            var bootModules = {};
            function bootRequire(id) {
                if (!bootModules[id] && definitions[id]) {
                    var exports = bootModules[id] = {};
                    bootModules[id] = definitions[id](bootRequire, exports) || exports;
                }
                return bootModules[id];
            }

            // execute bootstrap scripts
            function allModulesLoaded() {
                URL = bootRequire("mini-url");
                Promise = bootRequire("promise");
                Require = bootRequire("require");
                delete global.bootstrap;
                callbackIfReady();
            }

            function callbackIfReady() {
                if (DOM && Require) {
                    callback(Require, Promise, URL);
                }
            }

        },

        initMontage: function (montageRequire, applicationRequire, params) {

            var dependencies = [
                "core/core",
                "core/event/event-manager",
                "core/serialization/deserializer/montage-reviver",
                "core/logger"
            ];

            var Promise = montageRequire("core/promise").Promise;

            return Promise.all(dependencies.map(montageRequire.deepLoad))
            .then(function () {

                dependencies.forEach(montageRequire);

                var Montage = montageRequire("core/core").Montage;
                var EventManager = montageRequire("core/event/event-manager").EventManager;
                var MontageReviver = montageRequire("core/serialization/deserializer/montage-reviver").MontageReviver;
                var logger = montageRequire("core/logger").logger

                var defaultEventManager, application;

                // Setup Promise's longStackTrace support option
                logger("Promise stacktrace support", function(state) {
                    Promise.longStackSupport = !!state;
                });

                // Load the event-manager
                defaultEventManager = new EventManager().initWithWindow(window);

                // montageWillLoad is mostly for testing purposes
                if (typeof global.montageWillLoad === "function") {
                    global.montageWillLoad();
                }

                // Load the application

                var appProto = applicationRequire.packageDescription.applicationPrototype,
                    applicationLocation, appModulePromise;
                if (appProto) {
                    applicationLocation = MontageReviver.parseObjectLocationId(appProto);
                    appModulePromise = applicationRequire.async(applicationLocation.moduleId);
                } else {
                    appModulePromise = montageRequire.async("core/application");
                }

                return appModulePromise.then(function(exports) {
                    var Application = exports[(applicationLocation ? applicationLocation.objectName : "Application")];
                    application = new Application();
                    Object.defineProperty(window.document, "application", {
                        get: Montage.deprecate(
                            null,
                            function () {
                                return exports.application
                            },
                            "document.application is deprecated, use require(\"montage/core/application\").application instead."
                            )
                    });
                    defaultEventManager.application = application;
                    application.eventManager = defaultEventManager;
                    application._load(applicationRequire, function() {
                        if (params.module) {
                            // If a module was specified in the config then we initialize it now
                            applicationRequire.async(params.module)
                            .done();
                        }
                        if (typeof global.montageDidLoad === "function") {
                            global.montageDidLoad();
                        }
                    });
                })

            })
            .done();

        }
    };

    if (typeof window !== "undefined") {
        if (global.__MONTAGE_LOADED__) {
            console.warn("Montage already loaded!");
        } else {
            global.__MONTAGE_LOADED__ = true;
            exports.initMontage();
        }
    } else {
        // may cause additional exports to be injected:
        exports.getPlatform();
    }

})

;
//*/

/*
    Based in part on Motorola Mobility’s Montage
    Copyright (c) 2012, Motorola Mobility LLC. All Rights Reserved.
    3-Clause BSD License
    https://github.com/motorola-mobility/montage/blob/master/LICENSE.md
*/

/*global bootstrap,define */
(function (definition) {

    // Boostrapping Browser
    if (typeof bootstrap !== "undefined") {

        // Window
        if (typeof window !== "undefined") {
            bootstrap("require", function (require, exports) {
                var Promise = require("promise");
                var URL = require("mini-url");
                definition(exports, Promise, URL);
                require("require/browser");
            });

        // Worker
        } else {
            bootstrap("require", function (require, exports) {
                var Promise = require("promise").Promise;
                var URL = require("mini-url");
                definition(exports, Promise, URL);
            });
        }

    // Node Server
    } else if (typeof process !== "undefined") {
        // the parens trick the heuristic scanner for static dependencies, so
        // they are not pre-loaded by the asynchronous browser loader
        var Promise = (require)("q");
        var URL = (require)("url");
        definition(exports, Promise, URL);
        (require)("./node");
    } else {
        throw new Error("Can't support require on this platform");
    }

})(function (Require, Promise, URL) {

    if (!this)
        throw new Error("Require does not work in strict mode.");

    var globalEval = eval; // reassigning causes eval to not use lexical scope.

    // Non-CommonJS speced extensions should be marked with an "// EXTENSION"
    // comment.

    Require.makeRequire = function (config) {
        var require;

        // Configuration defaults:
        config = config || {};
        config.location = URL.resolve(config.location || Require.getLocation(), "./");
        config.lib = URL.resolve(config.location, config.lib || "./");
        config.paths = config.paths || [config.lib];
        config.mappings = config.mappings || {}; // EXTENSION
        config.exposedConfigs = config.exposedConfigs || Require.exposedConfigs;
        config.makeLoader = config.makeLoader || Require.makeLoader;
        config.load = config.load || config.makeLoader(config);
        config.makeCompiler = config.makeCompiler || Require.makeCompiler;
        config.compile = config.compile || config.makeCompiler(config);
        config.parseDependencies = config.parseDependencies || Require.parseDependencies;
        config.read = config.read || Require.read;

        // Modules: { exports, id, location, directory, factory, dependencies,
        // dependees, text, type }
        var modules = config.modules = config.modules || {};

        // produces an entry in the module state table, which gets built
        // up through loading and execution, ultimately serving as the
        // ``module`` free variable inside the corresponding module.
        function getModuleDescriptor(id) {
            var lookupId = id.toLowerCase();
            if (!has(modules, lookupId)) {
                modules[lookupId] = {
                    id: id,
                    display: (config.name || config.location) + "#" + id, // EXTENSION
                    require: require
                };
            }
            return modules[lookupId];
        }

        // for preloading modules by their id and exports, useful to
        // prevent wasteful multiple instantiation if a module was loaded
        // in the bootstrapping process and can be trivially injected into
        // the system.
        function inject(id, exports) {
            var module = getModuleDescriptor(id);
            module.exports = exports;
            module.location = URL.resolve(config.location, id);
            module.directory = URL.resolve(module.location, "./");
            module.injected = true;
            delete module.redirect;
            delete module.mappingRedirect;
        }

        // Ensures a module definition is loaded, compiled, analyzed
        var load = memoize(function (topId, viaId) {
            var module = getModuleDescriptor(topId);
            return Promise.fcall(function () {
                // if not already loaded, already instantiated, or
                // configured as a redirection to another module
                if (
                    module.factory === void 0 &&
                    module.exports === void 0 &&
                    module.redirect === void 0
                ) {
                    return Promise.fcall(config.load, topId, module);
                }
            })
            .then(function () {
                // compile and analyze dependencies
                config.compile(module);
                var dependencies =
                    module.dependencies =
                        module.dependencies || [];
                if (module.redirect !== void 0) {
                    dependencies.push(module.redirect);
                }
                if (module.extraDependencies !== void 0) {
                    Array.prototype.push.apply(module.dependencies, module.extraDependencies);
                }
            });
        });

        // Load a module definition, and the definitions of its transitive
        // dependencies
        function deepLoad(topId, viaId, loading) {
            var module = getModuleDescriptor(topId);
            // this is a memo of modules already being loaded so we don’t
            // data-lock on a cycle of dependencies.
            loading = loading || {};
            // has this all happened before?  will it happen again?
            if (has(loading, topId))
                return; // break the cycle of violence.
            loading[topId] = true; // this has happened before
            return load(topId, viaId)
            .then(function () {
                // load the transitive dependencies using the magic of
                // recursion.
                return Promise.all(module.dependencies.map(function (depId) {
                    depId = resolve(depId, topId);
                    // create dependees set, purely for debug purposes
                    var module = getModuleDescriptor(depId);
                    var dependees = module.dependees = module.dependees || {};
                    dependees[topId] = true;
                    return deepLoad(depId, topId, loading);
                }));
            }, function (error) {
                module.error = error;
            });
        }

        // Initializes a module by executing the factory function with a new
        // module "exports" object.
        function getExports(topId, viaId) {
            var module = getModuleDescriptor(topId);

            // check for consistent case convention
            if (module.id !== topId) {
                throw new Error(
                    "Can't require module " + JSON.stringify(module.id) +
                    " by alternate spelling " + JSON.stringify(topId)
                );
            }

            // check for load error
            if (module.error) {
                var error = new Error(
                    "Can't require module " + JSON.stringify(module.id) +
                    " via " + JSON.stringify(viaId) +
                    " because " + module.error.message
                );
                error.cause = module.error;
                throw error;
            }

            // handle redirects
            if (module.redirect !== void 0) {
                return getExports(module.redirect, viaId);
            }

            // handle cross-package linkage
            if (module.mappingRedirect !== void 0) {
                return module.mappingRequire(module.mappingRedirect, viaId);
            }

            // do not reinitialize modules
            if (module.exports !== void 0) {
                return module.exports;
            }

            // do not initialize modules that do not define a factory function
            if (module.factory === void 0) {
                throw new Error(
                    "Can't require module " + JSON.stringify(topId) +
                    " via " + JSON.stringify(viaId)
                );
            }

            module.directory = URL.resolve(module.location, "./"); // EXTENSION
            module.exports = {};

            // Execute the factory function:
            var returnValue = module.factory.call(
                // in the context of the module:
                void 0, // this (defaults to global)
                makeRequire(topId), // require
                module.exports, // exports
                module // module
            );

            // EXTENSION
            if (returnValue !== void 0) {
                module.exports = returnValue;
            }

            return module.exports;
        }

        // Finds the internal identifier for a module in a subpackage
        // The `seen` object is a memo of the packages we have seen to avoid
        // infinite recursion of cyclic package dependencies. It also causes
        // the function to return null instead of throwing an exception. I’m
        // guessing that throwing exceptions *and* being recursive would be
        // too much performance evil for one function.
        function identify(id2, require2, seen) {
            var location = config.location;
            if (require2.location === location)
                return id2;

            var internal = !!seen;
            seen = seen || {};
            if (has(seen, location))
                return null; // break the cycle of violence.
            seen[location] = true;

            for (var name in config.mappings) {
                var mapping = config.mappings[name];
                location = mapping.location;
                if (!config.hasPackage(location))
                    continue;
                var candidate = config.getPackage(location);
                var id1 = candidate.identify(id2, require2, seen);
                if (id1 === null) {
                    continue;
                } else if (id1 === "") {
                    return name;
                } else {
                    return name + "/" + id1;
                }
            }
            if (internal) {
                return null;
            } else {
                throw new Error(
                    "Can't identify " + id2 + " from " + require2.location
                );
            }
        }

        // Creates a unique require function for each module that encapsulates
        // that module's id for resolving relative module IDs against.
        function makeRequire(viaId) {

            // Main synchronously executing "require()" function
            var require = function(id) {
                var topId = resolve(id, viaId);
                return getExports(topId, viaId);
            };

            // Asynchronous "require.async()" which ensures async executation
            // (even with synchronous loaders)
            require.async = function(id) {
                var topId = resolve(id, viaId);
                var module = getModuleDescriptor(id);
                return deepLoad(topId, viaId)
                .then(function () {
                    return require(topId);
                });
            };

            require.resolve = function (id) {
                return normalizeId(resolve(id, viaId));
            };

            require.getModule = getModuleDescriptor; // XXX deprecated, use:
            require.getModuleDescriptor = getModuleDescriptor;
            require.load = load;
            require.deepLoad = deepLoad;

            require.loadPackage = function (dependency, givenConfig) {
                if (givenConfig) { // explicit configuration, fresh environment
                    return Require.loadPackage(dependency, givenConfig);
                } else { // inherited environment
                    return config.loadPackage(dependency, config);
                }
            };

            require.hasPackage = function (dependency) {
                return config.hasPackage(dependency);
            };

            require.getPackage = function (dependency) {
                return config.getPackage(dependency);
            };

            require.isMainPackage = function () {
                return require.location === config.mainPackageLocation;
            };

            require.injectPackageDescription = function (location, description) {
                Require.injectPackageDescription(location, description, config);
            };

            require.injectPackageDescriptionLocation = function (location, descriptionLocation) {
                Require.injectPackageDescriptionLocation(location, descriptionLocation, config);
            };

            require.injectMapping = function (dependency, name) {
                dependency = normalizeDependency(dependency, config, name);
                name = name || dependency.name;
                config.mappings[name] = dependency;
            };

            require.injectDependency = function (name) {
                require.injectMapping({name: name}, name);
            };

            require.identify = identify;
            require.inject = inject;

            config.exposedConfigs.forEach(function(name) {
                require[name] = config[name];
            });

            require.config = config;

            require.read = Require.read;

            return require;
        }

        require = makeRequire("");
        return require;
    };

    Require.injectPackageDescription = function (location, description, config) {
        var descriptions =
            config.descriptions =
                config.descriptions || {};
        descriptions[location] = Promise.resolve(description);
    };

    Require.injectPackageDescriptionLocation = function (location, descriptionLocation, config) {
        var descriptionLocations =
            config.descriptionLocations =
                config.descriptionLocations || {};
        descriptionLocations[location] = descriptionLocation;
    };

    Require.loadPackageDescription = function (dependency, config) {
        var location = dependency.location;
        var descriptions =
            config.descriptions =
                config.descriptions || {};
        if (descriptions[location] === void 0) {
            var descriptionLocations =
                config.descriptionLocations =
                    config.descriptionLocations || {};
            var descriptionLocation;
            if (descriptionLocations[location]) {
                descriptionLocation = descriptionLocations[location];
            } else {
                descriptionLocation = URL.resolve(location, "package.json");
            }
            descriptions[location] = Require.read(descriptionLocation)
            .then(function (json) {
                try {
                    return JSON.parse(json);
                } catch (error) {
                    error.message = error.message + " in " + JSON.stringify(descriptionLocation)
                    throw error;
                }
            });
        }
        return descriptions[location];
    };

    Require.loadPackage = function (dependency, config) {
        dependency = normalizeDependency(dependency, config);
        if (!dependency.location) {
            throw new Error("Can't find dependency: " + JSON.stringify(dependency));
        }
        var location = dependency.location;
        config = Object.create(config || null);
        var loadingPackages = config.loadingPackages = config.loadingPackages || {};
        var loadedPackages = config.packages = {};
        var registry = config.registry = config.registry || Object.create(null);
        config.mainPackageLocation = location;

        config.hasPackage = function (dependency) {
            dependency = normalizeDependency(dependency, config);
            if (!dependency.location)
                return false;
            var location = dependency.location;
            return !!loadedPackages[location];
        };

        config.getPackage = function (dependency) {
            dependency = normalizeDependency(dependency, config);
            if (!dependency.location) {
                throw new Error("Can't find dependency: " + JSON.stringify(dependency) + " from " + config.location);
            }
            var location = dependency.location;
            if (!loadedPackages[location]) {
                if (loadingPackages[location]) {
                    throw new Error(
                        "Dependency has not finished loading: " + JSON.stringify(dependency)
                    );
                } else {
                    throw new Error(
                        "Dependency was not loaded: " + JSON.stringify(dependency)
                    );
                }
            }
            return loadedPackages[location];
        };

        config.loadPackage = function (dependency, viaConfig) {
            dependency = normalizeDependency(dependency, viaConfig);
            if (!dependency.location) {
                throw new Error("Can't find dependency: " + JSON.stringify(dependency) + " from " + config.location);
            }
            var location = dependency.location;
            if (!loadingPackages[location]) {
                loadingPackages[location] = Require.loadPackageDescription(dependency, config)
                .then(function (packageDescription) {
                    var subconfig = configurePackage(
                        location,
                        packageDescription,
                        config
                    );
                    var pkg = Require.makeRequire(subconfig);
                    loadedPackages[location] = pkg;
                    return pkg;
                });
            }
            return loadingPackages[location];
        };

        var pkg = config.loadPackage(dependency);
        pkg.location = location;
        pkg.async = function (id, callback) {
            return pkg.then(function (require) {
                return require.async(id, callback);
            });
        };

        return pkg;
    };

    function normalizeDependency(dependency, config, name) {
        config = config || {};
        if (typeof dependency === "string") {
            dependency = {
                location: dependency
            };
        }
        if (dependency.main) {
            dependency.location = config.mainPackageLocation;
        }
        // if the named dependency has already been found at another
        // location, refer to the same eventual instance
        if (
            dependency.name &&
            config.registry &&
            config.registry[dependency.name]
        ) {
            dependency.location = config.registry[dependency.name];
        }
        // default location
        if (!dependency.location && config.packagesDirectory && dependency.name) {
            dependency.location = URL.resolve(
                config.packagesDirectory,
                dependency.name + "/"
            );
        }
        if (!dependency.location)
            return dependency; // partially completed
        // make sure the dependency location has a trailing slash so that
        // relative urls will resolve properly
        if (!/\/$/.test(dependency.location)) {
            dependency.location += "/";
        }
        // resolve the location relative to the current package
        if (!Require.isAbsolute(dependency.location)) {
            if (!config.location) {
                throw new Error(
                    "Dependency locations must be fully qualified: " +
                    JSON.stringify(dependency)
                );
            }
            dependency.location = URL.resolve(
                config.location,
                dependency.location
            );
        }
        // register the package name so the location can be reused
        if (dependency.name) {
            config.registry[dependency.name] = dependency.location;
        }
        return dependency;
    }

    function configurePackage(location, description, parent) {

        if (!/\/$/.test(location)) {
            location += "/";
        }

        var config = Object.create(parent);
        config.name = description.name;
        config.location = location || Require.getLocation();
        config.packageDescription = description;
        config.useScriptInjection = description.useScriptInjection;

        if (description.production !== void 0) {
            config.production = description.production;
        }

        // explicitly mask definitions and modules, which must
        // not apply to child packages
        var modules = config.modules = config.modules || {};

        var registry = config.registry;
        if (config.name !== void 0 && !registry[config.name]) {
            registry[config.name] = config.location;
        }

        // overlay
        var overlay = description.overlay || {};
        var layer;
        (config.overlays || Require.overlays).forEach(function (engine) {
            if (overlay[engine]) {
                var layer = overlay[engine];
                for (var name in layer) {
                    description[name] = layer[name];
                }
            }
        });
        delete description.overlay;

        // directories
        description.directories = description.directories || {};
        description.directories.lib =
            description.directories.lib === void 0 ? "./" : description.directories.lib;
        var lib = description.directories.lib;
        // lib
        config.lib = URL.resolve(location, "./" + lib);
        var packagesDirectory = description.directories.packages || "node_modules";
        packagesDirectory = URL.resolve(location, packagesDirectory + "/");
        config.packagesDirectory = packagesDirectory;

        // The default "main" module of a package has the same name as the
        // package.
        if (description.main !== void 0) {

            // main, injects a definition for the main module, with
            // only its path. makeRequire goes through special effort
            // in deepLoad to re-initialize this definition with the
            // loaded definition from the given path.
            modules[""] = {
                id: "",
                redirect: normalizeId(description.main),
                location: config.location
            };

            if (description.name !== modules[""].redirect) {
                modules[description.name] = {
                    id: description.name,
                    redirect: "",
                    location: URL.resolve(location, description.name)
                };
            }

        }

        //Deal with redirects
        var redirects = description.redirects;
        if (redirects !== void 0) {
            Object.keys(redirects).forEach(function (name) {
                modules[name] = {
                    id: name,
                    redirect: redirects[name],
                    location: URL.resolve(location, name)
                };
            });
        }

        // mappings, link this package to other packages.
        var mappings = description.mappings || {};
        // dependencies, devDependencies if not in production
        [description.dependencies, !config.production ? description.devDependencies : null]
        .forEach(function (dependencies) {
            if (!dependencies)
                return;
            Object.keys(dependencies).forEach(function (name) {
                if (!mappings[name]) {
                    // dependencies are equivalent to name and version mappings,
                    // though the version predicate string is presently ignored
                    // (TODO)
                    mappings[name] = {
                        name: name,
                        version: dependencies[name]
                    };
                }
            });
        });
        // mappings
        Object.keys(mappings).forEach(function (name) {
            var mapping = mappings[name] = normalizeDependency(
                mappings[name],
                config,
                name
            );
        });
        config.mappings = mappings;

        return config;
    }

    // Helper functions:

    function has(object, property) {
        return Object.prototype.hasOwnProperty.call(object, property);
    }

    // Resolves CommonJS module IDs (not paths)
    Require.resolve = resolve;
    function resolve(id, baseId) {
        id = String(id);
        var source = id.split("/");
        var target = [];
        if (source.length && source[0] === "." || source[0] === "..") {
            var parts = baseId.split("/");
            parts.pop();
            source.unshift.apply(source, parts);
        }
        for (var i = 0, ii = source.length; i < ii; i++) {
            var part = source[i];
            if (part === "" || part === ".") {
            } else if (part === "..") {
                if (target.length) {
                    target.pop();
                }
            } else {
                target.push(part);
            }
        }
        return target.join("/");
    }

    Require.base = function (location) {
        // matches Unix basename
        return String(location)
            .replace(/(.+?)\/+$/, "$1")
            .match(/([^\/]+$|^\/$|^$)/)[1];
    };

    // Tests whether the location or URL is a absolute.
    Require.isAbsolute = function(location) {
        return (/^[\w\-]+:/).test(location);
    };

    // Extracts dependencies by parsing code and looking for "require" (currently using a simple regexp)
    Require.parseDependencies = function(factory) {
        var o = {};
        String(factory).replace(/(?:^|[^\w\$_.])require\s*\(\s*["']([^"']*)["']\s*\)/g, function(_, id) {
            o[id] = true;
        });
        return Object.keys(o);
    };

    // Built-in compiler/preprocessor "middleware":

    Require.DependenciesCompiler = function(config, compile) {
        return function(module) {
            if (!module.dependencies && module.text !== void 0) {
                module.dependencies = config.parseDependencies(module.text);
            }
            compile(module);
            if (module && !module.dependencies) {
                if (module.text || module.factory) {
                    module.dependencies = Require.parseDependencies(module.text || module.factory);
                } else {
                    module.dependencies = [];
                }
            }
            return module;
        };
    };

    // Support she-bang for shell scripts by commenting it out (it is never
    // valid JavaScript syntax anyway)
    Require.ShebangCompiler = function(config, compile) {
        return function (module) {
            if (module.text) {
                module.text = module.text.replace(/^#!/, "//#!");
            }
            compile(module);
        };
    };

    Require.LintCompiler = function(config, compile) {
        return function(module) {
            try {
                compile(module);
            } catch (error) {
                if (config.lint) {
                    Promise.nextTick(function () {
                        config.lint(module);
                    });
                }
                throw error;
            }
        };
    };

    Require.exposedConfigs = [
        "paths",
        "mappings",
        "location",
        "packageDescription",
        "packages",
        "modules"
    ];

    Require.makeCompiler = function(config) {
        return Require.JsonCompiler(
            config,
            Require.ShebangCompiler(
                config,
                Require.DependenciesCompiler(
                    config,
                    Require.LintCompiler(
                        config,
                        Require.Compiler(config)
                    )
                )
            )
        );
    };

    Require.JsonCompiler = function (config, compile) {
        return function (module) {
            var json = (module.location || "").match(/\.json$/);
            if (json) {
                module.exports = JSON.parse(module.text);
                return module;
            } else {
                return compile(module);
            }
        };
    };

    // Built-in loader "middleware":

    // Using mappings hash to load modules that match a mapping.
    Require.MappingsLoader = function(config, load) {
        config.mappings = config.mappings || {};
        config.name = config.name;

        // finds a mapping to follow, if any
        return function (id, module) {
            var mappings = config.mappings;
            var prefixes = Object.keys(mappings);
            var length = prefixes.length;

            if (Require.isAbsolute(id)) {
                return load(id, module);
            }
            // TODO: remove this when all code has been migrated off of the autonomous name-space problem
            if (
                config.name !== void 0 &&
                id.indexOf(config.name) === 0 &&
                id.charAt(config.name.length) === "/"
            ) {
                console.warn("Package reflexive module ignored:", id);
            }
            var i, prefix;
            for (i = 0; i < length; i++) {
                prefix = prefixes[i];
                if (
                    id === prefix ||
                    id.indexOf(prefix) === 0 &&
                    id.charAt(prefix.length) === "/"
                ) {
                    var mapping = mappings[prefix];
                    var rest = id.slice(prefix.length + 1);
                    return config.loadPackage(mapping, config)
                    .then(function (mappingRequire) {
                        module.mappingRedirect = rest;
                        module.mappingRequire = mappingRequire;
                        return mappingRequire.deepLoad(rest, config.location);
                    });
                }
            }
            return load(id, module);
        };
    };

    Require.ExtensionsLoader = function(config, load) {
        var extensions = config.extensions || ["js"];
        var loadWithExtension = extensions.reduceRight(function (next, extension) {
            return function (id, module) {
                return load(id + "." + extension, module)
                .fail(function (error) {
                    if (/^Can't find /.test(error.message)) {
                        return next(id, module);
                    } else {
                        throw error;
                    }
                });
            };
        }, function (id, module) {
            throw new Error(
                "Can't find " + JSON.stringify(id) + " with extensions " +
                JSON.stringify(extensions) + " in package at " +
                JSON.stringify(config.location)
            );
        });
        return function (id, module) {
            if (Require.base(id).indexOf(".") !== -1) {
                // already has an extension
                return load(id, module);
            } else {
                return loadWithExtension(id, module);
            }
        };
    };

    // Attempts to load using multiple base paths (or one absolute path) with a
    // single loader.
    Require.PathsLoader = function(config, load) {
        var loadFromPaths = config.paths.reduceRight(function (next, path) {
            return function (id, module) {
                var newId = URL.resolve(path, id);
                return load(newId, module)
                .fail(function (error) {
                    if (/^Can't find /.test(error.message)) {
                        return next(id, module);
                    } else {
                        throw error;
                    }
                });
            };
        }, function (id, module) {
            throw new Error(
                "Can't find " + JSON.stringify(id) + " from paths " +
                JSON.stringify(config.paths) + " in package at " +
                JSON.stringify(config.location)
            );
        });
        return function(id, module) {
            if (Require.isAbsolute(id)) {
                // already fully qualified
                return load(id, module);
            } else {
                return loadFromPaths(id, module);
            }
        };
    };

    Require.MemoizedLoader = function (config, load) {
        var cache = config.cache = config.cache || {};
        return memoize(load, cache);
    };

    var normalizeId = function (id) {
        var match = /^(.*)\.js$/.exec(id);
        if (match) {
            id = match[1];
        }
        return id;
    };

    var memoize = function (callback, cache) {
        cache = cache || {};
        return function (key, arg) {
            if (!has(cache, key)) {
                cache[key] = Promise.fcall(callback, key, arg);
            }
            return cache[key];
        };
    };

});

;
//*/
/* <copyright>
 This file contains proprietary software owned by Motorola Mobility, Inc.<br/>
 No rights, expressed or implied, whatsoever to this software are provided by Motorola Mobility, Inc. hereunder.<br/>
 (c) Copyright 2012 Motorola Mobility, Inc.  All Rights Reserved.
 </copyright> */
/*global bootstrap */
bootstrap("require/browser", function (require) {

var Require = require("require");
var Promise = require("promise");
var URL = require("mini-url");
var GET = "GET";
var APPLICATION_JAVASCRIPT_MIMETYPE = "application/javascript";
var FILE_PROTOCOL = "file:";
var global = typeof global !== "undefined" ? global : window;

Require.getLocation = function() {
    return URL.resolve(window.location, ".");
};

Require.overlays = ["window", "browser", "montage"];

// Determine if an XMLHttpRequest was successful
// Some versions of WebKit return 0 for successful file:// URLs
function xhrSuccess(req) {
    return (req.status === 200 || (req.status === 0 && req.responseText));
}

// Due to crazy variabile availability of new and old XHR APIs across
// platforms, this implementation registers every known name for the event
// listeners.  The promise library ascertains that the returned promise
// is resolved only by the first event.
// http://dl.dropbox.com/u/131998/yui/misc/get/browser-capabilities.html
Require.read = function (url) {

    if (URL.resolve(window.location, url).indexOf(FILE_PROTOCOL) === 0) {
        throw new Error("XHR does not function for file: protocol");
    }

    var request = new XMLHttpRequest();
    var response = Promise.defer();

    function onload() {
        if (xhrSuccess(request)) {
            response.resolve(request.responseText);
        } else {
            onerror();
        }
    }

    function onerror() {
        response.reject(new Error("Can't XHR " + JSON.stringify(url)));
    }

    try {
        request.open(GET, url, true);
        if (request.overrideMimeType) {
            request.overrideMimeType(APPLICATION_JAVASCRIPT_MIMETYPE);
        }
        request.onreadystatechange = function () {
            if (request.readyState === 4) {
                onload();
            }
        };
        request.onload = request.load = onload;
        request.onerror = request.error = onerror;
    } catch (exception) {
        response.reject(exception.message, exception);
    }

    request.send();
    return response.promise;
};

// By using a named "eval" most browsers will execute in the global scope.
// http://www.davidflanagan.com/2010/12/global-eval-in.html
// Unfortunately execScript doesn't always return the value of the evaluated expression (at least in Chrome)
var globalEval = /*this.execScript ||*/eval;
// For Firebug evaled code isn't debuggable otherwise
// http://code.google.com/p/fbug/issues/detail?id=2198
if (global.navigator && global.navigator.userAgent.indexOf("Firefox") >= 0) {
    globalEval = new Function("_", "return eval(_)");
}

var __FILE__String = "__FILE__",
    DoubleUnderscoreString = "__",
    globalEvalConstantA = "(function ",
    globalEvalConstantB = "(require, exports, module) {",
    globalEvalConstantC = "//*/\n})\n//@ sourceURL=";

Require.Compiler = function (config) {
    return function(module) {
        if (module.factory || module.text === void 0)
            return module;
        if (config.useScriptInjection)
            throw new Error("Can't use eval.");

        // Here we use a couple tricks to make debugging better in various browsers:
        // TODO: determine if these are all necessary / the best options
        // 1. name the function with something inteligible since some debuggers display the first part of each eval (Firebug)
        // 2. append the "//@ sourceURL=location" hack (Safari, Chrome, Firebug)
        //  * http://pmuellr.blogspot.com/2009/06/debugger-friendly.html
        //  * http://blog.getfirebug.com/2009/08/11/give-your-eval-a-name-with-sourceurl/
        //      TODO: investigate why this isn't working in Firebug.
        // 3. set displayName property on the factory function (Safari, Chrome)

        var displayName = __FILE__String+module.location.replace(/\.\w+$|\W/g, DoubleUnderscoreString);

        try {
            module.factory = globalEval(globalEvalConstantA+displayName+globalEvalConstantB+module.text+globalEvalConstantC+module.location);
        } catch (exception) {
            exception.message = exception.message + " in " + module.location;
            throw exception;
        }

        // This should work and would be simpler, but Firebug does not show scripts executed via "new Function()" constructor.
        // TODO: sniff browser?
        // module.factory = new Function("require", "exports", "module", module.text + "\n//*/"+sourceURLComment);

        module.factory.displayName = displayName;
    }
};

Require.XhrLoader = function (config) {
    return function (url, module) {
        return config.read(url)
        .then(function (text) {
            module.type = "javascript";
            module.text = text;
            module.location = url;
        });
    };
};

var definitions = {};
var getDefinition = function (hash, id) {
    definitions[hash] = definitions[hash] || {};
    definitions[hash][id] = definitions[hash][id] || Promise.defer();
    return definitions[hash][id];
};
// global
montageDefine = function (hash, id, module) {
    getDefinition(hash, id).resolve(module);
};

Require.loadScript = function (location) {
    var script = document.createElement("script");
    script.onload = function() {
        script.parentNode.removeChild(script);
    };
    script.onerror = function (error) {
        script.parentNode.removeChild(script);
    };
    script.src = location;
    script.defer = true;
    document.getElementsByTagName("head")[0].appendChild(script);
};

Require.ScriptLoader = function (config) {
    var hash = config.packageDescription.hash;
    return function (location, module) {
        return Promise.fcall(function () {

            // short-cut by predefinition
            if (definitions[hash] && definitions[hash][module.id]) {
                return definitions[hash][module.id].promise;
            }

            if (/\.js$/.test(location)) {
                location = location.replace(/\.js/, ".load.js");
            } else {
                location += ".load.js";
            }

            Require.loadScript(location);

            return getDefinition(hash, module.id).promise;
        })
        .then(function (definition) {
            delete definitions[hash][module.id];
            for (var name in definition) {
                module[name] = definition[name];
            }
            module.location = location;
            module.directory = URL.resolve(location, ".");
        });
    };
};

// old version
var loadPackageDescription = Require.loadPackageDescription;
Require.loadPackageDescription = function (dependency, config) {
    if (dependency.hash) { // use script injection
        var definition = getDefinition(dependency.hash, "package.json").promise;
        var location = URL.resolve(dependency.location, "package.json.load.js");

        // The package.json might come in a preloading bundle. If so, we do not
        // want to issue a script injection. However, if by the time preloading
        // has finished the package.json has not arrived, we will need to kick off
        // a request for the package.json.load.js script.
        if (config.preloaded && config.preloaded.isPending()) {
            config.preloaded
            .then(function () {
                if (definition.isPending()) {
                    Require.loadScript(location);
                }
            })
            .done();
        } else if (definition.isPending()) {
            // otherwise preloading has already completed and we don't have the
            // package description, so load it
            Require.loadScript(location);
        }

        return definition.get("exports");
    } else {
        // fall back to normal means
        return loadPackageDescription(dependency, config);
    }
};

Require.makeLoader = function (config) {
    var Loader;
    if (config.useScriptInjection) {
        Loader = Require.ScriptLoader;
    } else {
        Loader = Require.XhrLoader;
    }
    return Require.MappingsLoader(
        config,
        Require.ExtensionsLoader(
            config,
            Require.PathsLoader(
                config,
                Require.MemoizedLoader(
                    config,
                    Loader(config)
                )
            )
        )
    );
};

});

;
//*/
// vim:ts=4:sts=4:sw=4:
/*!
 *
 * Copyright 2009-2012 Kris Kowal under the terms of the MIT
 * license found at http://github.com/kriskowal/q/raw/master/LICENSE
 *
 * With parts by Tyler Close
 * Copyright 2007-2009 Tyler Close under the terms of the MIT X license found
 * at http://www.opensource.org/licenses/mit-license.html
 * Forked at ref_send.js version: 2009-05-11
 *
 * With parts by Mark Miller
 * Copyright (C) 2011 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

(function (definition) {
    // Turn off strict mode for this function so we can assign to global.Q
    /* jshint strict: false */

    // This file will function properly as a <script> tag, or a module
    // using CommonJS and NodeJS or RequireJS module formats.  In
    // Common/Node/RequireJS, the module exports the Q API and when
    // executed as a simple <script>, it creates a Q global instead.

    // Montage Require
    if (typeof bootstrap === "function") {
        bootstrap("promise", definition);

    // CommonJS
    } else if (typeof exports === "object") {
        module.exports = definition();

    // RequireJS
    } else if (typeof define === "function" && define.amd) {
        define(definition);

    // SES (Secure EcmaScript)
    } else if (typeof ses !== "undefined") {
        if (!ses.ok()) {
            return;
        } else {
            ses.makeQ = definition;
        }

    // <script>
    } else {
        Q = definition();
    }

})(function () {
"use strict";

var hasStacks = false;
try {
    throw new Error();
} catch (e) {
    hasStacks = !!e.stack;
}

// All code after this point will be filtered from stack traces reported
// by Q.
var qStartingLine = captureLine();
var qFileName;

// shims

// used for fallback in "allResolved"
var noop = function () {};

// Use the fastest possible means to execute a task in a future turn
// of the event loop.
var nextTick =(function () {
    // linked list of tasks (single, with head node)
    var head = {task: void 0, next: null};
    var tail = head;
    var flushing = false;
    var requestTick = void 0;
    var isNodeJS = false;

    function flush() {
        while (head.next) {
            head = head.next;
            var task = head.task;
            head.task = void 0;
            var domain = head.domain;

            if (domain) {
                head.domain = void 0;
                domain.enter();
            }

            try {
                task();

            } catch (e) {
                if (isNodeJS) {
                    // In node, uncaught exceptions are considered fatal errors.
                    // Re-throw them synchronously to interrupt flushing!

                    // Ensure continuation if the uncaught exception is suppressed
                    // listening "uncaughtException" events (as domains does).
                    // Continue in next event to avoid tick recursion.
                    domain && domain.exit();
                    setTimeout(flush, 0);
                    domain && domain.enter();

                    throw e;

                } else {
                    // In browsers, uncaught exceptions are not fatal.
                    // Re-throw them asynchronously to avoid slow-downs.
                    setTimeout(function() {
                       throw e;
                    }, 0);
                }
            }

            if (domain) {
                domain.exit();
            }
        }

        flushing = false;
    }

    nextTick = function (task) {
        tail = tail.next = {
            task: task,
            domain: isNodeJS && process.domain,
            next: null
        };

        if (!flushing) {
            flushing = true;
            requestTick();
        }
    };

    if (typeof process !== "undefined" && process.nextTick) {
        // Node.js before 0.9. Note that some fake-Node environments, like the
        // Mocha test runner, introduce a `process` global without a `nextTick`.
        isNodeJS = true;

        requestTick = function () {
            process.nextTick(flush);
        };

    } else if (typeof setImmediate === "function") {
        // In IE10, Node.js 0.9+, or https://github.com/NobleJS/setImmediate
        if (typeof window !== "undefined") {
            requestTick = setImmediate.bind(window, flush);
        } else {
            requestTick = function () {
                setImmediate(flush);
            };
        }

    } else if (typeof MessageChannel !== "undefined") {
        // modern browsers
        // http://www.nonblocking.io/2011/06/windownexttick.html
        var channel = new MessageChannel();
        channel.port1.onmessage = flush;
        requestTick = function () {
            channel.port2.postMessage(0);
        };

    } else {
        // old browsers
        requestTick = function () {
            setTimeout(flush, 0);
        };
    }

    return nextTick;
})();

// Attempt to make generics safe in the face of downstream
// modifications.
// There is no situation where this is necessary.
// If you need a security guarantee, these primordials need to be
// deeply frozen anyway, and if you don’t need a security guarantee,
// this is just plain paranoid.
// However, this does have the nice side-effect of reducing the size
// of the code by reducing x.call() to merely x(), eliminating many
// hard-to-minify characters.
// See Mark Miller’s explanation of what this does.
// http://wiki.ecmascript.org/doku.php?id=conventions:safe_meta_programming
function uncurryThis(f) {
    var call = Function.call;
    return function () {
        return call.apply(f, arguments);
    };
}
// This is equivalent, but slower:
// uncurryThis = Function_bind.bind(Function_bind.call);
// http://jsperf.com/uncurrythis

var array_slice = uncurryThis(Array.prototype.slice);

var array_reduce = uncurryThis(
    Array.prototype.reduce || function (callback, basis) {
        var index = 0,
            length = this.length;
        // concerning the initial value, if one is not provided
        if (arguments.length === 1) {
            // seek to the first value in the array, accounting
            // for the possibility that is is a sparse array
            do {
                if (index in this) {
                    basis = this[index++];
                    break;
                }
                if (++index >= length) {
                    throw new TypeError();
                }
            } while (1);
        }
        // reduce
        for (; index < length; index++) {
            // account for the possibility that the array is sparse
            if (index in this) {
                basis = callback(basis, this[index], index);
            }
        }
        return basis;
    }
);

var array_indexOf = uncurryThis(
    Array.prototype.indexOf || function (value) {
        // not a very good shim, but good enough for our one use of it
        for (var i = 0; i < this.length; i++) {
            if (this[i] === value) {
                return i;
            }
        }
        return -1;
    }
);

var array_map = uncurryThis(
    Array.prototype.map || function (callback, thisp) {
        var self = this;
        var collect = [];
        array_reduce(self, function (undefined, value, index) {
            collect.push(callback.call(thisp, value, index, self));
        }, void 0);
        return collect;
    }
);

var object_create = Object.create || function (prototype) {
    function Type() { }
    Type.prototype = prototype;
    return new Type();
};

var object_hasOwnProperty = uncurryThis(Object.prototype.hasOwnProperty);

var object_keys = Object.keys || function (object) {
    var keys = [];
    for (var key in object) {
        if (object_hasOwnProperty(object, key)) {
            keys.push(key);
        }
    }
    return keys;
};

var object_toString = uncurryThis(Object.prototype.toString);

function isObject(value) {
    return value === Object(value);
}

// generator related shims

// FIXME: Remove this function once ES6 generators are in SpiderMonkey.
function isStopIteration(exception) {
    return (
        object_toString(exception) === "[object StopIteration]" ||
        exception instanceof QReturnValue
    );
}

// FIXME: Remove this helper and Q.return once ES6 generators are in
// SpiderMonkey.
var QReturnValue;
if (typeof ReturnValue !== "undefined") {
    QReturnValue = ReturnValue;
} else {
    QReturnValue = function (value) {
        this.value = value;
    };
}

// Until V8 3.19 / Chromium 29 is released, SpiderMonkey is the only
// engine that has a deployed base of browsers that support generators.
// However, SM's generators use the Python-inspired semantics of
// outdated ES6 drafts.  We would like to support ES6, but we'd also
// like to make it possible to use generators in deployed browsers, so
// we also support Python-style generators.  At some point we can remove
// this block.
var hasES6Generators;
try {
    /* jshint evil: true, nonew: false */
    new Function("(function* (){ yield 1; })");
    hasES6Generators = true;
} catch (e) {
    hasES6Generators = false;
}

// long stack traces

var STACK_JUMP_SEPARATOR = "From previous event:";

function makeStackTraceLong(error, promise) {
    // If possible, transform the error stack trace by removing Node and Q
    // cruft, then concatenating with the stack trace of `promise`. See #57.
    if (hasStacks &&
        promise.stack &&
        typeof error === "object" &&
        error !== null &&
        error.stack &&
        error.stack.indexOf(STACK_JUMP_SEPARATOR) === -1
    ) {
        var stacks = [];
        for (var p = promise; !!p; p = p.source) {
            if (p.stack) {
                stacks.unshift(p.stack);
            }
        }
        stacks.unshift(error.stack);

        var concatedStacks = stacks.join("\n" + STACK_JUMP_SEPARATOR + "\n");
        error.stack = filterStackString(concatedStacks);
    }
}

function filterStackString(stackString) {
    var lines = stackString.split("\n");
    var desiredLines = [];
    for (var i = 0; i < lines.length; ++i) {
        var line = lines[i];

        if (!isInternalFrame(line) && !isNodeFrame(line) && line) {
            desiredLines.push(line);
        }
    }
    return desiredLines.join("\n");
}

function isNodeFrame(stackLine) {
    return stackLine.indexOf("(module.js:") !== -1 ||
           stackLine.indexOf("(node.js:") !== -1;
}

function getFileNameAndLineNumber(stackLine) {
    // Named functions: "at functionName (filename:lineNumber:columnNumber)"
    // In IE10 function name can have spaces ("Anonymous function") O_o
    var attempt1 = /at .+ \((.+):(\d+):(?:\d+)\)$/.exec(stackLine);
    if (attempt1) {
        return [attempt1[1], Number(attempt1[2])];
    }

    // Anonymous functions: "at filename:lineNumber:columnNumber"
    var attempt2 = /at ([^ ]+):(\d+):(?:\d+)$/.exec(stackLine);
    if (attempt2) {
        return [attempt2[1], Number(attempt2[2])];
    }

    // Firefox style: "function@filename:lineNumber or @filename:lineNumber"
    var attempt3 = /.*@(.+):(\d+)$/.exec(stackLine);
    if (attempt3) {
        return [attempt3[1], Number(attempt3[2])];
    }
}

function isInternalFrame(stackLine) {
    var fileNameAndLineNumber = getFileNameAndLineNumber(stackLine);

    if (!fileNameAndLineNumber) {
        return false;
    }

    var fileName = fileNameAndLineNumber[0];
    var lineNumber = fileNameAndLineNumber[1];

    return fileName === qFileName &&
        lineNumber >= qStartingLine &&
        lineNumber <= qEndingLine;
}

// discover own file name and line number range for filtering stack
// traces
function captureLine() {
    if (!hasStacks) {
        return;
    }

    try {
        throw new Error();
    } catch (e) {
        var lines = e.stack.split("\n");
        var firstLine = lines[0].indexOf("@") > 0 ? lines[1] : lines[2];
        var fileNameAndLineNumber = getFileNameAndLineNumber(firstLine);
        if (!fileNameAndLineNumber) {
            return;
        }

        qFileName = fileNameAndLineNumber[0];
        return fileNameAndLineNumber[1];
    }
}

function deprecate(callback, name, alternative) {
    return function () {
        if (typeof console !== "undefined" &&
            typeof console.warn === "function") {
            console.warn(name + " is deprecated, use " + alternative +
                         " instead.", new Error("").stack);
        }
        return callback.apply(callback, arguments);
    };
}

// end of shims
// beginning of real work

/**
 * Creates fulfilled promises from non-thenables,
 * Passes Q promises through,
 * Coerces other thenables to Q promises.
 */
function Q(value) {
    return resolve(value);
}

/**
 * Performs a task in a future turn of the event loop.
 * @param {Function} task
 */
Q.nextTick = nextTick;

/**
 * Controls whether or not long stack traces will be on
 */
Q.longStackSupport = false;

/**
 * Constructs a {promise, resolve, reject} object.
 *
 * `resolve` is a callback to invoke with a more resolved value for the
 * promise. To fulfill the promise, invoke `resolve` with any value that is
 * not a thenable. To reject the promise, invoke `resolve` with a rejected
 * thenable, or invoke `reject` with the reason directly. To resolve the
 * promise to another thenable, thus putting it in the same state, invoke
 * `resolve` with that other thenable.
 */
Q.defer = defer;
function defer() {
    // if "messages" is an "Array", that indicates that the promise has not yet
    // been resolved.  If it is "undefined", it has been resolved.  Each
    // element of the messages array is itself an array of complete arguments to
    // forward to the resolved promise.  We coerce the resolution value to a
    // promise using the `resolve` function because it handles both fully
    // non-thenable values and other thenables gracefully.
    var messages = [], progressListeners = [], resolvedPromise;

    var deferred = object_create(defer.prototype);
    var promise = object_create(Promise.prototype);

    promise.promiseDispatch = function (resolve, op, operands) {
        var args = array_slice(arguments);
        if (messages) {
            messages.push(args);
            if (op === "when" && operands[1]) { // progress operand
                progressListeners.push(operands[1]);
            }
        } else {
            nextTick(function () {
                resolvedPromise.promiseDispatch.apply(resolvedPromise, args);
            });
        }
    };

    // XXX deprecated
    promise.valueOf = deprecate(function () {
        if (messages) {
            return promise;
        }
        var nearerValue = nearer(resolvedPromise);
        if (isPromise(nearerValue)) {
            resolvedPromise = nearerValue; // shorten chain
        }
        return nearerValue;
    }, "valueOf", "inspect");

    promise.inspect = function () {
        if (!resolvedPromise) {
            return { state: "pending" };
        }
        return resolvedPromise.inspect();
    };

    if (Q.longStackSupport && hasStacks) {
        try {
            throw new Error();
        } catch (e) {
            // NOTE: don't try to use `Error.captureStackTrace` or transfer the
            // accessor around; that causes memory leaks as per GH-111. Just
            // reify the stack trace as a string ASAP.
            //
            // At the same time, cut off the first line; it's always just
            // "[object Promise]\n", as per the `toString`.
            promise.stack = e.stack.substring(e.stack.indexOf("\n") + 1);
        }
    }

    // NOTE: we do the checks for `resolvedPromise` in each method, instead of
    // consolidating them into `become`, since otherwise we'd create new
    // promises with the lines `become(whatever(value))`. See e.g. GH-252.

    function become(newPromise) {
        resolvedPromise = newPromise;
        promise.source = newPromise;

        array_reduce(messages, function (undefined, message) {
            nextTick(function () {
                newPromise.promiseDispatch.apply(newPromise, message);
            });
        }, void 0);

        messages = void 0;
        progressListeners = void 0;
    }

    deferred.promise = promise;
    deferred.resolve = function (value) {
        if (resolvedPromise) {
            return;
        }

        become(resolve(value));
    };

    deferred.fulfill = function (value) {
        if (resolvedPromise) {
            return;
        }

        become(fulfill(value));
    };
    deferred.reject = function (reason) {
        if (resolvedPromise) {
            return;
        }

        become(reject(reason));
    };
    deferred.notify = function (progress) {
        if (resolvedPromise) {
            return;
        }

        array_reduce(progressListeners, function (undefined, progressListener) {
            nextTick(function () {
                progressListener(progress);
            });
        }, void 0);
    };

    return deferred;
}

/**
 * Creates a Node-style callback that will resolve or reject the deferred
 * promise.
 * @returns a nodeback
 */
defer.prototype.makeNodeResolver = function () {
    var self = this;
    return function (error, value) {
        if (error) {
            self.reject(error);
        } else if (arguments.length > 2) {
            self.resolve(array_slice(arguments, 1));
        } else {
            self.resolve(value);
        }
    };
};

/**
 * @param resolver {Function} a function that returns nothing and accepts
 * the resolve, reject, and notify functions for a deferred.
 * @returns a promise that may be resolved with the given resolve and reject
 * functions, or rejected by a thrown exception in resolver
 */
Q.promise = promise;
function promise(resolver) {
    if (typeof resolver !== "function") {
        throw new TypeError("resolver must be a function.");
    }

    var deferred = defer();
    fcall(
        resolver,
        deferred.resolve,
        deferred.reject,
        deferred.notify
    ).fail(deferred.reject);
    return deferred.promise;
}

/**
 * Constructs a Promise with a promise descriptor object and optional fallback
 * function.  The descriptor contains methods like when(rejected), get(name),
 * set(name, value), post(name, args), and delete(name), which all
 * return either a value, a promise for a value, or a rejection.  The fallback
 * accepts the operation name, a resolver, and any further arguments that would
 * have been forwarded to the appropriate method above had a method been
 * provided with the proper name.  The API makes no guarantees about the nature
 * of the returned object, apart from that it is usable whereever promises are
 * bought and sold.
 */
Q.makePromise = Promise;
function Promise(descriptor, fallback, inspect) {
    if (fallback === void 0) {
        fallback = function (op) {
            return reject(new Error(
                "Promise does not support operation: " + op
            ));
        };
    }
    if (inspect === void 0) {
        inspect = function () {
            return {state: "unknown"};
        };
    }

    var promise = object_create(Promise.prototype);

    promise.promiseDispatch = function (resolve, op, args) {
        var result;
        try {
            if (descriptor[op]) {
                result = descriptor[op].apply(promise, args);
            } else {
                result = fallback.call(promise, op, args);
            }
        } catch (exception) {
            result = reject(exception);
        }
        if (resolve) {
            resolve(result);
        }
    };

    promise.inspect = inspect;

    // XXX deprecated `valueOf` and `exception` support
    if (inspect) {
        var inspected = inspect();
        if (inspected.state === "rejected") {
            promise.exception = inspected.reason;
        }

        promise.valueOf = deprecate(function () {
            var inspected = inspect();
            if (inspected.state === "pending" ||
                inspected.state === "rejected") {
                return promise;
            }
            return inspected.value;
        });
    }

    return promise;
}

Promise.prototype.then = function (fulfilled, rejected, progressed) {
    var self = this;
    var deferred = defer();
    var done = false;   // ensure the untrusted promise makes at most a
                        // single call to one of the callbacks

    function _fulfilled(value) {
        try {
            return typeof fulfilled === "function" ? fulfilled(value) : value;
        } catch (exception) {
            return reject(exception);
        }
    }

    function _rejected(exception) {
        if (typeof rejected === "function") {
            makeStackTraceLong(exception, self);
            try {
                return rejected(exception);
            } catch (newException) {
                return reject(newException);
            }
        }
        return reject(exception);
    }

    function _progressed(value) {
        return typeof progressed === "function" ? progressed(value) : value;
    }

    nextTick(function () {
        self.promiseDispatch(function (value) {
            if (done) {
                return;
            }
            done = true;

            deferred.resolve(_fulfilled(value));
        }, "when", [function (exception) {
            if (done) {
                return;
            }
            done = true;

            deferred.resolve(_rejected(exception));
        }]);
    });

    // Progress propagator need to be attached in the current tick.
    self.promiseDispatch(void 0, "when", [void 0, function (value) {
        var newValue;
        var threw = false;
        try {
            newValue = _progressed(value);
        } catch (e) {
            threw = true;
            if (Q.onerror) {
                Q.onerror(e);
            } else {
                throw e;
            }
        }

        if (!threw) {
            deferred.notify(newValue);
        }
    }]);

    return deferred.promise;
};

Promise.prototype.thenResolve = function (value) {
    return when(this, function () { return value; });
};

Promise.prototype.thenReject = function (reason) {
    return when(this, function () { throw reason; });
};

// Chainable methods
array_reduce(
    [
        "isFulfilled", "isRejected", "isPending",
        "dispatch",
        "when", "spread",
        "get", "set", "del", "delete",
        "post", "send", "mapply", "invoke", "mcall",
        "keys",
        "fapply", "fcall", "fbind",
        "all", "allResolved",
        "timeout", "delay",
        "catch", "finally", "fail", "fin", "progress", "done",
        "nfcall", "nfapply", "nfbind", "denodeify", "nbind",
        "npost", "nsend", "nmapply", "ninvoke", "nmcall",
        "nodeify"
    ],
    function (undefined, name) {
        Promise.prototype[name] = function () {
            return Q[name].apply(
                Q,
                [this].concat(array_slice(arguments))
            );
        };
    },
    void 0
);

Promise.prototype.toSource = function () {
    return this.toString();
};

Promise.prototype.toString = function () {
    return "[object Promise]";
};

/**
 * If an object is not a promise, it is as "near" as possible.
 * If a promise is rejected, it is as "near" as possible too.
 * If it’s a fulfilled promise, the fulfillment value is nearer.
 * If it’s a deferred promise and the deferred has been resolved, the
 * resolution is "nearer".
 * @param object
 * @returns most resolved (nearest) form of the object
 */

// XXX should we re-do this?
Q.nearer = nearer;
function nearer(value) {
    if (isPromise(value)) {
        var inspected = value.inspect();
        if (inspected.state === "fulfilled") {
            return inspected.value;
        }
    }
    return value;
}

/**
 * @returns whether the given object is a promise.
 * Otherwise it is a fulfilled value.
 */
Q.isPromise = isPromise;
function isPromise(object) {
    return isObject(object) &&
        typeof object.promiseDispatch === "function" &&
        typeof object.inspect === "function";
}

Q.isPromiseAlike = isPromiseAlike;
function isPromiseAlike(object) {
    return isObject(object) && typeof object.then === "function";
}

/**
 * @returns whether the given object is a pending promise, meaning not
 * fulfilled or rejected.
 */
Q.isPending = isPending;
function isPending(object) {
    return isPromise(object) && object.inspect().state === "pending";
}

/**
 * @returns whether the given object is a value or fulfilled
 * promise.
 */
Q.isFulfilled = isFulfilled;
function isFulfilled(object) {
    return !isPromise(object) || object.inspect().state === "fulfilled";
}

/**
 * @returns whether the given object is a rejected promise.
 */
Q.isRejected = isRejected;
function isRejected(object) {
    return isPromise(object) && object.inspect().state === "rejected";
}

//// BEGIN UNHANDLED REJECTION TRACKING

// This promise library consumes exceptions thrown in handlers so they can be
// handled by a subsequent promise.  The exceptions get added to this array when
// they are created, and removed when they are handled.  Note that in ES6 or
// shimmed environments, this would naturally be a `Set`.
var unhandledReasons = [];
var unhandledRejections = [];
var unhandledReasonsDisplayed = false;
var trackUnhandledRejections = true;
function displayUnhandledReasons() {
    if (
        !unhandledReasonsDisplayed &&
        typeof window !== "undefined" &&
        !window.Touch &&
        window.console
    ) {
        console.warn("[Q] Unhandled rejection reasons (should be empty):",
                     unhandledReasons);
    }

    unhandledReasonsDisplayed = true;
}

function logUnhandledReasons() {
    for (var i = 0; i < unhandledReasons.length; i++) {
        var reason = unhandledReasons[i];
        if (reason && typeof reason.stack !== "undefined") {
            console.warn("Unhandled rejection reason:", reason.stack);
        } else {
            console.warn("Unhandled rejection reason (no stack):", reason);
        }
    }
}

function resetUnhandledRejections() {
    unhandledReasons.length = 0;
    unhandledRejections.length = 0;
    unhandledReasonsDisplayed = false;

    if (!trackUnhandledRejections) {
        trackUnhandledRejections = true;

        // Show unhandled rejection reasons if Node exits without handling an
        // outstanding rejection.  (Note that Browserify presently produces a
        // `process` global without the `EventEmitter` `on` method.)
        if (typeof process !== "undefined" && process.on) {
            process.on("exit", logUnhandledReasons);
        }
    }
}

function trackRejection(promise, reason) {
    if (!trackUnhandledRejections) {
        return;
    }

    unhandledRejections.push(promise);
    unhandledReasons.push(reason);
    displayUnhandledReasons();
}

function untrackRejection(promise) {
    if (!trackUnhandledRejections) {
        return;
    }

    var at = array_indexOf(unhandledRejections, promise);
    if (at !== -1) {
        unhandledRejections.splice(at, 1);
        unhandledReasons.splice(at, 1);
    }
}

Q.resetUnhandledRejections = resetUnhandledRejections;

Q.getUnhandledReasons = function () {
    // Make a copy so that consumers can't interfere with our internal state.
    return unhandledReasons.slice();
};

Q.stopUnhandledRejectionTracking = function () {
    resetUnhandledRejections();
    if (typeof process !== "undefined" && process.on) {
        process.removeListener("exit", logUnhandledReasons);
    }
    trackUnhandledRejections = false;
};

resetUnhandledRejections();

//// END UNHANDLED REJECTION TRACKING

/**
 * Constructs a rejected promise.
 * @param reason value describing the failure
 */
Q.reject = reject;
function reject(reason) {
    var rejection = Promise({
        "when": function (rejected) {
            // note that the error has been handled
            if (rejected) {
                untrackRejection(this);
            }
            return rejected ? rejected(reason) : this;
        }
    }, function fallback() {
        return this;
    }, function inspect() {
        return { state: "rejected", reason: reason };
    });

    // Note that the reason has not been handled.
    trackRejection(rejection, reason);

    return rejection;
}

/**
 * Constructs a fulfilled promise for an immediate reference.
 * @param value immediate reference
 */
Q.fulfill = fulfill;
function fulfill(value) {
    return Promise({
        "when": function () {
            return value;
        },
        "get": function (name) {
            return value[name];
        },
        "set": function (name, rhs) {
            value[name] = rhs;
        },
        "delete": function (name) {
            delete value[name];
        },
        "post": function (name, args) {
            // Mark Miller proposes that post with no name should apply a
            // promised function.
            if (name === null || name === void 0) {
                return value.apply(void 0, args);
            } else {
                return value[name].apply(value, args);
            }
        },
        "apply": function (thisP, args) {
            return value.apply(thisP, args);
        },
        "keys": function () {
            return object_keys(value);
        }
    }, void 0, function inspect() {
        return { state: "fulfilled", value: value };
    });
}

/**
 * Constructs a promise for an immediate reference, passes promises through, or
 * coerces promises from different systems.
 * @param value immediate reference or promise
 */
Q.resolve = resolve;
function resolve(value) {
    // If the object is already a Promise, return it directly.  This enables
    // the resolve function to both be used to created references from objects,
    // but to tolerably coerce non-promises to promises.
    if (isPromise(value)) {
        return value;
    }

    // assimilate thenables
    if (isPromiseAlike(value)) {
        return coerce(value);
    } else {
        return fulfill(value);
    }
}

/**
 * Converts thenables to Q promises.
 * @param promise thenable promise
 * @returns a Q promise
 */
function coerce(promise) {
    var deferred = defer();
    nextTick(function () {
        try {
            promise.then(deferred.resolve, deferred.reject, deferred.notify);
        } catch (exception) {
            deferred.reject(exception);
        }
    });
    return deferred.promise;
}

/**
 * Annotates an object such that it will never be
 * transferred away from this process over any promise
 * communication channel.
 * @param object
 * @returns promise a wrapping of that object that
 * additionally responds to the "isDef" message
 * without a rejection.
 */
Q.master = master;
function master(object) {
    return Promise({
        "isDef": function () {}
    }, function fallback(op, args) {
        return dispatch(object, op, args);
    }, function () {
        return resolve(object).inspect();
    });
}

/**
 * Registers an observer on a promise.
 *
 * Guarantees:
 *
 * 1. that fulfilled and rejected will be called only once.
 * 2. that either the fulfilled callback or the rejected callback will be
 *    called, but not both.
 * 3. that fulfilled and rejected will not be called in this turn.
 *
 * @param value      promise or immediate reference to observe
 * @param fulfilled  function to be called with the fulfilled value
 * @param rejected   function to be called with the rejection exception
 * @param progressed function to be called on any progress notifications
 * @return promise for the return value from the invoked callback
 */
Q.when = when;
function when(value, fulfilled, rejected, progressed) {
    return Q(value).then(fulfilled, rejected, progressed);
}

/**
 * Spreads the values of a promised array of arguments into the
 * fulfillment callback.
 * @param fulfilled callback that receives variadic arguments from the
 * promised array
 * @param rejected callback that receives the exception if the promise
 * is rejected.
 * @returns a promise for the return value or thrown exception of
 * either callback.
 */
Q.spread = spread;
function spread(promise, fulfilled, rejected) {
    return when(promise, function (valuesOrPromises) {
        return all(valuesOrPromises).then(function (values) {
            return fulfilled.apply(void 0, values);
        }, rejected);
    }, rejected);
}

/**
 * The async function is a decorator for generator functions, turning
 * them into asynchronous generators.  Although generators are only part
 * of the newest ECMAScript 6 drafts, this code does not cause syntax
 * errors in older engines.  This code should continue to work and will
 * in fact improve over time as the language improves.
 *
 * ES6 generators are currently part of V8 version 3.19 with the
 * --harmony-generators runtime flag enabled.  SpiderMonkey has had them
 * for longer, but under an older Python-inspired form.  This function
 * works on both kinds of generators.
 *
 * Decorates a generator function such that:
 *  - it may yield promises
 *  - execution will continue when that promise is fulfilled
 *  - the value of the yield expression will be the fulfilled value
 *  - it returns a promise for the return value (when the generator
 *    stops iterating)
 *  - the decorated function returns a promise for the return value
 *    of the generator or the first rejected promise among those
 *    yielded.
 *  - if an error is thrown in the generator, it propagates through
 *    every following yield until it is caught, or until it escapes
 *    the generator function altogether, and is translated into a
 *    rejection for the promise returned by the decorated generator.
 */
Q.async = async;
function async(makeGenerator) {
    return function () {
        // when verb is "send", arg is a value
        // when verb is "throw", arg is an exception
        function continuer(verb, arg) {
            var result;
            if (hasES6Generators) {
                try {
                    result = generator[verb](arg);
                } catch (exception) {
                    return reject(exception);
                }
                if (result.done) {
                    return result.value;
                } else {
                    return when(result.value, callback, errback);
                }
            } else {
                // FIXME: Remove this case when SM does ES6 generators.
                try {
                    result = generator[verb](arg);
                } catch (exception) {
                    if (isStopIteration(exception)) {
                        return exception.value;
                    } else {
                        return reject(exception);
                    }
                }
                return when(result, callback, errback);
            }
        }
        var generator = makeGenerator.apply(this, arguments);
        var callback = continuer.bind(continuer, "send");
        var errback = continuer.bind(continuer, "throw");
        return callback();
    };
}

/**
 * The spawn function is a small wrapper around async that immediately
 * calls the generator and also ends the promise chain, so that any
 * unhandled errors are thrown instead of forwarded to the error
 * handler. This is useful because it's extremely common to run
 * generators at the top-level to work with libraries.
 */
Q.spawn = spawn;
function spawn(makeGenerator) {
    Q.done(Q.async(makeGenerator)());
}

// FIXME: Remove this interface once ES6 generators are in SpiderMonkey.
/**
 * Throws a ReturnValue exception to stop an asynchronous generator.
 *
 * This interface is a stop-gap measure to support generator return
 * values in older Firefox/SpiderMonkey.  In browsers that support ES6
 * generators like Chromium 29, just use "return" in your generator
 * functions.
 *
 * @param value the return value for the surrounding generator
 * @throws ReturnValue exception with the value.
 * @example
 * // ES6 style
 * Q.async(function* () {
 *      var foo = yield getFooPromise();
 *      var bar = yield getBarPromise();
 *      return foo + bar;
 * })
 * // Older SpiderMonkey style
 * Q.async(function () {
 *      var foo = yield getFooPromise();
 *      var bar = yield getBarPromise();
 *      Q.return(foo + bar);
 * })
 */
Q["return"] = _return;
function _return(value) {
    throw new QReturnValue(value);
}

/**
 * The promised function decorator ensures that any promise arguments
 * are settled and passed as values (`this` is also settled and passed
 * as a value).  It will also ensure that the result of a function is
 * always a promise.
 *
 * @example
 * var add = Q.promised(function (a, b) {
 *     return a + b;
 * });
 * add(Q.resolve(a), Q.resolve(B));
 *
 * @param {function} callback The function to decorate
 * @returns {function} a function that has been decorated.
 */
Q.promised = promised;
function promised(callback) {
    return function () {
        return spread([this, all(arguments)], function (self, args) {
            return callback.apply(self, args);
        });
    };
}

/**
 * sends a message to a value in a future turn
 * @param object* the recipient
 * @param op the name of the message operation, e.g., "when",
 * @param args further arguments to be forwarded to the operation
 * @returns result {Promise} a promise for the result of the operation
 */
Q.dispatch = dispatch;
function dispatch(object, op, args) {
    var deferred = defer();
    nextTick(function () {
        resolve(object).promiseDispatch(deferred.resolve, op, args);
    });
    return deferred.promise;
}

/**
 * Constructs a promise method that can be used to safely observe resolution of
 * a promise for an arbitrarily named method like "propfind" in a future turn.
 *
 * "dispatcher" constructs methods like "get(promise, name)" and "set(promise)".
 */
Q.dispatcher = dispatcher;
function dispatcher(op) {
    return function (object) {
        var args = array_slice(arguments, 1);
        return dispatch(object, op, args);
    };
}

/**
 * Gets the value of a property in a future turn.
 * @param object    promise or immediate reference for target object
 * @param name      name of property to get
 * @return promise for the property value
 */
Q.get = dispatcher("get");

/**
 * Sets the value of a property in a future turn.
 * @param object    promise or immediate reference for object object
 * @param name      name of property to set
 * @param value     new value of property
 * @return promise for the return value
 */
Q.set = dispatcher("set");

/**
 * Deletes a property in a future turn.
 * @param object    promise or immediate reference for target object
 * @param name      name of property to delete
 * @return promise for the return value
 */
Q["delete"] = // XXX experimental
Q.del = dispatcher("delete");

/**
 * Invokes a method in a future turn.
 * @param object    promise or immediate reference for target object
 * @param name      name of method to invoke
 * @param value     a value to post, typically an array of
 *                  invocation arguments for promises that
 *                  are ultimately backed with `resolve` values,
 *                  as opposed to those backed with URLs
 *                  wherein the posted value can be any
 *                  JSON serializable object.
 * @return promise for the return value
 */
// bound locally because it is used by other methods
var post = Q.post = dispatcher("post");
Q.mapply = post; // experimental

/**
 * Invokes a method in a future turn.
 * @param object    promise or immediate reference for target object
 * @param name      name of method to invoke
 * @param ...args   array of invocation arguments
 * @return promise for the return value
 */
Q.send = send;
Q.invoke = send; // synonyms
Q.mcall = send; // experimental
function send(value, name) {
    var args = array_slice(arguments, 2);
    return post(value, name, args);
}

/**
 * Applies the promised function in a future turn.
 * @param object    promise or immediate reference for target function
 * @param args      array of application arguments
 */
Q.fapply = fapply;
function fapply(value, args) {
    return dispatch(value, "apply", [void 0, args]);
}

/**
 * Calls the promised function in a future turn.
 * @param object    promise or immediate reference for target function
 * @param ...args   array of application arguments
 */
Q["try"] = fcall; // XXX experimental
Q.fcall = fcall;
function fcall(value) {
    var args = array_slice(arguments, 1);
    return fapply(value, args);
}

/**
 * Binds the promised function, transforming return values into a fulfilled
 * promise and thrown errors into a rejected one.
 * @param object    promise or immediate reference for target function
 * @param ...args   array of application arguments
 */
Q.fbind = fbind;
function fbind(value) {
    var args = array_slice(arguments, 1);
    return function fbound() {
        var allArgs = args.concat(array_slice(arguments));
        return dispatch(value, "apply", [this, allArgs]);
    };
}

/**
 * Requests the names of the owned properties of a promised
 * object in a future turn.
 * @param object    promise or immediate reference for target object
 * @return promise for the keys of the eventually settled object
 */
Q.keys = dispatcher("keys");

/**
 * Turns an array of promises into a promise for an array.  If any of
 * the promises gets rejected, the whole array is rejected immediately.
 * @param {Array*} an array (or promise for an array) of values (or
 * promises for values)
 * @returns a promise for an array of the corresponding values
 */
// By Mark Miller
// http://wiki.ecmascript.org/doku.php?id=strawman:concurrency&rev=1308776521#allfulfilled
Q.all = all;
function all(promises) {
    return when(promises, function (promises) {
        var countDown = 0;
        var deferred = defer();
        array_reduce(promises, function (undefined, promise, index) {
            var snapshot;
            if (
                isPromise(promise) &&
                (snapshot = promise.inspect()).state === "fulfilled"
            ) {
                promises[index] = snapshot.value;
            } else {
                ++countDown;
                when(promise, function (value) {
                    promises[index] = value;
                    if (--countDown === 0) {
                        deferred.resolve(promises);
                    }
                }, deferred.reject);
            }
        }, void 0);
        if (countDown === 0) {
            deferred.resolve(promises);
        }
        return deferred.promise;
    });
}

/**
 * Waits for all promises to be settled, either fulfilled or
 * rejected.  This is distinct from `all` since that would stop
 * waiting at the first rejection.  The promise returned by
 * `allResolved` will never be rejected.
 * @param promises a promise for an array (or an array) of promises
 * (or values)
 * @return a promise for an array of promises
 */
Q.allResolved = deprecate(allResolved, "allResolved", "allSettled");
function allResolved(promises) {
    return when(promises, function (promises) {
        promises = array_map(promises, resolve);
        return when(all(array_map(promises, function (promise) {
            return when(promise, noop, noop);
        })), function () {
            return promises;
        });
    });
}

Q.allSettled = allSettled;
function allSettled(values) {
    return when(values, function (values) {
        return all(array_map(values, function (value, i) {
            return when(
                value,
                function (fulfillmentValue) {
                    values[i] = { state: "fulfilled", value: fulfillmentValue };
                    return values[i];
                },
                function (reason) {
                    values[i] = { state: "rejected", reason: reason };
                    return values[i];
                }
            );
        })).thenResolve(values);
    });
}

/**
 * Captures the failure of a promise, giving an oportunity to recover
 * with a callback.  If the given promise is fulfilled, the returned
 * promise is fulfilled.
 * @param {Any*} promise for something
 * @param {Function} callback to fulfill the returned promise if the
 * given promise is rejected
 * @returns a promise for the return value of the callback
 */
Q["catch"] = // XXX experimental
Q.fail = fail;
function fail(promise, rejected) {
    return when(promise, void 0, rejected);
}

/**
 * Attaches a listener that can respond to progress notifications from a
 * promise's originating deferred. This listener receives the exact arguments
 * passed to ``deferred.notify``.
 * @param {Any*} promise for something
 * @param {Function} callback to receive any progress notifications
 * @returns the given promise, unchanged
 */
Q.progress = progress;
function progress(promise, progressed) {
    return when(promise, void 0, void 0, progressed);
}

/**
 * Provides an opportunity to observe the settling of a promise,
 * regardless of whether the promise is fulfilled or rejected.  Forwards
 * the resolution to the returned promise when the callback is done.
 * The callback can return a promise to defer completion.
 * @param {Any*} promise
 * @param {Function} callback to observe the resolution of the given
 * promise, takes no arguments.
 * @returns a promise for the resolution of the given promise when
 * ``fin`` is done.
 */
Q["finally"] = // XXX experimental
Q.fin = fin;
function fin(promise, callback) {
    return when(promise, function (value) {
        return when(callback(), function () {
            return value;
        });
    }, function (exception) {
        return when(callback(), function () {
            return reject(exception);
        });
    });
}

/**
 * Terminates a chain of promises, forcing rejections to be
 * thrown as exceptions.
 * @param {Any*} promise at the end of a chain of promises
 * @returns nothing
 */
Q.done = done;
function done(promise, fulfilled, rejected, progress) {
    var onUnhandledError = function (error) {
        // forward to a future turn so that ``when``
        // does not catch it and turn it into a rejection.
        nextTick(function () {
            makeStackTraceLong(error, promise);

            if (Q.onerror) {
                Q.onerror(error);
            } else {
                throw error;
            }
        });
    };

    // Avoid unnecessary `nextTick`ing via an unnecessary `when`.
    var promiseToHandle = fulfilled || rejected || progress ?
        when(promise, fulfilled, rejected, progress) :
        promise;

    if (typeof process === "object" && process && process.domain) {
        onUnhandledError = process.domain.bind(onUnhandledError);
    }
    fail(promiseToHandle, onUnhandledError);
}

/**
 * Causes a promise to be rejected if it does not get fulfilled before
 * some milliseconds time out.
 * @param {Any*} promise
 * @param {Number} milliseconds timeout
 * @param {String} custom error message (optional)
 * @returns a promise for the resolution of the given promise if it is
 * fulfilled before the timeout, otherwise rejected.
 */
Q.timeout = timeout;
function timeout(promise, ms, msg) {
    var deferred = defer();
    var timeoutId = setTimeout(function () {
        deferred.reject(new Error(msg || "Timed out after " + ms + " ms"));
    }, ms);

    when(promise, function (value) {
        clearTimeout(timeoutId);
        deferred.resolve(value);
    }, function (exception) {
        clearTimeout(timeoutId);
        deferred.reject(exception);
    }, deferred.notify);

    return deferred.promise;
}

/**
 * Returns a promise for the given value (or promised value) after some
 * milliseconds.
 * @param {Any*} promise
 * @param {Number} milliseconds
 * @returns a promise for the resolution of the given promise after some
 * time has elapsed.
 */
Q.delay = delay;
function delay(promise, timeout) {
    if (timeout === void 0) {
        timeout = promise;
        promise = void 0;
    }

    var deferred = defer();

    when(promise, undefined, undefined, deferred.notify);
    setTimeout(function () {
        deferred.resolve(promise);
    }, timeout);

    return deferred.promise;
}

/**
 * Passes a continuation to a Node function, which is called with the given
 * arguments provided as an array, and returns a promise.
 *
 *      Q.nfapply(FS.readFile, [__filename])
 *      .then(function (content) {
 *      })
 *
 */
Q.nfapply = nfapply;
function nfapply(callback, args) {
    var nodeArgs = array_slice(args);
    var deferred = defer();
    nodeArgs.push(deferred.makeNodeResolver());

    fapply(callback, nodeArgs).fail(deferred.reject);
    return deferred.promise;
}

/**
 * Passes a continuation to a Node function, which is called with the given
 * arguments provided individually, and returns a promise.
 *
 *      Q.nfcall(FS.readFile, __filename)
 *      .then(function (content) {
 *      })
 *
 */
Q.nfcall = nfcall;
function nfcall(callback/*, ...args */) {
    var nodeArgs = array_slice(arguments, 1);
    var deferred = defer();
    nodeArgs.push(deferred.makeNodeResolver());

    fapply(callback, nodeArgs).fail(deferred.reject);
    return deferred.promise;
}

/**
 * Wraps a NodeJS continuation passing function and returns an equivalent
 * version that returns a promise.
 *
 *      Q.nfbind(FS.readFile, __filename)("utf-8")
 *      .then(console.log)
 *      .done()
 *
 */
Q.nfbind = nfbind;
Q.denodeify = Q.nfbind; // synonyms
function nfbind(callback/*, ...args */) {
    var baseArgs = array_slice(arguments, 1);
    return function () {
        var nodeArgs = baseArgs.concat(array_slice(arguments));
        var deferred = defer();
        nodeArgs.push(deferred.makeNodeResolver());

        fapply(callback, nodeArgs).fail(deferred.reject);
        return deferred.promise;
    };
}

Q.nbind = nbind;
function nbind(callback, thisArg /*, ... args*/) {
    var baseArgs = array_slice(arguments, 2);
    return function () {
        var nodeArgs = baseArgs.concat(array_slice(arguments));
        var deferred = defer();
        nodeArgs.push(deferred.makeNodeResolver());

        function bound() {
            return callback.apply(thisArg, arguments);
        }

        fapply(bound, nodeArgs).fail(deferred.reject);
        return deferred.promise;
    };
}

/**
 * Calls a method of a Node-style object that accepts a Node-style
 * callback with a given array of arguments, plus a provided callback.
 * @param object an object that has the named method
 * @param {String} name name of the method of object
 * @param {Array} args arguments to pass to the method; the callback
 * will be provided by Q and appended to these arguments.
 * @returns a promise for the value or error
 */
Q.npost = npost;
Q.nmapply = npost; // synonyms
function npost(object, name, args) {
    var nodeArgs = array_slice(args || []);
    var deferred = defer();
    nodeArgs.push(deferred.makeNodeResolver());

    post(object, name, nodeArgs).fail(deferred.reject);
    return deferred.promise;
}

/**
 * Calls a method of a Node-style object that accepts a Node-style
 * callback, forwarding the given variadic arguments, plus a provided
 * callback argument.
 * @param object an object that has the named method
 * @param {String} name name of the method of object
 * @param ...args arguments to pass to the method; the callback will
 * be provided by Q and appended to these arguments.
 * @returns a promise for the value or error
 */
Q.nsend = nsend;
Q.ninvoke = Q.nsend; // synonyms
Q.nmcall = Q.nsend; // synonyms
function nsend(object, name /*, ...args*/) {
    var nodeArgs = array_slice(arguments, 2);
    var deferred = defer();
    nodeArgs.push(deferred.makeNodeResolver());
    post(object, name, nodeArgs).fail(deferred.reject);
    return deferred.promise;
}

Q.nodeify = nodeify;
function nodeify(promise, nodeback) {
    if (nodeback) {
        promise.then(function (value) {
            nextTick(function () {
                nodeback(null, value);
            });
        }, function (error) {
            nextTick(function () {
                nodeback(error);
            });
        });
    } else {
        return promise;
    }
}

// All code before this point will be filtered from stack traces.
var qEndingLine = captureLine();

return Q;

});

;
//*/
montageDefine("6607c26","package.json",{exports: {"name":"todo-mvc","version":"1.2.3","dependencies":{"montage":"~0.13.5","matte":"~0.1.3","native":"~0.1.2"},"bundle":["ui/main.reel"],"shard":4,"directories":{"lib":"./"},"hash":"6607c26","mappings":{"montage":{"name":"montage","hash":"6364dae","location":"packages/montage@6364dae/"},"matte":{"name":"matte","hash":"262b1a4","location":"packages/matte@262b1a4/"},"native":{"name":"native","hash":"5bf8252","location":"packages/native@5bf8252/"}},"production":true,"useScriptInjection":true}})
;
//*/
montageDefine("6364dae","package.json",{exports: {"name":"montage","version":"0.13.5","description":"A UI Framework","repository":{"type":"git","url":"https://github.com/montagejs/montage.git"},"main":"core/core","dependencies":{"collections":"~0.1.23","frb":"~0.2.10","mousse":"~0.1.2","htmlparser2":"~3.0.5"},"devDependencies":{"montage-testing":"~0.2.0","mr":"~0.13.0","q":"0.9.6","q-io":"~1.7.2","jshint":"~2.0.1"},"scripts":{"test":"node test/node/node-spec.js","jsdoc":"node ../montagejs.org/build/jsdoc/jsdoc.js -out $PWD/builds/jsdoc/ montage@npm-link"},"exclude":["etc","lab","node.js","test","tools"],"readme":"#MontageJS Readme\nMontageJS is a new type of application framework powered by standard web technologies. Designed with an eye toward maintainability and performance, MontageJS simplifies the development of rich HTML5 applications by providing modular components, real-time two-way data binding, object serialization with DOM mapping, event handling, a managed component draw cycle, CommonJS dependency management, and many more conveniences to help build robust single-page web applications.\n\n> **Note**: MontageJS is still in alpha.\n \n##Encapsulated Components\nMontageJS has a clean interface for creating custom user interface components. Each component can stand alone or be composed of other components. Each component is modeled as a stand-alone web application with its own HTML template, JavaScript, CSS, serialized component object model, and resources. With few exceptions, a component can stand on the web platform like any other web page. There are no fully JavaScript-driven templates in MontageJS. This separation of concerns allows designers to use the technologies they are comfortable with (without having to dig into the JavaScript) and developers to isolate and test individual components using familiar techniques.\n\n##Declarative Data Binding\nMontageJS makes it easier to manage your application and UI state with data bindings. A UI component or MontageJS object can establish a simple or bi-directional binding with another component or object. When the bound property, or deeper property path, of the bound object is updated then the source object is kept in sync.\n\nMontageJS uses functional reactive bindings ([FRB](https://github.com/montagejs/frb)). Unlike “traditional” bindings, FRB can gracefully bind long property paths and the contents of collections. They can also incrementally update the results of chains of queries including maps, flattened arrays, sums, and averages as well as add and remove elements from sets based on the changes to a flag. FRB makes it easy to incrementally ensure consistent state. FRB is built from a combination of powerful functional and generic building blocks, making it reliable, easy to extend, and easy to maintain.\n\nFor more information, see [FRB](https://github.com/montagejs/frb).\n\n##CommonJS\nMontageJS fully supports [CommonJS](http://www.commonjs.org/) modules and is a part of the Node and NPM package ecosystem.\n\nFor more information, see [CommonJS](https://github.com/montagejs/...[TBD]).\n\n#Requirements\nTo get started with MontageJS, you will need the following:\n\n* Node.js and npm. MontageJS application development depends on npm, the Node package manager, which is distributed with Node.js.\n* A recent stable release of Chrome, Safari or Firefox. MontageJS is intended to leverage the evolving web platform of modern browsers.\n* To contribute: A Git client and public SSH key. For details on installing Git and creating your key, see the setup guides on github.com.\n\n#Quick Setup\nTo start using MontageJS, follow these steps:\n\n1. [Download](http://nodejs.org/download/) and run the prebuilt Node.js installer for your platform from the Node.js website.\n\n2. Install `minit`, the MontageJS Initializer.\n\n    `minit` is a command line utility that will help kickstart your MontageJS project by generating prebuilt MontageJS application templates and components and placing the associated files inside the proper directories of your project.\n\n    **Mac OS X / Linux**\n\n    ```\n    $ mkdir -p ~/.npm\n    $ sudo npm install -gq minit@latest\n    ```\n\n    **Windows**\n\n    Run the \"Node.js command prompt\"\n\n    ```\n    $ npm install -gq minit@latest\n    ```\n\n3. Use `minit` to create your MontageJS project:\n\n    ```\n    $ minit create:app -n yourappname\n    ```\n\n    **Note**: If you get an EACCES warning when trying to run `minit:create`, use `sudo chown -R <username> ~/.npm` and then use `$ minit create:app -n hello`. This is a workaround due to a bug in npm.\n\n    This generates a new directory—yourappname, which contains the default MontageJS application template, including the production dependencies—in your current directory.\n\n4. To verify your installation, switch to yourappname directory and serve your new MontageJS project using `minit`:\n\n    ```\n    $ cd yourappname\n    $ minit serve &\n    ```\n\n5. Point your browser to http://localhost:8083/.\n\n    You should see the contents of the Welcome component—a simple single-page application, which is explicitly loaded to accompany our [Quick Start](http://montagejs.org/docs/quick-start.html) tutorial.\n\n##Where to Go from Here\nFor a quick introduction on how to assemble MontageJS components into a user interface, refer to the section [“Say Hello to MontageJS”](http://montagejs.org/docs/quick-start.html) in our Quick Start tutorial.\nFor more information on MontageJS components, bindings, event handling, serialization etc. refer to the [documentation](http://montagejs.org/docs/) (be patient: we are currently in the process of updating the docs).\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/montagejs/montage/issues"},"_id":"montage@0.13.5","_from":"montage@~0.13.5","mappings":{"mr":{"name":"mr","hash":"f64f623","location":"../mr@f64f623/"},"q":{"name":"q","hash":"2847ee2","location":"../q@2847ee2/"},"collections":{"name":"collections","hash":"5ea2535","location":"../collections@5ea2535/"},"frb":{"name":"frb","hash":"73de13b","location":"../frb@73de13b/"},"mousse":{"name":"mousse","hash":"3343e6d","location":"../mousse@3343e6d/"},"htmlparser2":{"name":"htmlparser2","hash":"be0ede4","location":"../htmlparser2@be0ede4/"}},"directories":{"lib":"./"},"hash":"6364dae","production":true,"useScriptInjection":true}})
;
//*/
montageDefine("5ea2535","package.json",{exports: {"name":"collections","version":"0.1.24","description":"data structures with idiomatic JavaScript collection interfaces","homepage":"http://github.com/montagejs/collections","author":{"name":"Kris Kowal","email":"kris@cixar.com","url":"http://github.com/kriskowal"},"keywords":["collections","data structures","observable","list","set","map","splay"],"bugs":{"url":"http://github.com/montagejs/collections/issues"},"licenses":[{"type":"MIT","url":"https://raw.github.com/montagejs/collections/master/LICENSE.md"}],"repository":{"type":"git","url":"http://github.com/montagejs/collections.git"},"dependencies":{"weak-map":"1.0.0"},"devDependencies":{"jasmine-node":"*","istanbul":"*","opener":"*"},"scripts":{"test":"jasmine-node spec","cover":"istanbul cover node_modules/jasmine-node/bin/jasmine-node spec && istanbul report html && opener coverage/index.html"},"readme":"[![Build Status](https://travis-ci.org/montagejs/collections.png?branch=master)](http://travis-ci.org/montagejs/collections)\n\n# Collections\n\nThis package contains JavaScript implementations of common data\nstructures with idiomatic iterfaces, including extensions for Array and\nObject.\n\nYou can use these Node Packaged Modules with Node.js, [Browserify][],\n[Mr][], or any compatible CommonJS module loader.  Using a module loader\nor bundler when using Collections in web browsers has the advantage of\nonly incorporating the modules you need.  However, you can just embed\n`<script src=\"collections/collections.min.js\">` and *all* of the\ncollections will be introduced as globals.  :warning:\n`require(\"collections\")` is not supported.\n\n```\nnpm install collections --save\n```\n\n[Browserify]: https://github.com/substack/node-browserify\n[Mr]: https://github.com/montagejs/mr\n\n\n-   **List(values, equals, getDefault)**\n\n    ```javascript\n    var List = require(\"collections/list\");\n    ```\n\n    An ordered collection of values with fast insertion and deletion and\n    forward and backward traversal, backed by a cyclic doubly linked\n    list with a head node.  Lists support most of the Array interface,\n    except that they use and return nodes instead of integer indicies in\n    analogous functions.\n\n    Lists have a `head` `Node`. The node type is available as `Node` on\n    the list prototype and can be overridden by inheritors.  Each node\n    has `prev` and `next` properties.\n\n-   **Set(values, equals, hash, getDefault)**\n\n    ```javascript\n    var Set = require(\"collections/set\");\n    ```\n\n    A collection of unique values.  The set can be iterated in the order\n    of insertion.  With a good hash function for the stored values,\n    insertion and removal are fast regardless of the size of the\n    collection.  Values may be objects.  The `equals` and `hash`\n    functions can be overridden to provide alternate definitions of\n    \"unique\".  `Set` is backed by `FastSet` and `List`.\n\n-   **Map(map, equals, hash, getDefault)**\n\n    ```javascript\n    var Map = require(\"collections/map\");\n    ```\n\n    A collection of key and value entries with unique keys.  Keys may be\n    objects.  The collection iterates in the order of insertion.  `Map`\n    is backed by `Set`.\n\n-   **MultiMap(map, getDefault, equals, hash)**\n\n    ```javascript\n    var MultiMap = require(\"collections/multi-map\");\n    ```\n\n    A collection of keys mapped to collections of values.  The default\n    `getDefault` collection is an `Array`, but it can be a `List` or any\n    other array-like object.  `MultiMap` inherits `Map` but overrides\n    the `getDefault(key)` provider.\n\n-   **WeakMap()**\n\n    ```javascript\n    var WeakMap = require(\"collections/weak-map\");\n    ```\n\n    A non-iterable collection of key value pairs.  Keys must objects and\n    do not benefit from `hash` functions.  Some engines already\n    implement `WeakMap`.  The non-iterable requirement makes it possible\n    for weak maps to collect garbage when the key is no longer\n    available, without betraying when the key is collected.  The shimmed\n    implementation undetectably annotates the given key and thus does\n    not necessarily leak memory, but cannot collect certain reference\n    graphs.  This WeakMap shim was implemented by Mark Miller of Google.\n\n-   **SortedSet(values, equals, compare, getDefault)**\n\n    ```javascript\n    var SortedSet = require(\"collections/sorted-set\");\n    ```\n\n    A collection of unique values stored in stored order, backed by a\n    splay tree.  The `equals` and `compare` functions can be overridden\n    to provide alternate definitions of \"unique\".\n\n    The `compare` method *must* provide a total order of all unique\n    values.  That is, if `compare(a, b) === 0`, it *must* follow that\n    `equals(a, b)`.\n\n-   **SortedMap(map, equals, compare, getDefault)**\n\n    ```javascript\n    var SortedMap = require(\"collections/sorted-map\");\n    ```\n\n    A collection of key value pairs stored in sorted order.  `SortedMap`\n    is backed by `SortedSet` and the `GenericMap` mixin.\n\n-   **LruSet(values, maxLength, equals, hash, getDefault)**\n\n    ```javascript\n    var LruSet = require(\"collections/lru-set\");\n    ```\n\n    A cache with the Least-Recently-Used strategy for truncating its\n    content when it’s length exceeds `maxLength`.  `LruSet` is backed by\n    a `Set` and takes advantage of the already tracked insertion order.\n    Both getting and setting a value constitute usage, but checking\n    whether the set has a value and iterating values do not.\n\n-   **LruMap(map, maxLength, equals, hash, getDefault)**\n\n    ```javascript\n    var LruMap = require(\"collections/lru-map\");\n    ```\n\n    A cache of entries backed by an `LruSet`.\n\n-   **SortedArray(values, equals, compare, getDefault)**\n\n    ```javascript\n    var SortedArray = require(\"collections/sorted-array\");\n    ```\n\n    A collection of values stored in a stable sorted order, backed by an\n    array.\n\n-   **SortedArraySet(values, equals, compare, getDefault)**\n\n    ```javascript\n    var SortedArraySet = require(\"collections/sorted-array-set\");\n    ```\n\n    A collection of unique values stored in sorted order, backed by a\n    plain array.  If the given values are an actual array, the sorted\n    array set takes ownership of that array and retains its content.  A\n    sorted array set performs better than a sorted set when it has\n    roughly less than 100 values.\n\n-   **SortedArrayMap(values, equals, compare, getDefault)**\n\n    ```javascript\n    var SortedArrayMap = require(\"collections/sorted-array-map\");\n    ```\n\n    A collection of key value pairs stored in sorted order, backed by a\n    sorted array set.\n\n-   **FastSet(values, equals, hash, getDefault)**\n\n    ```javascript\n    var FastSet = require(\"collections/fast-set\");\n    ```\n\n    A collection of unique values stored like a hash table.  The\n    underlying storage is a `Dict` that maps hashes to lists of values\n    that share the same hash.  Values may be objects.  The `equals` and\n    `hash` functions can be overridden to provide alternate definitions\n    of \"unique\".\n\n-   **FastMap(map, equals, hash, getDefault)**\n\n    ```javascript\n    var FastMap = require(\"collections/fast-map\");\n    ```\n\n    A collection of key and value entries with unique keys, backed by a\n    set.  Keys may be objects.  `FastMap` is backed by `FastSet` and the\n    `GenericMap` mixin.\n\n-   **Dict(values, getDefault)**\n\n    ```javascript\n    var Dict = require(\"collections/dict\");\n    ```\n\n    A collection of string to value mappings backed by a plain\n    JavaScript object.  The keys are mangled to prevent collisions with\n    JavaScript properties.\n\n-   **Heap(values, equals, compare)**\n\n    ```javascript\n    var Heap = require(\"collections/heap\");\n    ```\n\n    A collection that can always quickly (constant time) report its\n    largest value, with reasonable performance for incremental changes\n    (logarithmic), using a contiguous array as its backing storage.\n    However, it does not track the sorted order of its elements.\n\n-   **Iterator(iterable)**\n\n    ```javascript\n    var Iterator = require(\"collections/iterator\");\n    ```\n\n    A wrapper for any iterable that implements `iterate` or iterator the\n    implements `next`, providing a rich lazy traversal interface.\n\n-   **Array**\n\n    ```javascript\n    require(\"collections/shim-array\");\n    ```\n\n    An ordered collection of values with fast random access, push, and\n    pop, but slow splice. The `array` module provides extensions so it\n    hosts all the expressiveness of other collections.  The `shim-array`\n    module shims EcmaScript 5 methods onto the array prototype if they\n    are not natively implemented.\n\n-   **Object**\n\n    ```javascript\n    require(\"collections/shim-object\");\n    ```\n\n    Can be used as a mapping of owned string keys to arbitrary values.\n    The `object` module provides extensions for the `Object` constructor\n    that support the map collection interface and can delegate to\n    methods of collections, allowing them to gracefully handle both\n    object literals and collections.\n\n## Constructor Arguments\n\nFor all of these constructors, the argument `values` is an optional\ncollection of initial values, and may be an array.  If the `values` are\nin a map collection, the the values are taken, but the keys are ignored.\n\n-   **map**\n\n    The `map` argument is an optional collection to copy shallowly into\n    the new mapping.  The `map` may be an object literal.  If `map`\n    implements `keys`, it is treated as a mapping itself and copied.\n    Otherwise, if `map` implements `forEach`, it may be any collection\n    of `[key, value]` pairs.\n\n`equals(x, y)`, `compare(x, y)`, and `hash(value)` are all optional\narguments overriding the meaning of equality, comparability, and\nconsistent hashing for the purposes of the collection.  `equals` must\nreturn a boolean.  `compare` must return an integer with the same\nrelationship to zero as x to y.  `hash` should consistently return the\nsame string for any given object.\n\n-   **equals(x, y)**\n\n    The default `equals` operator is implemented in terms of `===`, but\n    treats `NaN` as equal to itself and `-0` as distinct from `+0`.  It\n    also delegates to an `equals` method of either the left or right\n    argument if one exists.  The default equality operator is shimmed as\n    `Object.equals`.\n\n-   **compare(x, y)**\n\n    The default `compare` operator is implemented in terms of `<` and\n    `>`.  It delegates to the `compare` method of either the left or\n    right argument if one exists.  It inverts the result if it uses the\n    falls to the right argument.  The default comparator is shimmed as\n    `Object.compare`.\n\n-   **hash(value)**\n\n    The default `hash` operator uses `toString` for values and provides\n    a [Unique Label][] for arbitrary objects.  The default hash is\n    shimmed as `Object.hash`.\n\n[Unique Label]: (http://wiki.ecmascript.org/doku.php?id=harmony:weak_maps#unique_labeler)\n\n-   **getDefault(key or value)**\n\n    The default `getDefault` function is `Function.noop`, which returns\n    `undefined`.  The fallback function is used when you `get` a\n    nonexistant value from any collection.  The `getDefault` function\n    becomes a member of the collection object, so `getDefault` is called\n    with the collection as `this`, so you can also use it to guarantee\n    that default values in a collection are retained, as in `MultiMap`.\n\n\n## Collection Methods\n\nWhere these methods coincide with the specification of an existing\nmethod of Array, Array is noted as an implementation.  `Array+` refers\nto shimmed arrays, as installed with the `array` module.  `Object`\nrefers to methods implemented on the `Object` constructor function, as\nopposed to the `Object.prototype`.  `Object+` in turn refers to methods\nshimmed on the object constructor by the `object` module.  These\nfunctions accept the object as the first argument instead of the `this`\nimplied argument.  ~~Strikethrough~~ indicates an implementation that\nshould exist but has not yet been made (Send a pull request!).\n\nThese are all of the collections:\n\n(Array, Array+, Object+, Iterator, List, Set, Map, MultiMap, WeakMap,\nSortedSet, SortedMap, LruSet, LruMap, SortedArray, SortedArraySet,\nSortedArrayMap, FastSet, FastMap, Dict)\n\n-   **has(key)**\n\n    Whether a value for the given key exists.\n\n    (Object+, Map, MultiMap, SortedMap, LruMap, SortedArrayMap, FastMap,\n    Dict)\n\n    **has(value, opt_equals)**\n\n    Whether a value exists in this collection.  This is slow for list\n    (linear), but fast (logarithmic) for SortedSet and SortedArraySet,\n    and very fast (constant) for Set.\n\n    (Array+, List, Set, SortedSet, LruSet, SortedArray, SortedArraySet,\n    FastSet)\n\n-   **get(key or index)**\n\n    The value for a key.  If a Map or SortedMap lacks a key, returns\n    `getDefault(key)`.\n\n    (Array+, Map, SortedMap, SortedArrayMap, WeakMap, Object+)\n\n    **get(value)**\n\n    Gets the equivalent value, or falls back to `getDefault(value)`.\n\n    (List, Set, SortedSet, LruSet, SortedArray, SortedArraySet, FastSet)\n\n-   **set(key or index, value)**\n\n    Sets the value for a key.\n\n    (Map, MultiMap, WeakMap, SortedMap, LruMap, SortedArrayMap, FastMap,\n    Dict)\n\n-   **add(value)**\n\n    Adds a value.  Ignores the operation and returns false if an\n    equivalent value already exists.\n\n    (Array+, List, Set, SortedSet, LruSet, SortedArray, SortedArraySet,\n    FastSet, Heap)\n\n    **add(value, key)**\n\n    Aliases `set(key, value)`, to assist generic methods used for maps,\n    sets, and other collections.\n\n-   **addEach(values)**\n\n    Copies values from another collection to this one.\n\n    (Array+, List, Set, SortedSet, LruSet, SortedArray, SortedArraySet,\n    FastSet, Heap)\n\n    **addEach(mapping)**\n\n    Copies entries from another collection to this map.  If the mapping\n    implements `keys` (indicating that it is a mapping) and `forEach`,\n    all of the key value pairs are copied.  If the mapping only\n    implements `forEach`, it is assumed to contain `[key, value]` arrays\n    which are copied instead.\n\n    (Object+, Map, MultiMap, SortedMap, LruMap, SortedArrayMap, FastMap,\n    Dict)\n\n-   **delete(key)**\n\n    Deletes the value for a given key.  Returns whether the key was\n    found.\n\n    (Map, MultiMap, WeakMap, SortedMap, LruMap, SortedArrayMap, FastMap,\n    Dict)\n\n    **delete(value)**\n\n    Deletes a value.  Returns whether the value was found.\n\n    (Set, SortedSet, LruSet, SortedArray, SortedArraySet, FastSet, Heap)\n\n    **delete(value, equals)**\n\n    Deletes the equivalent value.  Returns whether the value was found.\n\n    (Array+, List)\n\n-   **deleteEach(values or keys)**\n\n    Deletes every value or every value for each key.\n\n    (Array+, List, Set, Map, MultiMap, SortedSet, SortedMap,\n    LruSet, LruMap, SortedArray, SortedArraySet, SortedArrayMap,\n    FastSet, FastMap, Dict, Heap)\n\n-   **indexOf(value)**\n\n    Returns the position in the collection of a value, or `-1` if it is\n    not found.  Returns the position of the first of equivalent values.\n    For an Array this takes linear time.  For a SortedArray and\n    SortedArraySet, it takes logarithmic time to perform a binary\n    search.  For a SortedSet, this takes ammortized logarithmic time\n    since it incrementally updates the number of nodes under each\n    subtree as it rotates.\n\n    (Array, ~~List~~, SortedSet, SortedArray, SortedArraySet)\n\n-   **lastIndexOf(value)**\n\n    Returns the position in the collection of a value, or `-1` if it is\n    not found.  Returns the position of the last of equivalent values.\n\n    (Array, ~~List~~, SortedArray, SortedArraySet)\n\n-   **find(value, opt_equals)**\n\n    Finds a value.  For List and SortedSet, returns the node at which\n    the value was found.  For SortedSet, the optional `equals` argument\n    is ignored.\n\n    (Array+, List, SortedSet)\n\n-   **findLast(value, opt_equals)**\n\n    Finds the last equivalent value, returning the node at which the\n    value was found.\n\n    (Array+, List, SortedArray, SortedArraySet)\n\n-   **findLeast()**\n\n    Finds the smallest value, returning the node at which it was found,\n    or undefined.  This is fast (logarithmic) and performs no rotations.\n\n    (SortedSet)\n\n-   **findLeastGreaterThan(value)**\n\n    Finds the smallest value greater than the given value.  This is fast\n    (logarithic) but does cause rotations.\n\n    (SortedSet)\n\n-   **findLeastGreaterThanOrEqual(value)**\n\n    Finds the smallest value greater than or equal to the given value.\n    This is fast (logarithmic) but does cause rotations.\n\n    (SortedSet)\n\n-   **findGreatest()**\n\n    (SortedSet)\n\n-   **findGreatestLessThan(value)**\n\n    (SortedSet)\n\n-   **findGreatestLessThanOrEqual(value)**\n\n    (SortedSet)\n\n-   **push(...values)**\n\n    Adds values to the end of a collection.\n\n    (Array, List)\n\n    **push(...values)** for non-dequeues\n\n    Adds values to their proper places in a collection.\n    This method exists only to have the same interface as other\n    collections.\n\n    (Set, SortedSet, LruSet, SortedArray, SortedArraySet, FastSet, Heap)\n\n-   **unshift(...values)**\n\n    Adds values to the beginning of a collection.\n\n    (Array, List)\n\n    **unshift(...values)** for non-dequeues\n\n    Adds values to their proper places in a collection.\n    This method exists only to have the same interface as other\n    collections.\n\n    (Set, SortedSet, LruSet, SortedArray, SortedArraySet, FastSet)\n\n-   **pop()**\n\n    Removes and returns the value at the end of a collection.  For a\n    Heap, this means the greatest contained value, as defined by the\n    comparator.\n\n    (Array, List, Set, SortedSet, LruSet, SortedArray, SortedArraySet,\n    Heap)\n\n-   **shift()**\n\n    Removes and returns the value at the beginning of a collection.\n\n    (Array, List, Set, SortedSet, LruSet, SortedArray, SortedArraySet)\n\n-   **peek()**\n\n    Returns the last value in an ordered collection.\n\n    (List)\n\n-   **poke(value)**\n\n    Replaces the last value in an ordered collection.\n\n    (List)\n\n-   **slice(start, end)**\n\n    Returns an array of the values contained in the\n    half-open interval [start, end), that is, including the start and\n    excluding the end.  For lists and arrays, both terms may be numeric\n    positive or negative indicies.  For a list, either term may be a\n    node.\n\n    (Array, List, SortedSet, SortedArray, SortedArraySet)\n\n-   **splice(start, length, ...values)**\n\n    Works as with an array, but for a list, the start may be an index or\n    a node.\n\n    (Array, List, SortedArray, SortedSet, SortedArraySet)\n\n-   **swap(start, length, values)**\n\n    Performs a splice without variadic arguments.\n\n    (Array+, List, SortedArray, SortedSet, SortedArraySet)\n\n-   **clear()**\n\n    Deletes the all values.\n\n    (Array+, Object+, List, Set, Map, MultiMap, SortedSet,\n    SortedMap, LruSet, LruMap, SortedArray, SortedArraySet,\n    SortedArrayMap, FastSet, FastMap, Dict, Heap)\n\n-   **sort(compare)**\n\n    Sorts a collection in place.  The comparator by only be a function.\n    The default comparator coerces unlike types rather than fail to\n    compare.\n\n    (Array)\n\n-   **sorted(compare, by, order)**\n\n    Returns a collection as an array in sorted order.  Accepts an\n    optional `compare(x, y)` function, `by(property(x))` function, and\n    `order` indicator, `-1` for descending, `1` for ascending, `0` for\n    stable.\n\n    Instead of a `compare` function, the comparator can be an object\n    with `compare` and `by` functions.  The default `compare` value is\n    `Object.compare`.\n\n    The `by` function must be a function that accepts a value from the\n    collection and returns a representative value on which to sort.\n\n    (Array+, List, Set, Map, SortedSet, LruSet, SortedArray,\n    SortedArraySet, FastSet, Heap)\n\n-   **group(callback, thisp, equals)**\n\n    Returns an array of [key, equivalence class] pairs where every\n    element from the collection is placed into an equivalence class\n    if they have the same corresponding return value from the given\n    callback.\n\n    (Array+, Object+, List, Set, Map, MultiMap, SortedSet,\n    SortedMap, LruSet, LruMap, SortedArray, SortedArraySet,\n    SortedArrayMap, FastSet, FastMap, Dict, Heap, Iterator)\n\n-   **reverse()**\n\n    Reverses a collection in place.\n\n    (Array, List)\n\n-   **reversed()**\n\n    Returns a collection of the same type with this collection's\n    contents in reverse order.\n\n    (Array, List)\n\n-   **enumerate(start=0)**\n\n    Returns an array of [index, value] pairs from the source collection,\n    starting with the given index.\n\n-   **concat(...iterables)**\n\n    Produces a new collection of the same type containing all the values\n    of itself and the values of any number of other collections.  Favors\n    the last of duplicate values.  For map-like objects, the given\n    iterables are treated as map-like objects and each successively\n    updates the result.  Array is like a map from index to value.  List,\n    Set, and SortedSet are like maps from nodes to values.\n\n    (Array, ~~Object+~~, Iterator, List, Set, Map, MultiMap,\n    SortedSet, SortedMap, LruSet, LruMap, SortedArray, SortedArraySet,\n    SortedArrayMap, FastSet, FastMap, Dict, Heap)\n\n-   **keys()**\n\n    Returns an array of the keys.\n\n    (Object, Map, MultiMap, SortedMap, LruMap, SortedArrayMap, FastMap,\n    Dict)\n\n-   **values()**\n\n    Returns an array of the values\n\n    (Object+, Map, MultiMap, SortedMap, LruMap, SortedArrayMap, FastMap,\n    Dict)\n\n-   **entries()**\n\n    Returns an array of `[key, value]` pairs for each entry.\n\n    (Object+, Map, MultiMap, SortedMap, LruMap, SortedArrayMap, FastMap,\n    Dict)\n\n-   **reduce(callback(result, value, key, object, depth), basis,\n    thisp)**:\n\n    (Array, Iterator, List, Set, Map, MultiMap, SortedSet, SortedMap,\n    LruSet, LruMap, SortedArray, SortedArraySet, SortedArrayMap,\n    FastSet, FastMap, Dict, Heap)\n\n-   **reduceRight(callback(result, value, key, object, depth), basis,\n    thisp)**:\n\n    (Array, List, SortedSet, ~~SortedMap~~, SortedArray, SortedArraySet,\n    ~~SortedArrayMap~~, Heap)\n\n-   **forEach(callback(value, key, object, depth), thisp)**\n\n    Calls the callback for each value in the collection.  The iteration\n    of lists is resilient to changes to the list.  Particularly, nodes\n    added after the current node will be visited and nodes added before\n    the current node will be ignored, and no node will be visited twice.\n\n    (Array, Object+, Iterator, List, Set, Map, MultiMap, WeakMap,\n    SortedSet, SortedMap, LruSet, LruMap, SortedArray, SortedArraySet,\n    SortedArrayMap, FastSet, FastMap, Dict, Heap)\n\n-   **map(callback(value, key, object, depth), thisp)**\n\n    (Array, Object+, Iterator, List, Set, Map, MultiMap, WeakMap,\n    SortedSet, SortedMap, LruSet, LruMap, SortedArray, SortedArraySet,\n    SortedArrayMap, FastSet, FastMap, Dict, Heap)\n\n-   **toArray()**\n\n    (Array+, Iterator, List, Set, Map, MultiMap, SortedSet, SortedMap,\n    LruSet, LruMap, SortedArray, SortedArraySet, SortedArrayMap,\n    FastSet, FastMap, Dict, Heap)\n\n-   **toObject()**\n\n    Converts any collection to an object, treating this collection as a\n    map-like object.  Array is like a map from index to value.\n\n    (Array+ Iterator, List, Map, MultiMap, SortedMap, LruMap,\n    SortedArrayMap, FastMap, Dict, Heap)\n\n-   **filter(callback(value, key, object, depth), thisp)**\n\n    (Array, Iterator, List, Set, Map, MultiMap, SortedSet, SortedMap,\n    LruSet, LruMap, SortedArray, SortedArraySet, SortedArrayMap,\n    FastSet, FastMap, Dict, Heap)\n\n-   **every(callback(value, key, object, depth), thisp)**\n\n    Whether every value passes a given guard.  Stops evaluating the\n    guard after the first failure.  Iterators stop consuming after the\n    the first failure.\n\n    (Array, Iterator, List, Set, Map, MultiMap, SortedSet, SortedMap,\n    LruSet, LruMap, SortedArray, SortedArraySet, SortedArrayMap,\n    FastSet, FastMap, Dict, Heap)\n\n-   **some(callback(value, key, object, depth), thisp)**\n\n    Whether there is a value that passes a given guard.  Stops\n    evaluating the guard after the first success.  Iterators stop\n    consuming after the first success.\n\n    (Array, Iterator, List, Set, Map, MultiMap, SortedSet, SortedMap,\n    LruSet, LruMap, SortedArray, SortedArraySet, SortedArrayMap,\n    FastSet, FastMap, Dict, Heap)\n\n-   **any()**\n\n    Whether any value is truthy.\n\n    (Array+, Iterator, List, Set, Map, MultiMap, SortedSet, SortedMap,\n    LruSet, LruMap, SortedArray, SortedArraySet, SortedArrayMap,\n    FastSet, FastMap, Dict, Heap)\n\n-   **all()**\n\n    Whether all values are truthy.\n\n    (Array+, Iterator, List, Set, Map, MultiMap, SortedSet, SortedMap,\n    LruSet, LruMap, SortedArray, SortedArraySet, SortedArrayMap,\n    FastSet, FastMap, Dict, Heap)\n\n-   **min()**\n\n    The smallest value.  This is fast for sorted collections (logarithic\n    for SortedSet, constant for SortedArray, SortedArraySet, and\n    SortedArrayMap), but slow for everything else (linear).\n\n    (Array+, Iterator, List, Set, Map, MultiMap, SortedSet, SortedMap,\n    LruSet, LruMap, SortedArray, SortedArraySet, SortedArrayMap,\n    FastSet, FastMap, Dict)\n\n-   **max()**\n\n    The largest value.  This is fast for sorted collections (logarithic\n    for SortedSet, constant for SortedArray, SortedArraySet, and\n    SortedArrayMap), but slow for everything else (linear).\n\n    (Array+, Iterator, List, Set, Map, MultiMap, SortedSet, SortedMap,\n    LruSet, LruMap, SortedArray, SortedArraySet, SortedArrayMap,\n    FastSet, FastMap, Dict, Heap)\n\n-   **one()**\n\n    Any single value, or throws an exception if there are no values.\n    This is very fast (constant) for all collections.  For a sorted set,\n    the value is not deterministic and depends on what value was most\n    recently accessed.\n\n    (Array+, List, Set, Map, MultiMap, SortedSet, SortedMap, LruSet,\n    LruMap, SortedArray, SortedArray, SortedArraySet, SortedArrayMap,\n    FastSet, FastMap, Dict, Heap)\n\n-   **only()**\n\n    The one and only value, or throws an exception if there are no\n    values or more than one value.\n\n    (Array+, List, Set, Map, MultiMap, SortedSet, SortedMap, LruSet,\n    LruMap, SortedArray, SortedArray, SortedArraySet, SortedArrayMap,\n    FastSet, FastMap, Dict, Heap)\n\n-   **sum()**\n\n    (Array+, Iterator, List, Set, Map, MultiMap, SortedSet,\n    SortedMap, LruSet, LruMap, SortedArray, SortedArraySet,\n    SortedArrayMap, FastSet, FastMap, Dict)\n\n-   **average()**\n\n    (Array+, Iterator, List, Set, Map, MultiMap, SortedSet,\n    SortedMap, LruSet, LruMap, SortedArray, SortedArraySet,\n    SortedArrayMap, FastSet, FastMap, Dict)\n\n-   **flatten()**\n\n    (Array+, Iterator, List, Set, Map, MultiMap, SortedSet,\n    SortedMap, LruSet, LruMap, SortedArray, SortedArraySet,\n    SortedArrayMap, FastSet, FastMap, Dict, Heap)\n\n-   **zip(...collections)**\n\n    (Array+, Iterator, List, Set, Map, MultiMap, SortedSet,\n    SortedMap, LruSet, LruMap, SortedArray, SortedArraySet,\n    SortedArrayMap, FastSet, FastMap, Dict, Heap)\n\n-   **enumrate(zero)**\n\n    (Array+, Iterator, List, Set, Map, MultiMap, SortedSet, SortedMap,\n    LruSet, LruMap, SortedArray, SortedArraySet, SortedArrayMap,\n    FastSet, FastMap, Dict, Heap)\n\n-   **clone(depth, memo)**\n\n    Replicates the collection.  Clones the values deeply, to the\n    specified depth, using the memo to resolve reference cycles.  (which\n    must the `has` and `set` parts of the Map interface, allowing\n    objects for keys)  The default depth is infinite and the default\n    memo is a WeakMap.\n\n    `Object.clone` can replicate object literals inheriting directly\n    from `Object.prototype` or `null`, or any object that implements\n    `clone` on its prototype.  Any other object causes `clone` to throw\n    an exception.\n\n    The `clone` method on any other objects is not intended to be used\n    directly since they do not necessarily supply a default depth and\n    memo.\n\n    (Array+, Object+, List, Set, Map, MultiMap, SortedSet,\n    SortedMap, LruSet, LruMap, SortedArray, SortedArraySet,\n    SortedArrayMap, FastSet, FastMap, Dict, Heap)\n\n-   **constructClone(values)**\n\n    Replicates a collection shallowly.  This is used by each `clone`\n    implementation to create a new collection of the same type, with the\n    same options (`equals`, `compare`, `hash` options), but it leaves\n    the job of deeply cloning the values to the more general `clone`\n    method.\n\n    (Array+, Object+, List, Set, Map, MultiMap, SortedSet,\n    SortedMap, LruSet, LruMap, SortedArray, SortedArraySet,\n    SortedArrayMap, FastSet, FastMap, Dict, Heap)\n\n-   **equals(that, equals)**\n\n    (Array+, Object+, List, Set, Map, MultiMap, SortedSet, SortedMap,\n    LruSet, LruMap, ~~SortedArray~~, SortedArraySet, SortedArrayMap,\n    FastSet, FastMap, Dict)\n\n-   **compare(that)**\n\n    (Array+, Object+, List, ~~SortedArray~~, ~~SortedArraySet~~)\n\n-   **iterate()**\n\n    Produces an iterator with a `next` method.  You may elect to get\n    richer iterators by wrapping this iterator with an `Iterator` from\n    the `iterator` module.  Iteration order of lists is resilient to\n    changes to the list.\n\n    (Array+, Iterator, List, Set, SortedSet, LruSet, SortedArray,\n    SortedArraySet, FastSet)\n\n    **iterate(start, end)**\n\n    Returns an iterator for all values at indicies in the half-open\n    interval [start, end), that is, greater than start, and less than\n    end.\n\n    (Array+)\n\n    **iterate(start, end)**\n\n    Returns an iterator for all values in the half-open interval [start,\n    end), that is, greater than start, and less than end.  The iterator\n    is resilient against changes to the data.\n\n    (SortedSet)\n\n-   **log(charmap, callback(node, write, writeAbove), log, logger)**\n\n    Writes a tree describing the internal state of the data structure to\n    the console.\n\n    `charmap` is an object that notes which characters to use to draw\n    lines.  By default, this is the `TreeLog.unicodeRound` property from the\n    `tree-log` module.  `TreeLog.unicodeSharp` and `TreeLog.ascii` are\n    alternatives.  The properties are:\n\n    -   intersection: ╋\n    -   through: ━\n    -   branchUp: ┻\n    -   branchDown: ┳\n    -   fromBelow: ╭\n    -   fromAbove: ╰\n    -   fromBoth: ┣\n    -   strafe: ┃\n\n    `callback` is a customizable function for rendering each node of the tree.\n    By default, it just writes the value of the node.  It accepts the node and\n    a writer functions.  The `write` function produces the line on which the\n    node joins the tree, and each subsequent line.  The `writeAbove` function\n    can write lines before the branch.\n\n    `log` and `loger` default to `console.log` and `console`.  To write\n    the representation to an array instead, they can be `array.push` and\n    `array`.\n\n    (SortedSet)\n\n\n### Iterator\n\n-   **dropWhile(callback(value, index, iterator), thisp)**\n\n-   **takeWhile(callback(value, index, iterator), thisp)**\n\n-   **mapIterator(callback(value, index, iterator))**\n\n    Returns an iterator for a mapping on the source values.  Values are\n    consumed on demand.\n\n-   **filterIterator(callback(value, index, iterator))**\n\n    Returns an iterator for those values from the source that pass the\n    given guard.  Values are consumed on demand.\n\n-   **zipIterator(...iterables)**\n\n    Returns an iterator that incrementally combines the respective\n    values of the given iterations.\n\n-   **enumerateIterator(start = 0)**\n\n    Returns an iterator that provides [index, value] pairs from the\n    source iteration.\n\n\n### Iterator utilities\n\n-   **cycle(iterable, times)**\n\n-   **concat(iterables)**\n\n-   **transpose(iterables)**\n\n-   **zip(...iterables)**\n\n    Variadic transpose.\n\n-   **chain(...iterables)**\n\n    Variadic concat.\n\n-   **range(start, stop, step)**\n\n    Iterates from start to stop by step.\n\n-   **count(start, step)**\n\n    Iterates from start by step, indefinitely.\n\n-   **repeat(value, times)**\n\n    Repeats the given value either finite times or indefinitely.\n\n\n## Change Listeners\n\nAll collections support change listeners.  There are three types of\nchanges.  Property changes, map changes, and range changes.\n\n### Property Changes\n\n`PropertyChanges` from the `listen/property-changes` module can\nconfigure listeners for property changes to specific keys of any object.\n\nWith the `listen/array-changes` module required, `PropertyChanges` can\nalso listen to changes to the length and indexed properties of an array.\nThe only caveat is that watched arrays can only modify their contents\nwith method calls like `array.push`.  All methods of a watched array\nsupport change dispatch.  In addition, arrays have a `set` method to\nmake setting the value at a particular index observable.\n\n-   PropertyChanges.addOwnPropertyChangeListener(object, key, listener, before)\n-   PropertyChanges.removeOwnPropertyChangeListener(object, key, listener, before)\n-   PropertyChanges.dispatchOwnPropertyChange(object, key, value, before)\n-   PropertyChanges.addBeforeOwnPropertyChangeListener(object, key, listener)\n-   PropertyChanges.removeBeforeOwnPropertyChangeListener(object, key, listener)\n-   PropertyChanges.dispatchBeforeOwnPropertyChange(object, key, value)\n-   PropertyChanges.getOwnPropertyChangeDescriptor(object, key)\n\nAll of these functions delegate to methods of the same name if one\nexists on the object.\n\n-   object.addOwnPropertyChangeListener(key, listener, before)\n-   object.removeOwnPropertyChangeListener(key, listener, before)\n-   object.dispatchOwnPropertyChange(key, value, before)\n-   object.addBeforeOwnPropertyChangeListener(key, listener)\n-   object.removeBeforeOwnPropertyChangeListener(key, listener)\n-   object.dispatchBeforeOwnPropertyChange(key, value)\n-   object.getOwnPropertyChangeDescriptor(key)\n\nAdditionally, `PropertyChanges.prototype` can be **mixed into** other\ntypes of objects to support the property change dispatch interface.  All\ncollections support this interface.\n\nThe **listener** for a property change receives the arguments `value`,\n`key`, and `object`, just as a `forEach` or `map` callback.  The\nlistener may alternately be a delegate object that implements one of\nthese methods:\n\n-   listener.handle + **key** + Change **or** WillChange\n-   listener.handleProperty + Change **or** WillChange\n-   listener.call\n\n### Map Changes\n\nA map change listener receives notifications for the creation, removal,\nor updates for any entry in a map data structure.\n\nWith the `listen/array-changes` module required, `Array` can also\ndispatch map changes for the values at each index.\n\n-   collection.addMapChangeListener(listener, token, before)\n-   collection.removeMapChangeListener(listener, token, before)\n-   collection.dispatchMapChange(key, value, before)\n-   collection.addBeforeMapChangeListener(listener)\n-   collection.removeBeforeMapChangeListener(listener)\n-   collection.dispatchBeforeMapChange(key, value)\n-   collection.getMapChangeDescriptor()\n\nThe **listener** for a map change receives the `value`, `key`, and\ncollection `object` as arguments, the same pattern as a `forEach` or\n`map` callback.  In the after change phase, a value of `undefined` may\nindicate that the value was deleted or set to `undefined`.  In the\nbefore change phase, a value of `undefined` may indicate the the value\nwas added or was previously `undefined`.\n\nThe listener may be a delegate object with one of the following methods,\nin order of precedence:\n\n-   listener.handleMap + Change **or** WillChange\n-   listener.handle + **token** + Map + Change **or** WillChange\n-   listener.call\n\nThe `listen/map-changes` module exports a map changes **mixin**.  The\nmethods of `MaxChanges.prototype` can be copied to any collection that\nneeds this interface.  Its mutation methods will then need to dispatch\nmap changes.\n\n### Range Changes\n\nA range change listener receives notifications when a range of values at\na particular position is added, removed, or replaced within an ordered\ncollection.\n\n-   collection.**add**RangeChange**Listener**(listener, token, before)\n-   collection.**remove**RangeChange**Listener**(listener, token, before)\n-   collection.**dispatch**RangeChange(plus, minus, index, before)\n-   collection.add**Before**RangeChange**Listener**(listener)\n-   collection.remove**Before**RangeChange**Listener**(listener)\n-   collection.dispatch**Before**RangeChange(plus, minus, index)\n-   collection.**get**RangeChange**Descriptor**()\n\nThe **listener** for a range change is a function that accepts `plus`,\n`minus`, and `index` arguments.  `plus` and `minus` are the values that\nwere added or removed at the `index`.  Whatever operation caused these\nchanges is equivalent to:\n\n```javascript\nvar minus = collection.splice(index, minus.length, ...plus)\n```\n\nThe listener can alternately be a delegate object with one of the\nfollowing methods in order of precedence:\n\n-   handle + **token** + Range + Change **or** WillChange\n-   handleRange + Change **or** WillChange\n-   call\n\nThe following support range change dispatch:\n\n-   `Array` with `require(\"collections/listen/array-changes\")`\n-   `SortedSet`\n-   `SortedArray`\n-   `SortedArraySet`\n\nThe `listen/range-changes` module exports a range changes **mixin**.\nThe methods of `RangeChanges.prototype` can be copied to any collection\nthat needs this interface.  Its mutation methods will need to dispatch\nthe range changes.\n\nAll **descriptors** are objects with the properties `changeListeners`\nand `willChangeListeners`.  Both are arrays of listener functions or\nobjects, in the order in which they were added.\n\n\n## Miscellanea\n\n### Set and Map\n\nSet and map are like hash tables, but not implemented with a block of\nmemory as they would be in a lower-level language.  Most of the work of\nproviding fast insertion and lookup based on a hash is performed by the\nunderlying plain JavaScript object.  Each key of the object is a hash\nstring and each value is a List of values with that hash.  The inner\nlist resolves collisions.  With a good `hash` method, the use of the\nlist can be avoided.\n\nSets and maps both have a `log` function that displays the internal\nstructure of the bucket list in an NPM-style.\n\n```\n┣━┳ 1\n┃ ┗━━ {\"key\":1,\"value\":\"a\"}\n┣━┳ 2\n┃ ┣━━ {\"key\":2,\"value\":\"c\"}\n┃ ┗━━ {\"key\":2,\"value\":\"d\"}\n┗━┳ 3\n  ┗━━ {\"key\":3,\"value\":\"b\"}\n```\n\n\n### Sorted Set and Sorted Map\n\nA binary splay tree is a balanced binary tree that rotates the most\nfrequently used entries toward the root such that they can be accessed the\nmost quickly.  `sorted-set` and `sorted-map` are backed by a splay tree.\n\nAll map implementations use an underlying set implementation.  Any map\ncan be implemented trivially atop a set by wrapping `compare`, `equals`,\nor `hash` to operate on the key of an entry.\n\nThe sorted set has a `root` node.  Each node has a `left` and `right`\nproperty, which may be null.  Nodes are returned by all of the \"find\"\nfunctions, and provided as the `key` argument to callbacks.\n\nBoth `sorted-set` and `sorted-map` implement a `log` function which can\nproduce NPM-style visualizations of the internal state of the sorted\ntree.\n\n```\n> set.log(SortedSet.ascii)\n  .-+ -3\n  | '-- -2\n.-+ -1\n+ 0\n| .-- 1\n'-+ 2\n  '-- 3\n```\n\n```\n> set.log(SortedSet.unicodeRound)\n  ╭━┳ -3\n  ┃ ╰━━ -2\n╭━┻ -1\n╋ 0\n┃ ╭━┳ 1\n┃ ┃ ╰━━ 2\n╰━┻ 3\n```\n\n\n### Object and Function Shims\n\nThe collection methods on the `Object` constructor all polymorphically\ndelegate to the corresponding method of any object that implements the\nmethod of the same name.  So, `Object.has` can be used to check whether\na key exists on an object, or in any collection that implements `has`.\nThis permits the `Object` interface to be agnostic of the input type.\n\n`Array.from` creates an array from any iterable.\n\n`Array.unzip` transposes a collection of arrays, so rows become columns.\n\n`Array.empty` is an empty array, frozen if possible.  Do not modify it.\n\n`Object.from` creates an object from any map or collection.  For arrays\nand array-like collections, uses the index for the key.\n\n`Object.empty` is an empty object literal.\n\n`Object.isObject(value)` tests whether it is safe to attempt to access\nproperties of a given value.\n\n`Object.is(x, y)` compares objects for exact identity and is a good\nalternative to `Object.equals` in many collections.\n\n`Object.getValueOf(value)` safely and idempotently returns the value of\nan object or value by only calling the `valueOf()` if the value\nimplements that method.\n\n`Object.owns` is a shorthand for `Object.prototype.hasOwnProperty.call`.\n\n`Object.can(value, name)` checks whether an object implements a method\non its prototype chain.  An owned function property does not qualify as\na method, to aid in distinguishing \"static\" functions.\n\n`Object.concat(...maps)` and `Object.from(entries)` construct an object\nby adding the entries of other objects in order.  The maps can be other\nobjects, arrays of entries, or map alike collections.\n\n`Function.noop` is returns undefined.\n\n`Function.identity` returns its first argument.\n\n`Function.by(relation)` creates a comparator from a relation function.\n\n`Function.get(key)` creates a relation that returns the value for the\nproperty of a given object.\n\n\n### References\n\n- a SplayTree impementation buried in Fedor Indutny’s super-secret\n  [Callgrind](https://github.com/indutny/callgrind.js). This\n  implementation uses parent references.\n- a SplayTree implementation adapted by [Paolo\n  Fragomeni](https://github.com/hij1nx/forest) from the V8 project and\n  based on the top-down splaying algorithm from \"Self-adjusting Binary\n  Search Trees\" by Sleator and Tarjan. This does not use or require\n  parent references, so I favored it over Fedor Indutny’s style.\n- the interface of ECMAScript harmony [simple maps and\n  sets](http://wiki.ecmascript.org/doku.php?id=harmony:simple_maps_and_sets)\n- a SplayTree implementation from [JavaScript data\n  structures](derrickburns/Javascript-Data-Structures) mainted by\n  Derrick Burns that supports change-resilient iterators and a\n  comprehensive set of introspection functions.\n\n### Future work\n\nGoals\n\n- automate the generation of the method support tables in readme and\n  normalize declaration order\n- comprehensive specs and spec coverage tests\n- fast list splicing\n- dict map changes\n- revise map changes to use separate handlers for add/delete\n- revise tokens for range and map changes to specify complete alternate\n  delegate methods, particularly for forwarding directly to dispatch\n- implement on/once/off listeners\n- Make it easier to created a SortedSet with a criterion like\n  Function.by(Function.get('name'))\n\nMore possible collections\n\n- sorted-order (sorted, can contain duplicates, perhaps backed by splay\n  tree with relaxation on the uniqueness invariant)\n- sorted-multi-map (sorted, can contain duplicate entries, backed by\n  sorted-map)\n- trie-set\n- trie-map\n- immutable-* (mutation functions return new objects that largely share\n  the previous version's internal state, some perhaps backed by a hash\n  trie)\n\n","readmeFilename":"README.md","_id":"collections@0.1.24","_from":"collections@~0.1.23","directories":{"lib":"./"},"hash":"5ea2535","mappings":{"weak-map":{"name":"weak-map","hash":"7aba37c","location":"../weak-map@7aba37c/"}},"production":true,"useScriptInjection":true}})
;
//*/
montageDefine("3343e6d","package.json",{exports: {"name":"mousse","version":"0.1.4","description":"Deserialization of JavaScript objects","keywords":["deserialization","serialization"],"author":{"name":"António Afonso","email":"antonio.afonso@gmail.com","url":"http://github.com/aadsm/"},"contributors":[{"name":"António Afonso","email":"antonio.afonso@gmail.com","url":"http://github.com/aadsm/"}],"bugs":{"url":"http://github.com/montagejs/mousse/issues"},"licenses":[{"type":"BSD","url":"http://github.com/montagejs/mousse/raw/master/LICENSE.md"}],"repository":{"type":"git","url":"http://github.com/montagejs/mousse.git"},"main":"mousse","dependencies":{"q":"~0.9.6","collections":"~0.1.21"},"devDependencies":{"jasmine-node":"~1.9.1"},"scripts":{"test":"./node_modules/.bin/jasmine-node spec"},"readme":"# Mousse\n\nA serialization library that serializes graphs of JavaScript objects.\n\nIts main purpose is to provide the features that are missing in JSON and a mechanism to easily extend the serialization format with new types or custom JavaScript objects.\n\nThis is the library used by the Montage framework to manage the serialization of objects in its templates.\n\n## What it does that JSON doesn't\n\n * Named objects\n * References and circular references (no more `TypeError: Converting circular structure to JSON`)\n * Regular Expressions\n * Custom Types\n * Asynchronous revivers\n\n## API Reference\n\n**`Serializer()`**\n\n-   **`serializeObject(object)`**\n\n    Serializes an object into a string.\n\n-   **`serialize(objects)`**\n\n    Serializes objects into a string, each object is passed with a label associated with it. Objects are passed in an object literal `{label1: object1, label2: object2, ..., labelN: objectN}`.\n\n-   **`setSerializationIndentation(indentation)`**\n\n    Set the indentation level of the serialization string (in number of spaces).\n\n**`Deserializer(serializationString)`**\n\n-   _**`constructor`**_\n\n    Creates a deserialization object to deserialize the objects serialized in `serializationString`.\n\n-   **`deserializeObject()`**\n\n    Returns a promise for an object that was serialized with `serializeObject`.\n\n-   **`deserialize(instances)`**\n\n    Returns a promise for the objects that were serialized with `serialize`. This result is an object literal with the deserialized objects and their respective labels: `{label1: object1, label2: object2, ..., labelN: objectN}`.\n\n    The `instances` parameter allows to override the deserialization of specific objects by using the instance passed instead, they are passed in an object literal: `{label1: object1, label2: object2, ..., labelN: objectN}`.\n\n## Serialization of JavaScript objects\n### Multiple Objects\n```javascript\nvar Serializer = require(\"mousse\").Serializer;\n\nvar object = {\n    x: 2,\n    y: 4\n};\n\nvar array = [1, 2, 3];\n\nvar serializationString = new Serializer().serialize({foo: object, bar: array});\n```\n\n### Single Object\n\nWhen serializing a single object there's no need to provide a label:\n\n```javascript\nvar Serializer = require(\"mousse\").Serializer;\n\nvar object = {\n    x: 2,\n    y: 4\n};\n\nvar serializationString = new Serializer().serializeObject(object);\n```\n\nThere's also a shorthand function to serialize a single object:\n\n```javascript\nvar serialize = require(\"mousse\").serialize;\n\nvar object = {\n    x: 2,\n    y: 4\n};\n\nvar serializationString = serialize(object);\n```\n\n## Deserialization of JavaScript objects\n### Multiple Objects\n```javascript\nvar Deserializer = require(\"mousse\").Deserializer,\n    deserializer = new Deserializer(serializationString);\n\ndeserializer.deserialize()\n.then(function(objects) {\n    // deserialized objects are in objects\n});\n```\n\n### Single Object\n```javascript\nvar Deserializer = require(\"mousse\").Deserializer,\n    deserializer = new Deserializer(serializationString);\n\ndeserializer.deserializeObject()\n.then(function(object) {\n    //\n});\n```\n\nAgain, like for serialization, there's a shorthand function to deserialize a single object:\n```javascript\nvar Deserializer = require(\"mousse\").deserialize;\n\ndeserialize(serializationString)\n.then(function(object) {\n    //\n});\n```\n\nConsecutive calls to `deserializer.deserialize()` will create a new set of objects from the serialization.\n\n## Serialization Format\n\nThe serialization format is inspired by JSON and it may even be considered as an extension. By itself the format is a JSON valid object.\n\nInstead of only serializing a single object, Mousse is able to serialize several independent objects by providing a label for each one. We can look at it as a dictionary.\n\nThe base format of the serialization is thus an object with as many entries as labeled objects:\n```javascript\n{\n    \"label1\": {\n        \"value\": <object1 serialization>\n    },\n\n    \"label2\": {\n        \"value\": <object2 serialization>\n    },\n\n    ...,\n\n    \"labelN\": {\n        \"value\": <objectN serialization>\n    }\n}\n```\nThe following JavaScript objects are supported:\n\n    * string\n    * number\n    * boolean\n    * null\n    * array\n    * object literal\n    * regular expression\n    * references\n\nNative JavaScript objects are stored just like their JSON representation with the exception of regular expressions (which are not supported by JSON).\n\n### String\n```javascript\nserialize({string: \"a string\"})\n\n{\n    \"string\": {\n        \"value\": \"a string\"\n    }\n}\n```\n### Number\n```javascript\nserialize({number: 42})\n\n{\n    \"number\": {\n        \"value\": 42\n    }\n}\n```\n### Boolean\n```javascript\nserialize({bool: true})\n\n{\n    \"bool\": {\n        \"value\": true\n    }\n}\n```\n### Null\n```javascript\nserialize({nil: null})\n\n{\n    \"nil\": {\n        \"value\": null\n    }\n}\n```\n### Array\n```javascript\nserialize({array: [1, 2, 3]})\n\n{\n    \"array\": {\n        \"value\": [1, 2, 3]\n    }\n}\n```\n### Object Literal\n```javascript\nserialize({object: {x: 2, y: 4}})\n\n{\n    \"object\": {\n        \"value\": {\n            \"x\": 2,\n            \"y\": 4\n        }\n    }\n}\n```\n### Regular Expression\n```javascript\nserialize({regexp: /regexp/gi})\n\n{\n    \"regexp\": {\n        \"value\": {\"/\": {\n            \"source\": \"regexp\",\n            \"flags\": \"gi\"\n        }}\n    }\n}\n```\n### References\n\nSince objects have labels it is possible to serialize a reference to an object instead of serializing the entire object again as it happens in JSON:\n```javascript\nvar manager = {\n    name: \"Foo\"\n}\n\nvar employee = {\n    name: \"Bar\",\n    manager: manager\n}\n\nserialize({manager: manager, employee: employee})\n\n{\n    \"manager\": {\n        \"value\": {\n            \"name\": \"Foo\"\n        }\n    },\n\n    \"employee\": {\n        \"value\": {\n            \"name\": \"Bar\",\n            \"manager\": {\"@\": \"manager\"}\n        }\n    }\n}\n```\nReferences also solves cycles in an object graph:\n```javascript\nvar object = {};\nobject.self = object;\n\nserialize({object: object});\n\n{\n    \"object\": {\n        \"value\": {\n            \"self\": {\"@\": \"object\"}\n        }\n    }\n}\n```\nWhen an object is referred more than once it will automatically be assigned a label and only references will be used to refer to it.\n```javascript\nvar array = [1, 2, 3];\nvar object = {\n    foo: array,\n    bar: array\n}\n\nserialize({object: object});\n\n{\n    \"object\": {\n        \"value\": {\n            \"foo\": {\"@\": \"array\"},\n            \"bar\": {\"@\": \"array\"}\n        }\n    },\n\n    \"array\": {\n        \"value\": [1, 2, 3]\n    }\n}\n```\n## Custom Objects\n\nRoot objects with the `value` property represent JavaScript objects. It is possible to define other types of objects by adding the necessary logic to recognize them and create them.\n\n### Serialization\n\nThe logic to serialize a custom object is handled by the Visitor object. This is the object that visits the graph of objects traversed during serialization and knows what data to store for each type.\nThe interface expects a `getTypeOf` function that returns the type of the object and a `visit<Type>` function that knows what data to store from the object. Multiple `visit<Type>` can be defined as long as `getTypeOf` returns different types.\n\n```javascript\nVisitor.addCustomObjectVisitor({\n    getTypeOf: function(value) {\n        if (value instanceof Map) {\n            return \"Map\";\n        }\n    },\n\n    visitMap: function(malker, visitor, object, name) {\n        var map = visitor.builder.createCustomObject();\n            mapData = object.toObject();\n\n        malker.visit(\"map\", \"type\");\n        malker.visit(mapData, \"object\");\n\n        visitor.storeValue(map, object, name);\n    }\n});\n```\nThe result of the serialization is:\n```javascript\n{\n    \"root\": {\n        \"type\": \"map\",\n        \"object\": {/* map data */}\n    }\n}\n```\n\n### Deserialization\n\nThe logic to deserialize a custom object is handled by the `Reviver` object. This is the object that knows how to revive objects. The interface expects a `getTypeOf` function that returns the type of the object and a `revive<Type>` function that knows how to revive the object. Multiple `revive<Type>` can be defined as long as `getTypeOf` returns different types.\n\nExample to deserialize:\n```javascript\n{\n    \"root\": {\n        \"type\": \"map\",\n        \"object\": {/* map data */}\n    }\n}\n```\n```javascript\nReviver.addCustomObjectReviver({\n    getTypeOf: function(value) {\n        if (value.type === \"map\") {\n            return \"Map\";\n        }\n    },\n\n    reviveMap: function(value, context, label) {\n        var map = new Map(value.object);\n\n        if (label) {\n            context.setObjectLabel(map, label);\n        }\n\n        return map;\n    }\n});\n```\nThe result of the deserialization is:\n```javascript\n{\n    root: <Map Object>\n}\n```\n\nReviver functions can be asynchronous by returning a promise to the revived value.\n\n### Context\n\nThe `context` object is given as the second parameter to all `revive*` functions and it is used to set labels on deserialized objects, so they can be accessed after the deserialization, and to get objects that were serialized under a specific label.\n\n * `setObjectLabel(object, label)` - Defines the label of `object`.\n * `getObject(label)` - Returns the object with label `label`.\n\n## Extending the Serialization Format\n\nThe Mousse serialization format can be extended by extending the Builder and Visitor objects.\n\n### Serialization\n\nDuring serialization an AST-like object is created that holds all the data needed to be serialized. It is the role of the `Visitor` to create this AST.\n\nWhen the AST is finished it is the role of the `Builder` to generate an output format by reading the AST. The `Builder` presented in Mousse generates JSON but it should be possible to create a Builder that generates another format, for instance XML.\n\nThe `Builder` provides the necessary methods to create the AST Nodes:\n\n * `createObjectLiteral()`\n * `createArray()`\n * `createObjectReference()`\n * `createRegExp(regexp)`\n * `createString(string)`\n * `createNumber(number)`\n * `createBoolean(value)`\n * `createNull()`\n * `createCustomObject()`\n\nThese are the JavaScript objects that Mousse supports. In order to create new ones it is necessary to extend the `Builder` and the `Visitor`.\n\nTo extend the serialization format to know about DOM elements and to serialize them into `{\"#\" \"<element id>\"}` we need to:\n\n#### Create the AST node\n```javascript\nfunction ElementReference(root, id) {\n    Value.call(this, root, id);\n}\n\nElementReference.prototype = Object.create(Value.prototype, {\n    constructor: {value: ElementReference},\n\n    _getSerializationValue: {\n        value: function() {\n            return {\"#\": this.value};\n        }\n    }\n});\n```\n#### Extend the Builder object\n```javascript\nfunction ExtendedBuilder() {\n    Builder.call(this);\n}\n\nExtendedBuilder.prototype = Object.create(Builder.prototype, {\n    constructor: {value: ExtendedBuilder},\n\n    createElementReference: {\n        value: function(id) {\n            return new ElementReference(this._root, id);\n        }\n    }\n});\n```\n#### Extend the Visitor object\n```javascript\nfunction ExtendedVisitor(builder, labeler) {\n    Visitor.call(this, builder, labeler);\n}\n\nExtendedVisitor.prototype = Object.create(Visitor.prototype, {\n    constructor: {value: ExtendedVisitor},\n\n    getTypeOf: {\n        value: function(object) {\n            if (!!(object && 1 === object.nodeType)) {\n                return \"ElementReference\";\n            }\n        }\n    },\n\n    visitElementReference: {\n        value: function(malker, object, name) {\n            var elementReference,\n                id = object.id;\n\n            elementReference = this.builder.createElementReference(id);\n            this.storeValue(elementReference, object, name);\n        }\n    }\n});\n```\n### Deserialization\n\nDeserialization is handled by the `Reviver` and as such this object needs to be extended to understand the new syntax added to the serialization (`{\"#\": \"<element id>\"}`).\n```javascript\nExtendedReviver.prototype = Object.create(Reviver.prototype, {\n    constructor: {value: ExtendedReviver},\n\n    getTypeOf: {\n        value: function(value) {\n            if (value !== null && typeof value === \"object\"\n                && Object.keys(value).length === 1 && \"#\" in value) {\n                return \"ElementReference\";\n            } else {\n                return Reviver.prototype.getTypeOf.call(this, value);\n            }\n        }\n    },\n\n    reviveElementReference: {\n        value: function(value, context, label) {\n            var elementId = value[\"#\"],\n                element = document.getElementById(elementId);\n\n            if (label) {\n                context.setObjectLabel(element, label);\n            }\n\n            return element;\n        }\n    }\n});\n```\n\n## Known Issues\n\nNot possible to serialize literal objects that can be mistaken as a reference or a regexp - `{\"@\": \"label\"}` and `{\"/\": {\"source\": \"regexp\"}}`).\n\n","readmeFilename":"README.md","_id":"mousse@0.1.4","_from":"mousse@~0.1.2","directories":{"lib":"./"},"hash":"3343e6d","mappings":{"q":{"name":"q","hash":"2847ee2","location":"../q@2847ee2/"},"collections":{"name":"collections","hash":"5ea2535","location":"../collections@5ea2535/"}},"production":true,"useScriptInjection":true}})
;
//*/
montageDefine("73de13b","package.json",{exports: {"name":"frb","version":"0.2.13","description":"Functional reactive bindings","keywords":["functional","reactive","bindings","observe","change"],"author":{"name":"Kris Kowal","email":"kris@cixar.com","url":"http://github.com/kriskowal/"},"contributors":[{"name":"Kris Kowal","email":"kris@cixar.com","url":"http://github.com/kriskowal/"}],"bugs":{"url":"http://github.com/kriskowal/frb/issues"},"licenses":[{"type":"MIT","url":"http://github.com/kriskowal/frb/raw/master/LICENSE.md"}],"repository":{"type":"git","url":"http://github.com/kriskowal/frb.git"},"main":"bindings","dependencies":{"collections":"~0.1.23"},"devDependencies":{"jasmine-node":"1.x.x","pegjs":"git://github.com/dmajda/pegjs.git"},"scripts":{"test":"jasmine-node spec","build-parser":"pegjs --allowed-start-rules expression,sheet grammar.pegjs"},"readme":"\n<img src=\"frb.png\" align=\"right\" alt=\"FRB Logo\">\n\n# Functional Reactive Bindings\n\nIn their simplest form, bindings provide the illusion that two objects\nhave the same property.  Changing the property on one object causes the\nsame change in the other.  This is useful for coordinating state between\nviews and models, among other entangled objects.  For example, if you\nenter text into a text field, the same text might be added to the\ncorresponding database record.\n\n```javascript\nbind(object, \"a.b\", {\"<->\": \"c.d\"});\n```\n\nFunctional Reactive Bindings go farther.  They can gracefully bind long\nproperty paths and the contents of collections.  They can also\nincrementally update the results of chains of queries including maps,\nflattened arrays, sums, and averages.  They can also add and remove\nelements from sets based on the changes to a flag.  FRB makes it easy to\nincrementally ensure consistent state.\n\n```javascript\nbind(company, \"payroll\", {\"<-\": \"departments.map{employees.sum{salary}}.sum()\"});\nbind(document, \"body.classList.has('dark')\", {\"<-\": \"darkMode\", source: viewModel});\n```\n\nFRB is built from a combination of powerful functional and generic\nbuilding blocks, making it reliable, easy to extend, and easy to\nmaintain.\n\n\n## Getting Started\n\n`frb` is a CommonJS package, with JavaScript modules suitable for use\nwith [Node.js][] on the server side or [Mr][] on the client side. \n\n```\n❯ npm install frb\n```\n\n\n\n\n## Tutorial\n\nIn this example, we bind `model.content` to `document.body.innerHTML`.\n\n```javascript\nvar bind = require(\"frb/bind\");\nvar model = {content: \"Hello, World!\"};\nvar cancelBinding = bind(document, \"body.innerHTML\", {\n    \"<-\": \"content\",\n    \"source\": model\n});\n```\n\nWhen a source property is bound to a target property, the target gets\nreassigned to the source any time the source changes.\n\n```javascript\nmodel.content = \"Farewell.\";\nexpect(document.body.innerHTML).toBe(\"Farewell.\");\n```\n\nBindings can be recursively detached from the objects they observe with\nthe returned cancel function.\n\n```javascript\ncancelBinding();\nmodel.content = \"Hello again!\"; // doesn't take\nexpect(document.body.innerHTML).toBe(\"Farewell.\");\n```\n\n### Two-way Bindings\n\nBindings can go one way or in both directions.  Declare one-way\nbindings with the ```<-``` property, and two-way bindings with the\n```<->``` property.\n\nIn this example, the \"foo\" and \"bar\" properties of an object will be\ninexorably intertwined.\n\n```javascript\nvar object = {};\nvar cancel = bind(object, \"foo\", {\"<->\": \"bar\"});\n\n// <-\nobject.bar = 10;\nexpect(object.foo).toBe(10);\n\n// ->\nobject.foo = 20;\nexpect(object.bar).toBe(20);\n```\n\n### Right-to-left\n\nNote that even with a two-way binding, the right-to-left binding\nprecedes the left-to-right.  In this example, \"foo\" and \"bar\" are bound\ntogether, but both have initial values.\n\n```javascript\nvar object = {foo: 10, bar: 20};\nvar cancel = bind(object, \"foo\", {\"<->\": \"bar\"});\nexpect(object.foo).toBe(20);\nexpect(object.bar).toBe(20);\n```\n\nThe right-to-left assignment of `bar` to `foo` happens first, so the\ninitial value of `foo` gets lost.\n\n### Properties\n\nBindings can follow deeply nested chains, on both the left and the right\nside.\n\nIn this example, we have two object graphs, `foo`, and `bar`, with the\nsame structure and initial values.  This binds `bar.a.b` to `foo.a.b`\nand also the other way around.\n\n```javascript\nvar foo = {a: {b: 10}};\nvar bar = {a: {b: 10}};\nvar cancel = bind(foo, \"a.b\", {\n    \"<->\": \"a.b\",\n    source: bar\n});\n// <-\nbar.a.b = 20;\nexpect(foo.a.b).toBe(20);\n// ->\nfoo.a.b = 30;\nexpect(bar.a.b).toBe(30);\n```\n\n### Structure changes\n\nChanges to the structure of either side of the binding are no matter.\nAll of the orphaned event listeners will automatically be canceled, and\nthe binders and observers will reattach to the new object graph.\n\nContinuing from the previous example, we store and replace the `a`\nobject from one side of the binding.  The old `b` property is now\norphaned, and the old `b` property adopted in its place.\n\n```javascript\nvar a = foo.a;\nexpect(a.b).toBe(30); // from before\n\nfoo.a = {}; // orphan a and replace\nfoo.a.b = 40;\n// ->\nexpect(bar.a.b).toBe(40); // updated\n\nbar.a.b = 50;\n// <-\nexpect(foo.a.b).toBe(50); // new one updated\nexpect(a.b).toBe(30); // from before it was orphaned\n```\n\n### Sum\n\nSome advanced queries are possible with one-way bindings from\ncollections.  FRB updates sums incrementally.  When values are added or\nremoved from the array, the sum of only those values is taken and added\nor removed from the last known sum.\n\n```javascript\nvar object = {array: [1, 2, 3]};\nbind(object, \"sum\", {\"<-\": \"array.sum()\"});\nexpect(object.sum).toEqual(6);\n```\n\n### Average\n\nThe arithmetic mean of a collection can be updated incrementally.  Each\ntime the array changes, the added and removed values adjust the last\nknown sum and count of values in the array.\n\n```javascript\nvar object = {array: [1, 2, 3]};\nbind(object, \"average\", {\"<-\": \"array.average()\"});\nexpect(object.average).toEqual(2);\n```\n\n### Last\n\nFRB provides an operator for watching the last value in an Array.\n\n```javascript\nvar array = [1, 2, 3];\nvar object = {array: array, last: null};\nBindings.defineBinding(object, \"last\", {\"<-\": \"array.last()\"});\nexpect(object.last).toBe(3);\n\narray.push(4);\nexpect(object.last).toBe(4);\n```\n\nWhen the dust settles, `array.last()` is equivalent to\n`array[array.length - 1]`, but the `last` observer guarantees that it\nwill not jitter between the ultimate value and null or the penultimate\nvalue of the collection.  With `array[array.length]`, the underlying may\nnot change its content and length atomically.\n\n```javascript\nvar changed = jasmine.createSpy();\nPropertyChanges.addOwnPropertyChangeListener(object, \"last\", changed);\narray.unshift(0);\narray.splice(3, 0, 3.5);\nexpect(object.last).toBe(4);\nexpect(changed).not.toHaveBeenCalled();\n\narray.pop();\nexpect(object.last).toBe(3);\n\narray.clear();\nexpect(object.last).toBe(null);\n```\n\n### Only\n\nFRB provides an `only` operator, which can either observe or bind the\nonly element of a collection.  The `only` observer watches a collection\nfor when there is only one value in that collection and emits that\nvalue..  If there are multiple values, it emits null.\n\n```javascript\nvar object = {array: [], only: null};\nBindings.defineBindings(object, {\n    only: {\"<->\": \"array.only()\"}\n});\n\nobject.array = [1];\nexpect(object.only).toBe(1);\n\nobject.array.pop();\nexpect(object.only).toBe(undefined);\n\nobject.array = [1, 2, 3];\nexpect(object.only).toBe(undefined);\n```\n\nTHe `only` binder watches a value.  When the value is null, it does\nnothing.  Otherwise, it will update the bound collection such that it\nonly contains that value.  If the collection was empty, it adds the\nvalue.  Otherwise, if the collection did not have the value, it replaces\nthe collection's content with the one value.  Otherwise, it removes\neverything but the value it already contains.  Regardless of the means,\nthe end result is the same.  If the value is non-null, it will be the\nonly value in the collection.\n\n```javascript\nobject.only = 2;\nexpect(object.array.slice()).toEqual([2]);\n// Note that slice() is necessary only because the testing scaffold\n// does not consider an observable array equivalent to a plain array\n// with the same content\n\nobject.only = null;\nobject.array.push(3);\nexpect(object.array.slice()).toEqual([2, 3]);\n```\n\n### Map\n\nYou can also create mappings from one array to a new array and an\nexpression to evaluate on each value.  The mapped array is bound once,\nand all changes to the source array are incrementally updated in the\ntarget array.\n\n```javascript\nvar object = {objects: [\n    {number: 10},\n    {number: 20},\n    {number: 30}\n]};\nbind(object, \"numbers\", {\"<-\": \"objects.map{number}\"});\nexpect(object.numbers).toEqual([10, 20, 30]);\nobject.objects.push({number: 40});\nexpect(object.numbers).toEqual([10, 20, 30, 40]);\n```\n\nAny function, like `sum` or `average`, can be applied to the result of a\nmapping.  The straight-forward path would be\n`objects.map{number}.sum()`, but you can use a block with any function\nas a short hand, `objects.sum{number}`.\n\n### Filter\n\nA filter block generates an incrementally updated array filter.  The\nresulting array will contain only those elements from the source array\nthat pass the test deescribed in the block.  As values of the source\narray are added, removed, or changed such that they go from passing to\nfailing or failing to passing, the filtered array gets incrementally\nupdated to include or exclude those values in their proper positions, as\nif the whole array were regenerated with `array.filter` by brute force.\n\n```javascript\nvar object = {numbers: [1, 2, 3, 4, 5, 6]};\nbind(object, \"evens\", {\"<-\": \"numbers.filter{!(%2)}\"});\nexpect(object.evens).toEqual([2, 4, 6]);\nobject.numbers.push(7, 8);\nobject.numbers.shift();\nobject.numbers.shift();\nexpect(object.evens).toEqual([4, 6, 8]);\n```\n\n### Some and Every\n\nA `some` block incrementally tracks whether some of the values in a\ncollection meet a criterion.\n\n```javascript\nvar object = Bindings.defineBindings({\n    options: [\n        {checked: true},\n        {checked: false},\n        {checked: false}\n    ]\n}, {\n    anyChecked: {\n        \"<-\": \"options.some{checked}\"\n    }\n});\nexpect(object.anyChecked).toBe(true);\n```\n\nAn `every` block incrementally tracks whether all of the values in a\ncollection meet a criterion.\n\n```javascript\nvar object = Bindings.defineBindings({\n    options: [\n        {checked: true},\n        {checked: false},\n        {checked: false}\n    ]\n}, {\n    allChecked: {\n        \"<-\": \"options.every{checked}\"\n    }\n});\nexpect(object.allChecked).toBe(false);\n```\n\nYou can use a two-way binding on `some` and `every` blocks.\n\n```javascript\nvar object = Bindings.defineBindings({\n    options: [\n        {checked: true},\n        {checked: false},\n        {checked: false}\n    ]\n}, {\n    allChecked: {\n        \"<->\": \"options.every{checked}\"\n    },\n    noneChecked: {\n        \"<->\": \"!options.some{checked}\"\n    }\n});\n\nobject.noneChecked = true;\nexpect(object.options.every(function (option) {\n    return !option.checked\n}));\n\nobject.allChecked = true;\nexpect(object.noneChecked).toBe(false);\n```\n\nThe caveat of an `equals` binding applies.  If the condition for every\nelement of the collection is set to true, the condition will be bound\nincrementally to true on each element.  When the condition is set to\nfalse, the binding will simply be canceled.\n\n```javascript\nobject.allChecked = false;\nexpect(object.options.every(function (option) {\n    return option.checked; // still checked\n}));\n```\n\n### Sorted\n\nA sorted block generates an incrementally updated sorted array.  The\nresulting array will contain all of the values from the source except in\nsorted order.\n\n```javascript\nvar object = {numbers: [5, 2, 7, 3, 8, 1, 6, 4]};\nbind(object, \"sorted\", {\"<-\": \"numbers.sorted{}\"});\nexpect(object.sorted).toEqual([1, 2, 3, 4, 5, 6, 7, 8]);\n```\n\nThe block may specify a property or expression by which to compare\nvalues.\n\n```javascript\nvar object = {arrays: [[1, 2, 3], [1, 2], [], [1, 2, 3, 4], [1]]};\nbind(object, \"sorted\", {\"<-\": \"arrays.sorted{-length}\"});\nexpect(object.sorted.map(function (array) {\n    return array.slice(); // to clone\n})).toEqual([\n    [1, 2, 3, 4],\n    [1, 2, 3],\n    [1, 2],\n    [1],\n    []\n]);\n```\n\nThe sorted binding responds to changes to the sorted property by\nremoving them at their former place and adding them back at their new\nposition.\n\n```javascript\nobject.arrays[0].push(4, 5);\nexpect(object.sorted.map(function (array) {\n    return array.slice(); // to clone\n})).toEqual([\n    [1, 2, 3, 4, 5], // new\n    [1, 2, 3, 4],\n    // old\n    [1, 2],\n    [1],\n    []\n]);\n```\n\n### Unique and Sorted\n\nFRB can create a sorted index of unique values using `sortedSet` blocks.\n\n```javascript\nvar object = Bindings.defineBindings({\n    folks: [\n        {id: 4, name: \"Bob\"},\n        {id: 2, name: \"Alice\"},\n        {id: 3, name: \"Bob\"},\n        {id: 1, name: \"Alice\"},\n        {id: 1, name: \"Alice\"} // redundant\n    ]\n}, {\n    inOrder: {\"<-\": \"folks.sortedSet{id}\"},\n    byId: {\"<-\": \"folks.map{[id, this]}.toMap()\"},\n    byName: {\"<-\": \"inOrder.toArray().group{name}.toMap()\"}\n});\n\nexpect(object.inOrder.toArray()).toEqual([\n    object.byId.get(1),\n    object.byId.get(2),\n    object.byId.get(3),\n    object.byId.get(4)\n]);\n\nexpect(object.byName.get(\"Alice\")).toEqual([\n    object.byId.get(1),\n    object.byId.get(2)\n]);\n```\n\nThe outcome is a `SortedSet` data structure, not an `Array`.  The sorted\nset is useful for fast lookups, inserts, and deletes on sorted, unique\ndata.  If you would prefer a sorted array of unique values, you can\ncombine other operators to the same effect.\n\n```javascript\nvar object = Bindings.defineBindings({\n    folks: [\n        {id: 4, name: \"Bob\"},\n        {id: 2, name: \"Alice\"},\n        {id: 3, name: \"Bob\"},\n        {id: 1, name: \"Alice\"},\n        {id: 1, name: \"Alice\"} // redundant\n    ]\n}, {\n    index: {\"<-\": \"folks.group{id}.sorted{.0}.map{.1.last()}\"}\n});\n\nexpect(object.index).toEqual([\n    {id: 1, name: \"Alice\"},\n    {id: 2, name: \"Alice\"},\n    {id: 3, name: \"Bob\"},\n    {id: 4, name: \"Bob\"}\n]);\n```\n\n\n### Min and Max\n\nA binding can observe the minimum or maximum of a collection.  FRB uses\na binary heap internally to incrementally track the minimum or maximum\nvalue of the collection.\n\n```javascript\nvar object = Bindings.defineBindings({}, {\n    min: {\"<-\": \"values.min{}\"},\n    max: {\"<-\": \"values.max{}\"}\n});\n\nexpect(object.min).toBe(undefined);\nexpect(object.max).toBe(undefined);\n\nobject.values = [2, 3, 2, 1, 2];\nexpect(object.min).toBe(1);\nexpect(object.max).toBe(3);\n```\n\nMin and max blocks accept an expression on which to compare values from\nthe collection.\n\n```javascript\nvar object = Bindings.defineBindings({}, {\n    loser: {\"<-\": \"rounds.min{score}.player\"},\n    winner: {\"<-\": \"rounds.max{score}.player\"}\n});\n\nobject.rounds = [\n    {score: 0, player: \"Luke\"},\n    {score: 100, player: \"Obi Wan\"},\n    {score: 250, player: \"Vader\"}\n];\nexpect(object.loser).toEqual(\"Luke\");\nexpect(object.winner).toEqual(\"Vader\");\n\nobject.rounds[1].score = 300;\nexpect(object.winner).toEqual(\"Obi Wan\");\n```\n\n### Group\n\nFRB can incrementally track equivalence classes within in a collection.\nThe group block accepts an expression that determines the equivalence\nclass for each object in a collection.  The result is a nested data\nstructure: an array of [key, class] pairs, where each class is itself an\narray of all members of the collection that have the corresponding key.\n\n```javascript\nvar store = Bindings.defineBindings({}, {\n    \"clothingByColor\": {\"<-\": \"clothing.group{color}\"}\n});\nstore.clothing = [\n    {type: 'shirt', color: 'blue'},\n    {type: 'pants', color: 'red'},\n    {type: 'blazer', color: 'blue'},\n    {type: 'hat', color: 'red'}\n];\nexpect(store.clothingByColor).toEqual([\n    ['blue', [\n        {type: 'shirt', color: 'blue'},\n        {type: 'blazer', color: 'blue'}\n    ]],\n    ['red', [\n        {type: 'pants', color: 'red'},\n        {type: 'hat', color: 'red'}\n    ]]\n]);\n```\n\nTracking the positions of every key and every value in its equivalence\nclass can be expensive.  Internally, `group` blocks are implemented with\na `groupMap` block followed by an `entries()` observer.  The `groupMap`\nproduces a `Map` data structure and does not waste any time, but does\nnot produce range change events.  The `entries()` observer projects the\nmap of classes into the nested array data structure.\n\nYou can use the `groupMap` block directly.\n\n```javascript\nBindings.cancelBinding(store, \"clothingByColor\");\nBindings.defineBindings(store, {\n    \"clothingByColor\": {\"<-\": \"clothing.groupMap{color}\"}\n});\nvar blueClothes = store.clothingByColor.get('blue');\nexpect(blueClothes).toEqual([\n    {type: 'shirt', color: 'blue'},\n    {type: 'blazer', color: 'blue'}\n]);\n\nstore.clothing.push({type: 'gloves', color: 'blue'});\nexpect(blueClothes).toEqual([\n    {type: 'shirt', color: 'blue'},\n    {type: 'blazer', color: 'blue'},\n    {type: 'gloves', color: 'blue'}\n]);\n```\n\nThe `group` and `groupMap` blocks both respect the type of the source\ncollection.  If instead of an array you were to use a `SortedSet`, the\nequivalence classes would each be sorted sets.  This is useful because\nreplacing values in a sorted set can be performed with much less waste\nthan with a large array.\n\n### View\n\nSuppose that your source is a large data store, like a `SortedSet` from\nthe [Collections][] package.  You might need to view a sliding window\nfrom that collection as an array.  The `view` binding reacts to changes\nto the collection and the position and length of the window.\n\n```javascript\nvar SortedSet = require(\"collections/sorted-set\");\nvar controller = {\n    index: SortedSet([1, 2, 3, 4, 5, 6, 7, 8]),\n    start: 2,\n    length: 4\n};\nvar cancel = bind(controller, \"view\", {\n    \"<-\": \"index.view(start, length)\"\n});\n\nexpect(controller.view).toEqual([3, 4, 5, 6]);\n\n// change the window length\ncontroller.length = 3;\nexpect(controller.view).toEqual([3, 4, 5]);\n\n// change the window position\ncontroller.start = 5;\nexpect(controller.view).toEqual([6, 7, 8]);\n\n// add content behind the window\ncontroller.index.add(0);\nexpect(controller.view).toEqual([5, 6, 7]);\n```\n\n### Enumerate\n\nAn enumeration observer produces `[index, value]` pairs.  You can bind\nto the index or the value in subsequent stages.  The prefix dot\ndistinguishes the zeroeth property from the literal zero.\n\n```javascript\nvar object = {letters: ['a', 'b', 'c', 'd']};\nbind(object, \"lettersAtEvenIndexes\", {\n    \"<-\": \"letters.enumerate().filter{!(.0 % 2)}.map{.1}\"\n});\nexpect(object.lettersAtEvenIndexes).toEqual(['a', 'c']);\nobject.letters.shift();\nexpect(object.lettersAtEvenIndexes).toEqual(['b', 'd']);\n```\n\n### Range\n\nA range observes a given length and produces and incrementally updates\nan array of consecutive integers starting with zero with that given\nlength.\n\n```javascript\nvar object = Bindings.defineBinding({}, \"stack\", {\n    \"<-\": \"&range(length)\"\n});\nexpect(object.stack).toEqual([]);\n\nobject.length = 3;\nexpect(object.stack).toEqual([0, 1, 2]);\n\nobject.length = 1;\nexpect(object.stack).toEqual([0]);\n```\n\n### Flatten\n\nYou can flatten nested arrays.  In this example, we have an array of\narrays and bind it to a flat array.\n\n```javascript\nvar arrays = [[1, 2, 3], [4, 5, 6]];\nvar object = {};\nbind(object, \"flat\", {\n    \"<-\": \"flatten()\",\n    source: arrays\n});\nexpect(object.flat).toEqual([1, 2, 3, 4, 5, 6]);\n```\n\nNote that changes to the inner and outer arrays are both projected into\nthe flattened array.\n\n```javascript\narrays.push([7, 8, 9]);\narrays[0].unshift(0);\nexpect(object.flat).toEqual([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);\n```\n\nAlso, as with all other bindings that produce arrays, the flattened\narray is never replaced, just incrementally updated.\n\n```javascript\nvar flat = object.flat;\narrays.splice(0, arrays.length);\nexpect(object.flat).toBe(flat); // === same object\n```\n\n### Concat\n\nYou can observe the concatenation of collection of dynamic arrays.\n\n```javascript\nvar object = Bindings.defineBinding({\n    head: 10,\n    tail: [20, 30]\n}, \"flat\", {\n    \"<-\": \"[head].concat(tail)\"\n});\nexpect(object.flat).toEqual([10, 20, 30]);\n```\n\nThe underlying mechanism is equivalent to `[[head], tail].flatten()`.\n\n### Reversed\n\nYou can bind the reversal of an array.\n\n```javascript\nvar object = {forward: [1, 2, 3]};\nbind(object, \"backward\", {\n    \"<->\": \"forward.reversed()\"\n});\nexpect(object.backward.slice()).toEqual([3, 2, 1]);\nobject.forward.push(4);\nexpect(object.forward.slice()).toEqual([1, 2, 3, 4]);\nexpect(object.backward.slice()).toEqual([4, 3, 2, 1]);\n```\n\nNote that you can do two-way bindings, ```<->``` with reversed arrays.\nChanges to either side are updated to the opposite side.\n\n```javascript\nobject.backward.pop();\nexpect(object.backward.slice()).toEqual([4, 3, 2]);\nexpect(object.forward.slice()).toEqual([2, 3, 4]);\n```\n\n### Has\n\nYou can bind a property to always reflect whether a collection contains\na particular value.\n\n```javascript\nvar object = {\n    haystack: [1, 2, 3],\n    needle: 3\n};\nbind(object, \"hasNeedle\", {\"<-\": \"haystack.has(needle)\"});\nexpect(object.hasNeedle).toBe(true);\nobject.haystack.pop(); // 3 comes off\nexpect(object.hasNeedle).toBe(false);\n```\n\nThe binding also reacts to changes to the value you seek.\n\n```javascript\n// Continued from above...\nobject.needle = 2;\nexpect(object.hasNeedle).toBe(true);\n```\n\n`has` bindings are not incremental, but with the right data-structure,\nupdates are cheap.  The [Collections][] package contains Lists, Sets,\nand OrderedSets that all can send content change notifications and thus\ncan be bound.\n\n```javascript\n// Continued from above...\nvar Set = require(\"collections/set\");\nobject.haystack = new Set([1, 2, 3]);\nexpect(object.hasNeedle).toBe(true);\n```\n\n`has` bindings can also be left-to-right and bi-directional.\n\n```javascript\nbind(object, \"hasNeedle\", {\"<->\": \"haystack.has(needle)\"});\nobject.hasNeedle = false;\nexpect(object.haystack.has(2)).toBe(false);\n```\n\nThe collection on the left-hand-side must implement `has` or `contains`,\n`add`, and `delete` or `remove`.  FRB shims `Array` to have `has`,\n`add`, and `delete`, just like all the collections in [Collections][].\nIt happens that the `classList` properties of DOM elements, when they\nare supported, implement `add`, `remove`, and `contains`.\n\n```javascript\nvar model = {darkMode: false};\nbind(document.body, \"classList.has('dark')\", {\n    \"<-\": \"darkMode\",\n    source: model\n});\n```\n\nThe DOM `classList` does not however implement\n`addContentChangeListener` or `removeContentChangeListener`, so it\ncannot be used on the right-hand-side of a binding, and such bindings\ncannot be bidirectional.  With some DOM [Mutation Observers][], you\nmight be able to help FRB overcome this limitation in the future.\n\n### Get\n\nA binding can observe changes in key-to-value mappings in arrays and map\n[Collections][].\n\n```javascript\nvar object = {\n    array: [1, 2, 3],\n    second: null\n};\nvar cancel = bind(object, \"second\", {\n    \"<->\": \"array.get(1)\"\n});\nexpect(object.array.slice()).toEqual([1, 2, 3]);\nexpect(object.second).toBe(2);\n\nobject.array.shift();\nexpect(object.array.slice()).toEqual([2, 3]);\nexpect(object.second).toBe(3);\n\nobject.second = 4;\nexpect(object.array.slice()).toEqual([2, 4]);\n\ncancel();\nobject.array.shift();\nexpect(object.second).toBe(4); // still\n```\n\nThe source collection can be a Map, Dict, MultiMap, SortedMap,\nSortedArrayMap, or anything that implements `get` and\n`addMapChangeListener` as specified in [Collections][].  The key can\nalso be a variable.\n\n```javascript\nvar Map = require(\"collections/map\");\nvar a = {id: 0}, b = {id: 1};\nvar object = {\n    source: Map([[a, 10], [b, 20]]),\n    key: null,\n    selected: null\n};\n\nvar cancel = bind(object, \"selected\", {\n    \"<-\": \"source.get(key)\"\n});\nexpect(object.selected).toBe(undefined);\n\nobject.key = a;\nexpect(object.selected).toBe(10);\n\nobject.key = b;\nexpect(object.selected).toBe(20);\n\nobject.source.set(b, 30);\nexpect(object.selected).toBe(30);\n\nvar SortedMap = require(\"collections/sorted-map\");\nobject.source = SortedMap();\nexpect(object.selected).toBe(undefined);\n\nobject.source.set(b, 40);\nexpect(object.selected).toBe(40);\n\ncancel();\nobject.key = a; // no effect\nexpect(object.selected).toBe(40);\n```\n\nYou can also bind the entire content of a map-like collection to the\ncontent of another.  Bear in mind that the content of the source\nreplaces the content of the target initially.\n\n```javascript\nvar Map = require(\"collections/map\");\nvar object = {\n    a: Map({a: 10}),\n    b: Map()\n};\nvar cancel = bind(object, \"a.mapContent()\", {\"<->\": \"b.mapContent()\"});\nexpect(object.a.toObject()).toEqual({});\nexpect(object.b.toObject()).toEqual({});\n\nobject.a.set('a', 10);\nexpect(object.a.toObject()).toEqual({a: 10});\nexpect(object.b.toObject()).toEqual({a: 10});\n\nobject.b.set('b', 20);\nexpect(object.a.toObject()).toEqual({a: 10, b: 20});\nexpect(object.b.toObject()).toEqual({a: 10, b: 20});\n```\n\nIn this case, the source of the binding is a different object than the\ntarget, so the binding descriptor specifies the alternate source.\n\n### Keys, Values, Entries\n\nIf the source of a binding is a map, FRB can also translate changes to\nthe map into changes on an array.  The `keys`, `values`, and `entries`\nobservers produce incrementally updated projections of the\nkey-value-mappings onto an array.\n\n```javascript\nvar Map = require(\"collections/map\");\nvar object = Bindings.defineBindings({}, {\n    keys: {\"<-\": \"map.keys()\"},\n    values: {\"<-\": \"map.values()\"},\n    entries: {\"<-\": \"map.entries()\"}\n});\nobject.map = Map({a: 10, b: 20, c: 30});\nexpect(object.keys).toEqual(['a', 'b', 'c']);\nexpect(object.values).toEqual([10, 20, 30]);\nexpect(object.entries).toEqual([['a', 10], ['b', 20], ['c', 30]]);\n\nobject.map.set('d', 40);\nobject.map.delete('a');\nexpect(object.keys).toEqual(['b', 'c', 'd']);\nexpect(object.values).toEqual([20, 30, 40]);\nexpect(object.entries).toEqual([['b', 20], ['c', 30], ['d', 40]]);\n```\n\n### Coerce to Map\n\nRecords (Objects with a fixed shape), arrays of entries, and Maps\nthemselves can be coerced to an incrementally updated `Map` with the\n`toMap` operator.\n\n```javascript\nvar object = Bindings.defineBindings({}, {\n    map: {\"<-\": \"entries.toMap()\"}\n});\n\n// map property will persist across changes to entries\nvar map = object.map;\nexpect(map).not.toBe(null);\n\nobject.entries = {a: 10};\nexpect(map.keys()).toEqual(['a']);\nexpect(map.has('a')).toBe(true);\nexpect(map.get('a')).toBe(10);\n```\n\nThe `toMap` observer maintains the insertion order of the keys.\n\n```javascript\n// Continued...\nobject.entries = [['b', 20], ['c', 30]];\nexpect(map.keys()).toEqual(['b', 'c']);\n\nobject.entries.push(object.entries.shift());\nexpect(map.keys()).toEqual(['c', 'b']);\n```\n\nIf the entries do not have unique keys, the last entry wins.  This is\nmanaged internally by observing, `entries.group{.0}.map{.1.last()}`.\n\n```javascript\n// Continued...\nobject.entries = [['a', 10], ['a', 20]];\nexpect(map.get('a')).toEqual(20);\nobject.entries.pop();\nexpect(map.get('a')).toEqual(10);\n```\n\n`toMap` binds the content of the output map to the content of the input\nmap and will clear and repopulate the output map if the input map is\nreplaced.\n\n```\n// Continued...\nobject.entries = new Map({a: 10});\nexpect(map.keys()).toEqual(['a']);\n```\n\n### Equals\n\nYou can bind to whether expressions are equal.\n\n```javascript\nvar fruit = {apples: 1, oranges: 2};\nbind(fruit, \"equal\", {\"<-\": \"apples == oranges\"});\nexpect(fruit.equal).toBe(false);\nfruit.orange = 1;\nexpect(fruit.equal).toBe(true);\n```\n\nEquality can be bound both directions.  In this example, we do a two-way\nbinding between whether a radio button is checked and a corresponding\nvalue in our model.\n\n```javascript\nvar component = {\n    orangeElement: {checked: false},\n    appleElement: {checked: true}\n};\nBindings.defineBindings(component, {\n    \"orangeElement.checked\": {\"<->\": \"fruit == 'orange'\"},\n    \"appleElement.checked\": {\"<->\": \"fruit == 'apple'\"},\n});\n\ncomponent.orangeElement.checked = true;\nexpect(component.fruit).toEqual(\"orange\");\n\ncomponent.appleElement.checked = true;\nexpect(component.fruit).toEqual(\"apple\");\n```\n\nBecause equality and assignment are interchanged in this language, you\ncan use either `=` or `==`.\n\nFRB also supports a comparison operator, `<=>`, which uses\n`Object.compare` to determines how two operands should be sorted in\nrelation to each other.\n\n### Array and Map Content\n\nIn JavaScript, arrays behave both like objects (in the sense that every\nindex is a property, but also like a map collection of index-to-value\npairs.  The [Collections][] package goes so far as to patch up the\n`Array` prototype so arrays can masquerade as maps, with the caveat that\n`delete(value)` behaves like a Set instead of a Map.\n\nThis duplicity is reflected in FRB.  You can access the values in an\narray using the object property notation or the mapped key notation.\n\n```javascript\nvar object = {\n    array: [1, 2, 3]\n};\nBindings.defineBindings(object, {\n    first: {\"<-\": \"array.0\"},\n    second: {\"<-\": \"array.get(1)\"}\n});\nexpect(object.first).toBe(1);\nexpect(object.second).toBe(2);\n```\n\nTo distinguish a numeric property of the source from a number literal,\nuse a dot.  To distingish a mapped index from an array literal, use an\nempty expression.\n\n```javascript\nvar array = [1, 2, 3];\nvar object = {};\nBindings.defineBindings(object, {\n    first: {\n        \"<-\": \".0\",\n        source: array\n    },\n    second: {\n        \"<-\": \"get(1)\",\n        source: array\n    }\n});\nexpect(object.first).toBe(1);\nexpect(object.second).toBe(2);\n```\n\nUnlike property notation, map notation can observe a variable index.\n\n```javascript\nvar object = {\n    array: [1, 2, 3],\n    index: 0\n};\nBindings.defineBinding(object, \"last\", {\n    \"<-\": \"array.get(array.length - 1)\"\n});\nexpect(object.last).toBe(3);\n\nobject.array.pop();\nexpect(object.last).toBe(2);\n```\n\nYou can also bind *all* of the content of an array by range or by\nmapping.  The notation for binding ranged content is `rangeContent()`.\nEvery change to an Array or SortedSet dispatches range changes and any\ncollection that implements `splice` and `swap` can be a target for such\nchanges.\n\n```javascript\nvar SortedSet = require(\"collections/sorted-set\");\nvar object = {\n    set: SortedSet(),\n    array: []\n};\nBindings.defineBindings(object, {\n    \"array.rangeContent()\": {\"<-\": \"set\"}\n});\nobject.set.addEach([5, 2, 6, 1, 4, 3]);\nexpect(object.array).toEqual([1, 2, 3, 4, 5, 6]);\n```\n\nThe notation for binding the content of any mapping collection using map\nchanges is `mapContent()`.  On the target of a binding, this will note\nwhen values are added or removed on each key of the source collection\nand apply the same change to the target.  The target and source can be\narrays or map collections.\n\n```javascript\nvar Map = require(\"collections/map\");\nvar object = {\n    map: Map(),\n    array: []\n};\nBindings.defineBinding(object, \"map.mapContent()\", {\n    \"<-\": \"array\"\n});\nobject.array.push(1, 2, 3);\nexpect(object.map.toObject()).toEqual({\n    0: 1,\n    1: 2,\n    2: 3\n});\n```\n\n### Value\n\nA note about the source value: an empty path implies the source value.\nUsing empty paths and empty expressions is useful in some situations.\n\nIf a value is ommitted on either side of an operator, it implies the\nsource value.  The expression `sorted{}` indicates a sorted array, where\neach value is sorted by its own numeric value.  The expression\n`filter{!!}` would filter falsy values.  The operand is implied.\nSimilarly, `filter{!(%2)}` produces only even values.\n\nThis is why you can use `.0` to get the zeroth property of an array, to\ndistingiush the form from `0` which would be a numeric literal, and why\nyou can use `()[0]` to map the zeroeth key of a map or array, to\ndistinguish the form from `[0]` which would be an array literal.\n\n### With Context Value\n\nExpressions can be evaluated in the context of another value using a\nvariant of property notation.  A parenthesized expression can follow a\npath.\n\n```javascript\nvar object = {\n    context: {a: 10, b: 20}\n};\nBindings.defineBinding(object, \"sum\", {\n    \"<-\": \"context.(a + b)\"\n});\nexpect(object.sum).toBe(30);\n\nBindings.cancelBinding(object, \"sum\");\nobject.context.a = 20;\nexpect(object.sum).toBe(30); // unchanged\n```\n\nTo observe a constructed array or object literal, the expression does\nnot need parentheses.\n\n```javascript\nvar object = {\n    context: {a: 10, b: 20}\n};\nBindings.defineBindings(object, {\n    \"duple\": {\"<-\": \"context.[a, b]\"},\n    \"pair\": {\"<-\": \"context.{key: a, value: b}\"}\n});\nexpect(object.duple).toEqual([10, 20]);\nexpect(object.pair).toEqual({key: 10, value: 20});\n\nBindings.cancelBindings(object);\n```\n\n### Operators\n\nFRB can also recognize many operators.  These are in order of precedence\nunary `-` negation, `+` numeric coercion, and `!` logical negation and\nthen binary `**` power, `//` root, `%%` logarithm, `*`, `/`, `%` modulo,\n`%%` remainder, `+`, `-`, ```<```, ```>```, ```<=```, ```>=```, `=` or\n`==`, `!=`, `&&` and `||`.\n\n```javascript\nvar object = {height: 10};\nbind(object, \"heightPx\", {\"<-\": \"height + 'px'\"});\nexpect(object.heightPx).toEqual(\"10px\");\n```\n\nThe unary `+` operator coerces a value to a number. It is handy for\nbinding a string to a number.\n\n```javascript\nvar object = {\n    number: null,\n    string: null,\n};\nBindings.defineBinding(object, \"+number\", {\n    \"<-\": \"string\"\n});\nobject.string = '10';\nexpect(object.number).toBe(10);\n```\n\n### Functions\n\nFRB supports some common functions.  `startsWith`, `endsWith`, and\n`contains` all operate on strings.  `join` concatenates an array of\nstrings with a given delimiter (or empty string).  `split` breaks a\nstring between every delimiter (or just between every character).\n`join` and `split` are algebraic and can be bound as well as observed.\n\n### Conditional\n\nFRB supports the ternary conditional operator, if `?` then `:` else.\n\n```javascript\nvar object = Bindings.defineBindings({\n    condition: null,\n    consequent: 10,\n    alternate: 20\n}, {\n    choice: {\"<->\": \"condition ? consequent : alternate\"}\n});\n\nexpect(object.choice).toBe(undefined); // no choice made\n\nobject.condition = true;\nexpect(object.choice).toBe(10);\n\nobject.condition = false;\nexpect(object.choice).toBe(20);\n```\n\nThe ternary operator can bind in both directions.\n\n```javascript\nobject.choice = 30;\nexpect(object.alternate).toBe(30);\n\nobject.condition = true;\nobject.choice = 40;\nexpect(object.consequent).toBe(40);\n```\n\n### Algebra\n\nFRB can automatically invert algebraic operators as long as they operate\nstrictly on the left-most expressions on both the source and target are\nbindable properties.\n\nIn this example, the primary binding is ```notToBe <- !toBe```, and the\ninverse binding is automatically computed ```toBe <- !notToBe```.\n\n```javascript\nvar caesar = {toBe: false};\nbind(caesar, \"notToBe\", {\"<->\": \"!toBe\"});\nexpect(caesar.toBe).toEqual(false);\nexpect(caesar.notToBe).toEqual(true);\n\ncaesar.notToBe = false;\nexpect(caesar.toBe).toEqual(true);\n```\n\nFRB does algebra by rotating the expressions on one side of a binding to\nthe other until only one independent property remains (the left most\nexpression) on the target side of the equation.\n\n```\nconvert: y <- !x\nrevert: x <- !y\n```\n\n```\nconvert: y <- x + a\nrevert: x <- y - a\n```\n\nThe left-most independent variable on the right hand side becomes the\ndependent variable on the inverted binding.  At present, this only works\nfor numbers and when the left-most expression is a bindable property\nbecause it cannot assign a new value to the literal 10.  For example,\nFRB cannot yet implicitly revert ```y <-> 10 + x```.\n\n### Literals\n\nYou may have noticed literals in the previous examples.  String literals\ntake the form of any characters between single quotes.  Any character\ncan be escaped with a back slash.\n\n```javascript\nvar object = {};\nbind(object, \"greeting\", {\"<-\": \"'Hello, World!'\"});\nexpect(object.greeting).toBe(\"Hello, World!\");\n```\n\nNumber literals are digits with an optional mantissa.\n\n```javascript\nbind(object, 'four', {\"<-\": \"2 + 2\"});\n```\n\n### Tuples\n\nBindings can produce fixed-length arrays.  These are most useful in\nconjunction with mappings.  Tuples are comma-delimited and\nparantheses-enclosed.\n\n```javascript\nvar object = {array: [[1, 2, 3], [4, 5]]};\nbind(object, \"summary\", {\"<-\": \"array.map{[length, sum()]}\"});\nexpect(object.summary).toEqual([\n    [3, 6],\n    [2, 9]\n]);\n```\n\n### Records\n\nBindings can also produce fixed-shape objects.  The notation is\ncomma-delimited, colon-separated entries, enclosed by curly-braces.\n\n```javascript\nvar object = {array: [[1, 2, 3], [4, 5]]};\nbind(object, \"summary\", {\n    \"<-\": \"array.map{{length: length, sum: sum()}}\"\n});\nexpect(object.summary).toEqual([\n    {length: 3, sum: 6},\n    {length: 2, sum: 9}\n]);\n```\n\nThe left hand side of an entry in a record is any combination of letters\nor numbers.  The right side is any expression.\n\n### Parameters\n\nBindings can also involve parameters.  The source of parameters is by\ndefault the same as the source.  The source, in turn, defaults to the\nsame as the target object.  It can be specified on the binding\ndescriptor.  Parameters are declared by any expression following a\ndollar sign.\n\n```javascript\nvar object = {a: 10, b: 20, c: 30};\nbind(object, \"foo\", {\n    \"<-\": \"[$a, $b, $c]\"},\n    parameters: object\n});\n```\n\nBindings also react to changes to the parameters.\n\n```javascript\nobject.a = 0;\nobject.b = 1;\nobject.c = 2;\nexpect(object.foo).toEqual([0, 1, 2]);\n```\n\nThe degenerate case of the property language is an empty string.  This\nis a valid property path that observes the value itself.  So, as an\nemergent pattern, a `$` expression by itself corresponds to the whole\nparameters object.\n\n```javascript\nvar object = {};\nbind(object, \"ten\", {\"<-\": \"$\", parameters: 10});\nexpect(object.ten).toEqual(10);\n```\n\n### Elements and Components\n\nFRB provides a `#` notation for reaching into the DOM for an element.\nThis is handy for binding views and models on a controller object.\n\nThe `defineBindings` method accepts an optional final argument,\n`parameters`, which is shared by all bindings (unless shadowed by a more\nspecific parameters object on an individual descriptor).\n\nThe `parameters` can include a `document`.  The `document` may be any\nobject that implements `getElementById`.\n\nAdditionally, the `frb/dom` is an experiment that monkey-patches the DOM\nto make some properties of DOM elements observable, like the `value` or\n`checked` attribute of an `input` or `textarea element`.\n\n```javascript\nvar Bindings = require(\"frb\");\nrequire(\"frb/dom\");\n\nvar controller = Bindings.defineBindings({}, {\n\n    \"fahrenheit\": {\"<->\": \"celsius * 1.8 + 32\"},\n    \"celsius\": {\"<->\": \"kelvin - 272.15\"},\n\n    \"#fahrenheit.value\": {\"<->\": \"+fahrenheit\"},\n    \"#celsius.value\": {\"<->\": \"+celsius\"},\n    \"#kelvin.value\": {\"<->\": \"+kelvin\"}\n\n}, {\n    document: document\n});\n\ncontroller.celsius = 0;\n```\n\nOne caveat of this approach is that it can cause a lot of DOM repaint\nand reflow events.  The [Montage][] framework uses a synchronized draw\ncycle and a component object model to minimize the cost of computing CSS\nproperties on the DOM and performing repaints and reflows, deferring\nsuch operations to individual animation frames.\n\nFor a future release of Montage, FRB provides an alternate notation for\nreaching into the component object model, using its deserializer.  The\n`@` prefix refers to another component by its label.  Instead of\nproviding a `document`, Montage provides a `serialization`, which in\nturn implements `getObjectForLabel`.\n\n```javascript\nvar Bindings = require(\"frb\");\n\nvar controller = Bindings.defineBindings({}, {\n\n    \"fahrenheit\": {\"<->\": \"celsius * 1.8 + 32\"},\n    \"celsius\": {\"<->\": \"kelvin - 272.15\"},\n\n    \"@fahrenheit.value\": {\"<->\": \"+fahrenheit\"},\n    \"@celsius.value\": {\"<->\": \"+celsius\"},\n    \"@kelvin.value\": {\"<->\": \"+kelvin\"}\n\n}, {\n    serializer: serializer\n});\n\ncontroller.celsius = 0;\n```\n\n### Observers\n\nFRB’s bindings use observers and binders internally.  You can create an\nobserver from a property path with the `observe` function exported by\nthe `frb/observe` module.\n\n```javascript\nvar results = [];\nvar object = {foo: {bar: 10}};\nvar cancel = observe(object, \"foo.bar\", function (value) {\n    results.push(value);\n});\n\nobject.foo.bar = 10;\nexpect(results).toEqual([10]);\n\nobject.foo.bar = 20;\nexpect(results).toEqual([10, 20]);\n```\n\nFor more complex cases, you can specify a descriptor instead of the\ncallback.  For example, to observe a property’s value *before it changes*, you can use the `beforeChange` flag.\n\n```javascript\nvar results = [];\nvar object = {foo: {bar: 10}};\nvar cancel = observe(object, \"foo.bar\", {\n    change: function (value) {\n        results.push(value);\n    },\n    beforeChange: true\n});\n\nexpect(results).toEqual([10]);\n\nobject.foo.bar = 20;\nexpect(results).toEqual([10, 10]);\n\nobject.foo.bar = 30;\nexpect(results).toEqual([10, 10, 20]);\n```\n\nIf the product of an observer is an array, that array is always updated\nincrementally.  It will only get emitted once.  If you want it to get\nemitted every time its content changes, you can use the `contentChange`\nflag.\n\n```javascript\nvar lastResult;\nvar array = [[1, 2, 3], [4, 5, 6]];\nobserve(array, \"map{sum()}\", {\n    change: function (sums) {\n        lastResult = sums.slice();\n        // 1. [6, 15]\n        // 2. [6, 15, 0]\n        // 3. [10, 15, 0]\n    },\n    contentChange: true\n});\n\nexpect(lastResult).toEqual([6, 15]);\n\narray.push([0]);\nexpect(lastResult).toEqual([6, 15, 0]);\n\narray[0].push(4);\nexpect(lastResult).toEqual([10, 15, 0]);\n```\n\n### Nested Observers\n\nTo get the same effect as the previous example, you would have to nest\nyour own content change observer.\n\n```javascript\nvar i = 0;\nvar array = [[1, 2, 3], [4, 5, 6]];\nvar cancel = observe(array, \"map{sum()}\", function (array) {\n    function contentChange() {\n        if (i === 0) {\n            expect(array.slice()).toEqual([6, 15]);\n        } else if (i === 1) {\n            expect(array.slice()).toEqual([6, 15, 0]);\n        } else if (i === 2) {\n            expect(array.slice()).toEqual([10, 15, 0]);\n        }\n        i++;\n    }\n    contentChange();\n    array.addContentChangeListener(contentChange);\n    return function cancelContentChange() {\n        array.removeContentChangeListener(contentChange);\n    };\n});\narray.push([0]);\narray[0].push(4);\ncancel();\n```\n\nThis illustrates one crucial aspect of the architecture.  Observers\nreturn cancelation functions.  You can also return a cancelation\nfunction inside a callback observer.  That canceler will get called each\ntime a new value is observed, or when the parent observer is canceled.\nThis makes it possible to nest observers.\n\n```javascript\nvar object = {foo: {bar: 10}};\nvar cancel = observe(object, \"foo\", function (foo) {\n    return observe(foo, \"bar\", function (bar) {\n        expect(bar).toBe(10);\n    });\n});\n```\n\n### Bindings\n\nFRB provides utilities for declaraing and managing multiple bindings on\nobjects.  The `frb` (`frb/bindings`) module exports this interface.\n\n```javascript\nvar Bindings = require(\"frb\");\n```\n\nThe `Bindings` module provides `defineBindings` and `cancelBindings`,\n`defineBinding` and `cancelBinding`, as well as binding inspector\nmethods `getBindings` and `getBinding`.  All of these take a target\nobject as the first argument.\n\nThe `Bindings.defineBinding(target, descriptors)` method returns the\ntarget object for convenience.\n\n```javascript\nvar target = Bindings.defineBindings({}, {\n    \"fahrenheit\": {\"<->\": \"celsius * 1.8 + 32\"},\n    \"celsius\": {\"<->\": \"kelvin - 272.15\"}\n});\ntarget.celsius = 0;\nexpect(target.fahrenheit).toEqual(32);\nexpect(target.kelvin).toEqual(272.15);\n```\n\n`Bindings.getBindings` in that case would return an object with\n`fahrenheit` and `celsius` keys.  The values would be identical to the\ngiven binding descriptor objects, like `{\"<->\": \"kelvin - 272.15\"}`, but\nit also gets annotated with a `cancel` function and the default values\nfor any ommitted properties like `source` (same as `target`),\n`parameters` (same as `source`), and others.\n\n`Bindings.cancelBindings` cancels all bindings attached to an object and\nremoves them from the bindings descriptors object.\n\n```javascript\nBindings.cancelBindings(target);\nexpect(Bindings.getBindings(object)).toEqual({});\n```\n\n### Binding Descriptors\n\nBinding descriptors describe the source of a binding and additional\nparameters.  `Bindings.defineBindings` can set up bindings (```<-``` or\n```<->```), computed (```compute```) properties, and falls back to\ndefining ES5 properties with permissive defaults (`enumerable`,\n`writable`, and `configurable` all on by default).\n\nIf a descriptor has a ```<-``` or ```<->```, it is a binding descriptor.\nFRB creates a binding, adds the canceler to the descriptor, and adds the\ndescriptor to an internal table that tracks all of the bindings defined\non that object.\n\n```javascript\nvar object = Bindings.defineBindings({\n    darkMode: false,\n    document: document\n}, {\n    \"document.body.classList.has('dark')\": {\n        \"<-\": \"darkMode\"\n    }\n});\n```\n\nYou can get all the binding descriptors with `Bindings.getBindings`, or a\nsingle binding descriptor with `Bindings.getBinding`.  `Bindings.cancel` cancels\nall the bindings to an object and `Bindings.cancelBinding` will cancel just\none.\n\n```javascript\n// Continued from above...\nvar bindings = Bindings.getBindings(object);\nvar descriptor = Bindings.getBinding(object, \"document.body.classList.has('dark')\");\nBindings.cancelBinding(object, \"document.body.classList.has('dark')\");\nBindings.cancelBindings(object);\nexpect(Object.keys(bindings)).toEqual([]);\n```\n\n### Converters\n\nA binding descriptor can have a `convert` function, a `revert` function,\nor alternately a `converter` object.  Converters are useful for\ntransformations that cannot be expressed in the property language, or\nare not reversible in the property language.\n\nIn this example, `a` and `b` are synchronized such that `a` is always\nhalf of `b`, regardless of which property gets updated.\n\n```javascript\nvar object = Bindings.defineBindings({\n    a: 10\n}, {\n    b: {\n        \"<->\": \"a\",\n        convert: function (a) {\n            return a * 2;\n        },\n        revert: function (b) {\n            return b / 2;\n        }\n    }\n});\nexpect(object.b).toEqual(20);\n\nobject.b = 10;\nexpect(object.a).toEqual(5);\n```\n\nConverter objects are useful for reusable or modular converter types and\nconverters that track additional state.\n\n```javascript\nfunction Multiplier(factor) {\n    this.factor = factor;\n}\nMultiplier.prototype.convert = function (value) {\n    return value * this.factor;\n};\nMultiplier.prototype.revert = function (value) {\n    return value / this.factor;\n};\n\nvar doubler = new Multiplier(2);\n\nvar object = Bindings.defineBindings({\n    a: 10\n}, {\n    b: {\n        \"<->\": \"a\",\n        converter: doubler\n    }\n});\nexpect(object.b).toEqual(20);\n\nobject.b = 10;\nexpect(object.a).toEqual(5);\n```\n\nReusable converters have an implied direction, from some source type to\na particular target type.  Sometimes the types on your binding are the\nother way around.  For that case, you can use the converter as a\nreverter.  This merely swaps the `convert` and `revert` methods.\n\n```javascript\nvar uriConverter = {\n    convert: encodeURI,\n    revert: decodeURI\n};\nvar model = Bindings.defineBindings({}, {\n    \"title\": {\n        \"<->\": \"location\",\n        reverter: uriConverter\n    }\n});\n\nmodel.title = \"Hello, World!\";\nexpect(model.location).toEqual(\"Hello,%20World!\");\n\nmodel.location = \"Hello,%20Dave.\";\nexpect(model.title).toEqual(\"Hello, Dave.\");\n```\n\n### Computed Properties\n\nA computed property is one that gets updated with a function call when\none of its arguments changes.  Like a converter, it is useful in cases\nwhere a transformation or computation cannot be expressed in the\nproperty language, but can additionally accept multiple arguments as\ninput.  A computed property can be used as the source for another\nbinding.\n\nIn this example, we create an object as the root of multiple bindings.\nThe object synchronizes the properties of a \"form\" object with the\nwindow’s search string, effectively navigating to a new page whenever\nthe \"q\" or \"charset\" values of the form change.\n\n```javascript\nBindings.defineBindings({\n    window: window,\n    form: {\n        q: \"\",\n        charset: \"utf-8\"\n    }\n}, {\n    queryString: {\n        args: [\"form.q\", \"form.charset\"],\n        compute: function (q, charset) {\n            return \"?\" + QS.stringify({\n                q: q,\n                charset: charset\n            });\n        }\n    },\n    \"window.location.search\": {\n        \"<-\": \"queryString\"\n    }\n});\n```\n\n### Debugging with Traces\n\nA binding can be configured to log when it changes and why.  The `trace`\nproperty on a descriptor instructs the binder to log changes to the\nconsole.\n\n```javascript\nBindings.defineBindings({\n    a: 10\n}, {\n    b: {\n        \"<-\": \"a + 1\",\n    }\n});\n```\n\n\n## Reference\n\nFunctional Reactive Bindings is an implementation of synchronous,\nincremental object-property and collection-content bindings for\nJavaScript.  It was ripped from the heart of the [Montage][] web\napplication framework and beaten into this new, slightly magical form.\nIt must prove itself worthy before it can return.\n\n-   **functional**: The implementation uses functional building blocks\n    to compose observers and binders.\n-   **generic**: The implementation uses generic methods on collections,\n    like `addContentChangeListener`, so any object can implement the\n    same interface and be used in a binding.\n-   **reactive**: The values of properties and contents of collections\n    react to changes in the objects and collections on which they\n    depend.\n-   **synchronous**: All bindings are made consistent in the statement\n    that causes the change.  The alternative is asynchronous, where\n    changes are queued up and consistency is restored in a later event.\n-   **incremental**: If you update an array, it produces a content\n    change which contains the values you added, removed, and the\n    location of the change.  Most bindings can be updated using only\n    these values.  For example, a sum is updated by decreasing by the\n    sum of the values removed, and increasing by the sum of the values\n    added.  FRB can incrementally update `map`, `reversed`, `flatten`,\n    `sum`, and `average` observers.  It can also incrementally update\n    `has` bindings.\n-   **unwrapped**: Rather than wrap objects and arrays with observable\n    containers, FRB modifies existing arrays and objects to make them\n    dispatch property and content changes.  For objects, this involves\n    installing getters and setters using the ES5 `Object.defineProperty`\n    method.  For arrays, this involves replacing all of the mutation\n    methods, like `push` and `pop`, with variants that dispatch change\n    notifications.  The methods are either replaced by swapping the\n    `__proto__` or adding the methods to the instance with\n    `Object.defineProperties`.  These techniques should [work][Define\n    Property] starting in Internet Explorer 9, Firefox 4, Safari 5,\n    Chrome 7, and Opera 12.\n\n\n### Architecture\n\n-   [Collections][] provides **property, mapped content, and ranged\n    content change events** for objects, arrays, and other collections.\n    For objects, this adds a property descriptor to the observed object.\n    For arrays, this either swaps the prototype or mixes methods into\n    the array so that all methods dispatch change events.  \n    Caveats: you have to use a `set` method on Arrays to dispatch\n    property and content change events.  Does not work in older Internet\n    Explorers since they support neither prototype assignment or ES5\n    property setters.\n-   **observer** functions for watching an entire object graph for\n    incremental changes, and gracefully rearranging and canceling those\n    observers as the graph changes.  Observers can be constructed\n    directly or with a very small query language that compiles to a tree\n    of functions so no parsing occurs while the graph is being watched.\n-   one- and two-way **bindings** using binder and obserer functions to\n    incrementally update objects.\n-   **declarative** interface for creating an object graph with\n    bindings, properties, and computed properties with dependencies.\n\n\n### Bindings\n\nThe highest level interface for FRB resembles the ES5 Object constructor\nand can be used to declare objects and define and cancel bindings on\nthem with extended property descriptors.\n\n```javascript\nvar Bindings = require(\"frb\");\n\n// create an object\nvar object = Bindings.defineBindings({\n    foo: 0,\n    graph: [\n        {numbers: [1,2,3]},\n        {numbers: [4,5,6]}\n    ]\n}, {\n    bar: {\"<->\": \"foo\", enumerable: false},\n    numbers: {\"<-\": \"graph.map{numbers}.flatten()\"},\n    sum: {\"<-\": \"numbers.sum()\"},\n    reversed: {\"<-\": \"numbers.reversed()\"}\n});\n\nexpect(object.bar).toEqual(object.foo);\nobject.bar = 10;\nexpect(object.bar).toEqual(object.foo);\nexpect.foo = 20;\nexpect(object.bar).toEqual(object.foo);\n\n// note that the identity of the bound numbers array never\n// changes, because all of the changes to that array are\n// incrementally updated\nvar numbers = object.numbers;\n\n// first computation\nexpect(object.sum).toEqual(21);\n\n// adds an element to graph,\n// which pushes [7, 8, 9] to \"graph.map{numbers}\",\n// which splices [7, 8, 9] to the end of\n//  \"graph.map{numbers}.flatten()\",\n// which increments \"sum()\" by [7, 8, 9].sum()\nobject.graph.push({numbers: [7, 8, 9]});\nexpect(object.sum).toEqual(45);\n\n// splices [1] to the beginning of [1, 2, 3],\n// which splices [1] to the beginning of \"...flatten()\"\n// which increments \"sum()\" by [1].sum()\nobject.graph[0].numbers.unshift(1);\nexpect(object.sum).toEqual(46);\n\n// cancels the entire observer hierarchy, then attaches\n//  listeners to the new one.  updates the sum.\nobject.graph = [{numbers: [1,2,3]}];\nexpect(object.sum).toEqual(6);\n\nexpect(object.reversed).toEqual([3, 2, 1]);\n\nexpect(object.numbers).toBe(numbers) // still the same object\n\nBindings.cancelBindings(object); // cancels all bindings on this object and\n// their transitive observers and event listeners as deep as\n// they go\n```\n\n-   `Bindings.defineBindings(object, name, descriptor)`\n-   `Bindings.defineBinding(object, name, descriptor)`\n-   `Bindings.getBindings(object)`\n-   `Bindings.getBinding(object, name)`\n-   `Bindings.cancelBindings(object)`\n-   `Bindings.cancelBinding(object, name)`\n\nA binding descriptor contains:\n\n-   `target`: the\n-   `targetPath`: the target\n-   `targetSyntax`: the syntax tree for the target path\n-   `source`: the source object, which defaults to `target`\n-   `sourcePath`: the source path, from either ```<-``` or ```<->```\n-   `sourceSyntax`: the syntax tree for the source path\n-   `twoWay`: whether the binding goes in both directions, if ```<->```\n    was the source path.\n-   `parameters`: the parameters, which default to `source`.\n-   `convert`: a function that converts the source value to the target\n    value, useful for coercing strings to dates, for example.\n-   `revert`: a function that converts the target value to the source\n    value, useful for two-way bindings.\n-   `converter`: an object with `convert` and optionally also a `revert`\n    method.  The implementation binds these methods to their converter\n    and stores them in `covert` and `revert`.\n-   `serializable`: a note from the Montage Deserializer, to the [Montage\n    Serializer][], indicating that the binding came from a\n    serialization, and to a serialization it must return.\n-   `cancel`: a function to cancel the binding\n\n[Montage Serializer]: https://github.com/montagejs/mousse\n\n### Bind\n\nThe `bind` module provides direct access to the `bind` function.\n\n```javascript\nvar bind = require(\"frb/bind\");\n\nvar source = [{numbers: [1,2,3]}, {numbers: [4,5,6]}];\nvar target = {};\nvar cancel = bind(target, \"summary\", {\n    \"<-\": \"map{[numbers.sum(), numbers.average()]}\",\n    source: source\n});\n\nexpect(target.summary).toEqual([\n    [6, 2],\n    [15, 5]\n]);\n\ncancel();\n```\n\n`bind` is built on top of `parse`, `compileBinder`, and\n`compileObserver`.\n\n### Compute\n\nThe `compute` module provides direct access to the `compute` function,\nused by `Bindings` to make computed properties.\n\n```javascript\nvar compute = require(\"frb/compute\");\n\nvar source = {operands: [10, 20]};\nvar target = {};\nvar cancel = compute(target, \"sum\", {\n    source: source,\n    args: [\"operands.0\", \"operands.1\"],\n    compute: function (a, b) {\n        return a + b;\n    }\n});\n\nexpect(target.sum).toEqual(30);\n\n// change one operand\nsource.operands.set(1, 30); // needed to dispatch change notification\nexpect(target.sum).toEqual(40);\n```\n\n### Observe\n\nThe `observe` modules provides direct access to the `observe` function.\n`observe` is built on top of `parse` and `compileObserver`.\n`compileObserver` creates a tree of observers using the methods in the\n`observers` module.\n\n```javascript\nvar observe = require(\"frb/observe\");\n\nvar source = [1, 2, 3];\nvar sum;\nvar cancel = observe(source, \"sum()\", function (newSum) {\n    sum = newSum;\n});\n\nexpect(sum).toBe(6);\n\nsource.push(4);\nexpect(sum).toBe(10);\n\nsource.unshift(0); // no change\nexpect(sum).toBe(10);\n\ncancel();\nsource.splice(0, source.length); // would change\nexpect(sum).toBe(10);\n```\n\n`observe` produces a cancelation hierarchy.  Each time a value is\nremoved from an array, the underlying observers are canceled.  Each time\na property is replaced, the underlying observer is canceled.  When new\nvalues are added or replaced, the observer produces a new canceler.  The\ncancel function returned by `observe` commands the entire underlying\ntree.\n\nObservers also optional accept a descriptor argument in place of a\ncallback.\n\n-   `set`: the change handler, receives `value` for most observers, but\n    also `key` and `object` for property changes.\n-   `parameters`: the value for `$` expressions.\n-   `beforeChange`: instructs an observer to emit the previous value\n    before a change occurs.\n-   `contentChange`: instructs an observer to emit an array every time\n    its content changes.  By default, arrays are only emitted once.\n\n```javascript\nvar object = {};\nvar cancel = observe(object, \"array\", {\n    change: function (value) {\n        // may return a cancel function for a nested observer\n    },\n    parameters: {},\n    beforeChange: false,\n    contentChange: true\n});\n\nobject.array = []; // emits []\nobject.array.push(10); // emits [10]\n```\n\n### Evaluate\n\nThe `compile-evaluator` module returns a function that accepts a syntax\ntree and returns an evaluator function.  The evaluator accepts a scope\n(which may include a value, parent scope, parameters, a document, and\ncomponents) and returns the corresponding value without all the cost or\nbenefit of setting up incremental observers.\n\n```javascript\nvar parse = require(\"frb/parse\");\nvar compile = require(\"frb/compile-evaluator\");\nvar Scope = require(\"frb/scope\");\n\nvar syntax = parse(\"a.b\");\nvar evaluate = compile(syntax);\nvar c = evaluate(new Scope({a: {b: 10}}))\nexpect(c).toBe(10);\n```\n\nThe `evaluate` module returns a function that accepts a path or syntax\ntree, a source value, and parameters and returns the corresponding\nvalue.\n\n```javascript\nvar evaluate = require(\"frb/evaluate\");\nvar c = evaluate(\"a.b\", {a: {b: 10}})\nexpect(c).toBe(10);\n```\n\n\n### Stringify\n\nThe `stringify` module returns a function that accepts a syntax tree and\nreturns the corresponding path in normal form.\n\n```javascript\nvar stringify = require(\"frb/stringify\");\n\nvar syntax = {type: \"and\", args: [\n    {type: \"property\", args: [\n        {type: \"value\"},\n        {type: \"literal\", value: \"a\"}\n    ]},\n    {type: \"property\", args: [\n        {type: \"value\"},\n        {type: \"literal\", value: \"b\"}\n    ]}\n]};\n\nvar path = stringify(syntax);\nexpect(path).toBe(\"a && b\");\n```\n\n\n### Grammar\n\n-   **expression** = **logical-or-expression**\n-   **conditional-expression** = **logical-or-expression** ( `?`\n    **expression** `:` **expression** )?\n-   **logical-or-expression** = **logical-and-expression** ( `||` *(or)*\n    **relation expression** )?\n-   **logical-and-expression** = **relation-expression** ( `&&` *(and)*\n    **relation-expression** )?\n-   **relation-expression** = **arithmetic expression** (\n    **relation-operator** **arithmetic-expression** )?\n    -   **relation-operator** = `==` *(equals)* or ```<``` *(lt)* or\n        ```<=``` *(le)* or ```>``` *(gt)* or ```>=``` *(ge)* or\n        ```<=>``` *(compare)*\n-   **arithmetic-expression** = **multiplicative-expresion** delimited by **arithmetic-operator**\n    -   **arithmetic-operator** = `+` *(add)* or `-` *(sub)*\n-   **multiplicative-expression** = **exponential-expression**\n    delimited by **multiplicative-operator**\n    -   **multiplicative-operator** = `*` *(mul)* or `/` *(div)* or\n        `%` *(mod)* or `rem` *(rem)*\n-   **exponential-expression** = **unary-expression** delimited by\n    **exponential-operator**\n    -   **exponential-operator** = `**` *(pow)* or `//` *(root)* or\n        `%%` *(log)*\n-   **unary-expression** = **unary-operator** ? **path-expression**\n    -   **unary-operator** = `+` *(number)* or `-` *(neg)* or `!` *(not)*\n-   **path-expression** =\n    -   **literal** *(literal with value)* or\n    -   **array-expression** *(tuple)* or\n    -   **object-expression** *(record)* or\n    -   `(` **expression** `)` **tail-expression** or\n    -   **property-name** **tail-expression** *(property)* or\n    -   **function-call** *(piped)* **tail-expression** or\n    -   **block-call** **tail-expression** or\n    -   `#` **element-id** **tail-expression** *(element by id)* or\n    -   `@` **component-label** **tail-expression** *(component by label)*\n    -   `&` **function-call** *(bare)* **tail-expression** or\n-   **tail-expression** =\n    -   **property-expression** or\n    -   **with-expression** or\n    -   **variable-property-expression**\n-   **property-expression** = `.` **property-name** **tail-expression**\n    *(property)*\n-   **with-expression** = `.`\n    -   `(` **expression** `)` **tail-expression** or\n    -   **array-expression** **tail-expression** or\n    -   **object-expression** **tail-expression**\n-   **variable-property-expression** = `[` **expression** `]`\n    **tail-expression** *(property)*\n-   **array-expression** = `[` ( **expression** or `()` *(value)* )\n    delimited by `,` `]` *(tuple with each expression in args array)*\n-   **object-expression** = `{` (**property-name** `:` **expression**)\n    delimited by `,` `}` *(record, with each expression as a value in an\n    args object instead of array)*\n-   **property-name** = ( **non-space-character** )+\n-   **function-call** = **function-name** `(` **expression** delimited\n    by `,` `)`\n    -   **function-name** = `flatten` or `reversed` or `enumerate` or\n        `sum` or `average` or `has` or `view` or `startsWith` or\n        `endsWith` or `contains` or `join` or `split` or `range` or\n        `keys` or `values` or `entries` *(eponymous syntax node types)*\n-   **block-call** = **function-name** `{` **expression** `}`\n    -   **block-name** = `map` *(mapBlock)* or `filter` *(filterBlock)*\n        or `some` *(someBlock)* or `every` *(everyBlock)* or `sorted`\n        *(sortedBlock)* or `sortedSet` *(sortedSetBlock)* or `min`\n        *(minBlock)* or `max` *(maxBlock)* or `group` *(groupBlock)* or\n        `groupMap` *(groupMapBlock)* or **function-name** *(map followed\n        by function-call)*\n-   **literal** = **string-literal** or **number-literal**\n    -   **number-literal** = **digits** ( `.` **digits** )? *(literal\n        and value is a number)*\n    -   **string-literal** = `'` ( **non-quote-character** or `\\`\n        **character** ) `'` *(literal and value is a string)*\n\nLegend:\n\n-   **terms-of-the-grammar**\n-   `tokens`\n-   *(corresponding syntax node type name)*\n-   (group)\n-   definition =\n-   or\n-   delimited by\n-   optional?\n-   any*\n-   some+\n\n### Semantics\n\nAn expression is observed with a source value and emits a target\none or more times.  All expressions emit an initial value.  Array\ntargets are always updated incrementally.  Numbers and boolean are\nemited anew each time their value changes.\n\nIf any operand is `null` or `undefine`, a binding will not emit an\nupdate.  Thus, if a binding’s source becomes invalid, it does not\ncorrupt its target but waits until a valid replacement becomes\navailable.\n\n-   Literals are interpreted as their corresponding value.\n-   Value terms provide the source.\n-   Parameters terms provide the parameters.\n-   In a path-expression, the first term is evaluated with the source\n    value.\n-   Each subsequent term of a path expression uses the target of the\n    previous as its source.\n-   A property-expression or variable-property-expression observes the\n    key of the source object using `Object.addPropertyChangeListener`.\n-   An element identifier (with the `#` prefix) uses the `document`\n    property of the `parameters` object and emits\n    `document.getElementById(id)`, or dies trying.  Changes to the\n    document are not observed.\n-   A component label (with the `@` prefix) uses the `serialization`\n    property of `parameters` object and emits\n    `serialization.getObjectForLable(label)`, or dies trying.  Changes\n    to the serialization are not observed.  This syntax exists to\n    support [Montage][] serializations.\n-   A \"map\" block observes the source array and emits a target array.\n    The target array is emitted once and all subsequent updates are\n    reflected as content changes that can be independently observed with\n    `addContentChangeListener`.  Each element of the target array\n    corresponds to the observed value of the block expression using the\n    respective element in the source array as the source value.\n-   A \"map\" function call receives a function as its argument rather\n    than a block.\n-   A \"filter\" block observes the source array and emits a target array\n    containing only those values from the source array that actively\n    pass the predicate described in the block expression useing the\n    respective element in the source array as the source value.  As with\n    \"map\", filters update the target array incrementally.\n-   A \"some\" block observes whether any of the values in the source\n    collection meet the given criterion.\n-   A \"every\" block observes whether all of the values in the source\n    collection meet the given criterion.\n-   A \"sorted\" block observes the sorted version of an array, by a\n    property of each value described in the block, or itself if empty.\n    Sorted arrays are incrementally updating as values are added and\n    deleted from the source.\n-   A \"sortedSet\" block observes a collection that emits range change\n    events, by way of a property of each value described in the block,\n    or itself if empty, emitting a `SortedSet` value exactly once.  If\n    the input is or becomes invalid, the sorted set is cleared, not\n    replaced.  The sorted set will always contain the last of each group\n    of equivalant values from the input.\n-   A \"min\" block observes the which of the values in a given collection\n    produces the smallest value through the given relation.\n-   A \"max\" block observes the which of the values in a given collection\n    produces the largest value through the given relation.\n-   A \"group\" block observes which values belong to corresponding\n    equivalence classes as determined by the result of a given\n    expression on each value.  The observer is responsible for adding\n    and removing classes as they are populated and depopulated.  Each\n    class tracks the key (result of the block expression for every\n    member of a class), and an the values of the corresponding class as\n    an array.  Values are added to the end of each array as they are\n    discovered.\n-   Any function call with a \"block\" implies calling the function on the\n    result of a \"map\" block.\n-   A \"flatten\" function call observes a source array and produces a\n    target array.  The source array must only contain inner arrays.  The\n    target array is emitted once and all subsequent updates can be\n    independently observed with `addContentChangeListener`.  The target\n    array will always contain the concatenation of all of the source\n    arrays.  Changes to the inner and outer source arrays are reflected\n    with incremental splices to the target array in their corresponding\n    positions.\n-   A \"reversed\" function call observes the source array and produces a\n    target array that contains the elements of the source array in\n    reverse order.  The target is incrementally updated.\n-   An \"enumerate\" expression observes [key, value] pairs from an array.\n    The output array of arrays is incrementally updated with range\n    changes from the source.\n-   A \"view\" function call observes a sliding window from the source,\n    from a start index (first argument) of a certain length (second\n    argument).  The source can be any collection that dispatches range\n    changes and the output will be an array of the given length.\n-   A \"sum\" function call observes the numeric sum of the source array.\n    Each alteration to the source array causes a new sum to be emitted,\n    but the sum is computed incrementally by observing the smaller sums\n    of the spliced values, added and removed.\n-   An \"average\" function call observes the average of the input values,\n    much like \"sum\".\n-   A \"has\" function call observes the source collection for whether it\n    contains an observed value.\n-   A \"tuple\" expression observes a source value and emits a single\n    target array with elements corresponding to the respective\n    expression in the tuple.  Each inner expression is evaluated with\n    the same source value as the outer expression.\n-   A \"startsWith\" function call observes whether the left string\n    starts with the right string.\n-   An \"endsWith\" function call observes whether the right string\n    ends with the right string.\n-   A \"contains\" function call observes whether the left string contains\n    the right string.\n-   A \"join\" function observes the left array joined by the right\n    delimiter, or an empty string.   This is not an incremental\n    operation.\n-   A \"split\" function observes the left string broken into an array\n    between the right delimiter, or an empty string.  This is not an\n    incremental operation.\n-   A \"range\" function call observes an array with the given length\n    containing sequential numbers starting with zero.  The output array\n    is updated incrementally and will dispatch one range change each\n    time the size changes by any difference.\n-   A \"keys\" function call observes an incrementally updated array of\n    the keys that a given map contains.  The keys are maintained in\n    insertion order.\n-   A \"values\" function call observes an incrementally updated array of\n    the values that a given map contains.  The values are maintained in\n    insertion order.\n-   An \"entries\" function call observes an incrementally updated array\n    of [key, value] pairs from a given mapping.  The entries are\n    retained in insertion order.\n\nUnary operators:\n\n-   \"number\" coerces the value to a number.\n-   \"neg\" converts a number to its negative.\n-   \"not\" converts a boolean to its logical opposite.\n\nBinary operators:\n\n-   \"add\" adds the left to the right\n-   \"sub\" subtracts the right from the left\n-   \"mul\" multiples the left to the right\n-   \"div\" divides the left by the right\n-   \"mod\" produces the left modula the right.  This is proper modula,\n    meaning a negative number that does not divide evenly into a\n    positive number will produce the difference between that number and\n    the next evenly divisible number in direction of negative infinity.\n-   \"rem\" produces the remainder of dividing the left by the right.  If\n    the left does not divide evenly into the right it will produce the\n    difference between that number and the next evenly divisible number\n    in the direction of zero.  That is to say, `rem` can produce\n    negative numbers.\n-   \"pow\" raises the left to the power of the right.\n-   \"root\" produces the \"righth\" root of the left.\n-   \"log\" produces the logarithm of the left on the right base.\n-   \"lt\" less than, as determined with `Object.compare(left, right) <\n    0`.\n-   \"le\" less than or equal, as determined with `Object.compare(left,\n    right) <= 0`.\n-   \"gt\" greater than, as determined with `Object.compare(left, right) >\n    0`.\n-   \"ge\" greater than or equal, as determined with `Object.compare(left,\n    right) >= 0`.\n-   \"compare\" as determined by `Object.compare(left, right)`.\n-   \"equals\" whether the left is equal to the right as determined by\n    `Object.equals(left, right)`.\n-   *Note: there is no \"not equals\" syntax node. The `!=` operator gets\n    converted into a \"not\" node around an \"equals\" node.\n-   \"and\" logical union\n-   \"or\" logical intersection\n\nTernary operator:\n\n-   \"if\" observes the condition (first argument, expression before the\n    `?`).  If the expression is true, the result observes the consequent\n    expression (second argument, between the question mark and the\n    colon), and if it is false, the result observes the alternate (the\n    third argument, after the colon).  If the condition is null or\n    undefined, the result is null or undefined.\n\nOn the left hand side of a binding, the last term has alternate\nsemantics.  Binders receive a target as well as a source.\n\n-   A \"property\" observes an object and a property name from the target,\n    and a value from the source.  When any of these change, the binder\n    upates the value for the property name of the object.\n-   A \"get\" observes a collection and a key from the target, and a value\n    from the source.  When any of these change, the binder updates the\n    value for the key on the collection using `collection.set(key,\n    value)`.  This is suitable for arrays and custom map\n    [Collections][].\n-   A \"equals\" expression observes a boolean value from the source.  If\n    that boolean becomes true, the equality expression is made true by\n    assigning the right expression to the left property of the equality,\n    turning the \"equals\" into an \"assign\" conceptually.  No action is\n    taken if the boolean becomes false.\n-   A \"reversed\" expression observes an indexed collection and maintains\n    a mirror array of that collection.\n-   A \"has\" function call observes a boolean value from the source, and\n    an collection and a sought value from the target.  When the value is\n    true and the value is absent in the collection, the binder uses the\n    `add` method of the collection (provided by a shim for arrays) to\n    make it true that the collection contains the sought value.  When\n    the value is false and the value does appear in the collection one\n    or more times, the binder uses the `delete` or `remove` method of\n    the collection to remove all occurrences of the sought value.\n-   An \"if\" binding observes the condition and binds the target either\n    to the consequent or alternate.  If the condition is null or\n    undefined, the target is not bound.\n\nIf the target expression ends with `.*`, the content of the target is\nbound instead of the property.  This is useful for binding the content\nof a non-array collection to the content of another indexed collection.\nThe collection can be any collection that implements the \"observable\ncontent\" interface including `dispatchContentChange(plus, minus,\nindex)`, `addContentChangeListener`, and `removeContentChangeListener`.\n\n### Language Interface\n\n```javascript\nvar parse = require(\"frb/parse\");\nvar compileObserver = require(\"frb/compile-observer\");\nvar compileBinder = require(\"frb/compile-binder\");\n```\n\n-   `parse(text)` returns a syntax tree.\n-   `compileObserver(syntax)` returns an observer function of the form\n    `observe(callback, source, parameters)` which in turn returns a\n    `cancel()` function.  `compileObserver` visits the syntax tree and\n    creates functions for each node, using the `observers` module.\n-   `compileBinder(syntax)` returns a binder function of the form\n    `bind(observeValue, source, target, parameters)` which in turn\n    returns a `cancel()` function.  `compileBinder` visits the root node\n    of the syntax tree and delegates to `compileObserver` for its terms.\n    The root node must be a `property` at this time, but could\n    conceivably be any function with a clear inverse operation like\n    `map` and `reversed`.\n\n### Syntax Tree\n\nThe syntax tree is JSON serializable and has a \"type\" property.  Nodes\nhave the following types:\n\n-   `value` corresponds to observing the source value\n-   `parameters` corresponds to observing the parameters object\n-   `literal` has a `value` property and observes that value\n-   `element` has an `id` property and observes an element from the\n    `parameters.document`, by way of `getElementById`.\n-   `component` has a `label` property and observes a component from the\n    `parameters.serialization`, by way of `getObjectForLabel`.  This\n    feature support's [Montage][]’s serialization format.\n\nAll other node types have an \"args\" property that is an array of syntax\nnodes (or an \"args\" object for `record`).\n\n-   `property`: corresponds to observing a property named by the right\n    argument of the left argument.\n-   `get`: corresponds to observing the value for a key (second\n    argument) in a collection (first argument).\n-   `with`: corresponds to observing the right expression using the left\n    expression as the source.\n-   `has`: corresponds to whether the key (second argument) exists\n    within a collection (first argument)\n-   `mapBlock`: the left is the input, the right is an expression to\n    observe on each element of the input.\n-   `filterBlock`: the left is the input, the right is an expression to\n    determine whether the result is included in the output.\n-   `someBlock`: the left is the input, the right is a criterion.\n-   `everyBlock`: the left is the input, the right is a criterion.\n-   `sortedBlock`: the left is the input, the right is a relation on\n    each value of the input on which to compare to determine the order.\n-   `sortedSetBlock`: differs only in semantics from `sortedBlock`.\n-   `minBlock`: the left is the input, the right is a relation on each\n    value of the input by which to compare the value to others.\n-   `maxBlock`: the left is the input, the right is a relation on each\n    value of the input by which to compare the value to others.\n-   `groupBlock`: the left is the input, the right is an expression that\n    provides the key for an equivalence class for each value in the\n    input.  The output is an array of entries, `[key, class]`, with the\n    shared key of every value in the equivalence class.\n-   `groupMapBlock`: has the same input semantics as `groupBlock`, but\n    the output is a `Map` instance instead of an array of entries.\n-   `tuple`: has any number of arguments, each an expression to observe\n    in terms of the source value.\n-   `record`: as an args object. The keys are property names for the\n    resulting object, and the values are the corresponding syntax nodes\n    for the values.\n-   `view`: the arguments are the input, the start position, and the\n    length of the sliding window to view from the input.  The input may\n    correspond to any ranged content collection, like an array or sorted\n    set.\n-   `rangeContent`: corresponds to the content of an ordered collection\n    that can dispatch indexed range changes like an array or sorted set.\n    This indicates to a binder that it should replace the content of the\n    target instead of replacing the target property with the observed\n    content of the source.  A range content node has no effect on the\n    source.\n-   `mapContent`: corresponds to the content of a map-like collection\n    including arrays and all map [Collections][].  These collections\n    dispatch map changes, which create, read, update, or delete\n    key-to-value pairs.  This indicates to a binder to replace the\n    content of the target map-like collection with the observed content\n    of the source, instead of replacing the target collection.  A map\n    change node on the source side just passes the collection forward\n    without alteration.\n\nFor all operators, the \"args\" property are operands.  The node types for\nunary operators are:\n\n-   ```+```: `number`, arithmetic coercion\n-   ```-```: `neg`, arithmetic negation\n-   ```!```: `not`, logical negation\n\nFor all binary operators, the node types are:\n\n-   ```**```: `pow`, exponential power\n-   ```//```: `root`, of 2 square root, of 3 cube root, etc\n-   ```%%```: `log`, logarithm with base\n-   ```*```: `mul`, multiplication\n-   ```/```: `div`, division\n-   ```%```: `mod`, modulo (toward negative infinity, always positive)\n-   ```rem```: `rem`, remainder (toward zero, negative if negative)\n-   ```+```: `add`, addition\n-   ```-```: `sub`, subtraction\n-   ```<```: `lt`, less than\n-   ```<=```: `le`, less than or equal\n-   ```>```: `gt`, greater than\n-   ```>=```: `ge`, greater than or equal\n-   ```<=>```: `compare`\n-   ```==```: ``equals``, equality comparison and assignment\n-   ```!=``` produces unary negation and equality comparison or\n    assignment so does not have a corresponding node type.  The\n    simplification makes it easier to rotate the syntax tree\n    algebraically.\n-   ```&&```, `and`, logical and\n-   ```||```, `or`, logical or\n\nFor the ternary operator:\n\n-   ```?``` and ```:```: `if`, ternary conditional\n\nFor all function calls, the right hand side is a tuple of arguments.\n\n-   `reversed`\n-   `enumerate`\n-   `flatten`\n-   `sum`\n-   `average`\n-   `startsWith`\n-   `endsWith`\n-   `contains`\n-   `join`\n-   `split`\n-   `range`\n-   `keys`\n-   `values`\n-   `entries`\n\n\n### Observers and Binders\n\nThe `observers` module contains functions for making all of the\ndifferent types of observers, and utilities for creating new ones.\nAll of these functions are or return an observer function of the form\n`observe(emit, value, parameters)` which in turn returns `cancel()`.\n\n-   `observeValue`\n-   `observeParameters`\n-   `makeLiteralObserver(value)`\n-   `makeElementObserver(id)`\n-   `makeComponentObserver(label)`\n-   `makeRelationObserver(callback, thisp)` is unavailable through the\n    property binding language, translates a value through a JavaScript\n    function.\n-   `makeComputerObserver(observeArgs, compute, thisp)` applies\n    arguments to the computation function to get a new value.\n-   `makeConverterObserver(observeValue, convert, thisp)` calls the\n    converter function to transform a value to a converted value.\n-   `makePropertyObserver(observeObject, observeKey)`\n-   `makeGetObserver(observeCollection, observeKey)`\n-   `makeMapFunctionObserver(observeArray, observeFunction)`\n-   `makeMapBlockObserver(observeArray, observeRelation)`\n-   `makeFilterBlockObserver(observeArray, observePredicate)`\n-   `makeSortedBlockObserver(observeArray, observeRelation)`\n-   `makeEnumerationObserver(observeArray)`\n-   `makeFlattenObserver(observeOuterArray)`\n-   `makeTupleObserver(...observers)`\n-   `makeObserversObserver(observers)`\n-   `makeReversedObserver(observeArrayh)`\n-   `makeWindowObserver` is not presently available through the language\n    and is subject to change.  It is for watching a length from an array\n    starting at an observable index.\n-   `makeSumObserver(observeArray)`\n-   `makeAverageObserver(observeArray)`\n-   `makeParentObserver(observeExpression)`\n-   *etc*\n\nThese are utilities for making observer functions.\n\n-   `makeNonReplacing(observe)` accepts an array observer (the emitted\n    values must be arrays) and returns an array observer that will only\n    emit the target once and then incrementally update that target.  All\n    array observers use this decorator to handle the case where the\n    source value gets replaced.\n-   `makeArrayObserverMaker(setup)` generates an observer that uses an\n    array as its source and then incrementally updates a target value,\n    like `sum` and `average`.  The `setup(source, emit)` function must\n    return an object of the form `{contentChange, cancel}` and arrange\n    for `emit` to be called with new values when `contentChange(plus,\n    minus, index)` receives incremental updates.\n-   `makeUniq(callback)` wraps an emitter callback such that it only\n    forwards new values.  So, if a value is repeated, subsequent calls\n    are ignored.\n-   `autoCancelPrevious(callback)` accepts an observer callback and\n    returns an observer callback.  Observer callbacks may return\n    cancelation functions, so this decorator arranges for the previous\n    canceler to be called before producing a new one, and arranges for\n    the last canceler to be called when the whole tree is done.\n-   `once(callback)` accepts a canceler function and ensures that the\n    cancelation routine is only called once.\n\nThe `binders` module contains similar functions for binding an observed\nvalue to a bound value.  All binders are of the form `bind(observeValue,\nsource, target, parameters)` and return a `cancel()` function.\n\n-   `makePropertyBinder(observeObject, observeKey)`\n-   `makeGetBinder(observeCollection, observeKey)`\n-   `makeHasBinder(observeCollection, observeValue)`\n-   `makeEqualityBinder(observeLeft, observeRight)`\n-   `makeRangeContentBinder(observeTarget)`\n-   `makeMapContentBinder(observeTarget)`\n-   `makeReversedBinder(observeTarget)`\n\n\n[Collections]: https://github.com/kriskowal/collections\n[Define Property]: http://kangax.github.com/es5-compat-table/#define-property-webkit-note\n[Montage]: https://github.com/montagejs/montage\n[Mr]: https://github.com/kriskowal/mr\n[Mutation Observers]: https://developer.mozilla.org/en-US/docs/DOM/DOM_Mutation_Observers\n[Node.js]: http://nodejs.org/\n\n","readmeFilename":"README.md","_id":"frb@0.2.13","_from":"frb@~0.2.10","directories":{"lib":"./"},"hash":"73de13b","mappings":{"collections":{"name":"collections","hash":"5ea2535","location":"../collections@5ea2535/"}},"production":true,"useScriptInjection":true}})
;
//*/
montageDefine("7aba37c","package.json",{exports: {"name":"weak-map","version":"1.0.0","main":"weak-map.js","readme":"ERROR: No README data found!","_id":"weak-map@1.0.0","_from":"weak-map@1.0.0","directories":{"lib":"./"},"hash":"7aba37c","mappings":{},"production":true,"useScriptInjection":true}})
;
//*/
montageDefine("2847ee2","package.json",{exports: {"name":"q","version":"0.9.6","description":"A library for promises (CommonJS/Promises/A,B,D)","homepage":"https://github.com/kriskowal/q","author":"Kris Kowal <kris@cixar.com> (https://github.com/kriskowal)","keywords":["q","promise","promises","promises-a","promises-aplus","deferred","future","async","flow control","fluent","browser","node"],"contributors":["Kris Kowal <kris@cixar.com> (https://github.com/kriskowal)","Irakli Gozalishvili <rfobic@gmail.com> (http://jeditoolkit.com)","Domenic Denicola <domenic@domenicdenicola.com> (http://domenicdenicola.com)"],"bugs":{"mail":"kris@cixar.com","url":"http://github.com/kriskowal/q/issues"},"licenses":[{"type":"MIT","url":"http://github.com/kriskowal/q/raw/master/LICENSE"}],"main":"q.js","repository":{"type":"git","url":"git://github.com/kriskowal/q.git"},"engines":{"node":">=0.6.0","teleport":">=0.2.0"},"dependencies":{},"devDependencies":{"jshint":"~2.1.3","cover":"*","jasmine-node":"1.2.2","opener":"*","promises-aplus-tests":"1.x","grunt":"~0.4.1","grunt-cli":"~0.1.9","grunt-contrib-uglify":"~0.2.2"},"scripts":{"test":"jasmine-node spec && promises-aplus-tests spec/aplus-adapter","test-browser":"opener spec/q-spec.html","lint":"jshint q.js","cover":"cover run node_modules/jasmine-node/bin/jasmine-node spec && cover report html && opener cover_html/index.html","minify":"grunt","prepublish":"grunt"},"directories":{"test":"./spec","lib":"./"},"hash":"2847ee2","mappings":{},"production":true,"useScriptInjection":true}})
;
//*/
montageDefine("6607c26","index.html",{text:'<!doctype html>\n<html lang=en data-framework=montage id=montage-todomvc>\n	<head>\n		<meta charset=utf-8>\n		<meta http-equiv=X-UA-Compatible content="IE=edge">\n		<title>Montage • TodoMVC</title>\n\n		<link rel=stylesheet href="bower_components/todomvc-common/base.css">\n		<link rel=stylesheet href="assets/app.css">\n	</head>\n	<body>\n		<div data-montage-id=todo-container id=todo-container></div>\n\n		<script src="bower_components/todomvc-common/base.js"></script>\n		<script src="packages/montage@6364dae/montage.js"></script>\n		<script type="text/montage-serialization">\n			{\n				"owner": {\n					"prototype": "montage/ui/loader.reel",\n					"properties": {\n						"element": {"#": "todo-container"}\n					}\n				}\n			}\n		</script>\n	</body>\n</html>'});
;
//*/
montageDefine("6364dae","core/core",{dependencies:["collections/shim","core/shim/object","core/shim/array","core/shim/string","core/extras/object","core/extras/date","core/extras/element","core/extras/function","core/extras/regexp","core/extras/string","core/uuid","collections/listen/property-changes","core/bindings","core/paths","core/serialization/bindings","core/promise"],factory:function(require,exports,module){/* <copyright>
Copyright (c) 2012, Motorola Mobility LLC.
All Rights Reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

* Redistributions of source code must retain the above copyright notice,
  this list of conditions and the following disclaimer.

* Redistributions in binary form must reproduce the above copyright notice,
  this list of conditions and the following disclaimer in the documentation
  and/or other materials provided with the distribution.

* Neither the name of Motorola Mobility LLC nor the names of its
  contributors may be used to endorse or promote products derived from this
  software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
</copyright> */
/*global Element */
/**
 @module montage
 @requires core/shim/object
 @requires core/shim/array
 @requires core/shim/string
 @requires core/extras/object
 @requires core/extras/string
 @requires core/extras/function
 @requires core/extras/date
 @requires core/extras/element
 @requires core/extras/regexp
*/
require("collections/shim");
require("core/shim/object");
require("core/shim/array");
require("core/shim/string");
require("core/extras/object");
require("core/extras/date");
require("core/extras/element");
require("core/extras/function");
require("core/extras/regexp");
require("core/extras/string");

var ATTRIBUTE_PROPERTIES = "AttributeProperties",
    UNDERSCORE = "_",
    PROTO = "__proto__",
    VALUE = "value",
    ENUMERABLE = "enumerable",
    DISTINCT = "distinct",
    SERIALIZABLE = "serializable",
    MODIFY = "modify";

var Array_prototype = Array.prototype;

var Object_prototype = Object.prototype;

// The CONSTRUCTOR_COMPATIBILITY flag marks areas that allow the migration from Montage.create to Constructor.specialize
// The following is done:
// - Any properties defined on the prototype that are used on the constructor fire a deperecation warning prompting the
//   developer to move them to the second argument of specialize().
// - Adds a create method to the constructor can be used as Proto.create().
// - Adds support for 'didCreate' so that it can be used interchangeably with the 'constructor' property.
// - When calling Montage.create with a function as the first argument we use the function as a constructor or call
//   specialize on it to create a subtype.
var CONSTRUCTOR_COMPATIBILITY = true;

/**
 @class Montage
 */
var Montage = exports.Montage = function Montage() {};

// to monkey patch a method on an object
Montage.deprecate = function deprecate(scope, deprecatedFunction, name, alternative) {
    var deprecationWrapper = function () {
        var depth = Error.stackTraceLimit;
        Error.stackTraceLimit = 2;
        if (typeof console !== "undefined" && typeof console.warn === "function") {
            if(alternative) {
                console.warn(name + " is deprecated, use " + alternative + " instead.", new Error("").stack);
            } else {
                //name is a complete message
                console.warn(name, new Error("").stack);
            }

        }
        Error.stackTraceLimit = depth;
        return deprecatedFunction.apply(scope ? scope : this, arguments);
    };
    deprecationWrapper.deprecatedFunction = deprecatedFunction;
    return deprecationWrapper;
}

// too call a function immediately and log a deprecation warning
Montage.callDeprecatedFunction = function callDeprecatedFunction(scope, callback, name, alternative/*, ...args */) {
    var depth = Error.stackTraceLimit,
        scopeName,
        args;

    Error.stackTraceLimit = 2;
    if (typeof console !== "undefined" && typeof console.warn === "function") {
        scopeName = Montage.getInfoForObject(scope).objectName;

        if(alternative) {
            console.warn(name + " is deprecated, use " + alternative + " instead.", scopeName);
        } else {
            //name is a complete message
            console.warn(name, scopeName);
        }

    }
    Error.stackTraceLimit = depth;
    args = Array_prototype.slice.call(arguments, 4);
    return callback.apply(scope ? scope : this, args);
};

var PROTO_IS_SUPPORTED = {}.__proto__ === Object.prototype;
var PROTO_PROPERTIES_BLACKLIST = {"_montage_metadata": 1, "__state__": 1};
var FUNCTION_PROPERTIES = Object.getOwnPropertyNames(Function);

Object.defineProperty(Montage, "specialize", {
    value: function specialize(prototypeProperties, constructorProperties) {
        var constructor, prototype, names, propertyName, property, i, constructorProperty,
            // check if this constructor has Montage capabilities
            parent = this,
            foreignParent = typeof this.specialize === "undefined";

        prototypeProperties = prototypeProperties || Object.empty;
        constructorProperties = constructorProperties || Object.empty;

        if (prototypeProperties.constructor && prototypeProperties.constructor.value) {
            constructor = prototypeProperties.constructor.value;
        } else if (prototypeProperties.didCreate && prototypeProperties.didCreate.value) {
            constructor = Montage.deprecate(null, prototypeProperties.didCreate.value, "didCreate", "constructor");
            //constructor = prototypeProperties.didCreate.value;
        } else {
            constructor = function Anonymous() {
                return parent.apply(this, arguments) || this;
            };
        }
        if (PROTO_IS_SUPPORTED) {
            constructor.__proto__ = parent;
        } else {
            names = Object.getOwnPropertyNames(parent);
            for (var i = 0; i < names.length; i++) {
                propertyName = names[i];
                if (!(PROTO_PROPERTIES_BLACKLIST.hasOwnProperty(propertyName))) {
                    property = Object.getOwnPropertyDescriptor(constructor, propertyName);
                    if (!property || property.configurable) {
                        Montage.defineProperty(constructor, propertyName, Object.getOwnPropertyDescriptor(parent, propertyName));
                    }
                }
            }
            constructor.__constructorProto__ = parent;
            Montage.defineProperty(constructor, "isPrototypeOf", {
                value: function(object) {
                    while (object !== null) {
                        if(Object.getPrototypeOf(object) === this) {
                            return true;
                        }
                        object = Object.getPrototypeOf(object)
                    }
                    return false;
                },
                enumerable: false
            });
        }

        prototype = Object.create(this.prototype);

        if(foreignParent) {
            // give the constructor all the properties of Montage
            names = Object.getOwnPropertyNames(Montage);
            for ( i = 0; i < names.length; i++) {
                propertyName = names[i];
                property = Object.getOwnPropertyDescriptor(constructor, propertyName);
                if (!property || property.configurable) {
                    Montage.defineProperty(constructor, propertyName, Object.getOwnPropertyDescriptor(Montage, propertyName));
                }
            }
            // give the prototype all the properties of Montage.prototype
            names = Object.getOwnPropertyNames(Montage.prototype);
            for ( i = 0; i < names.length; i++) {
                propertyName = names[i];
                property = Object.getOwnPropertyDescriptor(constructor, propertyName);
                if (!property || property.configurable) {
                    Montage.defineProperty(prototype, propertyName, Object.getOwnPropertyDescriptor(Montage.prototype, propertyName));
                }
            }
        }

        Montage.defineProperties(prototype, prototypeProperties);

        if (CONSTRUCTOR_COMPATIBILITY) {
            // to catch class properties
            constructorProperty = function(original, constructor, propertyName) {
                function deprecationWrapper() {
                    if(this === constructor) {
                        console.warn("Deprecated - " + Montage.getInfoForObject(constructor).objectName + "."
                            + propertyName + " should be moved to constructorProperties");
                    }
                    return original.apply(this, arguments);
                }
                deprecationWrapper.deprecatedFunction = original;
                return deprecationWrapper;
            };
            for (propertyName in prototypeProperties) {
                if(FUNCTION_PROPERTIES.has(propertyName)) {
                    // illegal properties on function
                    delete prototypeProperties[propertyName];
                } else {
                    property = prototypeProperties[propertyName];
                    if(property.value && typeof property.value === "function" && !property.value.__isConstructor__) {
                        property.value = constructorProperty(property.value, constructor, propertyName);
                    } else {
                        if(property.get) {
                            property.get = constructorProperty(property.get, constructor, propertyName);
                         }
                        if(property.set) {
                            property.set = constructorProperty(property.set, constructor, propertyName);
                        }
                    }
                }
            }
            Montage.defineProperties(constructor, prototypeProperties);
            Montage.defineProperty(constructor, "create", {
                value: function() {
                    return new constructor();
                },
                enumerable: false
            });
            if (! prototype.hasOwnProperty("didCreate")) {
                Montage.defineProperty(prototype, "didCreate", {
                    value: constructor,
                    enumerable: false
                });
            }
            if (! constructor.hasOwnProperty("didCreate")) {
                Montage.defineProperty(constructor, "didCreate", {
                    value: constructor,
                    enumerable: false
                });
            }
        }
        // end compatibility code
        // needs to be done afterwards so that it overrides any prototype properties
        Montage.defineProperties(constructor, constructorProperties);
        Montage.defineProperty(constructor, "__isConstructor__", {
            value: true,
            enumerable: false
        });

        constructor.prototype = prototype;
        Montage.defineProperty(prototype, "constructor", {
            value: constructor,
            enumerable: false
        });
        return constructor;

    },
    writable: true,
    configurable: true,
    enumerable: false
});
if (!PROTO_IS_SUPPORTED) {
    // If the __proto__ property isn't supported than we need to patch up behavior for constructor functions
    var originalGetPrototypeOf = Object.getPrototypeOf;
    Object.getPrototypeOf = function getPrototypeOf(object) {
        if (typeof object === "function" && object.__constructorProto__) {
            // we have set the __constructorProto__ property of the function to be it's parent constructor
            return object.__constructorProto__;
        } else {
            return originalGetPrototypeOf.apply(Object, arguments);
        }
    };
}

/**
    Creates a new Montage object.
    @function Montage.create
    @param {Object} aPrototype The prototype object to create the new object from. If not specified, the prototype is the Montage prototype.
    @param {Object} [propertyDescriptor] An object that contains the initial properties and values for the new object.
    @returns The new object
    @example
    <caption>Creating a "empty" Montage object, using Montage as the prototype</caption>
    var alpha = Montage.create();
    @example
    <caption>Creating a new Montage component with a property descriptor object.</caption>
    var Button = Component.specialize( {
        state: {
            value: null
        }
    });
*/
Object.defineProperty(Montage, "create", {
    configurable: true,
    value: function(aPrototype, propertyDescriptors) {
        if (aPrototype !== undefined && (typeof aPrototype !== "object"
                && /* CONSTRUCTOR_COMPATIBILITY*/typeof aPrototype !== "function")) {
            throw new TypeError("Object prototype may only be an Object or null, not '" + aPrototype + "'");
        }
        aPrototype = typeof aPrototype === "undefined" ? this : aPrototype;
        // CONSTRUCTOR_COMPATIBILITY
        // if aPrototype is a function then we behave as a constructor.
        if (typeof aPrototype === "function") {
            if (!propertyDescriptors) {
                return new aPrototype();
            } else {
                return aPrototype.specialize(propertyDescriptors);
            }
            // Otherwise behave like Object.create()
        } else {
            var result = Object.create(aPrototype);
            if(propertyDescriptors) {
                Montage.defineProperties(result, propertyDescriptors);
            }
            return result;
        }
    }
});

var extendedPropertyAttributes = [SERIALIZABLE];

// Extended property attributes, the property name format is "_" + attributeName + "AttributeProperties"
/**
@member external:Object#extendedPropertyAttributes
*/
extendedPropertyAttributes.forEach(function(name) {
    Object.defineProperty(Object.prototype, UNDERSCORE + name + ATTRIBUTE_PROPERTIES, {
        enumerable: false,
        configurable: false,
        writable: true,
        value: {}
    });
});

/**
    Defines a property on a Montage object.
    @function Montage.defineProperty
    @param {Object} obj The object on which to define the property.
    @param {String} prop The name of the property to define, or modify.
    @param {Object} descriptor A descriptor object that defines the properties being defined or modified.
    @example
    Montage.defineProperty(Object.prototype, "_eventListenerDescriptors", {
        enumerable: true | false,
        serializable: "reference" | "value" | "auto" | false,
        value: null,
        writable: true | false
    });
*/
Object.defineProperty(Montage, "defineProperty", {

    value: function(obj, prop, descriptor) {
        if (! (typeof obj === "object" || typeof obj === "function") || obj === null) {
            throw new TypeError("Object must be an object, not '" + obj + "'");
        }

        var isValueDescriptor = (VALUE in descriptor);

        if (DISTINCT in descriptor && !isValueDescriptor) {
            throw ("Cannot use distinct attribute on non-value property '" + prop + "'");
        }


        //reset defaults appropriately for framework.
        if (PROTO in descriptor) {
            descriptor.__proto__ = (isValueDescriptor ? (typeof descriptor.value === "function" ? _defaultFunctionValueProperty : _defaultObjectValueProperty) : _defaultAccessorProperty);
        } else {
            var defaults;
            if (isValueDescriptor) {
                if (typeof descriptor.value === "function") {
                    defaults = _defaultFunctionValueProperty;
                } else {
                    defaults = _defaultObjectValueProperty;
                }
            } else {
                defaults = _defaultAccessorProperty;
            }
            for (var key in defaults) {
                if (!(key in descriptor)) {
                    descriptor[key] = defaults[key];
                }
            }
        }


        if (!descriptor.hasOwnProperty(ENUMERABLE) && prop.charAt(0) === UNDERSCORE) {
            descriptor.enumerable = false;
        }
        if (!descriptor.hasOwnProperty(SERIALIZABLE)) {
            if (! descriptor.enumerable) {
                descriptor.serializable = false;
            } else if (descriptor.get && !descriptor.set) {
                descriptor.serializable = false;
            } else if (descriptor.writable === false) {
                descriptor.serializable = false;
            }
        }

        if (SERIALIZABLE in descriptor) {
            // get the _serializableAttributeProperties property or creates it through the entire chain if missing.
            getAttributeProperties(obj, SERIALIZABLE)[prop] = descriptor.serializable;
        }

        // TODO replace this with Object.clone from collections - @kriskowal
        //this is added to enable value properties with [] or Objects that are new for every instance
        if (descriptor.distinct === true && typeof descriptor.value === "object") {
            (function(prop,internalProperty, value, obj) {
                var defineInternalProperty = function(obj, internalProperty, value) {
                    Object.defineProperty(obj, internalProperty, {
                        enumerable: false,
                        configurable: true,
                        writable: true,
                        value: value
                    });
                };
                if (value.constructor === Object && Object.getPrototypeOf(value) === Object_prototype) {
                    // we have an object literal {...}
                    if (Object.keys(value).length !== 0) {
                        Object.defineProperty(obj, prop, {
                            configurable: true,
                            get: function() {
                                //Special case for object to copy the values
                                var returnValue = this[internalProperty];
                                if (!returnValue) {
                                    var k;
                                    returnValue = {};
                                    for (k in value) {
                                        returnValue[k] = value[k];
                                    }
                                    if(!this.hasOwnProperty(internalProperty)) {
                                        defineInternalProperty(this, internalProperty, returnValue);
                                    } else {
                                        this[internalProperty] = returnValue;
                                    }
                                }
                                return returnValue;
                            },
                            set: function(value) {
                                if(!this.hasOwnProperty(internalProperty)) {
                                    defineInternalProperty(this, internalProperty, value);
                                } else {
                                    this[internalProperty] = value;
                                }
                            }
                        });
                    } else {
                        Object.defineProperty(obj, prop, {
                            configurable: true,
                            get: function() {
                                var returnValue = this[internalProperty];
                                if (!returnValue) {
                                    returnValue = {};
                                    if (this.hasOwnProperty(internalProperty))  {
                                        this[internalProperty] = returnValue
                                    } else {
                                        defineInternalProperty(this, internalProperty, returnValue);
                                    }
                                }
                                return returnValue;
                            },
                            set: function(value) {
                                if(!this.hasOwnProperty(internalProperty)) {
                                    defineInternalProperty(this, internalProperty, value);
                                } else {
                                    this[internalProperty] = value;
                                }
                            }
                        });
                    }

                } else if ((value.__proto__ || Object.getPrototypeOf(value)) === Array_prototype) {
                    // we have an array literal [...]
                    if (value.length !== 0) {
                        Object.defineProperty(obj, prop, {
                            configurable: true,
                            get: function() {
                                //Special case for object to copy the values
                                var returnValue = this[internalProperty];
                                if (!returnValue) {
                                    var i, k;
                                    returnValue = [];
                                    for (i = 0; typeof (k = value[i]) !== "undefined"; i++) {
                                        returnValue[i] = k;
                                    }
                                    if(!this.hasOwnProperty(internalProperty)) {
                                        defineInternalProperty(this, internalProperty, returnValue);
                                    } else {
                                        this[internalProperty] = returnValue;
                                    }
                                }
                                return returnValue;
                            },
                            set: function(value) {
                                if(!this.hasOwnProperty(internalProperty)) {
                                    defineInternalProperty(this, internalProperty, value);
                                } else {
                                    this[internalProperty] = value;
                                }
                            }
                        });

                    } else {
                        Object.defineProperty(obj, prop, {
                            configurable: true,
                            get: function() {
                                var returnValue = this[internalProperty];
                                if (!returnValue) {
                                    returnValue = [];
                                    if (this.hasOwnProperty(internalProperty))  {
                                        this[internalProperty] = returnValue
                                    } else {
                                        defineInternalProperty(this, internalProperty, returnValue);
                                    }
                                }
                                return returnValue;
                            },
                            set: function(value) {
                                if(!this.hasOwnProperty(internalProperty)) {
                                    defineInternalProperty(this, internalProperty, value);
                                } else {
                                    this[internalProperty] = value;
                                }
                            }
                        });
                    }
                    //This case is to deal with objects that are created with a constructor
                } else if (value.constructor.prototype === Object.getPrototypeOf(value)) {
                    Object.defineProperty(obj, prop, {
                        configurable: true,
                        get: function() {
                            //Special case for object to copy the values
                            var returnValue = this[internalProperty];
                            if (!returnValue) {
                                var k;
                                returnValue = new value.constructor;
                                for (k in value) {
                                    returnValue[k] = value[k];
                                }
                                if(!this.hasOwnProperty(internalProperty)) {
                                    defineInternalProperty(this, internalProperty, returnValue);
                                } else {
                                    this[internalProperty] = returnValue;
                                }
                            }
                            return returnValue;
                        },
                        set: function(value) {
                            if(!this.hasOwnProperty(internalProperty)) {
                                defineInternalProperty(this, internalProperty, value);
                            } else {
                                this[internalProperty] = value;
                            }
                        }
                    });


                } else {
                    Object.defineProperty(obj, prop, {
                        configurable: true,
                        get: function() {
                            var returnValue = this[internalProperty];
                            if (!returnValue) {
                                returnValue = Object.create(value.__proto__ || Object.getPrototypeOf(value));
                                if (this.hasOwnProperty(internalProperty))  {
                                    this[internalProperty] = returnValue
                                } else {
                                    defineInternalProperty(this, internalProperty, returnValue);
                                }
                            }
                            return returnValue;
                        },
                        set: function(value) {
                            if(!this.hasOwnProperty(internalProperty)) {
                                defineInternalProperty(this, internalProperty, value)
                            } else {
                                this[internalProperty] = value;
                            }
                        }
                    });
                }
            })(prop, UNDERSCORE + prop, descriptor.value, obj);

        } else {
            return Object.defineProperty(obj, prop, descriptor);
        }
    }});

/**
    Description Defines one or more new properties to an object, or modifies existing properties on the object.
    @function Montage.defineProperties
    @param {Object} obj The object to which the properties are added.
    @param {Object} properties An object that contains one or more property descriptor objects.
*/
Object.defineProperty(Montage, "defineProperties", {value: function(obj, properties) {
    if (typeof properties !== "object" || properties === null) {
        throw new TypeError("Properties must be an object, not '" + properties + "'");
    }
    for (var property in properties) {
        if ("_bindingDescriptors" !== property) {
            this.defineProperty(obj, property, properties[property]);
        }
    }
    return obj;
}});

var _defaultAccessorProperty = {
    enumerable: true,
    configurable: true,
    serializable: true
};
var _defaultObjectValueProperty = {
    writable: true,
    enumerable: true,
    configurable: true,
    serializable: "reference"
};
var _defaultFunctionValueProperty = {
    writable: true,
    enumerable: false,
    configurable: true,
    serializable: false
};

function getAttributeProperties(proto, attributeName) {
    var attributePropertyName = UNDERSCORE + attributeName + ATTRIBUTE_PROPERTIES;

    if (proto.hasOwnProperty(attributePropertyName)) {
        return proto[attributePropertyName];
    } else {
        return Object.defineProperty(proto, attributePropertyName, {
            enumerable: false, configurable: false, writable: true,
            value: Object.create(getAttributeProperties(Object.getPrototypeOf(proto), attributeName))
        })[attributePropertyName];
    }
}

Montage.defineProperty(Montage, "didCreate", {
    value: Function.noop
});

var getSuper = function(object, method) {
    var propertyNames, propertyName, property, i, propCount, func, superFunction, superProperty;
    while (typeof superFunction === "undefined" && object !== null) {
        propertyNames = Object.getOwnPropertyNames(object);
        i = 0;
        propCount = propertyNames.length;
        for (i; i < propCount; i++) {
            propertyName = propertyNames[i];
            property = Object.getOwnPropertyDescriptor(object, propertyName);
            if ((func = property.value) != null) {
                if (func === method || (func.deprecatedFunction && func.deprecatedFunction === method)) {
                    superProperty = Object.getPropertyDescriptor(Object.getPrototypeOf(object), propertyName)
                    superFunction = superProperty ? superProperty.value : null;
                    break;
                }
            } else if ((func = property.get) != null) {
                if (func === method || (func.deprecatedFunction && func.deprecatedFunction === method)) {
                    superProperty = Object.getPropertyDescriptor(Object.getPrototypeOf(object), propertyName)
                    superFunction = superProperty ? superProperty.get : null;
                    break;
                }
            } else if ((func = property.set) != null) {
                if (func === method || (func.deprecatedFunction && func.deprecatedFunction === method)) {
                    superProperty = Object.getPropertyDescriptor(Object.getPrototypeOf(object), propertyName)
                    superFunction = superProperty ? superProperty.set : null;
                    break;
                }
            }
        }
        object = Object.getPrototypeOf(object)
    }
    return superFunction;
}


var superImplementation = function super_() {
    var superFunction = getSuper(this, superImplementation.caller);
    return typeof superFunction === "function" ? getSuper(this, superImplementation.caller).bind(this) : Function.noop;
};

Montage.defineProperty(Montage, "super", {
    get: superImplementation
});
Montage.defineProperty(Montage.prototype, "super", {
    get: superImplementation
});

/**
    Returns the names of serializable properties belonging to Montage object.
    @function Montage.getSerializablePropertyNames
    @param {Object} anObject A Montage object.
    @returns {Array} An array containing the names of the serializable properties belonging to <code>anObject</code>.
*/
Montage.defineProperty(Montage, "getSerializablePropertyNames", {value: function(anObject) {

    var propertyNames = [],
        attributes = anObject._serializableAttributeProperties;

    if (attributes) {
        for (var name in attributes) {
            if (attributes[name]) {
                propertyNames.push(name);
            }
        }
    }

    return propertyNames;
}});

/**
    Returns the attribute of a property belonging to an object.
    @function Montage.getPropertyAttribute
    @param {Object} anObject A object.
    @param {String} propertyName The name of a property belonging to <code>anObject</code>.
    @param {String} attributeName The name of a property's attribute.
    @returns attributes array
*/
Montage.defineProperty(Montage, "getPropertyAttribute", {value: function(anObject, propertyName, attributeName) {

    var attributePropertyName = UNDERSCORE + attributeName + ATTRIBUTE_PROPERTIES,
        attributes = anObject[attributePropertyName];

    if (attributes) {
        return attributes[propertyName];
    }
}});

/**
    @function Montage.getPropertyAttributes
    @param {Object} anObject An object.
    @param {String} attributeName The attribute name.
    @returns {Object} TODO getPropertyAttributes returns description
*/
Montage.defineProperty(Montage, "getPropertyAttributes", {value: function(anObject, attributeName) {
    var attributeValues = {},
        attributePropertyName = UNDERSCORE + attributeName + ATTRIBUTE_PROPERTIES,
        attributes = anObject[attributePropertyName];

    if (!attributes) {
        return;
    }

    for (var name in attributes) {
        attributeValues[name] = attributes[name];
    }

    return attributeValues;
}});

var _instanceMetadataDescriptor = {
    isInstance: {value: true}
};

var _functionInstanceMetadataDescriptor = {
    objectName: {value: "Function"},
    isInstance: {value: true}
};

/**
    Get the metadata Montage has on the given object
    @function Montage.getInfoForObject
    @param {Object} object An object.
    @returns {Object} object._montage_metadata
*/
Montage.defineProperty(Montage, "getInfoForObject", {
    value: function(object) {
        var metadata;
        var instanceMetadataDescriptor;

        if (hasOwnProperty.call(object, "_montage_metadata")) {
            return object._montage_metadata;
        } else {
            metadata = object._montage_metadata || (object.constructor && object.constructor._montage_metadata) || null;
            if (object.constructor === Function) {
                instanceMetadataDescriptor = _functionInstanceMetadataDescriptor;
            } else {
                instanceMetadataDescriptor = _instanceMetadataDescriptor;
            }

            // don't modify the Object prototype, because this will cause
            // future calls to Montage.getInfoForObject on objects without
            // their own _montage_metadata property to return this one
            if (object === Object.prototype) {
                return Object.create(metadata, instanceMetadataDescriptor);
            }

            try {
                return Object.defineProperty(object, "_montage_metadata", {
                    enumerable: false,
                    // this object needs to be overriden by the SerializationCompiler because this particular code might be executed on an exported object before the Compiler takes action, for instance, if this function is called within the module definition itself (happens with __core__).
                    writable: true,
                    value: Object.create(metadata, instanceMetadataDescriptor)
                })._montage_metadata;
            } catch(e) {
                // NOTE Safari (as of Version 5.0.2 (6533.18.5, r78685)
                // doesn't seem to allow redefining an existing property on a DOM Element
                return (object._montage_metadata = Object.create(metadata, instanceMetadataDescriptor));
            }
        }
    }
});

// TODO figure out why this code only works in this module.  Attempts to move
// it to core/extras/object resulted in _uuid becoming enumerable and tests
// breaking. - @kriskowal

var UUID = require("core/uuid");

// HACK: This is to fix an IE10 bug where a getter on the window prototype chain
// gets some kind of proxy Window object which cannot have properties defined
// on it, instead of the `window` itself. Adding the uuid directly to the
// window removes the needs to call the getter.
if (typeof window !== "undefined") {
    window.uuid = UUID.generate();
}

var hasOwnProperty = Object.prototype.hasOwnProperty;

var uuidGetGenerator = function() {

    var uuid = UUID.generate(),
        info = Montage.getInfoForObject(this);
    try {
        if (info !== null && info.isInstance === false) {
            this._uuid = uuid;
            Object.defineProperty(this, "uuid", {
                get: function() {
                    if (this.hasOwnProperty("uuid")) {
                        // we are calling uuid on the prototype
                        return this._uuid;
                    } else {
                        // we are calling uuid on instance of this prototype
                        return uuidGetGenerator.call(this);
                    }
                }
            });
        } else {
            //This is needed to workaround some bugs in Safari where re-defining uuid doesn't work for DOMWindow.
            if (info.isInstance) {
                Object.defineProperty(this, "uuid", {
                    configurable: true,
                    enumerable: false,
                    writable: false,
                    value: uuid
                });
            }
            //This is really because re-defining the property on DOMWindow actually doesn't work, so the original property with the getter is still there and return this._uuid if there.
            if (this instanceof Element || !info.isInstance || !(VALUE in (Object.getOwnPropertyDescriptor(this, "uuid")||{})) || !(PROTO in this /* lame way to detect IE */)) {
                //This is needed to workaround some bugs in Safari where re-defining uuid doesn't work for DOMWindow.
                this._uuid = uuid;
            }
        }
    } catch(e) {
        // NOTE Safari (as of Version 5.0.2 (6533.18.5, r78685)
        // doesn't seem to allow redefining an existing property on a DOM Element
        // Still want to redefine the property where possible for speed
        this._uuid = uuid;
    }

    return uuid;
};

var defaultUuidGet = function defaultUuidGet() {
    return (hasOwnProperty.call(this, "_uuid") ? this._uuid : uuidGetGenerator.call(this));
};

/**
    @private
*/
Object.defineProperty(Object.prototype, "_uuid", {
    enumerable: false,
    value: null,
    writable: true
});

/**
    Contains an object's unique ID.
    @member external:Object#uuid
    @default null
*/
Object.defineProperty(Object.prototype, "uuid", {
    configurable: true,
    get: defaultUuidGet,
    set: function() {
    }
});

Montage.defineProperty(Montage, "identifier", {
    value: null,
    serializable: true
});
Montage.defineProperty(Montage.prototype, "identifier", {
    value: null,
    serializable: true
});

/**
    Returns true if two objects are equal, otherwise returns false.
    @function Montage.equals
    @param {Object} anObject The object to compare for equality.
    @returns {Boolean} Returns <code>true</code> if the calling object and
    <code>anObject</code> are identical and their <code>uuid</code> properties
    are also equal. Otherwise, returns <code>false</code>.
*/
Montage.defineProperty(Montage.prototype, "equals", {
    value: function(anObject) {
        if (!anObject) return false;
        return this === anObject || this.uuid === anObject.uuid;
    }
});
Montage.defineProperty(Montage, "equals", {
    value: Montage.prototype.equals
});

/*
    This method calls the method named with the identifier prefix if it exists.
    Example: If the name parameter is "shouldDoSomething" and the caller's identifier is "bob", then
    this method will try and call "bobShouldDoSomething"
    @function callDelegateMethod
    @param {string} name
*/
Montage.defineProperty(Montage.prototype, "callDelegateMethod", {
    value: function(name) {
        var delegate = this.delegate, delegateFunctionName, delegateFunction;
        if (typeof this.identifier === "string") {
            delegateFunctionName = this.identifier + name.toCapitalized();
            if (delegate && typeof (delegateFunction = delegate[delegateFunctionName]) === "function") {
                // remove first argument
                Array_prototype.shift.call(arguments);
                return delegateFunction.apply(delegate, arguments);
            }
        }

        if (delegate && typeof (delegateFunction = delegate[name]) === "function") {
            // remove first argument
            Array_prototype.shift.call(arguments);
            return delegateFunction.apply(delegate, arguments);
        }
    }
});

var PropertyChanges = require("collections/listen/property-changes");

Object.addEach(Montage, PropertyChanges.prototype);
Object.addEach(Montage.prototype, PropertyChanges.prototype);

// have to come last since they use the Montage.defineProperties to augment Object.prototype
require("core/bindings");
require("core/paths");
// has to come last since serializer and deserializer depend on logger, which
// in turn depends on montage running to completion
require("core/serialization/bindings");

/*
 * Defines the module Id for blueprints. This is externalized so that it can be subclassed.
 * <b>Note</b> This is a class method beware...
 */
exports._blueprintModuleIdDescriptor = {
    serializable:false,
    enumerable: false,
    get:function () {
        var info = Montage.getInfoForObject(this);
        var self = (info && !info.isInstance) ? this : this.constructor;
        if ((!Object.getOwnPropertyDescriptor(self, "_blueprintModuleId")) || (!self._blueprintModuleId)) {
            info = Montage.getInfoForObject(self);
            var moduleId = info.moduleId,
                slashIndex = moduleId.lastIndexOf("/"),
                dotIndex = moduleId.lastIndexOf(".");
            slashIndex = ( slashIndex === -1 ? 0 : slashIndex + 1 );
            dotIndex = ( dotIndex === -1 ? moduleId.length : dotIndex );
            dotIndex = ( dotIndex < slashIndex ? moduleId.length : dotIndex );
            Montage.defineProperty(self, "_blueprintModuleId", {
                enumerable: false,
                value: moduleId.slice(0, dotIndex) + ".meta"
            });
        }
        return self._blueprintModuleId;
    }
};

exports._blueprintDescriptor = {
    serializable:false,
    enumerable: false,
    get:function () {
        var info = Montage.getInfoForObject(this);
        var self = (info && !info.isInstance) ? this : this.constructor;
        if ((!Object.getOwnPropertyDescriptor(self, "_blueprint")) || (!self._blueprint)) {
            var blueprintModuleId = self.blueprintModuleId;
            if (blueprintModuleId === "") {
                throw new TypeError("Blueprint moduleId undefined for the module '" + JSON.stringify(self) + "'");
            }

            if (!exports._blueprintDescriptor.BlueprintModulePromise) {
                exports._blueprintDescriptor.BlueprintModulePromise = require.async("core/meta/module-blueprint").get("ModuleBlueprint");
            }
            Montage.defineProperty(self, "_blueprint", {
                enumerable: false,
                value: exports._blueprintDescriptor.BlueprintModulePromise.then(function (Blueprint) {
                    var info = Montage.getInfoForObject(self);

                    return Blueprint.getBlueprintWithModuleId(blueprintModuleId, info.require)
                    .fail(function (error) {
                        // FIXME only generate blueprint if the moduleId
                        // requested does not exist. If any parents do not
                        // exist then the error should still be thrown.
                        if (error.message.indexOf("Can't XHR") !== -1) {
                            return Blueprint.createDefaultBlueprintForObject(self).then(function (blueprint) {
                                return blueprint;
                            });
                        } else {
                            throw error;
                        }
                    });
                })
            });
        }
        return self._blueprint;
    },
    set:function (value) {
        var info = Montage.getInfoForObject(this);
        var _blueprintValue;
        var self = (info && !info.isInstance) ? this : this.constructor;
        if (value === null) {
            _blueprintValue = null;
        } else if (typeof value.then === "function") {
            throw new TypeError("Object blueprint should not be a promise");
        } else {
            value.blueprintInstanceModule = self.blueprintModule;
            _blueprintValue = require("core/promise").Promise.resolve(value);
        }
        Montage.defineProperty(self, "_blueprint", {
            enumerable: false,
            value: _blueprintValue
        });
    }
};

}})
;
//*/
montageDefine("6364dae","core/event/event-manager",{dependencies:["montage","core/uuid","core/event/mutable-event","core/serialization"],factory:function(require,exports,module){/* <copyright>
Copyright (c) 2012, Motorola Mobility LLC.
All Rights Reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

* Redistributions of source code must retain the above copyright notice,
  this list of conditions and the following disclaimer.

* Redistributions in binary form must reproduce the above copyright notice,
  this list of conditions and the following disclaimer in the documentation
  and/or other materials provided with the distribution.

* Neither the name of Motorola Mobility LLC nor the names of its
  contributors may be used to endorse or promote products derived from this
  software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
</copyright> */
/*global Window,Document,Element,Event,Components,Touch */
/**
 *
 * @author: Lea Verou
 * @license: Licensed under The MIT License. See license.txt and http://www.opensource.org/licenses/mit-license.php.
 * @website: http://leaverou.github.com/chainvas/
 */

/**
 @module montage/core/event/event-manager
 @requires montage/core/core
 @requires montage/core/event/mutable-event
 @requires montage/core/event/action-event-listener
 */

var Montage = require("montage").Montage,
    UUID = require("core/uuid"),
    MutableEvent = require("core/event/mutable-event").MutableEvent,
    Serializer = require("core/serialization").Serializer,
    Deserializer = require("core/serialization").Deserializer,
    defaultEventManager;

// XXX Does not presently function server-side
if (typeof window !== "undefined") { // client-side
//jshint -W015
/* This is to handle browsers that have TouchEvents but don't have the global constructor function Touch */
if (typeof window.Touch === "undefined" && "ontouchstart" in window) {
    window.Touch = function() {
    };
    (function() {
        var onFirstTouchstart;

        document.addEventListener("touchstart", onFirstTouchstart = function (event) {
            window.Touch = event.touches[0].constructor;
            if (document.nativeRemoveEventListener) {
                document.nativeRemoveEventListener("touchstart", onFirstTouchstart, true);
            } else {
                document.removeEventListener("touchstart", onFirstTouchstart, true);
            }
            if (defaultEventManager && defaultEventManager.isStoringPointerEvents) {
                defaultEventManager.isStoringPointerEvents = false;
                defaultEventManager.isStoringPointerEvents = true;
            }
        }, true);
    })();
}

var EventListenerDescriptor = Montage.specialize( {
    type: {
        value: null
    },

    listener: {
        value: null
    },

    capture: {
        value: null
    }
});

Serializer.defineSerializationUnit("listeners", function(serializer, object) {
    var eventManager = defaultEventManager,
        uuid = object.uuid,
        eventListenerDescriptors = [],
        descriptors,
        descriptor,
        listener;

    for (var type in eventManager.registeredEventListeners) {
        descriptors = eventManager.registeredEventListeners[type];
        descriptor = descriptors && descriptors[uuid];
        if (descriptor) {
            for (var listenerUuid in descriptor.listeners) {
                listener = descriptor.listeners[listenerUuid];

                eventListenerDescriptors.push({
                    type: type,
                    listener: serializer.addObjectReference(listener.listener),
                    capture: listener.capture
                });
            }
        }
    }

    if (eventListenerDescriptors.length > 0) {
        return eventListenerDescriptors;
    }
});

Deserializer.defineDeserializationUnit("listeners", function(deserializer, object, listeners) {
    for (var i = 0, listener; (listener = listeners[i]); i++) {
        object.addEventListener(listener.type, listener.listener, listener.capture);
    }
});

var NONE = Event.NONE,
    CAPTURING_PHASE = Event.CAPTURING_PHASE,
    AT_TARGET = Event.AT_TARGET,
    BUBBLING_PHASE = Event.BUBBLING_PHASE,
    FUNCTION_TYPE = "function";

/**
 @class EventManager
 */
var EventManager = exports.EventManager = Montage.specialize(/** @lends EventManager# */ {

    // Utility
    eventDefinitions: {
        // ClipboardEvent http://dev.w3.org/2006/webapi/clipops/clipops.html#event-types-and-details
        // DND http://www.w3.org/TR/2010/WD-html5-20101019/dnd.html
        // document.implementation.hasFeature("HTMLEvents", "2.0")
        // DOM2 http://www.w3.org/TR/DOM-Level-2-Events/events.html
        // DOM3 http://dev.w3.org/2006/webapi/DOM-Level-3-Events/html/DOM3-Events.html
        // DOM4 http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#events
        // GECKO https://developer.mozilla.org/en/Gecko-Specific_DOM_Events
        // MSFT defacto standard
        // ProgressEvent http://www.w3.org/TR/progress-events/
        // TouchEvent http://dvcs.w3.org/hg/webevents/raw-file/tip/touchevents.html
        // INPUT http://dev.w3.org/html5/spec/common-input-element-apis.html#common-event-behaviors
        // WEBSOCKETS http://www.w3.org/TR/html5/comms.html

        // Other info:
        // http://www.quirksmode.org/dom/events/index.html
        // https://developer.mozilla.org/en/DOM/DOM_event_reference
        value: {
            abort: {bubbles: false, cancelable: false}, //ProgressEvent, DOM3, //DOM2 does bubble
            beforeunload: {bubbles: false}, //MSFT
            blur: {bubbles: false, cancelable: false}, //DOM2, DOM3
            change: {bubbles: true, cancelable: false}, //DOM2, INPUT
            click: {bubbles: true, cancelable: true}, //DOM3
            close: {bubbles: false, cancelable: false}, //WEBSOCKETS
            compositionend: {bubbles: true, cancelable: false}, //DOM3
            compositionstart: {bubbles: true, cancelable: true}, //DOM3
            compositionupdate: {bubbles: true, cancelable: false}, //DOM3
            contextmenu: {bubbles: true, cancelable: true}, //MSFT
            copy: {bubbles: true, cancelable: true}, //ClipboardEvent
            cut: {bubbles: true, cancelable: true}, //ClipboardEvent
            dblclick: {bubbles: true, cancelable: false}, //DOM3
            DOMActivate: {bubbles: true, cancelable: true, deprecated: true}, //DOM2, DOM3 deprecated
            DOMMouseScroll: {bubbles: true}, //GECKO
            drag: {bubbles: true, cancelable: true}, //DND
            dragend: {bubbles: true, cancelable: false}, //DND
            dragenter: {bubbles: true, cancelable: true}, //DND
            dragleave: {bubbles: true, cancelable: false}, //DND
            dragover: {bubbles: true, cancelable: true}, //DND
            dragstart: {bubbles: true, cancelable: true}, //DND
            drop: {bubbles: true, cancelable: true}, //DND
            error: {
                bubbles: function(target) {
                    // error does not bubble when used as a ProgressEvent
                    return !(XMLHttpRequest.prototype.isPrototypeOf(target) ||
                           target.tagName && "VIDEO" === target.tagName.toUpperCase() ||
                           target.tagName && "AUDIO" === target.tagName.toUpperCase());
                },
                cancelable: false
            }, //DOM2, DOM3, ProgressEvent
            focus: {bubbles: false, cancelable: false}, //DOM2, DOM3
            focusin: {bubbles: true, cancelable: false}, //DOM3
            focusout: {bubbles: true, cancelable: false}, //DOM3
            input: {bubbles: true, cancelable: false}, // INPUT
            keydown: {bubbles: true, cancelable: false}, //DOM3
            keypress: {bubbles: true, cancelable: false}, //DOM3
            keyup: {bubbles: true, cancelable: false}, //DOM3
            load: {bubbles: false, cancelable: false}, //ProgressEvent, DOM2, DOM3
            loadend: {bubbles: false, cancelable: false}, //ProgressEvent
            loadstart: {bubbles: false, cancelable: false}, //ProgressEvent
            message: {bubbles: false, cancelable: false}, //WEBSOCKETS
            mousedown: {bubbles: true, cancelable: true}, //DOM3
            mouseenter: {bubbles: false, cancelable: false}, //DOM3
            mouseleave: {bubbles: false, cancelable: false}, //DOM3
            mousemove: {bubbles: true, cancelable: true}, //DOM3
            mouseout: {bubbles: true, cancelable: true}, //DOM3
            mouseover: {bubbles: true, cancelable: true}, //DOM3
            mouseup: {bubbles: true, cancelable: true}, //DOM3
            mousewheel: {bubbles: true},
            orientationchange: {bubbles: false},
            paste: {bubbles: true, cancelable: true}, //ClipboardEvent
            progress: {bubbles: false, cancelable: false}, //ProgressEvent
            reset: {bubbles: true, cancelable: false}, //DOM2
            resize: {bubbles: false, cancelable: false}, //DOM2 bubbles, DOM3

            scroll: {
                bubbles: function(target) {
                    return /*isDocument*/!!target.defaultView;
                },
                cancelable: false
            }, //DOM2, DOM3 When dispatched on Document element must bubble to defaultView object

            select: {bubbles: true, cancelable: false}, //DOM2, DOM3

            submit: {bubbles: true, cancelable: true}, //DOM2
            touchcancel: {bubbles: true, cancelable: false}, //TouchEvent
            touchend: {bubbles: true, cancelable: true}, //TouchEvent
            touchmove: {bubbles: true, cancelable: true}, //TouchEvent
            touchstart: {bubbles: true, cancelable: true}, //TouchEvent
            unload: {bubbles: false, cancelable: false}, //DOM2, DOM3
            wheel: {bubbles: true, cancelable: true} //DOM3
        }
    },

    _DOMPasteboardElement: {
        value: null,
        enumerable: false
    },

    _delegate: {
        value: null,
        enumerable: false
    },
/**
        @type {String}
        @default null
    */
    delegate: {
        enumerable: false,
        get: function() {
            return this._delegate;
        },
        set: function(delegate) {
            this._delegate = delegate;
        }
    },
/**
  @private
*/
    _application: {
        value: null,
        enumerable: false
    },
/**
    The application object associated with the event manager.
    @type {String}
    @default null
    */
    application: {
        enumerable: false,
        get: function() {
            return this._application;
        },
        set: function(application) {
            // TODO if this changes...we probably need to unregister all the windows
            // we know about and frankly probably the components too
            // Really maybe this should be possible.
            this._application = application;
        }
    },

    // Dictionary keyed by event types with the collection of handlers per event type
    // This dictates why the event manager observes events of a particular type

    // Simple array of all windows this event Manager may be listening to

 /**
  @private
*/
    _registeredWindows: {
        value: null,
        enumerable: false
    },
/**
  @private
*/
    _windowsAwaitingFinalRegistration: {
        value: {},
        enumerable: false
    },

    // Initialization
/**
    @function
    @param {Window} aWindow
    @returns this registerWindow(aWindow)
    */
    initWithWindow: {
        enumerable: false,
        value: function(aWindow) {
            if (!!this._registeredWindows) {
                throw "EventManager has already been initialized";
            }

            // TODO do we also complain if no window is given? Technically
            // we don't need one until we start listening for events
            this.registerWindow(aWindow);
            return this;
        }
    },
/**
    @function
    @param {Window} aWindow
    */
    registerWindow: {
        enumerable: false,
        value: function(aWindow) {

            if (aWindow.defaultEventManager && aWindow.defaultEventManager !== this) {
                throw "EventManager cannot register a window already registered to another EventManager";
            }

            if (this._registeredWindows && this._registeredWindows.indexOf(aWindow) >= 0) {
                throw "EventManager cannot register a window more than once";
            }

            if (!this._registeredWindows) {
                this._registeredWindows = [];
            }

            if (!aWindow.uuid || aWindow.uuid.length === 0) {
                aWindow.uuid = UUID.generate();
            }

            if (this._windowsAwaitingFinalRegistration[aWindow.uuid] === aWindow) {
                return;
            }

            // Setup the window as much as possible now without knowing whether
            // the DOM is ready or not

            // Keep a reference to the original listener functions

            // Note I think it may be implementation specific how these are implemented
            // so I'd rather preserve any native optimizations a browser has for
            // adding listeners to the document versus and element etc.
            aWindow.Element.prototype.nativeAddEventListener = aWindow.Element.prototype.addEventListener;
            Object.defineProperty(aWindow, "nativeAddEventListener", {
                configurable: true,
                value: aWindow.addEventListener
            });
            Object.getPrototypeOf(aWindow.document).nativeAddEventListener = aWindow.document.addEventListener;
            aWindow.XMLHttpRequest.prototype.nativeAddEventListener = aWindow.XMLHttpRequest.prototype.addEventListener;
            if (aWindow.Worker) {
                aWindow.Worker.prototype.nativeAddEventListener = aWindow.Worker.prototype.addEventListener;
            }
            if (aWindow.MediaController) {
                aWindow.MediaController.prototype.nativeAddEventListener = aWindow.MediaController.prototype.addEventListener;
            }

            aWindow.Element.prototype.nativeRemoveEventListener = aWindow.Element.prototype.removeEventListener;
            Object.defineProperty(aWindow, "nativeRemoveEventListener", {
                configurable: true,
                value: aWindow.removeEventListener
            });
            Object.getPrototypeOf(aWindow.document).nativeRemoveEventListener = aWindow.document.removeEventListener;
            aWindow.XMLHttpRequest.prototype.nativeRemoveEventListener = aWindow.XMLHttpRequest.prototype.removeEventListener;
            if (aWindow.Worker) {
                aWindow.Worker.prototype.nativeRemoveEventListener = aWindow.Worker.prototype.removeEventListener;
            }
            if (aWindow.MediaController) {
                aWindow.MediaController.prototype.nativeRemoveEventListener = aWindow.MediaController.prototype.removeEventListener;
            }

            // Redefine listener functions

            Object.defineProperty(aWindow, "addEventListener", {
                configurable: true,
                value: (aWindow.XMLHttpRequest.prototype.addEventListener =
                        aWindow.Element.prototype.addEventListener =
                            Object.getPrototypeOf(aWindow.document).addEventListener =
                                function(eventType, listener, useCapture) {
                                    return aWindow.defaultEventManager.registerEventListener(this, eventType, listener, !!useCapture);
                                })
            });

            if (aWindow.Worker) {
                aWindow.Worker.prototype.addEventListener = aWindow.addEventListener;
            }
            if (aWindow.MediaController) {
                aWindow.MediaController.prototype.addEventListener = aWindow.addEventListener;
            }

            Object.defineProperty(aWindow, "removeEventListener", {
                configurable: true,
                value: (aWindow.XMLHttpRequest.prototype.removeEventListener =
                        aWindow.Element.prototype.removeEventListener =
                            Object.getPrototypeOf(aWindow.document).removeEventListener =
                                function(eventType, listener, useCapture) {
                                    return aWindow.defaultEventManager.unregisterEventListener(this, eventType, listener, !!useCapture);
                                })
            });

            if (aWindow.Worker) {
                aWindow.Worker.prototype.removeEventListener = aWindow.removeEventListener;
            }
            if (aWindow.MediaController) {
                aWindow.MediaController.prototype.removeEventListener = aWindow.removeEventListener;
            }

            // In some browsers (Firefox) each element has their own addEventLister/removeEventListener
            // Methodology to find all elements found in Chainvas
            if(aWindow.HTMLDivElement.prototype.addEventListener !== aWindow.Element.prototype.nativeAddEventListener) {
                if (aWindow.HTMLElement &&
                    'addEventListener' in aWindow.HTMLElement.prototype &&
                    aWindow.Components &&
                    aWindow.Components.interfaces
                ) {
                    var candidate, candidatePrototype;

                    for(candidate in Components.interfaces) {
                        if(candidate.match(/^nsIDOMHTML\w*Element$/)) {
                            candidate = candidate.replace(/^nsIDOM/, '');
                            if(candidate = window[candidate]) {
                                candidatePrototype = candidate.prototype;
                                candidatePrototype.nativeAddEventListener = candidatePrototype.addEventListener;
                                candidatePrototype.addEventListener = aWindow.Element.prototype.addEventListener;
                                candidatePrototype.nativeRemoveEventListener = candidatePrototype.removeEventListener;
                                candidatePrototype.removeEventListener = aWindow.Element.prototype.removeEventListener;
                            }
                        }
                    }
                }
            }

            /**
             HTML element event handler UUID
             @member external:Element#eventHandlerUUID
             */
            Montage.defineProperty(aWindow.Element.prototype, "eventHandlerUUID", /** @lends defineProperty */ {
                value: undefined,
                enumerable: false
            });

            /**
             The component instance directly associated with the specified element.
             @member external:Element#component
             */
            Montage.defineProperty(aWindow.Element.prototype, "component", {
                get: function() {
                    return defaultEventManager._elementEventHandlerByUUID[this.eventHandlerUUID];
                },
                enumerable: false
            });

            defaultEventManager = aWindow.defaultEventManager = exports.defaultEventManager = this;
            this._registeredWindows.push(aWindow);

            this._windowsAwaitingFinalRegistration[aWindow.uuid] = aWindow;

            // Some registration demands the window's dom be accessible
            // only finalize registration when that's true
            if (/loaded|complete|interactive/.test(aWindow.document.readyState)) {
                this._finalizeWindowRegistration(aWindow);
            } else {
                aWindow.document.addEventListener("DOMContentLoaded", this, true);
            }
        }
    },
/**
  @private
*/
    _finalizeWindowRegistration: {
        enumerable: false,
        value: function(aWindow) {

            if (this._windowsAwaitingFinalRegistration[aWindow.uuid] !== aWindow) {
                throw "EventManager wasn't expecting to register this window";
            }

            delete this._windowsAwaitingFinalRegistration[aWindow.uuid];

            this._listenToWindow(aWindow);
            // TODO uninstall DOMContentLoaded listener if all windows finalized
        }
    },
/**
    @function
    @param {Window} aWindow
    */
    unregisterWindow: {
        enumerable: false,
        value: function(aWindow) {
            if (this._registeredWindows.indexOf(aWindow) < 0) {
                throw "EventManager cannot unregister an unregistered window";
            }

            this._registeredWindows = this._registeredWindows.filter(function (element) {
                return (aWindow !== element);
            });

            delete aWindow.defaultEventManager;

            // Restore existing listener functions

            aWindow.Element.prototype.addEventListener = aWindow.Element.prototype.nativeAddEventListener;
            Object.defineProperty(aWindow, "addEventListener", {
                configurable: true,
                value: aWindow.nativeAddEventListener
            });
            Object.getPrototypeOf(aWindow.document).addEventListener = aWindow.document.nativeAddEventListener;
            aWindow.XMLHttpRequest.prototype.addEventListener = aWindow.XMLHttpRequest.prototype.nativeAddEventListener;
            if (aWindow.Worker) {
                aWindow.Worker.prototype.addEventListener = aWindow.Worker.prototype.nativeAddEventListener;
            }

            aWindow.Element.prototype.removeEventListener = aWindow.Element.prototype.nativeRemoveEventListener;
            Object.defineProperty(aWindow, "removeEventListener", {
                configurable: true,
                value: aWindow.nativeRemoveEventListener
            });
            Object.getPrototypeOf(aWindow.document).removeEventListener = aWindow.document.nativeRemoveEventListener;
            aWindow.XMLHttpRequest.prototype.removeEventListener = aWindow.XMLHttpRequest.prototype.nativeRemoveEventListener;
            if (aWindow.Worker) {
                aWindow.Worker.prototype.removeEventListener = aWindow.Worker.prototype.nativeRemoveEventListener;
            }

            // In some browsers (Firefox) each element has their own addEventLister/removeEventListener
            // Methodology to find all elements found in Chainvas
            if(aWindow.HTMLDivElement.prototype.nativeAddEventListener !== aWindow.Element.prototype.addEventListener) {
                if (aWindow.HTMLElement &&
                    'addEventListener' in aWindow.HTMLElement.prototype &&
                    aWindow.Components &&
                    aWindow.Components.interfaces
                ) {
                    var candidate, candidatePrototype;

                    for(candidate in Components.interfaces) {
                        if(candidate.match(/^nsIDOMHTML\w*Element$/)) {
                            candidate = candidate.replace(/^nsIDOM/, '');
                            if(candidate = window[candidate]) {
                                candidatePrototype = candidate.prototype;
                                candidatePrototype.addEventListener = candidatePrototype.nativeAddEventListener;
                                delete candidatePrototype.nativeAddEventListener;
                                candidatePrototype.removeEventListener = candidatePrototype.nativeRemoveEventListener;
                                delete candidatePrototype.nativeRemoveEventListener;
                            }
                        }
                    }
                }
            }

            // Delete our references

            delete aWindow.Element.prototype.nativeAddEventListener;
            delete aWindow.nativeAddEventListener;

            delete Object.getPrototypeOf(aWindow.document).nativeAddEventListener;
            delete aWindow.XMLHttpRequest.prototype.nativeAddEventListener;
            if (aWindow.Worker) {
                delete aWindow.Worker.prototype.nativeAddEventListener;
            }

            delete aWindow.Element.prototype.nativeRemoveEventListener;
            delete aWindow.nativeRemoveEventListener;

            delete Object.getPrototypeOf(aWindow.document).nativeRemoveEventListener;
            delete aWindow.XMLHttpRequest.prototype.nativeRemoveEventListener;
            if (aWindow.Worker) {
                delete aWindow.Worker.prototype.nativeRemoveEventListener;
            }

            delete aWindow.Element.prototype.eventHandlerUUID;
            delete aWindow.Element.prototype.component;

            this._stopListeningToWindow(aWindow);
        }
    },
/**
    @function
    */
    unregisterWindows: {
        enumerable: false,
        value: function() {
            this._registeredWindows.forEach(this.unregisterWindow);
        }
    },

    // Event Handler Registration

    // e.g.
    // mousedown: {
    //      target.uuid: {
    //          target: target,
    //          listeners: {
    //              Object1.uuid: {listener: Object1, capture: true, bubble: true},
    //              Object2.uuid: {listener: Object2, capture: true, bubble: false},
    //              Object3.uuid: {listener: Object3, capture: false, bubble: true}}}}


   /**
        Registered event listeners.
        @type {Listeners}
        @default {}
    */
    registeredEventListeners: {
        enumerable: false,
        value: {}
    },

  /**
    Returns a dictionary of all listeners registered for the specified eventType, regardless of the target being observed.
    @function
    @param {Event} eventType The event type.
    @returns null || listeners
    */
    registeredEventListenersForEventType_: {
        value: function(eventType) {
            var eventTypeEntry = this.registeredEventListeners[eventType],
                targetUid,
                listenerEntries,
                listenerUid,
                listeners;

            if (!eventTypeEntry) {
                return null;
            }

            listeners = {};

            for (targetUid in eventTypeEntry) {
                listenerEntries = eventTypeEntry[targetUid].listeners;

                for (listenerUid in listenerEntries) {
                    listeners[listenerUid] = listenerEntries[listenerUid];
                }
            }

            return listeners;
        }
    },

    /**
    Returns the dictionary of all listeners registered for the specified eventType, on the specified target.
    @function
    @param {Event} eventType The event type.
    @param {Event} target The event target.
    @returns targetRegistration ? targetRegistration.listeners : null
    */
    registeredEventListenersForEventType_onTarget_: {
        enumerable: false,
        value: function(eventType, target, application) {

            var eventRegistration,
                targetRegistration;

            if (target === application) {
                eventRegistration = application.eventManager.registeredEventListeners[eventType];
            } else {
                eventRegistration = this.registeredEventListeners[eventType];
            }

            if (!eventRegistration) {
                return null;
            } else {
                targetRegistration = eventRegistration[target.uuid];

                return targetRegistration ? targetRegistration.listeners : null;
            }
        }
    },


    /**
    Returns the dictionary of all listeners registered on the specified target, keyed by eventType.
    @function
    @param {Event} target The event target.
    @returns observedEventListeners
    */
    registeredEventListenersOnTarget_: {
        value: function(target) {

            var eventType,
                eventRegistration,
                observedEventListeners = [];

            for (eventType in this.registeredEventListeners) {
                eventRegistration = this.registeredEventListeners[eventType];
                if (target.uuid in eventRegistration) {
                    observedEventListeners.push(eventRegistration);
                }
            }

            return observedEventListeners;
        }
    },

    // NOTE this adds the listener to the definitive collection of what
    // targets are being observed for what eventTypes by whom and in what phases
    // This collection maintained by the EventManager is used throughout the
    // discovery and distribution steps of the event handling system

   /**
    This adds the listener to the definitive collection of what targets are being observed for what eventTypes by whom and in what phases. This collection maintained by the EventManager is used throughout the discovery and distribution steps of the event handling system.
    @function
    @param {Event} target The event target.
    @param {Event} eventType The event type.
    @param {Event} listener The event listener.
    @param {Event} useCapture The event capture.
    @returns returnResult
    */

    registerEventListener: {
        enumerable: false,
        value: function registerEventListener(target, eventType, listener, useCapture) {

            // console.log("EventManager.registerEventListener", target, eventType, listener, useCapture)

            var eventTypeRegistration = this.registeredEventListeners[eventType],
                targetRegistration,
                listenerRegistration,
                phase,
                isNewTarget = false,
                returnResult = false;

            if (typeof target.uuid === "undefined") {
                throw new Error("EventManager cannot observe a target without a uuid: " + (target.outerHTML || target));
            }

            if (!eventTypeRegistration) {
                // First time this eventType has been requested
                eventTypeRegistration = this.registeredEventListeners[eventType] = {};
                eventTypeRegistration[target.uuid] = {target: target, listeners: {}};
                eventTypeRegistration[target.uuid].listeners[listener.uuid] = {listener: listener, capture: useCapture, bubble: !useCapture};

                isNewTarget = true;
                returnResult = true;
            } else {

                // Or, the event type was already observed; install this new listener (or at least any new parts)
                if (!(targetRegistration = eventTypeRegistration[target.uuid])) {
                    targetRegistration = eventTypeRegistration[target.uuid] = {target: target, listeners: {}};
                    isNewTarget = true;
                }

                listenerRegistration = targetRegistration.listeners[listener.uuid];
                phase = useCapture ? "capture" : "bubble";

                if (listenerRegistration) {
                    listenerRegistration[phase] = true;
                    returnResult = true;
                } else {
                    listenerRegistration = {listener: listener, capture: useCapture, bubble: !useCapture};
                    targetRegistration.listeners[listener.uuid] = listenerRegistration;
                    returnResult = true;
                }

            }

            if (isNewTarget && typeof target.nativeAddEventListener === "function") {
                this._observeTarget_forEventType_(target, eventType);
            }

            // console.log("EventManager.registeredEventListeners", this.registeredEventListeners)

            return returnResult;
        }
    },
/**
    This unregisters the listener.
    @function
    @param {Event} target The event target.
    @param {Event} eventType The event type.
    @param {Event} listener The event listener.
    @param {Event} useCapture The event capture.
    */
    unregisterEventListener: {
        enumerable: false,
        value: function unregisterEventListener(target, eventType, listener, useCapture) {

            // console.log("EventManager.unregisterEventListener", target, eventType, listener, useCapture)

            var eventTypeRegistration = this.registeredEventListeners[eventType],
                targetRegistration,
                listenerRegistration,
                phase,
                listenerUUID,
                installedListener;

            if (!eventTypeRegistration) {
                // this eventType wasn't being observed at all
                return;
            }

            // the event type was observed; see if the target was registered
            targetRegistration = eventTypeRegistration[target.uuid];
            if (!targetRegistration) {
                return;
            }

            // the target was being observed for this eventType; see if the specified listener was registered
            listenerRegistration = targetRegistration.listeners[listener.uuid];

            if (!listenerRegistration) {

                for (listenerUUID in targetRegistration.listeners) {
                    installedListener = targetRegistration.listeners[listenerUUID].listener;

                    if (installedListener.originalListener && installedListener.originalListener.uuid === listener.uuid) {
                        listenerRegistration = targetRegistration.listeners[listenerUUID];
                        listener = installedListener;
                        break;
                    }
                }


                if (!listenerRegistration) {
                    return;
                }
            }

            phase = useCapture ? "capture" : "bubble";
            // console.log("unregistering listener from phase:", phase)

            listenerRegistration[phase] = false;

            // Done unregistering the listener for the specified phase
            // Now see if we need to remove any registrations as a result of that

            if (!listenerRegistration.bubble && !listenerRegistration.capture) {
                // this listener isn't listening in any phase; remove it
                delete targetRegistration.listeners[listener.uuid];

                if (Object.keys(targetRegistration.listeners).length === 0) {
                    // If no listeners for this target given this event type; remove this target
                    delete eventTypeRegistration[target.uuid];

                    if (Object.keys(eventTypeRegistration).length === 0) {
                        // If no targets for this eventType; stop observing this event
                        delete this.registeredEventListeners[eventType];
                        this._stopObservingTarget_forEventType_(target, eventType);
                    }

                }

            }
            // console.log("EventManager.unregisteredEventListener", this.registeredEventListeners)
        }
    },

   /**
    Determines the actual target to observe given a target and an eventType. This correctly decides whether to observe the element specified or to observe some other element to leverage event delegation. This should be consulted whenever starting or stopping the observation of a target for a given eventType.
    @function
    @param {Event} eventType
    @param {Event} target
    @returns null || target.screen ? target.document : target.ownerDocument
    */

    actualDOMTargetForEventTypeOnTarget: {
        value: function(eventType, target) {

            if (!target.nativeAddEventListener) {
                return null;
            } else {

                if (/*isDocument*/!!target.defaultView) {
                    return target;
                }

                var entry = this.eventDefinitions[eventType],
                    bubbles;

                // For events we know we can safely delegate to handling at a higher level, listen on the document
                // otherwise, be less surprising and listen on the specified target

                if (!entry) {
                    return target;
                }

                // TODO allow eventTypes to describe a preferred delegation target window|document|none etc.
                bubbles = (typeof entry.bubbles === FUNCTION_TYPE) ? entry.bubbles(target) : entry.bubbles;

                if (bubbles) {
                    // TODO why on the document and not the window?
                    return /* isWindow*/target.screen ? target.document : target.ownerDocument;;
                } else {
                    return target;
                }
            }

        }
    },
  /**
  @private
*/
   _observedTarget_byEventType_: {value:{}},

    // Individual Event Registration
/**
  @private
*/
    _observeTarget_forEventType_: {
        enumerable: false,
        value: function(target, eventType) {

            var listenerTarget;

            if ((listenerTarget = this.actualDOMTargetForEventTypeOnTarget(eventType, target)) && (!this._observedTarget_byEventType_[eventType] || !this._observedTarget_byEventType_[eventType][listenerTarget.uuid])) {
                if (!this._observedTarget_byEventType_[eventType]) {
                    this._observedTarget_byEventType_[eventType] = {};
                }
                this._observedTarget_byEventType_[eventType][listenerTarget.uuid] = this;

                listenerTarget.nativeAddEventListener(eventType, this, true);
            }
            // console.log("started listening: ", eventType, listenerTarget)
        }
    },
/**
  @private
*/
    _stopObservingTarget_forEventType_: {
        enumerable: false,
        value: function(target, eventType) {

            var listenerTarget;

            listenerTarget = this.actualDOMTargetForEventTypeOnTarget(eventType, target);
            if (listenerTarget) {
                delete this._observedTarget_byEventType_[eventType][listenerTarget.uuid];
                listenerTarget.nativeRemoveEventListener(eventType, this, true);
            }
            // console.log("stopped listening: ", eventType, window.uuid)
        }
    },

    _activationHandler: {
        enumerable: true,
        value: null
    },

    // Toggle listening for EventManager
/**
  @private
*/
    _listenToWindow: {
        enumerable: false,
        value: function(aWindow) {

            // We use our own function to handle activation events so it's not inadvertently
            // removed as a listener when removing the last listener that may have also been observing
            // the same eventType of an activation event
            if (!this._activationHandler) {
                var eventManager = this;
                this._activationHandler = function(evt) {
                    var eventType = evt.type,
                        touchCount;

                    // Prepare any components associated with elements that may receive this event
                    // They need to registered there listeners before the next step, which is to find the components that
                    // observing for this type of event
                    if ("focus" === eventType || "mousedown" === eventType || "touchstart" === eventType) {
                        if (evt.changedTouches) {
                            touchCount = evt.changedTouches.length;
                            for (var i = 0; i < touchCount; i++) {
                                eventManager._prepareComponentsForActivation(evt.changedTouches[i].target);
                            }
                        } else {
                            eventManager._prepareComponentsForActivation(evt.target);
                        }
                    }

                };
            }

            // The EventManager needs to handle "gateway/pointer/activation events" that we
            // haven't let children listen for yet
            // when the EM handles them eventually it will need to allow
            // all components from the event target to the window to prepareForPointerEvents
            // before finding event handlers that were registered for these events
            if (aWindow.Touch) {
                // TODO on iOS the touch doesn't capture up at the window, just the document; interesting
                aWindow.document.nativeAddEventListener("touchstart", this._activationHandler, true);
            } else {
                aWindow.document.nativeAddEventListener("mousedown", this._activationHandler, true);
                //TODO also should accommodate mouseenter/mouseover possibly
            }
            aWindow.document.nativeAddEventListener("focus", this._activationHandler, true);

            if (this.application) {

                var applicationLevelEvents = this.registeredEventListenersOnTarget_(this.application),
                    eventType;

                for (eventType in applicationLevelEvents) {
                    this._observeTarget_forEventType_(aWindow, eventType);
                }
            }

        }
    },
/**
  @private
*/
    _stopListeningToWindow: {
        enumerable: false,
        value: function(aWindow) {

            var applicationLevelEvents = this.registeredEventListenersOnTarget_(this.application),
                windowLevelEvents = this.registeredEventListenersOnTarget_(aWindow),
                eventType;

            for (eventType in applicationLevelEvents) {
                this._stopObservingTarget_forEventType_(aWindow, eventType);
            }

            for (eventType in windowLevelEvents) {
                this._stopObservingTarget_forEventType_(aWindow, eventType);
            }
        }
    },
/**
    @function
    */
    reset: {
        enumerable: false,
        value: function() {
            var eventType,
                eventRegistration,
                targetUUID,
                targetRegistration;

            for (eventType in this.registeredEventListeners) {
                eventRegistration = this.registeredEventListeners[eventType];
                for (targetUUID in eventRegistration.targets) {
                    targetRegistration = eventRegistration.targets[targetUUID];
                    this._stopObservingTarget_forEventType_(targetRegistration.target, eventType);
                }
            }

            this.registeredEventListeners = {};

            // TODO for each component claiming a pointer, force them to surrender the pointer?
            this._claimedPointers = {};
        }
    },
/**
    @function
    */
    unload: {
        enumerable: false,
        value: function() {
            this._stopListening();
        }
    },
/**
    @function
    */
    methodNameForBubblePhaseOfEventType: {
        enumerable: false,
        value: (function(_methodNameForBubblePhaseByEventType_) {
            return function(eventType, identifier) {
                var eventTypeKey = identifier ? eventType + "+" + identifier : eventType;
                return _methodNameForBubblePhaseByEventType_[eventTypeKey] || (_methodNameForBubblePhaseByEventType_[eventTypeKey] = ("handle" + (identifier ? identifier.toCapitalized() : "") + eventType.toCapitalized()));
            };
        })({})
    },

    _methodNameForCapturePhaseByEventType_: {
        value:{}
    },
    methodNameForCapturePhaseOfEventType: {
        enumerable: false,
        value: (function(_methodNameForCapturePhaseByEventType_) {
            return function(eventType, identifier) {
                var eventTypeKey = identifier ? eventType + "+" + identifier : eventType;
                return _methodNameForCapturePhaseByEventType_[eventTypeKey] || (_methodNameForCapturePhaseByEventType_[eventTypeKey] = "capture" + (identifier ? identifier.toCapitalized() : "") + eventType.toCapitalized());
            };
        })({})
    },

    // Claimed pointer information
/**
  @private
*/
    _claimedPointers: {
        enumerable: false,
        distinct: true,
        value: {}
    },
/**
    The component claiming the specified pointer component
    @function
    @param {String} pointer The pointer identifier in question
    @returns component
    */
    componentClaimingPointer: {
        value: function(pointer) {
            return this._claimedPointers[pointer];
        }
    },
/**
    Whether or not the specified pointer identifier is claimed by the specified component.
    @function
    @param {String} pointer The pointer identifier in question
    @param {String} component The component to interrogate regarding ownership of the specified pointer
    @returns boolean
    */
    isPointerClaimedByComponent: {
        value: function(pointer, component) {

            if (!component) {
                throw "Must specify a valid component to see if it claims the specified pointer, '" + component + "' is not valid.";
            }

            return this._claimedPointers[pointer] === component;
        }
    },
/**
    Claims that a pointer, referred to by the specified pointer identifier, is claimed by the specified component.
    This does not give the component exclusive use of the pointer per se, but does indicate that the component
    is acting in a manner where it expects to be the only one performing major actions in response to this pointer.
    Other components should respect the claimant's desire to react to this pointer in order to prevent an entire
    hierarchy of components from reacting to a pointer in potentially conflicting ways.

    If the pointer is currently claimed by another component that component is asked to surrender the pointer,
    which is may or may not agree to do.

    @function
    @param {String} pointer The pointer identifier to claim
    @param {String} component The component that is claiming the specified pointer
    @returns boolean Whether or not the pointer was successfully claimed.
    */
    claimPointer: {
        value: function(pointer, component) {

            // if null, undefined, false: complain
            if (!pointer && pointer !== 0) {
                throw "Must specify a valid pointer to claim, '" + pointer + "' is not valid.";
            }

            if (!component) {
                throw "Must specify a valid component to claim a pointer, '" + component + "' is not valid.";
            }

            var claimant = this._claimedPointers[pointer];

            if (claimant === component) {
                // Already claimed this pointer ourselves
                return true;

            } else if (!claimant) {
                //Nobody has claimed it; go for it
                this._claimedPointers[pointer] = component;
                return true;

            } else {
                //Somebody else has claimed it; ask them to surrender
                if (claimant.surrenderPointer(pointer, component)) {
                    this._claimedPointers[pointer] = component;
                    return true;
                } else {
                    return false;
                }
            }

        }
    },
/**
    Forfeits the specified pointer identifier from the specified component.
    The specified component must be the current claimant.
    @function
    @param {String} pointer The pointer identifier in question
    @param {String} component The component that is trying to forfeit the specified pointer
    */
    forfeitPointer: {
        value: function(pointer, component) {
            if (component === this._claimedPointers[pointer]) {
                delete this._claimedPointers[pointer];
            } else {
                throw "Not allowed to forfeit pointer '" + pointer + "' claimed by another component";
            }

        }
    },
/**
    Forfeits all pointers from the specified component.
    @function
    @param {Component} component
    */
    forfeitAllPointers: {
        value: function(component) {

            var pointerKey,
                claimant;

            for (pointerKey in this._claimedPointers) {
                claimant = this._claimedPointers[pointerKey];
                if (component === claimant) {
                    // NOTE basically doing the work ofr freePointerFromComponent
                    delete this._claimedPointers[pointerKey];
                }
            }

        }
    },

    // Pointer Storage for calculating velocities
/**
  @private
*/
    _isStoringPointerEvents: {
        enumerable: false,
        value: false
    },
 /**
        @type {Function}
        @default {Boolean} false
    */
    isStoringPointerEvents: {
        enumerable: true,
        get: function () {
            return this._isStoringPointerEvents;
        },
        set: function (value) {
            if (value === true) {
                if (!this._isStoringPointerEvents) {
                    this._isStoringPointerEvents = true;
                    if (window.Touch) {
                        Object.defineProperty(Touch.prototype, "velocity", {
                            get: function () {
                                return defaultEventManager.pointerMotion(this.identifier).velocity;
                            },
                            set: function () {
                            }
                        });
                    }
                }
            } else {
                this._isStoringPointerEvents = false;
                this._pointerStorage.memory = {};
                this._isMouseDragging = false;
            }
        }
    },
/**
  @private
*/
    _isStoringMouseEventsWhileDraggingOnly: {
        enumerable: false,
        value: true
    },

/**
        @type {Function}
        @default {Boolean} true
    */
    isStoringMouseEventsWhileDraggingOnly: {
        enumerable: true,
        get: function () {
            return this._isStoringMouseEventsWhileDraggingOnly;
        },
        set: function (value) {
            this._isStoringMouseEventsWhileDraggingOnly = (value === true) ? true : false;
        }
    },
/**
  @private
*/
    _isMouseDragging: {
        enumerable: false,
        value: false
    },
/**
  @private
*/
    _pointerStorage: {
        enumerable: false,
        value: {
            memory: {},
            add: function (identifier, data) {
                if (!this.memory[identifier]) {
                    this.memory[identifier] = {
                        data: new Array(32),
                        size: 0,
                        pos: 0
                    };
                }
                this.memory[identifier].data[this.memory[identifier].pos] = data;
                if (this.memory[identifier].size < this.memory[identifier].data.length) {
                    this.memory[identifier].size++;
                }
                this.memory[identifier].pos = (this.memory[identifier].pos + 1) % this.memory[identifier].data.length;
            },
            remove: function (identifier) {
                delete this.memory[identifier];
            },
            clear: function (identifier) {
                if (this.memory[identifier]) {
                    this.memory[identifier].size = 0;
                }
            },
            getMemory: function (identifier) {
                return this.memory[identifier];
            },
            isStored: function (identifier) {
                return (this.memory[identifier] && (this.memory[identifier].size > 0));
            },
            storeEvent: function(event) {
                var i;
                switch (event.type) {
                    case "mousedown":
                        defaultEventManager._isMouseDragging = true;
                        // TODO not sure if we are supposed to gave a break here or not. There wasn't
                        // but I'd like a comment to assuage my fears that it was omitted by mistake
                    case "mousemove":
                        if (defaultEventManager._isStoringMouseEventsWhileDraggingOnly) {
                            if (defaultEventManager._isMouseDragging) {
                                this.add("mouse", {
                                    clientX: event.clientX,
                                    clientY: event.clientY,
                                    timeStamp: event.timeStamp
                                });
                                Object.defineProperty(event, "velocity", {
                                    get: function () {
                                        return defaultEventManager.pointerMotion("mouse").velocity;
                                    },
                                    set: function () {
                                    }
                                });
                            }
                        } else {
                            this.add("mouse", {
                                clientX: event.clientX,
                                clientY: event.clientY,
                                timeStamp: event.timeStamp
                            });
                            Object.defineProperty(event, "velocity", {
                                get: function () {
                                    return defaultEventManager.pointerMotion("mouse").velocity;
                                },
                                set: function () {
                                }
                            });
                        }
                        break;
                    case "mouseup":
                        this.add("mouse", {
                            clientX: event.clientX,
                            clientY: event.clientY,
                            timeStamp: event.timeStamp
                        });
                        Object.defineProperty(event, "velocity", {
                            get: function () {
                                return defaultEventManager.pointerMotion("mouse").velocity;
                            },
                            set: function () {
                            }
                        });
                        break;
                    case "touchstart":
                    case "touchmove":
                        for (i = 0; i < event.touches.length; i++) {
                            this.add(event.touches[i].identifier, {
                                clientX: event.touches[i].clientX,
                                clientY: event.touches[i].clientY,
                                timeStamp: event.timeStamp
                            });
                        }
                        break;
                    case "touchend":
                        for (i = 0; i < event.changedTouches.length; i++) {
                            this.add(event.changedTouches[i].identifier, {
                                clientX: event.changedTouches[i].clientX,
                                clientY: event.changedTouches[i].clientY,
                                timeStamp: event.timeStamp
                            });
                        }
                        break;
                }
            },
            removeEvent: function(event) {
                var i;
                switch (event.type) {
                    case "mouseup":
                        defaultEventManager._isMouseDragging = false;
                        if (defaultEventManager._isStoringMouseEventsWhileDraggingOnly) {
                            this.clear("mouse");
                        }
                        break;
                    case "touchend":
                        for (i = 0; i < event.changedTouches.length; i++) {
                            this.remove(event.changedTouches[i].identifier);
                        }
                        break;
                }
            }
        }
    },
/**
  @private
*/
    _getPointerVelocityData: {
        enumerable: false,
        value: function (identifier) {
            var i = 0,
                memory,
                memoryLength,
                evt,
                startTime,
                iTime,
                oldTime, oldX, oldY, squaredModule,
                difTime = 0,
                addData = true,
                data = {
                    x: [],
                    y: [],
                    time: []
                };

            memory = defaultEventManager._pointerStorage.getMemory(identifier);
            memoryLength = memory.data.length;
            evt = memory.data[((memory.pos - 1) + memoryLength) % memoryLength];
            startTime = iTime = oldTime = evt.timeStamp;
            oldX = evt.clientX;
            oldY = evt.clientY;
            while (addData && (iTime > startTime - 350) && (i < memory.size)) {
                evt = memory.data[((memory.pos - i - 1) + memoryLength) % memoryLength];
                iTime = evt.timeStamp;
                squaredModule = oldX * oldX + oldY * oldY;
                if ((squaredModule > 2) && ((oldTime - iTime) <= 50)) {
                    data.x.push(evt.clientX);
                    data.y.push(evt.clientY);
                    data.time.push(iTime);
                    oldTime = iTime;
                    oldX = evt.clientX;
                    oldY = evt.clientY;
                    i++;
                } else {
                    addData = false;
                }
            }
            return data;
        }
    },
/**
  @private
*/
    _fitPointerCurve: {
        enumerable: false,
        value: function (bezier, data) {
            var pos, a, b, c, d, epsilon = 0.0001,
                dl = data.length, e, t, v, t2, t3, i,
                f0, c0, d0, b0, a0, s0, e0,
                f1, c1, d1, b1, a1, s1, e1,
                f2, c2, d2, b2, a2, s2, e2,
                f3, c3, d3, b3, a3, s3, e3;
            do {
                f0 = 0;
                c0 = 0;
                d0 = 0;
                b0 = 0;
                a0 = 0;
                s0 = 0;
                f1 = 0;
                c1 = 0;
                d1 = 0;
                b1 = 0;
                a1 = 0;
                s1 = 0;
                f2 = 0;
                c2 = 0;
                d2 = 0;
                b2 = 0;
                a2 = 0;
                s2 = 0;
                f3 = 0;
                c3 = 0;
                d3 = 0;
                b3 = 0;
                a3 = 0;
                s3 = 0;
                for (i = 0; i < dl; i++) {
                    e = data[i];
                    t = e.t;
                    t2 = t * t;
                    t3 = t2 * t;
                    v = e.v;
                    e0 = epsilon * (6 * (t2 - t) - t3 + 2);
                    e1 = epsilon * 6 * (t3 - 2 * t2 + t);
                    e2 = epsilon * 6 * (t2 - t3);
                    e3 = epsilon * 2 * t3;
                    s0 += e0 * e0;
                    s1 += e1 * e1;
                    s2 += e2 * e2;
                    s3 += e3 * e3;
                    f0 += v * e0;
                    f1 += v * e1;
                    f2 += v * e2;
                    f3 += v * e3;
                    d0 -= e0;
                    d1 -= e1;
                    d2 -= e2;
                    d3 -= e3;
                    c0 -= e0 * t;
                    c1 -= e1 * t;
                    c2 -= e2 * t;
                    c3 -= e3 * t;
                    b0 -= e0 * t2;
                    b1 -= e1 * t2;
                    b2 -= e2 * t2;
                    b3 -= e3 * t2;
                    a0 -= e0 * t3;
                    a1 -= e1 * t3;
                    a2 -= e2 * t3;
                    a3 -= e3 * t3;
                }
                epsilon *= 2;
            } while (s0 === 0 || s1 === 0 || s2 === 0 || s3 === 0);
            t = epsilon / s0;
            f0 *= t;
            c0 *= t * 3;
            d0 *= t;
            b0 *= t * 3;
            a0 *= t;
            t = epsilon / s1;
            f1 *= t;
            c1 *= t * 3;
            d1 *= t;
            b1 *= t * 3;
            a1 *= t;
            t = epsilon / s2;
            f2 *= t;
            c2 *= t * 3;
            d2 *= t;
            b2 *= t * 3;
            a2 *= t;
            t = epsilon / s3;
            f3 *= t;
            c3 *= t * 3;
            d3 *= t;
            b3 *= t * 3;
            a3 *= t;
            s0 = bezier[0];
            s1 = bezier[1];
            s2 = bezier[2];
            s3 = bezier[3];
            a = (s1 - s2) * 3 + s3 - s0;
            b = s0 + s2 - 2 * s1;
            c = s1 - s0;
            d = s0;
            for (i = 0; i < 20; i++) {
                t = f0 + d * d0 + c * c0 + b * b0 + a * a0;
                s0 += t;
                d += t;
                a -= t;
                b += t;
                c -= t;
                t = f1 + d * d1 + c * c1 + b * b1 + a * a1;
                s1 += t;
                a += t * 3;
                b -= t + t;
                c += t;
                t = f2 + d * d2 + c * c2 + b * b2 + a * a2;
                s2 += t;
                a -= t * 3;
                b += t;
                t = f3 + d * d3 + c * c3 + b * b3 + a * a3;
                s3 += t;
                a += t;
            }
            bezier[0] = s0;
            bezier[1] = s1;
            bezier[2] = s2;
            bezier[3] = s3;
        }
    },
/**
  @private
*/
    _pointerBezierValue: {
        enumerable: false,
        value: function (t, bezier) {
            var it = 1 - t;
            return it * it * it * bezier[0] + 3 * it * it * t * bezier[1] + 3 * it * t * t * bezier[2] + t * t * t * bezier[3];
        }
    },
/**
  @private
*/
    _calculatePointerVelocity: {
        enumerable: false,
        value: function (time, position) {
            var length = time.length,
                timeMin = time[0],
                timeMax = time[0],
                timeInterval,
                iMin = 0, i;
            for (i = 1; i < length; i++) {
                if (time[i] < timeMin) {
                    timeMin = time[i];
                    iMin = i;
                }
            }
            timeInterval = timeMax - timeMin;
            if (timeInterval) {
                if (length > 5) {
                    var s, e, bezier, data = [];
                    for (i = 0; i < length; i++) {
                        data[i] = {
                            v: position[i],
                            t: (time[i] - timeMin) / timeInterval
                        };
                    }
                    s = data[iMin].v;
                    e = data[0].v;
                    bezier = [s, (s * 2 + e) / 3, (s + e * 2) / 3, e];
                    this._fitPointerCurve(bezier, data);
                    return (this._pointerBezierValue(0.8, bezier) - this._pointerBezierValue(0.6, bezier)) * 5000 / timeInterval;
                } else if (length > 1) {
                    return (position[0] - position[iMin]) * 1000 / timeInterval;
                } else {
                    return 0;
                }
            } else {
                return 0;
            }
        }
    },
/**
    @function
    @param {attribute} identifier
    */
    pointerMotion: {
        value: function (identifier) {
            if (defaultEventManager._pointerStorage.isStored(identifier)) {
                var velocity = {};
                Object.defineProperties(velocity, {
                    _data: {
                        enumerable: false,
                        writable: true,
                        value: null
                    },
                    _x: {
                        enumerable: false,
                        writable: true,
                        value: null
                    },
                    _y: {
                        enumerable: false,
                        writable: true,
                        value: null
                    },
                    _speed: {
                        enumerable: false,
                        writable: true,
                        value: null
                    },
                    _angle: {
                        enumerable: false,
                        writable: true,
                        value: null
                    },
                    x: {
                        get: function () {
                            if (this._x === null) {
                                if (this._data === null) {
                                    this._data = defaultEventManager._getPointerVelocityData(identifier);
                                }
                                this._x = defaultEventManager._calculatePointerVelocity(this._data.time, this._data.x);
                            }
                            return this._x;
                        },
                        set: function () {
                        }
                    },
                    y: {
                        get: function () {
                            if (this._y === null) {
                                if (this._data === null) {
                                    this._data = defaultEventManager._getPointerVelocityData(identifier);
                                }
                                this._y = defaultEventManager._calculatePointerVelocity(this._data.time, this._data.y);
                            }
                            return this._y;
                        },
                        set: function () {
                        }
                    },
                    speed: {
                        get: function () {
                            if (this._speed === null) {
                                this._speed = Math.sqrt(this.x * this.x + this.y * this.y);
                            }
                            return this._speed;
                        },
                        set: function () {
                        }
                    },
                    angle: {
                        get: function () {
                            if (this._angle === null) {
                                this._angle = Math.atan2(this.y, this.x);
                            }
                            return this._angle;
                        },
                        set: function () {
                        }
                    }
                });
                return {
                    velocity: velocity
                };
            } else {
                return undefined;
            }
        }
    },
    monitorDOMModificationInEventHandling: {value: false},
    domModificationEventHandler: { value: Montage.specialize( {
        handleEvent: {value : function(event) {
            throw "DOM Modified";
        }},
        captureDOMSubtreeModified: {value: function(event) {
            throw "DOMSubtreeModified";
        }},
        captureDOMAttrModified: {value: function(event) {
            throw "DOMAttrModified";
        }},
        captureDOMCharacterDataModified: { value: function(event) {
            throw "DOMCharacterDataModified";
        }}})
    },
    // Event Handling
    /**
    @function
    @param {Event} event The handled event.
    */
    handleEvent: {
        enumerable: false,
        value: function(event) {

            if (this.monitorDOMModificationInEventHandling) {
                document.body.addEventListener("DOMSubtreeModified", this.domModificationEventHandler, true);
                document.body.addEventListener("DOMAttrModified", this.domModificationEventHandler, true);
                document.body.addEventListener("DOMCharacterDataModified", this.domModificationEventHandler, true);
            }


            var loadedWindow,
                i,
                iTarget,
                listenerEntries,
                j,
                jListenerEntry,
                listenerEntryKeys,
                listenerEntryKeyCount,
                jListener,
                eventPath,
                eventType = event.type,
                eventBubbles = event.bubbles,
                captureMethodName,
                bubbleMethodName,
                identifierSpecificCaptureMethodName,
                identifierSpecificBubbleMethodName,
                mutableEvent,
                touchCount;

            if ("DOMContentLoaded" === eventType) {
                loadedWindow = event.target.defaultView;
                if (loadedWindow && this._windowsAwaitingFinalRegistration[loadedWindow.uuid]) {
                    this._finalizeWindowRegistration(loadedWindow);
                    // Stop listening for DOMContentLoaded on this target
                    // Otherwise the eventManager's handleEvent will be called
                    // again from within here when the eventManager is found
                    // to be a listener for this event when we find the listeners
                    event.target.removeEventListener("DOMContentLoaded", this, true);
                }
            }

            if (typeof event.propagationStopped !== "boolean") {
                mutableEvent = MutableEvent.fromEvent(event);
            } else {
                mutableEvent = event;
            }

            if (Element.isElement(mutableEvent.target) || mutableEvent.target instanceof Document ||  mutableEvent.target instanceof Window) {
                eventPath = this._eventPathForDomTarget(mutableEvent.target);
            } else {
                eventPath = this._eventPathForTarget(mutableEvent.target);
            }

            // use most specific handler method available, possibly based upon the identifier of the event target
            if (mutableEvent.target.identifier) {
                identifierSpecificCaptureMethodName = this.methodNameForCapturePhaseOfEventType(eventType, mutableEvent.target.identifier);
            } else {
                identifierSpecificCaptureMethodName = null;
            }

            if (mutableEvent.target.identifier) {
                identifierSpecificBubbleMethodName = this.methodNameForBubblePhaseOfEventType(eventType, mutableEvent.target.identifier);
            } else {
                identifierSpecificBubbleMethodName = null;
            }

            captureMethodName = this.methodNameForCapturePhaseOfEventType(eventType);
            bubbleMethodName = this.methodNameForBubblePhaseOfEventType(eventType);

            // Let the delegate handle the event first
            if (this.delegate && typeof this.delegate.willDistributeEvent === FUNCTION_TYPE) {
                this.delegate.willDistributeEvent(mutableEvent);
            }

            if (this._isStoringPointerEvents) {
                this._pointerStorage.storeEvent(mutableEvent);
            }

            // Capture Phase Distribution
            mutableEvent.eventPhase = CAPTURING_PHASE;
            // The event path we generate is from bottom to top, capture needs to traverse this backwards
            for (i = eventPath.length - 1; !mutableEvent.propagationStopped && (iTarget = eventPath[i]); i--) {
                mutableEvent.currentTarget = iTarget;

                listenerEntries = this.registeredEventListenersForEventType_onTarget_(eventType, iTarget);
                if (!listenerEntries) {
                    continue;
                }
                listenerEntryKeys = Object.keys(listenerEntries);

                for (j = 0; listenerEntries && !mutableEvent.immediatePropagationStopped && (jListenerEntry = listenerEntries[listenerEntryKeys[j]]); j++) {

                    if (!jListenerEntry.capture) {
                        continue;
                    }

                    jListener = jListenerEntry.listener;

                    if (identifierSpecificCaptureMethodName && typeof jListener[identifierSpecificCaptureMethodName] === FUNCTION_TYPE) {
                        jListener[identifierSpecificCaptureMethodName](mutableEvent);
                    } else if (typeof jListener[captureMethodName] === FUNCTION_TYPE) {
                        jListener[captureMethodName](mutableEvent);
                    } else if (typeof jListener.handleEvent === FUNCTION_TYPE) {
                        jListener.handleEvent(mutableEvent);
                    } else if (typeof jListener === FUNCTION_TYPE && !jListener.__isConstructor__) {
                        jListener.call(iTarget, mutableEvent);
                    }
                }
            }

            // At Target Distribution
            if (!mutableEvent.propagationStopped) {
                mutableEvent.eventPhase = AT_TARGET;
                mutableEvent.currentTarget = iTarget = mutableEvent.target;

                listenerEntries = this.registeredEventListenersForEventType_onTarget_(eventType, iTarget);
                if (listenerEntries) {
                    listenerEntryKeys = Object.keys(listenerEntries);

                    for (j = 0; listenerEntries && !mutableEvent.immediatePropagationStopped && (jListenerEntry = listenerEntries[listenerEntryKeys[j]]); j++) {

                        jListener = jListenerEntry.listener;

                        if (jListenerEntry.capture) {
                            if (identifierSpecificCaptureMethodName && typeof jListener[identifierSpecificCaptureMethodName] === FUNCTION_TYPE) {
                                jListener[identifierSpecificCaptureMethodName](mutableEvent);
                            } else if (typeof jListener[captureMethodName] === FUNCTION_TYPE) {
                                jListener[captureMethodName](mutableEvent);
                            } else if (typeof jListener.handleEvent === FUNCTION_TYPE) {
                                jListener.handleEvent(mutableEvent);
                            } else if (typeof jListener === FUNCTION_TYPE) {
                                jListener.call(iTarget, mutableEvent);
                            }
                        }

                        if (jListenerEntry.bubble) {
                            if (identifierSpecificBubbleMethodName && typeof jListener[identifierSpecificBubbleMethodName] === FUNCTION_TYPE) {
                                jListener[identifierSpecificBubbleMethodName](mutableEvent);
                            } else if (typeof jListener[bubbleMethodName] === FUNCTION_TYPE) {
                                jListener[bubbleMethodName](mutableEvent);
                            } else if (typeof jListener.handleEvent === FUNCTION_TYPE) {
                                jListener.handleEvent(mutableEvent);
                            } else if (typeof jListener === FUNCTION_TYPE) {
                                jListener.call(iTarget, mutableEvent);
                            }
                        }

                    }
                }
            }

            // Bubble Phase Distribution
            mutableEvent.eventPhase = BUBBLING_PHASE;
            for (i = 0; eventBubbles && !mutableEvent.propagationStopped && (iTarget = eventPath[i]); i++) {
                mutableEvent.currentTarget = iTarget;

                listenerEntries = this.registeredEventListenersForEventType_onTarget_(eventType, iTarget);
                if (!listenerEntries) {
                    continue;
                }
                listenerEntryKeys = Object.keys(listenerEntries);

                for (j = 0; listenerEntries && !mutableEvent.immediatePropagationStopped && (jListenerEntry = listenerEntries[listenerEntryKeys[j]]); j++) {

                    if (!jListenerEntry.bubble) {
                        continue;
                    }

                    jListener = jListenerEntry.listener;

                    if (identifierSpecificBubbleMethodName && typeof jListener[identifierSpecificBubbleMethodName] === FUNCTION_TYPE) {
                        jListener[identifierSpecificBubbleMethodName](mutableEvent);
                    } else if (typeof jListener[bubbleMethodName] === FUNCTION_TYPE) {
                        jListener[bubbleMethodName](mutableEvent);
                    } else if (typeof jListener.handleEvent === FUNCTION_TYPE) {
                        jListener.handleEvent(mutableEvent);
                    } else if (typeof jListener === FUNCTION_TYPE) {
                        jListener.call(iTarget, mutableEvent);
                    }
                }
            }

            mutableEvent.eventPhase = NONE;
            mutableEvent.currentTarget = null;

            if (this._isStoringPointerEvents) {
                this._pointerStorage.removeEvent(event);
            }

            if (this.monitorDOMModificationInEventHandling) {
                document.body.removeEventListener("DOMSubtreeModified", this.domModificationEventHandler, true);
                document.body.removeEventListener("DOMAttrModified", this.domModificationEventHandler, true);
                document.body.removeEventListener("DOMCharacterDataModified", this.domModificationEventHandler, true);
            }
        }
    },

    // Ensure that any components associated with DOM elements in the hierarchy between the
    // original activationEvent target and the window are preparedForActionEvents

 /**
  @private
*/
    _prepareComponentsForActivation: {
        value: function(eventTarget) {

            var target = eventTarget,
                previousTarget,
                targetView = target && target.defaultView ? target.defaultView : window,
                targetDocument = targetView.document ? targetView.document : document,
                associatedComponent,
                lookedForActiveTarget = false,
                activeTarget = null;

            do {

                if (target) {
                    associatedComponent = this.eventHandlerForElement(target);
                    if (associatedComponent) {

                        // Once we've found a component starting point,
                        // find the closest Target that accepts focus
                        if (!lookedForActiveTarget) {
                            lookedForActiveTarget = true;
                            activeTarget = this._findActiveTarget(associatedComponent);
                        }

                        if (!associatedComponent._preparedForActivationEvents) {
                            associatedComponent._prepareForActivationEvents();
                            associatedComponent._preparedForActivationEvents = true;
                        }
                    }
                }

                previousTarget = target;

                // We only need to go up to the window, and even that's debateable as the activationEvent system really
                // only pertains to components, which are only ever associated with elements. The root element being the
                // exception which is associated with the document.
                switch (target) {
                    case targetView:
                        target = null;
                        break;
                    case targetDocument:
                        target = targetView;
                        break;
                    case targetDocument.documentElement:
                        target = targetDocument;
                        break;
                    default:
                        target = target.parentNode;
                        break;
                }

            } while (target && previousTarget !== target);

            this.activeTarget = activeTarget;
        }
    },

    /**
     *
     @private
     */
    _findActiveTarget: {
        value: function(target) {

            var foundTarget = null,
                uuidCheckedTargetMap = {};

            //TODO report if a cycle is detected?
            while (!foundTarget && target && !(target.uuid in uuidCheckedTargetMap)) {

                //TODO complain if a non-Target-alike is considered

                uuidCheckedTargetMap[target.uuid] = target;

                if (target.acceptsActiveTarget) {
                    foundTarget = target;
                } else {
                    target = target.nextTarget;
                }
            }

            return foundTarget;
        }
    },

    /**
     * Build the event target chain for the the specified Target
     * @private
     */
    _eventPathForTarget: {
        enumerable: false,
        value: function(target) {

            if (!target) {
                return [];
            }

            var targetCandidate  = target,
                application = this.application,
                eventPath = [],
                discoveredTargets = {};

            // Consider the target "discovered" for less specialized detection of cycles
            discoveredTargets[target.uuid] = target;

            do {
                if (!(targetCandidate.uuid in discoveredTargets)) {
                    eventPath.push(targetCandidate);
                    discoveredTargets[targetCandidate.uuid] = targetCandidate;
                }

                targetCandidate = targetCandidate.nextTarget;

                if (!targetCandidate || targetCandidate.uuid in discoveredTargets) {
                    targetCandidate = application;
                }

                if (targetCandidate && (targetCandidate.uuid in discoveredTargets)) {
                    targetCandidate = null;
                }
            }
            while (targetCandidate);

            return eventPath;
        }
    },

    /**
     * Build the event target chain for the the specified DOM target
     * @private
     */
    _eventPathForDomTarget: {
        enumerable: false,
        value: function(target) {

            if (!target) {
                return [];
            }

            var targetCandidate  = target,
                targetView = targetCandidate && targetCandidate.defaultView ? targetCandidate.defaultView : window,
                targetDocument = targetView.document ? targetView.document : document,
                targetApplication = this.application,
                previousBubblingTarget,
                eventPath = [];

            do {
                // Don't include the target itself as the root of the event path
                if (targetCandidate !== target) {
                    eventPath.push(targetCandidate);
                }

                previousBubblingTarget = targetCandidate;
                // use the structural DOM hierarchy until we run out of that and need
                // to give listeners on document, window, and application a chance to respond
                switch (targetCandidate) {
                    case targetApplication:
                        targetCandidate = targetCandidate.parentApplication;
                        if (targetCandidate) {
                            targetApplication = targetCandidate;
                        }
                        break;
                    case targetView:
                        targetCandidate = targetApplication;
                        break;
                    case targetDocument:
                        targetCandidate = targetView;
                        break;
                    case targetDocument.documentElement:
                        targetCandidate = targetDocument;
                        break;
                    default:
                        targetCandidate = targetCandidate.parentNode;

                        // Run out of hierarchy candidates? go up to the application
                        if (!targetCandidate) {
                            targetCandidate = targetApplication;
                        }

                        break;
                }
            }
            while (targetCandidate && previousBubblingTarget !== targetCandidate);

            return eventPath;
        }
    },
/**
  @private
*/
    _elementEventHandlerByUUID: {
        enumerable: false,
        value: {}
    },
/**
    @function
    @param {Event} anElementEventHandler
    @param {Element} anElement
    */
    registerEventHandlerForElement: {
        enumerable: false,
        value: function(anElementEventHandler, anElement) {
            // console.log("registerEventHandlerForElement",anElementEventHandler,anElementEventHandler.uuid,anElement)
            var oldEventHandler = this.eventHandlerForElement(anElement);
            // unreference unused event handlers
            if (oldEventHandler) {
                this.unregisterEventHandlerForElement(anElement);
            }
            this._elementEventHandlerByUUID[(anElement.eventHandlerUUID = anElementEventHandler.uuid)] = anElementEventHandler;
        }
    },
/**
    @function
    @param {Element} anElement
    */
    unregisterEventHandlerForElement: {
        enumerable: false,
        value: function(anElement) {
            delete this._elementEventHandlerByUUID[anElement.eventHandlerUUID];
            delete anElement.eventHandlerUUID;
        }
    },
/**
    @function
    @param {Element} anElement
    @returns this._elementEventHandlerByUUID[anElement.eventHandlerUUID]
    */
    eventHandlerForElement: {
        enumerable: false,
        value: function(anElement) {
            return this._elementEventHandlerByUUID[anElement.eventHandlerUUID];
        }
    },

    _activeTarget: {
        value: null
    },

    /**
     * The logical component that has focus within the application
     *
     * This can be used as the proximal target for dispatching in
     * situations where it logically makes sense that and event, while
     * created by some other component, should appear to originate from
     * where the user is currently focused.
     *
     * This is particularly useful for things such as keyboard shortcuts or
     * menuAction events.
     *
     * Prior to setting the activeTarget manually the desired target should
     * be checked to see if it `acceptsActiveTarget`. In the course of then
     * setting that target as the activeTarget, the current activeTarget
     * will be instructed to `surrendersActiveTarget`. If the activeTarget
     * refuses to surrender, the change is rejected.
     */
    activeTarget: {
        get: function () {
            return this._activeTarget || this.application;
        },
        set: function (value) {

            if (!value) {
                value = this.application;
            }

            if (value === this._activeTarget || (this.activeTarget && !this.activeTarget.surrendersActiveTarget(value))) {
                return;
            }

            value.willBecomeActiveTarget(this.activeTarget);
            this._activeTarget = value;
            value.didBecomeActiveTarget();
        }
    }

});

} // client-side


}})
;
//*/
montageDefine("6364dae","core/serialization/deserializer/montage-reviver",{dependencies:["core/core","mousse/deserialization/reviver","./properties-deserializer","./self-deserializer","./unit-deserializer","core/module-reference","core/promise"],factory:function(require,exports,module){var Montage = require("core/core").Montage;
var Reviver = require("mousse/deserialization/reviver").Reviver;
var PropertiesDeserializer = require("./properties-deserializer").PropertiesDeserializer;
var SelfDeserializer = require("./self-deserializer").SelfDeserializer;
var UnitDeserializer = require("./unit-deserializer").UnitDeserializer;
var ModuleReference = require("core/module-reference").ModuleReference;

var Promise = require("core/promise").Promise;

var ModuleLoader = Montage.specialize( {
    _require: {value: null},
    _objectRequires: {value: null},

    init: {
        value: function(_require, objectRequires) {
            if (typeof _require !== "function") {
                throw new Error("Function 'require' missing.");
            }

            if (typeof _require.location !== "string") {
                throw new Error("Function 'require' location is missing");
            }

            if (typeof objectRequires !== "object" &&
                typeof objectRequires !== "undefined") {
                throw new Error("Parameter 'objectRequires' should be an object.");
            }

            this._require = _require;
            this._objectRequires = objectRequires;

            return this;
        }
    },

    getExports: {
        value: function(_require, moduleId) {
            var module;

            // Transforms relative module ids into absolute module ids
            moduleId = _require.resolve(moduleId);
            module = _require.getModuleDescriptor(moduleId);

            while (module.redirect !== void 0) {
                module = _require.getModuleDescriptor(module.redirect);
            }

            if (module.mappingRedirect !== void 0) {
                return this.getExports(module.mappingRequire, module.mappingRedirect);
            }

            return module.exports;
        }
    },

    getModule: {
        value: function(moduleId, label) {
            var objectRequires = this._objectRequires,
                _require,
                module;

            if (objectRequires && label in objectRequires) {
                _require = objectRequires[label];
            } else {
                _require = this._require;
            }

            module = this.getExports(_require, moduleId);

            if (!module) {
                module = _require.async(moduleId);
            }

            return module;
        }
    }
});

var MontageReviver = exports.MontageReviver = Montage.specialize.call(Reviver, {
    moduleLoader: {value: null},

    /**
     * @param {Require} _require The require object to load modules
     * @param {Object} objectRequires A dictionary indexed by object label with
     *        the require object to use for a specific object of the
     *        serialization.
     */
    init: {
        value: function(_require, objectRequires) {
            this.moduleLoader = new ModuleLoader()
                                 .init(_require, objectRequires);

            return this;
        }
    },

    getTypeOf: {
        value: function(value) {
            if (value !== null &&
                typeof value === "object" &&
                Object.keys(value).length === 1
            ) {
                if ("#" in value) {
                    return "Element";
                } else if ("%" in value) {
                    return "Module";
                }
            }

            return Reviver.prototype.getTypeOf.call(this, value);
        }
    },

    reviveElement: {
        value: function(value, context, label) {
            var elementId = value["#"],
                element = context.getElementById(elementId);

            if (element) {
                if (label) {
                    context.setObjectLabel(element, label);
                }
                return element;
            } else {
                return Promise.reject(new Error("Element with id '" + elementId + "' was not found."));
            }
        }
    },

    reviveModule: {
        value: function(value, context, label) {
            var moduleId = value["%"],
                _require = context.getRequire();

            moduleId = _require.resolve(moduleId);
            var module = _require.getModuleDescriptor(moduleId);

            return new ModuleReference().initWithIdAndRequire(module.id, module.require);
        }
    },

    reviveCustomObject: {
        value: function(value, context, label) {
            //if ("prototype" in value || "object" in value) {
                return this.reviveMontageObject(value, context, label);
            //}
        }
    },

    reviveMontageObject: {
        value: function(value, context, label) {
            var self = this,
                module,
                locationDesc,
                locationId = value.prototype || value.object,
                objectName;

            if (locationId) {
                locationDesc = MontageReviver.parseObjectLocationId(locationId);
                module = this.moduleLoader.getModule(locationDesc.moduleId,
                    label);
                objectName = locationDesc.objectName;
            }

            if (Promise.isPromise(module)) {
                return module.then(function(exports) {
                    return self.instantiateMontageObject(value, exports, objectName, context, label);
                }, function (error) {
                    if (error.stack) {
                        console.error(error.stack);
                    }
                    throw new Error('Error deserializing "' + label +
                        '" when loading module "' + locationDesc.moduleId +
                        "' from '" + value.prototype + "'");
                });
            } else {
                return this.instantiateMontageObject(value, module, objectName, context, label);
            }
        }
    },

    instantiateMontageObject: {
        value: function(value, module, objectName, context, label) {
            var self = this,
                object,
                montageObjectDesc;

            object = this.getMontageObject(value, module, objectName, context, label);
            context.setObjectLabel(object, label);
            object.isDeserializing = true;

            montageObjectDesc = this.reviveObjectLiteral(value, context);

            if (Promise.isPromise(montageObjectDesc)) {
                return montageObjectDesc.then(function(montageObjectDesc) {
                    return self.deserializeMontageObject(montageObjectDesc, object, context, label);
                });
            } else {
                return this.deserializeMontageObject(montageObjectDesc, object,  context, label);
            }
        }
    },

    deserializeMontageObject: {
        value: function(montageObjectDesc, object, context, label) {
            var properties;

            if (typeof object.deserializeSelf === "function") {
                return this.deserializeCustomMontageObject(object, montageObjectDesc, context, label);
            } else {
                // Units are deserialized after all objects have been revived.
                // This happens at didReviveObjects.
                context.setUnitsToDeserialize(object, montageObjectDesc, MontageReviver._unitNames);
                properties = this.deserializeMontageObjectProperties(object, montageObjectDesc.properties, context);

                if (Promise.isPromise(properties)) {
                    return properties.then(function() {
                        return object;
                    });
                } else {
                    return object;
                }
            }
        }
    },

    deserializeMontageObjectProperties: {
        value: function(object, properties, context) {
            var value;

            if (typeof object.deserializeProperties === "function") {
                var propertiesDeserializer = new PropertiesDeserializer()
                    .initWithReviverAndObjects(this, context);
                value = object.deserializeProperties(propertiesDeserializer);
            } else {
                for (var key in properties) {
                    object[key] = properties[key];
                }
            }

            return value;
        }
    },

    deserializeCustomMontageObject: {
        value: function(object, objectDesc, context, label) {
            var substituteObject;

            var selfDeserializer = new SelfDeserializer()
                .initWithObjectAndObjectDescriptorAndContextAndUnitNames(object, objectDesc, context, MontageReviver._unitNames);
            substituteObject = object.deserializeSelf(selfDeserializer);

            if (Promise.isPromise(substituteObject)) {
                return substituteObject.then(function(substituteObject) {
                    context.setObjectLabel(substituteObject, label);
                    return substituteObject;
                });
            } else if (typeof substituteObject !== "undefined") {
                context.setObjectLabel(substituteObject, label);
                return substituteObject;
            } else {
                return object;
            }
        }
    },

    getMontageObject: {
        value: function(value, module, objectName, context, label) {
            var object;

            if (context.hasUserObject(label)) {

                return context.getUserObject(label);

            } else if ("prototype" in value) {

                if (!(objectName in module)) {
                    throw new Error('Error deserializing "' + label +
                        '": object named "' + objectName + '"' +
                        ' was not found in "' + value.prototype + '".' +
                        " Available objects are: " + Object.keys(module) + ".");
                }
                // TODO: For now we need this because we need to set
                // isDeserilizing before calling didCreate.
                object = Object.create(module[objectName].prototype);
                object.isDeserializing = true;
                if (typeof object.didCreate === "function") {
                    object.didCreate();
                } else if (typeof object.constructor === "function") {
                    object.constructor();
                }
                return object;
                //return module[objectName].create();

            } else if ("object" in value) {

                if (!(objectName in module)) {
                    throw new Error('Error deserializing "' + label +
                        '": object named "' + object +
                        "' was not found given '" + value.object + "'");
                }
                return module[objectName];

            } else {
                throw new Error("Error deserializing " + JSON.stringify(value) + ", might need \"prototype\" or \"object\" on label " + JSON.stringify(label));
            }
        }
    },

    didReviveObjects: {
        value: function(objects, context) {
            var self = this,
                value;

            value = this._deserializeUnits(context);

            if (Promise.isPromise(value)) {
                return value.then(function() {
                    self._invokeDeserializedFromSerialization(objects, context);
                });
            } else {
                this._invokeDeserializedFromSerialization(objects, context);
            }
        }
    },

    // TODO: can deserializeSelf make deserializedFromSerialization irrelevant?
    _invokeDeserializedFromSerialization: {
        value: function(objects, context) {
            var object;

            for (var label in objects) {
                object = objects[label];

                if (object != null) {
                    delete object.isDeserializing;
                }

                if (!context.hasUserObject(label)) {
                    // TODO: merge deserializedFromSerialization with
                    //       deserializedFromTemplate?
                    if (object && typeof object.deserializedFromSerialization === "function") {
                        object.deserializedFromSerialization(label);
                    }
                }
            }
        }
    },

    _deserializeUnits: {
        value: function(context) {
            var unitsToDeserialize = context.getUnitsToDeserialize(),
                units = MontageReviver._unitRevivers,
                unitNames,
                unitDeserializer;

            for (var i = 0, unitsDesc; unitsDesc = unitsToDeserialize[i]; i++) {
                unitNames = unitsDesc.unitNames;

                for (var j = 0, unitName; unitName = unitNames[j]; j++) {
                    if (unitName in unitsDesc.objectDesc) {
                        unitDeserializer = new UnitDeserializer()
                            .initWithContext(context);
                        units[unitName](unitDeserializer, unitsDesc.object, unitsDesc.objectDesc[unitName]);
                    }
                }
            }
        }
    }

}, {
    _unitRevivers: {value: Object.create(null)},
    _unitNames: {value: []},

    _findObjectNameRegExp: {
        value: /([^\/]+?)(\.reel)?$/
    },
    _toCamelCaseRegExp: {
        value: /(?:^|-)([^-])/g
    },
    _replaceToCamelCase: {
        value: function(_, g1) { return g1.toUpperCase() }
    },
    // Cache of location descriptors indexed by locationId
    _locationDescCache: {value: Object.create(null)},

    // Location Id is in the form of <moduleId>[<objectName>] where
    // [<objectName>] is optional. When objectName is missing it is derived
    // from the last path component of moduleId transformed into CamelCase.
    //
    // Example: "event/event-manager" has a default objectName of EventManager.
    //
    // When the last path component ends with ".reel" it is removed before
    // creating the default objectName.
    //
    // Example: "matte/ui/input-range.reel" has a default objectName of
    //          InputRange.
    //
    // @returns {moduleId, objectName}
    parseObjectLocationId: {
        value: function(locationId) {
            var locationDescCache = this._locationDescCache,
                locationDesc,
                bracketIndex,
                moduleId,
                objectName;

            if (locationId in locationDescCache) {
                locationDesc = locationDescCache[locationId];
            } else {
                bracketIndex = locationId.indexOf("[");

                if (bracketIndex > 0) {
                    moduleId = locationId.substr(0, bracketIndex);
                    objectName = locationId.slice(bracketIndex + 1, -1);
                } else {
                    moduleId = locationId;
                    this._findObjectNameRegExp.test(locationId);
                    objectName = RegExp.$1.replace(
                        this._toCamelCaseRegExp,
                        this._replaceToCamelCase
                    );
                }

                locationDesc = {
                    moduleId: moduleId,
                    objectName: objectName
                };
                locationDescCache[locationId] = locationDesc;
            }

            return locationDesc;
        }
    },

    defineUnitReviver: {
        value: function(name, funktion) {
            this._unitRevivers[name] = funktion;
            this._unitNames.push(name);
        }
    },

    getTypeOf: {
        value: function(value) {
            return this.prototype.getTypeOf.call(this, value);
        }
    }

});

if (typeof exports !== "undefined") {
    exports.MontageReviver = MontageReviver;
}

}})
;
//*/
montageDefine("6364dae","core/logger",{dependencies:["montage"],factory:function(require,exports,module){/* <copyright>
Copyright (c) 2012, Motorola Mobility LLC.
All Rights Reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

* Redistributions of source code must retain the above copyright notice,
  this list of conditions and the following disclaimer.

* Redistributions in binary form must reproduce the above copyright notice,
  this list of conditions and the following disclaimer in the documentation
  and/or other materials provided with the distribution.

* Neither the name of Motorola Mobility LLC nor the names of its
  contributors may be used to endorse or promote products derived from this
  software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
</copyright> */
/**
 * @module montage/core/logger
 * @requires montage/core/core
 */
var Montage = require("montage").Montage,
    Logger,
    loggers,
    consoleLog,
    consoleLogMontage,
    emptyLoggerFunction,
    getFunctionName,
    toTimeString,
    LoggerUI,
    localStorage;

loggers = exports.loggers = {};

/**
 * @private
 */
getFunctionName = function(montageObject) {
    var aCaller = getFunctionName.caller.caller,
        aFunctionName;
    aFunctionName = aCaller.name;
    if (aFunctionName === "") {
        aFunctionName = "anonymous";
    }
    return aFunctionName;
};

/**
 * @private
 */
toTimeString = function(date) {
    if (date.getHours) {
        var hours = date.getHours(),
        mins = date.getMinutes(),
        secs = date.getSeconds();
        return (hours.length === 1 ? "0" + hours : hours) + ":" + (mins.length === 1 ? "0" + mins : mins) + ":" + (secs.length === 1 ? "0" + secs : secs) + "." + date.getMilliseconds();
    }
};

/**
 * @private
 */
emptyLoggerFunction = function() {
};

/**
 * @private
 */
consoleLog = function() {
    console.log(arguments);
};

/**
 * @private
 */
consoleLogMontage = function() {
    var firstArgument = arguments[0],
        metadata = firstArgument._montage_metadata,
        now = new Date();
    //[].unshift.call(arguments, toTimeString(now));
    if (metadata) {
        [].shift.call(arguments);
        [].unshift.call(arguments, metadata.objectName + "." + getFunctionName(firstArgument) + "()");
        if (this.buffered) {
            this.buffer.push(arguments);
        } else {
            console.debug.apply(console, arguments);
        }
    } else {
        if (this.buffered) {
            this.buffer.push(arguments);
        } else {
            console.debug.apply(console, arguments);
        }
    }

};

/**
 * @class Logger
 * @extends Montage
 */
Logger = exports.Logger = Montage.specialize(/** @lends Logger# */ {

    constructor: {
        value: function Logger() {
            this.super();
        }
    },

   /**
    @function
    @param {String} name The name of the logger.
    @param {State} [dontStoreState=false] If true, don't store the isDebug state of the logger in localStorage .
    @returns itself
    */
    init: {
        value: function(name, onStateChange, dontStoreState) {
            this.name = name;
            this._onStateChange = onStateChange;
            this._storeState = !dontStoreState;
            if (this._storeState && localStorage) {
                var storedState = localStorage.getItem("_montage_logger_" + name);
                if (storedState) {
                    this.isDebug = storedState === "true";
                }
            }
            if (onStateChange) {
                this._onStateChange(storedState === "true");
            }
            this.isError = true;
            return this;
        }
    },

    /**
     * @type {String}
     * @default {String} null
     */
    name: {
        value: null
    },

    /**
     * @private
     * @type {Array}
     * @default {Array} []
     */
    buffer: {
        value: [],
        distinct: true
    },

    /**
     * @type {Property}
     * @default {Boolean} false
     */
    buffered: {
        value: false
    },

    /**
     * Log all the contents the logger's buffer.
     */
    flush: {
        value: function() {
            var buffer = this.buffer,
                args,
                i;
            for (i = 0; (args = buffer[i]); i++) {
                console.debug.apply(console, args);
            }
            this.buffer.length = 0;
        }
    },

    /**
     * @type {Boolean}
     */
    isDebug: {
        get: function() {
            return this.debug !== emptyLoggerFunction;
        },
        set: function(value) {
            if (value) {
                this.debug = consoleLogMontage;
            } else {
                this.debug = emptyLoggerFunction;
            }
        }

    },

    /**
     * @type {Boolean}
     */
    isError: {
        get: function() {
            return this.error !== emptyLoggerFunction;
        },
        set: function(value) {
            if (value) {
                this.error = consoleLogMontage;
            } else {
                this.error = emptyLoggerFunction;
            }
        }
    },

    /**
     * @method Logger#debug
     * @param {Function|String} object If the first argument is a function the logger with print its name
     * @param {String} [...]
     */
    debug: {
        value: emptyLoggerFunction
    },

    /**
     * @method Logger#error
     * @param {Function|String} object If the first argument is a function the logger with print its name
     * @param {String} [...]
     */
    error: {
        value: emptyLoggerFunction
    },

    /**
     * @method Logger#toTimeString
     * @description Prints the current time in format HH:MM:SS.000
     */
    toTimeString: {
        value: toTimeString
    },

    /**
     * @private
     */
    _storeState: {
        value: null
    },

    _onStateChange: {
        value: null
    }
});

/**
    @function module:montage/core/logger#logger
    */
exports.logger = function(loggerName, onStateChange, dontStoreState) {
    var logger;
    if ((logger = loggers[loggerName]) == null) {
        logger = new Logger().init(loggerName, onStateChange, dontStoreState);
        Montage.defineProperty(loggers, loggerName, {
            value: logger
        });
    }
    return logger;
};

LoggerUI = Montage.specialize( /** @lends LoggerUI# */{

    constructor: {
        value: function LoggerUI() {
            this.super();
        }
    },

    init: {
        value: function() {
            if (document.nativeAddEventListener) {
                document.nativeAddEventListener("keyup", this, false);
                document.nativeAddEventListener("keydown", this, false);
            } else {
                document.addEventListener("keyup", this, false);
                document.addEventListener("keydown", this, false);
            }
            return this;
        }
    },

    inspectorElement: {
        value: null
    },

    m_dontRemove: {
        value: null
    },

    titleHeader: {
        value: null
    },

    shown: {
        value: false
    },

    isCtrl: {
        value: false
    },

    isAlt: {
        value: false
    },

    keyup: {
        value: function(event) {
            if (event.which == 17) {
                this.isCtrl = false;
            }
            if (event.which == 18) {
                this.isAlt = false;
            }
        }
    },

    keydown: {
        value: function(event) {
            if (event.which == 17) {
                this.isCtrl = true;
            }
            if (event.which == 18) {
                this.isAlt = true;
            }
            if (event.which == 76 && this.isCtrl === true && this.isAlt === true) {
                if (this.shown) {
                    this.hideInspector();
                } else {
                    this.showInspector();
                }
                return false;
            }
        }
    },

    change: {
        value: function(event) {
            var value = event.target.checked,
                name = event.target.value,
                logger = loggers[name];
            logger.isDebug = value;
            if (logger._onStateChange) {
                logger._onStateChange(value);
            }
            if (logger._storeState && localStorage) {
                localStorage.setItem("_montage_logger_" + name, value);
            }
        }
    },

    mouseup: {
        value: function(event) {
            this.hideInspector();
        }
    },

    showInspector: {
        value: function() {
            if (! this.inspectorElement) {
                var i = 0,
                    iLogger,
                    div1,
                    h1,
                    div2,
                    label,
                    input,
                    storedValue,
                    storageKey,
                    loggerKeys,
                    style,
                    span;
                this.m_dontRemove = document.getElementsByTagName("body")[0];
                ;
                this.inspectorElement = document.createElement("div");
                this.inspectorElement.id = "_montage_logger_inspector";
                div1 = document.createElement("div");
                this.inspectorElement.appendChild(div1);
                div2 = document.createElement("div");
                div1.appendChild(div2);
                h1 = document.createElement("h1");
                h1.className = "_montage_logger_inspector-title";
                h1.textContent = "Logger Inspector";
                this.titleHeader = h1;
                div2.appendChild(h1);
                loggerKeys = Object.keys(loggers);

                for (i = 0; iLogger = loggers[loggerKeys[i]]; i++) {
                    label = document.createElement("label");
                    input = document.createElement("input");
                    span = document.createElement("span");
                    label.className = "_montage_logger_inspector-content";
                    span.textContent = iLogger.name;
                    label.appendChild(input);
                    label.appendChild(span);
                    input.value = iLogger.name;
                    input.type = "checkbox";
                    input.checked = !!iLogger.isDebug;
                    storageKey = "_montage_logger_" + iLogger.name;
                    if (iLogger._storeState && localStorage) {
                        storedValue = localStorage.getItem(storageKey);
                        if (storedValue == null) {
                            localStorage.setItem(storageKey, iLogger.isDebug);
                        }
                    }
                    div2.appendChild(label);
                }
                style = document.createElement("style");
                //YUCK!! I wish I could use a reel!!!
                var styleTest = "#_montage_logger_inspector {";
                styleTest += "    border: 1px solid rgba(15,15,15,0.4);";
                styleTest += "    position: fixed;";
                styleTest += "    right: 25px;";
                styleTest += "    top: 25px;";
                styleTest += "    -webkit-border-radius: 5px;";
                styleTest += "    color: #dddddd;";
                styleTest += '    font: 10px "Lucida Grande","Lucida Sans", sans;';
                styleTest += "    background:-webkit-gradient(linear, left top, left bottom, from(rgba(15,15,15,0.75)), to(rgba(15,15,15,0.95)) );";
                styleTest += "    -webkit-box-shadow: 0 0 15px rgba(0,0,0,.3);";
                styleTest += "    width: 250px;";
                styleTest += "}";
                styleTest += "#_montage_logger_inspector div {";
                styleTest += "    -webkit-border-radius: 5px;";
                styleTest += "    background: -webkit-gradient(radial, 100 -60, 0, 125 -50, 125, from(rgba(255,255,255,0.00)), to(rgba(0,0,0,.2)), color-stop(1, rgba(0,0,0,.2)));";
                styleTest += "}";
                styleTest += "#_montage_logger_inspector div div {";
                styleTest += "    background: -webkit-gradient(linear, left top, left bottom, from(rgba(255,255,255,0.2)), to(rgba(0,0,0,.1)), color-stop(0.33, rgba(255,255,255,.01)), color-stop(0.33, rgba(50,50,50,1)) );";
                styleTest += "    padding: 7px 10px;";
                styleTest += "    -webkit-border-radius: 3px;";
                styleTest += "    overflow-x: hidden;";
                styleTest += "}";
                styleTest += "._montage_logger_inspector-title {";
                styleTest += "    color: rgba(255,255,255,0.9);";
                styleTest += "    font-size: 13px;";
                styleTest += "    margin: 0 0 11px 0;";
                styleTest += "    padding: 0 0 0 5px;";
                styleTest += "}";
                styleTest += "._montage_logger_inspector-content {";
                styleTest += "    padding: 0 0 0 20px;";
                styleTest += "    margin: 0;";
                styleTest += "    display: block;";
                styleTest += "}";
                style.textContent = styleTest;
                document.head.appendChild(style);
            }
            this.shown = true;

            this.m_dontRemove.appendChild(this.inspectorElement);
            this.titleHeader.nativeAddEventListener("mouseup", this, false);
            this.inspectorElement.nativeAddEventListener("change", this, false);

        }
    },

    hideInspector: {
        value: function() {
            if (document.getElementById("_montage_logger_inspector")) {
                this.shown = false;
                this.m_dontRemove.removeChild(this.inspectorElement);
                this.titleHeader.nativeRemoveEventListener("mouseup", this, false);
                this.inspectorElement.nativeRemoveEventListener("change", this, false);
            }
        }
    },

    handleEvent: {
        enumerable: false,
        value: function(event) {
            if (this[event.type]) {
                this[event.type](event);
            }
        }
    }
});

var setupUI = function() {
    new LoggerUI().init();
}
if (typeof window !== "undefined") {
    // assigning to a local allows us to feature-test without typeof
    localStorage = window.localStorage;
    window.loggers = loggers;
    if (window.localStorage) {
        setupUI();
    }
}


}})
;
//*/
montageDefine("6364dae","core/shim/object",{dependencies:[],factory:function(require,exports,module){/* <copyright>
Copyright (c) 2012, Motorola Mobility LLC.
All Rights Reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

* Redistributions of source code must retain the above copyright notice,
  this list of conditions and the following disclaimer.

* Redistributions in binary form must reproduce the above copyright notice,
  this list of conditions and the following disclaimer in the documentation
  and/or other materials provided with the distribution.

* Neither the name of Motorola Mobility LLC nor the names of its
  contributors may be used to endorse or promote products derived from this
  software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
</copyright> */
/**
    Defines standardized shims for the intrinsic <code>Object</code>.
    @see {external:Object}
    @module montage/core/shim/object
*/
/**
    @external Object
*/

/**
    Creates a new object that inherits prototypically directly from a given
    prototype, optionally defining some properties.
    @function external:Object.create
    @param {Object} prototype the prototype to inherit, or
    <code>null</code> for no prototype, which makes "__proto__" the only
    special property name.
    @param {Object} descriptor a property descriptor
    @returns a new object inheriting from the given prototype and having
    the given property descriptor.
*/
if (!Object.create) {
    Object._creator = function _ObjectCreator() {
        this.__proto__ = _ObjectCreator.prototype;
    };
    Object.create = function(o, properties) {
        this._creator.prototype = o || Object.prototype;
        //Still needs to add properties....
        return new this._creator;
    };

    Object.getPrototypeOf = function(o) {
        return o.__proto__;
    };
}

// These are used in montage.js to ascertain whether we can annotate
// objects with montage metadata.

// TODO documentation
if (!Object.isSealed) {
    Object.defineProperty(Object, "isSealed", {
        value: function() {
            return false;
        },
        writable: true,
        configurable: true
    });
}

// TODO documentation
if (!Object.seal) {
    Object.defineProperty(Object, "seal", {
        value: function(object) {
            return object;
        },
        writable: true,
        configurable: true
    });
}


}})
;
//*/
montageDefine("6364dae","core/shim/array",{dependencies:[],factory:function(require,exports,module){/* <copyright>
Copyright (c) 2012, Motorola Mobility LLC.
All Rights Reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

* Redistributions of source code must retain the above copyright notice,
  this list of conditions and the following disclaimer.

* Redistributions in binary form must reproduce the above copyright notice,
  this list of conditions and the following disclaimer in the documentation
  and/or other materials provided with the distribution.

* Neither the name of Motorola Mobility LLC nor the names of its
  contributors may be used to endorse or promote products derived from this
  software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
</copyright> */
/**
    Defines standardized shims to intrinsic <code>Array</code> object.
    @see {external:Array}
    @module montage/core/shim/array
*/

/**
    @external Array
*/

/**
    Returns whether the given value is an array, regardless of which
    context it comes from.  The context may be another frame.

    <p>This is the proper idiomatic way to test whether an object is an
    array and replaces the less generally useful <code>instanceof</code>
    check (which does not work across contexts) and the strangeness that
    the <code>typeof</code> an array is <code>"object"</code>.

    @function external:Array.isArray
    @param {Any} value any value
    @returns {Boolean} whether the given value is an array
*/
if (!Array.isArray) {
    Object.defineProperty(Array, "isArray", {
        value: function(obj) {
            return Object.prototype.toString.call(obj) === "[object Array]";
        },
        writable: true,
        configurable: true
    });
}


}})
;
//*/
montageDefine("6364dae","core/shim/string",{dependencies:[],factory:function(require,exports,module){/* <copyright>
Copyright (c) 2012, Motorola Mobility LLC.
All Rights Reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

* Redistributions of source code must retain the above copyright notice,
  this list of conditions and the following disclaimer.

* Redistributions in binary form must reproduce the above copyright notice,
  this list of conditions and the following disclaimer in the documentation
  and/or other materials provided with the distribution.

* Neither the name of Motorola Mobility LLC nor the names of its
  contributors may be used to endorse or promote products derived from this
  software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
</copyright> */

/**
    Defines standardized shims for the intrinsic String object.
    @see {external:String}
    @module montage/core/shim/string
*/

/**
    @external String
*/

/**
    Returns whether this string begins with a given substring.

    @function external:String#startsWith
    @param {String} substring a potential substring of this string
    @returns {Boolean} whether this string starts with the given substring
*/
if (!String.prototype.startsWith) {
    Object.defineProperty(String.prototype, 'startsWith', {
        value: function (start) {
            return this.length >= start.length &&
                this.slice(0, start.length) === start;
        },
        writable: true,
        configurable: true
    });
}

/**
    Returns whether this string ends with a given substring.

    @function external:String#endsWith
    @param {String} substring a potential substring of this string
    @returns {Boolean} whether this string ends with the given substring
*/
if (!String.prototype.endsWith) {
    Object.defineProperty(String.prototype, 'endsWith', {
        value: function (end) {
            return this.length >= end.length &&
                this.slice(this.length - end.length, this.length) === end;
        },
        writable: true,
        configurable: true
    });
}


}})
;
//*/
montageDefine("6364dae","core/extras/object",{dependencies:["core/core","collections/weak-map","frb"],factory:function(require,exports,module){/* <copyright>
Copyright (c) 2012, Motorola Mobility LLC.
All Rights Reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

* Redistributions of source code must retain the above copyright notice,
  this list of conditions and the following disclaimer.

* Redistributions in binary form must reproduce the above copyright notice,
  this list of conditions and the following disclaimer in the documentation
  and/or other materials provided with the distribution.

* Neither the name of Motorola Mobility LLC nor the names of its
  contributors may be used to endorse or promote products derived from this
  software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
</copyright> */
/**
    Defines extensions to intrinsic <code>Object</code>.
    @see {external:Object}
    @module montage/core/extras/object
*/
var M = require("core/core"); // lazy bound because of dependency cycle

var WeakMap = require("collections/weak-map");

// string table, for strings that might be constructed multiple times
// seems to reduce allocations in a version of Firefox I once heard tell
var MODIFY = "modify";
var STRING = "string";
var FUNCTION = "function";

/**
    Returns the descriptor object for an object's property.
    @param {Object} anObject The object containing the property.
    @param {String} propertyName The name of the property.
    @returns {Object} The object's property descriptor.
    @function external:Object.getPropertyDescriptor
*/
Object.defineProperty(Object, "getPropertyDescriptor", {
    value: function(anObject, propertyName) {
        var current = anObject,
            currentDescriptor;

        do {
            currentDescriptor = Object.getOwnPropertyDescriptor(current, propertyName);
        } while (!currentDescriptor && (current = current.__proto__ || Object.getPrototypeOf(current)));

        return currentDescriptor;
    },
    writable: true,
    configurable: true
});

/**
    Returns the prototype object and property descriptor for a property belonging to an object.
    @param {Object} anObject The object to return the prototype for.
    @param {String} propertyName The name of the property.
    @returns {Object} An object containing two properties named <code>prototype</code> and <code>propertyDescriptor</code> that contain the object's prototype object and property descriptor, respectively.
    @function external:Object.getPrototypeAndDescriptorDefiningProperty
*/
Object.defineProperty(Object, "getPrototypeAndDescriptorDefiningProperty", {
    value: function(anObject, propertyName) {
        var current = anObject,
            currentDescriptor;
        if (propertyName) {

            do {
                currentDescriptor = Object.getOwnPropertyDescriptor(current, propertyName);
            } while (!currentDescriptor && (current = current.__proto__ || Object.getPrototypeOf(current)));

            return {
                prototype: current,
                propertyDescriptor: currentDescriptor
            };
        }
    },
    writable: true,
    configurable: true
});

/**
    Removes all properties owned by this object making the object suitable for
    reuse.

    @function external:Object#clear
    @returns this
*/
Object.defineProperty(Object.prototype, "clear", {
    value: function() {
        var keys = Object.keys(this),
            i = keys.length;

        while (i) {
            i--;
            delete this[keys[i]];
        }

        return this;
    },
    writable: true,
    configurable: true
});

Object.defineProperty(Object, "defineBinding", {
    value: function (target, targetPath, descriptor) {
        var depth = Error.stackTraceLimit;
        Error.stackTraceLimit = 2;
        console.warn(
            "Object.defineBinding deprecated.  " +
            "See the comment below this warning for migration instructions.",
            new Error("deprecated").stack
        );
        Error.stackTraceLimit = depth;

        //
        // Migration instructions:
        //
        // Replace Object.defineBinding with
        // import Bindings from "montage/core/bindings"
        // Bindings.defineBinding(target, targetPath, descriptor);
        // - Use "<-", "<->", and "source" in place of
        //   "boundObjectPropertyPath", "oneway", and "boundObject".
        // - Use "convert" or "converter.convert" in place of
        //   "boundValueMutator".
        //

        var Bindings = require("frb");

        descriptor.source = descriptor.boundObject;
        if (descriptor.oneway) {
            descriptor["<-"] = descriptor.boundObjectPropertyPath;
        } else {
            descriptor["<->"] = descriptor.boundObjectPropertyPath;
        }

        if (descriptor.boundValueMutator) {
            descriptor.convert = descriptor.boundValueMutator;
        }

        Bindings.defineBinding(target, targetPath, descriptor);
    }
});

Object.defineProperty(Object, "deleteBinding", {
    value: function (target, targetPath) {
        var Bindings = require("frb");
        Bindings.cancelBinding(target, targetPath);
    }
});


}})
;
//*/
montageDefine("6364dae","core/extras/date",{dependencies:[],factory:function(require,exports,module){/**
*  Defines extensions to intrinsic <code>Date</code> object.
*  @module montage/core/extras/date
*  @see {external:Date}
*/

/**
    @external
*/

/**
    Creates a copy of a date.

    @function external:Date#clone
    @returns {Date} a new date
*/
Object.defineProperty(Date.prototype, "clone", {
    value: function () {
        return new Date(this);
    },
    writable: true,
    configurable: true
});


}})
;
//*/
montageDefine("6364dae","core/extras/element",{dependencies:[],factory:function(require,exports,module){/* <copyright>
Copyright (c) 2012, António Afonso. All Rights Reserved.
3-Clause BSD License
http://opensource.org/licenses/BSD-3-Clause
</copyright> */

if (Element && !Element.isElement) {
    Object.defineProperty(Element, "isElement", {
        value: function (obj) {
            return !!(obj && 1 === obj.nodeType);
        },
        writable: true,
        configurable: true
    });
}
}})
;
//*/
montageDefine("6364dae","core/extras/function",{dependencies:["./object"],factory:function(require,exports,module){/* <copyright>
Copyright (c) 2012, Motorola Mobility LLC.
All Rights Reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

* Redistributions of source code must retain the above copyright notice,
  this list of conditions and the following disclaimer.

* Redistributions in binary form must reproduce the above copyright notice,
  this list of conditions and the following disclaimer in the documentation
  and/or other materials provided with the distribution.

* Neither the name of Motorola Mobility LLC nor the names of its
  contributors may be used to endorse or promote products derived from this
  software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
</copyright> */
/**
    Defines extensions to intrinsic <code>Function</code> object.
    @see {external:Function}
    @module montage/core/extras/function
*/

/**
    @external Function
*/

require("./object");

/**
    A utility to reduce unnecessary allocations of <code>function (x) {return
    x}</code> in its many colorful but ultimately wasteful parameter name
    variations.

    @function external:Function.identity
    @param {Any} any value
    @returns {Any} that value
*/
Object.defineProperty(Function, "identity", {
    value: function (x) {
        return x;
    },
    writable: true,
    configurable: true
});

/**
    A utility to reduce unnecessary allocations of <code>function () {}</code>
    in its many colorful variations.  It does nothing and thus makes a suitable
    default in some circumstances.

    @function external:Function.noop
*/
Object.defineProperty(Function, "noop", {
    value: function () {
    },
    writable: true,
    configurable: true
});

/**
    A utility for creating a comparator function for a particular aspect of a
    figurative class of objects.

    @function external:Function.by
    @param {Function} relation A function that accepts a value and returns a
    corresponding value to use as a representative when sorting that object.
    @param {Function} compare an alternate comparator for comparing the
    represented values.  The default is <code>Object.compare</code>, which
    does a deep, type-sensitive, polymorphic comparison.
    @returns {Function} a comparator that has been annotated with
    <code>by</code> and <code>compare</code> properties so
    <code>Array#sorted</code> can perform a transform that reduces the need to
    call <code>by</code> on each sorted object to just once.
*/
Object.defineProperty(Function, "by", {
    value: function (by, compare) {
        compare = compare || Object.compare;
        by = by || Function.identity;
        var compareBy = function (a, b) {
            return compare(by(a), by(b));
        };
        compareBy.compare = compare;
        compareBy.by = by;
        return compareBy;
    },
    writable: true,
    configurable: true
});


}})
;
//*/
montageDefine("6364dae","core/extras/regexp",{dependencies:[],factory:function(require,exports,module){/* <copyright>
Copyright (c) 2012, António Afonso. All Rights Reserved.
3-Clause BSD License
http://opensource.org/licenses/BSD-3-Clause
</copyright> */
/**
    @external RegExp
*/
/**
    Returns whether the given value is a regexp, regardless of which
    context it comes from.  The context may be another frame.

    <p>This is the proper idiomatic way to test whether an object is a
    regexp and replaces the less generally useful <code>instanceof</code>
    check (which does not work across contexts) and the strangeness that
    the <code>typeof</code> a regexp is <code>"object"</code>.

    @function external:RegExp.isRegExp
    @param {Any} value any value
    @returns {Boolean} whether the given value is a regexp
*/
if (!RegExp.isRegExp) {
    var toString = Object.prototype.toString;
    Object.defineProperty(RegExp, "isRegExp", {
        value: function(obj) {
            return toString.call(obj) === "[object RegExp]";
        },
        writable: true,
        configurable: true
    });
}


}})
;
//*/
montageDefine("6364dae","core/extras/string",{dependencies:[],factory:function(require,exports,module){/* <copyright>
Copyright (c) 2012, Motorola Mobility LLC.
All Rights Reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

* Redistributions of source code must retain the above copyright notice,
  this list of conditions and the following disclaimer.

* Redistributions in binary form must reproduce the above copyright notice,
  this list of conditions and the following disclaimer in the documentation
  and/or other materials provided with the distribution.

* Neither the name of Motorola Mobility LLC nor the names of its
  contributors may be used to endorse or promote products derived from this
  software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
</copyright> */

/**
    Defines extensions to the intrinsic <code>String</code> object.
    @see {external:String}
	@module montage/core/extras/string
*/

/**
    Returns true if the two strings are equal, otherwise returns false.

    @function external:String#equals
    @param {Object} that The object to compare to the string.
    @returns {Boolean} Returns true if the string is equal to
    <code>that</code>.
*/
Object.defineProperty(String.prototype, "equals", {
    value: function (that) {
        return this.valueOf() === Object.getValueOf(that);
    },
    writable: true,
    configurable: true
});

/**
    Determines whether a substring exists within this string.

    @function external:String#contains
    @param {String} content
    @returns {Boolean} whether this string contains the given content
*/
Object.defineProperty(String.prototype, "contains", {
    value: function (substring) {
        return this.indexOf(substring) !== -1;
    },
    writable: true,
    configurable: true
});

/**
    Capitalizes the first letter in the string.

    @function external:String#toCapitalized
    @returns {String} The original string with its first letter capitalized.
    @example
    var fname = "abe";
    var lname = "lincoln";
    var name = fname.toCapitalized() + " " + lname.toCapitalized();
    // name == "Abe Lincoln"
*/
Object.defineProperty(String.prototype, "toCapitalized", {
    value: function() {
        return this.charAt(0).toUpperCase() + this.slice(1);
    },
    writable: true,
    configurable: true
});

// TODO(kriskowal) discover where/whether we are currently using this
/**
    Does nothing, but exists so that strings may be used generically as
    potential event sources.

    @function external:String#addEventListener
    @param {Listener} type The type of event listener.
    @param {Listener} listener The event listener.
    @param {Function} useCapture The capturing function.
*/
Object.defineProperty(String.prototype, "addEventListener", {
    value: Function.noop, // on purpose
    writable: true,
    configurable: true
});


}})
;
//*/
montageDefine("6364dae","core/uuid",{dependencies:[],factory:function(require,exports,module){/* <copyright>
Copyright (c) 2012, Motorola Mobility LLC.
All Rights Reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

* Redistributions of source code must retain the above copyright notice,
  this list of conditions and the following disclaimer.

* Redistributions in binary form must reproduce the above copyright notice,
  this list of conditions and the following disclaimer in the documentation
  and/or other materials provided with the distribution.

* Neither the name of Motorola Mobility LLC nor the names of its
  contributors may be used to endorse or promote products derived from this
  software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
</copyright> */
/* <notice>
 Code from node-uuid: https://github.com/broofa/node-uuid/raw/master/uuid.js<br/>
 MIT license https://github.com/broofa/node-uuid/blob/master/LICENSE.md<br/>
 </notice> */

/**
	@module montage/core/uuid
    @requires montage/core/core
*/
/**
    @class Uuid
    @extends Montage
 */
var CHARS = '0123456789ABCDEF'.split(''),
    FORMAT = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.split(''),
    Uuid = exports.Uuid = Object.create(Object.prototype, /** @lends Uuid# */ {
        /**
            Returns a univerally unique ID (UUID).
            @function
            @param {Property} argument
            @returns {String} The UUID.
        */
        generate: {
            enumerable: false,
            value: generate
        }
    });

exports.generate = generate;
function generate(argument) {
    var c = CHARS, id = FORMAT, r;

    id[0] = c[(r = Math.random() * 0x100000000) & 0xf];
    id[1] = c[(r >>>= 4) & 0xf];
    id[2] = c[(r >>>= 4) & 0xf];
    id[3] = c[(r >>>= 4) & 0xf];
    id[4] = c[(r >>>= 4) & 0xf];
    id[5] = c[(r >>>= 4) & 0xf];
    id[6] = c[(r >>>= 4) & 0xf];
    id[7] = c[(r >>>= 4) & 0xf];

    id[9] = c[(r = Math.random() * 0x100000000) & 0xf];
    id[10] = c[(r >>>= 4) & 0xf];
    id[11] = c[(r >>>= 4) & 0xf];
    id[12] = c[(r >>>= 4) & 0xf];
    id[15] = c[(r >>>= 4) & 0xf];
    id[16] = c[(r >>>= 4) & 0xf];
    id[17] = c[(r >>>= 4) & 0xf];

    id[19] = c[(r = Math.random() * 0x100000000) & 0x3 | 0x8];
    id[20] = c[(r >>>= 4) & 0xf];
    id[21] = c[(r >>>= 4) & 0xf];
    id[22] = c[(r >>>= 4) & 0xf];
    id[24] = c[(r >>>= 4) & 0xf];
    id[25] = c[(r >>>= 4) & 0xf];
    id[26] = c[(r >>>= 4) & 0xf];
    id[27] = c[(r >>>= 4) & 0xf];

    id[28] = c[(r = Math.random() * 0x100000000) & 0xf];
    id[29] = c[(r >>>= 4) & 0xf];
    id[30] = c[(r >>>= 4) & 0xf];
    id[31] = c[(r >>>= 4) & 0xf];
    id[32] = c[(r >>>= 4) & 0xf];
    id[33] = c[(r >>>= 4) & 0xf];
    id[34] = c[(r >>>= 4) & 0xf];
    id[35] = c[(r >>>= 4) & 0xf];

    return id.join('');
}

}})
;
//*/
montageDefine("6364dae","core/bindings",{dependencies:["core/core","frb"],factory:function(require,exports,module){
var Montage = require("core/core").Montage;

var Bindings = exports.Bindings = require("frb");

var bindingPropertyDescriptors = {

    defineBinding: {
        value: function (targetPath, descriptor, commonDescriptor) {
            return Bindings.defineBinding(this, targetPath, descriptor, commonDescriptor);
        }
    },

    defineBindings: {
        value: function (descriptors, commonDescriptor) {
            return Bindings.defineBindings(this, descriptors, commonDescriptor);
        }
    },

    cancelBinding: {
        value: function (targetPath) {
            return Bindings.cancelBinding(this, targetPath);
        }
    },

    cancelBindings: {
        value: function () {
            return Bindings.cancelBindings(this);
        }
    },

    getBinding: {
        value: function (targetPath) {
            return Bindings.getBinding(this, targetPath);
        }
    },

    getBindings: {
        value: function () {
            return Bindings.getBindings(this);
        }
    }

};
Montage.defineProperties(Montage, bindingPropertyDescriptors);
Montage.defineProperties(Montage.prototype, bindingPropertyDescriptors);

}})
;
//*/
montageDefine("6364dae","core/paths",{dependencies:["core/core","collections/weak-map","collections/map","frb/parse","frb/evaluate","frb/assign","frb/observe","frb/bind","frb/compile-observer","frb/scope","frb/observers"],factory:function(require,exports,module){
var Montage = require("core/core").Montage;
var WeakMap = require("collections/weak-map");
var Map = require("collections/map");

var parse = require("frb/parse");
var evaluate = require("frb/evaluate");
var assign = require("frb/assign");
var observe = require("frb/observe");
var bind = require("frb/bind");
var compileObserver = require("frb/compile-observer");
var Scope = require("frb/scope");
var Observers = require("frb/observers");
var autoCancelPrevious = Observers.autoCancelPrevious;

var pathChangeDescriptors = new WeakMap();

var pathPropertyDescriptors = {

    getPath: {
        value: function (path, parameters, document, components) {
            return evaluate(
                path,
                this,
                parameters || this,
                document,
                components
            );
        }
    },

    setPath: {
        value: function (path, value, parameters, document, components) {
            return assign(
                this,
                path,
                value,
                parameters || this,
                document,
                components
            );
        }
    },

    observePath: {
        value: function (path, emit) {
            var syntax = parse(path);
            var observe = compileObserver(syntax);
            return observe(autoCancelPrevious(emit), new Scope(this));
        }
    },

    addRangeAtPathChangeListener: {
        value: function (path, handler, methodName) {
            methodName = methodName || "handleRangeChange";
            function dispatch(plus, minus, index) {
                if (handler[methodName]) {
                    handler[methodName](plus, minus, index);
                } else if (handler.call) {
                    handler.call(null, plus, minus, index);
                } else {
                    throw new Error("Can't dispatch range change to " + handler);
                }
            }
            var minus = [];
            return this.addPathChangeListener(path, function (plus) {
                plus = plus || [];
                // Give copies to avoid modification by the listener.
                dispatch(plus.slice(), minus.slice(), 0);
                minus = plus;
                return plus.addRangeChangeListener(dispatch);
            });
        }
    },

    // TODO removeRangeAtPathChangeListener
    // TODO add/removeMapAtPathChangeListener

    getPathChangeDescriptors: {
        value: function () {
            if (!pathChangeDescriptors.has(this)) {
                pathChangeDescriptors.set(this, {});
            }
            return pathChangeDescriptors.get(this);
        }
    },

    getPathChangeDescriptor: {
        value: function (path, handler, beforeChange) {
            var descriptors = Montage.getPathChangeDescriptors.call(this);
            if (!Object.owns(descriptors, path)) {
                descriptors[path] = {
                    willChangeListeners: new Map(), // handler to descriptor
                    changeListeners: new Map()
                };
            }

            descriptors = descriptors[path];
            if (beforeChange) {
                descriptors = descriptors.willChangeListeners;
            } else {
                descriptors = descriptors.changeListeners;
            }

            if (!descriptors.has(handler)) {
                descriptors.set(handler, {
                    path: path,
                    handler: handler,
                    beforeChange: beforeChange,
                    cancel: Function.noop
                })
            }

            return descriptors.get(handler);
        }
    },

    addPathChangeListener: {
        value: function (path, handler, methodName, beforeChange) {
            var self = this;

            handler = handler || Function.noop;

            var descriptor = Montage.getPathChangeDescriptor.call(this, path, handler, beforeChange);
            descriptor.cancel();

            var syntax = parse(path);

            var initialValue;
            var initialized;
            var emit;
            if (handler === Function.noop) {
                emit = function (value) {
                    if (initialized) {
                        throw new Error("Path change handler needs a handler because it emits new values when the source changes: " + JSON.stringify(path));
                    } else {
                        initialized = true;
                        initialValue = value;
                    }
                };
            } else if (methodName) {
                emit = function (value) {
                    return handler[methodName].call(handler, value, path, self);
                };
            } else if (handler.handlePathChange) {
                emit = function (value) {
                    return handler.handlePathChange.call(handler, value, path, self);
                };
            } else if (typeof handler === "function") {
                emit = function (value) {
                    return handler.call(self, value, path, self);
                };
            } else {
                throw new Error("Can't recognize handler type: " + handler + ". Must be function or delegate implementing handlePathChange.");
            }

            var observe = compileObserver(syntax);
            var cancel = observe(autoCancelPrevious(emit), new Scope(this));

            descriptor.cancel = cancel;

            if (initialized) {
                return initialValue;
            } else {
                return cancel;
            }
        }
    },

    removePathChangeListener: {
        value: function (path, handler, beforeChange) {
            handler = handler || Function.noop;
            var descriptorsForObject = Montage.getPathChangeDescriptors.call(this);
            var phase = beforeChange ? "willChangeListeners" : "changeListeners";

            if (!Object.owns(descriptorsForObject, path)) {
                throw new Error("Can't find " + phase + " for " + JSON.stringify(path));
            }
            var descriptorsForPath = descriptorsForObject[path];
            var descriptorsForPhase = descriptorsForPath[phase];
            if (!descriptorsForPhase.has(handler)) {
                throw new Error("Can't find " + phase + " for " + JSON.stringify(path));
            }
            var descriptor = descriptorsForPhase.get(handler);
            descriptor.cancel();
            descriptorsForPhase["delete"](handler);
            if (
                descriptorsForPath.willChangeListeners.length === 0 &&
                descriptorsForPath.changeListeners.length === 0
            ) {
                delete descriptorsForObject[path];
            }
            // if there are no other handlers
            for (var name in descriptorsForObject) {
                return;
            }
            pathChangeDescriptors["delete"](this);
        }
    },

    addBeforePathChangeListener: {
        value: function (path, handler, methodName) {
            return Montage.addPathChangeListener.call(this, path, handler, methodName, true);
        }
    },

    removeBeforePathChangeListener: {
        value: function (path, handler, methodName) {
            return Montage.removePathChangeListener.call(this, path, handler, true);
        }
    }

};
Montage.defineProperties(Montage, pathPropertyDescriptors);
Montage.defineProperties(Montage.prototype, pathPropertyDescriptors);

}})
;
//*/
montageDefine("6364dae","core/serialization/bindings",{dependencies:["frb","frb/parse","frb/stringify","frb/expand","frb/scope","core/serialization"],factory:function(require,exports,module){
var Bindings = require("frb"),
    parse = require("frb/parse"),
    stringify = require("frb/stringify"),
    expand = require("frb/expand"),
    Scope = require("frb/scope"),
    Serializer = require("core/serialization").Serializer,
    Deserializer = require("core/serialization").Deserializer;

Serializer.defineSerializationUnit("bindings", function (serializer, object) {
    var inputs = Bindings.getBindings(object);
    var outputs = {};
    var hasBindings;

    for (var targetPath in inputs) {
        var input = inputs[targetPath];

        var output = {};

        if (("serializable" in input) && !input.serializable)
            continue;

        var sourcePath = input.sourcePath;
        var syntax = input.sourceSyntax;
        if (input.source !== object) {
            var label = serializer.getObjectLabel(input.source);
            var scope = new Scope({
                type: "component",
                label: label
            });
            scope.components = serializer;
            syntax = expand(syntax, scope);
        }

        var scope = new Scope();
        scope.components = serializer;
        sourcePath = stringify(syntax, scope);

        if (input.twoWay) {
            output["<->"] = sourcePath;
        } else {
            output["<-"] = sourcePath;
        }

        if (input.converter) {
            output.converter = input.converter;
        } else {
            output.convert = input.convert;
            output.revert = input.revert;
        }

        if (input.trace) {
            output.trace = true;
        }

        outputs[targetPath] = output;
        hasBindings = true;
    }

    return hasBindings ? outputs : undefined;
});

Deserializer.defineDeserializationUnit("bindings", function (deserializer, object, bindings) {

    // normalize old and busted bindings
    for (var targetPath in bindings) {
        var descriptor = bindings[targetPath];

        if (typeof descriptor !== "object") {
            throw new Error("Binding descriptor must be an object, not " + typeof descriptor);
            // TODO isolate the source document and produce a more useful error
        }

        if ("boundObject" in descriptor) {
            descriptor.source = deserializer.getObjectByLabel(descriptor.boundObject);
            if (descriptor.oneway) {
                descriptor["<-"] = descriptor.boundPropertyPath;
            } else {
                descriptor["<->"] = descriptor.boundPropertyPath;
            }
            delete descriptor.boundObject;
            delete descriptor.boundObjectPropertyPath;
            delete descriptor.oneway;
        } else {
            if (descriptor["<<->"]) {
                console.warn("WARNING: <<-> in bindings is deprectated, use <-> only, please update now.");
                descriptor["<->"] = descriptor["<<->"];
                delete descriptor["<<->"];
            }
        }
    }

    Bindings.defineBindings(object, bindings, {
        components: deserializer
    });

});


}})
;
//*/
montageDefine("6364dae","core/serialization/deserializer/properties-deserializer",{dependencies:["core/core","core/promise"],factory:function(require,exports,module){var Montage = require("core/core").Montage;
var Promise = require("core/promise").Promise;

var PropertiesDeserializer = Montage.specialize( {
    _object: {value: null},
    _objectDescriptor: {value: null},
    _context: {value: null},

    initWithObjectAndObjectDescriptorAndContext: {
        value: function(object, objectDescriptor, context) {
            this._object = object;
            this._objectDescriptor = objectDescriptor;
            this._context = context;

            return this;
        }
    },

    get: {
        value: function(name) {
            if (this._objectDescriptor.properties) {
                return this._objectDescriptor.properties[name];
            }
        }
    },

    deserializeProperties: {
        value: function(propertyNames) {
            var object = this._object,
                properties = this._objectDescriptor.properties,
                propertyName;

            if (properties) {
                if (!propertyNames) {
                    propertyNames = Montage.getSerializablePropertyNames(object);
                }

                for (var i = 0, ii = propertyNames.length; i < ii; i++) {
                    propertyName = propertyNames[i];
                    object[propertyName] = properties[propertyName];
                }
            }
        }
    },

    getObjectByLabel: {
        value: function(label) {
            this._context.getObject(label);
        }
    }
});

exports.PropertiesDeserializer = PropertiesDeserializer;

}})
;
//*/
montageDefine("6364dae","core/serialization/deserializer/self-deserializer",{dependencies:["core/core","core/promise"],factory:function(require,exports,module){var Montage = require("core/core").Montage;
var Promise = require("core/promise").Promise;

var SelfDeserializer = Montage.specialize( {
    _object: {value: null},
    _objectDescriptor: {value: null},
    _context: {value: null},
    _unitNames: {value: null},
    _objectUnitNames: {value: null},

    create: {
        value: function() {
            return new this();
        }
    },

    initWithObjectAndObjectDescriptorAndContextAndUnitNames: {
        value: function(object, objectDescriptor, context, unitNames) {
            this._object = object;
            this._objectDescriptor = objectDescriptor;
            this._context = context;
            this._unitNames = unitNames;

            return this;
        }
    },

    getProperty: {
        value: function(name) {
            if (this._objectDescriptor.properties) {
                return this._objectDescriptor.properties[name];
            }
        }
    },

    getType: {
        value: function() {
            if ("prototype" in this._objectDescriptor) {
                return "prototype";
            } else if ("object" in this._objectDescriptor) {
                return "object";
            }
        }
    },

    getTypeValue: {
        value: function() {
            return this._objectDescriptor.prototype || this._objectDescriptor.object;
        }
    },

    getObjectByLabel: {
        value: function(label) {
            this._context.getObject(label);
        }
    },

    deserializeProperties: {
        value: function(propertyNames) {
            var object = this._object,
                properties = this._objectDescriptor.properties,
                propertyName;

            if (properties) {
                if (!propertyNames) {
                    propertyNames = Montage.getSerializablePropertyNames(object);
                }

                for (var i = 0, ii = propertyNames.length; i < ii; i++) {
                    propertyName = propertyNames[i];
                    object[propertyName] = properties[propertyName];
                }
            }
        }
    },

    deserializeUnit: {
        value: function(name) {
            var objectUnitNames = this._objectUnitNames;

            if (!objectUnitNames) {
                objectUnitNames = this._objectUnitNames = [name];
                this._context.setUnitsToDeserialize(this._object, this._objectDescriptor, objectUnitNames);
            } else if (objectUnitNames.indexOf(name) === -1) {
                objectUnitNames.push(name);
            }
        }
    },

    deserializeUnits: {
        value: function() {
            var objectUnitNames = this._objectUnitNames;

            if (!objectUnitNames) {
                objectUnitNames = this._objectUnitNames = this._unitNames;
                this._context.setUnitsToDeserialize(this._object, this._objectDescriptor, objectUnitNames);
            } else {
                for (var i = 0, name; name = objectUnitNames[i]; i++) {
                    if (objectUnitNames.indexOf(name) === -1) {
                        objectUnitNames.push(name);
                    }
                }
            }
        }
    }
});

exports.SelfDeserializer = SelfDeserializer;

}})
;
//*/
montageDefine("6364dae","core/serialization/deserializer/unit-deserializer",{dependencies:["core/core","core/promise"],factory:function(require,exports,module){var Montage = require("core/core").Montage;
var Promise = require("core/promise").Promise;

var UnitDeserializer = Montage.specialize( {
    _context: {value: null},

    create: {
        value: function() {
            return new this();
        }
    },

    initWithContext: {
        value: function(context) {
            this._context = context;

            return this;
        }
    },

    getObjectByLabel: {
        value: function(label) {
            var object = this._context.getObject(label);

            if (!Promise.isPromise(object)) {
                return object;
            }
        }
    }
});

exports.UnitDeserializer = UnitDeserializer;

}})
;
//*/
montageDefine("6364dae","core/module-reference",{dependencies:["montage"],factory:function(require,exports,module){var Montage = require("montage").Montage;

exports.ModuleReference = Montage.specialize({
    constructor: {
        value: function ModuleReference() {
            return this.super();
        }
    },

    initWithIdAndRequire: {
        value: function (id, require) {
            if (!id || ! require) {
                throw new Error("Module ID and require required");
            }
            this.id = id;
            this.require = require;

            return this;
        }
    },

    /**
     * The absolute id of the module within the `require` package
     * @type {string}
     */
    id: {
        value: null
    },

    /**
     * The require of a package.
     * @type {function}
     */
    require: {
        value: null
    },

    _exports: {
        value: null
    },
    /**
     * A promise for the exports of the module. The exports are loaded lazily
     * when the property is accessed.
     * @type {Promise.<Object>} The exports of the module.
     */
    exports: {
        get: function () {
            if (this._exports) {
                return this._exports;
            }
            return this._exports = this.require.async(this.id);
        }
    },

    /**
     * Resolves this module reference so that it can be required from
     * otherRequire.
     * @function
     * @param {function} otherRequire Require from another package that has
     * the package of this module as a dependency.
     * @returns {string} The module id to pass to otherRequire that results
     * in this module.
     * @throws {Error} If there is no mapping from this require inside otherRequire.
     * @example
     * var ref = new ModuleReference().initWithIdAndRequire("core/uuid", montageRequire);
     * ref.resolve(applicationRequire); // => "montage/core/uuid"
     * @example
     * var ref = new ModuleReference().initWithIdAndRequire("ui/main.reel", applicationRequire);
     * ref.resolve(montageRequire); // => Error
     * // because there is no module id such that montageRequire(id) can
     * // return the module from inside your application
     */
    resolve: {
        value: function (otherRequire) {
            return otherRequire.identify(this.id, this.require);
        }
    },

    // Used for cross-frame detection, similar to Array.isArray, but just
    // a property as there's no need for complex logic.
    isModuleReference: {
        writable: false,
        configurable: false,
        value: true
    }
});

}})
;
//*/
montageDefine("6364dae","core/event/mutable-event",{dependencies:["montage"],factory:function(require,exports,module){/* <copyright>
Copyright (c) 2012, Motorola Mobility LLC.
All Rights Reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

* Redistributions of source code must retain the above copyright notice,
  this list of conditions and the following disclaimer.

* Redistributions in binary form must reproduce the above copyright notice,
  this list of conditions and the following disclaimer in the documentation
  and/or other materials provided with the distribution.

* Neither the name of Motorola Mobility LLC nor the names of its
  contributors may be used to endorse or promote products derived from this
  software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
</copyright> */
/**
 @module montage/core/event/mutable-event
 @requires montage
 */
var Montage = require("montage").Montage;

// XXX Does not presently function server-side
if (typeof window !== "undefined") {

var _eventConstructorsByType = {};
var nullDescriptor = {value: null};

var wrapProperty = function(obj, key) {

    var storageKey = "_" + key;

    Montage.defineProperty(obj, storageKey, {value: undefined});

    Montage.defineProperty(obj, key, {
        get:(function(key, storageKey) {
            return function() {
                return this.hasOwnProperty(storageKey) ? this[storageKey] : (this._event ? this._event[key] : undefined);
            };
        })(key, storageKey),

        set: (function(storageKey) {
            return function(value) {
                this[storageKey] = value;
            };
        })(storageKey)
    });
};
/**
    @class MutableEvent
*/
var MutableEvent = exports.MutableEvent = Montage.specialize(/** @lends MutableEvent# */ {

/**
  @private
*/
    _initPrototypeWithEvent: {
        value: function(event) {
            var key;

            for (key in event) {

                //  Don't overwrite keys we have installed
                if (this[key]) {
                    continue;
                }

                // Skip methods, the ones we care about have been wrapped already
                // TODO actually wrap all known functions generically
                //if (typeof this[key] === "function") {
                // continue;
                //}

                // TODO ok, maybe it would be quicker to not make this a function, but I really hate duplicated code
                wrapProperty(this, key);
            }

            wrapProperty(this, "replayed");

            return this;
        }
    },
/**
  @private
*/
    _initWithEvent: {
        value: function(event) {
            this._event = event;
            return this;
        }
    },
/**
    @function
    */
    preventDefault: {
        value: function() {
            this._event.preventDefault();
        }
    },
/**
    @function
    */
    stopImmediatePropagation: {
        value: function() {
            this._event.stopImmediatePropagation();
            // TODO only if the event is cancellable?
            this.propagationStopped = true;
            this.immediatePropagationStopped = true;
        }
    },
/**
            @type {Property}
        @default {Boolean} false
    */
    propagationStopped: {
        value: false
    },
/**
            @type {Property}
        @default {Boolean} false
    */
    immediatePropagationStopped: {
        value: false
    },
/**
            @type {Property}
        @default {Boolean} true
    */
    mutable: {
        value: true
    },
/**
            @type {Property}
        @default {Element} null
    */
    target: {
        value: null
    },
/**
    @function
    */
    stopPropagation: {
        value: function() {
            this._event.stopPropagation();
            // TODO only if the event is cancellable?
            this.propagationStopped = true;
        }
    },
/**
    @function
    */
    stop: {
        value: function() {
            this.preventDefault();
            this.stopPropagation();
        }
    }
}, {
    /**
     @function
     @param {Event} event The original event.
     @returns newEvent
     */
    fromEvent: {
        value: function(event) {
            var type = event.type,
                constructor = _eventConstructorsByType[type],
                newEvent;
            if (!constructor) {
                constructor = function MutableEvent() {
                };
                constructor.prototype = new exports.MutableEvent()._initPrototypeWithEvent(event);
                _eventConstructorsByType[type] = constructor;
            }
            newEvent = new constructor();
            newEvent._initWithEvent(event);
            return newEvent;
        }
    },

    //    Same arguments as initEvent & initCustomEvent

    /**
    @function
    @param {Event} type TODO
    @param {Event} canBubbleArg TODO
    @param {Event} cancelableArg TODO
    @param {Event} data TODO
    @returns this.fromEvent(anEvent)
    */
    fromType: {
        value: function(type, canBubbleArg, cancelableArg, detail) {
            var anEvent = document.createEvent("CustomEvent");
            anEvent.initCustomEvent(type, canBubbleArg, cancelableArg, detail);
            return this.fromEvent(anEvent);
        }
    }

});

} // client-side

}})
;
//*/
montageDefine("6364dae","core/serialization",{dependencies:["./serialization/serializer/montage-serializer","./serialization/deserializer/montage-deserializer"],factory:function(require,exports,module){var Serializer = require("./serialization/serializer/montage-serializer").MontageSerializer,
    serialize = require("./serialization/serializer/montage-serializer").serialize,
    Deserializer = require("./serialization/deserializer/montage-deserializer").MontageDeserializer,
    deserialize = require("./serialization/deserializer/montage-deserializer").deserialize;

exports.Serializer = Serializer;
exports.serialize = serialize;

exports.Deserializer = Deserializer;
exports.deserialize = deserialize;
}})
;
//*/
montageDefine("6364dae","core/serialization/serializer/montage-serializer",{dependencies:["core/core","mousse/serialization/malker","mousse/serialization/serializer","./montage-builder","./montage-labeler","./montage-visitor","core/logger"],factory:function(require,exports,module){var Montage = require("core/core").Montage;
var Malker = require("mousse/serialization/malker").Malker;
var Serializer = require("mousse/serialization/serializer").Serializer;
var MontageBuilder = require("./montage-builder").MontageBuilder;
var MontageLabeler = require("./montage-labeler").MontageLabeler;
var MontageVisitor = require("./montage-visitor").MontageVisitor;

var logger = require("core/logger").logger("montage-serializer");

var MontageSerializer = Montage.specialize.call(Serializer, {
    _require: {value: null},
    _visitor: {value: null},
    _units: {value: Object.create(null)},

    _findObjectNameRegExp: {value: /([^\/]+?)(\.reel)?$/},
    _toCamelCaseRegExp: {value: /(?:^|-)([^-])/g},
    _replaceToCamelCase: {value: function(_, g1){return g1.toUpperCase()}},

    constructor: {
        value: function MontageSerializer() {}
    },

    initWithRequire: {
        value: function(_require) {
            this._require = _require;

            this._builder = new MontageBuilder();
            this._labeler = new MontageLabeler();
            this._visitor = new MontageVisitor()
                .initWithBuilderAndLabelerAndRequireAndUnits(
                    this._builder,
                    this._labeler,
                    this._require,
                    this._units);

            this._malker = new Malker(this._visitor);

            return this;
        }
    },

    getExternalObjects: {
        value: function() {
            return this._visitor.getExternalObjects();
        }
    },

    getExternalElements: {
        value: function() {
            return this._visitor.getExternalElements();
        }
    },

    defineSerializationUnit: {
        value: function(name, funktion) {
            this.constructor.defineSerializationUnit.call(this, name, funktion);
        }
    }
}, {

    defineSerializationUnit: {
        value: function(name, funktion) {
            this._units[name] = funktion;
        }
    },

    getDefaultObjectNameForModuleId: {
        value: function(moduleId) {
            this._findObjectNameRegExp.test(moduleId);

            return RegExp.$1.replace(this._toCamelCaseRegExp, this._replaceToCamelCase);
        }
    }

});

exports.MontageSerializer = MontageSerializer;
exports.serialize = function(object, _require) {
    return new MontageSerializer().initWithRequire(_require)
        .serializeObject(object);
};

}})
;
//*/
montageDefine("6364dae","core/serialization/deserializer/montage-deserializer",{dependencies:["core/core","mousse/deserialization/interpreter","mousse/deserialization/deserializer","./montage-interpreter","./montage-reviver","core/promise","core/jshint","core/logger"],factory:function(require,exports,module){var Montage = require("core/core").Montage;
var Interpreter = require("mousse/deserialization/interpreter").Interpreter;
var Deserializer = require("mousse/deserialization/deserializer").Deserializer;
var MontageInterpreter = require("./montage-interpreter").MontageInterpreter;
var MontageReviver = require("./montage-reviver").MontageReviver;
var Promise = require("core/promise").Promise;
var JSHINT = require("core/jshint").JSHINT;

var logger = require("core/logger").logger("montage-deserializer");

var MontageDeserializer = Montage.specialize.call(Deserializer, {
    _interpreter: {value: null},
    _serializationString: {value: null},
    _serialization: {value: null},

    init: {
        value: function(serializationString, _require, objectRequires, origin) {
            if (! this.isSerializationStringValid(serializationString)) {
                throw new Error(
                    this._formatSerializationSyntaxError(serializationString)
                );
            }

            Deserializer.call(this, serializationString);
            this._origin;
            this._serialization = null;
            this._interpreter = new MontageInterpreter()
                .init(_require, objectRequires);

            return this;
        }
    },

    serialization: {
        get: function() {
            var serialization = this._serialization;

            if (!serialization) {
                serialization = JSON.parse(this._serializationString);
                this._serialization = serialization;
            }

            return serialization;
        }
    },

    deserialize: {
        value: function(instances, element) {
            var serialization;

            try {
                serialization = JSON.parse(this._serializationString);
            } catch (error) {
                return Promise.reject(error);
            }

            return this._interpreter.instantiate(
                serialization, instances, element);
        }
    },

    preloadModules: {
        value: function() {
            var serialization = JSON.parse(this._serializationString);

            return this._interpreter.preloadModules(serialization);
        }
    },

    getExternalObjectLabels: {
        value: function() {
            var serialization = this.serialization,
                labels = [];

            for (var label in serialization) {
                if (Object.keys(serialization[label]).length === 0) {
                    labels.push(label);
                }
            }

            return labels;
        }
    },

    isSerializationStringValid: {
        value: function(serializationString) {
            try {
                JSON.parse(serializationString);
                return true;
            } catch (ex) {
                return false;
            }
        }
    },

    _formatSerializationSyntaxError: {
        value: function(source) {
            var gutterPadding = "   ",
                origin = this._origin,
                message,
                error,
                lines,
                gutterSize,
                line;

            if (!JSHINT(source)) {
                error = JSHINT.errors[0];
                lines = source.split("\n");
                gutterSize = (gutterPadding + lines.length).length;
                line = error.line - 1;

                for (var i = 0, l = lines.length; i < l; i++) {
                    lines[i] = (new Array(gutterSize - (i + 1 + "").length + 1)).join(i === line ? ">" : " ") +
                        (i + 1) + " " + lines[i];
                }
                message = "Syntax error at line " + error.line +
                    (origin ? " from " + origin : "") + ":\n" +
                    error.evidence + "\n" + error.reason + "\n" +
                    lines.join("\n");
            } else {
                message = "Syntax error in the serialization but not able to find it!\n" + source;
            }

            return message;
        }
    }

}, {

    defineDeserializationUnit: {
        value: function(name, funktion) {
            MontageReviver.defineUnitReviver(name, funktion);
        }
    }

});

exports.MontageDeserializer = MontageDeserializer;
exports.deserialize = function(serializationString, _require) {
    return new MontageDeserializer().
        init(serializationString, _require)
        .deserializeObject();
}


}})
;
//*/
montageDefine("6364dae","core/serialization/serializer/montage-builder",{dependencies:["montage","mousse/serialization/builder","./montage-ast"],factory:function(require,exports,module){var Montage = require("montage").Montage;
var Builder = require("mousse/serialization/builder").Builder;
var MontageAst = require("./montage-ast");

/**
 * ElementReference
 *
 * @extends Value
 */
var MontageBuilder = Montage.specialize.call(Builder, {
    constructor: {
        value: function MontageBuilder() {
            Builder.call(this);
        }
    },

    createElementReference: {
        value: function(id) {
            return new MontageAst.ElementReference()
                .initWithRootAndId(this._root, id);
        }
    },

    createModuleReference: {
        value: function(moduleId) {
            return new MontageAst.ModuleReference()
                .initWithRootAndModuleId(this._root, moduleId);
        }
    }
});

exports.MontageBuilder = MontageBuilder;

}})
;
//*/
montageDefine("6364dae","core/serialization/serializer/montage-labeler",{dependencies:["montage","mousse/serialization/labeler"],factory:function(require,exports,module){var Montage = require("montage").Montage;
var Labeler = require("mousse/serialization/labeler").Labeler;

exports.MontageLabeler = Montage.specialize.call(Labeler, {
    _labelRegexp: {value: /^[a-zA-Z_$][0-9a-zA-Z_$]*$/},

    constructor: {
        value: function MontageLabeler() {
            Labeler.call(this);
        }
    },

    getObjectName: {
        value: function(object) {
            var identifier = object.identifier,
                objectName;

            if (identifier && this._labelRegexp.test(identifier)) {
                objectName = object.identifier;
            } else if ("getInfoForObject" in object || "getInfoForObject" in object.constructor ) {
                objectName = Montage.getInfoForObject(object).objectName;
                objectName = objectName.toLowerCase();
            } else {
                objectName = Labeler.prototype.getObjectName.call(
                                this, object);
            }

            return objectName;
        }
    }
});

}})
;
//*/
montageDefine("6364dae","core/serialization/serializer/montage-visitor",{dependencies:["montage","./montage-serializer","./properties-serializer","./self-serializer","./unit-serializer","mousse/serialization/visitor"],factory:function(require,exports,module){var Montage = require("montage").Montage;
var MontageSerializerModule = require("./montage-serializer");
var PropertiesSerializer = require("./properties-serializer").PropertiesSerializer;
var SelfSerializer = require("./self-serializer").SelfSerializer;
var UnitSerializer = require("./unit-serializer").UnitSerializer;
var Visitor = require("mousse/serialization/visitor").Visitor;

var MontageVisitor = Montage.specialize.call(Visitor, {
    _MONTAGE_ID_ATTRIBUTE: {value: "data-montage-id"},
    _require: {value: null},
    _units: {value: null},
    _elements: {value: null},

    constructor: {
        value: function MontageVisitor() {}
    },

    initWithBuilderAndLabelerAndRequireAndUnits: {
        value: function(builder, labeler, require, units) {
            Visitor.call(this, builder, labeler);

            this._require = require;
            this._units = units;
            this._elements = [];

            return this;
        }
    },

    getTypeOf: {
        value: function(object) {
            if (object.isModuleReference) {
                // this needs to be first as a ModuleReference is also a MontageObject
                return "Module";
            } else if ("getInfoForObject" in object || "getInfoForObject" in object.constructor) {
                return "MontageObject";
            } else if (object.thisIsAReferenceCreatedByMontageSerializer) {
                return "MontageReference";
            } else if (typeof Element !== "undefined" && Element.isElement(object)) {
                return "Element";
            }
        }
    },

    visitMontageReference: {
        value: function(malker, object, name) {
            this.builder.top.setProperty(name, object.reference);
        }
    },

    visitElement: {
        value: function(malker, element, name) {
            var elementReference,
                id;

            id = element.getAttribute(this._MONTAGE_ID_ATTRIBUTE);

            if (id) {
                elementReference = this.builder.createElementReference(id);
                this.storeValue(elementReference, element, name);
                this._elements.push(element);
            } else {
                throw new Error("Not possible to serialize a DOM element with no " + this._MONTAGE_ID_ATTRIBUTE + " assigned: " + element.outerHTML);
            }
        }
    },

    visitModule: {
        value: function(malker, reference, name) {
            var referenceReference,
                moduleId;

            try {
                moduleId = reference.resolve(this._require);
            } catch (e) {
                throw new Error("Not possible to serialize module reference " +
                    reference.id + " from package " + reference.require.location +
                    " inside package " + this._require.location
                );
            }

            referenceReference = this.builder.createModuleReference(moduleId);
            this.storeValue(referenceReference, reference, name);
        }
    },

    visitMontageObject: {
        value: function(malker, object, name) {
            if (this.isObjectSerialized(object)) {
                this.serializeReferenceToMontageObject(malker, object, name);
            } else {
                this.handleMontageObject(malker, object, name);
            }
        }
    },

    handleMontageObject: {
        value: function(malker, object, name) {
            var builderObject = this.builder.createCustomObject(),
                substituteObject;

            this.setObjectSerialization(object, builderObject);

            substituteObject = this.serializeMontageObject(malker, object, builderObject);

            if (substituteObject) {
                this.serializeSubstituteObject(malker, object, name, builderObject, substituteObject);
            } else {
                builderObject.setLabel(this.labeler.getObjectLabel(object));
                this.builder.top.setProperty(name, builderObject);
            }
        }
    },

    serializeReferenceToMontageObject: {
        value: function(malker, object, name) {
            var label = this.labeler.getObjectLabel(object),
                reference = this.builder.createObjectReference(label);

            this.builder.top.setProperty(name, reference);
        }
    },

    serializeSubstituteObject: {
        value: function(malker, object, name, builderObject, substituteObject) {
            var label,
                oldLabel,
                newLabel,
                substituteBuilderObject;

            label = this.labeler.getObjectLabel(object);

            // There are two label strategies to choose from when an object
            // is substituted for another one in a serialization:
            //
            // 1) The object that was substituted now starts being recognized
            //    with the label of the substituted object, this means
            //    relabeling all previously created references.
            //
            // 2) In the case that the object being substituted has a user
            //    defined label then we want to honor this label and relabel
            //    the substitute object with the user defined label instead.
            if (this.labeler.isUserDefinedLabel(label)) {
                oldLabel = this.labeler.getObjectLabel(substituteObject);

                // Make sure that the substitute object is now
                // known by the user defined label instead.
                this.labeler.setObjectLabel(substituteObject, label);

                // If there were references made to the substitute object we
                // need to change them to start pointing to the user defined
                // label.
                this.builder.relabelReferences(oldLabel, label);

                // Check if the substitute object was already serialized, if it
                // was we need to change the label of the serialization.
                substituteBuilderObject = this.getObjectSerialization(substituteObject);
                if (substituteBuilderObject) {
                    substituteBuilderObject.setLabel(label);

                    // If the substitute object also has a user defined label
                    // then we create a reference from one label to the other
                    if (this.labeler.isUserDefinedLabel(oldLabel)) {
                        this.builder.createObjectReference(label).setLabel(oldLabel);
                    }
                }

                malker.visit(substituteObject, name);
            } else {
                malker.visit(substituteObject, name);

                newLabel = this.labeler.getObjectLabel(substituteObject);

                // Make sure that the substituted object is now known by the
                // label of the substitute object.
                this.labeler.setObjectLabel(object, newLabel);

                // If there were references made to object we need to change
                // them to start pointing to the substitute object.
                this.builder.relabelReferences(label, newLabel);
            }
        }
    },

    serializeMontageObject: {
        value: function(malker, object, builderObject) {
            var selfSerializer,
                substituteObject,
                propertiesBuilderObject = this.builder.createObjectLiteral();

            this.setObjectType(object, builderObject);
            builderObject.setProperty("properties", propertiesBuilderObject);

            this.builder.push(builderObject);

            if (typeof object.serializeSelf === "function") {
                selfSerializer = new SelfSerializer().
                    initWithMalkerAndVisitorAndObject(
                        malker, this, object, builderObject);
                substituteObject = object.serializeSelf(selfSerializer);
            } else {
                this.setObjectProperties(malker, object);
                this.setObjectCustomUnits(malker, object);
            }

            this.builder.pop();

            // Remove the properties unit in case none was serialized,
            // we need to add it before any other units to make sure that
            // it's the first unit to show up in the serialization, since we
            // don't have a way to order the property names in a serialization.
            if (propertiesBuilderObject.getPropertyNames().length === 0) {
                builderObject.clearProperty("properties");
            }

            return substituteObject;
        }
    },

    setObjectType: {
        value: function(object, builderObject) {
            var isInstance = Montage.getInfoForObject(object).isInstance,
                locationId = this.getObjectLocationId(object),
                locationIdBuilderObject = this.builder.createString(locationId);

            if (isInstance) {
                builderObject.setProperty("prototype", locationIdBuilderObject);
            } else {
                builderObject.setProperty("object", locationIdBuilderObject);
            }
        }
    },

    getObjectModuleId: {
        value: function(object) {
            var objectInfo = Montage.getInfoForObject(object);

            return this._require.identify(objectInfo.moduleId,
                                          objectInfo.require);
        }
    },

    getObjectLocationId: {
        value: function(object) {
            var moduleId = this.getObjectModuleId(object),
                defaultObjectName,
                objectInfo = Montage.getInfoForObject(object),
                objectName = objectInfo.objectName;

            defaultObjectName = MontageSerializerModule.MontageSerializer.getDefaultObjectNameForModuleId(moduleId);

            if (defaultObjectName === objectName) {
                return moduleId;
            } else {
                return moduleId + "[" + objectName + "]";
            }
        }
    },

    /*
     * Expected object at the top of the stack: CustomObject
     */
    setObjectProperties: {
        value: function(malker, object) {
            var propertiesSerializer,
                propertiesObject;

            propertiesObject = this.builder.top.getProperty("properties");
            this.builder.push(propertiesObject);

            if (typeof object.serializeProperties === "function") {
                propertiesSerializer = new PropertiesSerializer()
                    .initWithMalkerAndVisitorAndObject(malker, this, object);
                object.serializeProperties(propertiesSerializer);
            } else {
                this.setSerializableObjectProperties(malker, object);
            }

            this.builder.pop();
        }
    },

    /*
     * Expected object at the top of the stack: ObjectLiteral
     */
    setSerializableObjectProperties: {
        value: function(malker, object) {
            var type,
                propertyName,
                propertyNames = Montage.getSerializablePropertyNames(object),
                propertyNamesCount = propertyNames.length;

            for (var i = 0; i < propertyNamesCount; i++) {
                propertyName = propertyNames[i];
                type = Montage.getPropertyAttribute(object, propertyName, "serializable");
                this.setProperty(malker, propertyName, object[propertyName], type);
            }
        }
    },

    hackIsReferenceAllowedForValue: {
        value: function(value) {
            // Only serialize as a reference values that are non-null objects,
            // we don't support references to non-objects and elements.
            // There's nothing in the serialization that prevents us to store
            // a reference to an object but that would be an external reference
            // the problem here is that the serializable defaults to "reference"
            // for most cases when in reality we probably just want "value".
            return typeof value === "object" &&
                   value != null &&
                   !(typeof Element !== "undefined" &&
                     Element.isElement(value));
        }
    },

    /*
     * Expected object at the top of the stack: ObjectLiteral
     */
    setProperty: {
        value: function(malker, propertyName, value, type) {
            var label;

            if (type === "reference" && this.hackIsReferenceAllowedForValue(value)) {
                label = this.labeler.getObjectLabel(value);
                var reference = this.builder.createObjectReference(label);
                this.builder.top.setProperty(propertyName, reference);
            } else {
                malker.visit(value, propertyName);
            }
        }
    },

    setObjectCustomUnits: {
        value: function(malker, object) {
            for (var unitName in this._units) {
                this.setObjectCustomUnit(malker, object, unitName);
            }
        }
    },

    setObjectCustomUnit: {
        value: function(malker, object, unitName) {
            var unit = this._units[unitName],
                value,
                unitSerializer;

            if (!unit) {
                return;
            }

            unitSerializer = new UnitSerializer()
                .initWithMalkerAndVisitorAndObject(malker, this, object);

            value = unit(unitSerializer, object);
            if (value != null) {
                malker.visit(value, unitName);
            }
        }
    },

    getExternalObjects: {
        value: function() {
            var externalObjects = {},
                labels = this.builder.getExternalReferences(),
                label;

            for (var i = 0; label = labels[i]; i++) {
                externalObjects[label] = this.labeler.getObjectByLabel(label);
            }

            return externalObjects;
        }
    },

    getExternalElements: {
        value: function() {
            return this._elements;
        }
    }
});

exports.MontageVisitor = MontageVisitor;

}})
;
//*/
montageDefine("6364dae","core/serialization/deserializer/montage-interpreter",{dependencies:["core/core","mousse/deserialization/interpreter","mousse/deserialization/context","./montage-reviver","core/promise"],factory:function(require,exports,module){var Montage = require("core/core").Montage,
    Interpreter = require("mousse/deserialization/interpreter").Interpreter,
    Context = require("mousse/deserialization/context").Context,
    MontageReviver = require("./montage-reviver").MontageReviver,
    Promise = require("core/promise").Promise;

var MontageInterpreter = Montage.specialize.call(Interpreter, {
    _require: {value: null},
    _reviver: {value: null},

    init: {
        value: function(_require, objectRequires) {
            if (typeof _require !== "function") {
                throw new Error("Function 'require' missing.");
            }

            this._reviver = new MontageReviver()
                .init(_require, objectRequires);
            this._require = _require;

            return this;
        }
    },

    instantiate: {
        value: function(serialization, objects, element) {
            var context;

            context = new MontageContext()
                .init(serialization, this._reviver, objects, element, this._require);

            return context.getObjects();
        }
    },

    preloadModules: {
        value: function(serialization) {
            var reviver = this._reviver,
                moduleLoader = reviver.moduleLoader,
                object,
                locationId,
                locationDesc,
                module,
                promises = [];

            for (var label in serialization) {
                object = serialization[label];

                locationId = object.prototype || object.object;
                if (locationId) {
                    locationDesc = MontageReviver.parseObjectLocationId(locationId);
                    module = moduleLoader.getModule(
                        locationDesc.moduleId, label);
                    if (Promise.isPromise(module)) {
                        promises.push(module);
                    }
                }
            }

            if (promises.length > 0) {
                return Promise.all(promises);
            }
        }
    }
});

var MontageContext = Montage.specialize.call(Context, {
    _ELEMENT_ID_ATTRIBUTE: {value: "data-montage-id"},
    _unitsToDeserialize: {value: null},
    _element: {value: null},
    _require: {value: null},

    constructor: {
        value: function() {
            this._unitsToDeserialize = [];
        }
    },

    init: {
        value: function(serialization, reviver, objects, element, _require) {
            Context.call(this, serialization, reviver, objects);

            this._element = element;
            this._require = _require;

            return this;
        }
    },

    getRequire: {
        value: function () {
            return this._require;
        }
    },

    getElement: {
        value: function() {
            return this._element;
        }
    },

    getElementById: {
        value: function(id) {
            var selector = '*[' + this._ELEMENT_ID_ATTRIBUTE + '="' + id + '"]';

            return this._element.querySelector(selector);
        }
    },

    setUnitsToDeserialize: {
        value: function(object, objectDesc, unitNames) {
            this._unitsToDeserialize.push({
                object: object,
                objectDesc: objectDesc,
                unitNames: unitNames
            });
        }
    },

    getUnitsToDeserialize: {
        value: function() {
            return this._unitsToDeserialize;
        }
    }
});

exports.MontageInterpreter = MontageInterpreter;
exports.MontageContext = MontageContext;

}})
;
//*/
montageDefine("6364dae","core/jshint",{dependencies:[],factory:function(require,exports,module){/*
 * JSHint, by JSHint Community.
 *
 * Licensed under the same slightly modified MIT license that JSLint is.
 * It stops evil-doers everywhere.
 *
 * JSHint is a derivative work of JSLint:
 *
 *   Copyright (c) 2002 Douglas Crockford  (www.JSLint.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.
 *
 * JSHint was forked from 2010-12-16 edition of JSLint.
 *
 */

/*
 JSHINT is a global function. It takes two parameters.

 var myResult = JSHINT(source, option);

 The first parameter is either a string or an array of strings. If it is a
 string, it will be split on '\n' or '\r'. If it is an array of strings, it
 is assumed that each string represents one line. The source can be a
 JavaScript text or a JSON text.

 The second parameter is an optional object of options which control the
 operation of JSHINT. Most of the options are booleans: They are all
 optional and have a default value of false. One of the options, predef,
 can be an array of names, which will be used to declare global variables,
 or an object whose keys are used as global names, with a boolean value
 that determines if they are assignable.

 If it checks out, JSHINT returns true. Otherwise, it returns false.

 If false, you can inspect JSHINT.errors to find out the problems.
 JSHINT.errors is an array of objects containing these members:

 {
 line      : The line (relative to 0) at which the lint was found
 character : The character (relative to 0) at which the lint was found
 reason    : The problem
 evidence  : The text line in which the problem occurred
 raw       : The raw message before the details were inserted
 a         : The first detail
 b         : The second detail
 c         : The third detail
 d         : The fourth detail
 }

 If a fatal error was found, a null will be the last element of the
 JSHINT.errors array.

 You can request a Function Report, which shows all of the functions
 and the parameters and vars that they use. This can be used to find
 implied global variables and other problems. The report is in HTML and
 can be inserted in an HTML <body>.

 var myReport = JSHINT.report(limited);

 If limited is true, then the report will be limited to only errors.

 You can request a data structure which contains JSHint's results.

 var myData = JSHINT.data();

 It returns a structure with this form:

 {
 errors: [
 {
 line: NUMBER,
 character: NUMBER,
 reason: STRING,
 evidence: STRING
 }
 ],
 functions: [
 name: STRING,
 line: NUMBER,
 last: NUMBER,
 param: [
 STRING
 ],
 closure: [
 STRING
 ],
 var: [
 STRING
 ],
 exception: [
 STRING
 ],
 outer: [
 STRING
 ],
 unused: [
 STRING
 ],
 global: [
 STRING
 ],
 label: [
 STRING
 ]
 ],
 globals: [
 STRING
 ],
 member: {
 STRING: NUMBER
 },
 unuseds: [
 {
 name: STRING,
 line: NUMBER
 }
 ],
 implieds: [
 {
 name: STRING,
 line: NUMBER
 }
 ],
 urls: [
 STRING
 ],
 json: BOOLEAN
 }

 Empty arrays will not be included.

 */

/*jshint
 evil: true, nomen: false, onevar: false, regexp: false, strict: true, boss: true
 */

/*members "\b", "\t", "\n", "\f", "\r", "!=", "!==", "\"", "%", "(begin)",
 "(breakage)", "(context)", "(error)", "(global)", "(identifier)", "(last)",
 "(line)", "(loopage)", "(name)", "(onevar)", "(params)", "(scope)",
 "(statement)", "(verb)", "*", "+", "++", "-", "--", "\/", "<", "<=", "==",
 "===", ">", ">=", $, $$, $A, $F, $H, $R, $break, $continue, $w, Abstract, Ajax,
 __filename, __dirname, ActiveXObject, Array, ArrayBuffer, ArrayBufferView,
 Autocompleter, Assets, Boolean, Builder, Buffer, Browser, COM, CScript, Canvas,
 CustomAnimation, Class, Control, Chain, Color, Cookie, Core, DataView, Date,
 Debug, Draggable, Draggables, Droppables, Document, DomReady, DOMReady, Drag,
 E, Enumerator, Enumerable, Element, Elements, Error, Effect, EvalError, Event,
 Events, FadeAnimation, Field, Flash, Float32Array, Float64Array, Form,
 FormField, Frame, Function, Fx, GetObject, Group, Hash, HotKey, HTMLElement,
 HtmlTable, Iframe, IframeShim, Image, Int16Array, Int32Array, Int8Array,
 Insertion, InputValidator, JSON, Keyboard, Locale, LN10, LN2, LOG10E, LOG2E,
 MAX_VALUE, MIN_VALUE, Mask, Math, MenuItem, MoveAnimation, MooTools, Native,
 NEGATIVE_INFINITY, Number, Object, ObjectRange, Option, Options, OverText, PI,
 POSITIVE_INFINITY, PeriodicalExecuter, Point, Position, Prototype, RangeError,
 Rectangle, ReferenceError, RegExp, ResizeAnimation, Request, RotateAnimation,
 SQRT1_2, SQRT2, ScrollBar, ScriptEngine, ScriptEngineBuildVersion,
 ScriptEngineMajorVersion, ScriptEngineMinorVersion, Scriptaculous, Scroller,
 Slick, Slider, Selector, String, Style, SyntaxError, Sortable, Sortables,
 SortableObserver, Sound, Spinner, System, Swiff, Text, TextArea, Template,
 Timer, Tips, Type, TypeError, Toggle, Try, URI, URIError, URL, VBArray, WSH,
 WScript, Web, Window, XMLDOM, XMLHttpRequest, XPathEvaluator, XPathException,
 XPathExpression, XPathNamespace, XPathNSResolver, XPathResult, "\\", a,
 addEventListener, address, alert, apply, applicationCache, arguments, arity,
 asi, b, bitwise, block, blur, boolOptions, boss, browser, c, call, callee,
 caller, cases, charAt, charCodeAt, character, clearInterval, clearTimeout,
 close, closed, closure, comment, condition, confirm, console, constructor,
 content, couch, create, css, curly, d, data, datalist, dd, debug, decodeURI,
 decodeURIComponent, defaultStatus, defineClass, deserialize, devel, document,
 dojo, dijit, dojox, define, edition, else, emit, encodeURI, encodeURIComponent,
 entityify, eqeqeq, eqnull, errors, es5, escape, eval, event, evidence, evil,
 ex, exception, exec, exps, expr, exports, FileReader, first, floor, focus,
 forin, fragment, frames, from, fromCharCode, fud, funct, function, functions,
 g, gc, getComputedStyle, getRow, GLOBAL, global, globals, globalstrict,
 hasOwnProperty, help, history, i, id,
 identifier, immed, implieds, include, indent, indexOf, init, ins, instanceOf,
 isAlpha, isApplicationRunning, isArray, isDigit, isFinite, isNaN, join, jshint,
 JSHINT, json, jquery, jQuery, keys, label, labelled, last, lastsemic, laxbreak,
 latedef, lbp, led, left, length, line, load, loadClass, localStorage, location,
 log, loopfunc, m, match, maxerr, maxlen, member,message, meta, module, moveBy,
 moveTo, mootools, name, navigator, new, newcap, noarg, node, noempty, nomen,
 nonew, nud, onbeforeunload, onblur, onerror, onevar, onfocus, onload, onresize,
 onunload, open, openDatabase, openURL, opener, opera, outer, param, parent,
 parseFloat, parseInt, passfail, plusplus, predef, print, process, prompt,
 prototype, prototypejs, push, quit, range, raw, reach, reason, regexp,
 readFile, readUrl, regexdash, removeEventListener, replace, report, require,
 reserved, resizeBy, resizeTo, resolvePath, resumeUpdates, respond, rhino, right,
 runCommand, scroll, screen, scripturl, scrollBy, scrollTo, scrollbar, search, seal, send,
 serialize, setInterval, setTimeout, shift, slice, sort,spawn, split, stack,
 status, start, strict, sub, substr, supernew, shadow, supplant, sum, sync,
 test, toLowerCase, toString, toUpperCase, toint32, token, top, trailing, type,
 typeOf, Uint16Array, Uint32Array, Uint8Array, undef, unused, urls, value, valueOf,
 var, version, WebSocket, white, window, Worker, wsh*/

/*global exports: false,bootstrap */

// We build the application inside a function so that we produce only a single
// global variable. That function will be invoked immediately, and its return
// value is the JSHINT function itself.

var JSHINT = (function (setup) {

    var actual;
    var JSHINT = function () {
        if (!actual) {
            actual = setup(JSHINT);
        }
        return actual.apply(this, arguments);
    };

    return JSHINT;

})(function (JSHINT) {
    "use strict";

    var anonname,       // The guessed name for anonymous functions.

        // These are operators that should not be used with the ! operator.

        bang = {
            '<'  : true,
            '<=' : true,
            '==' : true,
            '===': true,
            '!==': true,
            '!=' : true,
            '>'  : true,
            '>=' : true,
            '+'  : true,
            '-'  : true,
            '*'  : true,
            '/'  : true,
            '%'  : true
        },

        // These are the JSHint boolean options.

        boolOptions = {
            asi         : true, // if automatic semicolon insertion should be tolerated
            bitwise     : true, // if bitwise operators should not be allowed
            boss        : true, // if advanced usage of assignments should be allowed
            browser     : true, // if the standard browser globals should be predefined
            couch       : true, // if CouchDB globals should be predefined
            curly       : true, // if curly braces around blocks should be required (even in if/for/while)
            debug       : true, // if debugger statements should be allowed
            devel       : true, // if logging globals should be predefined (console, alert, etc.)
            dojo        : true, // if Dojo Toolkit globals should be predefined
            eqeqeq      : true, // if === should be required
            eqnull      : true, // if == null comparisons should be tolerated
            es5         : true, // if ES5 syntax should be allowed
            evil        : true, // if eval should be allowed
            expr        : true, // if ExpressionStatement should be allowed as Programs
            forin       : true, // if for in statements must filter
            globalstrict: true, // if global "use strict"; should be allowed (also enables 'strict')
            immed       : true, // if immediate invocations must be wrapped in parens
            jquery      : true, // if jQuery globals should be predefined
            latedef     : true, // if the use before definition should not be tolerated
            laxbreak    : true, // if line breaks should not be checked
            loopfunc    : true, // if functions should be allowed to be defined within loops
            mootools    : true, // if MooTools globals should be predefined
            newcap      : true, // if constructor names must be capitalized
            noarg       : true, // if arguments.caller and arguments.callee should be disallowed
            node        : true, // if the Node.js environment globals should be predefined
            noempty     : true, // if empty blocks should be disallowed
            nonew       : true, // if using `new` for side-effects should be disallowed
            nomen       : true, // if names should be checked
            onevar      : true, // if only one var statement per function should be allowed
            passfail    : true, // if the scan should stop on first error
            plusplus    : true, // if increment/decrement should not be allowed
            prototypejs : true, // if Prototype and Scriptaculous globals should be predefined
            regexdash   : true, // if unescaped last dash (-) inside brackets should be tolerated
            regexp      : true, // if the . should not be allowed in regexp literals
            rhino       : true, // if the Rhino environment globals should be predefined
            undef       : true, // if variables should be declared before used
            scripturl   : true, // if script-targeted URLs should be tolerated
            shadow      : true, // if variable shadowing should be tolerated
            strict      : true, // require the "use strict"; pragma
            sub         : true, // if all forms of subscript notation are tolerated
            supernew    : true, // if `new function () { ... };` and `new Object;` should be tolerated
            trailing    : true, // if trailing whitespace rules apply
            white       : true, // if strict whitespace rules apply
            wsh         : true  // if the Windows Scripting Host environment globals should be predefined
        },

        // browser contains a set of global names which are commonly provided by a
        // web browser environment.

        browser = {
            ArrayBuffer     : false,
            ArrayBufferView : false,
            addEventListener: false,
            applicationCache: false,
            blur            : false,
            clearInterval   : false,
            clearTimeout    : false,
            close           : false,
            closed          : false,
            DataView        : false,
            defaultStatus   : false,
            document        : false,
            event           : false,
            FileReader      : false,
            Float32Array    : false,
            Float64Array    : false,
            focus           : false,
            frames          : false,
            getComputedStyle: false,
            HTMLElement     : false,
            history         : false,
            Int16Array      : false,
            Int32Array      : false,
            Int8Array       : false,
            Image           : false,
            length          : false,
            localStorage    : false,
            location        : false,
            moveBy          : false,
            moveTo          : false,
            name            : false,
            navigator       : false,
            onbeforeunload  : true,
            onblur          : true,
            onerror         : true,
            onfocus         : true,
            onload          : true,
            onresize        : true,
            onunload        : true,
            open            : false,
            openDatabase    : false,
            opener          : false,
            Option          : false,
            parent          : false,
            print           : false,
            removeEventListener: false,
            resizeBy        : false,
            resizeTo        : false,
            screen          : false,
            scroll          : false,
            scrollBy        : false,
            scrollTo        : false,
            setInterval     : false,
            setTimeout      : false,
            status          : false,
            top             : false,
            Uint16Array     : false,
            Uint32Array     : false,
            Uint8Array      : false,
            WebSocket       : false,
            window          : false,
            Worker          : false,
            XMLHttpRequest  : false,
            XPathEvaluator  : false,
            XPathException  : false,
            XPathExpression : false,
            XPathNamespace  : false,
            XPathNSResolver : false,
            XPathResult     : false
        },

        couch = {
            "require" : false,
            respond   : false,
            getRow    : false,
            emit      : false,
            send      : false,
            start     : false,
            sum       : false,
            log       : false,
            exports   : false,
            module    : false
        },

        devel = {
            alert   : false,
            confirm : false,
            console : false,
            Debug   : false,
            opera   : false,
            prompt  : false
        },

        dojo = {
            dojo      : false,
            dijit     : false,
            dojox     : false,
            define    : false,
            "require" : false
        },

        escapes = {
            '\b': '\\b',
            '\t': '\\t',
            '\n': '\\n',
            '\f': '\\f',
            '\r': '\\r',
            '"' : '\\"',
            '/' : '\\/',
            '\\': '\\\\'
        },

        funct,          // The current function

        functionicity = [
            'closure', 'exception', 'global', 'label',
            'outer', 'unused', 'var'
        ],

        functions,      // All of the functions

        global,         // The global scope
        implied,        // Implied globals
        inblock,
        indent,
        jsonmode,

        jquery = {
            '$'    : false,
            jQuery : false
        },

        lines,
        lookahead,
        member,
        membersOnly,

        mootools = {
            '$'             : false,
            '$$'            : false,
            Assets          : false,
            Browser         : false,
            Chain           : false,
            Class           : false,
            Color           : false,
            Cookie          : false,
            Core            : false,
            Document        : false,
            DomReady        : false,
            DOMReady        : false,
            Drag            : false,
            Element         : false,
            Elements        : false,
            Event           : false,
            Events          : false,
            Fx              : false,
            Group           : false,
            Hash            : false,
            HtmlTable       : false,
            Iframe          : false,
            IframeShim      : false,
            InputValidator  : false,
            instanceOf      : false,
            Keyboard        : false,
            Locale          : false,
            Mask            : false,
            MooTools        : false,
            Native          : false,
            Options         : false,
            OverText        : false,
            Request         : false,
            Scroller        : false,
            Slick           : false,
            Slider          : false,
            Sortables       : false,
            Spinner         : false,
            Swiff           : false,
            Tips            : false,
            Type            : false,
            typeOf          : false,
            URI             : false,
            Window          : false
        },

        nexttoken,

        node = {
            __filename  : false,
            __dirname   : false,
            exports     : false,
            Buffer      : false,
            GLOBAL      : false,
            global      : false,
            module      : false,
            process     : false,
            require     : false
        },

        noreach,
        option,
        predefined,     // Global variables defined by option
        prereg,
        prevtoken,

        prototypejs = {
            '$'               : false,
            '$$'              : false,
            '$A'              : false,
            '$F'              : false,
            '$H'              : false,
            '$R'              : false,
            '$break'          : false,
            '$continue'       : false,
            '$w'              : false,
            Abstract          : false,
            Ajax              : false,
            Class             : false,
            Enumerable        : false,
            Element           : false,
            Event             : false,
            Field             : false,
            Form              : false,
            Hash              : false,
            Insertion         : false,
            ObjectRange       : false,
            PeriodicalExecuter: false,
            Position          : false,
            Prototype         : false,
            Selector          : false,
            Template          : false,
            Toggle            : false,
            Try               : false,
            Autocompleter     : false,
            Builder           : false,
            Control           : false,
            Draggable         : false,
            Draggables        : false,
            Droppables        : false,
            Effect            : false,
            Sortable          : false,
            SortableObserver  : false,
            Sound             : false,
            Scriptaculous     : false
        },

        rhino = {
            defineClass : false,
            deserialize : false,
            gc          : false,
            help        : false,
            load        : false,
            loadClass   : false,
            print       : false,
            quit        : false,
            readFile    : false,
            readUrl     : false,
            runCommand  : false,
            seal        : false,
            serialize   : false,
            spawn       : false,
            sync        : false,
            toint32     : false,
            version     : false
        },

        scope,      // The current scope
        src,
        stack,

        // standard contains the global names that are provided by the
        // ECMAScript standard.

        standard = {
            Array               : false,
            Boolean             : false,
            Date                : false,
            decodeURI           : false,
            decodeURIComponent  : false,
            encodeURI           : false,
            encodeURIComponent  : false,
            Error               : false,
            'eval'              : false,
            EvalError           : false,
            Function            : false,
            hasOwnProperty      : false,
            isFinite            : false,
            isNaN               : false,
            JSON                : false,
            Math                : false,
            Number              : false,
            Object              : false,
            parseInt            : false,
            parseFloat          : false,
            RangeError          : false,
            ReferenceError      : false,
            RegExp              : false,
            String              : false,
            SyntaxError         : false,
            TypeError           : false,
            URIError            : false
        },

        standard_member = {
            E                   : true,
            LN2                 : true,
            LN10                : true,
            LOG2E               : true,
            LOG10E              : true,
            MAX_VALUE           : true,
            MIN_VALUE           : true,
            NEGATIVE_INFINITY   : true,
            PI                  : true,
            POSITIVE_INFINITY   : true,
            SQRT1_2             : true,
            SQRT2               : true
        },

        strict_mode,
        syntax = {},
        tab,
        token,
        urls,
        warnings,

        wsh = {
            ActiveXObject             : true,
            Enumerator                : true,
            GetObject                 : true,
            ScriptEngine              : true,
            ScriptEngineBuildVersion  : true,
            ScriptEngineMajorVersion  : true,
            ScriptEngineMinorVersion  : true,
            VBArray                   : true,
            WSH                       : true,
            WScript                   : true
        },

        // Regular expressions. Some of these are stupidly long.

        // unsafe comment or string
        ax = /@cc|<\/?|script|\]\s*\]|<\s*!|&lt/i,
        // unsafe characters that are silently deleted by one or more browsers
        cx = /[\u0000-\u001f\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/,
        // token
        tx = /^\s*([(){}\[.,:;'"~\?\]#@]|==?=?|\/(\*(jshint|jslint|members?|global)?|=|\/)?|\*[\/=]?|\+(?:=|\++)?|-(?:=|-+)?|%=?|&[&=]?|\|[|=]?|>>?>?=?|<([\/=!]|\!(\[|--)?|<=?)?|\^=?|\!=?=?|[a-zA-Z_$][a-zA-Z0-9_$]*|[0-9]+([xX][0-9a-fA-F]+|\.[0-9]*)?([eE][+\-]?[0-9]+)?)/,
        // characters in strings that need escapement
        nx = /[\u0000-\u001f&<"\/\\\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/,
        nxg = /[\u0000-\u001f&<"\/\\\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
        // star slash
        lx = /\*\/|\/\*/,
        // identifier
        ix = /^([a-zA-Z_$][a-zA-Z0-9_$]*)$/,
        // javascript url
        jx = /^(?:javascript|jscript|ecmascript|vbscript|mocha|livescript)\s*:/i,
        // catches /* falls through */ comments
        ft = /^\s*\/\*\s*falls\sthrough\s*\*\/\s*$/;

    function F() {
    }     // Used by Object.create

    function is_own(object, name) {

        // The object.hasOwnProperty method fails when the property under consideration
        // is named 'hasOwnProperty'. So we have to use this more convoluted form.

        return Object.prototype.hasOwnProperty.call(object, name);
    }

    // Provide critical ES5 functions to ES3.

    if (typeof Array.isArray !== 'function') {
        Array.isArray = function (o) {
            return Object.prototype.toString.apply(o) === '[object Array]';
        };
    }

    if (typeof Object.create !== 'function') {
        Object.create = function (o) {
            F.prototype = o;
            return new F();
        };
    }

    if (typeof Object.keys !== 'function') {
        Object.keys = function (o) {
            var a = [], k;
            for (k in o) {
                if (is_own(o, k)) {
                    a.push(k);
                }
            }
            return a;
        };
    }

    // Non standard methods

    if (typeof String.prototype.entityify !== 'function') {
        String.prototype.entityify = function () {
            return this
                .replace(/&/g, '&amp;')
                .replace(/</g, '&lt;')
                .replace(/>/g, '&gt;');
        };
    }

    if (typeof String.prototype.isAlpha !== 'function') {
        String.prototype.isAlpha = function () {
            return (this >= 'a' && this <= 'z\uffff') ||
                (this >= 'A' && this <= 'Z\uffff');
        };
    }

    if (typeof String.prototype.isDigit !== 'function') {
        String.prototype.isDigit = function () {
            return (this >= '0' && this <= '9');
        };
    }

    if (typeof String.prototype.supplant !== 'function') {
        String.prototype.supplant = function (o) {
            return this.replace(/\{([^{}]*)\}/g, function (a, b) {
                var r = o[b];
                return typeof r === 'string' || typeof r === 'number' ? r : a;
            });
        };
    }

    if (typeof String.prototype.name !== 'function') {
        String.prototype.name = function () {

            // If the string looks like an identifier, then we can return it as is.
            // If the string contains no control characters, no quote characters, and no
            // backslash characters, then we can simply slap some quotes around it.
            // Otherwise we must also replace the offending characters with safe
            // sequences.

            if (ix.test(this)) {
                return this;
            }
            if (nx.test(this)) {
                return '"' + this.replace(nxg, function (a) {
                    var c = escapes[a];
                    if (c) {
                        return c;
                    }
                    return '\\u' + ('0000' + a.charCodeAt().toString(16)).slice(-4);
                }) + '"';
            }
            return '"' + this + '"';
        };
    }


    function combine(t, o) {
        var n;
        for (n in o) {
            if (is_own(o, n)) {
                t[n] = o[n];
            }
        }
    }

    function assume() {
        if (option.couch) {
            combine(predefined, couch);
        }

        if (option.rhino) {
            combine(predefined, rhino);
        }

        if (option.prototypejs) {
            combine(predefined, prototypejs);
        }

        if (option.node) {
            combine(predefined, node);
        }

        if (option.devel) {
            combine(predefined, devel);
        }

        if (option.dojo) {
            combine(predefined, dojo);
        }

        if (option.browser) {
            combine(predefined, browser);
        }

        if (option.jquery) {
            combine(predefined, jquery);
        }

        if (option.mootools) {
            combine(predefined, mootools);
        }

        if (option.wsh) {
            combine(predefined, wsh);
        }

        if (option.globalstrict && option.strict !== false) {
            option.strict = true;
        }
    }


    // Produce an error warning.

    function quit(message, line, chr) {
        var percentage = Math.floor((line / lines.length) * 100);

        throw {
            name: 'JSHintError',
            line: line,
            character: chr,
            message: message + " (" + percentage + "% scanned)."
        };
    }

    function warning(m, t, a, b, c, d) {
        var ch, l, w;
        t = t || nexttoken;
        if (t.id === '(end)') {  // `~
            t = token;
        }
        l = t.line || 0;
        ch = t.from || 0;
        w = {
            id: '(error)',
            raw: m,
            evidence: lines[l - 1] || '',
            line: l,
            character: ch,
            a: a,
            b: b,
            c: c,
            d: d
        };
        w.reason = m.supplant(w);
        JSHINT.errors.push(w);
        if (option.passfail) {
            quit('Stopping. ', l, ch);
        }
        warnings += 1;
        if (warnings >= option.maxerr) {
            quit("Too many errors.", l, ch);
        }
        return w;
    }

    function warningAt(m, l, ch, a, b, c, d) {
        return warning(m, {
            line: l,
            from: ch
        }, a, b, c, d);
    }

    function error(m, t, a, b, c, d) {
        var w = warning(m, t, a, b, c, d);
        quit("Stopping, unable to continue.", w.line, w.character);
    }

    function errorAt(m, l, ch, a, b, c, d) {
        return error(m, {
            line: l,
            from: ch
        }, a, b, c, d);
    }


    // lexical analysis and token construction

    var lex = (function lex() {
        var character, from, line, s;

        // Private lex methods

        function nextLine() {
            var at,
                tw; // trailing whitespace check

            if (line >= lines.length) {
                return false;
            }

            character = 1;
            s = lines[line];
            line += 1;
            at = s.search(/ \t/);

            if (at >= 0) {
                warningAt("Mixed spaces and tabs.", line, at + 1);
            }

            s = s.replace(/\t/g, tab);
            at = s.search(cx);

            if (at >= 0) {
                warningAt("Unsafe character.", line, at);
            }

            if (option.maxlen && option.maxlen < s.length) {
                warningAt("Line too long.", line, s.length);
            }

            // Check for trailing whitespaces
            tw = s.search(/\s+$/);
            if (option.trailing && ~tw && !~s.search(/^\s+$/)) {
                warningAt("Trailing whitespace.", line, tw);
            }

            return true;
        }

        // Produce a token object.  The token inherits from a syntax symbol.

        function it(type, value) {
            var i, t;
            if (type === '(color)' || type === '(range)') {
                t = {type: type};
            } else if (type === '(punctuator)' ||
                (type === '(identifier)' && is_own(syntax, value))) {
                t = syntax[value] || syntax['(error)'];
            } else {
                t = syntax[type];
            }
            t = Object.create(t);
            if (type === '(string)' || type === '(range)') {
                if (!option.scripturl && jx.test(value)) {
                    warningAt("Script URL.", line, from);
                }
            }
            if (type === '(identifier)') {
                t.identifier = true;
                if (value === '__iterator__' || value === '__proto__') {
                    errorAt("Reserved name '{a}'.",
                        line, from, value);
                } else if (option.nomen &&
                    (value.charAt(0) === '_' ||
                        value.charAt(value.length - 1) === '_')) {
                    warningAt("Unexpected {a} in '{b}'.", line, from,
                        "dangling '_'", value);
                }
            }
            t.value = value;
            t.line = line;
            t.character = character;
            t.from = from;
            i = t.id;
            if (i !== '(endline)') {
                prereg = i &&
                    (('(,=:[!&|?{};'.indexOf(i.charAt(i.length - 1)) >= 0) ||
                        i === 'return');
            }
            return t;
        }

        // Public lex methods

        return {
            init: function (source) {
                if (typeof source === 'string') {
                    lines = source
                        .replace(/\r\n/g, '\n')
                        .replace(/\r/g, '\n')
                        .split('\n');
                } else {
                    lines = source;
                }

                // If the first line is a shebang (#!), make it a blank and move on.
                // Shebangs are used by Node scripts.
                if (lines[0] && lines[0].substr(0, 2) == '#!') {
                    lines[0] = '';
                }

                line = 0;
                nextLine();
                from = 1;
            },

            range: function (begin, end) {
                var c, value = '';
                from = character;
                if (s.charAt(0) !== begin) {
                    errorAt("Expected '{a}' and instead saw '{b}'.",
                        line, character, begin, s.charAt(0));
                }
                for (; ;) {
                    s = s.slice(1);
                    character += 1;
                    c = s.charAt(0);
                    switch (c) {
                        case '':
                            errorAt("Missing '{a}'.", line, character, c);
                            break;
                        case end:
                            s = s.slice(1);
                            character += 1;
                            return it('(range)', value);
                        case '\\':
                            warningAt("Unexpected '{a}'.", line, character, c);
                    }
                    value += c;
                }

            },

            // token -- this is called by advance to get the next token.

            token: function () {
                var b, c, captures, d, depth, high, i, l, low, q, t;

                function match(x) {
                    var r = x.exec(s), r1;
                    if (r) {
                        l = r[0].length;
                        r1 = r[1];
                        c = r1.charAt(0);
                        s = s.substr(l);
                        from = character + l - r1.length;
                        character += l;
                        return r1;
                    }
                }

                function string(x) {
                    var c, j, r = '';

                    if (jsonmode && x !== '"') {
                        warningAt("Strings must use doublequote.",
                            line, character);
                    }

                    function esc(n) {
                        var i = parseInt(s.substr(j + 1, n), 16);
                        j += n;
                        if (i >= 32 && i <= 126 &&
                            i !== 34 && i !== 92 && i !== 39) {
                            warningAt("Unnecessary escapement.", line, character);
                        }
                        character += n;
                        c = String.fromCharCode(i);
                    }

                    j = 0;
                    for (; ;) {
                        while (j >= s.length) {
                            j = 0;
                            if (!nextLine()) {
                                errorAt("Unclosed string.", line, from);
                            }
                        }
                        c = s.charAt(j);
                        if (c === x) {
                            character += 1;
                            s = s.substr(j + 1);
                            return it('(string)', r, x);
                        }
                        if (c < ' ') {
                            if (c === '\n' || c === '\r') {
                                break;
                            }
                            warningAt("Control character in string: {a}.",
                                line, character + j, s.slice(0, j));
                        } else if (c === '\\') {
                            j += 1;
                            character += 1;
                            c = s.charAt(j);
                            switch (c) {
                                case '\\':
                                case '"':
                                case '/':
                                    break;
                                case '\'':
                                    if (jsonmode) {
                                        warningAt("Avoid \\'.", line, character);
                                    }
                                    break;
                                case 'b':
                                    c = '\b';
                                    break;
                                case 'f':
                                    c = '\f';
                                    break;
                                case 'n':
                                    c = '\n';
                                    break;
                                case 'r':
                                    c = '\r';
                                    break;
                                case 't':
                                    c = '\t';
                                    break;
                                case 'u':
                                    esc(4);
                                    break;
                                case 'v':
                                    if (jsonmode) {
                                        warningAt("Avoid \\v.", line, character);
                                    }
                                    c = '\v';
                                    break;
                                case 'x':
                                    if (jsonmode) {
                                        warningAt("Avoid \\x-.", line, character);
                                    }
                                    esc(2);
                                    break;
                                default:
                                    warningAt("Bad escapement.", line, character);
                            }
                        }
                        r += c;
                        character += 1;
                        j += 1;
                    }
                }

                for (; ;) {
                    if (!s) {
                        return it(nextLine() ? '(endline)' : '(end)', '');
                    }
                    t = match(tx);
                    if (!t) {
                        t = '';
                        c = '';
                        while (s && s < '!') {
                            s = s.substr(1);
                        }
                        if (s) {
                            errorAt("Unexpected '{a}'.", line, character, s.substr(0, 1));
                        }
                    } else {

                        //      identifier

                        if (c.isAlpha() || c === '_' || c === '$') {
                            return it('(identifier)', t);
                        }

                        //      number

                        if (c.isDigit()) {
                            if (!isFinite(Number(t))) {
                                warningAt("Bad number '{a}'.",
                                    line, character, t);
                            }
                            if (s.substr(0, 1).isAlpha()) {
                                warningAt("Missing space after '{a}'.",
                                    line, character, t);
                            }
                            if (c === '0') {
                                d = t.substr(1, 1);
                                if (d.isDigit()) {
                                    if (token.id !== '.') {
                                        warningAt("Don't use extra leading zeros '{a}'.",
                                            line, character, t);
                                    }
                                } else if (jsonmode && (d === 'x' || d === 'X')) {
                                    warningAt("Avoid 0x-. '{a}'.",
                                        line, character, t);
                                }
                            }
                            if (t.substr(t.length - 1) === '.') {
                                warningAt(
                                    "A trailing decimal point can be confused with a dot '{a}'.", line, character, t);
                            }
                            return it('(number)', t);
                        }
                        switch (t) {

                            //      string

                            case '"':
                            case "'":
                                return string(t);

                            //      // comment

                            case '//':
                                if (src) {
                                    warningAt("Unexpected comment.", line, character);
                                }
                                s = '';
                                token.comment = true;
                                break;

                            //      /* comment

                            case '/*':
                                if (src) {
                                    warningAt("Unexpected comment.", line, character);
                                }
                                for (; ;) {
                                    i = s.search(lx);
                                    if (i >= 0) {
                                        break;
                                    }
                                    if (!nextLine()) {
                                        errorAt("Unclosed comment.", line, character);
                                    }
                                }
                                character += i + 2;
                                if (s.substr(i, 1) === '/') {
                                    errorAt("Nested comment.", line, character);
                                }
                                s = s.substr(i + 2);
                                token.comment = true;
                                break;

                            //      /*members /*jshint /*global

                            case '/*members':
                            case '/*member':
                            case '/*jshint':
                            case '/*jslint':
                            case '/*global':
                            case '*/':
                                return {
                                    value: t,
                                    type: 'special',
                                    line: line,
                                    character: character,
                                    from: from
                                };

                            case '':
                                break;
                            //      /
                            case '/':
                                if (token.id === '/=') {
                                    errorAt(
                                        "A regular expression literal can be confused with '/='.", line, from);
                                }
                                if (prereg) {
                                    depth = 0;
                                    captures = 0;
                                    l = 0;
                                    for (; ;) {
                                        b = true;
                                        c = s.charAt(l);
                                        l += 1;
                                        switch (c) {
                                            case '':
                                                errorAt("Unclosed regular expression.",
                                                    line, from);
                                                return;
                                            case '/':
                                                if (depth > 0) {
                                                    warningAt("Unescaped '{a}'.",
                                                        line, from + l, '/');
                                                }
                                                c = s.substr(0, l - 1);
                                                q = {
                                                    g: true,
                                                    i: true,
                                                    m: true
                                                };
                                                while (q[s.charAt(l)] === true) {
                                                    q[s.charAt(l)] = false;
                                                    l += 1;
                                                }
                                                character += l;
                                                s = s.substr(l);
                                                q = s.charAt(0);
                                                if (q === '/' || q === '*') {
                                                    errorAt("Confusing regular expression.",
                                                        line, from);
                                                }
                                                return it('(regexp)', c);
                                            case '\\':
                                                c = s.charAt(l);
                                                if (c < ' ') {
                                                    warningAt(
                                                        "Unexpected control character in regular expression.", line, from + l);
                                                } else if (c === '<') {
                                                    warningAt(
                                                        "Unexpected escaped character '{a}' in regular expression.", line, from + l, c);
                                                }
                                                l += 1;
                                                break;
                                            case '(':
                                                depth += 1;
                                                b = false;
                                                if (s.charAt(l) === '?') {
                                                    l += 1;
                                                    switch (s.charAt(l)) {
                                                        case ':':
                                                        case '=':
                                                        case '!':
                                                            l += 1;
                                                            break;
                                                        default:
                                                            warningAt(
                                                                "Expected '{a}' and instead saw '{b}'.", line, from + l, ':', s.charAt(l));
                                                    }
                                                } else {
                                                    captures += 1;
                                                }
                                                break;
                                            case '|':
                                                b = false;
                                                break;
                                            case ')':
                                                if (depth === 0) {
                                                    warningAt("Unescaped '{a}'.",
                                                        line, from + l, ')');
                                                } else {
                                                    depth -= 1;
                                                }
                                                break;
                                            case ' ':
                                                q = 1;
                                                while (s.charAt(l) === ' ') {
                                                    l += 1;
                                                    q += 1;
                                                }
                                                if (q > 1) {
                                                    warningAt(
                                                        "Spaces are hard to count. Use {{a}}.", line, from + l, q);
                                                }
                                                break;
                                            case '[':
                                                c = s.charAt(l);
                                                if (c === '^') {
                                                    l += 1;
                                                    if (option.regexp) {
                                                        warningAt("Insecure '{a}'.",
                                                            line, from + l, c);
                                                    } else if (s.charAt(l) === ']') {
                                                        errorAt("Unescaped '{a}'.",
                                                            line, from + l, '^');
                                                    }
                                                }
                                                q = false;
                                                if (c === ']') {
                                                    warningAt("Empty class.", line,
                                                        from + l - 1);
                                                    q = true;
                                                }
                                                klass:                                  do {
                                                    c = s.charAt(l);
                                                    l += 1;
                                                    switch (c) {
                                                        case '[':
                                                        case '^':
                                                            warningAt("Unescaped '{a}'.",
                                                                line, from + l, c);
                                                            q = true;
                                                            break;
                                                        case '-':
                                                            if (q) {
                                                                q = false;
                                                            } else {
                                                                warningAt("Unescaped '{a}'.",
                                                                    line, from + l, '-');
                                                                q = true;
                                                            }
                                                            break;
                                                        case ']':
                                                            if (!q && !option.regexdash) {
                                                                warningAt("Unescaped '{a}'.",
                                                                    line, from + l - 1, '-');
                                                            }
                                                            break klass;
                                                        case '\\':
                                                            c = s.charAt(l);
                                                            if (c < ' ') {
                                                                warningAt(
                                                                    "Unexpected control character in regular expression.", line, from + l);
                                                            } else if (c === '<') {
                                                                warningAt(
                                                                    "Unexpected escaped character '{a}' in regular expression.", line, from + l, c);
                                                            }
                                                            l += 1;
                                                            q = true;
                                                            break;
                                                        case '/':
                                                            warningAt("Unescaped '{a}'.",
                                                                line, from + l - 1, '/');
                                                            q = true;
                                                            break;
                                                        case '<':
                                                            q = true;
                                                            break;
                                                        default:
                                                            q = true;
                                                    }
                                                } while (c);
                                                break;
                                            case '.':
                                                if (option.regexp) {
                                                    warningAt("Insecure '{a}'.", line,
                                                        from + l, c);
                                                }
                                                break;
                                            case ']':
                                            case '?':
                                            case '{':
                                            case '}':
                                            case '+':
                                            case '*':
                                                warningAt("Unescaped '{a}'.", line,
                                                    from + l, c);
                                        }
                                        if (b) {
                                            switch (s.charAt(l)) {
                                                case '?':
                                                case '+':
                                                case '*':
                                                    l += 1;
                                                    if (s.charAt(l) === '?') {
                                                        l += 1;
                                                    }
                                                    break;
                                                case '{':
                                                    l += 1;
                                                    c = s.charAt(l);
                                                    if (c < '0' || c > '9') {
                                                        warningAt(
                                                            "Expected a number and instead saw '{a}'.", line, from + l, c);
                                                    }
                                                    l += 1;
                                                    low = +c;
                                                    for (; ;) {
                                                        c = s.charAt(l);
                                                        if (c < '0' || c > '9') {
                                                            break;
                                                        }
                                                        l += 1;
                                                        low = +c + (low * 10);
                                                    }
                                                    high = low;
                                                    if (c === ',') {
                                                        l += 1;
                                                        high = Infinity;
                                                        c = s.charAt(l);
                                                        if (c >= '0' && c <= '9') {
                                                            l += 1;
                                                            high = +c;
                                                            for (; ;) {
                                                                c = s.charAt(l);
                                                                if (c < '0' || c > '9') {
                                                                    break;
                                                                }
                                                                l += 1;
                                                                high = +c + (high * 10);
                                                            }
                                                        }
                                                    }
                                                    if (s.charAt(l) !== '}') {
                                                        warningAt(
                                                            "Expected '{a}' and instead saw '{b}'.", line, from + l, '}', c);
                                                    } else {
                                                        l += 1;
                                                    }
                                                    if (s.charAt(l) === '?') {
                                                        l += 1;
                                                    }
                                                    if (low > high) {
                                                        warningAt(
                                                            "'{a}' should not be greater than '{b}'.", line, from + l, low, high);
                                                    }
                                            }
                                        }
                                    }
                                    c = s.substr(0, l - 1);
                                    character += l;
                                    s = s.substr(l);
                                    return it('(regexp)', c);
                                }
                                return it('(punctuator)', t);

                            //      punctuator

                            case '#':
                                return it('(punctuator)', t);
                            default:
                                return it('(punctuator)', t);
                        }
                    }
                }
            }
        };
    }());


    function addlabel(t, type) {

        if (t === 'hasOwnProperty') {
            warning("'hasOwnProperty' is a really bad name.");
        }

        // Define t in the current function in the current scope.

        if (is_own(funct, t) && !funct['(global)']) {
            if (funct[t] === true) {
                if (option.latedef) {
                    warning("'{a}' was used before it was defined.", nexttoken, t);
                }
            } else {
                if (!option.shadow) {
                    warning("'{a}' is already defined.", nexttoken, t);
                }
            }
        }

        funct[t] = type;
        if (funct['(global)']) {
            global[t] = funct;
            if (is_own(implied, t)) {
                if (option.latedef) {
                    warning("'{a}' was used before it was defined.", nexttoken, t);
                }
                delete implied[t];
            }
        } else {
            scope[t] = funct;
        }
    }


    function doOption() {
        var b, obj, filter, o = nexttoken.value, t, v;
        switch (o) {
            case '*/':
                error("Unbegun comment.");
                break;
            case '/*members':
            case '/*member':
                o = '/*members';
                if (!membersOnly) {
                    membersOnly = {};
                }
                obj = membersOnly;
                break;
            case '/*jshint':
            case '/*jslint':
                obj = option;
                filter = boolOptions;
                break;
            case '/*global':
                obj = predefined;
                break;
            default:
                error("What?");
        }
        t = lex.token();
        loop:   for (; ;) {
            for (; ;) {
                if (t.type === 'special' && t.value === '*/') {
                    break loop;
                }
                if (t.id !== '(endline)' && t.id !== ',') {
                    break;
                }
                t = lex.token();
            }
            if (t.type !== '(string)' && t.type !== '(identifier)' &&
                o !== '/*members') {
                error("Bad option.", t);
            }
            v = lex.token();
            if (v.id === ':') {
                v = lex.token();
                if (obj === membersOnly) {
                    error("Expected '{a}' and instead saw '{b}'.",
                        t, '*/', ':');
                }
                if (t.value === 'indent' && (o === '/*jshint' || o === '/*jslint')) {
                    b = +v.value;
                    if (typeof b !== 'number' || !isFinite(b) || b <= 0 ||
                        Math.floor(b) !== b) {
                        error("Expected a small integer and instead saw '{a}'.",
                            v, v.value);
                    }
                    obj.white = true;
                    obj.indent = b;
                } else if (t.value === 'maxerr' && (o === '/*jshint' || o === '/*jslint')) {
                    b = +v.value;
                    if (typeof b !== 'number' || !isFinite(b) || b <= 0 ||
                        Math.floor(b) !== b) {
                        error("Expected a small integer and instead saw '{a}'.",
                            v, v.value);
                    }
                    obj.maxerr = b;
                } else if (t.value === 'maxlen' && (o === '/*jshint' || o === '/*jslint')) {
                    b = +v.value;
                    if (typeof b !== 'number' || !isFinite(b) || b <= 0 ||
                        Math.floor(b) !== b) {
                        error("Expected a small integer and instead saw '{a}'.",
                            v, v.value);
                    }
                    obj.maxlen = b;
                } else if (v.value === 'true') {
                    obj[t.value] = true;
                } else if (v.value === 'false') {
                    obj[t.value] = false;
                } else {
                    error("Bad option value.", v);
                }
                t = lex.token();
            } else {
                if (o === '/*jshint' || o === '/*jslint') {
                    error("Missing option value.", t);
                }
                obj[t.value] = false;
                t = v;
            }
        }
        if (filter) {
            assume();
        }
    }


    // We need a peek function. If it has an argument, it peeks that much farther
    // ahead. It is used to distinguish
    //     for ( var i in ...
    // from
    //     for ( var i = ...

    function peek(p) {
        var i = p || 0, j = 0, t;

        while (j <= i) {
            t = lookahead[j];
            if (!t) {
                t = lookahead[j] = lex.token();
            }
            j += 1;
        }
        return t;
    }


    // Produce the next token. It looks for programming errors.

    function advance(id, t) {
        switch (token.id) {
            case '(number)':
                if (nexttoken.id === '.') {
                    warning("A dot following a number can be confused with a decimal point.", token);
                }
                break;
            case '-':
                if (nexttoken.id === '-' || nexttoken.id === '--') {
                    warning("Confusing minusses.");
                }
                break;
            case '+':
                if (nexttoken.id === '+' || nexttoken.id === '++') {
                    warning("Confusing plusses.");
                }
                break;
        }
        if (token.type === '(string)' || token.identifier) {
            anonname = token.value;
        }

        if (id && nexttoken.id !== id) {
            if (t) {
                if (nexttoken.id === '(end)') {
                    warning("Unmatched '{a}'.", t, t.id);
                } else {
                    warning("Expected '{a}' to match '{b}' from line {c} and instead saw '{d}'.",
                        nexttoken, id, t.id, t.line, nexttoken.value);
                }
            } else if (nexttoken.type !== '(identifier)' ||
                nexttoken.value !== id) {
                warning("Expected '{a}' and instead saw '{b}'.",
                    nexttoken, id, nexttoken.value);
            }
        }
        prevtoken = token;
        token = nexttoken;
        for (; ;) {
            nexttoken = lookahead.shift() || lex.token();
            if (nexttoken.id === '(end)' || nexttoken.id === '(error)') {
                return;
            }
            if (nexttoken.type === 'special') {
                doOption();
            } else {
                if (nexttoken.id !== '(endline)') {
                    break;
                }
            }
        }
    }


    // This is the heart of JSHINT, the Pratt parser. In addition to parsing, it
    // is looking for ad hoc lint patterns. We add .fud to Pratt's model, which is
    // like .nud except that it is only used on the first token of a statement.
    // Having .fud makes it much easier to define statement-oriented languages like
    // JavaScript. I retained Pratt's nomenclature.

    // .nud     Null denotation
    // .fud     First null denotation
    // .led     Left denotation
    //  lbp     Left binding power
    //  rbp     Right binding power

    // They are elements of the parsing method called Top Down Operator Precedence.

    function expression(rbp, initial) {
        var left, isArray = false;

        if (nexttoken.id === '(end)') {
            error("Unexpected early end of program.", token);
        }

        advance();
        if (initial) {
            anonname = 'anonymous';
            funct['(verb)'] = token.value;
        }
        if (initial === true && token.fud) {
            left = token.fud();
        } else {
            if (token.nud) {
                left = token.nud();
            } else {
                if (nexttoken.type === '(number)' && token.id === '.') {
                    warning("A leading decimal point can be confused with a dot: '.{a}'.",
                        token, nexttoken.value);
                    advance();
                    return token;
                } else {
                    error("Expected an identifier and instead saw '{a}'.",
                        token, token.id);
                }
            }
            while (rbp < nexttoken.lbp) {
                isArray = token.value == 'Array';
                advance();
                if (isArray && token.id == '(' && nexttoken.id == ')') {
                    warning("Use the array literal notation [].", token);
                }
                if (token.led) {
                    left = token.led(left);
                } else {
                    error("Expected an operator and instead saw '{a}'.",
                        token, token.id);
                }
            }
        }
        return left;
    }


    // Functions for conformance of style.

    function adjacent(left, right) {
        left = left || token;
        right = right || nexttoken;
        if (option.white) {
            if (left.character !== right.from && left.line === right.line) {
                warning("Unexpected space after '{a}'.", right, left.value);
            }
        }
    }

    function nobreak(left, right) {
        left = left || token;
        right = right || nexttoken;
        if (option.white && (left.character !== right.from || left.line !== right.line)) {
            warning("Unexpected space before '{a}'.", right, right.value);
        }
    }

    function nospace(left, right) {
        left = left || token;
        right = right || nexttoken;
        if (option.white && !left.comment) {
            if (left.line === right.line) {
                adjacent(left, right);
            }
        }
    }

    function nonadjacent(left, right) {
        if (option.white) {
            left = left || token;
            right = right || nexttoken;
            if (left.line === right.line && left.character === right.from) {
                warning("Missing space after '{a}'.",
                    nexttoken, left.value);
            }
        }
    }

    function nobreaknonadjacent(left, right) {
        left = left || token;
        right = right || nexttoken;
        if (!option.laxbreak && left.line !== right.line) {
            warning("Bad line breaking before '{a}'.", right, right.id);
        } else if (option.white) {
            left = left || token;
            right = right || nexttoken;
            if (left.character === right.from) {
                warning("Missing space after '{a}'.",
                    nexttoken, left.value);
            }
        }
    }

    function indentation(bias) {
        var i;
        if (option.white && nexttoken.id !== '(end)') {
            i = indent + (bias || 0);
            if (nexttoken.from !== i) {
                warning(
                    "Expected '{a}' to have an indentation at {b} instead at {c}.",
                    nexttoken, nexttoken.value, i, nexttoken.from);
            }
        }
    }

    function nolinebreak(t) {
        t = t || token;
        if (t.line !== nexttoken.line) {
            warning("Line breaking error '{a}'.", t, t.value);
        }
    }


    function comma() {
        if (token.line !== nexttoken.line) {
            if (!option.laxbreak) {
                warning("Bad line breaking before '{a}'.", token, nexttoken.id);
            }
        } else if (token.character !== nexttoken.from && option.white) {
            warning("Unexpected space after '{a}'.", nexttoken, token.value);
        }
        advance(',');
        nonadjacent(token, nexttoken);
    }


    // Functional constructors for making the symbols that will be inherited by
    // tokens.

    function symbol(s, p) {
        var x = syntax[s];
        if (!x || typeof x !== 'object') {
            syntax[s] = x = {
                id: s,
                lbp: p,
                value: s
            };
        }
        return x;
    }


    function delim(s) {
        return symbol(s, 0);
    }


    function stmt(s, f) {
        var x = delim(s);
        x.identifier = x.reserved = true;
        x.fud = f;
        return x;
    }


    function blockstmt(s, f) {
        var x = stmt(s, f);
        x.block = true;
        return x;
    }


    function reserveName(x) {
        var c = x.id.charAt(0);
        if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) {
            x.identifier = x.reserved = true;
        }
        return x;
    }


    function prefix(s, f) {
        var x = symbol(s, 150);
        reserveName(x);
        x.nud = (typeof f === 'function') ? f : function () {
            this.right = expression(150);
            this.arity = 'unary';
            if (this.id === '++' || this.id === '--') {
                if (option.plusplus) {
                    warning("Unexpected use of '{a}'.", this, this.id);
                } else if ((!this.right.identifier || this.right.reserved) &&
                    this.right.id !== '.' && this.right.id !== '[') {
                    warning("Bad operand.", this);
                }
            }
            return this;
        };
        return x;
    }


    function type(s, f) {
        var x = delim(s);
        x.type = s;
        x.nud = f;
        return x;
    }


    function reserve(s, f) {
        var x = type(s, f);
        x.identifier = x.reserved = true;
        return x;
    }


    function reservevar(s, v) {
        return reserve(s, function () {
            if (typeof v === 'function') {
                v(this);
            }
            return this;
        });
    }


    function infix(s, f, p, w) {
        var x = symbol(s, p);
        reserveName(x);
        x.led = function (left) {
            if (!w) {
                nobreaknonadjacent(prevtoken, token);
                nonadjacent(token, nexttoken);
            }
            if (typeof f === 'function') {
                return f(left, this);
            } else {
                this.left = left;
                this.right = expression(p);
                return this;
            }
        };
        return x;
    }


    function relation(s, f) {
        var x = symbol(s, 100);
        x.led = function (left) {
            nobreaknonadjacent(prevtoken, token);
            nonadjacent(token, nexttoken);
            var right = expression(100);
            if ((left && left.id === 'NaN') || (right && right.id === 'NaN')) {
                warning("Use the isNaN function to compare with NaN.", this);
            } else if (f) {
                f.apply(this, [left, right]);
            }
            if (left.id === '!') {
                warning("Confusing use of '{a}'.", left, '!');
            }
            if (right.id === '!') {
                warning("Confusing use of '{a}'.", left, '!');
            }
            this.left = left;
            this.right = right;
            return this;
        };
        return x;
    }


    function isPoorRelation(node) {
        return node &&
            ((node.type === '(number)' && +node.value === 0) ||
                (node.type === '(string)' && node.value === '') ||
                (node.type === 'null' && !option.eqnull) ||
                node.type === 'true' ||
                node.type === 'false' ||
                node.type === 'undefined');
    }


    function assignop(s, f) {
        symbol(s, 20).exps = true;
        return infix(s, function (left, that) {
            var l;
            that.left = left;
            if (predefined[left.value] === false &&
                scope[left.value]['(global)'] === true) {
                warning("Read only.", left);
            } else if (left['function']) {
                warning("'{a}' is a function.", left, left.value);
            }
            if (left) {
                if (left.id === '.' || left.id === '[') {
                    if (!left.left || left.left.value === 'arguments') {
                        warning('Bad assignment.', that);
                    }
                    that.right = expression(19);
                    return that;
                } else if (left.identifier && !left.reserved) {
                    if (funct[left.value] === 'exception') {
                        warning("Do not assign to the exception parameter.", left);
                    }
                    that.right = expression(19);
                    return that;
                }
                if (left === syntax['function']) {
                    warning(
                        "Expected an identifier in an assignment and instead saw a function invocation.",
                        token);
                }
            }
            error("Bad assignment.", that);
        }, 20);
    }


    function bitwise(s, f, p) {
        var x = symbol(s, p);
        reserveName(x);
        x.led = (typeof f === 'function') ? f : function (left) {
            if (option.bitwise) {
                warning("Unexpected use of '{a}'.", this, this.id);
            }
            this.left = left;
            this.right = expression(p);
            return this;
        };
        return x;
    }


    function bitwiseassignop(s) {
        symbol(s, 20).exps = true;
        return infix(s, function (left, that) {
            if (option.bitwise) {
                warning("Unexpected use of '{a}'.", that, that.id);
            }
            nonadjacent(prevtoken, token);
            nonadjacent(token, nexttoken);
            if (left) {
                if (left.id === '.' || left.id === '[' ||
                    (left.identifier && !left.reserved)) {
                    expression(19);
                    return that;
                }
                if (left === syntax['function']) {
                    warning(
                        "Expected an identifier in an assignment, and instead saw a function invocation.",
                        token);
                }
                return that;
            }
            error("Bad assignment.", that);
        }, 20);
    }


    function suffix(s, f) {
        var x = symbol(s, 150);
        x.led = function (left) {
            if (option.plusplus) {
                warning("Unexpected use of '{a}'.", this, this.id);
            } else if ((!left.identifier || left.reserved) &&
                left.id !== '.' && left.id !== '[') {
                warning("Bad operand.", this);
            }
            this.left = left;
            return this;
        };
        return x;
    }


    // fnparam means that this identifier is being defined as a function
    // argument (see identifier())
    function optionalidentifier(fnparam) {
        if (nexttoken.identifier) {
            advance();
            if (token.reserved && !option.es5) {
                // `undefined` as a function param is a common pattern to protect
                // against the case when somebody does `undefined = true` and
                // help with minification. More info: https://gist.github.com/315916
                if (!fnparam || token.value != 'undefined') {
                    warning("Expected an identifier and instead saw '{a}' (a reserved word).",
                        token, token.id);
                }
            }
            return token.value;
        }
    }

    // fnparam means that this identifier is being defined as a function
    // argument
    function identifier(fnparam) {
        var i = optionalidentifier(fnparam);
        if (i) {
            return i;
        }
        if (token.id === 'function' && nexttoken.id === '(') {
            warning("Missing name in function declaration.");
        } else {
            error("Expected an identifier and instead saw '{a}'.",
                nexttoken, nexttoken.value);
        }
    }


    function reachable(s) {
        var i = 0, t;
        if (nexttoken.id !== ';' || noreach) {
            return;
        }
        for (; ;) {
            t = peek(i);
            if (t.reach) {
                return;
            }
            if (t.id !== '(endline)') {
                if (t.id === 'function') {
                    warning(
                        "Inner functions should be listed at the top of the outer function.", t);
                    break;
                }
                warning("Unreachable '{a}' after '{b}'.", t, t.value, s);
                break;
            }
            i += 1;
        }
    }


    function statement(noindent) {
        var i = indent, r, s = scope, t = nexttoken;

        // We don't like the empty statement.

        if (t.id === ';') {
            warning("Unnecessary semicolon.", t);
            advance(';');
            return;
        }

        // Is this a labelled statement?

        if (t.identifier && !t.reserved && peek().id === ':') {
            advance();
            advance(':');
            scope = Object.create(s);
            addlabel(t.value, 'label');
            if (!nexttoken.labelled) {
                warning("Label '{a}' on {b} statement.",
                    nexttoken, t.value, nexttoken.value);
            }
            if (jx.test(t.value + ':')) {
                warning("Label '{a}' looks like a javascript url.",
                    t, t.value);
            }
            nexttoken.label = t.value;
            t = nexttoken;
        }

        // Parse the statement.

        if (!noindent) {
            indentation();
        }
        r = expression(0, true);

        // Look for the final semicolon.

        if (!t.block) {
            if (!option.expr && (!r || !r.exps)) {
                warning("Expected an assignment or function call and instead saw an expression.", token);
            } else if (option.nonew && r.id === '(' && r.left.id === 'new') {
                warning("Do not use 'new' for side effects.");
            }
            if (nexttoken.id !== ';') {
                if (!option.asi && !(option.lastsemic && nexttoken.id == '}' && nexttoken.line == token.line)) {
                    warningAt("Missing semicolon.", token.line, token.from + token.value.length);
                }
            } else {
                adjacent(token, nexttoken);
                advance(';');
                nonadjacent(token, nexttoken);
            }
        }

        // Restore the indentation.

        indent = i;
        scope = s;
        return r;
    }


    function use_strict() {
        if (nexttoken.value === 'use strict') {
            if (strict_mode) {
                warning("Unnecessary \"use strict\".");
            }
            advance();
            advance(';');
            strict_mode = true;
            option.newcap = true;
            option.undef = true;
            return true;
        } else {
            return false;
        }
    }


    function statements(begin) {
        var a = [], f, p;

        while (!nexttoken.reach && nexttoken.id !== '(end)') {
            if (nexttoken.id === ';') {
                warning("Unnecessary semicolon.");
                advance(';');
            } else {
                a.push(statement());
            }
        }
        return a;
    }


    /*
     * Parses a single block. A block is a sequence of statements wrapped in
     * braces.
     *
     * ordinary - true for everything but function bodies and try blocks.
     * stmt     - true if block can be a single statement (e.g. in if/for/while).
     */
    function block(ordinary, stmt) {
        var a,
            b = inblock,
            old_indent = indent,
            m = strict_mode,
            s = scope,
            t;

        inblock = ordinary;
        scope = Object.create(scope);
        nonadjacent(token, nexttoken);
        t = nexttoken;

        if (nexttoken.id === '{') {
            advance('{');
            if (nexttoken.id !== '}' || token.line !== nexttoken.line) {
                indent += option.indent;
                while (!ordinary && nexttoken.from > indent) {
                    indent += option.indent;
                }
                if (!ordinary && !use_strict() && !m && option.strict &&
                    funct['(context)']['(global)']) {
                    warning("Missing \"use strict\" statement.");
                }
                a = statements();
                strict_mode = m;
                indent -= option.indent;
                indentation();
            }
            advance('}', t);
            indent = old_indent;
        } else if (!ordinary) {
            error("Expected '{a}' and instead saw '{b}'.",
                nexttoken, '{', nexttoken.value);
        } else {
            if (!stmt || option.curly) {
                warning("Expected '{a}' and instead saw '{b}'.",
                    nexttoken, '{', nexttoken.value);
            }

            noreach = true;
            a = [statement()];
            noreach = false;
        }
        funct['(verb)'] = null;
        scope = s;
        inblock = b;
        if (ordinary && option.noempty && (!a || a.length === 0)) {
            warning("Empty block.");
        }
        return a;
    }


    function countMember(m) {
        if (membersOnly && typeof membersOnly[m] !== 'boolean') {
            warning("Unexpected /*member '{a}'.", token, m);
        }
        if (typeof member[m] === 'number') {
            member[m] += 1;
        } else {
            member[m] = 1;
        }
    }


    function note_implied(token) {
        var name = token.value, line = token.line, a = implied[name];
        if (typeof a === 'function') {
            a = false;
        }
        if (!a) {
            a = [line];
            implied[name] = a;
        } else if (a[a.length - 1] !== line) {
            a.push(line);
        }
    }

    // Build the syntax table by declaring the syntactic elements of the language.

    type('(number)', function () {
        return this;
    });
    type('(string)', function () {
        return this;
    });

    syntax['(identifier)'] = {
        type: '(identifier)',
        lbp: 0,
        identifier: true,
        nud: function () {
            var v = this.value,
                s = scope[v],
                f;
            if (typeof s === 'function') {

                // Protection against accidental inheritance.

                s = undefined;
            } else if (typeof s === 'boolean') {
                f = funct;
                funct = functions[0];
                addlabel(v, 'var');
                s = funct;
                funct = f;
            }

            // The name is in scope and defined in the current function.

            if (funct === s) {

                //      Change 'unused' to 'var', and reject labels.

                switch (funct[v]) {
                    case 'unused':
                        funct[v] = 'var';
                        break;
                    case 'unction':
                        funct[v] = 'function';
                        this['function'] = true;
                        break;
                    case 'function':
                        this['function'] = true;
                        break;
                    case 'label':
                        warning("'{a}' is a statement label.", token, v);
                        break;
                }

                // The name is not defined in the function.  If we are in the global scope,
                // then we have an undefined variable.
                //
                // Operators typeof and delete do not raise runtime errors even if the base
                // object of a reference is null so no need to display warning if we're
                // inside of typeof or delete.

            } else if (funct['(global)']) {
                if (anonname != 'typeof' && anonname != 'delete' &&
                    option.undef && typeof predefined[v] !== 'boolean') {
                    warning("'{a}' is not defined.", token, v);
                }
                note_implied(token);

                // If the name is already defined in the current
                // function, but not as outer, then there is a scope error.

            } else {
                switch (funct[v]) {
                    case 'closure':
                    case 'function':
                    case 'var':
                    case 'unused':
                        warning("'{a}' used out of scope.", token, v);
                        break;
                    case 'label':
                        warning("'{a}' is a statement label.", token, v);
                        break;
                    case 'outer':
                    case 'global':
                        break;
                    default:

                        // If the name is defined in an outer function, make an outer entry, and if
                        // it was unused, make it var.

                        if (s === true) {
                            funct[v] = true;
                        } else if (s === null) {
                            warning("'{a}' is not allowed.", token, v);
                            note_implied(token);
                        } else if (typeof s !== 'object') {

                            // Operators typeof and delete do not raise runtime errors even if the base object of
                            // a reference is null so no need to display warning if we're inside of typeof or delete.

                            if (anonname != 'typeof' && anonname != 'delete' && option.undef) {
                                warning("'{a}' is not defined.", token, v);
                            } else {
                                funct[v] = true;
                            }
                            note_implied(token);
                        } else {
                            switch (s[v]) {
                                case 'function':
                                case 'unction':
                                    this['function'] = true;
                                    s[v] = 'closure';
                                    funct[v] = s['(global)'] ? 'global' : 'outer';
                                    break;
                                case 'var':
                                case 'unused':
                                    s[v] = 'closure';
                                    funct[v] = s['(global)'] ? 'global' : 'outer';
                                    break;
                                case 'closure':
                                case 'parameter':
                                    funct[v] = s['(global)'] ? 'global' : 'outer';
                                    break;
                                case 'label':
                                    warning("'{a}' is a statement label.", token, v);
                            }
                        }
                }
            }
            return this;
        },
        led: function () {
            error("Expected an operator and instead saw '{a}'.",
                nexttoken, nexttoken.value);
        }
    };

    type('(regexp)', function () {
        return this;
    });


    // ECMAScript parser

    delim('(endline)');
    delim('(begin)');
    delim('(end)').reach = true;
    delim('</').reach = true;
    delim('<!');
    delim('<!--');
    delim('-->');
    delim('(error)').reach = true;
    delim('}').reach = true;
    delim(')');
    delim(']');
    delim('"').reach = true;
    delim("'").reach = true;
    delim(';');
    delim(':').reach = true;
    delim(',');
    delim('#');
    delim('@');
    reserve('else');
    reserve('case').reach = true;
    reserve('catch');
    reserve('default').reach = true;
    reserve('finally');
    reservevar('arguments', function (x) {
        if (strict_mode && funct['(global)']) {
            warning("Strict violation.", x);
        }
    });
    reservevar('eval');
    reservevar('false');
    reservevar('Infinity');
    reservevar('NaN');
    reservevar('null');
    reservevar('this', function (x) {
        if (strict_mode && ((funct['(statement)'] &&
            funct['(name)'].charAt(0) > 'Z') || funct['(global)'])) {
            warning("Strict violation.", x);
        }
    });
    reservevar('true');
    reservevar('undefined');
    assignop('=', 'assign', 20);
    assignop('+=', 'assignadd', 20);
    assignop('-=', 'assignsub', 20);
    assignop('*=', 'assignmult', 20);
    assignop('/=', 'assigndiv', 20).nud = function () {
        error("A regular expression literal can be confused with '/='.");
    };
    assignop('%=', 'assignmod', 20);
    bitwiseassignop('&=', 'assignbitand', 20);
    bitwiseassignop('|=', 'assignbitor', 20);
    bitwiseassignop('^=', 'assignbitxor', 20);
    bitwiseassignop('<<=', 'assignshiftleft', 20);
    bitwiseassignop('>>=', 'assignshiftright', 20);
    bitwiseassignop('>>>=', 'assignshiftrightunsigned', 20);
    infix('?', function (left, that) {
        that.left = left;
        that.right = expression(10);
        advance(':');
        that['else'] = expression(10);
        return that;
    }, 30);

    infix('||', 'or', 40);
    infix('&&', 'and', 50);
    bitwise('|', 'bitor', 70);
    bitwise('^', 'bitxor', 80);
    bitwise('&', 'bitand', 90);
    relation('==', function (left, right) {
        var eqnull = option.eqnull &&
            (left.value == 'null' || right.value == 'null');

        if (!eqnull && option.eqeqeq) {
            warning("Expected '{a}' and instead saw '{b}'.",
                this, '===', '==');
        } else if (isPoorRelation(left)) {
            warning("Use '{a}' to compare with '{b}'.",
                this, '===', left.value);
        } else if (isPoorRelation(right)) {
            warning("Use '{a}' to compare with '{b}'.",
                this, '===', right.value);
        }
        return this;
    });
    relation('===');
    relation('!=', function (left, right) {
        if (option.eqeqeq) {
            warning("Expected '{a}' and instead saw '{b}'.",
                this, '!==', '!=');
        } else if (isPoorRelation(left)) {
            warning("Use '{a}' to compare with '{b}'.",
                this, '!==', left.value);
        } else if (isPoorRelation(right)) {
            warning("Use '{a}' to compare with '{b}'.",
                this, '!==', right.value);
        }
        return this;
    });
    relation('!==');
    relation('<');
    relation('>');
    relation('<=');
    relation('>=');
    bitwise('<<', 'shiftleft', 120);
    bitwise('>>', 'shiftright', 120);
    bitwise('>>>', 'shiftrightunsigned', 120);
    infix('in', 'in', 120);
    infix('instanceof', 'instanceof', 120);
    infix('+', function (left, that) {
        var right = expression(130);
        if (left && right && left.id === '(string)' && right.id === '(string)') {
            left.value += right.value;
            left.character = right.character;
            if (!option.scripturl && jx.test(left.value)) {
                warning("JavaScript URL.", left);
            }
            return left;
        }
        that.left = left;
        that.right = right;
        return that;
    }, 130);
    prefix('+', 'num');
    prefix('+++', function () {
        warning("Confusing pluses.");
        this.right = expression(150);
        this.arity = 'unary';
        return this;
    });
    infix('+++', function (left) {
        warning("Confusing pluses.");
        this.left = left;
        this.right = expression(130);
        return this;
    }, 130);
    infix('-', 'sub', 130);
    prefix('-', 'neg');
    prefix('---', function () {
        warning("Confusing minuses.");
        this.right = expression(150);
        this.arity = 'unary';
        return this;
    });
    infix('---', function (left) {
        warning("Confusing minuses.");
        this.left = left;
        this.right = expression(130);
        return this;
    }, 130);
    infix('*', 'mult', 140);
    infix('/', 'div', 140);
    infix('%', 'mod', 140);

    suffix('++', 'postinc');
    prefix('++', 'preinc');
    syntax['++'].exps = true;

    suffix('--', 'postdec');
    prefix('--', 'predec');
    syntax['--'].exps = true;
    prefix('delete',
        function () {
            var p = expression(0);
            if (!p || (p.id !== '.' && p.id !== '[')) {
                warning("Variables should not be deleted.");
            }
            this.first = p;
            return this;
        }).exps = true;

    prefix('~', function () {
        if (option.bitwise) {
            warning("Unexpected '{a}'.", this, '~');
        }
        expression(150);
        return this;
    });

    prefix('!', function () {
        this.right = expression(150);
        this.arity = 'unary';
        if (bang[this.right.id] === true) {
            warning("Confusing use of '{a}'.", this, '!');
        }
        return this;
    });
    prefix('typeof', 'typeof');
    prefix('new', function () {
        var c = expression(155), i;
        if (c && c.id !== 'function') {
            if (c.identifier) {
                c['new'] = true;
                switch (c.value) {
                    case 'Object':
                        warning("Use the object literal notation {}.", token);
                        break;
                    case 'Number':
                    case 'String':
                    case 'Boolean':
                    case 'Math':
                    case 'JSON':
                        warning("Do not use {a} as a constructor.", token, c.value);
                        break;
                    case 'Function':
                        if (!option.evil) {
                            warning("The Function constructor is eval.");
                        }
                        break;
                    case 'Date':
                    case 'RegExp':
                        break;
                    default:
                        if (c.id !== 'function') {
                            i = c.value.substr(0, 1);
                            if (option.newcap && (i < 'A' || i > 'Z')) {
                                warning("A constructor name should start with an uppercase letter.", token);
                            }
                        }
                }
            } else {
                if (c.id !== '.' && c.id !== '[' && c.id !== '(') {
                    warning("Bad constructor.", token);
                }
            }
        } else {
            if (!option.supernew) {
                warning("Weird construction. Delete 'new'.", this);
            }
        }
        adjacent(token, nexttoken);
        if (nexttoken.id !== '(' && !option.supernew) {
            warning("Missing '()' invoking a constructor.");
        }
        this.first = c;
        return this;
    });
    syntax['new'].exps = true;

    prefix('void').exps = true;

    infix('.', function (left, that) {
        adjacent(prevtoken, token);
        nobreak();
        var m = identifier();
        if (typeof m === 'string') {
            countMember(m);
        }
        that.left = left;
        that.right = m;
        if (option.noarg && left && left.value === 'arguments' &&
            (m === 'callee' || m === 'caller')) {
            warning("Avoid arguments.{a}.", left, m);
        } else if (!option.evil && left && left.value === 'document' &&
            (m === 'write' || m === 'writeln')) {
            warning("document.write can be a form of eval.", left);
        }
        if (!option.evil && (m === 'eval' || m === 'execScript')) {
            warning('eval is evil.');
        }
        return that;
    }, 160, true);

    infix('(',
        function (left, that) {
            if (prevtoken.id !== '}' && prevtoken.id !== ')') {
                nobreak(prevtoken, token);
            }
            nospace();
            if (option.immed && !left.immed && left.id === 'function') {
                warning("Wrap an immediate function invocation in parentheses " +
                    "to assist the reader in understanding that the expression " +
                    "is the result of a function, and not the function itself.");
            }
            var n = 0,
                p = [];
            if (left) {
                if (left.type === '(identifier)') {
                    if (left.value.match(/^[A-Z]([A-Z0-9_$]*[a-z][A-Za-z0-9_$]*)?$/)) {
                        if (left.value !== 'Number' && left.value !== 'String' &&
                            left.value !== 'Boolean' &&
                            left.value !== 'Date') {
                            if (left.value === 'Math') {
                                warning("Math is not a function.", left);
                            } else if (option.newcap) {
                                warning(
                                    "Missing 'new' prefix when invoking a constructor.", left);
                            }
                        }
                    }
                }
            }
            if (nexttoken.id !== ')') {
                for (; ;) {
                    p[p.length] = expression(10);
                    n += 1;
                    if (nexttoken.id !== ',') {
                        break;
                    }
                    comma();
                }
            }
            advance(')');
            nospace(prevtoken, token);
            if (typeof left === 'object') {
                if (left.value === 'parseInt' && n === 1) {
                    warning("Missing radix parameter.", left);
                }
                if (!option.evil) {
                    if (left.value === 'eval' || left.value === 'Function' ||
                        left.value === 'execScript') {
                        warning("eval is evil.", left);
                    } else if (p[0] && p[0].id === '(string)' &&
                        (left.value === 'setTimeout' ||
                            left.value === 'setInterval')) {
                        warning(
                            "Implied eval is evil. Pass a function instead of a string.", left);
                    }
                }
                if (!left.identifier && left.id !== '.' && left.id !== '[' &&
                    left.id !== '(' && left.id !== '&&' && left.id !== '||' &&
                    left.id !== '?') {
                    warning("Bad invocation.", left);
                }
            }
            that.left = left;
            return that;
        }, 155, true).exps = true;

    prefix('(', function () {
        nospace();
        if (nexttoken.id === 'function') {
            nexttoken.immed = true;
        }
        var v = expression(0);
        advance(')', this);
        nospace(prevtoken, token);
        if (option.immed && v.id === 'function') {
            if (nexttoken.id === '(') {
                warning(
                    "Move the invocation into the parens that contain the function.", nexttoken);
            } else {
                warning(
                    "Do not wrap function literals in parens unless they are to be immediately invoked.",
                    this);
            }
        }
        return v;
    });

    infix('[', function (left, that) {
        nobreak(prevtoken, token);
        nospace();
        var e = expression(0), s;
        if (e && e.type === '(string)') {
            if (!option.evil && (e.value === 'eval' || e.value === 'execScript')) {
                warning("eval is evil.", that);
            }
            countMember(e.value);
            if (!option.sub && ix.test(e.value)) {
                s = syntax[e.value];
                if (!s || !s.reserved) {
                    warning("['{a}'] is better written in dot notation.",
                        e, e.value);
                }
            }
        }
        advance(']', that);
        nospace(prevtoken, token);
        that.left = left;
        that.right = e;
        return that;
    }, 160, true);

    prefix('[', function () {
        var b = token.line !== nexttoken.line;
        this.first = [];
        if (b) {
            indent += option.indent;
            if (nexttoken.from === indent + option.indent) {
                indent += option.indent;
            }
        }
        while (nexttoken.id !== '(end)') {
            while (nexttoken.id === ',') {
                warning("Extra comma.");
                advance(',');
            }
            if (nexttoken.id === ']') {
                break;
            }
            if (b && token.line !== nexttoken.line) {
                indentation();
            }
            this.first.push(expression(10));
            if (nexttoken.id === ',') {
                comma();
                if (nexttoken.id === ']' && !option.es5) {
                    warning("Extra comma.", token);
                    break;
                }
            } else {
                break;
            }
        }
        if (b) {
            indent -= option.indent;
            indentation();
        }
        advance(']', this);
        return this;
    }, 160);


    function property_name() {
        var id = optionalidentifier(true);
        if (!id) {
            if (nexttoken.id === '(string)') {
                id = nexttoken.value;
                advance();
            } else if (nexttoken.id === '(number)') {
                id = nexttoken.value.toString();
                advance();
            }
        }
        return id;
    }


    function functionparams() {
        var i, t = nexttoken, p = [];
        advance('(');
        nospace();
        if (nexttoken.id === ')') {
            advance(')');
            nospace(prevtoken, token);
            return;
        }
        for (; ;) {
            i = identifier(true);
            p.push(i);
            addlabel(i, 'parameter');
            if (nexttoken.id === ',') {
                comma();
            } else {
                advance(')', t);
                nospace(prevtoken, token);
                return p;
            }
        }
    }


    function doFunction(i, statement) {
        var f,
            oldOption = option,
            oldScope = scope;

        option = Object.create(option);
        scope = Object.create(scope);

        funct = {
            '(name)'     : i || '"' + anonname + '"',
            '(line)'     : nexttoken.line,
            '(context)'  : funct,
            '(breakage)' : 0,
            '(loopage)'  : 0,
            '(scope)'    : scope,
            '(statement)': statement
        };
        f = funct;
        token.funct = funct;
        functions.push(funct);
        if (i) {
            addlabel(i, 'function');
        }
        funct['(params)'] = functionparams();

        block(false);
        scope = oldScope;
        option = oldOption;
        funct['(last)'] = token.line;
        funct = funct['(context)'];
        return f;
    }


    (function (x) {
        x.nud = function () {
            var b, f, i, j, p, seen = {}, t;

            b = token.line !== nexttoken.line;
            if (b) {
                indent += option.indent;
                if (nexttoken.from === indent + option.indent) {
                    indent += option.indent;
                }
            }
            for (; ;) {
                if (nexttoken.id === '}') {
                    break;
                }
                if (b) {
                    indentation();
                }
                if (nexttoken.value === 'get' && peek().id !== ':') {
                    advance('get');
                    if (!option.es5) {
                        error("get/set are ES5 features.");
                    }
                    i = property_name();
                    if (!i) {
                        error("Missing property name.");
                    }
                    t = nexttoken;
                    adjacent(token, nexttoken);
                    f = doFunction();
                    if (!option.loopfunc && funct['(loopage)']) {
                        warning("Don't make functions within a loop.", t);
                    }
                    p = f['(params)'];
                    if (p) {
                        warning("Unexpected parameter '{a}' in get {b} function.", t, p[0], i);
                    }
                    adjacent(token, nexttoken);
                    advance(',');
                    indentation();
                    advance('set');
                    j = property_name();
                    if (i !== j) {
                        error("Expected {a} and instead saw {b}.", token, i, j);
                    }
                    t = nexttoken;
                    adjacent(token, nexttoken);
                    f = doFunction();
                    p = f['(params)'];
                    if (!p || p.length !== 1 || p[0] !== 'value') {
                        warning("Expected (value) in set {a} function.", t, i);
                    }
                } else {
                    i = property_name();
                    if (typeof i !== 'string') {
                        break;
                    }
                    advance(':');
                    nonadjacent(token, nexttoken);
                    expression(10);
                }
                if (seen[i] === true) {
                    warning("Duplicate member '{a}'.", nexttoken, i);
                }
                seen[i] = true;
                countMember(i);
                if (nexttoken.id === ',') {
                    comma();
                    if (nexttoken.id === ',') {
                        warning("Extra comma.", token);
                    } else if (nexttoken.id === '}' && !option.es5) {
                        warning("Extra comma.", token);
                    }
                } else {
                    break;
                }
            }
            if (b) {
                indent -= option.indent;
                indentation();
            }
            advance('}', this);
            return this;
        };
        x.fud = function () {
            error("Expected to see a statement and instead saw a block.", token);
        };
    }(delim('{')));

    var varstatement = stmt('var', function (prefix) {
        // JavaScript does not have block scope. It only has function scope. So,
        // declaring a variable in a block can have unexpected consequences.
        var id, name, value;

        if (funct['(onevar)'] && option.onevar) {
            warning("Too many var statements.");
        } else if (!funct['(global)']) {
            funct['(onevar)'] = true;
        }
        this.first = [];
        for (; ;) {
            nonadjacent(token, nexttoken);
            id = identifier();
            if (funct['(global)'] && predefined[id] === false) {
                warning("Redefinition of '{a}'.", token, id);
            }
            addlabel(id, 'unused');
            if (prefix) {
                break;
            }
            name = token;
            this.first.push(token);
            if (nexttoken.id === '=') {
                nonadjacent(token, nexttoken);
                advance('=');
                nonadjacent(token, nexttoken);
                if (nexttoken.id === 'undefined') {
                    warning("It is not necessary to initialize '{a}' to 'undefined'.", token, id);
                }
                if (peek(0).id === '=' && nexttoken.identifier) {
                    error("Variable {a} was not declared correctly.",
                        nexttoken, nexttoken.value);
                }
                value = expression(0);
                name.first = value;
            }
            if (nexttoken.id !== ',') {
                break;
            }
            comma();
        }
        return this;
    });
    varstatement.exps = true;

    blockstmt('function', function () {
        if (inblock) {
            warning(
                "Function declarations should not be placed in blocks. Use a function expression or move the statement to the top of the outer function.", token);

        }
        var i = identifier();
        adjacent(token, nexttoken);
        addlabel(i, 'unction');
        doFunction(i, true);
        if (nexttoken.id === '(' && nexttoken.line === token.line) {
            error(
                "Function declarations are not invocable. Wrap the whole function invocation in parens.");
        }
        return this;
    });

    prefix('function', function () {
        var i = optionalidentifier();
        if (i) {
            adjacent(token, nexttoken);
        } else {
            nonadjacent(token, nexttoken);
        }
        doFunction(i);
        if (!option.loopfunc && funct['(loopage)']) {
            warning("Don't make functions within a loop.");
        }
        return this;
    });

    blockstmt('if', function () {
        var t = nexttoken;
        advance('(');
        nonadjacent(this, t);
        nospace();
        expression(20);
        if (nexttoken.id === '=') {
            if (!option.boss) {
                warning("Expected a conditional expression and instead saw an assignment.");
            }
            advance('=');
            expression(20);
        }
        advance(')', t);
        nospace(prevtoken, token);
        block(true, true);
        if (nexttoken.id === 'else') {
            nonadjacent(token, nexttoken);
            advance('else');
            if (nexttoken.id === 'if' || nexttoken.id === 'switch') {
                statement(true);
            } else {
                block(true, true);
            }
        }
        return this;
    });

    blockstmt('try', function () {
        var b, e, s;

        block(false);
        if (nexttoken.id === 'catch') {
            advance('catch');
            nonadjacent(token, nexttoken);
            advance('(');
            s = scope;
            scope = Object.create(s);
            e = nexttoken.value;
            if (nexttoken.type !== '(identifier)') {
                warning("Expected an identifier and instead saw '{a}'.",
                    nexttoken, e);
            } else {
                addlabel(e, 'exception');
            }
            advance();
            advance(')');
            block(false);
            b = true;
            scope = s;
        }
        if (nexttoken.id === 'finally') {
            advance('finally');
            block(false);
            return;
        } else if (!b) {
            error("Expected '{a}' and instead saw '{b}'.",
                nexttoken, 'catch', nexttoken.value);
        }
        return this;
    });

    blockstmt('while',
        function () {
            var t = nexttoken;
            funct['(breakage)'] += 1;
            funct['(loopage)'] += 1;
            advance('(');
            nonadjacent(this, t);
            nospace();
            expression(20);
            if (nexttoken.id === '=') {
                if (!option.boss) {
                    warning("Expected a conditional expression and instead saw an assignment.");
                }
                advance('=');
                expression(20);
            }
            advance(')', t);
            nospace(prevtoken, token);
            block(true, true);
            funct['(breakage)'] -= 1;
            funct['(loopage)'] -= 1;
            return this;
        }).labelled = true;

    reserve('with');

    blockstmt('switch',
        function () {
            var t = nexttoken,
                g = false;
            funct['(breakage)'] += 1;
            advance('(');
            nonadjacent(this, t);
            nospace();
            this.condition = expression(20);
            advance(')', t);
            nospace(prevtoken, token);
            nonadjacent(token, nexttoken);
            t = nexttoken;
            advance('{');
            nonadjacent(token, nexttoken);
            indent += option.indent;
            this.cases = [];
            for (; ;) {
                switch (nexttoken.id) {
                    case 'case':
                        switch (funct['(verb)']) {
                            case 'break':
                            case 'case':
                            case 'continue':
                            case 'return':
                            case 'switch':
                            case 'throw':
                                break;
                            default:
                                // You can tell JSHint that you don't use break intentionally by
                                // adding a comment /* falls through */ on a line just before
                                // the next `case`.
                                if (!ft.test(lines[nexttoken.line - 2])) {
                                    warning(
                                        "Expected a 'break' statement before 'case'.",
                                        token);
                                }
                        }
                        indentation(-option.indent);
                        advance('case');
                        this.cases.push(expression(20));
                        g = true;
                        advance(':');
                        funct['(verb)'] = 'case';
                        break;
                    case 'default':
                        switch (funct['(verb)']) {
                            case 'break':
                            case 'continue':
                            case 'return':
                            case 'throw':
                                break;
                            default:
                                if (!ft.test(lines[nexttoken.line - 2])) {
                                    warning(
                                        "Expected a 'break' statement before 'default'.",
                                        token);
                                }
                        }
                        indentation(-option.indent);
                        advance('default');
                        g = true;
                        advance(':');
                        break;
                    case '}':
                        indent -= option.indent;
                        indentation();
                        advance('}', t);
                        if (this.cases.length === 1 || this.condition.id === 'true' ||
                            this.condition.id === 'false') {
                            warning("This 'switch' should be an 'if'.", this);
                        }
                        funct['(breakage)'] -= 1;
                        funct['(verb)'] = undefined;
                        return;
                    case '(end)':
                        error("Missing '{a}'.", nexttoken, '}');
                        return;
                    default:
                        if (g) {
                            switch (token.id) {
                                case ',':
                                    error("Each value should have its own case label.");
                                    return;
                                case ':':
                                    statements();
                                    break;
                                default:
                                    error("Missing ':' on a case clause.", token);
                            }
                        } else {
                            error("Expected '{a}' and instead saw '{b}'.",
                                nexttoken, 'case', nexttoken.value);
                        }
                }
            }
        }).labelled = true;

    stmt('debugger',
        function () {
            if (!option.debug) {
                warning("All 'debugger' statements should be removed.");
            }
            return this;
        }).exps = true;

    (function () {
        var x = stmt('do', function () {
            funct['(breakage)'] += 1;
            funct['(loopage)'] += 1;
            this.first = block(true);
            advance('while');
            var t = nexttoken;
            nonadjacent(token, t);
            advance('(');
            nospace();
            expression(20);
            if (nexttoken.id === '=') {
                if (!option.boss) {
                    warning("Expected a conditional expression and instead saw an assignment.");
                }
                advance('=');
                expression(20);
            }
            advance(')', t);
            nospace(prevtoken, token);
            funct['(breakage)'] -= 1;
            funct['(loopage)'] -= 1;
            return this;
        });
        x.labelled = true;
        x.exps = true;
    }());

    blockstmt('for',
        function () {
            var s, t = nexttoken;
            funct['(breakage)'] += 1;
            funct['(loopage)'] += 1;
            advance('(');
            nonadjacent(this, t);
            nospace();
            if (peek(nexttoken.id === 'var' ? 1 : 0).id === 'in') {
                if (nexttoken.id === 'var') {
                    advance('var');
                    varstatement.fud.call(varstatement, true);
                } else {
                    switch (funct[nexttoken.value]) {
                        case 'unused':
                            funct[nexttoken.value] = 'var';
                            break;
                        case 'var':
                            break;
                        default:
                            warning("Bad for in variable '{a}'.",
                                nexttoken, nexttoken.value);
                    }
                    advance();
                }
                advance('in');
                expression(20);
                advance(')', t);
                s = block(true, true);
                if (option.forin && (s.length > 1 || typeof s[0] !== 'object' ||
                    s[0].value !== 'if')) {
                    warning("The body of a for in should be wrapped in an if statement to filter unwanted properties from the prototype.", this);
                }
                funct['(breakage)'] -= 1;
                funct['(loopage)'] -= 1;
                return this;
            } else {
                if (nexttoken.id !== ';') {
                    if (nexttoken.id === 'var') {
                        advance('var');
                        varstatement.fud.call(varstatement);
                    } else {
                        for (; ;) {
                            expression(0, 'for');
                            if (nexttoken.id !== ',') {
                                break;
                            }
                            comma();
                        }
                    }
                }
                nolinebreak(token);
                advance(';');
                if (nexttoken.id !== ';') {
                    expression(20);
                    if (nexttoken.id === '=') {
                        if (!option.boss) {
                            warning("Expected a conditional expression and instead saw an assignment.");
                        }
                        advance('=');
                        expression(20);
                    }
                }
                nolinebreak(token);
                advance(';');
                if (nexttoken.id === ';') {
                    error("Expected '{a}' and instead saw '{b}'.",
                        nexttoken, ')', ';');
                }
                if (nexttoken.id !== ')') {
                    for (; ;) {
                        expression(0, 'for');
                        if (nexttoken.id !== ',') {
                            break;
                        }
                        comma();
                    }
                }
                advance(')', t);
                nospace(prevtoken, token);
                block(true, true);
                funct['(breakage)'] -= 1;
                funct['(loopage)'] -= 1;
                return this;
            }
        }).labelled = true;


    stmt('break',
        function () {
            var v = nexttoken.value;
            if (funct['(breakage)'] === 0) {
                warning("Unexpected '{a}'.", nexttoken, this.value);
            }
            nolinebreak(this);
            if (nexttoken.id !== ';') {
                if (token.line === nexttoken.line) {
                    if (funct[v] !== 'label') {
                        warning("'{a}' is not a statement label.", nexttoken, v);
                    } else if (scope[v] !== funct) {
                        warning("'{a}' is out of scope.", nexttoken, v);
                    }
                    this.first = nexttoken;
                    advance();
                }
            }
            reachable('break');
            return this;
        }).exps = true;


    stmt('continue',
        function () {
            var v = nexttoken.value;
            if (funct['(breakage)'] === 0) {
                warning("Unexpected '{a}'.", nexttoken, this.value);
            }
            nolinebreak(this);
            if (nexttoken.id !== ';') {
                if (token.line === nexttoken.line) {
                    if (funct[v] !== 'label') {
                        warning("'{a}' is not a statement label.", nexttoken, v);
                    } else if (scope[v] !== funct) {
                        warning("'{a}' is out of scope.", nexttoken, v);
                    }
                    this.first = nexttoken;
                    advance();
                }
            } else if (!funct['(loopage)']) {
                warning("Unexpected '{a}'.", nexttoken, this.value);
            }
            reachable('continue');
            return this;
        }).exps = true;


    stmt('return',
        function () {
            nolinebreak(this);
            if (nexttoken.id === '(regexp)') {
                warning("Wrap the /regexp/ literal in parens to disambiguate the slash operator.");
            }
            if (nexttoken.id !== ';' && !nexttoken.reach) {
                nonadjacent(token, nexttoken);
                this.first = expression(20);
            }
            reachable('return');
            return this;
        }).exps = true;


    stmt('throw',
        function () {
            nolinebreak(this);
            nonadjacent(token, nexttoken);
            this.first = expression(20);
            reachable('throw');
            return this;
        }).exps = true;

    //  Superfluous reserved words

    reserve('class');
    reserve('const');
    reserve('enum');
    reserve('export');
    reserve('extends');
    reserve('import');
    reserve('super');

    reserve('let');
    reserve('yield');
    reserve('implements');
    reserve('interface');
    reserve('package');
    reserve('private');
    reserve('protected');
    reserve('public');
    reserve('static');


    // Parse JSON

    function jsonValue() {

        function jsonObject() {
            var o = {}, t = nexttoken;
            advance('{');
            if (nexttoken.id !== '}') {
                for (; ;) {
                    if (nexttoken.id === '(end)') {
                        error("Missing '}' to match '{' from line {a}.",
                            nexttoken, t.line);
                    } else if (nexttoken.id === '}') {
                        warning("Unexpected comma.", token);
                        break;
                    } else if (nexttoken.id === ',') {
                        error("Unexpected comma.", nexttoken);
                    } else if (nexttoken.id !== '(string)') {
                        warning("Expected a string and instead saw {a}.",
                            nexttoken, nexttoken.value);
                    }
                    if (o[nexttoken.value] === true) {
                        warning("Duplicate key '{a}'.",
                            nexttoken, nexttoken.value);
                    } else if (nexttoken.value === '__proto__') {
                        warning("Stupid key '{a}'.",
                            nexttoken, nexttoken.value);
                    } else {
                        o[nexttoken.value] = true;
                    }
                    advance();
                    advance(':');
                    jsonValue();
                    if (nexttoken.id !== ',') {
                        break;
                    }
                    advance(',');
                }
            }
            advance('}');
        }

        function jsonArray() {
            var t = nexttoken;
            advance('[');
            if (nexttoken.id !== ']') {
                for (; ;) {
                    if (nexttoken.id === '(end)') {
                        error("Missing ']' to match '[' from line {a}.",
                            nexttoken, t.line);
                    } else if (nexttoken.id === ']') {
                        warning("Unexpected comma.", token);
                        break;
                    } else if (nexttoken.id === ',') {
                        error("Unexpected comma.", nexttoken);
                    }
                    jsonValue();
                    if (nexttoken.id !== ',') {
                        break;
                    }
                    advance(',');
                }
            }
            advance(']');
        }

        switch (nexttoken.id) {
            case '{':
                jsonObject();
                break;
            case '[':
                jsonArray();
                break;
            case 'true':
            case 'false':
            case 'null':
            case '(number)':
            case '(string)':
                advance();
                break;
            case '-':
                advance('-');
                if (token.character !== nexttoken.from) {
                    warning("Unexpected space after '-'.", token);
                }
                adjacent(token, nexttoken);
                advance('(number)');
                break;
            default:
                error("Expected a JSON value.", nexttoken);
        }
    }


    // The actual JSHINT function itself.

    var itself = function (s, o, g) {
        var a, i, k;
        JSHINT.errors = [];
        predefined = Object.create(standard);
        combine(predefined, g || {});
        if (o) {
            a = o.predef;
            if (a) {
                if (Array.isArray(a)) {
                    for (i = 0; i < a.length; i += 1) {
                        predefined[a[i]] = true;
                    }
                } else if (typeof a === 'object') {
                    k = Object.keys(a);
                    for (i = 0; i < k.length; i += 1) {
                        predefined[k[i]] = !!a[k];
                    }
                }
            }
            option = o;
        } else {
            option = {};
        }
        option.indent = option.indent || 4;
        option.maxerr = option.maxerr || 50;

        tab = '';
        for (i = 0; i < option.indent; i += 1) {
            tab += ' ';
        }
        indent = 1;
        global = Object.create(predefined);
        scope = global;
        funct = {
            '(global)': true,
            '(name)': '(global)',
            '(scope)': scope,
            '(breakage)': 0,
            '(loopage)': 0
        };
        functions = [funct];
        urls = [];
        src = false;
        stack = null;
        member = {};
        membersOnly = null;
        implied = {};
        inblock = false;
        lookahead = [];
        jsonmode = false;
        warnings = 0;
        lex.init(s);
        prereg = true;
        strict_mode = false;

        prevtoken = token = nexttoken = syntax['(begin)'];
        assume();

        try {
            advance();
            switch (nexttoken.id) {
                case '{':
                case '[':
                    option.laxbreak = true;
                    jsonmode = true;
                    jsonValue();
                    break;
                default:
                    if (nexttoken.value === 'use strict') {
                        if (!option.globalstrict) {
                            warning("Use the function form of \"use strict\".");
                        }
                        use_strict();
                    }
                    statements('lib');
            }
            advance('(end)');
        } catch (e) {
            if (e) {
                JSHINT.errors.push({
                    reason    : e.message,
                    line      : e.line || nexttoken.line,
                    character : e.character || nexttoken.from
                }, null);
            }
        }
        return JSHINT.errors.length === 0;
    };


    // Data summary.

    JSHINT.data = function () {

        var data = {functions: []}, fu, globals, implieds = [], f, i, j,
            members = [], n, unused = [], v;
        if (JSHINT.errors.length) {
            data.errors = JSHINT.errors;
        }

        if (jsonmode) {
            data.json = true;
        }

        for (n in implied) {
            if (is_own(implied, n)) {
                implieds.push({
                    name: n,
                    line: implied[n]
                });
            }
        }
        if (implieds.length > 0) {
            data.implieds = implieds;
        }

        if (urls.length > 0) {
            data.urls = urls;
        }

        globals = Object.keys(scope);
        if (globals.length > 0) {
            data.globals = globals;
        }

        for (i = 1; i < functions.length; i += 1) {
            f = functions[i];
            fu = {};
            for (j = 0; j < functionicity.length; j += 1) {
                fu[functionicity[j]] = [];
            }
            for (n in f) {
                if (is_own(f, n) && n.charAt(0) !== '(') {
                    v = f[n];
                    if (v === 'unction') {
                        v = 'unused';
                    }
                    if (Array.isArray(fu[v])) {
                        fu[v].push(n);
                        if (v === 'unused') {
                            unused.push({
                                name: n,
                                line: f['(line)'],
                                'function': f['(name)']
                            });
                        }
                    }
                }
            }
            for (j = 0; j < functionicity.length; j += 1) {
                if (fu[functionicity[j]].length === 0) {
                    delete fu[functionicity[j]];
                }
            }
            fu.name = f['(name)'];
            fu.param = f['(params)'];
            fu.line = f['(line)'];
            fu.last = f['(last)'];
            data.functions.push(fu);
        }

        if (unused.length > 0) {
            data.unused = unused;
        }

        members = [];
        for (n in member) {
            if (typeof member[n] === 'number') {
                data.member = member;
                break;
            }
        }

        return data;
    };

    JSHINT.report = function (option) {
        var data = JSHINT.data();

        var a = [], c, e, err, f, i, k, l, m = '', n, o = [], s;

        function detail(h, array) {
            var b, i, singularity;
            if (array) {
                o.push('<div><i>' + h + '</i> ');
                array = array.sort();
                for (i = 0; i < array.length; i += 1) {
                    if (array[i] !== singularity) {
                        singularity = array[i];
                        o.push((b ? ', ' : '') + singularity);
                        b = true;
                    }
                }
                o.push('</div>');
            }
        }


        if (data.errors || data.implieds || data.unused) {
            err = true;
            o.push('<div id=errors><i>Error:</i>');
            if (data.errors) {
                for (i = 0; i < data.errors.length; i += 1) {
                    c = data.errors[i];
                    if (c) {
                        e = c.evidence || '';
                        o.push('<p>Problem' + (isFinite(c.line) ? ' at line ' +
                            c.line + ' character ' + c.character : '') +
                            ': ' + c.reason.entityify() +
                            '</p><p class=evidence>' +
                            (e && (e.length > 80 ? e.slice(0, 77) + '...' :
                                e).entityify()) + '</p>');
                    }
                }
            }

            if (data.implieds) {
                s = [];
                for (i = 0; i < data.implieds.length; i += 1) {
                    s[i] = '<code>' + data.implieds[i].name + '</code>&nbsp;<i>' +
                        data.implieds[i].line + '</i>';
                }
                o.push('<p><i>Implied global:</i> ' + s.join(', ') + '</p>');
            }

            if (data.unused) {
                s = [];
                for (i = 0; i < data.unused.length; i += 1) {
                    s[i] = '<code><u>' + data.unused[i].name + '</u></code>&nbsp;<i>' +
                        data.unused[i].line + '</i> <code>' +
                        data.unused[i]['function'] + '</code>';
                }
                o.push('<p><i>Unused variable:</i> ' + s.join(', ') + '</p>');
            }
            if (data.json) {
                o.push('<p>JSON: bad.</p>');
            }
            o.push('</div>');
        }

        if (!option) {

            o.push('<br><div id=functions>');

            if (data.urls) {
                detail("URLs<br>", data.urls, '<br>');
            }

            if (data.json && !err) {
                o.push('<p>JSON: good.</p>');
            } else if (data.globals) {
                o.push('<div><i>Global</i> ' +
                    data.globals.sort().join(', ') + '</div>');
            } else {
                o.push('<div><i>No new global variables introduced.</i></div>');
            }

            for (i = 0; i < data.functions.length; i += 1) {
                f = data.functions[i];

                o.push('<br><div class=function><i>' + f.line + '-' +
                    f.last + '</i> ' + (f.name || '') + '(' +
                    (f.param ? f.param.join(', ') : '') + ')</div>');
                detail('<big><b>Unused</b></big>', f.unused);
                detail('Closure', f.closure);
                detail('Variable', f['var']);
                detail('Exception', f.exception);
                detail('Outer', f.outer);
                detail('Global', f.global);
                detail('Label', f.label);
            }

            if (data.member) {
                a = Object.keys(data.member);
                if (a.length) {
                    a = a.sort();
                    m = '<br><pre id=members>/*members ';
                    l = 10;
                    for (i = 0; i < a.length; i += 1) {
                        k = a[i];
                        n = k.name();
                        if (l + n.length > 72) {
                            o.push(m + '<br>');
                            m = '    ';
                            l = 1;
                        }
                        l += n.length + 2;
                        if (data.member[k] === 1) {
                            n = '<i>' + n + '</i>';
                        }
                        if (i < a.length - 1) {
                            n += ', ';
                        }
                        m += n;
                    }
                    o.push(m + '<br>*/</pre>');
                }
                o.push('</div>');
            }
        }
        return o.join('');
    };
    JSHINT.jshint = itself;

    JSHINT.edition = '2011-04-16';

    return itself;
});

// Make JSHINT a CommonJS module, if possible.
if (typeof exports === "object") {
    exports.JSHINT = JSHINT;
}

if (typeof bootstrap !== "undefined") {
    bootstrap("core/jshint", function (require, exports) {
        "use strict";
        exports.JSHINT = JSHINT;
    });
}


}})
;
//*/
montageDefine("6364dae","core/serialization/serializer/montage-ast",{dependencies:["montage","mousse/serialization/ast"],factory:function(require,exports,module){var Montage = require("montage").Montage;
var Value = require("mousse/serialization/ast").Value;

/**
 * ElementReference
 *
 * @extends Value
 */
var ElementReference = Montage.specialize.call(Value, {

    constructor: {
        value: function ElementReference() {}
    },

    initWithRootAndId: {
        value: function(root, id) {
            Value.call(this, root, id);
            return this;
        }
    },

    _getSerializationValue: {
        value: function() {
            return {"#": this.value};
        }
    }
});

/**
 * ModuleReference
 *
 * @extends Value
 */
var ModuleReference = Montage.specialize.call(Value, {

    constructor: {
        value: function ModuleReference() {}
    },

    initWithRootAndModuleId: {
        value: function(root, moduleId) {
            Value.call(this, root, moduleId);
            return this;
        }
    },

    _getSerializationValue: {
        value: function() {
            return {"%": this.value};
        }
    }
});

exports.ElementReference = ElementReference;
exports.ModuleReference = ModuleReference;

}})
;
//*/
montageDefine("6364dae","core/serialization/serializer/properties-serializer",{dependencies:["montage"],factory:function(require,exports,module){var Montage = require("montage").Montage;

var PropertiesSerializer = Montage.specialize.call(Object, {
    _malker: {value: null},
    _visitor: {value: null},
    _object: {value: null},

    constructor: {
        value: function PropertiesSerializer() {}
    },

    initWithMalkerAndVisitorAndObject: {
        value: function(malker, visitor, object) {
            this._malker = malker;
            this._visitor = visitor;
            this._object = object;

            return this;
        }
    },

    addObject: {
        value: function(object) {
            if (typeof object === "object") {
                this._malker.visit(object);

                return object;
            }
        }
    },

    addObjectReference: {
        value: function(object) {
            var builder = this._visitor.builder,
                labeler = this._visitor.labeler,
                label = labeler.getObjectLabel(object);

            var reference = Object.create(ObjectReference);
            reference.reference = builder.createReference(label);

            return reference;
        }
    },

    set: {
        value: function(propertyName, value, type) {
            this._visitor.setProperty(this._malker, propertyName, value, type);
        }
    },

    setAll: {
        value: function() {
            this._visitor.setSerializableObjectProperties(
                this._malker, this._object);
        }
    }
});

var ObjectReference = {
    thisIsAReferenceCreatedByMontageSerializer: true,
    reference: null
};

exports.PropertiesSerializer = PropertiesSerializer;

}})
;
//*/
montageDefine("6364dae","core/serialization/serializer/self-serializer",{dependencies:["montage"],factory:function(require,exports,module){var Montage = require("montage").Montage;

var SelfSerializer = Montage.specialize.call(Object, {
    _malker: {value: null},
    _visitor: {value: null},
    _object: {value: null},

    constructor: {
        value: function SelfSerializer() {}
    },

    initWithMalkerAndVisitorAndObject: {
        value: function(malker, visitor, object) {
            this._malker = malker;
            this._visitor = visitor;
            this._object = object;

            return this;
        }
    },

    getObjectLabel: {
        value: function(object) {
            return this._visitor.labeler.getObjectLabel(object);
        }
    },

    addObject: {
        value: function(object) {
            if (typeof object === "object") {
                this._malker.visit(object);

                return object;
            }
        }
    },

    addObjectReference: {
        value: function(object) {
            var builder = this._visitor.builder,
                labeler = this._visitor.labeler,
                label = labeler.getObjectLabel(object);

            return {
                thisIsAReferenceCreatedByMontageSerializer: true,
                reference: builder.createObjectReference(label)
            };
        }
    },

    setProperty: {
        value: function(propertyName, value, type) {
            var builder = this._visitor.builder,
                propertiesObject;

            propertiesObject = builder.top.getProperty("properties");
            builder.push(propertiesObject);

            this._visitor.setProperty(this._malker, propertyName, value, type);

            builder.pop();
        }
    },

    setAllProperties: {
        value: function() {
            var builder = this._visitor.builder,
                propertiesObject;

            propertiesObject = builder.top.getProperty("properties");
            builder.push(propertiesObject);

            this._visitor.setSerializableObjectProperties(this._malker, this._object);

            builder.pop();
        }
    },

    setUnit: {
        value: function(unitName) {
            this._visitor.setObjectCustomUnit(this._malker, this._object, unitName);
        }
    },

    setAllUnits: {
        value: function(unitName) {
            this._visitor.setObjectCustomUnits(this._malker, this._object);
        }
    }
});

exports.SelfSerializer = SelfSerializer;

}})
;
//*/
montageDefine("6364dae","core/serialization/serializer/unit-serializer",{dependencies:["montage"],factory:function(require,exports,module){var Montage = require("montage").Montage;

function UnitSerializer(visitor, malker, object) {
    this._visitor = visitor;
    this._malker = malker;
    this._object = object;
};

var UnitSerializer = Montage.specialize.call(Object, {
    _malker: {value: null},
    _visitor: {value: null},
    _object: {value: null},

    constructor: {
        value: function UnitSerializer() {}
    },

    initWithMalkerAndVisitorAndObject: {
        value: function(malker, visitor, object) {
            this._malker = malker;
            this._visitor = visitor;
            this._object = object;

            return this;
        }
    },

    getObjectLabel: {
        value: function(object) {
            this.addObjectReference(object);

            return this._visitor.labeler.getObjectLabel(object);
        }
    },

    addObject: {
        value: function(object) {
            if (typeof object === "object") {
                this._malker.visit(object);

                return object;
            }
        }
    },

    addObjectReference: {
        value: function(object) {
            var builder = this._visitor.builder,
                labeler = this._visitor.labeler,
                label = labeler.getObjectLabel(object);

            return {
                thisIsAReferenceCreatedByMontageSerializer: true,
                reference: builder.createObjectReference(label)
            };
        }
    }
});

var ObjectReference = {
    thisIsAReferenceCreatedByMontageSerializer: true,
    reference: null
};

exports.UnitSerializer = UnitSerializer;

}})
;
//*/
montageDefine("6364dae","ui/loader.reel/loader",{dependencies:["core/core","ui/component","core/logger","core/event/event-manager"],factory:function(require,exports,module){/* <copyright>
Copyright (c) 2012, Motorola Mobility LLC.
All Rights Reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

* Redistributions of source code must retain the above copyright notice,
  this list of conditions and the following disclaimer.

* Redistributions in binary form must reproduce the above copyright notice,
  this list of conditions and the following disclaimer in the documentation
  and/or other materials provided with the distribution.

* Neither the name of Motorola Mobility LLC nor the names of its
  contributors may be used to endorse or promote products derived from this
  software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
</copyright> */

 /**
    @module montage/ui/loader
 */

var Montage = require("core/core").Montage,
    Component = require("ui/component").Component,
    logger = require("core/logger").logger("loader"),
    defaultEventManager = require("core/event/event-manager").defaultEventManager,
    bootstrappingTimeoutPropertyName = "_montageStartBootstrappingTimeout",
    MONTAGE_BOOTSTRAPPER_ELEMENT_ID = "montage-app-bootstrapper",
    MONTAGE_LOADER_ELEMENT_ID = "montage-app-loader",
    BOOTSTRAPPING_CLASS_NAME = "montage-app-bootstrapping",
    LOADING_CLASS_NAME = "montage-app-loading",
    LOADED_CLASS_NAME = "montage-app-loaded",
    PRELOADING = 0,
    BOOTSTRAPPING = 1,
    LOADING = 2,
    LOADED = 3;

/**
 @class Loader
 @extends Component
 */

exports.Loader = Component.specialize( /** @lends Loader# */ {

    constructor: {
        value: function Loader() {
            this.super();
        }
    },

    // Configuration Properties

/**
    The main module to require
*/
    mainModule: {
        value: "ui/main.reel"
    },

/**
    The name of the object to read from the mainModule exports
*/
    mainName: {
        value: "Main"
    },

/**
    Whether or not to include framework modules in the collection of required and initialized modules
*/
    includeFrameworkModules: {
        value: false
    },

/**
    The minimum amount of time the bootstrapping indicator must be shown for
*/
    minimumBootstrappingDuration: {
        value: 1500
    },

/**
    The minimum amount of time the loading indicator must be shown for
*/
    minimumLoadingDuration: {
        value: 2000
    },

    _initializedModules: {
        value: null
    },

/**
    The initialized modules...FIXME
*/
    initializedModules: {
        dependencies: ["includeFrameworkModules"],
        enumerable: false,
        get: function() {
            if (!this._initializedModules || this.includeFrameworkModules) {
                return this._initializedModules;
            } else {
                return this._initializedModules.slice(this._frameworkModuleCount - 1);
            }
        },
        set: function(value) {
            this._initializedModules = value;
        }
    },

    _requiredModules: {
        value: null
    },

/**
    The required modules for this application ... FIXME
*/
    requiredModules: {
        dependencies: ["includeFrameworkModules"],
        enumerable: false,
        get: function() {
            if (!this._requiredModules || this.includeFrameworkModules) {
                return this._requiredModules;
            } else {
                return this._requiredModules.slice(this._frameworkModuleCount - 1);
            }
        },
        set: function(value) {
            this._requiredModules = value;
        }
    },

    // States

    _currentStage: {
        value: PRELOADING
    },

/**
    Current loading stage.
*/
    currentStage: {
        get: function() {
            return this._currentStage;
        },
        set: function(value) {
            if (value === this._currentStage) {
                return;
            }

            if (logger.isDebug) {
                logger.debug(this, "CURRENT STAGE: " + value);
            }
            this._currentStage = value;
            this.needsDraw = true;
        }
    },

    _readyToShowLoader: {
        value: false
    },

/**
    Boolean that specifies whether the loader is loading the application's main component.
*/
    isLoadingMainComponent: {
        value: null
    },

/**
    Specifies whether the loader is ready to show the loading graphic...FIXME
*/
    readyToShowLoader: {
        get: function() {
            return this._readyToShowLoader;
        },
        set: function(value) {
            if (value !== this._readyToShowLoader) {
                return;
            }

            this._readyToShowLoader = value;
            this.needsDraw = true;
        }
    },

/**
    Specifies whether the main component is ready to be displayed.
*/
    readyToShowMainComponent: {
        get: function() {
            return !!this._mainComponent;
        }
    },

    // Internal Properties

    _frameworkModuleCount: {
        enumerable: false,
        value: null
    },

    hasTemplate: {
        enumerable: false,
        value: false
    },

    _mainComponent: {
        value: null
    },

    _mainComponentEnterDocument: {
        value: null
    },

    _showLoadingTimeout: {
        enumerable: false,
        value: null
    },

    _showMainComponentTimeout: {
        enumerable: false,
        value: null
    },

    // Implementation

    templateDidLoad: {
        value: function() {

            if (logger.isDebug) {
                logger.debug(this, "templateDidLoad");
            }

            if (!this.element) {
                this.element = document.documentElement;
                this.attachToParentComponent();
            }
            this.readyToShowLoader = true;

            var timing = document._montageTiming,
                remainingBootstrappingDelay,
                self = this;

            if (timing.bootstrappingStartTime) {

                if (logger.isDebug) {
                    logger.debug(this, "had already started bootstrapping");
                }

                // We just found out we were bootstrapping…
                this.currentStage = BOOTSTRAPPING;

                // but we're technically done bootstrapping and can show loader now if we should
                timing.bootstrappingEndTime = Date.now();

                remainingBootstrappingDelay = this.minimumBootstrappingDuration - (timing.bootstrappingEndTime - timing.bootstrappingStartTime)

                if (remainingBootstrappingDelay > 0) {
                    if (logger.isDebug) {
                        logger.debug(this, "still need to show bootstrapper for another " + remainingBootstrappingDelay + "ms");
                    }
                    this._showLoadingTimeout = setTimeout(function() {
                        self._revealLoader();
                    }, remainingBootstrappingDelay);
                } else {
                    this._revealLoader();
                }
            } else {
                // The bootstrapper hasn't decided to show yet, that's fine let's try to load main
                if (logger.isDebug) {
                    logger.debug(this, "bootstrapping has not started yet…");
                }
                this._loadMainComponent();
            }
        }
    },

    _revealLoader: {
        value: function() {

            if (logger.isDebug) {
                logger.debug(this, "_revealLoader");
            }

            this.currentStage = LOADING;
            document._montageTiming.loadingStartTime = Date.now();

            var i,
                loaderElement = document.getElementById(MONTAGE_LOADER_ELEMENT_ID),
                children,
                iChild,
                iComponent;

            if (loaderElement) {
                children = loaderElement.children;

                for (i = 0; (iChild = children[i]); i++) {
                    if ((iComponent = iChild.component)) {
                        iComponent.attachToParentComponent();
                        iComponent.needsDraw = true;
                    }
                }
            }

        }
    },

    _revealMainComponent: {
        value: function() {
            if (logger.isDebug) {
                logger.debug(this, "_revealMainComponent");
            }
            this.currentStage = LOADED;
        }
    },

    _loadMainComponent: {
        value: function() {
            if (logger.isDebug) {
                logger.debug(this, "_loadMainComponent");
            }
            this.isLoadingMainComponent = true;
            var self = this;
            window.require.async(this.mainModule)
            .then(function (exports) {
                if (!(self.mainName in exports)) {
                    throw new Error(self.mainName + " was not found in " + self.mainModule);
                }
                return self._mainLoadedCallback(exports);
            })
            .done();
        }
    },

    _mainLoadedCallback: {
        value: function(exports) {
            if (logger.isDebug) {
                logger.debug(this, "_mainLoadedCallback");
            }
            // We've loaded the class for the mainComponent
            // instantiate it and lets find out what else we need to load
            // based on its template
            this._mainComponent = exports[this.mainName].create();
            this._mainComponentEnterDocument = this._mainComponent.enterDocument;
            this._mainComponent.enterDocument = this.mainComponentEnterDocument.bind(this);
            this._mainComponent.setElementWithParentComponent(document.createElement("div"), this);
            this._mainComponent.attachToParentComponent();
            this._mainComponent.needsDraw = true;
        }
    },

    mainComponentEnterDocument: {
        value: function() {
            var self = this,
                insertionElement;

            if (logger.isDebug) {
                logger.debug(this, "main preparing to draw");
            }
            this.isLoadingMainComponent = false;

            // Determine old content
            this._contentToRemove = document.createRange();

            // If installing classnames on the documentElement (to affect as high a level as possible)
            // make sure content only ends up inside the body
            insertionElement = this.element === document.documentElement ? document.body : this.element;
            this._contentToRemove.selectNodeContents(insertionElement);

            // Add new content so mainComponent can actually draw
            this.childComponents = [this._mainComponent];
            insertionElement.appendChild(this._mainComponent.element);

            var startBootstrappingTimeout = document[bootstrappingTimeoutPropertyName],
                timing = document._montageTiming,
                remainingBootstrappingDelay,
                remainingLoadingDelay;

            // if we hadn't even started to say we were bootstrapping…
            if (!timing.bootstrappingStartTime) {
                // don't bother showing bootstrapping, just show the mainComponent
                if (logger.isDebug) {
                    logger.debug(this, "bootstrapper never shown");
                }
                clearTimeout(startBootstrappingTimeout);
                startBootstrappingTimeout = null;
                this._revealMainComponent();
            }

            // Otherwise if we started bootstrapping, but never started loading…
            else if (timing.bootstrappingStartTime && !timing.loadingStartTime) {

                // don't ever show the loader and wait until we've bootstrapped for the minimumBootstrappingDuration
                clearTimeout(this._showLoadingTimeout);
                this._showLoadingTimeout = null;

                timing.bootstrappingEndTime = Date.now();

                if ((remainingBootstrappingDelay = this.minimumBootstrappingDuration - (timing.bootstrappingEndTime - timing.bootstrappingStartTime)) > 0) {
                    if (logger.isDebug) {
                        logger.debug(this, "show bootstrapper for another " + remainingBootstrappingDelay + "ms");
                    }
                    this._showMainComponentTimeout = setTimeout(function () {
                        if (logger.isDebug) {
                            logger.debug(this, "ok, shown bootstrapper long enough");
                        }
                        self._revealMainComponent();
                    }, remainingBootstrappingDelay);
                } else {
                    setTimeout(function () {
                        if (logger.isDebug) {
                            logger.debug(this, "ok, showing bootstrapper now");
                        }
                        self._revealMainComponent();
                    }, 0);
                }
            }

            //Otherwise, we apparently started showing loading progress…
            else if (timing.loadingStartTime) {
                timing.loadingEndTime = Date.now();

                // wait until we've loaded for the minimumLoadingDuration
                // TODO this is not precise, but it's a decent start for scheduling the delay
                if ((remainingLoadingDelay = this.minimumLoadingDuration - (timing.loadingEndTime - timing.loadingStartTime)) > 0) {
                    if (logger.isDebug) {
                        logger.debug(this, "show loader for another " + remainingLoadingDelay + "ms");
                    }
                    this._showMainComponentTimeout = setTimeout(function () {
                        if (logger.isDebug) {
                            logger.debug(this, "ok, shown loader long enough");
                        }
                        self._revealMainComponent();
                    }, remainingLoadingDelay);
                } else {
                    // or we showed loading long enough, go ahead and show mainComponent
                    this._revealMainComponent();
                }
            }

            var mainComponent = this._mainComponent;

            // Remove the connection from the Loader to the DOM tree and add
            // the main component to the component tree.
            defaultEventManager.unregisterEventHandlerForElement(this.element);
            mainComponent.attachToParentComponent();

            // When the main component enters the document it is loaded, update
            // the currentStage to reflect that situation if we're behind.
            if (this.currentStage < LOADED) {
                this.currentStage = LOADED;
            }

            mainComponent.enterDocument = this._mainComponentEnterDocument;
            if (mainComponent.enterDocument) {
                return mainComponent.enterDocument.apply(mainComponent, arguments);
            }
        }
    },

/**
    Boolean that specifies whether to remove the loading content when load is completed
*/
    removeContentOnLoad: {
        value: true
    },

    _forceContentRemoval: {
        enumerable: false,
        value: false
    },

    _contentToRemove: {
        enumerable: false,
        value: null
    },

/**
    Forces a manual removal of loading content
*/
    removeContent: {
        value: function() {
            this._forceContentRemoval = true;
            this.needsDraw = true;
        }
    },

    draw: {
        value: function() {
            // start loading the mainComponent if we haven't already
            if (!this.readyToShowMainComponent && !this.isLoadingMainComponent) {
                if (logger.isDebug) {
                    logger.debug(this, "draw; start loading main component");
                }
                this._loadMainComponent();
            }

            // Reflect the current loading stage
            if (LOADING === this.currentStage) {

                this.element.classList.remove(BOOTSTRAPPING_CLASS_NAME);
                this.element.classList.add(LOADING_CLASS_NAME);

            } else if (LOADED === this.currentStage && this._contentToRemove) {

                this.element.classList.remove(BOOTSTRAPPING_CLASS_NAME);
                this.element.classList.remove(LOADING_CLASS_NAME);

                if(this.removeContentOnLoad || this._forceContentRemoval) {
                    this._contentToRemove.extractContents();
                    this._contentToRemove.detach();
                    this._contentToRemove = null;
                }

                this.element.classList.add(LOADED_CLASS_NAME);

                var loadEvent = document.createEvent("CustomEvent");
                loadEvent.initCustomEvent("componentLoaded", true, true, this._mainComponent);
                this.dispatchEvent(loadEvent, true, true);
                // Remove the Loader from the component tree, we can only do
                // this after the last draw the Loader needs to make.
                this.detachFromParentComponent();
            }

        }
    }

});

}})
;
//*/
montageDefine("6364dae","ui/component",{dependencies:["montage","core/target","core/bindings","core/template","core/document-resources","core/gate","core/promise","core/logger","core/event/event-manager","collections/set","core/application"],factory:function(require,exports,module){/* <copyright>
Copyright (c) 2012, Motorola Mobility LLC.
All Rights Reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

* Redistributions of source code must retain the above copyright notice,
  this list of conditions and the following disclaimer.

* Redistributions in binary form must reproduce the above copyright notice,
  this list of conditions and the following disclaimer in the documentation
  and/or other materials provided with the distribution.

* Neither the name of Motorola Mobility LLC nor the names of its
  contributors may be used to endorse or promote products derived from this
  software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
</copyright> */
/*global Element */
/**
 * @module montage/ui/component
 * @requires montage
 * @requires montage/ui/template
 * @requires montage/core/gate
 * @requires montage/core/logger | component
 * @requires montage/core/logger | drawing
 * @requires montage/core/event/event-manager
 */
var Montage = require("montage").Montage,
    Target = require("core/target").Target,
    Bindings = require("core/bindings").Bindings,
    Template = require("core/template").Template,
    DocumentResources = require("core/document-resources").DocumentResources,
    Gate = require("core/gate").Gate,
    Promise = require("core/promise").Promise,
    logger = require("core/logger").logger("component"),
    drawPerformanceLogger = require("core/logger").logger("Drawing performance"),
    drawLogger = require("core/logger").logger("drawing"),
    defaultEventManager = require("core/event/event-manager").defaultEventManager,
    Set = require("collections/set");

/**
 * @requires montage/ui/component-description
 */
/**
 * @class Component
 * @classdesc Base class for all Montage components.
 * @extends Montage
 */
var Component = exports.Component = Target.specialize(/** @lends module:montage/ui/component.Component# */ {
    DOM_ARG_ATTRIBUTE: {value: "data-arg"},

    constructor: {
        value: function Component() {
            this.super();
            this._isComponentExpanded = false;
            this._isTemplateLoaded = false;
            this._isTemplateInstantiated = false;
            this._isComponentTreeLoaded = false;
        }
    },

    /**
     * The delegate of this component
     * @type {Object}
     * @default null
    */
    delegate: {
        value: null
    },

    /**
     * The templateObjects property is populated by the template. It is a map of all the instances present in the
     * template's serialization keyed by their label. If the templateObjects is initialized prior to template load then
     * if one it's keys matches a label in the serialization that value is used rather than the what is defined in the
     * template.
     * @type {Object}
     * @default null
     */
    templateObjects: {
        serializable: false,
        value: null
    },

    _nextTarget: {
        value: null
    },

    /**
     * The next Target to consider in the event target chain
     *
     * Currently, components themselves do not allow this chain to be broken;
     * setting a component's nextTarget to a falsy value will cause nextTarget
     * to resolve as the parentComponent.
     *
     * To interrupt the propagation path a Target that accepts a falsy
     * nextTarget needs to be set at a component's nextTarget.
     */
    nextTarget: {
        get: function () {
            return this._nextTarget || this.parentComponent;
        },
        set: function (value) {
            this._nextTarget = value;
        }
    },

    _ownerDocumentPart: {
        value: null
    },

    _templateDocumentPart: {
        value: null
    },

    _domArguments: {
        value: null
    },

    /**
     * Dispatch the actionEvent this component is configured to emit upon interaction
     * @private
     */
    _dispatchActionEvent: {
        value: function() {
            this.dispatchEvent(this.createActionEvent());
        },
        enumerable: false
    },

    /**
     * Convenience to create a custom event named "action"
     * @function
     * @returns and event to dispatch upon interaction
     */
    createActionEvent: {
        value: function() {
            var actionEvent = document.createEvent("CustomEvent");
            actionEvent.initCustomEvent("action", true, true, null);
            return actionEvent;
        }
    },

    /**
     * The gate controlling the canDraw() response of the component.
     * @type {Gate}
     */
    canDrawGate: {
        get: function() {
            if (!this._canDrawGate) {
                this._canDrawGate = new Gate().initWithDelegate(this);
                this._canDrawGate.setField("componentTreeLoaded", false);
            }
            return this._canDrawGate;
        },
        enumerable: false
    },

    /**
     * @private
     */
    _blockDrawGate: {
        value: null
    },

    /**
     * The gate controlling whether the component will ask to draw.
     * @type {Gate}
     * @private
     */
    blockDrawGate: {
        enumerable: false,
        get: function() {
            if (!this._blockDrawGate) {
                this._blockDrawGate = new Gate().initWithDelegate(this);
                this._blockDrawGate.setField("element", false);
                this._blockDrawGate.setField("drawRequested", false);
            }
            return this._blockDrawGate;
        }
    },

    /**
     * @private
     */
    _firstDraw: {
        enumerable: false,
        value: true
    },

    /**
     * @private
     */
    _completedFirstDraw: {
        enumerable: false,
        value: false
    },

    originalElement: {
        value: null
    },

    /**
     * @private
     */
    _element: {
        enumerable: false,
        value: null
    },

    /**
     * The element of the component as defined in it's template.
     * ```json
     * {
     *    "component": {
     *        "properties": {
     *            "element": {"#": "datamontageid"}
     *        }
     *    }
     * }
     * ```
     * At prepareForDraw the element is replaced by the template if the component has one.
     * @type {DOMElement}
     * @default null
     */
    element: {
        get: function() {
            return this._element;
        },
        set: function(value) {
            if (value == null) {
                console.warn("Tried to set element of ", this, " to ", value);
                return;
            }

            if (this.isDeserializing) {
                this.eventManager.registerEventHandlerForElement(this, value);

                // if this component has a template and has been already instantiated then assume the value is the template.
                if (this._isTemplateInstantiated) {
                    // this is important for component extension, we don't want to override template element
                    if (!this._templateElement) {
                        this._templateElement = value;
                    }
                } else {
                    this._element = value;
                    if (!this.blockDrawGate.value && this._element) {
                        this.blockDrawGate.setField("element", true);
                    }
                }
            } else if (!this._firstDraw) {
                // If a draw has happened then at some point the element has been set
                console.error("Cannot change element of ", this, " after it has been set");
                return;
            } else {
                this.eventManager.registerEventHandlerForElement(this, value);

                this._element = value;
                if (!this.blockDrawGate.value && this._element) {
                    this.blockDrawGate.setField("element", true);
                }
            }
            this._initializeClassListFromElement(value);
        }
    },

    getElementId: {
        value: function() {
            var element = this._element;

            if (element) {
                return element.getAttribute("data-montage-id");
            }
        }
    },

    _initDomArguments: {
        value: function() {
            var candidates,
                domArguments = {},
                name,
                node,
                element = this.element;

            candidates = element.querySelectorAll("*[" + this.DOM_ARG_ATTRIBUTE + "]");

            // Need to make sure that we filter dom args that are for nested
            // components and not for this component.
            nextCandidate:
            for (var i = 0, candidate; (candidate = candidates[i]); i++) {
                node = candidate;
                while ((node = node.parentNode) !== element) {
                    // This candidate is inside another component so skip it.
                    if (node.component) {
                        continue nextCandidate;
                    }
                }
                this._findAndDetachComponents(candidate);
                candidate.parentNode.removeChild(candidate);
                name = candidate.getAttribute(this.DOM_ARG_ATTRIBUTE);
                candidate.removeAttribute(this.DOM_ARG_ATTRIBUTE);
                domArguments[name] = candidate;
            }

            this._domArguments = domArguments;
        }
    },

    getDomArgumentNames: {
        value: function() {
            return Object.keys(this._domArguments);
        }
    },

    /**
     * When a Dom Argument is extracted from a Component it is no longer
     * available
     *
     * @param {String} name The name of the argument
     * @returns The element
     */
    extractDomArgument: {
        value: function(name) {
            var argument;

            argument = this._domArguments[name];
            delete this._domArguments[name];

            return argument;
        }
    },

    _getDomArgument: {
        value: function(element, name) {
            var candidates,
                node,
                elementId,
                serialization,
                labels;

            candidates = element.querySelectorAll("*[" + this.DOM_ARG_ATTRIBUTE + "='" + name + "']");

            // Make sure that the argument we find is indeed part of element and
            // not an argument from an inner component.
            nextCandidate:
            for (var i = 0, candidate; (candidate = candidates[i]); i++) {
                node = candidate;
                while ((node = node.parentNode) !== element) {
                    elementId = this._template.getElementId(node);

                    // Check if this node is an element of a component.
                    // TODO: Make this operation faster
                    if (elementId) {
                        serialization = this._template.getSerialization();
                        labels = serialization.getSerializationLabelsWithElements(
                            elementId);

                        if (labels.length > 0) {
                            // This candidate is inside another component so
                            // skip it.
                            continue nextCandidate;
                        }
                    }
                }
                return candidate;
            }
        }
    },

    getTemplateParameterArgument: {
        value: function(template, name) {
            var element,
                range,
                argument;

            element = template.getElementById(this.getElementId());

            if (name === "*") {
                range = template.document.createRange();
                range.selectNodeContents(element);
                argument = range.cloneContents();
            } else {
                argument = this._getDomArgument(element, name).cloneNode(true);
                argument.removeAttribute(this.DOM_ARG_ATTRIBUTE);
            }

            return argument;
        }
    },

    setElementWithParentComponent: {
        value: function(element, parent) {
            this._alternateParentComponent = parent;
            if (this.element !== element) {
                this.element = element;
            }
        }
    },

    // access to the Application object
    /**
     * Convenience to access the application object.
     * @type {Application}
    */
    application: {
        enumerable: false,
        get: function() {
            return require("core/application").application;
        }
    },

    /**
     * Convenience to access the defaultEventManager object.
     * @type {EventManager}
     */
    eventManager: {
        enumerable: false,
        get: function() {
            return defaultEventManager;
        }
    },

    /**
     * Convenience to access the rootComponent object.
     * @type {RootComponent}
     */
    rootComponent: {
        enumerable: false,
        get: function() {
            return rootComponent;
        }
    },

    /**
     * @function
     * @returns targetElementController
     */
    elementControllerFromEvent: {
        enumerable: false,
        value: function(event, targetElementController) {
            return targetElementController;
        }
    },

    _alternateParentComponent: {
        value: null
    },

    /**
     * @private
     */
    __parentComponent: {
        value: null
    },

    _parentComponent: {
        set: function(value) {
            this.__parentComponent = value;
            this.dispatchOwnPropertyChange("parentComponent", value);
        },
        get: function() {
            return this.__parentComponent;
        }
    },

    /**
     * The parent component is found by walking up the DOM tree from the node returned by the <i>element</i> property.
     * If we find a parentNode that has a controller then we return this controller.
     * Returns undefined if this is the rootComponent.
     * @function
     * @returns undefined or cachedParentComponent
     */
    // TODO store the value and delete it after draw
    parentComponent: {
        enumerable: false,
        get: function() {
            return this._parentComponent;
        }
    },

    findParentComponent: {
        value: function() {
            var anElement = this.element,
                aParentNode,
                eventManager = this.eventManager;
            if (anElement) {
                while ((aParentNode = anElement.parentNode) != null && eventManager.eventHandlerForElement(aParentNode) == null) {
                    anElement = aParentNode;
                }
                return aParentNode ? eventManager.eventHandlerForElement(aParentNode) : this._alternateParentComponent;
            }
        }
    },

    querySelectorComponent: {
        value: function(selector) {
            if (typeof selector !== "string") {
                throw "querySelectorComponent: Selector needs to be a string.";
            }

            // \s*(?:@([^>\s]+)) leftHandOperand [<label>]
            // \s*(>)?\s* operator [>] (if undefined it's a space)
            // @([^>\s]+) rightHandOperand [<label>]
            // (.*) rest
            var matches = selector.match(/^\s*(?:@([^>\s]+))?(?:\s*(>)?\s*@([^>\s]+)(.*))?$/);
            if (!matches) {
                throw "querySelectorComponent: Syntax error \"" + selector + "\"";
            }

            var childComponents = this.childComponents,
                leftHandOperand = matches[1],
                operator = matches[2] || " ",
                rightHandOperand = matches[3],
                rest = matches[4],
                found,
                i,
                childComponent;

            if (leftHandOperand) {
                rest = rightHandOperand ? "@"+rightHandOperand + rest : "";

                for (i = 0, childComponent; (childComponent = childComponents[i]); i++) {
                    if (leftHandOperand === Montage.getInfoForObject(childComponent).label) {
                        if (rest) {
                            return childComponent.querySelectorComponent(rest);
                        } else {
                            return childComponent;
                        }
                    } else {
                        found = childComponent.querySelectorComponent(selector);
                        if (found) {
                            return found;
                        }
                    }
                }
            } else {
                for (i = 0, childComponent; (childComponent = childComponents[i]); i++) {
                    if (rightHandOperand === Montage.getInfoForObject(childComponent).label) {
                        if (rest) {
                            return childComponent.querySelectorComponent(rest);
                        } else {
                            return childComponent;
                        }
                    }
                }
            }

            return null;
        }
    },

    querySelectorAllComponent: {
        value: function(selector, owner) {
            if (typeof selector !== "string") {
                throw "querySelectorComponent: Selector needs to be a string.";
            }

            // (@([^>\s]+)? leftHandOperand [<label>]
            // \s*(>)?\s* operator [>] (if undefined it's a space)
            // @([^>\s]+) rightHandOperand [<label>]
            // (.*) rest
            var matches = selector.match(/^\s*(?:@([^>\s]+))?(?:\s*(>)?\s*@([^>\s]+)(.*))?$/);
            if (!matches) {
                throw "querySelectorComponent: Syntax error \"" + selector + "\"";
            }

            var childComponents = this.childComponents,
                leftHandOperand = matches[1],
                operator = matches[2] || " ",
                rightHandOperand = matches[3],
                rest = matches[4],
                found = [],
                i,
                childComponent;

            if (leftHandOperand) {
                rest = rightHandOperand ? "@"+rightHandOperand + rest : "";
                for (i = 0, childComponent; (childComponent = childComponents[i]); i++) {
                    if (leftHandOperand === Montage.getInfoForObject(childComponent).label && (!owner || owner === childComponent.ownerComponent)) {
                        if (rest) {
                            found = found.concat(childComponent.querySelectorAllComponent(rest));
                        } else {
                            found.push(childComponent);
                        }
                    } else {
                        found = found.concat(childComponent.querySelectorAllComponent(selector, owner));
                    }
                }
            } else {
                for (i = 0, childComponent; (childComponent = childComponents[i]); i++) {
                    if (rightHandOperand === Montage.getInfoForObject(childComponent).label && (!owner || owner === childComponent.ownerComponent)) {
                        if (rest) {
                            found = found.concat(childComponent.querySelectorAllComponent(rest, owner));
                        } else {
                            found.push(childComponent);
                        }
                    }
                }
            }

            return found;
        }
    },

    /**
     * @type {Template}
     * @default null
     */
    template: {
        enumerable: false,
        value: null
    },

    /**
     * Specifies whether the component has an HTML template file associated with it.
     * @type {Boolean}
     * @default {Boolean} true
     */
    hasTemplate: {
        enumerable: false,
        value: true
    },

    /**
     * @private
     * @type {String}
     */
    _templateModuleId: {
        serializable: false,
        value: null
    },

    _template: {
        value: null
    },

    // Tree level necessary for ordering drawing re: parent-child
    _treeLevel: {
        value: 0
    },

    /**
     * @private
     * @deprecated
     * @function
     * @param {Component} childComponent The childComponent
     */
    // TODO update all calls to use addChildComponent and remove this method.
    _addChildComponent: {
        value: function(childComponent) {
            return this.addChildComponent(childComponent);
        }
    },

    /**
     * Description TODO
     * @function
     * @param {Component} childComponent The childComponent
     */
    addChildComponent: {
        value: function (childComponent) {
            if (this.childComponents.indexOf(childComponent) === -1) {
                this.childComponents.push(childComponent);
                childComponent._prepareForEnterDocument();
                childComponent._parentComponent = this;

                if (childComponent.needsDraw) {
                    childComponent._addToParentsDrawList();
                }
            }
        }
    },

    /**
     * @function
     */
    attachToParentComponent: {
        value: function() {
            this.detachFromParentComponent();
            this._parentComponent = null;

            var parentComponent = this.findParentComponent(),
                childComponents,
                childComponent;

            if (parentComponent) {
                // This component's children may have been attached to the
                // parent before we were initialized, and so we now need to
                // check if any of our parent's children should actually be
                // ours.
                childComponents = parentComponent.childComponents;
                for (var i = 0; (childComponent = childComponents[i]); i++) {
                    var newParentComponent = childComponent.findParentComponent();
                    if (newParentComponent === this) {
                        parentComponent.removeChildComponent(childComponent);
                        newParentComponent.addChildComponent(childComponent);
                    }
                }

                parentComponent.addChildComponent(this);
            }
        }
    },

    detachFromParentComponent: {
        value: function() {
            var parentComponent = this.parentComponent;

            if (parentComponent) {
                parentComponent.removeChildComponent(this);
            }
        }
    },

    /**
     * @function
     * @param {Component} childComponent The childComponent
     */
    removeChildComponent: {
        value: function(childComponent) {
            var childComponents = this.childComponents,
                ix = childComponents.indexOf(childComponent);

            if (ix > -1) {

                childComponent._exitDocument();

                childComponents.splice(ix, 1);
                childComponent._parentComponent = null;
                childComponent._alternateParentComponent = null;

                if (childComponent._addedToDrawList) {
                    childComponent._addedToDrawList = false;
                    ix = this._drawList.indexOf(childComponent);
                    this._drawList.splice(ix, 1);
                }
                this.rootComponent.removeFromCannotDrawList(childComponent);
            }
        }
    },

    /**
     * The child componet sof the component. This should not be edited directly.
     * @see Component#attachToParentComponent
     * @type {Array}
     * @default Array []
    */
    childComponents: {
        enumerable: false,
        distinct: true,
        value: []
    },

    _needsEnterDocument: {
        value: false
    },

    _inDocument: {
        value: false
    },

    __exitDocument: {
        value: function() {
            if (this._inDocument && typeof this.exitDocument === "function") {
                this.exitDocument();
                this._inDocument = false;
            }
        }
    },

    _exitDocument: {
        value: function() {
            var traverse;

            if (this._needsEnterDocument) {
                this._needsEnterDocument = false;
            } else {
                traverse = function (component) {
                    var childComponents = component.childComponents,
                        childComponent;

                    for (var i = 0; (childComponent = childComponents[i]); i++) {
                        if (childComponent._isComponentExpanded) {
                            traverse(childComponent);
                        }
                    }

                    if (component._inDocument) {
                        component.__exitDocument();
                    }
                };

                traverse(this);
            }
        }
    },

    /**
     * Lifecycle method called when this component exits the document
     */
    exitDocument: {
        value: function () {
            if (this.isActiveTarget) {
                defaultEventManager.activeTarget = this.nextTarget;
            }
        }
    },

    _prepareForEnterDocument: {
        value: function() {
            // On their first draw components will have their needsDraw = true
            // when they loadComponentTree.
            if (this._firstDraw) {
                this._needsEnterDocument = true;
            } else {
                this.needsDraw = true;
                this.traverseComponentTree(function(component) {
                    if (component._needsEnterDocument) {
                        return false;
                    }
                    component._needsEnterDocument = true;
                    component.needsDraw = true;
                });
            }
        }
    },

    /**
     * The owner component is the owner of the template form which this component was instantiated.
     * @type {Component}
     * @default null
     */
    ownerComponent: {
        enumerable: false,
        value: null
    },

    /**
     * Unused?
     * @private
     */
    components: {
        enumerable: false,
        value: {}
    },

    /**
     * @private
     */
    _isComponentExpanded: {
        enumerable: false,
        value: null
    },

    /**
     * @private
     */
    _isTemplateLoaded: {
        enumerable: false,
        value: null
    },

    /**
     * @private
     */
    _isTemplateInstantiated: {
        enumerable: false,
        value: null
    },

    /**
     * Remove all bindings and starts buffering the needsDraw.
     * @function
     */
    cleanupDeletedComponentTree: {
        value: function(cancelBindings) {
            // Deleting bindings in all cases was causing the symptoms expressed in gh-603
            // Until we have a more granular way we shouldn't do this,
            // the cancelBindings parameter is a short term fix.
            if (cancelBindings) {
                Bindings.cancelBindings(this);
            }
            this.needsDraw = false;
            this.traverseComponentTree(function(component) {
                // See above comment
                if (cancelBindings) {
                    Bindings.cancelBindings(component);
                }
                component.needsDraw = false;
            });
        }
    },

    /**
     * @private
     */
    _newDomContent: {
        enumerable: false,
        value: null
    },

    domContent: {
        serializable: false,
        get: function() {
            if (this._element) {
                return Array.prototype.slice.call(this._element.childNodes, 0);
            } else {
                return null;
            }
        },
        set: function(value) {
            var components,
                componentsToAdd = [],
                i,
                component;

            this._newDomContent = value;
            this.needsDraw = true;

            if (this._newDomContent === null) {
                this._shouldClearDomContentOnNextDraw = true;
            }

            if (typeof this.contentWillChange === "function") {
                this.contentWillChange(value);
            }

            // cleanup current content
            components = this.childComponents;
            for (i = 0, component; (component = components[i]); i++) {
                component.detachFromParentComponent();
            }

            if (value instanceof Element) {
                this._findAndDetachComponents(value, componentsToAdd);
            } else if (value && value[0]) {
                for (i = 0; i < value.length; i++) {
                    this._findAndDetachComponents(value[i], componentsToAdd);
                }
            }

            // not sure if I can rely on _parentComponent to detach the nodes instead of doing one loop for dettach and another to attach...
            for (i = 0, component; (component = componentsToAdd[i]); i++) {
                this.addChildComponent(component);
            }
        }
    },

    _shouldClearDomContentOnNextDraw: {
        value: false
    },

    _findAndDetachComponents: {
        value: function(node, components) {
            // TODO: Check if searching the childComponents of the parent
            //       component can make the search faster..
            var component = node.component,
                children;

            if (!components) {
                components = [];
            }

            if (component) {
                component.detachFromParentComponent();
                components.push(component);
            } else {
                // DocumentFragments don't have children so we default to
                // childNodes.
                children = node.children || node.childNodes;
                for (var i = 0, child; (child = children[i]); i++) {
                    this._findAndDetachComponents(child, components);
                }
            }

            return components;
        }
    },

    // Some components, like the repetition, might use their initial set of
    // child components as a template to clone them and instantiate them as the
    // real/effective child components.
    //
    // When this happens the original child components are in a way pointless
    // to the application and should not be used.
    //
    // If other objects get a reference to these child components in the
    // template serialization the way to know that they are going to be
    // cloned is by checking if one of their parent components has
    // its clonesChildComponents set to true.
    clonesChildComponents: {
        writable: false,
        value: false
    },

    _innerTemplate: {value: null},

    innerTemplate: {
        serializable: false,
        get: function() {
            var innerTemplate = this._innerTemplate,
                ownerDocumentPart,
                ownerTemplate,
                elementId,
                serialization,
                externalObjectLabels,
                ownerTemplateObjects,
                externalObjects;

            if (!innerTemplate) {
                ownerDocumentPart = this._ownerDocumentPart;

                if (ownerDocumentPart) {
                    ownerTemplate = ownerDocumentPart.template;

                    elementId = this.getElementId();
                    innerTemplate = ownerTemplate.createTemplateFromElementContents(elementId);

                    serialization = innerTemplate.getSerialization();
                    externalObjectLabels = serialization.getExternalObjectLabels();
                    ownerTemplateObjects = ownerDocumentPart.objects;
                    externalObjects = Object.create(null);

                    for (var i = 0, label; (label = externalObjectLabels[i]); i++) {
                        externalObjects[label] = ownerTemplateObjects[label];
                    }
                    innerTemplate.setInstances(externalObjects);

                    this._innerTemplate = innerTemplate;
                }
            }

            return innerTemplate;
        },
        set: function(value) {
            this._innerTemplate = value;
        }
    },

    /**
     * This method is called right before draw is called.
     * If ```canDraw()``` returns false, then the component is re-added to the parent's draw list and draw isn't called.
     * @function
     * @returns {Boolean} true or false
     */
    canDraw: {
        value: function() {
            return this._canDraw;
        }
    },

    /**
     * @private
     */
    _canDraw: {
        get: function() {
            return (!this._canDrawGate || this._canDrawGate.value);
        },
        set: function(value) {
            rootComponent.componentCanDraw(this, value);
        },
        enumerable: false
    },

    /**
     * @private
     */
    _prepareCanDraw: {
        enumerable: false,
        value: function _prepareCanDraw() {
            if (!this._isComponentTreeLoaded) {
                this.loadComponentTree().done();
            }
        }
    },

    /**
     * @private
     */
    _isComponentTreeLoaded: {
        value: null
    },

    shouldLoadComponentTree: {
        value: true
    },

    _loadComponentTreeDeferred: {value: null},
    loadComponentTree: {
        value: function loadComponentTree() {
            var self = this,
                canDrawGate = this.canDrawGate,
                deferred = this._loadComponentTreeDeferred;

            if (!deferred) {
                deferred = Promise.defer();
                this._loadComponentTreeDeferred = deferred;

                canDrawGate.setField("componentTreeLoaded", false);

                // only put it in the root component's draw list if the
                // component has requested to be draw, it's possible to load the
                // component tree without asking for a draw.
                // What about the hasTemplate check?
                if (this.needsDraw || this.hasTemplate) {
                    this._canDraw = false;
                }

                this.expandComponent().then(function() {
                    if (self.hasTemplate || self.shouldLoadComponentTree) {
                        var promises = [],
                            childComponents = self.childComponents,
                            childComponent;

                        for (var i = 0; (childComponent = childComponents[i]); i++) {
                            promises.push(childComponent.loadComponentTree());
                        }

                        return Promise.all(promises);
                    }
                }).then(function() {
                    self._isComponentTreeLoaded = true;
                    // When the component tree is loaded we need to draw if the
                    // component needs to have its enterDocument() called.
                    // This is because we explicitly avoid drawing when we set
                    // _needsEnterDocument before the first draw because we
                    // don't want to trigger the draw before its component tree
                    // is loaded.
                    if (self._needsEnterDocument) {
                        self.needsDraw = true;
                    }
                    canDrawGate.setField("componentTreeLoaded", true);
                    deferred.resolve();
                }, deferred.reject).done();
            }
            return deferred.promise;
        }
    },

    /**
     *  Whenever traverseComponentTree reaches the end of a subtree Component#expandComponent~callback is called.
     * @function
     * @param {Component#traverseComponentTree~visitor} visitor  visitor
     * @param {Component#traverseComponentTree~callback} callback callback object
     */
    traverseComponentTree: {value: function traverseComponentTree(visitor, callback) {
        var self = this;

        function traverse() {
            var childComponents = self.childComponents;
            var childComponent;
            var childLeftCount;

            if (visitor) {
                // if the visitor returns false stop the traversal for this subtree
                if (visitor(self) === false) {
                    if (callback) {
                        callback();
                    }
                    return;
                }
            }

            if ((childLeftCount = childComponents.length) === 0) {
                if (callback) {
                    callback();
                }
                return;
            }

            var visitorFunction = function() {
                if (--childLeftCount === 0 && callback) {
                    callback();
                }
            };
            for (var i = 0; (childComponent = childComponents[i]); i++) {
                childComponent.traverseComponentTree(visitor, visitorFunction);
            }
        }

        if (this._isComponentExpanded) {
            traverse();
        } else if (callback) {
            callback();
        }
    }},
    /**
     * Visitor function for Component#traverseComponentTree. For every component in the tree, the visitor function is
     * called with the current component as an argument.
     * If the function returns false then the traversal is stopped for that subtree.
     * @function Component#traverseComponentTree~visitor
     * @param Component visitedComponent
     */
    /**
     * @function Component#traverseComponentTree~callback
     */


    /**
    * @function
    * @param {Component#expandComponent~callback} callback  TODO
    */
    _expandComponentDeferred: {value: null},
    expandComponent: {
        value: function expandComponent() {
            var self = this,
                deferred = this._expandComponentDeferred;

            if (!deferred) {
                deferred = Promise.defer();
                this._expandComponentDeferred = deferred;

                if (this.hasTemplate) {
                    this._instantiateTemplate().then(function() {
                        self._isComponentExpanded = true;
                        self._addTemplateStyles();
                        self.needsDraw = true;
                        deferred.resolve();
                    }, deferred.reject);
                } else {
                    this._isComponentExpanded = true;
                    deferred.resolve();
                }
            }

            return deferred.promise;
        }
    },

    _templateObjectDescriptor: {
        value: {
            enumerable: true,
            configurable: true
        }
    },

    /**
     * @private
     * @function
     */
    _setupTemplateObjects: {
        value: function(objects) {
            var descriptor = this._templateObjectDescriptor,
                templateObjects = Object.create(null);

            for (var label in objects) {
                var object = objects[label];

                if (typeof object === "object" && object != null) {
                    if (!Component.prototype.isPrototypeOf(object) || object === this ||
                        object.parentComponent === this) {
                        templateObjects[label] = object;
                    } else {
                        descriptor.get = this._makeTemplateObjectGetter(this, label, object);
                        Object.defineProperty(templateObjects, label, descriptor);
                    }
                }
            }

            this.templateObjects = templateObjects;
        }
    },

    /**
     * @private
     * @function
     */
    _makeTemplateObjectGetter: {
        value: function(owner, label, object) {
            var querySelectorLabel = "@"+label,
                isRepeated,
                components,
                component;

            return function templateObjectGetter() {
                if (isRepeated) {
                    return owner.querySelectorAllComponent(querySelectorLabel, owner);
                } else {
                    components = owner.querySelectorAllComponent(querySelectorLabel, owner);
                    // if there's only one maybe it's not repeated, let's go up
                    // the tree and found out.
                    if (components.length === 1) {
                        component = components[0];
                        while (component = component.parentComponent) {
                            if (component === owner) {
                                // we got to the owner without ever hitting a component
                                // that repeats its child components, we can
                                // safely recreate this property with a static value
                                Object.defineProperty(this, label, {
                                    value: components[0]
                                });
                                return components[0];
                            } else if (component.clonesChildComponents) {
                                break;
                            }
                        }
                    } else if (components.length === 0) {
                        // We didn't find any in the component tree
                        // so it was probably removed in the meanwhile.
                        // We return the one that was in the template
                        // TODO: need to make sure this component hasn't been
                        // disposed.
                        return object;
                    }

                    isRepeated = true;
                    return components;
                }
            };
        }
    },

    /**
     * @private
     * @function
     */
    _instantiateTemplate: {
        value: function() {
            var self = this;
            return this._loadTemplate().then(function(template) {
                if (!self._element) {
                    console.error("Cannot instantiate template without an element.", self);
                    return Promise.reject(new Error("Cannot instantiate template without an element.", self));
                }
                var instances = self.templateObjects,
                    _document = self._element.ownerDocument;

                if (!instances) {
                    instances = Object.create(null);
                }
                instances.owner = self;

                self._isTemplateInstantiated = true;

                return template.instantiateWithInstances(instances, _document)
                .then(function(documentPart) {
                    self._templateDocumentPart = documentPart;
                    documentPart.fragment = null;
                })
                .fail(function(reason) {
                    var message = reason.stack || reason;
                    console.error("Error in", template.getBaseUrl() + ":", message);
                    throw reason;
                });
            });
        }
    },

    _templateDidLoad: {
        value: function(documentPart) {
            this._setupTemplateObjects(documentPart.objects);
        }
    },

    _loadTemplatePromise: {value: null},
    _loadTemplate: {
        value: function _loadTemplate() {
            var self = this,
                promise = this._loadTemplatePromise,
                info;

            if (!promise) {
                info = Montage.getInfoForObject(this);

                promise = this._loadTemplatePromise = Template.getTemplateWithModuleId(
                    this.templateModuleId, info.require)
                .then(function(template) {
                    self._template = template;
                    self._isTemplateLoaded = true;

                    return template;
                });
            }

            return promise;
        }
    },

    /**
     * @private
     * @type {String}
     * @default
     */
    templateModuleId: {
        get: function() {
            return this._templateModuleId || this._getDefaultTemplateModuleId();
        }
    },

    _getDefaultTemplateModuleId: {
        value: function() {
            var templateModuleId,
                slashIndex,
                moduleId,
                info;

            info = Montage.getInfoForObject(this);
            moduleId = info.moduleId;
            slashIndex = moduleId.lastIndexOf("/");
            templateModuleId = moduleId + "/" + moduleId.slice(slashIndex === -1 ? 0 : slashIndex+1, -5 /* ".reel".length */) + ".html";

            return templateModuleId;
        }
    },

    deserializedFromSerialization: {
        value: function() {
            this.attachToParentComponent();
        }
    },

    _deserializedFromTemplate: {
        value: function(owner, label, documentPart) {
            Montage.getInfoForObject(this).label = label;
            this._ownerDocumentPart = documentPart;

            if (! this.hasOwnProperty("identifier")) {
                this.identifier = label;
            }

            if (!this.ownerComponent) {
                if (Component.prototype.isPrototypeOf(owner)) {
                    this.ownerComponent = owner;
                } else {
                    this.ownerComponent = this.rootComponent;
                }
            }

            if (this._needsDrawInDeserialization) {
                this.needsDraw = true;
            }
        }
    },

    blueprintModuleId: {
        serializable: false,
        enumerable: false,
        get: function () {
            var info = Montage.getInfoForObject(this);
            var self = (info && !info.isInstance) ? this : Object.getPrototypeOf(this);
            if ((!Object.getOwnPropertyDescriptor(self, "_blueprintModuleId")) || (!self._blueprintModuleId)) {
                info = Montage.getInfoForObject(self);
                var moduleId = info.moduleId,
                    slashIndex = moduleId.lastIndexOf("/"),
                    dotIndex = moduleId.lastIndexOf(".");
                slashIndex = ( slashIndex === -1 ? 0 : slashIndex + 1 );
                dotIndex = ( dotIndex === -1 ? moduleId.length : dotIndex );
                dotIndex = ( dotIndex < slashIndex ? moduleId.length : dotIndex );

                var blueprintModuleId;
                if ((dotIndex < moduleId.length) && ( moduleId.slice(dotIndex, moduleId.length) === ".reel")) {
                    // We are in a reel
                    blueprintModuleId = moduleId + "/" + moduleId.slice(slashIndex, dotIndex) + ".meta";
                } else {
                    // We look for the default
                    blueprintModuleId = moduleId.slice(0, dotIndex) + ".meta";
                }

                Montage.defineProperty(self, "_blueprintModuleId", {
                    value: blueprintModuleId
                });
            }
            return self._blueprintModuleId;
        }
    },

    blueprint: require("montage")._blueprintDescriptor,

    /**
    Callback for the ```canDrawGate```.
    Propagates to the parent and adds the component to the draw list.
    @function
    @param {Gate} gate
    @see Component#canDrawGate
    */
    gateDidBecomeTrue: {
        value: function(gate) {
            if (gate === this._canDrawGate) {
                this._canDraw = true;
            } else if (gate === this._blockDrawGate) {
                rootComponent.componentBlockDraw(this);
                this._prepareCanDraw();
            }
        },
        enumerable: false
    },

    /**
     * Gate that controls the _canDraw property. When it becomes true it sets _canDraw to true.
     * @function
     * @returns Gate
     * @private
     */
    _canDrawGate: {
        enumerable: false,
        value: null
    },

    /**
     * @private
     */
    _preparedForActivationEvents: {
        enumerable: false,
        value: false
    },

    /**
        If needsDraw property returns true this call adds the current component instance to the rootComponents draw list.
        Then it iterates on every child component in the component's drawList.
        On everyone of them it calls ```canDraw()```.
        If the result is true, ```_drawIfNeeded()``` is called, otherwise they are ignored.
        @private
     */
    _drawIfNeeded: {
        enumerable: false,
        value: function _drawIfNeeded(level) {
            var childComponent,
                oldDrawList, i, childComponentListLength,
                firstDraw = this._firstDraw;

            this._treeLevel = level;
            if (firstDraw) {
                this.originalElement = this.element;
            }
            if (this.needsDraw) {
                rootComponent.addToDrawCycle(this);
            }
            if (firstDraw && this.prepareForDraw) {
                Montage.callDeprecatedFunction(this, this.prepareForDraw, "prepareForDraw", "enterDocument(firstTime)");
            }
            if (this._needsEnterDocument) {
                this._needsEnterDocument = false;
                this._inDocument = true;
                if (typeof this.enterDocument === "function") {
                    this.enterDocument(firstDraw);
                }
                this._enterDocument(firstDraw);
            }
            if (firstDraw) {
                this.originalElement = null;
            }

            if (drawLogger.isDebug) {
                drawLogger.debug(this, "drawList: " + (this._drawList || []).length + " of " + this.childComponents.length);
            }
            if (this._drawList !== null && this._drawList.length > 0) {
                oldDrawList = this._drawList;
                this._drawList = [];
                childComponentListLength = oldDrawList.length;
                for (i = 0; i < childComponentListLength; i++) {
                    childComponent = oldDrawList[i];
                    if (drawLogger.isDebug) {
                        drawLogger.debug("Parent Component " + (this.element != null ? this.element.id : "") + " drawList length: " + oldDrawList.length);
                    }
                    childComponent._addedToDrawList = false;
                    if (drawLogger.isDebug) {
                        drawLogger.debug(this, "childComponent: " + childComponent.element + "; canDraw: " + childComponent.canDraw());
                    }
                    if (childComponent.canDraw()) { // TODO if canDraw is false when does needsDraw get reset?
                        childComponent._drawIfNeeded(level+1);
                    }
                }
            }
        }
    },

    _updateComponentDom: {
        value: function() {
            var component, composer, length, i;
            if (this._firstDraw) {

                this._prepareForDraw();

                // Load any non lazyLoad composers that have been added
                length = this.composerList.length;
                for (i = 0; i < length; i++) {
                    composer = this.composerList[i];
                    if (!composer.lazyLoad) {
                        composer._load();
                    }
                }

                // Will we expose a different property, firstDraw, for components to check
                this._firstDraw = false;
            }

            if (this._newDomContent !== null || this._shouldClearDomContentOnNextDraw) {
                if (drawLogger.isDebug) {
                    logger.debug("Component content changed: component ", this._montage_metadata.objectName, this.identifier, " newDomContent", this._newDomContent);
                }
                this._performDomContentChanges();
            }
        }
    },

    /**
     * @private
     * @function
     */
    _replaceElementWithTemplate: {
        enumerable: false,
        value: function() {
            var element = this.element,
                template = this._templateElement,
                attributes = this.element.attributes,
                attributeName,
                value,
                i,
                attribute;

            // TODO: get a spec for this, what attributes should we merge?
            for (i = 0; (attribute = attributes[i]); i++) {
                attributeName = attribute.nodeName;
                if (attributeName === "id" || attributeName === "data-montage-id") {
                    value = attribute.nodeValue;
                } else {
                    value = (template.getAttribute(attributeName) || "") + (attributeName === "style" ? "; " : " ") +
                        attribute.nodeValue;
                }

                template.setAttribute(attributeName, value);
            }

            this._initializeClassListFromElement(template);

            if (element.parentNode) {
                element.parentNode.replaceChild(template, element);
            } else {
                console.warn("Warning: Trying to replace element ", element," which has no parentNode");
            }

            this.eventManager.unregisterEventHandlerForElement(element);
            this.eventManager.registerEventHandlerForElement(this, template);
            this._element = template;
            this._templateElement = null;

            // if the DOM content of the component was changed before the
            // template has been drawn then we assume that this change is
            // meant to set the original content of the component and not to
            // replace the entire template with it, that wouldn't make much
            // sense.
            if (this._newDomContent) {
                this._newDomContent = null;
                this._shouldClearDomContentOnNextDraw = false;
            }
        }
    },

    /**
     * @private
     * @function
     */
    _addTemplateStyles: {
        value: function() {
            var part = this._templateDocumentPart,
                resources,
                styles,
                _document,
                documentHead;

            if (part) {
                resources = part.template.getResources();
                _document = this.element.ownerDocument;
                documentHead = _document.head;
                styles = resources.createStylesForDocument(_document);

                for (var i = 0, style; (style = styles[i]); i++) {
                    this.rootComponent.addStylesheet(style);
                }
            }
        }
    },

    /**
     * @private
     * @function
     */
    _prepareForDraw: {
        value: function _prepareForDraw() {
            if (logger.isDebug) {
                logger.debug(this, "_templateElement: " + this._templateElement);
            }

            this._initDomArguments();
            if (this._templateElement) {
                this._bindTemplateParametersToArguments();
                this._replaceElementWithTemplate();
            }
        },
        enumerable: false
    },

    _bindTemplateParametersToArguments: {
        value: function() {
            var parameters = this._templateDocumentPart.parameters,
                parameter,
                templateArguments,
                argument,
                validation,
                contents,
                components,
                range,
                component;

            templateArguments = this._domArguments;

            if (!this._template.hasParameters() &&
                templateArguments.length === 1) {
                return;
            }

            validation = this._validateTemplateArguments(
                templateArguments, parameters);
            if (validation) {
                throw validation;
            }

            for (var key in parameters) {
                parameter = parameters[key];
                argument = templateArguments[key];

                if (key === "*") {
                    range = this._element.ownerDocument.createRange();
                    range.selectNodeContents(this._element);
                    contents = range.extractContents();
                } else {
                    contents = argument;
                }

                components = this._findAndDetachComponents(contents);
                parameter.parentNode.replaceChild(contents, parameter);
                for (var i = 0; (component = components[i]); i++) {
                    component.attachToParentComponent();
                }
            }
        }
    },

    _validateTemplateArguments: {
        value: function(templateArguments, templateParameters) {
            var parameterNames = Object.keys(templateParameters),
                argumentNames,
                param;

            // If the template does not have parameters it is up to the
            // component to use its arguments.
            if (parameterNames.length === 0) {
                return;
            }

            if (templateArguments == null) {
                if (parameterNames.length > 0) {
                    return new Error('No arguments provided for ' +
                    this.templateModuleId + '. Arguments needed: ' +
                    parameterNames + '.');
                }
            } else {
                if ("*" in templateParameters) {
                    argumentNames = Object.keys(templateArguments);
                    if (argumentNames.length > 0) {
                        return new Error('Arguments "' + argumentNames +
                        '" were given to component but no named parameters ' +
                        'are defined in ' + this.templateModuleId);
                    }
                } else {
                    // All template parameters need to be satisfied.
                    for (param in templateParameters) {
                        if (!(param in templateArguments)) {
                            return new Error('"' + param + '" argument not ' +
                            'given in ' + this.templateModuleId);
                        }
                    }
                    // Arguments for non-existant parameters are not allowed.
                    // Only the star argument is allowed.
                    for (param in templateArguments) {
                        if (param !== "*" && !(param in templateParameters)) {
                            return new Error('"' + param + '" parameter does ' +
                            'not exist in ' + this.templateModuleId);
                        }
                    }
                }
            }
        }
    },

    /**
     * Called by the {EventManager} before dispatching a ```touchstart``` or ```mousedown```.
     * The component can implement this method to add event listeners for these events before they are dispatched.
     * @function
     */
    prepareForActivationEvents: {
        enumerable: false,
        value: null
    },

    /**
     * Called to add event listeners on demand
     * @type function
     * @private
     */
    _prepareForActivationEvents: {
        value: function() {
            var i = this.composerList.length, composer;
            for (i = 0; i < this.composerList.length; i++) {
                composer = this.composerList[i];
                if (composer.lazyLoad) {
                    composer._load();
                }
            }
            if (typeof this.prepareForActivationEvents === "function") {
                this.prepareForActivationEvents();
            }
        }
    },

    /**
     * @private
     */
    _performDomContentChanges: {
        value: function() {
            var contents = this._newDomContent,
                oldContent = this._element.childNodes[0],
                element;

            if (contents || this._shouldClearDomContentOnNextDraw) {
                element = this._element;

                element.innerHTML = "";

                if (Element.isElement(contents)) {
                    element.appendChild(contents);
                } else if(contents != null) {
                    for (var i = 0, content; (content = contents[i]); i++) {
                        element.appendChild(content);
                    }
                }

                this._newDomContent = null;
                if (typeof this.contentDidChange === "function") {
                    this.contentDidChange(this._element.childNodes[0], oldContent);
                }
                this._shouldClearDomContentOnNextDraw = false;
            }
        }
    },

    /**
     * Provides the component a chance to prepare for it being drawn for the first time. For a component with an HTML template, this method is invoked when the template been loaded and applied to the DOM.
     * @function
     * @see http://montagejs.org/docs/Component-draw-cycle.html
     */
    prepareForDraw: {
        enumerable: false,
        value: null
    },

    /**
     * This is the prescribed location for components to update its DOM structure or modify its styles.
     * @function
     * @see http://montagejs.org/docs/Component-draw-cycle.html
     */
    draw: {
        enumerable: false,
        value: function() {
        }
    },

    /**
     * Provides the component an opportunity to query the DOM for any necessary calculations before drawing. If the execution of this method sets needsDraw to true on other components, those components will be added to the current draw cycle.
     * @function
     * @see http://montagejs.org/docs/Component-draw-cycle.html
     */
    willDraw: {
        enumerable: false,
        value: null
    },

    /**
     * Provides the component an opportunity to query the DOM for any necessary calculations after drawing.
     * @function
     * @see http://montagejs.org/docs/Component-draw-cycle.html
     */
    didDraw: {
        enumerable: false,
        value: function() {
        }
    },

    /**
     * Records whether or not we have been added to the parent's drawList.
     * @private
     */
    _addedToDrawList: {
        value: false
    },

    /**
     * @private
     */
    _addToParentsDrawList: {
        enumerable: false,
        value: function() {
            if (!this._addedToDrawList) {
                var parentComponent = this.parentComponent;

                if (!parentComponent) {
                    if (drawLogger.isDebug) {
                        drawLogger.debug(this, "parentComponent is null");
                    }
                } else {
                    parentComponent._addToDrawList(this);
                    if (drawLogger.isDebug) {
                        drawLogger.debug("drawList -- childComponent",this._montage_metadata.objectName," added to ",parentComponent._montage_metadata.objectName);
                    }
                }
            }
        }
    },

    /**
     * @private
     */
    _needsDraw: {
        value: false
    },

    _needsDrawInDeserialization: {
        value: false
    },

    /**
     * The purpose of this property is to trigger the adding of the component to the draw list and maintain whether draw needs to be called as a consequence of drawIfNeeded.
     * If needsDraw is set to true, and the component can draw but is not yet added to the parent Draw List, then ```addToDrawList()``` is called on the parentComponent with this as the argument.
     * If the component cannot draw then it's recorded in the component's ```blockDrawGate``` that a draw was requested.
     *
     * Two actions are required for a component to load:
     *
     * - it needs an element
     * - a draw must have been requested
     *
     * @type {Boolean}
     * @default {Boolean} false
     */
    needsDraw: {
        enumerable: false,
        get: function() {
            return !!this._needsDraw;
        },
        set: function(value) {
            if (this.isDeserializing) {
                // Ignore needsDraw(s) which happen during deserialization
                this._needsDrawInDeserialization = true;
                return;
            }
            if (this._needsDraw !== value) {
                if (drawLogger.isDebug) {
                    drawLogger.debug("NEEDS DRAW TOGGLED " + value + " FOR " + this._montage_metadata.objectName);
                }
                this._needsDraw = !!value;
                if (value) {
                    if (this.canDrawGate.value) {
                        this._addToParentsDrawList();
                    } else {
                        this.blockDrawGate.setField("drawRequested", true);
                    }
                }
            }
        }
    },

    /**
     * Contains the list of childComponents this instance is reponsible for drawing.
     * @private
     */
    _drawList: {
        value: null
    },

    /**
     * @private
     */
    __addToDrawList: {
        enumerable: false,
        value: function(childComponent) {
            if (this._drawList === null) {
                this._drawList = [childComponent];
                childComponent._addedToDrawList = true;
            } else {
                if (this._drawList.indexOf(childComponent) === -1) {
                    this._drawList.push(childComponent);
                    childComponent._addedToDrawList = true;
                }
            }
        }
    },

    /**
     * Adds the passed in child component to the drawList
     * If the current instance isn't added to the drawList of its parentComponent, then it adds itself.
     * @private
     */
    _addToDrawList: {
        enumerable: false,
        value: function(childComponent) {
            this.__addToDrawList(childComponent);
            this._addToParentsDrawList();
        }
    },

    _templateElement: {
        enumerable: false,
        value: null
    },

    // Pointer Claiming

    /**
        Ask this component to surrender the specified pointer to the demandingComponent.
        The component can decide whether or not it should do this given the pointer and demandingComponent involved.
        Some components may decide not to surrender control ever, while others may do so in certain situations.
        Returns true if the pointer was surrendered, false otherwise.
        The demandingComponent is responsible for claiming the surrendered pointer if it desires.
        @function
        @param {Property} pointer The pointerIdentifier that the demanding component is asking this component to surrender
        @param {Object} demandingComponent The component that is asking this component to surrender the specified pointer
        @returns {Boolean} true
     */
    surrenderPointer: {
        value: function(pointer, demandingComponent) {
            return true;
        }
    },

    // Composers
    /*
     * Variable to track this component's associated composers
     * @private
     */
    composerList: {
        value: [],
        distinct: true,
        serializable: false
    },

    /**
     * Adds the passed in composer to the component's composer list.
     * @function
     * @param {Composer} composer Composer object
     */
    addComposer: {  // What if the same composer instance is added to more than one component?
        value: function(composer) {
            this.addComposerForElement(composer, composer.element);
        }
    },

    /**
     * Adds the passed in composer to the component's composer list and
     * sets the element of the composer to the passed in element.
     * @function
     * @param {Composer} composer Composer object
     * @param {Element} element Element
     */
    addComposerForElement: {
        value: function(composer, element) {
            composer.component = this;
            composer.element = element;
            this.composerList.push(composer);

            if (!this._firstDraw) {  // prepareForDraw has already happened so do the loading here
                if (!composer.lazyLoad) {
                    composer._load();
                } else if (this._preparedForActivationEvents) { // even though it's lazyLoad prepareForActivationEvents has already happened
                    composer._load();
                }
            }
        }
    },

    /**
     * Adds the passed in composer to the list of composers which will have their
     * frame method called during the next draw cycle.  It causes a draw cycle to be scheduled
     * iff one has not already been scheduled.
     * @function
     * @param {Composer} composer Composer object
     */
    scheduleComposer: {
        value: function(composer) {
            this.rootComponent.addToComposerList(composer);
        }
    },

    /**
     * Removes the passed in composer from this component's composer list.  It takes care
     * of calling the composers unload method before removing it from the list.
     * @function
     * @param {Composer} composer Composer object
     */
    removeComposer: {
        value: function(composer) {
            var i, length;
            length = this.composerList.length;
            for (i = 0; i < length; i++) {
                if (this.composerList[i].uuid === composer.uuid) {
                    this.composerList[i].unload();
                    this.composerList.splice(i, 1);
                    break;
                }
            }
        }
    },

    /**
     * A convenience method for removing all composers from a component.  This method
     * is responsible for calling unload on each composer before removing it.
     * @function
     */
    clearAllComposers: {
        value: function() {
            var i, length, composerList = this.composerList;
            length = composerList.length;
            for (i = 0; i < length; i++) {
                composerList[i].unload();
            }
            composerList.splice(0, length);
        }
    },

    /**
     * The localizer for this component
     * @type {Localizer}
     * @default null
     */
    localizer: {
        value: null
    },

    /**
     * @private
     */
    _waitForLocalizerMessages: {
        value: false
    },

    /**
     * Whether to wait for the localizer to load messages before drawing.
     * Make sure to set the localizer before setting to ```true```.
     * @type Boolean
     * @default false
     * @example
     * // require localizer
     * var defaultLocalizer = localizer.defaultLocalizer,
     *     _ = defaultLocalizer.localizeSync.bind(defaultLocalizer);
     *
     * exports.Main = Component.specialize( {
     *
     *     constructor: {
     *         value: function() {
     *             this.localizer = defaultLocalizer;
     *             this.waitForLocalizerMessages = true;
     *         }
     *     },
     *
     *     // ...
     *
     *     // no draw happens until the localizer's messages have been loaded
     *     prepareForDraw: {
     *         value: function() {
     *             this._greeting = _("hello", "Hello {name}!");
     *         }
     *     },
     *     draw: {
     *         value: function() {
     *             // this is for illustration only. This example is simple enough that
     *             // you should use a localizations binding
     *             this._element.textContent = this._greeting({name: this.name});
     *         }
     *     }
     * }
     */
    waitForLocalizerMessages: {
        enumerable: false,
        get: function() {
            return this._waitForLocalizerMessages;
        },
        set: function(value) {
            if (this._waitForLocalizerMessages !== value) {
                if (value === true && !this.localizer.messages) {
                    if (!this.localizer) {
                        throw "Cannot wait for messages on localizer if it is not set";
                    }

                    this._waitForLocalizerMessages = true;

                    var self = this;
                    logger.debug(this, "waiting for messages from localizer");
                    this.canDrawGate.setField("messages", false);

                    this.localizer.messagesPromise.then(function(messages) {
                        if (logger.isDebug) {
                            logger.debug(self, "got messages from localizer");
                        }
                        self.canDrawGate.setField("messages", true);
                    });
                } else {
                    this._waitForLocalizerMessages = false;
                    this.canDrawGate.setField("messages", true);
                }
            }
        }
    },

    //
    // Attribute Handling
    //

    /**
     * Stores values that need to be set on the element. Cleared each draw cycle.
     * @private
     */
    _elementAttributeValues: {
        value: null
    },

    /**
     * Stores the descriptors of the properties that can be set on this control
     * @private
     */
    _elementAttributeDescriptors: {
        value: null
    },


    _getElementAttributeDescriptor: {
        value: function(attributeName) {
            var attributeDescriptor, instance = this;
            // walk up the prototype chain from the instance to NativeControl's prototype
            // if _elementAttributeDescriptors is falsy, stop.
            while(instance && instance._elementAttributeDescriptors) {
                attributeDescriptor = instance._elementAttributeDescriptors[attributeName];
                if(attributeDescriptor) {
                    break;
                } else {
                    instance = Object.getPrototypeOf(instance);
                }
            }
            return attributeDescriptor;
        }
    },

    /**
     * Adds a property to the component with the specified name. This method is used internally by the framework convert a DOM element's standard attributes into bindable properties. It creates an accessor property (getter/setter) with the same name as the specified property, as well as a "backing" data property whose name is prepended with an underscore (_). The backing variable is assigned the value from the property descriptor. For example, if the name  "title" is passed as the first parameter, a "title" accessor property is created as well a data property named "_title".
     * @function
     * @param {String} name The property name to add.
     * @param {Object} descriptor An object that specifies the new properties default attributes such as configurable and enumerable.
     */
    defineAttribute: {
        value: function(name, descriptor) {
            descriptor = descriptor || {};
            var _name = '_' + name;


            var newDescriptor = {
                configurable: (typeof descriptor.configurable === 'undefined') ? true: descriptor.configurable,
                enumerable: (typeof descriptor.enumerable === 'undefined') ?  true: descriptor.enumerable,
                set: (function(name, attributeName) {
                    return function(value) {
                        var descriptor = this._getElementAttributeDescriptor(name, this);

                        // if requested dataType is boolean (eg: checked, readonly etc)
                        // coerce the value to boolean
                        if(descriptor && "boolean" === descriptor.dataType) {
                            value = ( (value || value === "") ? true : false);
                        }

                        // If the set value is different to the current one,
                        // update it here, and set it to be updated on the
                        // element in the next draw cycle.
                        if((typeof value !== 'undefined') && this[attributeName] !== value) {
                            this[attributeName] = value;
                            // at this point we know that we will need it so create it once.
                            if(this._elementAttributeValues === null) {
                                this._elementAttributeValues = {};
                            }
                            this._elementAttributeValues[name] = value;
                            this.needsDraw = true;
                        }
                    };
                }(name, _name)),
                get: (function(name, attributeName) {
                    return function() {
                        return this[attributeName];
                    };
                }(name, _name))
            };

            // Define _ property
            Montage.defineProperty(this.prototype, _name, {value: null});
            // Define property getter and setter
            Montage.defineProperty(this.prototype, name, newDescriptor);
        }
    },

    /**
     * Add the specified properties as properties of this component.
     * @function
     * @param {object} properties An object that contains the properties you want to add.
     */
    addAttributes: {
        value: function(properties) {
            var i, descriptor, property, object;
            this.prototype._elementAttributeDescriptors = properties;

            for(property in properties) {
                if(properties.hasOwnProperty(property)) {
                    object = properties[property];
                    // Make sure that the descriptor is of the correct form.
                    if(object === null || typeof object === "string") {
                        descriptor = {value: object, dataType: "string"};
                        properties[property] = descriptor;
                    } else {
                        descriptor = object;
                    }

                    // Only add the internal property, and getter and setter if
                    // they don't already exist.
                    if(typeof this[property] === 'undefined') {
                        this.defineAttribute(property, descriptor);
                    }
                }
            }
        }
    },

// callbacks

    /**
     * @private
     */
    _enterDocument: {
        value: function(firstTime) {
            var originalElement;

            if (firstTime) {
// The element is now ready, so we can read the attributes that
                // have been set on it.
                originalElement = this.originalElement;

                var attributes, i, length, name, value, attributeName, descriptor;
                attributes = originalElement.attributes;
                if (attributes) {
                    length = attributes.length;
                    for(i=0; i < length; i++) {
                        name = attributes[i].name;
                        value = attributes[i].value;

                        descriptor = this._getElementAttributeDescriptor(name, this);
                        // check if this attribute from the markup is a well-defined attribute of the component
                        if(descriptor || (typeof this[name] !== 'undefined')) {
                            // at this point we know that we will need it so create it.
                            if(this._elementAttributeValues === null) {
                                this._elementAttributeValues = {};
                            }
                            // only set the value if a value has not already been set by binding
                            if(typeof this._elementAttributeValues[name] === 'undefined') {
                                this._elementAttributeValues[name] = value;
                                if( (typeof this[name] === 'undefined') || this[name] == null) {
                                    this[name] = value;
                                }
                            }
                        }
                    }
                }

                // textContent is a special case since it isn't an attribute
                descriptor = this._getElementAttributeDescriptor('textContent', this);
                if(descriptor) {
                    // check if this element has textContent
                    var textContent = originalElement.textContent;
                    if(typeof this._elementAttributeValues.textContent === 'undefined') {
                        this._elementAttributeValues.textContent = textContent;
                        if( this.textContent == null) {
                            this.textContent = textContent;
                        }
                    }
                }

                // Set defaults for any properties that weren't serialised or set
                // as attributes on the element.
                if (this._elementAttributeDescriptors) {
                    for (attributeName in this._elementAttributeDescriptors) {
                        descriptor = this._elementAttributeDescriptors[attributeName];
                        var _name = "_"+attributeName;
                        if (this[_name] === null && descriptor !== null && "value" in descriptor) {
                            this[_name] = this._elementAttributeDescriptors[attributeName].value;
                        }
                    }
                }
            }

        }
    },

    /**
     * @private
     */
    _draw: {
        value: function() {
            var element = this.element, descriptor;

            for(var attributeName in this._elementAttributeValues) {
                if(this._elementAttributeValues.hasOwnProperty(attributeName)) {
                    var value = this[attributeName];
                    descriptor = this._getElementAttributeDescriptor(attributeName, this);
                    if(descriptor) {

                        if(descriptor.dataType === 'boolean') {
                            if(value === true) {
                                element[attributeName] = true;
                                element.setAttribute(attributeName, attributeName.toLowerCase());
                            } else {
                                element[attributeName] = false;
                                element.removeAttribute(attributeName);
                            }
                        } else {
                            if(typeof value !== 'undefined') {
                                if(attributeName === 'textContent') {
                                    element.textContent = value;
                                } else {
                                    //https://developer.mozilla.org/en/DOM/element.setAttribute
                                    element.setAttribute(attributeName, value);
                                }

                            }
                        }

                    }

                    delete this._elementAttributeValues[attributeName];
                }
            }
            // classList
            this._drawClassListIntoComponent();
        }
    },

    _classList: {
        value: null
    },

    _classListDirty: {
        value: false
    },

    /**
     The classList of the component's element, the purpose is to mimic the element's API but to also respect the draw.
     It can also be bound to by binding each class as a property.
     example to toggle the complete class: "classList.has('complete')" : { "<-" : "@owner.isCompete"}
     @type {Property}
     @default null
     */
    classList: {
        get: function () {
            if (this._classList === null) {
                this._classList = new Set();
                this._subscribeToToClassListChanges();
                this._initializeClassListFromElement(this.element);
            }
            return this._classList;
        }
    },

    _initializeClassListFromElement: {
        value: function(element) {
            var className;
            if (element && element.className && (className = element.className.trim())) {
                // classList
                if (className.length !== 0) {
                    // important to initializae the classList first, so that the listener doesn't get installed.
                    var classList = this.classList;
                    if (this._unsubscribeToClassListChanges) {
                        this._unsubscribeToClassListChanges();
                    }
                    classList.addEach(className.split(/\s+/));
                    this._subscribeToToClassListChanges();
                }
            }

        }
    },

    _unsubscribeToClassListChanges: {
        value: null
    },

    _subscribeToToClassListChanges: {
        value: function() {
            this._unsubscribeToClassListChanges = this._classList.addRangeChangeListener(this, "classList");
        }
    },

    handleClassListRangeChange: {
        value: function (plus, minus) {
            this._classListDirty = true;
            this.needsDraw = true;
        }
    },

    _drawClassListIntoComponent: {
        value: function () {
            if (this._classListDirty) {
                var elementClassList = this.element.classList,
                    classList = this._classList;

                for (var i = 0, ii = elementClassList.length, className; i < ii; i++) {
                    className = elementClassList.item(i);
                    if (!classList.has(className)) {
                        elementClassList.remove(className);
                        i--;
                        ii--;
                    }
                }

                this._classList.forEach(function (cssClass) {
                    elementClassList.add(cssClass);
                });
                this._classListDirty = false;
            }
        }
    },

    dispose: {
        value: function() {
            this.cancelBindings();
            this.detachFromParentComponent();
            defaultEventManager.unregisterEventHandlerForElement(this, this._element);
            this._element = null;

            this.childComponents.forEach(function(component) {
                component.dispose();
            });
        }
    }
});


/**
 * @class RootComponent
 * @extends Component
 */
var RootComponent = Component.specialize( /** @lends RootComponent# */{
    constructor: {
        value: function RootComponent() {
            this.super();
        }
    },
    /**
     * @private
     * @function
     * @returns itself
    */
    init: {
        value: function() {
            return this;
        }
    },

    /**
     * @private
     */
    needsDraw: {
        enumerable: true,
        get: function() {
            return false;
        },
        set: function(value) {
            if (this._needsDraw !== value) {
                this._needsDraw = !!value;
                if (value) {
                    var childComponents = this.childComponents;
                    for (var i = 0, childComponent; (childComponent = childComponents[i]); i++) {
                        if (drawLogger.isDebug) {
                            drawLogger.debug(this, "needsDraw = true for: " + childComponent._montage_metadata.exportedSymbol);
                        }
                        childComponent.needsDraw = true;
                    }
                }
            }
        }
    },

    /**
     * @private
     */
    canDrawGate: {
        get: function() {
            return this._canDrawGate || (this._canDrawGate = new Gate().initWithDelegate(this));
        }
    },

    /**
     * @private
     */
    _clearNeedsDrawTimeOut: {
        value: null
    },

    /**
     * @private
     */
    _needsDrawList: {
        value: []
    },

    /**
     * @private
     */
    _cannotDrawList: {
        value: null
    },

    /**
     * @function
     * @param {Object} component Component object
     */
    componentBlockDraw: {
        value: function(component) {
            this._cannotDrawList = (this._cannotDrawList ? this._cannotDrawList : {});
            this._cannotDrawList[component.uuid] = component;
            if (this._clearNeedsDrawTimeOut) {
                window.clearTimeout(this._clearNeedsDrawTimeOut);
                this._clearNeedsDrawTimeOut = null;
            }
        }
    },

    /**
     * @function
     * @param {Object} component Component object
     * @param {Number} value Component value
     */
    componentCanDraw: {
        value: function(component, value) {
            if (value) {
                if (!this._cannotDrawList) {
                    return;
                }
                delete this._cannotDrawList[component.uuid];
                this._needsDrawList.push(component);
                if (Object.keys(this._cannotDrawList).length === 0 && this._needsDrawList.length > 0) {
                    if (!this._clearNeedsDrawTimeOut) {
                        var self = this;
                        // Wait to clear the needsDraw list as components could be loaded synchronously
                        this._clearNeedsDrawTimeOut = window.setTimeout(function() {
                            self._clearNeedsDrawList();
                        }, 0);
                    }
                }
            } else {
                if (this._clearNeedsDrawTimeOut) {
                    window.clearTimeout(this._clearNeedsDrawTimeOut);
                    this._clearNeedsDrawTimeOut = null;
                }
            }
        }
    },

    /**
     * @private
     */
    _clearNeedsDrawList: {
        value: function() {
            var component, i, length, needsDrawList = this._needsDrawList;
            length = needsDrawList.length;
            for (i = 0; i < length; i++) {
                component = needsDrawList[i];
                if (component.needsDraw) {
                    component._addToParentsDrawList();
                }
            }
            this._clearNeedsDrawTimeOut = null;
            needsDrawList.splice(0, length);
        }
    },

    /**
     * @function
     * @param {Component} componentId The component ID
     */
    removeFromCannotDrawList: {
        value: function(component) {
            if (!this._cannotDrawList) {
                return;
            }

            delete this._cannotDrawList[component.uuid];

            if (Object.keys(this._cannotDrawList).length === 0 && this._needsDrawList.length > 0) {
                if (!this._clearNeedsDrawTimeOut) {
                    var self = this;
                    this._clearNeedsDrawTimeOut = window.setTimeout(function() {
                        self._clearNeedsDrawList();
                    }, 0);
                }
            }
        }
    },

    /**
     * @private
     */
    _cancelDrawIfScheduled: {
        value: function() {
            var requestedAnimationFrame = this.requestedAnimationFrame,
                cancelAnimationFrame = this.cancelAnimationFrame;
            if (requestedAnimationFrame !== null) {
                if (!this._frameTime) { // Only cancel it is not already in a drawTree call
                    if (logger.isDebug) {
                        logger.debug(this, "clearing draw");
                    }
                    if (cancelAnimationFrame) {
                        cancelAnimationFrame.call(window, requestedAnimationFrame);
                    } else {
                        window.clearTimeout(requestedAnimationFrame);
                    }
                    this.requestedAnimationFrame = null;
                }
            }
        }
    },

    /**
     * Adds the passed in child component to the drawList.
     * @private
     */
    _addToDrawList: {
        value: function(childComponent) {
            this.__addToDrawList(childComponent);
            if (drawLogger.isDebug) {
                drawLogger.debug(this, this.canDrawGate.value, this.requestedAnimationFrame);
            }
            this.drawTree();
        },
        enumerable: false
    },

    /**
     * Adds the passed in composer to the list of composers to be executed
     * in the next draw cycle and requests a draw cycle if one has not been
     * requested yet.
     * @function
     * @param {Composer} composer Composer object
     */
    addToComposerList: {
        value: function(composer) {
            this.composerList.push(composer);
            if (drawLogger.isDebug) {
                drawLogger.debug(this, composer, "Added to composer list");
            }
            // If a draw is already in progress this.drawTree() will not schedule another one, so track
            // that a composer requested a draw in case a new draw does need to be scheduled when the
            // current loop is done
            this._scheduleComposerRequest = true;
            this.drawTree();
        }
    },

    // Create a second composer list so that the lists can be swapped during a draw instead of creating a new array every time
    composerListSwap: {
        value: [],
        distinct: true
    },

    /*
     * Flag to track if a composer is requesting a draw
     * @private
     */
    _scheduleComposerRequest: {
        value: false
    },

    /**
     * The value returned by requestAnimationFrame.
     * If a request has been scheduled but not run yet, else null.
     * @private
     * @type {Number}
     * @default null
     */
    requestedAnimationFrame: {
        value: null,
        enumerable: false
    },

    /**
     * @private
     * @function
     */
    requestAnimationFrame: {
        value: (window.requestAnimationFrame || window.webkitRequestAnimationFrame 
             || window.mozRequestAnimationFrame ||  window.msRequestAnimationFrame),
        enumerable: false
    },

    /**
     * @private
     * @function
     */
    cancelAnimationFrame: {
        value: (window.cancelAnimationFrame ||  window.webkitCancelAnimationFrame 
             || window.mozCancelAnimationFrame || window.msCancelAnimationFrame),
        enumerable: false
    },

    /**
     * Set to the current time of the frame while drawing is in progress.
     * The frame time is either supplied by the requestAnimationFrame callback if available in the browser, or by using Date.now if it is a setTimeout.
     * @private
     */
    _frameTime: {
        value: null
    },

    /**
     * oldSource and diff are used to detect DOM modifications outside of the
     * draw loop, but only if drawLogger.isDebug is true.
     * @private
     */
    _oldSource: {
        value: null
    },
    _diff: {
        // Written by John Resig. Used under the Creative Commons Attribution 2.5 License.
        // http://ejohn.org/projects/javascript-diff-algorithm/
        value: function( o, n ) {
            var ns = {};
            var os = {};

            //jshint -W116
            for (var i = 0; i < n.length; i++ ) {
                if (ns[ n[i] ] == null ) {
                    ns[ n[i] ] = { rows: [], o: null };
                }
                ns[ n[i] ].rows.push( i );
            }

            for (i = 0; i < o.length; i++ ) {
                if (os[ o[i] ] == null ) {
                    os[ o[i] ] = { rows: [], n: null };
                }
                os[ o[i] ].rows.push( i );
            }

            for (i in ns ) {
                if (ns[i].rows.length === 1 && typeof(os[i]) !== "undefined" && os[i].rows.length === 1 ) {
                    n[ ns[i].rows[0] ] = { text: n[ ns[i].rows[0] ], row: os[i].rows[0] };
                    o[ os[i].rows[0] ] = { text: o[ os[i].rows[0] ], row: ns[i].rows[0] };
                }
            }

            for (i = 0; i < n.length - 1; i++ ) {
                if (n[i].text != null && n[i+1].text == null &&
                    n[i].row + 1 < o.length && o[ n[i].row + 1 ].text == null &&
                    n[i+1] == o[ n[i].row + 1 ]
                ) {
                    n[i+1] = { text: n[i+1], row: n[i].row + 1 };
                    o[n[i].row+1] = { text: o[n[i].row+1], row: i + 1 };
                }
            }

            for (i = n.length - 1; i > 0; i-- ) {
                if (n[i].text != null && n[i-1].text == null &&
                    n[i].row > 0 && o[ n[i].row - 1 ].text == null &&
                    n[i-1] == o[ n[i].row - 1 ]
                ) {
                    n[i-1] = { text: n[i-1], row: n[i].row - 1 };
                    o[n[i].row-1] = { text: o[n[i].row-1], row: i - 1 };
                }
            }
            //jshint +W116

            return { o: o, n: n };
        }
    },

    /**
     * @private
     */
    _previousDrawDate: {
        enumerable: false,
        value: 0
    },

    /**
     * @private
     */
    _documentResources: {
        value: null
    },

    /**
     * @private
     */
    _needsStylesheetsDraw: {
        value: false
    },

    /**
     * @private
     */
    _stylesheets: {
        value: []
    },

    /**
     * @private
     */
    addStylesheet: {
        value: function(style) {
            this._stylesheets.push(style);
            this._needsStylesheetsDraw = true;
        }
    },

    /**
     * @private
     */
    drawStylesheets: {
        value: function() {
            var documentResources = this._documentResources,
                stylesheets = this._stylesheets,
                stylesheet;

            while ((stylesheet = stylesheets.shift())) {
                documentResources.addStyle(stylesheet);
            }
            this._needsStylesheetsDraw = false;
        }
    },

    /**
     * @private
     */
    drawTree: {
        value: function drawTree() {
            if (this.requestedAnimationFrame === null) { // 0 is a valid requestedAnimationFrame value
                if (drawLogger.isDebug) {
                    drawLogger.debug(this, "requesting a draw");
                }
                var self = this, requestAnimationFrame = this.requestAnimationFrame;
                var _drawTree = function(timestamp) {
                    var drawPerformanceStartTime;

                    // Before initiating a draw cycle through the components we
                    // need to have a draw cycle just to add all the stylesheets
                    // if any is requested to draw.
                    // We need to do this because adding the stylesheets at the
                    // same time the components draw won't make the styles
                    // available at that first draw.
                    if (self._needsStylesheetsDraw) {
                        self.drawStylesheets();
                        self.requestedAnimationFrame = null;
                        self.drawTree();
                        return;
                    }

                    if (drawPerformanceLogger.isDebug) {
                        if (window.performance) {
                            drawPerformanceStartTime = window.performance.now();
                        } else {
                            drawPerformanceStartTime = Date.now();
                        }
                    }
                    self._frameTime = (timestamp ? timestamp : Date.now());
                    if (self._clearNeedsDrawTimeOut) {
                        self._clearNeedsDrawList();
                    }
                    if (drawLogger.isDebug) {
                        // Detect any DOM modification since the previous draw
                        var newSource = document.documentElement.innerHTML;
                        if (self._oldSource && newSource !== self._oldSource) {
                            var warning = ["DOM modified outside of the draw loop"];
                            var out = self._diff(self._oldSource.split("\n"), newSource.split("\n"));
                            for (var i = 0; i < out.n.length; i++) {
                                // == null ok. Is also checking for undefined
                                if (out.n[i].text == null) {
                                    warning.push('+ ' + out.n[i]);
                                } else {
                                    // == null ok. Is also checking for undefined
                                    for (var n = out.n[i].row + 1; n < out.o.length && out.o[n].text == null; n++) {
                                        warning.push('- ' + out.o[n]);
                                    }
                                }
                            }
                            console.warn(warning.join("\n"));
                        }

                        console.group((timestamp ? drawLogger.toTimeString(new Date(timestamp)) + " " : "") + "Draw Fired");
                    }

                    self.drawIfNeeded();

                    if (drawPerformanceLogger.isDebug) {
                        if (window.performance) {
                            var drawPerformanceEndTime = window.performance.now();
                        } else {
                            var drawPerformanceEndTime = Date.now();
                        }

                        console.log("Draw Cycle Time: ",
                            drawPerformanceEndTime - drawPerformanceStartTime,
                            ", Components: ", self._lastDrawComponentsCount);
                    }

                    if (drawLogger.isDebug) {
                        console.groupEnd();
                        self._oldSource = document.documentElement.innerHTML;
                    }
                    self._frameTime = null;
                    if (self._scheduleComposerRequest) {
                        self.drawTree();
                    }
                };
                if (requestAnimationFrame) {
                    this.requestedAnimationFrame = requestAnimationFrame.call(window, _drawTree);
                } else {
                    // Shim based in Erik Möller's code at
                    // http://my.opera.com/emoller/blog/2011/12/20/requestanimationframe-for-smart-er-animating
                    var currentDate = Date.now(),
                        miliseconds = 17 - currentDate + this._previousDrawDate;

                    if (miliseconds < 0) {
                        miliseconds = 0;
                    }
                    this.requestedAnimationFrame = setTimeout(_drawTree, miliseconds);
                    this._previousDrawDate = currentDate + miliseconds;
                }
                this._scheduleComposerRequest = false;
            }
        },
        enumerable: false
    },

    /**
     * @private
     */
    _readyToDrawList: {
        enumerable: false,
        value: []
    },

    /**
     * @private
     */
    _readyToDrawListIndex: {
        enumerable: false,
        value: null
    },

    /**
     * @function
     * @param {Component} component Component to add
     */
    addToDrawCycle: {
        value: function(component) {
            var needsDrawListIndex = this._readyToDrawListIndex, length, composer;

            if (needsDrawListIndex.hasOwnProperty(component.uuid)) {
                // Requesting a draw of a component that has already been drawn in the current cycle
                if (drawLogger.isDebug) {
                    drawLogger.debug("components should not be added to the draw cycle twice");
                }
                return;
            }
            this._readyToDrawList.push(component);
            this._readyToDrawListIndex[component.uuid] = true;

            component._updateComponentDom();
        }
    },


    _lastDrawComponentsCount: {
        value: null
    },

    /**
     * @private
     * @function
     * @returns Boolean true if all the components that needed to draw have drawn
    */
    drawIfNeeded:{
        value: function drawIfNeeded() {
            var needsDrawList = this._readyToDrawList, component, i, j, start = 0, firstDrawEvent,
                composerList = this.composerList, composer, composerListLength;
            needsDrawList.length = 0;
            composerListLength = composerList.length;
            this._readyToDrawListIndex = {};

            // Process the composers first so that any components that need to be newly drawn due to composer changes
            // get added in this cycle
            if (composerListLength > 0) {
                this.composerList = this.composerListSwap; // Swap between two arrays instead of creating a new array each draw cycle
                for (i = 0; i < composerListLength; i++) {
                    composer = composerList[i];
                    composer.needsFrame = false;
                    composer.frame(this._frameTime);
                }
                composerList.splice(0, composerListLength);
                this.composerListSwap = composerList;
            }

            this._drawIfNeeded(0);
            j = needsDrawList.length;

            while (start < j) {
                for (i = start; i < j; i++) {
                    component = needsDrawList[i];
                    if (typeof component.willDraw === "function") {
                        component.willDraw(this._frameTime);
                    }
                    if (drawLogger.isDebug) {
                        drawLogger.debug(component._montage_metadata.objectName, " willDraw treeLevel ",component._treeLevel);
                    }
                }
                this._drawIfNeeded(0);
                start = j;
                j = needsDrawList.length;
            }

            // Sort the needsDraw list so that any newly added items are drawn in the correct order re: parent-child
            var sortByLevel = function(component1, component2) {
                return component1._treeLevel - component2._treeLevel;
            };
            needsDrawList.sort(sortByLevel);

            for (i = 0; i < j; i++) {
                component = needsDrawList[i];
                component.needsDraw = false;
            }
            this.requestedAnimationFrame = null; // Allow a needsDraw called during a draw to schedule the next draw
            // TODO: add the possibility to display = "none" the body during development (IKXARIA-3631).
            for (i = j-1; i >= 0; i--) {
                component = needsDrawList[i];
                component._draw(this._frameTime);
                component.draw(this._frameTime);
                if (drawLogger.isDebug) {
                    drawLogger.debug(component._montage_metadata.objectName, " draw treeLevel ",component._treeLevel);
                }
            }

            for (i = 0; i < j; i++) {
                component = needsDrawList[i];
                component.didDraw(this._frameTime);
                if (!component._completedFirstDraw) {
                    firstDrawEvent = document.createEvent("CustomEvent");
                    firstDrawEvent.initCustomEvent("firstDraw", true, false, null);
                    component.dispatchEvent(firstDrawEvent);
                    component._completedFirstDraw = true;
                }
                if (drawLogger.isDebug) {
                    drawLogger.debug(component._montage_metadata.objectName, " didDraw treeLevel ",component._treeLevel);
                }
            }

            if (drawPerformanceLogger.isDebug) {
                this._lastDrawComponentsCount = needsDrawList.length;
            }

            return !!needsDrawList.length;
        }
    },

    /**
     * @private
     * @type {DOMElement}
     * @default null
     */
    element: {
        get:function() {
            return this._element;
        },
        set:function(value) {
            defaultEventManager.registerEventHandlerForElement(this, value);
            this._element = value;
            this._documentResources = DocumentResources.getInstanceForDocument(value);
        }
    }
});

var rootComponent = new RootComponent().init();
//if(window.parent && window.parent.jasmine) {
exports.__root__ = rootComponent;
//}

}})
;
//*/
montageDefine("6364dae","core/target",{dependencies:["montage","core/event/event-manager","core/event/mutable-event"],factory:function(require,exports,module){var Montage = require("montage").Montage,
    defaultEventManager = require("core/event/event-manager").defaultEventManager,
    MutableEvent = require("core/event/mutable-event").MutableEvent;

/**
 * A Target is any object that can be a candidate for dispatching and receiving events
 * throughout what is typically considered the "component tree" of a Montage application.
 *
 * @type {Target}
 */
exports.Target = Montage.specialize( {

    constructor: {
        value: function Target() {
            this.super();
        }
    },

    /**
     * Whether or not this target can accept user focus and become the activeTarget
     * This matches up with the <code>document.activeElement</code> property purpose-wise;
     * Events from components that should be dispatched as logically occurring at the point
     * of user focus should be dispatched at the activeTarget
     *
     * By default a target does not accept this responsibility.
     */
    acceptsActiveTarget: {
        serializable: false,
        value: false
    },

    /**
     * Whether or not this is the activeTarget
     */
    isActiveTarget: {
        get: function () {
            return this === defaultEventManager.activeTarget;
        }
    },

    /**
     * Called prior to this target becoming the activeTarget
     * @param {Target} oldTarget the current activeTarget
     */
    willBecomeActiveTarget: {
        value: Function.noop
    },

    /**
     * Called after to this target became the activeTarget
     */
    didBecomeActiveTarget: {
        value: Function.noop
    },

    /**
     * Ask this target to surrender its activeTarget status
     *
     * @param {Target} newTarget the Target that is about to become the activeTarget
     * @return {Boolean} Whether or not to surrender activeTarget status
     */
    surrendersActiveTarget: {
        value: function (newTarget) {
            return true;
        }
    },

    /**
     * Which target to distribute an event after this when distributing events throughout a
     * graph of targets.
     */
    nextTarget: {
        serializable: false,
        value: null
    },

    /**
     * Dispatches the specified event with this target
     * as the event's proximal target
     *
     * @param {Event} event The event object to dispatch
     */
    dispatchEvent: {
        value: function(event) {
            var targettedEvent = event;

            if (! (event instanceof MutableEvent)) {
                targettedEvent = MutableEvent.fromEvent(targettedEvent);
            }

            targettedEvent.target = this;
            defaultEventManager.handleEvent(targettedEvent);
        }
    },

    /**
     * Creates and dispatches an event with the specified properties with this
     * target as the event's proximal target
     *
     * @param {string} type The type of the event to dispatch
     * @param {boolean} canBubble Whether or not the event can bubble
     * @param {boolean} cancelable Whether or not the event can be cancelled
     * @param {Object} detail The optional detail object of the event
     */
    dispatchEventNamed: {
        value: function(type, canBubble, cancelable, detail) {
            var event = MutableEvent.fromType(type, canBubble, cancelable, detail);
            event.target = this;
            defaultEventManager.handleEvent(event);
        }
    },

    /**
     * Adds an event listener to the object.
     * @param {string} type The event type to listen for.
     * @param {object | function} listener The listener object or function.
     * @param {boolean} useCapture Specifies whether to listen for the event during the bubble or capture phases.
     */
    addEventListener: {
        value: function addEventListener(type, listener, useCapture) {
            if (listener) {
                defaultEventManager.registerEventListener(this, type, listener, useCapture);
            }
        }
    },

    /**
     * Removes an event listener from the object.
     * @param {string} type The event type.
     * @param {object | function} listener The listener object or function.
     * @param {boolean} useCapture The phase of the event listener.
     */
    removeEventListener: {
        value: function removeEventListener(type, listener, useCapture) {
            if (listener) {
                defaultEventManager.unregisterEventListener(this, type, listener, useCapture);
            }
        }
    }
});

}})
;
//*/
montageDefine("6364dae","core/template",{dependencies:["montage","core/serialization","core/document-part","core/document-resources","core/serialization/serialization","core/serialization/serializer/montage-labeler","core/promise","core/logger","core/event/event-manager","core/application"],factory:function(require,exports,module){var Montage = require("montage").Montage,
    Deserializer = require("core/serialization").Deserializer,
    DocumentPart = require("core/document-part").DocumentPart,
    DocumentResources = require("core/document-resources").DocumentResources,
    Serialization = require("core/serialization/serialization").Serialization,
    MontageLabeler = require("core/serialization/serializer/montage-labeler").MontageLabeler,
    Promise = require("core/promise").Promise,
    logger = require("core/logger").logger("template"),
    defaultEventManager = require("core/event/event-manager").defaultEventManager,
    defaultApplication;

var Template = Montage.specialize( {
    _SERIALIZATON_SCRIPT_TYPE: {value: "text/montage-serialization"},
    _ELEMENT_ID_ATTRIBUTE: {value: "data-montage-id"},
    PARAM_ATTRIBUTE: {value: "data-param"},

    _require: {value: null},
    _resources: {value: null},
    document: {value: null},
    _baseUrl: {value: null},
    _instances: {value: null},
    _metadata: {value: null},

    _objectsString: {value: null},
    objectsString: {
        get: function() {
            return this._objectsString;
        },
        set: function(value) {
            this._objectsString = value;
            if (this._serialization) {
                this._serialization.initWithString(value);
            }
            // Invalidate the deserializer cache since there's a new
            // serialization in town.
            this.__deserializer = null;
        }
    },

    // Deserializer cache
    __deserializer: {value: null},
    _deserializer: {
        get: function() {
            var deserializer = this.__deserializer,
                metadata,
                requires;

            if (!deserializer) {
                metadata = this._metadata;
                if (metadata) {
                    requires = Object.create(null);
                    for (var label in metadata) {
                        requires[label] = metadata[label].require;
                    }
                }
                deserializer = new Deserializer().init(this.objectsString,
                    this._require, requires);
                this.__deserializer = deserializer;
            }

            return deserializer;
        }
    },
    getDeserializer: {
        value: function() {
            return this._deserializer;
        }
    },

    _serialization: {
        value: null
    },
    getSerialization: {
        value: function() {
            var serialiation = this._serialization;

            if (!serialiation) {
                serialiation = this._serialization = new Serialization();
                serialiation.initWithString(this.objectsString);
            }

            return serialiation;
        }
    },

    constructor: {
        value: function Template() {
            this.super();
        }
    },

    /**
     * Initializes the Template with an empty document.
     *
     * @function
     * @param {require} _require The require function used to load modules when
     *                           a template is instantiated.
     */
    initWithRequire: {
        value: function(_require) {
            this._require = _require;
            this.document = this.createHtmlDocumentWithHtml("");
            this.objectsString = "";

            return this;
        }
    },

    /**
     * Initializes the Template with a document.
     *
     * @function
     * @param {HTMLDocument} _document The document to be used as a template.
     * @param {require} _require The require function used to load modules when
     *                           a template is instantiated.
     * @returns {Promise} A promise for the proper initialization of the
     *                    template.
     */
    initWithDocument: {
        value: function(_document, _require) {
            var self = this;

            this._require = _require;
            this.setDocument(_document);

            return this.getObjectsString(_document)
            .then(function(objectsString) {
                self.objectsString = objectsString;
                return self;
            });
        }
    },

    /**
     * Initializes the Template with an HTML string.
     *
     * @function
     * @param {HTMLDocument} html The HTML string to be used as a template.
     * @param {require} _require The require function used to load modules when
     *                           a template is instantiated.
     * @returns {Promise} A promise for the proper initialization of the
     *                    template.
     */
    initWithHtml: {
        value: function(html, _require) {
            var self = this;

            this._require = _require;
            this.document = this.createHtmlDocumentWithHtml(html);

            return this.getObjectsString(this.document)
            .then(function(objectsString) {
                self.objectsString = objectsString;
                return self;
            });
        }
    },

    /**
     * Initializes the Template with Objects and a DocumentFragment to be
     * used as the body of the document.
     *
     * @function
     * @param {Object} objects A JSON'able representation of the objects of the
     *                         template.
     * @param {DocumentFragment} html The HTML string to be used as the body.
     * @param {require} _require The require function used to load modules when
     *                           a template is instantiated.
     * @returns {Promise} A promise for the proper initialization of the
     *                    template.
     */
    initWithObjectsAndDocumentFragment: {
        value: function(objects, documentFragment, _require) {
            var self = this;

            this._require = _require;
            this.document = this.createHtmlDocumentWithHtml("");
            this.document.body.appendChild(
                this.document.importNode(documentFragment, true)
            );
            this.setObjects(objects);

            return this;
        }
    },

    /**
     * Initializes the Template with the HTML document at the module id.
     *
     * @function
     * @param {String} moduleId The module id of the HTML page to load.
     * @param {require} _require The require function used to load modules when
     *                           a template is instantiated.
     * @returns {Promise} A promise for the proper initialization of the
     *                    template.
     */
    initWithModuleId: {
        value: function(moduleId, _require) {
            var self = this;

            this._require = _require;

            return this.createHtmlDocumentWithModuleId(moduleId, _require)
            .then(function(_document) {
                var baseUrl = _require(moduleId).directory;

                self.document = _document;
                self.setBaseUrl(baseUrl);

                return self.getObjectsString(_document)
                .then(function(objectsString) {
                    self.objectsString = objectsString;

                    return self;
                });
            });
        }
    },

    clone: {
        value: function() {
            var clonedTemplate = new Template();

            clonedTemplate._require = this._require;
            clonedTemplate._baseUrl = this._baseUrl;
            clonedTemplate.setDocument(this.document);
            clonedTemplate.objectsString = this.objectsString;
            clonedTemplate._instances = Object.clone(this._instances, 1);

            return clonedTemplate;
        }
    },

    instantiate: {
        value: function(targetDocument) {
            return this.instantiateWithInstances(null, targetDocument);
        }
    },

    /**
     * @param instances {Object} The instances to use in the serialization
     *        section of the template, when given they will be used instead of
     *        creating a new object. It's dictionary where the keys are the
     *        labels and the values the instances.
     * @param targetDocument {Document} The document used to create the markup
     *        resultant of the instantiation.
     */
    instantiateWithInstances: {
        value: function(instances, targetDocument) {
            var self = this,
                fragment,
                part = new DocumentPart(),
                templateObjects,
                templateParameters;

            instances = instances || this._instances;
            fragment = this._createMarkupDocumentFragment(targetDocument);
            templateParameters = this._getParameters(fragment);

            part.initWithTemplateAndFragment(this, fragment);
            part.startActingAsTopComponent();
            part.parameters = templateParameters;

            templateObjects = this._createTemplateObjects(instances);

            return this._instantiateObjects(templateObjects, fragment)
            .then(function(objects) {
                var resources;

                part.objects = objects;
                self._invokeDelegates(part, instances);
                part.stopActingAsTopComponent();

                resources = self.getResources();
                if (!resources.resourcesLoaded() && resources.hasResources()) {
                    // TODO: We were waiting for all resources to be
                    // loaded before considering the template loaded.
                    // Since we need the template to be loaded in order to load
                    // the rest of the component tree this turns the loading of
                    // each component tree level into a serial operation.
                    // This is particularly bad for mobile devices like the
                    // iPad where the network connection is a bottleneck making
                    // the application load very slowly.
                    // We need to adopt a different strategy where the resources
                    // can be loaded in parallel, for now we preload the
                    // resources but don't wait for them.
                    resources.loadResources(targetDocument);
                }
                return part;
            });
        }
    },

    _objectsInstantiationOptimized: {
        value: false
    },
    _optimizeObjectsInstantiationPromise: {
        value: null
    },
    /**
     * @return {undefined|Promise} A promise if there are objects to optimize,
     *         nothing otherwise.
     */
    _optimizeObjectsInstantiation: {
        value: function() {
            var self = this,
                promise;

            if (!this._objectsInstantiationOptimized) {
                if (!this._optimizeObjectsInstantiationPromise) {
                    promise = this._deserializer.preloadModules();

                    if (promise) {
                        this._optimizeObjectsInstantiationPromise = promise
                        .then(function() {
                            self._objectsInstantiationOptimized = true;
                        });
                    } else {
                        this._objectsInstantiationOptimized = true;
                    }
                }

                return this._optimizeObjectsInstantiationPromise;
            }
        }
    },

    setBaseUrl: {
        value: function(baseUrl) {
            this._baseUrl = baseUrl;
        }
    },

    getBaseUrl: {
        value: function() {
            return this._baseUrl;
        }
    },

    getResources: {
        value: function() {
            var resources = this._resources;

            if (!resources) {
                resources = this._resources = new TemplateResources();
                resources.initWithTemplate(this);
            }

            return resources;
        }
    },

    /**
     * Creates the object instances to be passed to the deserialization.
     * It takes instances and augments it with "application" and "template".
     *
     * @param {Object} instances The instances object.
     * @returns {Object} The object with instances and application and template.
     */
    _createTemplateObjects: {
        value: function(instances) {
            var templateObjects = Object.create(instances || null);

            if (typeof defaultApplication === "undefined") {
                defaultApplication = require("core/application").application;
            }

            templateObjects.application = defaultApplication;
            templateObjects.template = this;

            return templateObjects;
        }
    },

    _instantiateObjects: {
        value: function(instances, fragment) {
            var self = this,
                deserializer = this._deserializer,
                optimizationPromise;

            optimizationPromise = this._optimizeObjectsInstantiation();

            if (optimizationPromise) {
                return optimizationPromise.then(function() {
                    return deserializer.deserialize(instances, fragment);
                });
            } else {
                return deserializer.deserialize(instances, fragment);
            }
        }
    },

    _createMarkupDocumentFragment: {
        value: function(targetDocument) {
            var fragment = targetDocument.createDocumentFragment(),
                nodes = this.document.body.childNodes;

            for (var i = 0, ii = nodes.length; i < ii; i++) {
                fragment.appendChild(
                    targetDocument.importNode(nodes[i], true)
                );
            }

            return fragment;
        }
    },

    getParameterName: {
        value: function(element) {
            return element.getAttribute(this.PARAM_ATTRIBUTE);
        }
    },

    getParameters: {
        value: function() {
            return this._getParameters(this.document.body);
        }
    },

    _getParameters: {
        value: function(rootElement) {
            var elements = rootElement.querySelectorAll("*[" + this.PARAM_ATTRIBUTE + "]"),
                elementsCount = elements.length,
                element,
                parameters = {};

            for (var i = 0; i < elementsCount; i++) {
                element = elements[i];
                var parameterName = this.getParameterName(element);

                if (parameterName in parameters) {
                    throw new Error('The parameter "' + parameterName + '" is' +
                        ' declared more than once in ' + this.getBaseUrl() +
                        '.');
                }

                parameters[parameterName] = element;
            }

            if ("*" in parameters && elementsCount > 1) {
                throw new Error('The star "*" template parameter was declared' +
                    ' when other parameters were also present in ' +
                    this.getBaseUrl() + ': ' + Object.keys(parameters) + '.');
            }

            return parameters;
        }
    },

    hasParameters: {
        value: function() {
            return !!this.document.querySelector("*[" + this.PARAM_ATTRIBUTE + "]");
        }
    },

    _invokeDelegates: {
        value: function(documentPart, instances) {
            var objects = documentPart.objects,
                object,
                owner = objects.owner || instances && instances.owner,
                objectOwner;

            for (var label in objects) {
                // Don't call delegate methods on objects that were passed to
                // the instantiation.
                if (instances && label in instances) {
                    continue;
                }

                object = objects[label];
                // getObjectOwner will take into account metadata that might
                // have been set for this object. Objects in the serialization
                // of the template might have different owners. This is true
                // when an object in the serialization is the result of a
                // data-param that was expanded using arguments from an external
                // template.
                objectOwner = this._getObjectOwner(label, owner)

                if (object) {
                    if (typeof object._deserializedFromTemplate === "function") {
                        object._deserializedFromTemplate(objectOwner, label, documentPart);
                    }
                    if (typeof object.deserializedFromTemplate === "function") {
                        object.deserializedFromTemplate(objectOwner, label, documentPart);
                    }
                }
            }

            if (owner) {
                var serialization = this.getSerialization();

                // Don't call delegate methods on external objects
                if (!serialization.isExternalObject("owner")) {
                    if (typeof owner._templateDidLoad === "function") {
                        owner._templateDidLoad(documentPart);
                    }
                    if (typeof owner.templateDidLoad === "function") {
                        owner.templateDidLoad(documentPart);
                    }
                }
            }
        }
    },

    /**
     * Sets the instances to use when instantiating the objects of the template.
     * These instances will always be used when instantiating the template
     * unless a different set of instances is passed in
     * instantiateWithInstances().
     *
     * @function
     * @param {Object} instances The objects' instances.
     */
    setInstances: {
        value: function(instances) {
            this._instances = instances;
        }
    },

    getInstances: {
        value: function() {
            return this._instances;
        }
    },

    setObjects: {
        value: function(objects) {
            // TODO: use Serializer.formatSerialization(object|string)
            this.objectsString = JSON.stringify(objects, null, 4);
        }
    },

    /**
     * Add metadata to specific objects of the serialization.
     *
     * @param {String} label The label of the object in the serialization.
     * @param {Require} _require The require function to be used when loading
     *        the module.
     * @param {String} effectiveLabel An alternative label to be given to the
     *        object.
     * @param {Object} owner The owner object to be given to the object.
     */
    setObjectMetadata: {
        value: function(label, _require, effectiveLabel, owner) {
            var metadata = this._metadata;

            if (!metadata) {
                this._metadata = metadata = Object.create(null);
            }

            metadata[label] = {
                "require": _require,
                "label": effectiveLabel,
                "owner": owner
            };

            // Invalidate the deserializer cache since we need to setup new
            // requires.
            this.__deserializer = null;
        }
    },

    getObjectMetadata: {
        value: function(label) {
            var metadata = this._metadata;

            if (metadata && label in metadata) {
                return metadata[label];
            } else {
                return {
                    "require": this._require,
                    "label": label
                }
            }
        }
    },

    _getObjectOwner: {
        value: function(label, defaultOwner) {
            var objectOwner,
                metadata = this._metadata;

            if (metadata && label in metadata) {
                objectOwner = metadata[label].owner;
            } else {
                objectOwner = defaultOwner;
            }

            return objectOwner;
        }
    },

    /**
     * Uses the document markup as the base of the template markup.
     *
     * @function
     * @param {HTMLDocument} doc The document.
     * @returns {Promise} A promise for the proper initialization of the
     *                    document.
     */
    setDocument: {
        value: function(_document) {
            var html = _document.documentElement.innerHTML;

            this.document = this.createHtmlDocumentWithHtml(html);
        }
    },

    /**
     * Searches for objects in the document.
     * The objects string can live as an inline script in the document or as an
     * external resource that needs to be loaded.
     *
     * @function
     * @param {HTMLDocument} doc The document with the objects string.
     * @returns {Promise} A promise for the objects string, null if not
     *                    found.
     */
    getObjectsString: {
        value: function(doc) {
            var objectsString;

            objectsString = this.getInlineObjectsString(doc);

            if (objectsString === null) {
                return this.getExternalObjectsString(doc);
            } else {
                return Promise.resolve(objectsString);
            }
        }
    },

    /**
     * Searches for an inline objects string in a document and returns it if
     * found.
     * @function
     * @param {HTMLDocument} doc The document with the objects string.
     * @returns {(String|null)} The objects string or null if not found.
     */
    getInlineObjectsString: {
        value: function(doc) {
            var selector = "script[type='" + this._SERIALIZATON_SCRIPT_TYPE + "']",
                script = doc.querySelector(selector);

            if (script) {
                return script.textContent;
            } else {
                return null;
            }
        }
    },

    /**
     * Searches for an external objects file in a document and returns its
     * contents if found.
     *
     * @function
     * @param {String} doc The document to search.
     * @returns {Promise} A promise to the contents of the objects file or null
     *                    if none found.
     */
    getExternalObjectsString: {
        value: function(doc) {
            var link = doc.querySelector('link[rel="serialization"]'),
                req,
                url,
                rootUrl,
                deferred;

            if (link) {
                req = new XMLHttpRequest();
                url = link.getAttribute("href");
                rootUrl = this.getBaseUrl() || "";

                if (! /^https?:\/\/|^\//.test(url)) {
                    url = rootUrl + url;
                } else {
                    return Promise.reject(
                        new Error("Relative link found for the objects file but the document URL is unknown: '" + url + "'.")
                    );
                }

                deferred = Promise.defer();

                req.open("GET", url);
                req.addEventListener("load", function() {
                    if (req.status == 200) {
                        deferred.resolve(req.responseText);
                    } else {
                        deferred.reject(
                            new Error("Unable to retrive '" + url + "', code status: " + req.status)
                        );
                    }
                }, false);
                req.addEventListener("error", function(event) {
                    deferred.reject(
                        new Error("Unable to retrive '" + url + "' with error: " + event.error + ".")
                    );
                }, false);
                req.send();

                return deferred.promise;
            } else {
                return Promise.resolve(null);
            }
        }
    },

    createHtmlDocumentWithHtml: {
        value: function(html) {
            var htmlDocument = document.implementation.createHTMLDocument("");

            htmlDocument.documentElement.innerHTML = html;

            return htmlDocument;
        }
    },

    createHtmlDocumentWithModuleId: {
        value: function(moduleId, _require) {
            var self = this;

            if (typeof _require !== "function") {
                return Promise.reject(
                    new Error("Missing 'require' function to load module '" + moduleId + "'.")
                );
            }

            return _require.async(moduleId).then(function(exports) {
                return self.createHtmlDocumentWithHtml(exports.content);
            });
        }
    },

    /**
     * Removes all artifacts related to objects string
     */
    _removeObjects: {
        value: function(doc) {
            var elements,
                selector = "script[type='" + this._SERIALIZATON_SCRIPT_TYPE + "'], link[rel='serialization']";

            Array.prototype.forEach.call(
                doc.querySelectorAll(selector),
                function (element) {
                   element.parentNode.removeChild(element);
                }
            );
        }
    },

    _addObjects: {
        value: function(doc, objectsString) {
            if (objectsString) {
                var script = doc.createElement("script");

                script.setAttribute("type", this._SERIALIZATON_SCRIPT_TYPE);
                script.textContent = JSON.stringify(JSON.parse(objectsString), null, 4);
                doc.head.appendChild(script);
            }
        }
    },

    _templateFromElementContentsCache: {
        value: null
    },
    clearTemplateFromElementContentsCache: {
        value: function () {
            this._templateFromElementContentsCache = null;
        }
    },

    createTemplateFromElementContents: {
        value: function(elementId) {
            var element,
                template,
                range,
                cache = this._templateFromElementContentsCache;

            if (!cache) {
                cache = Object.create(null);
                this._templateFromElementContentsCache = cache;
            }

            if (elementId in cache) {
                // We always return an extension of the cached object, this
                // is because the template can be assigned with instances.
                // An alternate idea would be to clone it but it's much more
                // expensive.
                return Object.create(cache[elementId]);
            }

            element = this.getElementById(elementId);

            // Clone the element contents
            range = this.document.createRange();
            range.selectNodeContents(element);

            // Create the new template with the extracted serialization and
            // markup.
            template = this.createTemplateFromRange(range);

            cache[elementId] = template;

            // We always return an extension of the cached object, this
            // is because the template is mutable.
            // An alternate idea would be to clone it but it's much more
            // expensive.
            return Object.create(template);
        }
    },

    createTemplateFromElement: {
        value: function(elementId) {
            var element,
                range;

            element = this.getElementById(elementId);

            // Clone the element contents
            range = this.document.createRange();
            range.selectNode(element);

            return this.createTemplateFromRange(range);
        }
    },

    createTemplateFromRange: {
        value: function (range) {
            var fragment,
                elementIds,
                labels,
                template,
                serialization = new Serialization(),
                extractedSerialization;

            fragment = range.cloneContents();

            // Find all elements of interest to the serialization.
            elementIds = this._getChildrenElementIds(fragment);

            // Create a new serialization with the components found in the
            // element.
            serialization.initWithString(this.objectsString);
            labels = serialization.getSerializationLabelsWithElements(
                elementIds);
            extractedSerialization = serialization.extractSerialization(
                labels, ["owner"]);

            // Create the new template with the extracted serialization and
            // markup.
            template = new Template();
            template.initWithObjectsAndDocumentFragment(
                null, fragment, this._require);
            template.objectsString = extractedSerialization
                .getSerializationString();
            template._resources = this.getResources();

            return template;
        }
    },

    // TODO: should this be on Serialization?
    _createSerializationWithElementIds: {
        value: function(elementIds) {
            var serialization = new Serialization(),
                labels,
                extractedSerialization;

            serialization.initWithString(this.objectsString);
            labels = serialization.getSerializationLabelsWithElements(
                elementIds);

            extractedSerialization = serialization.extractSerialization(
                labels, ["owner"]);

            return extractedSerialization;
        }
    },

    /**
     * @param {Template} template The template object where the arguments reside
     * @param {Object} delegate A delegate object that needs to implement
     *        getTemplateParameterArgument(template, name) function that returns
     *        the argument to replace with the `name` parameter.
     * @returns {Object} A dictionary with four properties representing the
     *          objects and elements that were imported into the template:
     *          - labels: the labels of the objects added from template
     *                    argument.
     *          - labelsCollisions: a dictionary of label collisions in the form
     *                              of {oldLabel: newLabel}.
     *          - elementIds: the element ids of the markup imported from
     *                        template argument.
     *          - elementIdsCollisions: a dictionary of element id collisions in
     *                                  the form of {oldElementId: newElementId}
     *
     */
    expandParameters: {
        value: function(template, delegate) {
            var parameterElements,
                argumentsElementIds = [],
                collisionTable,
                argumentElementsCollisionTable = {},
                objectsCollisionTable,
                parameterElement,
                argumentElement,
                serialization = this.getSerialization(),
                argumentsSerialization,
                result = {};

            parameterElements = this.getParameters();

            // Expand elements.
            for (var parameterName in parameterElements) {
                parameterElement = parameterElements[parameterName];
                argumentElement = delegate.getTemplateParameterArgument(
                    template, parameterName);

                // Store all element ids of the argument, we need to create
                // a serialization with the components that point to them.
                argumentsElementIds.push.apply(argumentsElementIds,
                    this._getElementIds(argumentElement)
                );

                // Replace the parameter with the argument and save the
                // element ids collision table because we need to correct the
                // serialization that is created from the stored element ids.
                collisionTable = this.replaceNode(argumentElement, parameterElement);
                if (collisionTable) {
                    for (var key in collisionTable) {
                        argumentElementsCollisionTable[key] = collisionTable[key];
                    }
                }
            }
            result.elementIds = argumentsElementIds;
            result.elementIdsCollisions = argumentElementsCollisionTable;

            // Expand objects.
            argumentsSerialization = template
                ._createSerializationWithElementIds(argumentsElementIds);

            argumentsSerialization.renameElementReferences(
                argumentElementsCollisionTable);

            objectsCollisionTable = serialization.mergeSerialization(
                argumentsSerialization);
            this.objectsString = serialization.getSerializationString();

            result.labels = argumentsSerialization.getSerializationLabels();
            result.labelsCollisions = objectsCollisionTable;

            return result;
        }
    },

    /**
     * Takes a foreign node and generate new ids for all element ids that
     * already exist in the current template.
     */
    _resolveElementIdCollisions: {
        value: function(node) {
            var collisionTable,
                nodeElements,
                elementIds,
                element,
                newId,
                labeler = new MontageLabeler();

            // Set up the labeler with the current element ids.
            elementIds = this.getElementIds();
            for (var i = 0, elementId; (elementId = elementIds[i]); i++) {
                labeler.setObjectLabel(null, elementId);
            }

            // Resolve element ids collisions.
            nodeElements = this._getElements(node);
            for (var elementId in nodeElements) {
                if (this.getElementById(elementId)) {
                    element = nodeElements[elementId];
                    newId = labeler.getObjectLabel(element);
                    this.setElementId(element, newId);
                    if (!collisionTable) {
                        collisionTable = Object.create(null);
                    }
                    collisionTable[elementId] = newId;
                }
            }

            return collisionTable;
        }
    },

    replaceNode: {
        value: function(newNode, oldNode) {
            var collisionTable;

            collisionTable = this._resolveElementIdCollisions(newNode);
            oldNode.parentNode.replaceChild(newNode, oldNode);

            return collisionTable;
        }
    },

    insertNodeBefore: {
        value: function(node, reference) {
            var collisionTable;

            collisionTable = this._resolveElementIdCollisions(node);
            reference.parentNode.insertBefore(node, reference);

            return collisionTable;
        }
    },

    appendNode: {
        value: function(node, parentNode) {
            var collisionTable;

            collisionTable = this._resolveElementIdCollisions(node);
            parentNode.appendChild(node);

            return collisionTable;
        }
    },

    getElementId: {
        value: function(element) {
            if (element.getAttribute) {
                return element.getAttribute(this._ELEMENT_ID_ATTRIBUTE);
            }
        }
    },

    setElementId: {
        value: function(element, elementId) {
            element.setAttribute(this._ELEMENT_ID_ATTRIBUTE, elementId);
        }
    },

    getElementIds: {
        value: function() {
            return this._getElementIds(this.document.body);
        }
    },

    _getElements: {
        value: function(rootNode) {
            var selector = "*[" + this._ELEMENT_ID_ATTRIBUTE + "]",
                elements,
                result = {},
                elementId,
                nodes;

            elements = rootNode.querySelectorAll(selector);

            for (var i = 0, element; (element = elements[i]); i++) {
                elementId = this.getElementId(element);
                result[elementId] = element;
            }

            elementId = this.getElementId(rootNode);
            if (elementId) {
                result[elementId] = rootNode;
            }

            return result;
        }
    },

    _getChildrenElementIds: {
        value: function(rootNode) {
            // XPath might do a better job here...should test.
            var selector = "*[" + this._ELEMENT_ID_ATTRIBUTE + "]",
                elements,
                elementIds = [];

            elements = rootNode.querySelectorAll(selector);

            for (var i = 0, element; (element = elements[i]); i++) {
                elementIds.push(this.getElementId(element));
            }

            return elementIds;
        }
    },

    _getElementIds: {
        value: function(rootNode) {
            var elementIds = this._getChildrenElementIds(rootNode),
                elementId;

            elementId = this.getElementId(rootNode);
            if (elementId) {
                elementIds.push(elementId);
            }

            return elementIds;
        }
    },

    getElementById: {
        value: function(elementId) {
            var selector = "*[" + this._ELEMENT_ID_ATTRIBUTE + "='" + elementId + "']";

            return this.document.querySelector(selector);
        }
    },

    html: {
        get: function() {
            var _document = this.document;

            this._removeObjects(_document);
            this._addObjects(_document, this.objectsString);

            return this._getDoctypeString(_document.doctype) + "\n" +
                _document.documentElement.outerHTML;
        }
    },

    _getDoctypeString: {
        value: function (doctype) {
            return "<!DOCTYPE " +
                doctype.name +
                (doctype.publicId ? ' PUBLIC "' + doctype.publicId + '"' : '') +
                (!doctype.publicId && doctype.systemId ? ' SYSTEM' : '') +
                (doctype.systemId ? ' "' + doctype.systemId + '"' : '') +
                '>';
        }
    }

}, {

    _templateCache: {
        value: {
            moduleId: Object.create(null)
        }
    },
    _getTemplateCacheKey: {
        value: function(moduleId, _require) {
            // Transforms relative module ids into absolute module ids
            moduleId = _require.resolve(moduleId);
            return _require.location + "#" + moduleId;
        }
    },
    getTemplateWithModuleId: {
        value: function(moduleId, _require) {
            var cacheKey,
                template;

            cacheKey = this._getTemplateCacheKey(moduleId, _require);
            template = this._templateCache.moduleId[cacheKey];

            if (!template) {
                template = new Template()
                .initWithModuleId(moduleId, _require);

                this._templateCache.moduleId[cacheKey] = template;
            }

            return template;
        }
    }

});

var TemplateResources = Montage.specialize( {
    _resources: {value: null},
    _resourcesLoaded: {value: false},
    template: {value: null},
    rootUrl: {value: ""},

    constructor: {
        value: function TemplateResources() {
            this._resources = Object.create(null);
        }
    },

    initWithTemplate: {
        value: function(template) {
            this.template = template;
        }
    },

    hasResources: {
        value: function() {
            return this.getStyles().length > 0 || this.getScripts().length > 0;
        }
    },

    resourcesLoaded: {
        value: function() {
            return this._resourcesLoaded;
        }
    },

    loadResources: {
        value: function(targetDocument) {
            this._resourcesLoaded = true;

            return Promise.all([
                this.loadScripts(targetDocument),
                this.loadStyles(targetDocument)
            ]);
        }
    },

    getScripts: {
        value: function() {
            var scripts = this._resources.scripts,
                script,
                type,
                template,
                templateScripts;

            if (!scripts) {
                template = this.template;

                scripts = this._resources.scripts = [];
                templateScripts = template.document.querySelectorAll("script");

                for (var i = 0, ii = templateScripts.length; i < ii; i++) {
                    script = templateScripts[i];

                    if (script.type !== this.template._SERIALIZATON_SCRIPT_TYPE) {
                        scripts.push(script);
                    }
                }
            }

            return scripts;
        }
    },

    loadScripts: {
        value: function(targetDocument) {
            var scripts,
                promises = [];

            scripts = this.getScripts();

            for (var i = 0, ii = scripts.length; i < ii; i++) {
                promises.push(
                    this.loadScript(scripts[i], targetDocument)
                );
            }

            return Promise.all(promises);
        }
    },

    loadScript: {
        value: function(script, targetDocument) {
            var url,
                documentResources,
                newScript;

            documentResources = DocumentResources.getInstanceForDocument(targetDocument);
            // Firefox isn't able to load a script that we reuse, we need to
            // create a new one :(.
            //newScript = targetDocument.importNode(script);
            newScript = this._cloneScriptElement(script, targetDocument);

            return documentResources.addScript(newScript);
        }
    },

    _cloneScriptElement: {
        value: function(scriptTemplate, _document) {
            var script = _document.createElement("script"),
                attributes = scriptTemplate.attributes,
                attribute;

            for (var i = 0, ii = attributes.length; i < ii; i++) {
                attribute = attributes[i];

                script.setAttribute(attribute.name, attribute.value);
            }

            return script;
        }
    },

    getStyles: {
        value: function() {
            var styles = this._resources.styles,
                template,
                templateStyles,
                styleSelector;

            if (!styles) {
                styleSelector = 'link[rel="stylesheet"], style';
                template = this.template;

                templateStyles = template.document.querySelectorAll(styleSelector);

                styles = Array.prototype.slice.call(templateStyles, 0);
                this._resources.styles = styles;
            }

            return styles;
        }
    },

    loadStyles: {
        value: function(targetDocument) {
            var promises = [],
                styles;

            styles = this.getStyles();

            for (var i = 0, ii = styles.length; i < ii; i++) {
                promises.push(
                    this.loadStyle(styles[i], targetDocument)
                );
            }

            return Promise.all(promises);
        }
    },

    loadStyle: {
        value: function(element, targetDocument) {
            var url,
                documentResources,
                baseUrl = this.template.getBaseUrl();

            url = element.getAttribute("href");

            if (url) {
                documentResources = DocumentResources.getInstanceForDocument(targetDocument);

                url = documentResources.normalizeUrl(url, baseUrl);

                return documentResources.preloadResource(url);
            } else {
                return Promise.resolve();
            }
        }
    },

    createStylesForDocument: {
        value: function(targetDocument) {
            var styles = this.getStyles(),
                newStyle,
                stylesForDocument = [],
                baseUrl = this.template.getBaseUrl(),
                documentResources,
                url,
                normalizedUrl;

            documentResources = DocumentResources.getInstanceForDocument(targetDocument);

            for (var i = 0, style; (style = styles[i]); i++) {
                url = style.getAttribute("href");
                newStyle = targetDocument.importNode(style, true);
                stylesForDocument.push(newStyle);

                if (url) {
                    normalizedUrl = documentResources.normalizeUrl(url, baseUrl);
                    newStyle.setAttribute("href", normalizedUrl);
                }
            }

            return stylesForDocument;
        }
    }
});

// Used to create a DocumentPart from a document without a Template
function instantiateDocument(_document, _require, instances) {
    var self = this,
        template = new Template(),
        html = _document.documentElement.outerHTML,
        part = new DocumentPart(),
        clonedDocument,
        templateObjects,
        rootElement = _document.documentElement;

    // Setup a template just like we'd do for a document in a template
    clonedDocument = template.createHtmlDocumentWithHtml(html);

    return template.initWithDocument(clonedDocument, _require)
    .then(function() {
        template.setBaseUrl(_document.location.href);
        // Instantiate it using the document given since we don't want to clone
        // the document markup
        templateObjects = template._createTemplateObjects(instances);
        part.initWithTemplateAndFragment(template);

        return template._instantiateObjects(templateObjects, rootElement)
        .then(function(objects) {
            part.objects = objects;
            template._invokeDelegates(part);

            return part;
        });
    });
}

exports.Template = Template;
exports.TemplateResources = TemplateResources;
exports.instantiateDocument = instantiateDocument;

}})
;
//*/
montageDefine("6364dae","core/document-resources",{dependencies:["montage","core/promise","core/mini-url"],factory:function(require,exports,module){var Montage = require("montage").Montage,
    Promise = require("core/promise").Promise,
    URL = require("core/mini-url");

var DocumentResources = Montage.specialize({
    _SCRIPT_TIMEOUT: {value: 5000},
    _document: {value: null},
    _resources: {value: null},
    _preloaded: {value: null},

    constructor: {
        value: function DocumentResources() {
            this.super();
        }
    },

    initWithDocument: {
        value: function(_document) {
            this.clear();
            this._document = _document;

            return this;
        }
    },

    clear: {
        value: function () {
            this._resources = Object.create(null);
            this._preloaded = Object.create(null);
        }
    },

    _addResource: {
        value: function(url) {
            this._resources[url] = true;
        }
    },

    hasResource: {
        value: function(url) {
            return url in this._resources;
        }
    },

    isResourcePreloaded: {
        value: function(url) {
            return this._preloaded[url] === true;
        }
    },

    isResourcePreloading: {
        value: function(url) {
            return Promise.isPromise(this._preloaded[url]);
        }
    },

    setResourcePreloadedPromise: {
        value: function(url, promise) {
            this._preloaded[url] = promise;
        }
    },

    setResourcePreloaded: {
        value: function(url) {
            this._preloaded[url] = true;
        }
    },

    getResourcePreloadedPromise: {
        value: function(url) {
            return this._preloaded[url];
        }
    },

    addScript: {
        value: function(script) {
            var url = this.normalizeUrl(script.src);

            if (url) {
                if (this.isResourcePreloaded(url)) {
                    return Promise.resolve();
                } else if (this.isResourcePreloading(url)) {
                    return this.getResourcePreloadedPromise(url);
                } else {
                    return this._importScript(script);
                }
            } else {
                return this._importScript(script);
            }
        }
    },

    // TODO: this should probably be in TemplateResources, need to come up with
    //       a better scheme for know what has been loaded in what document.
    //       This change would make addStyle sync and up to whoever is adding to
    //       listen for its proper loading.
    _importScript: {
        value: function(script) {
            var self = this,
                _document = this._document,
                documentHead = _document.head,
                scriptLoaded,
                deferred = Promise.defer(),
                loadingTimeout,
                url = script.src;

            if (url) {
                self._addResource(url);
                // We wait until all scripts are loaded, this is important
                // because templateDidLoad might need to access objects that are
                // defined in these scripts, the downsize is that it takes more
                // time for the template to be considered loaded.
                scriptLoaded = function(event) {
                    //if (event.type === "load") {
                        self.setResourcePreloaded(url);
                    //}
                    script.removeEventListener("load", scriptLoaded);
                    script.removeEventListener("error", scriptLoaded);

                    clearTimeout(loadingTimeout);
                    deferred.resolve();
                };
                script.addEventListener("load", scriptLoaded, false);
                script.addEventListener("error", scriptLoaded, false);

                // Setup the timeout to wait for the script until the resource
                // is considered loaded. The template doesn't fail loading just
                // because a single script didn't load.
                loadingTimeout = setTimeout(function() {
                    self.setResourcePreloaded(url);
                    deferred.resolve();
                }, this._SCRIPT_TIMEOUT);

                this.setResourcePreloadedPromise(url, deferred.promise);

            } else {

                deferred.resolve();

            }

            // This is one of the very few ocasions where we go around the draw
            // loop to modify the DOM. Since it doesn't affect the layout
            // (unless the script itself does) it shouldn't be a problem.
            documentHead.appendChild(
                _document.createComment("Inserted from FIXME")
            );
            documentHead.appendChild(script);

            return deferred.promise;
        }
    },

    addStyle: {
        value: function(element) {
            var self = this,
                url = element.getAttribute("href"),
                documentHead;

            url = this.normalizeUrl(url);

            if (url) {
                if (this.hasResource(url)) {
                    return;
                } else {
                    this._addResource(url);
                }
            }

            documentHead = this._document.head;

            documentHead.insertBefore(element, documentHead.firstChild);
        }
    },

    normalizeUrl: {
        value: function(url, baseUrl) {
            return URL.resolve(baseUrl || "http://", url);
        }
    },

    preloadResource: {
        value: function(url) {
            url = this.normalizeUrl(url);

            if (this.isResourcePreloaded(url)) {
                return Promise.resolve();
            } else if (this.isResourcePreloading(url)) {
                return this.getResourcePreloadedPromise(url);
            } else {
                return this._preloadResource(url);
            }
        }
    },

    _preloadResource: {
        value: function(url) {
            var self = this,
                req = new XMLHttpRequest(),
                loadHandler,
                loadingTimeout,
                deferred = Promise.defer();

            req.open("GET", url);

            loadHandler = function(event) {
                //if (event.type === "load") {
                    self.setResourcePreloaded(url);
                //}
                req.removeEventListener("load", loadHandler);
                req.removeEventListener("error", loadHandler);

                clearTimeout(loadingTimeout);
                deferred.resolve();
            };
            req.addEventListener("load", loadHandler, false);
            req.addEventListener("error", loadHandler, false);
            req.send();

            // Setup the timeout to wait for the script until the resource
            // is considered loaded.
            loadingTimeout = setTimeout(function() {
                self.setResourcePreloaded(url);
                deferred.resolve();
            }, this._SCRIPT_TIMEOUT);

            this.setResourcePreloadedPromise(url, deferred.promise);

            return deferred.promise;
        }
    }
}, {
    getInstanceForDocument: {
        value: function(_document) {
            var documentResources = _document.__montage_resources__;

            if (!documentResources) {
                documentResources = _document.__montage_resources__ = new DocumentResources().initWithDocument(_document);
            }

            return documentResources;
        }
    }
});

exports.DocumentResources = DocumentResources;

}})
;
//*/
montageDefine("6364dae","core/gate",{dependencies:["montage","core/logger"],factory:function(require,exports,module){ /* <copyright>
Copyright (c) 2012, Motorola Mobility LLC.
All Rights Reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

* Redistributions of source code must retain the above copyright notice,
  this list of conditions and the following disclaimer.

* Redistributions in binary form must reproduce the above copyright notice,
  this list of conditions and the following disclaimer in the documentation
  and/or other materials provided with the distribution.

* Neither the name of Motorola Mobility LLC nor the names of its
  contributors may be used to endorse or promote products derived from this
  software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
</copyright> */
/**
	@module montage/core/gate
    @requires montage/core/core
    @requires montage/core/logger
*/
var Montage = require("montage").Montage,
    logger = require("core/logger").logger("gate");
/**
 @class Gate
 @extends Montage
 */
var Gate = exports.Gate = Montage.specialize(/** @lends Gate# */ {

    constructor: {
        value: function Gate() {
            this.super();
        }
    },

/**
    @function
    @returns {Gate} A new Gate instance.
    */
    init: {
        enumerable: false,
        value: function() {
            this.reset();
            return this;
        }
    },
/**
    @function
    @param {String} delegate The delegate to be initialized.
    @returns itself
    */
    initWithDelegate: {
        enumerable: false,
        value: function(delegate) {
            this.reset();
            this.delegate = delegate;
            return this;
        }
    },
/**
    @function
    @param {String} propertyDescriptor The propertyDescriptor to be initialized.
    @returns itself
    */
    initWithDescriptor: {
        enumerable: false,
        value: function(propertyDescriptor) {
            var fieldName;
            this.reset();
            for (fieldName in propertyDescriptor) {
                this.setField(fieldName, propertyDescriptor[fieldName].value);
            }
            return this;
        }
    },
/**

        @type {Property}
        @default {Number} 0
    */
    count: {
        value: 0
    },
/**

        @type {Property}
        @default {String} null
    */
    table: {
        value: null
    },
/**
    @function
    @param {Array} aFieldName The aFieldName array.
    @returns !table or table[aFieldName]
    */
    getField: {
        enumerable: false,
        value: function(aFieldName) {
            var table = this.table;
            return !table || table[aFieldName];
        }
    },
/**
    @function
    @param {Array} aFieldName The aFieldName array.
    @param {Number} value The count on the array.
    */
    setField: {
        enumerable: false,
        value: function(aFieldName, value) {
            var table = this.table,
                fieldValue,
                oldCount = this.count;

            table = (!table ? this.table = {} : table);

            fieldValue = table[aFieldName];

            if (typeof fieldValue === "undefined" && !value) {
                // new field
                this.count++;
            } else if (typeof fieldValue !== "undefined" && fieldValue !== value) {
                if (value) {
                    this.count--;
                } else {
                    this.count++;
                }
            } else if (value) {
                logger.error(this, aFieldName + " was not set before.");
            }
            table[aFieldName] = !!value;
            if (this.count === 0 && oldCount > 0) {
                this.callDelegateMethod(true);
            } else if (oldCount === 0 && this.count > 0) {
                this.callDelegateMethod(false);
            }
        }
    },
/**
    @function
    @param {Array} aFieldName The aFieldName array to be removed.
    */
    removeField: {
        enumerable: false,
        value: function(aFieldName) {
            var table = this.table, fieldValue = table[aFieldName];
            if (typeof fieldValue !== "undefined" && !fieldValue) {
                // if the value was false decrement the count
                this.count--;
            }
            delete table[aFieldName];
        }
    },
/**

        @type {Property}
        @default {String} null
    */
    delegate: {
        enumerable: false,
        value: null
    },
/**
    @function
    @param {Number} value The value to be called.
    */
    callDelegateMethod: {
        value: function(value) {
            var delegateMethod;
            if (this.delegate && typeof (delegateMethod = this.delegate["gateDidBecome" + (value ? "True" : "False")]) === "function") {
                delegateMethod.call(this.delegate, this);
            }
        },
        enumerable: false
    },
/**
    @type {Function}
    @returns this.count === 0
    */
    value: {
        enumerable: false,
        get: function() {
            return this.count === 0;
        }
    },
/**
    @function
    */
    reset: {
        enumerable: false,
        value: function() {
            this.table = {};
            this.count = 0;
        }
    },
/**
    @function
    @returns {String} result
    */
    toString: {
        value: function() {
            var fieldNames = this._fields,
                i,
                iField,
                result = "";
            for (i = 0; (iField = fieldNames[i]); i++) {
                result += iField + "[" + (this._value & fieldNames[iField]) + "], ";
            }
            return result;
        }
    }
});

}})
;
//*/
montageDefine("6364dae","core/application",{dependencies:["core/core","core/target","window-loader/montage-window","core/dom","core/template"],factory:function(require,exports,module){/* <copyright>
Copyright (c) 2012, Motorola Mobility LLC.
All Rights Reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

* Redistributions of source code must retain the above copyright notice,
  this list of conditions and the following disclaimer.

* Redistributions in binary form must reproduce the above copyright notice,
  this list of conditions and the following disclaimer in the documentation
  and/or other materials provided with the distribution.

* Neither the name of Motorola Mobility LLC nor the names of its
  contributors may be used to endorse or promote products derived from this
  software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
</copyright> */

/**
    @module montage/core/application
    @requires montage/core/core
    @requires montage/core/event/event-manager
    @requires montage/core/template
    @requires montage/ui/component

    @requires montage/core/dom
*/

var Montage = require("core/core").Montage,
    Target = require("core/target").Target,
    MontageWindow = require("window-loader/montage-window").MontageWindow,
    Slot;

    require("core/dom");

/**
 This module defines the {Application} prototype.
 @module core/application
 @requires event/event-manager
 @requires template
 @requires ui/popup/popup
 @requires ui/popup/alert
 @requires ui/popup/confirm
 @requires ui/loading
 @requires ui/popup/growl
 @requires ui/slot
 */

/**
 * The application is a singleton, it initially loads and oversees the running program.
 * It is also responsible for window management.
 * The behavior of the application can be modified by implementing a delegate {Application#delegate}
 * It is also possible to subclass the application by specifying an ```applicationPrototype"``` in ```the package.json```
 * @class Application
 * @extends Target
 */
var Application = exports.Application = Target.specialize( /** @lends Application# */ {

    /**
     Provides a reference to the Montage event manager used in the application.
     @type {EventManager}
     */
    eventManager: {
        value: null
    },

    /**
     Provides a reference to the parent application.
     @type {Application}
     @default null
     */
    parentApplication: {
        value: null
    },

    /**
     Provides a reference to the main application.
     @type {Application}
     @default this
     */
    mainApplication: {
        get: function() {
            // JFD TODO: We should cache the result, would need to update it when the window is detached or attached
            var mainApplication = this;
            while (mainApplication.parentApplication) {
                mainApplication = mainApplication.parentApplication;
            }
            return mainApplication;
        }
    },

    // possible values: "z-order", "reverse-z-order", "z-order", "reverse-open-order"
    _windowsSortOrder: {
        value: "reverse-z-order"
    },

    /**
     Determines the sort order for the Application.windows array.
     Possible values are: z-order, reverse-z-order, open-order, reverse-open-order
     @type {String}
     @default "reverse-z-order"
     */
    windowsSortOrder: {
        get: function() {
            if (this.parentApplication == null) {
                return this._windowsSortOrder;
            } else {
                return this.mainApplication.windowsSortOrder;
            }
        },

        set: function(value) {
            if (this.parentApplication == null) {
                if (["z-order", "reverse-z-order", "z-order", "reverse-open-order"].indexOf(value) !== -1) {
                    this._windowsSortOrder = value;
                }
            } else {
                this.mainApplication.windowsSortOrder = value;
            }
        }
    },

    /**
     Provides a reference to all the windows opened by the main application or any of its descendents, including the main
     window itself The list is kept sorted, the sort order is determined by the Application.windowsSortOrder property
     @type {Array<MontageWindow>}
     */
    windows: {
        get: function() {
            var theWindow;

            if (this.parentApplication == null) {
                if (!this._windows) {
                    var theWindow = new MontageWindow();
                    theWindow.application = this;
                    theWindow.window = window;
                    this.window = theWindow;

                    this._windows = [this.window];
                    this._multipleWindow = true;
                }
                return this._windows;
            } else {
                return this.mainApplication.windows;
            }
        }
    },

    _window: {
        value: null
    },

    /**
     Provides a reference to the MontageWindow associated with the application.
     @type {MontageWindow}
     */
    window: {
        get: function() {
            if (!this._window && this == this.mainApplication) {
                var theWindow = new MontageWindow();
                theWindow.application = this;
                theWindow.window = window;
                this._window = theWindow;
            }
            return this._window;
        },

        set: function(value) {
            if (!this._window) {
                this._window = value;
            }
        }
    },

    /**
     An array of the child windows attached to the application.
     @type {Array<MontageWindow>}
     @default {Array} []
     */
    attachedWindows: {
        value: []
    },

    /**
     Returns the event manager for the specified window object.
     @function
     @param {Window} aWindow The browser window whose event manager object should be returned.
     @returns aWindow.defaultEventMananger
     */
    eventManagerForWindow: {
        value: function(aWindow) {
            return aWindow.defaultEventMananger;
        }
    },

    /**
     * Return the top most window of any of the Montage Windows.
     * @type {MontageWindow}
     * @default document.defaultView
     */
    focusWindow: {
        get: function() {
            var windows = this.windows,
                sortOrder = this.windowsSortOrder;

            if (sortOrder == "z-order") {
                return windows[0];
            } else if (sortOrder == "reverse-z-order") {
                return windows[windows.length - 1];
            } else {
                for (var i in windows) {
                    if (windows[i].focused) {
                        return windows[i];
                    }
                }
            }
        }
    },

    /**
     * The application's delegate object, it can implement a ```willFinishLoading``` method that will be called right
     * after the index.html is loaded
     * @type {Object}
     * @default null
     */
    delegate: {
        value: null
    },

    nextTarget: {
        get: function () {
            return this.delegate;
        }
    },

    /**
     * Opens a component in a new browser window, and registers the window with the Montage event manager.<br>
     * The component URL must be in the same domain as the calling script. Can be relative to the main application
     * @function
     * @param {String} component, the path to the reel component to open in the new window.
     * @param {String} name, the component main class name.
     * @param {Object} parameters, the new window parameters (accept same parameters than window.open).
     * @example
     * var app = document.application;
     * app.openWindow("docs/help.reel", "Help", "{width:300, height:500}");
     */
    openWindow: {
        value: function(component, name, parameters) {
            var thisRef = this,
                childWindow = new MontageWindow(),
                childApplication,
                event,
                windowParams = {
                    location: false,
//                  height: <pixels>,
//                  width: <pixels>,
//                  left: <pixels>,
//                  top: <pixels>,
                    menubar: false,
                    resizable: true,
                    scrollbars: true,
                    status: false,
                    titlebar: true,
                    toolbar: false
                };

            var loadInfo = {
                module: component,
                name: name,
                parent: window,
                callback: function(aWindow, aComponent) {
                    var sortOrder;

                    // Finishing the window object initialization and let the consumer knows the window is loaded and ready
                    childApplication = aWindow.document.application;
                    childWindow.window = aWindow;
                    childWindow.application = childApplication;
                    childWindow.component = aComponent;
                    childApplication.window = childWindow;

                    thisRef.attachedWindows.push(childWindow);

                    sortOrder = thisRef.mainApplication.windowsSortOrder;
                    if (sortOrder == "z-order" || sortOrder == "reverse-open-order") {
                        thisRef.windows.unshift(childWindow);
                    } else {
                        thisRef.windows.push(childWindow);
                    }

                    event = document.createEvent("CustomEvent");
                    event.initCustomEvent("load", true, true, null);
                    childWindow.dispatchEvent(event);
                }
            };

            // If this is the first time we open a window, let's install a focus listener and make sure the body element is focusable
            // Applicable only on the main application
            if (this === this.mainApplication && !this._multipleWindow) {
                var montageWindow = this.window;    // Will cause to create a Montage Window for the mainApplication and install the needed event handlers
            }

            if (typeof parameters == "object") {
                var param, value, separator = "", stringParamaters = "";

                // merge the windowParams with the parameters
                for (param in parameters) {
                    if (parameters.hasOwnProperty(param)) {
                        windowParams[param] = parameters[param];
                    }
                }
            }

            // now convert the windowParams into a string
            var excludedParams = ["name"];
            for (param in windowParams) {
                if (excludedParams.indexOf(param) == -1) {
                    value = windowParams[param];
                    if (typeof value == "boolean") {
                        value = value ? "yes" : "no";
                    } else {
                        value = String(value);
                        if (value.match(/[ ,"]/)) {
                            value = '"' + value.replace(/"/g, "\\\"") + '"';
                        }
                    }
                    stringParamaters += separator + param + "=" + value;
                    separator = ",";
                }
            }

            window.require.loadPackage({name: "montage"}).then(function(require) {
                var newWindow = window.open(require.location + "window-loader/index.html", "_blank", stringParamaters);
                newWindow.loadInfo = loadInfo;
            }).done();

            return childWindow;
        }
    },

    /**
     * Attach a window to a parent application. When a window open, it is automatically attached to the Application used to
     * create the window.
     * @function
     * @param {MontageWindow} window to detach.
     */
    attachWindow: {
        value: function(montageWindow) {
            var parentApplicaton = montageWindow.application.parentApplication,
                sortOrder;

            if (parentApplicaton !== this) {
                if (parentApplicaton) {
                    parentApplicaton.detachWindow(montageWindow);
                }

                montageWindow.parentApplication = this;
                this.attachedWindows.push(montageWindow);

                sortOrder = this.mainApplication.windowsSortOrder;
                if (sortOrder == "z-order" || sortOrder == "reverse-open-order") {
                    this.windows.unshift(montageWindow);
                } else {
                    this.windows.push(montageWindow);
                }
                montageWindow.focus()
            }
            return montageWindow;
        }
    },

    /**
     * Detach the window from its parent application. If no montageWindow is specified, the current application's windows
     * will be detached
     * @function
     * @param {MontageWindow} window to detach.
     */
    detachWindow: {
        value: function(montageWindow) {
            var index,
                parentApplicaton,
                windows = this.windows;

            if (montageWindow === undefined) {
                montageWindow = this.window;
            }
            parentApplicaton = montageWindow.application.parentApplication;

            if (parentApplicaton == this) {
                index = this.attachedWindows.indexOf(montageWindow);
                if (index !== -1) {
                    this.attachedWindows.splice(index, 1);
                }
                index = windows.indexOf(montageWindow);
                if (index !== -1) {
                    windows.splice(index, 1);
                }
                montageWindow.application.parentApplication = null;
            } else if (parentApplicaton) {
                parentApplicaton.detachWindow(montageWindow);
            }
            return montageWindow;
        }
    },

    /**
     @private
     */
    constructor: {
        value: function Application() {
            if (window.loadInfo && !this.parentApplication) {
                this.parentApplication = window.loadInfo.parent.document.application;
            }
        }
    },

    _load: {
        value: function(applicationRequire, callback) {
            var rootComponent,
                self = this;

            // assign to the exports so that it is available in the deserialization of the template
            exports.application = self;

            require.async("ui/component")
            .then(function(exports) {
                rootComponent = exports.__root__;
                rootComponent.element = document;

                return require("core/template").instantiateDocument(window.document, applicationRequire)
                .then(function(part) {
                    self.callDelegateMethod("willFinishLoading", self);
                    rootComponent.needsDraw = true;
                    if (callback) {
                        callback(self);
                    }
                });
            })
            .done();
        }
    },

    // @private
    _alertPopup: {value: null, enumerable: false},
    _confirmPopup: {value: null, enumerable: false},
    _notifyPopup: {value: null, enumerable: false},
    _zIndex: {value: null},

    _isSystemPopup: {value: function(type) {
        return (type === 'alert' || type === 'confirm' || type === 'notify');
    }},

    _createPopupSlot: {value: function(zIndex) {
        var slotEl = document.createElement('div');
        document.body.appendChild(slotEl);
        slotEl.style.zIndex = zIndex;
        slotEl.style.position = 'absolute';

        var popupSlot = new Slot();
        popupSlot.element = slotEl;
        popupSlot.attachToParentComponent();
        return popupSlot;
    }},

    getPopupSlot: {
        value: function(type, content, callback) {

            var self = this;
            require.async("ui/slot.reel/slot")
            .then(function(exports) {
                Slot = Slot || exports.Slot;
                type = type || "custom";
                var isSystemPopup = self._isSystemPopup(type), zIndex, slotEl, popupSlot;
                self.popupSlots = self.popupSlots || {};

                if(isSystemPopup) {
                    switch (type) {
                        case "alert":
                            zIndex = 9004;
                            break;
                        case "confirm":
                            zIndex = 9003;
                            break;
                        case "notify":
                            zIndex = 9002;
                            break;
                    }
                } else {
                    // custom popup
                    if(!self._zIndex) {
                        self._zIndex = 7000;
                    } else {
                        self._zIndex = self._zIndex + 1;
                    }
                    zIndex = self._zIndex;
                }

                popupSlot = self.popupSlots[type];
                if (!popupSlot) {
                    popupSlot = self.popupSlots[type] = self._createPopupSlot(zIndex);
                }
                // use the new zIndex for custom popup
                if(!isSystemPopup) {
                    popupSlot.element.style.zIndex = zIndex;
                }

                popupSlot.content = content;
                callback.call(this, popupSlot);

            })
            .done();
        }
    },

    returnPopupSlot: {value: function(type) {
        var self = this;
        if(self.popupSlots && self.popupSlots[type]) {
            var popupSlot = self.popupSlots[type];
            popupSlot.content = null;
            // is there a way to remove the Slot
            // OR should we remove the slotEl from the DOM to clean up ?
        }

    }},

    // private
    _getActivePopupSlots: {
        value: function() {
            var arr = [];
            if(this.popupSlots) {
                var keys = Object.keys(this.popupSlots);
                if(keys && keys.length > 0) {
                    var i=0, len = keys.length, slot;
                    for(i=0; i< len; i++) {
                        slot = this.popupSlots[keys[i]];
                        if(slot && slot.content !== null) {
                            arr.push(slot);
                        }

                    }
                }
            }
            return arr;
        }
    }

});

}})
;
//*/
montageDefine("6364dae","core/document-part",{dependencies:["montage","core/logger","core/promise","core/event/event-manager"],factory:function(require,exports,module){var Montage = require("montage").Montage,
    logger = require("core/logger").logger("document-part"),
    Promise = require("core/promise").Promise,
    defaultEventManager = require("core/event/event-manager").defaultEventManager;

var DocumentPart = Montage.specialize( {
    template: {value: null},
    fragment: {value: null},
    objects: {value: null},
    childComponents: {value: null},
    parameters: {value: null},

    constructor: {
        value: function DocumentPart() {
            this.super();
        }
    },

    initWithTemplateAndFragment: {
        value: function(template, fragment) {
            this.template = template;
            this.fragment = fragment;
            this.objects = null;
            this.childComponents = [];
            this.parameters = null;
        }
    },

    startActingAsTopComponent: {
        value: function() {
            if (this.fragment) {
                defaultEventManager.registerEventHandlerForElement(
                    this, this.fragment);
            }
        }
    },

    stopActingAsTopComponent: {
        value: function() {
            if (this.fragment) {
                defaultEventManager.unregisterEventHandlerForElement(
                    this.fragment);
            }
        }
    },

    addChildComponent: {
        value: function(childComponent) {
            if (this.childComponents.indexOf(childComponent) == -1) {
                this.childComponents.push(childComponent);
            }
        }
    },

    removeChildComponent: {
        value: function(childComponent) {
            var childComponents = this.childComponents,
                ix = childComponents.indexOf(childComponent);

            if (ix > -1) {
                childComponents.splice(ix, 1);
                childComponent._parentComponent = null;
                childComponent._alternateParentComponent = null;
            }
        }
    },

    _addToDrawList: {
        value: function() {}
    },

    _componentTreeLoadedDeferred: {value: null},
    loadComponentTree: {
        value: function() {
            var deferred = this._componentTreeLoadedDeferred,
                promises;

            if (!deferred) {
                deferred = Promise.defer();
                this._componentTreeLoadedDeferred = deferred;

                promises = [];

                this.childComponents.forEach(function(childComponent) {
                    promises.push(childComponent.loadComponentTree());
                });

                Promise.all(promises).then(function() {
                    deferred.resolve();
                }, deferred.reject).done();
            }

            return deferred.promise;
        }
    }
});

exports.DocumentPart = DocumentPart;

}})
;
//*/
montageDefine("6364dae","core/serialization/serialization",{dependencies:["core/core","./serializer/montage-labeler","./deserializer/montage-reviver","frb/parse","frb/stringify"],factory:function(require,exports,module){var Montage = require("core/core").Montage,
    MontageLabeler = require("./serializer/montage-labeler").MontageLabeler,
    MontageReviver = require("./deserializer/montage-reviver").MontageReviver,
    parse = require("frb/parse"),
    stringify = require("frb/stringify");

var Serialization = Montage.specialize( {
    _serializationString: {value: null},
    _serializationObject: {value: null},

    initWithString: {
        value: function(string) {
            this._serializationString = string;
            this._serializationObject = null;

            return this;
        }
    },

    initWithObject: {
        value: function(object) {
            this._serializationString = null;
            this._serializationObject = object;

            return this;
        }
    },

    getSerializationObject: {
        value: function() {
            if (!this._serializationObject) {
                this._serializationObject = JSON.parse(this._serializationString);
            }

            return this._serializationObject;
        }
    },

    getSerializationString: {
        value: function() {
            if (!this._serializationString) {
                this._serializationString = JSON.stringify(this._serializationObject);
            }

            return this._serializationString;
        }
    },

    getSerializationLabels: {
        value: function() {
            var serializationObject = this.getSerializationObject();

            return Object.keys(serializationObject);
        }
    },

    getExternalObjectLabels: {
        value: function() {
            var serializationObject = this.getSerializationObject(),
                labels = [];

            for (var label in serializationObject) {
                if (Object.keys(serializationObject[label]).length === 0) {
                    labels.push(label);
                }
            }

            return labels;
        }
    },

    isExternalObject: {
        value: function(label) {
            var serializationObject = this.getSerializationObject();

            if (serializationObject && label in serializationObject) {
                return Object.keys(serializationObject[label]).length === 0;
            } else {
                return false;
            }
        }
    },

    getSerializationLabelsWithElements: {
        value: function(elementIds) {
            var inspector = new SerializationInspector(),
                labels = [];

            inspector.initWithSerialization(this);
            inspector.visitSerialization(function(node) {
                // Check if this is one of the elements we're looking for
                if (node.type === "Element" && elementIds.indexOf(node.data) >= 0) {
                    // Check if it's inside a "properties" block
                    node = node.parent;
                    if (node && node.name === "properties") {
                        // Check if it's in a montage object
                        node = node.parent;
                        if (node && node.type === "montageObject") {
                            labels.push(node.label);
                        }
                    }
                }
            });

            return labels;
        }
    },

    renameElementReferences: {
        value: function(elementsTable) {
            var inspector = new SerializationInspector();

            inspector.initWithSerialization(this);
            inspector.visitSerialization(function(node) {
                if (node.type === "Element" && node.data in elementsTable) {
                    node.data = elementsTable[node.data];
                }
            });
        }
    },

    renameSerializationLabels: {
        value: function(labelsTable) {
            var inspector = new SerializationInspector();

            inspector.initWithSerialization(this);
            inspector.visitSerialization(function(node) {
                if (node.label) {
                    var label = node.label;

                    if (label in labelsTable) {
                        node.label = labelsTable[label];
                    }
                }
                if (node.type === "reference") {
                    var reference = node.data;

                    if (reference in labelsTable) {
                        node.data = labelsTable[reference];
                    }
                }
            });
        }
    },

    mergeSerialization: {
        value: function(serialization) {
            return SerializationMerger.mergeSerializations(this, serialization);
        }
    },

    extractSerialization: {
        value: function(labels, externalLabels) {
            var extractor = new SerializationExtractor();

            extractor.initWithSerialization(this);
            return extractor.extractSerialization(labels, externalLabels);
        }
    }
});

var SerializationMerger = Montage.specialize(null, {
    /**
     * Merges serialization2 into serialization1.
     *
     * @param {Serialization} serialization1 The serialization to be merged and
     *        end result of the merge operation.
     * @param {Serialization} serialization2 The serialization to be merged.
     * @return {Object} The collision table with the new labels generated for
     *         label clashes.
     */
    mergeSerializations: {
        value: function(serialization1, serialization2) {
            var serializationObject1,
                serializationObject2,
                serializationString2,
                labels1,
                labels2,
                collisionTable;

            labels1 = serialization1.getSerializationLabels();
            labels2 = serialization2.getSerializationLabels();

            // Check for name collisions and generate new labels
            collisionTable = this._createCollisionTable(labels1, labels2);

            // Replace the labels with the new, non-colliding, ones
            if (collisionTable) {
                // Clone serialization2 because we don't want to modify it.
                serializationString2 = serialization2.getSerializationString();
                serialization2 = new Serialization()
                    .initWithString(serializationString2);

                serialization2.renameSerializationLabels(collisionTable);
                labels2 = serialization2.getSerializationLabels();
            }

            // Merge the two serializations without the fear of name clashing
            serializationObject1 = serialization1.getSerializationObject();
            serializationObject2 = serialization2.getSerializationObject();

            for (var i = 0, label; (label = labels2[i]); i++) {
                serializationObject1[label] = serializationObject2[label];
            }

            serialization1.initWithObject(serializationObject1);

            return collisionTable;
        }
    },

    _createCollisionTable: {
        value: function(labels1, labels2) {
            var labeler = new MontageLabeler(),
                collisionTable = {},
                hasCollision = false;

            for (var i = 0; i < labels1.length; i++) {
                labeler.setObjectLabel(null, labels1[i]);
            }

            for (var i = 0, label; (label = labels2[i]); i++) {
                if (labels1.indexOf(label) >= 0) {
                    // All new labels will be "object<N>" because we give an
                    // object.
                    collisionTable[label] = labeler.generateObjectLabel({});
                    hasCollision = true;
                }
            }

            if (hasCollision) {
                return collisionTable;
            }
        }
    }
});

var SerializationInspector = Montage.specialize( {
    initWithSerialization: {
        value: function(serialization) {
            this._serialization = serialization;
        }
    },

    visitSerialization: {
        value: function(visitor) {
            var serialization = this._serialization.getSerializationObject();

            this._walkRootObjects(visitor, serialization);
            this._serialization.initWithObject(serialization);
        }
    },

    visitSerializationObject: {
        value: function(label, visitor) {
            var serialization = this._serialization.getSerializationObject();

            if (label in serialization) {
                this._walkRootObject(visitor, serialization, label);
                this._serialization.initWithObject(serialization);
            } else {
                throw new Error('Object "' + label + '" does not exist in ' + this._serialization.getSerializationString());
            }
        }
    },

    changeLabel: {
        value: function(oldLabel, newLabel) {
            var serialization = this._serialization.getSerializationObject(),
                object;

            object = serialization[oldLabel];
            delete serialization[oldLabel];
            serialization[newLabel] = object;
        }
    },

    _walkRootObjects: {
        value: function(visitor, objects) {
            var object,
                type;

            for (var label in objects) {
                this._walkRootObject(visitor, objects, label);
            }
        }
    },

    _walkRootObject: {
        value: function(visitor, objects, label) {
            var object = objects[label];

            if ("value" in object) {
                this._walkObject(visitor, object, "value", label);
            } else {
                this._walkCustomObject(visitor, objects, label);
            }
        }
    },

    /**
     * @param parentObject {Object} The parent object of the object to walk
     * @param key {String} The key of the object in the parent object
     * @param label {String} Optional label for when the object has no
     *                       parent
     * @param parent {Object} The representation of the object's parent
     */
    _walkObject: {
        value: function(visitor, parentObject, key, label, parent) {
            var object = parentObject[key],
                type = MontageReviver.getTypeOf(object),
                value,
                serialization,
                data;

            // Create the value representing this object in the serialization.
            value = {
                type: type
            };
            if (label) {
                value.label = label;
            } else {
                value.name = key;
            }
            if (parent) {
                value.parent = parent;
            }

            // Visit the value
            if (type === "number" || type === "string" || type === "null") {
                value.data = object;
                visitor(value);
                parentObject[key] = value.data;

            } else if (type === "regexp") {
                value.data = object["/"];
                visitor(value);
                object["/"] = value.data;

            } else if (type === "reference") {
                value.data = object["@"];
                visitor(value);
                object["@"] = value.data;

            } else if (type === "Element") {
                value.data = object["#"];
                visitor(value);
                object["#"] = value.data;

            } else if (type === "array") {
                value.data = object;
                visitor(value);
                parentObject[key] = object = value.data;

                for (var i = 0, ii = object.length; i < ii; i++) {
                    this._walkObject(visitor, object, ""+i, null, value);
                }

            } else if (type === "object") {
                value.data = object;
                visitor(value);
                parentObject[key] = object = value.data;

                for (var key in object) {
                    this._walkObject(visitor, object, key, null, value);
                }
            }

            // Update the label if it was changed.
            if (value.label != label) {
                this.changeLabel(label, value.label);
            }
        }
    },

    _walkCustomObject: {
        value: function(visitor, objects, label) {
            var object = objects[label],
                value;

            value = {
                type: "montageObject",
                label: label,
                data: object
            };

            visitor(value);
            objects[label] = object = value.data;
            if (value.label != label) {
                this.changeLabel(label, value.label);
            }

            if (object.properties) {
                this._walkObject(visitor, object, "properties", null, value);
            }
            if (object.listeners) {
                this._walkObject(visitor, object, "listeners", null, value);
            }
            if (object.bindings) {
                this._walkBindings(visitor, object, null, value);
            }
            if (object.localizations) {
                this._walkLocalizations(visitor, object, null, value);
            }
        }
    },

    _walkBindings: {
        value: function(visitor, parentObject, parent) {
            var object = parentObject.bindings,
                value;

            value = {
                type: "bindings",
                data: object,
                parent: parent
            };

            visitor(value);
            parentObject.bindings = object = value.data;

            for (var key in object) {
                this._walkBinding(visitor, object, key, value);
            }
        }
    },

    _walkBinding: {
        value: function(visitor, parentObject, key, parent) {
            var object = parentObject[key],
                value;

            value = {
                type: "binding",
                name: key,
                data: object,
                parent: parent
            };

            visitor(value);
            parentObject[key] = object = value.data;

            this._walkBindingData(visitor, object, value);
        }
    },

    _walkBindingData: {
        value: function(visitor, object, parent) {
            var sourcePath,
                parseTree,
                modified = false;

            sourcePath = object["<-"] || object["<->"];
            parseTree = parse(sourcePath);
            this._walksBindingReferences(parseTree, function(syntax) {
                var value = {
                    type: "reference",
                    data: syntax.label
                };
                visitor(value);
                if (syntax.label !== value.data) {
                    syntax.label = value.data;
                    modified = true;
                }
            });

            if (modified) {
                if ("<-" in object) {
                    object["<-"] = stringify(parseTree);
                } else {
                    object["<->"] = stringify(parseTree);
                }
            }

            if (object.converter) {
                this._walkObject(visitor, object, "converter", null, parent);
            }
        }
    },

    _walkLocalizations: {
        value: function(visitor, parentObject, parent) {
            var object = parentObject.localizations,
                value;

            value = {
                type: "localizations",
                data: object,
                parent: parent
            };

            visitor(value);
            parentObject.localizations = object = value.data;

            for (var key in object) {
                this._walkLocalization(visitor, object, key, value);
            }
        }
    },

    _walkLocalization: {
        value: function(visitor, parentObject, key, parent) {
            var object = parentObject[key],
                value,
                data;

            value = {
                type: "localization",
                name: key,
                data: object,
                parent: parent
            };

            visitor(value);
            parentObject[key] = object = value.data;

            if (typeof object.key === "object") {
                this._walkBindingData(visitor, object.key, value);
            }

            if (typeof object.default === "object") {
                this._walkBindingData(visitor, object.default, value);
            }

            if (typeof object.data === "object") {
                data = object.data;

                for (var key in data) {
                    this._walkBindingData(visitor, data[key], value);
                }
            }
        }
    },

    /**
     * Visits all object references made in the binding parsing tree
     */
    _walksBindingReferences: {
        value: function(parseTree, visitor) {
            var args = parseTree.args;

            if (parseTree.type === "component") {
                visitor(parseTree);
            }

            if (args) {
                for (var i = 0, ii = args.length; i < ii; i++) {
                    this._walksBindingReferences(args[i], visitor);
                }
            }
        }
    }
});

var SerializationExtractor = Montage.specialize( {
    _serialization: {value: null},

    initWithSerialization: {
        value: function(serialization) {
            this._serialization = serialization;
        }
    },

    /**
     * Creates a new serialization with the labels given.
     */
    extractSerialization: {
        value: function(labels, externalLabels) {
            var inspector = new SerializationInspector(),
                serializationObject,
                objects = {},
                references = [];

            serializationObject = this._serialization.getSerializationObject();
            inspector.initWithSerialization(this._serialization);

            for (var i = 0, label; (label = labels[i]); i++) {
                objects[label] = serializationObject[label];

                inspector.visitSerializationObject(label, function(node) {
                    var label;

                    if (node.type === "reference") {
                        label = node.data;
                        if (references.indexOf(label) === -1 &&
                            labels.indexOf(label) === -1) {
                            references.push(label);
                        }
                    }
                });
            }

            if (externalLabels) {
                for (var i = 0, label; (label = externalLabels[i]); i++) {
                    // Make sure we don't add objects that are not part of the
                    // serialization we're extracting from.
                    // If the same label is defined in both labels and
                    // externalLabels then labels takes precedence.
                    if (label in serializationObject && !(label in objects)) {
                        objects[label] = {};
                    }
                }
            }

            for (var i = 0, label; (label = references[i]); i++) {
                objects[label] = {};
            }

            return new Serialization().initWithObject(objects);
        }
    }
});

exports.Serialization = Serialization;
exports.SerializationMerger = SerializationMerger;
exports.SerializationInspector = SerializationInspector;
exports.SerializationExtractor = SerializationExtractor;

}})
;
//*/
montageDefine("6364dae","window-loader/montage-window",{dependencies:["montage"],factory:function(require,exports,module){/* <copyright>
Copyright (c) 2012, Motorola Mobility LLC.
All Rights Reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

* Redistributions of source code must retain the above copyright notice,
  this list of conditions and the following disclaimer.

* Redistributions in binary form must reproduce the above copyright notice,
  this list of conditions and the following disclaimer in the documentation
  and/or other materials provided with the distribution.

* Neither the name of Motorola Mobility LLC nor the names of its
  contributors may be used to endorse or promote products derived from this
  software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
</copyright> */
/**
	@module montage/ui/window
    @requires montage/core/core
    @requires montage/ui/reel
    @requires montage/core/gate
    @requires montage/core/logger | component
    @requires montage/core/logger | drawing
    @requires montage/core/event/event-manager
*/
var Montage = require("montage").Montage;

/**
 This module defines the {MontageWindow} prototype.
 @module ui/window
 @requires montage/core/core
 */

/**
 The Window object is responsible for managing a DOM window.
 @class MontageWindow
 @extends Montage
 */
var MontageWindow = exports.MontageWindow = Montage.specialize( /** @lends MontageWindow# */ {

    /**
     @private
     */
    _application: {
        value: null
    },

    /**
     Provides the Application associated with the window.
     @type {Application}
     */
    application: {
        get: function() { return this._application },
        set: function(value) {
            if (this._application === null) {
                this._application = value;
                if (this.focused) {
                    this._setFocusedWindow(this);
                }
            }
        }
    },

    /**
     @private
     */
    _window: {
        value: null
    },

    /**
     Provides a reference to the native window.
     @type {Window}
     */
    window: {
        get: function() { return this._window },
        set: function(value) {
            if (this._window === null) {
                var body = value.document.body;

                this._window = value;

                value.addEventListener("beforeunload", this, true);
                value.addEventListener("focus", this, true);
                value.addEventListener("mousedown", this, true);

                // In order to receive focus event, we need to make sure the body is focusable
                if (body.getAttribute("tabIndex") === null) {
                    body.setAttribute("tabIndex", -1);
                    body.focus();
                }
            }
        }
    },

    /**
     Provides a reference to the DOM document.
     @type {document}
     */
    document: {
        get: function() { return this._window.document }
    },

    /**
     @private
     */
    _component: {
        value: null
    },

    /**
     Provides a reference to the main Montage component loaded in the window.
     @type {component}
     */
    component: {
        get: function() {return this._component},
        set: function(value) {
            if (this._component === null) {
                this._component = value;
            }
        }
    },

    /**
     The window title. Make sure to use MontageWindow.title to access the window's title rather than directly accessing
     directly the title by the document, else you will not be able to use binding with the window's title.
     @type {string}
     */
    title: {
        get: function() { return this.document.title },
        set: function(value) {
            this.document.title = value;
        }
    },

    /**
     True if the window is currently the topmost Montage Window and has focus.
     @type {boolean}
     */
    focused: {
          value: false
    },

    /**
     Set the focus on the window, move it to the front.
     @function
     */
    focus: {
        value: function() {
            if (this._window) {
                this._window.focus();
            }
        }
    },

    /**
     @private
     */
    _setFocusedWindow: {
        value: function(aWindow) {
            var application = this.application,
                windows,
                theWindow,
                i;

            if (application._multipleWindow) {
                windows = application.windows;
                for (i in windows) {
                    theWindow = windows[i];
                    if (theWindow.window === aWindow) {
                        if (theWindow.focused !== true) {
                            theWindow.focused = true;
                            if (application.windowsSortOrder == "z-order") {
                                windows.splice(i, 1);
                                windows.unshift(theWindow);
                            } else if (application.windowsSortOrder == "reverse-z-order") {
                                windows.splice(i, 1);
                                windows.push(theWindow);
                            }
                        }
                    } else {
                        theWindow.focused = false;
                    }
                }
            } else {
                this.focused = true;
            }
        }
    },

    /**
     True is the window has been closed. Once a window has been close, the MontageWindow object still exist but you
     cannot use it anymore.
     @type {boolean}
     */
    closed: {
        get: function() { return this._window ? this._window.closed : false }
    },

    /**
     Close the window
     Note: Any child window will be closed too.
     @function
     */
    close: {
        value: function() {
            if (this._window) {
                this._window.close();
            }
        }
    },

    /**
     Resize the window to the specified width and height
     @function
     @param {Integer} width The window's width desired.
     @param {Integer} height The window's height desired.
     */
    resizeTo: {
        value: function(width, height) {
            if (this._window) {
                this._window.resizeTo(width, height);
            }
        }
    },

    /**
     Move the window to the specified screen coordinate x and y
     @function
     @param {Integer} x The window's x screen position.
     @param {Integer} y The window's y screen position.
     */
    moveTo: {
        value: function(x, y) {
            if (this._window) {
                this._window.moveTo(x, y);
            }
        }
    },

    /**
     @private
     */
    captureFocus: {
        value: function(event) {
            var application = this.application;

            if (!this.application) {
                // the Application has not yet been set, just mark the windows has being focused
                this.focused = true;
                return;
            }
            if (application.parentApplication) {
                application.mainApplication.window.captureFocus(event);
            } else {
                var target = (event.target.ownerDocument ? event.target.ownerDocument.defaultView : null) ||
                                event.target.defaultView || event.target;

                this._setFocusedWindow(target);
            }
        }
    },

    /**
     @private
     */
    captureMousedown: {
        value: function(event) {
            // Sometime, for some reason, we do not receive anymore a focus event... let presume that if we get a mouse click, we should have focus
            this.captureFocus(event);
        }
    },

    /**
     @private
     */
    captureBeforeunload: {
        value: function(event) {

            var application = this.application,
                windows = application.windows,
                attachedWindows = application.attachedWindows,
                i;

            // Close the attached windows
            application.attachedWindows = []; // this is necessary to prevent the unload of the child window to mess with the array while we iterate it
            for (var i in attachedWindows) {
                attachedWindows[i].close();
            }

            if (application.parentApplication) {
                attachedWindows = application.parentApplication.attachedWindows;   // We need the parent windows list
                i = attachedWindows.indexOf(this);
                if (i !== -1) {
                    attachedWindows.splice(i, 1);
                }

                i = windows.indexOf(this);
                if (i !== -1) {
                    windows.splice(i, 1);
                }

                this.window.removeEventListener("beforeunload", this, true);
                this.close();    // Force close the window in case the user is reloading it.
            }
        }
    }
});

}})
;
//*/
montageDefine("6364dae","core/dom",{dependencies:["core/geometry/point"],factory:function(require,exports,module){/* <copyright>
Copyright (c) 2012, Motorola Mobility LLC.
All Rights Reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

* Redistributions of source code must retain the above copyright notice,
  this list of conditions and the following disclaimer.

* Redistributions in binary form must reproduce the above copyright notice,
  this list of conditions and the following disclaimer in the documentation
  and/or other materials provided with the distribution.

* Neither the name of Motorola Mobility LLC nor the names of its
  contributors may be used to endorse or promote products derived from this
  software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
</copyright> */
/*global Node,Element,WebKitPoint,webkitConvertPointFromNodeToPage,webkitConvertPointFromPageToNode */
/**
    Provides DOM
    @module montage/core/dom
    @requires montage/core/geometry/point
*/

var Point = require("core/geometry/point").Point,
    NodePrototype = Node.prototype,
    ElementPrototype = Element.prototype;

/**
    @function external:Element#set
    @param {string} string
    @param {string} string
    @param {number} string
*/
Object.defineProperty(ElementPrototype, "set", {
    value: function(aPropertyPath, value, currentIndex) {
        var dotIndex = aPropertyPath.indexOf(".", currentIndex),
            result,
            currentKeyComponent,
            indexEnd,
            styleKey,
            classKey;

        currentIndex = currentIndex || 0;

        currentKeyComponent = aPropertyPath.substring(currentIndex, (dotIndex === -1 ? aPropertyPath.length : dotIndex));

        if (dotIndex === -1) {
            //This is only using properties.
            this.setAttribute(currentKeyComponent, value);
        }
        else {
            indexEnd = aPropertyPath.lastIndexOf(".");
            if (currentKeyComponent === "style") {
                styleKey = aPropertyPath.substring(dotIndex + 1, aPropertyPath.length);
                this.style[styleKey] = value;
            } else if (currentKeyComponent === "classList") {
                classKey = aPropertyPath.substring(dotIndex + 1, aPropertyPath.length);
                if (value) {
                    this.classList.add(classKey);
                } else {
                    this.classList.remove(classKey);
                }
            }

            else if ((result = this.get(aPropertyPath.substring(0, indexEnd)))) {
                (result[aPropertyPath.substring(indexEnd + 1, aPropertyPath.length)] = value);
            }
        }

    },
    enumerable: false
});

NodePrototype.get = function(key) {
    return this.getAttribute(key) || this[key];
};

Object.getPrototypeOf(document).addRule = function(selectorText, definition) {
    var styleSheet, cssRule;
    if ((styleSheet = document.styleSheets[0]) == null) {
        var style = document.createElement("style");
        style.type = "text/css";
        document.head.appendChild(style);
        styleSheet = document.styleSheets[0];
    } else {
        cssRule = document.getRule(selectorText, styleSheet);
    }
    if (!cssRule) {
        styleSheet.insertRule(selectorText + " " + definition, styleSheet.cssRules.length);
    }
};

Object.getPrototypeOf(document).getRule = function(ruleKey, styleSheet) {
    var cssRule;
    if (styleSheet.cssRules) {
        for (var j = 0; (cssRule = styleSheet.cssRules[j]); j++) {
            if (cssRule.name && cssRule.name === ruleKey) {
                // keyframe animation
                return cssRule;
            } else if (cssRule.selectorText === ruleKey) {
                return cssRule;
            }
        }
    }
};

/*
 * classList.js
 *
 * Implements a cross-browser element.classList getter.
 * By Eli Grey, http://eligrey.com
 */

"use strict";

if (typeof Element !== "undefined") {

    (function () {

        var classListProp = "classList";

        if (!Element.prototype.hasOwnProperty(classListProp)) {
            var trim = /^\s+|\s+$/g,
                setClasses = function (elem, classes) {
                    elem.setAttribute("class", classes.join(" "));
                },
                checkAndGetIndex = function (classes, token) {
                    if (token === "") {
                        throw "SYNTAX_ERR";
                    }
                    if (/\s/.test(token)) {
                        throw "INVALID_CHARACTER_ERR";
                    }

                    return classes.indexOf(token);
                },
                classListGetter = function () {
                    var elem = this,
                        classes = elem.getAttribute("class") || "";
                        classes = classes.replace(trim, "").split(/\s+/);
                    return {
                        length: classes.length,
                        item: function (i) {
                            return classes[i] || null;
                        },
                        contains: function (token) {
                            return checkAndGetIndex(classes, token) !== -1;
                        },
                        add: function (token) {
                            if (checkAndGetIndex(classes, token) === -1) {
                                classes.push(token);
                                this.length = classes.length;
                                setClasses(elem, classes);
                            }
                        },
                        remove: function (token) {
                            var index = checkAndGetIndex(classes, token);
                            if (index !== -1) {
                                classes.splice(index, 1);
                                this.length = classes.length;
                                setClasses(elem, classes);
                            }
                        },
                        toggle: function (token) {
                            if (checkAndGetIndex(classes, token) === -1) {
                                this.add(token);
                            } else {
                                this.remove(token);
                            }
                        },
                        toString: function () {
                            return (elem.getAttribute("class") || "");
                        }
                    };
                };

            if (Object.defineProperty) {
                Object.defineProperty(Element.prototype, classListProp, { get: classListGetter, enumerable: true });
            } else if (Object.prototype.__defineGetter__) {
                Element.prototype.__defineGetter__(classListProp, classListGetter);
            }
        }

    }());

}

NodePrototype.parentOf = function(child) {
    while ((child = child.parentNode) && child !== this) {};
    //If child is defined then we didn't walk all the way up to the root
    return child ? true : false;

};

var _offsetForElement = function(element) {
    var boundingClientRect,
        elementsDocument = element.ownerDocument,
        elementsDocumentElement,
        elementsBody,
        elementsWindow;

    if ( element && elementsDocument ) {
        elementsDocumentElement = elementsDocument.documentElement;
        elementsBody = elementsDocument.body;
        elementsWindow = elementsDocument.defaultView;

        if ( element !== elementsBody ) {
            boundingClientRect = element.getBoundingClientRect();
            if ( elementsDocumentElement.parentOf(element) ) {
                var clientTop  = elementsDocumentElement.clientTop  || elementsBody.clientTop  || 0,
                    clientLeft = elementsDocumentElement.clientLeft || elementsBody.clientLeft || 0,
                    scrollTop  = elementsWindow.pageYOffset || elementsDocumentElement.scrollTop  || elementsBody.scrollTop,
                    scrollLeft = elementsWindow.pageXOffset || elementsDocumentElement.scrollLeft || elementsBody.scrollLeft,
                    top  = boundingClientRect.top  + scrollTop  - clientTop,
                    left = boundingClientRect.left + scrollLeft - clientLeft;
                return { top: top, left: left };
            } else {
                return { top: boundingClientRect.top, left: boundingClientRect.left };
            }

        } else {
            return { top: elementsBody.offsetTop, left: elementsBody.offsetLeft };
        }
   } else {
        return null;
    }
};

var _webKitPoint = null;
try {
    _webKitPoint = new WebKitPoint(0,0);
} catch (e) {}

var webkitImplementation = function() {
    exports.convertPointFromNodeToPage = function(element, point) {
        if(point) {
            _webKitPoint.x = point.x;
            _webKitPoint.y = point.y;
        } else {
            _webKitPoint.x = 0;
            _webKitPoint.y = 0;
        }
        point = webkitConvertPointFromNodeToPage(element, _webKitPoint);
        return point ? new Point().init(point.x, point.y) : null;
    };

    exports.convertPointFromPageToNode = function(element, point) {
        if(point) {
            _webKitPoint.x = point.x;
            _webKitPoint.y = point.y;
        } else {
            _webKitPoint.x = 0;
            _webKitPoint.y = 0;
        }
        point = webkitConvertPointFromPageToNode(element, _webKitPoint);
        return point ? new Point().init(point.x, point.y) : null;
    };
};
var shimImplementation = function() {
    exports.convertPointFromNodeToPage = function(element, point) {
        if (!element || typeof element.x !== "undefined") {
            return null;
        }
        var offset;
        if (offset =_offsetForElement(element)) {
            return new Point().init((point ? point.x:0)+offset.left, (point ? point.y:0)+offset.top);
        } else {
            return new Point().init((point ? point.x:0), (point ? point.y:0));
        }
    };

    exports.convertPointFromPageToNode = function(element, point) {
        if (!element || typeof element.x !== "undefined") {
            return null;
        }
        var offset;
        if (offset =_offsetForElement(element)) {
            return new Point().init((point ? point.x:0)-offset.left, (point ? point.y:0)-offset.top);
        } else {
            return new Point().init((point ? point.x:0), (point ? point.y:0));
        }
    };
};

if (_webKitPoint) {
    webkitImplementation();
} else {
    shimImplementation();
}

}})
;
//*/
montageDefine("6364dae","core/geometry/point",{dependencies:["montage"],factory:function(require,exports,module){/* <copyright>
Copyright (c) 2012, Motorola Mobility LLC.
All Rights Reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

* Redistributions of source code must retain the above copyright notice,
  this list of conditions and the following disclaimer.

* Redistributions in binary form must reproduce the above copyright notice,
  this list of conditions and the following disclaimer in the documentation
  and/or other materials provided with the distribution.

* Neither the name of Motorola Mobility LLC nor the names of its
  contributors may be used to endorse or promote products derived from this
  software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
</copyright> */
/**
	@module montage/core/geometry/point
    @requires montage/core/core
*/
var Montage = require("montage").Montage;
/**
 @class Point
 @extends Montage
 */

exports.Point = Montage.specialize( /** @lends Point# */ {
    init: {
        enumerable: false,
        value: function(x, y) {
            this.x = x === null ? 0 : x;
            this.y = y === null ? 0 : y;
            return this;
        }
    },
/**
        The x axis point.
        @type {Number}
        @default  0
    */
    x: {
        enumerable: true,
        value: 0
    },
/**
        The y axis point.
        @type {Number}
        @default  0
    */
    y: {
        enumerable: true,
        value: 0
    }

}, {

    /**
     * Interpolates between two points.
     * @function
     * @param {Axis} percent The interpolation percentage.
     * @param {Axis} point0 The 0 interpolation point.
     * @param {Axis} point1 The 1 interpolation point.
     * @param {Axis} precision The interpolation precision.
     * @returns new Point().init(xValue, yValue)
     */
    interpolate: {
        enumerable: false,
        value: function(percent, point0, point1, precision) {
            var xValue,
                yValue;
            xValue = point0.x + (point1.x - point0.x) * percent;
            yValue = point0.y + (point1.y - point0.y) * percent;
            if (precision > 0) {
                xValue = Math.round(xValue * precision) / precision;
                yValue = Math.round(yValue * precision) / precision;
            }
            return new exports.Point().init(xValue, yValue);
        }
    }

});

}})
;
//*/
montageDefine("5ea2535","shim",{dependencies:["./shim-array","./shim-object","./shim-function","./shim-regexp"],factory:function(require,exports,module){
var Array = require("./shim-array");
var Object = require("./shim-object");
var Function = require("./shim-function");
var RegExp = require("./shim-regexp");


}})
;
//*/
montageDefine("5ea2535","listen/property-changes",{dependencies:["../shim","weak-map"],factory:function(require,exports,module){/*
    Based in part on observable arrays from Motorola Mobility’s Montage
    Copyright (c) 2012, Motorola Mobility LLC. All Rights Reserved.
    3-Clause BSD License
    https://github.com/motorola-mobility/montage/blob/master/LICENSE.md
*/

/*
    This module is responsible for observing changes to owned properties of
    objects and changes to the content of arrays caused by method calls.
    The interface for observing array content changes establishes the methods
    necessary for any collection with observable content.
*/

require("../shim");
var WeakMap = require("weak-map");

var object_owns = Object.prototype.hasOwnProperty;

/*
    Object property descriptors carry information necessary for adding,
    removing, dispatching, and shorting events to listeners for property changes
    for a particular key on a particular object.  These descriptors are used
    here for shallow property changes.

    {
        willChangeListeners:Array(Function)
        changeListeners:Array(Function)
    }
*/
var propertyChangeDescriptors = new WeakMap();

// Maybe remove entries from this table if the corresponding object no longer
// has any property change listeners for any key.  However, the cost of
// book-keeping is probably not warranted since it would be rare for an
// observed object to no longer be observed unless it was about to be disposed
// of or reused as an observable.  The only benefit would be in avoiding bulk
// calls to dispatchOwnPropertyChange events on objects that have no listeners.

/*
    To observe shallow property changes for a particular key of a particular
    object, we install a property descriptor on the object that overrides the previous
    descriptor.  The overridden descriptors are stored in this weak map.  The
    weak map associates an object with another object that maps property names
    to property descriptors.

    overriddenObjectDescriptors.get(object)[key]

    We retain the old descriptor for various purposes.  For one, if the property
    is no longer being observed by anyone, we revert the property descriptor to
    the original.  For "value" descriptors, we store the actual value of the
    descriptor on the overridden descriptor, so when the property is reverted, it
    retains the most recently set value.  For "get" and "set" descriptors,
    we observe then forward "get" and "set" operations to the original descriptor.
*/
var overriddenObjectDescriptors = new WeakMap();

module.exports = PropertyChanges;

function PropertyChanges() {
    throw new Error("This is an abstract interface. Mix it. Don't construct it");
}

PropertyChanges.debug = true;

PropertyChanges.prototype.getOwnPropertyChangeDescriptor = function (key) {
    if (!propertyChangeDescriptors.has(this)) {
        propertyChangeDescriptors.set(this, {});
    }
    var objectPropertyChangeDescriptors = propertyChangeDescriptors.get(this);
    if (!object_owns.call(objectPropertyChangeDescriptors, key)) {
        objectPropertyChangeDescriptors[key] = {
            willChangeListeners: [],
            changeListeners: []
        };
    }
    return objectPropertyChangeDescriptors[key];
};

PropertyChanges.prototype.hasOwnPropertyChangeDescriptor = function (key) {
    if (!propertyChangeDescriptors.has(this)) {
        return false;
    }
    if (!key) {
        return true;
    }
    var objectPropertyChangeDescriptors = propertyChangeDescriptors.get(this);
    if (!object_owns.call(objectPropertyChangeDescriptors, key)) {
        return false;
    }
    return true;
};

PropertyChanges.prototype.addOwnPropertyChangeListener = function (key, listener, beforeChange) {
    if (this.makeObservable && !this.isObservable) {
        this.makeObservable(); // particularly for observable arrays, for
        // their length property
    }
    var descriptor = PropertyChanges.getOwnPropertyChangeDescriptor(this, key);
    var listeners;
    if (beforeChange) {
        listeners = descriptor.willChangeListeners;
    } else {
        listeners = descriptor.changeListeners;
    }
    PropertyChanges.makePropertyObservable(this, key);
    listeners.push(listener);

    var self = this;
    return function cancelOwnPropertyChangeListener() {
        PropertyChanges.removeOwnPropertyChangeListener(self, key, listeners, beforeChange);
        self = null;
    };
};

PropertyChanges.prototype.addBeforeOwnPropertyChangeListener = function (key, listener) {
    return PropertyChanges.addOwnPropertyChangeListener(this, key, listener, true);
};

PropertyChanges.prototype.removeOwnPropertyChangeListener = function (key, listener, beforeChange) {
    var descriptor = PropertyChanges.getOwnPropertyChangeDescriptor(this, key);

    var listeners;
    if (beforeChange) {
        listeners = descriptor.willChangeListeners;
    } else {
        listeners = descriptor.changeListeners;
    }

    var index = listeners.lastIndexOf(listener);
    if (index === -1) {
        throw new Error("Can't remove listener: does not exist.");
    }
    listeners.splice(index, 1);

    if (descriptor.changeListeners.length + descriptor.willChangeListeners.length === 0) {
        PropertyChanges.makePropertyUnobservable(this, key);
    }
};

PropertyChanges.prototype.removeBeforeOwnPropertyChangeListener = function (key, listener) {
    return PropertyChanges.removeOwnPropertyChangeListener(this, key, listener, true);
};

PropertyChanges.prototype.dispatchOwnPropertyChange = function (key, value, beforeChange) {
    var descriptor = PropertyChanges.getOwnPropertyChangeDescriptor(this, key);

    if (descriptor.isActive) {
        return;
    }
    descriptor.isActive = true;

    var listeners;
    if (beforeChange) {
        listeners = descriptor.willChangeListeners;
    } else {
        listeners = descriptor.changeListeners;
    }

    var changeName = (beforeChange ? "Will" : "") + "Change";
    var genericHandlerName = "handleProperty" + changeName;
    var propertyName = String(key);
    propertyName = propertyName && propertyName[0].toUpperCase() + propertyName.slice(1);
    var specificHandlerName = "handle" + propertyName + changeName;

    try {
        // dispatch to each listener
        listeners.forEach(function (listener) {
            var thisp = listener;
            listener = (
                listener[specificHandlerName] ||
                listener[genericHandlerName] ||
                listener
            );
            listener.call(thisp, value, key, this);
        }, this);
    } finally {
        descriptor.isActive = false;
    }
};

PropertyChanges.prototype.dispatchBeforeOwnPropertyChange = function (key, listener) {
    return PropertyChanges.dispatchOwnPropertyChange(this, key, listener, true);
};

PropertyChanges.prototype.makePropertyObservable = function (key) {
    // arrays are special.  we do not support direct setting of properties
    // on an array.  instead, call .set(index, value).  this is observable.
    // 'length' property is observable for all mutating methods because
    // our overrides explicitly dispatch that change.
    if (Array.isArray(this)) {
        return;
    }

    if (!Object.isExtensible(this, key)) {
        throw new Error("Can't make property " + JSON.stringify(key) + " observable on " + this + " because object is not extensible");
    }

    var state;
    if (typeof this.__state__ === "object") {
        state = this.__state__;
    } else {
        state = {};
        if (Object.isExtensible(this, "__state__")) {
            Object.defineProperty(this, "__state__", {
                value: state,
                writable: true,
                enumerable: false
            });
        }
    }
    state[key] = this[key];

    // memoize overridden property descriptor table
    if (!overriddenObjectDescriptors.has(this)) {
        overriddenPropertyDescriptors = {};
        overriddenObjectDescriptors.set(this, overriddenPropertyDescriptors);
    }
    var overriddenPropertyDescriptors = overriddenObjectDescriptors.get(this);

    if (object_owns.call(overriddenPropertyDescriptors, key)) {
        // if we have already recorded an overridden property descriptor,
        // we have already installed the observer, so short-here
        return;
    }

    // walk up the prototype chain to find a property descriptor for
    // the property name
    var overriddenDescriptor;
    var attached = this;
    var formerDescriptor = Object.getOwnPropertyDescriptor(attached, key);
    do {
        overriddenDescriptor = Object.getOwnPropertyDescriptor(attached, key);
        if (overriddenDescriptor) {
            break;
        }
        attached = Object.getPrototypeOf(attached);
    } while (attached);
    // or default to an undefined value
    overriddenDescriptor = overriddenDescriptor || {
        value: undefined,
        enumerable: true,
        writable: true,
        configurable: true
    };

    if (!overriddenDescriptor.configurable) {
        throw new Error("Can't observe non-configurable properties");
    }

    // memoize the descriptor so we know not to install another layer,
    // and so we can reuse the overridden descriptor when uninstalling
    overriddenPropertyDescriptors[key] = overriddenDescriptor;

    // give up *after* storing the overridden property descriptor so it
    // can be restored by uninstall.  Unwritable properties are
    // silently not overriden.  Since success is indistinguishable from
    // failure, we let it pass but don't waste time on intercepting
    // get/set.
    if (!overriddenDescriptor.writable && !overriddenDescriptor.set) {
        return;
    }

    // TODO reflect current value on a displayed property

    var propertyListener;
    // in both of these new descriptor variants, we reuse the overridden
    // descriptor to either store the current value or apply getters
    // and setters.  this is handy since we can reuse the overridden
    // descriptor if we uninstall the observer.  We even preserve the
    // assignment semantics, where we get the value from up the
    // prototype chain, and set as an owned property.
    if ('value' in overriddenDescriptor) {
        propertyListener = {
            get: function () {
                return overriddenDescriptor.value
            },
            set: function (value) {
                if (value === overriddenDescriptor.value) {
                    return value;
                }
                PropertyChanges.dispatchBeforeOwnPropertyChange(this, key, overriddenDescriptor.value);
                overriddenDescriptor.value = value;
                state[key] = value;
                PropertyChanges.dispatchOwnPropertyChange(this, key, value);
                return value;
            },
            enumerable: overriddenDescriptor.enumerable,
            configurable: true
        };
    } else { // 'get' or 'set', but not necessarily both
        propertyListener = {
            get: function () {
                if (overriddenDescriptor.get) {
                    return overriddenDescriptor.get.apply(this, arguments);
                }
            },
            set: function (value) {
                var formerValue;

                // get the actual former value if possible
                if (overriddenDescriptor.get) {
                    formerValue = overriddenDescriptor.get.apply(this, arguments);
                }
                // call through to actual setter
                if (overriddenDescriptor.set) {
                    overriddenDescriptor.set.apply(this, arguments)
                }
                // use getter, if possible, to discover whether the set
                // was successful
                if (overriddenDescriptor.get) {
                    value = overriddenDescriptor.get.apply(this, arguments);
                    state[key] = value;
                }
                // if it has not changed, suppress a notification
                if (value === formerValue) {
                    return value;
                }
                PropertyChanges.dispatchBeforeOwnPropertyChange(this, key, formerValue);

                // dispatch the new value: the given value if there is
                // no getter, or the actual value if there is one
                PropertyChanges.dispatchOwnPropertyChange(this, key, value);
                return value;
            },
            enumerable: overriddenDescriptor.enumerable,
            configurable: true
        };
    }

    Object.defineProperty(this, key, propertyListener);
};

PropertyChanges.prototype.makePropertyUnobservable = function (key) {
    // arrays are special.  we do not support direct setting of properties
    // on an array.  instead, call .set(index, value).  this is observable.
    // 'length' property is observable for all mutating methods because
    // our overrides explicitly dispatch that change.
    if (Array.isArray(this)) {
        return;
    }

    if (!overriddenObjectDescriptors.has(this)) {
        throw new Error("Can't uninstall observer on property");
    }
    var overriddenPropertyDescriptors = overriddenObjectDescriptors.get(this);

    if (!overriddenPropertyDescriptors[key]) {
        throw new Error("Can't uninstall observer on property");
    }

    var overriddenDescriptor = overriddenPropertyDescriptors[key];
    delete overriddenPropertyDescriptors[key];

    var state;
    if (typeof this.__state__ === "object") {
        state = this.__state__;
    } else {
        state = {};
        if (Object.isExtensible(this, "__state__")) {
            Object.defineProperty(this, "__state__", {
                value: state,
                writable: true,
                enumerable: false
            });
        }
    }
    delete state[key];

    Object.defineProperty(this, key, overriddenDescriptor);
};

// constructor functions

PropertyChanges.getOwnPropertyChangeDescriptor = function (object, key) {
    if (object.getOwnPropertyChangeDescriptor) {
        return object.getOwnPropertyChangeDescriptor(key);
    } else {
        return PropertyChanges.prototype.getOwnPropertyChangeDescriptor.call(object, key);
    }
};

PropertyChanges.hasOwnPropertyChangeDescriptor = function (object, key) {
    if (object.hasOwnPropertyChangeDescriptor) {
        return object.hasOwnPropertyChangeDescriptor(key);
    } else {
        return PropertyChanges.prototype.hasOwnPropertyChangeDescriptor.call(object, key);
    }
};

PropertyChanges.addOwnPropertyChangeListener = function (object, key, listener, beforeChange) {
    if (!Object.isObject(object)) {
    } else if (object.addOwnPropertyChangeListener) {
        return object.addOwnPropertyChangeListener(key, listener, beforeChange);
    } else {
        return PropertyChanges.prototype.addOwnPropertyChangeListener.call(object, key, listener, beforeChange);
    }
};

PropertyChanges.removeOwnPropertyChangeListener = function (object, key, listener, beforeChange) {
    if (!Object.isObject(object)) {
    } else if (object.removeOwnPropertyChangeListener) {
        return object.removeOwnPropertyChangeListener(key, listener, beforeChange);
    } else {
        return PropertyChanges.prototype.removeOwnPropertyChangeListener.call(object, key, listener, beforeChange);
    }
};

PropertyChanges.dispatchOwnPropertyChange = function (object, key, value, beforeChange) {
    if (!Object.isObject(object)) {
    } else if (object.dispatchOwnPropertyChange) {
        return object.dispatchOwnPropertyChange(key, value, beforeChange);
    } else {
        return PropertyChanges.prototype.dispatchOwnPropertyChange.call(object, key, value, beforeChange);
    }
};

PropertyChanges.addBeforeOwnPropertyChangeListener = function (object, key, listener) {
    return PropertyChanges.addOwnPropertyChangeListener(object, key, listener, true);
};

PropertyChanges.removeBeforeOwnPropertyChangeListener = function (object, key, listener) {
    return PropertyChanges.removeOwnPropertyChangeListener(object, key, listener, true);
};

PropertyChanges.dispatchBeforeOwnPropertyChange = function (object, key, value) {
    return PropertyChanges.dispatchOwnPropertyChange(object, key, value, true);
};

PropertyChanges.makePropertyObservable = function (object, key) {
    if (object.makePropertyObservable) {
        return object.makePropertyObservable(key);
    } else {
        return PropertyChanges.prototype.makePropertyObservable.call(object, key);
    }
};

PropertyChanges.makePropertyUnobservable = function (object, key) {
    if (object.makePropertyUnobservable) {
        return object.makePropertyUnobservable(key);
    } else {
        return PropertyChanges.prototype.makePropertyUnobservable.call(object, key);
    }
};


}})
;
//*/
montageDefine("5ea2535","map",{dependencies:["./shim","./set","./generic-collection","./generic-map","./listen/property-changes"],factory:function(require,exports,module){"use strict";

var Shim = require("./shim");
var Set = require("./set");
var GenericCollection = require("./generic-collection");
var GenericMap = require("./generic-map");
var PropertyChanges = require("./listen/property-changes");

module.exports = Map;

function Map(values, equals, hash, getDefault) {
    if (!(this instanceof Map)) {
        return new Map(values, equals, hash, getDefault);
    }
    equals = equals || Object.equals;
    hash = hash || Object.hash;
    getDefault = getDefault || Function.noop;
    this.contentEquals = equals;
    this.contentHash = hash;
    this.getDefault = getDefault;
    this.store = new Set(
        undefined,
        function keysEqual(a, b) {
            return equals(a.key, b.key);
        },
        function keyHash(item) {
            return hash(item.key);
        }
    );
    this.length = 0;
    this.addEach(values);
}

Object.addEach(Map.prototype, GenericCollection.prototype);
Object.addEach(Map.prototype, GenericMap.prototype); // overrides GenericCollection
Object.addEach(Map.prototype, PropertyChanges.prototype);

Map.prototype.constructClone = function (values) {
    return new this.constructor(
        values,
        this.contentEquals,
        this.contentHash,
        this.getDefault
    );
};

Map.prototype.log = function (charmap, logNode, callback, thisp) {
    logNode = logNode || this.logNode;
    this.store.log(charmap, function (node, log, logBefore) {
        logNode(node.value.value, log, logBefore);
    }, callback, thisp);
};

Map.prototype.logNode = function (node, log) {
    log(' key: ' + node.key);
    log(' value: ' + node.value);
};


}})
;
//*/
montageDefine("5ea2535","shim-array",{dependencies:["./shim-function","./generic-collection","./generic-order","weak-map"],factory:function(require,exports,module){"use strict";

/*
    Based in part on extras from Motorola Mobility’s Montage
    Copyright (c) 2012, Motorola Mobility LLC. All Rights Reserved.
    3-Clause BSD License
    https://github.com/motorola-mobility/montage/blob/master/LICENSE.md
*/

var Function = require("./shim-function");
var GenericCollection = require("./generic-collection");
var GenericOrder = require("./generic-order");
var WeakMap = require("weak-map");

module.exports = Array;

Array.empty = [];

if (Object.freeze) {
    Object.freeze(Array.empty);
}

Array.from = function (values) {
    var array = [];
    array.addEach(values);
    return array;
};

Array.unzip = function (table) {
    var transpose = [];
    var length = Infinity;
    // compute shortest row
    for (var i = 0; i < table.length; i++) {
        var row = table[i];
        table[i] = row.toArray();
        if (row.length < length) {
            length = row.length;
        }
    }
    for (var i = 0; i < table.length; i++) {
        var row = table[i];
        for (var j = 0; j < row.length; j++) {
            if (j < length && j in row) {
                transpose[j] = transpose[j] || [];
                transpose[j][i] = row[j];
            }
        }
    }
    return transpose;
};

function define(key, value) {
    Object.defineProperty(Array.prototype, key, {
        value: value,
        writable: true,
        configurable: true,
        enumerable: false
    });
}

define("addEach", GenericCollection.prototype.addEach);
define("deleteEach", GenericCollection.prototype.deleteEach);
define("toArray", GenericCollection.prototype.toArray);
define("toObject", GenericCollection.prototype.toObject);
define("all", GenericCollection.prototype.all);
define("any", GenericCollection.prototype.any);
define("min", GenericCollection.prototype.min);
define("max", GenericCollection.prototype.max);
define("sum", GenericCollection.prototype.sum);
define("average", GenericCollection.prototype.average);
define("only", GenericCollection.prototype.only);
define("flatten", GenericCollection.prototype.flatten);
define("zip", GenericCollection.prototype.zip);
define("enumerate", GenericCollection.prototype.enumerate);
define("group", GenericCollection.prototype.group);
define("sorted", GenericCollection.prototype.sorted);
define("reversed", GenericCollection.prototype.reversed);

define("constructClone", function (values) {
    var clone = new this.constructor();
    clone.addEach(values);
    return clone;
});

define("has", function (value, equals) {
    return this.find(value, equals) !== -1;
});

define("get", function (index, defaultValue) {
    if (+index !== index)
        throw new Error("Indicies must be numbers");
    if (!index in this) {
        return defaultValue;
    } else {
        return this[index];
    }
});

define("set", function (index, value) {
    this.splice(index, 1, value);
    return true;
});

define("add", function (value) {
    this.push(value);
    return true;
});

define("delete", function (value, equals) {
    var index = this.find(value, equals);
    if (index !== -1) {
        this.splice(index, 1);
        return true;
    }
    return false;
});

define("find", function (value, equals) {
    equals = equals || this.contentEquals || Object.equals;
    for (var index = 0; index < this.length; index++) {
        if (index in this && equals(this[index], value)) {
            return index;
        }
    }
    return -1;
});

define("findLast", function (value, equals) {
    equals = equals || this.contentEquals || Object.equals;
    var index = this.length;
    do {
        index--;
        if (index in this && equals(this[index], value)) {
            return index;
        }
    } while (index > 0);
    return -1;
});

define("swap", function (index, length, plus) {
    var args = Array.prototype.slice.call(arguments, 0, 2);
    if (plus) {
        if (!Array.isArray(plus)) {
            plus = Array.prototype.slice.call(plus);
        }
        args.push.apply(args, plus);
    }
    return this.splice.apply(this, args);
});

define("one", function () {
    for (var i in this) {
        if (Object.owns(this, i)) {
            return this[i];
        }
    }
});

define("clear", function () {
    this.length = 0;
    return this;
});

define("compare", function (that, compare) {
    compare = compare || Object.compare;
    var i;
    var length;
    var lhs;
    var rhs;
    var relative;

    if (this === that) {
        return 0;
    }

    if (!that || !Array.isArray(that)) {
        return GenericOrder.prototype.compare.call(this, that, compare);
    }

    length = Math.min(this.length, that.length);

    for (i = 0; i < length; i++) {
        if (i in this) {
            if (!(i in that)) {
                return -1;
            } else {
                lhs = this[i];
                rhs = that[i];
                relative = compare(lhs, rhs);
                if (relative) {
                    return relative;
                }
            }
        } else if (i in that) {
            return 1;
        }
    }

    return this.length - that.length;
});

define("equals", function (that, equals) {
    equals = equals || Object.equals;
    var i = 0;
    var length = this.length;
    var left;
    var right;

    if (this === that) {
        return true;
    }
    if (!that || !Array.isArray(that)) {
        return GenericOrder.prototype.equals.call(this, that);
    }

    if (length !== that.length) {
        return false;
    } else {
        for (; i < length; ++i) {
            if (i in this) {
                if (!(i in that)) {
                    return false;
                }
                left = this[i];
                right = that[i];
                if (!equals(left, right)) {
                    return false;
                }
            } else {
                if (i in that) {
                    return false;
                }
            }
        }
    }
    return true;
});

define("clone", function (depth, memo) {
    if (depth === undefined) {
        depth = Infinity;
    } else if (depth === 0) {
        return this;
    }
    memo = memo || new WeakMap();
    var clone = [];
    for (var i in this) {
        if (Object.owns(this, i)) {
            clone[i] = Object.clone(this[i], depth - 1, memo);
        }
    };
    return clone;
});

define("iterate", function (start, end) {
    return new ArrayIterator(this, start, end);
});

define("Iterator", ArrayIterator);

function ArrayIterator(array, start, end) {
    this.array = array;
    this.start = start == null ? 0 : start;
    this.end = end;
};

ArrayIterator.prototype.next = function () {
    if (this.start === (this.end == null ? this.array.length : this.end)) {
        throw StopIteration;
    } else {
        return this.array[this.start++];
    }
};


}})
;
//*/
montageDefine("5ea2535","shim-object",{dependencies:["weak-map"],factory:function(require,exports,module){"use strict";

var WeakMap = require("weak-map");

module.exports = Object;

/*
    Based in part on extras from Motorola Mobility’s Montage
    Copyright (c) 2012, Motorola Mobility LLC. All Rights Reserved.
    3-Clause BSD License
    https://github.com/motorola-mobility/montage/blob/master/LICENSE.md
*/

/**
    Defines extensions to intrinsic <code>Object</code>.
    @see [Object class]{@link external:Object}
*/

/**
    A utility object to avoid unnecessary allocations of an empty object
    <code>{}</code>.  This object is frozen so it is safe to share.

    @object external:Object.empty
*/
Object.empty = Object.freeze(Object.create(null));

/**
    Returns whether the given value is an object, as opposed to a value.
    Unboxed numbers, strings, true, false, undefined, and null are not
    objects.  Arrays are objects.

    @function external:Object.isObject
    @param {Any} value
    @returns {Boolean} whether the given value is an object
*/
Object.isObject = function (object) {
    return Object(object) === object;
};

/**
    Returns the value of an any value, particularly objects that
    implement <code>valueOf</code>.

    <p>Note that, unlike the precedent of methods like
    <code>Object.equals</code> and <code>Object.compare</code> would suggest,
    this method is named <code>Object.getValueOf</code> instead of
    <code>valueOf</code>.  This is a delicate issue, but the basis of this
    decision is that the JavaScript runtime would be far more likely to
    accidentally call this method with no arguments, assuming that it would
    return the value of <code>Object</code> itself in various situations,
    whereas <code>Object.equals(Object, null)</code> protects against this case
    by noting that <code>Object</code> owns the <code>equals</code> property
    and therefore does not delegate to it.

    @function external:Object.getValueOf
    @param {Any} value a value or object wrapping a value
    @returns {Any} the primitive value of that object, if one exists, or passes
    the value through
*/
Object.getValueOf = function (value) {
    if (Object.can(value, "valueOf")) {
        value = value.valueOf();
    }
    return value;
};

var hashMap = new WeakMap();
Object.hash = function (object) {
    if (Object.can(object, "hash")) {
        return "" + object.hash();
    } else if (Object(object) === object) {
        if (!hashMap.has(object)) {
            hashMap.set(object, Math.random().toString(36).slice(2));
        }
        return hashMap.get(object);
    } else {
        return "" + object;
    }
};

/**
    A shorthand for <code>Object.prototype.hasOwnProperty.call(object,
    key)</code>.  Returns whether the object owns a property for the given key.
    It does not consult the prototype chain and works for any string (including
    "hasOwnProperty") except "__proto__".

    @function external:Object.owns
    @param {Object} object
    @param {String} key
    @returns {Boolean} whether the object owns a property wfor the given key.
*/
var owns = Object.prototype.hasOwnProperty;
Object.owns = function (object, key) {
    return owns.call(object, key);
};

/**
    Returns whether a value implements a particular duck-type method.

    <p>To qualify as a duck-type method, the value in question must have a
    method by the given name on the prototype chain.  To distinguish it from
    a property of an object literal, the property must not be owned by the
    object directly.

    <p>A value that implements a method is not necessarily an object, for
    example, numbers implement <code>valueOf</code>, so this is function
    does not imply <code>Object.isObject</code> of the same value.

    @function external:Object.can
    @param {Any} value a value
    @param {String} name a method name
    @returns {Boolean} whether the given value implements the given method

*/
Object.can = function (object, name) {
    return (
        object != null && // false only for null *and* undefined
        typeof object[name] === "function" &&
        !owns.call(object, name)
    );
};

/**
    A utility that is like Object.owns but is also useful for finding
    properties on the prototype chain, provided that they do not refer to
    methods on the Object prototype.  Works for all strings except "__proto__".

    <p>Alternately, you could use the "in" operator as long as the object
    descends from "null" instead of the Object.prototype, as with
    <code>Object.create(null)</code>.  However,
    <code>Object.create(null)</code> only works in fully compliant EcmaScript 5
    JavaScript engines and cannot be faithfully shimmed.

    <p>If the given object is an instance of a type that implements a method
    named "has", this function defers to the collection, so this method can be
    used to generically handle objects, arrays, or other collections.  In that
    case, the domain of the key depends on the instance.

    @param {Object} object
    @param {String} key
    @returns {Boolean} whether the object, or any of its prototypes except
    <code>Object.prototype</code>
    @function external:Object.has
*/
Object.has = function (object, key) {
    if (typeof object !== "object") {
        throw new Error("Object.has can't accept non-object: " + typeof object);
    }
    // forward to mapped collections that implement "has"
    if (Object.can(object, "has")) {
        return object.has(key);
    // otherwise report whether the key is on the prototype chain,
    // as long as it is not one of the methods on object.prototype
    } else if (typeof key === "string") {
        return key in object && object[key] !== Object.prototype[key];
    } else {
        throw new Error("Key must be a string for Object.has on plain objects");
    }
};

/**
    Gets the value for a corresponding key from an object.

    <p>Uses Object.has to determine whether there is a corresponding value for
    the given key.  As such, <code>Object.get</code> is capable of retriving
    values from the prototype chain as long as they are not from the
    <code>Object.prototype</code>.

    <p>If there is no corresponding value, returns the given default, which may
    be <code>undefined</code>.

    <p>If the given object is an instance of a type that implements a method
    named "get", this function defers to the collection, so this method can be
    used to generically handle objects, arrays, or other collections.  In that
    case, the domain of the key depends on the implementation.  For a `Map`,
    for example, the key might be any object.

    @param {Object} object
    @param {String} key
    @param {Any} value a default to return, <code>undefined</code> if omitted
    @returns {Any} value for key, or default value
    @function external:Object.get
*/
Object.get = function (object, key, value) {
    if (typeof object !== "object") {
        throw new Error("Object.get can't accept non-object: " + typeof object);
    }
    // forward to mapped collections that implement "get"
    if (Object.can(object, "get")) {
        return object.get(key, value);
    } else if (Object.has(object, key)) {
        return object[key];
    } else {
        return value;
    }
};

/**
    Sets the value for a given key on an object.

    <p>If the given object is an instance of a type that implements a method
    named "set", this function defers to the collection, so this method can be
    used to generically handle objects, arrays, or other collections.  As such,
    the key domain varies by the object type.

    @param {Object} object
    @param {String} key
    @param {Any} value
    @returns <code>undefined</code>
    @function external:Object.set
*/
Object.set = function (object, key, value) {
    if (Object.can(object, "set")) {
        object.set(key, value);
    } else {
        object[key] = value;
    }
};

Object.addEach = function (target, source) {
    if (Object.can(source, "forEach")) {
        // copy map-alikes
        if (typeof source.keys === "function") {
            source.forEach(function (value, key) {
                target[key] = value;
            });
        // iterate key value pairs of other iterables
        } else {
            source.forEach(function (pair) {
                target[pair[0]] = pair[1];
            });
        }
    } else {
        // copy other objects as map-alikes
        Object.keys(source).forEach(function (key) {
            target[key] = source[key];
        });
    }
    return target;
};

/**
    Iterates over the owned properties of an object.

    @function external:Object.forEach
    @param {Object} object an object to iterate.
    @param {Function} callback a function to call for every key and value
    pair in the object.  Receives <code>value</code>, <code>key</code>,
    and <code>object</code> as arguments.
    @param {Object} thisp the <code>this</code> to pass through to the
    callback
*/
Object.forEach = function (object, callback, thisp) {
    Object.keys(object).forEach(function (key) {
        callback.call(thisp, object[key], key, object);
    });
};

/**
    Iterates over the owned properties of a map, constructing a new array of
    mapped values.

    @function external:Object.map
    @param {Object} object an object to iterate.
    @param {Function} callback a function to call for every key and value
    pair in the object.  Receives <code>value</code>, <code>key</code>,
    and <code>object</code> as arguments.
    @param {Object} thisp the <code>this</code> to pass through to the
    callback
    @returns {Array} the respective values returned by the callback for each
    item in the object.
*/
Object.map = function (object, callback, thisp) {
    return Object.keys(object).map(function (key) {
        return callback.call(thisp, object[key], key, object);
    });
};

/**
    Returns the values for owned properties of an object.

    @function external:Object.map
    @param {Object} object
    @returns {Array} the respective value for each owned property of the
    object.
*/
Object.values = function (object) {
    return Object.map(object, Function.identity);
};

// TODO inline document concat
Object.concat = function () {
    var object = {};
    for (var i = 0; i < arguments.length; i++) {
        Object.addEach(object, arguments[i]);
    }
    return object;
};

Object.from = Object.concat;

/**
    Returns whether two values are identical.  Any value is identical to itself
    and only itself.  This is much more restictive than equivalence and subtly
    different than strict equality, <code>===</code> because of edge cases
    including negative zero and <code>NaN</code>.  Identity is useful for
    resolving collisions among keys in a mapping where the domain is any value.
    This method does not delgate to any method on an object and cannot be
    overridden.
    @see http://wiki.ecmascript.org/doku.php?id=harmony:egal
    @param {Any} this
    @param {Any} that
    @returns {Boolean} whether this and that are identical
    @function external:Object.is
*/
Object.is = function (x, y) {
    if (x === y) {
        // 0 === -0, but they are not identical
        return x !== 0 || 1 / x === 1 / y;
    }
    // NaN !== NaN, but they are identical.
    // NaNs are the only non-reflexive value, i.e., if x !== x,
    // then x is a NaN.
    // isNaN is broken: it converts its argument to number, so
    // isNaN("foo") => true
    return x !== x && y !== y;
};

/**
    Performs a polymorphic, type-sensitive deep equivalence comparison of any
    two values.

    <p>As a basic principle, any value is equivalent to itself (as in
    identity), any boxed version of itself (as a <code>new Number(10)</code> is
    to 10), and any deep clone of itself.

    <p>Equivalence has the following properties:

    <ul>
        <li><strong>polymorphic:</strong>
            If the given object is an instance of a type that implements a
            methods named "equals", this function defers to the method.  So,
            this function can safely compare any values regardless of type,
            including undefined, null, numbers, strings, any pair of objects
            where either implements "equals", or object literals that may even
            contain an "equals" key.
        <li><strong>type-sensitive:</strong>
            Incomparable types are not equal.  No object is equivalent to any
            array.  No string is equal to any other number.
        <li><strong>deep:</strong>
            Collections with equivalent content are equivalent, recursively.
        <li><strong>equivalence:</strong>
            Identical values and objects are equivalent, but so are collections
            that contain equivalent content.  Whether order is important varies
            by type.  For Arrays and lists, order is important.  For Objects,
            maps, and sets, order is not important.  Boxed objects are mutally
            equivalent with their unboxed values, by virtue of the standard
            <code>valueOf</code> method.
    </ul>
    @param this
    @param that
    @returns {Boolean} whether the values are deeply equivalent
    @function external:Object.equals
*/
Object.equals = function (a, b, equals) {
    equals = equals || Object.equals;
    // unbox objects, but do not confuse object literals
    a = Object.getValueOf(a);
    b = Object.getValueOf(b);
    if (a === b)
        // 0 === -0, but they are not equal
        return a !== 0 || 1 / a === 1 / b;
    if (a === null || b === null)
        return a === b;
    if (Object.can(a, "equals"))
        return a.equals(b, equals);
    // commutative
    if (Object.can(b, "equals"))
        return b.equals(a, equals);
    if (typeof a === "object" && typeof b === "object") {
        var aPrototype = Object.getPrototypeOf(a);
        var bPrototype = Object.getPrototypeOf(b);
        if (
            aPrototype === bPrototype && (
                aPrototype === Object.prototype ||
                aPrototype === null
            )
        ) {
            for (var key in a) {
                if (!equals(a[key], b[key])) {
                    return false;
                }
            }
            for (var key in b) {
                if (!equals(a[key], b[key])) {
                    return false;
                }
            }
            return true;
        }
    }
    // NaN !== NaN, but they are equal.
    // NaNs are the only non-reflexive value, i.e., if x !== x,
    // then x is a NaN.
    // isNaN is broken: it converts its argument to number, so
    // isNaN("foo") => true
    return a !== a && b !== b;
};

// Because a return value of 0 from a `compare` function  may mean either
// "equals" or "is incomparable", `equals` cannot be defined in terms of
// `compare`.  However, `compare` *can* be defined in terms of `equals` and
// `lessThan`.  Again however, more often it would be desirable to implement
// all of the comparison functions in terms of compare rather than the other
// way around.

/**
    Determines the order in which any two objects should be sorted by returning
    a number that has an analogous relationship to zero as the left value to
    the right.  That is, if the left is "less than" the right, the returned
    value will be "less than" zero, where "less than" may be any other
    transitive relationship.

    <p>Arrays are compared by the first diverging values, or by length.

    <p>Any two values that are incomparable return zero.  As such,
    <code>equals</code> should not be implemented with <code>compare</code>
    since incomparability is indistinguishable from equality.

    <p>Sorts strings lexicographically.  This is not suitable for any
    particular international setting.  Different locales sort their phone books
    in very different ways, particularly regarding diacritics and ligatures.

    <p>If the given object is an instance of a type that implements a method
    named "compare", this function defers to the instance.  The method does not
    need to be an owned property to distinguish it from an object literal since
    object literals are incomparable.  Unlike <code>Object</code> however,
    <code>Array</code> implements <code>compare</code>.

    @param {Any} left
    @param {Any} right
    @returns {Number} a value having the same transitive relationship to zero
    as the left and right values.
    @function external:Object.compare
*/
Object.compare = function (a, b) {
    // unbox objects, but do not confuse object literals
    // mercifully handles the Date case
    a = Object.getValueOf(a);
    b = Object.getValueOf(b);
    var aType = typeof a;
    var bType = typeof b;
    if (a === b)
        return 0;
    if (aType !== bType)
        return 0;
    if (aType === "number")
        return a - b;
    if (aType === "string")
        return a < b ? -1 : 1;
        // the possibility of equality elimiated above
    if (Object.can(a, "compare"))
        return a.compare(b);
    // not commutative, the relationship is reversed
    if (Object.can(b, "compare"))
        return -b.compare(a);
    return 0;
};

/**
    Creates a deep copy of any value.  Values, being immutable, are
    returned without alternation.  Forwards to <code>clone</code> on
    objects and arrays.

    @function external:Object.clone
    @param {Any} value a value to clone
    @param {Number} depth an optional traversal depth, defaults to infinity.
    A value of <code>0</code> means to make no clone and return the value
    directly.
    @param {Map} memo an optional memo of already visited objects to preserve
    reference cycles.  The cloned object will have the exact same shape as the
    original, but no identical objects.  Te map may be later used to associate
    all objects in the original object graph with their corresponding member of
    the cloned graph.
    @returns a copy of the value
*/
Object.clone = function (value, depth, memo) {
    value = Object.getValueOf(value);
    memo = memo || new WeakMap();
    if (depth === undefined) {
        depth = Infinity;
    } else if (depth === 0) {
        return value;
    }
    if (Object.isObject(value)) {
        if (!memo.has(value)) {
            if (Object.can(value, "clone")) {
                memo.set(value, value.clone(depth, memo));
            } else {
                var prototype = Object.getPrototypeOf(value);
                if (prototype === null || prototype === Object.prototype) {
                    var clone = Object.create(prototype);
                    memo.set(value, clone);
                    for (var key in value) {
                        clone[key] = Object.clone(value[key], depth - 1, memo);
                    }
                } else {
                    throw new Error("Can't clone " + value);
                }
            }
        }
        return memo.get(value);
    }
    return value;
};

/**
    Removes all properties owned by this object making the object suitable for
    reuse.

    @function external:Object.clear
    @returns this
*/
Object.clear = function (object) {
    if (Object.can(object, "clear")) {
        object.clear();
    } else {
        var keys = Object.keys(object),
            i = keys.length;
        while (i) {
            i--;
            delete object[keys[i]];
        }
    }
    return object;
};


}})
;
//*/
montageDefine("5ea2535","shim-function",{dependencies:[],factory:function(require,exports,module){
module.exports = Function;

/**
    A utility to reduce unnecessary allocations of <code>function () {}</code>
    in its many colorful variations.  It does nothing and returns
    <code>undefined</code> thus makes a suitable default in some circumstances.

    @function external:Function.noop
*/
Function.noop = function () {
};

/**
    A utility to reduce unnecessary allocations of <code>function (x) {return
    x}</code> in its many colorful but ultimately wasteful parameter name
    variations.

    @function external:Function.identity
    @param {Any} any value
    @returns {Any} that value
*/
Function.identity = function (value) {
    return value;
};

/**
    A utility for creating a comparator function for a particular aspect of a
    figurative class of objects.

    @function external:Function.by
    @param {Function} relation A function that accepts a value and returns a
    corresponding value to use as a representative when sorting that object.
    @param {Function} compare an alternate comparator for comparing the
    represented values.  The default is <code>Object.compare</code>, which
    does a deep, type-sensitive, polymorphic comparison.
    @returns {Function} a comparator that has been annotated with
    <code>by</code> and <code>compare</code> properties so
    <code>sorted</code> can perform a transform that reduces the need to call
    <code>by</code> on each sorted object to just once.
 */
Function.by = function (by , compare) {
    compare = compare || Object.compare;
    by = by || Function.identity;
    var compareBy = function (a, b) {
        return compare(by(a), by(b));
    };
    compareBy.compare = compare;
    compareBy.by = by;
    return compareBy;
};

// TODO document
Function.get = function (key) {
    return function (object) {
        return Object.get(object, key);
    };
};


}})
;
//*/
montageDefine("5ea2535","shim-regexp",{dependencies:[],factory:function(require,exports,module){
/**
    accepts a string; returns the string with regex metacharacters escaped.
    the returned string can safely be used within a regex to match a literal
    string. escaped characters are [, ], {, }, (, ), -, *, +, ?, ., \, ^, $,
    |, #, [comma], and whitespace.
*/
if (!RegExp.escape) {
    var special = /[-[\]{}()*+?.\\^$|,#\s]/g;
    RegExp.escape = function (string) {
        return string.replace(special, "\\$&");
    };
}


}})
;
//*/
montageDefine("5ea2535","set",{dependencies:["./shim","./list","./fast-set","./generic-collection","./generic-set","./listen/property-changes","./listen/range-changes"],factory:function(require,exports,module){"use strict";

var Shim = require("./shim");
var List = require("./list");
var FastSet = require("./fast-set");
var GenericCollection = require("./generic-collection");
var GenericSet = require("./generic-set");
var PropertyChanges = require("./listen/property-changes");
var RangeChanges = require("./listen/range-changes");

module.exports = Set;

function Set(values, equals, hash, getDefault) {
    if (!(this instanceof Set)) {
        return new Set(values, equals, hash, getDefault);
    }
    equals = equals || Object.equals;
    hash = hash || Object.hash;
    getDefault = getDefault || Function.noop;
    this.contentEquals = equals;
    this.contentHash = hash;
    this.getDefault = getDefault;
    // a list of values in insertion order, used for all operations that depend
    // on iterating in insertion order
    this.order = new this.Order(undefined, equals);
    // a set of nodes from the order list, indexed by the corresponding value,
    // used for all operations that need to quickly seek  value in the list
    this.store = new this.Store(
        undefined,
        function (a, b) {
            return equals(a.value, b.value);
        },
        function (node) {
            return hash(node.value);
        }
    );
    this.length = 0;
    this.addEach(values);
}

Object.addEach(Set.prototype, GenericCollection.prototype);
Object.addEach(Set.prototype, GenericSet.prototype);
Object.addEach(Set.prototype, PropertyChanges.prototype);
Object.addEach(Set.prototype, RangeChanges.prototype);

Set.prototype.Order = List;
Set.prototype.Store = FastSet;

Set.prototype.constructClone = function (values) {
    return new this.constructor(values, this.contentEquals, this.contentHash, this.getDefault);
};

Set.prototype.has = function (value) {
    var node = new this.order.Node(value);
    return this.store.has(node);
};

Set.prototype.get = function (value) {
    var node = new this.order.Node(value);
    node = this.store.get(node);
    if (node) {
        return node.value;
    } else {
        return this.getDefault(value);
    }
};

Set.prototype.add = function (value) {
    var node = new this.order.Node(value);
    if (!this.store.has(node)) {
        if (this.dispatchesRangeChanges) {
            this.dispatchBeforeRangeChange([value], [], 0);
        }
        this.order.add(value);
        node = this.order.head.prev;
        this.store.add(node);
        this.length++;
        if (this.dispatchesRangeChanges) {
            this.dispatchRangeChange([value], [], 0);
        }
        return true;
    }
    return false;
};

Set.prototype["delete"] = function (value) {
    var node = new this.order.Node(value);
    if (this.store.has(node)) {
        if (this.dispatchesRangeChanges) {
            this.dispatchBeforeRangeChange([], [value], 0);
        }
        var node = this.store.get(node);
        this.store["delete"](node); // removes from the set
        node["delete"](); // removes the node from the list in place
        this.length--;
        if (this.dispatchesRangeChanges) {
            this.dispatchRangeChange([], [value], 0);
        }
        return true;
    }
    return false;
};

Set.prototype.pop = function () {
    if (this.length) {
        var result = this.order.head.prev.value;
        this["delete"](result);
        return result;
    }
};

Set.prototype.shift = function () {
    if (this.length) {
        var result = this.order.head.next.value;
        this["delete"](result);
        return result;
    }
};

Set.prototype.one = function () {
    if (this.length > 0) {
        return this.store.one().value;
    }
};

Set.prototype.clear = function () {
    var clearing;
    if (this.dispatchesRangeChanges) {
        clearing = this.toArray();
        this.dispatchBeforeRangeChange([], clearing, 0);
    }
    this.store.clear();
    this.order.clear();
    this.length = 0;
    if (this.dispatchesRangeChanges) {
        this.dispatchRangeChange([], clearing, 0);
    }
};

Set.prototype.reduce = function (callback, basis /*, thisp*/) {
    var thisp = arguments[2];
    var list = this.order;
    var index = 0;
    return list.reduce(function (basis, value) {
        return callback.call(thisp, basis, value, index++, this);
    }, basis, this);
};

Set.prototype.reduceRight = function (callback, basis /*, thisp*/) {
    var thisp = arguments[2];
    var list = this.order;
    var index = this.length - 1;
    return list.reduceRight(function (basis, value) {
        return callback.call(thisp, basis, value, index--, this);
    }, basis, this);
};

Set.prototype.iterate = function () {
    return this.order.iterate();
};

Set.prototype.log = function () {
    var set = this.store;
    return set.log.apply(set, arguments);
};


}})
;
//*/
montageDefine("5ea2535","generic-collection",{dependencies:["./shim-array"],factory:function(require,exports,module){"use strict";

module.exports = GenericCollection;
function GenericCollection() {
    throw new Error("Can't construct. GenericCollection is a mixin.");
}

GenericCollection.prototype.addEach = function (values) {
    if (values && Object(values) === values) {
        if (typeof values.forEach === "function") {
            values.forEach(this.add, this);
        } else if (typeof values.length === "number") {
            // Array-like objects that do not implement forEach, ergo,
            // Arguments
            for (var i = 0; i < values.length; i++) {
                this.add(values[i], i);
            }
        } else {
            Object.keys(values).forEach(function (key) {
                this.add(values[key], key);
            }, this);
        }
    }
    return this;
};

// This is sufficiently generic for Map (since the value may be a key)
// and ordered collections (since it forwards the equals argument)
GenericCollection.prototype.deleteEach = function (values, equals) {
    values.forEach(function (value) {
        this["delete"](value, equals);
    }, this);
    return this;
};

// all of the following functions are implemented in terms of "reduce".
// some need "constructClone".

GenericCollection.prototype.forEach = function (callback /*, thisp*/) {
    var thisp = arguments[1];
    return this.reduce(function (undefined, value, key, object, depth) {
        callback.call(thisp, value, key, object, depth);
    }, undefined);
};

GenericCollection.prototype.map = function (callback /*, thisp*/) {
    var thisp = arguments[1];
    var result = [];
    this.reduce(function (undefined, value, key, object, depth) {
        result.push(callback.call(thisp, value, key, object, depth));
    }, undefined);
    return result;
};

GenericCollection.prototype.enumerate = function (start) {
    if (start == null) {
        start = 0;
    }
    var result = [];
    this.reduce(function (undefined, value) {
        result.push([start++, value]);
    }, undefined);
    return result;
};

GenericCollection.prototype.group = function (callback, thisp, equals) {
    equals = equals || Object.equals;
    var groups = [];
    var keys = [];
    this.forEach(function (value, key, object) {
        var key = callback.call(thisp, value, key, object);
        var index = keys.indexOf(key, equals);
        var group;
        if (index === -1) {
            group = [];
            groups.push([key, group]);
            keys.push(key);
        } else {
            group = groups[index][1];
        }
        group.push(value);
    });
    return groups;
};

GenericCollection.prototype.toArray = function () {
    return this.map(Function.identity);
};

// this depends on stringable keys, which apply to Array and Iterator
// because they have numeric keys and all Maps since they may use
// strings as keys.  List, Set, and SortedSet have nodes for keys, so
// toObject would not be meaningful.
GenericCollection.prototype.toObject = function () {
    var object = {};
    this.reduce(function (undefined, value, key) {
        object[key] = value;
    }, undefined);
    return object;
};

GenericCollection.prototype.filter = function (callback /*, thisp*/) {
    var thisp = arguments[1];
    var result = this.constructClone();
    this.reduce(function (undefined, value, key, object, depth) {
        if (callback.call(thisp, value, key, object, depth)) {
            result.add(value);
        }
    }, undefined);
    return result;
};

GenericCollection.prototype.every = function (callback /*, thisp*/) {
    var thisp = arguments[1];
    return this.reduce(function (result, value, key, object, depth) {
        return result && callback.call(thisp, value, key, object, depth);
    }, true);
};

GenericCollection.prototype.some = function (callback /*, thisp*/) {
    var thisp = arguments[1];
    return this.reduce(function (result, value, key, object, depth) {
        return result || callback.call(thisp, value, key, object, depth);
    }, false);
};

GenericCollection.prototype.all = function () {
    return this.every(Boolean);
};

GenericCollection.prototype.any = function () {
    return this.some(Boolean);
};

GenericCollection.prototype.min = function (compare) {
    compare = compare || this.contentCompare || Object.compare;
    var first = true;
    return this.reduce(function (result, value) {
        if (first) {
            first = false;
            return value;
        } else {
            return compare(value, result) < 0 ? value : result;
        }
    }, undefined);
};

GenericCollection.prototype.max = function (compare) {
    compare = compare || this.contentCompare || Object.compare;
    var first = true;
    return this.reduce(function (result, value) {
        if (first) {
            first = false;
            return value;
        } else {
            return compare(value, result) > 0 ? value : result;
        }
    }, undefined);
};

GenericCollection.prototype.sum = function (zero) {
    zero = zero === undefined ? 0 : zero;
    return this.reduce(function (a, b) {
        return a + b;
    }, zero);
};

GenericCollection.prototype.average = function (zero) {
    var sum = zero === undefined ? 0 : zero;
    var count = zero === undefined ? 0 : zero;
    this.reduce(function (undefined, value) {
        sum += value;
        count += 1;
    }, undefined);
    return sum / count;
};

GenericCollection.prototype.concat = function () {
    var result = this.constructClone(this);
    for (var i = 0; i < arguments.length; i++) {
        result.addEach(arguments[i]);
    }
    return result;
};

GenericCollection.prototype.flatten = function () {
    var self = this;
    return this.reduce(function (result, array) {
        array.forEach(function (value) {
            this.push(value);
        }, result, self);
        return result;
    }, []);
};

GenericCollection.prototype.zip = function () {
    var table = Array.prototype.slice.call(arguments);
    table.unshift(this);
    return Array.unzip(table);
}

GenericCollection.prototype.sorted = function (compare, by, order) {
    compare = compare || this.contentCompare || Object.compare;
    // account for comparators generated by Function.by
    if (compare.by) {
        by = compare.by;
        compare = compare.compare || this.contentCompare || Object.compare;
    } else {
        by = by || Function.identity;
    }
    if (order === undefined)
        order = 1;
    return this.map(function (item) {
        return {
            by: by(item),
            value: item
        };
    })
    .sort(function (a, b) {
        return compare(a.by, b.by) * order;
    })
    .map(function (pair) {
        return pair.value;
    });
};

GenericCollection.prototype.reversed = function () {
    return this.constructClone(this).reverse();
};

GenericCollection.prototype.clone = function (depth, memo) {
    if (depth === undefined) {
        depth = Infinity;
    } else if (depth === 0) {
        return this;
    }
    var clone = this.constructClone();
    this.forEach(function (value, key) {
        clone.add(Object.clone(value, depth - 1, memo), key);
    }, this);
    return clone;
};

GenericCollection.prototype.only = function () {
    if (this.length === 1) {
        return this.one();
    }
};

GenericCollection.prototype.iterator = function () {
    return this.iterate.apply(this, arguments);
};

require("./shim-array");


}})
;
//*/
montageDefine("5ea2535","generic-map",{dependencies:["./shim-object","./listen/map-changes","./listen/property-changes"],factory:function(require,exports,module){"use strict";

var Object = require("./shim-object");
var MapChanges = require("./listen/map-changes");
var PropertyChanges = require("./listen/property-changes");

module.exports = GenericMap;
function GenericMap() {
    throw new Error("Can't construct. GenericMap is a mixin.");
}

Object.addEach(GenericMap.prototype, MapChanges.prototype);
Object.addEach(GenericMap.prototype, PropertyChanges.prototype);

// all of these methods depend on the constructor providing a `store` set

GenericMap.prototype.addEach = function (values) {
    if (values && Object(values) === values) {
        if (typeof values.forEach === "function") {
            // copy map-alikes
            if (typeof values.keys === "function") {
                values.forEach(function (value, key) {
                    this.set(key, value);
                }, this);
            // iterate key value pairs of other iterables
            } else {
                values.forEach(function (pair) {
                    this.set(pair[0], pair[1]);
                }, this);
            }
        } else {
            // copy other objects as map-alikes
            Object.keys(values).forEach(function (key) {
                this.set(key, values[key]);
            }, this);
        }
    }
    return this;
}

GenericMap.prototype.get = function (key, defaultValue) {
    var item = this.store.get(new this.Item(key));
    if (item) {
        return item.value;
    } else if (arguments.length > 1) {
        return defaultValue;
    } else {
        return this.getDefault(key);
    }
};

GenericMap.prototype.set = function (key, value) {
    var item = new this.Item(key, value);
    var found = this.store.get(item);
    var grew = false;
    if (found) { // update
        if (this.dispatchesMapChanges) {
            this.dispatchBeforeMapChange(key, found.value);
        }
        found.value = value;
        if (this.dispatchesMapChanges) {
            this.dispatchMapChange(key, value);
        }
    } else { // create
        if (this.dispatchesMapChanges) {
            this.dispatchBeforeMapChange(key, undefined);
        }
        if (this.store.add(item)) {
            this.length++;
            grew = true;
        }
        if (this.dispatchesMapChanges) {
            this.dispatchMapChange(key, value);
        }
    }
    return grew;
};

GenericMap.prototype.add = function (value, key) {
    return this.set(key, value);
};

GenericMap.prototype.has = function (key) {
    return this.store.has(new this.Item(key));
};

GenericMap.prototype['delete'] = function (key) {
    var item = new this.Item(key);
    if (this.store.has(item)) {
        var from = this.store.get(item).value;
        if (this.dispatchesMapChanges) {
            this.dispatchBeforeMapChange(key, from);
        }
        this.store["delete"](item);
        this.length--;
        if (this.dispatchesMapChanges) {
            this.dispatchMapChange(key, undefined);
        }
        return true;
    }
    return false;
};

GenericMap.prototype.clear = function () {
    var keys;
    if (this.dispatchesMapChanges) {
        this.forEach(function (value, key) {
            this.dispatchBeforeMapChange(key, value);
        }, this);
        keys = this.keys();
    }
    this.store.clear();
    this.length = 0;
    if (this.dispatchesMapChanges) {
        keys.forEach(function (key) {
            this.dispatchMapChange(key);
        }, this);
    }
};

GenericMap.prototype.reduce = function (callback, basis, thisp) {
    return this.store.reduce(function (basis, item) {
        return callback.call(thisp, basis, item.value, item.key, this);
    }, basis, this);
};

GenericMap.prototype.reduceRight = function (callback, basis, thisp) {
    return this.store.reduceRight(function (basis, item) {
        return callback.call(thisp, basis, item.value, item.key, this);
    }, basis, this);
};

GenericMap.prototype.keys = function () {
    return this.map(function (value, key) {
        return key;
    });
};

GenericMap.prototype.values = function () {
    return this.map(Function.identity);
};

GenericMap.prototype.entries = function () {
    return this.map(function (value, key) {
        return [key, value];
    });
};

// XXX deprecated
GenericMap.prototype.items = function () {
    return this.entries();
};

GenericMap.prototype.equals = function (that, equals) {
    equals = equals || Object.equals;
    if (this === that) {
        return true;
    } else if (Object.can(that, "every")) {
        return that.length === this.length && that.every(function (value, key) {
            return equals(this.get(key), value);
        }, this);
    } else {
        var keys = Object.keys(that);
        return keys.length === this.length && Object.keys(that).every(function (key) {
            return equals(this.get(key), that[key]);
        }, this);
    }
};

GenericMap.prototype.Item = Item;

function Item(key, value) {
    this.key = key;
    this.value = value;
}

Item.prototype.equals = function (that) {
    return Object.equals(this.key, that.key) && Object.equals(this.value, that.value);
};

Item.prototype.compare = function (that) {
    return Object.compare(this.key, that.key);
};


}})
;
//*/
montageDefine("5ea2535","generic-order",{dependencies:["./shim-object"],factory:function(require,exports,module){
var Object = require("./shim-object");

module.exports = GenericOrder;
function GenericOrder() {
    throw new Error("Can't construct. GenericOrder is a mixin.");
}

GenericOrder.prototype.equals = function (that, equals) {
    equals = equals || this.contentEquals || Object.equals;

    if (this === that) {
        return true;
    }
    if (!that) {
        return false;
    }

    var self = this;
    return (
        this.length === that.length &&
        this.zip(that).every(function (pair) {
            return equals(pair[0], pair[1]);
        })
    );
};

GenericOrder.prototype.compare = function (that, compare) {
    compare = compare || this.contentCompare || Object.compare;

    if (this === that) {
        return 0;
    }
    if (!that) {
        return 1;
    }

    var length = Math.min(this.length, that.length);
    var comparison = this.zip(that).reduce(function (comparison, pair, index) {
        if (comparison === 0) {
            if (index >= length) {
                return comparison;
            } else {
                return compare(pair[0], pair[1]);
            }
        } else {
            return comparison;
        }
    }, 0);
    if (comparison === 0) {
        return this.length - that.length;
    }
    return comparison;
};


}})
;
//*/
montageDefine("5ea2535","listen/array-changes",{dependencies:["../shim","../list","weak-map","./property-changes","./range-changes","./map-changes"],factory:function(require,exports,module){/*
    Based in part on observable arrays from Motorola Mobility’s Montage
    Copyright (c) 2012, Motorola Mobility LLC. All Rights Reserved.
    3-Clause BSD License
    https://github.com/motorola-mobility/montage/blob/master/LICENSE.md
*/

/*
    This module is responsible for observing changes to owned properties of
    objects and changes to the content of arrays caused by method calls.
    The interface for observing array content changes establishes the methods
    necessary for any collection with observable content.
*/

require("../shim");
var List = require("../list");
var WeakMap = require("weak-map");
var PropertyChanges = require("./property-changes");
var RangeChanges = require("./range-changes");
var MapChanges = require("./map-changes");

var array_splice = Array.prototype.splice;
var array_slice = Array.prototype.slice;
var array_reverse = Array.prototype.reverse;
var array_sort = Array.prototype.sort;

// use different strategies for making arrays observable between Internet
// Explorer and other browsers.
var protoIsSupported = {}.__proto__ === Object.prototype;
var array_makeObservable;
if (protoIsSupported) {
    array_makeObservable = function () {
        this.__proto__ = ChangeDispatchArray;
    };
} else {
    array_makeObservable = function () {
        Object.defineProperties(this, observableArrayProperties);
    };
}

Object.defineProperty(Array.prototype, "makeObservable", {
    value: array_makeObservable,
    writable: true,
    configurable: true,
    enumerable: false
});

function defineEach(prototype) {
    for (var name in prototype) {
        Object.defineProperty(Array.prototype, name, {
            value: prototype[name],
            writable: true,
            configurable: true,
            enumerable: false
        });
    }
}

defineEach(PropertyChanges.prototype);
defineEach(RangeChanges.prototype);
defineEach(MapChanges.prototype);

var observableArrayProperties = {

    isObservable: {
        value: true,
        writable: true,
        configurable: true
    },

    makeObservable: {
        value: Function.noop, // idempotent
        writable: true,
        configurable: true
    },

    reverse: {
        value: function reverse() {

            // dispatch before change events
            this.dispatchBeforeRangeChange(this, this, 0);
            for (var i = 0; i < this.length; i++) {
                PropertyChanges.dispatchBeforeOwnPropertyChange(this, i, this[i]);
                this.dispatchBeforeMapChange(i, this[i]);
            }

            // actual work
            array_reverse.call(this);

            // dispatch after change events
            for (var i = 0; i < this.length; i++) {
                PropertyChanges.dispatchOwnPropertyChange(this, i, this[i]);
                this.dispatchMapChange(i, this[i]);
            }
            this.dispatchRangeChange(this, this, 0);

            return this;
        },
        writable: true,
        configurable: true
    },

    sort: {
        value: function sort() {

            // dispatch before change events
            this.dispatchBeforeRangeChange(this, this, 0);
            for (var i = 0; i < this.length; i++) {
                PropertyChanges.dispatchBeforeOwnPropertyChange(this, i, this[i]);
                this.dispatchBeforeMapChange(i, this[i]);
            }

            // actual work
            array_sort.apply(this, arguments);

            // dispatch after change events
            for (var i = 0; i < this.length; i++) {
                PropertyChanges.dispatchOwnPropertyChange(this, i, this[i]);
                this.dispatchMapChange(i, this[i]);
            }
            this.dispatchRangeChange(this, this, 0);

            return this;
        },
        writable: true,
        configurable: true
    },

    splice: {
        value: function splice(start, length) {
            var minus = array_slice.call(this, start, start + length);
            var plus = array_slice.call(arguments, 2);
            if (!minus.length && !plus.length)
                return plus; // [], but spare us an instantiation
            var diff = plus.length - minus.length;
            var oldLength = this.length;
            var newLength = Math.max(this.length + diff, start + plus.length);
            var longest = Math.max(oldLength, newLength);

            // dispatch before change events
            if (diff) {
                PropertyChanges.dispatchBeforeOwnPropertyChange(this, "length", this.length);
            }
            this.dispatchBeforeRangeChange(plus, minus, start);
            if (diff === 0) { // substring replacement
                for (var i = start; i < start + plus.length; i++) {
                    PropertyChanges.dispatchBeforeOwnPropertyChange(this, i, this[i]);
                    this.dispatchBeforeMapChange(i, this[i]);
                }
            } else if (PropertyChanges.hasOwnPropertyChangeDescriptor(this)) {
                // all subsequent values changed or shifted.
                // avoid (longest - start) long walks if there are no
                // registered descriptors.
                for (var i = start; i < longest; i++) {
                    PropertyChanges.dispatchBeforeOwnPropertyChange(this, i, this[i]);
                    this.dispatchBeforeMapChange(i, this[i]);
                }
            }

            // actual work
            if (start > oldLength) {
                this.length = start;
            }
            var result = array_splice.apply(this, arguments);

            // dispatch after change events
            if (diff === 0) { // substring replacement
                for (var i = start; i < start + plus.length; i++) {
                    PropertyChanges.dispatchOwnPropertyChange(this, i, this[i]);
                    this.dispatchMapChange(i, this[i]);
                }
            } else if (PropertyChanges.hasOwnPropertyChangeDescriptor(this)) {
                // all subsequent values changed or shifted.
                // avoid (longest - start) long walks if there are no
                // registered descriptors.
                for (var i = start; i < longest; i++) {
                    PropertyChanges.dispatchOwnPropertyChange(this, i, this[i]);
                    this.dispatchMapChange(i, this[i]);
                }
            }
            this.dispatchRangeChange(plus, minus, start);
            if (diff) {
                PropertyChanges.dispatchOwnPropertyChange(this, "length", this.length);
            }

            return result;
        },
        writable: true,
        configurable: true
    },

    // splice is the array content change utility belt.  forward all other
    // content changes to splice so we only have to write observer code in one
    // place

    set: {
        value: function set(index, value) {
            this.splice(index, 1, value);
            return this;
        },
        writable: true,
        configurable: true
    },

    shift: {
        value: function shift() {
            return this.splice(0, 1)[0];
        },
        writable: true,
        configurable: true
    },

    pop: {
        value: function pop() {
            if (this.length) {
                return this.splice(this.length - 1, 1)[0];
            }
        },
        writable: true,
        configurable: true
    },

    push: {
        value: function push(arg) {
            if (arguments.length === 1) {
                return this.splice(this.length, 0, arg);
            } else {
                var args = array_slice.call(arguments);
                return this.swap(this.length, 0, args);
            }
        },
        writable: true,
        configurable: true
    },

    unshift: {
        value: function unshift(arg) {
            if (arguments.length === 1) {
                return this.splice(0, 0, arg);
            } else {
                var args = array_slice.call(arguments);
                return this.swap(0, 0, args);
            }
        },
        writable: true,
        configurable: true
    },

    clear: {
        value: function clear() {
            return this.splice(0, this.length);
        },
        writable: true,
        configurable: true
    }

};

var ChangeDispatchArray = Object.create(Array.prototype, observableArrayProperties);


}})
;
//*/
montageDefine("5ea2535","sorted-array",{dependencies:["./shim","./generic-collection","./listen/property-changes","./listen/range-changes"],factory:function(require,exports,module){"use strict";

module.exports = SortedArray;

var Shim = require("./shim");
var GenericCollection = require("./generic-collection");
var PropertyChanges = require("./listen/property-changes");
var RangeChanges = require("./listen/range-changes");

function SortedArray(values, equals, compare, getDefault) {
    if (!(this instanceof SortedArray)) {
        return new SortedArray(values, equals, compare, getDefault);
    }
    if (Array.isArray(values)) {
        this.array = values;
        values = values.splice(0, values.length);
    } else {
        this.array = [];
    }
    this.contentEquals = equals || Object.equals;
    this.contentCompare = compare || Object.compare;
    this.getDefault = getDefault || Function.noop;

    this.length = 0;
    this.addEach(values);
}

Object.addEach(SortedArray.prototype, GenericCollection.prototype);
Object.addEach(SortedArray.prototype, PropertyChanges.prototype);
Object.addEach(SortedArray.prototype, RangeChanges.prototype);

function search(array, value, compare) {
    var first = 0;
    var last = array.length - 1;
    while (first <= last) {
        var middle = (first + last) >> 1; // Math.floor( / 2)
        var comparison = compare(value, array[middle]);
        if (comparison > 0) {
            first = middle + 1;
        } else if (comparison < 0) {
            last = middle - 1;
        } else {
            return middle;
        }
    }
    return -(first + 1);
}

function searchFirst(array, value, compare, equals) {
    var index = search(array, value, compare);
    if (index < 0) {
        return -1;
    } else {
        while (index > 0 && equals(value, array[index - 1])) {
            index--;
        }
        if (!equals(value, array[index])) {
            return -1;
        } else {
            return index;
        }
    }
}

function searchLast(array, value, compare, equals) {
    var index = search(array, value, compare);
    if (index < 0) {
        return -1;
    } else {
        while (index < array.length - 1 && equals(value, array[index + 1])) {
            index++;
        }
        if (!equals(value, array[index])) {
            return -1;
        } else {
            return index;
        }
    }
}

function searchForInsertionIndex(array, value, compare) {
    var index = search(array, value, compare);
    if (index < 0) {
        return -index - 1;
    } else {
        var last = array.length - 1;
        while (index < last && compare(value, array[index + 1]) === 0) {
            index++;
        }
        return index;
    }
}

SortedArray.prototype.constructClone = function (values) {
    return new this.constructor(
        values,
        this.contentEquals,
        this.contentCompare,
        this.getDefault
    );
};

SortedArray.prototype.has = function (value) {
    var index = search(this.array, value, this.contentCompare);
    return index >= 0 && this.contentEquals(this.array[index], value);
};

SortedArray.prototype.get = function (value) {
    var index = searchFirst(this.array, value, this.contentCompare, this.contentEquals);
    if (index !== -1) {
        return this.array[index];
    } else {
        return this.getDefault(value);
    }
};

SortedArray.prototype.add = function (value) {
    var index = searchForInsertionIndex(this.array, value, this.contentCompare);
    if (this.dispatchesRangeChanges) {
        this.dispatchBeforeRangeChange([value], [], index);
    }
    this.array.splice(index, 0, value);
    this.length++;
    if (this.dispatchesRangeChanges) {
        this.dispatchRangeChange([value], [], index);
    }
    return true;
};

SortedArray.prototype["delete"] = function (value) {
    var index = searchFirst(this.array, value, this.contentCompare, this.contentEquals);
    if (index !== -1) {
        if (this.dispatchesRangeChanges) {
            this.dispatchBeforeRangeChange([], [value], index);
        }
        this.array.splice(index, 1);
        this.length--;
        if (this.dispatchesRangeChanges) {
            this.dispatchRangeChange([], [value], index);
        }
        return true;
    } else {
        return false;
    }
};

SortedArray.prototype.indexOf = function (value) {
    return searchFirst(this.array, value, this.contentCompare, this.contentEquals);
};

SortedArray.prototype.lastIndexOf = function (value) {
    return searchLast(this.array, value, this.contentCompare, this.contentEquals);
};

SortedArray.prototype.find = function (value) {
    return searchFirst(this.array, value, this.contentCompare, this.contentEquals);
};

SortedArray.prototype.findLast = function (value) {
    return searchLast(this.array, value, this.contentCompare, this.contentEquals);
};

SortedArray.prototype.push = function () {
    this.addEach(arguments);
};

SortedArray.prototype.unshift = function () {
    this.addEach(arguments);
};

SortedArray.prototype.pop = function () {
    return this.array.pop();
};

SortedArray.prototype.shift = function () {
    return this.array.shift();
};

SortedArray.prototype.slice = function () {
    return this.array.slice.apply(this.array, arguments);
};

SortedArray.prototype.splice = function (index, length /*...plus*/) {
    return this.swap(index, length, Array.prototype.slice.call(arguments, 2));
};

SortedArray.prototype.swap = function (index, length, plus) {
    if (index === undefined && length === undefined) {
        return [];
    }
    index = index || 0;
    if (index < 0) {
        index += this.length;
    }
    if (length === undefined) {
        length = Infinity;
    }
    var minus = this.slice(index, index + length);
    if (this.dispatchesRangeChanges) {
        this.dispatchBeforeRangeChange(plus, minus, index);
    }
    this.array.splice(index, length);
    this.addEach(plus);
    if (this.dispatchesRangeChanges) {
        this.dispatchRangeChange(plus, minus, index);
    }
    return minus;
};

SortedArray.prototype.reduce = function (callback, basis /*, thisp*/) {
    var thisp = arguments[2];
    return this.array.reduce(function (basis, value, key) {
        return callback.call(thisp, basis, value, key, this);
    }, basis, this);
};

SortedArray.prototype.reduceRight = function () {
    var thisp = arguments[2];
    return this.array.reduceRight(function (basis, value, key) {
        return callback.call(thisp, basis, value, key, this);
    }, basis, this);
};

SortedArray.prototype.min = function () {
    if (this.length) {
        return this.array[0];
    }
};

SortedArray.prototype.max = function () {
    if (this.length) {
        return this.array[this.length - 1];
    }
};

SortedArray.prototype.one = function () {
    return this.array.one();
};

SortedArray.prototype.clear = function () {
    var minus;
    if (this.dispatchesRangeChanges) {
        minus = this.array.slice();
        this.dispatchBeforeRangeChange([], minus, 0);
    }
    this.length = 0;
    this.array.clear();
    if (this.dispatchesRangeChanges) {
        this.dispatchRangeChange([], minus, 0);
    }
};

SortedArray.prototype.equals = function (that, equals) {
    return this.array.equals(that, equals);
};

SortedArray.prototype.compare = function (that, compare) {
    return this.array.compare(that, compare);
};

SortedArray.prototype.iterate = function (start, end) {
    return new this.Iterator(this.array, start, end);
};

SortedArray.prototype.Iterator = Array.prototype.Iterator;


}})
;
//*/
montageDefine("5ea2535","sorted-set",{dependencies:["./shim","./generic-collection","./generic-set","./listen/property-changes","./listen/range-changes","./tree-log"],factory:function(require,exports,module){"use strict";

module.exports = SortedSet;

var Shim = require("./shim");
var GenericCollection = require("./generic-collection");
var GenericSet = require("./generic-set");
var PropertyChanges = require("./listen/property-changes");
var RangeChanges = require("./listen/range-changes");
var TreeLog = require("./tree-log");

function SortedSet(values, equals, compare, getDefault) {
    if (!(this instanceof SortedSet)) {
        return new SortedSet(values, equals, compare, getDefault);
    }
    this.contentEquals = equals || Object.equals;
    this.contentCompare = compare || Object.compare;
    this.getDefault = getDefault || Function.noop;
    this.root = null;
    this.length = 0;
    this.addEach(values);
}

Object.addEach(SortedSet.prototype, GenericCollection.prototype);
Object.addEach(SortedSet.prototype, GenericSet.prototype);
Object.addEach(SortedSet.prototype, PropertyChanges.prototype);
Object.addEach(SortedSet.prototype, RangeChanges.prototype);

SortedSet.prototype.constructClone = function (values) {
    return new this.constructor(
        values,
        this.contentEquals,
        this.contentCompare,
        this.getDefault
    );
};

SortedSet.prototype.has = function (value) {
    if (this.root) {
        this.splay(value);
        return this.contentEquals(value, this.root.value);
    } else {
        return false;
    }
};

SortedSet.prototype.get = function (value) {
    if (this.root) {
        this.splay(value);
        if (this.contentEquals(value, this.root.value)) {
            return this.root.value;
        }
    }
    return this.getDefault(value);
};

SortedSet.prototype.add = function (value) {
    var node = new this.Node(value);
    if (this.root) {
        this.splay(value);
        if (!this.contentEquals(value, this.root.value)) {
            var comparison = this.contentCompare(value, this.root.value);
            if (comparison === 0) {
                throw new Error("SortedSet cannot contain incomparable but inequal values: " + value + " and " + this.root.value);
            }
            if (this.dispatchesRangeChanges) {
                this.dispatchBeforeRangeChange([value], [], this.root.index);
            }
            if (comparison < 0) {
                // rotate right
                //   R        N
                //  / \  ->  / \
                // l   r    l   R
                // :   :    :    \
                //                r
                //                :
                node.right = this.root;
                node.left = this.root.left;
                this.root.left = null;
                this.root.touch();
            } else {
                // rotate left
                //   R        N
                //  / \  ->  / \
                // l   r    R   r
                // :   :   /    :
                //        l
                //        :
                node.left = this.root;
                node.right = this.root.right;
                this.root.right = null;
                this.root.touch();
            }
            node.touch();
            this.root = node;
            this.length++;
            if (this.dispatchesRangeChanges) {
                this.dispatchRangeChange([value], [], this.root.index);
            }
            return true;
        }
    } else {
        if (this.dispatchesRangeChanges) {
            this.dispatchBeforeRangeChange([value], [], 0);
        }
        this.root = node;
        this.length++;
        if (this.dispatchesRangeChanges) {
            this.dispatchRangeChange([value], [], 0);
        }
        return true;
    }
    return false;
};

SortedSet.prototype['delete'] = function (value) {
    if (this.root) {
        this.splay(value);
        if (this.contentEquals(value, this.root.value)) {
            var index = this.root.index;
            if (this.dispatchesRangeChanges) {
                this.dispatchBeforeRangeChange([], [value], index);
            }
            if (!this.root.left) {
                this.root = this.root.right;
            } else {
                // remove the right side of the tree,
                var right = this.root.right;
                this.root = this.root.left;
                // the tree now only contains the left side of the tree, so all
                // values are less than the value deleted.
                // splay so that the root has an empty right child
                this.splay(value);
                // put the right side of the tree back
                this.root.right = right;
            }
            this.length--;
            if (this.root) {
                this.root.touch();
            }
            if (this.dispatchesRangeChanges) {
                this.dispatchRangeChange([], [value], index);
            }
            return true;
        }
    }
    return false;
};

SortedSet.prototype.indexOf = function (value) {
    if (this.root) {
        this.splay(value);
        if (this.contentEquals(value, this.root.value)) {
            return this.root.index;
        }
    }
    return -1;
};

SortedSet.prototype.find = function (value) {
    if (this.root) {
        this.splay(value);
        if (this.contentEquals(value, this.root.value)) {
            return this.root;
        }
    }
};

SortedSet.prototype.findGreatest = function (at) {
    if (this.root) {
        at = at || this.root;
        while (at.right) {
            at = at.right;
        }
        return at;
    }
};

SortedSet.prototype.findLeast = function (at) {
    if (this.root) {
        at = at || this.root;
        while (at.left) {
            at = at.left;
        }
        return at;
    }
};

SortedSet.prototype.findGreatestLessThanOrEqual = function (value) {
    if (this.root) {
        this.splay(value);
        // assert root.value <= value
        return this.root;
    }
};

SortedSet.prototype.findGreatestLessThan = function (value) {
    if (this.root) {
        this.splay(value);
        // assert root.value <= value
        return this.root.getPrevious();
    }
};

SortedSet.prototype.findLeastGreaterThanOrEqual = function (value) {
    if (this.root) {
        this.splay(value);
        // assert root.value <= value
        var comparison = this.contentCompare(value, this.root.value);
        if (comparison === 0) {
            return this.root;
        } else {
            return this.root.getNext();
        }
    }
};

SortedSet.prototype.findLeastGreaterThan = function (value) {
    if (this.root) {
        this.splay(value);
        // assert root.value <= value
        var comparison = this.contentCompare(value, this.root.value);
        return this.root.getNext();
    }
};

SortedSet.prototype.pop = function () {
    if (this.root) {
        var found = this.findGreatest();
        this["delete"](found.value);
        return found.value;
    }
};

SortedSet.prototype.shift = function () {
    if (this.root) {
        var found = this.findLeast();
        this["delete"](found.value);
        return found.value;
    }
};

SortedSet.prototype.push = function () {
    this.addEach(arguments);
};

SortedSet.prototype.unshift = function () {
    this.addEach(arguments);
};

SortedSet.prototype.slice = function (start, end) {
    var temp;
    start = start || 0;
    end = end || this.length;
    if (start < 0) {
        start += this.length;
    }
    if (end < 0) {
        end += this.length;
    }
    var sliced = [];
    if (this.root) {
        this.splayIndex(start);
        while (this.root.index < end) {
            sliced.push(this.root.value);
            if (!this.root.right) {
                break;
            }
            this.splay(this.root.getNext().value);
        }
    }
    return sliced;
};

SortedSet.prototype.splice = function (at, length /*...plus*/) {
    return this.swap(at, length, Array.prototype.slice.call(arguments, 2));
};

SortedSet.prototype.swap = function (start, length, plus) {
    if (start === undefined && length === undefined) {
        return [];
    }
    start = start || 0;
    if (start < 0) {
        start += this.length;
    }
    if (length === undefined) {
        length = Infinity;
    }
    var swapped = [];

    if (this.root) {

        // start
        this.splayIndex(start);

        // minus length
        for (var i = 0; i < length; i++) {
            swapped.push(this.root.value);
            var next = this.root.getNext();
            this["delete"](this.root.value);
            if (!next) {
                break;
            }
            this.splay(next.value);
        }
    }

    // plus
    this.addEach(plus);

    return swapped;
};

// This is the simplified top-down splaying algorithm from: "Self-adjusting
// Binary Search Trees" by Sleator and Tarjan guarantees that the root.value <=
// value if root exists
// - as described in https://github.com/hij1nx/forest
SortedSet.prototype.splay = function (value) {
    var stub, left, right, temp, root, history;

    if (!this.root) {
        return;
    }

    // Create a stub node.  The use of the stub node is a bit
    // counter-intuitive: The right child of the stub node will hold the L tree
    // of the algorithm.  The left child of the stub node will hold the R tree
    // of the algorithm.  Using a stub node, left and right will always be
    // nodes and we avoid special cases.
    // - http://code.google.com/p/v8/source/browse/branches/bleeding_edge/src/splay-tree-inl.h
    stub = left = right = new this.Node();
    // The history is an upside down tree used to propagate new tree sizes back
    // up the left and right arms of a traversal.  The right children of the
    // transitive left side of the tree will be former roots while linking
    // left.  The left children of the transitive walk to the right side of the
    // history tree will all be previous roots from linking right.  The last
    // node of the left and right traversal will each become a child of the new
    // root.
    history = new this.Node();
    root = this.root;

    while (true) {
        var comparison = this.contentCompare(value, root.value);
        if (comparison < 0) {
            if (root.left) {
                if (this.contentCompare(value, root.left.value) < 0) {
                    // rotate right
                    //        Root         L(temp)
                    //      /     \       / \
                    //     L(temp) R    LL    Root
                    //    / \                /    \
                    //  LL   LR            LR      R
                    temp = root.left;
                    root.left = temp.right;
                    root.touch();
                    temp.right = root;
                    temp.touch();
                    root = temp;
                    if (!root.left) {
                        break;
                    }
                }
                // remember former root for repropagating length
                temp = new Node();
                temp.right = root;
                temp.left = history.left;
                history.left = temp;
                // link left
                right.left = root;
                right.touch();
                right = root;
                root = root.left;
            } else {
                break;
            }
        } else if (comparison > 0) {
            if (root.right) {
                if (this.contentCompare(value, root.right.value) > 0) {
                    // rotate left
                    //        Root         L(temp)
                    //      /     \       / \
                    //     L(temp) R    LL    Root
                    //    / \                /    \
                    //  LL   LR            LR      R
                    temp = root.right;
                    root.right = temp.left;
                    root.touch();
                    temp.left = root;
                    temp.touch();
                    root = temp;
                    if (!root.right) {
                        break;
                    }
                }
                // remember former root for repropagating length
                temp = new Node();
                temp.left = root;
                temp.right = history.right;
                history.right = temp;
                // link right
                left.right = root;
                left.touch();
                left = root;
                root = root.right;
            } else {
                break;
            }
        } else { // equal or incomparable
            break;
        }
    }

    // reassemble
    left.right = root.left;
    left.touch();
    right.left = root.right;
    right.touch();
    root.left = stub.right;
    root.right = stub.left;

    // propagate new lengths
    while (history.left) {
        history.left.right.touch();
        history.left = history.left.left;
    }
    while (history.right) {
        history.right.left.touch();
        history.right = history.right.right;
    }
    root.touch();

    this.root = root;
};

// an internal utility for splaying a node based on its index
SortedSet.prototype.splayIndex = function (index) {
    if (this.root) {
        var at = this.root;
        var atIndex = this.root.index;

        while (atIndex !== index) {
            if (atIndex > index && at.left) {
                at = at.left;
                atIndex -= 1 + (at.right ? at.right.length : 0);
            } else if (atIndex < index && at.right) {
                at = at.right;
                atIndex += 1 + (at.left ? at.left.length : 0);
            } else {
                break;
            }
        }

        this.splay(at.value);

        return this.root.index === index;
    }
    return false;
};

SortedSet.prototype.reduce = function (callback, basis, thisp) {
    if (this.root) {
        basis = this.root.reduce(callback, basis, 0, thisp, this);
    }
    return basis;
};

SortedSet.prototype.reduceRight = function (callback, basis, thisp) {
    if (this.root) {
        basis = this.root.reduceRight(callback, basis, this.length - 1, thisp, this);
    }
    return basis;
};

SortedSet.prototype.min = function (at) {
    var least = this.findLeast(at);
    if (least) {
        return least.value;
    }
};

SortedSet.prototype.max = function (at) {
    var greatest = this.findGreatest(at);
    if (greatest) {
        return greatest.value;
    }
};

SortedSet.prototype.one = function () {
    if (this.root) {
        return this.root.value;
    }
};

SortedSet.prototype.clear = function () {
    var minus;
    if (this.dispatchesRangeChanges) {
        minus = this.toArray();
        this.dispatchBeforeRangeChange([], minus, 0);
    }
    this.root = null;
    this.length = 0;
    if (this.dispatchesRangeChanges) {
        this.dispatchRangeChange([], minus, 0);
    }
};

SortedSet.prototype.iterate = function (start, end) {
    return new this.Iterator(this, start, end);
};

SortedSet.prototype.Iterator = Iterator;

SortedSet.prototype.summary = function () {
    if (this.root) {
        return this.root.summary();
    } else {
        return "()";
    }
};

SortedSet.prototype.log = function (charmap, logNode, callback, thisp) {
    charmap = charmap || TreeLog.unicodeRound;
    logNode = logNode || this.logNode;
    if (!callback) {
        callback = console.log;
        thisp = console;
    }
    callback = callback.bind(thisp);
    if (this.root) {
        this.root.log(charmap, logNode, callback, callback);
    }
};

SortedSet.prototype.logNode = function (node, log, logBefore) {
    log(" " + node.value);
};

SortedSet.logCharsets = TreeLog;

SortedSet.prototype.Node = Node;

function Node(value) {
    this.value = value;
    this.left = null;
    this.right = null;
    this.length = 1;
}

// TODO case where no basis is provided for reduction

Node.prototype.reduce = function (callback, basis, index, thisp, tree, depth) {
    depth = depth || 0;
    if (this.left) {
        // prerecord length to be resistant to mutation
        var length = this.left.length;
        basis = this.left.reduce(callback, basis, index, thisp, tree, depth + 1);
        index += length;
    }
    basis = callback.call(thisp, basis, this.value, index, tree, this, depth);
    index += 1;
    if (this.right) {
        basis = this.right.reduce(callback, basis, index, thisp, tree, depth + 1);
    }
    return basis;
};

Node.prototype.reduceRight = function (callback, basis, index, thisp, tree, depth) {
    depth = depth || 0;
    if (this.right) {
        basis = this.right.reduce(callback, basis, index, thisp, tree, depth + 1);
        index -= this.right.length;
    }
    basis = callback.call(thisp, basis, this.value, this.value, tree, this, depth);
    index -= 1;
    if (this.left) {
        basis = this.left.reduce(callback, basis, index, thisp, tree, depth + 1);
    }
    return basis;
};

Node.prototype.touch = function () {
    this.length = 1 +
        (this.left ? this.left.length : 0) +
        (this.right ? this.right.length : 0);
    this.index = this.left ? this.left.length : 0;
};

Node.prototype.checkIntegrity = function () {
    var length = 1;
    length += this.left ? this.left.checkIntegrity() : 0;
    length += this.right ? this.right.checkIntegrity() : 0;
    if (this.length !== length)
        throw new Error("Integrity check failed: " + this.summary());
    return length;
}

// get the next node in this subtree
Node.prototype.getNext = function () {
    var node = this;
    if (node.right) {
        node = node.right;
        while (node.left) {
            node = node.left;
        }
        return node;
    }
};

// get the previous node in this subtree
Node.prototype.getPrevious = function () {
    var node = this;
    if (node.left) {
        node = node.left;
        while (node.right) {
            node = node.right;
        }
        return node;
    }
};

Node.prototype.summary = function () {
    var value = this.value || "-";
    value += " <" + this.length;
    if (!this.left && !this.right) {
        return "(" + value + ")";
    }
    return "(" + value + " " + (
        this.left ? this.left.summary() : "()"
    ) + ", " + (
        this.right ? this.right.summary() : "()"
    ) + ")";
};

Node.prototype.log = function (charmap, logNode, log, logAbove) {
    var self = this;

    var branch;
    if (this.left && this.right) {
        branch = charmap.intersection;
    } else if (this.left) {
        branch = charmap.branchUp;
    } else if (this.right) {
        branch = charmap.branchDown;
    } else {
        branch = charmap.through;
    }

    var loggedAbove;
    this.left && this.left.log(
        charmap,
        logNode,
        function innerWrite(line) {
            if (!loggedAbove) {
                loggedAbove = true;
                // leader
                logAbove(charmap.fromBelow + charmap.through + line);
            } else {
                // below
                logAbove(charmap.strafe + " " + line);
            }
        },
        function innerWriteAbove(line) {
            // above
            logAbove("  " + line);
        }
    );

    var loggedOn;
    logNode(
        this,
        function innerWrite(line) {
            if (!loggedOn) {
                loggedOn = true;
                log(branch + line);
            } else {
                log((self.right ? charmap.strafe : " ") + line);
            }
        },
        function innerWriteAbove(line) {
            logAbove((self.left ? charmap.strafe : " ") + line);
        }
    );

    var loggedBelow;
    this.right && this.right.log(
        charmap,
        logNode,
        function innerWrite(line) {
            if (!loggedBelow) {
                loggedBelow = true;
                log(charmap.fromAbove + charmap.through + line);
            } else {
                log("  " + line);
            }
        },
        function innerWriteAbove(line) {
            log(charmap.strafe + " " + line);
        }
    );
};

function Iterator(set, start, end) {
    this.set = set;
    this.prev = null;
    this.end = end;
    if (start) {
        var next = this.set.findLeastGreaterThanOrEqual(start);
        if (next) {
            this.set.splay(next.value);
            this.prev = next.getPrevious();
        }
    }
}

Iterator.prototype.next = function () {
    var next;
    if (this.prev) {
        next = this.set.findLeastGreaterThan(this.prev.value);
    } else {
        next = this.set.findLeast();
    }
    if (!next) {
        throw StopIteration;
    }
    if (
        this.end !== undefined &&
        this.set.contentCompare(next.value, this.end) >= 0
    ) {
        throw StopIteration;
    }
    this.prev = next;
    return next.value;
};


}})
;
//*/
montageDefine("5ea2535","heap",{dependencies:["./listen/array-changes","./shim","./generic-collection","./listen/map-changes","./listen/range-changes","./listen/property-changes"],factory:function(require,exports,module){
// Adapted from Eloquent JavaScript by Marijn Haverbeke
// http://eloquentjavascript.net/appendix2.html

var ArrayChanges = require("./listen/array-changes");
var Shim = require("./shim");
var GenericCollection = require("./generic-collection");
var MapChanges = require("./listen/map-changes");
var RangeChanges = require("./listen/range-changes");
var PropertyChanges = require("./listen/property-changes");

// Max Heap by default.  Comparison can be reversed to produce a Min Heap.

module.exports = Heap;

function Heap(values, equals, compare) {
    if (!(this instanceof Heap)) {
        return new Heap(values, equals, compare);
    }
    this.contentEquals = equals || Object.equals;
    this.contentCompare = compare || Object.compare;
    this.content = [];
    this.length = 0;
    this.addEach(values);
}

Object.addEach(Heap.prototype, GenericCollection.prototype);
Object.addEach(Heap.prototype, PropertyChanges.prototype);
Object.addEach(Heap.prototype, RangeChanges.prototype);
Object.addEach(Heap.prototype, MapChanges.prototype);

Heap.prototype.constructClone = function (values) {
    return new this.constructor(
        values,
        this.contentEquals,
        this.contentCompare
    );
};

Heap.prototype.push = function (value) {
    this.content.push(value);
    this.float(this.content.length - 1);
    this.length++;
};

Heap.prototype.pop = function () {
    // Store the first value so we can return it later.  This will leave a gap
    // at index 0 that must be filled.
    var result = this.content[0];
    // Remove the value at the end of the array.  The value most be removed
    // from the end to preserve the completness of the tree, despite that the
    // last child is also among the most likely to need to sink back to the
    // bottom.
    var top = this.content.pop();
    // If there are any values remaining, put the last value on the top and
    // let it sink back down.
    if (this.content.length > 0) {
        this.content.set(0, top);
        this.sink(0);
    }
    this.length--;
    return result;
};

Heap.prototype.add = function (value) {
    this.push(value);
};

// indexOf must do a linear search since a binary heap does not preserve a
// strict sort order.  Thus, deletion takes linear time for all values except
// for the max value.

Heap.prototype.indexOf = function (value) {
    for (var index = 0; index < this.length; index++) {
        if (this.contentEquals(this.content[index], value)) {
            return index;
        }
    }
    return -1;
};

Heap.prototype.delete = function (value) {
    var index = this.indexOf(value);
    if (index === -1)
        return false;
    var top = this.content.pop();
    if (index === this.content.length)
        return true;
    this.content.set(index, top);
    var comparison = this.contentCompare(top, value);
    if (comparison > 0) {
        this.float(index);
    } else if (comparison < 0) {
        this.sink(index);
    }
    this.length--;
    return true;
};

Heap.prototype.peek = function () {
    if (this.length) {
        return this.content[0];
    }
};

Heap.prototype.max = function () {
    return this.peek();
};

Heap.prototype.one = function () {
    return this.peek();
};

// Brings a value up until its parent is greater than it
Heap.prototype.float = function (index) {
    // Grab the value that is being adjusted
    var value = this.content[index];
    // A value can go no higher that the top: index 0
    while (index > 0) {
        // Compute the parent value's index and fetch it
        var parentIndex = Math.floor((index + 1) / 2) - 1;
        var parent = this.content[parentIndex];
        // If the parent is less than it
        if (this.contentCompare(parent, value) < 0) {
            this.content.set(parentIndex, value);
            this.content.set(index, parent);
        } else {
            // Stop propagating if the parent is greater than the value.
            break;
        }
        // Proceed upward
        index = parentIndex;
    }
};

// Brings a value down until its children are both less than it
Heap.prototype.sink = function (index) {
    // Moves a value downward until it is greater than its children.
    var length = this.content.length;
    var value = this.content[index];
    var left, right, leftIndex, rightIndex, swapIndex, needsSwap;

    while (true) {
        // Invariant: the value is at index.
        // Variant: the index proceedes down the tree.

        // Compute the indicies of the children.
        rightIndex = (index + 1) * 2;
        leftIndex = rightIndex - 1;

        // If the left child exists, determine whether it is greater than the
        // parent (value) and thus whether it can be floated upward.
        needsSwap = false;
        if (leftIndex < length) {
            // Look it up and compare it.
            var left = this.content[leftIndex];
            var comparison = this.contentCompare(left, value);
            // If the child is greater than the parent, it can be floated.
            if (comparison > 0) {
                swapIndex = leftIndex;
                needsSwap = true;
            }
        }

        // If the right child exists, determine whether it is greater than the
        // parent (value), or even greater than the left child.
        if (rightIndex < length) {
            var right = this.content[rightIndex];
            var comparison = this.contentCompare(right, needsSwap ? left : value);
            if (comparison > 0) {
                swapIndex = rightIndex;
                needsSwap = true;
            }
        }

        // if there is a child that is less than the value, float the child and
        // sink the value.
        if (needsSwap) {
            this.content.set(index, this.content[swapIndex]);
            this.content.set(swapIndex, value);
            index = swapIndex;
            // and continue sinking
        } else {
            // if the children are both less than the value
            break;
        }

    }

};

Heap.prototype.clear = function () {
    this.content.clear();
    this.length = 0;
};

Heap.prototype.reduce = function (callback, basis /*, thisp*/) {
    var thisp = arguments[2];
    return this.content.reduce(function (basis, value, key) {
        return callback.call(thisp, basis, value, key, this);
    }, basis, this);
};

Heap.prototype.reduceRight = function () {
    var thisp = arguments[2];
    return this.content.reduceRight(function (basis, value, key) {
        return callback.call(thisp, basis, value, key, this);
    }, basis, this);
};

Heap.prototype.makeObservable = function () {
    // TODO refactor dispatchers to allow direct forwarding
    this.content.addRangeChangeListener(this, "content");
    this.content.addBeforeRangeChangeListener(this, "content");
    this.content.addMapChangeListener(this, "content");
    this.content.addBeforeMapChangeListener(this, "content");
};

Heap.prototype.handleContentRangeChange = function (plus, minus, index) {
    this.dispatchRangeChange(plus, minus, index);
};

Heap.prototype.handleContentRangeWillChange = function (plus, minus, index) {
    this.dispatchBeforeRangeChange(plus, minus, index);
};

Heap.prototype.handleContentMapChange = function (value, key) {
    this.dispatchMapChange(key, value);
};

Heap.prototype.handleContentMapWillChange = function (value, key) {
    this.dispatchBeforeMapChange(key, value);
};


}})
;
//*/
montageDefine("5ea2535","list",{dependencies:["./shim","./generic-collection","./generic-order","./listen/property-changes"],factory:function(require,exports,module){"use strict";

module.exports = List;

var Shim = require("./shim");
var GenericCollection = require("./generic-collection");
var GenericOrder = require("./generic-order");
var PropertyChanges = require("./listen/property-changes");

function List(values, equals, getDefault) {
    if (!(this instanceof List)) {
        return new List(values, equals, getDefault);
    }
    var head = this.head = new this.Node();
    head.next = head;
    head.prev = head;
    this.contentEquals = equals || Object.equals;
    this.getDefault = getDefault || Function.noop;
    this.length = 0;
    this.addEach(values);
}

Object.addEach(List.prototype, GenericCollection.prototype);
Object.addEach(List.prototype, GenericOrder.prototype);
Object.addEach(List.prototype, PropertyChanges.prototype);

List.prototype.constructClone = function (values) {
    return new this.constructor(values, this.contentEquals, this.getDefault);
};

List.prototype.find = function (value, equals) {
    equals = equals || this.contentEquals;
    var head = this.head;
    var at = head.next;
    while (at !== head) {
        if (equals(at.value, value)) {
            return at;
        }
        at = at.next;
    }
};

List.prototype.findLast = function (value, equals) {
    equals = equals || this.contentEquals;
    var head = this.head;
    var at = head.prev;
    while (at !== head) {
        if (equals(at.value, value)) {
            return at;
        }
        at = at.prev;
    }
};

List.prototype.has = function (value, equals) {
    return !!this.find(value, equals);
};

List.prototype.get = function (value, equals) {
    var found = this.find(value, equals);
    if (found) {
        return found.value;
    }
    return this.getDefault(value);
};

// LIFO (delete removes the most recently added equivalent value)
List.prototype['delete'] = function (value, equals) {
    var found = this.findLast(value, equals);
    if (found) {
        found['delete']();
        this.length--;
        return true;
    }
    return false;
};

List.prototype.clear = function () {
    var minus;
    this.head.next = this.head.prev = this.head;
    this.length = 0;
};

List.prototype.add = function (value) {
    this.head.addBefore(new this.Node(value));
    this.length++;
    return true;
};

List.prototype.push = function () {
    var head = this.head;
    for (var i = 0; i < arguments.length; i++) {
        var value = arguments[i];
        var node = new this.Node(value);
        head.addBefore(node);
        this.length++;
    }
};

List.prototype.unshift = function () {
    var at = this.head;
    for (var i = 0; i < arguments.length; i++) {
        var value = arguments[i];
        var node = new this.Node(value);
        at.addAfter(node);
        this.length++;
        at = node;
    }
};

List.prototype.pop = function () {
    var value;
    var head = this.head;
    if (head.prev !== head) {
        value = head.prev.value;
        head.prev['delete']();
        this.length--;
    }
    return value;
};

List.prototype.shift = function () {
    var value;
    var head = this.head;
    if (head.prev !== head) {
        value = head.next.value;
        head.next['delete']();
        this.length--;
    }
    return value;
};

List.prototype.peek = function () {
    if (this.head !== this.head.next) {
        return this.head.next.value;
    }
};

List.prototype.poke = function (value) {
    if (this.head !== this.head.next) {
        this.head.next.value = value;
    } else {
        this.push(value);
    }
};

List.prototype.one = function () {
    return this.peek();
};

// an internal utility for coercing index offsets to nodes
List.prototype.scan = function (at, fallback) {
    var head = this.head;
    if (typeof at === "number") {
        var count = at;
        if (count >= 0) {
            at = head.next;
            while (count) {
                count--;
                at = at.next;
                if (at == head) {
                    break;
                }
            }
        } else {
            at = head;
            while (count < 0) {
                count++;
                at = at.prev;
                if (at == head) {
                    break;
                }
            }
        }
        return at;
    } else {
        return at || fallback;
    }
};

// at and end may both be positive or negative numbers (in which cases they
// correspond to numeric indicies, or nodes)
List.prototype.slice = function (at, end) {
    var sliced = [];
    var head = this.head;
    at = this.scan(at, head.next);
    end = this.scan(end, head);

    while (at !== end && at !== head) {
        sliced.push(at.value);
        at = at.next;
    }

    return sliced;
};

List.prototype.splice = function (at, length /*...plus*/) {
    return this.swap(at, length, Array.prototype.slice.call(arguments, 2));
};

List.prototype.swap = function (at, length, plus) {
    var swapped = [];
    var initial = at;
    at = this.scan(at, this.head);
    if (length === undefined) {
        length = Infinity;
    }
    while (length-- && length >= 0 && at !== this.head) {
        swapped.push(at.value);
        at['delete']();
        at = at.next;
        this.length--;
    }
    if (plus) {
        if (initial === null && at === this.head) {
            at = this.head.next;
        }
        for (var i = 0; i < plus.length; i++) {
            var node = new this.Node(plus[i]);
            at.addBefore(node);
        }
        this.length += plus.length;
    }
    return swapped;
};

List.prototype.reverse = function () {
    var at = this.head;
    do {
        var temp = at.next;
        at.next = at.prev;
        at.prev = temp;
        at = at.next;
    } while (at !== this.head);
    return this;
};

// TODO account for missing basis argument
List.prototype.reduce = function (callback, basis /*, thisp*/) {
    var thisp = arguments[2];
    var head = this.head;
    var at = head.next;
    while (at !== head) {
        basis = callback.call(thisp, basis, at.value, at, this);
        at = at.next;
    }
    return basis;
};

List.prototype.reduceRight = function (callback, basis /*, thisp*/) {
    var thisp = arguments[2];
    var head = this.head;
    var at = head.prev;
    while (at !== head) {
        basis = callback.call(thisp, basis, at.value, at, this);
        at = at.prev;
    }
    return basis;
};

List.prototype.iterate = function () {
    return new ListIterator(this.head);
};

function ListIterator(head) {
    this.head = head;
    this.at = head.next;
};

ListIterator.prototype.next = function () {
    if (this.at === this.head) {
        throw StopIteration;
    } else {
        var value = this.at.value;
        this.at = this.at.next;
        return value;
    }
};

List.prototype.Node = Node;

function Node(value) {
    this.value = value;
    this.prev = null;
    this.next = null;
};

Node.prototype['delete'] = function () {
    this.prev.next = this.next;
    this.next.prev = this.prev;
};

Node.prototype.addBefore = function (node) {
    var prev = this.prev;
    this.prev = node;
    node.prev = prev;
    prev.next = node;
    node.next = this;
};

Node.prototype.addAfter = function (node) {
    var next = this.next;
    this.next = node;
    node.next = next;
    next.prev = node;
    node.prev = this;
};


}})
;
//*/
montageDefine("5ea2535","fast-set",{dependencies:["./shim","./dict","./list","./generic-collection","./generic-set","./tree-log","./listen/property-changes"],factory:function(require,exports,module){"use strict";

var Shim = require("./shim");
var Dict = require("./dict");
var List = require("./list");
var GenericCollection = require("./generic-collection");
var GenericSet = require("./generic-set");
var TreeLog = require("./tree-log");
var PropertyChanges = require("./listen/property-changes");

var object_has = Object.prototype.hasOwnProperty;

module.exports = FastSet;

function FastSet(values, equals, hash, getDefault) {
    if (!(this instanceof FastSet)) {
        return new FastSet(values, equals, hash, getDefault);
    }
    equals = equals || Object.equals;
    hash = hash || Object.hash;
    getDefault = getDefault || Function.noop;
    this.contentEquals = equals;
    this.contentHash = hash;
    this.getDefault = getDefault;
    this.buckets = new this.Buckets(null, this.Bucket);
    this.length = 0;
    this.addEach(values);
}

Object.addEach(FastSet.prototype, GenericCollection.prototype);
Object.addEach(FastSet.prototype, GenericSet.prototype);
Object.addEach(FastSet.prototype, PropertyChanges.prototype);

FastSet.prototype.Buckets = Dict;
FastSet.prototype.Bucket = List;

FastSet.prototype.constructClone = function (values) {
    return new this.constructor(
        values,
        this.contentEquals,
        this.contentHash,
        this.getDefault
    );
};

FastSet.prototype.has = function (value) {
    var hash = this.contentHash(value);
    return this.buckets.get(hash).has(value);
};

FastSet.prototype.get = function (value) {
    var hash = this.contentHash(value);
    var buckets = this.buckets;
    if (buckets.has(hash)) {
        return buckets.get(hash).get(value);
    } else {
        return this.getDefault(value);
    }
};

FastSet.prototype['delete'] = function (value) {
    var hash = this.contentHash(value);
    var buckets = this.buckets;
    if (buckets.has(hash)) {
        var bucket = buckets.get(hash);
        if (bucket["delete"](value)) {
            this.length--;
            if (bucket.length === 0) {
                buckets["delete"](hash);
            }
            return true;
        }
    }
    return false;
};

FastSet.prototype.clear = function () {
    this.buckets.clear();
    this.length = 0;
};

FastSet.prototype.add = function (value) {
    var hash = this.contentHash(value);
    var buckets = this.buckets;
    if (!buckets.has(hash)) {
        buckets.set(hash, new this.Bucket(null, this.contentEquals));
    }
    if (!buckets.get(hash).has(value)) {
        buckets.get(hash).add(value);
        this.length++;
        return true;
    }
    return false;
};

FastSet.prototype.reduce = function (callback, basis /*, thisp*/) {
    var thisp = arguments[2];
    var buckets = this.buckets;
    var index = 0;
    return buckets.reduce(function (basis, bucket) {
        return bucket.reduce(function (basis, value) {
            return callback.call(thisp, basis, value, index++, this);
        }, basis, this);
    }, basis, this);
};

FastSet.prototype.one = function () {
    if (this.length > 0) {
        return this.buckets.one().one();
    }
};

FastSet.prototype.iterate = function () {
    return this.buckets.values().flatten().iterate();
};

FastSet.prototype.log = function (charmap, logNode, callback, thisp) {
    charmap = charmap || TreeLog.unicodeSharp;
    logNode = logNode || this.logNode;
    if (!callback) {
        callback = console.log;
        thisp = console;
    }
    callback = callback.bind(thisp);

    var buckets = this.buckets;
    var hashes = buckets.keys();
    hashes.forEach(function (hash, index) {
        var branch;
        var leader;
        if (index === hashes.length - 1) {
            branch = charmap.fromAbove;
            leader = ' ';
        } else if (index === 0) {
            branch = charmap.branchDown;
            leader = charmap.strafe;
        } else {
            branch = charmap.fromBoth;
            leader = charmap.strafe;
        }
        var bucket = buckets.get(hash);
        callback.call(thisp, branch + charmap.through + charmap.branchDown + ' ' + hash);
        bucket.forEach(function (value, node) {
            var branch, below;
            if (node === bucket.head.prev) {
                branch = charmap.fromAbove;
                below = ' ';
            } else {
                branch = charmap.fromBoth;
                below = charmap.strafe;
            }
            var written;
            logNode(
                node,
                function (line) {
                    if (!written) {
                        callback.call(thisp, leader + ' ' + branch + charmap.through + charmap.through + line);
                        written = true;
                    } else {
                        callback.call(thisp, leader + ' ' + below + '  ' + line);
                    }
                },
                function (line) {
                    callback.call(thisp, leader + ' ' + charmap.strafe + '  ' + line);
                }
            );
        });
    });
};

FastSet.prototype.logNode = function (node, write) {
    var value = node.value;
    if (Object(value) === value) {
        JSON.stringify(value, null, 4).split("\n").forEach(function (line) {
            write(" " + line);
        });
    } else {
        write(" " + value);
    }
};


}})
;
//*/
montageDefine("5ea2535","generic-set",{dependencies:[],factory:function(require,exports,module){
module.exports = GenericSet;
function GenericSet() {
    throw new Error("Can't construct. GenericSet is a mixin.");
}

GenericSet.prototype.union = function (that) {
    var union =  this.constructClone(this);
    union.addEach(that);
    return union;
};

GenericSet.prototype.intersection = function (that) {
    return this.constructClone(this.filter(function (value) {
        return that.has(value);
    }));
};

GenericSet.prototype.difference = function (that) {
    var union =  this.constructClone(this);
    union.deleteEach(that);
    return union;
};

GenericSet.prototype.symmetricDifference = function (that) {
    var union = this.union(that);
    var intersection = this.intersection(that);
    return union.difference(intersection);
};

GenericSet.prototype.equals = function (that, equals) {
    var self = this;
    return (
        Object.can(that, "reduce") &&
        this.length === that.length &&
        that.reduce(function (equal, value) {
            return equal && self.has(value, equals);
        }, true)
    );
};

// W3C DOMTokenList API overlap (does not handle variadic arguments)

GenericSet.prototype.contains = function (value) {
    return this.has(value);
};

GenericSet.prototype.remove = function (value) {
    return this["delete"](value);
};

GenericSet.prototype.toggle = function (value) {
    if (this.has(value)) {
        this["delete"](value);
    } else {
        this.add(value);
    }
};


}})
;
//*/
montageDefine("5ea2535","listen/range-changes",{dependencies:["weak-map","../dict"],factory:function(require,exports,module){"use strict";

var WeakMap = require("weak-map");
var Dict = require("../dict");

var rangeChangeDescriptors = new WeakMap(); // {isActive, willChangeListeners, changeListeners}

module.exports = RangeChanges;
function RangeChanges() {
    throw new Error("Can't construct. RangeChanges is a mixin.");
}

RangeChanges.prototype.getAllRangeChangeDescriptors = function () {
    if (!rangeChangeDescriptors.has(this)) {
        rangeChangeDescriptors.set(this, Dict());
    }
    return rangeChangeDescriptors.get(this);
};

RangeChanges.prototype.getRangeChangeDescriptor = function (token) {
    var tokenChangeDescriptors = this.getAllRangeChangeDescriptors();
    token = token || "";
    if (!tokenChangeDescriptors.has(token)) {
        tokenChangeDescriptors.set(token, {
            isActive: false,
            changeListeners: [],
            willChangeListeners: []
        });
    }
    return tokenChangeDescriptors.get(token);
};

RangeChanges.prototype.addRangeChangeListener = function (listener, token, beforeChange) {
    // a concession for objects like Array that are not inherently observable
    if (!this.isObservable && this.makeObservable) {
        this.makeObservable();
    }

    var descriptor = this.getRangeChangeDescriptor(token);

    var listeners;
    if (beforeChange) {
        listeners = descriptor.willChangeListeners;
    } else {
        listeners = descriptor.changeListeners;
    }

    // even if already registered
    listeners.push(listener);
    this.dispatchesRangeChanges = true;

    var self = this;
    return function cancelRangeChangeListener() {
        if (!self) {
            // TODO throw new Error("Range change listener " + JSON.stringify(token) + " has already been canceled");
            return;
        }
        self.removeRangeChangeListener(listener, token, beforeChange);
        self = null;
    };
};

RangeChanges.prototype.removeRangeChangeListener = function (listener, token, beforeChange) {
    var descriptor = this.getRangeChangeDescriptor(token);

    var listeners;
    if (beforeChange) {
        listeners = descriptor.willChangeListeners;
    } else {
        listeners = descriptor.changeListeners;
    }

    var index = listeners.lastIndexOf(listener);
    if (index === -1) {
        throw new Error("Can't remove listener: does not exist.");
    }
    listeners.splice(index, 1);
};

RangeChanges.prototype.dispatchRangeChange = function (plus, minus, index, beforeChange) {
    var descriptors = this.getAllRangeChangeDescriptors();
    var changeName = "Range" + (beforeChange ? "WillChange" : "Change");
    descriptors.forEach(function (descriptor, token) {

        if (descriptor.isActive) {
            return;
        } else {
            descriptor.isActive = true;
        }

        // before or after
        var listeners;
        if (beforeChange) {
            listeners = descriptor.willChangeListeners;
        } else {
            listeners = descriptor.changeListeners;
        }

        var tokenName = "handle" + (
            token.slice(0, 1).toUpperCase() +
            token.slice(1)
        ) + changeName;
        // notably, defaults to "handleRangeChange" or "handleRangeWillChange"
        // if token is "" (the default)

        // dispatch each listener
        try {
            listeners.forEach(function (listener) {
                if (listener[tokenName]) {
                    listener[tokenName](plus, minus, index, this, beforeChange);
                } else if (listener.call) {
                    listener.call(this, plus, minus, index, this, beforeChange);
                } else {
                    throw new Error("Handler " + listener + " has no method " + tokenName + " and is not callable");
                }
            }, this);
        } finally {
            descriptor.isActive = false;
        }
    }, this);
};

RangeChanges.prototype.addBeforeRangeChangeListener = function (listener, token) {
    return this.addRangeChangeListener(listener, token, true);
};

RangeChanges.prototype.removeBeforeRangeChangeListener = function (listener, token) {
    return this.removeRangeChangeListener(listener, token, true);
};

RangeChanges.prototype.dispatchBeforeRangeChange = function (plus, minus, index) {
    return this.dispatchRangeChange(plus, minus, index, true);
};


}})
;
//*/
montageDefine("5ea2535","listen/map-changes",{dependencies:["weak-map","../list","../dict"],factory:function(require,exports,module){"use strict";

var WeakMap = require("weak-map");
var List = require("../list");

module.exports = MapChanges;
function MapChanges() {
    throw new Error("Can't construct. MapChanges is a mixin.");
}

var object_owns = Object.prototype.hasOwnProperty;

/*
    Object map change descriptors carry information necessary for adding,
    removing, dispatching, and shorting events to listeners for map changes
    for a particular key on a particular object.  These descriptors are used
    here for shallow map changes.

    {
        willChangeListeners:Array(Function)
        changeListeners:Array(Function)
    }
*/

var mapChangeDescriptors = new WeakMap();

MapChanges.prototype.getAllMapChangeDescriptors = function () {
    var Dict = require("../dict");
    if (!mapChangeDescriptors.has(this)) {
        mapChangeDescriptors.set(this, Dict());
    }
    return mapChangeDescriptors.get(this);
};

MapChanges.prototype.getMapChangeDescriptor = function (token) {
    var tokenChangeDescriptors = this.getAllMapChangeDescriptors();
    token = token || "";
    if (!tokenChangeDescriptors.has(token)) {
        tokenChangeDescriptors.set(token, {
            willChangeListeners: new List(),
            changeListeners: new List()
        });
    }
    return tokenChangeDescriptors.get(token);
};

MapChanges.prototype.addMapChangeListener = function (listener, token, beforeChange) {
    if (!this.isObservable && this.makeObservable) {
        // for Array
        this.makeObservable();
    }
    var descriptor = this.getMapChangeDescriptor(token);
    var listeners;
    if (beforeChange) {
        listeners = descriptor.willChangeListeners;
    } else {
        listeners = descriptor.changeListeners;
    }
    listeners.push(listener);
    this.dispatchesMapChanges = true;

    var self = this;
    return function cancelMapChangeListener() {
        if (!self) {
            // TODO throw new Error("Can't remove map change listener again");
            return;
        }
        self.removeMapChangeListener(listener, token, beforeChange);
        self = null;
    };
};

MapChanges.prototype.removeMapChangeListener = function (listener, token, beforeChange) {
    var descriptor = this.getMapChangeDescriptor(token);

    var listeners;
    if (beforeChange) {
        listeners = descriptor.willChangeListeners;
    } else {
        listeners = descriptor.changeListeners;
    }

    var node = listeners.findLast(listener);
    if (!node) {
        throw new Error("Can't remove listener: does not exist.");
    }
    node["delete"]();
};

MapChanges.prototype.dispatchMapChange = function (key, value, beforeChange) {
    var descriptors = this.getAllMapChangeDescriptors();
    var changeName = "Map" + (beforeChange ? "WillChange" : "Change");
    descriptors.forEach(function (descriptor, token) {

        if (descriptor.isActive) {
            return;
        } else {
            descriptor.isActive = true;
        }

        var listeners;
        if (beforeChange) {
            listeners = descriptor.willChangeListeners;
        } else {
            listeners = descriptor.changeListeners;
        }

        var tokenName = "handle" + (
            token.slice(0, 1).toUpperCase() +
            token.slice(1)
        ) + changeName;

        try {
            // dispatch to each listener
            listeners.forEach(function (listener) {
                if (listener[tokenName]) {
                    listener[tokenName](value, key, this);
                } else if (listener.call) {
                    listener.call(listener, value, key, this);
                } else {
                    throw new Error("Handler " + listener + " has no method " + tokenName + " and is not callable");
                }
            }, this);
        } finally {
            descriptor.isActive = false;
        }

    }, this);
};

MapChanges.prototype.addBeforeMapChangeListener = function (listener, token) {
    return this.addMapChangeListener(listener, token, true);
};

MapChanges.prototype.removeBeforeMapChangeListener = function (listener, token) {
    return this.removeMapChangeListener(listener, token, true);
};

MapChanges.prototype.dispatchBeforeMapChange = function (key, value) {
    return this.dispatchMapChange(key, value, true);
};


}})
;
//*/
montageDefine("5ea2535","tree-log",{dependencies:[],factory:function(require,exports,module){"use strict";

module.exports = TreeLog;

function TreeLog() {
}

TreeLog.ascii = {
    intersection: "+",
    through: "-",
    branchUp: "+",
    branchDown: "+",
    fromBelow: ".",
    fromAbove: "'",
    fromBoth: "+",
    strafe: "|"
};

TreeLog.unicodeRound = {
    intersection: "\u254b",
    through: "\u2501",
    branchUp: "\u253b",
    branchDown: "\u2533",
    fromBelow: "\u256d", // round corner
    fromAbove: "\u2570", // round corner
    fromBoth: "\u2523",
    strafe: "\u2503"
};

TreeLog.unicodeSharp = {
    intersection: "\u254b",
    through: "\u2501",
    branchUp: "\u253b",
    branchDown: "\u2533",
    fromBelow: "\u250f", // sharp corner
    fromAbove: "\u2517", // sharp corner
    fromBoth: "\u2523",
    strafe: "\u2503"
};


}})
;
//*/
montageDefine("5ea2535","dict",{dependencies:["./shim","./generic-collection","./generic-map","./listen/property-changes"],factory:function(require,exports,module){"use strict";

var Shim = require("./shim");
var GenericCollection = require("./generic-collection");
var GenericMap = require("./generic-map");
var PropertyChanges = require("./listen/property-changes");

// Burgled from https://github.com/domenic/dict

module.exports = Dict;
function Dict(values, getDefault) {
    if (!(this instanceof Dict)) {
        return new Dict(values, getDefault);
    }
    getDefault = getDefault || Function.noop;
    this.getDefault = getDefault;
    this.store = {};
    this.length = 0;
    this.addEach(values);
}

function mangle(key) {
    return "~" + key;
}

function unmangle(mangled) {
    return mangled.slice(1);
}

Object.addEach(Dict.prototype, GenericCollection.prototype);
Object.addEach(Dict.prototype, GenericMap.prototype);
Object.addEach(Dict.prototype, PropertyChanges.prototype);

Dict.prototype.constructClone = function (values) {
    return new this.constructor(values, this.mangle, this.getDefault);
};

Dict.prototype.assertString = function (key) {
    if (typeof key !== "string") {
        throw new TypeError("key must be a string.");
    }
}

Dict.prototype.get = function (key, defaultValue) {
    this.assertString(key);
    var mangled = mangle(key);
    if (mangled in this.store) {
        return this.store[mangled];
    } else if (arguments.length > 1) {
        return defaultValue;
    } else {
        return this.getDefault(key);
    }
};

Dict.prototype.set = function (key, value) {
    this.assertString(key);
    var mangled = mangle(key);
    if (mangled in this.store) { // update
        if (this.dispatchesBeforeMapChanges) {
            this.dispatchBeforeMapChange(key, this.store[mangled]);
        }
        this.store[mangled] = value;
        if (this.dispatchesMapChanges) {
            this.dispatchMapChange(key, undefined);
        }
        return false;
    } else { // create
        if (this.dispatchesMapChanges) {
            this.dispatchBeforeMapChange(key, undefined);
        }
        this.length++;
        this.store[mangled] = value;
        if (this.dispatchesMapChanges) {
            this.dispatchMapChange(key, value);
        }
        return true;
    }
};

Dict.prototype.has = function (key) {
    this.assertString(key);
    var mangled = mangle(key);
    return mangled in this.store;
};

Dict.prototype["delete"] = function (key) {
    this.assertString(key);
    var mangled = mangle(key);
    if (mangled in this.store) {
        if (this.dispatchesMapChanges) {
            this.dispatchBeforeMapChange(key, this.store[mangled]);
        }
        delete this.store[mangle(key)];
        this.length--;
        if (this.dispatchesMapChanges) {
            this.dispatchMapChange(key, undefined);
        }
        return true;
    }
    return false;
};

Dict.prototype.clear = function () {
    for (var mangled in this.store) {
        if (this.dispatchesMapChanges) {
            this.dispatchBeforeMapChange(key, this.store[mangled]);
        }
        delete this.store[mangled];
        if (this.dispatchesMapChanges) {
            this.dispatchMapChange(key, undefined);
        }
    }
    this.length = 0;
};

Dict.prototype.reduce = function (callback, basis, thisp) {
    for (var mangled in this.store) {
        basis = callback.call(thisp, basis, this.store[mangled], unmangle(mangled), this);
    }
    return basis;
};

Dict.prototype.reduceRight = function (callback, basis, thisp) {
    var self = this;
    var store = this.store;
    return Object.keys(this.store).reduceRight(function (basis, mangled) {
        return callback.call(thisp, basis, store[mangled], unmangle(mangled), self);
    }, basis);
};

Dict.prototype.one = function () {
    var key;
    for (var key in this.store) {
        return this.store[key];
    }
};


}})
;
//*/
montageDefine("3343e6d","deserialization/reviver",{dependencies:["q"],factory:function(require,exports,module){(function(ns) {
    var global = (function() { return this; })(),
        Promise = require("q");

    function Reviver() {

    }

    Object.defineProperties(Reviver.prototype, {
        _createAssignValueFunction: {
            value: function(object, propertyName) {
                return function(value) {
                    object[propertyName] = value;
                }
            }
        },

        getTypeOf: {
            value: function(value) {
                var typeOf = typeof value;

                if (value === null) {
                    return "null";
                } else if (Array.isArray(value)) {
                    return "array";
                } else if (typeOf === "object" && Object.keys(value).length === 1) {
                    if ("@" in value) {
                        return "reference";
                    } else if ("/" in value) {
                        return "regexp";
                    } else {
                        return "object";
                    }
                } else {
                    return typeOf;
                }
            }
        },

        getCustomObjectTypeOf: {
            writable: true,
            value: function() {}
        },

        reviveRootObject: {
            value: function(value, context, label) {
                var object;

                // Check if the optional "debugger" unit is set for this object
                // and stop the execution. This is intended to provide a certain
                // level of debugging in the serialization.
                if (value.debugger) {
                    debugger;
                }

                if ("value" in value) {

                    // it's overriden by a user object
                    if (context.hasUserObject(label)) {
                        object = context.getUserObject(label);
                        context.setObjectLabel(object, label);
                        return object;
                    } else {
                        return this.reviveValue(value.value, context, label);
                    }

                } else if (Object.keys(value).length === 0) {

                    // it's an external object
                    if (context.hasUserObject(label)) {
                        object = context.getUserObject(label);
                        context.setObjectLabel(object, label);
                        return object;
                    } else {
                        return this.reviveExternalObject(value, context, label);
                    }

                } else {

                    return this.reviveCustomObject(value, context, label);

                }
            }
        },

        reviveValue: {
            value: function(value, context, label) {
                var type = this.getTypeOf(value);

                if (type === "string" || type === "number" || type === "boolean" || type === "null" || type === "undefined") {
                    return this.reviveNativeValue(value, context, label);
                } else if (type === "regexp") {
                    return this.reviveRegExp(value, context, label);
                } else if (type === "reference") {
                    return this.reviveObjectReference(value, context, label);
                } else if (type === "array") {
                    return this.reviveArray(value, context, label);
                } else if (type === "object") {
                    return this.reviveObjectLiteral(value, context, label);
                } else {
                    return this._callReviveMethod("revive" + type, value, context, label);
                }
            }
        },

        reviveNativeValue: {
            value: function(value, context, label) {
                if (label) {
                    context.setObjectLabel(value, label);
                }

                return value;
            }
        },

        reviveObjectLiteral: {
            value: function(value, context, label) {
                var item,
                    promises = [];

                if (label) {
                    context.setObjectLabel(value, label);
                }

                for (var propertyName in value) {
                    item = this.reviveValue(value[propertyName], context);

                    if (Promise.isPromise(item)) {
                        promises.push(
                            item.then(this._createAssignValueFunction(
                                value, propertyName)
                            )
                        );
                    } else {
                        value[propertyName] = item;
                    }
                }

                if (promises.length === 0) {
                    return value;
                } else {
                    return Promise.all(promises).then(function() {
                        return value;
                    });
                }
            }
        },

        reviveRegExp: {
            value: function(value, context, label) {
                var value = value["/"],
                    regexp = new RegExp(value.source, value.flags);

                if (label) {
                    context.setObjectLabel(regexp, label);
                }

                return regexp;
            }
        },

        reviveObjectReference: {
            value: function(value, context, label) {
                var value = value["@"],
                    object = context.getObject(value);

                return object;
            }
        },

        reviveArray: {
            value: function(value, context, label) {
                var item,
                    promises = [];

                if (label) {
                    context.setObjectLabel(value, label);
                }

                for (var i = 0, ii = value.length; i < ii; i++) {
                    item = this.reviveValue(value[i], context);

                    if (Promise.isPromise(item)) {
                        promises.push(
                            item.then(this._createAssignValueFunction(value, i))
                        );
                    } else {
                        value[i] = item;
                    }
                }

                if (promises.length === 0) {
                    return value;
                } else {
                    return Promise.all(promises).then(function() {
                        return value;
                    });
                }
            }
        },

        reviveCustomObject: {
            value: function(value, context, label) {
                var type = this.getCustomObjectTypeOf(value),
                    method = customObjectRevivers["revive" + type];

                if (type) {
                    return method.call(global, value, context, label);
                } else {
                    return Promise.reject(
                        new Error("Object's type is unknown: " + JSON.stringify(value))
                    );
                }
            }
        },

        reviveExternalObject: {
            value: function(value, context, label) {
                return Promise.reject(
                    new Error("External object '" + label + "' not found in user objects.")
                );
            }
        },

        _callReviveMethod: {
            value: function(methodName, value, context, label) {
                return this[methodName](value, context, label);
            }
        }
    });


    /**
     * Custom Object Revivers
     */
    var customObjectRevivers = Object.create(null);

    function makeGetCustomObjectTypeOf(getCustomObjectTypeOf) {
        var previousGetCustomObjectTypeOf = Reviver.prototype.getCustomObjectTypeOf;

        return function(value) {
            return getCustomObjectTypeOf(value) || previousGetCustomObjectTypeOf(value);
        }
    }

    // reviver needs to be of type:
    // {
    //     getTypeOf: function(value) -> "<Type>",
    //     revive<Type>: function(value, context, label) -> object | Promise
    // }
    Reviver.addCustomObjectReviver = function(reviver) {
        for (var methodName in reviver) {
            if (methodName === "getTypeOf") {
                continue;
            }

            if (typeof reviver[methodName] === "function"
                && /^revive/.test(methodName)) {
                if (typeof customObjectRevivers[methodName] === "undefined") {
                    customObjectRevivers[methodName] = reviver[methodName].bind(reviver);
                } else {
                    return new Error("Reviver '" + methodName + "' is already registered.");
                }
            }
        }

        this.prototype.getCustomObjectTypeOf = makeGetCustomObjectTypeOf(reviver.getTypeOf);
    };

    Reviver.resetCustomObjectRevivers = function() {
        customObjectRevivers = Object.create(null);
        this.prototype.getCustomObjectTypeOf = function() {};
    };

    ns.Reviver = Reviver;
})(exports);

if (!Array.isArray) {
    Array.isArray = function(obj) {
        return Object.prototype.toString.call(obj) === "[object Array]";
    }
}

}})
;
//*/
montageDefine("3343e6d","serialization/malker",{dependencies:[],factory:function(require,exports,module){(function(ns) {
    function Malker(handlers) {
        this._handlers = handlers;
        this._enteredObjects = {};
    }

    Object.defineProperties(Malker.prototype, {
        _handlers: {value: null, writable: true},
        _enteredObjects: {value: null, writable: true},

        _isObjectEntered: {
            value: function(object) {
                return Object.hash(object) in this._enteredObjects;
            }
        },

        _markObjectAsEntered: {
            value: function(object) {
                this._enteredObjects[Object.hash(object)] = true;
            }
        },

        visit: {
            value: function(value, name) {
                this._visitValue(value, name);
            },
            enumerable: true
        },

        _getTypeOf: {
            value: function(value) {
                if (Array.isArray(value)) {
                    return "array";
                } else if (RegExp.isRegExp(value)) {
                    return "regexp";
                } else if (value === null) {
                    return "null";
                } else if (typeof value === "object" || typeof value === "function") {
                    return this._getObjectType(value);
                } else {
                    return typeof value;
                }
            }
        },

        _getObjectType: {
            value: function(object) {
                var visitor = this._handlers,
                    type;

                if (typeof visitor.getTypeOf === "function") {
                    type = visitor.getTypeOf(object);
                }

                if (typeof type === "undefined") {
                    return typeof object;
                } else {
                    return type;
                }
            }
        },

        _visitValue: {
            value: function(value, name) {
                var type = this._getTypeOf(value);

                if (type === "object") {
                    this._visitObject(value, name);
                } else if (type === "array") {
                    this._visitArray(value, name);
                } else if (type === "regexp") {
                    this._visitRegExp(value, name);
                } else if (type === "number") {
                    this._visitNumber(value, name);
                } else if (type === "string") {
                    this._visitString(value, name);
                } else if (type === "boolean") {
                    this._visitBoolean(value, name);
                } else if (type === "null") {
                    this._visitNull(name);
                } else if (type === "undefined") {
                    this._visitUndefined(name);
                } else {
                    this._visitCustomType(type, value, name);
                }
            }
        },

        _visitCustomType: {
            value: function(type, object, name) {
                this._callVisitorMethod("visit" + type, object, name);
            }
        },

        _enterCustomObject: {
            value: function(type, object, name) {
                this._callVisitorMethod("enter" + type, object, name);
                this._callVisitorMethod("exit" + type, object, name);
            }
        },

        _visitObject: {
            value: function(object, name) {
                var willEnterObject;

                if (this._isObjectEntered(object)) {
                    this._callVisitorMethod("visitObject", object, name);
                } else {
                    willEnterObject = this._callVisitorMethod("willEnterObject", object, name);
                    if (willEnterObject !== false) {
                        this._markObjectAsEntered(object);
                        this._enterObject(object, name);
                    }
                }
            }
        },

        _enterObject: {
            value: function(object, name) {
                var keys = Object.keys(object),
                    key;

                this._callVisitorMethod("enterObject", object, name);

                for (var i = 0, ii = keys.length; i < ii; i++) {
                    key = keys[i];
                    this._visitValue(object[key], key);
                }

                this._callVisitorMethod("exitObject", object, name);
            }
        },

        _visitArray: {
            value: function(array, name) {
                var willEnterArray;

                if (this._isObjectEntered(array)) {
                    this._callVisitorMethod("visitArray", array, name);
                } else {
                    willEnterArray = this._callVisitorMethod("willEnterArray", array, name);
                    if (willEnterArray !== false) {
                        this._markObjectAsEntered(array);
                        this._enterArray(array, name);
                    }
                }
            }
        },

        _enterArray: {
            value: function(array, name) {
                this._callVisitorMethod("enterArray", array, name);

                for (var i = 0, ii = array.length; i < ii; i++) {
                    this._visitValue(array[i], ""+i);
                }

                this._callVisitorMethod("exitArray", array, name);
            }
        },

        _visitRegExp: {
            value: function(regexp, name) {
                this._callVisitorMethod("visitRegExp", regexp, name);
            }
        },

        _visitString: {
            value: function(string, name) {
                this._callVisitorMethod("visitString", string, name);
            }
        },

        _visitNumber: {
            value: function(number, name) {
                this._callVisitorMethod("visitNumber", number, name);
            }
        },

        _visitBoolean: {
            value: function(boolean, name) {
                this._callVisitorMethod("visitBoolean", boolean, name);
            }
        },

        _visitNull: {
            value: function(name) {
                this._callVisitorMethod("visitNull", name);
            }
        },

        _visitUndefined: {
            value: function(name) {
                this._callVisitorMethod("visitUndefined", name);
            }
        },

        _callVisitorMethod: {
            value: function(methodName /*, args... */) {
                var visitor = this._handlers,
                    args;

                if (typeof visitor[methodName] === "function") {
                    args = Array.prototype.slice.call(arguments, 1);
                    // the first parameter of the handler function is always the malker
                    args.unshift(this);

                    return visitor[methodName].apply(
                        visitor,
                        args);
                }
            }
        }
    });

    if (!Array.isArray) {
        Array.isArray = function(obj) {
            return Object.prototype.toString.call(obj) === "[object Array]";
        }
    }

    if (!RegExp.isRegExp) {
        RegExp.isRegExp = function(obj) {
            return Object.prototype.toString.call(obj) === "[object RegExp]";
        }
    }

    ns.Malker = Malker;
    ns.visit = function(object, handlers) {
        var malker = new Malker(handlers);
        malker.visit(object);
    };
})(exports);
}})
;
//*/
montageDefine("3343e6d","serialization/serializer",{dependencies:["./malker","./builder","./labeler","./visitor"],factory:function(require,exports,module){(function(ns) {
    var Malker = require("./malker").Malker,
        Builder = require("./builder").Builder,
        Labeler = require("./labeler").Labeler,
        Visitor = require("./visitor").Visitor;

    function Serializer() {
        var visitor;

        this._builder = new Builder();
        this._labeler = new Labeler();
        visitor = new Visitor(this._builder, this._labeler);

        this._malker = new Malker(visitor);
    }

    Object.defineProperties(Serializer.prototype, {
        _labeler: {value: null, writable: true},
        _builder: {value: null, writable: true},
        _serializationIndentation: {value: 2, writable: true},
        _malker: {value: null, writable: true},

        setSerializationIndentation: {
            value: function(indentation) {
                this._serializationIndentation = indentation;
            }
        },

        serialize: {
            value: function(objects) {
                var serializationString;

                this._labeler.initWithObjects(objects);

                for (var label in objects) {
                    this._malker.visit(objects[label]);
                }

                serializationString = this._formatSerialization(
                    this._builder.getSerialization(
                        this._serializationIndentation
                    )
                );

                return serializationString;
            }
        },

        serializeObject: {
            value: function(object) {
                return this.serialize({root: object});
            }
        },

        _formatSerializationBindingsRegExp: {
            value: /\{\s*("(?:<->?)")\s*:\s*("[^"]+"\s*(?:,\s*"converter"\s*:\s*\{\s*"@"\s*:\s*"[^"]+"\s*\}\s*|,\s*"deferred"\s*:\s*(true|false)\s*)*)\}/gi
        },
        _formatSerializationBindingsReplacer: {
            value: function(_, g1, g2) {
                return '{' + g1 + ': ' +
                            g2.replace(/\n\s*/g, "").replace(/,\s*/g, ", ") +
                       '}';
            }
        },
        _formatSerializationBindings: {
            value: function(serialization) {
                return serialization.replace(
                    this._formatSerializationBindingsRegExp,
                    this._formatSerializationBindingsReplacer);
            }
        },

        _formatSerializationReferencesRegExp: {
            value: /\{\s*("[#@]")\s*:\s*("[^"]+")\s*\}/gi
        },
        _formatSerializationReferences: {
            value: function(serialization) {
                return serialization.replace(
                    this._formatSerializationReferencesRegExp, "{$1: $2}");
            }
        },

        _formatSerialization: {
            value: function(serialization) {
                return this._formatSerializationBindings(
                        this._formatSerializationReferences(serialization));
            }
        }
    });

    ns.Serializer = Serializer;
    ns.serialize = function(object) {
        return new Serializer().serializeObject(object);
    };
})(exports);

}})
;
//*/
montageDefine("3343e6d","deserialization/interpreter",{dependencies:["./reviver","./context"],factory:function(require,exports,module){(function(ns) {
    var Reviver = require("./reviver").Reviver,
        Context = require("./context").Context;

    function Interpreter() {

    }

    Object.defineProperties(Interpreter.prototype, {
        instantiate: {
            value: function(serialization, objects) {
                var reviver = new Reviver(),
                    context = new Context(serialization, reviver, objects);

                return context.getObjects();
            }
        }
    });

    ns.Interpreter = Interpreter;
})(exports);

}})
;
//*/
montageDefine("3343e6d","deserialization/deserializer",{dependencies:["q","./interpreter"],factory:function(require,exports,module){(function(ns) {
    var Promise = require("q"),
        Interpreter = require("./interpreter").Interpreter;

    function Deserializer(serializationString) {
        this._serializationString = serializationString;
    }

    Object.defineProperties(Deserializer.prototype, {
        _interpreter: {value: new Interpreter()},
        _serializationString: {value: null, writable: true},

        deserialize: {
            value: function(objects) {
                var serialization;

                try {
                    serialization = JSON.parse(this._serializationString);
                } catch (error) {
                    return Promise.reject(error);
                }

                return this._interpreter.instantiate(serialization, objects);
            }
        },

        deserializeObject: {
            value: function(objects) {
                return this.deserialize(objects).then(function(objects) {
                    return objects.root;
                });
            }
        }
    });

    function deserialize(serializationString) {
        return new Deserializer(serializationString).deserializeObject();
    };

    ns.Deserializer = Deserializer;
    ns.deserialize = deserialize;
})(exports);

}})
;
//*/
montageDefine("3343e6d","serialization/builder",{dependencies:["./ast"],factory:function(require,exports,module){(function(ns) {
    var ast = require("./ast");

    function Builder() {
        this.init();
    }

    Object.defineProperties(Builder.prototype, {
        _root: {value: null, writable: true},
        _stack: {value: null, writable: true},
        // _references structure is:
        // {
        //     <label>: [<reference>*]
        // }
        _references: {value: null, writable: true},
        _placeholderProperty: {value: Object.create(null)},

        init: {
            value: function() {
                this._references = Object.create(null);
                this._root = new ast.Root();
                this._stack = [this._root];

                return this;
            }
        },

        cleanup: {
            value: function() {
                this._references = null;
                this._root = null;
                this._stack = null;
            }
        },

        getExternalReferences: {
            value: function(oldLabel, newLabel) {
                var references = this._references,
                    root = this._root,
                    externalReferences = [];

                for (var label in references) {
                    // placeholder properties are not created at reference creation
                    // time, so we need to check for both states, before a
                    // placeholder is created and after.
                    if (!root.hasProperty(label) ||
                        root.getProperty(label) === this._placeholderProperty) {
                        externalReferences.push(label);
                    }
                }

                return externalReferences;
            }
        },

        relabelReferences: {
            value: function(oldLabel, newLabel) {
                var references = this._references[oldLabel];

                if (references) {
                    references = references.slice(0);
                    for (var i = 0, ii = references.length; i < ii; i++) {
                        references[i].value = newLabel;
                    }
                }
            }
        },

        _registerReference: {
            value: function(reference) {
                var references = this._references,
                    label = reference.value;

                if (references[label]) {
                    references[label].push(reference);
                } else {
                    references[label] = [reference];
                }
            }
        },

        _unregisterReference: {
            value: function(reference) {
                var label = reference.label,
                    labelReferences = this._references[label],
                    ix;

                if (labelReferences.length === 1) {
                    delete this._references[label];
                } else {
                    ix = labelReferences.indexOf(reference);

                    if (ix === -1) {
                        console.log("BUG: reference '" + label + "' not found in registry.");
                    } else {
                        labelReferences.splice(ix, 1);
                    }
                }
            }
        },

        _createPlaceholdersForReferences: {
            value: function() {
                var references = this._references,
                    root = this._root;

                for (var label in references) {
                    if (!root.hasProperty(label)) {
                        root.setProperty(label, this._placeholderProperty);
                    }
                }
            }
        },

        getSerialization: {
            value: function(indent) {
                this._createPlaceholdersForReferences();

                return this._root.serialize(indent);
            }
        },

        root: {
            get: function() {
                return this._root;
            }
        },

        top: {
            get: function() {
                return this._stack[0];
            }
        },

        push: {
            value: function(value) {
                return this._stack.unshift(value);
            }
        },

        pop: {
            value: function() {
                return this._stack.shift();
            }
        },

        createObjectLiteral: {
            value: function() {
                return new ast.ObjectLiteral(this._root, Object.create(null));
            }
        },

        createArray: {
            value: function() {
                return new ast.ObjectLiteral(this._root, []);
            }
        },

        createObjectReference: {
            value: function(label) {
                var reference = new ast.ObjectReference(this._root, label);

                this._registerReference(reference);

                return reference;
            }
        },

        createRegExp: {
            value: function(regexp) {
                return new ast.RegExpObject(this._root, regexp);
            }
        },

        createString: {
            value: function(string) {
                return new ast.Value(this._root, string);
            }
        },

        createNumber: {
            value: function(number) {
                return new ast.Value(this._root, number);
            }
        },

        createBoolean: {
            value: function(boolean) {
                return new ast.Value(this._root, boolean);
            }
        },

        createNull: {
            value: function(boolean) {
                return new ast.Value(this._root, null);
            }
        },

        createCustomObject: {
            value: function() {
                return new ast.CustomObject(this._root);
            }
        }
    });

    ns.Builder = Builder;
})(exports);
}})
;
//*/
montageDefine("3343e6d","serialization/labeler",{dependencies:[],factory:function(require,exports,module){(function(ns) {
    function Labeler() {
        this._objectsLabels = Object.create(null);
        this._objects = Object.create(null);
        this._objectNamesIndex = Object.create(null);
        this._userDefinedLabels = Object.create(null);
    }

    Object.defineProperties(Labeler.prototype, {
        // hash(object) -> label
        _objectsLabels: {value: null, writable: true},
        _objects: {value: null, writable: true},
        // Labels generation sequence is "label", "label2", "label3", ..., hence
        // starting at 2.
        _INITIAL_LABEL_NUMBER: {value: 2},
        _objectNamesIndex: {value: null, writable: true},
        _userDefinedLabels: {value: null, writable: true},

        initWithObjects: {
            value: function(labels) {
                for (var label in labels) {
                    this.setObjectLabel(labels[label], label);
                    this._userDefinedLabels[label] = true;
                }
            }
        },

        cleanup: {
            value: function() {
                this._objectsLabels = null;
                this._objects = null;
                this._objectNamesIndex = null;
                this._userDefinedLabels = null;
            }
        },

        getObjectName: {
            value: function(object) {
                if (Array.isArray(object)) {
                    return "array";
                } else if (RegExp.isRegExp(object)) {
                    return "regexp";
                } else {
                    return "object";
                }
            }
        },

        generateObjectLabel: {
            value: function(object) {
                var objectName = this.getObjectName(object),
                    index = this._objectNamesIndex[objectName],
                    objectLabel;

                do {
                    if (index) {
                        objectLabel = objectName + index;
                        this._objectNamesIndex[objectName] = index = index + 1;
                    } else {
                        objectLabel = objectName;
                        this._objectNamesIndex[objectName] = index = this._INITIAL_LABEL_NUMBER;
                    }
                } while (objectLabel in this._objects);

                return objectLabel;
            }
        },

        getObjectLabel: {
            value: function(object) {
                var hash = Object.hash(object),
                    label;

                if (hash in this._objectsLabels) {
                    label = this._objectsLabels[hash];
                } else {
                    label = this.generateObjectLabel(object);

                    this._objectsLabels[hash] = label;
                    this._objects[label] = object;
                }

                return label;
            }
        },

        setObjectLabel: {
            value: function(object, label) {
                if (typeof object !== "undefined") {
                    var hash = Object.hash(object);

                    this._objectsLabels[hash] = label;
                    this._objects[label] = object;
                }
            }
        },

        getObjectByLabel: {
            value: function(label) {
                return this._objects[label];
            }
        },

        isUserDefinedLabel: {
            value: function(label) {
                return label in this._userDefinedLabels;
            }
        }
    });

    ns.Labeler = Labeler;
})(exports);

}})
;
//*/
montageDefine("3343e6d","serialization/visitor",{dependencies:["collections/shim-object"],factory:function(require,exports,module){(function(ns) {
    // Provides Object.hash
    require("collections/shim-object");

    function Visitor(builder, labeler) {
        this.builder = builder;
        this.labeler = labeler;
        this._objectsSerialization = Object.create(null);
    }

    Object.defineProperties(Visitor.prototype, {
        builder: {value: null, writable: true},
        labeler: {value: null, writable: true},

        getTypeOf: {
            value: function(object) {
                if (this.isCustomObject(object)) {
                    return "CustomObject";
                }
            }
        },

        getCustomObjectTypeOf: {
            value: function() {},
            writable: true
        },

        isCustomObject: {
            value: function(object) {
                var type = this.getCustomObjectTypeOf(object);

                return typeof type === "string";
            }
        },

        _objectsSerialization: {value: null, writable: true},
        setObjectSerialization: {
            value: function(object, serialization) {
                this._objectsSerialization[Object.hash(object)] = serialization;
            }
        },

        getObjectSerialization: {
            value: function(object) {
                return this._objectsSerialization[Object.hash(object)];
            }
        },

        isObjectSerialized: {
            value: function(object) {
                return Object.hash(object) in this._objectsSerialization;
            }
        },

        enterObject: {
            value: function(malker, object, name) {
                var builderObject = this.builder.createObjectLiteral();

                this.setObjectSerialization(object, builderObject);
                this.builder.push(builderObject);
            }
        },

        exitObject: {
            value: function(malker, object, name) {
                this.storeValue(this.builder.pop(), object, name);
            }
        },

        visitObject: {
            value: function(malker, object, name) {
                var label = this.labeler.getObjectLabel(object),
                    reference = this.builder.createObjectReference(label);

                // visitObject is only called after the object has been entered
                // and serialized, if we're visiting it then label the serialization
                // because we need to create a reference to it now.
                this.getObjectSerialization(object).setLabel(label);
                this.builder.top.setProperty(name, reference);
            }
        },

        enterArray: {
            value: function(malker, array, name) {
                var builderObject = this.builder.createArray();

                this.setObjectSerialization(array, builderObject);
                this.builder.push(builderObject);
            }
        },

        exitArray: {
            value: function(malker, array, name) {
                this.storeValue(this.builder.pop(), array, name);
            }
        },

        visitArray: {
            value: function(malker, array, name) {
                var label = this.labeler.getObjectLabel(array),
                    reference = this.builder.createObjectReference(label);

                // visitArray is only called after the array has been entered
                // and serialized, if we're visiting it then label the serialization
                // because we need to create a reference to it now.
                this.getObjectSerialization(array).setLabel(label);
                this.builder.top.setProperty(name, reference);
            }
        },

        visitRegExp: {
            value: function(malker, regexp, name) {
                this.storeValue(this.builder.createRegExp(regexp), regexp, name);
            }
        },

        visitString: {
            value: function(malker, string, name) {
                this.storeValue(this.builder.createString(string), string, name);
            }
        },

        visitNumber: {
            value: function(malker, number, name) {
                this.storeValue(this.builder.createNumber(number), number, name);
            }
        },

        visitBoolean: {
            value: function(malker, boolean, name) {
                this.storeValue(this.builder.createBoolean(boolean), boolean, name);
            }
        },

        visitNull: {
            value: function(malker, name) {
                this.storeValue(this.builder.createNull(), null, name);
            }
        },

        visitCustomObject: {
            value: function(malker, object, name) {
                var type = this.getCustomObjectTypeOf(object),
                    method = customObjectVisitors["visit" + type];

                if (type) {
                    return method.call(global, malker, this, object, name);
                } else {
                    throw new Error("Object's type is unknown: " + object);
                }
            }
        },

        storeValue: {
            value: function(value, object, name) {
                // if the object has no name then give it a label otherwise it
                // won't be part of the serialization
                if (typeof name === "undefined") {
                    value.setLabel(this.labeler.getObjectLabel(object));
                } else {
                    this.builder.top.setProperty(name, value);
                }
            }
        }
    });

    /*
     * Custom Object Visitors
     */
    var customObjectVisitors = Object.create(null);

    function makeGetCustomObjectTypeOf(getCustomObjectTypeOf) {
        var previousGetCustomObjectTypeOf = Visitor.prototype.getCustomObjectTypeOf;

        return function(value) {
            return getCustomObjectTypeOf(value) ||
                   previousGetCustomObjectTypeOf(value);
        }
    }

    // visitor needs to be of type:
    // {
    //     getTypeOf: function(value) -> "<Type>",
    //     visit<Type>: function(malker, visitor, object, name)
    // }
    Visitor.addCustomObjectVisitor = function(visitor) {
        for (var methodName in visitor) {
            if (methodName === "getTypeOf") {
                continue;
            }

            if (typeof visitor[methodName] === "function"
                && /^visit/.test(methodName)) {
                if (typeof customObjectVisitors[methodName] === "undefined") {
                    customObjectVisitors[methodName] = visitor[methodName].bind(visitor);
                } else {
                    return new Error("Visitor '" + methodName + "' is already registered.");
                }
            }
        }

        this.prototype.getCustomObjectTypeOf = makeGetCustomObjectTypeOf(visitor.getTypeOf);
    };

    Visitor.resetCustomObjectVisitors = function() {
        customObjectVisitors = Object.create(null);
        this.prototype.getCustomObjectTypeOf = function() {};
    };

    ns.Visitor = Visitor;
})(exports);
}})
;
//*/
montageDefine("3343e6d","deserialization/context",{dependencies:["q"],factory:function(require,exports,module){var Promise = require("q");

(function(ns) {
    function Context(serialization, reviver, objects) {
        this._reviver = reviver;
        this._serialization = serialization;
        this._objects = Object.create(null);

        if (objects) {
            this._userObjects = Object.create(null);

            for (var label in objects) {
                this._userObjects[label] = objects[label];
            }
        }
    }

    Object.defineProperties(Context.prototype, {
        _objects: {value: null, writable: true},
        _userObjects: {value: null, writable: true},
        _serialization: {value: null, writable: true},
        _reviver: {value: null, writable: true},

        setObjectLabel: {
            value: function(object, label) {
                this._objects[label] = object;
            }
        },

        getObject: {
            value: function(label) {
                var serialization = this._serialization,
                    reviver = this._reviver,
                    objects = this._objects,
                    object;

                if (label in objects) {
                    return objects[label];
                } else if (label in serialization) {
                    object = reviver.reviveRootObject(serialization[label], this, label);
                    // If no object has been set by the reviver we safe its
                    // return, it could be a value or a promise, we need to
                    // make sure the object won't be revived twice.
                    if (!(label in objects)) {
                        objects[label] = object;
                    }

                    return object;
                } else {
                    return Promise.reject(
                        new Error("Object with label '" + label + "' was not found.")
                    );
                }
            }
        },

        getObjects: {
            value: function() {
                var self = this,
                    reviver = this._reviver,
                    serialization = this._serialization,
                    promises = [],
                    result;

                for (var label in serialization) {
                    result = this.getObject(label);

                    if (Promise.isPromise(result)) {
                        promises.push(result);
                    }
                }

                if (promises.length === 0) {
                    return Promise.resolve(this._invokeDidReviveObjects());
                } else {
                    return Promise.all(promises).then(function() {
                        return self._invokeDidReviveObjects();
                    });
                }
            }
        },

        hasUserObject: {
            value: function(label) {
                var userObjects = this._userObjects;

                if (userObjects) {
                    return label in userObjects;
                } else {
                    return false;
                }
            }
        },

        getUserObject: {
            value: function(label) {
                var userObjects = this._userObjects;

                if (userObjects) {
                    return userObjects[label];
                }
            }
        },

        _invokeDidReviveObjects: {
            value: function() {
                var self = this,
                    reviver = this._reviver,
                    result;

                if (typeof reviver.didReviveObjects === "function") {
                    result = reviver.didReviveObjects(this._objects, this);
                    if (Promise.isPromise(result)) {
                        return result.then(function() {
                            return self._objects;
                        });
                    }
                }

                return this._objects;
            }
        }
    });

    ns.Context = Context;
})(exports);
}})
;
//*/
montageDefine("3343e6d","serialization/ast",{dependencies:[],factory:function(require,exports,module){/**
 * Node Hierarchy:
 *
 * +-Root
 * +-Value
 *   +-ReferenceableValue
 *     +-ObjectLiteral
 *     +-RegExpObject
 *   +-CustomObject
 *   +-ObjectReference
 */

(function(ns) {
    /**
     * Root
     */
    function Root() {
        this.object = Object.create(null);
    }

    Object.defineProperties(Root.prototype, {
        object: {value: null, writable: true},

        setProperty: {
            value: function(name, value) {
                if (name != null) {
                    this.object[name] = value;
                }
            }
        },

        getProperty: {
            value: function(name) {
                return this.object[name];
            }
        },

        clearProperty: {
            value: function(name) {
                delete this.object[name];
            }
        },

        hasProperty: {
            value: function(name) {
                return name in this.object;
            }
        },

        serialize: {
            value: function(indent) {
                return JSON.stringify(this, null, indent);
            }
        },

        toJSON: {
            value: function() {
                var result = Object.create(null),
                    object;

                for (var label in this.object) {
                    object = this.object[label];

                    if (object.toJSON) {
                        result[label] = object.toJSON(1);
                    } else {
                        result[label] = object;
                    }
                }

                return result;
            }
        }
    });

    /**
     * Value
     */
    function Value(root, value) {
        this.root = root;
        this.value = value;
    }

    Object.defineProperties(Value.prototype, {
        root: {value: null, writable: true},
        label: {value: null, writable: true},
        value: {value: null, writable: true},

        setLabel: {
            value: function(label) {
                if (this.label) {
                    this.root.clearProperty(this.label);
                }

                this.label = label;
                this.root.setProperty(label, this);
            }
        },

        getLabel: {
            value: function() {
                return this.label;
            }
        },

        clearLabel: {
            value: function() {
                this.root.clearProperty(this.label);
                this.label = null;
            }
        },

        _getSerializationValue: {
            value: function() {
                return this.value;
            }
        },

        toJSON: {
            value: function(level) {
                var value = this._getSerializationValue();

                if (level === 1) {
                    return {value: value};
                } else {
                    return value;
                }
            }
        }
    });

    /**
     * ReferenceableValue
     *
     * @extends Value
     */
    function ReferenceableValue(root, value) {
        Value.call(this, root, value);
    }

    ReferenceableValue.prototype = Object.create(Value.prototype, {
        constructor: {value: ReferenceableValue},

        toJSON: {
            value: function(level) {
                var reference,
                    value = this._getSerializationValue();

                if (level === 1) {
                    return {value: value};
                } else if (this.label) {
                    reference = new ObjectReference(this.root, this.label);
                    return reference.toJSON();
                } else {
                    return value;
                }
            }
        }
    });

    /**
     * ObjectLiteral
     *
     * @extends ReferenceableValue
     */
    function ObjectLiteral(root, object) {
        Value.call(this, root, object);
    }

    ObjectLiteral.prototype = Object.create(ReferenceableValue.prototype, {
        constructor: {value: ObjectLiteral},

        setProperty: {
            value: function(name, value) {
                if (name != null) {
                    this.value[name] = value;
                }
            }
        },

        getProperty: {
            value: function(name) {
                return this.value[name];
            }
        },

        clearProperty: {
            value: function(name) {
                delete this.value[name];
            }
        },

        getPropertyNames: {
            value: function() {
                return Object.keys(this.value);
            }
        }
    });

    /**
     * RegExpObject
     *
     * @extends ReferenceableValue
     */
    function RegExpObject(root, regexp) {
        Value.call(this, root, regexp);
    }
    RegExpObject.prototype = Object.create(ReferenceableValue.prototype, {
        constructor: {value: RegExpObject},

        _getSerializationValue: {
            value: function() {
                var regexp = this.value;

                return {"/": {
                    source: regexp.source,
                    flags: (regexp.global ? "g" : "") + (regexp.ignoreCase ? "i" : "") + (regexp.multiline ? "m" : "")
                }};
            }
        }
    });

    /**
     * ObjectLiteral
     *
     * @extends Value
     */
    function CustomObject(root) {
        Value.call(this, root, Object.create(null));
    }

    CustomObject.prototype = Object.create(Value.prototype, {
        constructor: {value: CustomObject},

        setProperty: {
            value: function(name, value) {
                if (name != null) {
                    this.value[name] = value;
                }
            }
        },

        getProperty: {
            value: function(name) {
                return this.value[name];
            }
        },

        clearProperty: {
            value: function(name) {
                delete this.value[name];
            }
        },

        toJSON: {
            value: function(level) {
                var reference,
                    value = this._getSerializationValue();

                if (level === 1) {
                    return value;
                } else {
                    reference = new ObjectReference(this.root, this.label);

                    return reference.toJSON();
                }
            }
        }
    });

    /**
     * ObjectReference
     *
     * @extends Value
     */
    function ObjectReference(root, referenceLabel) {
        Value.call(this, root, referenceLabel);
    }

    ObjectReference.prototype = Object.create(Value.prototype, {
        constructor: {value: ObjectReference},

        _getSerializationValue: {
            value: function() {
                return {"@": this.value};
            }
        }
    });

    ns.Root = Root;
    ns.Value = Value;
    ns.ReferenceableValue = ReferenceableValue;
    ns.ObjectLiteral = ObjectLiteral;
    ns.RegExpObject = RegExpObject;
    ns.CustomObject = CustomObject;
    ns.ObjectReference = ObjectReference;
})(exports);
}})
;
//*/
montageDefine("73de13b","parse",{dependencies:["collections/shim","./grammar"],factory:function(require,exports,module){
require("collections/shim");
var grammar = require("./grammar");

module.exports = parse;
function parse(text, options) {
    if (Array.isArray(text)) {
        return {
            type: "tuple",
            args: text.map(function (text) {
                return parse(text, options);
            })
        };
    } else {
        try {
            return grammar.parse(text, options || Object.empty);
        } catch (error) {
            error.message = (
                error.message.replace(/[\s\.]+$/, "") + " " +
                " on line " + error.line + " column " + error.column
            );
            throw error;
        }
    }
}


}})
;
//*/
montageDefine("73de13b","evaluate",{dependencies:["./parse","./compile-evaluator","./scope"],factory:function(require,exports,module){
var parse = require("./parse");
var compile = require("./compile-evaluator");
var Scope = require("./scope");

module.exports = evaluate;
function evaluate(path, value, parameters, document, components) {
    var syntax;
    if (typeof path === "string") {
        syntax = parse(path);
    } else {
        syntax = path;
    }
    var evaluate = compile(syntax);
    return evaluate(new Scope(value, null, parameters, document, components));
}


}})
;
//*/
montageDefine("73de13b","assign",{dependencies:["./parse","./compile-assigner","./scope"],factory:function(require,exports,module){
var parse = require("./parse");
var compile = require("./compile-assigner");
var Scope = require("./scope");

module.exports = assign;
function assign(target, path, value, parameters, document, components) {
    var syntax;
    if (typeof path === "string") {
        syntax = parse(path);
    } else {
        syntax = path;
    }
    var assign = compile(syntax);
    return assign(value, new Scope(target, null, parameters, document, components));
}


}})
;
//*/
montageDefine("73de13b","observe",{dependencies:["./parse","./compile-observer","./observers","./scope"],factory:function(require,exports,module){
var parse = require("./parse");
var compile = require("./compile-observer");
var Observers = require("./observers");
var autoCancelPrevious = Observers.autoCancelPrevious;
var Scope = require("./scope");

module.exports = observe;
function observe(source, expression, descriptorOrFunction) {
    var descriptor;
    if (typeof descriptorOrFunction === "function") {
        descriptor = {change: descriptorOrFunction};
    } else {
        descriptor = descriptorOrFunction;
    }

    descriptor = descriptor || empty;
    descriptor.source = source;
    descriptor.sourcePath = expression;
    var parameters = descriptor.parameters = descriptor.parameters || source;
    var document = descriptor.document;
    var components = descriptor.components;
    var beforeChange = descriptor.beforeChange;
    var contentChange = descriptor.contentChange;
    var sourceScope = new Scope(
        source,
        null,
        parameters,
        document,
        components,
        beforeChange
    );


    var syntax = parse(expression);
    var observe = compile(syntax);

    // decorate for content change observations
    if (contentChange === true) {
        observe = Observers.makeRangeContentObserver(observe);
    }

    return observe(autoCancelPrevious(function (value) {
        if (!value) {
        } else if (typeof contentChange !== "function") {
            return descriptor.change.apply(source, arguments);
        } else if (typeof contentChange === "function") {
            value.addRangeChangeListener(contentChange);
            return Observers.once(function () {
                value.removeRangeChangeListener(contentChange);
            });
        }
    }), sourceScope);
}

var empty = {};


}})
;
//*/
montageDefine("73de13b","bind",{dependencies:["./parse","./algebra","./stringify","./compile-observer","./compile-binder","./observers","./scope"],factory:function(require,exports,module){
var parse = require("./parse");
var solve = require("./algebra");
var stringify = require("./stringify");
var compileObserver = require("./compile-observer");
var compileBinder = require("./compile-binder");
var Observers = require("./observers");
var Scope = require("./scope");

module.exports = bind;
function bind(target, targetPath, descriptor) {

    descriptor.target = target;
    descriptor.targetPath = targetPath;
    var source = descriptor.source = descriptor.source || target;

    var sourcePath = descriptor["<-"] || descriptor["<->"] || "";
    var twoWay = descriptor.twoWay = "<->" in descriptor;
    descriptor.sourcePath = sourcePath;
    var value = descriptor.value;
    var parameters = descriptor.parameters = descriptor.parameters || source;
    var document = descriptor.document;
    var components = descriptor.components;
    var trace = descriptor.trace;

    var sourceScope = descriptor.sourceScope = new Scope(source, null, parameters, document, components);
    var targetScope = descriptor.targetScope = new Scope(target, null, parameters, document, components);

    // promote convert and revert from a converter object up to the descriptor
    if (descriptor.converter) {
        var converter = descriptor.converter;
        if (converter.convert) {
            descriptor.convert = converter.convert.bind(converter);
        }
        if (converter.revert) {
            descriptor.revert = converter.revert.bind(converter);
        }
    } else if (descriptor.reverter) {
        var reverter = descriptor.reverter;
        if (reverter.convert) {
            descriptor.revert = reverter.convert.bind(reverter);
        }
        if (reverter.revert) {
            descriptor.convert = reverter.revert.bind(reverter);
        }
    }

    var convert = descriptor.convert;
    var revert = descriptor.revert;

    var sourceSyntax = descriptor.sourceSyntax = parse(sourcePath);
    var targetSyntax = descriptor.targetSyntax = parse(targetPath);

    // <- source to target
    trace && console.log("DEFINE BINDING", targetPath, "<-", sourcePath, target);
    var cancelSourceToTarget = bindOneWay(
        targetScope,
        targetSyntax,
        sourceScope,
        sourceSyntax,
        convert,
        descriptor,
        trace
    );

    // -> target to source
    var cancelTargetToSource = noop;
    if (twoWay) {
        trace && console.log("DEFINE BINDING", targetPath, "->", sourcePath, source);
        cancelTargetToSource = bindOneWay(
            sourceScope,
            sourceSyntax,
            targetScope,
            targetSyntax,
            revert,
            descriptor,
            trace
        );
    }

    return function cancel() {
        cancelSourceToTarget();
        cancelTargetToSource();
    };

}

function bindOneWay(
    targetScope,
    targetSyntax,
    sourceScope,
    sourceSyntax,
    convert,
    descriptor,
    trace
) {

    // rotate operators from the target side of the binding to the
    // by inversion onto the source
    var solution = solve(targetSyntax, sourceSyntax);
    targetSyntax = solution[0];
    sourceSyntax = solution[1];

    var observeSource = compileObserver(sourceSyntax);
    if (convert) {
        observeSource = Observers.makeConverterObserver(
            observeSource,
            convert,
            sourceScope
        );
    }

    var bindTarget = compileBinder(targetSyntax);
    return bindTarget(
        observeSource,
        sourceScope,
        targetScope,
        descriptor,
        trace ? {
            sourcePath: stringify(sourceSyntax),
            targetPath: stringify(targetSyntax)
        } : null
    );

}

function noop() {}


}})
;
//*/
montageDefine("73de13b","compile-observer",{dependencies:["./observers","./operators"],factory:function(require,exports,module){
var Observers = require("./observers");
var Operators = require("./operators");

module.exports = compile;
function compile(syntax) {
    return semantics.compile(syntax);
}

var semantics = compile.semantics = {

    compilers: {
        property: Observers.makePropertyObserver,
        get: Observers.makeGetObserver,
        path: Observers.makePathObserver,
        "with": Observers.makeWithObserver,
        "if": Observers.makeConditionalObserver,
        parent: Observers.makeParentObserver,
        not: Observers.makeNotObserver,
        and: Observers.makeAndObserver,
        or: Observers.makeOrObserver,
        "default": Observers.makeDefaultObserver,
        defined: Observers.makeDefinedObserver,
        rangeContent: Observers.makeAsArrayObserver,
        mapContent: Function.identity,
        keys: Observers.makeKeysObserver,
        values: Observers.makeValuesObserver,
        items: Observers.makeEntriesObserver, // XXX deprecated
        entries: Observers.makeEntriesObserver,
        toMap: Observers.makeToMapObserver,
        mapBlock: Observers.makeMapBlockObserver,
        filterBlock: Observers.makeFilterBlockObserver,
        everyBlock: Observers.makeEveryBlockObserver,
        someBlock: Observers.makeSomeBlockObserver,
        sortedBlock: Observers.makeSortedBlockObserver,
        sortedSetBlock: Observers.makeSortedSetBlockObserver,
        groupBlock: Observers.makeGroupBlockObserver,
        groupMapBlock: Observers.makeGroupMapBlockObserver,
        minBlock: Observers.makeMinBlockObserver,
        maxBlock: Observers.makeMaxBlockObserver,
        enumerate: Observers.makeEnumerationObserver,
        reversed: Observers.makeReversedObserver,
        flatten: Observers.makeFlattenObserver,
        concat: Observers.makeConcatObserver,
        view: Observers.makeViewObserver,
        sum: Observers.makeSumObserver,
        average: Observers.makeAverageObserver,
        last: Observers.makeLastObserver,
        only: Observers.makeOnlyObserver,
        has: Observers.makeHasObserver,
        // TODO zip
        tuple: Observers.makeArrayObserver,
        range: Observers.makeRangeObserver,
        startsWith: Observers.makeStartsWithObserver,
        endsWith: Observers.makeEndsWithObserver,
        contains: Observers.makeContainsObserver,
        join: Observers.makeJoinObserver,
        toArray: Observers.makeToArrayObserver,
        asArray: Observers.makeToArrayObserver // XXX deprecated
    },

    compile: function (syntax) {
        var compilers = this.compilers;
        if (syntax.type === "literal") {
            return Observers.makeLiteralObserver(syntax.value);
        } else if (syntax.type === "value") {
            return Observers.observeValue;
        } else if (syntax.type === "parameters") {
            return Observers.observeParameters;
        } else if (syntax.type === "element") {
            return Observers.makeElementObserver(syntax.id);
        } else if (syntax.type === "component") {
            return Observers.makeComponentObserver(syntax.label, syntax);
        } else if (syntax.type === "record") {
            var observers = {};
            var args = syntax.args;
            for (var name in args) {
                observers[name] = this.compile(args[name]);
            }
            return Observers.makeObjectObserver(observers);
        } else {
            if (!compilers.hasOwnProperty(syntax.type)) {
                compilers[syntax.type] = Observers.makeMethodObserverMaker(syntax.type);
            }
            var argObservers = syntax.args.map(this.compile, this);
            return compilers[syntax.type].apply(null, argObservers);
        }
    }

};

var compilers = semantics.compilers;
Object.keys(Operators).forEach(function (name) {
    if (!compilers[name]) {
        compilers[name] = Observers.makeOperatorObserverMaker(Operators[name]);
    }
});

// a special Hell for non-enumerable inheritance
compilers.toString = Observers.makeOperatorObserverMaker(Operators.toString);


}})
;
//*/
montageDefine("73de13b","scope",{dependencies:[],factory:function(require,exports,module){
module.exports = Scope;
function Scope(value, parent, parameters, document, components, beforeChange) {
    this.value = value;
    this.parent = parent;
    this.parameters = parameters;
    this.document = document;
    this.components = components;
    this.beforeChange = beforeChange;
}

Scope.nest = function (scope, value) {
    scope = scope || new Scope();
    return new Scope(
        value,
        scope,
        scope.parameters,
        scope.document,
        scope.components,
        scope.beforeChange
    );
};


}})
;
//*/
montageDefine("73de13b","observers",{dependencies:["collections/shim","collections/listen/property-changes","collections/listen/array-changes","collections/sorted-array","collections/sorted-set","collections/map","collections/set","collections/heap","./scope","./operators","./parse","./compile-observer","./merge"],factory:function(require,exports,module){
require("collections/shim"); // Function.noop
var PropertyChanges = require("collections/listen/property-changes");
require("collections/listen/array-changes");
var SortedArray = require("collections/sorted-array");
var SortedSet = require("collections/sorted-set");
var Map = require("collections/map");
var Set = require("collections/set");
var Heap = require("collections/heap");
var Scope = require("./scope");
var Operators = require("./operators");

// Simple stuff..."degenerate" even

exports.makeLiteralObserver = makeLiteralObserver;
function makeLiteralObserver(literal) {
    return function observeLiteral(emit) {
        return emit(literal) || Function.noop;
    };
}

exports.observeValue = observeValue;
function observeValue(emit, scope) {
    return emit(scope.value) || Function.noop;
}

exports.observeParameters = observeParameters;
function observeParameters(emit, scope) {
    return emit(scope.parameters) || Function.noop;
}

// This is a concession that in practice FRB may be used in conjunction with a
// browser DOM.
exports.makeElementObserver = makeElementObserver;
function makeElementObserver(id) {
    return function observeElement(emit, scope) {
        return emit(scope.document.getElementById(id)) || Function.noop;
    };
}

// This is a concession that in practice FRB will probably be used mostly in
// conjunction with MontageJS for its component model.
exports.makeComponentObserver = makeComponentObserver;
function makeComponentObserver(label, syntax) {
    return function observeComponent(emit, scope) {
        // TODO error if scope.components does not exist or components for
        // label does not exist
        var components = scope.components;
        var method = components.getObjectByLabel || components.getComponentByLabel;
        var component = method.call(components, label);
        syntax.component = component;
        return emit(component) || Function.noop;
    };
}

exports.observeProperty = observeProperty;
var _observeProperty = observeProperty; // to bypass scope shadowing problems below
function observeProperty(object, key, emit, scope) {
    if (object == null)
        return emit();
    var cancel = Function.noop;
    function propertyChange(value, key, object) {
        cancel();
        cancel = emit(value, key, object) || Function.noop;
    }
    PropertyChanges.addOwnPropertyChangeListener(
        object,
        key,
        propertyChange,
        scope.beforeChange
    );
    propertyChange(object[key], key, object);
    return once(function cancelPropertyObserver() {
        cancel();
        PropertyChanges.removeOwnPropertyChangeListener(
            object,
            key,
            propertyChange,
            scope.beforeChange
        );
    });
}

exports.makePropertyObserver = makePropertyObserver;
function makePropertyObserver(observeObject, observeKey) {
    return function observeProperty(emit, scope) {
        return observeKey(autoCancelPrevious(function replaceKey(key) {
            if (key == null) return emit();
            return observeObject(autoCancelPrevious(function replaceObject(object) {
                if (object == null) return emit();
                if (object.observeProperty) {
                    return object.observeProperty(key, emit, scope);
                } else {
                    return _observeProperty(object, key, emit, scope);
                }
            }), scope);
        }), scope);
    };
}

exports.observeKey = observeGet; // deprecated
exports.observeGet = observeGet;
var _observeGet = observeGet; // to bypass scope shadowing below
function observeGet(collection, key, emit, scope) {
    var cancel = Function.noop;
    var equals = collection.contentEquals || Object.equals;
    function mapChange(value, mapKey, collection) {
        if (equals(key, mapKey)) {
            cancel();
            cancel = emit(value, key, collection) || Function.noop;
        }
    }
    mapChange(collection.get(key), key, collection);
    collection.addMapChangeListener(mapChange, scope.beforeChange);
    return once(function cancelMapObserver() {
        cancel();
        collection.removeMapChangeListener(mapChange);
    });
}

exports.makeGetObserver = makeGetObserver;
function makeGetObserver(observeCollection, observeKey) {
    return function observeGet(emit, scope) {
        return observeCollection(autoCancelPrevious(function replaceCollection(collection) {
            if (!collection) return emit();
            return observeKey(autoCancelPrevious(function replaceKey(key) {
                if (key == null) return emit();
                if (collection.observeGet) {
                    // polymorphic override
                    return collection.observeGet(key, emit, scope);
                } else {
                    // common case
                    return _observeGet(collection, key, emit, scope);
                }
            }), scope);
        }), scope);
    }
}

exports.makeHasObserver = makeHasObserver;
function makeHasObserver(observeSet, observeValue) {
    return function observeHas(emit, scope) {
        emit = makeUniq(emit);
        return observeValue(autoCancelPrevious(function replaceValue(sought) {
            return observeSet(autoCancelPrevious(function replaceSet(set) {
                if (!set) return emit();
                return observeRangeChange(set, function rangeChange() {
                    // this could be done incrementally if there were guarantees of
                    // uniqueness, but if there are guarantees of uniqueness, the
                    // data structure can probably efficiently check
                    return emit((set.has || set.contains).call(set, sought));
                }, scope);
            }), scope);
        }), scope);
    };
}


// Compound Observers

// accepts an array of observers and emits an array of the corresponding
// values, incrementally updated
exports.makeObserversObserver = makeObserversObserver;
function makeObserversObserver(observers) {
    return function observeObservers(emit, scope) {
        var output = Array(observers.length);
        for (var i = 0; i < observers.length; i++) {
            output[i] = undefined; // pevent sparse/holes
        }
        var cancelers = observers.map(function observeObserver(observe, index) {
            return observe(function replaceValue(value) {
                output.set(index, value);
            }, scope);
        })
        var cancel = emit(output) || Function.noop;
        return once(function cancelObserversObserver() {
            cancel();
            cancelEach(cancelers);
        });
    };
}

// {type: "record", args: {key: observe}}
// {a: 10, b: c + d}
// {type: "record", args: {a: {type: "literal", value: 10 ...
exports.makeRecordObserver = makeObjectObserver; // deprecated
exports.makeObjectObserver = makeObjectObserver;
function makeObjectObserver(observers) {
    return function observeObject(emit, scope) {
        var cancelers = {};
        var output = {};
        for (var name in observers) {
            (function (name, observe) {
                cancelers[name] = observe(function (value) {
                    output[name] = value;
                }, scope);
            })(name, observers[name]);
        }
        var cancel = emit(output) || Function.noop;
        return function cancelRecordObserver() {
            cancel();
            for (var name in cancelers) {
                cancelers[name]();
            }
        };
    };
}

exports.makeTupleObserver = makeArrayObserver; // deprecated
exports.makeArrayObserver = makeArrayObserver;
function makeArrayObserver() {
    return makeObserversObserver(Array.prototype.slice.call(arguments));
}

// Operators

exports.makeOperatorObserverMaker = makeOperatorObserverMaker;
function makeOperatorObserverMaker(operator) {
    return function makeOperatorObserver(/*...observers*/) {
        var observeOperands = makeObserversObserver(Array.prototype.slice.call(arguments));
        var observeOperandChanges = makeRangeContentObserver(observeOperands);
        return function observeOperator(emit, scope) {
            return observeOperandChanges(autoCancelPrevious(function (operands) {
                if (operands.every(Operators.defined)) {
                    return emit(operator.apply(void 0, operands));
                } else {
                    return emit()
                }
            }), scope);
        };
    };
}

exports.makeMethodObserverMaker = makeMethodObserverMaker;
function makeMethodObserverMaker(name) {
    var capitalName = name.slice(0, 1).toUpperCase() + name.slice(1);
    var makeObserverName = 'make' + capitalName + 'Observer';
    var observeName = 'observe' + capitalName;
    return function makeMethodObserver(/*...observers*/) {
        var observeObject = arguments[0];
        var operandObservers = Array.prototype.slice.call(arguments, 1);
        var autoCancelingOperandObservers = operandObservers.map(function (observe) {
            return function autoCancelingOperandObserver(emit, scope) {
                return observe(autoCancelPrevious(emit), scope);
            };
        });
        var observeOperands = makeObserversObserver(operandObservers);
        var observeOperandChanges = makeRangeContentObserver(observeOperands);
        return function observeMethod(emit, scope) {
            return observeObject(autoCancelPrevious(function (object) {
                if (!object)
                    return emit();
                if (object[makeObserverName])
                    return object[makeObserverName].apply(object, autoCancelingOperandObservers)(emit, scope);
                if (object[observeName])
                    return object[observeName](emit, scope);
                return observeOperandChanges(autoCancelPrevious(function (operands) {
                    if (!operands.every(Operators.defined))
                        return emit();
                    if (typeof object[name] === "function") {
                        return emit(object[name].apply(object, operands));
                    } else {
                        return emit();
                    }
                }), scope);
            }), scope);
        };
    };
}

// The "not" operator coerces null and undefined, so it is not adequate to
// implement it with makeOperatorObserverMaker.

exports.makeNotObserver = makeNotObserver;
function makeNotObserver(observeValue) {
    return function observeNot(emit, scope) {
        return observeValue(autoCancelPrevious(function replaceValue(value) {
            return emit(!value);
        }), scope);
    };
}

// The "and" and "or" operators short-circuit, so it is not adequate to
// implement them with makeOperatorObserverMaker.

exports.makeAndObserver = makeAndObserver;
function makeAndObserver(observeLeft, observeRight) {
    return function observeAnd(emit, scope) {
        return observeLeft(autoCancelPrevious(function replaceLeft(left) {
            if (!left) {
                return emit(left);
            } else {
                return observeRight(emit, scope);
            }
        }), scope);
    };
}

exports.makeOrObserver = makeOrObserver;
function makeOrObserver(observeLeft, observeRight) {
    return function observeOr(emit, scope) {
        return observeLeft(autoCancelPrevious(function replaceLeft(left) {
            if (left) {
                return emit(left);
            } else {
                return observeRight(emit, scope);
            }
        }), scope);
    };
}

// expression: condition ? consequent : alternate
// syntax: {type: "if", args: [condition, consequent, alternate]}
exports.makeConditionalObserver = makeConditionalObserver;
function makeConditionalObserver(observeCondition, observeConsequent, observeAlternate) {
    return function observeConditional(emit, scope) {
        return observeCondition(autoCancelPrevious(function replaceCondition(condition) {
            if (condition == null) {
                return emit();
            } else if (condition) {
                return observeConsequent(emit, scope);
            } else {
                return observeAlternate(emit, scope);
            }
        }), scope);
    };
}

// This cannot be written in terms of the defined operator because the input
// may be null or undefined and still emit a value.
exports.makeDefinedObserver = makeDefinedObserver;
function makeDefinedObserver(observeValue) {
    return function observeDefault(emit, scope) {
        return observeValue(autoCancelPrevious(function replaceValue(value) {
            return emit(value != null);
        }), scope);
    };
}

exports.makeDefaultObserver = makeDefaultObserver;
function makeDefaultObserver(observeValue, observeAlternate) {
    return function observeDefault(emit, scope) {
        return observeValue(autoCancelPrevious(function replaceValue(value) {
            if (value == null) {
                return observeAlternate(emit, scope);
            } else {
                return emit(value);
            }
        }), scope);
    };
}

// Comprehension Observers

// The map comprehension
// object.array.map{+1}
// Handles both range content changes and full replacement of the input
// object.array.splice(0, 1, 2);
// object.array = [1, 2, 3]
var makeMapBlockObserver = exports.makeMapBlockObserver = makeNonReplacing(makeReplacingMapBlockObserver);
function makeReplacingMapBlockObserver(observeCollection, observeRelation) {
    return function observeMap(emit, scope) {
        return observeCollection(autoCancelPrevious(function replaceMapInput(input) {
            if (!input) return emit();

            var output = [];
            var indexRefs = [];
            var cancelers = [];

            function update(index) {
                for (; index < input.length; index++) {
                    indexRefs[index].index = index;
                }
            }

            function rangeChange(plus, minus, index) {
                indexRefs.swap(index, minus.length, plus.map(function (value, offset) {
                    return {index: index + offset};
                }));
                update(index + plus.length);
                var initialized;
                var initial = [];
                cancelEach(cancelers.swap(index, minus.length, plus.map(function (value, offset) {
                    var indexRef = indexRefs[index + offset];
                    return observeRelation(autoCancelPrevious(function replaceRelationOutput(value) {
                        if (initialized) {
                            output.set(indexRef.index, value);
                        } else {
                            // It is unnecessary to use .set() because initial
                            // does not dispatch changes.
                            initial[offset] = value;
                        }
                    }), Scope.nest(scope, value));
                })));
                initialized = true;
                output.swap(index, minus.length, initial);
            }

            var cancelRangeChange = observeRangeChange(input, rangeChange, scope);
            // passing the input as a second argument is a special feature of a
            // mapping observer, utilized by filter observers
            var cancel = emit(output, input) || Function.noop;

            return once(function cancelMapObserver() {
                cancel();
                cancelEach(cancelers);
                cancelRangeChange();
            });
        }), scope);
    };
}

var makeFilterBlockObserver = exports.makeFilterBlockObserver = makeNonReplacing(makeReplacingFilterBlockObserver);
function makeReplacingFilterBlockObserver(observeCollection, observePredicate) {
    var observePredicates = makeReplacingMapBlockObserver(observeCollection, observePredicate);
    return function observeFilter(emit, scope) {
        return observePredicates(autoCancelPrevious(function (predicates, input) {
            if (!input) return emit();

            var output = [];
            var cancelers = [];
            var cumulativeLengths = [0];

            function update(index) {
                for (; index < predicates.length; index++) {
                    cumulativeLengths[index + 1] = cumulativeLengths[index] + predicates[index];
                }
            }

            function rangeChange(plusPredicates, minusPredicates, index) {
                var plusValues = input.slice(index, index + plusPredicates.length);
                var minusLength = minusPredicates.map(Boolean).sum();
                var plusOutput = plusValues.filter(function (value, offset) {
                    return plusPredicates[offset];
                });
                var start = cumulativeLengths[index];
                var minusOutput = output.slice(start, start + minusLength);
                // avoid propagating a range change if the output would not be
                // changed
                if (
                    minusOutput.length !== plusOutput.length ||
                    minusOutput.every(function (value, offset) {
                        return value !== plusOutput[offset];
                    })
                ) {
                    output.swap(start, minusLength, plusOutput);
                }
                update(start);
            }

            var cancelRangeChange = observeRangeChange(predicates, rangeChange, scope);
            var cancel = emit(output) || Function.noop;
            return once(function cancelFilterObserver() {
                cancel();
                cancelEach(cancelers);
                cancelRangeChange();
            });

        }), scope);
    };
}

exports.makeSortedBlockObserver = makeSortedBlockObserver;
function makeSortedBlockObserver(observeCollection, observeRelation) {
    var observeRelationEntry = makeRelationEntryObserver(observeRelation);
    var observeRelationEntries = makeReplacingMapBlockObserver(observeCollection, observeRelationEntry);
    var observeSort = function (emit, scope) {
        return observeRelationEntries(autoCancelPrevious(function (input) {
            if (!input) return emit();
            var output = [];
            var sorted = SortedArray(output, entryValueEquals, entryValueCompare);
            function rangeChange(plus, minus) {
                sorted.addEach(plus);
                sorted.deleteEach(minus);
            }
            var cancelRangeChange = observeRangeChange(input, rangeChange, scope);
            var cancel = emit(output) || Function.noop;
            return function cancelSortedObserver() {
                cancel();
                cancelRangeChange();
            };
        }), scope);
    };
    return makeMapBlockObserver(observeSort, observeEntryKey);
}

function entryValueEquals(x, y) {
    return Object.equals(x[1], y[1]);
}

function entryValueCompare(x, y) {
    return Object.compare(x[1], y[1]);
}

// Transforms a value into a [value, relation(value)] tuple
function makeRelationEntryObserver(observeRelation) {
    return function (emit, scope) {
        return observeRelation(autoCancelPrevious(function (value) {
            return emit([scope.value, value]) || Function.noop;
        }), scope);
    };
}

exports.makeSortedSetBlockObserver = makeSortedSetBlockObserver;
function makeSortedSetBlockObserver(observeCollection, observeRelation) {
    var observeRelationEntry = makeRelationEntryObserver(observeRelation);
    var observeRelationEntries = makeReplacingMapBlockObserver(observeCollection, observeRelationEntry);
    var observeRelationEntryGroups = makeGroupBlockObserver(observeRelationEntries, observeEntryValue);
    var observeUniqueRelationEntries = makeReplacingMapBlockObserver(observeRelationEntryGroups, makeLastObserver(observeEntryValue));
    return function observeSortedSetBlock(emit, scope) {
        var order = new Map();
        function compare(x, y) {
            x = order.get(x);
            y = order.get(y);
            return Object.compare(x, y);
        }
        function equals(x, y) {
            x = order.get(x);
            y = order.get(y);
            return Object.equals(x, y);
        }
        var sortedSet = new SortedSet(null, equals, compare);
        var cancel = emit(sortedSet) || Function.noop;
        function rangeChange(plus, minus) {
            minus.forEach(function (entry) {
                sortedSet["delete"](entry[0]);
                order["delete"](entry[0]);
            });
            plus.forEach(function (entry) {
                order.set(entry[0], entry[1]);
                sortedSet.add(entry[0]);
            });
        }
        function entriesChange(entries) {
            sortedSet.clear();
            return observeRangeChange(entries, rangeChange, scope);
        }
        var cancelUniqueValuesObserver = observeUniqueRelationEntries(entriesChange, scope);
        return function cancelSortedSetObserver() {
            cancel();
            cancelUniqueValuesObserver();
        };
    };
}

// calculating the reflected index for an incremental change:
// [0, 1, 2, 3]  length 4
//     -------  -4 (1+3)
// --------    0-  (outer.length - index - inner.length)
exports.makeReversedObserver = makeNonReplacing(makeReplacingReversedObserver);
function makeReplacingReversedObserver(observeArray) {
    return function observeReversed(emit, scope) {
        return observeArray(autoCancelPrevious(function (input) {
            if (!input) return emit();
            var output = [];
            function rangeChange(plus, minus, index) {
                var reflected = output.length - index - minus.length;
                output.swap(reflected, minus.length, plus.reversed());
            };
            var cancelRangeChange = observeRangeChange(input, rangeChange, scope);
            var cancel = emit(output);
            return once(function cancelReversedObserver() {
                cancel();
                cancelRangeChange();
            });
        }), scope);
    };
}

var makeFlattenObserver =
exports.makeFlattenObserver = makeNonReplacing(makeReplacingFlattenObserver);
function makeReplacingFlattenObserver(observeArray) {
    return function (emit, scope) {
        return observeArray(autoCancelPrevious(function (input) {
            if (!input) return emit();

            var output = [];
            var cancelers = [];
            var cumulativeLengths = [0];
            var indexRefs = [];

            function update(i) {
                for (var j = i; j < input.length; j++) {
                    indexRefs[j].index = j;
                    if (input[j]) {
                        cumulativeLengths[j + 1] = cumulativeLengths[j] + input[j].length;
                    } else {
                        cumulativeLengths[j + 1] = cumulativeLengths[j];
                    }
                }
            }

            function rangeChange(plus, minus, i) {

                // minus
                var start = cumulativeLengths[i];
                var end = cumulativeLengths[i + minus.length];
                var length = end - start;
                output.swap(start, length, []);

                indexRefs.swap(i, minus.length, plus.map(function () {
                    return {index: null};
                }));
                update(i);

                // plus
                cancelEach(cancelers.swap(
                    i,
                    minus.length,
                    plus.map(function (inner, j) {
                        var index = indexRefs[i + j];
                        function innerRangeChange(plus, minus, k) {
                            update(index.index);
                            var start = cumulativeLengths[index.index] + k;
                            var end = cumulativeLengths[index.index] + k + minus.length;
                            var length = end - start;
                            output.swap(start, length, plus);
                        }
                        return observeRangeChange(inner, innerRangeChange, scope);
                    })
                ));

            }

            var cancelRangeChange = observeRangeChange(input, rangeChange, scope);
            var cancel = emit(output) || Function.noop;

            return once(function cancelFlattenObserver() {
                cancel();
                cancelEach(cancelers);
                cancelRangeChange();
            });
        }), scope);
    };
}

exports.makeConcatObserver = makeConcatObserver;
function makeConcatObserver() {
    return makeFlattenObserver(
        makeObserversObserver(
            Array.prototype.slice.call(arguments)
        )
    );
}

exports.makeSomeBlockObserver = makeSomeBlockObserver;
function makeSomeBlockObserver(observeCollection, observePredicate) {
    // collection.some{predicate} is equivalent to
    // collection.filter{predicate}.length !== 0
    var observeFilter = makeFilterBlockObserver(observeCollection, observePredicate);
    var observeLength = makePropertyObserver(observeFilter, observeLengthLiteral);
    return makeConverterObserver(observeLength, Boolean);
}

exports.makeEveryBlockObserver = makeEveryBlockObserver;
function makeEveryBlockObserver(observeCollection, observePredicate) {
    // collection.every{predicate} is equivalent to
    // collection.filter{!predicate}.length === 0
    var observeNotPredicate = makeConverterObserver(observePredicate, Operators.not);
    var observeFilter = makeFilterBlockObserver(observeCollection, observeNotPredicate);
    var observeLength = makePropertyObserver(observeFilter, observeLengthLiteral);
    return makeConverterObserver(observeLength, Operators.not);
}

exports.makeGroupBlockObserver = makeGroupBlockObserver;
function makeGroupBlockObserver(observeCollection, observeRelation) {
    var observeGroup = makeGroupMapBlockObserver(observeCollection, observeRelation);
    return makeEntriesObserver(observeGroup);
}

exports.makeGroupMapBlockObserver = makeGroupMapBlockObserver;
function makeGroupMapBlockObserver(observeCollection, observeRelation) {
    var observeRelationEntry = makeRelationEntryObserver(observeRelation);
    var observeRelationEntries = makeReplacingMapBlockObserver(observeCollection, observeRelationEntry);
    return function observeGroup(emit, scope) {
        return observeRelationEntries(autoCancelPrevious(function (input, original) {
            if (!input) return emit();

            var groups = Map();

            function rangeChange(plus, minus, index) {
                minus.forEach(function (item) {
                    // ASSERT groups.has(item[1]);
                    var group = groups.get(item[1]);
                    if (group.length === 1) {
                        groups["delete"](item[1]);
                    } else {
                        group["delete"](item[0]);
                    }
                });
                plus.forEach(function (item) {
                    var create = !groups.has(item[1]);
                    var group;
                    if (create) {
                        // constructClone ensures that the equivalence classes
                        // are the same type as the input.  It is shimmed on
                        // Array by Collections, and supported by all others.
                        group = original.constructClone();
                    } else {
                        group = groups.get(item[1]);
                    }
                    group.add(item[0]);
                    if (create) {
                        groups.set(item[1], group);
                    }
                });
            }

            var cancelRangeChange = observeRangeChange(input, rangeChange, scope);
            var cancel = emit(groups) || Function.noop;
            return function cancelGroupObserver() {
                cancelRangeChange();
                cancel();
            };
        }), scope);
    };
}

function makeHeapBlockObserver(observeCollection, observeRelation, order) {
    var observeRelationEntry = makeRelationEntryObserver(observeRelation);
    var observeRelationEntries = makeReplacingMapBlockObserver(observeCollection, observeRelationEntry);

    function entryValueOrderCompare(a, b) {
        return Object.compare(a[1], b[1]) * order;
    }

    return function observeHeapBlock(emit, scope) {

        return observeRelationEntries(autoCancelPrevious(function (input) {
            if (!input) return emit();

            var heap = new Heap(null, entryValueEquals, entryValueOrderCompare);

            function rangeChange(plus, minus) {
                heap.addEach(plus);
                heap.deleteEach(minus);
            }

            function heapChange(item, key) {
                if (key === 0) {
                    if (!item) {
                        return emit();
                    } else {
                        return emit(item[0]);
                    }
                }
            }

            var cancelRangeChange = observeRangeChange(input, rangeChange, scope);
            var cancelHeapChange = observeMapChange(heap, heapChange, scope);

            return function cancelHeapObserver() {
                cancelRangeChange();
                cancelHeapChange();
            };
        }), scope);
    };
}

exports.makeMaxBlockObserver = makeMaxBlockObserver;
function makeMaxBlockObserver(observeCollection, observeRelation) {
    return makeHeapBlockObserver(observeCollection, observeRelation, 1);
}

exports.makeMinBlockObserver = makeMinBlockObserver;
function makeMinBlockObserver(observeCollection, observeRelation) {
    return makeHeapBlockObserver(observeCollection, observeRelation, -1);
}

// used by both some and every blocks
var observeLengthLiteral = makeLiteralObserver("length");

// A utility for generating sum and average observers since they both need to
// capture some internal state on intiailization, and update that state on
// range changes.
function makeCollectionObserverMaker(setup) {
    return function (observeCollection) {
        return function (emit, scope) {
            emit = makeUniq(emit);
            return observeCollection(autoCancelPrevious(function (collection) {
                if (!collection) return emit();
                var rangeChange = setup(collection, emit);
                return observeRangeChange(collection, function (plus, minus, index) {
                    return emit(rangeChange(plus, minus, index));
                }, scope);
            }), scope);
        };
    };
}

exports.makeSumObserver = makeCollectionObserverMaker(function setup() {
    var sum = 0;
    return function rangeChange(plus, minus, index) {
        plus = plus.filter(isNumber);
        minus = minus.filter(isNumber);
        sum += plus.sum() - minus.sum();
        return sum;
    };
});

exports.makeAverageObserver = makeCollectionObserverMaker(function setup() {
    var sum = 0;
    var count = 0;
    return function rangeChange(plus, minus, index) {
        plus = plus.filter(isNumber);
        minus = minus.filter(isNumber);
        sum += plus.sum() - minus.sum();
        count += plus.length - minus.length;
        return sum / count;
    };
});

function isNumber(value) {
    return typeof value === "number" && !isNaN(value);
}

exports.makeViewObserver = makeNonReplacing(makeReplacingViewObserver);
function makeReplacingViewObserver(observeInput, observeStart, observeLength) {
    return function observeView(emit, scope) {
        return observeInput(autoCancelPrevious(function (input) {
            if (!input) return emit();
            return observeLength(autoCancelPrevious(function (length) {
                if (length == null) return emit();
                var previousStart;
                return observeStart(autoCancelPrevious(function (start) {
                    if (start == null) return emit();
                    var output = [];
                    function rangeChange(plus, minus, index) {
                        var diff = plus.length - minus.length;
                        if (index < start && diff < 0 && diff < length) { // shrink before
                            // inject elements at the end
                            output.swap(output.length, 0, input.slice(start + length + diff, start + length));
                            // remove elements at the beginning
                            output.splice(0, -diff);
                        } else if (index < start && diff > 0 && diff < length) { // grow before
                            // inject elements
                            output.swap(0, 0, input.slice(start, start + diff));
                            // remove elements from end
                            output.splice(output.length - diff, diff);
                        } else if (index >= start && diff < 0 && index < start + length) { // shrink within
                            // inject elements to end
                            output.swap(output.length, 0, input.slice(start + length + diff, start + length));
                            // remove elements from within
                            output.splice(index - start, -diff);
                        } else if (index >= start && diff > 0 && index < start + length) { // grow within
                            // inject elements within
                            output.swap(index - start, 0, input.slice(index, index + diff));
                            // remove elements from end
                            output.splice(output.length - diff, diff);
                        } else if (index < start + length) {
                            output.swap(0, output.length, input.slice(start, start + length));
                        }
                    }
                    var cancelRangeChange = observeRangeChange(input, rangeChange, scope);
                    var cancel = emit(output) || Function.noop;
                    return once(function cancelViewObserver() {
                        cancel();
                        cancelRangeChange();
                    });
                }), scope);
            }), scope);
        }), scope);
    };
}

exports.makeEnumerateObserver = makeNonReplacing(makeReplacingEnumerateObserver);
exports.makeEnumerationObserver = exports.makeEnumerateObserver; // deprecated
function makeReplacingEnumerateObserver(observeArray) {
    return function (emit, scope) {
        return observeArray(autoCancelPrevious(function replaceArray(input) {
            if (!input) return emit();

            var output = [];
            function update(index) {
                for (; index < output.length; index++) {
                    output[index].set(0, index);
                }
            }
            function rangeChange(plus, minus, index) {
                output.swap(index, minus.length, plus.map(function (value, offset) {
                    return [index + offset, value];
                }));
                update(index + plus.length);
            }
            var cancelRangeChange = observeRangeChange(input, rangeChange, scope);
            var cancel = emit(output) || Function.noop;
            return function cancelEnumerateObserver() {
                cancel();
                cancelRangeChange();
            };
        }), scope);
    };
}

exports.makeRangeObserver = makeRangeObserver;
function makeRangeObserver(observeLength) {
    return function observeRange(emit, scope) {
        var output = [];
        var cancel = emit(output) || Function.noop;
        var cancelLengthObserver = observeLength(function (length) {
            length = length >>> 0;
            if (length == null) {
                output.clear();
            } else if (length > output.length) {
                // pre-fab the extension so the we only have to propagate one
                // range change to the output.
                var extension = [];
                for (var i = output.length; i < length; i++) {
                    extension.push(i);
                }
                output.swap(output.length, 0, extension);
            } else if (length < output.length) {
                output.splice(length, output.length);
            }
        }, scope);
        return function cancelObserveRange() {
            cancel();
            cancelLengthObserver();
        };
    };
}


// String Observers

exports.makeStartsWithObserver = makeStartsWithObserver;
function makeStartsWithObserver(observeHaystack, observeNeedle) {
    return function observeStartsWith(emit, scope) {
        return observeNeedle(function (needle) {
            var expression = new RegExp("^" + RegExp.escape(needle));
            return observeHaystack(function (haystack) {
                return emit(expression.test(haystack)) || Function.noop;
            }, scope);
        }, scope);
    }
}

exports.makeEndsWithObserver = makeEndsWithObserver;
function makeEndsWithObserver(observeHaystack, observeNeedle) {
    return function observeEndsWith(emit, scope) {
        return observeNeedle(function (needle) {
            var expression = new RegExp(RegExp.escape(needle) + "$");
            return observeHaystack(function (haystack) {
                return emit(expression.test(haystack)) || Function.noop;
            }, scope);
        }, scope);
    }
}

exports.makeContainsObserver = makeContainsObserver;
function makeContainsObserver(observeHaystack, observeNeedle) {
    return function observeContains(emit, scope) {
        return observeNeedle(function (needle) {
            var expression = new RegExp(RegExp.escape(needle));
            return observeHaystack(function (haystack) {
                return emit(expression.test(haystack)) || Function.noop;
            }, scope);
        }, scope);
    }
}

exports.makeJoinObserver = makeJoinObserver;
function makeJoinObserver(observeArray, observeDelimiter) {
    observeDelimiter = observeDelimiter || observeNullStringLiteral;
    return function observeJoin(emit, scope) {
        return observeArray(autoCancelPrevious(function changeJoinArray(array) {
            if (!array)
                return emit() || Function.noop;
            return observeDelimiter(autoCancelPrevious(function changeJoinDelimiter(delimiter) {
                if (typeof delimiter !== "string")
                    return emit() || Function.noop;
                var cancel = Function.noop;
                function rangeChange() {
                    cancel = emit(array.join(delimiter)) || Function.noop;
                }
                var cancelRangeChange = observeRangeChange(array, rangeChange, scope);
                return function cancelJoinObserver() {
                    cancelRangeChange();
                    cancel();
                };
            }), scope);
        }), scope);
    };
}

var observeNullStringLiteral = makeLiteralObserver("");

// Collection Observers

exports.observeRangeChange = observeRangeChange;
function observeRangeChange(collection, emit, scope) {
    if (!collection)
        return;
    var cancelChild = Function.noop;
    function rangeChange(plus, minus, index) {
        cancelChild();
        cancelChild = emit(plus, minus, index) || Function.noop;
    }
    if (!collection.toArray) {
        return;
    }
    if (!collection.addRangeChangeListener) {
        return;
    }
    rangeChange(collection.toArray(), [], 0);
    var cancelRangeChange = collection.addRangeChangeListener(
        rangeChange,
        scope.beforeChange
    );
    return once(function cancelRangeObserver() {
        cancelChild();
        cancelRangeChange();
    });
}

exports.makeLastObserver = makeLastObserver;
function makeLastObserver(observeCollection) {
    return function observeLast(emit, scope) {
        return observeCollection(autoCancelPrevious(function (collection) {
            return _observeLast(collection, emit, scope);
        }), scope);
    };
}

// []
// [1, 2, 3], [], 0 -> [1, 2, 3] grow from start
// [4], [], 3 -> [1, 2, 3, 4] grow
// [], [4], 3 -> [1, 2, 3]
exports.observeLast = observeLast;
var _observeLast = observeLast;
function observeLast(collection, emit, scope) {
    var lastIndex = -1;
    var cancel = Function.noop;
    var prev = null;
    function rangeChange(plus, minus, index) {
        lastIndex += plus.length - minus.length;
        // short circuit if the change does not have the reach to change the
        // last value
        if (
            index + minus.length < lastIndex &&
            index + plus.length < lastIndex
        ) {
            return;
        }
        var next = lastIndex < 0 ? null : collection.get(lastIndex);
        cancel();
        cancel = emit(next) || Function.noop;
        prev = next;
    }
    var cancelRangeChange = observeRangeChange(collection, rangeChange, scope);
    return function cancelLastObserver() {
        cancel();
        cancelRangeChange();
    };
}

exports.makeOnlyObserver = makeOnlyObserver;
function makeOnlyObserver(observeCollection) {
    return function (emit, scope) {
        return observeCollection(autoCancelPrevious(makeUniq(function replaceCollectionForOnly(collection) {
            return observeOnly(collection, emit, scope);
        })), scope);
    };
}

exports.observeOnly = observeOnly;
function observeOnly(collection, emit, scope) {
    var length = 0;
    function rangeChange(plus, minus, index) {
        length += plus.length - minus.length;
        if (length === 1) {
            return emit(collection.only());
        } else {
            return emit();
        }
    }
    return observeRangeChange(collection, rangeChange, scope);
}

exports.makeRangeContentObserver = makeRangeContentObserver;
function makeRangeContentObserver(observeCollection) {
    return function observeContent(emit, scope) {
        return observeCollection(autoCancelPrevious(function (collection) {
            if (!collection || !collection.addRangeChangeListener) {
                return emit(collection);
            } else {
                return observeRangeChange(collection, function rangeChange() {
                    return emit(collection);
                }, scope);
            }
        }), scope);
    };
}

exports.makeMapContentObserver = makeMapContentObserver;
function makeMapContentObserver(observeCollection) {
    return function observeContent(emit, scope) {
        return observeCollection(autoCancelPrevious(function (collection) {
            if (!collection || !collection.addMapChangeListener) {
                return emit(collection);
            } else {
                return observeMapChange(collection, function rangeChange() {
                    return emit(collection);
                }, scope);
            }
        }), scope);
    };
}

exports.observeMapChange = observeMapChange;
function observeMapChange(collection, emit, scope) {
    if (!collection.addMapChangeListener)
        return;
    var cancelers = new Map();
    function mapChange(value, key, collection) {
        var cancelChild = cancelers.get(key) || Function.noop;
        cancelers["delete"](key);
        cancelChild();
        cancelChild = emit(value, key, collection) || Function.noop;
        if (value === undefined) {
            cancelChild();
        } else {
            cancelers.set(key, cancelChild);
        }
    }
    collection.forEach(mapChange);
    var cancelMapChange = collection.addMapChangeListener(mapChange, scope.beforeChange);
    return once(function cancelMapObserver() {
        cancelers.forEach(function (cancel) {
            cancel();
        });
        cancelMapChange();
    });
}

var makeEntriesObserver = exports.makeEntriesObserver = makeNonReplacing(makeReplacingEntriesObserver);
function makeReplacingEntriesObserver(observeCollection) {
    return function _observeEntries(emit, scope) {
        return observeCollection(autoCancelPrevious(function (collection) {
            if (!collection) return emit();
            return observeEntries(collection, emit, scope);
        }), scope);
    };
}

exports.observeEntries = observeEntries;
function observeEntries(collection, emit, scope) {
    var items = [];
    var keyToEntry = Map();
    var cancel = emit(items) || Function.noop;
    // TODO observe addition and deletion with separate observers
    function mapChange(value, key, collection) {
        var item, index;
        if (!keyToEntry.has(key)) { // add
            item = [key, value];
            keyToEntry.set(key, item);
            items.push(item);
        } else if (value == null) { // delete
            item = keyToEntry.get(key);
            keyToEntry["delete"](key);
            index = items.indexOf(item);
            items.splice(index, 1);
        } else { // update
            item = keyToEntry.get(key);
            item.set(1, value);
        }
    }
    var cancelMapChange = observeMapChange(collection, mapChange, scope) || Function.noop;
    return once(function cancelObserveEntries() {
        cancel();
        cancelMapChange();
    });
}

exports.makeKeysObserver = makeKeysObserver;
function makeKeysObserver(observeCollection) {
    var observeEntries = makeEntriesObserver(observeCollection);
    return makeMapBlockObserver(observeEntries, observeEntryKey);
}

exports.observeEntryKey = observeEntryKey;
function observeEntryKey(emit, scope) {
    if (!scope.value) return emit();
    return emit(scope.value[0]) || Function.noop;
}

exports.makeValuesObserver = makeValuesObserver;
function makeValuesObserver(observeCollection) {
    var observeEntries = makeEntriesObserver(observeCollection);
    return makeMapBlockObserver(observeEntries, observeEntryValue);
}

exports.observeEntryValue = observeEntryValue;
function observeEntryValue(emit, scope) {
    if (!scope.value) return emit();
    return emit(scope.value[1]) || Function.noop;
}

exports.makeToMapObserver = makeToMapObserver;
function makeToMapObserver(observeObject) {
    return function observeToMap(emit, scope) {
        var map = new Map();
        var cancel = emit(map) || Function.noop;

        var cancelObjectObserver = observeObject(autoCancelPrevious(function replaceObject(object) {
            map.clear();
            if (!object || typeof object !== "object") return;

            // Must come first because Arrays also implement map changes, but
            // Maps do not implement range changes.
            if (object.addRangeChangeListener) { // array/collection of items
                return observeUniqueEntries(autoCancelPrevious(function (entries) {
                    function rangeChange(plus, minus) {
                        minus.forEach(function (entry) {
                            map["delete"](entry[0]);
                        });
                        plus.forEach(function (entry) {
                            map.set(entry[0], entry[1]);
                        });
                    }
                    return observeRangeChange(entries, rangeChange, scope);
                }), Scope.nest(scope, object));
            } else if (object.addMapChangeListener) { // map reflection
                function mapChange(value, key) {
                    if (value === undefined) {
                        map["delete"](key);
                    } else {
                        map.set(key, value);
                    }
                }
                return observeMapChange(object, mapChange, scope);
            } else { // object literal
                var cancelers = Object.keys(object).map(function (key) {
                    return _observeProperty(object, key, autoCancelPrevious(function (value) {
                        if (value === undefined) {
                            map["delete"](key);
                        } else {
                            map.set(key, value);
                        }
                    }), scope);
                });
                return function cancelPropertyObservers() {
                    cancelEach(cancelers);
                };
            }
        }), scope);

        return function cancelObjectToMapObserver() {
            cancel();
            cancelObjectObserver();
        };
    };
}

// A utility for makeToMapObserver
// object.group{.0}.map{.1.last()}
var observeUniqueEntries = makeMapBlockObserver(
    makeGroupBlockObserver(
        observeValue,
        observeEntryKey
    ),
    makeLastObserver(observeEntryValue)
);


// Combinatoric Observers

exports.makeParentObserver = makeParentObserver;
function makeParentObserver(observeExpression) {
    return function observeParentScope(emit, scope) {
        return observeExpression(emit, scope.parent || new Scope());
    };
}

exports.makeConverterObserver = makeConverterObserver;
function makeConverterObserver(observeValue, convert, thisp) {
    return function observeConversion(emit, scope) {
        emit = makeUniq(emit);
        return observeValue(autoCancelPrevious(function replaceValue(value) {
            return emit(convert.call(thisp, value));
        }), scope);
    };
}

exports.makeComputerObserver = makeComputerObserver;
function makeComputerObserver(observeArgs, compute, thisp) {
    return function (emit, scope) {
        emit = makeUniq(emit);
        return observeArgs(autoCancelPrevious(function replaceArgs(args) {
            if (!args || !args.every(Operators.defined)) return;
            return emit(compute.apply(thisp, args));
        }), scope);
    };
}

exports.makePathObserver = makeExpressionObserver; // deprecated
exports.makeExpressionObserver = makeExpressionObserver;
function makeExpressionObserver(observeInput, observeExpression) {
    var parse = require("./parse");
    var compileObserver = require("./compile-observer");
    return function expressionObserver(emit, scope) {
        emit = makeUniq(emit);
        return observeExpression(autoCancelPrevious(function replaceExpression(expression) {
            if (expression == null) return emit();
            var syntax, observeOutput;
            try {
                syntax = parse(expression);
                observeOutput = compileObserver(syntax);
            } catch (exception) {
                return emit();
            }
            return observeInput(autoCancelPrevious(function replaceInput(input) {
                return observeOutput(emit, Scope.nest(scope, input));
            }), scope);
        }), scope);
    };
}

exports.makeWithObserver = makeWithObserver;
function makeWithObserver(observeInput, observeExpression) {
    return function observeWith(emit, scope) {
        return observeInput(autoCancelPrevious(function replaceInput(input) {
            return observeExpression(autoCancelPrevious(function replaceValue(value) {
                return emit(value);
            }), Scope.nest(scope, input));
        }), scope);
    };
}

exports.makeToArrayObserver = makeNonReplacing(Function.identity);
exports.makeAsArrayObserver = exports.makeToArrayObserver; // XXX deprecated

// Utility Methods
// ---------------

var merge = require("./merge").merge;

// A utility for generating map and filter observers because they both replace
// the output array whenever the input array is replaced.  instead, this
// wrapper receives the replacement array and mirrors it on an output array
// that only gets emitted once.
function makeNonReplacing(wrapped) {
    return function () {
        var observe = wrapped.apply(this, arguments);
        return function observeArrayWithoutReplacing(emit, scope) {
            var output = [];
            var cancelObserver = observe(autoCancelPrevious(function (input) {
                if (!input) {
                    output.clear();
                } else {
                    // equivalent to: output.swap(0, output.length, input);
                    merge(output, input);
                    function rangeChange(plus, minus, index) {
                        output.swap(index, minus.length, plus);
                    }
                    // TODO fix problem that this would get called twice on replacement
                    var cancelRangeChange = input.addRangeChangeListener(
                        rangeChange,
                        scope.beforeChange
                    );
                    return once(cancelRangeChange);
                }
            }), scope);
            var cancel = emit(output) || Function.noop;
            return once(function cancelNonReplacingObserver() {
                cancelObserver();
                cancel();
            });
        };
    };
}

// wraps an emitter such that repeated values are ignored
exports.makeUniq = makeUniq;
function makeUniq(emit) {
    var previous;
    return function uniqEmit(next) {
        if (next !== previous) {
            var result = emit.apply(this, arguments);
            previous = next;
            return result;
        }
    };
}

exports.cancelEach = cancelEach;
function cancelEach(cancelers) {
    cancelers.forEach(function (cancel) {
        if (cancel) {
            cancel();
        }
    });
}

// wraps an emitter that returns a canceler.  each time the wrapped function is
// called, it cancels the previous canceler, and calls the last canceler when
// it is canceled.  this is useful for observers that update a value and attach
// a new event listener tree to the value.
exports.autoCancelPrevious = autoCancelPrevious;
function autoCancelPrevious(emit) {
    var cancelPrevious = Function.noop;
    return function replaceObserver(value) {
        cancelPrevious();
        cancelPrevious = emit.apply(this, arguments) || Function.noop;
        return function cancelObserver() {
            cancelPrevious();
        };
    };
}

exports.once = once;
function once(callback) {
    var done;
    return function once() {
        if (done) {
            return Function.noop; // TODO fix bugs that make this sensitive
            //throw new Error("Redundant call: " + callback + " " + done.stack + "\nSecond call:");
        }
        done = true;
        //done = new Error("First call:");
        return callback.apply(this, arguments);
    }
}


}})
;
//*/
montageDefine("73de13b","stringify",{dependencies:["./parse","./language"],factory:function(require,exports,module){"use strict";

var parse = require("./parse");
var precedence = require("./language").precedence;
var typeToToken = require("./language").operatorTypes;
var tokenToType = require("./language").operatorTokens;

module.exports = stringify;
function stringify(syntax, scope) {
    return stringify.semantics.stringify(syntax, scope);
}

function makeBlockStringifier(type) {
    return function (syntax, scope, stringify) {
        var chain = type + '{' + stringify(syntax.args[1], scope) + '}';
        if (syntax.args[0].type === "value") {
            return chain;
        } else {
            return stringify(syntax.args[0], scope) + '.' + chain;
        }
    };
}

stringify.semantics = {

    makeBlockStringifier: makeBlockStringifier,

    stringify: function (syntax, scope, parent) {
        var stringify = this.stringify.bind(this);
        var stringifiers = this.stringifiers;
        var string;
        function stringifyChild(child) {
            var arg = stringify(child, scope);
            if (!arg) return "this";
            return arg;
        }
        if (stringifiers[syntax.type]) {
            // operators
            string = stringifiers[syntax.type](syntax, scope, stringify);
        } else if (syntax.inline) {
            // inline invocations
            string = (
                "&" + syntax.type + "(" +
                syntax.args.map(stringifyChild).join(", ") + ")"
            );
        } else {
            // method invocations
            var chain;
            if (syntax.args.length === 1 && syntax.args[0].type === "mapBlock") {
                // map block function calls
                chain = syntax.type + "{" + stringify(syntax.args[0].args[1], scope) + "}";
                syntax = syntax.args[0];
            } else {
                // normal function calls
                chain = (
                    syntax.type + "(" +
                    syntax.args.slice(1).map(stringifyChild).join(", ") + ")"
                );
            }
            // left-side if it exists
            if (syntax.args[0].type === "value") {
                string = chain;
            } else {
                string = stringify(syntax.args[0], scope) + "." + chain;
            }
        }
        // parenthesize if we're going backward in precedence
        if (
            !parent ||
            (parent.type === syntax.type && parent.type !== "if") ||
            // TODO check on weirdness of "if"
            precedence.get(parent.type).has(syntax.type)
        ) {
            return string;
        } else {
            return "(" + string + ")";
        }
    },

    stringifiers: {

        value: function (syntax, scope, stringify) {
            return '';
        },

        literal: function (syntax, scope, stringify) {
            if (typeof syntax.value === 'string') {
                return "'" + syntax.value.replace("'", "\\'") + "'";
            } else {
                return "" + syntax.value;
            }
        },

        parameters: function (syntax, scope, stringify) {
            return '$';
        },

        record: function (syntax, scope, stringify) {
            return "{" + Object.map(syntax.args, function (value, key) {
                var string;
                if (value.type === "value") {
                    string = "this";
                } else {
                    string = stringify(value, scope);
                }
                return key + ": " + string;
            }).join(", ") + "}";
        },

        tuple: function (syntax, scope, stringify) {
            return "[" + Object.map(syntax.args, function (value) {
                if (value.type === "value") {
                    return "this";
                } else {
                    return stringify(value);
                }
            }).join(", ") + "]";
        },

        component: function (syntax, scope) {
            var label;
            if (scope && scope.components && syntax.component) {
                if (scope.components.getObjectLabel) {
                    label = scope.components.getObjectLabel(syntax.component);
                } else if (scope.components.getLabelForObject) {
                    // I am hoping that we will change Montage to use this API
                    // for consistency with document.getElementById,
                    // components.getObjectByLabel, & al
                    label = scope.components.getLabelForObject(syntax.component);
                }
            } else {
                label = syntax.label;
            }
            return '@' + label;
        },

        element: function (syntax) {
            return '#' + syntax.id;
        },

        mapBlock: makeBlockStringifier("map"),
        filterBlock: makeBlockStringifier("filter"),
        someBlock: makeBlockStringifier("some"),
        everyBlock: makeBlockStringifier("every"),
        sortedBlock: makeBlockStringifier("sorted"),
        sortedSetBlock: makeBlockStringifier("sortedSet"),
        groupBlock: makeBlockStringifier("group"),
        groupMapBlock: makeBlockStringifier("groupMap"),
        minBlock: makeBlockStringifier("min"),
        maxBlock: makeBlockStringifier("max"),

        property: function (syntax, scope, stringify) {
            if (syntax.args[0].type === "value") {
                if (typeof syntax.args[1].value === "string") {
                    return syntax.args[1].value;
                } else if (syntax.args[1].type === "literal") {
                    return "." + syntax.args[1].value;
                } else {
                    return "this[" + stringify(syntax.args[1], scope) + "]";
                }
            } else if (syntax.args[0].type === "parameters") {
                return "$" + syntax.args[1].value;
            } else if (
                syntax.args[1].type === "literal" &&
                /^[\w\d_]+$/.test(syntax.args[1].value)
            ) {
                return stringify(syntax.args[0], scope, {
                    type: "scope"
                }) + '.' + syntax.args[1].value;
            } else {
                return stringify(syntax.args[0], {
                    type: "scope"
                }, scope) + '[' + stringify(syntax.args[1], scope) + ']';
            }
        },

        "with": function (syntax, scope, stringify) {
            var right = stringify(syntax.args[1], scope, syntax);
            return stringify(syntax.args[0], scope) + "." + right;
        },

        not: function (syntax, scope, stringify) {
            if (syntax.args[0].type === "equals") {
                return (
                    stringify(syntax.args[0].args[0], scope, {type: "equals"}) +
                    " != " +
                    stringify(syntax.args[0].args[1], scope, {type: "equals"})
                );
            } else {
                return '!' + stringify(syntax.args[0], scope, syntax)
            }
        },

        neg: function (syntax, scope, stringify) {
            return '-' + stringify(syntax.args[0], scope, syntax)
        },

        toNumber: function (syntax, scope, stringify) {
            return '+' + stringify(syntax.args[0], scope, syntax)
        },

        parent: function (syntax, scope, stringify) {
            return '^' + stringify(syntax.args[0], scope, syntax)
        },

        if: function (syntax, scope, stringify) {
            return (
                stringify(syntax.args[0], scope, syntax) + " ? " +
                stringify(syntax.args[1], scope) + " : " +
                stringify(syntax.args[2], scope)
            );
        },

        event: function (syntax, scope, stringify) {
            return syntax.when + " " + syntax.event + " -> " + stringify(syntax.listener, scope);
        },

        binding: function (arrow, syntax, scope, stringify) {

            var header = stringify(syntax.args[0], scope) + " " + arrow + " " + stringify(syntax.args[1], scope);
            var trailer = "";

            var descriptor = syntax.descriptor;
            if (descriptor) {
                for (var name in descriptor) {
                    trailer += ", " + name + ": " + stringify(descriptor[name], scope);
                }
            }

            return header + trailer;
        },

        bind: function (syntax, scope, stringify) {
            return this.binding("<-", syntax, scope, stringify);
        },

        bind2: function (syntax, scope, stringify) {
            return this.binding("<->", syntax, scope, stringify);
        },

        assign: function (syntax, scope, stringify) {
            return stringify(syntax.args[0], scope) + ": " + stringify(syntax.args[1], scope);
        },

        block: function (syntax, scope, stringify) {
            var header = "@" + syntax.label;
            if (syntax.connection) {
                if (syntax.connection === "prototype") {
                    header += " < ";
                } else if (syntax.connection === "object") {
                    header += " : ";
                }
                header += stringify({type: 'literal', value: syntax.module});
                if (syntax.exports && syntax.exports.type !== "value") {
                    header += " " + stringify(syntax.exports, scope);
                }
            }
            return header + " {\n" + syntax.statements.map(function (statement) {
                return "    " + stringify(statement, scope) + ";\n";
            }).join("") + "}\n";
        },

        sheet: function (syntax, scope, stringify) {
            return "\n" + syntax.blocks.map(function (block) {
                return stringify(block, scope);
            }).join("\n") + "\n";
        }

    }

};

// book a stringifier for all the defined symbolic operators
typeToToken.forEach(function (token, type) {
    stringify.semantics.stringifiers[type] = function (syntax, scope, stringify) {
        return syntax.args.map(function (child) {
            return stringify(child, scope, syntax);
        }).join(" " + token + " ").trim();
    }
});


}})
;
//*/
montageDefine("73de13b","expand",{dependencies:["collections/set","collections/map","./operators"],factory:function(require,exports,module){
var Set = require("collections/set");
var Map = require("collections/map");
var Operators = require("./operators");

module.exports = expand;
function expand(syntax, scope) {
    var bound = expand.semantics.expand.bind(expand.semantics);
    return bound(syntax, scope, bound);
}

expand.semantics = {

    reflexive: Set([
        "literal",
        "element",
        "rangeContent",
        "mapContent"
    ]),

    traverseLeft: Set([
        "with",
        "mapBlock",
        "filterBlock",
        "someBlock",
        "everyBlock",
        "sortedBlock",
        "groupBlock",
        "groupMapBlock"
    ]),

    expanders: Map({
        value: function (syntax, scope) {
            return scope.value || {"type": "value"};
        },
        parameters: function (syntax, scope) {
            return scope.parameters || {"type": "parameters"};
        },
        record: function (syntax, scope, expand) {
            var expanded = {type: "record", args: []};
            for (var name in syntax.args) {
                expanded.args[name] = expand(syntax.args[name], scope, expand);
            }
            return expanded;
        },
        component: function (syntax, scope, expand) {
            if (scope.components && syntax.component) {
                return {
                    type: "component",
                    label: scope.components.getObjectLabel(syntax.component)
                };
            } else {
                return syntax;
            }
        }
    }),

    expand: function (syntax, scope, expand) {
        if (this.expanders.has(syntax.type)) {
            return this.expanders.get(syntax.type)(syntax, scope, expand);
        } else if (this.traverseLeft.has(syntax.type)) {
            return {type: syntax.type, args: [
                expand(syntax.args[0], scope, expand)
            ].concat(syntax.args.slice(1))};
        } else if (this.reflexive.has(syntax.type)) {
            return syntax;
        } else {
            return {type: syntax.type, args: syntax.args.map(function (arg) {
                return expand(arg, scope, expand);
            })};
        }
    }

};


}})
;
//*/
montageDefine("73de13b","bindings",{dependencies:["collections/map","./bind","./compute","./observe"],factory:function(require,exports,module){
var Map = require("collections/map");
var bind = require("./bind");
var compute = require("./compute");
var observe = require("./observe");

var bindingsForObject = new Map();
var owns = Object.prototype.hasOwnProperty;

exports.count = 0;
exports.bindings = bindingsForObject;

exports.defineBindings = defineBindings;
function defineBindings(object, descriptors, commonDescriptor) {
    if (descriptors) {
        for (var name in descriptors) {
            defineBinding(object, name, descriptors[name], commonDescriptor);
        }
    }
    return object;
}

exports.defineBinding = defineBinding;
function defineBinding(object, name, descriptor, commonDescriptor) {
    commonDescriptor = commonDescriptor || Object.empty;
    var bindingsForName = getBindings(object);
    if (owns.call(bindingsForName, name)) {
        throw new Error("Can't bind to already bound target, " + JSON.stringify(name));
    }
    if ("<-" in descriptor || "<->" in descriptor || "compute" in descriptor) {
        descriptor.target = object;
        descriptor.parameters = descriptor.parameters || commonDescriptor.parameters;
        descriptor.document = descriptor.document || commonDescriptor.document;
        descriptor.components = descriptor.components || commonDescriptor.components;
        if ("compute" in descriptor) {
            descriptor.cancel = compute(object, name, descriptor);
        } else {
            descriptor.cancel = bind(object, name, descriptor);
        }
        bindingsForName[name] = descriptor;
        exports.count++;
    } else {
        if (!("get" in descriptor) && !("set" in descriptor) && !("writable" in descriptor)) {
            descriptor.writable = true;
        }
        if (!("configurable" in descriptor)) {
            descriptor.configurable = true;
        }
        if (!("enumerable" in descriptor)) {
            descriptor.enumerable = true;
        }
        Object.defineProperty(object, name, descriptor);
    }
    return object;
}

exports.getBindings = getBindings;
function getBindings(object) {
    if (!bindingsForObject.has(object)) {
        bindingsForObject.set(object, {});
    }
    return bindingsForObject.get(object);
}

exports.getBinding = getBinding;
function getBinding(object, name) {
    var bindingsForName = getBindings(object);
    return bindingsForName[name];
}

exports.cancelBindings = cancelBindings;
function cancelBindings(object) {
    var bindings = getBindings(object);
    for (var name in bindings) {
        cancelBinding(object, name);
    }
}

exports.cancelBinding = cancelBinding;
function cancelBinding(object, name) {
    var bindings = getBindings(object);
    if (!bindings[name]) {
        throw new Error("Can't cancel non-existent binding to " + JSON.stringify(name));
    }
    var binding = bindings[name];
    if (binding && binding.cancel) {
        binding.cancel();
        delete bindings[name];
        exports.count--;
        for (var name in bindings) {
            return; // if there are any remaining bindings, short-circuit
        }
        bindingsForObject["delete"](object);
    }
}


}})
;
//*/
montageDefine("73de13b","grammar",{dependencies:[],factory:function(require,exports,module){module.exports = (function() {
  /*
   * Generated by PEG.js 0.7.0.
   *
   * http://pegjs.majda.cz/
   */

  function peg$subclass(child, parent) {
    function ctor() { this.constructor = child; }
    ctor.prototype = parent.prototype;
    child.prototype = new ctor();
  }

  function SyntaxError(expected, found, offset, line, column) {
    function buildMessage(expected, found) {
      function stringEscape(s) {
        function hex(ch) { return ch.charCodeAt(0).toString(16).toUpperCase(); }

        return s
          .replace(/\\/g,   '\\\\')
          .replace(/"/g,    '\\"')
          .replace(/\x08/g, '\\b')
          .replace(/\t/g,   '\\t')
          .replace(/\n/g,   '\\n')
          .replace(/\f/g,   '\\f')
          .replace(/\r/g,   '\\r')
          .replace(/[\x00-\x07\x0B\x0E\x0F]/g, function(ch) { return '\\x0' + hex(ch); })
          .replace(/[\x10-\x1F\x80-\xFF]/g,    function(ch) { return '\\x'  + hex(ch); })
          .replace(/[\u0180-\u0FFF]/g,         function(ch) { return '\\u0' + hex(ch); })
          .replace(/[\u1080-\uFFFF]/g,         function(ch) { return '\\u'  + hex(ch); });
      }

      var expectedDesc, foundDesc;

      switch (expected.length) {
        case 0:
          expectedDesc = "end of input";
          break;

        case 1:
          expectedDesc = expected[0];
          break;

        default:
          expectedDesc = expected.slice(0, -1).join(", ")
            + " or "
            + expected[expected.length - 1];
      }

      foundDesc = found ? "\"" + stringEscape(found) + "\"" : "end of input";

      return "Expected " + expectedDesc + " but " + foundDesc + " found.";
    }

    this.expected = expected;
    this.found    = found;
    this.offset   = offset;
    this.line     = line;
    this.column   = column;

    this.name     = "SyntaxError";
    this.message  = buildMessage(expected, found);
  }

  peg$subclass(SyntaxError, Error);

  function parse(input) {
    var options = arguments.length > 1 ? arguments[1] : {},

        peg$startRuleFunctions = { expression: peg$parseexpression, sheet: peg$parsesheet },
        peg$startRuleFunction  = peg$parseexpression,

        peg$c0 = "expression",
        peg$c1 = null,
        peg$c2 = [],
        peg$c3 = ",",
        peg$c4 = "\",\"",
        peg$c5 = function(head, tail) {
                var result = [head];
                for (var i = 0; i < tail.length; i++) {
                    result.push(tail[i][2]);
                }
                return result;
            },
        peg$c6 = "(",
        peg$c7 = "\"(\"",
        peg$c8 = ")",
        peg$c9 = "\")\"",
        peg$c10 = function() {
                return [];
            },
        peg$c11 = function(expressions) {
                return expressions;
            },
        peg$c12 = "",
        peg$c13 = "?",
        peg$c14 = "\"?\"",
        peg$c15 = ":",
        peg$c16 = "\":\"",
        peg$c17 = function(condition, tail) {
                if (tail) {
                    var consequent = tail[2];
                    var alternate = tail[6];
                    return {
                        type: "if",
                        args: [condition, consequent, alternate]
                    };
                } else {
                    return condition;
                }
            },
        peg$c18 = "||",
        peg$c19 = "\"||\"",
        peg$c20 = function(head, tail) {
                for (var i = 0; i < tail.length; i++) {
                    head = {
                        type: BINARY[tail[i][1]],
                        args: [
                            head,
                            tail[i][3]
                        ]
                    }
                }
                return head;
            },
        peg$c21 = "&&",
        peg$c22 = "\"&&\"",
        peg$c23 = "<=>",
        peg$c24 = "\"<=>\"",
        peg$c25 = "<=",
        peg$c26 = "\"<=\"",
        peg$c27 = ">=",
        peg$c28 = "\">=\"",
        peg$c29 = "<",
        peg$c30 = "\"<\"",
        peg$c31 = "-",
        peg$c32 = "\"-\"",
        peg$c33 = ">",
        peg$c34 = "\">\"",
        peg$c35 = "==",
        peg$c36 = "\"==\"",
        peg$c37 = "!=",
        peg$c38 = "\"!=\"",
        peg$c39 = function(left, tail) {
                if (!tail) {
                    return left;
                } else {
                    var operator = tail[1];
                    var right = tail[3];
                    if (operator === "!=") {
                        return {type: "not", args: [{type: "equals", args: [left, right]}]};
                    } else {
                        return {type: BINARY[operator], args: [left, right]};
                    }
                }
            },
        peg$c40 = "+",
        peg$c41 = "\"+\"",
        peg$c42 = "*",
        peg$c43 = "\"*\"",
        peg$c44 = "/",
        peg$c45 = "\"/\"",
        peg$c46 = "%",
        peg$c47 = "\"%\"",
        peg$c48 = "rem",
        peg$c49 = "\"rem\"",
        peg$c50 = "**",
        peg$c51 = "\"**\"",
        peg$c52 = "//",
        peg$c53 = "\"//\"",
        peg$c54 = "%%",
        peg$c55 = "\"%%\"",
        peg$c56 = "??",
        peg$c57 = "\"??\"",
        peg$c58 = "!",
        peg$c59 = "\"!\"",
        peg$c60 = function(operator, arg) {
                return {type: UNARY[operator], args: [arg]};
            },
        peg$c61 = function(head, tail) {
                for (var i = 0; i < tail.length; i++) {
                    head = tail[i](head);
                }
                return head;
            },
        peg$c62 = ".",
        peg$c63 = "\".\"",
        peg$c64 = function(tail) {
                return tail;
            },
        peg$c65 = "[",
        peg$c66 = "\"[\"",
        peg$c67 = "]",
        peg$c68 = "\"]\"",
        peg$c69 = function(arg) {
                return function (previous) {
                    return {
                        type: "property",
                        args: [
                            previous,
                            arg
                        ]
                    };
                };
            },
        peg$c70 = "{",
        peg$c71 = "\"{\"",
        peg$c72 = "}",
        peg$c73 = "\"}\"",
        peg$c74 = function(name, expression) {
                if (BLOCKS[name]) {
                    return function (previous) {
                        return {
                            type: BLOCKS[name],
                            args: [previous, expression]
                        };
                    }
                } else if (expression.type === "value") {
                    return function (previous) {
                        return {
                            type: name,
                            args: [previous]
                        };
                    };
                } else {
                    return function (previous) {
                        return {
                            type: name,
                            args: [
                                {type: "mapBlock", args: [
                                    previous,
                                    expression
                                ]}
                            ]
                        };
                    };
                }
            },
        peg$c75 = function(name, args) {
                return function (previous) {
                    return {
                        type: name,
                        args: [previous].concat(args)
                    };
                };
            },
        peg$c76 = function(index) {
                return function (previous) {
                    return {
                        type: "property",
                        args: [
                            previous,
                            {type: "literal", value: +index.join("")}
                        ]
                    };
                };
            },
        peg$c77 = function(name) {
                return function (previous) {
                    return {
                        type: "property",
                        args: [
                            previous,
                            {type: "literal", value: name}
                        ]
                    };
                };
            },
        peg$c78 = function(expression) {
                return function (previous) {
                    return {
                        type: "with",
                        args: [
                            previous,
                            expression
                        ]
                    };
                };
            },
        peg$c79 = "this",
        peg$c80 = "\"this\"",
        peg$c81 = function() { return {type: "value"}; },
        peg$c82 = "true",
        peg$c83 = "\"true\"",
        peg$c84 = function() { return {type: "literal", value: true}; },
        peg$c85 = "false",
        peg$c86 = "\"false\"",
        peg$c87 = function() { return {type: "literal", value: false}; },
        peg$c88 = "null",
        peg$c89 = "\"null\"",
        peg$c90 = function() { return {type: "literal", value: null}; },
        peg$c91 = "@",
        peg$c92 = "\"@\"",
        peg$c93 = function(name) {
                return {type: "component", label: name};
            },
        peg$c94 = "$",
        peg$c95 = "\"$\"",
        peg$c96 = function(name) {
                return {type: "property", args: [
                    {type: "parameters"},
                    {type: "literal", value: name}
                ]};
            },
        peg$c97 = function() {
                return {type: "parameters"};
            },
        peg$c98 = "#",
        peg$c99 = "\"#\"",
        peg$c100 = function(name) {
                return {type: "element", id: name};
            },
        peg$c101 = "&",
        peg$c102 = "\"&\"",
        peg$c103 = function(name, args) {
                return {type: name, args: args, inline: true};
            },
        peg$c104 = "^",
        peg$c105 = "\"^\"",
        peg$c106 = function(value) {
                return {type: "parent", args: [value]};
            },
        peg$c107 = function(expression) {
                return expression;
            },
        peg$c108 = function(tail) {
                return tail({type: "value"});
            },
        peg$c109 = function() {
                return {type: "value"};
            },
        peg$c110 = "word",
        peg$c111 = /^[a-zA-Z_0-9\-]/,
        peg$c112 = "[a-zA-Z_0-9\\-]",
        peg$c113 = function(chars) {
                return chars.join("");
            },
        peg$c114 = "string",
        peg$c115 = "'",
        peg$c116 = "\"'\"",
        peg$c117 = function(chars) { return {type: "literal", value: chars.join("")}; },
        peg$c118 = "\"",
        peg$c119 = "\"\\\"\"",
        peg$c120 = /^[^'\\\0-\x1F]/,
        peg$c121 = "[^'\\\\\\0-\\x1F]",
        peg$c122 = "\\'",
        peg$c123 = "\"\\\\'\"",
        peg$c124 = function() { return "'";  },
        peg$c125 = /^[^"\\\0-\x1F]/,
        peg$c126 = "[^\"\\\\\\0-\\x1F]",
        peg$c127 = "\\\"",
        peg$c128 = "\"\\\\\\\"\"",
        peg$c129 = function() { return "\"";  },
        peg$c130 = "\\\\",
        peg$c131 = "\"\\\\\\\\\"",
        peg$c132 = function() { return "\\"; },
        peg$c133 = "\\/",
        peg$c134 = "\"\\\\/\"",
        peg$c135 = function() { return "/";  },
        peg$c136 = "\\b",
        peg$c137 = "\"\\\\b\"",
        peg$c138 = function() { return "\b"; },
        peg$c139 = "\\f",
        peg$c140 = "\"\\\\f\"",
        peg$c141 = function() { return "\f"; },
        peg$c142 = "\\n",
        peg$c143 = "\"\\\\n\"",
        peg$c144 = function() { return "\n"; },
        peg$c145 = "\\r",
        peg$c146 = "\"\\\\r\"",
        peg$c147 = function() { return "\r"; },
        peg$c148 = "\\t",
        peg$c149 = "\"\\\\t\"",
        peg$c150 = function() { return "\t"; },
        peg$c151 = "\\0",
        peg$c152 = "\"\\\\0\"",
        peg$c153 = function() { return "\0"; },
        peg$c154 = "\\u",
        peg$c155 = "\"\\\\u\"",
        peg$c156 = function(digits) {
                return String.fromCharCode(parseInt(digits, 16));
            },
        peg$c157 = /^[0-9a-fA-F]/,
        peg$c158 = "[0-9a-fA-F]",
        peg$c159 = function() {
                return {type: "tuple", args: []};
            },
        peg$c160 = function(expressions) {
                return {type: "tuple", args: expressions};
            },
        peg$c161 = function() { return {type: "record", args: []}; },
        peg$c162 = function(pairs) { return {type: "record", args: pairs}; },
        peg$c163 = function(head, tail) {
                var result = {};
                result[head[0]] = head[1];
                for (var i = 0; i < tail.length; i++) {
                    result[tail[i][2][0]] = tail[i][2][1];
                }
                return result;
            },
        peg$c164 = function(name, value) { return [name, value]; },
        peg$c165 = "number",
        peg$c166 = function(parts) {
                return {type: "literal", value: +parts}
            },
        peg$c167 = /^[eE]/,
        peg$c168 = "[eE]",
        peg$c169 = /^[+\-]/,
        peg$c170 = "[+\\-]",
        peg$c171 = /^[0-9]/,
        peg$c172 = "[0-9]",
        peg$c173 = /^[1-9]/,
        peg$c174 = "[1-9]",
        peg$c175 = "whitespace",
        peg$c176 = /^[\t\x0B\f \xA0\uFEFF]/,
        peg$c177 = "[\\t\\x0B\\f \\xA0\\uFEFF]",
        peg$c178 = /^[ \xA0\u1680\u180E\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u202F\u205F\u3000]/,
        peg$c179 = "[ \\xA0\\u1680\\u180E\\u2000\\u2001\\u2002\\u2003\\u2004\\u2005\\u2006\\u2007\\u2008\\u2009\\u200A\\u202F\\u205F\\u3000]",
        peg$c180 = "line terminator",
        peg$c181 = /^[\n\r\u2028\u2029]/,
        peg$c182 = "[\\n\\r\\u2028\\u2029]",
        peg$c183 = "/*",
        peg$c184 = "\"/*\"",
        peg$c185 = "*/",
        peg$c186 = "\"*/\"",
        peg$c187 = "any character",
        peg$c188 = function(comment) {
                return comment;
            },
        peg$c189 = function(blocks) {
                return {type: "sheet", blocks: blocks};
            },
        peg$c190 = function(name, annotation, statements) {
                return {
                    type: "block",
                    connection: annotation.connection,
                    module: annotation.module,
                    exports: annotation.exports,
                    label: name,
                    statements: statements
                };
            },
        peg$c191 = function(connection, module, exports) {
                return {
                    connection: {"<": "prototype", ":": "object"}[connection],
                    module: module && module.value,
                    exports: exports !== "" ? exports[1] : undefined
                }
            },
        peg$c192 = function() {
                return {};
            },
        peg$c193 = ";",
        peg$c194 = "\";\"",
        peg$c195 = function(statement) {
                return [statement];
            },
        peg$c196 = "on",
        peg$c197 = "\"on\"",
        peg$c198 = "before",
        peg$c199 = "\"before\"",
        peg$c200 = " ",
        peg$c201 = "\" \"",
        peg$c202 = "->",
        peg$c203 = "\"->\"",
        peg$c204 = function(when, type, listener) {
                return {type: "event", when: when, event: type, listener: listener};
            },
        peg$c205 = "<->",
        peg$c206 = "\"<->\"",
        peg$c207 = "<-",
        peg$c208 = "\"<-\"",
        peg$c209 = function(target, arrow, source, descriptor) {
                var result = {type: STATEMENTS[arrow], args: [
                    target,
                    source
                ]};
                if (descriptor.length) {
                    var describe = {};
                    for (var i = 0; i < descriptor.length; i++) {
                        describe[descriptor[i][2]] = descriptor[i][6];
                    }
                    result.descriptor = describe;
                }
                return result;
            },
        peg$c210 = function(name, expression) {
                return {type: "unit", name: name, value: expression};
            },

        peg$currPos          = 0,
        peg$reportedPos      = 0,
        peg$cachedPos        = 0,
        peg$cachedPosDetails = { line: 1, column: 1, seenCR: false },
        peg$maxFailPos       = 0,
        peg$maxFailExpected  = [],
        peg$silentFails      = 0,

        peg$result;

    if ("startRule" in options) {
      if (!(options.startRule in peg$startRuleFunctions)) {
        throw new Error("Can't start parsing from rule \"" + options.startRule + "\".");
      }

      peg$startRuleFunction = peg$startRuleFunctions[options.startRule];
    }

    function text() {
      return input.substring(peg$reportedPos, peg$currPos);
    }

    function offset() {
      return peg$reportedPos;
    }

    function line() {
      return peg$computePosDetails(peg$reportedPos).line;
    }

    function column() {
      return peg$computePosDetails(peg$reportedPos).column;
    }

    function peg$computePosDetails(pos) {
      function advance(details, startPos, endPos) {
        var p, ch;

        for (p = startPos; p < endPos; p++) {
          ch = input.charAt(p);
          if (ch === "\n") {
            if (!details.seenCR) { details.line++; }
            details.column = 1;
            details.seenCR = false;
          } else if (ch === "\r" || ch === "\u2028" || ch === "\u2029") {
            details.line++;
            details.column = 1;
            details.seenCR = true;
          } else {
            details.column++;
            details.seenCR = false;
          }
        }
      }

      if (peg$cachedPos !== pos) {
        if (peg$cachedPos > pos) {
          peg$cachedPos = 0;
          peg$cachedPosDetails = { line: 1, column: 1, seenCR: false };
        }
        advance(peg$cachedPosDetails, peg$cachedPos, pos);
        peg$cachedPos = pos;
      }

      return peg$cachedPosDetails;
    }

    function peg$fail(expected) {
      if (peg$currPos < peg$maxFailPos) { return; }

      if (peg$currPos > peg$maxFailPos) {
        peg$maxFailPos = peg$currPos;
        peg$maxFailExpected = [];
      }

      peg$maxFailExpected.push(expected);
    }

    function peg$cleanupExpected(expected) {
      var i = 0;

      expected.sort();

      while (i < expected.length) {
        if (expected[i - 1] === expected[i]) {
          expected.splice(i, 1);
        } else {
          i++;
        }
      }
    }

    function peg$parseexpression() {
      var s0, s1;

      peg$silentFails++;
      s0 = peg$parseif();
      peg$silentFails--;
      if (s0 === null) {
        s1 = null;
        if (peg$silentFails === 0) { peg$fail(peg$c0); }
      }

      return s0;
    }

    function peg$parseexpressions() {
      var s0, s1, s2, s3, s4, s5, s6;

      s0 = peg$currPos;
      s1 = peg$parseexpression();
      if (s1 !== null) {
        s2 = [];
        s3 = peg$currPos;
        if (input.charCodeAt(peg$currPos) === 44) {
          s4 = peg$c3;
          peg$currPos++;
        } else {
          s4 = null;
          if (peg$silentFails === 0) { peg$fail(peg$c4); }
        }
        if (s4 !== null) {
          s5 = peg$parse_();
          if (s5 !== null) {
            s6 = peg$parseexpression();
            if (s6 !== null) {
              s4 = [s4, s5, s6];
              s3 = s4;
            } else {
              peg$currPos = s3;
              s3 = peg$c1;
            }
          } else {
            peg$currPos = s3;
            s3 = peg$c1;
          }
        } else {
          peg$currPos = s3;
          s3 = peg$c1;
        }
        while (s3 !== null) {
          s2.push(s3);
          s3 = peg$currPos;
          if (input.charCodeAt(peg$currPos) === 44) {
            s4 = peg$c3;
            peg$currPos++;
          } else {
            s4 = null;
            if (peg$silentFails === 0) { peg$fail(peg$c4); }
          }
          if (s4 !== null) {
            s5 = peg$parse_();
            if (s5 !== null) {
              s6 = peg$parseexpression();
              if (s6 !== null) {
                s4 = [s4, s5, s6];
                s3 = s4;
              } else {
                peg$currPos = s3;
                s3 = peg$c1;
              }
            } else {
              peg$currPos = s3;
              s3 = peg$c1;
            }
          } else {
            peg$currPos = s3;
            s3 = peg$c1;
          }
        }
        if (s2 !== null) {
          peg$reportedPos = s0;
          s1 = peg$c5(s1,s2);
          if (s1 === null) {
            peg$currPos = s0;
            s0 = s1;
          } else {
            s0 = s1;
          }
        } else {
          peg$currPos = s0;
          s0 = peg$c1;
        }
      } else {
        peg$currPos = s0;
        s0 = peg$c1;
      }

      return s0;
    }

    function peg$parseargs() {
      var s0, s1, s2, s3;

      s0 = peg$currPos;
      if (input.charCodeAt(peg$currPos) === 40) {
        s1 = peg$c6;
        peg$currPos++;
      } else {
        s1 = null;
        if (peg$silentFails === 0) { peg$fail(peg$c7); }
      }
      if (s1 !== null) {
        s2 = peg$parse_();
        if (s2 !== null) {
          if (input.charCodeAt(peg$currPos) === 41) {
            s3 = peg$c8;
            peg$currPos++;
          } else {
            s3 = null;
            if (peg$silentFails === 0) { peg$fail(peg$c9); }
          }
          if (s3 !== null) {
            peg$reportedPos = s0;
            s1 = peg$c10();
            if (s1 === null) {
              peg$currPos = s0;
              s0 = s1;
            } else {
              s0 = s1;
            }
          } else {
            peg$currPos = s0;
            s0 = peg$c1;
          }
        } else {
          peg$currPos = s0;
          s0 = peg$c1;
        }
      } else {
        peg$currPos = s0;
        s0 = peg$c1;
      }
      if (s0 === null) {
        s0 = peg$currPos;
        if (input.charCodeAt(peg$currPos) === 40) {
          s1 = peg$c6;
          peg$currPos++;
        } else {
          s1 = null;
          if (peg$silentFails === 0) { peg$fail(peg$c7); }
        }
        if (s1 !== null) {
          s2 = peg$parseexpressions();
          if (s2 !== null) {
            if (input.charCodeAt(peg$currPos) === 41) {
              s3 = peg$c8;
              peg$currPos++;
            } else {
              s3 = null;
              if (peg$silentFails === 0) { peg$fail(peg$c9); }
            }
            if (s3 !== null) {
              peg$reportedPos = s0;
              s1 = peg$c11(s2);
              if (s1 === null) {
                peg$currPos = s0;
                s0 = s1;
              } else {
                s0 = s1;
              }
            } else {
              peg$currPos = s0;
              s0 = peg$c1;
            }
          } else {
            peg$currPos = s0;
            s0 = peg$c1;
          }
        } else {
          peg$currPos = s0;
          s0 = peg$c1;
        }
      }

      return s0;
    }

    function peg$parseif() {
      var s0, s1, s2, s3, s4, s5, s6, s7, s8, s9, s10;

      s0 = peg$currPos;
      s1 = peg$parseor();
      if (s1 !== null) {
        s2 = peg$parse_();
        if (s2 !== null) {
          s3 = peg$currPos;
          if (input.charCodeAt(peg$currPos) === 63) {
            s4 = peg$c13;
            peg$currPos++;
          } else {
            s4 = null;
            if (peg$silentFails === 0) { peg$fail(peg$c14); }
          }
          if (s4 !== null) {
            s5 = peg$parse_();
            if (s5 !== null) {
              s6 = peg$parseexpression();
              if (s6 !== null) {
                s7 = peg$parse_();
                if (s7 !== null) {
                  if (input.charCodeAt(peg$currPos) === 58) {
                    s8 = peg$c15;
                    peg$currPos++;
                  } else {
                    s8 = null;
                    if (peg$silentFails === 0) { peg$fail(peg$c16); }
                  }
                  if (s8 !== null) {
                    s9 = peg$parse_();
                    if (s9 !== null) {
                      s10 = peg$parseexpression();
                      if (s10 !== null) {
                        s4 = [s4, s5, s6, s7, s8, s9, s10];
                        s3 = s4;
                      } else {
                        peg$currPos = s3;
                        s3 = peg$c1;
                      }
                    } else {
                      peg$currPos = s3;
                      s3 = peg$c1;
                    }
                  } else {
                    peg$currPos = s3;
                    s3 = peg$c1;
                  }
                } else {
                  peg$currPos = s3;
                  s3 = peg$c1;
                }
              } else {
                peg$currPos = s3;
                s3 = peg$c1;
              }
            } else {
              peg$currPos = s3;
              s3 = peg$c1;
            }
          } else {
            peg$currPos = s3;
            s3 = peg$c1;
          }
          if (s3 === null) {
            s3 = peg$c12;
          }
          if (s3 !== null) {
            peg$reportedPos = s0;
            s1 = peg$c17(s1,s3);
            if (s1 === null) {
              peg$currPos = s0;
              s0 = s1;
            } else {
              s0 = s1;
            }
          } else {
            peg$currPos = s0;
            s0 = peg$c1;
          }
        } else {
          peg$currPos = s0;
          s0 = peg$c1;
        }
      } else {
        peg$currPos = s0;
        s0 = peg$c1;
      }

      return s0;
    }

    function peg$parseor() {
      var s0, s1, s2, s3, s4, s5, s6, s7;

      s0 = peg$currPos;
      s1 = peg$parseand();
      if (s1 !== null) {
        s2 = [];
        s3 = peg$currPos;
        s4 = peg$parse_();
        if (s4 !== null) {
          if (input.substr(peg$currPos, 2) === peg$c18) {
            s5 = peg$c18;
            peg$currPos += 2;
          } else {
            s5 = null;
            if (peg$silentFails === 0) { peg$fail(peg$c19); }
          }
          if (s5 !== null) {
            s6 = peg$parse_();
            if (s6 !== null) {
              s7 = peg$parseand();
              if (s7 !== null) {
                s4 = [s4, s5, s6, s7];
                s3 = s4;
              } else {
                peg$currPos = s3;
                s3 = peg$c1;
              }
            } else {
              peg$currPos = s3;
              s3 = peg$c1;
            }
          } else {
            peg$currPos = s3;
            s3 = peg$c1;
          }
        } else {
          peg$currPos = s3;
          s3 = peg$c1;
        }
        while (s3 !== null) {
          s2.push(s3);
          s3 = peg$currPos;
          s4 = peg$parse_();
          if (s4 !== null) {
            if (input.substr(peg$currPos, 2) === peg$c18) {
              s5 = peg$c18;
              peg$currPos += 2;
            } else {
              s5 = null;
              if (peg$silentFails === 0) { peg$fail(peg$c19); }
            }
            if (s5 !== null) {
              s6 = peg$parse_();
              if (s6 !== null) {
                s7 = peg$parseand();
                if (s7 !== null) {
                  s4 = [s4, s5, s6, s7];
                  s3 = s4;
                } else {
                  peg$currPos = s3;
                  s3 = peg$c1;
                }
              } else {
                peg$currPos = s3;
                s3 = peg$c1;
              }
            } else {
              peg$currPos = s3;
              s3 = peg$c1;
            }
          } else {
            peg$currPos = s3;
            s3 = peg$c1;
          }
        }
        if (s2 !== null) {
          peg$reportedPos = s0;
          s1 = peg$c20(s1,s2);
          if (s1 === null) {
            peg$currPos = s0;
            s0 = s1;
          } else {
            s0 = s1;
          }
        } else {
          peg$currPos = s0;
          s0 = peg$c1;
        }
      } else {
        peg$currPos = s0;
        s0 = peg$c1;
      }

      return s0;
    }

    function peg$parseand() {
      var s0, s1, s2, s3, s4, s5, s6, s7;

      s0 = peg$currPos;
      s1 = peg$parsecomparison();
      if (s1 !== null) {
        s2 = [];
        s3 = peg$currPos;
        s4 = peg$parse_();
        if (s4 !== null) {
          if (input.substr(peg$currPos, 2) === peg$c21) {
            s5 = peg$c21;
            peg$currPos += 2;
          } else {
            s5 = null;
            if (peg$silentFails === 0) { peg$fail(peg$c22); }
          }
          if (s5 !== null) {
            s6 = peg$parse_();
            if (s6 !== null) {
              s7 = peg$parsecomparison();
              if (s7 !== null) {
                s4 = [s4, s5, s6, s7];
                s3 = s4;
              } else {
                peg$currPos = s3;
                s3 = peg$c1;
              }
            } else {
              peg$currPos = s3;
              s3 = peg$c1;
            }
          } else {
            peg$currPos = s3;
            s3 = peg$c1;
          }
        } else {
          peg$currPos = s3;
          s3 = peg$c1;
        }
        while (s3 !== null) {
          s2.push(s3);
          s3 = peg$currPos;
          s4 = peg$parse_();
          if (s4 !== null) {
            if (input.substr(peg$currPos, 2) === peg$c21) {
              s5 = peg$c21;
              peg$currPos += 2;
            } else {
              s5 = null;
              if (peg$silentFails === 0) { peg$fail(peg$c22); }
            }
            if (s5 !== null) {
              s6 = peg$parse_();
              if (s6 !== null) {
                s7 = peg$parsecomparison();
                if (s7 !== null) {
                  s4 = [s4, s5, s6, s7];
                  s3 = s4;
                } else {
                  peg$currPos = s3;
                  s3 = peg$c1;
                }
              } else {
                peg$currPos = s3;
                s3 = peg$c1;
              }
            } else {
              peg$currPos = s3;
              s3 = peg$c1;
            }
          } else {
            peg$currPos = s3;
            s3 = peg$c1;
          }
        }
        if (s2 !== null) {
          peg$reportedPos = s0;
          s1 = peg$c20(s1,s2);
          if (s1 === null) {
            peg$currPos = s0;
            s0 = s1;
          } else {
            s0 = s1;
          }
        } else {
          peg$currPos = s0;
          s0 = peg$c1;
        }
      } else {
        peg$currPos = s0;
        s0 = peg$c1;
      }

      return s0;
    }

    function peg$parsecomparison() {
      var s0, s1, s2, s3, s4, s5, s6, s7, s8;

      s0 = peg$currPos;
      s1 = peg$parsearithmetic();
      if (s1 !== null) {
        s2 = peg$currPos;
        s3 = peg$parse_();
        if (s3 !== null) {
          s4 = peg$currPos;
          if (input.substr(peg$currPos, 3) === peg$c23) {
            s5 = peg$c23;
            peg$currPos += 3;
          } else {
            s5 = null;
            if (peg$silentFails === 0) { peg$fail(peg$c24); }
          }
          if (s5 === null) {
            if (input.substr(peg$currPos, 2) === peg$c25) {
              s5 = peg$c25;
              peg$currPos += 2;
            } else {
              s5 = null;
              if (peg$silentFails === 0) { peg$fail(peg$c26); }
            }
            if (s5 === null) {
              if (input.substr(peg$currPos, 2) === peg$c27) {
                s5 = peg$c27;
                peg$currPos += 2;
              } else {
                s5 = null;
                if (peg$silentFails === 0) { peg$fail(peg$c28); }
              }
              if (s5 === null) {
                s5 = peg$currPos;
                if (input.charCodeAt(peg$currPos) === 60) {
                  s6 = peg$c29;
                  peg$currPos++;
                } else {
                  s6 = null;
                  if (peg$silentFails === 0) { peg$fail(peg$c30); }
                }
                if (s6 !== null) {
                  s7 = peg$currPos;
                  peg$silentFails++;
                  if (input.charCodeAt(peg$currPos) === 45) {
                    s8 = peg$c31;
                    peg$currPos++;
                  } else {
                    s8 = null;
                    if (peg$silentFails === 0) { peg$fail(peg$c32); }
                  }
                  peg$silentFails--;
                  if (s8 === null) {
                    s7 = peg$c12;
                  } else {
                    peg$currPos = s7;
                    s7 = peg$c1;
                  }
                  if (s7 !== null) {
                    s6 = [s6, s7];
                    s5 = s6;
                  } else {
                    peg$currPos = s5;
                    s5 = peg$c1;
                  }
                } else {
                  peg$currPos = s5;
                  s5 = peg$c1;
                }
                if (s5 === null) {
                  if (input.charCodeAt(peg$currPos) === 62) {
                    s5 = peg$c33;
                    peg$currPos++;
                  } else {
                    s5 = null;
                    if (peg$silentFails === 0) { peg$fail(peg$c34); }
                  }
                  if (s5 === null) {
                    if (input.substr(peg$currPos, 2) === peg$c35) {
                      s5 = peg$c35;
                      peg$currPos += 2;
                    } else {
                      s5 = null;
                      if (peg$silentFails === 0) { peg$fail(peg$c36); }
                    }
                    if (s5 === null) {
                      if (input.substr(peg$currPos, 2) === peg$c37) {
                        s5 = peg$c37;
                        peg$currPos += 2;
                      } else {
                        s5 = null;
                        if (peg$silentFails === 0) { peg$fail(peg$c38); }
                      }
                    }
                  }
                }
              }
            }
          }
          if (s5 !== null) {
            s5 = input.substring(s4, peg$currPos);
          }
          s4 = s5;
          if (s4 !== null) {
            s5 = peg$parse_();
            if (s5 !== null) {
              s6 = peg$parsearithmetic();
              if (s6 !== null) {
                s3 = [s3, s4, s5, s6];
                s2 = s3;
              } else {
                peg$currPos = s2;
                s2 = peg$c1;
              }
            } else {
              peg$currPos = s2;
              s2 = peg$c1;
            }
          } else {
            peg$currPos = s2;
            s2 = peg$c1;
          }
        } else {
          peg$currPos = s2;
          s2 = peg$c1;
        }
        if (s2 === null) {
          s2 = peg$c12;
        }
        if (s2 !== null) {
          peg$reportedPos = s0;
          s1 = peg$c39(s1,s2);
          if (s1 === null) {
            peg$currPos = s0;
            s0 = s1;
          } else {
            s0 = s1;
          }
        } else {
          peg$currPos = s0;
          s0 = peg$c1;
        }
      } else {
        peg$currPos = s0;
        s0 = peg$c1;
      }

      return s0;
    }

    function peg$parsearithmetic() {
      var s0, s1, s2, s3, s4, s5, s6, s7;

      s0 = peg$currPos;
      s1 = peg$parsemultiplicative();
      if (s1 !== null) {
        s2 = [];
        s3 = peg$currPos;
        s4 = peg$parse_();
        if (s4 !== null) {
          s5 = peg$currPos;
          if (input.charCodeAt(peg$currPos) === 43) {
            s6 = peg$c40;
            peg$currPos++;
          } else {
            s6 = null;
            if (peg$silentFails === 0) { peg$fail(peg$c41); }
          }
          if (s6 === null) {
            if (input.charCodeAt(peg$currPos) === 45) {
              s6 = peg$c31;
              peg$currPos++;
            } else {
              s6 = null;
              if (peg$silentFails === 0) { peg$fail(peg$c32); }
            }
          }
          if (s6 !== null) {
            s6 = input.substring(s5, peg$currPos);
          }
          s5 = s6;
          if (s5 !== null) {
            s6 = peg$parse_();
            if (s6 !== null) {
              s7 = peg$parsemultiplicative();
              if (s7 !== null) {
                s4 = [s4, s5, s6, s7];
                s3 = s4;
              } else {
                peg$currPos = s3;
                s3 = peg$c1;
              }
            } else {
              peg$currPos = s3;
              s3 = peg$c1;
            }
          } else {
            peg$currPos = s3;
            s3 = peg$c1;
          }
        } else {
          peg$currPos = s3;
          s3 = peg$c1;
        }
        while (s3 !== null) {
          s2.push(s3);
          s3 = peg$currPos;
          s4 = peg$parse_();
          if (s4 !== null) {
            s5 = peg$currPos;
            if (input.charCodeAt(peg$currPos) === 43) {
              s6 = peg$c40;
              peg$currPos++;
            } else {
              s6 = null;
              if (peg$silentFails === 0) { peg$fail(peg$c41); }
            }
            if (s6 === null) {
              if (input.charCodeAt(peg$currPos) === 45) {
                s6 = peg$c31;
                peg$currPos++;
              } else {
                s6 = null;
                if (peg$silentFails === 0) { peg$fail(peg$c32); }
              }
            }
            if (s6 !== null) {
              s6 = input.substring(s5, peg$currPos);
            }
            s5 = s6;
            if (s5 !== null) {
              s6 = peg$parse_();
              if (s6 !== null) {
                s7 = peg$parsemultiplicative();
                if (s7 !== null) {
                  s4 = [s4, s5, s6, s7];
                  s3 = s4;
                } else {
                  peg$currPos = s3;
                  s3 = peg$c1;
                }
              } else {
                peg$currPos = s3;
                s3 = peg$c1;
              }
            } else {
              peg$currPos = s3;
              s3 = peg$c1;
            }
          } else {
            peg$currPos = s3;
            s3 = peg$c1;
          }
        }
        if (s2 !== null) {
          peg$reportedPos = s0;
          s1 = peg$c20(s1,s2);
          if (s1 === null) {
            peg$currPos = s0;
            s0 = s1;
          } else {
            s0 = s1;
          }
        } else {
          peg$currPos = s0;
          s0 = peg$c1;
        }
      } else {
        peg$currPos = s0;
        s0 = peg$c1;
      }

      return s0;
    }

    function peg$parsemultiplicative() {
      var s0, s1, s2, s3, s4, s5, s6, s7;

      s0 = peg$currPos;
      s1 = peg$parseexponential();
      if (s1 !== null) {
        s2 = [];
        s3 = peg$currPos;
        s4 = peg$parse_();
        if (s4 !== null) {
          s5 = peg$currPos;
          if (input.charCodeAt(peg$currPos) === 42) {
            s6 = peg$c42;
            peg$currPos++;
          } else {
            s6 = null;
            if (peg$silentFails === 0) { peg$fail(peg$c43); }
          }
          if (s6 === null) {
            if (input.charCodeAt(peg$currPos) === 47) {
              s6 = peg$c44;
              peg$currPos++;
            } else {
              s6 = null;
              if (peg$silentFails === 0) { peg$fail(peg$c45); }
            }
            if (s6 === null) {
              if (input.charCodeAt(peg$currPos) === 37) {
                s6 = peg$c46;
                peg$currPos++;
              } else {
                s6 = null;
                if (peg$silentFails === 0) { peg$fail(peg$c47); }
              }
              if (s6 === null) {
                if (input.substr(peg$currPos, 3) === peg$c48) {
                  s6 = peg$c48;
                  peg$currPos += 3;
                } else {
                  s6 = null;
                  if (peg$silentFails === 0) { peg$fail(peg$c49); }
                }
              }
            }
          }
          if (s6 !== null) {
            s6 = input.substring(s5, peg$currPos);
          }
          s5 = s6;
          if (s5 !== null) {
            s6 = peg$parse_();
            if (s6 !== null) {
              s7 = peg$parseexponential();
              if (s7 !== null) {
                s4 = [s4, s5, s6, s7];
                s3 = s4;
              } else {
                peg$currPos = s3;
                s3 = peg$c1;
              }
            } else {
              peg$currPos = s3;
              s3 = peg$c1;
            }
          } else {
            peg$currPos = s3;
            s3 = peg$c1;
          }
        } else {
          peg$currPos = s3;
          s3 = peg$c1;
        }
        while (s3 !== null) {
          s2.push(s3);
          s3 = peg$currPos;
          s4 = peg$parse_();
          if (s4 !== null) {
            s5 = peg$currPos;
            if (input.charCodeAt(peg$currPos) === 42) {
              s6 = peg$c42;
              peg$currPos++;
            } else {
              s6 = null;
              if (peg$silentFails === 0) { peg$fail(peg$c43); }
            }
            if (s6 === null) {
              if (input.charCodeAt(peg$currPos) === 47) {
                s6 = peg$c44;
                peg$currPos++;
              } else {
                s6 = null;
                if (peg$silentFails === 0) { peg$fail(peg$c45); }
              }
              if (s6 === null) {
                if (input.charCodeAt(peg$currPos) === 37) {
                  s6 = peg$c46;
                  peg$currPos++;
                } else {
                  s6 = null;
                  if (peg$silentFails === 0) { peg$fail(peg$c47); }
                }
                if (s6 === null) {
                  if (input.substr(peg$currPos, 3) === peg$c48) {
                    s6 = peg$c48;
                    peg$currPos += 3;
                  } else {
                    s6 = null;
                    if (peg$silentFails === 0) { peg$fail(peg$c49); }
                  }
                }
              }
            }
            if (s6 !== null) {
              s6 = input.substring(s5, peg$currPos);
            }
            s5 = s6;
            if (s5 !== null) {
              s6 = peg$parse_();
              if (s6 !== null) {
                s7 = peg$parseexponential();
                if (s7 !== null) {
                  s4 = [s4, s5, s6, s7];
                  s3 = s4;
                } else {
                  peg$currPos = s3;
                  s3 = peg$c1;
                }
              } else {
                peg$currPos = s3;
                s3 = peg$c1;
              }
            } else {
              peg$currPos = s3;
              s3 = peg$c1;
            }
          } else {
            peg$currPos = s3;
            s3 = peg$c1;
          }
        }
        if (s2 !== null) {
          peg$reportedPos = s0;
          s1 = peg$c20(s1,s2);
          if (s1 === null) {
            peg$currPos = s0;
            s0 = s1;
          } else {
            s0 = s1;
          }
        } else {
          peg$currPos = s0;
          s0 = peg$c1;
        }
      } else {
        peg$currPos = s0;
        s0 = peg$c1;
      }

      return s0;
    }

    function peg$parseexponential() {
      var s0, s1, s2, s3, s4, s5, s6, s7;

      s0 = peg$currPos;
      s1 = peg$parsedefault();
      if (s1 !== null) {
        s2 = [];
        s3 = peg$currPos;
        s4 = peg$parse_();
        if (s4 !== null) {
          s5 = peg$currPos;
          if (input.substr(peg$currPos, 2) === peg$c50) {
            s6 = peg$c50;
            peg$currPos += 2;
          } else {
            s6 = null;
            if (peg$silentFails === 0) { peg$fail(peg$c51); }
          }
          if (s6 === null) {
            if (input.substr(peg$currPos, 2) === peg$c52) {
              s6 = peg$c52;
              peg$currPos += 2;
            } else {
              s6 = null;
              if (peg$silentFails === 0) { peg$fail(peg$c53); }
            }
            if (s6 === null) {
              if (input.substr(peg$currPos, 2) === peg$c54) {
                s6 = peg$c54;
                peg$currPos += 2;
              } else {
                s6 = null;
                if (peg$silentFails === 0) { peg$fail(peg$c55); }
              }
            }
          }
          if (s6 !== null) {
            s6 = input.substring(s5, peg$currPos);
          }
          s5 = s6;
          if (s5 !== null) {
            s6 = peg$parse_();
            if (s6 !== null) {
              s7 = peg$parsedefault();
              if (s7 !== null) {
                s4 = [s4, s5, s6, s7];
                s3 = s4;
              } else {
                peg$currPos = s3;
                s3 = peg$c1;
              }
            } else {
              peg$currPos = s3;
              s3 = peg$c1;
            }
          } else {
            peg$currPos = s3;
            s3 = peg$c1;
          }
        } else {
          peg$currPos = s3;
          s3 = peg$c1;
        }
        while (s3 !== null) {
          s2.push(s3);
          s3 = peg$currPos;
          s4 = peg$parse_();
          if (s4 !== null) {
            s5 = peg$currPos;
            if (input.substr(peg$currPos, 2) === peg$c50) {
              s6 = peg$c50;
              peg$currPos += 2;
            } else {
              s6 = null;
              if (peg$silentFails === 0) { peg$fail(peg$c51); }
            }
            if (s6 === null) {
              if (input.substr(peg$currPos, 2) === peg$c52) {
                s6 = peg$c52;
                peg$currPos += 2;
              } else {
                s6 = null;
                if (peg$silentFails === 0) { peg$fail(peg$c53); }
              }
              if (s6 === null) {
                if (input.substr(peg$currPos, 2) === peg$c54) {
                  s6 = peg$c54;
                  peg$currPos += 2;
                } else {
                  s6 = null;
                  if (peg$silentFails === 0) { peg$fail(peg$c55); }
                }
              }
            }
            if (s6 !== null) {
              s6 = input.substring(s5, peg$currPos);
            }
            s5 = s6;
            if (s5 !== null) {
              s6 = peg$parse_();
              if (s6 !== null) {
                s7 = peg$parsedefault();
                if (s7 !== null) {
                  s4 = [s4, s5, s6, s7];
                  s3 = s4;
                } else {
                  peg$currPos = s3;
                  s3 = peg$c1;
                }
              } else {
                peg$currPos = s3;
                s3 = peg$c1;
              }
            } else {
              peg$currPos = s3;
              s3 = peg$c1;
            }
          } else {
            peg$currPos = s3;
            s3 = peg$c1;
          }
        }
        if (s2 !== null) {
          peg$reportedPos = s0;
          s1 = peg$c20(s1,s2);
          if (s1 === null) {
            peg$currPos = s0;
            s0 = s1;
          } else {
            s0 = s1;
          }
        } else {
          peg$currPos = s0;
          s0 = peg$c1;
        }
      } else {
        peg$currPos = s0;
        s0 = peg$c1;
      }

      return s0;
    }

    function peg$parsedefault() {
      var s0, s1, s2, s3, s4, s5, s6, s7;

      s0 = peg$currPos;
      s1 = peg$parseunary();
      if (s1 !== null) {
        s2 = [];
        s3 = peg$currPos;
        s4 = peg$parse_();
        if (s4 !== null) {
          if (input.substr(peg$currPos, 2) === peg$c56) {
            s5 = peg$c56;
            peg$currPos += 2;
          } else {
            s5 = null;
            if (peg$silentFails === 0) { peg$fail(peg$c57); }
          }
          if (s5 !== null) {
            s6 = peg$parse_();
            if (s6 !== null) {
              s7 = peg$parseunary();
              if (s7 !== null) {
                s4 = [s4, s5, s6, s7];
                s3 = s4;
              } else {
                peg$currPos = s3;
                s3 = peg$c1;
              }
            } else {
              peg$currPos = s3;
              s3 = peg$c1;
            }
          } else {
            peg$currPos = s3;
            s3 = peg$c1;
          }
        } else {
          peg$currPos = s3;
          s3 = peg$c1;
        }
        while (s3 !== null) {
          s2.push(s3);
          s3 = peg$currPos;
          s4 = peg$parse_();
          if (s4 !== null) {
            if (input.substr(peg$currPos, 2) === peg$c56) {
              s5 = peg$c56;
              peg$currPos += 2;
            } else {
              s5 = null;
              if (peg$silentFails === 0) { peg$fail(peg$c57); }
            }
            if (s5 !== null) {
              s6 = peg$parse_();
              if (s6 !== null) {
                s7 = peg$parseunary();
                if (s7 !== null) {
                  s4 = [s4, s5, s6, s7];
                  s3 = s4;
                } else {
                  peg$currPos = s3;
                  s3 = peg$c1;
                }
              } else {
                peg$currPos = s3;
                s3 = peg$c1;
              }
            } else {
              peg$currPos = s3;
              s3 = peg$c1;
            }
          } else {
            peg$currPos = s3;
            s3 = peg$c1;
          }
        }
        if (s2 !== null) {
          peg$reportedPos = s0;
          s1 = peg$c20(s1,s2);
          if (s1 === null) {
            peg$currPos = s0;
            s0 = s1;
          } else {
            s0 = s1;
          }
        } else {
          peg$currPos = s0;
          s0 = peg$c1;
        }
      } else {
        peg$currPos = s0;
        s0 = peg$c1;
      }

      return s0;
    }

    function peg$parseunary() {
      var s0, s1, s2;

      s0 = peg$currPos;
      s1 = peg$currPos;
      if (input.charCodeAt(peg$currPos) === 33) {
        s2 = peg$c58;
        peg$currPos++;
      } else {
        s2 = null;
        if (peg$silentFails === 0) { peg$fail(peg$c59); }
      }
      if (s2 === null) {
        if (input.charCodeAt(peg$currPos) === 43) {
          s2 = peg$c40;
          peg$currPos++;
        } else {
          s2 = null;
          if (peg$silentFails === 0) { peg$fail(peg$c41); }
        }
        if (s2 === null) {
          if (input.charCodeAt(peg$currPos) === 45) {
            s2 = peg$c31;
            peg$currPos++;
          } else {
            s2 = null;
            if (peg$silentFails === 0) { peg$fail(peg$c32); }
          }
        }
      }
      if (s2 !== null) {
        s2 = input.substring(s1, peg$currPos);
      }
      s1 = s2;
      if (s1 !== null) {
        s2 = peg$parseunary();
        if (s2 !== null) {
          peg$reportedPos = s0;
          s1 = peg$c60(s1,s2);
          if (s1 === null) {
            peg$currPos = s0;
            s0 = s1;
          } else {
            s0 = s1;
          }
        } else {
          peg$currPos = s0;
          s0 = peg$c1;
        }
      } else {
        peg$currPos = s0;
        s0 = peg$c1;
      }
      if (s0 === null) {
        s0 = peg$parsepipe();
      }

      return s0;
    }

    function peg$parsepipe() {
      var s0, s1, s2, s3;

      s0 = peg$currPos;
      s1 = peg$parsevalue();
      if (s1 !== null) {
        s2 = [];
        s3 = peg$parsechain();
        while (s3 !== null) {
          s2.push(s3);
          s3 = peg$parsechain();
        }
        if (s2 !== null) {
          peg$reportedPos = s0;
          s1 = peg$c61(s1,s2);
          if (s1 === null) {
            peg$currPos = s0;
            s0 = s1;
          } else {
            s0 = s1;
          }
        } else {
          peg$currPos = s0;
          s0 = peg$c1;
        }
      } else {
        peg$currPos = s0;
        s0 = peg$c1;
      }

      return s0;
    }

    function peg$parsechain() {
      var s0, s1, s2, s3;

      s0 = peg$currPos;
      if (input.charCodeAt(peg$currPos) === 46) {
        s1 = peg$c62;
        peg$currPos++;
      } else {
        s1 = null;
        if (peg$silentFails === 0) { peg$fail(peg$c63); }
      }
      if (s1 !== null) {
        s2 = peg$parsetail();
        if (s2 !== null) {
          peg$reportedPos = s0;
          s1 = peg$c64(s2);
          if (s1 === null) {
            peg$currPos = s0;
            s0 = s1;
          } else {
            s0 = s1;
          }
        } else {
          peg$currPos = s0;
          s0 = peg$c1;
        }
      } else {
        peg$currPos = s0;
        s0 = peg$c1;
      }
      if (s0 === null) {
        s0 = peg$currPos;
        if (input.charCodeAt(peg$currPos) === 91) {
          s1 = peg$c65;
          peg$currPos++;
        } else {
          s1 = null;
          if (peg$silentFails === 0) { peg$fail(peg$c66); }
        }
        if (s1 !== null) {
          s2 = peg$parseexpression();
          if (s2 !== null) {
            if (input.charCodeAt(peg$currPos) === 93) {
              s3 = peg$c67;
              peg$currPos++;
            } else {
              s3 = null;
              if (peg$silentFails === 0) { peg$fail(peg$c68); }
            }
            if (s3 !== null) {
              peg$reportedPos = s0;
              s1 = peg$c69(s2);
              if (s1 === null) {
                peg$currPos = s0;
                s0 = s1;
              } else {
                s0 = s1;
              }
            } else {
              peg$currPos = s0;
              s0 = peg$c1;
            }
          } else {
            peg$currPos = s0;
            s0 = peg$c1;
          }
        } else {
          peg$currPos = s0;
          s0 = peg$c1;
        }
      }

      return s0;
    }

    function peg$parsetail() {
      var s0, s1, s2, s3, s4;

      s0 = peg$currPos;
      s1 = peg$parseword();
      if (s1 !== null) {
        if (input.charCodeAt(peg$currPos) === 123) {
          s2 = peg$c70;
          peg$currPos++;
        } else {
          s2 = null;
          if (peg$silentFails === 0) { peg$fail(peg$c71); }
        }
        if (s2 !== null) {
          s3 = peg$parseexpression();
          if (s3 !== null) {
            if (input.charCodeAt(peg$currPos) === 125) {
              s4 = peg$c72;
              peg$currPos++;
            } else {
              s4 = null;
              if (peg$silentFails === 0) { peg$fail(peg$c73); }
            }
            if (s4 !== null) {
              peg$reportedPos = s0;
              s1 = peg$c74(s1,s3);
              if (s1 === null) {
                peg$currPos = s0;
                s0 = s1;
              } else {
                s0 = s1;
              }
            } else {
              peg$currPos = s0;
              s0 = peg$c1;
            }
          } else {
            peg$currPos = s0;
            s0 = peg$c1;
          }
        } else {
          peg$currPos = s0;
          s0 = peg$c1;
        }
      } else {
        peg$currPos = s0;
        s0 = peg$c1;
      }
      if (s0 === null) {
        s0 = peg$currPos;
        s1 = peg$parseword();
        if (s1 !== null) {
          s2 = peg$parseargs();
          if (s2 !== null) {
            peg$reportedPos = s0;
            s1 = peg$c75(s1,s2);
            if (s1 === null) {
              peg$currPos = s0;
              s0 = s1;
            } else {
              s0 = s1;
            }
          } else {
            peg$currPos = s0;
            s0 = peg$c1;
          }
        } else {
          peg$currPos = s0;
          s0 = peg$c1;
        }
        if (s0 === null) {
          s0 = peg$currPos;
          s1 = peg$parsedigits();
          if (s1 !== null) {
            peg$reportedPos = s0;
            s1 = peg$c76(s1);
          }
          if (s1 === null) {
            peg$currPos = s0;
            s0 = s1;
          } else {
            s0 = s1;
          }
          if (s0 === null) {
            s0 = peg$currPos;
            s1 = peg$parseword();
            if (s1 !== null) {
              peg$reportedPos = s0;
              s1 = peg$c77(s1);
            }
            if (s1 === null) {
              peg$currPos = s0;
              s0 = s1;
            } else {
              s0 = s1;
            }
            if (s0 === null) {
              s0 = peg$currPos;
              s1 = peg$parsearray();
              if (s1 !== null) {
                peg$reportedPos = s0;
                s1 = peg$c78(s1);
              }
              if (s1 === null) {
                peg$currPos = s0;
                s0 = s1;
              } else {
                s0 = s1;
              }
              if (s0 === null) {
                s0 = peg$currPos;
                s1 = peg$parseobject();
                if (s1 !== null) {
                  peg$reportedPos = s0;
                  s1 = peg$c78(s1);
                }
                if (s1 === null) {
                  peg$currPos = s0;
                  s0 = s1;
                } else {
                  s0 = s1;
                }
                if (s0 === null) {
                  s0 = peg$currPos;
                  if (input.charCodeAt(peg$currPos) === 40) {
                    s1 = peg$c6;
                    peg$currPos++;
                  } else {
                    s1 = null;
                    if (peg$silentFails === 0) { peg$fail(peg$c7); }
                  }
                  if (s1 !== null) {
                    s2 = peg$parseexpression();
                    if (s2 !== null) {
                      if (input.charCodeAt(peg$currPos) === 41) {
                        s3 = peg$c8;
                        peg$currPos++;
                      } else {
                        s3 = null;
                        if (peg$silentFails === 0) { peg$fail(peg$c9); }
                      }
                      if (s3 !== null) {
                        peg$reportedPos = s0;
                        s1 = peg$c78(s2);
                        if (s1 === null) {
                          peg$currPos = s0;
                          s0 = s1;
                        } else {
                          s0 = s1;
                        }
                      } else {
                        peg$currPos = s0;
                        s0 = peg$c1;
                      }
                    } else {
                      peg$currPos = s0;
                      s0 = peg$c1;
                    }
                  } else {
                    peg$currPos = s0;
                    s0 = peg$c1;
                  }
                }
              }
            }
          }
        }
      }

      return s0;
    }

    function peg$parsevalue() {
      var s0, s1, s2, s3;

      s0 = peg$parsearray();
      if (s0 === null) {
        s0 = peg$parseobject();
        if (s0 === null) {
          s0 = peg$parsestring();
          if (s0 === null) {
            s0 = peg$parsenumber();
            if (s0 === null) {
              s0 = peg$currPos;
              if (input.substr(peg$currPos, 4) === peg$c79) {
                s1 = peg$c79;
                peg$currPos += 4;
              } else {
                s1 = null;
                if (peg$silentFails === 0) { peg$fail(peg$c80); }
              }
              if (s1 !== null) {
                peg$reportedPos = s0;
                s1 = peg$c81();
              }
              if (s1 === null) {
                peg$currPos = s0;
                s0 = s1;
              } else {
                s0 = s1;
              }
              if (s0 === null) {
                s0 = peg$currPos;
                if (input.substr(peg$currPos, 4) === peg$c82) {
                  s1 = peg$c82;
                  peg$currPos += 4;
                } else {
                  s1 = null;
                  if (peg$silentFails === 0) { peg$fail(peg$c83); }
                }
                if (s1 !== null) {
                  peg$reportedPos = s0;
                  s1 = peg$c84();
                }
                if (s1 === null) {
                  peg$currPos = s0;
                  s0 = s1;
                } else {
                  s0 = s1;
                }
                if (s0 === null) {
                  s0 = peg$currPos;
                  if (input.substr(peg$currPos, 5) === peg$c85) {
                    s1 = peg$c85;
                    peg$currPos += 5;
                  } else {
                    s1 = null;
                    if (peg$silentFails === 0) { peg$fail(peg$c86); }
                  }
                  if (s1 !== null) {
                    peg$reportedPos = s0;
                    s1 = peg$c87();
                  }
                  if (s1 === null) {
                    peg$currPos = s0;
                    s0 = s1;
                  } else {
                    s0 = s1;
                  }
                  if (s0 === null) {
                    s0 = peg$currPos;
                    if (input.substr(peg$currPos, 4) === peg$c88) {
                      s1 = peg$c88;
                      peg$currPos += 4;
                    } else {
                      s1 = null;
                      if (peg$silentFails === 0) { peg$fail(peg$c89); }
                    }
                    if (s1 !== null) {
                      peg$reportedPos = s0;
                      s1 = peg$c90();
                    }
                    if (s1 === null) {
                      peg$currPos = s0;
                      s0 = s1;
                    } else {
                      s0 = s1;
                    }
                    if (s0 === null) {
                      s0 = peg$currPos;
                      if (input.charCodeAt(peg$currPos) === 64) {
                        s1 = peg$c91;
                        peg$currPos++;
                      } else {
                        s1 = null;
                        if (peg$silentFails === 0) { peg$fail(peg$c92); }
                      }
                      if (s1 !== null) {
                        s2 = peg$parseword();
                        if (s2 !== null) {
                          peg$reportedPos = s0;
                          s1 = peg$c93(s2);
                          if (s1 === null) {
                            peg$currPos = s0;
                            s0 = s1;
                          } else {
                            s0 = s1;
                          }
                        } else {
                          peg$currPos = s0;
                          s0 = peg$c1;
                        }
                      } else {
                        peg$currPos = s0;
                        s0 = peg$c1;
                      }
                      if (s0 === null) {
                        s0 = peg$currPos;
                        if (input.charCodeAt(peg$currPos) === 36) {
                          s1 = peg$c94;
                          peg$currPos++;
                        } else {
                          s1 = null;
                          if (peg$silentFails === 0) { peg$fail(peg$c95); }
                        }
                        if (s1 !== null) {
                          s2 = peg$parseword();
                          if (s2 !== null) {
                            peg$reportedPos = s0;
                            s1 = peg$c96(s2);
                            if (s1 === null) {
                              peg$currPos = s0;
                              s0 = s1;
                            } else {
                              s0 = s1;
                            }
                          } else {
                            peg$currPos = s0;
                            s0 = peg$c1;
                          }
                        } else {
                          peg$currPos = s0;
                          s0 = peg$c1;
                        }
                        if (s0 === null) {
                          s0 = peg$currPos;
                          if (input.charCodeAt(peg$currPos) === 36) {
                            s1 = peg$c94;
                            peg$currPos++;
                          } else {
                            s1 = null;
                            if (peg$silentFails === 0) { peg$fail(peg$c95); }
                          }
                          if (s1 !== null) {
                            peg$reportedPos = s0;
                            s1 = peg$c97();
                          }
                          if (s1 === null) {
                            peg$currPos = s0;
                            s0 = s1;
                          } else {
                            s0 = s1;
                          }
                          if (s0 === null) {
                            s0 = peg$currPos;
                            if (input.charCodeAt(peg$currPos) === 35) {
                              s1 = peg$c98;
                              peg$currPos++;
                            } else {
                              s1 = null;
                              if (peg$silentFails === 0) { peg$fail(peg$c99); }
                            }
                            if (s1 !== null) {
                              s2 = peg$parseword();
                              if (s2 !== null) {
                                peg$reportedPos = s0;
                                s1 = peg$c100(s2);
                                if (s1 === null) {
                                  peg$currPos = s0;
                                  s0 = s1;
                                } else {
                                  s0 = s1;
                                }
                              } else {
                                peg$currPos = s0;
                                s0 = peg$c1;
                              }
                            } else {
                              peg$currPos = s0;
                              s0 = peg$c1;
                            }
                            if (s0 === null) {
                              s0 = peg$currPos;
                              if (input.charCodeAt(peg$currPos) === 38) {
                                s1 = peg$c101;
                                peg$currPos++;
                              } else {
                                s1 = null;
                                if (peg$silentFails === 0) { peg$fail(peg$c102); }
                              }
                              if (s1 !== null) {
                                s2 = peg$parseword();
                                if (s2 !== null) {
                                  s3 = peg$parseargs();
                                  if (s3 !== null) {
                                    peg$reportedPos = s0;
                                    s1 = peg$c103(s2,s3);
                                    if (s1 === null) {
                                      peg$currPos = s0;
                                      s0 = s1;
                                    } else {
                                      s0 = s1;
                                    }
                                  } else {
                                    peg$currPos = s0;
                                    s0 = peg$c1;
                                  }
                                } else {
                                  peg$currPos = s0;
                                  s0 = peg$c1;
                                }
                              } else {
                                peg$currPos = s0;
                                s0 = peg$c1;
                              }
                              if (s0 === null) {
                                s0 = peg$currPos;
                                if (input.charCodeAt(peg$currPos) === 94) {
                                  s1 = peg$c104;
                                  peg$currPos++;
                                } else {
                                  s1 = null;
                                  if (peg$silentFails === 0) { peg$fail(peg$c105); }
                                }
                                if (s1 !== null) {
                                  s2 = peg$parsevalue();
                                  if (s2 !== null) {
                                    peg$reportedPos = s0;
                                    s1 = peg$c106(s2);
                                    if (s1 === null) {
                                      peg$currPos = s0;
                                      s0 = s1;
                                    } else {
                                      s0 = s1;
                                    }
                                  } else {
                                    peg$currPos = s0;
                                    s0 = peg$c1;
                                  }
                                } else {
                                  peg$currPos = s0;
                                  s0 = peg$c1;
                                }
                                if (s0 === null) {
                                  s0 = peg$currPos;
                                  if (input.charCodeAt(peg$currPos) === 40) {
                                    s1 = peg$c6;
                                    peg$currPos++;
                                  } else {
                                    s1 = null;
                                    if (peg$silentFails === 0) { peg$fail(peg$c7); }
                                  }
                                  if (s1 !== null) {
                                    s2 = peg$parseexpression();
                                    if (s2 !== null) {
                                      if (input.charCodeAt(peg$currPos) === 41) {
                                        s3 = peg$c8;
                                        peg$currPos++;
                                      } else {
                                        s3 = null;
                                        if (peg$silentFails === 0) { peg$fail(peg$c9); }
                                      }
                                      if (s3 !== null) {
                                        peg$reportedPos = s0;
                                        s1 = peg$c107(s2);
                                        if (s1 === null) {
                                          peg$currPos = s0;
                                          s0 = s1;
                                        } else {
                                          s0 = s1;
                                        }
                                      } else {
                                        peg$currPos = s0;
                                        s0 = peg$c1;
                                      }
                                    } else {
                                      peg$currPos = s0;
                                      s0 = peg$c1;
                                    }
                                  } else {
                                    peg$currPos = s0;
                                    s0 = peg$c1;
                                  }
                                  if (s0 === null) {
                                    s0 = peg$currPos;
                                    s1 = peg$parsetail();
                                    if (s1 !== null) {
                                      peg$reportedPos = s0;
                                      s1 = peg$c108(s1);
                                    }
                                    if (s1 === null) {
                                      peg$currPos = s0;
                                      s0 = s1;
                                    } else {
                                      s0 = s1;
                                    }
                                    if (s0 === null) {
                                      s0 = peg$currPos;
                                      s1 = [];
                                      if (s1 !== null) {
                                        peg$reportedPos = s0;
                                        s1 = peg$c109();
                                      }
                                      if (s1 === null) {
                                        peg$currPos = s0;
                                        s0 = s1;
                                      } else {
                                        s0 = s1;
                                      }
                                    }
                                  }
                                }
                              }
                            }
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }

      return s0;
    }

    function peg$parseword() {
      var s0, s1, s2;

      peg$silentFails++;
      s0 = peg$currPos;
      s1 = [];
      if (peg$c111.test(input.charAt(peg$currPos))) {
        s2 = input.charAt(peg$currPos);
        peg$currPos++;
      } else {
        s2 = null;
        if (peg$silentFails === 0) { peg$fail(peg$c112); }
      }
      if (s2 !== null) {
        while (s2 !== null) {
          s1.push(s2);
          if (peg$c111.test(input.charAt(peg$currPos))) {
            s2 = input.charAt(peg$currPos);
            peg$currPos++;
          } else {
            s2 = null;
            if (peg$silentFails === 0) { peg$fail(peg$c112); }
          }
        }
      } else {
        s1 = peg$c1;
      }
      if (s1 !== null) {
        peg$reportedPos = s0;
        s1 = peg$c113(s1);
      }
      if (s1 === null) {
        peg$currPos = s0;
        s0 = s1;
      } else {
        s0 = s1;
      }
      peg$silentFails--;
      if (s0 === null) {
        s1 = null;
        if (peg$silentFails === 0) { peg$fail(peg$c110); }
      }

      return s0;
    }

    function peg$parsestring() {
      var s0, s1, s2, s3;

      peg$silentFails++;
      s0 = peg$currPos;
      if (input.charCodeAt(peg$currPos) === 39) {
        s1 = peg$c115;
        peg$currPos++;
      } else {
        s1 = null;
        if (peg$silentFails === 0) { peg$fail(peg$c116); }
      }
      if (s1 !== null) {
        s2 = [];
        s3 = peg$parsetickedChar();
        while (s3 !== null) {
          s2.push(s3);
          s3 = peg$parsetickedChar();
        }
        if (s2 !== null) {
          if (input.charCodeAt(peg$currPos) === 39) {
            s3 = peg$c115;
            peg$currPos++;
          } else {
            s3 = null;
            if (peg$silentFails === 0) { peg$fail(peg$c116); }
          }
          if (s3 !== null) {
            peg$reportedPos = s0;
            s1 = peg$c117(s2);
            if (s1 === null) {
              peg$currPos = s0;
              s0 = s1;
            } else {
              s0 = s1;
            }
          } else {
            peg$currPos = s0;
            s0 = peg$c1;
          }
        } else {
          peg$currPos = s0;
          s0 = peg$c1;
        }
      } else {
        peg$currPos = s0;
        s0 = peg$c1;
      }
      if (s0 === null) {
        s0 = peg$currPos;
        if (input.charCodeAt(peg$currPos) === 34) {
          s1 = peg$c118;
          peg$currPos++;
        } else {
          s1 = null;
          if (peg$silentFails === 0) { peg$fail(peg$c119); }
        }
        if (s1 !== null) {
          s2 = [];
          s3 = peg$parsequotedChar();
          while (s3 !== null) {
            s2.push(s3);
            s3 = peg$parsequotedChar();
          }
          if (s2 !== null) {
            if (input.charCodeAt(peg$currPos) === 34) {
              s3 = peg$c118;
              peg$currPos++;
            } else {
              s3 = null;
              if (peg$silentFails === 0) { peg$fail(peg$c119); }
            }
            if (s3 !== null) {
              peg$reportedPos = s0;
              s1 = peg$c117(s2);
              if (s1 === null) {
                peg$currPos = s0;
                s0 = s1;
              } else {
                s0 = s1;
              }
            } else {
              peg$currPos = s0;
              s0 = peg$c1;
            }
          } else {
            peg$currPos = s0;
            s0 = peg$c1;
          }
        } else {
          peg$currPos = s0;
          s0 = peg$c1;
        }
      }
      peg$silentFails--;
      if (s0 === null) {
        s1 = null;
        if (peg$silentFails === 0) { peg$fail(peg$c114); }
      }

      return s0;
    }

    function peg$parsetickedChar() {
      var s0, s1;

      if (peg$c120.test(input.charAt(peg$currPos))) {
        s0 = input.charAt(peg$currPos);
        peg$currPos++;
      } else {
        s0 = null;
        if (peg$silentFails === 0) { peg$fail(peg$c121); }
      }
      if (s0 === null) {
        s0 = peg$currPos;
        if (input.substr(peg$currPos, 2) === peg$c122) {
          s1 = peg$c122;
          peg$currPos += 2;
        } else {
          s1 = null;
          if (peg$silentFails === 0) { peg$fail(peg$c123); }
        }
        if (s1 !== null) {
          peg$reportedPos = s0;
          s1 = peg$c124();
        }
        if (s1 === null) {
          peg$currPos = s0;
          s0 = s1;
        } else {
          s0 = s1;
        }
        if (s0 === null) {
          s0 = peg$parseescape();
        }
      }

      return s0;
    }

    function peg$parsequotedChar() {
      var s0, s1;

      if (peg$c125.test(input.charAt(peg$currPos))) {
        s0 = input.charAt(peg$currPos);
        peg$currPos++;
      } else {
        s0 = null;
        if (peg$silentFails === 0) { peg$fail(peg$c126); }
      }
      if (s0 === null) {
        s0 = peg$currPos;
        if (input.substr(peg$currPos, 2) === peg$c127) {
          s1 = peg$c127;
          peg$currPos += 2;
        } else {
          s1 = null;
          if (peg$silentFails === 0) { peg$fail(peg$c128); }
        }
        if (s1 !== null) {
          peg$reportedPos = s0;
          s1 = peg$c129();
        }
        if (s1 === null) {
          peg$currPos = s0;
          s0 = s1;
        } else {
          s0 = s1;
        }
        if (s0 === null) {
          s0 = peg$parseescape();
        }
      }

      return s0;
    }

    function peg$parseescape() {
      var s0, s1, s2, s3, s4, s5, s6, s7;

      s0 = peg$currPos;
      if (input.substr(peg$currPos, 2) === peg$c130) {
        s1 = peg$c130;
        peg$currPos += 2;
      } else {
        s1 = null;
        if (peg$silentFails === 0) { peg$fail(peg$c131); }
      }
      if (s1 !== null) {
        peg$reportedPos = s0;
        s1 = peg$c132();
      }
      if (s1 === null) {
        peg$currPos = s0;
        s0 = s1;
      } else {
        s0 = s1;
      }
      if (s0 === null) {
        s0 = peg$currPos;
        if (input.substr(peg$currPos, 2) === peg$c133) {
          s1 = peg$c133;
          peg$currPos += 2;
        } else {
          s1 = null;
          if (peg$silentFails === 0) { peg$fail(peg$c134); }
        }
        if (s1 !== null) {
          peg$reportedPos = s0;
          s1 = peg$c135();
        }
        if (s1 === null) {
          peg$currPos = s0;
          s0 = s1;
        } else {
          s0 = s1;
        }
        if (s0 === null) {
          s0 = peg$currPos;
          if (input.substr(peg$currPos, 2) === peg$c136) {
            s1 = peg$c136;
            peg$currPos += 2;
          } else {
            s1 = null;
            if (peg$silentFails === 0) { peg$fail(peg$c137); }
          }
          if (s1 !== null) {
            peg$reportedPos = s0;
            s1 = peg$c138();
          }
          if (s1 === null) {
            peg$currPos = s0;
            s0 = s1;
          } else {
            s0 = s1;
          }
          if (s0 === null) {
            s0 = peg$currPos;
            if (input.substr(peg$currPos, 2) === peg$c139) {
              s1 = peg$c139;
              peg$currPos += 2;
            } else {
              s1 = null;
              if (peg$silentFails === 0) { peg$fail(peg$c140); }
            }
            if (s1 !== null) {
              peg$reportedPos = s0;
              s1 = peg$c141();
            }
            if (s1 === null) {
              peg$currPos = s0;
              s0 = s1;
            } else {
              s0 = s1;
            }
            if (s0 === null) {
              s0 = peg$currPos;
              if (input.substr(peg$currPos, 2) === peg$c142) {
                s1 = peg$c142;
                peg$currPos += 2;
              } else {
                s1 = null;
                if (peg$silentFails === 0) { peg$fail(peg$c143); }
              }
              if (s1 !== null) {
                peg$reportedPos = s0;
                s1 = peg$c144();
              }
              if (s1 === null) {
                peg$currPos = s0;
                s0 = s1;
              } else {
                s0 = s1;
              }
              if (s0 === null) {
                s0 = peg$currPos;
                if (input.substr(peg$currPos, 2) === peg$c145) {
                  s1 = peg$c145;
                  peg$currPos += 2;
                } else {
                  s1 = null;
                  if (peg$silentFails === 0) { peg$fail(peg$c146); }
                }
                if (s1 !== null) {
                  peg$reportedPos = s0;
                  s1 = peg$c147();
                }
                if (s1 === null) {
                  peg$currPos = s0;
                  s0 = s1;
                } else {
                  s0 = s1;
                }
                if (s0 === null) {
                  s0 = peg$currPos;
                  if (input.substr(peg$currPos, 2) === peg$c148) {
                    s1 = peg$c148;
                    peg$currPos += 2;
                  } else {
                    s1 = null;
                    if (peg$silentFails === 0) { peg$fail(peg$c149); }
                  }
                  if (s1 !== null) {
                    peg$reportedPos = s0;
                    s1 = peg$c150();
                  }
                  if (s1 === null) {
                    peg$currPos = s0;
                    s0 = s1;
                  } else {
                    s0 = s1;
                  }
                  if (s0 === null) {
                    s0 = peg$currPos;
                    if (input.substr(peg$currPos, 2) === peg$c151) {
                      s1 = peg$c151;
                      peg$currPos += 2;
                    } else {
                      s1 = null;
                      if (peg$silentFails === 0) { peg$fail(peg$c152); }
                    }
                    if (s1 !== null) {
                      peg$reportedPos = s0;
                      s1 = peg$c153();
                    }
                    if (s1 === null) {
                      peg$currPos = s0;
                      s0 = s1;
                    } else {
                      s0 = s1;
                    }
                    if (s0 === null) {
                      s0 = peg$currPos;
                      if (input.substr(peg$currPos, 2) === peg$c154) {
                        s1 = peg$c154;
                        peg$currPos += 2;
                      } else {
                        s1 = null;
                        if (peg$silentFails === 0) { peg$fail(peg$c155); }
                      }
                      if (s1 !== null) {
                        s2 = peg$currPos;
                        s3 = peg$currPos;
                        s4 = peg$parsehexDigit();
                        if (s4 !== null) {
                          s5 = peg$parsehexDigit();
                          if (s5 !== null) {
                            s6 = peg$parsehexDigit();
                            if (s6 !== null) {
                              s7 = peg$parsehexDigit();
                              if (s7 !== null) {
                                s4 = [s4, s5, s6, s7];
                                s3 = s4;
                              } else {
                                peg$currPos = s3;
                                s3 = peg$c1;
                              }
                            } else {
                              peg$currPos = s3;
                              s3 = peg$c1;
                            }
                          } else {
                            peg$currPos = s3;
                            s3 = peg$c1;
                          }
                        } else {
                          peg$currPos = s3;
                          s3 = peg$c1;
                        }
                        if (s3 !== null) {
                          s3 = input.substring(s2, peg$currPos);
                        }
                        s2 = s3;
                        if (s2 !== null) {
                          peg$reportedPos = s0;
                          s1 = peg$c156(s2);
                          if (s1 === null) {
                            peg$currPos = s0;
                            s0 = s1;
                          } else {
                            s0 = s1;
                          }
                        } else {
                          peg$currPos = s0;
                          s0 = peg$c1;
                        }
                      } else {
                        peg$currPos = s0;
                        s0 = peg$c1;
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }

      return s0;
    }

    function peg$parsehexDigit() {
      var s0;

      if (peg$c157.test(input.charAt(peg$currPos))) {
        s0 = input.charAt(peg$currPos);
        peg$currPos++;
      } else {
        s0 = null;
        if (peg$silentFails === 0) { peg$fail(peg$c158); }
      }

      return s0;
    }

    function peg$parsearray() {
      var s0, s1, s2, s3;

      s0 = peg$currPos;
      if (input.charCodeAt(peg$currPos) === 91) {
        s1 = peg$c65;
        peg$currPos++;
      } else {
        s1 = null;
        if (peg$silentFails === 0) { peg$fail(peg$c66); }
      }
      if (s1 !== null) {
        s2 = peg$parse_();
        if (s2 !== null) {
          if (input.charCodeAt(peg$currPos) === 93) {
            s3 = peg$c67;
            peg$currPos++;
          } else {
            s3 = null;
            if (peg$silentFails === 0) { peg$fail(peg$c68); }
          }
          if (s3 !== null) {
            peg$reportedPos = s0;
            s1 = peg$c159();
            if (s1 === null) {
              peg$currPos = s0;
              s0 = s1;
            } else {
              s0 = s1;
            }
          } else {
            peg$currPos = s0;
            s0 = peg$c1;
          }
        } else {
          peg$currPos = s0;
          s0 = peg$c1;
        }
      } else {
        peg$currPos = s0;
        s0 = peg$c1;
      }
      if (s0 === null) {
        s0 = peg$currPos;
        if (input.charCodeAt(peg$currPos) === 91) {
          s1 = peg$c65;
          peg$currPos++;
        } else {
          s1 = null;
          if (peg$silentFails === 0) { peg$fail(peg$c66); }
        }
        if (s1 !== null) {
          s2 = peg$parseexpressions();
          if (s2 !== null) {
            if (input.charCodeAt(peg$currPos) === 93) {
              s3 = peg$c67;
              peg$currPos++;
            } else {
              s3 = null;
              if (peg$silentFails === 0) { peg$fail(peg$c68); }
            }
            if (s3 !== null) {
              peg$reportedPos = s0;
              s1 = peg$c160(s2);
              if (s1 === null) {
                peg$currPos = s0;
                s0 = s1;
              } else {
                s0 = s1;
              }
            } else {
              peg$currPos = s0;
              s0 = peg$c1;
            }
          } else {
            peg$currPos = s0;
            s0 = peg$c1;
          }
        } else {
          peg$currPos = s0;
          s0 = peg$c1;
        }
      }

      return s0;
    }

    function peg$parseobject() {
      var s0, s1, s2, s3, s4, s5;

      s0 = peg$currPos;
      if (input.charCodeAt(peg$currPos) === 123) {
        s1 = peg$c70;
        peg$currPos++;
      } else {
        s1 = null;
        if (peg$silentFails === 0) { peg$fail(peg$c71); }
      }
      if (s1 !== null) {
        s2 = peg$parse_();
        if (s2 !== null) {
          if (input.charCodeAt(peg$currPos) === 125) {
            s3 = peg$c72;
            peg$currPos++;
          } else {
            s3 = null;
            if (peg$silentFails === 0) { peg$fail(peg$c73); }
          }
          if (s3 !== null) {
            s4 = peg$parse_();
            if (s4 !== null) {
              peg$reportedPos = s0;
              s1 = peg$c161();
              if (s1 === null) {
                peg$currPos = s0;
                s0 = s1;
              } else {
                s0 = s1;
              }
            } else {
              peg$currPos = s0;
              s0 = peg$c1;
            }
          } else {
            peg$currPos = s0;
            s0 = peg$c1;
          }
        } else {
          peg$currPos = s0;
          s0 = peg$c1;
        }
      } else {
        peg$currPos = s0;
        s0 = peg$c1;
      }
      if (s0 === null) {
        s0 = peg$currPos;
        if (input.charCodeAt(peg$currPos) === 123) {
          s1 = peg$c70;
          peg$currPos++;
        } else {
          s1 = null;
          if (peg$silentFails === 0) { peg$fail(peg$c71); }
        }
        if (s1 !== null) {
          s2 = peg$parse_();
          if (s2 !== null) {
            s3 = peg$parsepairs();
            if (s3 !== null) {
              if (input.charCodeAt(peg$currPos) === 125) {
                s4 = peg$c72;
                peg$currPos++;
              } else {
                s4 = null;
                if (peg$silentFails === 0) { peg$fail(peg$c73); }
              }
              if (s4 !== null) {
                s5 = peg$parse_();
                if (s5 !== null) {
                  peg$reportedPos = s0;
                  s1 = peg$c162(s3);
                  if (s1 === null) {
                    peg$currPos = s0;
                    s0 = s1;
                  } else {
                    s0 = s1;
                  }
                } else {
                  peg$currPos = s0;
                  s0 = peg$c1;
                }
              } else {
                peg$currPos = s0;
                s0 = peg$c1;
              }
            } else {
              peg$currPos = s0;
              s0 = peg$c1;
            }
          } else {
            peg$currPos = s0;
            s0 = peg$c1;
          }
        } else {
          peg$currPos = s0;
          s0 = peg$c1;
        }
      }

      return s0;
    }

    function peg$parsepairs() {
      var s0, s1, s2, s3, s4, s5, s6;

      s0 = peg$currPos;
      s1 = peg$parsepair();
      if (s1 !== null) {
        s2 = [];
        s3 = peg$currPos;
        if (input.charCodeAt(peg$currPos) === 44) {
          s4 = peg$c3;
          peg$currPos++;
        } else {
          s4 = null;
          if (peg$silentFails === 0) { peg$fail(peg$c4); }
        }
        if (s4 !== null) {
          s5 = peg$parse_();
          if (s5 !== null) {
            s6 = peg$parsepair();
            if (s6 !== null) {
              s4 = [s4, s5, s6];
              s3 = s4;
            } else {
              peg$currPos = s3;
              s3 = peg$c1;
            }
          } else {
            peg$currPos = s3;
            s3 = peg$c1;
          }
        } else {
          peg$currPos = s3;
          s3 = peg$c1;
        }
        while (s3 !== null) {
          s2.push(s3);
          s3 = peg$currPos;
          if (input.charCodeAt(peg$currPos) === 44) {
            s4 = peg$c3;
            peg$currPos++;
          } else {
            s4 = null;
            if (peg$silentFails === 0) { peg$fail(peg$c4); }
          }
          if (s4 !== null) {
            s5 = peg$parse_();
            if (s5 !== null) {
              s6 = peg$parsepair();
              if (s6 !== null) {
                s4 = [s4, s5, s6];
                s3 = s4;
              } else {
                peg$currPos = s3;
                s3 = peg$c1;
              }
            } else {
              peg$currPos = s3;
              s3 = peg$c1;
            }
          } else {
            peg$currPos = s3;
            s3 = peg$c1;
          }
        }
        if (s2 !== null) {
          peg$reportedPos = s0;
          s1 = peg$c163(s1,s2);
          if (s1 === null) {
            peg$currPos = s0;
            s0 = s1;
          } else {
            s0 = s1;
          }
        } else {
          peg$currPos = s0;
          s0 = peg$c1;
        }
      } else {
        peg$currPos = s0;
        s0 = peg$c1;
      }

      return s0;
    }

    function peg$parsepair() {
      var s0, s1, s2, s3, s4;

      s0 = peg$currPos;
      s1 = peg$parseword();
      if (s1 !== null) {
        if (input.charCodeAt(peg$currPos) === 58) {
          s2 = peg$c15;
          peg$currPos++;
        } else {
          s2 = null;
          if (peg$silentFails === 0) { peg$fail(peg$c16); }
        }
        if (s2 !== null) {
          s3 = peg$parse_();
          if (s3 !== null) {
            s4 = peg$parseexpression();
            if (s4 !== null) {
              peg$reportedPos = s0;
              s1 = peg$c164(s1,s4);
              if (s1 === null) {
                peg$currPos = s0;
                s0 = s1;
              } else {
                s0 = s1;
              }
            } else {
              peg$currPos = s0;
              s0 = peg$c1;
            }
          } else {
            peg$currPos = s0;
            s0 = peg$c1;
          }
        } else {
          peg$currPos = s0;
          s0 = peg$c1;
        }
      } else {
        peg$currPos = s0;
        s0 = peg$c1;
      }

      return s0;
    }

    function peg$parsenumber() {
      var s0, s1, s2;

      peg$silentFails++;
      s0 = peg$currPos;
      s1 = peg$currPos;
      s2 = peg$parsenumberPattern();
      if (s2 !== null) {
        s2 = input.substring(s1, peg$currPos);
      }
      s1 = s2;
      if (s1 !== null) {
        peg$reportedPos = s0;
        s1 = peg$c166(s1);
      }
      if (s1 === null) {
        peg$currPos = s0;
        s0 = s1;
      } else {
        s0 = s1;
      }
      peg$silentFails--;
      if (s0 === null) {
        s1 = null;
        if (peg$silentFails === 0) { peg$fail(peg$c165); }
      }

      return s0;
    }

    function peg$parsenumberPattern() {
      var s0, s1, s2, s3;

      s0 = peg$currPos;
      s1 = peg$parseint();
      if (s1 !== null) {
        s2 = peg$parsefrac();
        if (s2 !== null) {
          s3 = peg$parseexp();
          if (s3 !== null) {
            s1 = [s1, s2, s3];
            s0 = s1;
          } else {
            peg$currPos = s0;
            s0 = peg$c1;
          }
        } else {
          peg$currPos = s0;
          s0 = peg$c1;
        }
      } else {
        peg$currPos = s0;
        s0 = peg$c1;
      }
      if (s0 === null) {
        s0 = peg$currPos;
        s1 = peg$parseint();
        if (s1 !== null) {
          s2 = peg$parsefrac();
          if (s2 !== null) {
            s1 = [s1, s2];
            s0 = s1;
          } else {
            peg$currPos = s0;
            s0 = peg$c1;
          }
        } else {
          peg$currPos = s0;
          s0 = peg$c1;
        }
        if (s0 === null) {
          s0 = peg$currPos;
          s1 = peg$parseint();
          if (s1 !== null) {
            s2 = peg$parseexp();
            if (s2 !== null) {
              s1 = [s1, s2];
              s0 = s1;
            } else {
              peg$currPos = s0;
              s0 = peg$c1;
            }
          } else {
            peg$currPos = s0;
            s0 = peg$c1;
          }
          if (s0 === null) {
            s0 = peg$parseint();
          }
        }
      }

      return s0;
    }

    function peg$parseint() {
      var s0, s1, s2, s3;

      s0 = peg$currPos;
      s1 = peg$parsedigit19();
      if (s1 !== null) {
        s2 = peg$parsedigits();
        if (s2 !== null) {
          s1 = [s1, s2];
          s0 = s1;
        } else {
          peg$currPos = s0;
          s0 = peg$c1;
        }
      } else {
        peg$currPos = s0;
        s0 = peg$c1;
      }
      if (s0 === null) {
        s0 = peg$parsedigit();
        if (s0 === null) {
          s0 = peg$currPos;
          if (input.charCodeAt(peg$currPos) === 45) {
            s1 = peg$c31;
            peg$currPos++;
          } else {
            s1 = null;
            if (peg$silentFails === 0) { peg$fail(peg$c32); }
          }
          if (s1 !== null) {
            s2 = peg$parsedigit19();
            if (s2 !== null) {
              s3 = peg$parsedigits();
              if (s3 !== null) {
                s1 = [s1, s2, s3];
                s0 = s1;
              } else {
                peg$currPos = s0;
                s0 = peg$c1;
              }
            } else {
              peg$currPos = s0;
              s0 = peg$c1;
            }
          } else {
            peg$currPos = s0;
            s0 = peg$c1;
          }
          if (s0 === null) {
            s0 = peg$currPos;
            if (input.charCodeAt(peg$currPos) === 45) {
              s1 = peg$c31;
              peg$currPos++;
            } else {
              s1 = null;
              if (peg$silentFails === 0) { peg$fail(peg$c32); }
            }
            if (s1 !== null) {
              s2 = peg$parsedigit();
              if (s2 !== null) {
                s1 = [s1, s2];
                s0 = s1;
              } else {
                peg$currPos = s0;
                s0 = peg$c1;
              }
            } else {
              peg$currPos = s0;
              s0 = peg$c1;
            }
          }
        }
      }

      return s0;
    }

    function peg$parsefrac() {
      var s0, s1, s2;

      s0 = peg$currPos;
      if (input.charCodeAt(peg$currPos) === 46) {
        s1 = peg$c62;
        peg$currPos++;
      } else {
        s1 = null;
        if (peg$silentFails === 0) { peg$fail(peg$c63); }
      }
      if (s1 !== null) {
        s2 = peg$parsedigits();
        if (s2 !== null) {
          s1 = [s1, s2];
          s0 = s1;
        } else {
          peg$currPos = s0;
          s0 = peg$c1;
        }
      } else {
        peg$currPos = s0;
        s0 = peg$c1;
      }

      return s0;
    }

    function peg$parseexp() {
      var s0, s1, s2;

      s0 = peg$currPos;
      s1 = peg$parsee();
      if (s1 !== null) {
        s2 = peg$parsedigits();
        if (s2 !== null) {
          s1 = [s1, s2];
          s0 = s1;
        } else {
          peg$currPos = s0;
          s0 = peg$c1;
        }
      } else {
        peg$currPos = s0;
        s0 = peg$c1;
      }

      return s0;
    }

    function peg$parsedigits() {
      var s0, s1;

      s0 = [];
      s1 = peg$parsedigit();
      if (s1 !== null) {
        while (s1 !== null) {
          s0.push(s1);
          s1 = peg$parsedigit();
        }
      } else {
        s0 = peg$c1;
      }

      return s0;
    }

    function peg$parsee() {
      var s0, s1, s2;

      s0 = peg$currPos;
      if (peg$c167.test(input.charAt(peg$currPos))) {
        s1 = input.charAt(peg$currPos);
        peg$currPos++;
      } else {
        s1 = null;
        if (peg$silentFails === 0) { peg$fail(peg$c168); }
      }
      if (s1 !== null) {
        if (peg$c169.test(input.charAt(peg$currPos))) {
          s2 = input.charAt(peg$currPos);
          peg$currPos++;
        } else {
          s2 = null;
          if (peg$silentFails === 0) { peg$fail(peg$c170); }
        }
        if (s2 === null) {
          s2 = peg$c12;
        }
        if (s2 !== null) {
          s1 = [s1, s2];
          s0 = s1;
        } else {
          peg$currPos = s0;
          s0 = peg$c1;
        }
      } else {
        peg$currPos = s0;
        s0 = peg$c1;
      }

      return s0;
    }

    function peg$parsedigit() {
      var s0;

      if (peg$c171.test(input.charAt(peg$currPos))) {
        s0 = input.charAt(peg$currPos);
        peg$currPos++;
      } else {
        s0 = null;
        if (peg$silentFails === 0) { peg$fail(peg$c172); }
      }

      return s0;
    }

    function peg$parsedigit19() {
      var s0;

      if (peg$c173.test(input.charAt(peg$currPos))) {
        s0 = input.charAt(peg$currPos);
        peg$currPos++;
      } else {
        s0 = null;
        if (peg$silentFails === 0) { peg$fail(peg$c174); }
      }

      return s0;
    }

    function peg$parse_() {
      var s0, s1;

      s0 = [];
      s1 = peg$parsewhiteSpace();
      if (s1 === null) {
        s1 = peg$parselineTerminator();
      }
      while (s1 !== null) {
        s0.push(s1);
        s1 = peg$parsewhiteSpace();
        if (s1 === null) {
          s1 = peg$parselineTerminator();
        }
      }

      return s0;
    }

    function peg$parsewhiteSpace() {
      var s0, s1;

      peg$silentFails++;
      if (peg$c176.test(input.charAt(peg$currPos))) {
        s0 = input.charAt(peg$currPos);
        peg$currPos++;
      } else {
        s0 = null;
        if (peg$silentFails === 0) { peg$fail(peg$c177); }
      }
      if (s0 === null) {
        if (peg$c178.test(input.charAt(peg$currPos))) {
          s0 = input.charAt(peg$currPos);
          peg$currPos++;
        } else {
          s0 = null;
          if (peg$silentFails === 0) { peg$fail(peg$c179); }
        }
      }
      peg$silentFails--;
      if (s0 === null) {
        s1 = null;
        if (peg$silentFails === 0) { peg$fail(peg$c175); }
      }

      return s0;
    }

    function peg$parselineTerminator() {
      var s0, s1;

      peg$silentFails++;
      if (peg$c181.test(input.charAt(peg$currPos))) {
        s0 = input.charAt(peg$currPos);
        peg$currPos++;
      } else {
        s0 = null;
        if (peg$silentFails === 0) { peg$fail(peg$c182); }
      }
      peg$silentFails--;
      if (s0 === null) {
        s1 = null;
        if (peg$silentFails === 0) { peg$fail(peg$c180); }
      }

      return s0;
    }

    function peg$parsecomment() {
      var s0, s1, s2, s3, s4, s5, s6;

      s0 = peg$currPos;
      if (input.substr(peg$currPos, 2) === peg$c183) {
        s1 = peg$c183;
        peg$currPos += 2;
      } else {
        s1 = null;
        if (peg$silentFails === 0) { peg$fail(peg$c184); }
      }
      if (s1 !== null) {
        s2 = peg$currPos;
        s3 = [];
        s4 = peg$currPos;
        s5 = peg$currPos;
        peg$silentFails++;
        if (input.substr(peg$currPos, 2) === peg$c185) {
          s6 = peg$c185;
          peg$currPos += 2;
        } else {
          s6 = null;
          if (peg$silentFails === 0) { peg$fail(peg$c186); }
        }
        peg$silentFails--;
        if (s6 === null) {
          s5 = peg$c12;
        } else {
          peg$currPos = s5;
          s5 = peg$c1;
        }
        if (s5 !== null) {
          if (input.length > peg$currPos) {
            s6 = input.charAt(peg$currPos);
            peg$currPos++;
          } else {
            s6 = null;
            if (peg$silentFails === 0) { peg$fail(peg$c187); }
          }
          if (s6 !== null) {
            s5 = [s5, s6];
            s4 = s5;
          } else {
            peg$currPos = s4;
            s4 = peg$c1;
          }
        } else {
          peg$currPos = s4;
          s4 = peg$c1;
        }
        while (s4 !== null) {
          s3.push(s4);
          s4 = peg$currPos;
          s5 = peg$currPos;
          peg$silentFails++;
          if (input.substr(peg$currPos, 2) === peg$c185) {
            s6 = peg$c185;
            peg$currPos += 2;
          } else {
            s6 = null;
            if (peg$silentFails === 0) { peg$fail(peg$c186); }
          }
          peg$silentFails--;
          if (s6 === null) {
            s5 = peg$c12;
          } else {
            peg$currPos = s5;
            s5 = peg$c1;
          }
          if (s5 !== null) {
            if (input.length > peg$currPos) {
              s6 = input.charAt(peg$currPos);
              peg$currPos++;
            } else {
              s6 = null;
              if (peg$silentFails === 0) { peg$fail(peg$c187); }
            }
            if (s6 !== null) {
              s5 = [s5, s6];
              s4 = s5;
            } else {
              peg$currPos = s4;
              s4 = peg$c1;
            }
          } else {
            peg$currPos = s4;
            s4 = peg$c1;
          }
        }
        if (s3 !== null) {
          s3 = input.substring(s2, peg$currPos);
        }
        s2 = s3;
        if (s2 !== null) {
          if (input.substr(peg$currPos, 2) === peg$c185) {
            s3 = peg$c185;
            peg$currPos += 2;
          } else {
            s3 = null;
            if (peg$silentFails === 0) { peg$fail(peg$c186); }
          }
          if (s3 !== null) {
            peg$reportedPos = s0;
            s1 = peg$c188(s2);
            if (s1 === null) {
              peg$currPos = s0;
              s0 = s1;
            } else {
              s0 = s1;
            }
          } else {
            peg$currPos = s0;
            s0 = peg$c1;
          }
        } else {
          peg$currPos = s0;
          s0 = peg$c1;
        }
      } else {
        peg$currPos = s0;
        s0 = peg$c1;
      }

      return s0;
    }

    function peg$parsesheet() {
      var s0, s1, s2, s3;

      s0 = peg$currPos;
      s1 = peg$parse_();
      if (s1 !== null) {
        s2 = [];
        s3 = peg$parseblock();
        while (s3 !== null) {
          s2.push(s3);
          s3 = peg$parseblock();
        }
        if (s2 !== null) {
          s3 = peg$parse_();
          if (s3 !== null) {
            peg$reportedPos = s0;
            s1 = peg$c189(s2);
            if (s1 === null) {
              peg$currPos = s0;
              s0 = s1;
            } else {
              s0 = s1;
            }
          } else {
            peg$currPos = s0;
            s0 = peg$c1;
          }
        } else {
          peg$currPos = s0;
          s0 = peg$c1;
        }
      } else {
        peg$currPos = s0;
        s0 = peg$c1;
      }

      return s0;
    }

    function peg$parseblock() {
      var s0, s1, s2, s3, s4, s5, s6, s7, s8, s9;

      s0 = peg$currPos;
      if (input.charCodeAt(peg$currPos) === 64) {
        s1 = peg$c91;
        peg$currPos++;
      } else {
        s1 = null;
        if (peg$silentFails === 0) { peg$fail(peg$c92); }
      }
      if (s1 !== null) {
        s2 = peg$parseword();
        if (s2 !== null) {
          s3 = peg$parse_();
          if (s3 !== null) {
            s4 = peg$parseannotation();
            if (s4 === null) {
              s4 = peg$c12;
            }
            if (s4 !== null) {
              if (input.charCodeAt(peg$currPos) === 123) {
                s5 = peg$c70;
                peg$currPos++;
              } else {
                s5 = null;
                if (peg$silentFails === 0) { peg$fail(peg$c71); }
              }
              if (s5 !== null) {
                s6 = peg$parse_();
                if (s6 !== null) {
                  s7 = peg$parsestatements();
                  if (s7 !== null) {
                    if (input.charCodeAt(peg$currPos) === 125) {
                      s8 = peg$c72;
                      peg$currPos++;
                    } else {
                      s8 = null;
                      if (peg$silentFails === 0) { peg$fail(peg$c73); }
                    }
                    if (s8 !== null) {
                      s9 = peg$parse_();
                      if (s9 !== null) {
                        peg$reportedPos = s0;
                        s1 = peg$c190(s2,s4,s7);
                        if (s1 === null) {
                          peg$currPos = s0;
                          s0 = s1;
                        } else {
                          s0 = s1;
                        }
                      } else {
                        peg$currPos = s0;
                        s0 = peg$c1;
                      }
                    } else {
                      peg$currPos = s0;
                      s0 = peg$c1;
                    }
                  } else {
                    peg$currPos = s0;
                    s0 = peg$c1;
                  }
                } else {
                  peg$currPos = s0;
                  s0 = peg$c1;
                }
              } else {
                peg$currPos = s0;
                s0 = peg$c1;
              }
            } else {
              peg$currPos = s0;
              s0 = peg$c1;
            }
          } else {
            peg$currPos = s0;
            s0 = peg$c1;
          }
        } else {
          peg$currPos = s0;
          s0 = peg$c1;
        }
      } else {
        peg$currPos = s0;
        s0 = peg$c1;
      }

      return s0;
    }

    function peg$parseannotation() {
      var s0, s1, s2, s3, s4, s5, s6, s7;

      s0 = peg$currPos;
      if (input.charCodeAt(peg$currPos) === 60) {
        s1 = peg$c29;
        peg$currPos++;
      } else {
        s1 = null;
        if (peg$silentFails === 0) { peg$fail(peg$c30); }
      }
      if (s1 === null) {
        if (input.charCodeAt(peg$currPos) === 58) {
          s1 = peg$c15;
          peg$currPos++;
        } else {
          s1 = null;
          if (peg$silentFails === 0) { peg$fail(peg$c16); }
        }
      }
      if (s1 !== null) {
        s2 = peg$parse_();
        if (s2 !== null) {
          s3 = peg$parsestring();
          if (s3 === null) {
            s3 = peg$c12;
          }
          if (s3 !== null) {
            s4 = peg$parse_();
            if (s4 !== null) {
              s5 = peg$currPos;
              s6 = peg$currPos;
              peg$silentFails++;
              if (input.charCodeAt(peg$currPos) === 123) {
                s7 = peg$c70;
                peg$currPos++;
              } else {
                s7 = null;
                if (peg$silentFails === 0) { peg$fail(peg$c71); }
              }
              peg$silentFails--;
              if (s7 === null) {
                s6 = peg$c12;
              } else {
                peg$currPos = s6;
                s6 = peg$c1;
              }
              if (s6 !== null) {
                s7 = peg$parseexpression();
                if (s7 !== null) {
                  s6 = [s6, s7];
                  s5 = s6;
                } else {
                  peg$currPos = s5;
                  s5 = peg$c1;
                }
              } else {
                peg$currPos = s5;
                s5 = peg$c1;
              }
              if (s5 === null) {
                s5 = peg$c12;
              }
              if (s5 !== null) {
                s6 = peg$parse_();
                if (s6 !== null) {
                  peg$reportedPos = s0;
                  s1 = peg$c191(s1,s3,s5);
                  if (s1 === null) {
                    peg$currPos = s0;
                    s0 = s1;
                  } else {
                    s0 = s1;
                  }
                } else {
                  peg$currPos = s0;
                  s0 = peg$c1;
                }
              } else {
                peg$currPos = s0;
                s0 = peg$c1;
              }
            } else {
              peg$currPos = s0;
              s0 = peg$c1;
            }
          } else {
            peg$currPos = s0;
            s0 = peg$c1;
          }
        } else {
          peg$currPos = s0;
          s0 = peg$c1;
        }
      } else {
        peg$currPos = s0;
        s0 = peg$c1;
      }
      if (s0 === null) {
        s0 = peg$currPos;
        s1 = peg$parse_();
        if (s1 !== null) {
          peg$reportedPos = s0;
          s1 = peg$c192();
        }
        if (s1 === null) {
          peg$currPos = s0;
          s0 = s1;
        } else {
          s0 = s1;
        }
      }

      return s0;
    }

    function peg$parsestatements() {
      var s0, s1, s2, s3, s4, s5, s6, s7, s8;

      s0 = peg$currPos;
      s1 = peg$parsestatement();
      if (s1 !== null) {
        s2 = peg$parse_();
        if (s2 !== null) {
          s3 = [];
          s4 = peg$currPos;
          if (input.charCodeAt(peg$currPos) === 59) {
            s5 = peg$c193;
            peg$currPos++;
          } else {
            s5 = null;
            if (peg$silentFails === 0) { peg$fail(peg$c194); }
          }
          if (s5 !== null) {
            s6 = peg$parse_();
            if (s6 !== null) {
              s7 = peg$parsestatement();
              if (s7 !== null) {
                s8 = peg$parse_();
                if (s8 !== null) {
                  s5 = [s5, s6, s7, s8];
                  s4 = s5;
                } else {
                  peg$currPos = s4;
                  s4 = peg$c1;
                }
              } else {
                peg$currPos = s4;
                s4 = peg$c1;
              }
            } else {
              peg$currPos = s4;
              s4 = peg$c1;
            }
          } else {
            peg$currPos = s4;
            s4 = peg$c1;
          }
          while (s4 !== null) {
            s3.push(s4);
            s4 = peg$currPos;
            if (input.charCodeAt(peg$currPos) === 59) {
              s5 = peg$c193;
              peg$currPos++;
            } else {
              s5 = null;
              if (peg$silentFails === 0) { peg$fail(peg$c194); }
            }
            if (s5 !== null) {
              s6 = peg$parse_();
              if (s6 !== null) {
                s7 = peg$parsestatement();
                if (s7 !== null) {
                  s8 = peg$parse_();
                  if (s8 !== null) {
                    s5 = [s5, s6, s7, s8];
                    s4 = s5;
                  } else {
                    peg$currPos = s4;
                    s4 = peg$c1;
                  }
                } else {
                  peg$currPos = s4;
                  s4 = peg$c1;
                }
              } else {
                peg$currPos = s4;
                s4 = peg$c1;
              }
            } else {
              peg$currPos = s4;
              s4 = peg$c1;
            }
          }
          if (s3 !== null) {
            if (input.charCodeAt(peg$currPos) === 59) {
              s4 = peg$c193;
              peg$currPos++;
            } else {
              s4 = null;
              if (peg$silentFails === 0) { peg$fail(peg$c194); }
            }
            if (s4 === null) {
              s4 = peg$c12;
            }
            if (s4 !== null) {
              s5 = peg$parse_();
              if (s5 !== null) {
                peg$reportedPos = s0;
                s1 = peg$c5(s1,s3);
                if (s1 === null) {
                  peg$currPos = s0;
                  s0 = s1;
                } else {
                  s0 = s1;
                }
              } else {
                peg$currPos = s0;
                s0 = peg$c1;
              }
            } else {
              peg$currPos = s0;
              s0 = peg$c1;
            }
          } else {
            peg$currPos = s0;
            s0 = peg$c1;
          }
        } else {
          peg$currPos = s0;
          s0 = peg$c1;
        }
      } else {
        peg$currPos = s0;
        s0 = peg$c1;
      }
      if (s0 === null) {
        s0 = peg$currPos;
        s1 = peg$parsestatement();
        if (s1 !== null) {
          s2 = peg$parse_();
          if (s2 !== null) {
            if (input.charCodeAt(peg$currPos) === 59) {
              s3 = peg$c193;
              peg$currPos++;
            } else {
              s3 = null;
              if (peg$silentFails === 0) { peg$fail(peg$c194); }
            }
            if (s3 === null) {
              s3 = peg$c12;
            }
            if (s3 !== null) {
              s4 = peg$parse_();
              if (s4 !== null) {
                peg$reportedPos = s0;
                s1 = peg$c195(s1);
                if (s1 === null) {
                  peg$currPos = s0;
                  s0 = s1;
                } else {
                  s0 = s1;
                }
              } else {
                peg$currPos = s0;
                s0 = peg$c1;
              }
            } else {
              peg$currPos = s0;
              s0 = peg$c1;
            }
          } else {
            peg$currPos = s0;
            s0 = peg$c1;
          }
        } else {
          peg$currPos = s0;
          s0 = peg$c1;
        }
        if (s0 === null) {
          s0 = peg$currPos;
          s1 = peg$parse_();
          if (s1 !== null) {
            peg$reportedPos = s0;
            s1 = peg$c10();
          }
          if (s1 === null) {
            peg$currPos = s0;
            s0 = s1;
          } else {
            s0 = s1;
          }
        }
      }

      return s0;
    }

    function peg$parsestatement() {
      var s0, s1, s2, s3, s4, s5, s6, s7, s8, s9, s10, s11, s12, s13, s14, s15, s16;

      s0 = peg$currPos;
      if (input.substr(peg$currPos, 2) === peg$c196) {
        s1 = peg$c196;
        peg$currPos += 2;
      } else {
        s1 = null;
        if (peg$silentFails === 0) { peg$fail(peg$c197); }
      }
      if (s1 === null) {
        if (input.substr(peg$currPos, 6) === peg$c198) {
          s1 = peg$c198;
          peg$currPos += 6;
        } else {
          s1 = null;
          if (peg$silentFails === 0) { peg$fail(peg$c199); }
        }
      }
      if (s1 !== null) {
        if (input.charCodeAt(peg$currPos) === 32) {
          s2 = peg$c200;
          peg$currPos++;
        } else {
          s2 = null;
          if (peg$silentFails === 0) { peg$fail(peg$c201); }
        }
        if (s2 !== null) {
          s3 = peg$parse_();
          if (s3 !== null) {
            s4 = peg$parseword();
            if (s4 !== null) {
              s5 = peg$parse_();
              if (s5 !== null) {
                if (input.substr(peg$currPos, 2) === peg$c202) {
                  s6 = peg$c202;
                  peg$currPos += 2;
                } else {
                  s6 = null;
                  if (peg$silentFails === 0) { peg$fail(peg$c203); }
                }
                if (s6 !== null) {
                  s7 = peg$parse_();
                  if (s7 !== null) {
                    s8 = peg$parseexpression();
                    if (s8 !== null) {
                      s9 = peg$parse_();
                      if (s9 !== null) {
                        peg$reportedPos = s0;
                        s1 = peg$c204(s1,s4,s8);
                        if (s1 === null) {
                          peg$currPos = s0;
                          s0 = s1;
                        } else {
                          s0 = s1;
                        }
                      } else {
                        peg$currPos = s0;
                        s0 = peg$c1;
                      }
                    } else {
                      peg$currPos = s0;
                      s0 = peg$c1;
                    }
                  } else {
                    peg$currPos = s0;
                    s0 = peg$c1;
                  }
                } else {
                  peg$currPos = s0;
                  s0 = peg$c1;
                }
              } else {
                peg$currPos = s0;
                s0 = peg$c1;
              }
            } else {
              peg$currPos = s0;
              s0 = peg$c1;
            }
          } else {
            peg$currPos = s0;
            s0 = peg$c1;
          }
        } else {
          peg$currPos = s0;
          s0 = peg$c1;
        }
      } else {
        peg$currPos = s0;
        s0 = peg$c1;
      }
      if (s0 === null) {
        s0 = peg$currPos;
        s1 = peg$parseexpression();
        if (s1 !== null) {
          s2 = peg$parse_();
          if (s2 !== null) {
            if (input.charCodeAt(peg$currPos) === 58) {
              s3 = peg$c15;
              peg$currPos++;
            } else {
              s3 = null;
              if (peg$silentFails === 0) { peg$fail(peg$c16); }
            }
            if (s3 === null) {
              if (input.substr(peg$currPos, 3) === peg$c205) {
                s3 = peg$c205;
                peg$currPos += 3;
              } else {
                s3 = null;
                if (peg$silentFails === 0) { peg$fail(peg$c206); }
              }
              if (s3 === null) {
                if (input.substr(peg$currPos, 2) === peg$c207) {
                  s3 = peg$c207;
                  peg$currPos += 2;
                } else {
                  s3 = null;
                  if (peg$silentFails === 0) { peg$fail(peg$c208); }
                }
              }
            }
            if (s3 !== null) {
              s4 = peg$parse_();
              if (s4 !== null) {
                s5 = peg$parseexpression();
                if (s5 !== null) {
                  s6 = peg$parse_();
                  if (s6 !== null) {
                    s7 = [];
                    s8 = peg$currPos;
                    if (input.charCodeAt(peg$currPos) === 44) {
                      s9 = peg$c3;
                      peg$currPos++;
                    } else {
                      s9 = null;
                      if (peg$silentFails === 0) { peg$fail(peg$c4); }
                    }
                    if (s9 !== null) {
                      s10 = peg$parse_();
                      if (s10 !== null) {
                        s11 = peg$parseword();
                        if (s11 !== null) {
                          s12 = peg$parse_();
                          if (s12 !== null) {
                            if (input.charCodeAt(peg$currPos) === 58) {
                              s13 = peg$c15;
                              peg$currPos++;
                            } else {
                              s13 = null;
                              if (peg$silentFails === 0) { peg$fail(peg$c16); }
                            }
                            if (s13 !== null) {
                              s14 = peg$parse_();
                              if (s14 !== null) {
                                s15 = peg$parseexpression();
                                if (s15 !== null) {
                                  s16 = peg$parse_();
                                  if (s16 !== null) {
                                    s9 = [s9, s10, s11, s12, s13, s14, s15, s16];
                                    s8 = s9;
                                  } else {
                                    peg$currPos = s8;
                                    s8 = peg$c1;
                                  }
                                } else {
                                  peg$currPos = s8;
                                  s8 = peg$c1;
                                }
                              } else {
                                peg$currPos = s8;
                                s8 = peg$c1;
                              }
                            } else {
                              peg$currPos = s8;
                              s8 = peg$c1;
                            }
                          } else {
                            peg$currPos = s8;
                            s8 = peg$c1;
                          }
                        } else {
                          peg$currPos = s8;
                          s8 = peg$c1;
                        }
                      } else {
                        peg$currPos = s8;
                        s8 = peg$c1;
                      }
                    } else {
                      peg$currPos = s8;
                      s8 = peg$c1;
                    }
                    while (s8 !== null) {
                      s7.push(s8);
                      s8 = peg$currPos;
                      if (input.charCodeAt(peg$currPos) === 44) {
                        s9 = peg$c3;
                        peg$currPos++;
                      } else {
                        s9 = null;
                        if (peg$silentFails === 0) { peg$fail(peg$c4); }
                      }
                      if (s9 !== null) {
                        s10 = peg$parse_();
                        if (s10 !== null) {
                          s11 = peg$parseword();
                          if (s11 !== null) {
                            s12 = peg$parse_();
                            if (s12 !== null) {
                              if (input.charCodeAt(peg$currPos) === 58) {
                                s13 = peg$c15;
                                peg$currPos++;
                              } else {
                                s13 = null;
                                if (peg$silentFails === 0) { peg$fail(peg$c16); }
                              }
                              if (s13 !== null) {
                                s14 = peg$parse_();
                                if (s14 !== null) {
                                  s15 = peg$parseexpression();
                                  if (s15 !== null) {
                                    s16 = peg$parse_();
                                    if (s16 !== null) {
                                      s9 = [s9, s10, s11, s12, s13, s14, s15, s16];
                                      s8 = s9;
                                    } else {
                                      peg$currPos = s8;
                                      s8 = peg$c1;
                                    }
                                  } else {
                                    peg$currPos = s8;
                                    s8 = peg$c1;
                                  }
                                } else {
                                  peg$currPos = s8;
                                  s8 = peg$c1;
                                }
                              } else {
                                peg$currPos = s8;
                                s8 = peg$c1;
                              }
                            } else {
                              peg$currPos = s8;
                              s8 = peg$c1;
                            }
                          } else {
                            peg$currPos = s8;
                            s8 = peg$c1;
                          }
                        } else {
                          peg$currPos = s8;
                          s8 = peg$c1;
                        }
                      } else {
                        peg$currPos = s8;
                        s8 = peg$c1;
                      }
                    }
                    if (s7 !== null) {
                      peg$reportedPos = s0;
                      s1 = peg$c209(s1,s3,s5,s7);
                      if (s1 === null) {
                        peg$currPos = s0;
                        s0 = s1;
                      } else {
                        s0 = s1;
                      }
                    } else {
                      peg$currPos = s0;
                      s0 = peg$c1;
                    }
                  } else {
                    peg$currPos = s0;
                    s0 = peg$c1;
                  }
                } else {
                  peg$currPos = s0;
                  s0 = peg$c1;
                }
              } else {
                peg$currPos = s0;
                s0 = peg$c1;
              }
            } else {
              peg$currPos = s0;
              s0 = peg$c1;
            }
          } else {
            peg$currPos = s0;
            s0 = peg$c1;
          }
        } else {
          peg$currPos = s0;
          s0 = peg$c1;
        }
        if (s0 === null) {
          s0 = peg$currPos;
          s1 = peg$parseword();
          if (s1 !== null) {
            s2 = peg$parse_();
            if (s2 !== null) {
              s3 = peg$parseexpression();
              if (s3 !== null) {
                s4 = peg$parse_();
                if (s4 !== null) {
                  peg$reportedPos = s0;
                  s1 = peg$c210(s1,s3);
                  if (s1 === null) {
                    peg$currPos = s0;
                    s0 = s1;
                  } else {
                    s0 = s1;
                  }
                } else {
                  peg$currPos = s0;
                  s0 = peg$c1;
                }
              } else {
                peg$currPos = s0;
                s0 = peg$c1;
              }
            } else {
              peg$currPos = s0;
              s0 = peg$c1;
            }
          } else {
            peg$currPos = s0;
            s0 = peg$c1;
          }
        }
      }

      return s0;
    }


        var BINARY = {
            "**": "pow",
            "//": "root",
            "%%": "log",
            "*": "mul",
            "/": "div",
            "%": "mod",
            "rem": "rem",
            "+": "add",
            "-": "sub",
            "<": "lt",
            ">": "gt",
            "<=": "le",
            ">=": "ge",
            "==": "equals",
            "<=>": "compare",
            "??": "default",
            "&&": "and",
            "||": "or",
            "<-": "bind",
            "<->": "bind2",
            ":": "assign"
        };

        var UNARY = {
            "+": "toNumber",
            "-": "neg",
            "!": "not",
            "^": "parent"
        };

        var BLOCKS = {
            "map": "mapBlock",
            "filter": "filterBlock",
            "some": "someBlock",
            "every": "everyBlock",
            "sorted": "sortedBlock",
            "sortedSet": "sortedSetBlock",
            "group": "groupBlock",
            "groupMap": "groupMapBlock",
            "min": "minBlock",
            "max": "maxBlock"
        };

        var STATEMENTS = {
            ":": "assign",
            "<-": "bind",
            "<->": "bind2"
        };



    peg$result = peg$startRuleFunction();

    if (peg$result !== null && peg$currPos === input.length) {
      return peg$result;
    } else {
      peg$cleanupExpected(peg$maxFailExpected);
      peg$reportedPos = Math.max(peg$currPos, peg$maxFailPos);

      throw new SyntaxError(
        peg$maxFailExpected,
        peg$reportedPos < input.length ? input.charAt(peg$reportedPos) : null,
        peg$reportedPos,
        peg$computePosDetails(peg$reportedPos).line,
        peg$computePosDetails(peg$reportedPos).column
      );
    }
  }

  return {
    SyntaxError: SyntaxError,
    parse      : parse
  };
})();

}})
;
//*/
montageDefine("73de13b","compile-evaluator",{dependencies:["collections/shim-object","collections/map","collections/sorted-set","./operators","./scope","./parse"],factory:function(require,exports,module){
var Object = require("collections/shim-object");
var Map = require("collections/map");
var SortedSet = require("collections/sorted-set");
var Operators = require("./operators");
var Scope = require("./scope");

module.exports = compile;
function compile(syntax) {
    return compile.semantics.compile(syntax);
}

var compilers = {

    literal: function (syntax) {
        return function () {
            return syntax.value;
        };
    },

    value: function (syntax) {
        return function (scope) {
            return scope.value;
        };
    },

    parameters: function (syntax) {
        return function (scope) {
            return scope.parameters;
        };
    },

    element: function (syntax) {
        return function (scope) {
            return scope.document.getElementById(syntax.id);
        };
    },

    component: function (syntax) {
        return function (scope) {
            return scope.components.getObjectByLabel(syntax.label);
        };
    },

    tuple: function (syntax) {
        var argEvaluators = syntax.args.map(this.compile, this);
        return function (scope) {
            return argEvaluators.map(function (evaluateArg) {
                return evaluateArg(scope);
            });
        };
    },

    record: function (syntax) {
        var args = syntax.args;
        var argEvaluators = {};
        for (var name in args) {
            argEvaluators[name] = this.compile(args[name]);
        }
        return function (scope) {
            var object = {};
            for (var name in argEvaluators) {
                object[name] = argEvaluators[name](scope);
            }
            return object;
        };
    }

};

var argCompilers = {

    mapBlock: function (evaluateCollection, evaluateRelation) {
        return function (scope) {
            return evaluateCollection(scope)
            .map(function (value) {
                return evaluateRelation(Scope.nest(scope, value));
            });
        };
    },

    filterBlock: function (evaluateCollection, evaluatePredicate) {
        return function (scope) {
            return evaluateCollection(scope)
            .filter(function (value) {
                return evaluatePredicate(Scope.nest(scope, value));
            });
        };
    },

    someBlock: function (evaluateCollection, evaluatePredicate) {
        return function (scope) {
            return evaluateCollection(scope)
            .some(function (value) {
                return evaluatePredicate(Scope.nest(scope, value));
            });
        };
    },

    everyBlock: function (evaluateCollection, evaluatePredicate) {
        return function (scope) {
            return evaluateCollection(scope)
            .every(function (value) {
                return evaluatePredicate(Scope.nest(scope, value));
            });
        };
    },

    sortedBlock: function (evaluateCollection, evaluateRelation) {
        return function (scope) {
            return evaluateCollection(scope)
            .sorted(Function.by(function (value) {
                return evaluateRelation(Scope.nest(scope, value));
            }));
        };
    },

    sortedSetBlock: function (evaluateCollection, evaluateRelation) {
        return function (scope) {
            function map(x) {
                return evaluateRelation(Scope.nest(scope, x));
            }
            function contentCompare(x, y) {
                return Object.compare(map(x), map(y));
            }
            function contentEquals(x, y) {
                return Object.equals(map(x), map(y));
            }
            return new SortedSet(
                evaluateCollection(scope),
                contentEquals,
                contentCompare
            );
        };
    },

    groupBlock: function (evaluateCollection, evaluateRelation) {
        return function (scope) {
            return evaluateCollection(scope)
            .group(function (value) {
                return evaluateRelation(Scope.nest(scope, value));
            });
        };
    },

    groupMapBlock: function (evaluateCollection, evaluateRelation) {
        return function (scope) {
            return new Map(evaluateCollection(scope)
            .group(function (value) {
                return evaluateRelation(Scope.nest(scope, value));
            }));
        };
    },

    minBlock: function (evaluateCollection, evaluateRelation) {
        return function (scope) {
            return evaluateCollection(scope)
            .min(Function.by(function (value) {
                return evaluateRelation(Scope.nest(scope, value));
            }))
        };
    },

    maxBlock: function (evaluateCollection, evaluateRelation) {
        return function (scope) {
            return evaluateCollection(scope)
            .max(Function.by(function (value) {
                return evaluateRelation(Scope.nest(scope, value));
            }))
        };
    },

    parent: function (evaluateExpression) {
        return function (scope) {
            return evaluateExpression(scope.parent);
        };
    },

    "with": function (evaluateContext, evaluateExpression) {
        return function (scope) {
            return evaluateExpression(Scope.nest(scope, evaluateContext(scope)));
        };
    },

    "if": function (evaluateCondition, evaluateConsequent, evaluateAlternate) {
        return function (scope) {
            if (evaluateCondition(scope)) {
                return evaluateConsequent(scope);
            } else {
                return evaluateAlternate(scope);
            }
        }
    },

    not: function (evaluateValue) {
        return function (scope) {
            return !evaluateValue(scope);
        };
    },

    and: function (evaluateLeft, evaluateRight) {
        return function (scope) {
            return evaluateLeft(scope) && evaluateRight(scope);
        };
    },

    or: function (evaluateLeft, evaluateRight) {
        return function (scope) {
            return evaluateLeft(scope) || evaluateRight(scope);
        };
    },

    "default": function (evaluateLeft, evaluateRight) {
        return function (scope) {
            var result = evaluateLeft(scope);
            if (result == null) { // implies "iff === null or undefined"
                result = evaluateRight(scope);
            }
            return result;
        }
    },

    defined: function (evaluate) {
        return function (scope) {
            var value = evaluate(scope);
            return value != null; // implies exactly !== null or undefined
        };
    },

    // TODO rename to evaluate
    path: function (evaluateObject, evaluatePath) {
        return function (scope) {
            var value = evaluateObject(scope);
            var path = evaluatePath(scope);
            var parse = require("./parse");
            try {
                var syntax = parse(path);
                var evaluate = compile(syntax);
                return evaluate(Scope.nest(scope, value));
            } catch (exception) {
            }
        }
    }

};

var operators = Object.clone(Operators, 1);

Object.addEach(operators, {

    property: function (object, key) {
        return object[key];
    },

    get: function (collection, key) {
        return collection.get(key);
    },

    mapContent: Function.identity,

    rangeContent: Function.identity,

    view: function (collection, start, length) {
        return collection.slice(start, start + length);
    }

});

var semantics = compile.semantics = {

    compilers: compilers,
    argCompilers: argCompilers,
    operators: operators,

    compile: function (syntax) {
        var compilers = this.compilers;
        var argCompilers = this.argCompilers;
        var operators = this.operators;
        if (compilers.hasOwnProperty(syntax.type)) {
            return compilers[syntax.type].call(this, syntax);
        } else if (argCompilers.hasOwnProperty(syntax.type)) {
            var argEvaluators = syntax.args.map(this.compile, this);
            return argCompilers[syntax.type].apply(null, argEvaluators);
        } else {
            if (!operators.hasOwnProperty(syntax.type)) {
                operators[syntax.type] = function (object) {
                    var args = Array.prototype.slice.call(arguments, 1);
                    if (!object[syntax.type])
                        throw new TypeError("Can't call " + JSON.stringify(syntax.type) + " of " + object);
                    return object[syntax.type].apply(object, args);
                };
            }
            var operator = operators[syntax.type];
            var argEvaluators = syntax.args.map(this.compile, this);
            return function (scope) {
                var args = argEvaluators.map(function (evaluateArg) {
                    return evaluateArg(scope);
                });
                if (!args.every(Operators.defined))
                    return;
                return operator.apply(null, args);
            };
        }

    }

};


}})
;
//*/
montageDefine("73de13b","compile-assigner",{dependencies:["./compile-evaluator","./algebra","./scope"],factory:function(require,exports,module){
var compileEvaluator = require("./compile-evaluator");
var solve = require("./algebra");
var Scope = require("./scope");
var valueSyntax = {type: "value"};
var trueScope = {type: "literal", value: true};
var falseScope = {type: "literal", value: false};

module.exports = compile;
function compile(syntax) {
    return compile.semantics.compile(syntax);
}

compile.semantics = {

    compile: function (syntax) {
        var compilers = this.compilers;
        if (syntax.type === "equals") {
            var assignLeft = this.compile(syntax.args[0]);
            var evaluateRight = this.compileEvaluator(syntax.args[1]);
            return compilers.equals(assignLeft, evaluateRight);
        } else if (syntax.type === "if") {
            var evaluateCondition = this.compileEvaluator(syntax.args[0]);
            var assignConsequent = this.compile(syntax.args[1]);
            var assignAlternate = this.compile(syntax.args[2]);
            return compilers["if"](evaluateCondition, assignConsequent, assignAlternate);
        } else if (syntax.type === "and" || syntax.type === "or") {
            var leftArgs = solve(syntax.args[0], valueSyntax);
            var rightArgs = solve(syntax.args[1], valueSyntax);
            var evaluateLeft = this.compileEvaluator(syntax.args[0]);
            var evaluateRight = this.compileEvaluator(syntax.args[1]);
            var evaluateLeftAssign = this.compileEvaluator(leftArgs[1]);
            var evaluateRightAssign = this.compileEvaluator(rightArgs[1]);
            var assignLeft = this.compile(leftArgs[0]);
            var assignRight = this.compile(rightArgs[0]);
            return compilers[syntax.type](
                assignLeft,
                assignRight,
                evaluateLeft,
                evaluateRight,
                evaluateLeftAssign,
                evaluateRightAssign
            );
        } else if (syntax.type === "everyBlock") {
            var evaluateCollection = this.compileEvaluator(syntax.args[0]);
            var args = solve(syntax.args[1], {type: "literal", value: true});
            var assignCondition = this.compile(args[0]);
            var evaluateValue = this.compileEvaluator(args[1]);
            return compilers["everyBlock"](evaluateCollection, assignCondition, evaluateValue);
        } else if (compilers.hasOwnProperty(syntax.type)) {
            var argEvaluators = syntax.args.map(this.compileEvaluator, this.compileEvaluator.semantics);
            return compilers[syntax.type].apply(null, argEvaluators);
        } else {
            throw new Error("Can't compile assigner for " + JSON.stringify(syntax.type));
        }
    },

    compileEvaluator: compileEvaluator,

    compilers: {

        property: function (evaluateObject, evaluateKey) {
            return function (value, scope) {
                var object = evaluateObject(scope);
                if (!object) return;
                var key = evaluateKey(scope);
                if (key == null) return;
                if (Array.isArray(object)) {
                    object.set(key, value);
                } else {
                    object[key] = value;
                }
            };
        },

        get: function (evaluateCollection, evaluateKey) {
            return function (value, scope) {
                var collection = evaluateCollection(scope);
                if (!collection) return;
                var key = evaluateKey(scope);
                if (key == null) return;
                collection.set(key, value);
            };
        },

        has: function (evaluateCollection, evaluateValue) {
            return function (has, scope) {
                var collection = evaluateCollection(scope);
                if (!collection) return;
                var value = evaluateValue(scope);
                if (has == null) return;
                if (has) {
                    if (!(collection.has || collection.contains).call(collection, value)) {
                        collection.add(value);
                    }
                } else {
                    if ((collection.has || collection.contains).call(collection, value)) {
                        (collection.remove || collection["delete"]).call(collection, value);
                    }
                }
            };
        },

        equals: function (assignLeft, evaluateRight) {
            return function (value, scope) {
                if (value) {
                    return assignLeft(evaluateRight(scope), scope);
                }
            };
        },

        "if": function (evaluateCondition, assignConsequent, assignAlternate) {
            return function (value, scope) {
                var condition = evaluateCondition(scope);
                if (condition == null) return;
                if (condition) {
                    return assignConsequent(value, scope);
                } else {
                    return assignAlternate(value, scope);
                }
            };
        },

        and: function (assignLeft, assignRight, evaluateLeft, evaluateRight, evaluateLeftAssign, evaluateRightAssign) {
            return function (value, scope) {
                if (value == null) return;
                if (value) {
                    assignLeft(evaluateLeftAssign(trueScope), scope);
                    assignRight(evaluateRightAssign(trueScope), scope);
                } else {
                    assignLeft(evaluateLeft(scope) && !evaluateRight(scope), scope);
                }
            }
        },

        or: function (assignLeft, assignRight, evaluateLeft, evaluateRight, evaluateLeftAssign, evaluateRightAssign) {
            return function (value, scope) {
                if (value == null) return;
                if (!value) {
                    assignLeft(evaluateLeftAssign(falseScope), scope);
                    assignRight(evaluateRightAssign(falseScope), scope);
                } else {
                    assignLeft(evaluateLeft(scope) || !evaluateRight(scope), scope);
                }
            }
        },

        rangeContent: function (evaluateTarget) {
            return function (value, scope) {
                var target = evaluateTarget(scope);
                if (!target) return;
                if (!value) {
                    target.clear();
                } else {
                    target.swap(0, target.length, value);
                }
            };
        },

        mapContent: function (evaluateTarget) {
            return function (value, scope) {
                var target = evaluateTarget(scope);
                if (!target) return;
                target.clear();
                if (scope.value) {
                    target.addEach(value);
                }
            };
        },

        reversed: function (evaluateTarget) {
            return function (value, scope) {
                var target = evaluateTarget(scope);
                if (!target) return;
                target.swap(0, target.length, value.reversed());
            };
        },

        everyBlock: function (evaluateCollection, assignCondition, evaluateEffect) {
            return function (value, scope) {
                if (value) {
                    var collection = evaluateCollection(scope);
                    var effect = evaluateEffect(scope);
                    collection.forEach(function (content) {
                        assignCondition(effect, Scope.nest(scope, content));
                    });
                }
            };
        }

    }

}


}})
;
//*/
montageDefine("73de13b","algebra",{dependencies:[],factory:function(require,exports,module){
// TODO commute literals on the left side of a target operand, when possible

module.exports = solve;
function solve(target, source) {
    return solve.semantics.solve(target, source);
}

solve.semantics = {

    solve: function (target, source) {
        while (true) {
            // simplify the target
            while (this.simplifiers.hasOwnProperty(target.type)) {
                var simplification = this.simplifiers[target.type](target);
                if (simplification) {
                    target = simplification;
                } else {
                    break;
                }
            }
            // solve for bindable target (rotate terms to source)
            if (!this.solvers.hasOwnProperty(target.type)) {
                break;
            }
            source = this.solvers[target.type](target, source);
            target = target.args[0];
        }
        return [target, source];
    },

    simplifiers: {
        // !!x -> x
        not: function (syntax) {
            var left = syntax.args[0];
            if (left.type === "not") {
                return left.args[0];
            }
        },
        // "" + x -> x.toString()
        add: function (syntax) {
            var left = syntax.args[0];
            if (left.type === "literal" && left.value === "") {
                // "" + x
                // toString(x)
                // because this can be bound bidirectionally with toNumber(y)
                return {
                    type: "toString",
                    args: [syntax.args[1]]
                };
            }
        },
        // DeMorgan's law applied to `some` so we only have to implement
        // `every`.
        // some{x} -> !every{!x}
        someBlock: function (syntax) {
            return {type: "not", args: [
                {type: "everyBlock", args: [
                    syntax.args[0],
                    {type: "not", args: [
                        syntax.args[1]
                    ]}
                ]}
            ]};
        }
    },

    solvers: {
        // e.g.,
        // !y = x
        // y = !x
        reflect: function (target, source) {
            return {type: target.type, args: [source]};
        },
        // e.g.,
        // y + 1 = x
        // y = x - 1
        invert: function (target, source, operator) {
            return {type: operator, args: [
                source,
                target.args[1]
            ]};
        },
        toNumber: function (target, source) {
            return this.reflect(target, source);
        },
        toString: function (target, source) {
            return this.reflect(target, source);
        },
        not: function (target, source) {
            return this.reflect(target, source);
        },
        neg: function (target, source) {
            return this.reflect(target, source);
        },
        add: function (target, source) {
            return this.invert(target, source, 'sub');
        },
        sub: function (target, source) {
            return this.invert(target, source, 'add');
        },
        mul: function (target, source) {
            return this.invert(target, source, 'div');
        },
        div: function (target, source) {
            return this.invert(target, source, 'mul');
        },
        pow: function (target, source) {
            return this.invert(target, source, 'root');
        },
        root: function (target, source) {
            return this.invert(target, source, 'pow');
        },
        // terms.join(delimiter) <- string
        // terms <- string.split(delimiter)
        join: function (target, source) {
            return this.invert(target, source, 'split');
        },
        split: function (target, source) {
            return this.invert(target, source, 'join');
        }

    }

};


}})
;
//*/
montageDefine("73de13b","compile-binder",{dependencies:["./compile-observer","./observers","./binders","./algebra"],factory:function(require,exports,module){
var compileObserver = require("./compile-observer");
var Observers = require("./observers");
var Binders = require("./binders");
var solve = require("./algebra");

var valueSyntax = {type: "value"};
var trueSyntax = {type: "literal", value: true};

module.exports = compile;
function compile(syntax) {
    return compile.semantics.compile(syntax);
}

compile.semantics = {

    compilers: {
        property: Binders.makePropertyBinder,
        get: Binders.makeGetBinder,
        has: Binders.makeHasBinder,
        only: Binders.makeOnlyBinder,
        rangeContent: Binders.makeRangeContentBinder,
        mapContent: Binders.makeMapContentBinder,
        reversed: Binders.makeReversedBinder,
        and: Binders.makeAndBinder,
        or: Binders.makeOrBinder
    },

    compile: function (syntax) {
        var compilers = this.compilers;
        if (syntax.type === "default") {
            return this.compile(syntax.args[0]);
        } else if (syntax.type === "literal") {
            if (syntax.value == null) {
                return Function.noop;
            } else {
                throw new Error("Can't bind to literal: " + syntax.value);
            }
        } else if (syntax.type === "equals") {
            var bindLeft = this.compile(syntax.args[0]);
            var observeRight = compileObserver(syntax.args[1]);
            return Binders.makeEqualityBinder(bindLeft, observeRight);
        } else if (syntax.type === "if") {
            var observeCondition = compileObserver(syntax.args[0]);
            var bindConsequent = this.compile(syntax.args[1]);
            var bindAlternate = this.compile(syntax.args[2]);
            return Binders.makeConditionalBinder(observeCondition, bindConsequent, bindAlternate);
        } else if (syntax.type === "and" || syntax.type === "or") {
            var leftArgs = solve(syntax.args[0], valueSyntax);
            var rightArgs = solve(syntax.args[1], valueSyntax);
            var bindLeft = this.compile(leftArgs[0]);
            var bindRight = this.compile(rightArgs[0]);
            var observeLeftBind = compileObserver(leftArgs[1]);
            var observeRightBind = compileObserver(rightArgs[1]);
            var observeLeft = compileObserver(syntax.args[0]);
            var observeRight = compileObserver(syntax.args[1]);
            return this.compilers[syntax.type](
                bindLeft,
                bindRight,
                observeLeft,
                observeRight,
                observeLeftBind,
                observeRightBind
            );
        } else if (syntax.type === "everyBlock") {
            var observeCollection = compileObserver(syntax.args[0]);
            var args = solve(syntax.args[1], trueSyntax);
            var bindCondition = this.compile(args[0]);
            var observeValue = compileObserver(args[1]);
            return Binders.makeEveryBlockBinder(observeCollection, bindCondition, observeValue);
        } else if (syntax.type === "rangeContent") {
            var observeTarget = compileObserver(syntax.args[0]);
            var bindTarget;
            try {
                bindTarget = this.compile(syntax.args[0]);
            } catch (exception) {
                bindTarget = Function.noop;
            }
            return Binders.makeRangeContentBinder(observeTarget, bindTarget);
        } else if (syntax.type === "defined") {
            var bindTarget = this.compile(syntax.args[0]);
            return Binders.makeDefinedBinder(bindTarget);
        } else if (compilers.hasOwnProperty(syntax.type)) {
            var argObservers = syntax.args.map(compileObserver, compileObserver.semantics);
            return compilers[syntax.type].apply(null, argObservers);
        } else {
            throw new Error("Can't compile binder for " + JSON.stringify(syntax.type));
        }
    }

};


}})
;
//*/
montageDefine("73de13b","operators",{dependencies:["collections/shim-object","collections/shim-regexp","collections/map","collections/set"],factory:function(require,exports,module){
require("collections/shim-object"); // equals, compare
require("collections/shim-regexp"); // escape
var Map = require("collections/map");
var Set = require("collections/set");

// from highest to lowest precedence

exports.toNumber = function (s) {
    return +s;
};

exports.toString = function (value) {
    if (value == null) {
        return value;
    } else if (typeof value === "string" || typeof value === "number") {
        return "" + value;
    } else {
        return null;
    }
};

exports.toArray = Array.from;

exports.toMap = Map;

exports.toSet = Set;

exports.not = function (b) {
    return !b;
};

exports.neg = function (n) {
    return -n;
};

exports.pow = function (a, b) {
    return Math.pow(a, b);
};

exports.root = function (a, b) {
    return Math.pow(a, 1 / b);
};

exports.log = function (a, b) {
    return Math.log(a) / Math.log(b);
};

exports.mul = function (a, b) {
    return a * b;
};

exports.div = function (a, b) {
    return a / b;
};

exports.mod = function (a, b) {
    return ((a % b) + b) % b;
};

exports.rem = function (a, b) {
    return a % b;
};

exports.add = function (a, b) {
    return a + b;
};

exports.sub = function (a, b) {
    return a - b;
};

exports.lt = function (a, b) {
    return Object.compare(a, b) < 0;
};

exports.gt = function (a, b) {
    return Object.compare(a, b) > 0;
};

exports.le = function (a, b) {
    return Object.compare(a, b) <= 0;
};

exports.ge = function (a, b) {
    return Object.compare(a, b) >= 0;
};

exports.equals = Object.equals;

exports.compare = Object.compare;

exports.and = function (a, b) {
    return a && b;
};

exports.or = function (a, b) {
    return a || b;
};

exports.defined = function (value) {
    return value != null;
};

// "startsWith", "endsWith", and "contains"  are overridden in
// complile-observer so they can precompile the regular expression and reuse it
// in each reaction.

exports.startsWith = function (a, b) {
    var expression = new RegExp("^" + RegExp.escape(b));
    return expression.test(a);
};

exports.endsWith = function (a, b) {
    var expression = new RegExp(RegExp.escape(b) + "$");
    return expression.test(a);
};

exports.contains = function (a, b) {
    var expression = new RegExp(RegExp.escape(b));
    return expression.test(a);
};

exports.join = function (a, b) {
    return a.join(b || "");
};

exports.split = function (a, b) {
    return a.split(b || "");
};

exports.range = function (stop) {
    var range = [];
    for (var start = 0; start < stop; start++) {
        range.push(start);
    }
    return range;
};

exports.last = function (collection) {
    return collection.get(collection.length - 1);
};


}})
;
//*/
montageDefine("73de13b","merge",{dependencies:["collections/shim"],factory:function(require,exports,module){"use strict";

require("collections/shim");

/*
 * See: Introduction to Algorithms, Longest Common Subsequence, Algorithms,
 * Cormen et al, 15.4
 * This is an adaptation of the LCS and Lehvenstein distance algorithm that
 * instead of computing the longest subsequence, or the cost of edit, finds the
 * shortest operational transform.  The cost is the cost of traversal in terms
 * of "insert" and "delete" operations, where "retain" is free.
 */
exports.graphOt = graphOt;
function graphOt(target, source) {
    var size = (source.length + 1) * (target.length + 1);

    // edges is a 2D linearized array with a height of target.length + 1 and a
    // width of source.length + 1.  Each cell corresponds to an operational
    // transform walking from the top-left to the bottom-right.
    var edges = Array(size);
    // we only need to know the costs of the previous source column and the
    // current source column up to the cell in question.
    var theseCosts = Array(source.length + 1);
    var prevCosts = Array(source.length + 1);
    var tempCosts;

    for (var t = 0; t < target.length + 1; t++) {
        for (var s = 0; s < source.length + 1; s++) {
            var direction, cost;
            if (t === 0 && s === 0) {
                direction = " ";
                cost = 0;
            } else if (t === 0) {
                direction = "insert";
                cost = s;
            } else if (s === 0) {
                direction = "delete";
                cost = t;
            } else if (target[t - 1] === source[s - 1]) {
                direction = "retain";
                cost = prevCosts[s - 1];
            } else {
                var tCost = theseCosts[s - 1];
                var sCost = prevCosts[s];
                // favoring the source tends to produce more removal followed
                // by insertion, which packs into a swap transforms better
                if (sCost < tCost) {
                    direction = "delete";
                    cost = sCost + 1;
                } else {
                    direction = "insert";
                    cost = tCost + 1;
                }
            }
            edges[t + s * (target.length + 1)] = direction;
            theseCosts[s] = cost;

        }
        // swap columns, reuse and overwrite the previous column as the current
        // column for the next iteration.
        tempCosts = theseCosts;
        theseCosts = prevCosts;
        prevCosts = tempCosts;
    }

    return {
        edges: edges,
        cost: prevCosts[source.length],
        source: source,
        target: target
    };
}

/**
 * Tracks backward through a graph produced by graphOt along the cheapest path
 * from the bottom-right to the top-left to produce the cheapest sequence of
 * operations.  Accumulates adjacent operations of the same type into a single
 * operation of greater length.
 */
exports.traceOt = traceOt;
function traceOt(graph) {
    var ops = [];
    var edges = graph.edges;
    var t, tl = t = graph.target.length;
    var s = graph.source.length;
    var previous;
    while (t || s) {
        var direction = edges[t + s * (tl + 1)];
        if (direction === "delete") {
            if (previous && previous[0] === "delete") {
                previous[1]++;
            } else {
                var op = ["delete", 1];
                previous = op;
                ops.push(op);
            }
            t--;
        } else if (direction === "insert") {
            if (previous && previous[0] === "insert") {
                previous[1]++;
            } else {
                var op = ["insert", 1];
                previous = op;
                ops.push(op);
            }
            s--;
        } else if (direction === "retain") {
            var op = ["retain", 1];
            if (previous && previous[0] === "retain") {
                previous[1]++;
            } else {
                previous = op;
                ops.push(op);
            }
            t--; s--;
        }
    }
    ops.reverse();
    return ops;
}

/**
 * Compute the shortest operational transform on the target sequence to become
 * the source sequence.
 */
exports.ot = ot;
function ot(target, source) {
    return traceOt(graphOt(target, source));
}

/**
 * Compute the shortest sequence of splice or swap operations on the target
 * sequence to become the source sequence.
 */
exports.diff = diff;
function diff(target, source) {
    var ops = ot(target, source);
    var swops = [];

    // convert ops to splice/swap operations
    var t = 0;
    var s = 0;
    var o = 0;
    var previous;
    while (o < ops.length) {
        var op = ops[o++];
        if (op[0] === "insert") {
            swops.push([s, 0, source.slice(s, s + op[1])]);
            s += op[1];
        } else if (op[0] === "delete") {
            if (o < ops.length && ops[o][0] === "insert") {
                var insert = ops[o++];
                swops.push([s, op[1], source.slice(s, s + insert[1])]);
                t += op[1];
                s += insert[1];
            } else {
                swops.push([s, op[1]]);
                t += op[1];
            }
        } else if (op[0] == "retain") {
            t += op[1];
            s += op[1];
        }
    }

    return swops;
}

/**
 * Apply the given sequence of swap operations on the target sequence.
 */
exports.apply = apply;
function apply(target, patch) {
    for (var s = 0; s < patch.length; s++) {
        target.swap.apply(target, patch[s]);
    }
}

/**
 * Apply the shortest sequence of swap operations on the target sequence for it
 * to become equivalent to the target sequence.
 */
exports.merge = merge;
function merge(target, source) {
    apply(target, diff(target, source));
}


}})
;
//*/
montageDefine("73de13b","language",{dependencies:["collections/set","collections/dict"],factory:function(require,exports,module){
var Set = require("collections/set");
var Dict = require("collections/dict");

var precedence = exports.precedence = Dict();
var levels = exports.precedenceLevels = [
    ["tuple", "record"],
    [
        "literal",
        "value",
        "parameters",
        "property",
        "element",
        "component",
        "mapBlock",
        "filterBlock",
        "sortedBlock",
        "groupBlock",
        "groupMapBlock",
        "with"
    ],
    ["not", "neg", "number", "parent"],
    ["scope"],
    ["default"],
    ["pow", "root", "log"],
    ["mul", "div", "mod", "rem"],
    ["add", "sub"],
    ["equals", "lt", "gt", "le", "ge", "compare"],
    ["and"],
    ["or"],
    ["if"]
];

levels.forEach(function (level) {
    var predecessors = Set(precedence.keys());
    level.forEach(function (operator) {
        precedence.set(operator, predecessors);
    });
});

var operatorTokens = exports.operatorTokens = Dict({
    "**": "pow",
    "//": "root",
    "%%": "log",
    "*": "mul",
    "/": "div",
    "%": "mod",
    "rem": "rem",
    "+": "add",
    "-": "sub",
    "<": "lt",
    ">": "gt",
    "<=": "le",
    ">=": "ge",
    "==": "equals",
    "<=>": "compare",
    "!=": "notEquals",
    "??": "default",
    "&&": "and",
    "||": "or",
    "?": "then",
    ":": "else"
});

exports.operatorTypes = Dict(operatorTokens.map(function (type, token) {
    return [type, token];
}));


}})
;
//*/
montageDefine("73de13b","compute",{dependencies:["./parse","./compile-observer","./compile-binder","./observers","./scope"],factory:function(require,exports,module){
var parse = require("./parse");
var compileObserver = require("./compile-observer");
var compileBinder = require("./compile-binder");
var Observers = require("./observers");
var Scope = require("./scope");

module.exports = compute;
function compute(target, targetPath, descriptor) {
    descriptor.target = target;
    descriptor.targetPath = targetPath;
    var source = descriptor.source = descriptor.source || target;
    var args = descriptor.args;
    var compute = descriptor.compute;
    var parameters = descriptor.parameters = descriptor.parameters || source;
    var document = descriptor.document;
    var components = descriptor.components;
    var trace = descriptor.trace;
    var sourceScope = descriptor.sourceScope = new Scope(source, null, parameters, document, components);
    var targetScope = descriptor.targetScope = new Scope(target, null, parameters, document, components);

    var argObservers = args.map(function (arg) {
        return parse(arg);
    }).map(function (argSyntax) {
        if (argSyntax.type === "rangeContent") {
            var observeArg = compileObserver(argSyntax.args[0]);
            return Observers.makeRangeContentObserver(observeArg);
        } else if (argSyntax.type === "mapContent") {
            var observeArg = compileObserver(argSyntax.args[0]);
            return Observers.makeMapContentObserver(observeArg);
        } else {
            return compileObserver(argSyntax);
        }
    });
    var argsObserver = Observers.makeRangeContentObserver(
        Observers.makeObserversObserver(argObservers)
    );
    var observeSource = Observers.makeComputerObserver(argsObserver, compute, target);

    var targetSyntax = parse(targetPath);
    var bindTarget = compileBinder(targetSyntax);

    return bindTarget(observeSource, sourceScope, targetScope, descriptor, trace ? {
        sourcePath: args.join(", "),
        targetPath: targetPath
    }: undefined);
}


}})
;
//*/
montageDefine("73de13b","binders",{dependencies:["./scope","./observers"],factory:function(require,exports,module){
var Scope = require("./scope");
var Observers = require("./observers");
var autoCancelPrevious = Observers.autoCancelPrevious;
var once = Observers.once;
var observeRangeChange = Observers.observeRangeChange;
var cancelEach = Observers.cancelEach;
var makeNotObserver = Observers.makeNotObserver;
var makeOrObserver = Observers.makeOrObserver;
var makeAndObserver = Observers.makeAndObserver;
var observeValue = Observers.observeValue;
var observeUndefined = Observers.makeLiteralObserver();
var trueScope = new Scope(true);
var falseScope = new Scope(false);

function getStackTrace() {
    return new Error("").stack.replace(/^.*\n.*\n/, "\n");
}

exports.bindProperty = bindProperty;
var _bindProperty = bindProperty; // to bypass scope shadowing problems below
function bindProperty(object, key, observeValue, source, descriptor, trace) {
    return observeValue(autoCancelPrevious(function replaceBoundPropertyValue(value) {
        if (descriptor.isActive) {
            return;
        }
        try {
            descriptor.isActive = true;
            trace && console.log("SET", trace.targetPath, "TO", value, "ON", object, "BECAUSE", trace.sourcePath, getStackTrace());
            if (Array.isArray(object) && key >>> 0 === key) {
                // TODO spec this case
                object.set(key, value);
            } else {
                object[key] = value;
            }
        } finally {
            descriptor.isActive = false;
        }
    }), source);
}

exports.makePropertyBinder = makePropertyBinder;
function makePropertyBinder(observeObject, observeKey) {
    return function bindProperty(observeValue, source, target, descriptor, trace) {
        return observeKey(autoCancelPrevious(function replaceKey(key) {
            if (key == null) return;
            return observeObject(autoCancelPrevious(function replaceObject(object) {
                if (object == null) return;
                if (object.bindProperty) {
                    return object.bindProperty(key, observeValue, source, descriptor, trace);
                } else {
                    return _bindProperty(object, key, observeValue, source, descriptor, trace);
                }
            }), target);
        }), target);
    };
}

exports.bindGet = bindGet;
var _bindGet = bindGet; // to bypass scope shadowing below
function bindGet(collection, key, observeValue, source, descriptor, trace) {
    return observeValue(autoCancelPrevious(function replaceValue(value) {
        if (descriptor.isActive) {
            return;
        }
        try {
            descriptor.isActive = true;
            trace && console.log("SET FOR KEY", key, "TO", value, "ON", collection, "BECAUSE", trace.sourcePath, getStackTrace());
            collection.set(key, value);
        } finally {
            descriptor.isActive = false;
        }
    }), source);
}

exports.makeGetBinder = makeGetBinder;
function makeGetBinder(observeCollection, observeKey) {
    return function bindGet(observeValue, source, target, descriptor, trace) {
        return observeCollection(autoCancelPrevious(function replaceCollection(collection) {
            if (!collection) return;
            return observeKey(autoCancelPrevious(function replaceKey(key) {
                if (key == null) return;
                return _bindGet(collection, key, observeValue, source, descriptor, trace);
            }), target);
        }), target);
    };
}

exports.makeHasBinder = makeHasBinder;
function makeHasBinder(observeSet, observeValue) {
    return function bindHas(observeHas, source, target, descriptor, trace) {
        return observeSet(autoCancelPrevious(function replaceHasBindingSet(set) {
            if (!set) return;
            return observeValue(autoCancelPrevious(function replaceHasBindingValue(value) {
                if (value == null) return;
                return observeHas(autoCancelPrevious(function changeWhetherSetHas(has) {
                    // wait for the initial value to be updated by the
                    // other-way binding
                    if (has) { // should be in set
                        if (!(set.has || set.contains).call(set, value)) {
                            trace && console.log("ADD", value, "TO", trace.targetPath, "BECAUSE", trace.sourcePath, getStackTrace());
                            set.add(value);
                        }
                    } else { // should not be in set
                        while ((set.has || set.contains).call(set, value)) {
                            trace && console.log("REMOVE", value, "FROM", trace.targetPath, "BECAUSE", trace.sourcePath, getStackTrace());
                            (set.remove || set['delete']).call(set, value);
                        }
                    }
                }), source);
            }), target);
        }), target);
    };
}

// a == b <-> c
exports.makeEqualityBinder = makeEqualityBinder;
function makeEqualityBinder(bindLeft, observeRight) {
    return function bindEquals(observeEquals, source, target, descriptor, trace) {
        // c
        return observeEquals(autoCancelPrevious(function changeWhetherEquals(equals) {
            if (equals) {
                trace && console.log("BIND", trace.targetPath, "TO", trace.sourcePath, getStackTrace());
                // a <-> b
                var cancel = bindLeft(observeRight, source, source, descriptor, trace);
                return function cancelEqualityBinding() {
                    trace && console.log("UNBIND", trace.targetPath, "FROM", trace.sourcePath, getStackTrace());
                };
            }
        }), target);
    };
}

// collection.every{condition} <- everyCondition
exports.makeEveryBlockBinder = makeEveryBlockBinder;
function makeEveryBlockBinder(observeCollection, bindCondition, observeValue) {
    return function bindEveryBlock(observeEveryCondition, source, target, descriptor, trace) {
        return observeEveryCondition(autoCancelPrevious(function replaceCondition(condition) {
            if (!condition) return;
            return observeCollection(autoCancelPrevious(function replaceCollection(collection) {
                if (!collection) return;
                var cancelers = [];
                function rangeChange(plus, minus, index) {
                    cancelers.swap(index, minus.length, plus.map(function (value, offset) {
                        var scope = Scope.nest(target, value);
                        return bindCondition(observeValue, scope, scope, descriptor, trace);
                    }));
                }
                var cancelRangeChange = observeRangeChange(collection, rangeChange, target);
                return function cancelEveryBinding() {
                    cancelEach(cancelers);
                    cancelRangeChange();
                };
            }), target);
        }), source);
    };
};

exports.makeAndBinder = makeAndBinder;
function makeAndBinder(bindLeft, bindRight, observeLeft, observeRight, observeLeftBind, observeRightBind) {
    var observeNotRight = makeNotObserver(observeRight);
    var observeLeftAndNotRight = makeAndObserver(observeLeft, observeNotRight);
    return function bindEveryBlock(observeAndCondition, source, target, descriptor, trace) {
        return observeAndCondition(autoCancelPrevious(function replaceAndCondition(condition) {
            if (condition == null) {
            } else if (condition) {
                var cancelLeft = bindLeft(observeLeftBind, trueScope, target, descriptor, trace);
                var cancelRight = bindRight(observeRightBind, trueScope, target, descriptor, trace);
                return function cancelAndBinding() {
                    cancelLeft();
                    cancelRight();
                };
            } else {
                return bindLeft(observeLeftAndNotRight, target, target, descriptor, trace);
            }
        }), source);
    };
}

exports.makeOrBinder = makeOrBinder;
function makeOrBinder(bindLeft, bindRight, observeLeft, observeRight, observeLeftBind, observeRightBind) {
    var observeNotRight = makeNotObserver(observeRight);
    var observeLeftOrNotRight = makeOrObserver(observeLeft, observeNotRight);
    return function bindEveryBlock(observeOrCondition, source, target, descriptor, trace) {
        return observeOrCondition(autoCancelPrevious(function replaceOrCondition(condition) {
            if (condition == null) {
            } else if (!condition) {
                var cancelLeft = bindLeft(observeLeftBind, falseScope, target, descriptor, trace);
                var cancelRight = bindRight(observeRightBind, falseScope, target, descriptor, trace); return function cancelOrBinding() {
                    cancelLeft();
                    cancelRight();
                };
            } else {
                return bindLeft(observeLeftOrNotRight, target, target, descriptor, trace);
            }
        }), source);
    };
}

// (a ? b : c) <- d
exports.makeConditionalBinder = makeConditionalBinder;
function makeConditionalBinder(observeCondition, bindConsequent, bindAlternate) {
    return function bindCondition(observeSource, source, target, descriptor, trace) {
        // a
        return observeCondition(autoCancelPrevious(function replaceCondition(condition) {
            if (condition == null) return;
            if (condition) {
                // b <- d
                return bindConsequent(observeSource, source, target, descriptor, trace);
            } else {
                // c <- d
                return bindAlternate(observeSource, source, target, descriptor, trace);
            }
        }), source);
    };
}

// array.only() <- value
exports.makeOnlyBinder = makeOnlyBinder;
function makeOnlyBinder(observeArray) {
    return function bindOnly(observeValue, sourceScope, targetScope, descriptor, trace) {
        return observeArray(autoCancelPrevious(function replaceArray(array) {
            if (!array) return;
            return observeValue(autoCancelPrevious(function replaceOnlyValue(value) {
                if (value == null) return;
                array.splice(0, array.length, value);
            }), sourceScope);
        }), targetScope);
    };
}

// a.* <- b.*
exports.makeRangeContentBinder = makeRangeContentBinder;
function makeRangeContentBinder(observeTarget, bindTarget) {
    return function bindRangeContent(observeSource, sourceScope, targetScope, descriptor, trace) {
        return observeTarget(autoCancelPrevious(function replaceRangeContentTarget(target) {
            if (!target) {
                return bindTarget(
                    Observers.makeLiteralObserver([]),
                    sourceScope,
                    targetScope,
                    descriptor,
                    trace
                );
            }

            return observeSource(autoCancelPrevious(function replaceRangeContentSource(source) {
                if (source === target) {
                    return;
                }
                if (!source) {
                    target.clear();
                    return;
                }
                if (!source.addRangeChangeListener) {
                    return;
                }

                function rangeContentSourceRangeChange(plus, minus, index) {
                    if (isActive(target))
                        return;
                    if (trace) {
                        console.log("SWAPPING", minus, "FOR", plus, "AT", index, "ON", trace.targetPath, getStackTrace());
                    }
                    if (target.swap) {
                        target.swap(index, minus.length, plus);
                    } else if (target.add && (target.remove || target["delete"])) {
                        plus.forEach(target.add, target);
                        minus.forEach(target.remove || target["delete"], target);
                    }
                }

                source.addRangeChangeListener(rangeContentSourceRangeChange);
                rangeContentSourceRangeChange(Array.from(source), Array.from(target), 0);
                return once(function cancelRangeContentBinding() {
                    source.removeRangeChangeListener(rangeContentSourceRangeChange);
                });
            }), sourceScope);
        }), targetScope);
    };
}

exports.makeMapContentBinder = makeMapContentBinder;
function makeMapContentBinder(observeTarget) {
    return function bindMapContent(observeSource, source, target, descriptor, trace) {
        return observeTarget(autoCancelPrevious(function replaceMapContentBindingTarget(target) {
            if (!target) return;
            return observeSource(autoCancelPrevious(function replaceMapContentBindingSource(source) {
                if (!source) {
                    target.clear();
                    return;
                }

                function mapChange(value, key) {
                    if (descriptor.isActive) {
                        return;
                    }
                    try {
                        descriptor.isActive = true;
                        if (value === undefined) {
                            if (trace) {
                                trace && console.log("DELETED", trace.targetPath, "FOR KEY", key, "ON", target, "BECAUSE", trace.sourcePath, getStackTrace());
                            }
                            if (Array.isArray(target)) {
                                target.splice(key, 1);
                            } else {
                                target["delete"](key);
                            }
                        } else {
                            if (trace) {
                                trace && console.log("SET", trace.targetPath, "FOR KEY", key, "TO", value, "ON", target, "BECAUSE", trace.sourcePath, getStackTrace());
                            }
                            target.set(key, value);
                        }
                    } finally {
                        descriptor.isActive = false;
                    }
                }
                target.clear();
                source.forEach(mapChange);
                return source.addMapChangeListener(mapChange);
            }), source);
        }), target);
    };
}

// a.reversed() <-> b
exports.makeReversedBinder = makeReversedBinder;
function makeReversedBinder(observeTarget) {
    return function bindReversed(observeSource, source, target, descriptor, trace) {
        return observeTarget(autoCancelPrevious(function replaceReversedBindingTarget(target) {
            if (!target) return;
            return observeSource(autoCancelPrevious(function replaceReversedBindingSource(source) {
                if (!source) {
                    target.clear();
                    return;
                }

                function rangeChange(plus, minus, index) {
                    if (isActive(target))
                        return;
                    var reflected = target.length - index - minus.length;
                    target.swap(reflected, minus.length, plus.reversed());
                }
                source.addRangeChangeListener(rangeChange);
                rangeChange(source, target, 0);
                return once(function cancelReversedBinding() {
                    source.removeRangeChangeListener(rangeChange);
                });
            }), source);
        }), target);
    };
}

exports.makeDefinedBinder = makeDefinedBinder;
function makeDefinedBinder(bindTarget) {
    return function bindReversed(observeSource, sourceScope, targetScope, descriptor, trace) {
        return observeSource(autoCancelPrevious(function replaceSource(condition) {
            if (!condition) {
                return bindTarget(
                    observeUndefined,
                    sourceScope,
                    targetScope,
                    descriptor,
                    trace
                );
            } else {
                return Function.noop;
            }
        }), targetScope);
    }
}


function isActive(target) {
    return (
        target.getRangeChangeDescriptor &&
        target.getRangeChangeDescriptor().isActive
    );
}


}})
;
//*/
montageDefine("7aba37c","weak-map",{dependencies:[],factory:function(require,exports,module){// Copyright (C) 2011 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

/**
 * @fileoverview Install a leaky WeakMap emulation on platforms that
 * don't provide a built-in one.
 *
 * <p>Assumes that an ES5 platform where, if {@code WeakMap} is
 * already present, then it conforms to the anticipated ES6
 * specification. To run this file on an ES5 or almost ES5
 * implementation where the {@code WeakMap} specification does not
 * quite conform, run <code>repairES5.js</code> first.
 *
 * <p> Even though WeakMapModule is not global, the linter thinks it
 * is, which is why it is in the overrides list below.
 *
 * @author Mark S. Miller
 * @requires crypto, ArrayBuffer, Uint8Array, navigator
 * @overrides WeakMap, ses, Proxy
 * @overrides WeakMapModule
 */

/**
 * This {@code WeakMap} emulation is observably equivalent to the
 * ES-Harmony WeakMap, but with leakier garbage collection properties.
 *
 * <p>As with true WeakMaps, in this emulation, a key does not
 * retain maps indexed by that key and (crucially) a map does not
 * retain the keys it indexes. A map by itself also does not retain
 * the values associated with that map.
 *
 * <p>However, the values associated with a key in some map are
 * retained so long as that key is retained and those associations are
 * not overridden. For example, when used to support membranes, all
 * values exported from a given membrane will live for the lifetime
 * they would have had in the absence of an interposed membrane. Even
 * when the membrane is revoked, all objects that would have been
 * reachable in the absence of revocation will still be reachable, as
 * far as the GC can tell, even though they will no longer be relevant
 * to ongoing computation.
 *
 * <p>The API implemented here is approximately the API as implemented
 * in FF6.0a1 and agreed to by MarkM, Andreas Gal, and Dave Herman,
 * rather than the offially approved proposal page. TODO(erights):
 * upgrade the ecmascript WeakMap proposal page to explain this API
 * change and present to EcmaScript committee for their approval.
 *
 * <p>The first difference between the emulation here and that in
 * FF6.0a1 is the presence of non enumerable {@code get___, has___,
 * set___, and delete___} methods on WeakMap instances to represent
 * what would be the hidden internal properties of a primitive
 * implementation. Whereas the FF6.0a1 WeakMap.prototype methods
 * require their {@code this} to be a genuine WeakMap instance (i.e.,
 * an object of {@code [[Class]]} "WeakMap}), since there is nothing
 * unforgeable about the pseudo-internal method names used here,
 * nothing prevents these emulated prototype methods from being
 * applied to non-WeakMaps with pseudo-internal methods of the same
 * names.
 *
 * <p>Another difference is that our emulated {@code
 * WeakMap.prototype} is not itself a WeakMap. A problem with the
 * current FF6.0a1 API is that WeakMap.prototype is itself a WeakMap
 * providing ambient mutability and an ambient communications
 * channel. Thus, if a WeakMap is already present and has this
 * problem, repairES5.js wraps it in a safe wrappper in order to
 * prevent access to this channel. (See
 * PATCH_MUTABLE_FROZEN_WEAKMAP_PROTO in repairES5.js).
 */

/**
 * If this is a full <a href=
 * "http://code.google.com/p/es-lab/wiki/SecureableES5"
 * >secureable ES5</a> platform and the ES-Harmony {@code WeakMap} is
 * absent, install an approximate emulation.
 *
 * <p>If WeakMap is present but cannot store some objects, use our approximate
 * emulation as a wrapper.
 *
 * <p>If this is almost a secureable ES5 platform, then WeakMap.js
 * should be run after repairES5.js.
 *
 * <p>See {@code WeakMap} for documentation of the garbage collection
 * properties of this WeakMap emulation.
 */
(function WeakMapModule() {
  "use strict";

  if (typeof ses !== 'undefined' && ses.ok && !ses.ok()) {
    // already too broken, so give up
    return;
  }

  /**
   * In some cases (current Firefox), we must make a choice betweeen a
   * WeakMap which is capable of using all varieties of host objects as
   * keys and one which is capable of safely using proxies as keys. See
   * comments below about HostWeakMap and DoubleWeakMap for details.
   *
   * This function (which is a global, not exposed to guests) marks a
   * WeakMap as permitted to do what is necessary to index all host
   * objects, at the cost of making it unsafe for proxies.
   *
   * Do not apply this function to anything which is not a genuine
   * fresh WeakMap.
   */
  function weakMapPermitHostObjects(map) {
    // identity of function used as a secret -- good enough and cheap
    if (map.permitHostObjects___) {
      map.permitHostObjects___(weakMapPermitHostObjects);
    }
  }
  if (typeof ses !== 'undefined') {
    ses.weakMapPermitHostObjects = weakMapPermitHostObjects;
  }

  // Check if there is already a good-enough WeakMap implementation, and if so
  // exit without replacing it.
  if (typeof WeakMap === 'function') {
    var HostWeakMap = WeakMap;
    // There is a WeakMap -- is it good enough?
    if (typeof navigator !== 'undefined' &&
        /Firefox/.test(navigator.userAgent)) {
      // We're now *assuming not*, because as of this writing (2013-05-06)
      // Firefox's WeakMaps have a miscellany of objects they won't accept, and
      // we don't want to make an exhaustive list, and testing for just one
      // will be a problem if that one is fixed alone (as they did for Event).

      // If there is a platform that we *can* reliably test on, here's how to
      // do it:
      //  var problematic = ... ;
      //  var testHostMap = new HostWeakMap();
      //  try {
      //    testHostMap.set(problematic, 1);  // Firefox 20 will throw here
      //    if (testHostMap.get(problematic) === 1) {
      //      return;
      //    }
      //  } catch (e) {}

      // Fall through to installing our WeakMap.
    } else {
      module.exports = WeakMap;
      return;
    }
  }

  var hop = Object.prototype.hasOwnProperty;
  var gopn = Object.getOwnPropertyNames;
  var defProp = Object.defineProperty;
  var isExtensible = Object.isExtensible;

  /**
   * Security depends on HIDDEN_NAME being both <i>unguessable</i> and
   * <i>undiscoverable</i> by untrusted code.
   *
   * <p>Given the known weaknesses of Math.random() on existing
   * browsers, it does not generate unguessability we can be confident
   * of.
   *
   * <p>It is the monkey patching logic in this file that is intended
   * to ensure undiscoverability. The basic idea is that there are
   * three fundamental means of discovering properties of an object:
   * The for/in loop, Object.keys(), and Object.getOwnPropertyNames(),
   * as well as some proposed ES6 extensions that appear on our
   * whitelist. The first two only discover enumerable properties, and
   * we only use HIDDEN_NAME to name a non-enumerable property, so the
   * only remaining threat should be getOwnPropertyNames and some
   * proposed ES6 extensions that appear on our whitelist. We monkey
   * patch them to remove HIDDEN_NAME from the list of properties they
   * returns.
   *
   * <p>TODO(erights): On a platform with built-in Proxies, proxies
   * could be used to trap and thereby discover the HIDDEN_NAME, so we
   * need to monkey patch Proxy.create, Proxy.createFunction, etc, in
   * order to wrap the provided handler with the real handler which
   * filters out all traps using HIDDEN_NAME.
   *
   * <p>TODO(erights): Revisit Mike Stay's suggestion that we use an
   * encapsulated function at a not-necessarily-secret name, which
   * uses the Stiegler shared-state rights amplification pattern to
   * reveal the associated value only to the WeakMap in which this key
   * is associated with that value. Since only the key retains the
   * function, the function can also remember the key without causing
   * leakage of the key, so this doesn't violate our general gc
   * goals. In addition, because the name need not be a guarded
   * secret, we could efficiently handle cross-frame frozen keys.
   */
  var HIDDEN_NAME_PREFIX = 'weakmap:';
  var HIDDEN_NAME = HIDDEN_NAME_PREFIX + 'ident:' + Math.random() + '___';

  if (typeof crypto !== 'undefined' &&
      typeof crypto.getRandomValues === 'function' &&
      typeof ArrayBuffer === 'function' &&
      typeof Uint8Array === 'function') {
    var ab = new ArrayBuffer(25);
    var u8s = new Uint8Array(ab);
    crypto.getRandomValues(u8s);
    HIDDEN_NAME = HIDDEN_NAME_PREFIX + 'rand:' +
      Array.prototype.map.call(u8s, function(u8) {
        return (u8 % 36).toString(36);
      }).join('') + '___';
  }

  function isNotHiddenName(name) {
    return !(
        name.substr(0, HIDDEN_NAME_PREFIX.length) == HIDDEN_NAME_PREFIX &&
        name.substr(name.length - 3) === '___');
  }

  /**
   * Monkey patch getOwnPropertyNames to avoid revealing the
   * HIDDEN_NAME.
   *
   * <p>The ES5.1 spec requires each name to appear only once, but as
   * of this writing, this requirement is controversial for ES6, so we
   * made this code robust against this case. If the resulting extra
   * search turns out to be expensive, we can probably relax this once
   * ES6 is adequately supported on all major browsers, iff no browser
   * versions we support at that time have relaxed this constraint
   * without providing built-in ES6 WeakMaps.
   */
  defProp(Object, 'getOwnPropertyNames', {
    value: function fakeGetOwnPropertyNames(obj) {
      return gopn(obj).filter(isNotHiddenName);
    }
  });

  /**
   * getPropertyNames is not in ES5 but it is proposed for ES6 and
   * does appear in our whitelist, so we need to clean it too.
   */
  if ('getPropertyNames' in Object) {
    var originalGetPropertyNames = Object.getPropertyNames;
    defProp(Object, 'getPropertyNames', {
      value: function fakeGetPropertyNames(obj) {
        return originalGetPropertyNames(obj).filter(isNotHiddenName);
      }
    });
  }

  /**
   * <p>To treat objects as identity-keys with reasonable efficiency
   * on ES5 by itself (i.e., without any object-keyed collections), we
   * need to add a hidden property to such key objects when we
   * can. This raises several issues:
   * <ul>
   * <li>Arranging to add this property to objects before we lose the
   *     chance, and
   * <li>Hiding the existence of this new property from most
   *     JavaScript code.
   * <li>Preventing <i>certification theft</i>, where one object is
   *     created falsely claiming to be the key of an association
   *     actually keyed by another object.
   * <li>Preventing <i>value theft</i>, where untrusted code with
   *     access to a key object but not a weak map nevertheless
   *     obtains access to the value associated with that key in that
   *     weak map.
   * </ul>
   * We do so by
   * <ul>
   * <li>Making the name of the hidden property unguessable, so "[]"
   *     indexing, which we cannot intercept, cannot be used to access
   *     a property without knowing the name.
   * <li>Making the hidden property non-enumerable, so we need not
   *     worry about for-in loops or {@code Object.keys},
   * <li>monkey patching those reflective methods that would
   *     prevent extensions, to add this hidden property first,
   * <li>monkey patching those methods that would reveal this
   *     hidden property.
   * </ul>
   * Unfortunately, because of same-origin iframes, we cannot reliably
   * add this hidden property before an object becomes
   * non-extensible. Instead, if we encounter a non-extensible object
   * without a hidden record that we can detect (whether or not it has
   * a hidden record stored under a name secret to us), then we just
   * use the key object itself to represent its identity in a brute
   * force leaky map stored in the weak map, losing all the advantages
   * of weakness for these.
   */
  function getHiddenRecord(key) {
    if (key !== Object(key)) {
      throw new TypeError('Not an object: ' + key);
    }
    var hiddenRecord = key[HIDDEN_NAME];
    if (hiddenRecord && hiddenRecord.key === key) { return hiddenRecord; }
    if (!isExtensible(key)) {
      // Weak map must brute force, as explained in doc-comment above.
      return void 0;
    }
    var gets = [];
    var vals = [];
    hiddenRecord = {
      key: key,   // self pointer for quick own check above.
      gets: gets, // get___ methods identifying weak maps
      vals: vals  // values associated with this key in each
                  // corresponding weak map.
    };
    defProp(key, HIDDEN_NAME, {
      value: hiddenRecord,
      writable: false,
      enumerable: false,
      configurable: false
    });
    return hiddenRecord;
  }


  /**
   * Monkey patch operations that would make their argument
   * non-extensible.
   *
   * <p>The monkey patched versions throw a TypeError if their
   * argument is not an object, so it should only be done to functions
   * that should throw a TypeError anyway if their argument is not an
   * object.
   */
  (function(){
    var oldFreeze = Object.freeze;
    defProp(Object, 'freeze', {
      value: function identifyingFreeze(obj) {
        getHiddenRecord(obj);
        return oldFreeze(obj);
      }
    });
    var oldSeal = Object.seal;
    defProp(Object, 'seal', {
      value: function identifyingSeal(obj) {
        getHiddenRecord(obj);
        return oldSeal(obj);
      }
    });
    var oldPreventExtensions = Object.preventExtensions;
    defProp(Object, 'preventExtensions', {
      value: function identifyingPreventExtensions(obj) {
        getHiddenRecord(obj);
        return oldPreventExtensions(obj);
      }
    });
  })();


  function constFunc(func) {
    func.prototype = null;
    return Object.freeze(func);
  }

  // Right now (12/25/2012) the histogram supports the current
  // representation. We should check this occasionally, as a true
  // constant time representation is easy.
  // var histogram = [];

  var OurWeakMap = function() {
    // We are currently (12/25/2012) never encountering any prematurely
    // non-extensible keys.
    var keys = []; // brute force for prematurely non-extensible keys.
    var vals = []; // brute force for corresponding values.

    function get___(key, opt_default) {
      var hr = getHiddenRecord(key);
      var i, vs;
      if (hr) {
        i = hr.gets.indexOf(get___);
        vs = hr.vals;
      } else {
        i = keys.indexOf(key);
        vs = vals;
      }
      return (i >= 0) ? vs[i] : opt_default;
    }

    function has___(key) {
      var hr = getHiddenRecord(key);
      var i;
      if (hr) {
        i = hr.gets.indexOf(get___);
      } else {
        i = keys.indexOf(key);
      }
      return i >= 0;
    }

    function set___(key, value) {
      var hr = getHiddenRecord(key);
      var i;
      if (hr) {
        i = hr.gets.indexOf(get___);
        if (i >= 0) {
          hr.vals[i] = value;
        } else {
//          i = hr.gets.length;
//          histogram[i] = (histogram[i] || 0) + 1;
          hr.gets.push(get___);
          hr.vals.push(value);
        }
      } else {
        i = keys.indexOf(key);
        if (i >= 0) {
          vals[i] = value;
        } else {
          keys.push(key);
          vals.push(value);
        }
      }
    }

    function delete___(key) {
      var hr = getHiddenRecord(key);
      var i;
      if (hr) {
        i = hr.gets.indexOf(get___);
        if (i >= 0) {
          hr.gets.splice(i, 1);
          hr.vals.splice(i, 1);
        }
      } else {
        i = keys.indexOf(key);
        if (i >= 0) {
          keys.splice(i, 1);
          vals.splice(i, 1);
        }
      }
      return true;
    }

    return Object.create(OurWeakMap.prototype, {
      get___:    { value: constFunc(get___) },
      has___:    { value: constFunc(has___) },
      set___:    { value: constFunc(set___) },
      delete___: { value: constFunc(delete___) }
    });
  };
  OurWeakMap.prototype = Object.create(Object.prototype, {
    get: {
      /**
       * Return the value most recently associated with key, or
       * opt_default if none.
       */
      value: function get(key, opt_default) {
        return this.get___(key, opt_default);
      },
      writable: true,
      configurable: true
    },

    has: {
      /**
       * Is there a value associated with key in this WeakMap?
       */
      value: function has(key) {
        return this.has___(key);
      },
      writable: true,
      configurable: true
    },

    set: {
      /**
       * Associate value with key in this WeakMap, overwriting any
       * previous association if present.
       */
      value: function set(key, value) {
        this.set___(key, value);
      },
      writable: true,
      configurable: true
    },

    'delete': {
      /**
       * Remove any association for key in this WeakMap, returning
       * whether there was one.
       *
       * <p>Note that the boolean return here does not work like the
       * {@code delete} operator. The {@code delete} operator returns
       * whether the deletion succeeds at bringing about a state in
       * which the deleted property is absent. The {@code delete}
       * operator therefore returns true if the property was already
       * absent, whereas this {@code delete} method returns false if
       * the association was already absent.
       */
      value: function remove(key) {
        return this.delete___(key);
      },
      writable: true,
      configurable: true
    }
  });

  if (typeof HostWeakMap === 'function') {
    (function() {
      // If we got here, then the platform has a WeakMap but we are concerned
      // that it may refuse to store some key types. Therefore, make a map
      // implementation which makes use of both as possible.

      function DoubleWeakMap() {
        // Preferable, truly weak map.
        var hmap = new HostWeakMap();

        // Our hidden-property-based pseudo-weak-map. Lazily initialized in the
        // 'set' implementation; thus we can avoid performing extra lookups if
        // we know all entries actually stored are entered in 'hmap'.
        var omap = undefined;

        // Hidden-property maps are not compatible with proxies because proxies
        // can observe the hidden name and either accidentally expose it or fail
        // to allow the hidden property to be set. Therefore, we do not allow
        // arbitrary WeakMaps to switch to using hidden properties, but only
        // those which need the ability, and unprivileged code is not allowed
        // to set the flag.
        var enableSwitching = false;

        function dget(key, opt_default) {
          if (omap) {
            return hmap.has(key) ? hmap.get(key)
                : omap.get___(key, opt_default);
          } else {
            return hmap.get(key, opt_default);
          }
        }

        function dhas(key) {
          return hmap.has(key) || (omap ? omap.has___(key) : false);
        }

        function dset(key, value) {
          if (enableSwitching) {
            try {
              hmap.set(key, value);
            } catch (e) {
              if (!omap) { omap = new OurWeakMap(); }
              omap.set___(key, value);
            }
          } else {
            hmap.set(key, value);
          }
        }

        function ddelete(key) {
          hmap['delete'](key);
          if (omap) { omap.delete___(key); }
        }

        return Object.create(OurWeakMap.prototype, {
          get___:    { value: constFunc(dget) },
          has___:    { value: constFunc(dhas) },
          set___:    { value: constFunc(dset) },
          delete___: { value: constFunc(ddelete) },
          permitHostObjects___: { value: constFunc(function(token) {
            if (token === weakMapPermitHostObjects) {
              enableSwitching = true;
            } else {
              throw new Error('bogus call to permitHostObjects___');
            }
          })}
        });
      }
      DoubleWeakMap.prototype = OurWeakMap.prototype;
      module.exports = DoubleWeakMap;

      // define .constructor to hide OurWeakMap ctor
      Object.defineProperty(WeakMap.prototype, 'constructor', {
        value: WeakMap,
        enumerable: false,  // as default .constructor is
        configurable: true,
        writable: true
      });
    })();
  } else {
    // There is no host WeakMap, so we must use the emulation.

    // Emulated WeakMaps are incompatible with native proxies (because proxies
    // can observe the hidden name), so we must disable Proxy usage (in
    // ArrayLike and Domado, currently).
    if (typeof Proxy !== 'undefined') {
      Proxy = undefined;
    }

    module.exports = OurWeakMap;
  }
})();

}})