<commit_before>(function (window, ch) {
    'use strict';

    /**
     * The Positioner lets you position elements on the screen and changes its positions.
     * @memberof ch
     * @constructor
     * @param {Object} options Configuration object.
     * @param {String} options.target A CSS Selector that reference to the element to be positioned.
     * @param {String} [options.reference] A CSS Selector that it's a reference to position and size of element that will be considered to carry out the position. If it isn't defined through configuration, it will be the ch.viewport.
     * @param {String} [options.side] The side option where the target element will be positioned. You must use: "left", "right", "top", "bottom" or "center". Default: "center".
     * @param {String} [options.align] The align options where the target element will be positioned. You must use: "left", "right", "top", "bottom" or "center". Default: "center".
     * @param {Number} [options.offsetX] Distance to displace the target horizontally. Default: 0.
     * @param {Number} [options.offsetY] Distance to displace the target vertically. Default: 0.
     * @param {String} [options.position] Thethe type of positioning used. You must use: "absolute" or "fixed". Default: "fixed".
     * @requires ch.util
     * @requires ch.Viewport
     * @returns {positioner} Returns a new instance of Positioner.
     * @example
     * // Instance the Positioner It requires a little configuration.
     * // The default behavior place an element center into the Viewport.
     * var positioned = new ch.Positioner({
     *     'target': 'CSS Selector',
     *     'reference': 'CSS Selector',
     *     'side': 'top',
     *     'align': 'left',
     *     'offsetX': 20,
     *     'offsetY': 10
     * });
     * @example
     * // offsetX: The Positioner could be configurated with an offsetX.
     * // This example show an element displaced horizontally by 10px of defined position.
     * var positioned = new ch.Positioner({
     *     'target': 'CSS Selector',
     *     'reference': 'CSS Selector',
     *     'side': 'top',
     *     'align': 'left',
     *     'offsetX': 10
     * });
     * @example
     * // offsetY: The Positioner could be configurated with an offsetY.
     * // This example show an element displaced vertically by 10px of defined position.
     * var positioned = new ch.Positioner({
     *     'target': 'CSS Selector',
     *     'reference': 'CSS Selector',
     *     'side': 'top',
     *     'align': 'left',
     *     'offsetY': 10
     * });
     * @example
     * // positioned: The positioner could be configured to work with fixed or absolute position value.
     * var positioned = new ch.Positioner({
     *     'target': 'CSS Selector',
     *     'reference': 'CSS Selector',
     *     'position': 'fixed'
     * });
     */
    function Positioner(options) {

        if (options === undefined) {
            throw new window.Error('ch.Positioner: Expected options defined.');
        }

        // Creates its private options
        this._options = ch.util.clone(this._defaults);

        // Init
        this._configure(options);
    }

    /**
     * The name of the component.
     * @memberof! ch.Positioner.prototype
     * @type {String}
     */
    Positioner.prototype.name = 'positioner';

    /**
     * Returns a reference to the Constructor function that created the instance's prototype.
     * @memberof! ch.Positioner.prototype
     * @function
     * @private
     */
    Positioner.prototype._constructor = Positioner;

    /**
     * Configuration by default.
     * @type {Object}
     * @private
     */
    Positioner.prototype._defaults = {
        'offsetX': 0,
        'offsetY': 0,
        'side': 'center',
        'align': 'center',
        'reference': ch.viewport,
        'position': 'fixed'
    };

    /**
     * Configures the positioner instance with a given options.
     * @memberof! ch.Positioner.prototype
     * @function
     * @private
     * @returns {positioner}
     * @params {Object} options A configuration object.
     */
    Positioner.prototype._configure = function (options) {

        // Merge user options with its options
        ch.util.extend(this._options, options);

        console.log(this._options.reference)

        this._options.offsetX = parseInt(this._options.offsetX, 10);
        this._options.offsetY = parseInt(this._options.offsetY, 10);

        /**
         * Reference to the element to be positioned.
         * @type {HTMLElement}
         */
        this.target = options.target || this.target;

        /**
         * It's a reference to position and size of element that will be considered to carry out the position.
         * @type {HTMLElement}
         */
        this.reference = options.reference || this.reference;
        this._reference = this._options.reference;

        this.target.style.position = this._options.position;

        return this;
    };

    /**
     * Updates the current position with a given options
     * @memberof! ch.Positioner.prototype
     * @function
     * @returns {positioner}
     * @params {Object} options A configuration object.
     * @example
     * // Updates the current position.
     * positioned.refresh();
     * @example
     * // Updates the current position with new offsetX and offsetY.
     * positioned.refresh({
     *     'offestX': 100,
     *     'offestY': 10
     * });
     */
    Positioner.prototype.refresh = function (options) {

        if (options !== undefined) {
            this._configure(options);
        }

        if (this._reference !== ch.viewport) {
            this._calculateReference();
        }

        this._calculateTarget();

        // the object that stores the top, left reference to set to the target
        this._setPoint();

        return this;
    };

    /**
     * Calculates the reference (element or ch.viewport) of the position.
     * @memberof! ch.Positioner.prototype
     * @function
     * @private
     * @returns {positioner}
     */
    Positioner.prototype._calculateReference = function () {

        var reference = this.reference,
            offset;

        reference.setAttribute('data-side', this._options.side);
        reference.setAttribute('data-align', this._options.align);

        this._reference = ch.util.getOuterDimensions(reference);

        if (reference.offsetParent === this.target.offsetParent) {
            this._reference.left = reference.offsetLeft;
            this._reference.top = reference.offsetTop;

        } else {
            offset = ch.util.getOffset(reference);
            this._reference.left = offset.left;
            this._reference.top = offset.top;
        }

        return this;
    };

    /**
     * Calculates the positioned element.
     * @memberof! ch.Positioner.prototype
     * @function
     * @private
     * @returns {positioner}
     */
    Positioner.prototype._calculateTarget = function () {

        var target = this.target;
        target.setAttribute('data-side', this._options.side);
        target.setAttribute('data-align', this._options.align);

        this._target = ch.util.getOuterDimensions(target);

        return this;
    };

    /**
     * Calculates the points.
     * @memberof! ch.Positioner.prototype
     * @function
     * @private
     * @returns {positioner}
     */
    Positioner.prototype._setPoint = function () {
        var side = this._options.side,
            oritentation = (side === 'top' || side === 'bottom') ? 'horizontal' : ((side === 'right' || side === 'left') ? 'vertical' : 'center'),
            coors,
            oritentationMap;

        // take the side and calculate the alignment and make the CSSpoint
        if (oritentation === 'center') {
            // calculates the coordinates related to the center side to locate the target
            coors = {
                'top': (this._reference.top + (this._reference.height / 2 - this._target.height / 2)),
                'left': (this._reference.left + (this._reference.width / 2 - this._target.width / 2))
            };

        } else if (oritentation === 'horizontal') {
            // calculates the coordinates related to the top or bottom side to locate the target
            oritentationMap = {
                'left': this._reference.left,
                'center': (this._reference.left + (this._reference.width / 2 - this._target.width / 2)),
                'right': (this._reference.left + this._reference.width - this._target.width),
                'top': this._reference.top - this._target.height,
                'bottom': (this._reference.top + this._reference.height)
            };

            coors = {
                'top': oritentationMap[side],
                'left': oritentationMap[this._options.align]
            };

        } else {
            // calculates the coordinates related to the right or left side to locate the target
            oritentationMap = {
                'top': this._reference.top,
                'center': (this._reference.top + (this._reference.height / 2 - this._target.height / 2)),
                'bottom': (this._reference.top + this._reference.height - this._target.height),
                'right': (this._reference.left + this._reference.width),
                'left': (this._reference.left - this._target.width)
            };

            coors = {
                'top': oritentationMap[this._options.align],
                'left': oritentationMap[side]
            };
        }

        coors.top += this._options.offsetY;
        coors.left += this._options.offsetX;

        this.target.style.top = coors.top + 'px';
        this.target.style.left = coors.left + 'px';

        return this;
    };

    ch.Positioner = Positioner;

}(this, this.ch));<commit_msg>#1210 Positioner.js, remove console.log()<commit_after>(function (window, ch) {
    'use strict';

    /**
     * The Positioner lets you position elements on the screen and changes its positions.
     * @memberof ch
     * @constructor
     * @param {Object} options Configuration object.
     * @param {String} options.target A CSS Selector that reference to the element to be positioned.
     * @param {String} [options.reference] A CSS Selector that it's a reference to position and size of element that will be considered to carry out the position. If it isn't defined through configuration, it will be the ch.viewport.
     * @param {String} [options.side] The side option where the target element will be positioned. You must use: "left", "right", "top", "bottom" or "center". Default: "center".
     * @param {String} [options.align] The align options where the target element will be positioned. You must use: "left", "right", "top", "bottom" or "center". Default: "center".
     * @param {Number} [options.offsetX] Distance to displace the target horizontally. Default: 0.
     * @param {Number} [options.offsetY] Distance to displace the target vertically. Default: 0.
     * @param {String} [options.position] Thethe type of positioning used. You must use: "absolute" or "fixed". Default: "fixed".
     * @requires ch.util
     * @requires ch.Viewport
     * @returns {positioner} Returns a new instance of Positioner.
     * @example
     * // Instance the Positioner It requires a little configuration.
     * // The default behavior place an element center into the Viewport.
     * var positioned = new ch.Positioner({
     *     'target': 'CSS Selector',
     *     'reference': 'CSS Selector',
     *     'side': 'top',
     *     'align': 'left',
     *     'offsetX': 20,
     *     'offsetY': 10
     * });
     * @example
     * // offsetX: The Positioner could be configurated with an offsetX.
     * // This example show an element displaced horizontally by 10px of defined position.
     * var positioned = new ch.Positioner({
     *     'target': 'CSS Selector',
     *     'reference': 'CSS Selector',
     *     'side': 'top',
     *     'align': 'left',
     *     'offsetX': 10
     * });
     * @example
     * // offsetY: The Positioner could be configurated with an offsetY.
     * // This example show an element displaced vertically by 10px of defined position.
     * var positioned = new ch.Positioner({
     *     'target': 'CSS Selector',
     *     'reference': 'CSS Selector',
     *     'side': 'top',
     *     'align': 'left',
     *     'offsetY': 10
     * });
     * @example
     * // positioned: The positioner could be configured to work with fixed or absolute position value.
     * var positioned = new ch.Positioner({
     *     'target': 'CSS Selector',
     *     'reference': 'CSS Selector',
     *     'position': 'fixed'
     * });
     */
    function Positioner(options) {

        if (options === undefined) {
            throw new window.Error('ch.Positioner: Expected options defined.');
        }

        // Creates its private options
        this._options = ch.util.clone(this._defaults);

        // Init
        this._configure(options);
    }

    /**
     * The name of the component.
     * @memberof! ch.Positioner.prototype
     * @type {String}
     */
    Positioner.prototype.name = 'positioner';

    /**
     * Returns a reference to the Constructor function that created the instance's prototype.
     * @memberof! ch.Positioner.prototype
     * @function
     * @private
     */
    Positioner.prototype._constructor = Positioner;

    /**
     * Configuration by default.
     * @type {Object}
     * @private
     */
    Positioner.prototype._defaults = {
        'offsetX': 0,
        'offsetY': 0,
        'side': 'center',
        'align': 'center',
        'reference': ch.viewport,
        'position': 'fixed'
    };

    /**
     * Configures the positioner instance with a given options.
     * @memberof! ch.Positioner.prototype
     * @function
     * @private
     * @returns {positioner}
     * @params {Object} options A configuration object.
     */
    Positioner.prototype._configure = function (options) {

        // Merge user options with its options
        ch.util.extend(this._options, options);

        this._options.offsetX = parseInt(this._options.offsetX, 10);
        this._options.offsetY = parseInt(this._options.offsetY, 10);

        /**
         * Reference to the element to be positioned.
         * @type {HTMLElement}
         */
        this.target = options.target || this.target;

        /**
         * It's a reference to position and size of element that will be considered to carry out the position.
         * @type {HTMLElement}
         */
        this.reference = options.reference || this.reference;
        this._reference = this._options.reference;

        this.target.style.position = this._options.position;

        return this;
    };

    /**
     * Updates the current position with a given options
     * @memberof! ch.Positioner.prototype
     * @function
     * @returns {positioner}
     * @params {Object} options A configuration object.
     * @example
     * // Updates the current position.
     * positioned.refresh();
     * @example
     * // Updates the current position with new offsetX and offsetY.
     * positioned.refresh({
     *     'offestX': 100,
     *     'offestY': 10
     * });
     */
    Positioner.prototype.refresh = function (options) {

        if (options !== undefined) {
            this._configure(options);
        }

        if (this._reference !== ch.viewport) {
            this._calculateReference();
        }

        this._calculateTarget();

        // the object that stores the top, left reference to set to the target
        this._setPoint();

        return this;
    };

    /**
     * Calculates the reference (element or ch.viewport) of the position.
     * @memberof! ch.Positioner.prototype
     * @function
     * @private
     * @returns {positioner}
     */
    Positioner.prototype._calculateReference = function () {

        var reference = this.reference,
            offset;

        reference.setAttribute('data-side', this._options.side);
        reference.setAttribute('data-align', this._options.align);

        this._reference = ch.util.getOuterDimensions(reference);

        if (reference.offsetParent === this.target.offsetParent) {
            this._reference.left = reference.offsetLeft;
            this._reference.top = reference.offsetTop;

        } else {
            offset = ch.util.getOffset(reference);
            this._reference.left = offset.left;
            this._reference.top = offset.top;
        }

        return this;
    };

    /**
     * Calculates the positioned element.
     * @memberof! ch.Positioner.prototype
     * @function
     * @private
     * @returns {positioner}
     */
    Positioner.prototype._calculateTarget = function () {

        var target = this.target;
        target.setAttribute('data-side', this._options.side);
        target.setAttribute('data-align', this._options.align);

        this._target = ch.util.getOuterDimensions(target);

        return this;
    };

    /**
     * Calculates the points.
     * @memberof! ch.Positioner.prototype
     * @function
     * @private
     * @returns {positioner}
     */
    Positioner.prototype._setPoint = function () {
        var side = this._options.side,
            oritentation = (side === 'top' || side === 'bottom') ? 'horizontal' : ((side === 'right' || side === 'left') ? 'vertical' : 'center'),
            coors,
            oritentationMap;

        // take the side and calculate the alignment and make the CSSpoint
        if (oritentation === 'center') {
            // calculates the coordinates related to the center side to locate the target
            coors = {
                'top': (this._reference.top + (this._reference.height / 2 - this._target.height / 2)),
                'left': (this._reference.left + (this._reference.width / 2 - this._target.width / 2))
            };

        } else if (oritentation === 'horizontal') {
            // calculates the coordinates related to the top or bottom side to locate the target
            oritentationMap = {
                'left': this._reference.left,
                'center': (this._reference.left + (this._reference.width / 2 - this._target.width / 2)),
                'right': (this._reference.left + this._reference.width - this._target.width),
                'top': this._reference.top - this._target.height,
                'bottom': (this._reference.top + this._reference.height)
            };

            coors = {
                'top': oritentationMap[side],
                'left': oritentationMap[this._options.align]
            };

        } else {
            // calculates the coordinates related to the right or left side to locate the target
            oritentationMap = {
                'top': this._reference.top,
                'center': (this._reference.top + (this._reference.height / 2 - this._target.height / 2)),
                'bottom': (this._reference.top + this._reference.height - this._target.height),
                'right': (this._reference.left + this._reference.width),
                'left': (this._reference.left - this._target.width)
            };

            coors = {
                'top': oritentationMap[this._options.align],
                'left': oritentationMap[side]
            };
        }

        coors.top += this._options.offsetY;
        coors.left += this._options.offsetX;

        this.target.style.top = coors.top + 'px';
        this.target.style.left = coors.left + 'px';

        return this;
    };

    ch.Positioner = Positioner;

}(this, this.ch));<|endoftext|>


<commit_before>// Holds anything kiwi client specific (ie. front, gateway, _kiwi.plugs..)
/**
*   @namespace
*/
var _kiwi = {};

_kiwi.misc = {};
_kiwi.model = {};
_kiwi.view = {};
_kiwi.applets = {};
_kiwi.utils = {};

/**
 * A global container for third party access
 * Will be used to access a limited subset of kiwi functionality
 * and data (think: plugins)
 */
_kiwi.global = {
    build_version: '',  // Kiwi IRC version this is built from (Set from index.html)
    settings: undefined, // Instance of _kiwi.model.DataStore
    plugins: undefined, // Instance of _kiwi.model.PluginManager
    events: undefined, // Instance of PluginInterface
    rpc: undefined, // Instance of WebsocketRpc
    utils: {}, // References to misc. re-usable helpers / functions

    // Make public some internal utils for plugins to make use of
    initUtils: function() {
        this.utils.randomString = randomString;
        this.utils.secondsToTime = secondsToTime;
        this.utils.parseISO8601 = parseISO8601;
        this.utils.escapeRegex = escapeRegex;
        this.utils.formatIRCMsg = formatIRCMsg;
        this.utils.styleText = styleText;
        this.utils.hsl2rgb = hsl2rgb;
        this.utils.toUserMask = toUserMask;

        this.utils.notifications = _kiwi.utils.notifications;
        this.utils.formatDate = _kiwi.utils.formatDate;
    },

    addMediaMessageType: function(match, buildHtml) {
        _kiwi.view.MediaMessage.addType(match, buildHtml);
    },

    // Event managers for plugins
    components: {
        EventComponent: function(event_source, proxy_event_name) {
            /*
             * proxyEvent() listens for events then re-triggers them on its own
             * event emitter. Why? So we can .off() on this emitter without
             * effecting the source of events. Handy for plugins that we don't
             * trust meddling with the core events.
             *
             * If listening for 'all' events the arguments are as follows:
             *     1. Name of the triggered event
             *     2. The event data
             * For all other events, we only have one argument:
             *     1. The event data
             *
             * When this is used via `new kiwi.components.Network()`, this listens
             * for 'all' events so the first argument is the event name which is
             * the connection ID. We don't want to re-trigger this event name so
             * we need to juggle the arguments to find the real event name we want
             * to emit.
             */
            function proxyEvent(event_name, event_data) {
                if (proxy_event_name == 'all') {
                } else {
                    event_data = event_name.event_data;
                    event_name = event_name.event_name;
                }

                this.trigger(event_name, event_data);
            }

            // The event we are to proxy
            proxy_event_name = proxy_event_name || 'all';

            _.extend(this, Backbone.Events);
            this._source = event_source;

            // Proxy the events to this dispatcher
            event_source.on(proxy_event_name, proxyEvent, this);

            // Clean up this object
            this.dispose = function () {
                event_source.off(proxy_event_name, proxyEvent);
                this.off();
                delete this.event_source;
            };
        },

        Network: function(connection_id) {
            var connection_event;

            // If no connection id given, use all connections
            if (typeof connection_id !== 'undefined') {
                connection_event = 'connection:' + connection_id.toString();
            } else {
                connection_event = 'connection';
            }

            // Helper to get the network object
            var getNetwork = function() {
                var network = typeof connection_id === 'undefined' ?
                    _kiwi.app.connections.active_connection :
                    _kiwi.app.connections.getByConnectionId(connection_id);

                return network ?
                    network :
                    undefined;
            };

            // Create the return object (events proxy from the gateway)
            var obj = new this.EventComponent(_kiwi.gateway, connection_event);

            // Proxy several gateway functions onto the return object
            var funcs = {
                kiwi: 'kiwi', raw: 'raw', kick: 'kick', topic: 'topic',
                part: 'part', join: 'join', action: 'action', ctcp: 'ctcp',
                ctcpRequest: 'ctcpRequest', ctcpResponse: 'ctcpResponse',
                notice: 'notice', msg: 'privmsg', say: 'privmsg',
                changeNick: 'changeNick', channelInfo: 'channelInfo',
                mode: 'mode', quit: 'quit'
            };

            _.each(funcs, function(gateway_fn, func_name) {
                obj[func_name] = function() {
                    var fn_name = gateway_fn;

                    // Add connection_id to the argument list
                    var args = Array.prototype.slice.call(arguments, 0);
                    args.unshift(connection_id);

                    // Call the gateway function on behalf of this connection
                    return _kiwi.gateway[fn_name].apply(_kiwi.gateway, args);
                };
            });

            // Now for some network related functions...
            obj.createQuery = function(nick) {
                var network, restricted_keys;

                network = getNetwork();
                if (!network) {
                    return;
                }

                return network.createQuery(nick);
            };

            obj.ignoreMask = function(mask) {
                var network = getNetwork();
                if (!network) {
                    return;
                }

                return network.ignore_list.addMask(mask);
            };

            obj.unignoreMask = function(mask) {
                var network = getNetwork();
                if (!network) {
                    return;
                }

                return network.ignore_list.removeMask(mask);
            };

            // Add the networks getters/setters
            obj.get = function(name) {
                var network, restricted_keys;

                network = getNetwork();
                if (!network) {
                    return;
                }

                restricted_keys = [
                    'password'
                ];
                if (restricted_keys.indexOf(name) > -1) {
                    return undefined;
                }

                return network.get(name);
            };

            obj.set = function() {
                var network = getNetwork();
                if (!network) {
                    return;
                }

                return network.set.apply(network, arguments);
            };

            return obj;
        },

        ControlInput: function() {
            var obj = new this.EventComponent(_kiwi.app.controlbox);
            var funcs = {
                run: 'processInput', addPluginIcon: 'addPluginIcon'
            };

            _.each(funcs, function(controlbox_fn, func_name) {
                obj[func_name] = function() {
                    var fn_name = controlbox_fn;
                    return _kiwi.app.controlbox[fn_name].apply(_kiwi.app.controlbox, arguments);
                };
            });

            // Give access to the control input textarea
            obj.input = _kiwi.app.controlbox.$('.inp');

            return obj;
        }
    },

    // Entry point to start the kiwi application
    init: function (opts, callback) {
        var locale_promise, theme_promise,
            that = this;

        opts = opts || {};

        this.initUtils();

        // Set up the settings datastore
        _kiwi.global.settings = _kiwi.model.DataStore.instance('kiwi.settings');
        _kiwi.global.settings.load();

        // Set the window title
        window.document.title = opts.server_settings.client.window_title || 'Kiwi IRC';

        locale_promise = new Promise(function (resolve) {
            // In order, find a locale from the users saved settings, the URL, default settings on the server, or auto detect
            var locale = _kiwi.global.settings.get('locale') || opts.locale || opts.server_settings.client.settings.locale || 'magic';
            $.getJSON(opts.base_path + '/assets/locales/' + locale + '.json', function (locale) {
                if (locale) {
                    that.i18n = new Jed(locale);
                } else {
                    that.i18n = new Jed();
                }
                resolve();
            });
        });

        theme_promise = new Promise(function (resolve) {
            var text_theme = opts.server_settings.client.settings.text_theme || 'default';
            $.getJSON(opts.base_path + '/assets/text_themes/' + text_theme + '.json', function(text_theme) {
                opts.text_theme = text_theme;
                resolve();
            });
        });

        Promise.all([locale_promise, theme_promise]).then(function () {
            _kiwi.app = new _kiwi.model.Application(opts);

            // Start the client up
            _kiwi.app.initializeInterfaces();

            // Event emitter to let plugins interface with parts of kiwi
            _kiwi.global.events  = new PluginInterface();

            // Now everything has started up, load the plugin manager for third party plugins
            _kiwi.global.plugins = new _kiwi.model.PluginManager();

            callback();

        }).then(null, function(err) {
            console.error(err.stack);
        });
    },

    start: function() {
        _kiwi.app.showStartup();
    },

    // Allow plugins to change the startup applet
    registerStartupApplet: function(startup_applet_name) {
        _kiwi.app.startup_applet_name = startup_applet_name;
    },

    /**
     * Open a new IRC connection
     * @param {Object} connection_details {nick, host, port, ssl, password, options}
     * @param {Function} callback function(err, network){}
     */
    newIrcConnection: function(connection_details, callback) {
        _kiwi.gateway.newConnection(connection_details, callback);
    },

    /**
     * Taking settings from the server and URL, extract the default server/channel/nick settings
     */
    defaultServerSettings: function () {
        var parts;
        var defaults = {
            nick: '',
            server: '',
            port: 6667,
            ssl: false,
            channel: '',
            channel_key: ''
        };
        var uricheck;

        /**
         * Get any settings set by the server
         * These settings may be changed in the server selection dialog or via URL parameters
         */
        if (_kiwi.app.server_settings.client) {
            if (_kiwi.app.server_settings.client.nick)
                defaults.nick = _kiwi.app.server_settings.client.nick;

            if (_kiwi.app.server_settings.client.server)
                defaults.server = _kiwi.app.server_settings.client.server;

            if (_kiwi.app.server_settings.client.port)
                defaults.port = _kiwi.app.server_settings.client.port;

            if (_kiwi.app.server_settings.client.ssl)
                defaults.ssl = _kiwi.app.server_settings.client.ssl;

            if (_kiwi.app.server_settings.client.channel)
                defaults.channel = _kiwi.app.server_settings.client.channel;

            if (_kiwi.app.server_settings.client.channel_key)
                defaults.channel_key = _kiwi.app.server_settings.client.channel_key;
        }

        /**
         * Get any settings passed in the URL
         * These settings may be changed in the server selection dialog
         */

        // Any query parameters first
        if (getQueryVariable('nick'))
            defaults.nick = getQueryVariable('nick');

        if (window.location.hash)
            defaults.channel = window.location.hash;

        // Process the URL part by part, extracting as we go
        parts = window.location.pathname.toString().replace(_kiwi.app.get('base_path'), '').split('/');

        if (parts.length > 0) {
            parts.shift();

            if (parts.length > 0 && parts[0]) {
                // Check to see if we're dealing with an irc: uri, or whether we need to extract the server/channel info from the HTTP URL path.
                uricheck = parts[0].substr(0, 7).toLowerCase();
                if ((uricheck === 'ircs%3a') || (uricheck.substr(0,6) === 'irc%3a')) {
                    parts[0] = decodeURIComponent(parts[0]);
                    // irc[s]://<host>[:<port>]/[<channel>[?<password>]]
                    uricheck = /^irc(s)?:(?:\/\/?)?([^:\/]+)(?::([0-9]+))?(?:(?:\/)([^\?]*)(?:(?:\?)(.*))?)?$/.exec(parts[0]);
                    /*
                        uricheck[1] = ssl (optional)
                        uricheck[2] = host
                        uricheck[3] = port (optional)
                        uricheck[4] = channel (optional)
                        uricheck[5] = channel key (optional, channel must also be set)
                    */
                    if (uricheck) {
                        if (typeof uricheck[1] !== 'undefined') {
                            defaults.ssl = true;
                            if (defaults.port === 6667) {
                                defaults.port = 6697;
                            }
                        }
                        defaults.server = uricheck[2];
                        if (typeof uricheck[3] !== 'undefined') {
                            defaults.port = uricheck[3];
                        }
                        if (typeof uricheck[4] !== 'undefined') {
                            defaults.channel = '#' + uricheck[4];
                            if (typeof uricheck[5] !== 'undefined') {
                                defaults.channel_key = uricheck[5];
                            }
                        }
                    }
                    parts = [];
                } else {
                    // Extract the port+ssl if we find one
                    if (parts[0].search(/:/) > 0) {
                        defaults.port = parts[0].substring(parts[0].search(/:/) + 1);
                        defaults.server = parts[0].substring(0, parts[0].search(/:/));
                        if (defaults.port[0] === '+') {
                            defaults.port = parseInt(defaults.port.substring(1), 10);
                            defaults.ssl = true;
                        } else {
                            defaults.ssl = false;
                        }

                    } else {
                        defaults.server = parts[0];
                    }

                    parts.shift();
                }
            }

            if (parts.length > 0 && parts[0]) {
                defaults.channel = '#' + parts[0];
                parts.shift();
            }
        }

        // If any settings have been given by the server.. override any auto detected settings
        /**
         * Get any server restrictions as set in the server config
         * These settings can not be changed in the server selection dialog
         */
        if (_kiwi.app.server_settings && _kiwi.app.server_settings.connection) {
            if (_kiwi.app.server_settings.connection.server) {
                defaults.server = _kiwi.app.server_settings.connection.server;
            }

            if (_kiwi.app.server_settings.connection.port) {
                defaults.port = _kiwi.app.server_settings.connection.port;
            }

            if (_kiwi.app.server_settings.connection.ssl) {
                defaults.ssl = _kiwi.app.server_settings.connection.ssl;
            }
        }

        // Set any random numbers if needed
        defaults.nick = defaults.nick.replace(/\?/g, Math.floor(Math.random() * 100000).toString());

        if (getQueryVariable('encoding'))
            defaults.encoding = getQueryVariable('encoding');

        return defaults;
    },
};

// If within a closure, expose the kiwi globals
if (typeof global !== 'undefined') {
    global.kiwi = _kiwi.global;
} else {
    // Not within a closure so set a var in the current scope
    var kiwi = _kiwi.global;
}
<commit_msg>Correctly parsing channels irc:// URIs when provided with a # channel prefix #847<commit_after>// Holds anything kiwi client specific (ie. front, gateway, _kiwi.plugs..)
/**
*   @namespace
*/
var _kiwi = {};

_kiwi.misc = {};
_kiwi.model = {};
_kiwi.view = {};
_kiwi.applets = {};
_kiwi.utils = {};

/**
 * A global container for third party access
 * Will be used to access a limited subset of kiwi functionality
 * and data (think: plugins)
 */
_kiwi.global = {
    build_version: '',  // Kiwi IRC version this is built from (Set from index.html)
    settings: undefined, // Instance of _kiwi.model.DataStore
    plugins: undefined, // Instance of _kiwi.model.PluginManager
    events: undefined, // Instance of PluginInterface
    rpc: undefined, // Instance of WebsocketRpc
    utils: {}, // References to misc. re-usable helpers / functions

    // Make public some internal utils for plugins to make use of
    initUtils: function() {
        this.utils.randomString = randomString;
        this.utils.secondsToTime = secondsToTime;
        this.utils.parseISO8601 = parseISO8601;
        this.utils.escapeRegex = escapeRegex;
        this.utils.formatIRCMsg = formatIRCMsg;
        this.utils.styleText = styleText;
        this.utils.hsl2rgb = hsl2rgb;
        this.utils.toUserMask = toUserMask;

        this.utils.notifications = _kiwi.utils.notifications;
        this.utils.formatDate = _kiwi.utils.formatDate;
    },

    addMediaMessageType: function(match, buildHtml) {
        _kiwi.view.MediaMessage.addType(match, buildHtml);
    },

    // Event managers for plugins
    components: {
        EventComponent: function(event_source, proxy_event_name) {
            /*
             * proxyEvent() listens for events then re-triggers them on its own
             * event emitter. Why? So we can .off() on this emitter without
             * effecting the source of events. Handy for plugins that we don't
             * trust meddling with the core events.
             *
             * If listening for 'all' events the arguments are as follows:
             *     1. Name of the triggered event
             *     2. The event data
             * For all other events, we only have one argument:
             *     1. The event data
             *
             * When this is used via `new kiwi.components.Network()`, this listens
             * for 'all' events so the first argument is the event name which is
             * the connection ID. We don't want to re-trigger this event name so
             * we need to juggle the arguments to find the real event name we want
             * to emit.
             */
            function proxyEvent(event_name, event_data) {
                if (proxy_event_name == 'all') {
                } else {
                    event_data = event_name.event_data;
                    event_name = event_name.event_name;
                }

                this.trigger(event_name, event_data);
            }

            // The event we are to proxy
            proxy_event_name = proxy_event_name || 'all';

            _.extend(this, Backbone.Events);
            this._source = event_source;

            // Proxy the events to this dispatcher
            event_source.on(proxy_event_name, proxyEvent, this);

            // Clean up this object
            this.dispose = function () {
                event_source.off(proxy_event_name, proxyEvent);
                this.off();
                delete this.event_source;
            };
        },

        Network: function(connection_id) {
            var connection_event;

            // If no connection id given, use all connections
            if (typeof connection_id !== 'undefined') {
                connection_event = 'connection:' + connection_id.toString();
            } else {
                connection_event = 'connection';
            }

            // Helper to get the network object
            var getNetwork = function() {
                var network = typeof connection_id === 'undefined' ?
                    _kiwi.app.connections.active_connection :
                    _kiwi.app.connections.getByConnectionId(connection_id);

                return network ?
                    network :
                    undefined;
            };

            // Create the return object (events proxy from the gateway)
            var obj = new this.EventComponent(_kiwi.gateway, connection_event);

            // Proxy several gateway functions onto the return object
            var funcs = {
                kiwi: 'kiwi', raw: 'raw', kick: 'kick', topic: 'topic',
                part: 'part', join: 'join', action: 'action', ctcp: 'ctcp',
                ctcpRequest: 'ctcpRequest', ctcpResponse: 'ctcpResponse',
                notice: 'notice', msg: 'privmsg', say: 'privmsg',
                changeNick: 'changeNick', channelInfo: 'channelInfo',
                mode: 'mode', quit: 'quit'
            };

            _.each(funcs, function(gateway_fn, func_name) {
                obj[func_name] = function() {
                    var fn_name = gateway_fn;

                    // Add connection_id to the argument list
                    var args = Array.prototype.slice.call(arguments, 0);
                    args.unshift(connection_id);

                    // Call the gateway function on behalf of this connection
                    return _kiwi.gateway[fn_name].apply(_kiwi.gateway, args);
                };
            });

            // Now for some network related functions...
            obj.createQuery = function(nick) {
                var network, restricted_keys;

                network = getNetwork();
                if (!network) {
                    return;
                }

                return network.createQuery(nick);
            };

            obj.ignoreMask = function(mask) {
                var network = getNetwork();
                if (!network) {
                    return;
                }

                return network.ignore_list.addMask(mask);
            };

            obj.unignoreMask = function(mask) {
                var network = getNetwork();
                if (!network) {
                    return;
                }

                return network.ignore_list.removeMask(mask);
            };

            // Add the networks getters/setters
            obj.get = function(name) {
                var network, restricted_keys;

                network = getNetwork();
                if (!network) {
                    return;
                }

                restricted_keys = [
                    'password'
                ];
                if (restricted_keys.indexOf(name) > -1) {
                    return undefined;
                }

                return network.get(name);
            };

            obj.set = function() {
                var network = getNetwork();
                if (!network) {
                    return;
                }

                return network.set.apply(network, arguments);
            };

            return obj;
        },

        ControlInput: function() {
            var obj = new this.EventComponent(_kiwi.app.controlbox);
            var funcs = {
                run: 'processInput', addPluginIcon: 'addPluginIcon'
            };

            _.each(funcs, function(controlbox_fn, func_name) {
                obj[func_name] = function() {
                    var fn_name = controlbox_fn;
                    return _kiwi.app.controlbox[fn_name].apply(_kiwi.app.controlbox, arguments);
                };
            });

            // Give access to the control input textarea
            obj.input = _kiwi.app.controlbox.$('.inp');

            return obj;
        }
    },

    // Entry point to start the kiwi application
    init: function (opts, callback) {
        var locale_promise, theme_promise,
            that = this;

        opts = opts || {};

        this.initUtils();

        // Set up the settings datastore
        _kiwi.global.settings = _kiwi.model.DataStore.instance('kiwi.settings');
        _kiwi.global.settings.load();

        // Set the window title
        window.document.title = opts.server_settings.client.window_title || 'Kiwi IRC';

        locale_promise = new Promise(function (resolve) {
            // In order, find a locale from the users saved settings, the URL, default settings on the server, or auto detect
            var locale = _kiwi.global.settings.get('locale') || opts.locale || opts.server_settings.client.settings.locale || 'magic';
            $.getJSON(opts.base_path + '/assets/locales/' + locale + '.json', function (locale) {
                if (locale) {
                    that.i18n = new Jed(locale);
                } else {
                    that.i18n = new Jed();
                }
                resolve();
            });
        });

        theme_promise = new Promise(function (resolve) {
            var text_theme = opts.server_settings.client.settings.text_theme || 'default';
            $.getJSON(opts.base_path + '/assets/text_themes/' + text_theme + '.json', function(text_theme) {
                opts.text_theme = text_theme;
                resolve();
            });
        });

        Promise.all([locale_promise, theme_promise]).then(function () {
            _kiwi.app = new _kiwi.model.Application(opts);

            // Start the client up
            _kiwi.app.initializeInterfaces();

            // Event emitter to let plugins interface with parts of kiwi
            _kiwi.global.events  = new PluginInterface();

            // Now everything has started up, load the plugin manager for third party plugins
            _kiwi.global.plugins = new _kiwi.model.PluginManager();

            callback();

        }).then(null, function(err) {
            console.error(err.stack);
        });
    },

    start: function() {
        _kiwi.app.showStartup();
    },

    // Allow plugins to change the startup applet
    registerStartupApplet: function(startup_applet_name) {
        _kiwi.app.startup_applet_name = startup_applet_name;
    },

    /**
     * Open a new IRC connection
     * @param {Object} connection_details {nick, host, port, ssl, password, options}
     * @param {Function} callback function(err, network){}
     */
    newIrcConnection: function(connection_details, callback) {
        _kiwi.gateway.newConnection(connection_details, callback);
    },

    /**
     * Taking settings from the server and URL, extract the default server/channel/nick settings
     */
    defaultServerSettings: function () {
        var parts;
        var defaults = {
            nick: '',
            server: '',
            port: 6667,
            ssl: false,
            channel: '',
            channel_key: ''
        };
        var uricheck;

        /**
         * Get any settings set by the server
         * These settings may be changed in the server selection dialog or via URL parameters
         */
        if (_kiwi.app.server_settings.client) {
            if (_kiwi.app.server_settings.client.nick)
                defaults.nick = _kiwi.app.server_settings.client.nick;

            if (_kiwi.app.server_settings.client.server)
                defaults.server = _kiwi.app.server_settings.client.server;

            if (_kiwi.app.server_settings.client.port)
                defaults.port = _kiwi.app.server_settings.client.port;

            if (_kiwi.app.server_settings.client.ssl)
                defaults.ssl = _kiwi.app.server_settings.client.ssl;

            if (_kiwi.app.server_settings.client.channel)
                defaults.channel = _kiwi.app.server_settings.client.channel;

            if (_kiwi.app.server_settings.client.channel_key)
                defaults.channel_key = _kiwi.app.server_settings.client.channel_key;
        }

        /**
         * Get any settings passed in the URL
         * These settings may be changed in the server selection dialog
         */

        // Any query parameters first
        if (getQueryVariable('nick'))
            defaults.nick = getQueryVariable('nick');

        if (window.location.hash)
            defaults.channel = window.location.hash;

        // Process the URL part by part, extracting as we go
        parts = window.location.pathname.toString().replace(_kiwi.app.get('base_path'), '').split('/');

        if (parts.length > 0) {
            parts.shift();

            if (parts.length > 0 && parts[0]) {
                // Check to see if we're dealing with an irc: uri, or whether we need to extract the server/channel info from the HTTP URL path.
                uricheck = parts[0].substr(0, 7).toLowerCase();
                if ((uricheck === 'ircs%3a') || (uricheck.substr(0,6) === 'irc%3a')) {
                    parts[0] = decodeURIComponent(parts[0]);
                    // irc[s]://<host>[:<port>]/[<channel>[?<password>]]
                    uricheck = /^irc(s)?:(?:\/\/?)?([^:\/]+)(?::([0-9]+))?(?:(?:\/)([^\?]*)(?:(?:\?)(.*))?)?$/.exec(parts[0]);
                    /*
                        uricheck[1] = ssl (optional)
                        uricheck[2] = host
                        uricheck[3] = port (optional)
                        uricheck[4] = channel (optional)
                        uricheck[5] = channel key (optional, channel must also be set)
                    */
                    if (uricheck) {
                        if (typeof uricheck[1] !== 'undefined') {
                            defaults.ssl = true;
                            if (defaults.port === 6667) {
                                defaults.port = 6697;
                            }
                        }
                        defaults.server = uricheck[2];
                        if (typeof uricheck[3] !== 'undefined') {
                            defaults.port = uricheck[3];
                        }
                        if (typeof uricheck[4] !== 'undefined') {
                            // Only prepend # if it doesn't already start with it
                            defaults.channel = (uricheck[4][0] === '#') ?
                                uricheck[4] :
                                '#' + uricheck[4];

                            if (typeof uricheck[5] !== 'undefined') {
                                defaults.channel_key = uricheck[5];
                            }
                        }
                    }
                    parts = [];
                } else {
                    // Extract the port+ssl if we find one
                    if (parts[0].search(/:/) > 0) {
                        defaults.port = parts[0].substring(parts[0].search(/:/) + 1);
                        defaults.server = parts[0].substring(0, parts[0].search(/:/));
                        if (defaults.port[0] === '+') {
                            defaults.port = parseInt(defaults.port.substring(1), 10);
                            defaults.ssl = true;
                        } else {
                            defaults.ssl = false;
                        }

                    } else {
                        defaults.server = parts[0];
                    }

                    parts.shift();
                }
            }

            if (parts.length > 0 && parts[0]) {
                defaults.channel = '#' + parts[0];
                parts.shift();
            }
        }

        // If any settings have been given by the server.. override any auto detected settings
        /**
         * Get any server restrictions as set in the server config
         * These settings can not be changed in the server selection dialog
         */
        if (_kiwi.app.server_settings && _kiwi.app.server_settings.connection) {
            if (_kiwi.app.server_settings.connection.server) {
                defaults.server = _kiwi.app.server_settings.connection.server;
            }

            if (_kiwi.app.server_settings.connection.port) {
                defaults.port = _kiwi.app.server_settings.connection.port;
            }

            if (_kiwi.app.server_settings.connection.ssl) {
                defaults.ssl = _kiwi.app.server_settings.connection.ssl;
            }
        }

        // Set any random numbers if needed
        defaults.nick = defaults.nick.replace(/\?/g, Math.floor(Math.random() * 100000).toString());

        if (getQueryVariable('encoding'))
            defaults.encoding = getQueryVariable('encoding');

        return defaults;
    },
};

// If within a closure, expose the kiwi globals
if (typeof global !== 'undefined') {
    global.kiwi = _kiwi.global;
} else {
    // Not within a closure so set a var in the current scope
    var kiwi = _kiwi.global;
}
<|endoftext|>


<commit_before>'use strict';
var colName = 'services';
var fs = require('fs');
var formidable = require('formidable');
var util = require('util');
var unzip = require('unzip');
var ncp = require('ncp').ncp;
var shelljs = require('shelljs');

function extractAPIsList(schema) {
    var excluded = ['commonFields'];
    var apiList = [];
    for (var route in schema) {
        if (Object.hasOwnProperty.call(schema, route)) {
            if (excluded.indexOf(route) !== -1) {
                continue;
            }

            var oneApi = {
                'l': schema[route]._apiInfo.l,
                'v': route
            };

            if (schema[route]._apiInfo.group) {
                oneApi.group = schema[route]._apiInfo.group;
            }

            if (schema[route]._apiInfo.groupMain) {
                oneApi.groupMain = schema[route]._apiInfo.groupMain;
            }

            apiList.push(oneApi);
        }
    }
    return apiList;
}

module.exports = {

    "list": function (config, mongo, req, res) {
        var criteria = ((req.soajs.inputmaskData.serviceNames) && (req.soajs.inputmaskData.serviceNames.length > 0)) ? {'name': {$in: req.soajs.inputmaskData.serviceNames}} : {};
        mongo.find(colName, criteria, function (err, records) {
            if (err) {
                return res.jsonp(req.soajs.buildResponse({"code": 600, "msg": config.errors[600]}));
            }
            return res.jsonp(req.soajs.buildResponse(null, records));
        });
    },

    "update": function (config, mongo, req, res) {
        var set = {
            '$set': {
                "extKeyRequired": req.soajs.inputmaskData.extKeyRequired || false,
                "requestTimeout": req.soajs.inputmaskData.requestTimeout || null,
                "requestTimeoutRenewal": req.soajs.inputmaskData.requestTimeoutRenewal || null,
                "apis": req.soajs.inputmaskData.apis,
                "awareness": req.soajs.inputmaskData.awareness
            }
        };
        mongo.update(colName, {'name': req.soajs.inputmaskData.name}, set, {
            'upsert': false,
            'safe': true
        }, function (err, data) {
            if (err) {
                return res.jsonp(req.soajs.buildResponse({"code": 600, "msg": config.errors[600]}));
            }
            if (data === 0) {
                return res.jsonp(req.soajs.buildResponse({"code": 604, "msg": config.errors[604]}));
            }
            return res.jsonp(req.soajs.buildResponse(null, "service updated successfully."));
        });
    },

    "upload": function (config, mongo, req, res) {

        var form = new formidable.IncomingForm();
        form.encoding = 'utf-8';
        form.uploadDir = config.workDir + "soajs" + config.upload.uploadFolderName;
        form.keepExtensions = true;

        form.parse(req, function (err, fields, files) {
            if (err) {
                return res.jsonp(req.soajs.buildResponse({code: 616, msg: config.errors[616]}));
            }

            var fileName = Object.keys(files)[0];
            if (!files || Object.keys(files).length === 0) {
                fs.unlinkSync(files[fileName].path);
                return res.jsonp(req.soajs.buildResponse({code: 616, msg: config.errors[616]}));
            }

            if (files && fileName && files[fileName].type !== 'application/zip' && files[fileName].name.indexOf(".zip") === -1) {
                fs.unlinkSync(files[fileName].path);
                return res.jsonp(req.soajs.buildResponse({code: 616, msg: config.errors[616]}));
            }

            fs.createReadStream(files[fileName].path)
                .pipe(unzip.Extract({"path": config.workDir + "soajs" + config.upload.uploadFolderName}))
                .on('close', function () {
                    var tmpFolder = files[fileName].name.replace('.zip', '');
                    var tmpPath = config.workDir + "soajs" + config.upload.uploadFolderName + tmpFolder;

                    var validatorSchemas = require("../schemas/upload.js");
                    var configFile = tmpPath + "/config.js";
                    var packageFile = tmpPath + "/package.json";

                    checkIFFile(configFile, validatorSchemas.config, function (error, loadedConfigFile) {
                        if (error) {
                            shelljs.rm('-rf', tmpPath);
                            shelljs.rm('-f', files[fileName].path);
                            return res.json(req.soajs.buildResponse({code: 617, msg: error.message}));
                        }
                        req.soajs.log.debug(configFile + "is valid");
                        checkIFFile(packageFile, validatorSchemas.package, function (error) {
                            if (error) {
                                shelljs.rm('-rf', tmpPath);
                                shelljs.rm('-f', files[fileName].path);
                                return res.json(req.soajs.buildResponse({code: 618, msg: error.message}));
                            }
                            req.soajs.log.debug(packageFile + "is valid");

                            //move the service to where it should be located eventually
                            validateBuildTenantDir(config.workDir + "soajs" + config.upload.tenantFolderName + req.soajs.tenant.code, loadedConfigFile.serviceName, function (error) {
                                if (error) {
                                    req.soajs.log.error(error);
                                    return res.jsonp(req.soajs.buildResponse({"code": 616, "msg": error.message}));
                                }
                                moveFiles(files, tmpPath, loadedConfigFile.serviceName, fileName, function (serviceFolder) {
                                    var doc = {
                                        '$set': {
                                            'port': loadedConfigFile.servicePort,
                                            'extKeyRequired': loadedConfigFile.extKeyRequired,
                                            "awareness": loadedConfigFile.awareness || false,
                                            "requestTimeout": loadedConfigFile.requestTimeout,
                                            "requestTimeoutRenewal": loadedConfigFile.requestTimeoutRenewal,
                                            "apis": extractAPIsList(loadedConfigFile.schema),
                                            "custom": serviceFolder
                                        }
                                    };
                                    mongo.update("services", {'name': loadedConfigFile.serviceName}, doc, {'upsert': true}, function (error) {
                                        if (error) {
                                            return res.jsonp(req.soajs.buildResponse({
                                                'code': 617,
                                                'msg': config.errors[617]
                                            }));
                                        }

                                        return res.jsonp(req.soajs.buildResponse(null, true));
                                    });
                                });
                            });
                        });
                    });
                });
        });

        function moveFiles(files, tmpPath, serviceName, fileName, cb) {
            ncp.limit = config.ncpLimit;
            var srvdest = config.workDir + "soajs" + config.upload.tenantFolderName + req.soajs.tenant.code + config.upload.servicesFolderName + serviceName;
            var uiDest = config.workDir + "soajs" + config.upload.tenantFolderName + req.soajs.tenant.code + config.upload.uiFolderName + serviceName;
            req.soajs.log.debug("copying upload module:" + tmpPath);

            ncp(tmpPath, srvdest, function (err) {
                if (err) {
                    shelljs.rm('-rf', tmpPath);
                    shelljs.rm('-f', files[fileName].path);
                    return res.json(req.soajs.buildResponse({code: 619, msg: err.message}));
                }

                if (fs.existsSync(srvdest + "/ui")) {
                    shelljs.mv(srvdest + config.upload.uiFolderName + "*", uiDest);
                    shelljs.rm('-rf', srvdest + "/ui");
                }

                if (fs.existsSync(srvdest + "/ui-dashboard")) {
                    var dashUIDest = config.workDir + "soajs/open_source" + config.upload.dashboardFolderName + "modules/" + serviceName;
                    shelljs.mkdir("-p", dashUIDest);
                    ncp(srvdest + "/ui-dashboard/" + serviceName, dashUIDest, function(err){
                        if(err){
                            req.soajs.log.error(err);
                        }

                        installModuleInDashboardUI(serviceName, function(err){
                            if(err){ req.soajs.log.error(err); }
                            shelljs.rm('-rf', srvdest + "/ui-dashboard");
                        });
                    });
                }

                req.soajs.log.debug("cleaned up upload and tmp files");
                shelljs.rm('-rf', tmpPath);
                shelljs.rm('-f', files[fileName].path);

                return cb(srvdest);
            });
        }

        function installModuleInDashboardUI(serviceName, cb){
            var uiFile = config.workDir + "soajs/open_source" + config.upload.dashboardFolderName + "index.html";
            fs.readFile(uiFile, 'utf8', function(err, data){
               if(err){ return cb(err); }

                data = data.split('<script src="app/controller.js"></script>');
                data[0] += '<script src="modules/' + serviceName + '/install.js"></script>';
                data = data.join('<script src="app/controller.js"></script>')

                fs.writeFile(uiFile, data, cb);
            });
        }

        function validateBuildTenantDir(path, serviceName, cb) {
            var p1 = path + config.upload.servicesFolderName + serviceName;
            var p2 = path + config.upload.uiFolderName + serviceName;

            checkBuildPath(p1, function (error) {
                if (error) {
                    return cb(error);
                }
                checkBuildPath(p2, cb);
            });

            function checkBuildPath(path, cb) {
                fs.exists(path, function (exists) {
                    if (exists) {
                        fs.stat(path, function (err, stats) {
                            if (err) {
                                return cb(err);
                            }
                            else if (!stats.isDirectory()) {
                                return cb(new Error(path + " is not a directory."));
                            }
                            return cb(null, true);
                        });
                    }
                    else {
                        req.soajs.log.debug("destination: " + path + " not found. Creating Directory...");
                        shelljs.mkdir("-p", path);
                        return cb(null, true);
                    }
                });
            }
        }

        function checkIFFile(moduleFile, schema, cb) {

            fs.exists(moduleFile, function (exists) {
                if (!exists) {
                    return cb(new Error(moduleFile + " not Found!"));
                }

                fs.stat(moduleFile, function (err, stats) {
                    if (err)
                        return cb(new Error("Error reading" + moduleFile));
                    else {
                        if (!stats.isFile()) {
                            return cb(new Error(moduleFile + " is not a file!"));
                        }

                        validateFile(moduleFile, schema, cb);
                    }
                });
            });
        }

        function validateFile(filePath, schema, cb) {
            var core = require("soajs/modules/soajs.core");
            var validator = new core.validator.Validator();

            var errMsgs = [];
            if (require.resolve(filePath)) {
                delete require.cache[require.resolve(filePath)];
            }
            var loadedFile = require(filePath);

            //validate package.json
            var check = validator.validate(loadedFile, schema);
            if (!check.valid) {
                check.errors.forEach(function (oneError) {
                    errMsgs.push(oneError.stack);
                });
                return cb(new Error(errMsgs.join(" - ")));
            }

            if (filePath.indexOf("package.json") === -1) {
                mongo.findOne('services', {
                    'port': loadedFile.servicePort,
                    'name': {$ne: loadedFile.serviceName}
                }, function (error, oneRecord) {
                    if (error) {
                        return cb(error);
                    }

                    if (oneRecord) {
                        return cb(new Error("Another service with the same port exists"));
                    }

                    return cb(null, loadedFile);
                });
            }
            else {
                loadedFile.peerDependencies = {
                    "soajs": loadedFile.dependencies.soajs
                };
                delete loadedFile.dependencies.soajs;
                fs.writeFile(filePath, JSON.stringify(loadedFile), "utf8", cb);
            }
        }

        function extractAPIsList(schema) {
            var excluded = ['commonFields'];
            var apiList = [];
            for (var route in schema) {
                if (Object.hasOwnProperty.call(schema, route)) {
                    if (excluded.indexOf(route) !== -1) {
                        continue;
                    }

                    var oneApi = {
                        'l': schema[route]._apiInfo.l,
                        'v': route
                    };

                    if (schema[route]._apiInfo.group) {
                        oneApi.group = schema[route]._apiInfo.group;
                    }

                    if (schema[route]._apiInfo.groupMain) {
                        oneApi.groupMain = schema[route]._apiInfo.groupMain;
                    }

                    apiList.push(oneApi);
                }
            }
            return apiList;
        }
    }
};<commit_msg>minor update.<commit_after>'use strict';
var colName = 'services';
var fs = require('fs');
var formidable = require('formidable');
var util = require('util');
var unzip = require('unzip');
var ncp = require('ncp').ncp;
var shelljs = require('shelljs');

function extractAPIsList(schema) {
    var excluded = ['commonFields'];
    var apiList = [];
    for (var route in schema) {
        if (Object.hasOwnProperty.call(schema, route)) {
            if (excluded.indexOf(route) !== -1) {
                continue;
            }

            var oneApi = {
                'l': schema[route]._apiInfo.l,
                'v': route
            };

            if (schema[route]._apiInfo.group) {
                oneApi.group = schema[route]._apiInfo.group;
            }

            if (schema[route]._apiInfo.groupMain) {
                oneApi.groupMain = schema[route]._apiInfo.groupMain;
            }

            apiList.push(oneApi);
        }
    }
    return apiList;
}

module.exports = {

    "list": function (config, mongo, req, res) {
        var criteria = ((req.soajs.inputmaskData.serviceNames) && (req.soajs.inputmaskData.serviceNames.length > 0)) ? {'name': {$in: req.soajs.inputmaskData.serviceNames}} : {};
        mongo.find(colName, criteria, function (err, records) {
            if (err) {
                return res.jsonp(req.soajs.buildResponse({"code": 600, "msg": config.errors[600]}));
            }
            return res.jsonp(req.soajs.buildResponse(null, records));
        });
    },

    "update": function (config, mongo, req, res) {
        var set = {
            '$set': {
                "extKeyRequired": req.soajs.inputmaskData.extKeyRequired || false,
                "requestTimeout": req.soajs.inputmaskData.requestTimeout || null,
                "requestTimeoutRenewal": req.soajs.inputmaskData.requestTimeoutRenewal || null,
                "apis": req.soajs.inputmaskData.apis,
                "awareness": req.soajs.inputmaskData.awareness
            }
        };
        mongo.update(colName, {'name': req.soajs.inputmaskData.name}, set, {
            'upsert': false,
            'safe': true
        }, function (err, data) {
            if (err) {
                return res.jsonp(req.soajs.buildResponse({"code": 600, "msg": config.errors[600]}));
            }
            if (data === 0) {
                return res.jsonp(req.soajs.buildResponse({"code": 604, "msg": config.errors[604]}));
            }
            return res.jsonp(req.soajs.buildResponse(null, "service updated successfully."));
        });
    },

    "upload": function (config, mongo, req, res) {

        var form = new formidable.IncomingForm();
        form.encoding = 'utf-8';
        form.uploadDir = config.workDir + "soajs" + config.upload.uploadFolderName;
        form.keepExtensions = true;

        form.parse(req, function (err, fields, files) {
            if (err) {
                return res.jsonp(req.soajs.buildResponse({code: 616, msg: config.errors[616]}));
            }

            var fileName = Object.keys(files)[0];
            if (!files || Object.keys(files).length === 0) {
                fs.unlinkSync(files[fileName].path);
                return res.jsonp(req.soajs.buildResponse({code: 616, msg: config.errors[616]}));
            }

            if (files && fileName && files[fileName].type !== 'application/zip' && files[fileName].name.indexOf(".zip") === -1) {
                fs.unlinkSync(files[fileName].path);
                return res.jsonp(req.soajs.buildResponse({code: 616, msg: config.errors[616]}));
            }

            fs.createReadStream(files[fileName].path)
                .pipe(unzip.Extract({"path": config.workDir + "soajs" + config.upload.uploadFolderName}))
                .on('close', function () {
                    var tmpFolder = files[fileName].name.replace('.zip', '');
                    var tmpPath = config.workDir + "soajs" + config.upload.uploadFolderName + tmpFolder;

                    var validatorSchemas = require("../schemas/upload.js");
                    var configFile = tmpPath + "/config.js";
                    var packageFile = tmpPath + "/package.json";

                    checkIFFile(configFile, validatorSchemas.config, function (error, loadedConfigFile) {
                        if (error) {
                            shelljs.rm('-rf', tmpPath);
                            shelljs.rm('-f', files[fileName].path);
                            return res.json(req.soajs.buildResponse({code: 617, msg: error.message}));
                        }
                        req.soajs.log.debug(configFile + "is valid");
                        checkIFFile(packageFile, validatorSchemas.package, function (error) {
                            if (error) {
                                shelljs.rm('-rf', tmpPath);
                                shelljs.rm('-f', files[fileName].path);
                                return res.json(req.soajs.buildResponse({code: 618, msg: error.message}));
                            }
                            req.soajs.log.debug(packageFile + "is valid");

                            //move the service to where it should be located eventually
                            validateBuildTenantDir(config.workDir + "soajs" + config.upload.tenantFolderName + req.soajs.tenant.code, loadedConfigFile.serviceName, function (error) {
                                if (error) {
                                    req.soajs.log.error(error);
                                    return res.jsonp(req.soajs.buildResponse({"code": 616, "msg": error.message}));
                                }
                                moveFiles(files, tmpPath, loadedConfigFile.serviceName, fileName, function (serviceFolder) {
                                    var doc = {
                                        '$set': {
                                            'port': loadedConfigFile.servicePort,
                                            'extKeyRequired': loadedConfigFile.extKeyRequired,
                                            "awareness": loadedConfigFile.awareness || false,
                                            "requestTimeout": loadedConfigFile.requestTimeout,
                                            "requestTimeoutRenewal": loadedConfigFile.requestTimeoutRenewal,
                                            "apis": extractAPIsList(loadedConfigFile.schema),
                                            "custom": serviceFolder
                                        }
                                    };
                                    mongo.update("services", {'name': loadedConfigFile.serviceName}, doc, {'upsert': true}, function (error) {
                                        if (error) {
                                            return res.jsonp(req.soajs.buildResponse({
                                                'code': 617,
                                                'msg': config.errors[617]
                                            }));
                                        }

                                        return res.jsonp(req.soajs.buildResponse(null, true));
                                    });
                                });
                            });
                        });
                    });
                });
        });

        function moveFiles(files, tmpPath, serviceName, fileName, cb) {
            ncp.limit = config.ncpLimit;
            var srvdest = config.workDir + "soajs" + config.upload.tenantFolderName + req.soajs.tenant.code + config.upload.servicesFolderName + serviceName;
            var uiDest = config.workDir + "soajs" + config.upload.tenantFolderName + req.soajs.tenant.code + config.upload.uiFolderName + serviceName;
            req.soajs.log.debug("copying upload module:" + tmpPath);

            ncp(tmpPath, srvdest, function (err) {
                if (err) {
                    shelljs.rm('-rf', tmpPath);
                    shelljs.rm('-f', files[fileName].path);
                    return res.json(req.soajs.buildResponse({code: 619, msg: err.message}));
                }

                if (fs.existsSync(srvdest + "/ui")) {
                    shelljs.mv(srvdest + config.upload.uiFolderName + "*", uiDest);
                    shelljs.rm('-rf', srvdest + "/ui");
                }

                if (fs.existsSync(srvdest + "/ui-dashboard")) {
                    var dashUIDest = config.workDir + "soajs/open_source" + config.upload.dashboardFolderName + "modules/" + serviceName;
                    shelljs.mkdir("-p", dashUIDest);
                    ncp(srvdest + "/ui-dashboard/" + serviceName, dashUIDest, function(err){
                        if(err){
                            req.soajs.log.error(err);
                        }

                        installModuleInDashboardUI(serviceName, function(err){
                            if(err){ req.soajs.log.error(err); }
                            shelljs.rm('-rf', srvdest + "/ui-dashboard");
                        });
                    });
                }

                req.soajs.log.debug("cleaned up upload and tmp files");
                shelljs.rm('-rf', tmpPath);
                shelljs.rm('-f', files[fileName].path);

                return cb(srvdest);
            });
        }

        function installModuleInDashboardUI(serviceName, cb){
            var uiFile = config.workDir + "soajs/open_source" + config.upload.dashboardFolderName + "index.html";
            fs.readFile(uiFile, 'utf8', function(err, data){
               if(err){ return cb(err); }

                if(data.indexOf("modules/" + serviceName + "/install.js") === -1){
                    data = data.split('<script src="app/controller.js"></script>');
                    data[0] += '<script src="modules/' + serviceName + '/install.js"></script>\n';
                    data = data.join('<script src="app/controller.js"></script>');

                    fs.writeFile(uiFile, data, cb);
                }
                else{ return cb(null, true); }
            });
        }

        function validateBuildTenantDir(path, serviceName, cb) {
            var p1 = path + config.upload.servicesFolderName + serviceName;
            var p2 = path + config.upload.uiFolderName + serviceName;

            checkBuildPath(p1, function (error) {
                if (error) {
                    return cb(error);
                }
                checkBuildPath(p2, cb);
            });

            function checkBuildPath(path, cb) {
                fs.exists(path, function (exists) {
                    if (exists) {
                        fs.stat(path, function (err, stats) {
                            if (err) {
                                return cb(err);
                            }
                            else if (!stats.isDirectory()) {
                                return cb(new Error(path + " is not a directory."));
                            }
                            return cb(null, true);
                        });
                    }
                    else {
                        req.soajs.log.debug("destination: " + path + " not found. Creating Directory...");
                        shelljs.mkdir("-p", path);
                        return cb(null, true);
                    }
                });
            }
        }

        function checkIFFile(moduleFile, schema, cb) {

            fs.exists(moduleFile, function (exists) {
                if (!exists) {
                    return cb(new Error(moduleFile + " not Found!"));
                }

                fs.stat(moduleFile, function (err, stats) {
                    if (err)
                        return cb(new Error("Error reading" + moduleFile));
                    else {
                        if (!stats.isFile()) {
                            return cb(new Error(moduleFile + " is not a file!"));
                        }

                        validateFile(moduleFile, schema, cb);
                    }
                });
            });
        }

        function validateFile(filePath, schema, cb) {
            var core = require("soajs/modules/soajs.core");
            var validator = new core.validator.Validator();

            var errMsgs = [];
            if (require.resolve(filePath)) {
                delete require.cache[require.resolve(filePath)];
            }
            var loadedFile = require(filePath);

            //validate package.json
            var check = validator.validate(loadedFile, schema);
            if (!check.valid) {
                check.errors.forEach(function (oneError) {
                    errMsgs.push(oneError.stack);
                });
                return cb(new Error(errMsgs.join(" - ")));
            }

            if (filePath.indexOf("package.json") === -1) {
                mongo.findOne('services', {
                    'port': loadedFile.servicePort,
                    'name': {$ne: loadedFile.serviceName}
                }, function (error, oneRecord) {
                    if (error) {
                        return cb(error);
                    }

                    if (oneRecord) {
                        return cb(new Error("Another service with the same port exists"));
                    }

                    return cb(null, loadedFile);
                });
            }
            else {
                loadedFile.peerDependencies = {
                    "soajs": loadedFile.dependencies.soajs
                };
                delete loadedFile.dependencies.soajs;
                fs.writeFile(filePath, JSON.stringify(loadedFile), "utf8", cb);
            }
        }

        function extractAPIsList(schema) {
            var excluded = ['commonFields'];
            var apiList = [];
            for (var route in schema) {
                if (Object.hasOwnProperty.call(schema, route)) {
                    if (excluded.indexOf(route) !== -1) {
                        continue;
                    }

                    var oneApi = {
                        'l': schema[route]._apiInfo.l,
                        'v': route
                    };

                    if (schema[route]._apiInfo.group) {
                        oneApi.group = schema[route]._apiInfo.group;
                    }

                    if (schema[route]._apiInfo.groupMain) {
                        oneApi.groupMain = schema[route]._apiInfo.groupMain;
                    }

                    apiList.push(oneApi);
                }
            }
            return apiList;
        }
    }
};<|endoftext|>


<commit_before>import React from 'react';
import ReactDOM from 'react-dom';
import injectTapEventPlugin from 'react-tap-event-plugin';
import AskQuestionBoard from './components/AskQuestionBoard.jsx';
import HomePage from './components/HomePage.jsx';
import Login from './components/Login.jsx';
import Profile from './components/Profile.jsx';
import Signup from './components/Signup.jsx';
import {
  BrowserRouter as Router,
  Route,
  Link,
} from 'react-router-dom';

class App extends React.Component {
  constructor(props) {
    super(props);
  }
  render() {

    return (
      <Router>

          <Route exact path="/" component={HomePage}/>
          <Route exact path="/login" component={Login} />
          <Route exact path="/signup" component={Signup} />
          <Route exact path="/profile" user={this.props.user} component={Profile} />

      </Router>
    );
  }
}
injectTapEventPlugin();
ReactDOM.render(<App {...(document.getElementById('app').dataset)} />, document.getElementById('app'));<commit_msg>Update app.js to match Github repo<commit_after>import React from 'react';
import ReactDOM from 'react-dom';
import injectTapEventPlugin from 'react-tap-event-plugin';
import AskQuestionBoard from './components/AskQuestionBoard.jsx';
import HomePage from './components/HomePage.jsx';
import Login from './components/Login.jsx';
import Profile from './components/Profile.jsx';
import Signup from './components/Signup.jsx';
import {
  BrowserRouter as Router,
  Route
} from 'react-router-dom';

class App extends React.Component {
  constructor(props) {
    super(props);
  }
  render() {

    return (
      <Router>

          <Route exact path="/" component={HomePage}/>
          <Route exact path="/login" component={Login} />
          <Route exact path="/signup" component={Signup} />
          <Route exact path="/profile" user={this.props.user} component={Profile} />

      </Router>
    );
  }
}
injectTapEventPlugin();
ReactDOM.render(<App {...(document.getElementById('app').dataset)} />, document.getElementById('app'));
<|endoftext|>


<commit_before>var _ = require('lodash');
var	fs = require('fs-extra');
var path = require('path');
var eachGraph = require("../graph/each_dependencies");
var transformImport = require("../build/transform");
var helpers = require('./helpers/helpers');
var winston = require('winston');
var logging = require("../logger");

var mergeModules = function(items, modules){
	var i = 0,
		item;
	while(i < items.length) {
		item = items[i];
		if(typeof item === "object" && !(item instanceof RegExp) ) {
			var moduleNames = _.map( _.where(modules, item), "moduleName");
			items.splice.apply(items,[i,1].concat(moduleNames));
			i = i + moduleNames.length;
		} else {
			i++;
		}
	}
};

var addDefaults = function(name, obj, defaults){
	var helpers = [];

	name.replace(/\+([\w-]+)/g,function(whole, part){
		var defs = defaults[part];
		if(defs) {
			if(typeof defs === "function") {
				defs = defs(obj);
				_.assign(obj, defs);
			} else {
				_.defaults(obj, defs);
			}
			helpers.push(part);
		}

	});

	if(helpers.length) {
		winston.debug("  added helpers: "+helpers.join(","));
	}
};

// config {system: {}, options: {}, outputs: {}}
module.exports = function(config, defaults, modules){
	return new Promise(function(resolve, reject) {

		logging.setup(config.options || {}, config.system || {});

		// allow defaults that overwrite our helpers
		var defaultHelpers = _.clone(helpers);
		_.assign(defaultHelpers, defaults);

		// converts to an array of outputs
		config.outputs = _.map(config.outputs || config.transforms, function(output, name){

			addDefaults(name, output, defaultHelpers || {}, config.options || {});
			// merge modules and graphs
			mergeModules(output.modules || [], modules);
			mergeModules(output.eachModule || [], modules);
			mergeModules(output.graphs || [], modules);
			mergeModules(output.ignore || [], modules);

			return {
				name: name,
				output: output
			};
		});

		var modulesMap = _.indexBy(modules,"moduleName");
		var finished = function(){
			if(errors.length) {
				reject(errors);
			} else {
				resolve();
			}
		};

		// writing utils
		var fileWrites = 0,
			errors = [],
			writeFile = function(filename, data){
				var code = data.code;
				var map = data.map;
				var check = function(){
					fileWrites--;
					if(fileWrites === 0) {
						finished();
					}
				};

				fileWrites++;
				fs.mkdirs(path.dirname(filename), function(err){
					if(err) {
						errors.push(err);
						check();
					} else {
						fs.writeFile(filename, code, function(err){
							if(err) {
								errors.push(err);
							}
							var sourceMaps = map && config.options &&
								config.options.sourceMaps;
							if(sourceMaps) {
								fs.writeFile(filename+".map", map+"", function(err){
									if(err) { errors.push(err); }
									check();
								});
								return;
							}
							check();
						});
					}
				});
			};

		var transform,
			transformAndWriteOut = function(moduleNames, out, extraOptions){
				extraOptions = extraOptions || {};
				var outputOptions = _.assign(extraOptions, out.output, {
					ignore: extraOptions.ignore || out.output.ignore
				});

				var result = transform(moduleNames, outputOptions ),
					filePath;

				if(typeof out.output.dest === "string") {
					filePath = out.output.dest;
				} else {
					// pull out the moduleNames
					var loads = ( typeof moduleNames === "string" ?
						transform.graph[moduleNames].load :
						moduleNames.map(function(moduleName){
							return transform.graph[moduleName].load;
						}) );

					filePath = out.output.dest(moduleNames, modulesMap[moduleNames], loads, transform.loader);
				}
				winston.info("> "+filePath);
				writeFile(filePath, result);
			};

		var processOutput = function(out){
			winston.info("OUTPUT: "+out.name);

			var loader = transform.loader;
			var mods, doTransform;
			// write out each module and its dependencies in the list
			if(out.output.eachModule) {
				if(Array.isArray( out.output.eachModule) ) {
					mods = normalized(out.output.eachModule);
				} else {
					mods = normalized(_.map( _.where(modules, out.output.eachModule), "moduleName"));
				}

				doTransform = function(mods, ignores){
					mods.forEach(function(mod){
						transformAndWriteOut(mod, out, {
							ignore: ignores
						});
					});
				};

			// write out the graphs
			} else if(out.output.graphs){
				if(typeof out.output.graphs === "function") {
					mods = normalized(out.output.graphs(loader));
				} else {
					mods = normalized(out.output.graphs);
				}

				doTransform = function(mods, ignores){
					eachGraph(transform.graph, mods, function(name, node){
						if(!transformImport.matches(ignores, name, node.load)) {
							transformAndWriteOut(name, out, {ignoreAllDependencies: true});
						}
					});
				};
			// write out all the modules combined
			} else {
				if(Array.isArray( out.output.modules) ) {
					mods = normalized(out.output.modules);
				} else if(typeof out.output.modules === "function"){
					mods = normalized(out.output.modules(loader));
				} else if(out.output.modules) {
					mods = normalized([out.output.modules]);
				}
				doTransform = function(mods, ignores){
					transformAndWriteOut(mods, out, {
						ignore: ignores
					});
				};
			}

			Promise.all(mods).then(function(mods) {
				var ignore = out.output.ignore || [];
				Promise.all(normalized(ignore))
				.then(function(outputIgnore){
					var ignores = transformImport.getAllIgnores(outputIgnore,
																transform.graph);
					doTransform(mods, ignores);
				});
			});

			// Give an array of moduleNames, normalize them.
			function normalized(mods) {
				if(mods && !Array.isArray(mods)) {
					mods = [mods];
				}

				return (mods || []).map(function(moduleName) {
					if(typeof moduleName === "function") {
						return Promise.resolve(moduleName);
					}
					return loader.normalize(moduleName);
				});
			}

		};

		fileWrites++;
		transformImport(config.system, config.options).then(function(configPluginify){
			fileWrites--;
			transform = configPluginify;
			config.outputs.forEach(processOutput);

		})["catch"](function(e){
			reject(e);
		});
	});
};
<commit_msg>Normalize modelNames only if typeof === string<commit_after>var _ = require('lodash');
var	fs = require('fs-extra');
var path = require('path');
var eachGraph = require("../graph/each_dependencies");
var transformImport = require("../build/transform");
var helpers = require('./helpers/helpers');
var winston = require('winston');
var logging = require("../logger");

var mergeModules = function(items, modules){
	var i = 0,
		item;
	while(i < items.length) {
		item = items[i];
		if(typeof item === "object" && !(item instanceof RegExp) ) {
			var moduleNames = _.map( _.where(modules, item), "moduleName");
			items.splice.apply(items,[i,1].concat(moduleNames));
			i = i + moduleNames.length;
		} else {
			i++;
		}
	}
};

var addDefaults = function(name, obj, defaults){
	var helpers = [];

	name.replace(/\+([\w-]+)/g,function(whole, part){
		var defs = defaults[part];
		if(defs) {
			if(typeof defs === "function") {
				defs = defs(obj);
				_.assign(obj, defs);
			} else {
				_.defaults(obj, defs);
			}
			helpers.push(part);
		}

	});

	if(helpers.length) {
		winston.debug("  added helpers: "+helpers.join(","));
	}
};

// config {system: {}, options: {}, outputs: {}}
module.exports = function(config, defaults, modules){
	return new Promise(function(resolve, reject) {

		logging.setup(config.options || {}, config.system || {});

		// allow defaults that overwrite our helpers
		var defaultHelpers = _.clone(helpers);
		_.assign(defaultHelpers, defaults);

		// converts to an array of outputs
		config.outputs = _.map(config.outputs || config.transforms, function(output, name){

			addDefaults(name, output, defaultHelpers || {}, config.options || {});
			// merge modules and graphs
			mergeModules(output.modules || [], modules);
			mergeModules(output.eachModule || [], modules);
			mergeModules(output.graphs || [], modules);
			mergeModules(output.ignore || [], modules);

			return {
				name: name,
				output: output
			};
		});

		var modulesMap = _.indexBy(modules,"moduleName");
		var finished = function(){
			if(errors.length) {
				reject(errors);
			} else {
				resolve();
			}
		};

		// writing utils
		var fileWrites = 0,
			errors = [],
			writeFile = function(filename, data){
				var code = data.code;
				var map = data.map;
				var check = function(){
					fileWrites--;
					if(fileWrites === 0) {
						finished();
					}
				};

				fileWrites++;
				fs.mkdirs(path.dirname(filename), function(err){
					if(err) {
						errors.push(err);
						check();
					} else {
						fs.writeFile(filename, code, function(err){
							if(err) {
								errors.push(err);
							}
							var sourceMaps = map && config.options &&
								config.options.sourceMaps;
							if(sourceMaps) {
								fs.writeFile(filename+".map", map+"", function(err){
									if(err) { errors.push(err); }
									check();
								});
								return;
							}
							check();
						});
					}
				});
			};

		var transform,
			transformAndWriteOut = function(moduleNames, out, extraOptions){
				extraOptions = extraOptions || {};
				var outputOptions = _.assign(extraOptions, out.output, {
					ignore: extraOptions.ignore || out.output.ignore
				});

				var result = transform(moduleNames, outputOptions ),
					filePath;

				if(typeof out.output.dest === "string") {
					filePath = out.output.dest;
				} else {
					// pull out the moduleNames
					var loads = ( typeof moduleNames === "string" ?
						transform.graph[moduleNames].load :
						moduleNames.map(function(moduleName){
							return transform.graph[moduleName].load;
						}) );

					filePath = out.output.dest(moduleNames, modulesMap[moduleNames], loads, transform.loader);
				}
				winston.info("> "+filePath);
				writeFile(filePath, result);
			};

		var processOutput = function(out){
			winston.info("OUTPUT: "+out.name);

			var loader = transform.loader;
			var mods, doTransform;
			// write out each module and its dependencies in the list
			if(out.output.eachModule) {
				if(Array.isArray( out.output.eachModule) ) {
					mods = normalized(out.output.eachModule);
				} else {
					mods = normalized(_.map( _.where(modules, out.output.eachModule), "moduleName"));
				}

				doTransform = function(mods, ignores){
					mods.forEach(function(mod){
						transformAndWriteOut(mod, out, {
							ignore: ignores
						});
					});
				};

			// write out the graphs
			} else if(out.output.graphs){
				if(typeof out.output.graphs === "function") {
					mods = normalized(out.output.graphs(loader));
				} else {
					mods = normalized(out.output.graphs);
				}

				doTransform = function(mods, ignores){
					eachGraph(transform.graph, mods, function(name, node){
						if(!transformImport.matches(ignores, name, node.load)) {
							transformAndWriteOut(name, out, {ignoreAllDependencies: true});
						}
					});
				};
			// write out all the modules combined
			} else {
				if(Array.isArray( out.output.modules) ) {
					mods = normalized(out.output.modules);
				} else if(typeof out.output.modules === "function"){
					mods = normalized(out.output.modules(loader));
				} else if(out.output.modules) {
					mods = normalized([out.output.modules]);
				}
				doTransform = function(mods, ignores){
					transformAndWriteOut(mods, out, {
						ignore: ignores
					});
				};
			}

			Promise.all(mods).then(function(mods) {
				var ignore = out.output.ignore || [];
				Promise.all(normalized(ignore))
				.then(function(outputIgnore){
					var ignores = transformImport.getAllIgnores(outputIgnore,
																transform.graph);
					doTransform(mods, ignores);
				});
			});

			// Give an array of moduleNames, normalize them.
			function normalized(mods) {
				if(mods && !Array.isArray(mods)) {
					mods = [mods];
				}

				return (mods || []).map(function(moduleName) {
					if(typeof moduleName !== "string") {
						return Promise.resolve(moduleName);
					}
					return loader.normalize(moduleName);
				});
			}

		};

		fileWrites++;
		transformImport(config.system, config.options).then(function(configPluginify){
			fileWrites--;
			transform = configPluginify;
			config.outputs.forEach(processOutput);

		})["catch"](function(e){
			reject(e);
		});
	});
};
<|endoftext|>