<commit_before>/**
 This Source Code is licensed under the MIT license. If a copy of the
 MIT-license was not distributed with this file, You can obtain one at:
 http://opensource.org/licenses/mit-license.html.

 @author: Tom Clement (tjclement)
 @license MIT
 @copyright Bizboard, 2015

 */

import {DataSource}         from '../../core/DataSource';
import ObjectHelper         from '../../utils/objectHelper';
import {Context}            from 'arva-context/Context';
import EventEmitter         from 'eventemitter3';

export default
class PrioritisedArray extends Array {

    /* Extending Array does not work fluently yet. The length property always returns 0,
     * regardless of how many entries are in the array. We'll override the length prop to determine
     * the amount of enumerable properties in our PrioritisedArray instead of using the built-in length property.
     */
    get length() {
        return Object.keys(this).length
    }

    set length(value) {
    }

    /**
     *
     * @param {Function} dataType
     * @param {DataSource} dataSource
     * @param {Snapshot} dataSnapshot
     */
    constructor(dataType, dataSource = null, dataSnapshot = null) {
        super();
        /**** Callbacks ****/
        this._valueChangedCallback = null;

        /**** Private properties ****/
        this._dataType = dataType;
        this._dataSource = dataSource;
        this._isBeingReordered = false; /* Flag to determine when we're reordering so we don't listen to move updates */
        this._eventEmitter = new EventEmitter();

        /* Bind all local methods to the current object instance, so we can refer to "this"
         * in the methods as expected, even when they're called from event handlers.        */
        ObjectHelper.bindAllMethods(this, this);

        /* Hide all private properties (starting with '_') and methods from enumeration,
         * so when you do for( in ), only actual data properties show up. */
        ObjectHelper.hideMethodsAndPrivatePropertiesFromObject(this);

        /* Hide the priority field from enumeration, so we don't save it to the dataSource. */
        ObjectHelper.hidePropertyFromObject(Object.getPrototypeOf(this), 'length');

        /* If no dataSource is given, create own one with guessed path */
        if(!dataSource) {
            let modelName = Object.getPrototypeOf(this).constructor.name;
            let path = modelName; //+ 's';

            // retrieve dataSource from the DI context
            dataSource = Context.getContext().get(DataSource);

            dataSource = dataSource.child(path);
            this._dataSource = dataSource;
        }

        /* If a snapshot is present use it, otherwise generate one by subscribing to the dataSource one time. */
        if (dataSnapshot) {
            this._buildFromSnapshot(dataSnapshot);
        } else {
            this._buildFromDataSource(dataSource);
        }
    }

    /**
     *
     * @param event
     * @param fn
     * @param context
     * @returns {*}
     */
    once(event, fn, context = this) {
        return this.on(event, function(){
            fn.call(context, arguments);
            this.off(event, fn, context);
        }, this);
    }

    /**
     * Subscribes to events emitted by this PrioritisedArray.
     * @param {String} event 'value', 'child_changed', 'child_moved', 'child_removed'
     * @param {Function} fn Function to call when event is emitted.
     * @param {Object} context Object to bind the given callback function to.
     * @returns {*}
     */
    on(event, fn, context) {
        /* If we're already ready, fire immediately */
        if(event === 'ready' && this._dataSource && this._dataSource.ready) { fn.call(context, this); }

        return this._eventEmitter.on(event, fn, context);
    }

    /**
     * Removes subscription to events emitted by this PrioritisedArray. If no fn or context is given, all handlers for
     * the given event are removed. If no parameters are given at all, all event types will have their handlers removed.
     * @param {String} event 'value', 'child_changed', 'child_moved', 'child_removed'
     * @param {Function} fn Function to call when event is emitted.
     * @param {Object} context Object to bind the given callback function to.
     * @returns {*}
     */
    off(event, fn, context) {

        if(event && (fn || context)) {
            this._eventEmitter.removeListener(event, fn, context);
        } else {
            this._eventEmitter.removeAllListeners(event);
        }
    }

    /**
     * Adds a model instance to the rear of the PrioritisedArray
     * @param {Model} model Subclass of Model
     * @returns {*} Same model as the one originally passed as parameter
     */
    add(model) {
        if (model instanceof this._dataType) {
            if (this._findIndexById(model.id) < 0) {
                model.priority = this.length;
                this.push(model);

                if (!model._inheritable) {
                    model.on('value', (modelData) => {
                        this._onChildChanged(modelData);
                    });
                }

            } else {
                /* TODO: change to throw exception */
                console.log('Tried to append an object with the same ID as one already present.');
            }
        }
        /* Let's try to parse the object using property reflection */
        else if(model instanceof Object) {
            // retrieve dataSource from the DI context
            var options = { dataSource: this._dataSource};
            let newModel = new this._dataType(null, model, options);
            this.add(newModel);
        }
        else {
            /* TODO: change to throw exception */
            console.log('Tried to append an object that is not the same type as the one this PrioritisedArray was created with.');
        }

        /* Return model so we can do this: let newModel = PrioArray.add(new Model()); newModel.someProperty = true; */
        return model;
    }

    /**
     * Inserts a model instance at the given position of the PrioritisedArray, and recalculates the priority (position)
     * of all models after the inserted position.
     * @param {Model} model Subclass of Model
     * @param {Number} position Zero-based index where to put the new model instance.
     */
    insertAt(model, position) {
        if (model instanceof this._dataType) {
            this.splice(position, 0, model);
            this._recalculatePriorities(position);
        }
        else {
            /* TODO: change to throw exception */
            console.log('Tried to append an object that is not the same type as the PrioritisedArray was created with.');
        }

        /* Return model so we can do this: let newModel = PrioArray.add(new Model()); newModel.someProperty = true; */
        return model;
    }

    /**
     * Moves a model instance from one position to another.
     * @param {Number} fromPosition Zero-based index of original position
     * @param {Number} toPosition Zero-based index of target position
     */
    move(fromPosition, toPosition) {
        let model = this[fromPosition];
        this.splice(fromPosition, 1);
        this.splice(toPosition, 0, model);
        this._recalculatePriorities();
    }

    /**
     * Removes the model instance at the given position. Does not remove the model from the datasource, to do that
     * call model.remove() directly, or PrioArray[index].remove().
     * @param position
     */
    remove(position) {
        this.splice(position, 1);
    }

    /**
     * Assigns models' priorities based on their position in the PrioritisedArray.
     * @param {Number} start Index to start calculation from, so we don't process unnecessary models.
     * @private
     */
    _recalculatePriorities(start = 0) {
        this._isBeingReordered = true;
        for (let i = start; i < this.length; i++) {
            this[i].priority = i;
        }
        this._isBeingReordered = false;
    }

    /**
     * Interprets all childs of a given snapshot as instances of the given data type for this PrioritisedArray,
     * and attempts to instantiate new model instances based on these sub-snapshots. It adds them to the
     * PrioritisedArray, which also assigns their priority based on their inserted position.
     * @param {Snapshot} dataSnapshot
     * @private
     */
    _buildFromSnapshot(dataSnapshot) {

        let numChildren = dataSnapshot.numChildren(), currentChild = 1;

        /* If there is no data at this point yet, fire a ready event */
        if (numChildren === 0) {
            this._dataSource.ready = true;
            this._eventEmitter.emit('ready');
        }

        dataSnapshot.forEach(
            /** @param {Snapshot} child **/
            function(child){
                /* Create a new instance of the given data type and prefill it with the snapshot data. */
                let options = {dataSnapshot: child};

                /* whenever the ref() is a datasource, we can bind that source to the model.
                 * whenever it's not a datasource, we assume the model should instantiate a new
                  * datasource to bind the model */

                 if (child.ref() instanceof DataSource)
                    options.dataSource = child.ref();
                 else {
                     var rootPath = child.ref().root().toString();
                     options.path = child.ref().toString().replace(rootPath,'/');
                 }

                let newModel = new this._dataType(child.key(), child.val(), options);
                this.add(newModel);

                /* If this is the last child, fire a ready event */
                if(currentChild++ == numChildren){
                    this._dataSource.ready = true;
                    this._eventEmitter.emit('ready');
                }

            }.bind(this));

        if (dataSnapshot.ref() instanceof DataSource &&
            dataSnapshot.ref().inheritable)
            this._eventEmitter.emit('value', this);

        //this._registerCallbacks(this._dataSource);
    }

    /**
     * Clones a dataSource (to not disturb any existing callbacks defined on the original) and uses it
     * to get a dataSnapshot which is used in _buildSnapshot to build our array.
     * @param dataSource
     * @private
     */
    _buildFromDataSource(dataSource) {

        let path = dataSource.path();
        let DataSource = Object.getPrototypeOf(dataSource).constructor;
        let newSource = new DataSource(path);
        newSource.setValueChangedCallback((dataSnapshot) => {
            newSource.removeValueChangedCallback();
            this._buildFromSnapshot(dataSnapshot);
            this._registerCallbacks(newSource);
        });

    }

    _registerCallbacks(dataSource) {
        dataSource.setChildAddedCallback(this._onChildAdded);
        dataSource.setChildMovedCallback(this._onChildMoved);
        if (dataSource.inheritable)
            dataSource.setChildChangedCallback(this._onChildChanged);
        dataSource.setChildRemovedCallback(this._onChildRemoved);
    }

    /**
     * Called by dataSource when a new child is added.
     * @param {Snapshot} snapshot
     * @private
     */
    _onChildAdded(snapshot) {
        let id = snapshot.key();
        var rootPath = snapshot.ref().root().toString();
        let model = this.add(new this._dataType(id, null, {dataSnapshot: snapshot, path: child.ref().toString().replace(rootPath,'/') }));

        this._eventEmitter.emit('child_added', model);
        this._eventEmitter.emit('value', this);
    }

    /**
     *
     */
    _onChildChanged(snapshot) {
        let id = snapshot.key();
        let itemIndex = this._findIndexById(id);
        let changedModel = new this._dataType(id, null, {dataSnapshot: snapshot, dataSource: snapshot.ref() });

        if (!(JSON.stringify(this[itemIndex])===JSON.stringify(changedModel))) {
            this[itemIndex] = changedModel;

            this._eventEmitter.emit('child_changed', changedModel);
            this._eventEmitter.emit('value', this);
        }
    }

    /**
     * Called by dataSource when a child is moved, which changes its priority.
     * @param {Snapshot} snapshot
     * @private
     */
    _onChildMoved(snapshot, prevSiblingId) {
        /* Ignore priority updates whilst we're reordering to avoid floods */
        if (!this._isBeingReordered) {

            let id = snapshot.key();
            let previousPosition = this._findIndexById(id);
            let tempModel = this[previousPosition];
            this.remove(previousPosition);

            let newPosition = this._findIndexById(prevSiblingId)+1;
            this.insertAt(tempModel, newPosition);

            this._recalculatePriorities();

            let model = this[newPosition];

            this._eventEmitter.emit('child_moved', model, previousPosition);
            this._eventEmitter.emit('value', this);
        }
    }

    /**
     * Called by dataSource when a child is removed.
     * @param {Snapshot} oldSnapshot
     * @private
     */
    _onChildRemoved(oldSnapshot) {
        /* TODO: figure out if we can use the snapshot's priority as our array index reliably, to avoid big loops. */
        let id = oldSnapshot.key();
        let position = this._findIndexById(id);
        let model = this[position];

        if (position !== -1) {
            this.remove(position, false);

            this._eventEmitter.emit('child_removed', model);
            this._eventEmitter.emit('value', this);
        }
    }

    /**
     * Searches for the index in the PrioritisedArray of a model that has an id equal to the given id.
     * @param id Id field of the model we're looking for
     * @returns {Number} Zero-based index if found, -1 otherwise
     * @private
     */
    _findIndexById(id) {
        for (let i = 0; i < this.length; i++) {
            if (this[i].id == id) {
                return (i);
            }
        }
        return -1;
    }

}<commit_msg>child needed to be snapshot<commit_after>/**
 This Source Code is licensed under the MIT license. If a copy of the
 MIT-license was not distributed with this file, You can obtain one at:
 http://opensource.org/licenses/mit-license.html.

 @author: Tom Clement (tjclement)
 @license MIT
 @copyright Bizboard, 2015

 */

import {DataSource}         from '../../core/DataSource';
import ObjectHelper         from '../../utils/objectHelper';
import {Context}            from 'arva-context/Context';
import EventEmitter         from 'eventemitter3';

export default
class PrioritisedArray extends Array {

    /* Extending Array does not work fluently yet. The length property always returns 0,
     * regardless of how many entries are in the array. We'll override the length prop to determine
     * the amount of enumerable properties in our PrioritisedArray instead of using the built-in length property.
     */
    get length() {
        return Object.keys(this).length
    }

    set length(value) {
    }

    /**
     *
     * @param {Function} dataType
     * @param {DataSource} dataSource
     * @param {Snapshot} dataSnapshot
     */
    constructor(dataType, dataSource = null, dataSnapshot = null) {
        super();
        /**** Callbacks ****/
        this._valueChangedCallback = null;

        /**** Private properties ****/
        this._dataType = dataType;
        this._dataSource = dataSource;
        this._isBeingReordered = false; /* Flag to determine when we're reordering so we don't listen to move updates */
        this._eventEmitter = new EventEmitter();

        /* Bind all local methods to the current object instance, so we can refer to "this"
         * in the methods as expected, even when they're called from event handlers.        */
        ObjectHelper.bindAllMethods(this, this);

        /* Hide all private properties (starting with '_') and methods from enumeration,
         * so when you do for( in ), only actual data properties show up. */
        ObjectHelper.hideMethodsAndPrivatePropertiesFromObject(this);

        /* Hide the priority field from enumeration, so we don't save it to the dataSource. */
        ObjectHelper.hidePropertyFromObject(Object.getPrototypeOf(this), 'length');

        /* If no dataSource is given, create own one with guessed path */
        if(!dataSource) {
            let modelName = Object.getPrototypeOf(this).constructor.name;
            let path = modelName; //+ 's';

            // retrieve dataSource from the DI context
            dataSource = Context.getContext().get(DataSource);

            dataSource = dataSource.child(path);
            this._dataSource = dataSource;
        }

        /* If a snapshot is present use it, otherwise generate one by subscribing to the dataSource one time. */
        if (dataSnapshot) {
            this._buildFromSnapshot(dataSnapshot);
        } else {
            this._buildFromDataSource(dataSource);
        }
    }

    /**
     *
     * @param event
     * @param fn
     * @param context
     * @returns {*}
     */
    once(event, fn, context = this) {
        return this.on(event, function(){
            fn.call(context, arguments);
            this.off(event, fn, context);
        }, this);
    }

    /**
     * Subscribes to events emitted by this PrioritisedArray.
     * @param {String} event 'value', 'child_changed', 'child_moved', 'child_removed'
     * @param {Function} fn Function to call when event is emitted.
     * @param {Object} context Object to bind the given callback function to.
     * @returns {*}
     */
    on(event, fn, context) {
        /* If we're already ready, fire immediately */
        if(event === 'ready' && this._dataSource && this._dataSource.ready) { fn.call(context, this); }

        return this._eventEmitter.on(event, fn, context);
    }

    /**
     * Removes subscription to events emitted by this PrioritisedArray. If no fn or context is given, all handlers for
     * the given event are removed. If no parameters are given at all, all event types will have their handlers removed.
     * @param {String} event 'value', 'child_changed', 'child_moved', 'child_removed'
     * @param {Function} fn Function to call when event is emitted.
     * @param {Object} context Object to bind the given callback function to.
     * @returns {*}
     */
    off(event, fn, context) {

        if(event && (fn || context)) {
            this._eventEmitter.removeListener(event, fn, context);
        } else {
            this._eventEmitter.removeAllListeners(event);
        }
    }

    /**
     * Adds a model instance to the rear of the PrioritisedArray
     * @param {Model} model Subclass of Model
     * @returns {*} Same model as the one originally passed as parameter
     */
    add(model) {
        if (model instanceof this._dataType) {
            if (this._findIndexById(model.id) < 0) {
                model.priority = this.length;
                this.push(model);

                if (!model._inheritable) {
                    model.on('value', (modelData) => {
                        this._onChildChanged(modelData);
                    });
                }

            } else {
                /* TODO: change to throw exception */
                console.log('Tried to append an object with the same ID as one already present.');
            }
        }
        /* Let's try to parse the object using property reflection */
        else if(model instanceof Object) {
            // retrieve dataSource from the DI context
            var options = { dataSource: this._dataSource};
            let newModel = new this._dataType(null, model, options);
            this.add(newModel);
        }
        else {
            /* TODO: change to throw exception */
            console.log('Tried to append an object that is not the same type as the one this PrioritisedArray was created with.');
        }

        /* Return model so we can do this: let newModel = PrioArray.add(new Model()); newModel.someProperty = true; */
        return model;
    }

    /**
     * Inserts a model instance at the given position of the PrioritisedArray, and recalculates the priority (position)
     * of all models after the inserted position.
     * @param {Model} model Subclass of Model
     * @param {Number} position Zero-based index where to put the new model instance.
     */
    insertAt(model, position) {
        if (model instanceof this._dataType) {
            this.splice(position, 0, model);
            this._recalculatePriorities(position);
        }
        else {
            /* TODO: change to throw exception */
            console.log('Tried to append an object that is not the same type as the PrioritisedArray was created with.');
        }

        /* Return model so we can do this: let newModel = PrioArray.add(new Model()); newModel.someProperty = true; */
        return model;
    }

    /**
     * Moves a model instance from one position to another.
     * @param {Number} fromPosition Zero-based index of original position
     * @param {Number} toPosition Zero-based index of target position
     */
    move(fromPosition, toPosition) {
        let model = this[fromPosition];
        this.splice(fromPosition, 1);
        this.splice(toPosition, 0, model);
        this._recalculatePriorities();
    }

    /**
     * Removes the model instance at the given position. Does not remove the model from the datasource, to do that
     * call model.remove() directly, or PrioArray[index].remove().
     * @param position
     */
    remove(position) {
        this.splice(position, 1);
    }

    /**
     * Assigns models' priorities based on their position in the PrioritisedArray.
     * @param {Number} start Index to start calculation from, so we don't process unnecessary models.
     * @private
     */
    _recalculatePriorities(start = 0) {
        this._isBeingReordered = true;
        for (let i = start; i < this.length; i++) {
            this[i].priority = i;
        }
        this._isBeingReordered = false;
    }

    /**
     * Interprets all childs of a given snapshot as instances of the given data type for this PrioritisedArray,
     * and attempts to instantiate new model instances based on these sub-snapshots. It adds them to the
     * PrioritisedArray, which also assigns their priority based on their inserted position.
     * @param {Snapshot} dataSnapshot
     * @private
     */
    _buildFromSnapshot(dataSnapshot) {

        let numChildren = dataSnapshot.numChildren(), currentChild = 1;

        /* If there is no data at this point yet, fire a ready event */
        if (numChildren === 0) {
            this._dataSource.ready = true;
            this._eventEmitter.emit('ready');
        }

        dataSnapshot.forEach(
            /** @param {Snapshot} child **/
            function(child){
                /* Create a new instance of the given data type and prefill it with the snapshot data. */
                let options = {dataSnapshot: child};

                /* whenever the ref() is a datasource, we can bind that source to the model.
                 * whenever it's not a datasource, we assume the model should instantiate a new
                  * datasource to bind the model */

                 if (child.ref() instanceof DataSource)
                    options.dataSource = child.ref();
                 else {
                     var rootPath = child.ref().root().toString();
                     options.path = child.ref().toString().replace(rootPath,'/');
                 }

                let newModel = new this._dataType(child.key(), child.val(), options);
                this.add(newModel);

                /* If this is the last child, fire a ready event */
                if(currentChild++ == numChildren){
                    this._dataSource.ready = true;
                    this._eventEmitter.emit('ready');
                }

            }.bind(this));

        if (dataSnapshot.ref() instanceof DataSource &&
            dataSnapshot.ref().inheritable)
            this._eventEmitter.emit('value', this);

        //this._registerCallbacks(this._dataSource);
    }

    /**
     * Clones a dataSource (to not disturb any existing callbacks defined on the original) and uses it
     * to get a dataSnapshot which is used in _buildSnapshot to build our array.
     * @param dataSource
     * @private
     */
    _buildFromDataSource(dataSource) {

        let path = dataSource.path();
        let DataSource = Object.getPrototypeOf(dataSource).constructor;
        let newSource = new DataSource(path);
        newSource.setValueChangedCallback((dataSnapshot) => {
            newSource.removeValueChangedCallback();
            this._buildFromSnapshot(dataSnapshot);
            this._registerCallbacks(newSource);
        });

    }

    _registerCallbacks(dataSource) {
        dataSource.setChildAddedCallback(this._onChildAdded);
        dataSource.setChildMovedCallback(this._onChildMoved);
        if (dataSource.inheritable)
            dataSource.setChildChangedCallback(this._onChildChanged);
        dataSource.setChildRemovedCallback(this._onChildRemoved);
    }

    /**
     * Called by dataSource when a new child is added.
     * @param {Snapshot} snapshot
     * @private
     */
    _onChildAdded(snapshot) {
        let id = snapshot.key();
        var rootPath = snapshot.ref().root().toString();
        let model = this.add(new this._dataType(id, null, {dataSnapshot: snapshot, path: snapshot.ref().toString().replace(rootPath,'/') }));

        this._eventEmitter.emit('child_added', model);
        this._eventEmitter.emit('value', this);
    }

    /**
     *
     */
    _onChildChanged(snapshot) {
        let id = snapshot.key();
        let itemIndex = this._findIndexById(id);
        let changedModel = new this._dataType(id, null, {dataSnapshot: snapshot, dataSource: snapshot.ref() });

        if (!(JSON.stringify(this[itemIndex])===JSON.stringify(changedModel))) {
            this[itemIndex] = changedModel;

            this._eventEmitter.emit('child_changed', changedModel);
            this._eventEmitter.emit('value', this);
        }
    }

    /**
     * Called by dataSource when a child is moved, which changes its priority.
     * @param {Snapshot} snapshot
     * @private
     */
    _onChildMoved(snapshot, prevSiblingId) {
        /* Ignore priority updates whilst we're reordering to avoid floods */
        if (!this._isBeingReordered) {

            let id = snapshot.key();
            let previousPosition = this._findIndexById(id);
            let tempModel = this[previousPosition];
            this.remove(previousPosition);

            let newPosition = this._findIndexById(prevSiblingId)+1;
            this.insertAt(tempModel, newPosition);

            this._recalculatePriorities();

            let model = this[newPosition];

            this._eventEmitter.emit('child_moved', model, previousPosition);
            this._eventEmitter.emit('value', this);
        }
    }

    /**
     * Called by dataSource when a child is removed.
     * @param {Snapshot} oldSnapshot
     * @private
     */
    _onChildRemoved(oldSnapshot) {
        /* TODO: figure out if we can use the snapshot's priority as our array index reliably, to avoid big loops. */
        let id = oldSnapshot.key();
        let position = this._findIndexById(id);
        let model = this[position];

        if (position !== -1) {
            this.remove(position, false);

            this._eventEmitter.emit('child_removed', model);
            this._eventEmitter.emit('value', this);
        }
    }

    /**
     * Searches for the index in the PrioritisedArray of a model that has an id equal to the given id.
     * @param id Id field of the model we're looking for
     * @returns {Number} Zero-based index if found, -1 otherwise
     * @private
     */
    _findIndexById(id) {
        for (let i = 0; i < this.length; i++) {
            if (this[i].id == id) {
                return (i);
            }
        }
        return -1;
    }

}<|endoftext|>


<commit_before>var Class = require('../utils/Class');
var Extend = require('../utils/object/Extend');
var EventDispatcher = require('../events/EventDispatcher');
var NOOP = require('../utils/NOOP');
//  Phaser.Sound.BaseSound
var BaseSound = new Class({
    initialize: function BaseSound(manager, key, config) {
        /**
         * Local reference to the sound manager.
         *
         * @private
         * @property {Phaser.Sound.BaseSoundManager} manager
         */
        this.manager = manager;
        /**
         * [description]
         *
         * @property {string} key
         */
        this.key = key;
        /**
         * Flag indicating if sound is currently playing.
         *
         * @readonly
         * @property {boolean} isPlaying
         */
        this.isPlaying = false;
        /**
         * Flag indicating if sound is currently paused.
         *
         * @readonly
         * @property {boolean} isPaused
         */
        this.isPaused = false;
        /**
         * A property that holds the value of sound's actual playback rate,
         * after its rate and detune values has been combined with global
         * rate and detune values.
         *
         * @property {number} totalRate
         */
        this.totalRate = 1;
        /**
         * [description]
         *
         * Default values will be set by properties' setters.
         *
         * @private
         * @property {ISoundConfig} config
         */
        this.config = {};
        /**
         * Reference to the currently used config.
         * It could be default config or marker config.
         *
         * @private
         * @property {ISoundConfig} currentConfig
         */
        this.currentConfig = this.config;
        /**
         * [description]
         *
         * @property {boolean} mute
         */
        this.mute = false;
        /**
         * [description]
         *
         * @property {number} volume
         */
        this.volume = 1;
        /**
         * Defines the speed at which the audio asset will be played.
         * Value of 1.0 plays the audio at full speed, 0.5 plays the audio
         * at half speed and 2.0 doubles the audio's playback speed.
         * This value gets multiplied by global rate to have the final playback speed.
         *
         * @property {number} rate
         */
        this.rate = 1;
        /**
         * Represents detuning of sound in [cents](https://en.wikipedia.org/wiki/Cent_%28music%29).
         * The range of the value is -1200 to 1200, but we recommend setting it to [50](https://en.wikipedia.org/wiki/50_Cent).
         *
         * @property {number} detune
         */
        this.detune = 0;
        /**
         * [description]
         *
         * @property {number} seek
         */
        this.seek = 0;
        /**
         * [description]
         *
         * @property {boolean} loop
         */
        this.loop = false;
        this.config = Extend(this.config, config);
        /**
         * A value representing the duration, in seconds.
         * It could be total sound duration or a marker duration.
         *
         * @readonly
         * @property {number} duration
         */
        this.duration = 0;
        /**
         * Duration of the entire sound.
         *
         * @readonly
         * @property {number}
         */
        this.totalDuration = 0;
        /**
         * Object containing markers definitions.
         *
         * @readonly
         * @property {{}} markers
         */
        this.markers = {};
        /**
         * Currently playing marker.
         * 'null' if whole sound is playing.
         *
         * @readonly
         * @property {ISoundMarker} currentMarker
         */
        this.currentMarker = null;
        /**
         * [description]
         *
         * @protected
         * @property {Phaser.Tween}
         */
        this.fadeTween = null; // TODO see how to use global tween
        /**
         * Event dispatches used to handle all sound instance
         * relevant events.
         *
         * @property {Phaser.Events.EventDispatcher}
         */
        this.events = new EventDispatcher();
    },
    addMarker: function (marker) {
        if (!marker) {
            console.error('addMarker - Marker object has to be provided!');
            return false;
        }
        if (!marker.name || typeof marker.name !== 'string') {
            console.error('addMarker - Marker has to have a valid name!');
            return false;
        }
        if (this.markers[marker.name]) {
            console.error('addMarker - Marker with name \'' + marker.name + '\' already exists for sound \'' + this.key + '\'!');
            return false;
        }
        marker = Extend(true, {
            name: '',
            start: 0,
            duration: this.totalDuration,
            config: {
                mute: false,
                volume: 1,
                rate: 1,
                detune: 0,
                seek: 0,
                loop: false
            }
        }, marker);
        this.markers[marker.name] = marker;
        return true;
    },
    updateMarker: function (marker) {
        if (!marker) {
            console.error('updateMarker - Marker object has to be provided!');
            return false;
        }
        if (!marker.name || typeof marker.name !== 'string') {
            console.error('updateMarker - Marker has to have a valid name!');
            return false;
        }
        if (!this.markers[marker.name]) {
            console.error('updateMarker - Marker with name \'' + marker.name + '\' does not exist for sound \'' + this.key + '\'!');
            return false;
        }
        this.markers[marker.name] = Extend(true, this.markers[marker.name], marker);
        return true;
    },
    removeMarker: function (markerName) {
        var marker = this.markers[markerName];
        if (!marker) {
            console.error('removeMarker - Marker with name \'' + marker.name + '\' does not exist for sound \'' + this.key + '\'!');
            return null;
        }
        this.markers[markerName] = null;
        return marker;
    },
    play: function (markerName, config) {
        if (markerName === void 0) { markerName = ''; }
        if (typeof markerName === 'object') {
            config = markerName;
            markerName = '';
        }
        if (typeof markerName !== 'string') {
            console.error('Sound marker name has to be a string!');
            return null;
        }
        if (!markerName) {
            this.currentMarker = null;
            this.currentConfig = this.config;
            this.duration = this.totalDuration;
        }
        else {
            if (!this.markers[markerName]) {
                console.error('No marker with name \'' + markerName + '\' found for sound \'' + this.key + '\'!');
                return null;
            }
            this.currentMarker = this.markers[markerName];
            this.currentConfig = this.currentMarker.config;
            this.duration = this.currentMarker.duration;
        }
        this.resetConfig();
        this.currentConfig = Extend(this.currentConfig, config);
        this.isPlaying = true;
        this.isPaused = false;
        return this;
    },
    pause: function () {
        if (this.isPaused || !this.isPlaying) {
            return false;
        }
        this.isPlaying = false;
        this.isPaused = true;
        return true;
    },
    resume: function () {
        if (!this.isPaused || this.isPlaying) {
            return false;
        }
        this.isPlaying = true;
        this.isPaused = false;
        return true;
    },
    stop: function () {
        if (!this.isPaused && !this.isPlaying) {
            return false;
        }
        this.isPlaying = false;
        this.isPaused = false;
        return true;
    },
    /**
     * @protected
     */
    applyConfig: function () {
        this.mute = this.currentConfig.mute;
        this.volume = this.currentConfig.volume;
        this.rate = this.currentConfig.rate;
        this.detune = this.currentConfig.detune;
        // TODO assign other config values to buffer source
    },
    /**
     * @protected
     */
    resetConfig: function () {
        this.currentConfig.seek = 0;
        // TODO add delay
    },
    fadeTo: function (volume, duration) {
        return null;
    },
    /**
     * @protected
     */
    update: NOOP,
    destroy: function () {
    },
    /**
     * @protected
     */
    setRate: function () {
        var cent = 1.0005777895065548; // Math.pow(2, 1/1200);
        var totalDetune = this.currentConfig.detune + this.manager.detune;
        var detuneRate = Math.pow(cent, totalDetune);
        this.totalRate = this.currentConfig.rate * this.manager.rate * detuneRate;
    }
});
module.exports = BaseSound;
<commit_msg>Initializing duration properties before config because some config options' setters use those values (seek)<commit_after>var Class = require('../utils/Class');
var Extend = require('../utils/object/Extend');
var EventDispatcher = require('../events/EventDispatcher');
var NOOP = require('../utils/NOOP');
//  Phaser.Sound.BaseSound
var BaseSound = new Class({
    initialize: function BaseSound(manager, key, config) {
        /**
         * Local reference to the sound manager.
         *
         * @private
         * @property {Phaser.Sound.BaseSoundManager} manager
         */
        this.manager = manager;
        /**
         * [description]
         *
         * @property {string} key
         */
        this.key = key;
        /**
         * Flag indicating if sound is currently playing.
         *
         * @readonly
         * @property {boolean} isPlaying
         */
        this.isPlaying = false;
        /**
         * Flag indicating if sound is currently paused.
         *
         * @readonly
         * @property {boolean} isPaused
         */
        this.isPaused = false;
        /**
         * A property that holds the value of sound's actual playback rate,
         * after its rate and detune values has been combined with global
         * rate and detune values.
         *
         * @property {number} totalRate
         */
        this.totalRate = 1;
        /**
         * A value representing the duration, in seconds.
         * It could be total sound duration or a marker duration.
         *
         * @readonly
         * @property {number} duration
         */
        this.duration = 0;
        /**
         * Duration of the entire sound.
         *
         * @readonly
         * @property {number}
         */
        this.totalDuration = 0;
        /**
         * [description]
         *
         * Default values will be set by properties' setters.
         *
         * @private
         * @property {ISoundConfig} config
         */
        this.config = {};
        /**
         * Reference to the currently used config.
         * It could be default config or marker config.
         *
         * @private
         * @property {ISoundConfig} currentConfig
         */
        this.currentConfig = this.config;
        /**
         * [description]
         *
         * @property {boolean} mute
         */
        this.mute = false;
        /**
         * [description]
         *
         * @property {number} volume
         */
        this.volume = 1;
        /**
         * Defines the speed at which the audio asset will be played.
         * Value of 1.0 plays the audio at full speed, 0.5 plays the audio
         * at half speed and 2.0 doubles the audio's playback speed.
         * This value gets multiplied by global rate to have the final playback speed.
         *
         * @property {number} rate
         */
        this.rate = 1;
        /**
         * Represents detuning of sound in [cents](https://en.wikipedia.org/wiki/Cent_%28music%29).
         * The range of the value is -1200 to 1200, but we recommend setting it to [50](https://en.wikipedia.org/wiki/50_Cent).
         *
         * @property {number} detune
         */
        this.detune = 0;
        /**
         * [description]
         *
         * @property {number} seek
         */
        this.seek = 0;
        /**
         * [description]
         *
         * @property {boolean} loop
         */
        this.loop = false;
        this.config = Extend(this.config, config);
        /**
         * Object containing markers definitions.
         *
         * @readonly
         * @property {{}} markers
         */
        this.markers = {};
        /**
         * Currently playing marker.
         * 'null' if whole sound is playing.
         *
         * @readonly
         * @property {ISoundMarker} currentMarker
         */
        this.currentMarker = null;
        /**
         * [description]
         *
         * @protected
         * @property {Phaser.Tween}
         */
        this.fadeTween = null; // TODO see how to use global tween
        /**
         * Event dispatches used to handle all sound instance
         * relevant events.
         *
         * @property {Phaser.Events.EventDispatcher}
         */
        this.events = new EventDispatcher();
    },
    addMarker: function (marker) {
        if (!marker) {
            console.error('addMarker - Marker object has to be provided!');
            return false;
        }
        if (!marker.name || typeof marker.name !== 'string') {
            console.error('addMarker - Marker has to have a valid name!');
            return false;
        }
        if (this.markers[marker.name]) {
            console.error('addMarker - Marker with name \'' + marker.name + '\' already exists for sound \'' + this.key + '\'!');
            return false;
        }
        marker = Extend(true, {
            name: '',
            start: 0,
            duration: this.totalDuration,
            config: {
                mute: false,
                volume: 1,
                rate: 1,
                detune: 0,
                seek: 0,
                loop: false
            }
        }, marker);
        this.markers[marker.name] = marker;
        return true;
    },
    updateMarker: function (marker) {
        if (!marker) {
            console.error('updateMarker - Marker object has to be provided!');
            return false;
        }
        if (!marker.name || typeof marker.name !== 'string') {
            console.error('updateMarker - Marker has to have a valid name!');
            return false;
        }
        if (!this.markers[marker.name]) {
            console.error('updateMarker - Marker with name \'' + marker.name + '\' does not exist for sound \'' + this.key + '\'!');
            return false;
        }
        this.markers[marker.name] = Extend(true, this.markers[marker.name], marker);
        return true;
    },
    removeMarker: function (markerName) {
        var marker = this.markers[markerName];
        if (!marker) {
            console.error('removeMarker - Marker with name \'' + marker.name + '\' does not exist for sound \'' + this.key + '\'!');
            return null;
        }
        this.markers[markerName] = null;
        return marker;
    },
    play: function (markerName, config) {
        if (markerName === void 0) { markerName = ''; }
        if (typeof markerName === 'object') {
            config = markerName;
            markerName = '';
        }
        if (typeof markerName !== 'string') {
            console.error('Sound marker name has to be a string!');
            return null;
        }
        if (!markerName) {
            this.currentMarker = null;
            this.currentConfig = this.config;
            this.duration = this.totalDuration;
        }
        else {
            if (!this.markers[markerName]) {
                console.error('No marker with name \'' + markerName + '\' found for sound \'' + this.key + '\'!');
                return null;
            }
            this.currentMarker = this.markers[markerName];
            this.currentConfig = this.currentMarker.config;
            this.duration = this.currentMarker.duration;
        }
        this.resetConfig();
        this.currentConfig = Extend(this.currentConfig, config);
        this.isPlaying = true;
        this.isPaused = false;
        return this;
    },
    pause: function () {
        if (this.isPaused || !this.isPlaying) {
            return false;
        }
        this.isPlaying = false;
        this.isPaused = true;
        return true;
    },
    resume: function () {
        if (!this.isPaused || this.isPlaying) {
            return false;
        }
        this.isPlaying = true;
        this.isPaused = false;
        return true;
    },
    stop: function () {
        if (!this.isPaused && !this.isPlaying) {
            return false;
        }
        this.isPlaying = false;
        this.isPaused = false;
        return true;
    },
    /**
     * @protected
     */
    applyConfig: function () {
        this.mute = this.currentConfig.mute;
        this.volume = this.currentConfig.volume;
        this.rate = this.currentConfig.rate;
        this.detune = this.currentConfig.detune;
        // TODO assign other config values to buffer source
    },
    /**
     * @protected
     */
    resetConfig: function () {
        this.currentConfig.seek = 0;
        // TODO add delay
    },
    fadeTo: function (volume, duration) {
        return null;
    },
    /**
     * @protected
     */
    update: NOOP,
    destroy: function () {
    },
    /**
     * @protected
     */
    setRate: function () {
        var cent = 1.0005777895065548; // Math.pow(2, 1/1200);
        var totalDetune = this.currentConfig.detune + this.manager.detune;
        var detuneRate = Math.pow(cent, totalDetune);
        this.totalRate = this.currentConfig.rate * this.manager.rate * detuneRate;
    }
});
module.exports = BaseSound;
<|endoftext|>


<commit_before>// Handle events in body
Template.home.events({
    // When the project name changes, update the Project Summary section of the UI
    'change #PrjName': function (event) {
        console.log("Project changed to " + event.target.value);
        // Get the project name and update UI
        Session.set("projectName", event.target.value);
        updateUIFromPrj();
    },
    // When the project name is clicked, clear-up the UI
    'click #PrjName': function () {
        // Clear the input so that a new project can be selected
        console.log("PrjName clicked, clearing Project Name so that the pulldown shows all projects");
        $("#PrjName").val("");
        Session.set("projectName", "");
        clearUI();
    },
    // When LastTID link was clicked, move the test selector and refresh the UI from Test KB
    'click #lastTIDTxt': function(){
        var lastTID = Session.get("lastTID");
        console.log("Continuing at lastTID " + lastTID)
        $("#testSel").val(lastTID);
        updateUIFromTestKB();
        updateUIFromIssueColl()
    },
    // When the project parameters are changed, persist them
    'change #PrjNotes, change #PrjSoftware': function (event) {
        console.log("Changed " + event.target.id);
        saveProjectDataFromUI();
        updateUIFromPrj();
    },
    // When the scope/methodology selector is changed, persist the value
    'change #ScopeSel': function (event) {
        console.log("Changed scope/methodology selector")
        Session.set("projectScope", event.target.value);
        saveProjectDataFromUI();
    },
    // When the test selector is changed, update the Testing and Generic Issue sections
    'change #testSel': function () {
        console.log("Changed test selection")
        Session.set("lastTID", $("#testSel").val());
        updateUIFromTestKB();
        $('#testNameTA').val("");
        refreshLastTIDLink();
        saveProjectDataFromUI(); // save last TID
        updateUIFromIssueColl()
    },
    // When a test player button is changed, update the Testing and Generic Issue sections
    'click #btnBack': function () {
        selected = $("#testSel").prop("selectedIndex") - 1;
        if (selected <= 0) selected = 1;
        console.log("Selected: " + selected);
        $("#testSel").prop("selectedIndex", selected);
        Session.set("lastTID", $("#testSel").val());
        updateUIFromTestKB();
        $('#testNameTA').typeahead('val', "");
        refreshLastTIDLink();
        saveProjectDataFromUI();
        updateUIFromIssueColl()
    },
    // When a test player button is changed, update the Testing and Generic Issue sections
    'click #btnNext': function () {
        selected = $("#testSel").prop("selectedIndex") + 1;
        if (selected >= $('#testSel > option').length)
            selected = $('#testSel > option').length - 1;
        console.log("Selected: " + selected);
        $("#testSel").prop("selectedIndex", selected);
        Session.set("lastTID", $("#testSel").val());
        updateUIFromTestKB();
        $('#testNameTA').typeahead('val', "");
        refreshLastTIDLink();
        saveProjectDataFromUI();
        updateUIFromIssueColl()
    },
    // When the test name type-ahead is clicked, clear the previous value to help with the search
    'click #testNameTA': function () {
        // Clear the input so that a new test name can be selected
        console.log("Test name text area clicked, clearing field so that another test can be chosen");
        $('#testNameTA').typeahead('val', "");
    },
    // When the New Test button is pressed, clear the UI and create another test
    'click #kbBtnNew': function () {
        console.log('Creating new test: ' + $('#TTestName').val());
        $("#testSel").prop("selectedIndex", 0);
        clearTestingFields();
        clearIssueFields();
        newTest();
    },
    // When the test fields values change, update the Test KB
    'change #TTestName, change #TTesterSupport, change #TTRef, change #cwename, change #cweid, change #TIssueName, change .testKbCB, change #TSeverity, change #TRef1, change #TRef2': function (event) {
        updateTestKBFromUI(event.target.id, event.target.value);
    },
    // When the Specific Issue Data changes, save it to the Issue collection
    'change #IURIs, change #IEvidence, change #IScreenshots, change #IPriority': function (event) {
        saveIssueDataFromUI(event.target.id, event.target.value);

        // Update titles so that mouse-over information matches the content
        $("#IURIs").attr("title", $("#IURIs").val());
        $("#IEvidence").attr("title", $("#IEvidence").val());
        $("#INotes").attr("title", $("#INotes").val());
        updateScreenshots();
    },

    // When the Specific Issue Data changes, parse it if it's formatted as a Burp issue. Then, save it to the Issue collection
    'change #INotes': function (event) {

        // If the note is Burp-formatted, parse it
        var notes = $("#INotes").val();
        var lines = notes.split('\n');
        var issue, evidence, urls;
        var urlSection=false;
        for (var i in lines){
            var t = lines[i].split(':');

            // Capture the Issue Name
            if (lines[i].startsWith('Issue:'))
                issue       = t[1].trim();

            // Capture the URL list
            if (lines[i].startsWith('URL(s):')){
                urlSection = true;
            }
            if (urlSection){
                var url = lines[i];
                if ((url !== undefined) && (url.length > 0)){
                    url = url.replace("^ - ", "");
                    urls += url + "\n";
                }
                else
                    urlSection = false;
            }

            // Capture the evidence
            if (lines[i].startsWith('Evidence:')){
                evidence    = t[1].trim();
            }
        }

        // Push the captured data to the UI and DB
        if ((issue !== undefined) && (issue.length > 0)){
            $("#TIssueName").val(issue);
            saveIssueDataFromUI("#TIssueName", issue);
        }
        if ((evidence !== undefined) && (evidence.length > 0)){
            // Decode the Base64 value
            evidence = decodeURIComponent(Array.prototype.map.call(atob(evidence), function(c) {
                    return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2);
                }).join(''));
            $("#IEvidence").val(evidence);
            saveIssueDataFromUI("#IEvidence", evidence);
        }
        if ((urls !== undefined) && (urls.length > 0)){
            $("#IURIs").val(urls);
            saveIssueDataFromUI("#IURIs", urls);
        }

        // Update titles so that mouse-over information matches the content
        $("#IURIs").attr("title", $("#IURIs").val());
        $("#IEvidence").attr("title", $("#IEvidence").val());
        $("#INotes").attr("title", $("#INotes").val());
    },

    // When some fields are clicked, increase the text box size
    'click #IURIs, click #IEvidence, click #IScreenshots, click #INotes, click #PrjNotes, click #TTesterSupport': function (event) {
        //console.log("Increasing the height for " + event.target.id);
        $("#" + event.target.id).height(200);
    },
    // When some fields are unselected, decrease the text box size to a default height
    'blur #IURIs, blur #IEvidence, blur #IScreenshots, blur #INotes, blur #PrjNotes, blur #TTesterSupport': function (event) {
        //console.log("Decreasing the height for " + event.target.id);
        $("#" + event.target.id).height(20);
    },
    // When Evidence and Notes fields are double-clicked, prefill them with template text.
    'dblclick #IEvidence, dblclick #INotes': function () {
        addIssueTemplateTextToUI();
    },
    // When pasting images in Evidence, add a Base64 representation
    'paste #IScreenshots': function (event) {
        pasteScreenshotToUI(event);
    },
    // Generate an HTML report when the 'HTML Report' button is pressed
    'click #btnHtmlReport': function () {
        var prjName = $("#PrjName").val();
        console.log("Generating HTML report for project " + prjName);
        window.open("/report/html/"+prjName, "DownloadWin");
    },
    // Generate a CSV report when the 'CSV Report' button is pressed
    'click #btnCsvReport': function () {
        var prjName = $("#PrjName").val();
        console.log("Generating CSV report for project " + prjName);
        window.open("/report/csv/"+prjName, "DownloadWin");
    },
    // Update this app when the 'Update App' button is pressed
    'click #btnUpdate': function () {
        console.log("Trying to update the WAPT Runner code from GIT...");
        Meteor.call('runCode', function (err, response) {
            if (response !== undefined)
                console.log(response);
            if (err !== undefined)
                console.log(err);
        });
    },
});

// Event handlers for the issue list
Template.myIssueTmpl.events({
    // Delete Issue
    "click .delete": function (event) {
        console.log('Delete clicked from myIssueTmpl! ID=' + this._id);
        issueColl.remove(this._id);
    },
    // Show the issue details when clicking in the list
    "click .isTD": function (event) {
        console.log('Clicked in myIssueTmpl ID=' + this._id + ". TID=" + this.TID);
        $("#testSel").val(this.TID);
        Session.set("lastTID", $("#testSel").val());
        updateUIFromTestKB();
        $('#testNameTA').typeahead('val', "");
        refreshLastTIDLink();
        saveProjectDataFromUI(); // save last TID
        updateUIFromIssueColl()
   }
});
<commit_msg>Minor update<commit_after>// Handle events in body
Template.home.events({
    // When the project name changes, update the Project Summary section of the UI
    'change #PrjName': function (event) {
        console.log("Project changed to " + event.target.value);
        // Get the project name and update UI
        Session.set("projectName", event.target.value);
        updateUIFromPrj();
    },
    // When the project name is clicked, clear-up the UI
    'click #PrjName': function () {
        // Clear the input so that a new project can be selected
        console.log("PrjName clicked, clearing Project Name so that the pulldown shows all projects");
        $("#PrjName").val("");
        Session.set("projectName", "");
        clearUI();
    },
    // When LastTID link was clicked, move the test selector and refresh the UI from Test KB
    'click #lastTIDTxt': function(){
        var lastTID = Session.get("lastTID");
        console.log("Continuing at lastTID " + lastTID)
        $("#testSel").val(lastTID);
        updateUIFromTestKB();
        updateUIFromIssueColl()
    },
    // When the project parameters are changed, persist them
    'change #PrjNotes, change #PrjSoftware': function (event) {
        console.log("Changed " + event.target.id);
        saveProjectDataFromUI();
        updateUIFromPrj();
    },
    // When the scope/methodology selector is changed, persist the value
    'change #ScopeSel': function (event) {
        console.log("Changed scope/methodology selector")
        Session.set("projectScope", event.target.value);
        saveProjectDataFromUI();
    },
    // When the test selector is changed, update the Testing and Generic Issue sections
    'change #testSel': function () {
        console.log("Changed test selection")
        Session.set("lastTID", $("#testSel").val());
        updateUIFromTestKB();
        $('#testNameTA').val("");
        refreshLastTIDLink();
        saveProjectDataFromUI(); // save last TID
        updateUIFromIssueColl()
    },
    // When a test player button is changed, update the Testing and Generic Issue sections
    'click #btnBack': function () {
        selected = $("#testSel").prop("selectedIndex") - 1;
        if (selected <= 0) selected = 1;
        console.log("Selected: " + selected);
        $("#testSel").prop("selectedIndex", selected);
        Session.set("lastTID", $("#testSel").val());
        updateUIFromTestKB();
        $('#testNameTA').typeahead('val', "");
        refreshLastTIDLink();
        saveProjectDataFromUI();
        updateUIFromIssueColl()
    },
    // When a test player button is changed, update the Testing and Generic Issue sections
    'click #btnNext': function () {
        selected = $("#testSel").prop("selectedIndex") + 1;
        if (selected >= $('#testSel > option').length)
            selected = $('#testSel > option').length - 1;
        console.log("Selected: " + selected);
        $("#testSel").prop("selectedIndex", selected);
        Session.set("lastTID", $("#testSel").val());
        updateUIFromTestKB();
        $('#testNameTA').typeahead('val', "");
        refreshLastTIDLink();
        saveProjectDataFromUI();
        updateUIFromIssueColl()
    },
    // When the test name type-ahead is clicked, clear the previous value to help with the search
    'click #testNameTA': function () {
        // Clear the input so that a new test name can be selected
        console.log("Test name text area clicked, clearing field so that another test can be chosen");
        $('#testNameTA').typeahead('val', "");
    },
    // When the New Test button is pressed, clear the UI and create another test
    'click #kbBtnNew': function () {
        console.log('Creating new test: ' + $('#TTestName').val());
        $("#testSel").prop("selectedIndex", 0);
        clearTestingFields();
        clearIssueFields();
        newTest();
    },
    // When the test fields values change, update the Test KB
    'change #TTestName, change #TTesterSupport, change #TTRef, change #cwename, change #cweid, change #TIssueName, change .testKbCB, change #TSeverity, change #TRef1, change #TRef2': function (event) {
        updateTestKBFromUI(event.target.id, event.target.value);
    },
    // When the Specific Issue Data changes, save it to the Issue collection
    'change #IURIs, change #IEvidence, change #IScreenshots, change #IPriority': function (event) {
        saveIssueDataFromUI(event.target.id, event.target.value);

        // Update titles so that mouse-over information matches the content
        $("#IURIs").attr("title", $("#IURIs").val());
        $("#IEvidence").attr("title", $("#IEvidence").val());
        $("#INotes").attr("title", $("#INotes").val());
        updateScreenshots();
    },

    // When the Specific Issue Data changes, parse it if it's formatted as a Burp issue. Then, save it to the Issue collection
    'change #INotes': function (event) {

        // If the note is Burp-formatted, parse it
        var notes = $("#INotes").val();
        var lines = notes.split('\n');
        var issue, evidence, urls;
        var urlSection=false;
        var newNotes="";
        for (var i in lines){
            var t = lines[i].split(':');

            // Capture the Issue Name
            if (lines[i].startsWith('Issue:')){
                issue       = t[1].trim();
            }

            // Capture the URL list
            else if (lines[i].startsWith('URL(s):')){
                urlSection = true;
            }

            else if (urlSection){
                var url = lines[i];
                if ((url !== undefined) && (url.length > 0)){
                    url = url.replace("^ - ", "");
                    urls += url + "\n";
                }
                else
                    urlSection = false;
            }

            // Capture the evidence
            else if (lines[i].startsWith('Evidence:')){
                evidence    = t[1].trim();
            }

            // In the updated notes, prune what we've captured above
            else{
                newNotes = lines[i];
            }
        }

        // Push the captured data to the UI and DB
        if ((issue !== undefined) && (issue.length > 0)){
            saveIssueDataFromUI("#TIssueName", issue);
            $("#TIssueName").val(issue);
        }
        if ((evidence !== undefined) && (evidence.length > 0)){
            // Decode the Base64 value
            evidence = decodeURIComponent(Array.prototype.map.call(atob(evidence), function(c) {
                    return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2);
                }).join(''));
            saveIssueDataFromUI("#IEvidence", evidence);
            $("#IEvidence").val(evidence);
        }
        if ((urls !== undefined) && (urls.length > 0)){
            saveIssueDataFromUI("#IURIs", urls);
            $("#IURIs").val(urls);
        }
        if (newNotes.length > 0){
            saveIssueDataFromUI("#INotes", newNotes);
            $("#INotes").val(newNotes);
        }

        // Update titles so that mouse-over information matches the content
        $("#IURIs").attr("title", $("#IURIs").val());
        $("#IEvidence").attr("title", $("#IEvidence").val());
        $("#INotes").attr("title", $("#INotes").val());
    },

    // When some fields are clicked, increase the text box size
    'click #IURIs, click #IEvidence, click #IScreenshots, click #INotes, click #PrjNotes, click #TTesterSupport': function (event) {
        //console.log("Increasing the height for " + event.target.id);
        $("#" + event.target.id).height(200);
    },
    // When some fields are unselected, decrease the text box size to a default height
    'blur #IURIs, blur #IEvidence, blur #IScreenshots, blur #INotes, blur #PrjNotes, blur #TTesterSupport': function (event) {
        //console.log("Decreasing the height for " + event.target.id);
        $("#" + event.target.id).height(20);
    },
    // When Evidence and Notes fields are double-clicked, prefill them with template text.
    'dblclick #IEvidence, dblclick #INotes': function () {
        addIssueTemplateTextToUI();
    },
    // When pasting images in Evidence, add a Base64 representation
    'paste #IScreenshots': function (event) {
        pasteScreenshotToUI(event);
    },
    // Generate an HTML report when the 'HTML Report' button is pressed
    'click #btnHtmlReport': function () {
        var prjName = $("#PrjName").val();
        console.log("Generating HTML report for project " + prjName);
        window.open("/report/html/"+prjName, "DownloadWin");
    },
    // Generate a CSV report when the 'CSV Report' button is pressed
    'click #btnCsvReport': function () {
        var prjName = $("#PrjName").val();
        console.log("Generating CSV report for project " + prjName);
        window.open("/report/csv/"+prjName, "DownloadWin");
    },
    // Update this app when the 'Update App' button is pressed
    'click #btnUpdate': function () {
        console.log("Trying to update the WAPT Runner code from GIT...");
        Meteor.call('runCode', function (err, response) {
            if (response !== undefined)
                console.log(response);
            if (err !== undefined)
                console.log(err);
        });
    },
});

// Event handlers for the issue list
Template.myIssueTmpl.events({
    // Delete Issue
    "click .delete": function (event) {
        console.log('Delete clicked from myIssueTmpl! ID=' + this._id);
        issueColl.remove(this._id);
    },
    // Show the issue details when clicking in the list
    "click .isTD": function (event) {
        console.log('Clicked in myIssueTmpl ID=' + this._id + ". TID=" + this.TID);
        $("#testSel").val(this.TID);
        Session.set("lastTID", $("#testSel").val());
        updateUIFromTestKB();
        $('#testNameTA').typeahead('val', "");
        refreshLastTIDLink();
        saveProjectDataFromUI(); // save last TID
        updateUIFromIssueColl()
   }
});
<|endoftext|>


<commit_before>/*jshint worker: true*/

// Modules and dependencies to expose in the public Tangram module
import Utils from './utils';

// The leaflet layer plugin is currently the primary public API
import {LeafletLayer, leafletLayer} from './leaflet_layer';

// The scene worker is only activated when a worker thread is instantiated, but must always be loaded
import {SceneWorker} from '../src/scene_worker';

// Additional modules are exposed for debugging
import log from 'loglevel';
import {Geo} from './geo';
import GL from './gl/gl';
import GLProgram from './gl/gl_program';
import GLTexture from './gl/gl_texture';
import WorkerBroker from './worker_broker';

// Window can only be set in main thread
Utils.inMainThread(() => {
    // Main thread objects that can be called from workers
    WorkerBroker.addTarget('GLTexture', GLTexture);

    window.Tangram = module.exports = {
        LeafletLayer,
        leafletLayer,
        debug: {
            log,
            Utils,
            Geo,
            GL,
            GLProgram,
            GLTexture,
            WorkerBroker
        }
    };
});

// Make some modules accessible in worker for debugging
Utils.inWorkerThread(() => {
    self.Tangram = {
        debug: {
            Utils,
            SceneWorker,
            WorkerBroker
        }
    };
});
<commit_msg>expose same modules for debug in main thread + worker<commit_after>/*jshint worker: true*/

// Modules and dependencies to expose in the public Tangram module
import Utils from './utils';

// The leaflet layer plugin is currently the primary public API
import {LeafletLayer, leafletLayer} from './leaflet_layer';

// The scene worker is only activated when a worker thread is instantiated, but must always be loaded
import {SceneWorker} from '../src/scene_worker';

// Additional modules are exposed for debugging
import log from 'loglevel';
import {Geo} from './geo';
import GL from './gl/gl';
import GLProgram from './gl/gl_program';
import GLTexture from './gl/gl_texture';
import WorkerBroker from './worker_broker';

// Make some modules accessible for debugging
var debug = {
    log,
    Utils,
    Geo,
    GL,
    GLProgram,
    GLTexture,
    SceneWorker,
    WorkerBroker
};

// Window can only be set in main thread
Utils.inMainThread(() => {
    // Main thread objects that can be called from workers
    WorkerBroker.addTarget('GLTexture', GLTexture);

    window.Tangram = module.exports = {
        LeafletLayer,
        leafletLayer,
        debug
    };
});

Utils.inWorkerThread(() => {
    self.Tangram = {
        debug
    };
});
<|endoftext|>


<commit_before>import { combine, merge } from 'most'
import { domEvent } from '@most/dom-event'
import classnames from 'classnames';

import { h, component, inputTypes } from 'stream-dom'
import * as actions from '../model/todo-actions'
import { TodoList } from './TodoList'

const input = {
  todos$: inputTypes.observable,
  locationHash$: inputTypes.observable,
  filter$: inputTypes.observable,
  filterRoutes: inputTypes.object
}

function structure ({
  todos$: allTodos$,
  locationHash$,
  filter$,
  filterRoutes: {
    hashShowActive,
    hashShowCompleted,
    hashDefault
  }
}) {
  // TODO: Rename to filteredTodos$
  const todos$ = combine(
    (allTodos, filter) => allTodos.filter(filter),
    allTodos$,
    filter$
  )

  const activeCount$ = allTodos$.map(
    todos => todos.reduce((count, t) => count + (t.completed ? 0 : 1), 0)
  )

  const footerStyle$ = allTodos$.map(todos => {
    const type = todos.length === 0 ? 'none' : ''
    return `display: ${type}`
  })

  return (

todos

        <input node-name="newTodo"
          class="new-todo"
          placeholder="What needs to be done?"
          autofocus/>

        <input node-name="toggleAll"
          id="toggle-all" type="checkbox" class="toggle-all"/>
        Mark all as complete
        <TodoList node-name="todoList" todos$={todos$} />

          {activeCount$} items left

          <FilterListItem hash={hashDefault} locationHash$={locationHash$} label="All" />
          <FilterListItem hash={hashShowActive} locationHash$={locationHash$} label="Active" />
          <FilterListItem hash={hashShowCompleted} locationHash$={locationHash$} label="Completed" />

        <button node-name="clearCompleted" type="button" class="clear-completed">
          Clear Completed
        </button>

Double-click to edit a todo

Created by Brandon Payton

)
}

function output ({
  newTodo,
  toggleAll,
  clearCompleted,
  todoList
}) {
  const newTodo$ = domEvent('keypress', newTodo)
    .filter(e => e.key === 'Enter')
    .map(e => {
      const text = e.target.value.trim()
      e.target.value = ''
      return text
    })
    .filter(text => text.length > 0)
    .map(text => actions.create(text))

  const toggleAll$ = domEvent('change', toggleAll).map(
    e => actions.toggleAll(e.target.checked)
  )

  const clearCompleted$ = domEvent('click', clearCompleted).map(
    () => actions.destroyAllCompleted()
  )

  return {
    action$: merge(newTodo$, toggleAll$, clearCompleted$, todoList.action$)
  }
}

export const App = component({ input, structure, output })

const FilterListItem = component({
  input: {
    hash: inputTypes.string,
    locationHash$: inputTypes.observable,
    label: inputTypes.string
  },
  structure: ({ hash, locationHash$, label }) => {
    const class$ = locationHash$.map(currentHash => classnames({
      selected: hash === currentHash
    }))

    return (

      {label}

)
  }
})
<commit_msg>Fix todomvc clear-completed visibility<commit_after>import { combine, merge } from 'most'
import { domEvent } from '@most/dom-event'
import classnames from 'classnames';

import { h, component, inputTypes } from 'stream-dom'
import * as actions from '../model/todo-actions'
import { TodoList } from './TodoList'

const input = {
  todos$: inputTypes.observable,
  locationHash$: inputTypes.observable,
  filter$: inputTypes.observable,
  filterRoutes: inputTypes.object
}

function structure ({
  todos$: allTodos$,
  locationHash$,
  filter$,
  filterRoutes: {
    hashShowActive,
    hashShowCompleted,
    hashDefault
  }
}) {
  // TODO: Rename to filteredTodos$
  const todos$ = combine(
    (allTodos, filter) => allTodos.filter(filter),
    allTodos$,
    filter$
  )

  const activeCount$ = allTodos$.map(
    todos => todos.reduce((count, t) => count + (t.completed ? 0 : 1), 0)
  )
  const completedCount$ = allTodos$.map(
    todos => todos.reduce((count, t) => count + (t.completed ? 1 : 0), 0)
  )

  const footerStyle$ = allTodos$.map(
    todos => todos.length === 0 ? `display: none` : ``
  )
  const clearCompletedStyle$ = completedCount$.map(
    completedCount => completedCount === 0 ? `display: none` : ``
  )

  return (

todos

        <input node-name="newTodo"
          class="new-todo"
          placeholder="What needs to be done?"
          autofocus/>

        <input node-name="toggleAll"
          id="toggle-all" type="checkbox" class="toggle-all"/>
        Mark all as complete
        <TodoList node-name="todoList" todos$={todos$} />

          {activeCount$} items left

          <FilterListItem hash={hashDefault} locationHash$={locationHash$} label="All" />
          <FilterListItem hash={hashShowActive} locationHash$={locationHash$} label="Active" />
          <FilterListItem hash={hashShowCompleted} locationHash$={locationHash$} label="Completed" />

        <button node-name="clearCompleted"
          type="button"
          class="clear-completed"
          style={clearCompletedStyle$}>Clear Completed</button>

Double-click to edit a todo

Created by Brandon Payton

)
}

function output ({
  newTodo,
  toggleAll,
  clearCompleted,
  todoList
}) {
  const newTodo$ = domEvent('keypress', newTodo)
    .filter(e => e.key === 'Enter')
    .map(e => {
      const text = e.target.value.trim()
      e.target.value = ''
      return text
    })
    .filter(text => text.length > 0)
    .map(text => actions.create(text))

  const toggleAll$ = domEvent('change', toggleAll).map(
    e => actions.toggleAll(e.target.checked)
  )

  const clearCompleted$ = domEvent('click', clearCompleted).map(
    () => actions.destroyAllCompleted()
  )

  return {
    action$: merge(newTodo$, toggleAll$, clearCompleted$, todoList.action$)
  }
}

export const App = component({ input, structure, output })

const FilterListItem = component({
  input: {
    hash: inputTypes.string,
    locationHash$: inputTypes.observable,
    label: inputTypes.string
  },
  structure: ({ hash, locationHash$, label }) => {
    const class$ = locationHash$.map(currentHash => classnames({
      selected: hash === currentHash
    }))

    return (

      {label}

)
  }
})
<|endoftext|>


<commit_before>const SuperPlug = require('superplug')
const http = require('../http')
const getFromObject = require('../utils/getFromObject')
const debug = require('debug')('genie-router:plugins/Loader')
const path = require('path')
const os = require('os')
const Promise = require('bluebird')
const stat = Promise.promisify(require('fs').stat)
const mkdir = Promise.promisify(require('fs').mkdir)
const writeFile = Promise.promisify(require('fs').writeFile)
const npmUtils = require('npm-utils')

class Loader
{
  constructor (config, clientStartObjectGenerator, brainSelector, eventEmitter) {
    this.config = config
    this.plugins = {}
    this.clients = {}
    this.clientStartObjectGenerator = clientStartObjectGenerator
    this.brainSelector = brainSelector
    this.eventEmitter = eventEmitter
  }

  setHttpEnabled (value) {
    this.httpEnabled = value
  }

  /**
   * Returns a promise that resolves when all plugins are loaded.
   * @return {Promise}
   */
  startPlugins () {
    return this._getPluginStoreLocation()
      .then((location) => {
        this.location = location
        return stat(path.join(location, 'node_modules'))
      })
      .catch((err) => {
        if (err.code === 'ENOENT') {
          return this._npmInstall()
        }
        throw err
      })
      .then(() => {
        this._superPlug = new SuperPlug({
          location: this.location,
          packageProperty: 'genieRouterPlugin'
        })
      })
      .then(this._loadPlugins.bind(this))
      .then(this._startPlugins.bind(this))
  }

  /**
   * If this a configured path, make sure we can access it, if it is the default, create it if it does not exists.
   * @return Promise
   */
  _getPluginStoreLocation () {
    const configuredPath = getFromObject(this.config, 'pluginStore.location', false)
    if (configuredPath) {
      debug('Found configured path', configuredPath)
      return this._checkConfiguredPath(configuredPath)
    } else {
      return this._useDefaultConfigurationPath()
    }
  }

  /**
   * Make sure that we can access the configured path.
   * @return Promise
   */
  _checkConfiguredPath (configuredPath) {
    return stat(configuredPath)
      .then((result) => {
        if (result.isDirectory()) {
          return configuredPath
        }
        throw new Error('Configured pluginStore location is not a directory.')
      })
      .then(this._checkPluginLocation)
  }

  /**
   * Check if the default configuration path ($HOME/.genie-router) exists,
   * if not, create it.
   */
  _useDefaultConfigurationPath () {
    const defaultPath = path.join(os.homedir(), '.genie-router')
    return stat(defaultPath)
      .then((result) => {
        debug(defaultPath, 'found')
        if (!result.isDirectory()) {
          throw new Error(defaultPath + ' exists, but is not a directory')
        }
        return defaultPath
      })
      .catch((err) => {
        if (err.code === 'ENOENT') {
          debug(defaultPath, 'did not exist.')
          // Attempt to create the folder
          return mkdir(defaultPath)
            .then(() => {
              debug('Created', defaultPath)
              return defaultPath
            })
        } else {
          throw err
        }
      })
      .then(this._checkPluginLocation)
  }

  _checkPluginLocation (pluginPath) {
    debug('Checking if ' + pluginPath + '/package.json exists')

    return stat(path.join(pluginPath, 'package.json'))
      .then((statResult) => {
        if (statResult.isFile()) {
          debug('Found package.json in ', pluginPath)
          return pluginPath // It exists, we can return the Promise
        }
        throw new Error(pluginPath + '/package.json is not a file.')
      })
      .catch((err) => {
        if (err.code !== 'ENOENT') {
          throw err
        }

        // Create package.json
        debug('Creating package.json in folder', pluginPath)
        return writeFile(
          path.join(pluginPath, 'package.json'),
          JSON.stringify({
            name: 'genie-router-plugins',
            dependencies: { // Default dependencies
              'genie-router-cli-local': 'github:matueranet/genie-router-plugin-cli-local',
              'genie-router-plugin-echo': 'github:matueranet/genie-router-plugin-echo'
            }
          })
        )
      })
      .then(() => {
        return pluginPath
      })
  }

  getClients () {
    return this.clients
  }

  _startPlugins () {
    const plugins = Object.keys(this.plugins)
    debug('Found plugins: ', plugins)
    let promises = []
    let brains = {}
    plugins.forEach((pluginName) => {
      const config = getFromObject(this.config, 'plugins.' + pluginName)
      const plugin = this.plugins[pluginName]
      if (plugin.brain) {
        promises.push(
          this.plugins[pluginName].brain.start(config)
          .then((brain) => {
            brains[pluginName] = brain
          })
        )
      }
      if (plugin.client) {
        // thing here is that the startObject needs the speak function, which
        // is only available after the start function has been invoked. Needed
        // some trickery to make it work.
        const startObject = this.clientStartObjectGenerator(pluginName, plugin)
        promises.push(
          this.plugins[pluginName].client.start(
            config,
            startObject
          ).then((client) => {
            this.clients[pluginName] = client
          })
        )
      }
      if (plugin.brainSelector) {
        promises.push(
          this.plugins[pluginName].brainSelector.start(config)
          .then((brainSelector) => {
            this.brainSelector.use(pluginName, brainSelector)
          })
        )
      }
      if (plugin.http) {
        if (!this.httpEnabled) {
          console.log('HTTP is not enabled, Ignoring http component of plugin', pluginName)
        } else {
          promises.push(
            http()
              .then((app) => {
                return this.plugins[pluginName].http.start(config, app)
              })
          )
        }
      }
      if (plugin.listener) {
        promises.push(
          this.plugins[pluginName].listener.start(config, this.eventEmitter)
        )
      }
    })
    return Promise.all(promises)
      .then(() => {
        this.brainSelector.setBrains(brains)
      })
  }

  _loadPlugins () {
    return this._superPlug.getPlugins()
      .then((foundPlugins) => {
        let promises = []
        for (var iter in foundPlugins) {
          let foundPlugin = foundPlugins[iter]
          // Only load the plugins which have a configuration
          if (getFromObject(this.config, 'plugins.' + foundPlugin.getName()) !== undefined) {
            const p = foundPlugin.getPlugin()
            p.then((pluginModule) => {
              this.plugins[foundPlugin.getName()] = pluginModule
            })
            promises.push(p)
          }
        }
        return Promise.all(promises)
      })
  }

  /**
   * Runs 'npm install' without a module name, so that the modules are installed/updated.
   * @return Promise
   */
  _npmInstall () {
    const cwd = process.cwd()
    process.chdir(this.location)
    return npmUtils.install({
      name: '',
      flags: ['--quiet', '--production']
    })
      .then(() => {
        // move back to the old current dir
        process.chdir(cwd)
      })
  }
}

module.exports = Loader
<commit_msg>Added express to http plugin start invocation.<commit_after>const SuperPlug = require('superplug')
const http = require('../http')
const getFromObject = require('../utils/getFromObject')
const debug = require('debug')('genie-router:plugins/Loader')
const path = require('path')
const os = require('os')
const Promise = require('bluebird')
const stat = Promise.promisify(require('fs').stat)
const mkdir = Promise.promisify(require('fs').mkdir)
const writeFile = Promise.promisify(require('fs').writeFile)
const npmUtils = require('npm-utils')
const express = require('express')

class Loader
{
  constructor (config, clientStartObjectGenerator, brainSelector, eventEmitter) {
    this.config = config
    this.plugins = {}
    this.clients = {}
    this.clientStartObjectGenerator = clientStartObjectGenerator
    this.brainSelector = brainSelector
    this.eventEmitter = eventEmitter
  }

  setHttpEnabled (value) {
    this.httpEnabled = value
  }

  /**
   * Returns a promise that resolves when all plugins are loaded.
   * @return {Promise}
   */
  startPlugins () {
    return this._getPluginStoreLocation()
      .then((location) => {
        this.location = location
        return stat(path.join(location, 'node_modules'))
      })
      .catch((err) => {
        if (err.code === 'ENOENT') {
          return this._npmInstall()
        }
        throw err
      })
      .then(() => {
        this._superPlug = new SuperPlug({
          location: this.location,
          packageProperty: 'genieRouterPlugin'
        })
      })
      .then(this._loadPlugins.bind(this))
      .then(this._startPlugins.bind(this))
  }

  /**
   * If this a configured path, make sure we can access it, if it is the default, create it if it does not exists.
   * @return Promise
   */
  _getPluginStoreLocation () {
    const configuredPath = getFromObject(this.config, 'pluginStore.location', false)
    if (configuredPath) {
      debug('Found configured path', configuredPath)
      return this._checkConfiguredPath(configuredPath)
    } else {
      return this._useDefaultConfigurationPath()
    }
  }

  /**
   * Make sure that we can access the configured path.
   * @return Promise
   */
  _checkConfiguredPath (configuredPath) {
    return stat(configuredPath)
      .then((result) => {
        if (result.isDirectory()) {
          return configuredPath
        }
        throw new Error('Configured pluginStore location is not a directory.')
      })
      .then(this._checkPluginLocation)
  }

  /**
   * Check if the default configuration path ($HOME/.genie-router) exists,
   * if not, create it.
   */
  _useDefaultConfigurationPath () {
    const defaultPath = path.join(os.homedir(), '.genie-router')
    return stat(defaultPath)
      .then((result) => {
        debug(defaultPath, 'found')
        if (!result.isDirectory()) {
          throw new Error(defaultPath + ' exists, but is not a directory')
        }
        return defaultPath
      })
      .catch((err) => {
        if (err.code === 'ENOENT') {
          debug(defaultPath, 'did not exist.')
          // Attempt to create the folder
          return mkdir(defaultPath)
            .then(() => {
              debug('Created', defaultPath)
              return defaultPath
            })
        } else {
          throw err
        }
      })
      .then(this._checkPluginLocation)
  }

  _checkPluginLocation (pluginPath) {
    debug('Checking if ' + pluginPath + '/package.json exists')

    return stat(path.join(pluginPath, 'package.json'))
      .then((statResult) => {
        if (statResult.isFile()) {
          debug('Found package.json in ', pluginPath)
          return pluginPath // It exists, we can return the Promise
        }
        throw new Error(pluginPath + '/package.json is not a file.')
      })
      .catch((err) => {
        if (err.code !== 'ENOENT') {
          throw err
        }

        // Create package.json
        debug('Creating package.json in folder', pluginPath)
        return writeFile(
          path.join(pluginPath, 'package.json'),
          JSON.stringify({
            name: 'genie-router-plugins',
            dependencies: { // Default dependencies
              'genie-router-cli-local': 'github:matueranet/genie-router-plugin-cli-local',
              'genie-router-plugin-echo': 'github:matueranet/genie-router-plugin-echo'
            }
          })
        )
      })
      .then(() => {
        return pluginPath
      })
  }

  getClients () {
    return this.clients
  }

  _startPlugins () {
    const plugins = Object.keys(this.plugins)
    debug('Found plugins: ', plugins)
    let promises = []
    let brains = {}
    plugins.forEach((pluginName) => {
      const config = getFromObject(this.config, 'plugins.' + pluginName)
      const plugin = this.plugins[pluginName]
      if (plugin.brain) {
        promises.push(
          this.plugins[pluginName].brain.start(config)
          .then((brain) => {
            brains[pluginName] = brain
          })
        )
      }
      if (plugin.client) {
        // thing here is that the startObject needs the speak function, which
        // is only available after the start function has been invoked. Needed
        // some trickery to make it work.
        const startObject = this.clientStartObjectGenerator(pluginName, plugin)
        promises.push(
          this.plugins[pluginName].client.start(
            config,
            startObject
          ).then((client) => {
            this.clients[pluginName] = client
          })
        )
      }
      if (plugin.brainSelector) {
        promises.push(
          this.plugins[pluginName].brainSelector.start(config)
          .then((brainSelector) => {
            this.brainSelector.use(pluginName, brainSelector)
          })
        )
      }
      if (plugin.http) {
        if (!this.httpEnabled) {
          console.log('HTTP is not enabled, Ignoring http component of plugin', pluginName)
        } else {
          promises.push(
            http()
              .then((app) => {
                return this.plugins[pluginName].http.start(config, app, express)
              })
          )
        }
      }
      if (plugin.listener) {
        promises.push(
          this.plugins[pluginName].listener.start(config, this.eventEmitter)
        )
      }
    })
    return Promise.all(promises)
      .then(() => {
        this.brainSelector.setBrains(brains)
      })
  }

  _loadPlugins () {
    return this._superPlug.getPlugins()
      .then((foundPlugins) => {
        let promises = []
        for (var iter in foundPlugins) {
          let foundPlugin = foundPlugins[iter]
          // Only load the plugins which have a configuration
          if (getFromObject(this.config, 'plugins.' + foundPlugin.getName()) !== undefined) {
            const p = foundPlugin.getPlugin()
            p.then((pluginModule) => {
              this.plugins[foundPlugin.getName()] = pluginModule
            })
            promises.push(p)
          }
        }
        return Promise.all(promises)
      })
  }

  /**
   * Runs 'npm install' without a module name, so that the modules are installed/updated.
   * @return Promise
   */
  _npmInstall () {
    const cwd = process.cwd()
    process.chdir(this.location)
    return npmUtils.install({
      name: '',
      flags: ['--quiet', '--production']
    })
      .then(() => {
        // move back to the old current dir
        process.chdir(cwd)
      })
  }
}

module.exports = Loader
<|endoftext|>