<commit_before>// helper functions for defining Classes

// both getter and prop must register the name into __props__ array
var _appendProp = function (name/*, isGetter*/) {
    //if (!isGetter) {
    //    // checks whether getter/setter defined
    //    var d = Object.getOwnPropertyDescriptor(this, name);
    //    var hasGetterOrSetter = (d && (d.get || d.set));
    //    if (hasGetterOrSetter) {
    //        Fire.error(Fire.getClassName(this) + '.' + name + ' is already defined as a getter or setter!');
    //        return;
    //    }
    //}

    if (!this.__props__) {
        this.__props__ = [name];
    }
    else {
        var index = this.__props__.indexOf(name);
        if (index < 0) {
            this.__props__.push(name);
        }
        //else {
        //    Fire.error(Fire.getClassName(this) + '.' + name + ' is already defined!');
        //}
    }
};

/**
 * @param {object} obj
 * @returns {boolean} is {} ?
 */
var _isPlainEmptyObj = function (obj) {
    if (obj.constructor !== ({}).constructor) {
        return false;
    }
    var k;
    for (k in obj) {
        return false;
    }
    return true;
};

var _cloneable = function (obj) {
    return obj && typeof obj.clone === 'function' && (obj.constructor.prototype.hasOwnProperty('clone') || obj.hasOwnProperty('clone'));
};

/**
 * the metaclass of the "fire class" created by Fire.define, all its static members
 * will inherited by fire class.
 */
var _metaClass = {

    /**
     * @property {string[]} class.__props__
     * @private
     */
    __props__: null,

    /**
     * Add new instance field, propertie, or method made available on the class.
     * 该方法定义的变量默认情况下都会被序列化，也会在inspector中显示。
     * 如果传入属性包含Fire.HideInInspector则仍会序列化但不在inspector中显示。
     * 如果传入属性包含Fire.NonSerialized则不会序列化并且不会在inspector中显示。
     * 如果传入属性包含Fire.EditorOnly则只在编辑器下序列化，打包时不序列化。
     *
     * @method class.prop
     * @param {string} name - the property name
     * @param {*} defaultValue - the default value
     * @param {...object} attribute - additional property attributes, any number of attributes can be added
     * @returns {function} the class itself
     */
    prop: function (name, defaultValue, attribute) {
        'use strict';
        // check default object value
        if (typeof defaultValue === 'object' && defaultValue) {
            if (Array.isArray(defaultValue)) {
                // check array empty
                if (defaultValue.length > 0) {
                    Fire.error('Default array must be empty, set default value of ' + Fire.getClassName(this) + '.prop("' + name +
                        '", ...) to null or [], and initialize in constructor please. (just like "this.' +
                        name + ' = [...];")');
                    return this;
                }
            }
            else if (!_isPlainEmptyObj(defaultValue)) {
                // check cloneable
                if (!_cloneable(defaultValue)) {
                    Fire.error('Do not set default value to non-empty object, unless the object defines its own "clone" function. Set default value of ' + Fire.getClassName(this) + '.prop("' + name +
                        '", ...) to null or {}, and initialize in constructor please. (just like "this.' +
                        name + ' = {foo: bar};")');
                    return this;
                }
            }
        }

        // set default value
        Fire.attr(this, name, { 'default': defaultValue });

        // register property
        _appendProp.call(this, name);

        // apply default type (NOTE: if user provide type attribute, this one will be overwrote)
        var mytype = typeof defaultValue;
        if ( mytype === 'number' ) {
            mytype = 'float';
        }
        Fire.attr( this, name, { 'type': mytype } );

        // apply attributes
        if (attribute) {
            var onAfterProp = null;
            for (var i = 2; i < arguments.length; i++) {
                var attr = arguments[i];
                Fire.attr(this, name, attr);
                // register callback
                if (attr._onAfterProp) {
                    onAfterProp = onAfterProp || [];
                    onAfterProp.push(attr._onAfterProp);
                }
            }
            // call callback
            if (onAfterProp) {
                for (var c = 0; c < onAfterProp.length; c++) {
                    onAfterProp[c](this, name);
                }
            }
        }
        return this;
    },

    /**
     * 该方法定义的变量**不会**被序列化，默认会在inspector中显示。
     * 如果传入参数包含Fire.HideInInspector则不在inspector中显示。
     *
     * @method class.get
     * @param {string} name - the getter property
     * @param {function} getter - the getter function which returns the real property
     * @param {...object} attribute - additional property attributes, any number of attributes can be added
     * @returns {function} the class itself
     */
    get: function (name, getter, attribute) {
        'use strict';
        var d = Object.getOwnPropertyDescriptor(this, name);
        if (d && d.get) {
            Fire.error(Fire.getClassName(this) + ': the getter of "' + name + '" is already defined!');
            return this;
        }

        var displayInInspector = true;
        if (attribute) {
            for (var i = 2; i < arguments.length; i++) {
                var attr = arguments[i];
                Fire.attr(this, name, attr);
                // check attributes
                if (attr.hideInInspector) {
                    displayInInspector = false;
                }
                if (attr.serializable === false || attr.editorOnly === true) {
                    Fire.warn('No need to use Fire.NonSerialized or Fire.EditorOnly for the getter of ' +
                        Fire.getClassName(this) + '.' + name + ', every getter is actually non-serialized.');
                }
                if (attr.hasOwnProperty('default')) {
                    Fire.error(Fire.getClassName(this) + ': Can not set default value of a getter!');
                    return this;
                }
            }
        }
        Fire.attr(this, name, Fire.NonSerialized);

        if (displayInInspector) {
            _appendProp.call(this, name/*, true*/);
        }
        else {
            var index = this.__props__.indexOf(name);
            if (index >= 0) {
                Fire.error(Fire.getClassName(this) + '.' + name + ' is already defined!');
                return this;
            }
        }
        Object.defineProperty(this.prototype, name, {
            get: getter,
            configurable: true
        });
        return this;
    },

    /**
     * 该方法定义的变量**不会**被序列化，除非有对应的getter否则不在inspector中显示。
     *
     * @method class.set
     * @param {string} name - the setter property
     * @param {function} setter - the setter function
     * @returns {function} the class itself
     */
    set: function (name, setter) {
        var d = Object.getOwnPropertyDescriptor(this, name);
        if (d && d.set) {
            Fire.error(Fire.getClassName(this) + ': the setter of "' + name + '" is already defined!');
            return this;
        }
        Object.defineProperty(this.prototype, name, {
            set: setter,
            configurable: true
        });
        return this;
    },

    /**
     * 该方法定义的变量**不会**被序列化，默认会在inspector中显示。
     * 如果传入参数包含Fire.HideInInspector则不在inspector中显示。
     *
     * @method class.get
     * @param {string} name - the getter property
     * @param {function} getter - the getter function which returns the real property
     * @param {function} setter - the setter function
     * @param {...object} attribute - additional property attributes, any number of attributes can be added
     * @returns {function} the class itself
     */
    getset: function (name, getter, setter, attribute) {
        this.get(name, getter, attribute);
        this.set(name, setter);
        return this;
    },
};

var _createInstanceProps = function (instance, itsClass) {
    var propList = itsClass.__props__;
    if (propList) {
        for (var i = 0; i < propList.length; i++) {
            var prop = propList[i];
            var attrs = Fire.attr(itsClass, prop);
            if (attrs && attrs.hasOwnProperty('default')) {  // getter does not have default
                var def = attrs.default;
                if (typeof def === 'object' && def) {
                    // 防止多个实例引用相同对象
                    if (def.clone) {
                        def = def.clone();
                    }
                    else if (Array.isArray(def)) {
                        def = [];
                    }
                    else {
                        def = {};
                    }
                }
                instance[prop] = def;
            }
        }
    }
};

/**
 * Checks whether the constructor is created by Fire.define
 *
 * @method Fire._isFireClass
 * @param {function} constructor
 * @returns {boolean}
 * @private
 */
Fire._isFireClass = function (constructor) {
    return !!constructor && (constructor.prop === _metaClass.prop);
};

/**
 * Checks whether myclass is child of superclass
 *
 * @method Fire._childof
 * @param {function} myclass
 * @param {function} superclass
 * @returns {boolean}
 * @private
 */

Fire._childof = function (myclass, superclass) {
    var mysuper = myclass.$super;
    while ( mysuper ) {
        if ( mysuper === superclass )
            return true;
        mysuper = mysuper.$super;
    }
    return false;
};

/**
 * Checks whether myclass is child of superclass
 *
 * @method Fire.childof
 * @param {function} myclass
 * @param {function} superclass
 * @returns {boolean}
 */

Fire.childof = function (myclass, superclass) {
    return Fire._childof(myclass, superclass);
};

/**
 * Checks whether myclass is super of childclass
 *
 * @method Fire.childof
 * @param {function} myclass
 * @param {function} childclass
 * @returns {boolean}
 */

Fire.superof = function (myclass, childclass) {
    return Fire._childof(childclass, myclass);
};

/**
 * Creates a FireClass and returns its constructor function.
 * You can also creates a sub-class by supplying a baseClass parameter.
 *
 * @method Fire.define
 * @param {string} className - the name of class that is used to deserialize this class
 * @param {function} [baseOrConstructor] - The base class to inherit from.
 *                                         如果你的父类不是由Fire.define定义的，那么必须传入第三个参数(constructor)，否则会被当成创建新类而非继承类。
 *                                         如果你不需要构造函数，可以传入null。
 * @param {function} [constructor] - a constructor function that is used to instantiate this class,
 *                                   if not supplied, the constructor of base class will be called automatically
 * @param {[object[]]} instanceMembers - NYI
 * @param {[object[]]} staticMembers - NYI
 * @returns {function} the defined class
 *
 * @see Fire.extend
 */
Fire.define = function (className, baseOrConstructor, constructor) {
    'use strict';
    // check arguments
    var isInherit = false;
    switch (arguments.length) {
        case 2:
            isInherit = Fire._isFireClass(baseOrConstructor);
            break;
        case 3:
            isInherit = true;
            break;
    }
    var baseClass;
    if (isInherit) {
        baseClass = baseOrConstructor;
        if (!constructor) {
            constructor = function () {
                baseClass.apply(this, arguments);
            };
        }
    }
    else {
        constructor = baseOrConstructor;
    }

    // create a new constructor
    var fireClass;
    if (constructor) {
        fireClass = function () {
            _createInstanceProps(this, fireClass);
            constructor.apply(this, arguments);
        };
    }
    else {
        fireClass = function () {
            _createInstanceProps(this, fireClass);
        };
    }

    // occupy some non-inherited static members
    for (var staticMember in _metaClass) {
        Object.defineProperty(fireClass, staticMember, {
            value: _metaClass[staticMember],
            // __props__ is writable
            writable: staticMember === '__props__',
            // __props__ is enumerable so it can be inherited by Fire.extend
            enumerable: staticMember === '__props__',
        });
    }

    // inherit
    if (isInherit) {
        Fire.extend(fireClass, baseClass);
        fireClass.$super = baseClass;
        if (baseClass.__props__) {
            // copy __props__
            var len = baseClass.__props__.length;
            fireClass.__props__ = new Array(len);
            for (var i = 0; i < len; i++) {
                fireClass.__props__[i] = baseClass.__props__[i];
            }
        }
    }
    Fire.registerClass(className, fireClass);

    //// nicify constructor name
    //if (className && fireClass.toString) {
    //    var toString = fireClass.toString;
    //    fireClass.toString = function () {
    //        return _toNiceString.call(this, toString);
    //    };
    //}
    return fireClass;
};

/**
 * If you dont need a class (which defined by Fire.define) anymore,
 * you'd better undefine it to reduce memory usage.
 * Please note that its still your responsibility to free other references to the class.
 *
 * @method Fire.undefine
 * @param {...function} [constructor] - the class you will want to undefine, any number of classes can be added
 *
 * @see Fire.define
 */
Fire.undefine = function (constructor) {
    'use strict';
    for (var i = 0; i < arguments.length; i++) {
        Fire.unregisterClass(arguments[i]);
    }
};

//_toNiceString = function (originalToString) {
//    var str = originalToString.call(this);
//    return str.replace('function ', 'function ' + Fire.getClassName(this));
//};
<commit_msg>remove type convertion from prop<commit_after>// helper functions for defining Classes

// both getter and prop must register the name into __props__ array
var _appendProp = function (name/*, isGetter*/) {
    //if (!isGetter) {
    //    // checks whether getter/setter defined
    //    var d = Object.getOwnPropertyDescriptor(this, name);
    //    var hasGetterOrSetter = (d && (d.get || d.set));
    //    if (hasGetterOrSetter) {
    //        Fire.error(Fire.getClassName(this) + '.' + name + ' is already defined as a getter or setter!');
    //        return;
    //    }
    //}

    if (!this.__props__) {
        this.__props__ = [name];
    }
    else {
        var index = this.__props__.indexOf(name);
        if (index < 0) {
            this.__props__.push(name);
        }
        //else {
        //    Fire.error(Fire.getClassName(this) + '.' + name + ' is already defined!');
        //}
    }
};

/**
 * @param {object} obj
 * @returns {boolean} is {} ?
 */
var _isPlainEmptyObj = function (obj) {
    if (obj.constructor !== ({}).constructor) {
        return false;
    }
    var k;
    for (k in obj) {
        return false;
    }
    return true;
};

var _cloneable = function (obj) {
    return obj && typeof obj.clone === 'function' && (obj.constructor.prototype.hasOwnProperty('clone') || obj.hasOwnProperty('clone'));
};

/**
 * the metaclass of the "fire class" created by Fire.define, all its static members
 * will inherited by fire class.
 */
var _metaClass = {

    /**
     * @property {string[]} class.__props__
     * @private
     */
    __props__: null,

    /**
     * Add new instance field, propertie, or method made available on the class.
     * 该方法定义的变量默认情况下都会被序列化，也会在inspector中显示。
     * 如果传入属性包含Fire.HideInInspector则仍会序列化但不在inspector中显示。
     * 如果传入属性包含Fire.NonSerialized则不会序列化并且不会在inspector中显示。
     * 如果传入属性包含Fire.EditorOnly则只在编辑器下序列化，打包时不序列化。
     *
     * @method class.prop
     * @param {string} name - the property name
     * @param {*} defaultValue - the default value
     * @param {...object} attribute - additional property attributes, any number of attributes can be added
     * @returns {function} the class itself
     */
    prop: function (name, defaultValue, attribute) {
        'use strict';
        // check default object value
        if (typeof defaultValue === 'object' && defaultValue) {
            if (Array.isArray(defaultValue)) {
                // check array empty
                if (defaultValue.length > 0) {
                    Fire.error('Default array must be empty, set default value of ' + Fire.getClassName(this) + '.prop("' + name +
                        '", ...) to null or [], and initialize in constructor please. (just like "this.' +
                        name + ' = [...];")');
                    return this;
                }
            }
            else if (!_isPlainEmptyObj(defaultValue)) {
                // check cloneable
                if (!_cloneable(defaultValue)) {
                    Fire.error('Do not set default value to non-empty object, unless the object defines its own "clone" function. Set default value of ' + Fire.getClassName(this) + '.prop("' + name +
                        '", ...) to null or {}, and initialize in constructor please. (just like "this.' +
                        name + ' = {foo: bar};")');
                    return this;
                }
            }
        }

        // set default value
        Fire.attr(this, name, { 'default': defaultValue });

        // register property
        _appendProp.call(this, name);

        // 禁用，因为getter/setter需要动态获得类型，所以类型统一由上层处理
        //// apply default type (NOTE: if user provide type attribute, this one will be overwrote)
        //var mytype = typeof defaultValue;
        //if ( mytype === 'number' ) {
        //    mytype = 'float';
        //}
        //Fire.attr( this, name, { 'type': mytype } );

        // apply attributes
        if (attribute) {
            var onAfterProp = null;
            for (var i = 2; i < arguments.length; i++) {
                var attr = arguments[i];
                Fire.attr(this, name, attr);
                // register callback
                if (attr._onAfterProp) {
                    onAfterProp = onAfterProp || [];
                    onAfterProp.push(attr._onAfterProp);
                }
            }
            // call callback
            if (onAfterProp) {
                for (var c = 0; c < onAfterProp.length; c++) {
                    onAfterProp[c](this, name);
                }
            }
        }
        return this;
    },

    /**
     * 该方法定义的变量**不会**被序列化，默认会在inspector中显示。
     * 如果传入参数包含Fire.HideInInspector则不在inspector中显示。
     *
     * @method class.get
     * @param {string} name - the getter property
     * @param {function} getter - the getter function which returns the real property
     * @param {...object} attribute - additional property attributes, any number of attributes can be added
     * @returns {function} the class itself
     */
    get: function (name, getter, attribute) {
        'use strict';
        var d = Object.getOwnPropertyDescriptor(this, name);
        if (d && d.get) {
            Fire.error(Fire.getClassName(this) + ': the getter of "' + name + '" is already defined!');
            return this;
        }

        var displayInInspector = true;
        if (attribute) {
            for (var i = 2; i < arguments.length; i++) {
                var attr = arguments[i];
                Fire.attr(this, name, attr);
                // check attributes
                if (attr.hideInInspector) {
                    displayInInspector = false;
                }
                if (attr.serializable === false || attr.editorOnly === true) {
                    Fire.warn('No need to use Fire.NonSerialized or Fire.EditorOnly for the getter of ' +
                        Fire.getClassName(this) + '.' + name + ', every getter is actually non-serialized.');
                }
                if (attr.hasOwnProperty('default')) {
                    Fire.error(Fire.getClassName(this) + ': Can not set default value of a getter!');
                    return this;
                }
            }
        }
        Fire.attr(this, name, Fire.NonSerialized);

        if (displayInInspector) {
            _appendProp.call(this, name/*, true*/);
        }
        else {
            var index = this.__props__.indexOf(name);
            if (index >= 0) {
                Fire.error(Fire.getClassName(this) + '.' + name + ' is already defined!');
                return this;
            }
        }
        Object.defineProperty(this.prototype, name, {
            get: getter,
            configurable: true
        });
        return this;
    },

    /**
     * 该方法定义的变量**不会**被序列化，除非有对应的getter否则不在inspector中显示。
     *
     * @method class.set
     * @param {string} name - the setter property
     * @param {function} setter - the setter function
     * @returns {function} the class itself
     */
    set: function (name, setter) {
        var d = Object.getOwnPropertyDescriptor(this, name);
        if (d && d.set) {
            Fire.error(Fire.getClassName(this) + ': the setter of "' + name + '" is already defined!');
            return this;
        }
        Object.defineProperty(this.prototype, name, {
            set: setter,
            configurable: true
        });
        return this;
    },

    /**
     * 该方法定义的变量**不会**被序列化，默认会在inspector中显示。
     * 如果传入参数包含Fire.HideInInspector则不在inspector中显示。
     *
     * @method class.get
     * @param {string} name - the getter property
     * @param {function} getter - the getter function which returns the real property
     * @param {function} setter - the setter function
     * @param {...object} attribute - additional property attributes, any number of attributes can be added
     * @returns {function} the class itself
     */
    getset: function (name, getter, setter, attribute) {
        this.get(name, getter, attribute);
        this.set(name, setter);
        return this;
    },
};

var _createInstanceProps = function (instance, itsClass) {
    var propList = itsClass.__props__;
    if (propList) {
        for (var i = 0; i < propList.length; i++) {
            var prop = propList[i];
            var attrs = Fire.attr(itsClass, prop);
            if (attrs && attrs.hasOwnProperty('default')) {  // getter does not have default
                var def = attrs.default;
                if (typeof def === 'object' && def) {
                    // 防止多个实例引用相同对象
                    if (def.clone) {
                        def = def.clone();
                    }
                    else if (Array.isArray(def)) {
                        def = [];
                    }
                    else {
                        def = {};
                    }
                }
                instance[prop] = def;
            }
        }
    }
};

/**
 * Checks whether the constructor is created by Fire.define
 *
 * @method Fire._isFireClass
 * @param {function} constructor
 * @returns {boolean}
 * @private
 */
Fire._isFireClass = function (constructor) {
    return !!constructor && (constructor.prop === _metaClass.prop);
};

/**
 * Checks whether myclass is child of superclass
 *
 * @method Fire._childof
 * @param {function} myclass
 * @param {function} superclass
 * @returns {boolean}
 * @private
 */

Fire._childof = function (myclass, superclass) {
    var mysuper = myclass.$super;
    while ( mysuper ) {
        if ( mysuper === superclass )
            return true;
        mysuper = mysuper.$super;
    }
    return false;
};

/**
 * Checks whether myclass is child of superclass
 *
 * @method Fire.childof
 * @param {function} myclass
 * @param {function} superclass
 * @returns {boolean}
 */

Fire.childof = function (myclass, superclass) {
    return Fire._childof(myclass, superclass);
};

/**
 * Checks whether myclass is super of childclass
 *
 * @method Fire.childof
 * @param {function} myclass
 * @param {function} childclass
 * @returns {boolean}
 */

Fire.superof = function (myclass, childclass) {
    return Fire._childof(childclass, myclass);
};

/**
 * Creates a FireClass and returns its constructor function.
 * You can also creates a sub-class by supplying a baseClass parameter.
 *
 * @method Fire.define
 * @param {string} className - the name of class that is used to deserialize this class
 * @param {function} [baseOrConstructor] - The base class to inherit from.
 *                                         如果你的父类不是由Fire.define定义的，那么必须传入第三个参数(constructor)，否则会被当成创建新类而非继承类。
 *                                         如果你不需要构造函数，可以传入null。
 * @param {function} [constructor] - a constructor function that is used to instantiate this class,
 *                                   if not supplied, the constructor of base class will be called automatically
 * @param {[object[]]} instanceMembers - NYI
 * @param {[object[]]} staticMembers - NYI
 * @returns {function} the defined class
 *
 * @see Fire.extend
 */
Fire.define = function (className, baseOrConstructor, constructor) {
    'use strict';
    // check arguments
    var isInherit = false;
    switch (arguments.length) {
        case 2:
            isInherit = Fire._isFireClass(baseOrConstructor);
            break;
        case 3:
            isInherit = true;
            break;
    }
    var baseClass;
    if (isInherit) {
        baseClass = baseOrConstructor;
        if (!constructor) {
            constructor = function () {
                baseClass.apply(this, arguments);
            };
        }
    }
    else {
        constructor = baseOrConstructor;
    }

    // create a new constructor
    var fireClass;
    if (constructor) {
        fireClass = function () {
            _createInstanceProps(this, fireClass);
            constructor.apply(this, arguments);
        };
    }
    else {
        fireClass = function () {
            _createInstanceProps(this, fireClass);
        };
    }

    // occupy some non-inherited static members
    for (var staticMember in _metaClass) {
        Object.defineProperty(fireClass, staticMember, {
            value: _metaClass[staticMember],
            // __props__ is writable
            writable: staticMember === '__props__',
            // __props__ is enumerable so it can be inherited by Fire.extend
            enumerable: staticMember === '__props__',
        });
    }

    // inherit
    if (isInherit) {
        Fire.extend(fireClass, baseClass);
        fireClass.$super = baseClass;
        if (baseClass.__props__) {
            // copy __props__
            var len = baseClass.__props__.length;
            fireClass.__props__ = new Array(len);
            for (var i = 0; i < len; i++) {
                fireClass.__props__[i] = baseClass.__props__[i];
            }
        }
    }
    Fire.registerClass(className, fireClass);

    //// nicify constructor name
    //if (className && fireClass.toString) {
    //    var toString = fireClass.toString;
    //    fireClass.toString = function () {
    //        return _toNiceString.call(this, toString);
    //    };
    //}
    return fireClass;
};

/**
 * If you dont need a class (which defined by Fire.define) anymore,
 * you'd better undefine it to reduce memory usage.
 * Please note that its still your responsibility to free other references to the class.
 *
 * @method Fire.undefine
 * @param {...function} [constructor] - the class you will want to undefine, any number of classes can be added
 *
 * @see Fire.define
 */
Fire.undefine = function (constructor) {
    'use strict';
    for (var i = 0; i < arguments.length; i++) {
        Fire.unregisterClass(arguments[i]);
    }
};

//_toNiceString = function (originalToString) {
//    var str = originalToString.call(this);
//    return str.replace('function ', 'function ' + Fire.getClassName(this));
//};
<|endoftext|>


<commit_before>/*
 * music.js - Music streaming commands
 *
 * Contributed by Ovyerus
 */

const MusicHandler = require(`${__dirname}/musicHandler`);
const got = require('got');
var handler;

exports.loadAsSubcommands = true;
exports.commands = [
    'play',
    'queue',
    'leave',
    'join',
    'nowplaying',
    'sources',
    'skip'
];

exports.init = bot => {
    if (!bot.config.soundCloudKey) {
        got('https://raw.githubusercontent.com/rg3/youtube-dl/master/youtube_dl/extractor/soundcloud.py').then(r => {
            bot.config.soundCloudKey = r.body.match(/_CLIENT_ID = '([A-Z0-9]+)'/i)[1];
            logger.info('SoundCloud key has been automatically scraped from Youtube-DL. If you do not wish for this to happen, please insert a key into the config.json manually.');
            handler = new MusicHandler(bot);
        }).catch(logger.error);
    } else {
        handler = new MusicHandler(bot);
    }
};

exports.main = {
    desc: 'Music commands'
};

exports.play = {
    desc: 'Play or queue a song.',
    usage: '[url|search terms]',
    main(bot, ctx) {
        return new Promise((resolve, reject) => {
            if (!ctx.suffix && !bot.music.queues.get(ctx.guild.id)) {
                ctx.createMessage('Please tell me something to play.').then(resolve).catch(reject);
            } else if (!ctx.member.voiceState.channelID) {
                ctx.createMessage('You are not in a voice channel.').then(resolve).catch(reject);
            } else {
                if (ctx.suffix) {
                    if (!urlRegex.test(ctx.suffix) && bot.config.ytSearchKey) {
                        handler.search(ctx, ctx.suffix).then(res => {
                            return handler.prePlay(ctx, res);
                        }).then(resolve).catch(err => {
                            if (err.message && err.message === 'Invalid selection (Number too high or not a number).') {
                                return ctx.createMessage(err.message);
                            } else {
                                reject(err);
                            }
                        }).then(resolve);
                    } else if (!urlRegex.test(ctx.suffix) && !bot.config.ytSearchKey) {
                        ctx.createMessage('Search token appears to be missing. Please queue songs via direct links.').then(resolve).catch(reject);
                    } else {
                        handler.prePlay(ctx, ctx.suffix).then(resolve).catch(reject);
                    }
                } else if (bot.music.queues.get(ctx.guild.id).queue.length > 0) {
                    let item = bot.music.queues.get(ctx.guild.id).queue[0];
                    handler.prePlay(item.ctx, item.url).then(resolve).catch(reject);
                } else {
                    ctx.createMessage('Please tell me something to play.').then(resolve).catch(reject);
                }
            }
        });
    }
};

exports.queue = {
    desc: 'View the current queue.',
    usage: '[page number]',
    main(bot, ctx) {
        return new Promise((resolve, reject) => {
            let embed = {
                title: 'Music Queue'
            };

            if (!bot.music.queues.get(ctx.guild.id) || bot.music.queues.get(ctx.guild.id).queue.length === 0) {
                embed.description = 'Queue is more empty than my will to live. I mean... :eyes:';
                ctx.createMessage({embed}).then(resolve).catch(reject);
            } else {
                let q = bot.music.queues.get(ctx.guild.id).queue;

                let page = !Number(ctx.suffix) || Number(ctx.suffix) === 0 ? 0 : Number(ctx.suffix) - 1;
                let pageAmt = 10;
                let pages = Math.ceil(q.length / pageAmt);
                let thisPage = [];

                if (page <= pages) {
                    queuePaginate(q, page, pageAmt, thisPage);
                } else {
                    queuePaginate(q, 0, pageAmt, thisPage);
                }

                if (pages > 1) {
                    embed.footer = {text: `Page ${page + 1}/${pages}`};
                    embed.fields = [{
                        name: `${q.length} items in queue`, value: thisPage.join('\n')
                    }];
                } else {
                    embed.description = thisPage.join('\n');
                }

                ctx.createMessage({embed}).then(resolve).catch(reject);
            }
        });
    }
};

exports.nowplaying = {
    desc: 'Show what song is now playing.',
    main(bot, ctx) {
        return new Promise((resolve, reject) => {
            if ((bot.music.connections.get(ctx.guild.id) && !bot.music.connections.get(ctx.guild.id).playing) || !bot.music.queues.get(ctx.guild.id) || bot.music.queues.get(ctx.guild.id).queue.length === 0) {
                ctx.createMessage('I am currently not playing anything.').then(resolve).catch(reject);
            } else {
                let {info: item, ctx: c} = bot.music.queues.get(ctx.guild.id).queue[0];
                let embed = {
                    author: {name: 'Now Playing'},
                    title: item.title,
                    description: `Duration: ${typeof item.length === 'string'? item.length : timeFormat(item.length)}, [**Link**](${item.url})`,
                    color: utils.randomColour(),
                    image: {url: item.thumbnail},
                    footer: {
                        text: `Queued by ${utils.formatUsername(c.member)} | ${item.type}`
                    }
                };

                ctx.createMessage({embed}).then(resolve).catch(reject);
            }
        });
    }
};

exports.join = {
    desc: 'Joins a voice channel.',
    main(bot, ctx) {
        return new Promise((resolve, reject) => {
            if (bot.music.connections.get(ctx.guild.id)) {
                ctx.createMessage('I am already in a voice channel.').then(resolve).catch(reject);
            } else if (!ctx.member.voiceState.channelID) {
                ctx.createMessage('You are not in a voice channel.').then(resolve).catch(reject);
            } else {
                bot.joinVoiceChannel(ctx.member.voiceState.channelID).then(cnc => {
                    bot.music.inactives.push([bot.guilds.get(cnc.id).channels.get(cnc.channelID), Date.now()]);
                    return ctx.createMessage(`Joined your voice channel. Run \`${bot.config.mainPrefix}music play <song>\` to play something.`);
                }).then(resolve).catch(reject);
            }
        });
    }
};

exports.leave = {
    desc: 'Leave the voice channel.',
    main(bot, ctx) {
        return new Promise((resolve, reject) => {
            if (!bot.music.connections.get(ctx.guild.id)) {
                ctx.createMessage('I am not in a voice channel.').then(resolve).catch(reject);
            } else if (!ctx.member.voiceState.channelID) {
                ctx.createMessage('You are not in a voice channel.').then(resolve).catch(reject);
            } else if (ctx.member.voiceState.channelID !== bot.music.connections.get(ctx.guild.id).channelID) {
                ctx.createMessage('You are not in my voice channel.').then(resolve).catch(reject);
            } else {
                let cnc = bot.music.connections.get(ctx.guild.id);
                cnc.stopPlaying();
                bot.leaveVoiceChannel(cnc.channelID);
                ctx.createMessage('Left the voice channel and destroyed music data.').then(resolve).catch(reject);
            }
        });
    }
};

exports.sources = {
    desc: 'Show all available sources for music.',
    main(bot, ctx) {
        return ctx.createMessage({embed: {
            title: 'Music Sources',
            description: '**YouTube**: `https://youtube.com/watch?v=ID | https://youtu.be/ID`\n'
            + '**SoundCloud**: `https://soundcloud.com/USER/SONG`\n'
            + '**Clyp**: `https://clyp.it/ID`\n'
            + '**Twitch**: `https://twitch.tv/USER`\n'
            + '**YouTube Playlist**: `https://youtube.com/playlist?list=ID`\n'
            + '**SoundCloud Playlist**: `https://soundcloud.com/USER/sets/NAME`\n'
        }});
    }
};

exports.skip = {
    desc: 'Skip the current playing song.',
    usage: '[force]',
    main(bot, ctx) {
        return new Promise((resolve, reject) => {
            if (!bot.music.connections.get(ctx.guild.id)) {
                ctx.createMessage('I am not in a voice channel.').then(resolve).catch(reject);
            } else if (!ctx.member.voiceState.channelID) {
                ctx.createMessage('You are not in a voice channel.').then(resolve).catch(reject);
            } else if (ctx.member.voiceState.channelID !== bot.music.connections.get(ctx.guild.id).channelID) {
                ctx.createMessage('You are not in my voice channel.').then(resolve).catch(reject);
            } else if (!bot.music.connections.get(ctx.guild.id).playing) {
                ctx.createMessage('I am not playing anything.').then(resolve).catch(reject);
            } else if (ctx.args[0] !== 'force') {
                if (!bot.music.skips.get(ctx.guild.id)) bot.music.skips.add({id: ctx.guild.id, users: []});
                let skips = bot.music.skips.get(ctx.guild.id);

                if (!skips.users.includes(ctx.author.id)) {
                    skips.users.push(ctx.author.id);
                    let chan = ctx.guild.channels.get(bot.music.connections.get(ctx.guild.id).channelID);

                    if (skips.users.length >= Math.floor(chan.voiceMembers.filter(m => !m.bot && !m.voiceState.selfDeaf && !m.voiceState.deaf && m.id !== bot.id).length)) {
                        skips.users = [];
                        let track = bot.music.queues.get(ctx.guild.id).queue[0].info;

                        bot.music.connections.get(ctx.guild.id).stopPlaying();
                        ctx.createMessage(`Skipped **${track.title}**.`).then(resolve).catch(reject);
                    } else {
                        let track = bot.music.queues.get(ctx.guild.id).queue[0].info;
                        let chan = ctx.guild.channels.get(bot.music.connections.get(ctx.guild.id).channelID);

                        ctx.createMessage(`**${bot.formatUsername(ctx.member)}** voted to skip **${track.title}**.\n`
                        + `${skips.users.length}/${chan.voiceMembers.filter(m => !m.bot && !m.voiceState.selfDeaf && !m.voiceState.deaf && m.id !== bot.id).length} votes.`).then(resolve).catch(reject);
                    }
                }
            } else if (ctx.args[0] === 'force' && !(bot.checkBotPerms(ctx.author.id) || ctx.hasPermission('manageGuild', 'author'))) {
                ctx.createMessage('You do not have permissions to force skip.\n(Manage Server permission required)').then(resolve).catch(reject);
            } else {
                if (!bot.music.skips.get(ctx.guild.id)) bot.music.skips.add({id: ctx.guild.id, users: []});
                let skips = bot.music.skips.get(ctx.guild.id);
                let track = bot.music.queues.get(ctx.guild.id).queue[0].info;
                skips.users = [];

                bot.music.connections.get(ctx.guild.id).stopPlaying();
                ctx.createMessage(`Skipped **${track.title}**.`).then(resolve).catch(reject);
            }
        });
    }
};

function queuePaginate(q, page, pageAmt, collect) {
    for (let i = page * pageAmt; i < page * pageAmt + pageAmt; i++) {
        if (!q[i]) break;
        collect.push(`**${Number(i) + 1}.** \`${q[i].info.title}\` (${q[i].info.uploader}) **[${timeFormat(q[i].info.length)}]**`);
    }
}

function timeFormat(secs) {
    let all = [secs / 60 / 60, secs / 60 % 60, secs % 60];

    for (let i in all) {
        all[i] = Math.floor(all[i]);
        all[i] = all[i].toString().length === 1 ? `0${all[i]}` : all[i];
    }

    return all[0] === '00' ? all.slice(1).join(':') : all.join(':');
}

const urlRegex = /^(?:(?:https?:)?\/\/)?(?:\S+(?::\S*)?@)?(?:(?!(?:10|127)(?:\.\d{1,3}){3})(?!(?:169\.254|192\.168)(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)(?:\.(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)*(?:\.(?:[a-z\u00a1-\uffff]{2,})))(?::\d{2,5})?(?:[/?#]\S*)?$/i;<commit_msg>fix buggerino?<commit_after>/*
 * music.js - Music streaming commands
 *
 * Contributed by Ovyerus
 */

const MusicHandler = require(`${__dirname}/musicHandler`);
const got = require('got');
var handler;

exports.loadAsSubcommands = true;
exports.commands = [
    'play',
    'queue',
    'leave',
    'join',
    'nowplaying',
    'sources',
    'skip'
];

exports.init = bot => {
    if (!bot.config.soundCloudKey) {
        got('https://raw.githubusercontent.com/rg3/youtube-dl/master/youtube_dl/extractor/soundcloud.py').then(r => {
            bot.config.soundCloudKey = r.body.match(/_CLIENT_ID = '([A-Z0-9]+)'/i)[1];
            logger.info('SoundCloud key has been automatically scraped from Youtube-DL. If you do not wish for this to happen, please insert a key into the config.json manually.');
            handler = new MusicHandler(bot);
        }).catch(logger.error);
    } else {
        handler = new MusicHandler(bot);
    }
};

exports.main = {
    desc: 'Music commands'
};

exports.play = {
    desc: 'Play or queue a song.',
    usage: '[url|search terms]',
    main(bot, ctx) {
        return new Promise((resolve, reject) => {
            if (!ctx.suffix && !bot.music.queues.get(ctx.guild.id)) {
                ctx.createMessage('Please tell me something to play.').then(resolve).catch(reject);
            } else if (!ctx.member.voiceState.channelID) {
                ctx.createMessage('You are not in a voice channel.').then(resolve).catch(reject);
            } else {
                if (ctx.suffix) {
                    if (!urlRegex.test(ctx.suffix) && bot.config.ytSearchKey) {
                        handler.search(ctx, ctx.suffix).then(res => {
                            return handler.prePlay(ctx, res);
                        }).then(resolve).catch(err => {
                            if (err.message && err.message === 'Invalid selection (Number too high or not a number).') {
                                return ctx.createMessage(err.message);
                            } else {
                                reject(err);
                            }
                        }).then(resolve);
                    } else if (!urlRegex.test(ctx.suffix) && !bot.config.ytSearchKey) {
                        ctx.createMessage('Search token appears to be missing. Please queue songs via direct links.').then(resolve).catch(reject);
                    } else {
                        handler.prePlay(ctx, ctx.suffix).then(resolve).catch(reject);
                    }
                } else if (bot.music.queues.get(ctx.guild.id).queue.length > 0) {
                    let item = bot.music.queues.get(ctx.guild.id).queue[0];
                    handler.prePlay(item.ctx, item.url).then(resolve).catch(reject);
                } else {
                    ctx.createMessage('Please tell me something to play.').then(resolve).catch(reject);
                }
            }
        });
    }
};

exports.queue = {
    desc: 'View the current queue.',
    usage: '[page number]',
    main(bot, ctx) {
        return new Promise((resolve, reject) => {
            let embed = {
                title: 'Music Queue'
            };

            if (!bot.music.queues.get(ctx.guild.id) || bot.music.queues.get(ctx.guild.id).queue.length === 0) {
                embed.description = 'Queue is more empty than my will to live. I mean... :eyes:';
                ctx.createMessage({embed}).then(resolve).catch(reject);
            } else {
                let q = bot.music.queues.get(ctx.guild.id).queue;

                let page = !Number(ctx.suffix) || Number(ctx.suffix) === 0 ? 0 : Number(ctx.suffix) - 1;
                let pageAmt = 10;
                let pages = Math.ceil(q.length / pageAmt);
                let thisPage = [];

                if (page <= pages) {
                    queuePaginate(q, page, pageAmt, thisPage);
                } else {
                    queuePaginate(q, 0, pageAmt, thisPage);
                }

                if (pages > 1) {
                    embed.footer = {text: `Page ${page + 1}/${pages}`};
                    embed.fields = [{
                        name: `${q.length} items in queue`, value: thisPage.join('\n')
                    }];
                } else {
                    embed.description = thisPage.join('\n');
                }

                ctx.createMessage({embed}).then(resolve).catch(reject);
            }
        });
    }
};

exports.nowplaying = {
    desc: 'Show what song is now playing.',
    main(bot, ctx) {
        return new Promise((resolve, reject) => {
            if ((bot.music.connections.get(ctx.guild.id) && !bot.music.connections.get(ctx.guild.id).playing) || !bot.music.queues.get(ctx.guild.id) || bot.music.queues.get(ctx.guild.id).queue.length === 0) {
                ctx.createMessage('I am currently not playing anything.').then(resolve).catch(reject);
            } else {
                let {info: item, ctx: c} = bot.music.queues.get(ctx.guild.id).queue[0];
                let embed = {
                    author: {name: 'Now Playing'},
                    title: item.title,
                    description: `Duration: ${typeof item.length === 'string'? item.length : timeFormat(item.length)}, [**Link**](${item.url})`,
                    color: utils.randomColour(),
                    image: {url: item.thumbnail},
                    footer: {
                        text: `Queued by ${utils.formatUsername(c.member)} | ${item.type}`
                    }
                };

                ctx.createMessage({embed}).then(resolve).catch(reject);
            }
        });
    }
};

exports.join = {
    desc: 'Joins a voice channel.',
    main(bot, ctx) {
        return new Promise((resolve, reject) => {
            if (bot.music.connections.get(ctx.guild.id)) {
                ctx.createMessage('I am already in a voice channel.').then(resolve).catch(reject);
            } else if (!ctx.member.voiceState.channelID) {
                ctx.createMessage('You are not in a voice channel.').then(resolve).catch(reject);
            } else {
                bot.joinVoiceChannel(ctx.member.voiceState.channelID).then(cnc => {
                    bot.music.inactives.push([bot.guilds.get(cnc.id).channels.get(cnc.channelID), Date.now()]);
                    return ctx.createMessage(`Joined your voice channel. Run \`${bot.config.mainPrefix}music play <song>\` to play something.`);
                }).then(resolve).catch(reject);
            }
        });
    }
};

exports.leave = {
    desc: 'Leave the voice channel.',
    main(bot, ctx) {
        return new Promise((resolve, reject) => {
            if (!bot.music.connections.get(ctx.guild.id)) {
                ctx.createMessage('I am not in a voice channel.').then(resolve).catch(reject);
            } else if (!ctx.member.voiceState.channelID) {
                ctx.createMessage('You are not in a voice channel.').then(resolve).catch(reject);
            } else if (ctx.member.voiceState.channelID !== bot.music.connections.get(ctx.guild.id).channelID) {
                ctx.createMessage('You are not in my voice channel.').then(resolve).catch(reject);
            } else {
                let cnc = bot.music.connections.get(ctx.guild.id);
                cnc.stopPlaying();
                bot.leaveVoiceChannel(cnc.channelID);
                ctx.createMessage('Left the voice channel and destroyed music data.').then(resolve).catch(reject);
            }
        });
    }
};

exports.sources = {
    desc: 'Show all available sources for music.',
    main(bot, ctx) {
        return ctx.createMessage({embed: {
            title: 'Music Sources',
            description: '**YouTube**: `https://youtube.com/watch?v=ID | https://youtu.be/ID`\n'
            + '**SoundCloud**: `https://soundcloud.com/USER/SONG`\n'
            + '**Clyp**: `https://clyp.it/ID`\n'
            + '**Twitch**: `https://twitch.tv/USER`\n'
            + '**YouTube Playlist**: `https://youtube.com/playlist?list=ID`\n'
            + '**SoundCloud Playlist**: `https://soundcloud.com/USER/sets/NAME`\n'
        }});
    }
};

exports.skip = {
    desc: 'Skip the current playing song.',
    usage: '[force]',
    main(bot, ctx) {
        return new Promise((resolve, reject) => {
            if (!bot.music.connections.get(ctx.guild.id)) {
                ctx.createMessage('I am not in a voice channel.').then(resolve).catch(reject);
            } else if (!ctx.member.voiceState.channelID) {
                ctx.createMessage('You are not in a voice channel.').then(resolve).catch(reject);
            } else if (ctx.member.voiceState.channelID !== bot.music.connections.get(ctx.guild.id).channelID) {
                ctx.createMessage('You are not in my voice channel.').then(resolve).catch(reject);
            } else if (!bot.music.connections.get(ctx.guild.id).playing) {
                ctx.createMessage('I am not playing anything.').then(resolve).catch(reject);
            } else if (ctx.args[0] !== 'force') {
                if (!bot.music.skips.get(ctx.guild.id)) bot.music.skips.add({id: ctx.guild.id, users: []});
                let skips = bot.music.skips.get(ctx.guild.id);

                if (!skips.users.includes(ctx.author.id)) {
                    skips.users.push(ctx.author.id);
                    let chan = ctx.guild.channels.get(bot.music.connections.get(ctx.guild.id).channelID);

                    if (skips.users.length >= Math.floor(chan.voiceMembers.filter(m => !m.bot && !m.voiceState.selfDeaf && !m.voiceState.deaf && m.id !== bot.id).length)) {
                        skips.users = [];
                        let track = bot.music.queues.get(ctx.guild.id).queue[0].info;

                        bot.music.connections.get(ctx.guild.id).stopPlaying();
                        ctx.createMessage(`Skipped **${track.title}**.`).then(resolve).catch(reject);
                    } else {
                        let track = bot.music.queues.get(ctx.guild.id).queue[0].info;
                        let chan = ctx.guild.channels.get(bot.music.connections.get(ctx.guild.id).channelID);

                        ctx.createMessage(`**${bot.utils.formatUsername(ctx.member)}** voted to skip **${track.title}**.\n`
                        + `${skips.users.length}/${chan.voiceMembers.filter(m => !m.bot && !m.voiceState.selfDeaf && !m.voiceState.deaf && m.id !== bot.id).length} votes.`).then(resolve).catch(reject);
                    }
                }
            } else if (ctx.args[0] === 'force' && !(bot.checkBotPerms(ctx.author.id) || ctx.hasPermission('manageGuild', 'author'))) {
                ctx.createMessage('You do not have permissions to force skip.\n(Manage Server permission required)').then(resolve).catch(reject);
            } else {
                if (!bot.music.skips.get(ctx.guild.id)) bot.music.skips.add({id: ctx.guild.id, users: []});
                let skips = bot.music.skips.get(ctx.guild.id);
                let track = bot.music.queues.get(ctx.guild.id).queue[0].info;
                skips.users = [];

                bot.music.connections.get(ctx.guild.id).stopPlaying();
                ctx.createMessage(`Skipped **${track.title}**.`).then(resolve).catch(reject);
            }
        });
    }
};

function queuePaginate(q, page, pageAmt, collect) {
    for (let i = page * pageAmt; i < page * pageAmt + pageAmt; i++) {
        if (!q[i]) break;
        collect.push(`**${Number(i) + 1}.** \`${q[i].info.title}\` (${q[i].info.uploader}) **[${timeFormat(q[i].info.length)}]**`);
    }
}

function timeFormat(secs) {
    let all = [secs / 60 / 60, secs / 60 % 60, secs % 60];

    for (let i in all) {
        all[i] = Math.floor(all[i]);
        all[i] = all[i].toString().length === 1 ? `0${all[i]}` : all[i];
    }

    return all[0] === '00' ? all.slice(1).join(':') : all.join(':');
}

const urlRegex = /^(?:(?:https?:)?\/\/)?(?:\S+(?::\S*)?@)?(?:(?!(?:10|127)(?:\.\d{1,3}){3})(?!(?:169\.254|192\.168)(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)(?:\.(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)*(?:\.(?:[a-z\u00a1-\uffff]{2,})))(?::\d{2,5})?(?:[/?#]\S*)?$/i;<|endoftext|>


<commit_before>var complement = require("./complement");
var flushGetRequest = require("./flushGetRequest");
var REQUEST_ID = 0;
var GetRequestType = require("./RequestTypes").GetRequest;
var setJSONGraphs = require("./../set/setJSONGraphs");
var setPathValues = require("./../set/setPathValues");
var $error = require("./../types/error");
var emptyArray = [];
var InvalidSourceError = require("./../errors/InvalidSourceError");

/**
 * Creates a new GetRequest.  This GetRequest takes a scheduler and
 * the request queue.  Once the scheduler fires, all batched requests
 * will be sent to the server.  Upon request completion, the data is
 * merged back into the cache and all callbacks are notified.
 *
 * @param {Scheduler} scheduler -
 * @param {RequestQueueV2} requestQueue -
 */
var GetRequestV2 = function(scheduler, requestQueue) {
    this.sent = false;
    this.scheduled = false;
    this.requestQueue = requestQueue;
    this.id = ++REQUEST_ID;
    this.type = GetRequestType;

    this._scheduler = scheduler;
    this._pathMap = {};
    this._optimizedPaths = [];
    this._requestedPaths = [];
    this._callbacks = [];
    this._count = 0;
    this._disposable = null;
    this._collapsed = null;
    this._disposed = false;
};

GetRequestV2.prototype = {
    /**
     * batches the paths that are passed in.  Once the request is complete,
     * all callbacks will be called and the request will be removed from
     * parent queue.
     * @param {Array} requestedPaths -
     * @param {Array} optimizedPaths -
     * @param {Function} callback -
     */
    batch: function(requestedPaths, optimizedPaths, callback) {
        var self = this;
        var oPaths = self._optimizedPaths;
        var rPaths = self._requestedPaths;
        var callbacks = self._callbacks;
        var idx = oPaths.length;

        // If its not sent, simply add it to the requested paths
        // and callbacks.
        oPaths[idx] = optimizedPaths;
        rPaths[idx] = requestedPaths;
        callbacks[idx] = callback;
        ++self._count;

        // If it has not been scheduled, then schedule the action
        if (!self.scheduled) {
            self.scheduled = true;

            var flushedDisposable;
            var scheduleDisposable = self._scheduler.schedule(function() {
                flushedDisposable =
                    flushGetRequest(self, oPaths, function(err, data) {
                        var i, fn, len;
                        var model = self.requestQueue.model;
                        self.requestQueue.removeRequest(self);
                        self._disposed = true;

                        if (model._treatDataSourceErrorsAsJSONGraphErrors ? err instanceof InvalidSourceError : !!err) {
                            for (i = 0, len = callbacks.length; i < len; ++i) {
                                fn = callbacks[i];
                                if (fn) {
                                    fn(err);
                                }
                            }
                            return;
                        }

                        // If there is at least one callback remaining, then
                        // callback the callbacks.
                        if (self._count) {
                            var mergeContext = {hasInvalidatedResult : false};
                            self._merge(rPaths, err, data, mergeContext);
                            // Call the callbacks.  The first one inserts all
                            // the data so that the rest do not have consider
                            // if their data is present or not.
                            for (i = 0, len = callbacks.length; i < len; ++i) {
                                fn = callbacks[i];
                                if (fn) {
                                    fn(err, data, mergeContext.hasInvalidatedResult);
                                }
                            }
                        }
                    });
            });

            // There is a race condition here. If the scheduler is sync then it
            // exposes a condition where the flush request cannot be disposed.
            // To correct this issue, if there is no flushedDisposable, then the
            // scheduler is async and should use scheduler disposable, else use
            // the flushedDisposable.
            self._disposable = flushedDisposable || scheduleDisposable;
        }

        // Disposes this batched request.  This does not mean that the
        // entire request has been disposed, but just the local one, if all
        // requests are disposed, then the outer disposable will be removed.
        return createDisposable(self, idx);
    },

    /**
     * Attempts to add paths to the outgoing request.  If there are added
     * paths then the request callback will be added to the callback list.
     *
     * @returns {Array} - the remaining paths in the request.
     */
    add: function(requested, optimized, depthDifferences, callback) {
        // uses the length tree complement calculator.
        var self = this;
        var complementTuple = complement(requested, optimized, depthDifferences, self._pathMap);
        var optimizedComplement;
        var requestedComplement;

        if (complementTuple) {
            requestedComplement = complementTuple[2];
            optimizedComplement = complementTuple[1];
        } else {
            requestedComplement = requested;
            optimizedComplement = optimized;
        }

        var inserted = false;
        var disposable = false;

        // If the out paths is less than the passed in paths, then there
        // has been an intersection and the complement has been returned.
        // Therefore, this can be deduped across requests.
        if (complementTuple[0].length) {
            inserted = true;
            var idx = self._callbacks.length;
            self._callbacks[idx] = callback;
            self._requestedPaths[idx] = complementTuple[0];
            self._optimizedPaths[idx] = [];
            ++self._count;

            disposable = createDisposable(self, idx);
        }

        return [inserted, requestedComplement, optimizedComplement, disposable];
    },

    /**
     * merges the response into the model"s cache.
     */
    _merge: function(requested, err, data, mergeContext) {
        var self = this;
        var model = self.requestQueue.model;
        var modelRoot = model._root;
        var errorSelector = modelRoot.errorSelector;
        var comparator = modelRoot.comparator;
        var boundPath = model._path;

        model._path = emptyArray;

        // flatten all the requested paths, adds them to the
        var nextPaths = flattenRequestedPaths(requested);

        // Insert errors in every requested position.
        if (err && model._treatDataSourceErrorsAsJSONGraphErrors) {
            var error = err;

            // Converts errors to objects, a more friendly storage
            // of errors.
            if (error instanceof Error) {
                error = {
                    message: error.message
                };
            }

            // Not all errors are value $types.
            if (!error.$type) {
                error = {
                    $type: $error,
                    value: error
                };
            }

            var pathValues = nextPaths.map(function(x) {
                return {
                    path: x,
                    value: error
                };
            });
            setPathValues(model, pathValues, null, errorSelector, comparator, mergeContext);
        }

        // Insert the jsonGraph from the dataSource.
        else {
            setJSONGraphs(model, [{
                paths: nextPaths,
                jsonGraph: data.jsonGraph
            }], null, errorSelector, comparator, mergeContext);
        }

        // return the model"s boundPath
        model._path = boundPath;
    }
};

// Creates a more efficient closure of the things that are
// needed.  So the request and the idx.  Also prevents code
// duplication.
function createDisposable(request, idx) {
    var disposed = false;
    return function() {
        if (disposed || request._disposed) {
            return;
        }

        disposed = true;
        request._callbacks[idx] = null;
        request._optimizedPaths[idx] = [];
        request._requestedPaths[idx] = [];

        // If there are no more requests, then dispose all of the request.
        var count = --request._count;
        if (count === 0) {
            request._disposable.dispose();
            request.requestQueue.removeRequest(request);
        }
    };
}

function flattenRequestedPaths(requested) {
    var out = [];
    var outLen = -1;
    for (var i = 0, len = requested.length; i < len; ++i) {
        var paths = requested[i];
        for (var j = 0, innerLen = paths.length; j < innerLen; ++j) {
            out[++outLen] = paths[j];
        }
    }
    return out;
}

module.exports = GetRequestV2;
<commit_msg>safer check and better comment<commit_after>var complement = require("./complement");
var flushGetRequest = require("./flushGetRequest");
var REQUEST_ID = 0;
var GetRequestType = require("./RequestTypes").GetRequest;
var setJSONGraphs = require("./../set/setJSONGraphs");
var setPathValues = require("./../set/setPathValues");
var $error = require("./../types/error");
var emptyArray = [];
var InvalidSourceError = require("./../errors/InvalidSourceError");

/**
 * Creates a new GetRequest.  This GetRequest takes a scheduler and
 * the request queue.  Once the scheduler fires, all batched requests
 * will be sent to the server.  Upon request completion, the data is
 * merged back into the cache and all callbacks are notified.
 *
 * @param {Scheduler} scheduler -
 * @param {RequestQueueV2} requestQueue -
 */
var GetRequestV2 = function(scheduler, requestQueue) {
    this.sent = false;
    this.scheduled = false;
    this.requestQueue = requestQueue;
    this.id = ++REQUEST_ID;
    this.type = GetRequestType;

    this._scheduler = scheduler;
    this._pathMap = {};
    this._optimizedPaths = [];
    this._requestedPaths = [];
    this._callbacks = [];
    this._count = 0;
    this._disposable = null;
    this._collapsed = null;
    this._disposed = false;
};

GetRequestV2.prototype = {
    /**
     * batches the paths that are passed in.  Once the request is complete,
     * all callbacks will be called and the request will be removed from
     * parent queue.
     * @param {Array} requestedPaths -
     * @param {Array} optimizedPaths -
     * @param {Function} callback -
     */
    batch: function(requestedPaths, optimizedPaths, callback) {
        var self = this;
        var oPaths = self._optimizedPaths;
        var rPaths = self._requestedPaths;
        var callbacks = self._callbacks;
        var idx = oPaths.length;

        // If its not sent, simply add it to the requested paths
        // and callbacks.
        oPaths[idx] = optimizedPaths;
        rPaths[idx] = requestedPaths;
        callbacks[idx] = callback;
        ++self._count;

        // If it has not been scheduled, then schedule the action
        if (!self.scheduled) {
            self.scheduled = true;

            var flushedDisposable;
            var scheduleDisposable = self._scheduler.schedule(function() {
                flushedDisposable =
                    flushGetRequest(self, oPaths, function(err, data) {
                        var i, fn, len;
                        var model = self.requestQueue.model;
                        self.requestQueue.removeRequest(self);
                        self._disposed = true;

                        if (model._treatDataSourceErrorsAsJSONGraphErrors ? err instanceof InvalidSourceError : !!err) {
                            for (i = 0, len = callbacks.length; i < len; ++i) {
                                fn = callbacks[i];
                                if (fn) {
                                    fn(err);
                                }
                            }
                            return;
                        }

                        // If there is at least one callback remaining, then
                        // callback the callbacks.
                        if (self._count) {
                            var mergeContext = {hasInvalidatedResult : false};
                            self._merge(rPaths, err, data, mergeContext);
                            // Call the callbacks.  The first one inserts all
                            // the data so that the rest do not have consider
                            // if their data is present or not.
                            for (i = 0, len = callbacks.length; i < len; ++i) {
                                fn = callbacks[i];
                                if (fn) {
                                    fn(err, data, mergeContext.hasInvalidatedResult);
                                }
                            }
                        }
                    });
            });

            // There is a race condition here. If the scheduler is sync then it
            // exposes a condition where the flush request cannot be disposed.
            // To correct this issue, if there is no flushedDisposable, then the
            // scheduler is async and should use scheduler disposable, else use
            // the flushedDisposable.
            self._disposable = flushedDisposable || scheduleDisposable;
        }

        // Disposes this batched request.  This does not mean that the
        // entire request has been disposed, but just the local one, if all
        // requests are disposed, then the outer disposable will be removed.
        return createDisposable(self, idx);
    },

    /**
     * Attempts to add paths to the outgoing request.  If there are added
     * paths then the request callback will be added to the callback list.
     *
     * @returns {Array} - the remaining paths in the request.
     */
    add: function(requested, optimized, depthDifferences, callback) {
        // uses the length tree complement calculator.
        var self = this;
        var complementTuple = complement(requested, optimized, depthDifferences, self._pathMap);
        var optimizedComplement;
        var requestedComplement;

        if (complementTuple) {
            requestedComplement = complementTuple[2];
            optimizedComplement = complementTuple[1];
        } else {
            requestedComplement = requested;
            optimizedComplement = optimized;
        }

        var inserted = false;
        var disposable = false;

        // If we found an intersection, then just add new callback
        // as one of the dependents of that request
        if (complementTuple && complementTuple[0].length) {
            inserted = true;
            var idx = self._callbacks.length;
            self._callbacks[idx] = callback;
            self._requestedPaths[idx] = complementTuple[0];
            self._optimizedPaths[idx] = [];
            ++self._count;

            disposable = createDisposable(self, idx);
        }

        return [inserted, requestedComplement, optimizedComplement, disposable];
    },

    /**
     * merges the response into the model"s cache.
     */
    _merge: function(requested, err, data, mergeContext) {
        var self = this;
        var model = self.requestQueue.model;
        var modelRoot = model._root;
        var errorSelector = modelRoot.errorSelector;
        var comparator = modelRoot.comparator;
        var boundPath = model._path;

        model._path = emptyArray;

        // flatten all the requested paths, adds them to the
        var nextPaths = flattenRequestedPaths(requested);

        // Insert errors in every requested position.
        if (err && model._treatDataSourceErrorsAsJSONGraphErrors) {
            var error = err;

            // Converts errors to objects, a more friendly storage
            // of errors.
            if (error instanceof Error) {
                error = {
                    message: error.message
                };
            }

            // Not all errors are value $types.
            if (!error.$type) {
                error = {
                    $type: $error,
                    value: error
                };
            }

            var pathValues = nextPaths.map(function(x) {
                return {
                    path: x,
                    value: error
                };
            });
            setPathValues(model, pathValues, null, errorSelector, comparator, mergeContext);
        }

        // Insert the jsonGraph from the dataSource.
        else {
            setJSONGraphs(model, [{
                paths: nextPaths,
                jsonGraph: data.jsonGraph
            }], null, errorSelector, comparator, mergeContext);
        }

        // return the model"s boundPath
        model._path = boundPath;
    }
};

// Creates a more efficient closure of the things that are
// needed.  So the request and the idx.  Also prevents code
// duplication.
function createDisposable(request, idx) {
    var disposed = false;
    return function() {
        if (disposed || request._disposed) {
            return;
        }

        disposed = true;
        request._callbacks[idx] = null;
        request._optimizedPaths[idx] = [];
        request._requestedPaths[idx] = [];

        // If there are no more requests, then dispose all of the request.
        var count = --request._count;
        if (count === 0) {
            request._disposable.dispose();
            request.requestQueue.removeRequest(request);
        }
    };
}

function flattenRequestedPaths(requested) {
    var out = [];
    var outLen = -1;
    for (var i = 0, len = requested.length; i < len; ++i) {
        var paths = requested[i];
        for (var j = 0, innerLen = paths.length; j < innerLen; ++j) {
            out[++outLen] = paths[j];
        }
    }
    return out;
}

module.exports = GetRequestV2;
<|endoftext|>


<commit_before>import 'aurelia-polyfills';
import {PLATFORM, isInitialized} from 'aurelia-pal';

let bootstrapPromises = [];
let startResolve;

const startPromise = new Promise(resolve => startResolve = resolve);
const host = PLATFORM.global;
const isNodeLike = typeof process !== 'undefined' && !process.browser;

function ready() {
  if (!host.document || host.document.readyState === 'complete') {
    return Promise.resolve();
  }

  return new Promise(resolve => {
    host.document.addEventListener('DOMContentLoaded', completed);
    host.addEventListener('load', completed);

    function completed() {
      host.document.removeEventListener('DOMContentLoaded', completed);
      host.removeEventListener('load', completed);
      resolve();
    }
  });
}

function createLoader() {
  // Note: Please do NOT add any PLATFORM.moduleName annotation in this method,
  //       for example around 'aurelia-loader-default'.
  //       Each import has been carefully written so that it is picked up by
  //       its respective bundler and is ignored by others.
  //       Adding moduleName() would add a static dependency, which we don't
  //       want as the correct loader is determined at build time.

  // Custom Loader Support
  if (PLATFORM.Loader) {
    return Promise.resolve(new PLATFORM.Loader());
  }

  if (typeof AURELIA_WEBPACK_2_0 === 'undefined') {
    // Webpack Loader Support
    if (typeof __webpack_require__ !== 'undefined') {
      // Webpack needs the require to be top level to parse the request.
      // However, we don't want to use require or that will cause the Babel
      // transpiler to detect an incorrect dependency.
      const m = __webpack_require__(require.resolve('aurelia-loader-webpack'));
      return Promise.resolve(new m.WebpackLoader());
    }

    // SystemJS Loader Support
    if (host.System && typeof host.System.config === 'function') {
      return host.System.normalize('aurelia-bootstrapper').then(bsn => {
        return host.System.normalize('aurelia-loader-default', bsn);
      }).then(loaderName => {
        return host.System.import(loaderName).then(m => new m.DefaultLoader());
      });
    }

    // AMD Module Loader Support
    if (typeof host.require === 'function' && typeof host.define === 'function' && typeof host.define.amd === 'object') {
      return new Promise((resolve, reject) => host.require(['aurelia-loader-default'], m => resolve(new m.DefaultLoader()), reject));
    }

    // Node.js and Electron Support
    if (isNodeLike && typeof module !== 'undefined' && typeof module.require !== 'undefined') {
      // note: we use a scoped module.require() instead of simply require()
      // so that Webpack's parser does not automatically include this loader as a dependency,
      // similarly to the non-global call to System.import() above
      const m = module.require('aurelia-loader-nodejs');
      return Promise.resolve(new m.NodeJsLoader());
    }
  } // endif AURELIA_WEBPACK_2_0

  return Promise.reject('No PLATFORM.Loader is defined and there is neither a System API (ES6) or a Require API (AMD) globally available to load your app.');
}

function initializePal(loader) {
  let type;

  const isRenderer = isNodeLike && (process.type === 'renderer' || process.versions['node-webkit']);

  if (isNodeLike && !isRenderer) {
    type = 'nodejs';
  } else if (typeof window !== 'undefined') {
    type = 'browser';
  } else if (typeof self !== 'undefined') {
    type = 'worker';
  } else {
    throw new Error('Could not determine platform implementation to load.');
  }

  // Note: Please do NOT try to add PLATFORM.moduleName() annotations here.
  //       This would create a static dependency between bootstrapper and a PAL, which we don't want.
  //       The correct PAL to bundle must be determined by the bundling tool at build time.
  return loader.loadModule('aurelia-pal-' + type)
               .then(palModule => type === 'nodejs' && !isInitialized && palModule.globalize() || palModule.initialize());
}

function preparePlatform(loader) {
  const map = (moduleId, relativeTo) =>
    loader.normalize(moduleId, relativeTo)
          .then(normalized => {
            loader.map(moduleId, normalized);
            return normalized;
          });

  return initializePal(loader)
    .then(() => loader.normalize('aurelia-bootstrapper'))
    .then(bootstrapperName => {
      // aurelia-framework re-exports pretty much everything.
      // As can be seen at the end of this method, the only field accessed by bootstrapper is `Aurelia`,
      // so we document that to enable tree shaking on all other exported members.
      const frameworkPromise = map(PLATFORM.moduleName('aurelia-framework', { exports: ['Aurelia'] }),
                                   bootstrapperName);
      // Please do NOT add PLATFORM.moduleName() around any of those modules.
      // They are not actually loaded here, only mapped.
      return Promise.all([
        frameworkPromise,
        frameworkPromise.then(frameworkName => map('aurelia-dependency-injection', frameworkName)),
        map('aurelia-router', bootstrapperName),
        map('aurelia-logging-console', bootstrapperName)
      ]);
    })
    .then(([frameworkName]) => loader.loadModule(frameworkName))
    .then(fx => startResolve(() => new fx.Aurelia(loader)));
}

function config(appHost, configModuleId, aurelia) {
  aurelia.host = appHost;
  aurelia.configModuleId = configModuleId || null;

  if (configModuleId) {
    return aurelia.loader
      .loadModule(configModuleId)
      .then(customConfig => {
        if (!customConfig.configure) {
          throw new Error(`Cannot initialize module '${configModuleId}' without a configure function.`);
        }

        return customConfig.configure(aurelia);
      });
  }

  aurelia.use
         .standardConfiguration()
         .developmentLogging();

  return aurelia.start().then(() => aurelia.setRoot());
}

function run() {
  return ready()
    .then(createLoader)
    .then(preparePlatform)
    .then(() => {
      const appHosts = host.document.querySelectorAll('[aurelia-app],[data-aurelia-app]');
      for (let i = 0, ii = appHosts.length; i < ii; ++i) {
        const appHost = appHosts[i];
        const moduleId = appHost.getAttribute('aurelia-app') || appHost.getAttribute('data-aurelia-app');
        bootstrap(config.bind(null, appHost, moduleId));
      }

      // This can't be moved before preparePlatform.
      // In old IE the console object only exists after F12 tools have been opened and PAL creates a substitute.
      const toConsole = console.error.bind(console);
      const bootstraps = bootstrapPromises.map(p => p.catch(toConsole));
      bootstrapPromises = null;
      return Promise.all(bootstraps);
    });
}

/**
 * Manually bootstraps an application.
 * @param configure A callback which passes an Aurelia instance to the developer to manually configure and start up the app.
 * @return A Promise that completes when configuration is done.
 */
export function bootstrap(configure: Function): Promise<void> {
  const p = startPromise.then(factory => configure(factory()));
  if (bootstrapPromises) bootstrapPromises.push(p);
  return p;
}

/**
 * A promise that represents the bootstrapper's startup process.
 * It resolves when the process has finished starting.
 */
export const starting = run();
<commit_msg>fix: avoid unnecessary pal loading when pal is already initialized<commit_after>import 'aurelia-polyfills';
import {PLATFORM, isInitialized} from 'aurelia-pal';

let bootstrapPromises = [];
let startResolve;

const startPromise = new Promise(resolve => startResolve = resolve);
const host = PLATFORM.global;
const isNodeLike = typeof process !== 'undefined' && !process.browser;

function ready() {
  if (!host.document || host.document.readyState === 'complete') {
    return Promise.resolve();
  }

  return new Promise(resolve => {
    host.document.addEventListener('DOMContentLoaded', completed);
    host.addEventListener('load', completed);

    function completed() {
      host.document.removeEventListener('DOMContentLoaded', completed);
      host.removeEventListener('load', completed);
      resolve();
    }
  });
}

function createLoader() {
  // Note: Please do NOT add any PLATFORM.moduleName annotation in this method,
  //       for example around 'aurelia-loader-default'.
  //       Each import has been carefully written so that it is picked up by
  //       its respective bundler and is ignored by others.
  //       Adding moduleName() would add a static dependency, which we don't
  //       want as the correct loader is determined at build time.

  // Custom Loader Support
  if (PLATFORM.Loader) {
    return Promise.resolve(new PLATFORM.Loader());
  }

  if (typeof AURELIA_WEBPACK_2_0 === 'undefined') {
    // Webpack Loader Support
    if (typeof __webpack_require__ !== 'undefined') {
      // Webpack needs the require to be top level to parse the request.
      // However, we don't want to use require or that will cause the Babel
      // transpiler to detect an incorrect dependency.
      const m = __webpack_require__(require.resolve('aurelia-loader-webpack'));
      return Promise.resolve(new m.WebpackLoader());
    }

    // SystemJS Loader Support
    if (host.System && typeof host.System.config === 'function') {
      return host.System.normalize('aurelia-bootstrapper').then(bsn => {
        return host.System.normalize('aurelia-loader-default', bsn);
      }).then(loaderName => {
        return host.System.import(loaderName).then(m => new m.DefaultLoader());
      });
    }

    // AMD Module Loader Support
    if (typeof host.require === 'function' && typeof host.define === 'function' && typeof host.define.amd === 'object') {
      return new Promise((resolve, reject) => host.require(['aurelia-loader-default'], m => resolve(new m.DefaultLoader()), reject));
    }

    // Node.js and Electron Support
    if (isNodeLike && typeof module !== 'undefined' && typeof module.require !== 'undefined') {
      // note: we use a scoped module.require() instead of simply require()
      // so that Webpack's parser does not automatically include this loader as a dependency,
      // similarly to the non-global call to System.import() above
      const m = module.require('aurelia-loader-nodejs');
      return Promise.resolve(new m.NodeJsLoader());
    }
  } // endif AURELIA_WEBPACK_2_0

  return Promise.reject('No PLATFORM.Loader is defined and there is neither a System API (ES6) or a Require API (AMD) globally available to load your app.');
}

function initializePal(loader) {
  if (isInitialized) return Promise.resolve();

  let type;

  const isRenderer = isNodeLike && (process.type === 'renderer' || process.versions['node-webkit']);

  if (isNodeLike && !isRenderer) {
    type = 'nodejs';
  } else if (typeof window !== 'undefined') {
    type = 'browser';
  } else if (typeof self !== 'undefined') {
    type = 'worker';
  } else {
    throw new Error('Could not determine platform implementation to load.');
  }

  // Note: Please do NOT try to add PLATFORM.moduleName() annotations here.
  //       This would create a static dependency between bootstrapper and a PAL, which we don't want.
  //       The correct PAL to bundle must be determined by the bundling tool at build time.
  return loader.loadModule('aurelia-pal-' + type)
               .then(palModule => type === 'nodejs' && !isInitialized && palModule.globalize() || palModule.initialize());
}

function preparePlatform(loader) {
  const map = (moduleId, relativeTo) =>
    loader.normalize(moduleId, relativeTo)
          .then(normalized => {
            loader.map(moduleId, normalized);
            return normalized;
          });

  return initializePal(loader)
    .then(() => loader.normalize('aurelia-bootstrapper'))
    .then(bootstrapperName => {
      // aurelia-framework re-exports pretty much everything.
      // As can be seen at the end of this method, the only field accessed by bootstrapper is `Aurelia`,
      // so we document that to enable tree shaking on all other exported members.
      const frameworkPromise = map(PLATFORM.moduleName('aurelia-framework', { exports: ['Aurelia'] }),
                                   bootstrapperName);
      // Please do NOT add PLATFORM.moduleName() around any of those modules.
      // They are not actually loaded here, only mapped.
      return Promise.all([
        frameworkPromise,
        frameworkPromise.then(frameworkName => map('aurelia-dependency-injection', frameworkName)),
        map('aurelia-router', bootstrapperName),
        map('aurelia-logging-console', bootstrapperName)
      ]);
    })
    .then(([frameworkName]) => loader.loadModule(frameworkName))
    .then(fx => startResolve(() => new fx.Aurelia(loader)));
}

function config(appHost, configModuleId, aurelia) {
  aurelia.host = appHost;
  aurelia.configModuleId = configModuleId || null;

  if (configModuleId) {
    return aurelia.loader
      .loadModule(configModuleId)
      .then(customConfig => {
        if (!customConfig.configure) {
          throw new Error(`Cannot initialize module '${configModuleId}' without a configure function.`);
        }

        return customConfig.configure(aurelia);
      });
  }

  aurelia.use
         .standardConfiguration()
         .developmentLogging();

  return aurelia.start().then(() => aurelia.setRoot());
}

function run() {
  return ready()
    .then(createLoader)
    .then(preparePlatform)
    .then(() => {
      const appHosts = host.document.querySelectorAll('[aurelia-app],[data-aurelia-app]');
      for (let i = 0, ii = appHosts.length; i < ii; ++i) {
        const appHost = appHosts[i];
        const moduleId = appHost.getAttribute('aurelia-app') || appHost.getAttribute('data-aurelia-app');
        bootstrap(config.bind(null, appHost, moduleId));
      }

      // This can't be moved before preparePlatform.
      // In old IE the console object only exists after F12 tools have been opened and PAL creates a substitute.
      const toConsole = console.error.bind(console);
      const bootstraps = bootstrapPromises.map(p => p.catch(toConsole));
      bootstrapPromises = null;
      return Promise.all(bootstraps);
    });
}

/**
 * Manually bootstraps an application.
 * @param configure A callback which passes an Aurelia instance to the developer to manually configure and start up the app.
 * @return A Promise that completes when configuration is done.
 */
export function bootstrap(configure: Function): Promise<void> {
  const p = startPromise.then(factory => configure(factory()));
  if (bootstrapPromises) bootstrapPromises.push(p);
  return p;
}

/**
 * A promise that represents the bootstrapper's startup process.
 * It resolves when the process has finished starting.
 */
export const starting = run();
<|endoftext|>


<commit_before>const Discord = require("discord.js");
const config = require("./../../config.json");
const MSS = require("./../../functions/");
const request = require("request");

module.exports = function place(message) {
	let input = message.content.replace (/\n/g, "").split(" ");
	var user = encodeURI(input[2]);

	request('http://moustacheminer.com/place/searchapi.php?username=' + user, function(error, response, body) {
		if(error) {
			message.reply("An error occured while retrieving the data: " + error);
		}

		var data = JSON.parse(body);
		var reply;
		var colour = [];

		for (i=0; i<16; i++) {
			colour[i] = 0;
		}

		if(!data.info) {
			reply += "The user has not placed any dots down, or does not exist.";
		} else {
			data.info.forEach(function(element) {
				colour[element.colour]++
			});

			reply = `Info for ` + user + `
\`\`\`
White      | ` + colour[0]  + ` | ` + ((colour[0]  / data.info.length)*100).toFixed(2) + `%
Light Grey | ` + colour[1]  + ` | ` + ((colour[1]  / data.info.length)*100).toFixed(2) + `%
Grey       | ` + colour[2]  + ` | ` + ((colour[2]  / data.info.length)*100).toFixed(2) + `%
Black      | ` + colour[3]  + ` | ` + ((colour[3]  / data.info.length)*100).toFixed(2) + `%
Pink       | ` + colour[4]  + ` | ` + ((colour[4]  / data.info.length)*100).toFixed(2) + `%
Red        | ` + colour[5]  + ` | ` + ((colour[5]  / data.info.length)*100).toFixed(2) + `%
Orange     | ` + colour[6]  + ` | ` + ((colour[6]  / data.info.length)*100).toFixed(2) + `%
Brown      | ` + colour[7]  + ` | ` + ((colour[7]  / data.info.length)*100).toFixed(2) + `%
Yellow     | ` + colour[8]  + ` | ` + ((colour[8]  / data.info.length)*100).toFixed(2) + `%
Lime       | ` + colour[9]  + ` | ` + ((colour[9]  / data.info.length)*100).toFixed(2) + `%
Green      | ` + colour[10] + ` | ` + ((colour[10] / data.info.length)*100).toFixed(2) + `%
Cyan       | ` + colour[11] + ` | ` + ((colour[11] / data.info.length)*100).toFixed(2) + `%
Blue       | ` + colour[12] + ` | ` + ((colour[12] / data.info.length)*100).toFixed(2) + `%
Dark Blue  | ` + colour[13] + ` | ` + ((colour[13] / data.info.length)*100).toFixed(2) + `%
Magenta    | ` + colour[14] + ` | ` + ((colour[14] / data.info.length)*100).toFixed(2) + `%
Purple     | ` + colour[15] + ` | ` + ((colour[15] / data.info.length)*100).toFixed(2) + `%
-----------------------------
Total      | ` + data.info.length + `\`\`\``;
		}

		//Yes, false is a string. I'm so sorry
		if(data.error != "false") {
			message.reply(data.error);
		} else {
			var embed = new Discord.RichEmbed()
				.setTitle(input[1])
				.setAuthor("/r/place info for " + input[1], "http://moustacheminer.com/mss.png")
				.setColor("#00AE86")
				.setDescription(reply)
				.setFooter("MSS-Discord, " + config.MSS.version, "")
				.setTimestamp()
				.setURL("http://moustacheminer.com/place/?username=" + user)
				.addField("Powered by /r/PlaceDevs", "Please go to this URL to view the rest of the log. http://moustacheminer.com/place/?username=" + user);

			message.channel.sendEmbed(embed, "", { disableEveryone: true });
		}
	});
}
<commit_msg>Add message for /r/place<commit_after>const Discord = require("discord.js");
const config = require("./../../config.json");
const MSS = require("./../../functions/");
const request = require("request");

module.exports = function place(message) {
	let input = message.content.replace (/\n/g, "").split(" ");
	var user = encodeURI(input[2]);

    message.reply("Your query has been sent to the database. Please wait for a response.");

	request('http://moustacheminer.com/place/searchapi.php?username=' + user, function(error, response, body) {
		if(error) {
			message.reply("An error occured while retrieving the data: " + error);
		}

		var data = JSON.parse(body);
		var reply;
		var colour = [];

		for (i=0; i<16; i++) {
			colour[i] = 0;
		}

		if(!data.info) {
			reply += "The user has not placed any dots down, or does not exist.";
		} else {
			data.info.forEach(function(element) {
				colour[element.colour]++
			});

			reply = `Info for ` + user + `
\`\`\`
White      | ` + colour[0]  + ` | ` + ((colour[0]  / data.info.length)*100).toFixed(2) + `%
Light Grey | ` + colour[1]  + ` | ` + ((colour[1]  / data.info.length)*100).toFixed(2) + `%
Grey       | ` + colour[2]  + ` | ` + ((colour[2]  / data.info.length)*100).toFixed(2) + `%
Black      | ` + colour[3]  + ` | ` + ((colour[3]  / data.info.length)*100).toFixed(2) + `%
Pink       | ` + colour[4]  + ` | ` + ((colour[4]  / data.info.length)*100).toFixed(2) + `%
Red        | ` + colour[5]  + ` | ` + ((colour[5]  / data.info.length)*100).toFixed(2) + `%
Orange     | ` + colour[6]  + ` | ` + ((colour[6]  / data.info.length)*100).toFixed(2) + `%
Brown      | ` + colour[7]  + ` | ` + ((colour[7]  / data.info.length)*100).toFixed(2) + `%
Yellow     | ` + colour[8]  + ` | ` + ((colour[8]  / data.info.length)*100).toFixed(2) + `%
Lime       | ` + colour[9]  + ` | ` + ((colour[9]  / data.info.length)*100).toFixed(2) + `%
Green      | ` + colour[10] + ` | ` + ((colour[10] / data.info.length)*100).toFixed(2) + `%
Cyan       | ` + colour[11] + ` | ` + ((colour[11] / data.info.length)*100).toFixed(2) + `%
Blue       | ` + colour[12] + ` | ` + ((colour[12] / data.info.length)*100).toFixed(2) + `%
Dark Blue  | ` + colour[13] + ` | ` + ((colour[13] / data.info.length)*100).toFixed(2) + `%
Magenta    | ` + colour[14] + ` | ` + ((colour[14] / data.info.length)*100).toFixed(2) + `%
Purple     | ` + colour[15] + ` | ` + ((colour[15] / data.info.length)*100).toFixed(2) + `%
-----------------------------
Total      | ` + data.info.length + `\`\`\``;
		}

		//Yes, false is a string. I'm so sorry
		if(data.error != "false") {
			message.reply(data.error);
		} else {
			var embed = new Discord.RichEmbed()
				.setTitle(input[1])
				.setAuthor("/r/place info for " + input[1], "http://moustacheminer.com/mss.png")
				.setColor("#00AE86")
				.setDescription(reply)
				.setFooter("MSS-Discord, " + config.MSS.version, "")
				.setTimestamp()
				.setURL("http://moustacheminer.com/place/?username=" + user)
				.addField("Powered by /r/PlaceDevs", "Please go to this URL to view the rest of the log. http://moustacheminer.com/place/?username=" + user);

			message.channel.sendEmbed(embed, "", { disableEveryone: true });
		}
	});
}
<|endoftext|>


<commit_before><commit_msg>Delete find_controller.js<commit_after><|endoftext|>