<commit_before>/*/////////////////////////////////////////////////////////////////////////////////

 MO5.js - JavaScript Library For Building Modern DOM And Canvas Applications

 Copyright (c) 2013 Jonathan Steinbeck
 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 MO5.js 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 HOLDERS 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.

/////////////////////////////////////////////////////////////////////////////////*/

/* global window, require, process, document, console */

if (typeof window !== "undefined") {
    // If the browser doesn't support requestAnimationFrame, use a fallback.
    window.requestAnimationFrame = (function ()
    {
        "use strict";

        return window.requestAnimationFrame ||
            window.webkitRequestAnimationFrame ||
            window.mozRequestAnimationFrame ||
            window.oRequestAnimationFrame ||
            window.msRequestAnimationFrame ||
            function (callback) {
                window.setTimeout(callback, 1000 / 60);
            };
    }());
}

// [].forEach() shim
(function () {
    if (Array.prototype.forEach) {
        return;
    }

    Array.prototype.forEach = function (callback) {
        for (var i = 0, len = this.length; i < len; i += 1) {
            callback(this[i], i, this);
        }
    };
}());

var MO5 = (function () {

    var modules = {}, loadedScripts = {};

    /**
     * Inner class representing a Module with a state and dependencies.
     * There can be only one module with a specific name. If another one
     * with the same name is created, the existing one is returned instead.
     */
    function Module (name, callback, dependencies) {

        if (Module.moduleIsReady(name)) {
            return modules[name];
        }

        /** @var The module's name. */
        this.name = name;

        /**
         * @var A callback that genererates the module's contents after
         * all dependencies are ready.
         */
        this.callback = callback;

        /** @var Names of modules this module depends on. */
        this.dependencies = dependencies || [];

        /** @var The current state of the module. Should be one of the "class constants". */
        this.state = Module.STATE_UNINITIALIZED;

        /** @var A List of callbacks to be executed once the module's state is STATE_READY. */
        this.observers = [];

        /** @var The module's content. Only available when the module's state is STATE_READY. */
        this.content = undefined;

        modules[name] = this;
    }

    /** @var State when the loading of the module has not been started yet. */
    Module.STATE_UNINITIALIZED = "unitialized";
    Module.STATE_READY = "ready";

    /**
     * Is the module specified by the moduleName parameter ready to be used?
     */
    Module.moduleIsReady = function (moduleName) {

        if (!(moduleName in modules)) {
            return false;
        }

        return typeof modules[moduleName].isReady === "undefined" ||
            modules[moduleName].isReady();
    };

    /**
     * Are the modules in the moduleNames parameter ready to be used?
     * @param moduleNames Array of module names.
     */
    Module.modulesAreReady = function (moduleNames) {

        var i, len;

        for (i = 0, len = moduleNames.length; i < len; i += 1) {
            if (!Module.moduleIsReady(moduleNames[i])) {
                return false;
            }
        }

        return true;
    };

    /**
     * Gather the contents of all the modules specified in parameter moduleNames.
     * @moduleNames An array with module names.
     */
    Module.gatherDependencies = function (moduleNames) {

        var dependencies = [];

        moduleNames.forEach(function (moduleName) {
            if (modules[moduleName] && typeof modules[moduleName].content === "undefined") {
                dependencies.push(modules[moduleName]);
            }
            else {
                dependencies.push(modules[moduleName].content);
            }
        });

        return dependencies;

    };

    /**
     * Is this module ready to be used?
     */
    Module.prototype.isReady = function () {
        return (this.state === Module.STATE_READY);
    };

    /**
     * Are the dependencies loaded yet?
     */
    Module.prototype.dependenciesReady = function () {
        return Module.modulesAreReady(this.dependencies);
    };

    Module.prototype.getDependenciesNotReady = function () {

        var dependenciesNotReady = [];

        this.dependencies.forEach(function (moduleName) {
            if (modules[moduleName] && !modules[moduleName].isReady()) {
                dependenciesNotReady.push(modules[moduleName]);
            }
        });

        return dependenciesNotReady;
    };

    Module.prototype.addObserverToDependenciesNotReady = function (callback) {
        this.getDependenciesNotReady().forEach(function (module) {
            module.addObserver(callback);
        });
    };

    /**
     * Registers an observer callback with this module which will be called
     * when the module is ready. If the module already is ready, the observer
     * will be called immediately.
     */
    Module.prototype.addObserver = function (observer) {

        if (this.isReady()) {
            observer(this.content);
            return;
        }

        this.observers.push(observer);
    };

    /**
     * Marks this module as ready and calls all observers.
     */
    Module.prototype.finish = function () {

        var dependencies = [], content, self = this;

        if (this.isReady()) {
            return;
        }

        if (!this.dependenciesReady()) {
            this.addObserverToDependenciesNotReady(function () { self.finish(); });
            return;
        }

        dependencies = Module.gatherDependencies(this.dependencies);
        content = this.callback.apply(undefined, dependencies);

        this.content = content;
        this.state = Module.STATE_READY;

        this.observers.forEach(function (observer) {
            observer(content);
        });

        this.observers = []; // remove references to aid the garbage collector
    };

    function MO5 (/* module names */) {

        var moduleNames, runCallbacks = [], defineCallbacks = [], dependencies = [];
        var modulesLoaded = 0, done = false, modulesLoading = false, capabilityObject;

        moduleNames = [].slice.call(arguments);

        capabilityObject = {
            run: run,
            define: define
        };

        return capabilityObject;

        ///////////////////////////////////
        // Helper functions
        ///////////////////////////////////

        function loadModules () {

            var moduleOffset = 0;

            modulesLoading = true;

            moduleNames.forEach(function (moduleName) {

                var currentOffset = moduleOffset;

                moduleOffset += 1;

                if (moduleName.match(/^ajax:/)) {
                    MO5.ajax(MO5.ajax.HTTP_METHOD_GET, moduleName.replace(/^ajax:/, ""),
                        null, ajaxResourceSuccessFn, ajaxResourceSuccessFn);
                }
                else {
                    loadModule(moduleName, loadModuleSuccessFn);
                }

                function ajaxResourceSuccessFn (request) {
                    modules[moduleName] = request;
                    loadModuleSuccessFn(request);
                }

                function loadModuleSuccessFn (module) {

                    modulesLoaded += 1;

                    dependencies[currentOffset] = module;

                    if (modulesLoaded === moduleNames.length) {
                        done = true;

                        defineCallbacks.forEach(function (callback) {
                            try {
                                callback();
                            }
                            catch (e) {
                                console.error("Error while loading module '" + moduleName +
                                    "':", e.stack, callback);
                            }
                        });

                        runCallbacks.forEach(function (callback) {
                            try {
                                callback.apply(undefined, dependencies);
                            }
                            catch (e) {
                                console.error(e.message, e.stack);
                                console.dir(callback);
                            }
                        });
                    }
                }
            });
        }

        function run (callback) {

            if (done) {
                return callback.apply(undefined, dependencies);
            }

            runCallbacks.push(callback);

            if (!modulesLoading) {
                loadModules();
            }

            return capabilityObject;
        }

        function define (moduleName, callback) {

            var module = new Module(moduleName, callback, moduleNames);

            if (module.isReady()) {
                return capabilityObject;
            }

            if (done) {
                module.finish();
                return capabilityObject;
            }

            defineCallbacks.push(finishModule);

            if (!modulesLoading) {
                loadModules();
            }

            return capabilityObject;

            function finishModule () {
                module.finish();
            }
        }
    }

    MO5.path = "";

    (function () {
        var scripts = document.getElementsByTagName("script");

        MO5.path = scripts[scripts.length - 1].src.replace(/MO5\.js$/, "");

    }());

    MO5.modules = {
        "MO5.ajax": MO5.path + "MO5.ajax.js",
        "MO5.assert": MO5.path + "MO5.assert.js",
        "MO5.Exception": MO5.path + "MO5.Exception.js",
        "MO5.fail": MO5.path + "MO5.fail.js",
        "MO5.EventBus": MO5.path + "MO5.EventBus.js",
        "MO5.CoreObject": MO5.path + "MO5.CoreObject.js",
        "MO5.List": MO5.path + "MO5.List.js",
        "MO5.Queue": MO5.path + "MO5.Queue.js",
        "MO5.Map": MO5.path + "MO5.Map.js",
        "MO5.Result": MO5.path + "MO5.Result.js", // deprecated - use MO5.Promise instead!
        "MO5.Promise": MO5.path + "MO5.Promise.js",
        "MO5.Timer": MO5.path + "MO5.Timer.js",
        "MO5.TimerWatcher": MO5.path + "MO5.TimerWatcher.js",
        "MO5.easing": MO5.path + "MO5.easing.js",
        "MO5.transform": MO5.path + "MO5.transform.js",
        "MO5.tools": MO5.path + "MO5.tools.js",
        "MO5.Point": MO5.path + "MO5.Point.js",
        "MO5.Animation": MO5.path + "MO5.Animation.js",
        "MO5.dom.effects.typewriter": MO5.path + "MO5.dom.effects.typewriter.js",
        "MO5.dom.Element": MO5.path + "MO5.dom.Element.js",
        "MO5.dom.escape": MO5.path + "MO5.dom.escape.js",
        "MO5.canvas.Canvas": MO5.path + "MO5.canvas.Canvas.js",
        "MO5.canvas.Group": MO5.path + "MO5.canvas.Group.js",
        "MO5.canvas.Layer": MO5.path + "MO5.canvas.Layer.js",
        "MO5.canvas.Object": MO5.path + "MO5.canvas.Object.js",
        "MO5.canvas.ImagePack": MO5.path + "MO5.canvas.ImagePack.js",
        "MO5.canvas.Rain": MO5.path + "MO5.canvas.Rain.js",
        "MO5.canvas.Rectangle": MO5.path + "MO5.canvas.Rectangle.js",
        "MO5.canvas.TextBox": MO5.path + "MO5.canvas.TextBox.js",
        "MO5.script.Tokenizer": MO5.path + "MO5.script.Tokenizer.js",
        "MO5.script.Parser": MO5.path + "MO5.script.Parser.js",
        "MO5.script.Context": MO5.path + "MO5.script.Context.js",
        "MO5.script.SpecialFormsContainer": MO5.path + "MO5.script.SpecialFormsContainer.js",
        "MO5.script.GlobalScope": MO5.path + "MO5.script.GlobalScope.js",
        "MO5.script.Interpreter": MO5.path + "MO5.script.Interpreter.js",
        "MO5.script.Pair": MO5.path + "MO5.script.Pair.js",
        "MO5.script.Printer": MO5.path + "MO5.script.Printer.js",
        "MO5.script.errors": MO5.path + "MO5.script.errors.js",
        "MO5.types": MO5.path + "MO5.types.js"
    };

    function loadModule (moduleName, onSuccess, onError) {

        onError = onError || function (e) { console.error(e.message, e.stack); };

        if (!(moduleName in MO5.modules)) {
            onError(new Error("Unknown module '" + moduleName + "'."));
            return;
        }

        if (Module.moduleIsReady(moduleName)) {
            onScriptLoadSuccess();
        }
        else {
            MO5.loadScript(MO5.modules[moduleName], onScriptLoadSuccess, onError);
        }

        function onScriptLoadSuccess () {

            if (!modules[moduleName] || !modules[moduleName].addObserver) {
                throw new Error("MO5: No module definition for module '" + moduleName +
                    "' found in specified source file '" + MO5.modules[moduleName] + "'.");
            }

            modules[moduleName].addObserver(onSuccess);

            if (modules[moduleName].dependenciesReady()) {
                modules[moduleName].finish();
            }
        }

    }

    MO5.loadScript = function (url, onSuccess, onError) {

        var script = document.createElement("script");

        if (loadedScripts[url]) {
            onSuccess();
            return;
        }

        try {
            script.onload = onload;
            script.onreadystatechange = onload;
            script.src = url;
            document.body.appendChild(script);
        }
        catch (e) {
            onError(e);
        }

        function onload () {
            if (!script.readyState || script.readyState === "loaded" || script.readyState === "complete") {
                loadedScripts[url] = true;
                onSuccess();
            }
        }
    };

    MO5.defaults = {
        fps: 30,
        debug: true,
        canvas: {
            width: 640, // default width for canvas elements
            height: 480 // default height for canvas elements
        }
    };

    return MO5;

}());

/* global MO5, XMLHttpRequest, ActiveXObject */

MO5.ajax = (function () {

    var HTTP_STATUS_OK = 200;
    var READY_STATE_UNSENT = 0;
    var READY_STATE_OPENED = 1;
    var READY_STATE_HEADERS_RECEIVED = 2;
    var READY_STATE_LOADING = 3;
    var READY_STATE_DONE = 4;

    function ajax (method, url, data, onSuccess, onError) {

        var requestObject = XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP");

        requestObject.open(method, url + "?random=" + Math.random(), true);

        requestObject.onreadystatechange = function() {
            var done, statusOk;

            done = requestObject.readyState === READY_STATE_DONE;
            statusOk = requestObject.status === HTTP_STATUS_OK;

            if (done) {
                if (statusOk) {
                    onSuccess(requestObject);
                }
                else {
                    onError(requestObject);
                }
            }
        };

        if (data) {
            requestObject.send(data);
        }
        else {
            requestObject.send();
        }

        return requestObject;
    }

    ajax.HTTP_STATUS_OK = HTTP_STATUS_OK;

    ajax.READY_STATE_UNSENT = READY_STATE_UNSENT;
    ajax.READY_STATE_OPENED = READY_STATE_OPENED;
    ajax.READY_STATE_HEADERS_RECEIVED = READY_STATE_HEADERS_RECEIVED;
    ajax.READY_STATE_LOADING = READY_STATE_LOADING;
    ajax.READY_STATE_DONE = READY_STATE_DONE;

    ajax.HTTP_METHOD_GET = "GET";
    ajax.HTTP_METHOD_POST = "POST";
    ajax.HTTP_METHOD_PUT = "PUT";
    ajax.HTTP_METHOD_DELETE = "DELETE";
    ajax.HTTP_METHOD_HEAD = "HEAD";

    return ajax;
}());<commit_msg>Added pseudo shim for console methods.<commit_after>/*/////////////////////////////////////////////////////////////////////////////////

 MO5.js - JavaScript Library For Building Modern DOM And Canvas Applications

 Copyright (c) 2013 Jonathan Steinbeck
 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 MO5.js 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 HOLDERS 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.

/////////////////////////////////////////////////////////////////////////////////*/

/* global window, require, process, document, console */

if (typeof window !== "undefined") {
    // If the browser doesn't support requestAnimationFrame, use a fallback.
    window.requestAnimationFrame = (function ()
    {
        "use strict";

        return window.requestAnimationFrame ||
            window.webkitRequestAnimationFrame ||
            window.mozRequestAnimationFrame ||
            window.oRequestAnimationFrame ||
            window.msRequestAnimationFrame ||
            function (callback) {
                window.setTimeout(callback, 1000 / 60);
            };
    }());
}

// [].forEach() shim
(function () {
    if (Array.prototype.forEach) {
        return;
    }

    Array.prototype.forEach = function (callback) {
        for (var i = 0, len = this.length; i < len; i += 1) {
            callback(this[i], i, this);
        }
    };
}());

if (typeof console === "undefined") {
    this.console = {};
}

if (!console.log) {
    console.log = function () {};
}

if (!console.dir) {
    console.dir = console.log;
}

if (!console.error) {
    console.error = console.log;
}

if (!console.warn) {
    console.warn = console.log;
}

var MO5 = (function () {

    var modules = {}, loadedScripts = {};

    /**
     * Inner class representing a Module with a state and dependencies.
     * There can be only one module with a specific name. If another one
     * with the same name is created, the existing one is returned instead.
     */
    function Module (name, callback, dependencies) {

        if (Module.moduleIsReady(name)) {
            return modules[name];
        }

        /** @var The module's name. */
        this.name = name;

        /**
         * @var A callback that genererates the module's contents after
         * all dependencies are ready.
         */
        this.callback = callback;

        /** @var Names of modules this module depends on. */
        this.dependencies = dependencies || [];

        /** @var The current state of the module. Should be one of the "class constants". */
        this.state = Module.STATE_UNINITIALIZED;

        /** @var A List of callbacks to be executed once the module's state is STATE_READY. */
        this.observers = [];

        /** @var The module's content. Only available when the module's state is STATE_READY. */
        this.content = undefined;

        modules[name] = this;
    }

    /** @var State when the loading of the module has not been started yet. */
    Module.STATE_UNINITIALIZED = "unitialized";
    Module.STATE_READY = "ready";

    /**
     * Is the module specified by the moduleName parameter ready to be used?
     */
    Module.moduleIsReady = function (moduleName) {

        if (!(moduleName in modules)) {
            return false;
        }

        return typeof modules[moduleName].isReady === "undefined" ||
            modules[moduleName].isReady();
    };

    /**
     * Are the modules in the moduleNames parameter ready to be used?
     * @param moduleNames Array of module names.
     */
    Module.modulesAreReady = function (moduleNames) {

        var i, len;

        for (i = 0, len = moduleNames.length; i < len; i += 1) {
            if (!Module.moduleIsReady(moduleNames[i])) {
                return false;
            }
        }

        return true;
    };

    /**
     * Gather the contents of all the modules specified in parameter moduleNames.
     * @moduleNames An array with module names.
     */
    Module.gatherDependencies = function (moduleNames) {

        var dependencies = [];

        moduleNames.forEach(function (moduleName) {
            if (modules[moduleName] && typeof modules[moduleName].content === "undefined") {
                dependencies.push(modules[moduleName]);
            }
            else {
                dependencies.push(modules[moduleName].content);
            }
        });

        return dependencies;

    };

    /**
     * Is this module ready to be used?
     */
    Module.prototype.isReady = function () {
        return (this.state === Module.STATE_READY);
    };

    /**
     * Are the dependencies loaded yet?
     */
    Module.prototype.dependenciesReady = function () {
        return Module.modulesAreReady(this.dependencies);
    };

    Module.prototype.getDependenciesNotReady = function () {

        var dependenciesNotReady = [];

        this.dependencies.forEach(function (moduleName) {
            if (modules[moduleName] && !modules[moduleName].isReady()) {
                dependenciesNotReady.push(modules[moduleName]);
            }
        });

        return dependenciesNotReady;
    };

    Module.prototype.addObserverToDependenciesNotReady = function (callback) {
        this.getDependenciesNotReady().forEach(function (module) {
            module.addObserver(callback);
        });
    };

    /**
     * Registers an observer callback with this module which will be called
     * when the module is ready. If the module already is ready, the observer
     * will be called immediately.
     */
    Module.prototype.addObserver = function (observer) {

        if (this.isReady()) {
            observer(this.content);
            return;
        }

        this.observers.push(observer);
    };

    /**
     * Marks this module as ready and calls all observers.
     */
    Module.prototype.finish = function () {

        var dependencies = [], content, self = this;

        if (this.isReady()) {
            return;
        }

        if (!this.dependenciesReady()) {
            this.addObserverToDependenciesNotReady(function () { self.finish(); });
            return;
        }

        dependencies = Module.gatherDependencies(this.dependencies);
        content = this.callback.apply(undefined, dependencies);

        this.content = content;
        this.state = Module.STATE_READY;

        this.observers.forEach(function (observer) {
            observer(content);
        });

        this.observers = []; // remove references to aid the garbage collector
    };

    function MO5 (/* module names */) {

        var moduleNames, runCallbacks = [], defineCallbacks = [], dependencies = [];
        var modulesLoaded = 0, done = false, modulesLoading = false, capabilityObject;

        moduleNames = [].slice.call(arguments);

        capabilityObject = {
            run: run,
            define: define
        };

        return capabilityObject;

        ///////////////////////////////////
        // Helper functions
        ///////////////////////////////////

        function loadModules () {

            var moduleOffset = 0;

            modulesLoading = true;

            moduleNames.forEach(function (moduleName) {

                var currentOffset = moduleOffset;

                moduleOffset += 1;

                if (moduleName.match(/^ajax:/)) {
                    MO5.ajax(MO5.ajax.HTTP_METHOD_GET, moduleName.replace(/^ajax:/, ""),
                        null, ajaxResourceSuccessFn, ajaxResourceSuccessFn);
                }
                else {
                    loadModule(moduleName, loadModuleSuccessFn);
                }

                function ajaxResourceSuccessFn (request) {
                    modules[moduleName] = request;
                    loadModuleSuccessFn(request);
                }

                function loadModuleSuccessFn (module) {

                    modulesLoaded += 1;

                    dependencies[currentOffset] = module;

                    if (modulesLoaded === moduleNames.length) {
                        done = true;

                        defineCallbacks.forEach(function (callback) {
                            try {
                                callback();
                            }
                            catch (e) {
                                console.error("Error while loading module '" + moduleName +
                                    "':", e.stack, callback);
                            }
                        });

                        runCallbacks.forEach(function (callback) {
                            try {
                                callback.apply(undefined, dependencies);
                            }
                            catch (e) {
                                console.error(e.message, e.stack);
                                console.dir(callback);
                            }
                        });
                    }
                }
            });
        }

        function run (callback) {

            if (done) {
                return callback.apply(undefined, dependencies);
            }

            runCallbacks.push(callback);

            if (!modulesLoading) {
                loadModules();
            }

            return capabilityObject;
        }

        function define (moduleName, callback) {

            var module = new Module(moduleName, callback, moduleNames);

            if (module.isReady()) {
                return capabilityObject;
            }

            if (done) {
                module.finish();
                return capabilityObject;
            }

            defineCallbacks.push(finishModule);

            if (!modulesLoading) {
                loadModules();
            }

            return capabilityObject;

            function finishModule () {
                module.finish();
            }
        }
    }

    MO5.path = "";

    (function () {
        var scripts = document.getElementsByTagName("script");

        MO5.path = scripts[scripts.length - 1].src.replace(/MO5\.js$/, "");

    }());

    MO5.modules = {
        "MO5.ajax": MO5.path + "MO5.ajax.js",
        "MO5.assert": MO5.path + "MO5.assert.js",
        "MO5.Exception": MO5.path + "MO5.Exception.js",
        "MO5.fail": MO5.path + "MO5.fail.js",
        "MO5.EventBus": MO5.path + "MO5.EventBus.js",
        "MO5.CoreObject": MO5.path + "MO5.CoreObject.js",
        "MO5.List": MO5.path + "MO5.List.js",
        "MO5.Queue": MO5.path + "MO5.Queue.js",
        "MO5.Map": MO5.path + "MO5.Map.js",
        "MO5.Result": MO5.path + "MO5.Result.js", // deprecated - use MO5.Promise instead!
        "MO5.Promise": MO5.path + "MO5.Promise.js",
        "MO5.Timer": MO5.path + "MO5.Timer.js",
        "MO5.TimerWatcher": MO5.path + "MO5.TimerWatcher.js",
        "MO5.easing": MO5.path + "MO5.easing.js",
        "MO5.transform": MO5.path + "MO5.transform.js",
        "MO5.tools": MO5.path + "MO5.tools.js",
        "MO5.Point": MO5.path + "MO5.Point.js",
        "MO5.Animation": MO5.path + "MO5.Animation.js",
        "MO5.dom.effects.typewriter": MO5.path + "MO5.dom.effects.typewriter.js",
        "MO5.dom.Element": MO5.path + "MO5.dom.Element.js",
        "MO5.dom.escape": MO5.path + "MO5.dom.escape.js",
        "MO5.canvas.Canvas": MO5.path + "MO5.canvas.Canvas.js",
        "MO5.canvas.Group": MO5.path + "MO5.canvas.Group.js",
        "MO5.canvas.Layer": MO5.path + "MO5.canvas.Layer.js",
        "MO5.canvas.Object": MO5.path + "MO5.canvas.Object.js",
        "MO5.canvas.ImagePack": MO5.path + "MO5.canvas.ImagePack.js",
        "MO5.canvas.Rain": MO5.path + "MO5.canvas.Rain.js",
        "MO5.canvas.Rectangle": MO5.path + "MO5.canvas.Rectangle.js",
        "MO5.canvas.TextBox": MO5.path + "MO5.canvas.TextBox.js",
        "MO5.script.Tokenizer": MO5.path + "MO5.script.Tokenizer.js",
        "MO5.script.Parser": MO5.path + "MO5.script.Parser.js",
        "MO5.script.Context": MO5.path + "MO5.script.Context.js",
        "MO5.script.SpecialFormsContainer": MO5.path + "MO5.script.SpecialFormsContainer.js",
        "MO5.script.GlobalScope": MO5.path + "MO5.script.GlobalScope.js",
        "MO5.script.Interpreter": MO5.path + "MO5.script.Interpreter.js",
        "MO5.script.Pair": MO5.path + "MO5.script.Pair.js",
        "MO5.script.Printer": MO5.path + "MO5.script.Printer.js",
        "MO5.script.errors": MO5.path + "MO5.script.errors.js",
        "MO5.types": MO5.path + "MO5.types.js"
    };

    function loadModule (moduleName, onSuccess, onError) {

        onError = onError || function (e) { console.error(e.message, e.stack); };

        if (!(moduleName in MO5.modules)) {
            onError(new Error("Unknown module '" + moduleName + "'."));
            return;
        }

        if (Module.moduleIsReady(moduleName)) {
            onScriptLoadSuccess();
        }
        else {
            MO5.loadScript(MO5.modules[moduleName], onScriptLoadSuccess, onError);
        }

        function onScriptLoadSuccess () {

            if (!modules[moduleName] || !modules[moduleName].addObserver) {
                throw new Error("MO5: No module definition for module '" + moduleName +
                    "' found in specified source file '" + MO5.modules[moduleName] + "'.");
            }

            modules[moduleName].addObserver(onSuccess);

            if (modules[moduleName].dependenciesReady()) {
                modules[moduleName].finish();
            }
        }

    }

    MO5.loadScript = function (url, onSuccess, onError) {

        var script = document.createElement("script");

        if (loadedScripts[url]) {
            onSuccess();
            return;
        }

        try {
            script.onload = onload;
            script.onreadystatechange = onload;
            script.src = url;
            document.body.appendChild(script);
        }
        catch (e) {
            onError(e);
        }

        function onload () {
            if (!script.readyState || script.readyState === "loaded" || script.readyState === "complete") {
                loadedScripts[url] = true;
                onSuccess();
            }
        }
    };

    MO5.defaults = {
        fps: 30,
        debug: true,
        canvas: {
            width: 640, // default width for canvas elements
            height: 480 // default height for canvas elements
        }
    };

    return MO5;

}());

/* global MO5, XMLHttpRequest, ActiveXObject */

MO5.ajax = (function () {

    var HTTP_STATUS_OK = 200;
    var READY_STATE_UNSENT = 0;
    var READY_STATE_OPENED = 1;
    var READY_STATE_HEADERS_RECEIVED = 2;
    var READY_STATE_LOADING = 3;
    var READY_STATE_DONE = 4;

    function ajax (method, url, data, onSuccess, onError) {

        var requestObject = XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP");

        requestObject.open(method, url + "?random=" + Math.random(), true);

        requestObject.onreadystatechange = function() {
            var done, statusOk;

            done = requestObject.readyState === READY_STATE_DONE;
            statusOk = requestObject.status === HTTP_STATUS_OK;

            if (done) {
                if (statusOk) {
                    onSuccess(requestObject);
                }
                else {
                    onError(requestObject);
                }
            }
        };

        if (data) {
            requestObject.send(data);
        }
        else {
            requestObject.send();
        }

        return requestObject;
    }

    ajax.HTTP_STATUS_OK = HTTP_STATUS_OK;

    ajax.READY_STATE_UNSENT = READY_STATE_UNSENT;
    ajax.READY_STATE_OPENED = READY_STATE_OPENED;
    ajax.READY_STATE_HEADERS_RECEIVED = READY_STATE_HEADERS_RECEIVED;
    ajax.READY_STATE_LOADING = READY_STATE_LOADING;
    ajax.READY_STATE_DONE = READY_STATE_DONE;

    ajax.HTTP_METHOD_GET = "GET";
    ajax.HTTP_METHOD_POST = "POST";
    ajax.HTTP_METHOD_PUT = "PUT";
    ajax.HTTP_METHOD_DELETE = "DELETE";
    ajax.HTTP_METHOD_HEAD = "HEAD";

    return ajax;
}());<|endoftext|>


<commit_before>class PlayerAI {
    constructor() {
        this.have_ball = false
        this.toball_cnt = 0
        this.aggressive_cnt = 0
        this.tofront_cnt = 0
        this.togoal_cnt = 0
        this.managed = []
    }

    step(dt) {

    }

    manage(player, dt, delta) {

        if (this.managed[player] == null) {
            this.managed[player] = {
                "mode": "toball",
                "lastmode_ts": 0,
            }
        }
        var infos = this.managed[player]
        let managed = this.managed[player]
        var mode_duration = 3
        var goals = [
            [106, -101, -114], // goal area on left side
            [283, -101, -114], // goal area on right side
        ]

        for (let dude of app.menus[0].enemies) {
          if (dude.ball) {
            this.have_ball = true
          } else {
            this.have_ball = false
          }
        }
        console.log(this.have_ball);

        // set  ai behaviour
        if (player.ball) {
            // when near the goal and have ball
            if (player.pos.x < (goals[0][0] + 40) &&
                player.pos.y < goals[0][1] &&
                player.pos.y > goals[0][2]) {
                infos.mode = "togoal"
                this.togoal_cnt = this.togoal_cnt + 1
            } else {
                infos.mode = 'tofrontgoal'
                this.tofront_cnt = managed.tofront_cnt + 1
            }
        } else {
            if (app.menus[0].player.ball) { // an another ennemy player have the ball
                infos.mode = 'aggressive'
                this.aggressive_cnt = managed.aggressive_cnt + 1
            } else if (infos.lastmode == null || infos.lastmode_ts > mode_duration) {
                infos.lastmode_ts = 0

                infos.mode = 'toball'
                this.toball_cnt = this.toball_cnt + 1

            } else {
                infos.lastmode_ts = infos.lastmode_ts + dt
                infos.mode = infos.lastmode
            }
        }
        // infos.mode = "toball"
        // now apply the behavior according to the chosen mode

        var x = null
        var y = null
        if (infos.mode == "toball") {
            let random_player = app.menus[0].ball

            var dist = player.distance_from(random_player.pos)
            let [targets, angle] = player.targets(random_player.pos, 20)
            if (angle > 0 || angle < -Math.PI) {
                player.left(delta);
            } else {
                player.right(delta);
            }
            if (targets)
                player.forward(delta);
        } else if (infos.mode == "aggressive") {
            let random_player = app.menus[0].player
            var dist = player.distance_from(random_player.pos)
            let [targets, angle] = player.targets(random_player.pos, 20)
            if (angle > 0 || angle < -Math.PI) {
                player.left(delta);
            } else {
                player.right(delta);
            }
            if (targets)
                player.forward(delta);
        } else if (infos.mode == "tofrontgoal") {
            let aim = new planck.Vec2(goals[0][0] + 40, (goals[0][1] + goals[0][2]) / 2)
            console.log(aim);
            var dist = player.distance_from(aim)
            let [targets, angle] = player.targets(aim, 40)
            if (angle > 0 || angle < -Math.PI) {
                player.left(delta);
            } else {
                player.right(delta);
            }
            if (targets)
                player.forward(delta);
        } else if (infos.mode == "togoal") {
            let aim = new planck.Vec2(goals[0][0] - 5, (goals[0][1] + goals[0][2]) / 2)
            console.log(aim);
            var dist = player.distance_from(aim)
            let [targets, angle] = player.targets(aim, 40)
            if (angle > 0 || angle < -Math.PI) {
                player.left(delta);
            } else {
                player.right(delta);
            }
            if (targets)
                player.forward(delta);
        }

        console.log(infos.mode);
    }

    clear() {

    }
}
<commit_msg>dudes stops quarreling for ball<commit_after>class PlayerAI {
    constructor() {
        this.have_ball = false
        this.toball_cnt = 0
        this.aggressive_cnt = 0
        this.tofront_cnt = 0
        this.togoal_cnt = 0
        this.managed = []
    }

    step(dt) {

    }

    manage(player, dt, delta) {

        if (this.managed[player] == null) {
            this.managed[player] = {
                "mode": "toball",
                "lastmode_ts": 0,
            }
        }
        var infos = this.managed[player]
        let managed = this.managed[player]
        var mode_duration = 3
        var goals = [
            [106, -101, -114], // goal area on left side
            [283, -101, -114], // goal area on right side
        ]

        for (let dude of app.menus[0].enemies) {
          if (dude.ball) {
            this.have_ball = true
          } else {
            this.have_ball = false
          }
        }
        console.log(this.have_ball);

        // set  ai behaviour
        if (player.ball) {
            // when near the goal and have ball
            if (player.pos.x < (goals[0][0] + 40) &&
                player.pos.y < goals[0][1] &&
                player.pos.y > goals[0][2]) {
                infos.mode = "togoal"
                this.togoal_cnt = this.togoal_cnt + 1
            } else {
                infos.mode = 'tofrontgoal'
                this.tofront_cnt = managed.tofront_cnt + 1
            }
        } else {
            if (app.menus[0].player.ball) { // an another ennemy player have the ball
                infos.mode = 'aggressive'
                this.aggressive_cnt = managed.aggressive_cnt + 1
            } else if (infos.lastmode == null || infos.lastmode_ts > mode_duration) {
                infos.lastmode_ts = 0
                if (!this.have_ball) {

                  infos.mode = 'toball'
                  this.toball_cnt = this.toball_cnt + 1
                }

            } else {
                infos.lastmode_ts = infos.lastmode_ts + dt
                infos.mode = infos.lastmode
            }
        }
        // infos.mode = "toball"
        // now apply the behavior according to the chosen mode

        var x = null
        var y = null
        if (infos.mode == "toball") {
            let random_player = app.menus[0].ball

            var dist = player.distance_from(random_player.pos)
            let [targets, angle] = player.targets(random_player.pos, 20)
            if (angle > 0 || angle < -Math.PI) {
                player.left(delta);
            } else {
                player.right(delta);
            }
            if (targets)
                player.forward(delta);
        } else if (infos.mode == "aggressive") {
            let random_player = app.menus[0].player
            var dist = player.distance_from(random_player.pos)
            let [targets, angle] = player.targets(random_player.pos, 20)
            if (angle > 0 || angle < -Math.PI) {
                player.left(delta);
            } else {
                player.right(delta);
            }
            if (targets)
                player.forward(delta);
        } else if (infos.mode == "tofrontgoal") {
            let aim = new planck.Vec2(goals[0][0] + 40, (goals[0][1] + goals[0][2]) / 2)
            console.log(aim);
            var dist = player.distance_from(aim)
            let [targets, angle] = player.targets(aim, 40)
            if (angle > 0 || angle < -Math.PI) {
                player.left(delta);
            } else {
                player.right(delta);
            }
            if (targets)
                player.forward(delta);
        } else if (infos.mode == "togoal") {
            let aim = new planck.Vec2(goals[0][0] - 5, (goals[0][1] + goals[0][2]) / 2)
            console.log(aim);
            var dist = player.distance_from(aim)
            let [targets, angle] = player.targets(aim, 40)
            if (angle > 0 || angle < -Math.PI) {
                player.left(delta);
            } else {
                player.right(delta);
            }
            if (targets)
                player.forward(delta);
        }

        console.log(infos.mode);
    }

    clear() {

    }
}
<|endoftext|>


<commit_before>var activeRoom;
var previewTracks;
var identity;
var roomName;

function attachTracks(tracks, container) {
  tracks.forEach(function(track) {
    container.appendChild(track.attach());
  });
}

function attachParticipantTracks(participant, container) {
  var tracks = Array.from(participant.tracks.values());
  attachTracks(tracks, container);
}

function detachTracks(tracks) {
  tracks.forEach(function(track) {
    track.detach().forEach(function(detachedElement) {
      detachedElement.remove();
    });
  });
}

function detachParticipantTracks(participant) {
  var tracks = Array.from(participant.tracks.values());
  detachTracks(tracks);
}

// Check for WebRTC
if (!navigator.webkitGetUserMedia && !navigator.mozGetUserMedia) {
  alert('WebRTC is not available in your browser.');
}

// When we are about to transition away from this page, disconnect
// from the room, if joined.
window.addEventListener('beforeunload', leaveRoomIfJoined);

$.getJSON('/token', function(data) {
  identity = data.identity;

  document.getElementById('room-controls').style.display = 'block';

  // Bind button to join room
  document.getElementById('button-join').onclick = function () {
    roomName = document.getElementById('room-name').value;
    if (roomName) {
      log("Joining room '" + roomName + "'...");

      var connectOptions = { name: roomName, logLevel: 'debug' };
      if (previewTracks) {
        connectOptions.tracks = previewTracks;
      }

      Twilio.Video.connect(data.token, connectOptions).then(roomJoined, function(error) {
        log('Could not connect to Twilio: ' + error.message);
      });
    } else {
      alert('Please enter a room name.');
    }
  };

  // Bind button to leave room
  document.getElementById('button-leave').onclick = function () {
    log('Leaving room...');
    activeRoom.disconnect();
  };
});

// Successfully connected!
function roomJoined(room) {
  activeRoom = room;

  log("Joined as '" + identity + "'");
  document.getElementById('button-join').style.display = 'none';
  document.getElementById('button-leave').style.display = 'inline';

  // Draw local video, if not already previewing
  var previewContainer = document.getElementById('local-media');
  if (!previewContainer.querySelector('video')) {
    attachParticipantTracks(room.localParticipant, previewContainer);
  }

  room.participants.forEach(function(participant) {
    log("Already in Room: '" + participant.identity + "'");
    var previewContainer = document.getElementById('remote-media');
    attachParticipantTracks(participant, previewContainer);
  });

  // When a participant joins, draw their video on screen
  room.on('participantConnected', function(participant) {
    log("Joining: '" + participant.identity + "'");
  });

  room.on('trackAdded', function(track, participant) {
    log(participant.identity + " added track: " + track.kind);
    var previewContainer = document.getElementById('remote-media');
    attachTracks([track], previewContainer);
  });

  room.on('trackRemoved', function(track, participant) {
    log(participant.identity + " removed track: " + track.kind);
    detachTracks([track]);
  });

  // When a participant disconnects, note in log
  room.on('participantDisconnected', function(participant) {
    log("Participant '" + participant.identity + "' left the room");
    detachParticipantTracks(participant);
  });

  // When we are disconnected, stop capturing local video
  // Also remove media for all remote participants
  room.on('disconnected', function() {
    log('Left');
    detachParticipantTracks(room.localParticipant);
    room.participants.forEach(detachParticipantTracks);
    activeRoom = null;
    document.getElementById('button-join').style.display = 'inline';
    document.getElementById('button-leave').style.display = 'none';
  });
}

//  Local video preview
document.getElementById('button-preview').onclick = function() {
  var localTracksPromise = previewTracks
    ? Promise.resolve(previewTracks)
    : Twilio.Video.createLocalTracks();

  localTracksPromise.then(function(tracks) {
    previewTracks = tracks;
    var previewContainer = document.getElementById('local-media');
    if (!previewContainer.querySelector('video')) {
      attachTracks(tracks, previewContainer);
    }
  }, function(error) {
    console.error('Unable to access local media', error);
    log('Unable to access Camera and Microphone');
  });
};

// Activity log
function log(message) {
  var logDiv = document.getElementById('log');
  logDiv.innerHTML += '
> ' + message + '
';
  logDiv.scrollTop = logDiv.scrollHeight;
}

function leaveRoomIfJoined() {
  if (activeRoom) {
    activeRoom.disconnect();
  }
}
<commit_msg>fix token loading<commit_after>var activeRoom;
var previewTracks;
var identity;
var roomName;

function attachTracks(tracks, container) {
  tracks.forEach(function(track) {
    container.appendChild(track.attach());
  });
}

function attachParticipantTracks(participant, container) {
  var tracks = Array.from(participant.tracks.values());
  attachTracks(tracks, container);
}

function detachTracks(tracks) {
  tracks.forEach(function(track) {
    track.detach().forEach(function(detachedElement) {
      detachedElement.remove();
    });
  });
}

function detachParticipantTracks(participant) {
  var tracks = Array.from(participant.tracks.values());
  detachTracks(tracks);
}

// Check for WebRTC
if (!navigator.webkitGetUserMedia && !navigator.mozGetUserMedia) {
  alert('WebRTC is not available in your browser.');
}

// When we are about to transition away from this page, disconnect
// from the room, if joined.
window.addEventListener('beforeunload', leaveRoomIfJoined);

$.getJSON('/token.php', function(data) {
  identity = data.identity;

  document.getElementById('room-controls').style.display = 'block';

  // Bind button to join room
  document.getElementById('button-join').onclick = function () {
    roomName = document.getElementById('room-name').value;
    if (roomName) {
      log("Joining room '" + roomName + "'...");

      var connectOptions = { name: roomName, logLevel: 'debug' };
      if (previewTracks) {
        connectOptions.tracks = previewTracks;
      }

      Twilio.Video.connect(data.token, connectOptions).then(roomJoined, function(error) {
        log('Could not connect to Twilio: ' + error.message);
      });
    } else {
      alert('Please enter a room name.');
    }
  };

  // Bind button to leave room
  document.getElementById('button-leave').onclick = function () {
    log('Leaving room...');
    activeRoom.disconnect();
  };
});

// Successfully connected!
function roomJoined(room) {
  activeRoom = room;

  log("Joined as '" + identity + "'");
  document.getElementById('button-join').style.display = 'none';
  document.getElementById('button-leave').style.display = 'inline';

  // Draw local video, if not already previewing
  var previewContainer = document.getElementById('local-media');
  if (!previewContainer.querySelector('video')) {
    attachParticipantTracks(room.localParticipant, previewContainer);
  }

  room.participants.forEach(function(participant) {
    log("Already in Room: '" + participant.identity + "'");
    var previewContainer = document.getElementById('remote-media');
    attachParticipantTracks(participant, previewContainer);
  });

  // When a participant joins, draw their video on screen
  room.on('participantConnected', function(participant) {
    log("Joining: '" + participant.identity + "'");
  });

  room.on('trackAdded', function(track, participant) {
    log(participant.identity + " added track: " + track.kind);
    var previewContainer = document.getElementById('remote-media');
    attachTracks([track], previewContainer);
  });

  room.on('trackRemoved', function(track, participant) {
    log(participant.identity + " removed track: " + track.kind);
    detachTracks([track]);
  });

  // When a participant disconnects, note in log
  room.on('participantDisconnected', function(participant) {
    log("Participant '" + participant.identity + "' left the room");
    detachParticipantTracks(participant);
  });

  // When we are disconnected, stop capturing local video
  // Also remove media for all remote participants
  room.on('disconnected', function() {
    log('Left');
    detachParticipantTracks(room.localParticipant);
    room.participants.forEach(detachParticipantTracks);
    activeRoom = null;
    document.getElementById('button-join').style.display = 'inline';
    document.getElementById('button-leave').style.display = 'none';
  });
}

//  Local video preview
document.getElementById('button-preview').onclick = function() {
  var localTracksPromise = previewTracks
    ? Promise.resolve(previewTracks)
    : Twilio.Video.createLocalTracks();

  localTracksPromise.then(function(tracks) {
    previewTracks = tracks;
    var previewContainer = document.getElementById('local-media');
    if (!previewContainer.querySelector('video')) {
      attachTracks(tracks, previewContainer);
    }
  }, function(error) {
    console.error('Unable to access local media', error);
    log('Unable to access Camera and Microphone');
  });
};

// Activity log
function log(message) {
  var logDiv = document.getElementById('log');
  logDiv.innerHTML += '
> ' + message + '
';
  logDiv.scrollTop = logDiv.scrollHeight;
}

function leaveRoomIfJoined() {
  if (activeRoom) {
    activeRoom.disconnect();
  }
}
<|endoftext|>


<commit_before>/*!
 * backbone.layoutmanager.js v0.2.0
 * Copyright 2011, Tim Branyen (@tbranyen)
 * backbone.layoutmanager.js may be freely distributed under the MIT license.
 */
(function(Backbone, _, $) {

"use strict";

// This gets passed to all _render methods.
function viewRender(root) {
  var url, contents, handler;
  var options = root._options();

  // Once the template is successfully fetched, use its contents to
  // proceed.  Context argument is first, since it is bound for
  // partial application reasons.
  function done(context, contents) {
    // Ensure the cache is up-to-date.
    LayoutManager.cache(url, contents);

    // Render the View into the el property.
    options.html(root.el, options.render(contents, context));

    // Resolve partials with the View element.
    handler.resolve(root.el);
  }

  return {
    // Shorthand to root.view function with append flag
    insert: function(partial, view) {
      return root.view(partial, view, true);
    },

    render: function(context) {
      var template = root.template || options.template;

      if (root.serialize) {
        options.serialize = root.serialize;
      }

      // Seek out serialize method and use that object.
      if (!context && _.isFunction(options.serialize)) {
        context = options.serialize.call(root);
      // If serialize is an object, just use that
      } else if (!context && _.isObject(options.serialize)) {
        context = options.serialize;
      }

      // Create an asynchronous handler
      handler = LayoutManager.makeAsync(options, _.bind(done, root, context));

      // Set the url to the prefix + the view's template property.
      if (_.isString(template)) {
        url = root._prefix + template;
      }

      // Check if contents are already cached
      if (contents = LayoutManager.cache(url)) {
        // Make this function act asynchronous to avoid issues with event
        // binding and other unintentional consequences of different timing
        // from synchronous operations.
        setTimeout(function() {
          done(context, contents, url);
        }, 0);

        return handler;
      }

      // Fetch layout and template contents
      if (_.isString(template)) {
        contents = options.fetch.call(handler, root._prefix + template);
      // If its not a string just pass the object/function/whatever
      } else {
        contents = options.fetch.call(handler, template);
      }

      // If the function was synchronous, continue execution.
      if (!handler._isAsync) {
        setTimeout(function() {
          done(context, contents);
        }, 0);
      }

      return handler;
    }
  };
}

var LayoutManager = Backbone.View.extend({
  // Internal state object used to store whether or not a View has been
  // taken over by layout manager and if it has been rendered into the DOM.
  __manager__: {},

  // This is a named function to improve logging and debugging within browser
  // dev tools.  Typically you do not use "anonymous" named functions since IE
  // has a well known bug, BUT I think we all know the reason why I'm ignoring
  // that here.
  constructor: function LayoutManager(options) {
    var proto = Backbone.LayoutManager.prototype;
    // Extend the options with the prototype and passed options
    options = _.extend({}, proto.options, options);

    // Apply the default render scheme.
    this._render = function(layout) {
      return layout(this).render();
    };

    // Set up top level views object
    this.views = {};

    // If the user provided their own render override, use that instead of the
    // default.
    if (this.render !== proto.render) {
      this._render = this.render;
      this.render = proto.render;
    }

    // Set the prefix for a layout
    if (options.paths) {
      this._prefix = options.paths.layout || "";
    }

    // Set the internal views
    if (options.views) {
      this.setViews(options.views);
    }

    Backbone.View.call(this, options);
  },

  // Allows the setting of multiple views instead of a single view.
  setViews: function(views) {
    // Iterate over all the views and use the View's view method to assign.
    _.each(views, function(view, name) {
      // Assign each view using the view function
      this.view(name, view);
    }, this);
  },

  // This takes in a partial name and view instance and assigns them to
  // the internal collection of views.  If a view is not a LayoutManager
  // instance, then mix in the LayoutManager prototype.  This ensures
  // all Views can be used successfully.
  //
  // Must definitely wrap any render method passed in or defaults to a
  // typical render function `return layout(this).render()`.
  view: function(name, view, append) {
    var partials, options;
    var root = this;

    // Internal property necessary for every View.
    view.__manager__ = {};

    // Add in all missing LayoutManager properties and methods.
    if (!(view instanceof LayoutManager)) {
      view._render = view.render;

      // If no render override was specified assign the default
      if (view.render === Backbone.View.prototype.render) {
        view._render = function(layout) {
          return layout(this).render();
        };
      }

      if (view.views) {
        view.options.views = view.views;
      }

      // Mix in reusable properties
      _.extend(view, {
        views: {},
        view: LayoutManager.prototype.view,
        setViews: LayoutManager.prototype.setViews,
        _options: LayoutManager.prototype._options
      });
    }

    if (!append) {
      view.__manager__.isManaged = true;
    }

    view.render = function(done) {
      var viewDeferred = options.deferred();

      if (!view.__manager__.isManaged) {
        return viewDeferred.resolve(view.el);
      }

      LayoutManager.prototype.render.call(view).then(function() {
        if (!view.__manager__.hasRendered) {
          options.partial(root.el, name, view.el, append);
          view.__manager__.hasRendered = true;
        }

        view.delegateEvents();
        viewDeferred.resolve(view.el);
      });

      return viewDeferred.promise();
    };

    // Instance overrides take precedence, fallback to prototype options.
    options = view._options();

    // Set the prefix for a layout
    if (options.paths) {
      view._prefix = options.paths.template || "";
    }

    // Set the internal views
    if (options.views) {
      view.setViews(options.views);
    }

    // Special logic for appending items
    if (append) {
      partials = this.views[name] = this.views[name] || [];
      partials.push(view);

      return view;
    }

    return this.views[name] = view;
  },

  // By default this should find all nested views and render them into
  // the this.el and call done once all of them have successfully been
  // resolved.
  //
  // This function returns a promise that can be chained to determine
  // once all subviews and main view have been rendered into the view.el.
  render: function(done) {
    var root = this;
    var options = this._options();
    var viewDeferred = options.deferred();

    // Wait until this View has rendered before dealing with nested Views.
    this._render(viewRender).then(function() {
      // Ensure element is removed from DOM before updating
      if (!root.__manager__.hasRendered) {
        options.detach(root.el);
      }

      // Create a list of promises to wait on until rendering is done. Since
      // this method will run on all children as well, its sufficient for a
      // full hierarchical.
      var promises = _.map(root.views, function(view) {
        var def;

        // Ensure views are rendered in sequence
        function seqRender(views, done) {
          // Once all views have been rendered invoke the sequence render callback
          if (!views.length) {
            return done();
          }

          // Get each view in order, grab the first one off the stack
          var view = views.shift();

          // Call render on the view, and once complete call the next view
          view.__manager__.isManaged = true;
          view.render().then(function() {
            // Invoke the recursive sequence render function with the remaining
            // views
            seqRender(views, done);
          });
        }

        // If rendering a list out, ensure they happen in a serial order
        if (_.isArray(view)) {
          def = options.deferred();

          seqRender(_.clone(view), function() {
            def.resolve();
          });

          return def.promise();
        }

        view.__manager__.isManaged = true;
        return view.render();
      });

      // Once all subViews have been rendered, resolve this View's deferred.
      options.when(promises).then(function() {
        viewDeferred.resolve(root.el);
      });
    });

    // Return a promise that resolves once all immediate subViews have
    // rendered.
    return viewDeferred.then(function(el) {
      root.delegateEvents();

      // Only call the done function if a callback was provided.
      if (_.isFunction(done)) {
        done(root.el);
      }
    }).promise();
  },

  _options: function() {
    // Instance overrides take precedence, fallback to prototype options.
    return _.extend({}, LayoutManager.prototype.options, this.options);
  }

},
{
  // Clearable cache
  _cache: {},

  // Cache templates into LayoutManager._cache
  cache: function(path, contents) {
    // If template path is found in the cache, return the contents.
    if (path in this._cache) {
      return this._cache[path];
    // Ensure path and contents aren't undefined
    } else if (path != null && contents != null) {
      return this._cache[path] = contents;
    }

    // If template is not in the cache, return undefined.
  },

  // This static method allows for global configuration of LayoutManager.
  configure: function(opts) {
    if (_.isObject(opts)) {
      _.extend(LayoutManager.prototype.options, opts);
    }
  },

  makeAsync: function(options, done) {
    var handler = options.deferred();

    // Used to handle asynchronous renders
    handler.async = function() {
      handler._isAsync = true;

      return done;
    };

    return handler;
  }
});

// Attach to Backbone
Backbone.LayoutManager = LayoutManager;
Backbone.LayoutManager.View = LayoutManager;

// Default configuration options; designed to be overriden.
LayoutManager.prototype.options = {

  // Layout and template properties can be assigned here to prefix
  // template/layout names.
  paths: {},

  // Can be used to supply a different deferred that implements Promises/A.
  deferred: function() {
    return $.Deferred();
  },

  // Fetch is passed a path and is expected to return template contents as a
  // string.
  fetch: function(path) {
    return _.template($(path).html());
  },

  // This is really the only way you will want to partially apply a view into
  // a layout.  Its entirely possible you'll want to do it differently, so
  // this method is available to change.
  partial: function(root, name, el, append) {
    var $root = $(root).find(name);
    this[append ? "append" : "html"]($root, el);
  },

  // Override this with a custom HTML method, passed a root element and an
  // element to replace the innerHTML with.
  html: function(root, el) {
    $(root).html(el);
  },

  // Very similar to HTML except this one will appendChild.
  append: function(root, el) {
    $(root).append(el);
  },

  // Abstract out the $.fn.detach method
  detach: function(el) {
    $(el).detach();
  },

  // Return a deferred for when all promises resolve/reject.
  when: function(promises) {
    return $.when.apply(null, promises);
  },

  // By default, render using underscore's templating.
  render: function(template, context) {
    return template(context);
  }

};

})(this.Backbone, this._, this.jQuery);
<commit_msg>Invokes the template and forces the context to be that of the provided template context.<commit_after>/*!
 * backbone.layoutmanager.js v0.2.0
 * Copyright 2011, Tim Branyen (@tbranyen)
 * backbone.layoutmanager.js may be freely distributed under the MIT license.
 */
(function(Backbone, _, $) {

"use strict";

// This gets passed to all _render methods.
function viewRender(root) {
  var url, contents, handler;
  var options = root._options();

  // Once the template is successfully fetched, use its contents to
  // proceed.  Context argument is first, since it is bound for
  // partial application reasons.
  function done(context, contents) {
    // Ensure the cache is up-to-date.
    LayoutManager.cache(url, contents);

    // Render the View into the el property.
    options.html(root.el, options.render(contents, context));

    // Resolve partials with the View element.
    handler.resolve(root.el);
  }

  return {
    // Shorthand to root.view function with append flag
    insert: function(partial, view) {
      return root.view(partial, view, true);
    },

    render: function(context) {
      var template = root.template || options.template;

      if (root.serialize) {
        options.serialize = root.serialize;
      }

      // Seek out serialize method and use that object.
      if (!context && _.isFunction(options.serialize)) {
        context = options.serialize.call(root);
      // If serialize is an object, just use that
      } else if (!context && _.isObject(options.serialize)) {
        context = options.serialize;
      }

      // Create an asynchronous handler
      handler = LayoutManager.makeAsync(options, _.bind(done, root, context));

      // Set the url to the prefix + the view's template property.
      if (_.isString(template)) {
        url = root._prefix + template;
      }

      // Check if contents are already cached
      if (contents = LayoutManager.cache(url)) {
        // Make this function act asynchronous to avoid issues with event
        // binding and other unintentional consequences of different timing
        // from synchronous operations.
        setTimeout(function() {
          done(context, contents, url);
        }, 0);

        return handler;
      }

      // Fetch layout and template contents
      if (_.isString(template)) {
        contents = options.fetch.call(handler, root._prefix + template);
      // If its not a string just pass the object/function/whatever
      } else {
        contents = options.fetch.call(handler, template);
      }

      // If the function was synchronous, continue execution.
      if (!handler._isAsync) {
        setTimeout(function() {
          done(context, contents);
        }, 0);
      }

      return handler;
    }
  };
}

var LayoutManager = Backbone.View.extend({
  // Internal state object used to store whether or not a View has been
  // taken over by layout manager and if it has been rendered into the DOM.
  __manager__: {},

  // This is a named function to improve logging and debugging within browser
  // dev tools.  Typically you do not use "anonymous" named functions since IE
  // has a well known bug, BUT I think we all know the reason why I'm ignoring
  // that here.
  constructor: function LayoutManager(options) {
    var proto = Backbone.LayoutManager.prototype;
    // Extend the options with the prototype and passed options
    options = _.extend({}, proto.options, options);

    // Apply the default render scheme.
    this._render = function(layout) {
      return layout(this).render();
    };

    // Set up top level views object
    this.views = {};

    // If the user provided their own render override, use that instead of the
    // default.
    if (this.render !== proto.render) {
      this._render = this.render;
      this.render = proto.render;
    }

    // Set the prefix for a layout
    if (options.paths) {
      this._prefix = options.paths.layout || "";
    }

    // Set the internal views
    if (options.views) {
      this.setViews(options.views);
    }

    Backbone.View.call(this, options);
  },

  // Allows the setting of multiple views instead of a single view.
  setViews: function(views) {
    // Iterate over all the views and use the View's view method to assign.
    _.each(views, function(view, name) {
      // Assign each view using the view function
      this.view(name, view);
    }, this);
  },

  // This takes in a partial name and view instance and assigns them to
  // the internal collection of views.  If a view is not a LayoutManager
  // instance, then mix in the LayoutManager prototype.  This ensures
  // all Views can be used successfully.
  //
  // Must definitely wrap any render method passed in or defaults to a
  // typical render function `return layout(this).render()`.
  view: function(name, view, append) {
    var partials, options;
    var root = this;

    // Internal property necessary for every View.
    view.__manager__ = {};

    // Add in all missing LayoutManager properties and methods.
    if (!(view instanceof LayoutManager)) {
      view._render = view.render;

      // If no render override was specified assign the default
      if (view.render === Backbone.View.prototype.render) {
        view._render = function(layout) {
          return layout(this).render();
        };
      }

      if (view.views) {
        view.options.views = view.views;
      }

      // Mix in reusable properties
      _.extend(view, {
        views: {},
        view: LayoutManager.prototype.view,
        setViews: LayoutManager.prototype.setViews,
        _options: LayoutManager.prototype._options
      });
    }

    if (!append) {
      view.__manager__.isManaged = true;
    }

    view.render = function(done) {
      var viewDeferred = options.deferred();

      if (!view.__manager__.isManaged) {
        return viewDeferred.resolve(view.el);
      }

      LayoutManager.prototype.render.call(view).then(function() {
        if (!view.__manager__.hasRendered) {
          options.partial(root.el, name, view.el, append);
          view.__manager__.hasRendered = true;
        }

        view.delegateEvents();
        viewDeferred.resolve(view.el);
      });

      return viewDeferred.promise();
    };

    // Instance overrides take precedence, fallback to prototype options.
    options = view._options();

    // Set the prefix for a layout
    if (options.paths) {
      view._prefix = options.paths.template || "";
    }

    // Set the internal views
    if (options.views) {
      view.setViews(options.views);
    }

    // Special logic for appending items
    if (append) {
      partials = this.views[name] = this.views[name] || [];
      partials.push(view);

      return view;
    }

    return this.views[name] = view;
  },

  // By default this should find all nested views and render them into
  // the this.el and call done once all of them have successfully been
  // resolved.
  //
  // This function returns a promise that can be chained to determine
  // once all subviews and main view have been rendered into the view.el.
  render: function(done) {
    var root = this;
    var options = this._options();
    var viewDeferred = options.deferred();

    // Wait until this View has rendered before dealing with nested Views.
    this._render(viewRender).then(function() {
      // Ensure element is removed from DOM before updating
      if (!root.__manager__.hasRendered) {
        options.detach(root.el);
      }

      // Create a list of promises to wait on until rendering is done. Since
      // this method will run on all children as well, its sufficient for a
      // full hierarchical.
      var promises = _.map(root.views, function(view) {
        var def;

        // Ensure views are rendered in sequence
        function seqRender(views, done) {
          // Once all views have been rendered invoke the sequence render callback
          if (!views.length) {
            return done();
          }

          // Get each view in order, grab the first one off the stack
          var view = views.shift();

          // Call render on the view, and once complete call the next view
          view.__manager__.isManaged = true;
          view.render().then(function() {
            // Invoke the recursive sequence render function with the remaining
            // views
            seqRender(views, done);
          });
        }

        // If rendering a list out, ensure they happen in a serial order
        if (_.isArray(view)) {
          def = options.deferred();

          seqRender(_.clone(view), function() {
            def.resolve();
          });

          return def.promise();
        }

        view.__manager__.isManaged = true;
        return view.render();
      });

      // Once all subViews have been rendered, resolve this View's deferred.
      options.when(promises).then(function() {
        viewDeferred.resolve(root.el);
      });
    });

    // Return a promise that resolves once all immediate subViews have
    // rendered.
    return viewDeferred.then(function(el) {
      root.delegateEvents();

      // Only call the done function if a callback was provided.
      if (_.isFunction(done)) {
        done(root.el);
      }
    }).promise();
  },

  _options: function() {
    // Instance overrides take precedence, fallback to prototype options.
    return _.extend({}, LayoutManager.prototype.options, this.options);
  }

},
{
  // Clearable cache
  _cache: {},

  // Cache templates into LayoutManager._cache
  cache: function(path, contents) {
    // If template path is found in the cache, return the contents.
    if (path in this._cache) {
      return this._cache[path];
    // Ensure path and contents aren't undefined
    } else if (path != null && contents != null) {
      return this._cache[path] = contents;
    }

    // If template is not in the cache, return undefined.
  },

  // This static method allows for global configuration of LayoutManager.
  configure: function(opts) {
    if (_.isObject(opts)) {
      _.extend(LayoutManager.prototype.options, opts);
    }
  },

  makeAsync: function(options, done) {
    var handler = options.deferred();

    // Used to handle asynchronous renders
    handler.async = function() {
      handler._isAsync = true;

      return done;
    };

    return handler;
  }
});

// Attach to Backbone
Backbone.LayoutManager = LayoutManager;
Backbone.LayoutManager.View = LayoutManager;

// Default configuration options; designed to be overriden.
LayoutManager.prototype.options = {

  // Layout and template properties can be assigned here to prefix
  // template/layout names.
  paths: {},

  // Can be used to supply a different deferred that implements Promises/A.
  deferred: function() {
    return $.Deferred();
  },

  // Fetch is passed a path and is expected to return template contents as a
  // string.
  fetch: function(path) {
    return _.template($(path).html());
  },

  // This is really the only way you will want to partially apply a view into
  // a layout.  Its entirely possible you'll want to do it differently, so
  // this method is available to change.
  partial: function(root, name, el, append) {
    var $root = $(root).find(name);
    this[append ? "append" : "html"]($root, el);
  },

  // Override this with a custom HTML method, passed a root element and an
  // element to replace the innerHTML with.
  html: function(root, el) {
    $(root).html(el);
  },

  // Very similar to HTML except this one will appendChild.
  append: function(root, el) {
    $(root).append(el);
  },

  // Abstract out the $.fn.detach method
  detach: function(el) {
    $(el).detach();
  },

  // Return a deferred for when all promises resolve/reject.
  when: function(promises) {
    return $.when.apply(null, promises);
  },

  // By default, render using underscore's templating.
  render: function(template, context) {
    return template.call(context, context);
  }

};

})(this.Backbone, this._, this.jQuery);
<|endoftext|>


<commit_before>'use babel';

import {Emitter} from 'atom';
import CSON from 'season';
import fs from 'fs';
import path from 'path';
import os from 'os';
import _ from 'underscore-plus';

export default class DB {
  constructor(searchKey=null, searchValue=null) {
    this.setSearchQuery(searchKey, searchValue);
    this.emitter = new Emitter();

    fs.exists(this.file(), (exists) => {
      if (exists) {
        this.observeProjects();
      } else {
        this.writeFile({});
      }
    });
  }

  get environmentSpecificProjects() {
    return atom.config.get('project-manager.environmentSpecificProjects');
  }

  setSearchQuery(searchKey=null, searchValue=null) {
    this.searchKey = searchKey;
    this.searchValue = searchValue;
  }

  find(callback) {
    this.readFile(results => {
      let found = false;
      let projects = [];
      let project = null;
      let result = null;
      let template = null;
      let key;

      for (key in results) {
        result = results[key];
        template = result.template || null;
        result._id = key;

        if (template && results[template] !== null) {
          result = _.deepExtend(result, results[template]);
        }

        for (let i in result.paths) {
          result.paths[i] = result.paths[i].replace('~', os.homedir());
        }

        projects.push(result);
      }

      if (this.searchKey && this.searchValue) {
        for (key in projects) {
          project = projects[key];
          if (_.isEqual(project[this.searchKey], this.searchValue)) {
            found = project;
          }
        }
      } else {
        found = projects;
      }

      callback(found);
    });
  }

  add(props, callback) {
    this.readFile(projects => {
      const id = this.generateID(props.title);
      projects[id] = props;

      this.writeFile(projects, () => {
        atom.notifications.addSuccess(`${props.title} has been added`);
        callback(id);
      });
    });
  }

  update(props) {
    if (!props._id) {
      return false;
    }

    let project = null;
    let key;
    this.readFile(projects => {
      for (key in projects) {
        project = projects[key];
        if (key === props._id) {
          delete(props._id);
          projects[key] = props;
        }

        this.writeFile(projects);
      }
    });
  }

  delete(id, callback) {
    this.readFile(projects => {
      for (let key in projects) {
        if (key === id) {
          delete(projects[key]);
        }
      }

      this.writeFile(projects, () => {
        if (callback) {
          callback();
        }
      });
    });
  }

  onUpdate(callback) {
    this.emitter.on('db-updated', () => {
      this.find(callback);
    });
  }

  observeProjects() {
    if (this.fileWatcher) {
      this.fileWatcher.close();
    }

    try {
      this.fileWatcher = fs.watch(this.file(), () => {
        this.emitter.emit('db-updated');
      });
    } catch (error) {
      let url = 'https://github.com/atom/atom/blob/master/docs/';
      url += 'build-instructions/linux.md#typeerror-unable-to-watch-path';
      const filename = path.basename(this.file());
      const errorMessage = `Project Manager
Could not watch changes
        to ${filename}. Make sure you have permissions to ${this.file()}.
        On linux there can be problems with watch sizes.
        See  this document for more info.>`;
      this.notifyFailure(errorMessage);
    }
  }

  updateFile() {
    fs.exists(this.file(true), (exists) => {
      if (!exists) {
        this.writeFile({});
      }
    });
  }

  generateID(string) {
    return string.replace(/\s+/g, '').toLowerCase();
  }

  file() {
    let filename = 'projects.cson';
    const filedir = atom.getConfigDirPath();

    if (this.environmentSpecificProjects) {
      let hostname = os.hostname().split('.').shift().toLowerCase();
      filename = `projects.${hostname}.cson`;
    }

    return `${filedir}/${filename}`;
  }

  readFile(callback) {
    fs.exists(this.file(), (exists) => {
      if (exists) {
        try {
          let projects = CSON.readFileSync(this.file()) || {};
          callback(projects);
        } catch (error) {
          const message = `Failed to load ${path.basename(this.file())}`;
          const detail = error.location != null ? error.stack : error.message;
          this.notifyFailure(message, detail);
        }
      } else {
        fs.writeFile(this.file(), '{}', () => callback({}));
      }
    });
  }

  writeFile(projects, callback) {
    CSON.writeFileSync(this.file(), projects);
    if (callback) {
      callback();
    }
  }

  notifyFailure(message, detail=null) {
    atom.notifications.addError(message, {
      detail: detail,
      dismissable: true
    });
  }
}
<commit_msg>Adds a check to make sure paths[i] is a string before using replace()<commit_after>'use babel';

import {Emitter} from 'atom';
import CSON from 'season';
import fs from 'fs';
import path from 'path';
import os from 'os';
import _ from 'underscore-plus';

export default class DB {
  constructor(searchKey=null, searchValue=null) {
    this.setSearchQuery(searchKey, searchValue);
    this.emitter = new Emitter();

    fs.exists(this.file(), (exists) => {
      if (exists) {
        this.observeProjects();
      } else {
        this.writeFile({});
      }
    });
  }

  get environmentSpecificProjects() {
    return atom.config.get('project-manager.environmentSpecificProjects');
  }

  setSearchQuery(searchKey=null, searchValue=null) {
    this.searchKey = searchKey;
    this.searchValue = searchValue;
  }

  find(callback) {
    this.readFile(results => {
      let found = false;
      let projects = [];
      let project = null;
      let result = null;
      let template = null;
      let key;

      for (key in results) {
        result = results[key];
        template = result.template || null;
        result._id = key;

        if (template && results[template] !== null) {
          result = _.deepExtend(result, results[template]);
        }

        for (let i in result.paths) {
          if (typeof result.paths[i] !== 'string') {
            continue;
          }

          result.paths[i] = result.paths[i].replace('~', os.homedir());
        }

        projects.push(result);
      }

      if (this.searchKey && this.searchValue) {
        for (key in projects) {
          project = projects[key];
          if (_.isEqual(project[this.searchKey], this.searchValue)) {
            found = project;
          }
        }
      } else {
        found = projects;
      }

      callback(found);
    });
  }

  add(props, callback) {
    this.readFile(projects => {
      const id = this.generateID(props.title);
      projects[id] = props;

      this.writeFile(projects, () => {
        atom.notifications.addSuccess(`${props.title} has been added`);
        callback(id);
      });
    });
  }

  update(props) {
    if (!props._id) {
      return false;
    }

    let project = null;
    let key;
    this.readFile(projects => {
      for (key in projects) {
        project = projects[key];
        if (key === props._id) {
          delete(props._id);
          projects[key] = props;
        }

        this.writeFile(projects);
      }
    });
  }

  delete(id, callback) {
    this.readFile(projects => {
      for (let key in projects) {
        if (key === id) {
          delete(projects[key]);
        }
      }

      this.writeFile(projects, () => {
        if (callback) {
          callback();
        }
      });
    });
  }

  onUpdate(callback) {
    this.emitter.on('db-updated', () => {
      this.find(callback);
    });
  }

  observeProjects() {
    if (this.fileWatcher) {
      this.fileWatcher.close();
    }

    try {
      this.fileWatcher = fs.watch(this.file(), () => {
        this.emitter.emit('db-updated');
      });
    } catch (error) {
      let url = 'https://github.com/atom/atom/blob/master/docs/';
      url += 'build-instructions/linux.md#typeerror-unable-to-watch-path';
      const filename = path.basename(this.file());
      const errorMessage = `Project Manager
Could not watch changes
        to ${filename}. Make sure you have permissions to ${this.file()}.
        On linux there can be problems with watch sizes.
        See  this document for more info.>`;
      this.notifyFailure(errorMessage);
    }
  }

  updateFile() {
    fs.exists(this.file(true), (exists) => {
      if (!exists) {
        this.writeFile({});
      }
    });
  }

  generateID(string) {
    return string.replace(/\s+/g, '').toLowerCase();
  }

  file() {
    let filename = 'projects.cson';
    const filedir = atom.getConfigDirPath();

    if (this.environmentSpecificProjects) {
      let hostname = os.hostname().split('.').shift().toLowerCase();
      filename = `projects.${hostname}.cson`;
    }

    return `${filedir}/${filename}`;
  }

  readFile(callback) {
    fs.exists(this.file(), (exists) => {
      if (exists) {
        try {
          let projects = CSON.readFileSync(this.file()) || {};
          callback(projects);
        } catch (error) {
          const message = `Failed to load ${path.basename(this.file())}`;
          const detail = error.location != null ? error.stack : error.message;
          this.notifyFailure(message, detail);
        }
      } else {
        fs.writeFile(this.file(), '{}', () => callback({}));
      }
    });
  }

  writeFile(projects, callback) {
    CSON.writeFileSync(this.file(), projects);
    if (callback) {
      callback();
    }
  }

  notifyFailure(message, detail=null) {
    atom.notifications.addError(message, {
      detail: detail,
      dismissable: true
    });
  }
}
<|endoftext|>


<commit_before>"use strict";
require('dotenv').load();

const pmongo = require('promised-mongo');

var db;

if(process.env.DBUSER && process.env.DBPASS){
    db = pmongo('mongodb://' + process.env.DBUSER + ':' + process.env.DBPASS + '@' + process.env.DBHOST + ':' + process.env.DBPORT + '/super-public', ['tasks', 'data']);
} else {
    db = pmongo('mongodb://' + process.env.DBHOST + ':' + process.env.DBPORT + '/super-public', ['tasks', 'data']);
}

exports.add = function add(spData){
    return db.data.insert(spData);
};

<commit_msg>Add db name as config<commit_after>"use strict";
require('dotenv').load();

const pmongo = require('promised-mongo');

var db;

if(process.env.DBUSER && process.env.DBPASS){
    db = pmongo('mongodb://' + process.env.DBUSER + ':' + process.env.DBPASS + '@' + process.env.DBHOST + ':' + process.env.DBPORT + '/' + process.env.DBNAME, ['tasks', 'data']);
} else {
    db = pmongo('mongodb://' + process.env.DBHOST + ':' + process.env.DBPORT + '/super-public', ['tasks', 'data']);
}

exports.add = function add(spData){
    return db.data.insert(spData);
};

<|endoftext|>


<commit_before>const logger = require(`${__dirname}/logger.js`),
	_ = require('lodash'),
	shuffle = require('shuffle-array'),
	knex = require('knex')({
		client: 'sqlite3',
		connection: {
			filename: `${__dirname}/../opr.sqlite`,
		},
		useNullAsDefault: true,
	}),
	Promise = require('bluebird');

function createDatabase() {
	const query = knex.schema.createTableIfNotExists('shows', (table) => {
		table.increments('id').primary();
		table.string('showName').notNullable();
		table.string('fileName').notNullable();
		table.string('filePath').notNullable();
		table.dateTime('addedTime').defaultTo(knex.fn.now()).notNullable();
		table.integer('playedCount').unsigned().defaultTo(0).notNullable();
		table.dateTime('lastPlayedTime').defaultTo(null);
		table.boolean('fileNotPlayable').defaultTo(false).notNullable();
		table.boolean('deleted').defaultTo(false).notNullable();
	});

	return query;
}

module.exports = {
	initDb: function() {
		const query = knex.select(1)
			.from('shows')
			.limit(1);

		return query.then(() => {
			return true;
		}).catch(() => {
			return createDatabase();
		});
	},

	getFromFilePath: function(episodePath) {
		const query = knex.select('*')
			.from('shows')
			.where('filePath', episodePath)
			.limit(1);

		return query.then((row) => {
			if (!Array.isArray(row) || row.length < 1) {
				return null;
			}

			return row[0];
		});
	},

	getAllActive: function() {
		return knex.select('*')
			.from('shows')
			.where('deleted', false);
	},

	add: function(showName, fileName, episodePath) {
		const insertData = {
			showName: showName,
			fileName: fileName,
			filePath: episodePath,
			deleted: false,
		};

		return knex.insert(insertData).into('shows');
	},

	delete: function(id) {
		return knex('shows').where('id', id).update({
			deleted: true,
		});
	},

	undelete: function(id) {
		return knex('shows').where('id', id).update({
			deleted: false,
		});
	},

	setLastPlayedTime: function(filePaths) {
		filePaths = (Array.isArray(filePaths) ? filePaths : [filePaths]);

		return knex('shows').whereIn('filePath', filePaths).update({
			lastPlayedTime: knex.raw('datetime()'),
		});
	},

	setFileNotPlayable: function(filePath) {
		return knex('shows').where('filePath', filePath).update({
			fileNotPlayable: true,
		});
	},

	fileConvertedToPlayable: function(id, filePath) {
		return knex('shows').where('id', id).update({
			filePath: filePath,
			fileNotPlayable: false,
			lastPlayedTime: null,
		});
	},

	getNotPlayableFiles: function() {
		return knex.select([
				'id',
				'filePath',
			])
			.from('shows')
			.where('fileNotPlayable', true)
			.andWhere('deleted', false);
	},

	selectRandom: function(showNames, amount) {
		// TODO! Move most of this logic out to the shows.js file!

		// if we don't care about show names, just get random shows!
		if (!showNames || !showNames.length) {
			return knex.select()
				.from('shows')
				.where('deleted', false)
				.andWhere('fileNotPlayable', false)
				.orderByRaw('lastPlayedTime ASC, RANDOM()')
				.limit(amount);
		}

		shuffle(showNames);

		// only get 1 of each show, unless amount > count of shows asked for
		if (showNames.length > amount) {
			let randomShowNames = [];

			for (let i = 0; i < amount; i++) {
				randomShowNames[i] = showNames[i];
			}

			showNames = randomShowNames;
		} else if (showNames.length < amount) {
			// if we have less shows than our amount, increase size till we are ok
			const missingShowsCount = amount - showNames.length;

			for (let i = 0; i < missingShowsCount; i++) {
				showNames.push(showNames[Math.floor(Math.random() * showNames.length)]);
			}
		}

		// TODO, we should execute the promises 1 at a time so we get the previously
		// selected ids. If we have the same show more than once, we don't want to
		// get the exact same show out multiple times
		let randomShowPromises = [];

		_.forEach(showNames, (showName) => {
			const select = knex.select()
				.from('shows')
				.where({
					deleted: false,
					showName: showName,
				})
				.orderByRaw('lastPlayedTime ASC')
				.limit(5);

			randomShowPromises.push(select);
		});

		return Promise.all(randomShowPromises).then((shows) => {
			let randomShows = [];

			_.forEach(shows, (episodes) => {
				if (!Array.isArray(episodes) || episodes.length < 1) {
					return;
				}

				const episode = episodes[Math.floor(Math.random() * episodes.length)];

				randomShows.push(episode);
			});

			return randomShows;
		});
	},

	getUniqueShowNames: function() {
		const sql = knex.select(knex.raw('DISTINCT showName'))
			.from('shows')
			.where('deleted', false);

		return sql.then((rows) => {
			let showNames = [];

			_.forEach(rows, (row) => {
				showNames.push(row.showName);
			});

			return showNames;
		});
	},
};
<commit_msg>Added history table<commit_after>const logger = require(`${__dirname}/logger.js`),
	_ = require('lodash'),
	shuffle = require('shuffle-array'),
	knex = require('knex')({
		client: 'sqlite3',
		connection: {
			filename: `${__dirname}/../opr.sqlite`,
		},
		useNullAsDefault: true,
	}),
	Promise = require('bluebird');

function createShowsTable() {
	return knex.schema.createTableIfNotExists('shows', (table) => {
		table.increments('id').primary();
		table.string('showName').notNullable();
		table.string('fileName').notNullable();
		table.string('filePath').notNullable();
		table.dateTime('addedTime').defaultTo(knex.fn.now()).notNullable();
		table.integer('playedCount').unsigned().defaultTo(0).notNullable();
		table.dateTime('lastPlayedTime').defaultTo(null);
		table.boolean('fileNotPlayable').defaultTo(false).notNullable();
		table.boolean('deleted').defaultTo(false).notNullable();
	});
}

function createPlayHistoryTable() {
	return knex.schema.createTableIfNotExists('playHistory', (table) => {
		table.increments('id').primary();
		table.string('showId').notNullable();
		table.dateTime('playedTime').defaultTo(knex.fn.now()).notNullable();
	});
}

module.exports = {
	initDb: function() {
		const query = knex.select(1)
			.from('shows')
			.limit(1);

		return query.then(() => {
			return true;
		}).catch(() => {
			return createShowsTable();
		}).then(() => {
			return knex.select(1)
				.from('playHistory')
				.limit(1);
		})
		.catch(() => {
			return createPlayHistoryTable();
		}).then(() => {
			return true;
		});
	},

	getFromFilePath: function(episodePath) {
		const query = knex.select('*')
			.from('shows')
			.where('filePath', episodePath)
			.limit(1);

		return query.then((row) => {
			if (!Array.isArray(row) || row.length < 1) {
				return null;
			}

			return row[0];
		});
	},

	getAllActive: function() {
		return knex.select('*')
			.from('shows')
			.where('deleted', false);
	},

	add: function(showName, fileName, episodePath) {
		const insertData = {
			showName: showName,
			fileName: fileName,
			filePath: episodePath,
			deleted: false,
		};

		return knex.insert(insertData).into('shows');
	},

	delete: function(id) {
		return knex('shows').where('id', id).update({
			deleted: true,
		});
	},

	undelete: function(id) {
		return knex('shows').where('id', id).update({
			deleted: false,
		});
	},

	setLastPlayedTime: function(filePaths) {
		filePaths = (Array.isArray(filePaths) ? filePaths : [filePaths]);

		const pathIds = knex.select([
			'id',
			'filePath',
		]).from('shows').whereIn('filePath', filePaths);

		return pathIds.then((data) => {
			const historyInserts = [];

			data.forEach((row) => {
				const insert = knex.insert({
					showId: row.id,
				}).into('playHistory');

				historyInserts.push(insert);
			});

			return Promise.all(historyInserts);
		}).then(() => {
			return knex('shows').whereIn('filePath', filePaths).update({
				lastPlayedTime: knex.raw('datetime()'),
				playedCount: knex.raw('(playedCount + 1)'),
			})
		});
	},

	setFileNotPlayable: function(filePath) {
		return knex('shows').where('filePath', filePath).update({
			fileNotPlayable: true,
		});
	},

	fileConvertedToPlayable: function(id, filePath) {
		return knex('shows').where('id', id).update({
			filePath: filePath,
			fileNotPlayable: false,
			lastPlayedTime: null,
		});
	},

	getNotPlayableFiles: function() {
		return knex.select([
				'id',
				'filePath',
			])
			.from('shows')
			.where('fileNotPlayable', true)
			.andWhere('deleted', false);
	},

	selectRandom: function(showNames, amount) {
		// TODO! Move most of this logic out to the shows.js file!

		// if we don't care about show names, just get random shows!
		if (!showNames || !showNames.length) {
			return knex.select()
				.from('shows')
				.where('deleted', false)
				.andWhere('fileNotPlayable', false)
				.orderByRaw('lastPlayedTime ASC, RANDOM()')
				.limit(amount);
		}

		shuffle(showNames);

		// only get 1 of each show, unless amount > count of shows asked for
		if (showNames.length > amount) {
			let randomShowNames = [];

			for (let i = 0; i < amount; i++) {
				randomShowNames[i] = showNames[i];
			}

			showNames = randomShowNames;
		} else if (showNames.length < amount) {
			// if we have less shows than our amount, increase size till we are ok
			const missingShowsCount = amount - showNames.length;

			for (let i = 0; i < missingShowsCount; i++) {
				showNames.push(showNames[Math.floor(Math.random() * showNames.length)]);
			}
		}

		// TODO, we should execute the promises 1 at a time so we get the previously
		// selected ids. If we have the same show more than once, we don't want to
		// get the exact same show out multiple times
		let randomShowPromises = [];

		_.forEach(showNames, (showName) => {
			const select = knex.select()
				.from('shows')
				.where({
					deleted: false,
					showName: showName,
				})
				.orderByRaw('lastPlayedTime ASC')
				.limit(5);

			randomShowPromises.push(select);
		});

		return Promise.all(randomShowPromises).then((shows) => {
			let randomShows = [];

			_.forEach(shows, (episodes) => {
				if (!Array.isArray(episodes) || episodes.length < 1) {
					return;
				}

				const episode = episodes[Math.floor(Math.random() * episodes.length)];

				randomShows.push(episode);
			});

			return randomShows;
		});
	},

	getUniqueShowNames: function() {
		const sql = knex.select(knex.raw('DISTINCT showName'))
			.from('shows')
			.where('deleted', false);

		return sql.then((rows) => {
			let showNames = [];

			_.forEach(rows, (row) => {
				showNames.push(row.showName);
			});

			return showNames;
		});
	},
};
<|endoftext|>