<commit_before>pc.extend(pc, function () {
    /**
    * @component
    * @name pc.SoundComponent
    * @class The Sound Component controls playback of {@link pc.Sound}s.
    * @description Create a new Sound Component
    * @param {pc.SoundComponentSystem} system The ComponentSystem that created this Component
    * @param {pc.Entity} entity The entity that the Component is attached to
    * @extends pc.Component
    * @property {Number} volume The volume modifier to play the audio with. In range 0-1.
    * @property {Number} pitch The pitch modifier to play the audio with. Must be larger than 0.01
    * @property {Boolean} positional If true the audio will play back at the location of the Entity in space, so the audio will be affect by the position of the {@link pc.AudioListenerComponent}.
    * @property {String} distanceModel Determines which algorithm to use to reduce the volume of the audio as it moves away from the listener. Can be one of {@link pc.DISTANCE_LINEAR}, {@link pc.DISTANCE_INVERSE} or {@link pc.DISTANCE_EXPONENTIAL}. Default is {@link pc.DISTANCE_LINEAR}.
    * @property {Number} refDistance The reference distance for reducing volume as the sound source moves further from the listener.
    * @property {Number} maxDistance The maximum distance from the listener at which audio falloff stops. Note the volume of the audio is not 0 after this distance, but just doesn't fall off anymore.
    * @property {Number} rollOffFactor The factor used in the falloff equation.
    * @property {Object} slots A dictionary that contains the {@link pc.SoundSlot}s managed by this Component.
    */
    var SoundComponent = function (system, entity) {
        this.on('set_slots', this.onSetSlots, this);
        this.on('set_volume', this.onSetVolume, this);
        this.on('set_pitch', this.onSetPitch, this);
        this.on("set_refDistance", this.onSetRefDistance, this);
        this.on("set_maxDistance", this.onSetMaxDistance, this);
        this.on("set_rollOffFactor", this.onSetRollOffFactor, this);
        this.on("set_distanceModel", this.onSetDistanceModel, this);
        this.on("set_positional", this.onSetPositional, this);
    };

    SoundComponent = pc.inherits(SoundComponent, pc.Component);

    pc.extend(SoundComponent.prototype, {
        onSetSlots: function (name, oldValue, newValue) {
            // stop previous slots
            if (oldValue) {
                for (var key in oldValue) {
                    oldValue[key].stop();
                }
            }

            var slots = {};

            // convert data to slots
            for (var key in newValue) {
                if (! (newValue[key] instanceof pc.SoundSlot)) {
                    if (newValue[key].name) {
                        slots[newValue[key].name] = new pc.SoundSlot(this, newValue[key].name, newValue[key]);
                    }
                } else {
                    slots[newValue[key].name] = newValue[key];
                }
            }

            this.data.slots = slots;

            // call onEnable in order to start autoPlay slots
            if (this.enabled && this.entity.enabled)
                this.onEnable();
        },

        onSetVolume: function (name, oldValue, newValue) {
            var slots = this.data.slots;
            for (var key in slots) {
                var slot = slots[key];
                // change volume of non-overlapping instances
                if (! slot.overlap) {
                    var instances = slot.instances;
                    for (var i = 0, len = instances.length; i < len; i++) {
                        instances[i].volume = slot.volume * newValue;
                    }
                }
            }
        },

        onSetPitch: function (name, oldValue, newValue) {
            var slots = this.data.slots;
            for (var key in slots) {
                var slot = slots[key];
                // change pitch of non-overlapping instances
                if (! slot.overlap) {
                    var instances = slot.instances;
                    for (var i = 0, len = instances.length; i < len; i++) {
                        instances[i].pitch = slot.pitch * newValue;
                    }
                }
            }
        },

        onSetRefDistance: function (name, oldValue, newValue) {
            var slots = this.data.slots;
            for (var key in slots) {
                var slot = slots[key];
                // change refDistance of non-overlapping instances
                if (! slot.overlap) {
                    var instances = slot.instances;
                    for (var i = 0, len = instances.length; i < len; i++) {
                        instances[i].refDistance = newValue;
                    }
                }
            }
        },

        onSetMaxDistance: function (name, oldValue, newValue) {
            var slots = this.data.slots;
            for (var key in slots) {
                var slot = slots[key];
                // change maxDistance of non-overlapping instances
                if (! slot.overlap) {
                    var instances = slot.instances;
                    for (var i = 0, len = instances.length; i < len; i++) {
                        instances[i].maxDistance = newValue;
                    }
                }
            }
        },

        onSetRollOffFactor: function (name, oldValue, newValue) {
            var slots = this.data.slots;
            for (var key in slots) {
                var slot = slots[key];
                // change rollOffFactor of non-overlapping instances
                if (! slot.overlap) {
                    var instances = slot.instances;
                    for (var i = 0, len = instances.length; i < len; i++) {
                        instances[i].rollOffFactor = newValue;
                    }
                }
            }
        },

        onSetDistanceModel: function (name, oldValue, newValue) {
            var slots = this.data.slots;
            for (var key in slots) {
                var slot = slots[key];
                // change distanceModel of non-overlapping instances
                if (! slot.overlap) {
                    var instances = slot.instances;
                    for (var i = 0, len = instances.length; i < len; i++) {
                        instances[i].distanceModel = newValue;
                    }
                }
            }
        },

        onSetPositional: function (name, oldValue, newValue) {
            var slots = this.data.slots;
            for (var key in slots) {
                var slot = slots[key];
                // recreate non overlapping sounds
                if (! slot.overlap) {
                    var instances = slot.instances;
                    for (var i = 0, len = instances.length; i < len; i++) {
                        var isPlaying = instances[i].isPlaying || instances[i].isSuspended;
                        var currentTime = instances[i].currentTime;
                        if (isPlaying)
                            instances[i].stop();

                        instances[i] = slot._createInstance();
                        if (isPlaying) {
                            instances[i].play();
                            instances[i].currentTime = currentTime;
                        }
                    }
                }
            }
        },

        onEnable: function () {
            SoundComponent._super.onEnable.call(this);

            // do not run if running in Editor
            if (this.system._inTools) {
                return;
            }

            var slots = this.data.slots;
            var playingBeforeDisable = this.data.playingBeforeDisable;

            for (var key in slots) {
                var slot = slots[key];
                // play if autoPlay is true or
                // if the slot was paused when the component
                // got disabled
                if (slot.autoPlay && slot.isStopped) {
                    slot.play();
                } else if (playingBeforeDisable[key]) {
                    slot.resume();
                } else if (!slot.isLoaded) {
                    // start loading slots
                    slot.load();
                }
            }
        },

        onDisable: function () {
            SoundComponent._super.onDisable.call(this);

            var slots = this.data.slots;
            var playingBeforeDisable = {};
            for (var key in slots) {
                // pause non-overlapping sounds
                if (! slots[key].overlap) {
                    if (slots[key].isPlaying) {
                        slots[key].pause();
                        // remember sounds playing when we disable
                        // so we can resume them on enable
                        playingBeforeDisable[key] = true;
                    }
                }
            }

            this.data.playingBeforeDisable = playingBeforeDisable;
        },

        /**
         * @function
         * @name pc.SoundComponent#addSlot
         * @description Creates a new {@link pc.SoundSlot} with the specified name.
         * @param {String} name The name of the slot
         * @param {Object} options Settings for the slot
         * @param {Number} [options.volume=1] The playback volume, between 0 and 1.
         * @param {Number} [options.pitch=1] The relative pitch, default of 1, plays at normal pitch.
         * @param {Boolean} [options.loop=false] If true the sound will restart when it reaches the end.
         * @param {Number} [options.startTime=0] The start time from which the sound will start playing.
         * @param {Number} [options.duration=null] The duration of the sound that the slot will play starting from startTime.
         * @param {Boolean} [options.overlap=false] If true then sounds played from slot will be played independently of each other. Otherwise the slot will first stop the current sound before starting the new one.
         * @param {Boolean} [options.autoPlay=false] If true the slot will start playing as soon as its audio asset is loaded.
         * @param {Number} [options.asset=null] The asset id of the audio asset that is going to be played by this slot.
         * @returns {pc.SoundSlot} The new slot.
         * @example
         * // get an asset by id
         * var asset = app.assets.get(10);
         * // add a slot
         * this.entity.sound.addSlot('beep', {
         *     asset: asset
         * });
         * // play
         * this.entity.sound.play('beep');
         */
        addSlot: function (name, options) {
            var slots = this.data.slots;
            if (slots[name]) {
                logWARNING('A sound slot with name ' + name + ' already exists on Entity ' + this.entity.getPath());
                return null;
            }

            var slot = new pc.SoundSlot(this, name, options);
            slots[name] = slot;

            if (slot.autoPlay && this.enabled && this.entity.enabled) {
                slot.play();
            }

            return slot;
        },

        /**
         * @function
         * @name pc.SoundComponent@removeSlot
         * @description Removes the {@link pc.SoundSlot} with the specified name.
         * @param  {String} name The name of the slot
         * @example
         * // remove a slot called 'beep'
         * this.entity.removeSlot('beep');
         */
        removeSlot: function (name) {
            var slots = this.data.slots;
            if (slots[name]) {
                slots[name].stop();
                delete slots[name];
            }
        },

        /**
         * @function
         * @name pc.SoundComponent@slot
         * @description Returns the slot with the specified name
         * @param  {String} name The name of the slot
         * @returns {pc.SoundSlot} The slot
         * @example
         * // get a slot and set its volume
         * this.entity.sound.slot('beep').volume = 0.5;
         *
         */
        slot: function (name) {
            return this.data.slots[name];
        },

        /**
        * @function
        * @name pc.SoundComponent#play
        * @description Begins playing the sound slot with the specified name. The slot will restart playing if it is already playing unless the overlap field is true in which case a new sound will be created and played.
        * @param {String} name The name of the {@link pc.SoundSlot} to play
        * @example
        * // get asset by id
        * var asset = app.assets.get(10);
        * // create a slot and play it
        * this.entity.addSlot('beep', {
        *     asset: asset
        * });
        * this.entity.play('beep');
        * @returns {pc.SoundInstance} The sound instance that will be played.
        */
        play: function (name) {
            if (!this.enabled || !this.entity.enabled) {
                return null;
            }

            var slot = this.slots[name];
            if (! slot) {
                logWARNING('Trying to play sound slot with name ' + name + ' which does not exist');
                return null;
            }

            var instance = slot.play();

            this.fire('play', this, slot, instance);

            return instance;
        },

        /**
        * @function
        * @name pc.SoundComponent#pause
        * @description Pauses playback of the slot with the specified name. If the name is undefined then all slots currently played will be paused. The slots can be resumed by calling {@link pc.SoundComponent#resume}.
        * @param {String} [name] The name of the slot to pause. Leave undefined to pause everything.
        * @example
        * // pause all sounds
        * this.entity.sound.pause();
        * // pause a specific sound
        * this.entity.sound.pause('beep');
        */
        pause: function (name) {
            var slot;
            var slots = this.data.slots;

            if (name) {
                slot = slots[name];
                if (! slot) {
                    logWARNING('Trying to pause sound slot with name ' + name + ' which does not exist');
                    return;
                }

                slot.pause();

                this.fire('pause', this, slot);
            } else {
                for (var key in slots) {
                    slots[key].pause();
                }

                this.fire('pause', this);
            }
        },

        /**
        * @function
        * @name pc.SoundComponent#resume
        * @description Resumes playback of the sound slot with the specified name if it's paused. If no name is specified all slots will be resumed.
        * @param {String} name The name of the slot to resume. Leave undefined to resume everything.
        * @example
        * // resume all sounds
        * this.entity.sound.resume();
        * // resume a specific sound
        * this.entity.sound.resume('beep');
        */
        resume: function (name) {
            var slot;
            var slots = this.data.slots;

            if (name) {
                slot = slots[name];
                if (! slot) {
                    logWARNING('Trying to resume sound slot with name ' + name + ' which does not exist');
                    return;
                }

                if (slot.isPaused) {
                    if (slot.resume()) {
                        this.fire('resume', this, slot);
                    }
                }
            } else {
                for (var key in slots) {
                    slots[key].resume();
                }

                this.fire('resume', this);
            }
        },

        /**
        * @function
        * @name pc.SoundComponent#stop
        * @description Stops playback of the sound slot with the specified name if it's paused. If no name is specified all slots will be stopped.
        * @param {String} name The name of the slot to stop. Leave undefined to stop everything.
        * @example
        * // stop all sounds
        * this.entity.sound.stop();
        * // stop a specific sound
        * this.entity.sound.stop('beep');
        */
        stop: function (name) {
            var slot;
            var slots = this.data.slots;

            if (name) {
                slot = slots[name];
                if (! slot) {
                    logWARNING('Trying to stop sound slot with name ' + name + ' which does not exist');
                    return;
                }

                if (slot.stop()) {
                    this.fire('stop', this, slot);
                }

            } else {
                for (var key in slots) {
                    slots[key].stop();
                }

                this.fire('stop', this);
            }
        }
    });

    return {
        SoundComponent: SoundComponent
    };
}());

//**** Events Documentation *****//

/**
* @event
* @name pc.SoundComponent#play
* @description Fired when the component starts playing
* @param {pc.SoundComponent} component The component
* @param {pc.SoundSlot} slot The slot that started playing
* @param {pc.SoundInstance} instance The instance created to play the sound
*/

/**
* @event
* @name pc.SoundComponent#pause
* @description Fired when the component is paused.
* @param {pc.SoundComponent} component The component
* @param {pc.SoundSlot} slot The slot that was paused. If multiple slots were paused this is undefined.
*/

/**
* @event
* @name pc.SoundComponent#resume
* @description Fired when the component is resumed.
* @param {pc.SoundComponent} component The component
* @param {pc.SoundSlot} slot The slot that was resumed. If multiple slots were resumed this is undefined.
*/

/**
* @event
* @name pc.SoundComponent#stop
* @description Fired when the component is stopped.
* @param {pc.SoundComponent} component The component
* @param {pc.SoundSlot} slot The slot that was stopped. If multiple slots were stopped this is undefined.
*/
<commit_msg>Doc change<commit_after>pc.extend(pc, function () {
    /**
    * @component
    * @name pc.SoundComponent
    * @class The Sound Component controls playback of {@link pc.Sound}s.
    * @description Create a new Sound Component
    * @param {pc.SoundComponentSystem} system The ComponentSystem that created this Component
    * @param {pc.Entity} entity The entity that the Component is attached to
    * @extends pc.Component
    * @property {Number} volume The volume modifier to play the audio with. In range 0-1.
    * @property {Number} pitch The pitch modifier to play the audio with. Must be larger than 0.01
    * @property {Boolean} positional If true the audio will play back at the location of the Entity in space, so the audio will be affect by the position of the {@link pc.AudioListenerComponent}.
    * @property {String} distanceModel Determines which algorithm to use to reduce the volume of the sound as it moves away from the listener. Can be one of {@link pc.DISTANCE_LINEAR}, {@link pc.DISTANCE_INVERSE} or {@link pc.DISTANCE_EXPONENTIAL}. Default is {@link pc.DISTANCE_LINEAR}.
    * @property {Number} refDistance The reference distance for reducing volume as the sound source moves further from the listener.
    * @property {Number} maxDistance The maximum distance from the listener at which audio falloff stops. Note the volume of the audio is not 0 after this distance, but just doesn't fall off anymore.
    * @property {Number} rollOffFactor The factor used in the falloff equation.
    * @property {Object} slots A dictionary that contains the {@link pc.SoundSlot}s managed by this Component.
    */
    var SoundComponent = function (system, entity) {
        this.on('set_slots', this.onSetSlots, this);
        this.on('set_volume', this.onSetVolume, this);
        this.on('set_pitch', this.onSetPitch, this);
        this.on("set_refDistance", this.onSetRefDistance, this);
        this.on("set_maxDistance", this.onSetMaxDistance, this);
        this.on("set_rollOffFactor", this.onSetRollOffFactor, this);
        this.on("set_distanceModel", this.onSetDistanceModel, this);
        this.on("set_positional", this.onSetPositional, this);
    };

    SoundComponent = pc.inherits(SoundComponent, pc.Component);

    pc.extend(SoundComponent.prototype, {
        onSetSlots: function (name, oldValue, newValue) {
            // stop previous slots
            if (oldValue) {
                for (var key in oldValue) {
                    oldValue[key].stop();
                }
            }

            var slots = {};

            // convert data to slots
            for (var key in newValue) {
                if (! (newValue[key] instanceof pc.SoundSlot)) {
                    if (newValue[key].name) {
                        slots[newValue[key].name] = new pc.SoundSlot(this, newValue[key].name, newValue[key]);
                    }
                } else {
                    slots[newValue[key].name] = newValue[key];
                }
            }

            this.data.slots = slots;

            // call onEnable in order to start autoPlay slots
            if (this.enabled && this.entity.enabled)
                this.onEnable();
        },

        onSetVolume: function (name, oldValue, newValue) {
            var slots = this.data.slots;
            for (var key in slots) {
                var slot = slots[key];
                // change volume of non-overlapping instances
                if (! slot.overlap) {
                    var instances = slot.instances;
                    for (var i = 0, len = instances.length; i < len; i++) {
                        instances[i].volume = slot.volume * newValue;
                    }
                }
            }
        },

        onSetPitch: function (name, oldValue, newValue) {
            var slots = this.data.slots;
            for (var key in slots) {
                var slot = slots[key];
                // change pitch of non-overlapping instances
                if (! slot.overlap) {
                    var instances = slot.instances;
                    for (var i = 0, len = instances.length; i < len; i++) {
                        instances[i].pitch = slot.pitch * newValue;
                    }
                }
            }
        },

        onSetRefDistance: function (name, oldValue, newValue) {
            var slots = this.data.slots;
            for (var key in slots) {
                var slot = slots[key];
                // change refDistance of non-overlapping instances
                if (! slot.overlap) {
                    var instances = slot.instances;
                    for (var i = 0, len = instances.length; i < len; i++) {
                        instances[i].refDistance = newValue;
                    }
                }
            }
        },

        onSetMaxDistance: function (name, oldValue, newValue) {
            var slots = this.data.slots;
            for (var key in slots) {
                var slot = slots[key];
                // change maxDistance of non-overlapping instances
                if (! slot.overlap) {
                    var instances = slot.instances;
                    for (var i = 0, len = instances.length; i < len; i++) {
                        instances[i].maxDistance = newValue;
                    }
                }
            }
        },

        onSetRollOffFactor: function (name, oldValue, newValue) {
            var slots = this.data.slots;
            for (var key in slots) {
                var slot = slots[key];
                // change rollOffFactor of non-overlapping instances
                if (! slot.overlap) {
                    var instances = slot.instances;
                    for (var i = 0, len = instances.length; i < len; i++) {
                        instances[i].rollOffFactor = newValue;
                    }
                }
            }
        },

        onSetDistanceModel: function (name, oldValue, newValue) {
            var slots = this.data.slots;
            for (var key in slots) {
                var slot = slots[key];
                // change distanceModel of non-overlapping instances
                if (! slot.overlap) {
                    var instances = slot.instances;
                    for (var i = 0, len = instances.length; i < len; i++) {
                        instances[i].distanceModel = newValue;
                    }
                }
            }
        },

        onSetPositional: function (name, oldValue, newValue) {
            var slots = this.data.slots;
            for (var key in slots) {
                var slot = slots[key];
                // recreate non overlapping sounds
                if (! slot.overlap) {
                    var instances = slot.instances;
                    for (var i = 0, len = instances.length; i < len; i++) {
                        var isPlaying = instances[i].isPlaying || instances[i].isSuspended;
                        var currentTime = instances[i].currentTime;
                        if (isPlaying)
                            instances[i].stop();

                        instances[i] = slot._createInstance();
                        if (isPlaying) {
                            instances[i].play();
                            instances[i].currentTime = currentTime;
                        }
                    }
                }
            }
        },

        onEnable: function () {
            SoundComponent._super.onEnable.call(this);

            // do not run if running in Editor
            if (this.system._inTools) {
                return;
            }

            var slots = this.data.slots;
            var playingBeforeDisable = this.data.playingBeforeDisable;

            for (var key in slots) {
                var slot = slots[key];
                // play if autoPlay is true or
                // if the slot was paused when the component
                // got disabled
                if (slot.autoPlay && slot.isStopped) {
                    slot.play();
                } else if (playingBeforeDisable[key]) {
                    slot.resume();
                } else if (!slot.isLoaded) {
                    // start loading slots
                    slot.load();
                }
            }
        },

        onDisable: function () {
            SoundComponent._super.onDisable.call(this);

            var slots = this.data.slots;
            var playingBeforeDisable = {};
            for (var key in slots) {
                // pause non-overlapping sounds
                if (! slots[key].overlap) {
                    if (slots[key].isPlaying) {
                        slots[key].pause();
                        // remember sounds playing when we disable
                        // so we can resume them on enable
                        playingBeforeDisable[key] = true;
                    }
                }
            }

            this.data.playingBeforeDisable = playingBeforeDisable;
        },

        /**
         * @function
         * @name pc.SoundComponent#addSlot
         * @description Creates a new {@link pc.SoundSlot} with the specified name.
         * @param {String} name The name of the slot
         * @param {Object} options Settings for the slot
         * @param {Number} [options.volume=1] The playback volume, between 0 and 1.
         * @param {Number} [options.pitch=1] The relative pitch, default of 1, plays at normal pitch.
         * @param {Boolean} [options.loop=false] If true the sound will restart when it reaches the end.
         * @param {Number} [options.startTime=0] The start time from which the sound will start playing.
         * @param {Number} [options.duration=null] The duration of the sound that the slot will play starting from startTime.
         * @param {Boolean} [options.overlap=false] If true then sounds played from slot will be played independently of each other. Otherwise the slot will first stop the current sound before starting the new one.
         * @param {Boolean} [options.autoPlay=false] If true the slot will start playing as soon as its audio asset is loaded.
         * @param {Number} [options.asset=null] The asset id of the audio asset that is going to be played by this slot.
         * @returns {pc.SoundSlot} The new slot.
         * @example
         * // get an asset by id
         * var asset = app.assets.get(10);
         * // add a slot
         * this.entity.sound.addSlot('beep', {
         *     asset: asset
         * });
         * // play
         * this.entity.sound.play('beep');
         */
        addSlot: function (name, options) {
            var slots = this.data.slots;
            if (slots[name]) {
                logWARNING('A sound slot with name ' + name + ' already exists on Entity ' + this.entity.getPath());
                return null;
            }

            var slot = new pc.SoundSlot(this, name, options);
            slots[name] = slot;

            if (slot.autoPlay && this.enabled && this.entity.enabled) {
                slot.play();
            }

            return slot;
        },

        /**
         * @function
         * @name pc.SoundComponent@removeSlot
         * @description Removes the {@link pc.SoundSlot} with the specified name.
         * @param  {String} name The name of the slot
         * @example
         * // remove a slot called 'beep'
         * this.entity.removeSlot('beep');
         */
        removeSlot: function (name) {
            var slots = this.data.slots;
            if (slots[name]) {
                slots[name].stop();
                delete slots[name];
            }
        },

        /**
         * @function
         * @name pc.SoundComponent@slot
         * @description Returns the slot with the specified name
         * @param  {String} name The name of the slot
         * @returns {pc.SoundSlot} The slot
         * @example
         * // get a slot and set its volume
         * this.entity.sound.slot('beep').volume = 0.5;
         *
         */
        slot: function (name) {
            return this.data.slots[name];
        },

        /**
        * @function
        * @name pc.SoundComponent#play
        * @description Begins playing the sound slot with the specified name. The slot will restart playing if it is already playing unless the overlap field is true in which case a new sound will be created and played.
        * @param {String} name The name of the {@link pc.SoundSlot} to play
        * @example
        * // get asset by id
        * var asset = app.assets.get(10);
        * // create a slot and play it
        * this.entity.addSlot('beep', {
        *     asset: asset
        * });
        * this.entity.play('beep');
        * @returns {pc.SoundInstance} The sound instance that will be played.
        */
        play: function (name) {
            if (!this.enabled || !this.entity.enabled) {
                return null;
            }

            var slot = this.slots[name];
            if (! slot) {
                logWARNING('Trying to play sound slot with name ' + name + ' which does not exist');
                return null;
            }

            var instance = slot.play();

            this.fire('play', this, slot, instance);

            return instance;
        },

        /**
        * @function
        * @name pc.SoundComponent#pause
        * @description Pauses playback of the slot with the specified name. If the name is undefined then all slots currently played will be paused. The slots can be resumed by calling {@link pc.SoundComponent#resume}.
        * @param {String} [name] The name of the slot to pause. Leave undefined to pause everything.
        * @example
        * // pause all sounds
        * this.entity.sound.pause();
        * // pause a specific sound
        * this.entity.sound.pause('beep');
        */
        pause: function (name) {
            var slot;
            var slots = this.data.slots;

            if (name) {
                slot = slots[name];
                if (! slot) {
                    logWARNING('Trying to pause sound slot with name ' + name + ' which does not exist');
                    return;
                }

                slot.pause();

                this.fire('pause', this, slot);
            } else {
                for (var key in slots) {
                    slots[key].pause();
                }

                this.fire('pause', this);
            }
        },

        /**
        * @function
        * @name pc.SoundComponent#resume
        * @description Resumes playback of the sound slot with the specified name if it's paused. If no name is specified all slots will be resumed.
        * @param {String} name The name of the slot to resume. Leave undefined to resume everything.
        * @example
        * // resume all sounds
        * this.entity.sound.resume();
        * // resume a specific sound
        * this.entity.sound.resume('beep');
        */
        resume: function (name) {
            var slot;
            var slots = this.data.slots;

            if (name) {
                slot = slots[name];
                if (! slot) {
                    logWARNING('Trying to resume sound slot with name ' + name + ' which does not exist');
                    return;
                }

                if (slot.isPaused) {
                    if (slot.resume()) {
                        this.fire('resume', this, slot);
                    }
                }
            } else {
                for (var key in slots) {
                    slots[key].resume();
                }

                this.fire('resume', this);
            }
        },

        /**
        * @function
        * @name pc.SoundComponent#stop
        * @description Stops playback of the sound slot with the specified name if it's paused. If no name is specified all slots will be stopped.
        * @param {String} name The name of the slot to stop. Leave undefined to stop everything.
        * @example
        * // stop all sounds
        * this.entity.sound.stop();
        * // stop a specific sound
        * this.entity.sound.stop('beep');
        */
        stop: function (name) {
            var slot;
            var slots = this.data.slots;

            if (name) {
                slot = slots[name];
                if (! slot) {
                    logWARNING('Trying to stop sound slot with name ' + name + ' which does not exist');
                    return;
                }

                if (slot.stop()) {
                    this.fire('stop', this, slot);
                }

            } else {
                for (var key in slots) {
                    slots[key].stop();
                }

                this.fire('stop', this);
            }
        }
    });

    return {
        SoundComponent: SoundComponent
    };
}());

//**** Events Documentation *****//

/**
* @event
* @name pc.SoundComponent#play
* @description Fired when the component starts playing
* @param {pc.SoundComponent} component The component
* @param {pc.SoundSlot} slot The slot that started playing
* @param {pc.SoundInstance} instance The instance created to play the sound
*/

/**
* @event
* @name pc.SoundComponent#pause
* @description Fired when the component is paused.
* @param {pc.SoundComponent} component The component
* @param {pc.SoundSlot} slot The slot that was paused. If multiple slots were paused this is undefined.
*/

/**
* @event
* @name pc.SoundComponent#resume
* @description Fired when the component is resumed.
* @param {pc.SoundComponent} component The component
* @param {pc.SoundSlot} slot The slot that was resumed. If multiple slots were resumed this is undefined.
*/

/**
* @event
* @name pc.SoundComponent#stop
* @description Fired when the component is stopped.
* @param {pc.SoundComponent} component The component
* @param {pc.SoundSlot} slot The slot that was stopped. If multiple slots were stopped this is undefined.
*/
<|endoftext|>


<commit_before>$.extend(view, {
  // Get listening by year.
  getListeningHistory: function (type) {
    app.initChart();
    if (type == '%w') {
      var where = 'DATE_FORMAT(<?=TBL_listening?>.`date`, \'' + type + '\') IS NOT NULL';
    }
    else if (type == '%Y%m') {
      var where = 'DATE_FORMAT(<?=TBL_listening?>.`date`, \'%m\') != \'00\'';
    }
    else {
      var where = 'DATE_FORMAT(<?=TBL_listening?>.`date`, \'' + type + '\') != \'00\'';
    }
    $.ajax({
      type:'GET',
      dataType:'json',
      url:'/api/listener/get',
      data:{
        group_by:'DATE_FORMAT(<?=TBL_listening?>.`date`, \'' + type + '\')',
        limit:200,
        order_by:'DATE_FORMAT(<?=TBL_listening?>.`date`, \'' + type + '\') ASC',
        select:'DATE_FORMAT(<?=TBL_listening?>.`date`, \'' + type + '\') as `bar_date`',
        username:'<?php echo !empty($username) ? $username: ''?>',
        where:where
      },
      statusCode:{
        200: function (data) { // 200 OK
          $.ajax({
            type:'POST',
            url:'/ajax/barChart',
            data:{
              json_data:data,
              type:type
            },
            success: function (data) {
              $('#historyLoader').hide();
              $('#history').html(data).hide();
              app.chart.xAxis[0].setCategories(view.categories, false);
              app.chart.series[0].setData(view.chart_data, true);
            }
          });
        },
        204: function () { // 204 No Content
          $('#historyLoader').hide();
          $('#history').html('<?=ERR_NO_RESULTS?>');
        },
        400: function (data) {alert('400 Bad Request')}
      }
    });
  },
  // Get recent listenings.
  getRecentListenings: function (isFirst, callback) {
    if (isFirst != true) {
      $('#recentlyListenedLoader2').show();
    }
    $.ajax({
      type:'GET',
      dataType:'json',
      url:'/api/listening/get',
      data:{
        limit:10,
        username:'<?php echo !empty($username) ? $username: ''?>'
      },
      statusCode:{
        200: function (data) { // 200 OK
          $.ajax({
            type:'POST',url:'/ajax/chartTable',
            data:{
              json_data:data,
              hide:{
                del:true
              }
            },
            success: function (data) {
              $('#recentlyListenedLoader2').hide();
              $('#recentlyListenedLoader').hide();
              $('#recentlyListened').html(data);
            }
          })
        },
        204: function () { // 204 No Content
          $('#recentlyListenedLoader').hide();
          $('#recentlyListened').html('<?=ERR_NO_RESULTS?>');
        },
        400: function (data) {alert('400 Bad Request')}
      }
    });
  },
  // Get top albums.
  getTopAlbums: function () {
    $.ajax({
      type:'GET',
      dataType:'json',
      url:'/api/album/get',
      data:{
        limit:10,
        lower_limit:'<?=date('Y-m-d', ($interval == 'overall') ? 0 : time() - ($interval * 24 * 60 * 60))?>',
        username:'<?php echo !empty($username) ? $username: ''?>'
      },
      statusCode:{
        200: function (data) {
          $.ajax({
            type:'POST',
            url:'/ajax/albumList/124',
            data:{
              json_data:data,
            },
            success: function (data) {
              $('#topAlbumLoader').hide();
              $('#topAlbum').html(data);
            },
            complete: function () {
              setTimeout(view.getTopAlbums, 60 * 10 * 1000);
            }
          });
        },
        204: function () { // 204 No Content
          $('#topAlbumLoader').hide();
          $('#topAlbum').html('<?=ERR_NO_RESULTS?>');
        }
      }
    });
  },
  // Get top artists.
  getTopArtists: function () {
    $.ajax({
      type:'GET',
      dataType:'json',
      url:'/api/artist/get',
      data:{
        limit:10,
        lower_limit:'<?=date('Y-m-d', ($interval == 'overall') ? 0 : time() - ($interval * 24 * 60 * 60))?>',
        username:'<?php echo !empty($username) ? $username: ''?>'
      },
      statusCode:{
        200: function (data) {
          $.ajax({
            type:'POST',
            url:'/ajax/artistList/124',
            data:{
              json_data:data,
            },
            success: function (data) {
              $('#topArtistLoader').hide();
              $('#topArtist').html(data);
            },
            complete: function () {
              setTimeout(view.getTopArtists, 60 * 10 * 1000);
            }
          });
        },
        204: function () { // 204 No Content
          $('#topArtistLoader').hide();
          $('#topArtist').html('<?=ERR_NO_RESULTS?>');
        }
      }
    });
  },
  recentlyFaned: function () {
    $.ajax({
      type:'GET',
      dataType:'json',
      url:'/api/fan/get',
      data:{
        limit:20,
        username:'<?php echo !empty($username) ? $username: ''?>'
      },
      statusCode:{
        200: function (data) {
          $.ajax({
            type:'POST',
            url:'/ajax/likeTable',
            data:{
              json_data:data,
              hide:{
                rank:true,
                user:true
              }
            },
            success: function (data) {
              $('#recentlyFanedLoader').hide();
              $('#recentlyFaned').html(data);
            }
          });
        }
      }
    });
  },
  recentlyLoved: function () {
    $.ajax({
      type:'GET',
      dataType:'json',
      url:'/api/love/get',
      data:{
        limit:20,
        username:'<?php echo !empty($username) ? $username: ''?>'
      },
      statusCode:{
        200: function (data) {
          $.ajax({
            type:'POST',
            url:'/ajax/likeTable',
            data:{
              json_data:data,
              hide:{
                rank:true,
                user:true
              }
            },
            success: function (data) {
              $('#recentlyLovedLoader').hide();
              $('#recentlyLoved').html(data);
            }
          });
        }
      }
    });
  }
});

$(document).ready(function () {
  view.getListeningHistory('%Y');
  view.getRecentListenings();
  view.getTopAlbums();
  view.getTopArtists();
  view.recentlyFaned();
  view.recentlyLoved();

  $(document).ajaxStop(function (event, request, settings ) {
    $('#recentlyLiked').append($('.recentlyLiked tr').detach().sort(function (a, b) {
      return app.compareStrings($(a).attr('data-created'), $(b).attr('data-created'));
    }));
    $('#recentlyLikedLoader').hide();
  });
});<commit_msg>Fixed app.initChart to view.initChart in profile.js.<commit_after>$.extend(view, {
  // Get listening by year.
  getListeningHistory: function (type) {
    view.initChart();
    if (type == '%w') {
      var where = 'DATE_FORMAT(<?=TBL_listening?>.`date`, \'' + type + '\') IS NOT NULL';
    }
    else if (type == '%Y%m') {
      var where = 'DATE_FORMAT(<?=TBL_listening?>.`date`, \'%m\') != \'00\'';
    }
    else {
      var where = 'DATE_FORMAT(<?=TBL_listening?>.`date`, \'' + type + '\') != \'00\'';
    }
    $.ajax({
      type:'GET',
      dataType:'json',
      url:'/api/listener/get',
      data:{
        group_by:'DATE_FORMAT(<?=TBL_listening?>.`date`, \'' + type + '\')',
        limit:200,
        order_by:'DATE_FORMAT(<?=TBL_listening?>.`date`, \'' + type + '\') ASC',
        select:'DATE_FORMAT(<?=TBL_listening?>.`date`, \'' + type + '\') as `bar_date`',
        username:'<?php echo !empty($username) ? $username: ''?>',
        where:where
      },
      statusCode:{
        200: function (data) { // 200 OK
          $.ajax({
            type:'POST',
            url:'/ajax/barChart',
            data:{
              json_data:data,
              type:type
            },
            success: function (data) {
              $('#historyLoader').hide();
              $('#history').html(data).hide();
              app.chart.xAxis[0].setCategories(view.categories, false);
              app.chart.series[0].setData(view.chart_data, true);
            }
          });
        },
        204: function () { // 204 No Content
          $('#historyLoader').hide();
          $('#history').html('<?=ERR_NO_RESULTS?>');
        },
        400: function (data) {alert('400 Bad Request')}
      }
    });
  },
  // Get recent listenings.
  getRecentListenings: function (isFirst, callback) {
    if (isFirst != true) {
      $('#recentlyListenedLoader2').show();
    }
    $.ajax({
      type:'GET',
      dataType:'json',
      url:'/api/listening/get',
      data:{
        limit:10,
        username:'<?php echo !empty($username) ? $username: ''?>'
      },
      statusCode:{
        200: function (data) { // 200 OK
          $.ajax({
            type:'POST',url:'/ajax/chartTable',
            data:{
              json_data:data,
              hide:{
                del:true
              }
            },
            success: function (data) {
              $('#recentlyListenedLoader2').hide();
              $('#recentlyListenedLoader').hide();
              $('#recentlyListened').html(data);
            }
          })
        },
        204: function () { // 204 No Content
          $('#recentlyListenedLoader').hide();
          $('#recentlyListened').html('<?=ERR_NO_RESULTS?>');
        },
        400: function (data) {alert('400 Bad Request')}
      }
    });
  },
  // Get top albums.
  getTopAlbums: function () {
    $.ajax({
      type:'GET',
      dataType:'json',
      url:'/api/album/get',
      data:{
        limit:10,
        lower_limit:'<?=date('Y-m-d', ($interval == 'overall') ? 0 : time() - ($interval * 24 * 60 * 60))?>',
        username:'<?php echo !empty($username) ? $username: ''?>'
      },
      statusCode:{
        200: function (data) {
          $.ajax({
            type:'POST',
            url:'/ajax/albumList/124',
            data:{
              json_data:data,
            },
            success: function (data) {
              $('#topAlbumLoader').hide();
              $('#topAlbum').html(data);
            },
            complete: function () {
              setTimeout(view.getTopAlbums, 60 * 10 * 1000);
            }
          });
        },
        204: function () { // 204 No Content
          $('#topAlbumLoader').hide();
          $('#topAlbum').html('<?=ERR_NO_RESULTS?>');
        }
      }
    });
  },
  // Get top artists.
  getTopArtists: function () {
    $.ajax({
      type:'GET',
      dataType:'json',
      url:'/api/artist/get',
      data:{
        limit:10,
        lower_limit:'<?=date('Y-m-d', ($interval == 'overall') ? 0 : time() - ($interval * 24 * 60 * 60))?>',
        username:'<?php echo !empty($username) ? $username: ''?>'
      },
      statusCode:{
        200: function (data) {
          $.ajax({
            type:'POST',
            url:'/ajax/artistList/124',
            data:{
              json_data:data,
            },
            success: function (data) {
              $('#topArtistLoader').hide();
              $('#topArtist').html(data);
            },
            complete: function () {
              setTimeout(view.getTopArtists, 60 * 10 * 1000);
            }
          });
        },
        204: function () { // 204 No Content
          $('#topArtistLoader').hide();
          $('#topArtist').html('<?=ERR_NO_RESULTS?>');
        }
      }
    });
  },
  recentlyFaned: function () {
    $.ajax({
      type:'GET',
      dataType:'json',
      url:'/api/fan/get',
      data:{
        limit:20,
        username:'<?php echo !empty($username) ? $username: ''?>'
      },
      statusCode:{
        200: function (data) {
          $.ajax({
            type:'POST',
            url:'/ajax/likeTable',
            data:{
              json_data:data,
              hide:{
                rank:true,
                user:true
              }
            },
            success: function (data) {
              $('#recentlyFanedLoader').hide();
              $('#recentlyFaned').html(data);
            }
          });
        }
      }
    });
  },
  recentlyLoved: function () {
    $.ajax({
      type:'GET',
      dataType:'json',
      url:'/api/love/get',
      data:{
        limit:20,
        username:'<?php echo !empty($username) ? $username: ''?>'
      },
      statusCode:{
        200: function (data) {
          $.ajax({
            type:'POST',
            url:'/ajax/likeTable',
            data:{
              json_data:data,
              hide:{
                rank:true,
                user:true
              }
            },
            success: function (data) {
              $('#recentlyLovedLoader').hide();
              $('#recentlyLoved').html(data);
            }
          });
        }
      }
    });
  }
});

$(document).ready(function () {
  view.getListeningHistory('%Y');
  view.getRecentListenings();
  view.getTopAlbums();
  view.getTopArtists();
  view.recentlyFaned();
  view.recentlyLoved();

  $(document).ajaxStop(function (event, request, settings ) {
    $('#recentlyLiked').append($('.recentlyLiked tr').detach().sort(function (a, b) {
      return app.compareStrings($(a).attr('data-created'), $(b).attr('data-created'));
    }));
    $('#recentlyLikedLoader').hide();
  });
});<|endoftext|>


<commit_before>pc.extend(pc, function () {
    /**
     * @component
     * @name pc.SoundComponent
     * @class The Sound Component controls playback of {@link pc.Sound}s.
     * @description Create a new Sound Component
     * @param {pc.SoundComponentSystem} system The ComponentSystem that created this Component
     * @param {pc.Entity} entity The entity that the Component is attached to
     * @extends pc.Component
     * @property {Number} volume The volume modifier to play the audio with. In range 0-1.
     * @property {Number} pitch The pitch modifier to play the audio with. Must be larger than 0.01
     * @property {Boolean} positional If true the audio will play back at the location of the Entity in space, so the audio will be affect by the position of the {@link pc.AudioListenerComponent}.
     * @property {String} distanceModel Determines which algorithm to use to reduce the volume of the audio as it moves away from the listener. Can be one of 'linear', 'inverse' or 'exponential'. Default is 'inverse'.
     * @property {Number} refDistance The reference distance for reducing volume as the sound source moves further from the listener.
     * @property {Number} maxDistance The maximum distance from the listener at which audio falloff stops. Note the volume of the audio is not 0 after this distance, but just doesn't fall off anymore.
     * @property {Number} rollOffFactor The factor used in the falloff equation.
     * @property {Object} slots A dictionary that contains the {@link pc.SoundSlot}s managed by this Component.
     */

    var SoundComponent = function (system, entity) {
        this.on('set_slots', this.onSetSlots, this);
        this.on('set_volume', this.onSetVolume, this);
        this.on('set_pitch', this.onSetPitch, this);
        this.on("set_refDistance", this.onSetRefDistance, this);
        this.on("set_maxDistance", this.onSetMaxDistance, this);
        this.on("set_rollOffFactor", this.onSetRollOffFactor, this);
        this.on("set_distanceModel", this.onSetDistanceModel, this);
        this.on("set_positional", this.onSetPositional, this);
    };

    SoundComponent = pc.inherits(SoundComponent, pc.Component);

    pc.extend(SoundComponent.prototype, {
        onSetSlots: function (name, oldValue, newValue) {
            // convert data to slots
            for (var key in newValue) {
                if (! (newValue[key] instanceof pc.SoundSlot)) {
                    this.data.slots[key] = new pc.SoundSlot(this, key, newValue[key]);
                }
            }

            // call onEnable in order to start autoPlay slots
            if (this.enabled && this.entity.enabled)
                this.onEnable();
        },

        onSetVolume: function (name, oldValue, newValue) {
            var slots = this.data.slots;
            for (var key in slots) {
                var slot = slots[key];
                // change volume of non-overlapping instances
                if (! slot.overlap) {
                    var instances = slot.instances;
                    for (var i = 0, len = instances.length; i < len; i++) {
                        instances[i].volume = slot.volume * newValue;
                    }
                }
            }
        },

        onSetPitch: function (name, oldValue, newValue) {
            var slots = this.data.slots;
            for (var key in slots) {
                var slot = slots[key];
                // change pitch of non-overlapping instances
                if (! slot.overlap) {
                    var instances = slot.instances;
                    for (var i = 0, len = instances.length; i < len; i++) {
                        instances[i].pitch = slot.pitch * newValue;
                    }
                }
            }
        },

        onSetRefDistance: function (name, oldValue, newValue) {
            var slots = this.data.slots;
            for (var key in slots) {
                var slot = slots[key];
                // change refDistance of non-overlapping instances
                if (! slot.overlap) {
                    var instances = slot.instances;
                    for (var i = 0, len = instances.length; i < len; i++) {
                        instances[i].refDistance = newValue;
                    }
                }
            }
        },

        onSetMaxDistance: function (name, oldValue, newValue) {
            var slots = this.data.slots;
            for (var key in slots) {
                var slot = slots[key];
                // change maxDistance of non-overlapping instances
                if (! slot.overlap) {
                    var instances = slot.instances;
                    for (var i = 0, len = instances.length; i < len; i++) {
                        instances[i].maxDistance = newValue;
                    }
                }
            }
        },

        onSetRollOffFactor: function (name, oldValue, newValue) {
            var slots = this.data.slots;
            for (var key in slots) {
                var slot = slots[key];
                // change rollOffFactor of non-overlapping instances
                if (! slot.overlap) {
                    var instances = slot.instances;
                    for (var i = 0, len = instances.length; i < len; i++) {
                        instances[i].rollOffFactor = newValue;
                    }
                }
            }
        },

        onSetDistanceModel: function (name, oldValue, newValue) {
            var slots = this.data.slots;
            for (var key in slots) {
                var slot = slots[key];
                // change distanceModel of non-overlapping instances
                if (! slot.overlap) {
                    var instances = slot.instances;
                    for (var i = 0, len = instances.length; i < len; i++) {
                        instances[i].distanceModel = newValue;
                    }
                }
            }
        },

        onSetPositional: function (name, oldValue, newValue) {
            var slots = this.data.slots;
            for (var key in slots) {
                var slot = slots[key];
                // recreate non overlapping sounds
                if (! slot.overlap) {
                    var instances = slot.instances;
                    for (var i = 0, len = instances.length; i < len; i++) {
                        var isPlaying = instances[i].isPlaying;
                        var currentTime = instances[i].currentTime;
                        if (isPlaying)
                            instances[i].stop();

                        instances[i] = slot._createInstance();
                        if (isPlaying) {
                            instances[i].play();
                            instances[i].currentTime = currentTime;
                        }
                    }
                }
            }
        },

        onEnable: function () {
            SoundComponent._super.onEnable.call(this);

            var slots = this.data.slots;
            var playingBeforeDisable = this.data.playingBeforeDisable;

            for (var key in slots) {
                var slot = slots[key];
                // play if autoPlay is true or
                // if the slot was paused when the component
                // got disabled
                if (slot.autoPlay && slot.isStopped) {
                    slot.play();
                } else if (playingBeforeDisable[key]) {
                    slot.resume();
                } else if (!slot.isLoaded) {
                    // start loading slots
                    slot.load();
                }
            }
        },

        onDisable: function () {
            SoundComponent._super.onDisable.call(this);

            var slots = this.data.slots;
            var playingBeforeDisable = {};
            for (var key in slots) {
                // pause non-overlapping sounds
                if (! slots[key].overlap) {
                    if (slots[key].isPlaying) {
                        slots[key].pause();
                        // remember sounds playing when we disable
                        // so we can resume them on enable
                        playingBeforeDisable[key] = true;
                    }
                }
            }

            this.data.playingBeforeDisable = playingBeforeDisable;
        },

        /**
         * @function
         * @param {[type]} name    [description]
         * @param {[type]} options [description]
         */

        /**
         * @function
         * @name pc.SoundComponent#addTime
         * @description Create a {@link pc.SoundSlot}
         * @param {String} name The name of the slot
         * @param {Object} options Settings for the slot
         * @param {Number} [options.volume=1] The playback volume, between 0 and 1.
         * @param {Number} [options.pitch=1] The relative pitch, default of 1, plays at normal pitch.
         * @param {Boolean} [options.loop=false] If true the sound will restart when it reaches the end.
         * @param {Number} [options.startTime=0] The start time from which the sound will start playing.
         * @param {Number} [options.duration=null] The duration of the sound that the slot will play starting from startTime.
         * @param {Boolean} [options.overlap=false] If true then sounds played from slot will be played independently of each other. Otherwise the slot will first stop the current sound before starting the new one.
         * @param {Boolean} [options.autoPlay=false] If true the slot will start playing as soon as its audio asset is loaded.
         * @param {Number} [options.asset=null] The asset id of the audio asset that is going to be played by this slot.
         * @returns {pc.SoundSlot} The new slot.
         */
        addSlot: function (name, options) {
            var slots = this.data.slots;
            if (slots[name]) {
                logERROR('A sound slot with name ' + name + ' already exists on Entity ' + this.entity.getPath());
                return slots[name];
            }

            var slot = new pc.SoundSlot(this, name, options);
            slots[name] = slot;

            if (slot.autoPlay && this.enabled && this.entity.enabled) {
                slot.play();
            }

            return slot;
        },

        /**
         * @function
         * @name pc.SoundComponent@removeSlot
         * @description Removes the {@link pc.SoundSlot} with the specified name.
         * @param  {String} name The name of the slot
         */
        removeSlot: function (name) {
            var slots = this.data.slots;
            if (slots[name]) {
                slots[name].stop();
                delete slots[name];
            }
        },

        /**
         * @function
         * @name pc.SoundComponent@slot
         * @description Returns the slot with the specified name
         * @param  {String} name The name of the slot
         * @returns {pc.SoundSlot} The slot
         */
        slot: function (name) {
            return this.data.slots[name];
        },

        /**
        * @function
        * @name pc.SoundComponent#play
        * @description Begin playing the sound slot with the specified name. The slot will restart playing if it is already playing unless the overlap field is true in which case a new sound will be created and played.
        * @param {String} name The name of the {@link pc.SoundSlot} to play
        * @returns {pc.SoundInstance} The sound instance that will be played.
        */
        play: function (name) {
            if (!this.enabled || !this.entity.enabled) {
                return null;
            }

            var slot = this.slots[name];
            if (! slot) {
                logWARNING('Trying to play sound slot with name ' + name + ' which does not exist');
                return null;
            }

            return slot.play();
        },

        /**
        * @function
        * @name pc.SoundComponent#pause
        * @description Pause playback of the slot with the specified name. If the name is undefined then all slots currently played will be paused. The slots can be resumed by calling {@link pc.SoundComponent#resume}.
        * @param {String} [name] The name of the slot to pause. Leave undefined to pause everything.
        */
        pause: function (name) {
            var slot;
            var slots = this.data.slots;

            if (name) {
                slot = slots[name];
                if (! slot) {
                    logWARNING('Trying to pause sound slot with name ' + name + ' which does not exist');
                    return;
                }

                slot.pause();

                this.fire('pause', slot);
            } else {
                for (var key in slots) {
                    slots[key].pause();
                }

                this.fire('pause');
            }
        },

        /**
        * @function
        * @name pc.SoundComponent#resume
        * @description Resume playback of the sound slot with the specified name if it's paused. If no name is specified all slots will be resumed.
        * @param {String} name The name of the slot to resume. Leave undefined to resume everything.
        */
        resume: function (name) {
            var slot;
            var slots = this.data.slots;

            if (name) {
                slot = slots[name];
                if (! slot) {
                    logWARNING('Trying to resume sound slot with name ' + name + ' which does not exist');
                    return;
                }

                if (slot.isPaused) {
                    slot.resume();
                }
            } else {
                for (var key in slots) {
                    slots[key].resume();
                }

                this.fire('resume');
            }
        },

        /**
        * @function
        * @name pc.SoundComponent#stop
        * @description Stop playback of the sound slot with the specified name if it's paused. If no name is specified all slots will be stopped.
        * @param {String} name The name of the slot to stop. Leave undefined to stop everything.
        */
        stop: function (name) {
            var slot;
            var slots = this.data.slots;

            if (name) {
                slot = slots[name];
                if (! slot) {
                    logWARNING('Trying to stop sound slot with name ' + name + ' which does not exist');
                    return;
                }

                slot.stop();

                this.fire('stop', slot);
            } else {
                for (var key in slots) {
                    slots[key].stop();
                }

                this.fire('stop');
            }
        }
    });

    return {
        SoundComponent: SoundComponent
    };
}());
<commit_msg>Make error in addSlot a warning and return null<commit_after>pc.extend(pc, function () {
    /**
     * @component
     * @name pc.SoundComponent
     * @class The Sound Component controls playback of {@link pc.Sound}s.
     * @description Create a new Sound Component
     * @param {pc.SoundComponentSystem} system The ComponentSystem that created this Component
     * @param {pc.Entity} entity The entity that the Component is attached to
     * @extends pc.Component
     * @property {Number} volume The volume modifier to play the audio with. In range 0-1.
     * @property {Number} pitch The pitch modifier to play the audio with. Must be larger than 0.01
     * @property {Boolean} positional If true the audio will play back at the location of the Entity in space, so the audio will be affect by the position of the {@link pc.AudioListenerComponent}.
     * @property {String} distanceModel Determines which algorithm to use to reduce the volume of the audio as it moves away from the listener. Can be one of 'linear', 'inverse' or 'exponential'. Default is 'inverse'.
     * @property {Number} refDistance The reference distance for reducing volume as the sound source moves further from the listener.
     * @property {Number} maxDistance The maximum distance from the listener at which audio falloff stops. Note the volume of the audio is not 0 after this distance, but just doesn't fall off anymore.
     * @property {Number} rollOffFactor The factor used in the falloff equation.
     * @property {Object} slots A dictionary that contains the {@link pc.SoundSlot}s managed by this Component.
     */

    var SoundComponent = function (system, entity) {
        this.on('set_slots', this.onSetSlots, this);
        this.on('set_volume', this.onSetVolume, this);
        this.on('set_pitch', this.onSetPitch, this);
        this.on("set_refDistance", this.onSetRefDistance, this);
        this.on("set_maxDistance", this.onSetMaxDistance, this);
        this.on("set_rollOffFactor", this.onSetRollOffFactor, this);
        this.on("set_distanceModel", this.onSetDistanceModel, this);
        this.on("set_positional", this.onSetPositional, this);
    };

    SoundComponent = pc.inherits(SoundComponent, pc.Component);

    pc.extend(SoundComponent.prototype, {
        onSetSlots: function (name, oldValue, newValue) {
            // convert data to slots
            for (var key in newValue) {
                if (! (newValue[key] instanceof pc.SoundSlot)) {
                    this.data.slots[key] = new pc.SoundSlot(this, key, newValue[key]);
                }
            }

            // call onEnable in order to start autoPlay slots
            if (this.enabled && this.entity.enabled)
                this.onEnable();
        },

        onSetVolume: function (name, oldValue, newValue) {
            var slots = this.data.slots;
            for (var key in slots) {
                var slot = slots[key];
                // change volume of non-overlapping instances
                if (! slot.overlap) {
                    var instances = slot.instances;
                    for (var i = 0, len = instances.length; i < len; i++) {
                        instances[i].volume = slot.volume * newValue;
                    }
                }
            }
        },

        onSetPitch: function (name, oldValue, newValue) {
            var slots = this.data.slots;
            for (var key in slots) {
                var slot = slots[key];
                // change pitch of non-overlapping instances
                if (! slot.overlap) {
                    var instances = slot.instances;
                    for (var i = 0, len = instances.length; i < len; i++) {
                        instances[i].pitch = slot.pitch * newValue;
                    }
                }
            }
        },

        onSetRefDistance: function (name, oldValue, newValue) {
            var slots = this.data.slots;
            for (var key in slots) {
                var slot = slots[key];
                // change refDistance of non-overlapping instances
                if (! slot.overlap) {
                    var instances = slot.instances;
                    for (var i = 0, len = instances.length; i < len; i++) {
                        instances[i].refDistance = newValue;
                    }
                }
            }
        },

        onSetMaxDistance: function (name, oldValue, newValue) {
            var slots = this.data.slots;
            for (var key in slots) {
                var slot = slots[key];
                // change maxDistance of non-overlapping instances
                if (! slot.overlap) {
                    var instances = slot.instances;
                    for (var i = 0, len = instances.length; i < len; i++) {
                        instances[i].maxDistance = newValue;
                    }
                }
            }
        },

        onSetRollOffFactor: function (name, oldValue, newValue) {
            var slots = this.data.slots;
            for (var key in slots) {
                var slot = slots[key];
                // change rollOffFactor of non-overlapping instances
                if (! slot.overlap) {
                    var instances = slot.instances;
                    for (var i = 0, len = instances.length; i < len; i++) {
                        instances[i].rollOffFactor = newValue;
                    }
                }
            }
        },

        onSetDistanceModel: function (name, oldValue, newValue) {
            var slots = this.data.slots;
            for (var key in slots) {
                var slot = slots[key];
                // change distanceModel of non-overlapping instances
                if (! slot.overlap) {
                    var instances = slot.instances;
                    for (var i = 0, len = instances.length; i < len; i++) {
                        instances[i].distanceModel = newValue;
                    }
                }
            }
        },

        onSetPositional: function (name, oldValue, newValue) {
            var slots = this.data.slots;
            for (var key in slots) {
                var slot = slots[key];
                // recreate non overlapping sounds
                if (! slot.overlap) {
                    var instances = slot.instances;
                    for (var i = 0, len = instances.length; i < len; i++) {
                        var isPlaying = instances[i].isPlaying;
                        var currentTime = instances[i].currentTime;
                        if (isPlaying)
                            instances[i].stop();

                        instances[i] = slot._createInstance();
                        if (isPlaying) {
                            instances[i].play();
                            instances[i].currentTime = currentTime;
                        }
                    }
                }
            }
        },

        onEnable: function () {
            SoundComponent._super.onEnable.call(this);

            var slots = this.data.slots;
            var playingBeforeDisable = this.data.playingBeforeDisable;

            for (var key in slots) {
                var slot = slots[key];
                // play if autoPlay is true or
                // if the slot was paused when the component
                // got disabled
                if (slot.autoPlay && slot.isStopped) {
                    slot.play();
                } else if (playingBeforeDisable[key]) {
                    slot.resume();
                } else if (!slot.isLoaded) {
                    // start loading slots
                    slot.load();
                }
            }
        },

        onDisable: function () {
            SoundComponent._super.onDisable.call(this);

            var slots = this.data.slots;
            var playingBeforeDisable = {};
            for (var key in slots) {
                // pause non-overlapping sounds
                if (! slots[key].overlap) {
                    if (slots[key].isPlaying) {
                        slots[key].pause();
                        // remember sounds playing when we disable
                        // so we can resume them on enable
                        playingBeforeDisable[key] = true;
                    }
                }
            }

            this.data.playingBeforeDisable = playingBeforeDisable;
        },

        /**
         * @function
         * @param {[type]} name    [description]
         * @param {[type]} options [description]
         */

        /**
         * @function
         * @name pc.SoundComponent#addTime
         * @description Create a {@link pc.SoundSlot}
         * @param {String} name The name of the slot
         * @param {Object} options Settings for the slot
         * @param {Number} [options.volume=1] The playback volume, between 0 and 1.
         * @param {Number} [options.pitch=1] The relative pitch, default of 1, plays at normal pitch.
         * @param {Boolean} [options.loop=false] If true the sound will restart when it reaches the end.
         * @param {Number} [options.startTime=0] The start time from which the sound will start playing.
         * @param {Number} [options.duration=null] The duration of the sound that the slot will play starting from startTime.
         * @param {Boolean} [options.overlap=false] If true then sounds played from slot will be played independently of each other. Otherwise the slot will first stop the current sound before starting the new one.
         * @param {Boolean} [options.autoPlay=false] If true the slot will start playing as soon as its audio asset is loaded.
         * @param {Number} [options.asset=null] The asset id of the audio asset that is going to be played by this slot.
         * @returns {pc.SoundSlot} The new slot.
         */
        addSlot: function (name, options) {
            var slots = this.data.slots;
            if (slots[name]) {
                logWARNING('A sound slot with name ' + name + ' already exists on Entity ' + this.entity.getPath());
                return null;
            }

            var slot = new pc.SoundSlot(this, name, options);
            slots[name] = slot;

            if (slot.autoPlay && this.enabled && this.entity.enabled) {
                slot.play();
            }

            return slot;
        },

        /**
         * @function
         * @name pc.SoundComponent@removeSlot
         * @description Removes the {@link pc.SoundSlot} with the specified name.
         * @param  {String} name The name of the slot
         */
        removeSlot: function (name) {
            var slots = this.data.slots;
            if (slots[name]) {
                slots[name].stop();
                delete slots[name];
            }
        },

        /**
         * @function
         * @name pc.SoundComponent@slot
         * @description Returns the slot with the specified name
         * @param  {String} name The name of the slot
         * @returns {pc.SoundSlot} The slot
         */
        slot: function (name) {
            return this.data.slots[name];
        },

        /**
        * @function
        * @name pc.SoundComponent#play
        * @description Begin playing the sound slot with the specified name. The slot will restart playing if it is already playing unless the overlap field is true in which case a new sound will be created and played.
        * @param {String} name The name of the {@link pc.SoundSlot} to play
        * @returns {pc.SoundInstance} The sound instance that will be played.
        */
        play: function (name) {
            if (!this.enabled || !this.entity.enabled) {
                return null;
            }

            var slot = this.slots[name];
            if (! slot) {
                logWARNING('Trying to play sound slot with name ' + name + ' which does not exist');
                return null;
            }

            return slot.play();
        },

        /**
        * @function
        * @name pc.SoundComponent#pause
        * @description Pause playback of the slot with the specified name. If the name is undefined then all slots currently played will be paused. The slots can be resumed by calling {@link pc.SoundComponent#resume}.
        * @param {String} [name] The name of the slot to pause. Leave undefined to pause everything.
        */
        pause: function (name) {
            var slot;
            var slots = this.data.slots;

            if (name) {
                slot = slots[name];
                if (! slot) {
                    logWARNING('Trying to pause sound slot with name ' + name + ' which does not exist');
                    return;
                }

                slot.pause();

                this.fire('pause', slot);
            } else {
                for (var key in slots) {
                    slots[key].pause();
                }

                this.fire('pause');
            }
        },

        /**
        * @function
        * @name pc.SoundComponent#resume
        * @description Resume playback of the sound slot with the specified name if it's paused. If no name is specified all slots will be resumed.
        * @param {String} name The name of the slot to resume. Leave undefined to resume everything.
        */
        resume: function (name) {
            var slot;
            var slots = this.data.slots;

            if (name) {
                slot = slots[name];
                if (! slot) {
                    logWARNING('Trying to resume sound slot with name ' + name + ' which does not exist');
                    return;
                }

                if (slot.isPaused) {
                    slot.resume();
                }
            } else {
                for (var key in slots) {
                    slots[key].resume();
                }

                this.fire('resume');
            }
        },

        /**
        * @function
        * @name pc.SoundComponent#stop
        * @description Stop playback of the sound slot with the specified name if it's paused. If no name is specified all slots will be stopped.
        * @param {String} name The name of the slot to stop. Leave undefined to stop everything.
        */
        stop: function (name) {
            var slot;
            var slots = this.data.slots;

            if (name) {
                slot = slots[name];
                if (! slot) {
                    logWARNING('Trying to stop sound slot with name ' + name + ' which does not exist');
                    return;
                }

                slot.stop();

                this.fire('stop', slot);
            } else {
                for (var key in slots) {
                    slots[key].stop();
                }

                this.fire('stop');
            }
        }
    });

    return {
        SoundComponent: SoundComponent
    };
}());
<|endoftext|>


<commit_before>'use babel'
/** @jsx etch.dom */

import etch from 'etch'

export default class TestFailures {
  constructor (props) {
    this.props = props

    this.itemsToRender = new Set()
    this.props.runner.on('fail', this.onTestFail.bind(this))

    etch.initialize(this)
  }

  update (props) {
    this.props = props
    etch.update(this)
  }

  onTestFail (test) {
    this.itemsToRender.add(test)
    while (test.parent) {
      this.itemsToRender.add(test.parent)
      test = test.parent
    }
    etch.update(this)
  }

  render () {
    return (

        {this.props.rootSuite.suites.map(this.renderTopLevelSuite.bind(this))}

    )
  }

  renderTopLevelSuite (suite) {
    if (this.itemsToRender.has(suite)) {
      return (

          {suite.title}

            {this.renderTestFailures(suite.tests)}
            {suite.suites.map(this.renderSubSuite.bind(this))}

      )
    }
  }

  renderTestFailures (tests) {
    return tests.map(test => {
      if (this.itemsToRender.has(test)) {
        return this.renderFailedTest(test)
      }
    })
  }

  renderFailedTest (test) {
    return (

{test.title}

{test.err.message}

{test.err.stack.split('\n').map(cs =>
{cs}
)}

    )
  }

  renderSubSuite (suite) {
    if (this.itemsToRender.has(suite)) {
      return (

          {suite.title}

            {this.renderTestFailures(suite.tests)}
            {suite.suites.map(this.renderSubSuite.bind(this))}

      )
    }
  }
}
<commit_msg>Show failures from before/after hooks<commit_after>'use babel'
/** @jsx etch.dom */

import etch from 'etch'

export default class TestFailures {
  constructor (props) {
    this.props = props

    this.itemsToRender = new Set()
    this.props.runner.on('fail', this.onTestFail.bind(this))

    etch.initialize(this)
  }

  update (props) {
    this.props = props
    etch.update(this)
  }

  onTestFail (test) {
    this.itemsToRender.add(test)
    while (test.parent) {
      this.itemsToRender.add(test.parent)
      test = test.parent
    }
    etch.update(this)
  }

  render () {
    return (

        {this.props.rootSuite.suites.map(this.renderTopLevelSuite.bind(this))}

    )
  }

  renderTopLevelSuite (suite) {
    if (this.itemsToRender.has(suite)) {
      return (

          {suite.title}

            {this.renderSuiteFailures(suite)}

      )
    }
  }

  renderSuiteFailures (suite) {
    return (

        {this.renderTestFailures(suite._beforeAll)}
        {this.renderTestFailures(suite._beforeEach)}
        {this.renderTestFailures(suite._afterEach)}
        {this.renderTestFailures(suite._afterAll)}
        {this.renderTestFailures(suite.tests)}
        {suite.suites.map(this.renderSubSuite.bind(this))}

    )
  }

  renderTestFailures (tests) {
    return tests.map(test => {
      if (this.itemsToRender.has(test)) {
        return this.renderFailedTest(test)
      }
    })
  }

  renderFailedTest (test) {
    return (

{test.title}

{test.err.message}

{test.err.stack.split('\n').map(cs =>
{cs}
)}

    )
  }

  renderSubSuite (suite) {
    if (this.itemsToRender.has(suite)) {
      return (

          {suite.title}

            {this.renderSuiteFailures(suite)}

      )
    }
  }
}
<|endoftext|>


<commit_before>
// Highlight some node. This ought to be moved to some common JS
$.fn.animateHighlight = function(highlightColor, duration) {
    var highlightBg = highlightColor || "#FFFF9C";
    var animateMs = duration || 1500;
    var originalBg = this.css("backgroundColor");
    this.stop().css("background-color",
    highlightBg).animate({backgroundColor:
        originalBg}, animateMs);
};

// wait for the DOM to be loaded
$(document).ready(function() {
    // first hide the bookmarklet
    // $('#bookmarklet').hide();

    // bind 'myForm' and provide a simple callback function
    var options = {
        target:     '#divToUpdate',
        dataType:   'json',
        url:        '/api/signin/',
        error:      function(ctx) {
            var errors = JSON.parse(ctx.responseText).errors;

            if ('email' in errors) {
                $('#id_email').animateHighlight("#dd0000", 1000);
            }

            if ('name' in errors) {
                $('#id_name').animateHighlight("#dd0000", 1000);
            }

            if ('confirm_public' in errors) {
                $('label.confirm-label').animateHighlight("#dd0000", 1000);
            }
        },
        success:    function(responseJSON, statusText, xhr, formElem) {
            $('#bookmarklet-js').attr('href',
            "javascript:document.getElementsByTagName('body')[0].appendChild(document.createElement('script')).setAttribute('src','"+responseJSON['url']+"');");

            // Show the new bookmarklet
            $('#bookmarklet').show();

            var dialog = new BootstrapDialog({
                title : $('
Drag this to your bookmark bar
'),
                content: $(" Open Access Button "),
                buttons :   [{
                    label : 'OK',
                    onclick :
                    function(dialog){
                        dialog.close();
                    }
                }]
            });
            dialog.open();

        }
    };
    $('#form-bookmarklet').ajaxForm(options);
});
<commit_msg>got rid of BootstrapDialog<commit_after>
// Highlight some node. This ought to be moved to some common JS
$.fn.animateHighlight = function(highlightColor, duration) {
    var highlightBg = highlightColor || "#FFFF9C";
    var animateMs = duration || 1500;
    var originalBg = this.css("backgroundColor");
    this.stop().css("background-color",
    highlightBg).animate({backgroundColor:
        originalBg}, animateMs);
};

// wait for the DOM to be loaded
$(document).ready(function() {
    // first hide the bookmarklet
    // $('#bookmarklet').hide();

    // bind 'myForm' and provide a simple callback function
    var options = {
        target:     '#divToUpdate',
        dataType:   'json',
        url:        '/api/signin/',
        error:      function(ctx) {
            var errors = JSON.parse(ctx.responseText).errors;

            if ('email' in errors) {
                $('#id_email').animateHighlight("#dd0000", 1000);
            }

            if ('name' in errors) {
                $('#id_name').animateHighlight("#dd0000", 1000);
            }

            if ('confirm_public' in errors) {
                $('label.confirm-label').animateHighlight("#dd0000", 1000);
            }
        },
        success:    function(responseJSON, statusText, xhr, formElem) {
            $('#bookmarklet-js').attr('href',
            "javascript:document.getElementsByTagName('body')[0].appendChild(document.createElement('script')).setAttribute('src','"+responseJSON['url']+"');");

            // Show the new bookmarklet
            $('#bookmarklet').show();
        }
    };
    $('#form-bookmarklet').ajaxForm(options);
});
<|endoftext|>


<commit_before>module.exports.getSql = function QueryBuilder$getSql(query, filters) {
    filters = filters || [];
    return filters.reduce(function(sql, filter) {
        return filter.sql(sql);
    }, query);
};<commit_msg>Style<commit_after>module.exports.getSql = function QueryBuilder$getSql(query, filters) {
    filters = filters || [];
    return filters.reduce(function(sql, filter) {
        return filter.sql(sql);
    }, query);
};
<|endoftext|>