<commit_before>/*#ifndef(UMD)*/
(function () { /* Begin of privacy scope */
    "use strict";
/*#endif*/

    var
        _emptyMember = 0,
        _defAttrBase = gpf._genDefHandler("gpf.attributes", "Attribute");

    gpf.attributes = {};

    /**
     * Generates a factory capable of creating a new instance of a class
     *
     * @param {Function} objectClass Object constructor
     * @param {String} name Alias name (will be prefixed by $)
     * @private
     * @closure
     */
    function _alias(objectClass, name) {
        name = "$" + name;
        gpf[name] = (function(){
            var Proxy = (gpf._func("return function " + name + "(args) {" +
                "this.constructor.apply(this, args);" +
            "};"))();
            Proxy.prototype = objectClass.prototype;
            return function() {
                return new Proxy(arguments);
            };
        }());
    }

    /**
     * Defines an attribute (relies on gpf.define)
     *
     * @param {String} name Attribute name. If it contains a dot, it is
     * treated as absolute contextual. Otherwise, it is relative to
     * "gpf.attributes". If starting with $ (and no dot), the contextual name
     * will be the "gpf.attributes." + name(without $) + "Attribute" and an
     * alias is automatically created (otherwise, use $Alias attribute on class)
     * @param {Function|string} [base=undefined] base Base attribute
     * (or contextual name)
     * @param {Object} [definition=undefined] definition Attribute definition
     * @return {Function}
     * @private
     */
    gpf._defAttr = function (name, base, definition) {
        var
            isAlias = name.charAt(0) === "$",
            fullName,
            result;
        if (isAlias) {
            name = name.substr(1);
            fullName = name + "Attribute";
        } else {
            fullName = name;
        }
        result = _defAttrBase(fullName, base, definition);
        if (isAlias) {
            _alias(result, name);
        }
        return result;
    };

    /**
     * Base class for any attribute
     *
     * @class gpf.attributes.Attribute
     */
    gpf._defAttr("Attribute", {

        protected: {

            /**
             * Name of the member the attribute is associated to
             *
             * @type {String}
             * @protected
             * @friend {gpf.attributes.add}
             */
            _member: "",

            /**
             * This method is the implementation of the attribute: it receives
             * the prototype to alter.
             *
             * NOTE: this is called *after* all declared members are set
             *
             * @param {Object} objPrototype Class prototype
             * @protected
             * @friend {gpf.attributes.add}
             */
            _alterPrototype: function /*abstract*/ (objPrototype) {
                gpf.interfaces.ignoreParameter(objPrototype);
            }

        },

        public: {

            /**
             * Get the member name
             *
             * @return {String}
             */
            member: function () {
                return this._member;
            }

        }

    });

    /**
     * Generates an alias for the attribute. An alias is a factory function that
     * allocates an attribute instance (parameters are forwarded).
     * As a result, instead of using:
     * "[Class]" : [new gpf.attributes.AliasAttribute("Name")]
     * It is reduced to:
     * "[Class]" : [$Alias("Name")]
     *
     * @param {String} name Name of the alias to build below gpf
     *
     * @class gpf.attributes.AliasAttribute
     * @extends gpf.attributes.Attribute
     * @alias gpf.$Alias
     */
    gpf._defAttr("$Alias", {

        private: {

            /**
             * Name of the alias to create
             *
             * @type {String}
             * @private
             */
            _name: ""

        },

        protected: {

            /**
             * @inheritDoc gpf.attributes.Attribute:_alterPrototype
             */
            _alterPrototype: function (objPrototype) {
                _alias(objPrototype.constructor, this._name);
            }

        },

        public: {

            /**
             * Defines an alias
             *
             * @param {String} name Name of the alias to create
             * @constructor
             */
            constructor: function (name) {
                this._name = name;
            }

        }

    });

    /**
     * Attribute array, generally used to list attributes on a class member
     *
     * @class gpf.attributes.Array
     */
    gpf.define("gpf.attributes.Array", {

        private: {

            /**
             * @type {gpf.attributes.Attribute[]}
             * @private
             */
            _array: []

        },

        public: {

            /**
             * @constructor
             */
            constructor: function () {
                this._array = []; // Create a new instance of the array
            },

            /**
             * Return the first occurrence of the expected class
             *
             * @param {gpf.attributes.Attribute} expectedClass the class to
             * match
             * @return {gpf.attributes.Attribute}
             */
            has: function (expectedClass) {
                gpf.ASSERT("function" === typeof expectedClass,
                    "Expected a class parameter");
                gpf.ASSERT(expectedClass instanceof gpf.attributes.Attribute,
                    "Expected an Attribute-like class parameter");
                var
                    idx,
                    len = this._array.length,
                    item;
                for (idx = 0; idx < len; ++idx) {
                    item = this._array[idx];
                    if (item instanceof expectedClass) {
                        return item;
                    }
                }
                return null;
            },

            /**
             * Returns a new array with all attributes matching the expected
             * class
             *
             * @param {Function} expectedClass the class to match
             * @return {gpf.attributes.Array}
             */
            filter: function (expectedClass) {
                gpf.ASSERT("function" === typeof expectedClass,
                    "Expected a class parameter");
                gpf.ASSERT(expectedClass instanceof gpf.attributes.Attribute,
                    "Expected an Attribute-like class parameter");
                var
                    idx,
                    len = this._array.length,
                    attribute,
                    result = new gpf.attributes.Array();
                for (idx = 0; idx < len; ++idx) {
                    attribute = this._array[idx];
                    if (attribute instanceof expectedClass) {
                        result._array.push(attribute);
                    }
                }
                return result;
            },

            /**
             * Apply the callback for each attribute in the array
             *
             * @param {Function} callback, defined with parameters
             * * {gpf.attributes.Attribute} attribute
             * No result is expected
             * @param {Object} [scope=undefined] scope
             * @param {*} [params=undefined] params Additional parameters
             * appended at the end of the expected parameter list
             */
            each: function (callback, scope, params) {
                scope = gpf.Callback.resolveScope(scope);
                params = gpf.Callback.buildParamArray(1, params);
                var
                    idx,
                    len = this._array.length;
                for (idx = 0; idx < len; ++idx) {
                    gpf.Callback.doApply(callback, scope, params,
                        this._array[idx]);
                }
            }
        }

    });

    /**
     * Attribute map, generally used to list attributes of a class
     *
     * @class gpf.attributes.Map
     */
    gpf.define("gpf.attributes.Map", {

        private: {

            /**
             * @type {Object} Map(String,gpf.attributes.Array)
             * @private
             */
            _members: {},

            /**
             * @type {Number}
             * @private
             */
            _count: 0,

            /**
             * Copy the content of this map to a new one
             *
             * @param {gpf.attributes.Map} attributesMap recipient of the copy
             * @param {Function} [callback=undefined] callback callback function
             * to test if the mapping should be added
             * @param {*} [param=undefined] param additional parameter for the
             * callback
             * @private
             */
            _copyTo: function (attributesMap, callback, param) {
                var
                    member,
                    array,
                    idx,
                    attribute;
                if (this._count) {
                    for (member in this._members) {
                        if (this._members.hasOwnProperty(member)) {
                            array = this._members[member]._array;
                            for (idx = 0; idx < array.length; ++idx) {
                                attribute = array[ idx ];
                                if (!callback
                                    || callback(member, attribute, param)) {
                                    attributesMap.add(member, attribute);
                                }
                            }
                        }
                    }
                }
            },

            /**
             * Callback for _copyTo, test if attribute is of a given class
             *
             * @param {String} member
             * @param {gpf.attributes.Attribute} attribute
             * @param {Function} expectedClass
             * @return {Boolean}
             * @private
             */
            _filterCallback: function (member, attribute, expectedClass) {
                gpf.interfaces.ignoreParameter(member);
                return attribute instanceof expectedClass;
            }

        },

        public: {

            /**
             * @param {Object} [object=undefined] object Object to read
             *        attributes from
             * @constructor
             */
            constructor: function (object) {
                this._members = {}; // Creates a new dictionary
                this._count = 0;
                if (undefined !== object) {
                    this.fillFromObject(object);
                }
            },

            /**
             * Gives the total number of attributes enclosed in the map
             *
             * @return {Number}
             */
            count: function () {
                return this._count;
            },

            /**
             * Associate an attribute to a member
             *
             * @param {String} member member name
             * @param {gpf.attributes.Attribute} attribute attribute to map
             */
            add: function (member, attribute) {
                var array = this._members[member];
                if (undefined === array) {
                    array = this._members[member] = new gpf.attributes.Array();
                }
                array._array.push(attribute);
                ++this._count;
            },

            /**
             * Fill the map using object's attributes
             *
             * @param {Object} object object to get attributes from
             * @return {Number} number of attributes in the resulting map
             */
            fillFromObject: function (object) {
                var
                    classDef = gpf.classDef(object.constructor),
                    attributes;
                while (classDef) { // !undefined && !null
                    attributes = classDef.attributes();
                    if (attributes) {
                        attributes._copyTo(this);
                    }
                    if (classDef.Base() !== Object) { // Can't go upper
                        classDef = gpf.classDef(classDef.Base());
                    } else {
                        break;
                    }
                }
                return this._count;
            },

            /**
             * Creates a new map that contains only instances of the given
             * attribute class
             *
             * @param {Function} expectedClass
             * @return {gpf.attributes.Map}
             */
            filter: function (expectedClass) {
                gpf.ASSERT("function" === typeof expectedClass,
                    "Expected a class parameter");
                gpf.ASSERT(expectedClass instanceof gpf.attributes.Attribute,
                    "Expected an Attribute-like class parameter");
                var result = new gpf.attributes.Map();
                this._copyTo(result, this._filterCallback, expectedClass);
                return result;
            },

            /**
             * Returns the array of map associated to a member
             *
             * @param {String} name
             * @return {gpf.attributes.Array}
             */
            member: function (name) {
                var result = this._members[name];
                if (undefined === result) {
                    if (0 === _emptyMember) {
                        _emptyMember = new gpf.attributes.Array();
                    }
                    result = _emptyMember;
                }
                return result;
            },

            /**
             * Returns the list of members stored in this map
             *
             * @perf_warn Result is computed on each call
             * @return {String[]}
             */
            members: function () {
                var
                    result = [],
                    member;
                for (member in this._members) {
                    if (this._members.hasOwnProperty(member)) {
                        result.push(member);
                    }
                }
                return result;
            },

            /**
             * Apply the callback for each member in the map
             *
             * @param {Function} callback, defined with parameters
             * * {String} member
             * * {gpf.attributes.Array} attributes
             * No result is expected
             * @param {Object} [scope=undefined] scope
             * @param {*} [params=undefined] params Additional parameters
             * appended at the end of the expected parameter list
             */
            each: function (callback, scope, params) {
                scope = gpf.Callback.resolveScope(scope);
                params = gpf.Callback.buildParamArray(2, params);
                var
                    member;
                for (member in this._members) {
                    if (this._members.hasOwnProperty(member)) {
                        gpf.Callback.doApply(callback, scope, params,
                            member, this._members[member]);
                    }
                }
            }

        }

    });

    /**
     * Add the attribute list to the given prototype associated with the
     * provided member name
     *
     * @param {Function} objectClass class constructor
     * @param {String} name member name
     * @param {gpf.attributes.Attribute[]} attributes
     */
    gpf.attributes.add = function (objectClass, name, attributes) {
        var
            attributeList,
            len,
            idx,
            attribute;
        attributeList = gpf.classDef(objectClass).attributes();
        len = attributes.length;
        for (idx = 0; idx < len; ++idx) {
            attribute = attributes[idx];
            attribute._member = name; // Assign member name
            attributeList.add(name, attribute);
            attribute._alterPrototype(objectClass.prototype);
        }
    };

/*#ifndef(UMD)*/
}()); /* End of privacy scope */
/*#endif*/<commit_msg>Fixed gpf.ASSERT<commit_after>/*#ifndef(UMD)*/
(function () { /* Begin of privacy scope */
    "use strict";
/*#endif*/

    var
        _emptyMember = 0,
        _defAttrBase = gpf._genDefHandler("gpf.attributes", "Attribute");

    gpf.attributes = {};

    /**
     * Generates a factory capable of creating a new instance of a class
     *
     * @param {Function} objectClass Object constructor
     * @param {String} name Alias name (will be prefixed by $)
     * @private
     * @closure
     */
    function _alias(objectClass, name) {
        name = "$" + name;
        gpf[name] = (function(){
            var Proxy = (gpf._func("return function " + name + "(args) {" +
                "this.constructor.apply(this, args);" +
            "};"))();
            Proxy.prototype = objectClass.prototype;
            return function() {
                return new Proxy(arguments);
            };
        }());
    }

    /**
     * Defines an attribute (relies on gpf.define)
     *
     * @param {String} name Attribute name. If it contains a dot, it is
     * treated as absolute contextual. Otherwise, it is relative to
     * "gpf.attributes". If starting with $ (and no dot), the contextual name
     * will be the "gpf.attributes." + name(without $) + "Attribute" and an
     * alias is automatically created (otherwise, use $Alias attribute on class)
     * @param {Function|string} [base=undefined] base Base attribute
     * (or contextual name)
     * @param {Object} [definition=undefined] definition Attribute definition
     * @return {Function}
     * @private
     */
    gpf._defAttr = function (name, base, definition) {
        var
            isAlias = name.charAt(0) === "$",
            fullName,
            result;
        if (isAlias) {
            name = name.substr(1);
            fullName = name + "Attribute";
        } else {
            fullName = name;
        }
        result = _defAttrBase(fullName, base, definition);
        if (isAlias) {
            _alias(result, name);
        }
        return result;
    };

    /**
     * Base class for any attribute
     *
     * @class gpf.attributes.Attribute
     */
    gpf._defAttr("Attribute", {

        protected: {

            /**
             * Name of the member the attribute is associated to
             *
             * @type {String}
             * @protected
             * @friend {gpf.attributes.add}
             */
            _member: "",

            /**
             * This method is the implementation of the attribute: it receives
             * the prototype to alter.
             *
             * NOTE: this is called *after* all declared members are set
             *
             * @param {Object} objPrototype Class prototype
             * @protected
             * @friend {gpf.attributes.add}
             */
            _alterPrototype: function /*abstract*/ (objPrototype) {
                gpf.interfaces.ignoreParameter(objPrototype);
            }

        },

        public: {

            /**
             * Get the member name
             *
             * @return {String}
             */
            member: function () {
                return this._member;
            }

        }

    });

    /**
     * Generates an alias for the attribute. An alias is a factory function that
     * allocates an attribute instance (parameters are forwarded).
     * As a result, instead of using:
     * "[Class]" : [new gpf.attributes.AliasAttribute("Name")]
     * It is reduced to:
     * "[Class]" : [$Alias("Name")]
     *
     * @param {String} name Name of the alias to build below gpf
     *
     * @class gpf.attributes.AliasAttribute
     * @extends gpf.attributes.Attribute
     * @alias gpf.$Alias
     */
    gpf._defAttr("$Alias", {

        private: {

            /**
             * Name of the alias to create
             *
             * @type {String}
             * @private
             */
            _name: ""

        },

        protected: {

            /**
             * @inheritDoc gpf.attributes.Attribute:_alterPrototype
             */
            _alterPrototype: function (objPrototype) {
                _alias(objPrototype.constructor, this._name);
            }

        },

        public: {

            /**
             * Defines an alias
             *
             * @param {String} name Name of the alias to create
             * @constructor
             */
            constructor: function (name) {
                this._name = name;
            }

        }

    });

    /**
     * Attribute array, generally used to list attributes on a class member
     *
     * @class gpf.attributes.Array
     */
    gpf.define("gpf.attributes.Array", {

        private: {

            /**
             * @type {gpf.attributes.Attribute[]}
             * @private
             */
            _array: []

        },

        public: {

            /**
             * @constructor
             */
            constructor: function () {
                this._array = []; // Create a new instance of the array
            },

            /**
             * Return the first occurrence of the expected class
             *
             * @param {gpf.attributes.Attribute} expectedClass the class to
             * match
             * @return {gpf.attributes.Attribute}
             */
            has: function (expectedClass) {
                gpf.ASSERT("function" === typeof expectedClass,
                    "Expected a class parameter");
                gpf.ASSERT(
                    expectedClass.prototype instanceof gpf.attributes.Attribute,
                    "Expected an Attribute-like class parameter");
                var
                    idx,
                    len = this._array.length,
                    item;
                for (idx = 0; idx < len; ++idx) {
                    item = this._array[idx];
                    if (item instanceof expectedClass) {
                        return item;
                    }
                }
                return null;
            },

            /**
             * Returns a new array with all attributes matching the expected
             * class
             *
             * @param {Function} expectedClass the class to match
             * @return {gpf.attributes.Array}
             */
            filter: function (expectedClass) {
                gpf.ASSERT("function" === typeof expectedClass,
                    "Expected a class parameter");
                gpf.ASSERT(
                    expectedClass.prototype instanceof gpf.attributes.Attribute,
                    "Expected an Attribute-like class parameter");
                var
                    idx,
                    len = this._array.length,
                    attribute,
                    result = new gpf.attributes.Array();
                for (idx = 0; idx < len; ++idx) {
                    attribute = this._array[idx];
                    if (attribute instanceof expectedClass) {
                        result._array.push(attribute);
                    }
                }
                return result;
            },

            /**
             * Apply the callback for each attribute in the array
             *
             * @param {Function} callback, defined with parameters
             * * {gpf.attributes.Attribute} attribute
             * No result is expected
             * @param {Object} [scope=undefined] scope
             * @param {*} [params=undefined] params Additional parameters
             * appended at the end of the expected parameter list
             */
            each: function (callback, scope, params) {
                scope = gpf.Callback.resolveScope(scope);
                params = gpf.Callback.buildParamArray(1, params);
                var
                    idx,
                    len = this._array.length;
                for (idx = 0; idx < len; ++idx) {
                    gpf.Callback.doApply(callback, scope, params,
                        this._array[idx]);
                }
            }
        }

    });

    /**
     * Attribute map, generally used to list attributes of a class
     *
     * @class gpf.attributes.Map
     */
    gpf.define("gpf.attributes.Map", {

        private: {

            /**
             * @type {Object} Map(String,gpf.attributes.Array)
             * @private
             */
            _members: {},

            /**
             * @type {Number}
             * @private
             */
            _count: 0,

            /**
             * Copy the content of this map to a new one
             *
             * @param {gpf.attributes.Map} attributesMap recipient of the copy
             * @param {Function} [callback=undefined] callback callback function
             * to test if the mapping should be added
             * @param {*} [param=undefined] param additional parameter for the
             * callback
             * @private
             */
            _copyTo: function (attributesMap, callback, param) {
                var
                    member,
                    array,
                    idx,
                    attribute;
                if (this._count) {
                    for (member in this._members) {
                        if (this._members.hasOwnProperty(member)) {
                            array = this._members[member]._array;
                            for (idx = 0; idx < array.length; ++idx) {
                                attribute = array[ idx ];
                                if (!callback
                                    || callback(member, attribute, param)) {
                                    attributesMap.add(member, attribute);
                                }
                            }
                        }
                    }
                }
            },

            /**
             * Callback for _copyTo, test if attribute is of a given class
             *
             * @param {String} member
             * @param {gpf.attributes.Attribute} attribute
             * @param {Function} expectedClass
             * @return {Boolean}
             * @private
             */
            _filterCallback: function (member, attribute, expectedClass) {
                gpf.interfaces.ignoreParameter(member);
                return attribute instanceof expectedClass;
            }

        },

        public: {

            /**
             * @param {Object} [object=undefined] object Object to read
             *        attributes from
             * @constructor
             */
            constructor: function (object) {
                this._members = {}; // Creates a new dictionary
                this._count = 0;
                if (undefined !== object) {
                    this.fillFromObject(object);
                }
            },

            /**
             * Gives the total number of attributes enclosed in the map
             *
             * @return {Number}
             */
            count: function () {
                return this._count;
            },

            /**
             * Associate an attribute to a member
             *
             * @param {String} member member name
             * @param {gpf.attributes.Attribute} attribute attribute to map
             */
            add: function (member, attribute) {
                var array = this._members[member];
                if (undefined === array) {
                    array = this._members[member] = new gpf.attributes.Array();
                }
                array._array.push(attribute);
                ++this._count;
            },

            /**
             * Fill the map using object's attributes
             *
             * @param {Object} object object to get attributes from
             * @return {Number} number of attributes in the resulting map
             */
            fillFromObject: function (object) {
                var
                    classDef = gpf.classDef(object.constructor),
                    attributes;
                while (classDef) { // !undefined && !null
                    attributes = classDef.attributes();
                    if (attributes) {
                        attributes._copyTo(this);
                    }
                    if (classDef.Base() !== Object) { // Can't go upper
                        classDef = gpf.classDef(classDef.Base());
                    } else {
                        break;
                    }
                }
                return this._count;
            },

            /**
             * Creates a new map that contains only instances of the given
             * attribute class
             *
             * @param {Function} expectedClass
             * @return {gpf.attributes.Map}
             */
            filter: function (expectedClass) {
                gpf.ASSERT("function" === typeof expectedClass,
                    "Expected a class parameter");
                gpf.ASSERT(
                    expectedClass.prototype instanceof gpf.attributes.Attribute,
                    "Expected an Attribute-like class parameter");
                var result = new gpf.attributes.Map();
                this._copyTo(result, this._filterCallback, expectedClass);
                return result;
            },

            /**
             * Returns the array of map associated to a member
             *
             * @param {String} name
             * @return {gpf.attributes.Array}
             */
            member: function (name) {
                var result = this._members[name];
                if (undefined === result) {
                    if (0 === _emptyMember) {
                        _emptyMember = new gpf.attributes.Array();
                    }
                    result = _emptyMember;
                }
                return result;
            },

            /**
             * Returns the list of members stored in this map
             *
             * @perf_warn Result is computed on each call
             * @return {String[]}
             */
            members: function () {
                var
                    result = [],
                    member;
                for (member in this._members) {
                    if (this._members.hasOwnProperty(member)) {
                        result.push(member);
                    }
                }
                return result;
            },

            /**
             * Apply the callback for each member in the map
             *
             * @param {Function} callback, defined with parameters
             * * {String} member
             * * {gpf.attributes.Array} attributes
             * No result is expected
             * @param {Object} [scope=undefined] scope
             * @param {*} [params=undefined] params Additional parameters
             * appended at the end of the expected parameter list
             */
            each: function (callback, scope, params) {
                scope = gpf.Callback.resolveScope(scope);
                params = gpf.Callback.buildParamArray(2, params);
                var
                    member;
                for (member in this._members) {
                    if (this._members.hasOwnProperty(member)) {
                        gpf.Callback.doApply(callback, scope, params,
                            member, this._members[member]);
                    }
                }
            }

        }

    });

    /**
     * Add the attribute list to the given prototype associated with the
     * provided member name
     *
     * @param {Function} objectClass class constructor
     * @param {String} name member name
     * @param {gpf.attributes.Attribute[]} attributes
     */
    gpf.attributes.add = function (objectClass, name, attributes) {
        var
            attributeList,
            len,
            idx,
            attribute;
        attributeList = gpf.classDef(objectClass).attributes();
        len = attributes.length;
        for (idx = 0; idx < len; ++idx) {
            attribute = attributes[idx];
            attribute._member = name; // Assign member name
            attributeList.add(name, attribute);
            attribute._alterPrototype(objectClass.prototype);
        }
    };

/*#ifndef(UMD)*/
}()); /* End of privacy scope */
/*#endif*/<|endoftext|>


<commit_before>/*    Copyright 2017 Firewalla LLC
 *
 *    This program is free software: you can redistribute it and/or  modify
 *    it under the terms of the GNU Affero General Public License, version 3,
 *    as published by the Free Software Foundation.
 *
 *    This program is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    GNU Affero General Public License for more details.
 *
 *    You should have received a copy of the GNU Affero General Public License
 *    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
/**
 * Created by Melvin Tu on 05/01/2017.
 */

/*
 * WARNING:
 *   UPNP operations must be isolated to be one process.  NATPMP requires
 *   openning a port, which may cause trouble if two processes are doing
 *   the same
 */

'use strict';

let instance = null;

const log = require("../../net2/logger.js")(__filename);
const util = require('util');

const _ = require('lodash');

const f = require('../../net2/Firewalla.js');

const natpmp = require('./nat-pmp');
const natupnp = require('./nat-upnp');

const Promise = require('bluebird');

const upnpClient = natupnp.createClient();
//upnpClient.timeout = 10000; // set timeout to 10 seconds to avoid timeout too often
const natpmpTimeout = 86400;  // 1 day = 24 * 60 * 60 seconds

let upnpIntervalHandler = null;
let upnpMappings = [];
const upnpCheckInterval = 15 * 60 * 1000 // 15 mins

module.exports = class {
  constructor(loglevel, gw) {
    if (instance == null) {
      this.gw = gw;
      instance = this;
      this.refreshTimers = {};

      // TODO: move this to UPNPSensor
      // periodical checks whether all upnp mappings registered are alive
      // if not, adds back
      if (process.title === "FireMain") {
        upnpIntervalHandler = setInterval(
          () => {
            log.info("UPnP periodical check starts")
            if (upnpMappings.isEmpty) {
              log.info("No mapping registered.")
              return;
            }
            upnpClient.getMappings((err, results) => {
              if (err) {
                log.error("Failed to get current mappings", err);
                return;
              }
              log.info("Current mappings: ", results);

              upnpMappings.forEach((check) => {
                log.info("Checking registered mapping:", check);
                if (_.isEmpty(
                  results.find((m) => this.mappingCompare(m, check))
                )) {
                  log.info("Mapping no longer exists, adding back to router...")
                  let { protocol, localPort, externalPort, description } = check;
                  this.addPortMappingUPNP(protocol, localPort, externalPort, description)
                } else {
                  log.info("Mapping still exists")
                }
              })
            })
          },
          upnpCheckInterval
        )
      }
    }
    return instance;
  }

  natpmpClient() {
    try {
      if (this._natpmpClient == null) {
        this._natpmpClient = natpmp.connect(this.gw);
        this._natpmpClient.on('error', err => {
          log.error("natpmp emitted", err);
        });
      }
      return this._natpmpClient;
    } catch (e) {
      log.error("UPNP:natpmpClient Unable to initalize", e);
    }
  }

  /* return if NATPMP or UPNP
   *
   */
  getCapability(callback) {
    try {
      upnpClient.externalIp((err, ip) => {
        if (err != null || ip == null) {
          this.upnpEnabled = false;
          if (this.natpmpClient()) {

            this.natpmpClient().externalIp((err, info) => {
              if (err == null && info != null) {
                this.natpmpIP = info.ip.join('.');
                this.natpmpEnabled = true;
              } else {
                this.natpmpEnabled = false;
              }
              callback(null, this.upnpEnabled, this.natpmpEnabled);
            });
          }
        } else {
          this.upnpIP = ip;
          this.upnpEnabled = true;
          callback(null, this.upnpEnabled, this.natpmpEnabled);
        }
      });
    } catch (e) {
      log.error("UPNP.getCapability exception ", e, {});
    }

  }

  addPortMapping(protocol, localPort, externalPort, description, callback) {
    this.getCapability(() => {
      try {
        if (this.upnpEnabled == true) {
          return this.addPortMappingUPNP(protocol, localPort, externalPort, description, callback);
        } else if (this.natpmpEnabled == true) {
          return this.addPortMappingNATPMP(protocol, localPort, externalPort, description, callback);
        } else {
          callback(new Error("no upnp/natpmp"));
        }
      } catch (e) {
        log.error("UPNP.addPortMapping exception", e, {});
      }
    });
  }

  addPortMappingUPNP(protocol, localPort, externalPort, description, callback) {
    callback = callback || function () { };

    upnpClient.portMapping({
      type: protocol,
      protocol: protocol,
      private: localPort,
      public: externalPort,
      ttl: 0, // set ttl to 0 for better compatibility
      description: description
    }, (err) => {
      if (err) {
        log.error("Failed to add port mapping ", description, " :", err);
        callback(err);
        return;
      }
      log.info(util.format("Port mapping [%s, %s, %s] is added successfully.",
        protocol, localPort, externalPort));

      let mappingObj = { protocol, localPort, externalPort, description };

      // check if mapping registered
      if (_.isEmpty(upnpMappings.find((m) =>
        m.localPort     == localPort &&
        m.externalPort  == externalPort &&
        m.protocol      === protocol
      ))) {
        upnpMappings.push(mappingObj);
      } else {
        log.info("Mapping handler already exists");
      }

      callback(err);
    });
  }

  addPortMappingNATPMP(protocol, localPort, externalPort, description, callback) {
    callback = callback || function () { };
    if (this.natpmpClient() == null) {
      callback(new Error("natpmpClient null"), null);
      return;
    }
    this.natpmpClient().portMapping({ type: protocol, private: localPort, public: externalPort, ttl: natpmpTimeout }, (err, info) => {
      if (err == null) {
        this.refreshTimers[localPort + ":" + externalPort] = setTimeout(() => {
          this.addPortMappingNATPMP(protocol, localPort, externalPort, description, () => {
          });
        }, natpmpTimeout / 2 * 1000);
      }
      callback(err, info);
    });
  }

  removePortMappingNATPMP(protocol, localPort, externalPort, callback) {
    callback = callback || function () { };
    let timer = this.refreshTimers[localPort + ":" + externalPort];
    if (this.natpmpClient() == null) {
      callback(new Error("natpmpClient null"), null);
      return;
    }
    if (timer) {
      clearTimeout(timer);
    }
    this.natpmpClient().portUnmapping({ type: protocol, private: localPort, public: externalPort, ttl: 0 }, (err, info) => {
      if (err) {
        log.error("UPNP.removePortMappingNATPMP", err, {});
      }
      callback(err, info);
    });
  }

  removePortMapping(protocol, localPort, externalPort, callback) {
    callback = callback || function () { }
    this.getCapability(() => {
      try {
        if (this.upnpEnabled == true) {
          return this.removePortMappingUPNP(protocol, localPort, externalPort, callback);
        } else if (this.natpmpEnabled == true) {
          return this.removePortMappingNATPMP(protocol, localPort, externalPort, callback);
        } else {
          if (typeof callback === 'function') {
            callback(new Error("no upnp/natpmp"));
          }
        }
      } catch (e) {
        log.error("UPNP.removePortMapping Exception", e, {});
      }
    });
  }

  removePortMappingUPNP(protocol, localPort, externalPort, callback) {
    callback = callback || function () { };

    upnpClient.portUnmapping({
      protocol: protocol,
      private: localPort,
      public: externalPort
    }, (err) => {
      if (err) {
        log.error("UPNP Failed to remove port mapping: " + err);
        if (callback) {
          callback(err);
        }
        return;
      }

      upnpMappings = _.reject(upnpMappings, (m) =>
        m.localPort     == localPort &&
        m.externalPort  == externalPort &&
        m.protocol      === protocol
      );

      log.info(util.format("Port mapping [%s, %s, %s] is removed successfully"
        , protocol, localPort, externalPort));

      if (callback) {
        callback();
      }
    });
  }

  getLocalPortMappings(description, callback) {
    upnpClient.getMappings({
      // local: true,
      // description: description
    }, (err, results) => {
      callback(err, results);
    });
  }

  getPortMappingsUPNP(callback) {
    upnpClient.getMappings(callback);
  }

  hasPortMapping(protocol, localPort, externalPort, description, callback) {
    upnpClient.getMappings({
      // local: true
      // description: description
    }, (err, results) => {
      if (err) {
        log.error("Failed to get upnp mappings");
        callback(err);
        return;
      }
      log.debug(util.inspect(results));
      let matches = results.find((r) => this.mappingCompare(r, {protocol, localPort, externalPort}));

      log.debug(util.inspect(matches));

      callback(null, !matches.isEmpty)
    });
  }

  getRegisteredUpnpMappings() {
    return upnpMappings;
  }

  mappingCompare(natUpnpMapping, localMapping) {
    // "==" is used instead of "===" with intention here to enable comparison between number and string
    return  natUpnpMapping.public.port   ==  localMapping.externalPort &&
      natUpnpMapping.private.port  ==  localMapping.localPort &&
      natUpnpMapping.protocol      === localMapping.protocol;
  }

  async getExternalIP() {
    return new Promise((resolve, reject) => {
      upnpClient.externalIp((err, ip) => {
        if(err) {
          reject(err);
        } else {
          resolve(ip);
        }
      })
    });
  }
}

<commit_msg>make sure protocol is in lower case<commit_after>/*    Copyright 2017 Firewalla LLC
 *
 *    This program is free software: you can redistribute it and/or  modify
 *    it under the terms of the GNU Affero General Public License, version 3,
 *    as published by the Free Software Foundation.
 *
 *    This program is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    GNU Affero General Public License for more details.
 *
 *    You should have received a copy of the GNU Affero General Public License
 *    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
/**
 * Created by Melvin Tu on 05/01/2017.
 */

/*
 * WARNING:
 *   UPNP operations must be isolated to be one process.  NATPMP requires
 *   openning a port, which may cause trouble if two processes are doing
 *   the same
 */

'use strict';

let instance = null;

const log = require("../../net2/logger.js")(__filename);
const util = require('util');

const _ = require('lodash');

const f = require('../../net2/Firewalla.js');

const natpmp = require('./nat-pmp');
const natupnp = require('./nat-upnp');

const Promise = require('bluebird');

const upnpClient = natupnp.createClient();
//upnpClient.timeout = 10000; // set timeout to 10 seconds to avoid timeout too often
const natpmpTimeout = 86400;  // 1 day = 24 * 60 * 60 seconds

let upnpIntervalHandler = null;
let upnpMappings = [];
const upnpCheckInterval = 15 * 60 * 1000 // 15 mins

module.exports = class {
  constructor(loglevel, gw) {
    if (instance == null) {
      this.gw = gw;
      instance = this;
      this.refreshTimers = {};

      // TODO: move this to UPNPSensor
      // periodical checks whether all upnp mappings registered are alive
      // if not, adds back
      if (process.title === "FireMain") {
        upnpIntervalHandler = setInterval(
          () => {
            log.info("UPnP periodical check starts")
            if (upnpMappings.isEmpty) {
              log.info("No mapping registered.")
              return;
            }
            upnpClient.getMappings((err, results) => {
              if (err) {
                log.error("Failed to get current mappings", err);
                return;
              }
              log.info("Current mappings: ", results);

              upnpMappings.forEach((check) => {
                log.info("Checking registered mapping:", check);
                if (_.isEmpty(
                  results.find((m) => this.mappingCompare(m, check))
                )) {
                  log.info("Mapping no longer exists, adding back to router...")
                  let { protocol, localPort, externalPort, description } = check;
                  this.addPortMappingUPNP(protocol, localPort, externalPort, description)
                } else {
                  log.info("Mapping still exists")
                }
              })
            })
          },
          upnpCheckInterval
        )
      }
    }
    return instance;
  }

  natpmpClient() {
    try {
      if (this._natpmpClient == null) {
        this._natpmpClient = natpmp.connect(this.gw);
        this._natpmpClient.on('error', err => {
          log.error("natpmp emitted", err);
        });
      }
      return this._natpmpClient;
    } catch (e) {
      log.error("UPNP:natpmpClient Unable to initalize", e);
    }
  }

  /* return if NATPMP or UPNP
   *
   */
  getCapability(callback) {
    try {
      upnpClient.externalIp((err, ip) => {
        if (err != null || ip == null) {
          this.upnpEnabled = false;
          if (this.natpmpClient()) {

            this.natpmpClient().externalIp((err, info) => {
              if (err == null && info != null) {
                this.natpmpIP = info.ip.join('.');
                this.natpmpEnabled = true;
              } else {
                this.natpmpEnabled = false;
              }
              callback(null, this.upnpEnabled, this.natpmpEnabled);
            });
          }
        } else {
          this.upnpIP = ip;
          this.upnpEnabled = true;
          callback(null, this.upnpEnabled, this.natpmpEnabled);
        }
      });
    } catch (e) {
      log.error("UPNP.getCapability exception ", e, {});
    }

  }

  addPortMapping(protocol, localPort, externalPort, description, callback) {
    protocol = protocol.toLowerCase()
    this.getCapability(() => {
      try {
        if (this.upnpEnabled == true) {
          return this.addPortMappingUPNP(protocol, localPort, externalPort, description, callback);
        } else if (this.natpmpEnabled == true) {
          return this.addPortMappingNATPMP(protocol, localPort, externalPort, description, callback);
        } else {
          callback(new Error("no upnp/natpmp"));
        }
      } catch (e) {
        log.error("UPNP.addPortMapping exception", e, {});
      }
    });
  }

  addPortMappingUPNP(protocol, localPort, externalPort, description, callback) {
    protocol = protocol.toLowerCase()
    callback = callback || function () { };

    upnpClient.portMapping({
      type: protocol,
      protocol: protocol,
      private: localPort,
      public: externalPort,
      ttl: 0, // set ttl to 0 for better compatibility
      description: description
    }, (err) => {
      if (err) {
        log.error("Failed to add port mapping ", description, " :", err);
        callback(err);
        return;
      }
      log.info(util.format("Port mapping [%s, %s, %s] is added successfully.",
        protocol, localPort, externalPort));

      let mappingObj = { protocol, localPort, externalPort, description };

      // check if mapping registered
      if (_.isEmpty(upnpMappings.find((m) =>
        m.localPort     == localPort &&
        m.externalPort  == externalPort &&
        m.protocol      === protocol
      ))) {
        upnpMappings.push(mappingObj);
      } else {
        log.info("Mapping handler already exists");
      }

      callback(err);
    });
  }

  addPortMappingNATPMP(protocol, localPort, externalPort, description, callback) {
    protocol = protocol.toLowerCase()
    callback = callback || function () { };
    if (this.natpmpClient() == null) {
      callback(new Error("natpmpClient null"), null);
      return;
    }
    this.natpmpClient().portMapping({ type: protocol, private: localPort, public: externalPort, ttl: natpmpTimeout }, (err, info) => {
      if (err == null) {
        this.refreshTimers[localPort + ":" + externalPort] = setTimeout(() => {
          this.addPortMappingNATPMP(protocol, localPort, externalPort, description, () => {
          });
        }, natpmpTimeout / 2 * 1000);
      }
      callback(err, info);
    });
  }

  removePortMappingNATPMP(protocol, localPort, externalPort, callback) {
    protocol = protocol.toLowerCase()
    callback = callback || function () { };
    let timer = this.refreshTimers[localPort + ":" + externalPort];
    if (this.natpmpClient() == null) {
      callback(new Error("natpmpClient null"), null);
      return;
    }
    if (timer) {
      clearTimeout(timer);
    }
    this.natpmpClient().portUnmapping({ type: protocol, private: localPort, public: externalPort, ttl: 0 }, (err, info) => {
      if (err) {
        log.error("UPNP.removePortMappingNATPMP", err, {});
      }
      callback(err, info);
    });
  }

  removePortMapping(protocol, localPort, externalPort, callback) {
    protocol = protocol.toLowerCase()
    callback = callback || function () { }
    this.getCapability(() => {
      try {
        if (this.upnpEnabled == true) {
          return this.removePortMappingUPNP(protocol, localPort, externalPort, callback);
        } else if (this.natpmpEnabled == true) {
          return this.removePortMappingNATPMP(protocol, localPort, externalPort, callback);
        } else {
          if (typeof callback === 'function') {
            callback(new Error("no upnp/natpmp"));
          }
        }
      } catch (e) {
        log.error("UPNP.removePortMapping Exception", e, {});
      }
    });
  }

  removePortMappingUPNP(protocol, localPort, externalPort, callback) {
    protocol = protocol.toLowerCase()
    callback = callback || function () { };

    upnpClient.portUnmapping({
      protocol: protocol,
      private: localPort,
      public: externalPort
    }, (err) => {
      if (err) {
        log.error("UPNP Failed to remove port mapping: " + err);
        if (callback) {
          callback(err);
        }
        return;
      }

      upnpMappings = _.reject(upnpMappings, (m) =>
        m.localPort     == localPort &&
        m.externalPort  == externalPort &&
        m.protocol      === protocol
      );

      log.info(util.format("Port mapping [%s, %s, %s] is removed successfully"
        , protocol, localPort, externalPort));

      if (callback) {
        callback();
      }
    });
  }

  getLocalPortMappings(description, callback) {
    upnpClient.getMappings({
      // local: true,
      // description: description
    }, (err, results) => {
      callback(err, results);
    });
  }

  getPortMappingsUPNP(callback) {
    upnpClient.getMappings(callback);
  }

  hasPortMapping(protocol, localPort, externalPort, description, callback) {
    protocol = protocol.toLowerCase()
    upnpClient.getMappings({
      // local: true
      // description: description
    }, (err, results) => {
      if (err) {
        log.error("Failed to get upnp mappings");
        callback(err);
        return;
      }
      log.debug(util.inspect(results));
      let matches = results.find((r) => this.mappingCompare(r, {protocol, localPort, externalPort}));

      log.debug(util.inspect(matches));

      callback(null, !matches.isEmpty)
    });
  }

  getRegisteredUpnpMappings() {
    return upnpMappings;
  }

  mappingCompare(natUpnpMapping, localMapping) {
    // "==" is used instead of "===" with intention here to enable comparison between number and string
    return  natUpnpMapping.public.port   ==  localMapping.externalPort &&
      natUpnpMapping.private.port  ==  localMapping.localPort &&
      natUpnpMapping.protocol      === localMapping.protocol;
  }

  async getExternalIP() {
    return new Promise((resolve, reject) => {
      upnpClient.externalIp((err, ip) => {
        if(err) {
          reject(err);
        } else {
          resolve(ip);
        }
      })
    });
  }
}

<|endoftext|>


<commit_before>/**
 * Copyright 2003-present Greg Hurrell. All rights reserved.
 * Licensed under the terms of the MIT license.
 *
 * @flow
 */

import addDigits from './addDigits';

/**
 * Multiply an array of decimal digits, `multiplicand`, by `number`, returning
 * the result as an array of decimal digits in base `base`.
 *
 * Note that multiplication is implemented as repeated addition.
 */
export default function multiplyDigits(
  multiplicand: Array<number>,
  multiplier: number,
  base: number
): Array<number> {
  // Partial result being built up.
  let result = [0];

  // Running total of additions performed so far.
  let count = 0;

  // We memoize partial subresults in a faux-stack so they can be re-used.
  // (Faux because we never actually pop, as an optimization.)
  const memo = [];
  let memoIndex = -1;

  while (multiplier) {
    if (count && count * 2 <= multiplier) {
      // We can double the current result without exceeding the target.
      result = addDigits(result, result, base);
      multiplier -= count;
      count *= 2;
      memo.push({count, result});
      memoIndex++;
    } else if (memoIndex >= 0) {
      // We can use a previous result from the stack to leap ahead.
      while (memoIndex >= 0) {
        const previous = memo[memoIndex--];
        if (previous.count <= multiplier) {
          result = addDigits(result, previous.result, base);
          multiplier -= previous.count;
          count += previous.count;
          break;
        }
      }
    } else {
      // Stack is empty, so fall back to base case (single addition).
      result = addDigits(result, multiplicand, base);
      multiplier--;
      count++;
      memo.push({count, result});
      memoIndex++;
    }
  }

  return result;
}
<commit_msg>Some more probably immeasurable microoptimization<commit_after>/**
 * Copyright 2003-present Greg Hurrell. All rights reserved.
 * Licensed under the terms of the MIT license.
 *
 * @flow
 */

import addDigits from './addDigits';

/**
 * Multiply an array of decimal digits, `multiplicand`, by `number`, returning
 * the result as an array of decimal digits in base `base`.
 *
 * Note that multiplication is implemented as repeated addition.
 */
export default function multiplyDigits(
  multiplicand: Array<number>,
  multiplier: number,
  base: number
): Array<number> {
  // Partial result being built up.
  let result = [0];

  // Running total of additions performed so far.
  let count = 0;

  // We memoize partial subresults in a faux-stack so they can be re-used.
  // (Faux because we never actually pop, as an optimization.)
  const memo = [];
  let memoIndex = -1;

  while (multiplier) {
    if (count && count * 2 <= multiplier) {
      // We can double the current result without exceeding the target.
      result = addDigits(result, result, base);
      multiplier -= count;
      count *= 2;
      memo.push({count, result});
      memoIndex++;
    } else if (memoIndex >= 0) {
      // We can use a previous result from the stack to leap ahead.
      while (true) {
        const previous = memo[memoIndex--];
        if (previous.count <= multiplier) {
          result = addDigits(result, previous.result, base);
          multiplier -= previous.count;
          count += previous.count;
          break;
        }
      }
    } else {
      // Stack is empty, so fall back to base case (single addition).
      result = addDigits(result, multiplicand, base);
      multiplier--;
      count++;
      memo.push({count, result});
      memoIndex++;
    }
  }

  return result;
}
<|endoftext|>


<commit_before>var d = React.DOM
var ZONES = ['main', 'side'];

var Game = React.createClass({
  getInitialState() {
    return {
      isHost: false,
      selected: null,
      self: null,
      land: this.resetLand(),
      players: [],
      pack: [],
      main: [],
      side: [],
      junk: []
    };
  },
  componentDidMount() {
    this.decrement();

    App.on('join', this.join)
    App.on('add', this.add);
    App.on('set', this.set);

    this.join(this.props.room);
  },
  componentWillUnmount() {
    clearTimeout(this.timeoutID);
    App.off()
  },

  join(room) {
    this.setState(this.getInitialState());
    App.send('join', room);
  },

  add([card, isJunk]) {
    var state = {};
    var zone = isJunk ? 'junk' : App.state.zone
    state[zone] = this.state[zone].concat(card);
    this.setState(state);
  },

  set(state) {
    if (state.pool) {
      state[App.state.zone] = state.pool;
      delete state.pool;
    }
    if (state.pack && App.state.beep)
      this.refs.audio.getDOMNode().play();
    this.setState(state);
  },

  start() {
    App.send('start', App.state.bots);
  },

  decrement() {
    this.state.players.forEach(x => {
      if (x.time)
        x.time--;
    });
    this.forceUpdate();
    this.timeoutID = setTimeout(this.decrement, 1e3);
  },

  lands: {
    w: { cmc: 0, code: 'UNH', color: 'A', rarity: 5, type: 'Land', key: 'w', url: 'http://mtgimage.com/multiverseid/73963.jpg', name: 'Plains'   },
    u: { cmc: 0, code: 'UNH', color: 'A', rarity: 5, type: 'Land', key: 'u', url: 'http://mtgimage.com/multiverseid/73951.jpg', name: 'Island'   },
    b: { cmc: 0, code: 'UNH', color: 'A', rarity: 5, type: 'Land', key: 'b', url: 'http://mtgimage.com/multiverseid/73973.jpg', name: 'Swamp'    },
    r: { cmc: 0, code: 'UNH', color: 'A', rarity: 5, type: 'Land', key: 'r', url: 'http://mtgimage.com/multiverseid/73958.jpg', name: 'Mountain' },
    g: { cmc: 0, code: 'UNH', color: 'A', rarity: 5, type: 'Land', key: 'g', url: 'http://mtgimage.com/multiverseid/73946.jpg', name: 'Forest'   }
  },

  clickPack(index) {
    if (this.state.selected !== index)
      return this.state.selected = index;

    App.send('pick', index);
    this.setState({pack: [], selected: null});
  },

  clickPool(index, zoneName, e) {
    var {land, main, side, junk} = this.state;
    var from = this.state[zoneName];
    var card = from[index];

    if (card.key) {
      var {key} = card;
      land[zoneName][key]--;
      this.setLand(land);
      return;
    }

    var to;
    if (e.shiftKey)
      to = zoneName === 'junk' ? main : junk;
    else
      to = zoneName === 'side' ? main : side;

    from.splice(index, 1);
    to.push(card);
    this.setState({ main, side, junk });
  },

  setLand(land) {
    ZONES.forEach(zoneName => {
      var zone = this.state[zoneName].filter(x => !x.key)
      var color, i;
      for (color in land[zoneName]) {
        i = 0;
        while (i++ < land[zoneName][color])
          zone.push(this.lands[color]);
      }
      this.state[zoneName] = zone;
    });
    this.forceUpdate();
  },

  changeLand(color, zoneName, e) {
    var {land} = this.state;
    land[zoneName][color] = parseInt(e.target.value);
    this.setLand(land);
  },

  changeZone(e) {
    var {value} = e.target;
    var {main, side} = this.state;
    var all = main.concat(side).filter(x => !x.key)
    value === 'main' ?
      (main = all, side = []) :
      (main = [],  side = all);

    var land = this.resetLand();

    this.setState({ land, main, side });
    App.save('zone', value);
  },

  resetLand() {
    var land = {};
    ZONES.forEach(zoneName => {
      land[zoneName] = {};
      ['w', 'u', 'b', 'r', 'g'].forEach(color =>
        land[zoneName][color] = 0);
    });
    return land;
  },

  download() {
    var {filename, filetype} = App.state;

    var fileText = this.generate(filetype);

    var link = document.createElement('a');
    link.download = filename + '.' + filetype;
    link.href = 'data:,' + encodeURIComponent(fileText);
    document.body.appendChild(link);
    link.click();
    link.remove();
  },

  generate(filetype) {
    var deck = {
      main: {},
      side: {}
    };
    var codes = {};
    ZONES.forEach(zone => {
      this.state[zone].forEach(card => {
        var {code, name} = card;
        codes[name] = code;
        deck[zone][name] || (deck[zone][name] = 0);
        deck[zone][name]++;
      });
    });

    App.send('hash', deck)

    return this.gen[filetype].call(this, deck, codes);
  },

  gen: {
    cod(deck) {
      var fn = zone => {
        var arr = [];
        for (var name in zone)
          arr.push(`    <card number="${zone[name]}" name="${name}"/>`);
        return arr.join('\n');
      };

      var data = `<?xml version="1.0" encoding="UTF-8"?>
<cockatrice_deck version="1">
  <deckname>${App.state.filename}</deckname>
  <zone name="main">
${fn(deck.main)}
  </zone>
  <zone name="side">
${fn(deck.side)}
  </zone>
</cockatrice_deck>`;
      return data;
    },

    json(deck) {
      return JSON.stringify(deck, null, 2);
    },

    mwdeck(deck, codes) {
      var data = [];
      var code, name, prefix, zone, zoneName;
      for (zoneName in deck) {
        prefix = zoneName === 'side' ? 'SB: ' : '';
        zone = deck[zoneName];
        for (name in zone) {
          code = codes[name];
          name = name.replace(' // ', '/');
          data.push(prefix + zone[name] + ' [' + code + '] ' + name);
        }
      }
      return data.join('\n');
    },

    txt(deck) {
      var _deck = {}
      ZONES.forEach(zoneName => {
        _deck[zoneName] = []
        var zone = deck[zoneName]
        for (var card in zone)
          _deck[zoneName].push(zone[card] + ' ' + card)
      })
      return _deck.main.join('\n') + '\nSideboard\n' + _deck.side.join('\n')
    }
  },

  render() {
    return d.div({},
      d.audio({
        ref: 'audio',
        src: '/beep.wav'}),
      Settings({
        change: this.change,
        changeLand: this.changeLand,
        changeZone: this.changeZone,
        download: this.download,
        generate: this.generate,
        set: this.set,

        land: this.state.land,
        round: this.state.round}),
      Stats({
        change: this.change,
        start: this.start,

        isHost: this.state.isHost,
        players: this.state.players,
        round: this.state.round,
        self: this.state.self,
        title: this.state.title}),
      Cards({
        clickPack: this.clickPack,
        clickPool: this.clickPool,

        pack: this.state.pack,
        main: this.state.main,
        side: this.state.side,
        junk: this.state.junk}))
  }
});

var Settings = React.createClass({
  getInitialState() {
    return {
      decklist: ''
    };
  },

  copy() {
    var state = { decklist: this.props.generate('txt') };
    var cb = () => this.refs.decklist.getDOMNode().select();
    this.setState(state, cb);
  },

  render() {
    var sort = ['cmc', 'color', 'rarity', 'type'].map(x =>
      d.button({
        onClick: App.change('sort'),
        disabled: App.state.sort === x},
        x))
    var zone = ZONES.map(x =>
      d.label({},
        d.input({
          type: 'radio',
          name: 'zone',
          value: x,
          checked: App.state.zone === x,
          onChange: this.props.changeZone},
          x)))

    var lands = {};
    ZONES.forEach(zoneName => {
      lands[zoneName] = ['w', 'u', 'b', 'r', 'g'].map(x =>
        d.td({},
          d.input({
            type: 'number',
            min: 0,
            value: this.props.land[zoneName][x],
            onChange: this.props.changeLand.bind(null, x, zoneName)})))})

    return d.div({ className: 'settings' },
      d.table({},
        d.tbody({},
          d.tr({},
            d.td(),
            d.td({}, d.img({ src: 'http://mtgimage.com/symbol/mana/w.svg' })),
            d.td({}, d.img({ src: 'http://mtgimage.com/symbol/mana/u.svg' })),
            d.td({}, d.img({ src: 'http://mtgimage.com/symbol/mana/b.svg' })),
            d.td({}, d.img({ src: 'http://mtgimage.com/symbol/mana/r.svg' })),
            d.td({}, d.img({ src: 'http://mtgimage.com/symbol/mana/g.svg' }))),
          d.tr({},
            d.td({}, 'main'),
            lands.main),
          d.tr({},
            d.td({}, 'side'),
            lands.side))),
      d.div({},
        d.button({
          disabled: this.props.round !== -1,
          onClick: this.props.download},
          'download'),
        d.input({
          placeholder: 'filename',
          value: App.state.filename,
          onChange: App.change('filename')}),
        d.select({
          value: App.state.filetype,
          onChange: App.change('filetype')},
          d.option({}, 'cod'),
          d.option({}, 'json'),
          d.option({}, 'mwdeck'),
          d.option({}, 'txt'))),
      d.div({},
        d.button({
          disabled: this.props.round !== -1,
          onClick: this.copy},
          'copy'),
        d.textarea({
          placeholder: 'decklist',
          ref: 'decklist',
          readOnly: true,
          value: this.state.decklist})),
      d.div({},
        d.label({},
          'beep when receiving packs',
          d.input({
            type: 'checkbox',
            checked: App.state.beep,
            onChange: App.change('beep')}))),
      d.div({}, sort))
  }
});

var Stats = React.createClass({
  getInitialState() {
    return {
      edit: false
    };
  },

  click() {
    this.setState({ edit: true }, () =>
      this.refs.nameEl.getDOMNode().focus()
    );
  },

  _edit() {
    this.setState({ edit: true }, ()=>
      this.refs.name.getDOMNode().focus())
  },
  _name(e) {
    e.preventDefault()

    this.setState({ edit: false })
    var name = this.refs.name.getDOMNode().value.slice(0, 15)

    if (!name || name === App.state.name)
      return

    App.save('name', name)
    App.send('name', name)
  },

  render() {
    var {players, self} = this.props;
    var {length} = players
    if (!(length % 2))
      var opp = (self + length / 2) % length
    players = players.map((p, i) => {
      if (i === self)
        var className = 'self'
      else if (i === opp)
        className = 'opponent'

      if (i === self) {
        if (this.state.edit)
          var content = d.form({ onSubmit: this._name },
            d.input({ ref: 'name', defaultValue: p.name}))
        else
          content = d.a({ href: 'javascript:;' }, p.name)
        var td = d.td({ onClick: this._edit }, content)
      } else
        td = d.td({}, p.name)

      return d.tr({
        className: className},
        d.td({}, i + 1),
        td,
        d.td({}, p.time),
        d.td({}, p.packs),
        d.td({}, p.hash && p.hash.cock),
        d.td({}, p.hash && p.hash.mws))
    })

    return d.div({},
      d.div({}, d.strong({}, this.props.title)),
      d.div({
        hidden: this.props.round || !this.props.isHost},
        d.button({
          onClick: this.props.start},
          'start'),
        d.label({},
          'add bots',
          d.input({
            type: 'checkbox',
            checked: App.state.bots,
            onChange: App.change('bots')}))),
      d.table({}, d.tbody({
        id: 'stats'},
        d.tr({},
          d.th({}, '#'),
          d.th({}, 'name'),
          d.th({}, 'time'),
          d.th({}, 'packs'),
          d.th({}, 'cock'),
          d.th({}, 'mws')),
        players)))
  }
});

var Cards = React.createClass({
  sort(arr) {
    var {sort} = App.state;

    return arr.sort((a, b) => {
      if (a[sort] < b[sort])
        return -1;
      if (a[sort] > b[sort])
        return +1;

      if (a.name < b.name)
        return -1;
      if (a.name > b.name)
        return +1;

      return 0;
    });
  },

  show(arr, zone) {
    var cb = zone === 'pack' ?
      this.props.clickPack:
      this.props.clickPool;

    return arr.map((card, i) =>
      d.img({
        onClick: cb.bind(null, i, zone),
        src: card.url}))
  },

  render() {
    var {pack, main, side, junk} = this.props;
    pack = this.show(pack, 'pack');
    main = this.show(this.sort(main), 'main');
    side = this.show(this.sort(side), 'side');
    junk = this.show(this.sort(junk), 'junk');

    return d.div({
      className: 'cards'},
      d.div({
        hidden: !pack.length},
        d.h1({}, 'pack ' + pack.length),
        pack),
      d.div({},
        d.h1({}, 'main ' + main.length),
        main),
      d.div({},
        d.h1({}, 'side ' + side.length),
        side),
      d.div({},
        d.h1({}, 'junk ' + junk.length),
        junk))
  }
});
<commit_msg>readd zone switcher<commit_after>var d = React.DOM
var ZONES = ['main', 'side'];

var Game = React.createClass({
  getInitialState() {
    return {
      isHost: false,
      selected: null,
      self: null,
      land: this.resetLand(),
      players: [],
      pack: [],
      main: [],
      side: [],
      junk: []
    };
  },
  componentDidMount() {
    this.decrement();

    App.on('join', this.join)
    App.on('add', this.add);
    App.on('set', this.set);

    this.join(this.props.room);
  },
  componentWillUnmount() {
    clearTimeout(this.timeoutID);
    App.off()
  },

  join(room) {
    this.setState(this.getInitialState());
    App.send('join', room);
  },

  add([card, isJunk]) {
    var state = {};
    var zone = isJunk ? 'junk' : App.state.zone
    state[zone] = this.state[zone].concat(card);
    this.setState(state);
  },

  set(state) {
    if (state.pool) {
      state[App.state.zone] = state.pool;
      delete state.pool;
    }
    if (state.pack && App.state.beep)
      this.refs.audio.getDOMNode().play();
    this.setState(state);
  },

  start() {
    App.send('start', App.state.bots);
  },

  decrement() {
    this.state.players.forEach(x => {
      if (x.time)
        x.time--;
    });
    this.forceUpdate();
    this.timeoutID = setTimeout(this.decrement, 1e3);
  },

  lands: {
    w: { cmc: 0, code: 'UNH', color: 'A', rarity: 5, type: 'Land', key: 'w', url: 'http://mtgimage.com/multiverseid/73963.jpg', name: 'Plains'   },
    u: { cmc: 0, code: 'UNH', color: 'A', rarity: 5, type: 'Land', key: 'u', url: 'http://mtgimage.com/multiverseid/73951.jpg', name: 'Island'   },
    b: { cmc: 0, code: 'UNH', color: 'A', rarity: 5, type: 'Land', key: 'b', url: 'http://mtgimage.com/multiverseid/73973.jpg', name: 'Swamp'    },
    r: { cmc: 0, code: 'UNH', color: 'A', rarity: 5, type: 'Land', key: 'r', url: 'http://mtgimage.com/multiverseid/73958.jpg', name: 'Mountain' },
    g: { cmc: 0, code: 'UNH', color: 'A', rarity: 5, type: 'Land', key: 'g', url: 'http://mtgimage.com/multiverseid/73946.jpg', name: 'Forest'   }
  },

  clickPack(index) {
    if (this.state.selected !== index)
      return this.state.selected = index;

    App.send('pick', index);
    this.setState({pack: [], selected: null});
  },

  clickPool(index, zoneName, e) {
    var {land, main, side, junk} = this.state;
    var from = this.state[zoneName];
    var card = from[index];

    if (card.key) {
      var {key} = card;
      land[zoneName][key]--;
      this.setLand(land);
      return;
    }

    var to;
    if (e.shiftKey)
      to = zoneName === 'junk' ? main : junk;
    else
      to = zoneName === 'side' ? main : side;

    from.splice(index, 1);
    to.push(card);
    this.setState({ main, side, junk });
  },

  setLand(land) {
    ZONES.forEach(zoneName => {
      var zone = this.state[zoneName].filter(x => !x.key)
      var color, i;
      for (color in land[zoneName]) {
        i = 0;
        while (i++ < land[zoneName][color])
          zone.push(this.lands[color]);
      }
      this.state[zoneName] = zone;
    });
    this.forceUpdate();
  },

  changeLand(color, zoneName, e) {
    var {land} = this.state;
    land[zoneName][color] = parseInt(e.target.value);
    this.setLand(land);
  },

  changeZone(e) {
    var {value} = e.target;
    var {main, side} = this.state;
    var all = main.concat(side).filter(x => !x.key)
    value === 'main' ?
      (main = all, side = []) :
      (main = [],  side = all);

    var land = this.resetLand();

    this.setState({ land, main, side });
    App.save('zone', value);
  },

  resetLand() {
    var land = {};
    ZONES.forEach(zoneName => {
      land[zoneName] = {};
      ['w', 'u', 'b', 'r', 'g'].forEach(color =>
        land[zoneName][color] = 0);
    });
    return land;
  },

  download() {
    var {filename, filetype} = App.state;

    var fileText = this.generate(filetype);

    var link = document.createElement('a');
    link.download = filename + '.' + filetype;
    link.href = 'data:,' + encodeURIComponent(fileText);
    document.body.appendChild(link);
    link.click();
    link.remove();
  },

  generate(filetype) {
    var deck = {
      main: {},
      side: {}
    };
    var codes = {};
    ZONES.forEach(zone => {
      this.state[zone].forEach(card => {
        var {code, name} = card;
        codes[name] = code;
        deck[zone][name] || (deck[zone][name] = 0);
        deck[zone][name]++;
      });
    });

    App.send('hash', deck)

    return this.gen[filetype].call(this, deck, codes);
  },

  gen: {
    cod(deck) {
      var fn = zone => {
        var arr = [];
        for (var name in zone)
          arr.push(`    <card number="${zone[name]}" name="${name}"/>`);
        return arr.join('\n');
      };

      var data = `<?xml version="1.0" encoding="UTF-8"?>
<cockatrice_deck version="1">
  <deckname>${App.state.filename}</deckname>
  <zone name="main">
${fn(deck.main)}
  </zone>
  <zone name="side">
${fn(deck.side)}
  </zone>
</cockatrice_deck>`;
      return data;
    },

    json(deck) {
      return JSON.stringify(deck, null, 2);
    },

    mwdeck(deck, codes) {
      var data = [];
      var code, name, prefix, zone, zoneName;
      for (zoneName in deck) {
        prefix = zoneName === 'side' ? 'SB: ' : '';
        zone = deck[zoneName];
        for (name in zone) {
          code = codes[name];
          name = name.replace(' // ', '/');
          data.push(prefix + zone[name] + ' [' + code + '] ' + name);
        }
      }
      return data.join('\n');
    },

    txt(deck) {
      var _deck = {}
      ZONES.forEach(zoneName => {
        _deck[zoneName] = []
        var zone = deck[zoneName]
        for (var card in zone)
          _deck[zoneName].push(zone[card] + ' ' + card)
      })
      return _deck.main.join('\n') + '\nSideboard\n' + _deck.side.join('\n')
    }
  },

  render() {
    return d.div({},
      d.audio({
        ref: 'audio',
        src: '/beep.wav'}),
      Settings({
        change: this.change,
        changeLand: this.changeLand,
        changeZone: this.changeZone,
        download: this.download,
        generate: this.generate,
        set: this.set,

        land: this.state.land,
        round: this.state.round}),
      Stats({
        change: this.change,
        start: this.start,

        isHost: this.state.isHost,
        players: this.state.players,
        round: this.state.round,
        self: this.state.self,
        title: this.state.title}),
      Cards({
        clickPack: this.clickPack,
        clickPool: this.clickPool,

        pack: this.state.pack,
        main: this.state.main,
        side: this.state.side,
        junk: this.state.junk}))
  }
});

var Settings = React.createClass({
  getInitialState() {
    return {
      decklist: ''
    };
  },

  copy() {
    var state = { decklist: this.props.generate('txt') };
    var cb = () => this.refs.decklist.getDOMNode().select();
    this.setState(state, cb);
  },

  render() {
    var sort = ['cmc', 'color', 'rarity', 'type'].map(x =>
      d.button({
        onClick: App.change('sort'),
        disabled: App.state.sort === x},
        x))
    var zone = ZONES.map(x =>
      d.label({},
        d.input({
          type: 'radio',
          name: 'zone',
          value: x,
          checked: App.state.zone === x,
          onChange: this.props.changeZone},
          x)))

    var lands = {};
    ZONES.forEach(zoneName => {
      lands[zoneName] = ['w', 'u', 'b', 'r', 'g'].map(x =>
        d.td({},
          d.input({
            type: 'number',
            min: 0,
            value: this.props.land[zoneName][x],
            onChange: this.props.changeLand.bind(null, x, zoneName)})))})

    return d.div({ className: 'settings' },
      d.table({},
        d.tbody({},
          d.tr({},
            d.td(),
            d.td({}, d.img({ src: 'http://mtgimage.com/symbol/mana/w.svg' })),
            d.td({}, d.img({ src: 'http://mtgimage.com/symbol/mana/u.svg' })),
            d.td({}, d.img({ src: 'http://mtgimage.com/symbol/mana/b.svg' })),
            d.td({}, d.img({ src: 'http://mtgimage.com/symbol/mana/r.svg' })),
            d.td({}, d.img({ src: 'http://mtgimage.com/symbol/mana/g.svg' }))),
          d.tr({},
            d.td({}, 'main'),
            lands.main),
          d.tr({},
            d.td({}, 'side'),
            lands.side))),
      d.div({},
        d.button({
          disabled: this.props.round !== -1,
          onClick: this.props.download},
          'download'),
        d.input({
          placeholder: 'filename',
          value: App.state.filename,
          onChange: App.change('filename')}),
        d.select({
          value: App.state.filetype,
          onChange: App.change('filetype')},
          d.option({}, 'cod'),
          d.option({}, 'json'),
          d.option({}, 'mwdeck'),
          d.option({}, 'txt'))),
      d.div({},
        d.button({
          disabled: this.props.round !== -1,
          onClick: this.copy},
          'copy'),
        d.textarea({
          placeholder: 'decklist',
          ref: 'decklist',
          readOnly: true,
          value: this.state.decklist})),
      d.div({},
        'add cards to',
        zone),
      d.div({},
        d.label({},
          'beep when receiving packs',
          d.input({
            type: 'checkbox',
            checked: App.state.beep,
            onChange: App.change('beep')}))),
      d.div({}, sort))
  }
});

var Stats = React.createClass({
  getInitialState() {
    return {
      edit: false
    };
  },

  click() {
    this.setState({ edit: true }, () =>
      this.refs.nameEl.getDOMNode().focus()
    );
  },

  _edit() {
    this.setState({ edit: true }, ()=>
      this.refs.name.getDOMNode().focus())
  },
  _name(e) {
    e.preventDefault()

    this.setState({ edit: false })
    var name = this.refs.name.getDOMNode().value.slice(0, 15)

    if (!name || name === App.state.name)
      return

    App.save('name', name)
    App.send('name', name)
  },

  render() {
    var {players, self} = this.props;
    var {length} = players
    if (!(length % 2))
      var opp = (self + length / 2) % length
    players = players.map((p, i) => {
      if (i === self)
        var className = 'self'
      else if (i === opp)
        className = 'opponent'

      if (i === self) {
        if (this.state.edit)
          var content = d.form({ onSubmit: this._name },
            d.input({ ref: 'name', defaultValue: p.name}))
        else
          content = d.a({ href: 'javascript:;' }, p.name)
        var td = d.td({ onClick: this._edit }, content)
      } else
        td = d.td({}, p.name)

      return d.tr({
        className: className},
        d.td({}, i + 1),
        td,
        d.td({}, p.time),
        d.td({}, p.packs),
        d.td({}, p.hash && p.hash.cock),
        d.td({}, p.hash && p.hash.mws))
    })

    return d.div({},
      d.div({}, d.strong({}, this.props.title)),
      d.div({
        hidden: this.props.round || !this.props.isHost},
        d.button({
          onClick: this.props.start},
          'start'),
        d.label({},
          'add bots',
          d.input({
            type: 'checkbox',
            checked: App.state.bots,
            onChange: App.change('bots')}))),
      d.table({}, d.tbody({
        id: 'stats'},
        d.tr({},
          d.th({}, '#'),
          d.th({}, 'name'),
          d.th({}, 'time'),
          d.th({}, 'packs'),
          d.th({}, 'cock'),
          d.th({}, 'mws')),
        players)))
  }
});

var Cards = React.createClass({
  sort(arr) {
    var {sort} = App.state;

    return arr.sort((a, b) => {
      if (a[sort] < b[sort])
        return -1;
      if (a[sort] > b[sort])
        return +1;

      if (a.name < b.name)
        return -1;
      if (a.name > b.name)
        return +1;

      return 0;
    });
  },

  show(arr, zone) {
    var cb = zone === 'pack' ?
      this.props.clickPack:
      this.props.clickPool;

    return arr.map((card, i) =>
      d.img({
        onClick: cb.bind(null, i, zone),
        src: card.url}))
  },

  render() {
    var {pack, main, side, junk} = this.props;
    pack = this.show(pack, 'pack');
    main = this.show(this.sort(main), 'main');
    side = this.show(this.sort(side), 'side');
    junk = this.show(this.sort(junk), 'junk');

    return d.div({
      className: 'cards'},
      d.div({
        hidden: !pack.length},
        d.h1({}, 'pack ' + pack.length),
        pack),
      d.div({},
        d.h1({}, 'main ' + main.length),
        main),
      d.div({},
        d.h1({}, 'side ' + side.length),
        side),
      d.div({},
        d.h1({}, 'junk ' + junk.length),
        junk))
  }
});
<|endoftext|>


<commit_before>(function () {
    'use-strict';

    var defaults = {
        templateUrl: 'ng-iframeUpload.html'
    };
    var iframeUpload = function ($templateCache) {
        $templateCache.put(defaults.template_url,
            '' +
            '<form style="display: none" action="{{url}}" method="post" enctype="multipart/form-data" target="{{iframeName}}">' +
            '<input type="hidden" ng-repeat="item in hiddens" name="{{item.key}}" value="{{item.value}}" /></form>');
        return {
            restrict: 'EA',
            scope: {
                images: '=',
                thumbsNum: '@'
            },
            controller: [
                '$scope',
                function ($scope) {
                    $scope.$on('ng-change', function (e, args) {
                        $scope.openGallery(args.index);
                    });
                }
            ],
            templateUrl: function (element, attrs) {
                return attrs.templateUrl || defaults.templateUrl;
            }
        }
    };
    angular.module('iframeUpload', []).directive('iframeUpload', iframeUpload);
    iframeUpload.$inject = ['$templateCache'];
})();<commit_msg>Update ngiframeUpload.js<commit_after>(function () {
    'use-strict';

    var defaults = {
        templateUrl: 'ng-iframeUpload.html'
    };
    var iframeUpload = function ($templateCache) {
        $templateCache.put(defaults.template_url,
            '' +
            '<form style="display: none" action="{{url}}" method="post" enctype="multipart/form-data" target="{{iframeName}}">' +
            '<input type="hidden" ng-repeat="item in hiddens" name="{{item.key}}" value="{{item.value}}" /></form>');
        return {
            restrict: 'EA',
            scope: {
                change: '&',
                url: '@'
            },
            controller: [
                '$scope',
                function ($scope) {
                }
            ],
            templateUrl: function (element, attrs) {
                return attrs.templateUrl || defaults.templateUrl;
            }
        }
    };
    angular.module('iframeUpload', []).directive('iframeUpload', iframeUpload);
    iframeUpload.$inject = ['$templateCache'];
})();
<|endoftext|>


<commit_before>const { app, BrowserWindow, Tray, Menu } = require('electron');
const MenuBuilder = require('./menu');

let mainWindow = null;
let preloader = null;

global.globalObj = {
  id: 123,
  launch: null,
};

const shouldQuit = app.makeSingleInstance((commandLine, workingDirectory) => {
  if (mainWindow) {
    if (mainWindow.isMinimized()) {
      mainWindow.restore();
    }

    mainWindow.show();
    mainWindow.focus();

    mainWindow.webContents.send("cmdArgs", commandLine);
  }
});

if (shouldQuit) {
  app.quit();
}

if (process.env.NODE_ENV === 'production') {
  const sourceMapSupport = require('source-map-support');
  sourceMapSupport.install();
}

var options = process.argv;

if (options.indexOf('logcrypto') !== -1) {
  global.sharedObject = { logcrypto: true };
} else {
  global.sharedObject = { logcrypto: false };
}

global.sharedObject.isQuiting = false;

if (process.env.NODE_ENV === 'development' || process.env.DEBUG_PROD === 'true') {
  require('electron-debug')();
  const path = require('path');
  const p = path.join(__dirname, '..', 'app', 'node_modules');
  require('module').globalPaths.push(p);
}

const installExtensions = async () => {
  const installer = require('electron-devtools-installer');
  const forceDownload = !!process.env.UPGRADE_EXTENSIONS;
  const extensions = [
    'REACT_DEVELOPER_TOOLS',
    'REDUX_DEVTOOLS'
  ];

  return Promise
    .all(extensions.map(name => installer.default(installer[name], forceDownload)))
    .catch(console.log);
};

app.on('window-all-closed', () => {
  // Respect the OSX convention of having the application in memory even
  // after all windows have been closed
  if (process.platform !== 'darwin') {
    app.quit();
  }
});

app.on('ready', async () => {
  if (process.env.NODE_ENV === 'development' || process.env.DEBUG_PROD === 'true') {
    await installExtensions();
  }

  app.commandLine.appendSwitch('ignore-certificate-errors');

  mainWindow = new BrowserWindow({
    alwaysOnTop: false,
    width: 800, height: 600,
    resizable: false,
    frame: false,
    toolbar: false,
    show: false,
  });

  preloader = new BrowserWindow({
    alwaysOnTop: true,
    width: 496, height: 149,
    resizable: false,
    frame: false,
    toolbar: false,
    show: false
  });

  mainWindow.loadURL(`file://${__dirname}/resources/index.html`);
  preloader.loadURL(`file://${__dirname}/resources/preloader_index.html`);

  if (options.indexOf("devtools") !== -1) {
    // Open the DevTools.
    mainWindow.webContents.openDevTools();
  }

  const platform = require('os').platform();
  const lang = app.getLocale().split("-")[0];
  let trayIcon;

  if (platform == 'win32') {
    trayIcon = new Tray(__dirname + '/resources/image/tray.ico');
  } else if (platform == 'darwin') {
    trayIcon = new Tray(__dirname + '/resources/image/tray_mac.png');
  } else {
    trayIcon = new Tray(__dirname + '/resources/image/tray.png');
  }

  const trayMenuTemplate = [
    {
      label: lang === 'ru' ? 'Открыть приложение' : 'Open Application',
      click: function () {
        mainWindow.show();

        if (process.platform === "darwin") {
          app.dock.show();
        }
      }
    },
    {
      label: lang === 'ru' ? 'Выйти' : 'Close',
      click: function () {
        global.sharedObject.isQuiting = true;
        app.quit();
      }
    }
  ];

  var trayMenu = Menu.buildFromTemplate(trayMenuTemplate);
  trayIcon.setContextMenu(trayMenu);

  if (process.platform === "darwin") {
    app.dock.setMenu(trayMenu);
  }

  var startMinimized = (process.argv || []).indexOf('--service') !== -1;

  startMinimized = false; //Временно

  if (startMinimized == true) {
    //console.log('App is started by AutoLaunch');
    globalObj.launch = true;
    if (mainWindow) mainWindow.hide();
  } else {
    globalObj.launch = false;
    //console.log('App is started by User');
  }

  preloader.webContents.on("did-finish-load", () => {
    preloader.show();
    preloader.focus();
  });

  preloader.on("close", function () {
    preloader = null;
  });

  // @TODO: Use 'ready-to-show' event
  //        https://github.com/electron/electron/blob/master/docs/api/browser-window.md#using-ready-to-show-event
  mainWindow.webContents.on('did-finish-load', () => {
    if (!mainWindow) {
      throw new Error('"mainWindow" is not defined');
    }

    preloader.close();
    if (!startMinimized) {
      mainWindow.show();
      mainWindow.focus();
    }

    mainWindow.webContents.send("cmdArgs", options);
  });

  mainWindow.on('close', function (event) {
    if (!global.sharedObject.isQuiting) {
      event.preventDefault();
      mainWindow.hide();

      if (process.platform === "darwin") {
        app.dock.hide();
      }
    }

    return false;
  });

  const menuBuilder = new MenuBuilder(mainWindow);
  menuBuilder.buildMenu();
});

<commit_msg>quit from app on side menu exit click<commit_after>const { app, BrowserWindow, Tray, Menu } = require('electron');
const MenuBuilder = require('./menu');

let mainWindow = null;
let preloader = null;

global.globalObj = {
  id: 123,
  launch: null,
};

const shouldQuit = app.makeSingleInstance((commandLine, workingDirectory) => {
  if (mainWindow) {
    if (mainWindow.isMinimized()) {
      mainWindow.restore();
    }

    mainWindow.show();
    mainWindow.focus();

    mainWindow.webContents.send("cmdArgs", commandLine);
  }
});

if (shouldQuit) {
  app.quit();
}

if (process.env.NODE_ENV === 'production') {
  const sourceMapSupport = require('source-map-support');
  sourceMapSupport.install();
}

var options = process.argv;

if (options.indexOf('logcrypto') !== -1) {
  global.sharedObject = { logcrypto: true };
} else {
  global.sharedObject = { logcrypto: false };
}

global.sharedObject.isQuiting = false;

if (process.env.NODE_ENV === 'development' || process.env.DEBUG_PROD === 'true') {
  require('electron-debug')();
  const path = require('path');
  const p = path.join(__dirname, '..', 'app', 'node_modules');
  require('module').globalPaths.push(p);
}

const installExtensions = async () => {
  const installer = require('electron-devtools-installer');
  const forceDownload = !!process.env.UPGRADE_EXTENSIONS;
  const extensions = [
    'REACT_DEVELOPER_TOOLS',
    'REDUX_DEVTOOLS'
  ];

  return Promise
    .all(extensions.map(name => installer.default(installer[name], forceDownload)))
    .catch(console.log);
};

app.on('window-all-closed', () => {
    app.quit();
});

app.on('ready', async () => {
  if (process.env.NODE_ENV === 'development' || process.env.DEBUG_PROD === 'true') {
    await installExtensions();
  }

  app.commandLine.appendSwitch('ignore-certificate-errors');

  mainWindow = new BrowserWindow({
    alwaysOnTop: false,
    width: 800, height: 600,
    resizable: false,
    frame: false,
    toolbar: false,
    show: false,
  });

  preloader = new BrowserWindow({
    alwaysOnTop: true,
    width: 496, height: 149,
    resizable: false,
    frame: false,
    toolbar: false,
    show: false
  });

  mainWindow.loadURL(`file://${__dirname}/resources/index.html`);
  preloader.loadURL(`file://${__dirname}/resources/preloader_index.html`);

  if (options.indexOf("devtools") !== -1) {
    // Open the DevTools.
    mainWindow.webContents.openDevTools();
  }

  const platform = require('os').platform();
  const lang = app.getLocale().split("-")[0];
  let trayIcon;

  if (platform == 'win32') {
    trayIcon = new Tray(__dirname + '/resources/image/tray.ico');
  } else if (platform == 'darwin') {
    trayIcon = new Tray(__dirname + '/resources/image/tray_mac.png');
  } else {
    trayIcon = new Tray(__dirname + '/resources/image/tray.png');
  }

  const trayMenuTemplate = [
    {
      label: lang === 'ru' ? 'Открыть приложение' : 'Open Application',
      click: function () {
        mainWindow.show();

        if (process.platform === "darwin") {
          app.dock.show();
        }
      }
    },
    {
      label: lang === 'ru' ? 'Выйти' : 'Close',
      click: function () {
        global.sharedObject.isQuiting = true;
        app.quit();
      }
    }
  ];

  var trayMenu = Menu.buildFromTemplate(trayMenuTemplate);
  trayIcon.setContextMenu(trayMenu);

  if (process.platform === "darwin") {
    app.dock.setMenu(trayMenu);
  }

  var startMinimized = (process.argv || []).indexOf('--service') !== -1;

  startMinimized = false; //Временно

  if (startMinimized == true) {
    //console.log('App is started by AutoLaunch');
    globalObj.launch = true;
    if (mainWindow) mainWindow.hide();
  } else {
    globalObj.launch = false;
    //console.log('App is started by User');
  }

  preloader.webContents.on("did-finish-load", () => {
    preloader.show();
    preloader.focus();
  });

  preloader.on("close", function () {
    preloader = null;
  });

  // @TODO: Use 'ready-to-show' event
  //        https://github.com/electron/electron/blob/master/docs/api/browser-window.md#using-ready-to-show-event
  mainWindow.webContents.on('did-finish-load', () => {
    if (!mainWindow) {
      throw new Error('"mainWindow" is not defined');
    }

    preloader.close();
    if (!startMinimized) {
      mainWindow.show();
      mainWindow.focus();
    }

    mainWindow.webContents.send("cmdArgs", options);
  });

  mainWindow.on('close', function (event) {
    if (!global.sharedObject.isQuiting) {
      event.preventDefault();
      mainWindow.hide();

      if (process.platform === "darwin") {
        app.dock.hide();
      }
    }

    return false;
  });

  const menuBuilder = new MenuBuilder(mainWindow);
  menuBuilder.buildMenu();
});

<|endoftext|>