<commit_before>/*
 * Copyright(c) 2014, Unional (https://github.com/unional)
 * @license Licensed under the MIT License (https://github.com/unional/unional/LICENSE)).
 * @version 0.4.5
 * Created by unional on 9/21/14.
 */
//noinspection ThisExpressionReferencesGlobalObjectJS
(function(root) {
    "use strict";

    var contexts = {};

    /**
     * This counter is used to create unique contexts.
     * @type {number}
     */
    var contextCount = 0;

    /**
     * Global definitions of modules referenced using umd.require().
     * This is used to re-run the definition in order to stub the dependencies.
     */
    var definitions = {};

    // region # getParamNames helper function
    var STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg;
    var ARGUMENT_NAMES = /([^\s,]+)/g;

    /**
     * Gets parameter names of the specified function
     * @param func
     * @returns {string[]}
     */
    function getParamNames(func) {
        var fnStr = func.toString().replace(STRIP_COMMENTS, '');
        var result = fnStr.slice(fnStr.indexOf('(') + 1, fnStr.indexOf(')')).match(ARGUMENT_NAMES);
        if (result === null) {
            result = [];
        }
        return result
    }

    // endregion

    //noinspection JSUnresolvedVariable
    if (typeof global !== "undefined") {
        // node environment use global as root.
        //noinspection JSUnresolvedVariable
        root = global;
    }

    function wrapRequire(require, mapping) {
        return function(moduleName) {
            return require(mapping[moduleName] || moduleName);
        };
    }

    function wrapDefine(define, require, mapping) {
        return function() {
            if (arguments.length > 0 && typeof arguments[0] === "function") {
                var definitionMethod = arguments[0];
                var params = getParamNames(definitionMethod);
                if (params.length > 0 && params[0] === "require") {
                    var currentContext = require.s.contexts._;

                    // wrap the makeRequire method to inject the wrapped require method.
                    var makeRequire = currentContext.makeRequire;
                    currentContext.makeRequire = function() {
                        var localRequire = makeRequire.apply(currentContext, arguments);
                        return wrapRequire(localRequire, mapping);
                    };

                    // Wrap the definition method so that I can restore the makeRequire method
                    // once the define() has everything setup.
                    // requireJS looks at the params length to determine whether it is
                    // function (require) or function (require, exports, module)
                    // If params only have 'require', it will only load 'require' using deps.
                    // Therefore I mimic it here so that requireJS will invoke the same logic.
                    arguments[0] = params.length === 1 ?
                                   function(require) {
                                       currentContext.makeRequire = makeRequire;
                                       return definitionMethod.apply(currentContext, arguments)
                                   } :
                                   function(require, exports, module) {
                                       currentContext.makeRequire = makeRequire;
                                       return definitionMethod.apply(currentContext, arguments)
                                   };
                }
            }
            define.apply(this, arguments);
        }
    }

    function deepMerge(target, source) {
        var result = {};
        var key;

        if (target && typeof target === 'object') {
            for (key in target) {
                if (target.hasOwnProperty(key)) {
                    result[key] = target[key];
                }
            }
        }

        for (key in source) {
            if (source.hasOwnProperty(key)) {
                var value = source[key];
                if (typeof value !== 'object' || !value) {
                    result[key] = value;
                }
                else {
                    if (!target[key]) {
                        result[key] = value;
                    }
                    else {
                        result[key] = deepMerge(target[key], value);
                    }
                }
            }
        }

        return result;
    }

    function convertToBrowserGlobalIdentifier(dep) {
        return dep.replace(/\./g, '/');
    }

    /**
     * Require dependencies while injects specified stubs.
     * @param {[]} deps Dependencies
     * @param {object} stubs Stubs `{ "moduleA": stubA }`. Can be null, undefined, or empty object.
     * In that case the deps will still be reloaded.
     * @param {function} callback The callback function.
     * @param {function} [errback] The error back function.
     * @returns {Function|*}
     */
    function stubRequire(deps, stubs, callback, errback) {
        if (!Array.isArray(deps)) {
            throw new Error("Dependencies must be an array.");
        }

        var require = umd.globalRequire;
        if (typeof require === "undefined" || require === umd.require) {
            // browser global
            contextCount++;
            var stubContext = 'stub' + contextCount;
            var context = contexts[stubContext] = new Context({
                stubs: stubs,
                mapping: contexts.default.config.mapping,
                paths: contexts.default.config.paths
            });

            context.setReloadTargets(deps);

            context.require(deps, callback, errback);
        }
        else if (require.defined) {
            // require.js
            var map = {};
            var hasStubs = false;
            for (var key in stubs) {
                if (stubs.hasOwnProperty(key)) {
                    hasStubs = true;
                    var stubName = 'stub' + key + contextCount;
                    map[key] = stubName;
                    (function(key) {
                        var value = stubs[key];
                        define(stubName, [], function() {
                            return value;
                        });
                    }(key))
                }
            }

            if (hasStubs) {
                contextCount++;
                var contextName = "context_" + contextCount;
                var config = deepMerge({ context: contextName}, require.s.contexts._.config);

                if (hasStubs) {
                    config.map = {
                        "*": map
                    };
                }

                var result = require.config(config);

                var parentDefined = require.s.contexts._.defined;
                for (var m in parentDefined) {
                    if (parentDefined.hasOwnProperty(m) && !map[m] && deps.indexOf(m) === -1) {
                        require.s.contexts[contextName].defined[m] = parentDefined[m];
                    }
                }

                result(deps, callback, errback);
            }
            else {
                // There are no stubs. Remove cache and reload the modules in deps.
                for (var dep in deps) {
                    require.undef(deps[dep]);
                }

                require(deps, callback, errback);
            }
        }
        else {
            // node
            throw new Error("stubRequire not implemented for node.js yet.");
        }
    }

    function Context(config) {
        this.reloadTargets = [];
        this.modules = {};
        this.config = deepMerge({mapping: {}, paths: {}}, config);

        var self = this;
        /**
         * A simple stub for requireJS and commonJS require function.
         * This is use to support universal module definition (umd) for browser globals code.
         * @param {string|string[]} moduleName Name of the module.
         * @param {function} [callback] Function to call after resolving the module.
         * @param {function} [errback] Error back function.
         * @returns {*} The target module if found; otherwise, undefined.
         */
        this.require = function require(moduleName, callback, errback) {
            var error;
            if (!moduleName) {
                error = new Error("moduleName can't be empty");
                if (errback) {
                    errback(error);
                    return;
                }
                else {
                    throw error;
                }
            }

            var module;
            try {
                if (Array.isArray(moduleName)) {
                    var results = moduleName.map(function(item) {
                        return self.resolveModule(item);
                    });

                    if (results && callback) {
                        callback.apply(this, results);
                    }
                }
                else {
                    module = self.resolveModule(moduleName);

                    if (module && callback) {
                        callback(module);
                    }

                    return module;
                }
            }
            catch (error) {
                if (errback) {
                    errback(error);
                }

                throw error;
            }
        };
        /**
         * Config require similar to requireJS.
         * @param {object} option Config option.
         * @param {string} [option.context] Context name. If not specified, it modifies the default context.
         * @param {object} [option.mapping] Module name mapping. "func": "sampleModules/umd/defineFunction"
         * @param {object} [option.paths] Mapping for the FIRST component of the module name. ".": "sampleModules/umd"
         * @returns {*}
         */
        this.require.config = function requireConfig(option) {
            var context = contexts.default;
            if (option.context) {
                context = contexts[option.context];
                if (!context) {
                    context = contexts[option.context] = new Context(option);
                }
                else {
                    context.updateConfig(option);
                }
            }
            else {
                context.updateConfig(option);
            }

            return context.require;
        };
        this.createDefine = function createDefine(browserGlobalIdentifier, mapping) {
            return function browserGlobalDefine(definition) {
                var module = {exports: {}};
                var result;
                if (typeof definition === "object") {
                    result = definition;
                }
                else {
                    var localRequire = (mapping) ? wrapRequire(self.require, mapping) : self.require;
                    result = definition(localRequire, module.exports, module);
                    result = (typeof result !== 'undefined') ? result : module.exports;
                }

                if (browserGlobalIdentifier) {
                    var defId = convertToBrowserGlobalIdentifier(browserGlobalIdentifier);
                    definitions[defId] = definition;

                    if (self.reloadTargets.indexOf(defId) === -1) {
                        var terms = browserGlobalIdentifier.split(/[.\/]/);
                        var id = terms.pop();
                        var base = umd.ns(terms.join("."));
                        base[id] = result;
                    }

                    self.modules[defId] = result;
                }
            };
        };
    }

    Context.prototype.updateConfig = function updateConfig(config) {
        this.config = deepMerge(this.config, config);
    };

    Context.prototype.setReloadTargets = function setReloadTargets(deps) {
        this.reloadTargets = deps.map(function(dep) {
            return convertToBrowserGlobalIdentifier(dep);
        });
    };

    Context.prototype.resolveModule = function resolveModule(moduleName) {
        var id = convertToBrowserGlobalIdentifier(moduleName);
        var stubs = this.config.stubs || {};
        var mapping = this.config.mapping || {};
        var paths = this.config.paths || {};

        if (stubs[id]) {
            return stubs[id];
        }

        if (this.reloadTargets.indexOf(id) !== -1) {

            var definition = definitions[id];
            if (definition) {
                this.createDefine(id)(definition);
            }
        }

        if (this.modules[id]) {
            return this.modules[id];
        }

        moduleName = mapping[moduleName] || moduleName;

        var parts = moduleName.split('!', 2);
        var arg = undefined;
        if (parts.length == 2) {
            moduleName = parts[0];
            if (parts[1]) {
                arg = parts[1];
            }
        }

        var names = moduleName.split("/");
        var name = names.shift();

        if (paths[name]) {
            var subNames = paths[name].split("/");
            names = subNames.concat(names);
            name = names.shift();
        }

        var module = root[name];
        while (module && names.length) {
            name = names.shift();
            module = module[name];
        }

        if (parts.length == 2) {
            module = module(arg);
        }

        return module;
    };

    contexts.default = new Context({mapping: {}, paths: {}});

    // ********** PUBLIC API ************

    /**
     * Universal module definition method. Use this method to simplify module definition.
     * To use this method, write your module as follow:
     *

     *     umd(function(define) {
      *         define(function(require, exports, module) {
      *             // Your code here
      *         });
      *    }, "MyCompany.MyProduct.MySection.MyComponentName", require, exports, module);
     *

     * Notice the last three parameters are require, exports and module. Keep them as is.
     * They are used to round tripped back to your code for node.js.
     *
     * For require.js, load this umd module before other modules
     * (at common/main.js where you write require.config({...}))
     * For r.js, you can use onBuildRead to trim out the umd code, just leaving the define call in-place.
     * @param factory
     * @param {string|null} browserGlobalIdentifier Identifier for browser global. Pass in falsy value to omit browser global definition.
     * @param require Round trip require function (pre-defined)
     * @param exports Round trip exports object (pre-defined)
     * @param module Round trip module object (pre-defined)
     * @param {{nodeJS: {}, requireJS: {}, browserGlobal: {}}} [mappings] Optional mapping if the require identifiers
     * are different in different environments.
     */
    var umd = function(factory, browserGlobalIdentifier, require, exports, module, mappings) {
        if (umd.isRequireJS()) {
            var localDefine = (mappings && mappings.requireJS) ?
                              wrapDefine(define, require, mappings.requireJS) : define;
            factory(localDefine);
        }
        else if (umd.isNodeJS()) {
            // Node (not CommonJS because module.exports does not conform)
            factory(function(definition) {
                var localRequire = (mappings && mappings.nodeJS) ? wrapRequire(require, mappings.nodeJS) : require;

                var result = (typeof definition === "object") ? definition : definition(localRequire, exports, module);

                if (typeof result !== "undefined") {
                    module.exports = result;
                }
            });
        }
        else {
            // browser global.
            if (mappings && mappings.browserGlobal) {
                factory(umd.createDefine(browserGlobalIdentifier, mappings.browserGlobal));
            }
            else {
                factory(umd.createDefine(browserGlobalIdentifier));
            }
        }
    };

    /**
     * Creates namespaces to be used for scoping variables and classes so that they are not global.
     * Specifying the last node of a namespace implicitly creates all other nodes.
     * Similar to Ext.ns() without the dependency.
     * @param {...string} namespace
     * @return {Object} The namespace object. (If multiple arguments are passed, this will be the last namespace created)
     * @method namespace
     */
    umd.namespace = function namespace(namespace) {
        var result;
        for (var i = 0, len = arguments.length; i < len; i++) {
            namespace = arguments[i];
            var components = namespace.split(/[.\/]/);

            var component = components.shift();
            result = root[component] = root[component] || {};

            while (components.length) {
                component = components.shift();
                result = result[component] = result[component] || {};
            }
        }

        return result;
    };
    umd.ns = umd.namespace;

    /**
     * Determine whether the environment is require.js
     * @returns {boolean}
     */
    umd.isRequireJS = function isRequireJS() {
        return typeof define === "function" && define.amd != undefined;
    };
    /**
     * Determine whether the environment is node.js
     * Technically this is also true for CommonJS but I named it as NodeJS because
     * I don't fully support CommonJS yet.
     * @returns {boolean}
     */
    umd.isNodeJS = function isNodeJS() {
        return typeof require === "function" &&
               typeof exports === 'object' &&
               typeof module === 'object';
    };

    /**
     * Determine whether the environment is browser global.
     * @returns {boolean}
     */
    umd.isBrowserGlobal = function isBrowserGlobal() {
        return !umd.isRequireJS() && !umd.isNodeJS();
    };

    umd.stubRequire = stubRequire;
    // require === func meaning it is node environment
    /**
     * Reference the global require function.
     * Change this if you want to use a different require function (e.g. umd.require).
     * This is typically used only for testing purpose.
     * @type {require|*}
     */
    umd.globalRequire = (typeof require === "function") ? require : root.require;

    umd.require = contexts.default.require;
    umd.createDefine = contexts.default.createDefine;

    //noinspection JSUnresolvedVariable
    if (typeof global !== 'undefined') {
        // Node js
        //noinspection JSUnresolvedVariable
        global.umd = umd;
        module.exports = umd;
    }
    else {
        root.umd = umd;
        // define require, exports, and module so that they won't cause
        // ReferenceError in the module for browser global scenario.
        root.require = root.require || undefined;
        root.module = root.module || undefined;
        root.exports = root.exports || undefined;
    }
}(this));<commit_msg>deepMerge is too aggressive for merging config in stubRequire. That corrupt the logic in requireJS<commit_after>/*
 * Copyright(c) 2014, Unional (https://github.com/unional)
 * @license Licensed under the MIT License (https://github.com/unional/unional/LICENSE)).
 * @version 0.4.5
 * Created by unional on 9/21/14.
 */
//noinspection ThisExpressionReferencesGlobalObjectJS
(function(root) {
    "use strict";

    var contexts = {};

    /**
     * This counter is used to create unique contexts.
     * @type {number}
     */
    var contextCount = 0;

    /**
     * Global definitions of modules referenced using umd.require().
     * This is used to re-run the definition in order to stub the dependencies.
     */
    var definitions = {};

    // region # getParamNames helper function
    var STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg;
    var ARGUMENT_NAMES = /([^\s,]+)/g;

    /**
     * Gets parameter names of the specified function
     * @param func
     * @returns {string[]}
     */
    function getParamNames(func) {
        var fnStr = func.toString().replace(STRIP_COMMENTS, '');
        var result = fnStr.slice(fnStr.indexOf('(') + 1, fnStr.indexOf(')')).match(ARGUMENT_NAMES);
        if (result === null) {
            result = [];
        }
        return result
    }

    // endregion

    //noinspection JSUnresolvedVariable
    if (typeof global !== "undefined") {
        // node environment use global as root.
        //noinspection JSUnresolvedVariable
        root = global;
    }

    function wrapRequire(require, mapping) {
        return function(moduleName) {
            return require(mapping[moduleName] || moduleName);
        };
    }

    function wrapDefine(define, require, mapping) {
        return function() {
            if (arguments.length > 0 && typeof arguments[0] === "function") {
                var definitionMethod = arguments[0];
                var params = getParamNames(definitionMethod);
                if (params.length > 0 && params[0] === "require") {
                    var currentContext = require.s.contexts._;

                    // wrap the makeRequire method to inject the wrapped require method.
                    var makeRequire = currentContext.makeRequire;
                    currentContext.makeRequire = function() {
                        var localRequire = makeRequire.apply(currentContext, arguments);
                        return wrapRequire(localRequire, mapping);
                    };

                    // Wrap the definition method so that I can restore the makeRequire method
                    // once the define() has everything setup.
                    // requireJS looks at the params length to determine whether it is
                    // function (require) or function (require, exports, module)
                    // If params only have 'require', it will only load 'require' using deps.
                    // Therefore I mimic it here so that requireJS will invoke the same logic.
                    arguments[0] = params.length === 1 ?
                                   function(require) {
                                       currentContext.makeRequire = makeRequire;
                                       return definitionMethod.apply(currentContext, arguments)
                                   } :
                                   function(require, exports, module) {
                                       currentContext.makeRequire = makeRequire;
                                       return definitionMethod.apply(currentContext, arguments)
                                   };
                }
            }
            define.apply(this, arguments);
        }
    }

    function deepMerge(target, source) {
        var result = {};
        var key;

        if (target && typeof target === 'object') {
            for (key in target) {
                if (target.hasOwnProperty(key)) {
                    result[key] = target[key];
                }
            }
        }

        for (key in source) {
            if (source.hasOwnProperty(key)) {
                var value = source[key];
                if (typeof value !== 'object' || !value) {
                    result[key] = value;
                }
                else {
                    if (!target[key]) {
                        result[key] = value;
                    }
                    else {
                        result[key] = deepMerge(target[key], value);
                    }
                }
            }
        }

        return result;
    }

    function convertToBrowserGlobalIdentifier(dep) {
        return dep.replace(/\./g, '/');
    }

    /**
     * Require dependencies while injects specified stubs.
     * @param {[]} deps Dependencies
     * @param {object} stubs Stubs `{ "moduleA": stubA }`. Can be null, undefined, or empty object.
     * In that case the deps will still be reloaded.
     * @param {function} callback The callback function.
     * @param {function} [errback] The error back function.
     * @returns {Function|*}
     */
    function stubRequire(deps, stubs, callback, errback) {
        if (!Array.isArray(deps)) {
            throw new Error("Dependencies must be an array.");
        }

        var require = umd.globalRequire;
        if (typeof require === "undefined" || require === umd.require) {
            // browser global
            contextCount++;
            var stubContext = 'stub' + contextCount;
            var context = contexts[stubContext] = new Context({
                stubs: stubs,
                mapping: contexts.default.config.mapping,
                paths: contexts.default.config.paths
            });

            context.setReloadTargets(deps);

            context.require(deps, callback, errback);
        }
        else if (require.defined) {
            // require.js
            var map = {};
            var hasStubs = false;
            for (var key in stubs) {
                if (stubs.hasOwnProperty(key)) {
                    hasStubs = true;
                    var stubName = 'stub' + key + contextCount;
                    map[key] = stubName;
                    (function(key) {
                        var value = stubs[key];
                        define(stubName, [], function() {
                            return value;
                        });
                    }(key))
                }
            }

            if (hasStubs) {
                contextCount++;
                var contextName = "context_" + contextCount;
                var _config = require.s.contexts._.config;
                var config = {
                    context: contextName,
                    baseUrl: _config.baseUrl,
                    paths: _config.paths,
                    pkgs: _config.pkgs,
                    shim: _config.shim
                };

                if (hasStubs) {
                    config.map = {
                        "*": map
                    };
                }

                var result = require.config(config);

                var parentDefined = require.s.contexts._.defined;
                for (var m in parentDefined) {
                    if (parentDefined.hasOwnProperty(m) && !map[m] && deps.indexOf(m) === -1) {
                        require.s.contexts[contextName].defined[m] = parentDefined[m];
                    }
                }

                result(deps, callback, errback);
            }
            else {
                // There are no stubs. Remove cache and reload the modules in deps.
                for (var dep in deps) {
                    require.undef(deps[dep]);
                }

                require(deps, callback, errback);
            }
        }
        else {
            // node
            throw new Error("stubRequire not implemented for node.js yet.");
        }
    }

    function Context(config) {
        this.reloadTargets = [];
        this.modules = {};
        this.config = deepMerge({mapping: {}, paths: {}}, config);

        var self = this;
        /**
         * A simple stub for requireJS and commonJS require function.
         * This is use to support universal module definition (umd) for browser globals code.
         * @param {string|string[]} moduleName Name of the module.
         * @param {function} [callback] Function to call after resolving the module.
         * @param {function} [errback] Error back function.
         * @returns {*} The target module if found; otherwise, undefined.
         */
        this.require = function require(moduleName, callback, errback) {
            var error;
            if (!moduleName) {
                error = new Error("moduleName can't be empty");
                if (errback) {
                    errback(error);
                    return;
                }
                else {
                    throw error;
                }
            }

            var module;
            try {
                if (Array.isArray(moduleName)) {
                    var results = moduleName.map(function(item) {
                        return self.resolveModule(item);
                    });

                    if (results && callback) {
                        callback.apply(this, results);
                    }
                }
                else {
                    module = self.resolveModule(moduleName);

                    if (module && callback) {
                        callback(module);
                    }

                    return module;
                }
            }
            catch (error) {
                if (errback) {
                    errback(error);
                }

                throw error;
            }
        };
        /**
         * Config require similar to requireJS.
         * @param {object} option Config option.
         * @param {string} [option.context] Context name. If not specified, it modifies the default context.
         * @param {object} [option.mapping] Module name mapping. "func": "sampleModules/umd/defineFunction"
         * @param {object} [option.paths] Mapping for the FIRST component of the module name. ".": "sampleModules/umd"
         * @returns {*}
         */
        this.require.config = function requireConfig(option) {
            var context = contexts.default;
            if (option.context) {
                context = contexts[option.context];
                if (!context) {
                    context = contexts[option.context] = new Context(option);
                }
                else {
                    context.updateConfig(option);
                }
            }
            else {
                context.updateConfig(option);
            }

            return context.require;
        };
        this.createDefine = function createDefine(browserGlobalIdentifier, mapping) {
            return function browserGlobalDefine(definition) {
                var module = {exports: {}};
                var result;
                if (typeof definition === "object") {
                    result = definition;
                }
                else {
                    var localRequire = (mapping) ? wrapRequire(self.require, mapping) : self.require;
                    result = definition(localRequire, module.exports, module);
                    result = (typeof result !== 'undefined') ? result : module.exports;
                }

                if (browserGlobalIdentifier) {
                    var defId = convertToBrowserGlobalIdentifier(browserGlobalIdentifier);
                    definitions[defId] = definition;

                    if (self.reloadTargets.indexOf(defId) === -1) {
                        var terms = browserGlobalIdentifier.split(/[.\/]/);
                        var id = terms.pop();
                        var base = umd.ns(terms.join("."));
                        base[id] = result;
                    }

                    self.modules[defId] = result;
                }
            };
        };
    }

    Context.prototype.updateConfig = function updateConfig(config) {
        this.config = deepMerge(this.config, config);
    };

    Context.prototype.setReloadTargets = function setReloadTargets(deps) {
        this.reloadTargets = deps.map(function(dep) {
            return convertToBrowserGlobalIdentifier(dep);
        });
    };

    Context.prototype.resolveModule = function resolveModule(moduleName) {
        var id = convertToBrowserGlobalIdentifier(moduleName);
        var stubs = this.config.stubs || {};
        var mapping = this.config.mapping || {};
        var paths = this.config.paths || {};

        if (stubs[id]) {
            return stubs[id];
        }

        if (this.reloadTargets.indexOf(id) !== -1) {

            var definition = definitions[id];
            if (definition) {
                this.createDefine(id)(definition);
            }
        }

        if (this.modules[id]) {
            return this.modules[id];
        }

        moduleName = mapping[moduleName] || moduleName;

        var parts = moduleName.split('!', 2);
        var arg = undefined;
        if (parts.length == 2) {
            moduleName = parts[0];
            if (parts[1]) {
                arg = parts[1];
            }
        }

        var names = moduleName.split("/");
        var name = names.shift();

        if (paths[name]) {
            var subNames = paths[name].split("/");
            names = subNames.concat(names);
            name = names.shift();
        }

        var module = root[name];
        while (module && names.length) {
            name = names.shift();
            module = module[name];
        }

        if (parts.length == 2) {
            module = module(arg);
        }

        return module;
    };

    contexts.default = new Context({mapping: {}, paths: {}});

    // ********** PUBLIC API ************

    /**
     * Universal module definition method. Use this method to simplify module definition.
     * To use this method, write your module as follow:
     *

     *     umd(function(define) {
      *         define(function(require, exports, module) {
      *             // Your code here
      *         });
      *    }, "MyCompany.MyProduct.MySection.MyComponentName", require, exports, module);
     *

     * Notice the last three parameters are require, exports and module. Keep them as is.
     * They are used to round tripped back to your code for node.js.
     *
     * For require.js, load this umd module before other modules
     * (at common/main.js where you write require.config({...}))
     * For r.js, you can use onBuildRead to trim out the umd code, just leaving the define call in-place.
     * @param factory
     * @param {string|null} browserGlobalIdentifier Identifier for browser global. Pass in falsy value to omit browser global definition.
     * @param require Round trip require function (pre-defined)
     * @param exports Round trip exports object (pre-defined)
     * @param module Round trip module object (pre-defined)
     * @param {{nodeJS: {}, requireJS: {}, browserGlobal: {}}} [mappings] Optional mapping if the require identifiers
     * are different in different environments.
     */
    var umd = function(factory, browserGlobalIdentifier, require, exports, module, mappings) {
        if (umd.isRequireJS()) {
            var localDefine = (mappings && mappings.requireJS) ?
                              wrapDefine(define, require, mappings.requireJS) : define;
            factory(localDefine);
        }
        else if (umd.isNodeJS()) {
            // Node (not CommonJS because module.exports does not conform)
            factory(function(definition) {
                var localRequire = (mappings && mappings.nodeJS) ? wrapRequire(require, mappings.nodeJS) : require;

                var result = (typeof definition === "object") ? definition : definition(localRequire, exports, module);

                if (typeof result !== "undefined") {
                    module.exports = result;
                }
            });
        }
        else {
            // browser global.
            if (mappings && mappings.browserGlobal) {
                factory(umd.createDefine(browserGlobalIdentifier, mappings.browserGlobal));
            }
            else {
                factory(umd.createDefine(browserGlobalIdentifier));
            }
        }
    };

    /**
     * Creates namespaces to be used for scoping variables and classes so that they are not global.
     * Specifying the last node of a namespace implicitly creates all other nodes.
     * Similar to Ext.ns() without the dependency.
     * @param {...string} namespace
     * @return {Object} The namespace object. (If multiple arguments are passed, this will be the last namespace created)
     * @method namespace
     */
    umd.namespace = function namespace(namespace) {
        var result;
        for (var i = 0, len = arguments.length; i < len; i++) {
            namespace = arguments[i];
            var components = namespace.split(/[.\/]/);

            var component = components.shift();
            result = root[component] = root[component] || {};

            while (components.length) {
                component = components.shift();
                result = result[component] = result[component] || {};
            }
        }

        return result;
    };
    umd.ns = umd.namespace;

    /**
     * Determine whether the environment is require.js
     * @returns {boolean}
     */
    umd.isRequireJS = function isRequireJS() {
        return typeof define === "function" && define.amd != undefined;
    };
    /**
     * Determine whether the environment is node.js
     * Technically this is also true for CommonJS but I named it as NodeJS because
     * I don't fully support CommonJS yet.
     * @returns {boolean}
     */
    umd.isNodeJS = function isNodeJS() {
        return typeof require === "function" &&
               typeof exports === 'object' &&
               typeof module === 'object';
    };

    /**
     * Determine whether the environment is browser global.
     * @returns {boolean}
     */
    umd.isBrowserGlobal = function isBrowserGlobal() {
        return !umd.isRequireJS() && !umd.isNodeJS();
    };

    umd.stubRequire = stubRequire;
    // require === func meaning it is node environment
    /**
     * Reference the global require function.
     * Change this if you want to use a different require function (e.g. umd.require).
     * This is typically used only for testing purpose.
     * @type {require|*}
     */
    umd.globalRequire = (typeof require === "function") ? require : root.require;

    umd.require = contexts.default.require;
    umd.createDefine = contexts.default.createDefine;

    //noinspection JSUnresolvedVariable
    if (typeof global !== 'undefined') {
        // Node js
        //noinspection JSUnresolvedVariable
        global.umd = umd;
        module.exports = umd;
    }
    else {
        root.umd = umd;
        // define require, exports, and module so that they won't cause
        // ReferenceError in the module for browser global scenario.
        root.require = root.require || undefined;
        root.module = root.module || undefined;
        root.exports = root.exports || undefined;
    }
}(this));<|endoftext|>


<commit_before>var sys = require('pex-sys');
var glu = require('pex-glu');
var geom = require('pex-geom');
var materials = require('pex-materials');
var color = require('pex-color');
var gen = require('pex-gen');

var LineBuilder = gen.LineBuilder;
var Mesh = glu.Mesh;
var ShowColors = materials.ShowColors;
var PerspectiveCamera = glu.PerspectiveCamera;
var Arcball = glu.Arcball;
var Color = color.Color;
var Vec3 = geom.Vec3;
var Path = geom.Path;
var Spline3D = geom.Spline3D;

sys.Window.create({
  settings: {
    width: 1280,
    height: 720,
    type: '3d',
    fullscreen: sys.Platform.isBrowser
  },
  init: function() {
    var lineBuilder = new LineBuilder();

    var points = [
      new Vec3(-1.5, -1.0, 0),
      new Vec3(-0.5, -0.7, 0),
      new Vec3( 0.5,  0.7, 0),
      new Vec3( 1.5,  1.0, 0)
    ];

    var path = new Path(points.map(function(p) {
      return p.dup().add(new Vec3(0, 0, -0.1))
    }));

    var spline = new Spline3D(points.map(function(p) {
      return p.dup().add(new Vec3(0, 0, 0.1))
    }));

    points.forEach(function(p, pi) {
      lineBuilder.addCross(p, 0.05, Color.Green);
      if (pi > 0) {
        lineBuilder.addLine(p, points[pi-1], Color.Green);
      }
    });

    lineBuilder.addPath(path, Color.Blue, 8, true);
    lineBuilder.addPath(spline, Color.Orange, 8, true);
    lineBuilder.addCross(new Vec3(0, 0, 0), 2, Color.Red);

    this.mesh = new Mesh(lineBuilder, new ShowColors(), { lines: true });

    this.camera = new PerspectiveCamera(60, this.width / this.height);
    this.arcball = new Arcball(this, this.camera, 3);
  },
  draw: function() {
    glu.clearColorAndDepth(Color.White);
    glu.enableDepthReadAndWrite(true);
    this.mesh.draw(this.camera);
  }
});
<commit_msg>Increased resolution of Spline in Path example<commit_after>var sys = require('pex-sys');
var glu = require('pex-glu');
var geom = require('pex-geom');
var materials = require('pex-materials');
var color = require('pex-color');
var gen = require('pex-gen');

var LineBuilder = gen.LineBuilder;
var Mesh = glu.Mesh;
var ShowColors = materials.ShowColors;
var PerspectiveCamera = glu.PerspectiveCamera;
var Arcball = glu.Arcball;
var Color = color.Color;
var Vec3 = geom.Vec3;
var Path = geom.Path;
var Spline3D = geom.Spline3D;

sys.Window.create({
  settings: {
    width: 1280,
    height: 720,
    type: '3d',
    fullscreen: sys.Platform.isBrowser
  },
  init: function() {
    var lineBuilder = new LineBuilder();

    var points = [
      new Vec3(-1.5, -1.0, 0),
      new Vec3(-0.5, -0.7, 0),
      new Vec3( 0.5,  0.7, 0),
      new Vec3( 1.5,  1.0, 0)
    ];

    var path = new Path(points.map(function(p) {
      return p.dup().add(new Vec3(0, 0, -0.1))
    }));

    var spline = new Spline3D(points.map(function(p) {
      return p.dup().add(new Vec3(0, 0, 0.1))
    }));

    points.forEach(function(p, pi) {
      lineBuilder.addCross(p, 0.05, Color.Green);
      if (pi > 0) {
        lineBuilder.addLine(p, points[pi-1], Color.Green);
      }
    });

    lineBuilder.addPath(path, Color.Blue, 8, true);
    lineBuilder.addPath(spline, Color.Orange, 16, true);
    lineBuilder.addCross(new Vec3(0, 0, 0), 2, Color.Red);

    this.mesh = new Mesh(lineBuilder, new ShowColors(), { lines: true });

    this.camera = new PerspectiveCamera(60, this.width / this.height);
    this.arcball = new Arcball(this, this.camera, 3);
  },
  draw: function() {
    glu.clearColorAndDepth(Color.White);
    glu.enableDepthReadAndWrite(true);
    this.mesh.draw(this.camera);
  }
});
<|endoftext|>


<commit_before>import { Template } from 'meteor/templating';
import { ReactiveVar } from 'meteor/reactive-var';
import { _ } from 'meteor/underscore';
import { $ } from 'meteor/jquery';
import { OHIF } from 'meteor/ohif:core';

/*
 * input: controls a select2 component
 */
OHIF.mixins.select2 = new OHIF.Mixin({
    dependencies: 'select',
    composition: {
        onCreated() {
            const instance = Template.instance();

            // Set the custom focus flag
            instance.component.isCustomFocus = true;

            // Check if this select will include a placeholder
            const placeholder = instance.data.options && instance.data.options.placeholder;
            if (placeholder) {
                // Get the option items
                let items = instance.data.items;

                // Check if the items are reactive and get them if true
                const isReactive = items instanceof ReactiveVar;
                if (isReactive) {
                    items = items.get();
                }

                // Check if there is already an empty option on items list
                if (!_.findWhere(items, { value: '' })) {
                    // Clone the current items
                    const newItems = _.clone(items) || [];
                    newItems.unshift({
                        label: placeholder,
                        value: ''
                    });

                    // Set the new items list including the empty option
                    if (isReactive) {
                        instance.data.items.set(newItems);
                    } else {
                        instance.data.items = newItems;
                    }
                }
            }
        },

        onRendered() {
            const instance = Template.instance();
            const component = instance.component;

            // Apply the select2 to the component
            component.$element.select2(instance.data.options);

            // Store the select2 instance to allow its further destruction
            component.select2Instance = component.$element.data('select2');

            // Get the focusable elements
            const elements = [];
            elements.push(component.$element[0]);
            elements.push(component.$element.nextAll('.select2:first').find('.select2-selection')[0]);

            // Attach focus and blur handlers to focusable elements
            $(elements).on('focus', event => {
                if (event.target === event.currentTarget) {
                    // Show the state message on elements focus
                    component.toggleMessage(true);
                }
            }).on('blur', event => {
                if (event.target === event.currentTarget) {
                    // Hide the state message on elements blur
                    component.toggleMessage(false);
                }
            });
        },

        onDestroyed() {
            const instance = Template.instance();
            const component = instance.component;

            // Destroy the select2 instance to remove unwanted DOM elements
            if (component.select2Instance) {
                component.select2Instance.destroy();
            }
        }
    }
});
<commit_msg>Fixing select2 placeholder when the data is reactive<commit_after>import { Template } from 'meteor/templating';
import { ReactiveVar } from 'meteor/reactive-var';
import { _ } from 'meteor/underscore';
import { $ } from 'meteor/jquery';
import { OHIF } from 'meteor/ohif:core';

/*
 * input: controls a select2 component
 */
OHIF.mixins.select2 = new OHIF.Mixin({
    dependencies: 'select',
    composition: {
        onCreated() {
            const instance = Template.instance();

            // Set the custom focus flag
            instance.component.isCustomFocus = true;

            // Check if this select will include a placeholder
            const placeholder = instance.data.options && instance.data.options.placeholder;
            if (placeholder) {
                instance.autorun(() => {
                    // Get the option items
                    let items = instance.data.items;

                    // Check if the items are reactive and get them if true
                    const isReactive = items instanceof ReactiveVar;
                    if (isReactive) {
                        items = items.get();
                    }

                    // Check if there is already an empty option on items list
                    if (!_.findWhere(items, { value: '' })) {
                        // Clone the current items
                        const newItems = _.clone(items) || [];
                        newItems.unshift({
                            label: placeholder,
                            value: ''
                        });

                        // Set the new items list including the empty option
                        if (isReactive) {
                            instance.data.items.set(newItems);
                        } else {
                            instance.data.items = newItems;
                        }
                    }
                });
            }
        },

        onRendered() {
            const instance = Template.instance();
            const component = instance.component;

            // Apply the select2 to the component
            component.$element.select2(instance.data.options);

            // Store the select2 instance to allow its further destruction
            component.select2Instance = component.$element.data('select2');

            // Get the focusable elements
            const elements = [];
            elements.push(component.$element[0]);
            elements.push(component.$element.nextAll('.select2:first').find('.select2-selection')[0]);

            // Attach focus and blur handlers to focusable elements
            $(elements).on('focus', event => {
                if (event.target === event.currentTarget) {
                    // Show the state message on elements focus
                    component.toggleMessage(true);
                }
            }).on('blur', event => {
                if (event.target === event.currentTarget) {
                    // Hide the state message on elements blur
                    component.toggleMessage(false);
                }
            });
        },

        onDestroyed() {
            const instance = Template.instance();
            const component = instance.component;

            // Destroy the select2 instance to remove unwanted DOM elements
            if (component.select2Instance) {
                component.select2Instance.destroy();
            }
        }
    }
});
<|endoftext|>


<commit_before>var MongoClient = require('mongodb').MongoClient;

/**
 * search all talks
 * @param index     full-text index path string
 * @param q         string query to search
 * @param callback  function to execute with the resulting data
 */
exports.search = function(index, q, callback) {
    "use strict";
    var si = require('search-index')({
        indexPath: index
    });
    si.search(q, function(err, results) {
        if (err) {
            throw new Error(err);
        }
        return callback(null, results);
    });
};

/**
 * get all the talks
 * @param dburl     database url string
 * @param callback  function to execute with the resulting data
 */
exports.all = function(dburl, callback) {
    "use strict";
    MongoClient.connect(dburl, function(err, db) {
        if (err) {
            return callback(err, null);
        }
        var talks = db.collection('talks');
        talks
            .find({})
            .toArray(function(err, docs) {
                if (err) {
                    return callback(err, null);
                }
                db.close();
                return callback(null, docs);
            });
    });
};

/**
 * get the latest talks
 * @param dburl         database url string
 * @param quantity      number of results to get
 * @param pageNumber    page number to get results
 * @param callback      function to execute with the resulting data
 */
exports.latest = function(dburl, quantity, pageNumber, callback) {
    "use strict";
    var sort = {
        created: -1
    };
    return searchTalks(dburl, {}, sort, quantity, callback, pageNumber);
};

/**
 * get the most popular talks
 * @param dburl         database url string
 * @param quantity      number of results to get
 * @param pageNumber    page number to get results
 * @param callback      function to execute with the resulting data
 */
exports.popular = function(dburl, quantity, pageNumber, callback) {
    "use strict";
    var conds = {};
    var sort = {
        voteCount: -1,
        viewCount: -1
    };
    return searchTalks(dburl, conds, sort, quantity, callback, pageNumber);
};

/**
 * generic method for getting talks
 * @param dburl         database url string
 * @param conds         search and filter conditions
 * @param sort          sorting conditions
 * @param quantity      number of results to fetch
 * @param pageNumber    page number to get
 * @param callback      function to execute with the resulting data
 */
function searchTalks(dburl, conds, sort, quantity, pageNumber, callback) {
    "use strict";
    var pageNumber = pageNumber || 1;
    MongoClient.connect(dburl, function(err, db) {
        if (err) {
            return callback(err, null);
        }
        var talks = db.collection('talks');
        talks
            .find(conds)
            .sort(sort)
            .skip(pageNumber > 0 ? ((pageNumber - 1) * quantity) : 0)
            .limit(quantity)
            .toArray(function(err, docs) {
                if (err) {
                    return callback(err, null);
                }
                db.close();
                return callback(null, docs);
            });
    });
}

/**
 * create a new talk
 * @param dburl     database url string
 * @param obj       number of results to get
 * @param callback  function to execute with the resulting data
 */
exports.createTalk = function(dburl, obj, callback) {
    "use strict";
    MongoClient.connect(dburl, function(err, db) {
        if (err) {
            return callback(err, null);
        }
        var talks = db.collection('talks');
        talks.insert(obj, function(err, docs) {
            if (err) {
                return callback(err, null);
            }
            db.close();
            return callback(null, docs);
        });
    });
};

/**
 * delete a talk by its id
 * @param dburl     database url string
 * @param id        talk id string
 * @param callback  function to execute with the resulting data
 */
exports.deleteTalk = function(dburl, id, callback) {
    "use strict";
    MongoClient.connect(dburl, function(err, db) {
        if (err) {
            return callback(err, null);
        }
        var talks = db.collection('talks');
        talks.remove({
            id: id
        }, function(err, docs) {
            if (err) {
                return callback(err, null);
            }
            db.close();
            return callback(null, docs);
        });
    });
};

/**
 * get a talk by its id
 * @param dburl     database url string
 * @param id        talk id string
 * @param callback  function to execute with the resulting data
 */
exports.get = function(dburl, id, callback) {
    "use strict";
    MongoClient.connect(dburl, function(err, db) {
        if (err) {
            return callback(err, null);
        }
        var talks = db.collection('talks');
        talks.findOne({
            id: id
        }, function(err, doc) {
            if (err) {
                return callback(err, null);
            }
            db.close();
            return callback(null, doc);
        });
    });
};

/**
 * upvote a talk
 * @param dburl     database url string
 * @param id        Talk id
 * @param userid    User that upvotes the talk
 * @param callback  function to execute with the resulting data
 */
exports.upvote = function(dburl, id, userid, callback) {
    "use strict";
    MongoClient.connect(dburl, function(err, db) {
        if (err) {
            return callback(err, null);
        }
        var talks = db.collection('talks');
        talks.update({
            "id": id,
            "votes": {
                "$ne": userid
            }
        }, {
            "$inc": {
                "voteCount": 1
            },
            "$push": {
                "votes": userid
            }
        }, function(err, talk) {
            if (err) {
                return callback(err, null);
            }
            db.close();
            return callback(null, talk);
        });
    });
};

/**
 * favorite a talk
 * @param dburl     database url string
 * @param id        Talk id
 * @param userid    User that favorites the talk
 * @param callback  function to execute with the resulting data
 */
exports.favorite = function(dburl, id, userid, callback) {
    "use strict";
    MongoClient.connect(dburl, function(err, db) {
        if (err) {
            return callback(err, null);
        }
        var talks = db.collection('talks');
        talks.update({
            "id": id,
            "favorites": {
                "$ne": userid
            }
        }, {
            "$inc": {
                "favoriteCount": 1
            },
            "$push": {
                "favorites": userid
            }
        }, function(err, talk) {
            if (err) {
                return callback(err, null);
            }
            db.close();
            return callback(null, talk);
        });
    });
};

/**
 * unfavorite a talk
 * @param dburl     database url string
 * @param id        Talk id
 * @param userid    User that favorites the talk
 * @param callback  function to execute with the resulting data
 */
exports.unfavorite = function(dburl, id, userid, callback) {
    "use strict";
    MongoClient.connect(dburl, function(err, db) {
        if (err) {
            return callback(err, null);
        }
        var talks = db.collection('talks');
        talks.update({
            "id": id,
            "favorites": {
                "$in": [userid]
            }
        }, {
            "$inc": {
                "favoriteCount": -1
            },
            "$pull": {
                "favorites": userid
            }
        }, function(err, talk) {
            if (err) {
                return callback(err, null);
            }
            db.close();
            return callback(null, talk);
        });
    });
};

/**
 * rank a a talk
 * Update raking score field
 * @param dburl     database url string
 * @param id        Talk Unique ID
 * @param score     Raking score
 * @param callback  function to execute with the resulting data
 */
exports.rank = function(dburl, id, score, callback) {
    "use strict";
    MongoClient.connect(dburl, function(err, db) {
        if (err) {
            return callback(err, null);
        }
        var talks = db.collection('talks');
        talks.update({
            "id": id
        }, {
            $set: {
                "ranking": score
            }
        }, function(err) {
            if (err) {
                return callback(err);
            }
            db.close();
            return callback(null);
        });
    });
};

/**
 * play a talk
 * Updates views field by 1.
 * @param dburl     database url string
 * @param slug      Talk slug
 * @param callback  function to execute with the resulting data
 */
exports.play = function(dburl, slug, callback) {
    "use strict";
    MongoClient.connect(dburl, function(err, db) {
        if (err) {
            return callback(err, null);
        }
        var talks = db.collection('talks');
        talks.findOne({
            "slug": slug
        }, function(err, talk) {
            if (err) {
                return callback(err, null);
            }
            talk.viewCount = talk.viewCount + 1;
            talks.update({
                "slug": slug
            }, {
                $inc: {
                    "viewCount": 1
                },
                $set: {
                    updated: Date.now()
                }
            }, function(err) {
                if (err) {
                    return callback(err, null);
                }
                db.close();
                return callback(null, talk);
            });
        });
    });
};

/**
 * get a talk by tag
 * @param dburl         database url string
 * @param tag           talk tag string
 * @param pageNumber    page number to get
 * @param callback      function to execute with the resulting data
 */
exports.getByTag = function(dburl, tag, pageNumber, callback) {
    "use strict";
    var conds = {
        tags: tag
    };
    var sort = {
        voteCount: -1,
        viewCount: -1
    };
    var quantity = 10;
    return searchTalks(dburl, conds, sort, quantity, pageNumber, callback);
};

/**
 * get a talk by its slug field
 * @param dburl         database url string
 * @param slug          talk slug string
 * @param callback      function to execute with the resulting data
 */
exports.getBySlug = function(dburl, slug, callback) {
    "use strict";
    MongoClient.connect(dburl, function(err, db) {
        if (err) {
            return callback(err, null);
        }
        var talks = db.collection('talks');
        talks.find({
            "slug": slug
        }).toArray(function(err, docs) {
            if (err) {
                return callback(err, null);
            }
            db.close();
            return callback(null, docs);
        });
    });
};

/**
 * get reltated talks from a list of tags
 * @param dburl     string      database url string
 * @param id        int         referenced talk
 * @param tags      Array       list of tag strings
 * @param callback  function    to execute with the resulting data
 */
exports.related = function(dburl, id, tags, quantity, callback) {
    "use strict";
    MongoClient.connect(dburl, function(err, db) {
        if (err) {
            return callback(err, null);
        }
        var talks = db.collection('talks');
        talks
            .find({
                "tags": {
                    $in: tags
                },
                "id": {
                    "$ne": id
                }
            })
            .limit(quantity)
            .toArray(function(err, docs) {
                if (err) {
                    return callback(err, null);
                }
                db.close();
                return callback(null, docs);
            });
    });
};

/**
 * get a talk by its id
 * @param dburl     database url string
 * @param id        author id string
 * @param callback  function to execute with the resulting data
 */
exports.getByAuthorId = function(dburl, id, callback) {
    "use strict";
    MongoClient.connect(dburl, function(err, db) {
        if (err) {
            return callback(err, null);
        }
        var talks = db.collection('talks');
        talks.find({
            "author.id": id
        }).sort({
            "created": -1
        }).toArray(function(err, docs) {
            if (err) {
                return callback(err, null);
            }
            db.close();
            return callback(null, docs);
        });
    });
};

/**
 * get list of talks upvoted by an user
 * @param dburl     database url string
 * @param userid    author id string
 * @param callback  function to execute with the resulting data
 */
exports.getUpvotedByAuthorId = function(dburl, userid, callback) {
    "use strict";
    MongoClient.connect(dburl, function(err, db) {
        if (err) {
            return callback(err, null);
        }
        var talks = db.collection('talks');
        talks.find({
            "votes": {
                "$in": [userid]
            }
        }).sort({
            "created": -1
        }).toArray(function(err, docs) {
            if (err) {
                return callback(err, null);
            }
            db.close();
            return callback(null, docs);
        });
    });
};

/**
 * get list of talks favorited by an user
 * @param dburl     database url string
 * @param userid    author id string
 * @param callback  function to execute with the resulting data
 */
exports.getFavoritedByAuthorId = function(dburl, userid, callback) {
    "use strict";
    MongoClient.connect(dburl, function(err, db) {
        if (err) {
            return callback(err, null);
        }
        var talks = db.collection('talks');
        talks.find({
            "favorites": {
                "$in": [userid]
            }
        }).sort({
            "created": -1
        }).toArray(function(err, docs) {
            if (err) {
                return callback(err, null);
            }
            db.close();
            return callback(null, docs);
        });
    });
};
<commit_msg>Fix order of arguments<commit_after>var MongoClient = require('mongodb').MongoClient;

/**
 * search all talks
 * @param index     full-text index path string
 * @param q         string query to search
 * @param callback  function to execute with the resulting data
 */
exports.search = function(index, q, callback) {
    "use strict";
    var si = require('search-index')({
        indexPath: index
    });
    si.search(q, function(err, results) {
        if (err) {
            throw new Error(err);
        }
        return callback(null, results);
    });
};

/**
 * get all the talks
 * @param dburl     database url string
 * @param callback  function to execute with the resulting data
 */
exports.all = function(dburl, callback) {
    "use strict";
    MongoClient.connect(dburl, function(err, db) {
        if (err) {
            return callback(err, null);
        }
        var talks = db.collection('talks');
        talks
            .find({})
            .toArray(function(err, docs) {
                if (err) {
                    return callback(err, null);
                }
                db.close();
                return callback(null, docs);
            });
    });
};

/**
 * get the latest talks
 * @param dburl         database url string
 * @param quantity      number of results to get
 * @param pageNumber    page number to get results
 * @param callback      function to execute with the resulting data
 */
exports.latest = function(dburl, quantity, pageNumber, callback) {
    "use strict";
    var sort = {
        created: -1
    };
    return searchTalks(dburl, {}, sort, quantity, pageNumber, callback);
};

/**
 * get the most popular talks
 * @param dburl         database url string
 * @param quantity      number of results to get
 * @param pageNumber    page number to get results
 * @param callback      function to execute with the resulting data
 */
exports.popular = function(dburl, quantity, pageNumber, callback) {
    "use strict";
    var conds = {};
    var sort = {
        voteCount: -1,
        viewCount: -1
    };
    return searchTalks(dburl, conds, sort, quantity, pageNumber, callback);
};

/**
 * generic method for getting talks
 * @param dburl         database url string
 * @param conds         search and filter conditions
 * @param sort          sorting conditions
 * @param quantity      number of results to fetch
 * @param pageNumber    page number to get
 * @param callback      function to execute with the resulting data
 */
function searchTalks(dburl, conds, sort, quantity, pageNumber, callback) {
    "use strict";
    var pageNumber = pageNumber || 1;
    MongoClient.connect(dburl, function(err, db) {
        if (err) {
            return callback(err, null);
        }
        var talks = db.collection('talks');
        talks
            .find(conds)
            .sort(sort)
            .skip(pageNumber > 0 ? ((pageNumber - 1) * quantity) : 0)
            .limit(quantity)
            .toArray(function(err, docs) {
                if (err) {
                    return callback(err, null);
                }
                db.close();
                return callback(null, docs);
            });
    });
}

/**
 * create a new talk
 * @param dburl     database url string
 * @param obj       number of results to get
 * @param callback  function to execute with the resulting data
 */
exports.createTalk = function(dburl, obj, callback) {
    "use strict";
    MongoClient.connect(dburl, function(err, db) {
        if (err) {
            return callback(err, null);
        }
        var talks = db.collection('talks');
        talks.insert(obj, function(err, docs) {
            if (err) {
                return callback(err, null);
            }
            db.close();
            return callback(null, docs);
        });
    });
};

/**
 * delete a talk by its id
 * @param dburl     database url string
 * @param id        talk id string
 * @param callback  function to execute with the resulting data
 */
exports.deleteTalk = function(dburl, id, callback) {
    "use strict";
    MongoClient.connect(dburl, function(err, db) {
        if (err) {
            return callback(err, null);
        }
        var talks = db.collection('talks');
        talks.remove({
            id: id
        }, function(err, docs) {
            if (err) {
                return callback(err, null);
            }
            db.close();
            return callback(null, docs);
        });
    });
};

/**
 * get a talk by its id
 * @param dburl     database url string
 * @param id        talk id string
 * @param callback  function to execute with the resulting data
 */
exports.get = function(dburl, id, callback) {
    "use strict";
    MongoClient.connect(dburl, function(err, db) {
        if (err) {
            return callback(err, null);
        }
        var talks = db.collection('talks');
        talks.findOne({
            id: id
        }, function(err, doc) {
            if (err) {
                return callback(err, null);
            }
            db.close();
            return callback(null, doc);
        });
    });
};

/**
 * upvote a talk
 * @param dburl     database url string
 * @param id        Talk id
 * @param userid    User that upvotes the talk
 * @param callback  function to execute with the resulting data
 */
exports.upvote = function(dburl, id, userid, callback) {
    "use strict";
    MongoClient.connect(dburl, function(err, db) {
        if (err) {
            return callback(err, null);
        }
        var talks = db.collection('talks');
        talks.update({
            "id": id,
            "votes": {
                "$ne": userid
            }
        }, {
            "$inc": {
                "voteCount": 1
            },
            "$push": {
                "votes": userid
            }
        }, function(err, talk) {
            if (err) {
                return callback(err, null);
            }
            db.close();
            return callback(null, talk);
        });
    });
};

/**
 * favorite a talk
 * @param dburl     database url string
 * @param id        Talk id
 * @param userid    User that favorites the talk
 * @param callback  function to execute with the resulting data
 */
exports.favorite = function(dburl, id, userid, callback) {
    "use strict";
    MongoClient.connect(dburl, function(err, db) {
        if (err) {
            return callback(err, null);
        }
        var talks = db.collection('talks');
        talks.update({
            "id": id,
            "favorites": {
                "$ne": userid
            }
        }, {
            "$inc": {
                "favoriteCount": 1
            },
            "$push": {
                "favorites": userid
            }
        }, function(err, talk) {
            if (err) {
                return callback(err, null);
            }
            db.close();
            return callback(null, talk);
        });
    });
};

/**
 * unfavorite a talk
 * @param dburl     database url string
 * @param id        Talk id
 * @param userid    User that favorites the talk
 * @param callback  function to execute with the resulting data
 */
exports.unfavorite = function(dburl, id, userid, callback) {
    "use strict";
    MongoClient.connect(dburl, function(err, db) {
        if (err) {
            return callback(err, null);
        }
        var talks = db.collection('talks');
        talks.update({
            "id": id,
            "favorites": {
                "$in": [userid]
            }
        }, {
            "$inc": {
                "favoriteCount": -1
            },
            "$pull": {
                "favorites": userid
            }
        }, function(err, talk) {
            if (err) {
                return callback(err, null);
            }
            db.close();
            return callback(null, talk);
        });
    });
};

/**
 * rank a a talk
 * Update raking score field
 * @param dburl     database url string
 * @param id        Talk Unique ID
 * @param score     Raking score
 * @param callback  function to execute with the resulting data
 */
exports.rank = function(dburl, id, score, callback) {
    "use strict";
    MongoClient.connect(dburl, function(err, db) {
        if (err) {
            return callback(err, null);
        }
        var talks = db.collection('talks');
        talks.update({
            "id": id
        }, {
            $set: {
                "ranking": score
            }
        }, function(err) {
            if (err) {
                return callback(err);
            }
            db.close();
            return callback(null);
        });
    });
};

/**
 * play a talk
 * Updates views field by 1.
 * @param dburl     database url string
 * @param slug      Talk slug
 * @param callback  function to execute with the resulting data
 */
exports.play = function(dburl, slug, callback) {
    "use strict";
    MongoClient.connect(dburl, function(err, db) {
        if (err) {
            return callback(err, null);
        }
        var talks = db.collection('talks');
        talks.findOne({
            "slug": slug
        }, function(err, talk) {
            if (err) {
                return callback(err, null);
            }
            talk.viewCount = talk.viewCount + 1;
            talks.update({
                "slug": slug
            }, {
                $inc: {
                    "viewCount": 1
                },
                $set: {
                    updated: Date.now()
                }
            }, function(err) {
                if (err) {
                    return callback(err, null);
                }
                db.close();
                return callback(null, talk);
            });
        });
    });
};

/**
 * get a talk by tag
 * @param dburl         database url string
 * @param tag           talk tag string
 * @param pageNumber    page number to get
 * @param callback      function to execute with the resulting data
 */
exports.getByTag = function(dburl, tag, pageNumber, callback) {
    "use strict";
    var conds = {
        tags: tag
    };
    var sort = {
        voteCount: -1,
        viewCount: -1
    };
    var quantity = 10;
    return searchTalks(dburl, conds, sort, quantity, pageNumber, callback);
};

/**
 * get a talk by its slug field
 * @param dburl         database url string
 * @param slug          talk slug string
 * @param callback      function to execute with the resulting data
 */
exports.getBySlug = function(dburl, slug, callback) {
    "use strict";
    MongoClient.connect(dburl, function(err, db) {
        if (err) {
            return callback(err, null);
        }
        var talks = db.collection('talks');
        talks.find({
            "slug": slug
        }).toArray(function(err, docs) {
            if (err) {
                return callback(err, null);
            }
            db.close();
            return callback(null, docs);
        });
    });
};

/**
 * get reltated talks from a list of tags
 * @param dburl     string      database url string
 * @param id        int         referenced talk
 * @param tags      Array       list of tag strings
 * @param callback  function    to execute with the resulting data
 */
exports.related = function(dburl, id, tags, quantity, callback) {
    "use strict";
    MongoClient.connect(dburl, function(err, db) {
        if (err) {
            return callback(err, null);
        }
        var talks = db.collection('talks');
        talks
            .find({
                "tags": {
                    $in: tags
                },
                "id": {
                    "$ne": id
                }
            })
            .limit(quantity)
            .toArray(function(err, docs) {
                if (err) {
                    return callback(err, null);
                }
                db.close();
                return callback(null, docs);
            });
    });
};

/**
 * get a talk by its id
 * @param dburl     database url string
 * @param id        author id string
 * @param callback  function to execute with the resulting data
 */
exports.getByAuthorId = function(dburl, id, callback) {
    "use strict";
    MongoClient.connect(dburl, function(err, db) {
        if (err) {
            return callback(err, null);
        }
        var talks = db.collection('talks');
        talks.find({
            "author.id": id
        }).sort({
            "created": -1
        }).toArray(function(err, docs) {
            if (err) {
                return callback(err, null);
            }
            db.close();
            return callback(null, docs);
        });
    });
};

/**
 * get list of talks upvoted by an user
 * @param dburl     database url string
 * @param userid    author id string
 * @param callback  function to execute with the resulting data
 */
exports.getUpvotedByAuthorId = function(dburl, userid, callback) {
    "use strict";
    MongoClient.connect(dburl, function(err, db) {
        if (err) {
            return callback(err, null);
        }
        var talks = db.collection('talks');
        talks.find({
            "votes": {
                "$in": [userid]
            }
        }).sort({
            "created": -1
        }).toArray(function(err, docs) {
            if (err) {
                return callback(err, null);
            }
            db.close();
            return callback(null, docs);
        });
    });
};

/**
 * get list of talks favorited by an user
 * @param dburl     database url string
 * @param userid    author id string
 * @param callback  function to execute with the resulting data
 */
exports.getFavoritedByAuthorId = function(dburl, userid, callback) {
    "use strict";
    MongoClient.connect(dburl, function(err, db) {
        if (err) {
            return callback(err, null);
        }
        var talks = db.collection('talks');
        talks.find({
            "favorites": {
                "$in": [userid]
            }
        }).sort({
            "created": -1
        }).toArray(function(err, docs) {
            if (err) {
                return callback(err, null);
            }
            db.close();
            return callback(null, docs);
        });
    });
};
<|endoftext|>


<commit_before>var fs = require('fs');
var path = require('path');
var glob = require("glob");
var chalk = require('chalk');
var async = require('async');
var mkdirs = require('mkdirs');
var string = require('snack-string');
var stdio = require('./stdio');
var retry = require('./retry');

function Task(name, destdir) {
    this.object = require(name);
    this.basedir = path.join(require.resolve(name + '/package.json'), '../root');
    this.destdir = path.resolve(destdir);
    this.encoding = this.object.encoding || 'utf-8';
    this.context = {};
}

Task.prototype.run = function() {
    async.series([
        this.ask.bind(this),
        this.confirm.bind(this),
        this.copy.bind(this),
        this.customize.bind(this),
    ], function(err) {
        if (err) {
            stdio.error(err);
            process.exit(1);
        } else {
            this.done();
            process.exit(0);
        }
    }.bind(this));
};

Task.prototype.ask = function(done) {
    stdio.info('Press ^C at any time to quit.');

    var context = this.context;
    async.eachSeries(this.object.variables || [], function(variable, callback) {
        retry(function(callback) {
            stdio.write('%s', chalk.yellow(variable.desc));
            stdio.write(': ');
            if (variable.defaults) {
                stdio.write('(%s) ', variable.defaults);
            }
            stdio.readline(function(data) {
                if (data === '' && variable.required) {
                    callback(new Error(variable.desc + ' is required'));
                } else {
                    context[variable.name] = data || variable.defaults;
                    callback(null);
                }
            });
        }, function(err) {
            callback(err);
        });
    }, done);
};

Task.prototype.confirm = function(done) {
    stdio.info();
    retry(function(callback) {
        stdio.write('Is this ok? [Y/n] ');
        stdio.readline(function(data) {
            var answer = data.toLowerCase();
            if (answer === 'y' || answer === 'n' || answer === '') {
                callback(null, answer);
            } else {
                callback(new Error('not accepted answer: ' + answer));
            }
        });
    }, function(err, results) {
        if (results === 'n') {
            this.abort();
        }
        done(err);
    }, this);
};

Task.prototype.copy = function(done) {
    var files = glob.sync('**', {
        cwd: this.basedir
    });
    files.forEach(function(file) {
        var src = path.join(this.basedir, file);

        var buffer = fs.readFileSync(src);
        if (string.endsWith(src, '.in')) {
            var s = buffer.toString(this.encoding);
            s = string.interpolate(s, this.context);
            buffer = new Buffer(s, this.encoding);

            file = string.removeEnd(file, '.in');
        }

        var dest = path.join(this.destdir, file);
        mkdirs(path.dirname(dest));
        fs.writeFileSync(dest, buffer);
        stdio.info('create %s', file);
    }, this);

    done(null);
};

Task.prototype.customize = function(done) {
    if (typeof this.object.run === 'function') {
        var config = {
            outputdir: this.destdir,
            variables: this.context
        };
        this.object.run(config, done);
    }
};

Task.prototype.abort = function() {
    stdio.info();
    stdio.info('Abort!');
    process.exit(1);
};

Task.prototype.done = function() {
    stdio.info();
    stdio.info('Successfully!');
    stdio.info();
};

module.exports = Task;
<commit_msg>update<commit_after>var fs = require('fs');
var path = require('path');
var glob = require("glob");
var chalk = require('chalk');
var async = require('async');
var mkdirs = require('mkdirs');
var string = require('snack-string');
var stdio = require('./stdio');
var retry = require('./retry');

function Task(name, destdir) {
    this.object = require(name);
    this.basedir = path.join(require.resolve(name + '/package.json'), '../templates');
    this.destdir = path.resolve(destdir);
    this.encoding = this.object.encoding || 'utf-8';
    this.context = {};
}

Task.prototype.run = function() {
    async.series([
        this.ask.bind(this),
        this.confirm.bind(this),
        this.copy.bind(this),
        this.customize.bind(this),
    ], function(err) {
        if (err) {
            stdio.error(err);
            process.exit(1);
        } else {
            this.done();
            process.exit(0);
        }
    }.bind(this));
};

Task.prototype.ask = function(done) {
    stdio.info('Press ^C at any time to quit.');

    var context = this.context;
    async.eachSeries(this.object.variables || [], function(variable, callback) {
        retry(function(callback) {
            stdio.write('%s', chalk.yellow(variable.desc));
            stdio.write(': ');
            if (variable.defaults) {
                stdio.write('(%s) ', variable.defaults);
            }
            stdio.readline(function(data) {
                if (data === '' && variable.required) {
                    callback(new Error(variable.desc + ' is required'));
                } else {
                    context[variable.name] = data || variable.defaults;
                    callback(null);
                }
            });
        }, function(err) {
            callback(err);
        });
    }, done);
};

Task.prototype.confirm = function(done) {
    stdio.info();
    retry(function(callback) {
        stdio.write('Is this ok? [Y/n] ');
        stdio.readline(function(data) {
            var answer = data.toLowerCase();
            if (answer === 'y' || answer === 'n' || answer === '') {
                callback(null, answer);
            } else {
                callback(new Error('not accepted answer: ' + answer));
            }
        });
    }, function(err, results) {
        if (results === 'n') {
            this.abort();
        }
        done(err);
    }, this);
};

Task.prototype.copy = function(done) {
    var files = glob.sync('**', {
        cwd: this.basedir,
        dot: true,
        nodir: true
    });

    stdio.info();
    stdio.info('Found %d files to create ...', files.length);
    stdio.info();

    files.forEach(function(file) {
        var src = path.join(this.basedir, file);

        var buffer = fs.readFileSync(src);
        if (string.endsWith(src, '.in')) {
            var s = buffer.toString(this.encoding);
            s = string.interpolate(s, this.context);
            buffer = new Buffer(s, this.encoding);

            file = string.removeEnd(file, '.in');
        }

        var dest = path.join(this.destdir, file);
        mkdirs(path.dirname(dest));
        fs.writeFileSync(dest, buffer);
        stdio.info('create %s', file);
    }, this);

    done(null);
};

Task.prototype.customize = function(done) {
    if (typeof this.object.run === 'function') {
        var config = {
            outputdir: this.destdir,
            variables: this.context
        };
        this.object.run(config, done);
    }
};

Task.prototype.abort = function() {
    stdio.info();
    stdio.info('Abort!');
    process.exit(1);
};

Task.prototype.done = function() {
    stdio.info();
    stdio.info('Successfully!');
    stdio.info();
};

module.exports = Task;
<|endoftext|>


<commit_before>"use strict";

var fs = require("fs");
var path = require("path");
var _ = require("lodash");
var chalk = require("chalk");
var args = require("./args");
var Environment = require("./environment");
var log = require("./log");
var runner = require("./runner");

/**
 * Task wrapper.
 *
 * @param {Object} opts         Options object
 * @param {Object} opts.config  Configuration object
 * @param {Object} opts.env     Environment object to mutate (Default `process.env`)
 * @param {Array}  opts.argv    Arguments array (Default: `process.argv`)
 * @returns {void}
 */
var Task = module.exports = function (opts) {
  opts = opts || {};

  // Options.
  this._config = opts.config;
  this._env = opts.env || new Environment();

  // Infer parts.
  this.argv = opts.argv || process.argv;
  var parsed = args.general(this.argv);
  var remain = parsed.argv.remain;
  this._script = this.argv[1];
  this._action = remain[0];
  this._command = remain[1];
  this._commands = remain.slice(1);

  // Validation.
  if (!this._config) {
    throw new Error("Configuration object required");
  }
  if (!_.contains(this.ACTIONS, this._action)) {
    throw new Error("Invalid action: " + this._action +
      " - Valid actions: " + this.ACTIONS.join(", "));
  }
};

Task.prototype.ACTIONS = ["help", "run", "concurrent", "envs"];

Task.prototype.toString = function () {
  var cmd = this._command;
  if (this._action === "concurrent") {
    cmd = this._commands.join(", ");
  }

  return this._action + " " + cmd;
};

/**
 * Is this task a simple passthrough to another builder command?
 *
 * @param   {String} task   Task
 * @returns {Boolean}       Is this task a passthrough?
 */
Task.prototype.isPassthrough = function (task) {
  var builder = path.basename(this._script);
  var taskParts = task.split(/\s+/);
  var taskBin = taskParts[0];
  var taskAction = taskParts[1];
  var taskCommand = taskParts[2];

  // Note: Assumes a binary script match without `.js` extension.
  return builder === taskBin &&
    this._action === taskAction &&
    this._command === taskCommand;
};

/**
 * Get executable command.
 *
 * @param   {String} cmd  Script command
 * @returns {String}      String to execute
 */
Task.prototype.getCommand = function (cmd) {
  // Select first non-passthrough command.
  var task = _.find(this._config.getCommands(cmd), function (curCmd) {
    /*eslint-disable no-invalid-this*/
    return !this.isPassthrough(curCmd);
  }, this);

  // Error out if still can't find task.
  if (!task) {
    throw new Error("Unable to find task for: " + cmd);
  }

  return task;
};

/**
 * Help.
 *
 * ```sh
 * $ builder help [ARCHETYPE, ARCHETYPE]
 * ```
 *
 * @param   {Function} callback   Callback function `(err)`
 * @returns {void}
 */
Task.prototype.help = function (callback) {
  // Arguments after `help` are archetypes.
  var cmd = this._command;

  var actions = this.ACTIONS.map(function (val) {
    return val === cmd ? chalk.red(cmd) : val;
  }).join(", ");
  var action = cmd ? chalk.red(cmd) : "[action]";
  var actionFlags = "";
  if (cmd) {
    actionFlags = "\n\n" + chalk.green.bold("Flags") + ": " + chalk.red(cmd) + "\n\n  " +
      args.help(cmd);
  }

  log.info("help",
    "\n\n" + chalk.green.bold("Usage") + ": \n\n  builder " + action + " [flags] [task]" +
    "\n\n" + chalk.green.bold("Actions") + ": \n\n  " + actions +
    "\n\n" + chalk.green.bold("Flags") + ": General\n\n  " + args.help() +
    actionFlags +
    "\n\n" + chalk.green.bold("Tasks") + ": \n" + this._config.displayScripts());

  callback();
};

/**
 * Run.
 *
 * @param   {Function} callback   Callback function `(err)`
 * @returns {void}
 */
Task.prototype.run = function (callback) {
  // `builder run` -> `builder help`
  if (!this._command) {
    return this.help(callback);
  }

  var env = this._env.env; // Raw environment object.
  var task = this.getCommand(this._command);
  var flags = args.run(this.argv);
  var opts = _.extend({}, flags);

  log.info(this._action, this._command + chalk.gray(" - " + task));

  runner.run(task, { env: env }, opts, callback);
};

/**
 * Concurrent.
 *
 * @param   {Function} callback   Callback function `(err)`
 * @returns {void}
 */
Task.prototype.concurrent = function (callback) {
  var env = this._env.env; // Raw environment object.
  var cmds = this._commands;
  var tasks = cmds.map(this.getCommand.bind(this));
  var flags = args.concurrent(this.argv);
  var opts = _.extend({}, flags);

  log.info(this._action, cmds.join(", ") + tasks.map(function (t, i) {
    return "\n * " + cmds[i] + chalk.gray(" - " + t);
  }).join(""));

  runner.concurrent(tasks, { env: env }, opts, callback);
};

Task.prototype._parseJson = function (objStr) {
  try {
    return JSON.parse(objStr);
  } catch (err) {
    log.error(this._action + ":json-obj", "Failed to load JSON object: " + objStr);
    throw err;
  }
};

Task.prototype._parseJsonFile = function (filePath) {
  try {
    return JSON.parse(fs.readFileSync(filePath));
  } catch (err) {
    log.error(this._action + ":json-file", "Failed to load JSON file: " + filePath);
    throw err;
  }
};

/**
 * Run multiple environments.
 *
 * @param   {Function} callback   Callback function `(err)`
 * @returns {void}
 */
Task.prototype.envs = function (callback) {
  /*eslint max-statements: [2, 20]*/
  // Core setup.
  var env = this._env.env;
  var task = this.getCommand(this._command);
  var flags = args.envs(this.argv);
  var opts = _.extend({}, flags);

  // Get task environment array.
  var envsStr = this._commands[1];
  if (envsStr) {
    // Try string on command line first:
    // $ builder envs <task> '[{ "FOO": "VAL1" }, { "FOO": "VAL2" }]'
    opts._envs = this._parseJson(envsStr);
  } else if (opts.envsPath) {
    // Try JSON file path next:
    // $ builder envs <task> --envs-path=my-environment-vars.json
    opts._envs = this._parseJsonFile(opts.envsPath);
  }

  // Validation
  var err;
  if (_.isEmpty(opts._envs)) {
    err = new Error("Empty/null JSON environments array.");
  } else if (!_.isArray(opts._envs)) {
    err = new Error("Non-array JSON environments object: " + JSON.stringify(opts._envs));
  }

  if (err) {
    log.error("envs:json-error", err);
    return callback(err);
  }

  runner.envs(task, { env: env }, opts, callback);
};

/**
 * Execute task or tasks.
 *
 * @param   {Function} callback   Callback function `(err)`
 * @returns {void}
 */
Task.prototype.execute = function (callback) {
  // Check task action method exists.
  if (!this[this._action]) {
    return callback(new Error("Unrecognized action: " + this._action));
  }

  // Call action.
  this[this._action](callback);
};
<commit_msg>Update usage to match README<commit_after>"use strict";

var fs = require("fs");
var path = require("path");
var _ = require("lodash");
var chalk = require("chalk");
var args = require("./args");
var Environment = require("./environment");
var log = require("./log");
var runner = require("./runner");

/**
 * Task wrapper.
 *
 * @param {Object} opts         Options object
 * @param {Object} opts.config  Configuration object
 * @param {Object} opts.env     Environment object to mutate (Default `process.env`)
 * @param {Array}  opts.argv    Arguments array (Default: `process.argv`)
 * @returns {void}
 */
var Task = module.exports = function (opts) {
  opts = opts || {};

  // Options.
  this._config = opts.config;
  this._env = opts.env || new Environment();

  // Infer parts.
  this.argv = opts.argv || process.argv;
  var parsed = args.general(this.argv);
  var remain = parsed.argv.remain;
  this._script = this.argv[1];
  this._action = remain[0];
  this._command = remain[1];
  this._commands = remain.slice(1);

  // Validation.
  if (!this._config) {
    throw new Error("Configuration object required");
  }
  if (!_.contains(this.ACTIONS, this._action)) {
    throw new Error("Invalid action: " + this._action +
      " - Valid actions: " + this.ACTIONS.join(", "));
  }
};

Task.prototype.ACTIONS = ["help", "run", "concurrent", "envs"];

Task.prototype.toString = function () {
  var cmd = this._command;
  if (this._action === "concurrent") {
    cmd = this._commands.join(", ");
  }

  return this._action + " " + cmd;
};

/**
 * Is this task a simple passthrough to another builder command?
 *
 * @param   {String} task   Task
 * @returns {Boolean}       Is this task a passthrough?
 */
Task.prototype.isPassthrough = function (task) {
  var builder = path.basename(this._script);
  var taskParts = task.split(/\s+/);
  var taskBin = taskParts[0];
  var taskAction = taskParts[1];
  var taskCommand = taskParts[2];

  // Note: Assumes a binary script match without `.js` extension.
  return builder === taskBin &&
    this._action === taskAction &&
    this._command === taskCommand;
};

/**
 * Get executable command.
 *
 * @param   {String} cmd  Script command
 * @returns {String}      String to execute
 */
Task.prototype.getCommand = function (cmd) {
  // Select first non-passthrough command.
  var task = _.find(this._config.getCommands(cmd), function (curCmd) {
    /*eslint-disable no-invalid-this*/
    return !this.isPassthrough(curCmd);
  }, this);

  // Error out if still can't find task.
  if (!task) {
    throw new Error("Unable to find task for: " + cmd);
  }

  return task;
};

/**
 * Help.
 *
 * ```sh
 * $ builder help [ARCHETYPE, ARCHETYPE]
 * ```
 *
 * @param   {Function} callback   Callback function `(err)`
 * @returns {void}
 */
Task.prototype.help = function (callback) {
  // Arguments after `help` are archetypes.
  var cmd = this._command;

  var actions = this.ACTIONS.map(function (val) {
    return val === cmd ? chalk.red(cmd) : val;
  }).join(", ");
  var action = cmd ? chalk.red(cmd) : "[action]";
  var actionFlags = "";
  if (cmd) {
    actionFlags = "\n\n" + chalk.green.bold("Flags") + ": " + chalk.red(cmd) + "\n\n  " +
      args.help(cmd);
  }

  log.info("help",
    "\n\n" + chalk.green.bold("Usage") + ": \n\n  builder " + action + " <action> <task(s)>" +
    "\n\n" + chalk.green.bold("Actions") + ": \n\n  " + actions +
    "\n\n" + chalk.green.bold("Flags") + ": General\n\n  " + args.help() +
    actionFlags +
    "\n\n" + chalk.green.bold("Tasks") + ": \n" + this._config.displayScripts());

  callback();
};

/**
 * Run.
 *
 * @param   {Function} callback   Callback function `(err)`
 * @returns {void}
 */
Task.prototype.run = function (callback) {
  // `builder run` -> `builder help`
  if (!this._command) {
    return this.help(callback);
  }

  var env = this._env.env; // Raw environment object.
  var task = this.getCommand(this._command);
  var flags = args.run(this.argv);
  var opts = _.extend({}, flags);

  log.info(this._action, this._command + chalk.gray(" - " + task));

  runner.run(task, { env: env }, opts, callback);
};

/**
 * Concurrent.
 *
 * @param   {Function} callback   Callback function `(err)`
 * @returns {void}
 */
Task.prototype.concurrent = function (callback) {
  var env = this._env.env; // Raw environment object.
  var cmds = this._commands;
  var tasks = cmds.map(this.getCommand.bind(this));
  var flags = args.concurrent(this.argv);
  var opts = _.extend({}, flags);

  log.info(this._action, cmds.join(", ") + tasks.map(function (t, i) {
    return "\n * " + cmds[i] + chalk.gray(" - " + t);
  }).join(""));

  runner.concurrent(tasks, { env: env }, opts, callback);
};

Task.prototype._parseJson = function (objStr) {
  try {
    return JSON.parse(objStr);
  } catch (err) {
    log.error(this._action + ":json-obj", "Failed to load JSON object: " + objStr);
    throw err;
  }
};

Task.prototype._parseJsonFile = function (filePath) {
  try {
    return JSON.parse(fs.readFileSync(filePath));
  } catch (err) {
    log.error(this._action + ":json-file", "Failed to load JSON file: " + filePath);
    throw err;
  }
};

/**
 * Run multiple environments.
 *
 * @param   {Function} callback   Callback function `(err)`
 * @returns {void}
 */
Task.prototype.envs = function (callback) {
  /*eslint max-statements: [2, 20]*/
  // Core setup.
  var env = this._env.env;
  var task = this.getCommand(this._command);
  var flags = args.envs(this.argv);
  var opts = _.extend({}, flags);

  // Get task environment array.
  var envsStr = this._commands[1];
  if (envsStr) {
    // Try string on command line first:
    // $ builder envs <task> '[{ "FOO": "VAL1" }, { "FOO": "VAL2" }]'
    opts._envs = this._parseJson(envsStr);
  } else if (opts.envsPath) {
    // Try JSON file path next:
    // $ builder envs <task> --envs-path=my-environment-vars.json
    opts._envs = this._parseJsonFile(opts.envsPath);
  }

  // Validation
  var err;
  if (_.isEmpty(opts._envs)) {
    err = new Error("Empty/null JSON environments array.");
  } else if (!_.isArray(opts._envs)) {
    err = new Error("Non-array JSON environments object: " + JSON.stringify(opts._envs));
  }

  if (err) {
    log.error("envs:json-error", err);
    return callback(err);
  }

  runner.envs(task, { env: env }, opts, callback);
};

/**
 * Execute task or tasks.
 *
 * @param   {Function} callback   Callback function `(err)`
 * @returns {void}
 */
Task.prototype.execute = function (callback) {
  // Check task action method exists.
  if (!this[this._action]) {
    return callback(new Error("Unrecognized action: " + this._action));
  }

  // Call action.
  this[this._action](callback);
};
<|endoftext|>