<commit_before>/**
 * Created by Jared Rewerts on 3/15/2016.
 */
"use strict";

var Pixi = require('pixi.js');
var PixiParticles = require('pixi-particles'); // jshint ignore:line
var Physics = require('physicsjs');
var Util = require('polyball/shared/Util');
var Blackhole = require('polyball/shared/model/powerups/Blackhole');
var StyleCommons = require('polyball/shared/StyleCommons');
var Logger = require('polyball/shared/Logger');

// Shim in the PIXI global that Physics.js is looking for to do its primitive rendering.
window.PIXI = Pixi;

// Declare the polyball renderer as a large extension of the existing pixi renderer.
Physics.renderer('polyball', 'pixi', function (parent) {

    //
    //    ########  ########  #### ##     ##    ###    ######## ########
    //    ##     ## ##     ##  ##  ##     ##   ## ##      ##    ##
    //    ##     ## ##     ##  ##  ##     ##  ##   ##     ##    ##
    //    ########  ########   ##  ##     ## ##     ##    ##    ######
    //    ##        ##   ##    ##   ##   ##  #########    ##    ##
    //    ##        ##    ##   ##    ## ##   ##     ##    ##    ##
    //    ##        ##     ## ####    ###    ##     ##    ##    ########
    //
    ///////////////////////////////////////////////////////////////////////////

    var model;
    var emitterContainer;
    var emitters;
    var elapsed;
    var bhRendered;
    var textContainer;
    var bhSprite;
    var bhContainer;
    var twistFilter;
    var twistUp;
    var titleID;
    var self;

    /**
     * Applies the visual effects for the black hole to the arena.
     */
    var activateBlackhole = function() {
        twistFilter = new Pixi.filters.TwistFilter();
        twistFilter.radius = 0.08;
        twistFilter.offset.x = 0.5;
        twistFilter.offset.y = 0.5;
        twistFilter.angle = 0;

        twistUp = true;

        self.stage.addChild(bhContainer);

        self.stage.filters = [twistFilter];
    };

    /**
     * Removes the visual effects of the black hole.
     */
    var deactivateBlackhole = function() {
        self.stage.filters = null;
        self.stage.removeChild(bhContainer);
    };

    /**
     * Updates the particle rendering.
     */
    var update = function() {
        requestAnimationFrame(update);

        if (twistFilter) {
            var twistMax = 23;
            var twistMin = 7;
            var twistChange = 0.3;

            if (twistUp) {
                twistFilter.angle += twistChange;
            }
            else {
                twistFilter.angle -= twistChange;
            }

            if (twistFilter.angle > twistMax) {
                twistUp = false;
            }
            else if (twistFilter.angle < twistMin) {
                twistUp = true;
            }

            if (Math.random() > 0.995) {
                twistUp = !twistUp;
            }

        }

        var now = Date.now();

        // The emitter requires the elapsed
        // number of seconds since the last update
        for (var i = 0; i < emitters.length; i++) {
            emitters[i].update((now - elapsed) * 0.001);
        }

        elapsed = now;
    };

    //
    //    ########  ##     ## ########  ##       ####  ######
    //    ##     ## ##     ## ##     ## ##        ##  ##    ##
    //    ##     ## ##     ## ##     ## ##        ##  ##
    //    ########  ##     ## ########  ##        ##  ##
    //    ##        ##     ## ##     ## ##        ##  ##
    //    ##        ##     ## ##     ## ##        ##  ##    ##
    //    ##         #######  ########  ######## ####  ######
    //
    ///////////////////////////////////////////////////////////////////////////

    return {

        /**
         * This is the initializer that's called from `var renderer = Physics.renderer('polyball', options);`
         * @param {Object} config
         * @property {Model} config.model
         */
        init: function (config) {
            // initialize the parent pixi renderer
            parent.init.call(this, config);

            self = this;

            bhRendered = false;
            titleID = 'title-screen';

            textContainer = new Pixi.Container();
            this.stage.addChild(textContainer);

            model = config.model;

            emitterContainer = new Pixi.Container();
            emitterContainer.layer = 100;
            this.stage.addChild(emitterContainer);

            emitters = [];

            bhContainer = new Pixi.Container();
            bhContainer.layer = -1;
            bhSprite = Pixi.Sprite.fromImage('res/blackhole.png');
            bhSprite.anchor.set(0.5, 0.5);
            bhContainer.addChild(bhSprite);

            elapsed = Date.now();
        },

        /**
         * This creates the initial view of a physics body for polyball to augment.
         * @param geometry - The physics geometry to render.
         * @param styles - The styles object associated with the specific physics body, or the geometry in general.
         * @returns {*}
         */
        createView: function (geometry, styles) {

            var view = parent.createView.call(this, geometry, styles);

            styles = styles || this.options.styles.circle || {};

            // Handle view layers.
            if (styles.layer) {
                view.layer = styles.layer;
            }
            else {
                view.layer = 0;
            }

            this.stage.children.sort(function(a, b) {
                return a.layer - b.layer;
            });

            // Add child icons if specified.
            // \uf0d6: dollar bill
            // \uf155: dollar sign
            // \uf111: circle
            // \uf254: hourglass
            // \uf069: asterisk
            // \uf219: diamond
            if (styles.icon) {
                var text = new Pixi.Text(styles.icon, styles.fontstyle);
                text.anchor.set(0.5, 0.5);
                view.addChild(text);
            }

            return view;
        },

        /**
         * This function forces awesomefonts to be loaded. If this isn't called,
         * icons won't show up.
         */
        forceFontLoad: function() {
            var text = new Pixi.Text('\uf254', {fill: '#ffffff', font: '40px fontawesome'});
            text.anchor.set(0.5, 0.5);
            text.position.set(50, 50);
            this.stage.addChild(text);
            text.renderable = false;
        },

        /**
         * Adds text to the scene. If the Text object already exists, it's modified.
         * @param {String} text
         * @param {Object} style - the Pixi.Text style object
         * @param {Point} offset - the offset of Text relative to textContainer
         * @param {Number} rotation - the rotation of the text object
         * @param {String} id - used to uniquely identify text that already exists
         */
        addText: function(text, style, offset, rotation, id) {
            var textObject;

            // See if there exists a Text
            var textObjects = textContainer.children.filter(function(textChild) {
                return textChild.polyID === id;
            });

            if (textObjects.length > 1) {
                Logger.error('Found multiple Text objects with id: ' + id + ' in addText');
            }
            else if (textObjects.length === 1) {
                textObject = textObjects[0];
                textObject.text = text;
                textObject.style = style;
                textObject.anchor.set(0.5, 0.5);
                textObject.position = offset;
                textObject.rotation = rotation;
            }
            else {
                textObject = new Pixi.Text(text, style);
                textObject.anchor.set(0.5, 0.5);
                textObject.position = offset;
                textObject.rotation = rotation;
                textObject.polyID = id;

                textContainer.addChild(textObject);
            }
        },

        // NOTE: Used to be called render(), but overriding that is a last resort for physics renderers.
        //       See https://github.com/wellcaffeinated/PhysicsJS/wiki/Renderers#further-customization
        beforeRender: function() {
            var style;
            var center;
            var rotation;

            if (model.getArena() !== undefined) {
                // Remove the title
                var textObjects = textContainer.children.filter(function(textChild) {
                    return textChild.polyID === titleID;
                });
                if (textObjects.length > 0) {
                    textObjects[0].visible = false;
                }

                style = StyleCommons.fontStyle;
                center = model.getArena().getCenter();

                // Add/update the round timer
                // Update the round timer
                if (model.currentRoundTime) {
                    var timePoint = new Physics.vector(this.renderer.width / 2, this.renderer.height / 2);
                    this.addText(model.currentRoundTime.toString(), style, timePoint, 0, 'round-timer');
                }

                this.stage.pivot.set(center.x, center.y);
                this.stage.position.set(center.x, center.y);
                bhContainer.position.set(center.x, center.y);
                textContainer.pivot.set(center.x, center.y);
                textContainer.position.set(center.x, center.y);

                var players = model.getPlayers();

                var oldPlayersText = textContainer.children.filter(function(textChild) {
                    if (textChild.polyID.indexOf('player') !== -1) {
                        var discoveries = players.filter(function(player) {
                            return textChild.polyID === 'player' + player.id;
                        });

                        if (discoveries.length === 0) {
                            return textChild;
                        }
                    }
                    return [];
                });

                oldPlayersText.forEach(function(player) {
                    textContainer.removeChild(player);
                });

                for (var i = 0; i < players.length; i++) {
                    var player = players[i];
                    var worldPos = model.getArena().getScorePosition(player.arenaPosition);
                    var localPos = this.worldToClient(worldPos);
                    rotation = (model.getArena().getGoalRotation(player.arenaPosition) + this.stage.rotation);

                    if (rotation < -Math.PI / 2 && rotation > -(3/2) * Math.PI) {
                        rotation = rotation - Math.PI;
                    }
                    else if (rotation > Math.PI / 2 && rotation < (3/2) * Math.PI) {
                        rotation = rotation - Math.PI;
                    }

                    this.addText(player.client.name + ': ' + player.score, style, localPos, rotation, 'player' + player.id);
                }

                var localPlayer = model.getPlayer(model.getLocalClientID());
                if (model.playerCount() > 0 && localPlayer !== undefined) {
                    rotation = localPlayer.arenaPosition * 2*Math.PI / model.getArena().getBumpers().length;

                    this.stage.rotation = 0;
                    this.rotate(rotation);
                }

                var desiredX = this.renderer.view.width / 2;
                var desiredY = this.renderer.view.height / 2;

                this.stage.position.set(desiredX, desiredY);
            }
            else {
                style = StyleCommons.titleFontStyle;
                center = {
                    x : this.renderer.width / 2,
                    y : this.renderer.height / 2
                };

                this.addText('polyball', style, center, 0, titleID);
            }

            var blackholes = model.getPowerups(function(x) {
                return x.name === Blackhole.Name;
            });

            if (blackholes.length > 0 && bhRendered === false && blackholes[0].active === true) {
                activateBlackhole();
                bhRendered = true;
            }
            else if (blackholes.length > 0 && blackholes[0].active === false) {
                deactivateBlackhole();
                bhRendered = false;
            }
            else if (blackholes.length === 0 && bhRendered === true) {
                deactivateBlackhole();
                bhRendered = false;
            }
        },

        /**
         * This disobeys the Physics js pure renderer interface, because our renderer already has a model, and thus a world.
         * This method just renders the world!
         */
        renderGame: function () {
            // Assumes that world.add(this) has been done.  See createNew() function at the bottom of this file.
            model.getWorld().render();
        },

        renderParticles: function() {
            update();
        },

        /**
         * Adds an emitter.
         * @param imageArray An array of different Pixi images to use as particles.
         * @param config The PixiParticles config object to use.
         */
        addEmitter: function(imageArray, config) {
            var emitter = new window.cloudkid.Emitter(
                emitterContainer,
                imageArray,
                config
            );
            emitters.push(emitter);
        },

        addTestEmitter: function() {
            this.addEmitter([Pixi.Texture.fromImage('res/Sparks.png')],
                {
                    "alpha": {
                        "start": 1,
                        "end": 0.31
                    },
                    "scale": {
                        "start": 0.5,
                        "end": 1
                    },
                    "color": {
                        "start": "ffffff",
                        "end": "9ff3ff"
                    },
                    "speed": {
                        "start": 100,
                        "end": 200
                    },
                    "startRotation": {
                        "min": 225,
                        "max": 320
                    },
                    "rotationSpeed": {
                        "min": 0,
                        "max": 20
                    },
                    "lifetime": {
                        "min": 0.25,
                        "max": 0.5
                    },
                    "blendMode": "normal",
                    "frequency": 0.001,
                    "emitterLifetime": 100,
                    "maxParticles": 1000,
                    "pos": {
                        "x": Util.getRandomInt(0, 600),
                        "y": Util.getRandomInt(0, 600)
                    },
                    "addAtBack": false,
                    "spawnType": "circle",
                    "spawnCircle": {
                        "x": 0,
                        "y": 0,
                        "r": 0
                    }
                }
            );
        },

        /**
         * Rotates the renderer. If the pivot/location hasn't been set,
         * it can result in rendered objects in strange places.
         * @param radians: number
         */
        rotate: function(radians) {
            var stage = this.stage;
            stage.rotation += radians;
            textContainer.rotation = -radians;
        },

        /**
         * Resizes the rendering area. Good to do on window resizes.
         * @param width: number
         * @param height: number
         */
        resize: function(width, height) {
            this.renderer.view.style.position = 'absolute';
            //this.renderer.view.style.left = '50%';
            //this.renderer.view.style.top = '50%';
            //this.renderer.view.style.transform = 'translate3d( -50%, -50%, 0 )';
            if (!height || !width) {
                Logger.debug(
                    "renderer resize called with suspicious values: width " +
                    width +
                    ", height " +
                    height);

                return;
            }

            parent.resize.call(this, width, height);
            this.renderer.resize(width, height);
        },

        worldToClient: function(point) {
            var rotation = this.stage.rotation;
            var offset = this.stage.position;
            var center = model.getArena().getCenter();

            point.rotate(rotation, center);
            point.x = point.x + offset.x - center.x;
            point.y = point.y + offset.y - center.y;

            return point;
        },

        unrotate: function(point) {
            var rotation = this.stage.rotation;
            var center = model.getArena().getCenter();

            point.rotate(-rotation, center);

            return point;
        }

    };
});

/***
 * Creates a polyball renderer and adds it to the model's world.
 *
 * @param {Object} config
 * @property {Model} config.model
 */
module.exports.createNew = function(config) {
    var renderer = Physics.renderer('polyball', config);

    var world = config.model.getWorld();
    world.add(renderer);

    return renderer;
};
<commit_msg>Added emitter CRUD methods.<commit_after>/**
 * Created by Jared Rewerts on 3/15/2016.
 */
"use strict";

var Pixi = require('pixi.js');
var PixiParticles = require('pixi-particles'); // jshint ignore:line
var Physics = require('physicsjs');
var Blackhole = require('polyball/shared/model/powerups/Blackhole');
var StyleCommons = require('polyball/shared/StyleCommons');
var Logger = require('polyball/shared/Logger');

// Shim in the PIXI global that Physics.js is looking for to do its primitive rendering.
window.PIXI = Pixi;

// Declare the polyball renderer as a large extension of the existing pixi renderer.
Physics.renderer('polyball', 'pixi', function (parent) {

    //
    //    ########  ########  #### ##     ##    ###    ######## ########
    //    ##     ## ##     ##  ##  ##     ##   ## ##      ##    ##
    //    ##     ## ##     ##  ##  ##     ##  ##   ##     ##    ##
    //    ########  ########   ##  ##     ## ##     ##    ##    ######
    //    ##        ##   ##    ##   ##   ##  #########    ##    ##
    //    ##        ##    ##   ##    ## ##   ##     ##    ##    ##
    //    ##        ##     ## ####    ###    ##     ##    ##    ########
    //
    ///////////////////////////////////////////////////////////////////////////

    var model;
    var emitterContainer;
    var emitters;
    var elapsed;
    var bhRendered;
    var textContainer;
    var bhSprite;
    var bhContainer;
    var twistFilter;
    var twistUp;
    var titleID;
    var self;

    /**
     * Applies the visual effects for the black hole to the arena.
     */
    var activateBlackhole = function() {
        twistFilter = new Pixi.filters.TwistFilter();
        twistFilter.radius = 0.08;
        twistFilter.offset.x = 0.5;
        twistFilter.offset.y = 0.5;
        twistFilter.angle = 0;

        twistUp = true;

        self.stage.addChild(bhContainer);

        self.stage.filters = [twistFilter];
    };

    /**
     * Removes the visual effects of the black hole.
     */
    var deactivateBlackhole = function() {
        self.stage.filters = null;
        self.stage.removeChild(bhContainer);
    };

    /**
     * Updates the particle rendering.
     */
    var update = function() {
        requestAnimationFrame(update);

        if (twistFilter) {
            var twistMax = 23;
            var twistMin = 7;
            var twistChange = 0.3;

            if (twistUp) {
                twistFilter.angle += twistChange;
            }
            else {
                twistFilter.angle -= twistChange;
            }

            if (twistFilter.angle > twistMax) {
                twistUp = false;
            }
            else if (twistFilter.angle < twistMin) {
                twistUp = true;
            }

            if (Math.random() > 0.995) {
                twistUp = !twistUp;
            }

        }

        var now = Date.now();

        // The emitter requires the elapsed
        // number of seconds since the last update
        for (var i = 0; i < emitters.length; i++) {
            emitters[i].update((now - elapsed) * 0.001);
        }

        elapsed = now;
    };

    //
    //    ########  ##     ## ########  ##       ####  ######
    //    ##     ## ##     ## ##     ## ##        ##  ##    ##
    //    ##     ## ##     ## ##     ## ##        ##  ##
    //    ########  ##     ## ########  ##        ##  ##
    //    ##        ##     ## ##     ## ##        ##  ##
    //    ##        ##     ## ##     ## ##        ##  ##    ##
    //    ##         #######  ########  ######## ####  ######
    //
    ///////////////////////////////////////////////////////////////////////////

    return {

        /**
         * This is the initializer that's called from `var renderer = Physics.renderer('polyball', options);`
         * @param {Object} config
         * @property {Model} config.model
         */
        init: function (config) {
            // initialize the parent pixi renderer
            parent.init.call(this, config);

            self = this;

            bhRendered = false;
            titleID = 'title-screen';

            textContainer = new Pixi.Container();
            this.stage.addChild(textContainer);

            model = config.model;

            emitterContainer = new Pixi.Container();
            emitterContainer.layer = 100;
            this.stage.addChild(emitterContainer);

            emitters = [];

            bhContainer = new Pixi.Container();
            bhContainer.layer = -1;
            bhSprite = Pixi.Sprite.fromImage('res/blackhole.png');
            bhSprite.anchor.set(0.5, 0.5);
            bhContainer.addChild(bhSprite);

            elapsed = Date.now();
        },

        /**
         * This creates the initial view of a physics body for polyball to augment.
         * @param geometry - The physics geometry to render.
         * @param styles - The styles object associated with the specific physics body, or the geometry in general.
         * @returns {*}
         */
        createView: function (geometry, styles) {

            var view = parent.createView.call(this, geometry, styles);

            // Handle view layers.
            if (styles.layer) {
                view.layer = styles.layer;
            }
            else {
                view.layer = 0;
            }

            this.stage.children.sort(function(a, b) {
                return a.layer - b.layer;
            });

            // Add child icons if specified.
            // \uf0d6: dollar bill
            // \uf155: dollar sign
            // \uf111: circle
            // \uf254: hourglass
            // \uf069: asterisk
            // \uf219: diamond
            if (styles.icon) {
                var text = new Pixi.Text(styles.icon, styles.fontstyle);
                text.anchor.set(0.5, 0.5);
                view.addChild(text);
            }

            return view;
        },

        /**
         * This function forces awesomefonts to be loaded. If this isn't called,
         * icons won't show up.
         */
        forceFontLoad: function() {
            var text = new Pixi.Text('\uf254', {fill: '#ffffff', font: '40px fontawesome'});
            text.anchor.set(0.5, 0.5);
            text.position.set(50, 50);
            this.stage.addChild(text);
            text.renderable = false;
        },

        /**
         * Adds text to the scene. If the Text object already exists, it's modified.
         * @param {String} text
         * @param {Object} style - the Pixi.Text style object
         * @param {Point} offset - the offset of Text relative to textContainer
         * @param {Number} rotation - the rotation of the text object
         * @param {String} id - used to uniquely identify text that already exists
         */
        addText: function(text, style, offset, rotation, id) {
            var textObject;

            // See if there exists a Text
            var textObjects = textContainer.children.filter(function(textChild) {
                return textChild.polyID === id;
            });

            if (textObjects.length > 1) {
                Logger.error('Found multiple Text objects with id: ' + id + ' in addText');
            }
            else if (textObjects.length === 1) {
                textObject = textObjects[0];
                textObject.text = text;
                textObject.style = style;
                textObject.anchor.set(0.5, 0.5);
                textObject.position = offset;
                textObject.rotation = rotation;
            }
            else {
                textObject = new Pixi.Text(text, style);
                textObject.anchor.set(0.5, 0.5);
                textObject.position = offset;
                textObject.rotation = rotation;
                textObject.polyID = id;

                textContainer.addChild(textObject);
            }
        },

        // NOTE: Used to be called render(), but overriding that is a last resort for physics renderers.
        //       See https://github.com/wellcaffeinated/PhysicsJS/wiki/Renderers#further-customization
        beforeRender: function() {
            var style;
            var center;
            var rotation;

            if (model.getArena() !== undefined) {
                // Remove the title
                var textObjects = textContainer.children.filter(function(textChild) {
                    return textChild.polyID === titleID;
                });
                if (textObjects.length > 0) {
                    textObjects[0].visible = false;
                }

                style = StyleCommons.fontStyle;
                center = model.getArena().getCenter();

                // Add/update the round timer
                // Update the round timer
                if (model.currentRoundTime) {
                    var timePoint = new Physics.vector(this.renderer.width / 2, this.renderer.height / 2);
                    this.addText(model.currentRoundTime.toString(), style, timePoint, 0, 'round-timer');
                }

                this.stage.pivot.set(center.x, center.y);
                this.stage.position.set(center.x, center.y);
                bhContainer.position.set(center.x, center.y);
                textContainer.pivot.set(center.x, center.y);
                textContainer.position.set(center.x, center.y);

                var players = model.getPlayers();

                var oldPlayersText = textContainer.children.filter(function(textChild) {
                    if (textChild.polyID.indexOf('player') !== -1) {
                        var discoveries = players.filter(function(player) {
                            return textChild.polyID === 'player' + player.id;
                        });

                        if (discoveries.length === 0) {
                            return textChild;
                        }
                    }
                    return [];
                });

                oldPlayersText.forEach(function(player) {
                    textContainer.removeChild(player);
                });

                for (var i = 0; i < players.length; i++) {
                    var player = players[i];
                    var worldPos = model.getArena().getScorePosition(player.arenaPosition);
                    var localPos = this.worldToClient(worldPos);
                    rotation = (model.getArena().getGoalRotation(player.arenaPosition) + this.stage.rotation);

                    if (rotation < -Math.PI / 2 && rotation > -(3/2) * Math.PI) {
                        rotation = rotation - Math.PI;
                    }
                    else if (rotation > Math.PI / 2 && rotation < (3/2) * Math.PI) {
                        rotation = rotation - Math.PI;
                    }

                    this.addText(player.client.name + ': ' + player.score, style, localPos, rotation, 'player' + player.id);
                }

                var localPlayer = model.getPlayer(model.getLocalClientID());
                if (model.playerCount() > 0 && localPlayer !== undefined) {
                    rotation = localPlayer.arenaPosition * 2*Math.PI / model.getArena().getBumpers().length;

                    this.stage.rotation = 0;
                    this.rotate(rotation);
                }

                var desiredX = this.renderer.view.width / 2;
                var desiredY = this.renderer.view.height / 2;

                this.stage.position.set(desiredX, desiredY);
            }
            else {
                style = StyleCommons.titleFontStyle;
                center = {
                    x : this.renderer.width / 2,
                    y : this.renderer.height / 2
                };

                this.addText('polyball', style, center, 0, titleID);
            }

            // Render powerups
            model.getPowerups().forEach(function(powerup) {
                powerup.render(self);
            });

            var blackholes = model.getPowerups(function(x) {
                return x.name === Blackhole.Name;
            });

            if (blackholes.length > 0 && bhRendered === false && blackholes[0].active === true) {
                activateBlackhole();
                bhRendered = true;
            }
            else if (blackholes.length > 0 && blackholes[0].active === false) {
                deactivateBlackhole();
                bhRendered = false;
            }
            else if (blackholes.length === 0 && bhRendered === true) {
                deactivateBlackhole();
                bhRendered = false;
            }
        },

        /**
         * This disobeys the Physics js pure renderer interface, because our renderer already has a model, and thus a world.
         * This method just renders the world!
         */
        renderGame: function () {
            // Assumes that world.add(this) has been done.  See createNew() function at the bottom of this file.
            model.getWorld().render();
        },

        renderParticles: function() {
            update();
        },

        /**
         * Adds an emitter.
         * @param imageArray An array of different Pixi images to use as particles.
         * @param config The PixiParticles config object to use.
         */
        addEmitter: function(imageArray, config, container) {
            container = container || emitterContainer;

            var newImageArray = [];

            imageArray.forEach(function(image) {
                if (typeof image === 'string') {
                    newImageArray.push(Pixi.Texture.fromImage(image));
                }
                else {
                    newImageArray.push(image);
                }
            });

            var emitter = new window.cloudkid.Emitter(
                container,
                newImageArray,
                config
            );
            emitters.push(emitter);

            return emitter;
        },

        /**
         * Searches for and removes any emitter in both emitters and emitterContainer
         * @param emitter: cloudkid.Emitter
         */
        removeEmitter: function(emitter) {
            for (var i = 0; i < emitters.length; i++) {
                if (emitters[i] === emitter) {
                    emitters[i].cleanup();
                    emitters.splice(i, 1);
                }
            }
        },

        /**
         * Attempts to move the specified emitter to location.
         * @param emitter: cloudkid.Emitter
         * @param location: Point
         */
        moveEmitter: function(emitter, location) {
            var foundEmitters = emitters.filter(function(inEmitter) {
                return inEmitter === emitter;
            });

            if (foundEmitters.length > 0) {
                foundEmitters[0].updateOwnerPos(location.x, location.y);
            }
        },

        /**
         * Gets the array of emitters
         * @returns {array of cloudkid.Emitter}
         */
        getEmitters: function() {
            return emitters;
        },

        addTestEmitter: function(container) {
            return this.addEmitter([Pixi.Texture.fromImage('res/Sparks.png')],
                {
                    "alpha": {
                        "start": 1,
                        "end": 0.31
                    },
                    "scale": {
                        "start": 0.5,
                        "end": 1
                    },
                    "color": {
                        "start": "ffffff",
                        "end": "9ff3ff"
                    },
                    "speed": {
                        "start": 100,
                        "end": 200
                    },
                    "startRotation": {
                        "min": 225,
                        "max": 320
                    },
                    "rotationSpeed": {
                        "min": 0,
                        "max": 20
                    },
                    "lifetime": {
                        "min": 0.25,
                        "max": 0.5
                    },
                    "blendMode": "normal",
                    "frequency": 0.001,
                    "emitterLifetime": 100,
                    "maxParticles": 100,
                    "pos": {
                        "x": 0,
                        "y": 0
                    },
                    "addAtBack": false,
                    "spawnType": "circle",
                    "spawnCircle": {
                        "x": 0,
                        "y": 0,
                        "r": 0
                    }
                },
                container
            );
        },

        /**
         * Rotates the renderer. If the pivot/location hasn't been set,
         * it can result in rendered objects in strange places.
         * @param radians: number
         */
        rotate: function(radians) {
            var stage = this.stage;
            stage.rotation += radians;
            textContainer.rotation = -radians;
        },

        /**
         * Resizes the rendering area. Good to do on window resizes.
         * @param width: number
         * @param height: number
         */
        resize: function(width, height) {
            this.renderer.view.style.position = 'absolute';
            //this.renderer.view.style.left = '50%';
            //this.renderer.view.style.top = '50%';
            //this.renderer.view.style.transform = 'translate3d( -50%, -50%, 0 )';
            if (!height || !width) {
                Logger.debug(
                    "renderer resize called with suspicious values: width " +
                    width +
                    ", height " +
                    height);

                return;
            }

            parent.resize.call(this, width, height);
            this.renderer.resize(width, height);
        },

        worldToClient: function(point) {
            var rotation = this.stage.rotation;
            var offset = this.stage.position;
            var center = model.getArena().getCenter();

            var newPoint = Physics.vector(point.x, point.y);

            newPoint.rotate(rotation, center);
            newPoint.x = newPoint.x + offset.x - center.x;
            newPoint.y = newPoint.y + offset.y - center.y;

            return newPoint;
        },

        unrotate: function(point) {
            var rotation = this.stage.rotation;
            var center = model.getArena().getCenter();

            point.rotate(-rotation, center);

            return point;
        }

    };
});

/***
 * Creates a polyball renderer and adds it to the model's world.
 *
 * @param {Object} config
 * @property {Model} config.model
 */
module.exports.createNew = function(config) {
    var renderer = Physics.renderer('polyball', config);

    var world = config.model.getWorld();
    world.add(renderer);

    return renderer;
};
<|endoftext|>


<commit_before>(function (global) {
	var doc = global.document;
	var docEl = doc.documentElement;
	var body = doc.body || doc.createElement('body');

	function scrollX() {
		return (docEl.scrollLeft || body.scrollLeft || 0) - (docEl.clientLeft || body.clientLeft || 0);
	}

	function scrollY() {
		return (docEl.scrollTop || body.scrollTop || 0) - (docEl.clientTop || body.clientTop || 0);
	}

	function setStatically() {
		window.scrollX = scrollX();
		window.scrollY = scrollY();
		window.innerWidth = docEl.clientWidth;
		window.innerHeight = docEl.clientHeight;
	}

	try {
		Object.defineProperties(global, {
			'innerWidth': {
				get: function () {
					return docEl.clientWidth;
				}
			},
			'innerHeight': {
				get: function () {
					return docEl.clientHeight;
				}
			},
			'pageXOffset': {
				get: scrollX
			},
			'pageYOffset': {
				get: scrollY
			},
			'scrollX': {
				get: scrollX
			},
			'scrollY': {
				get: scrollY
			}
		});
	} catch(e) {
		window.attachEvent('onresize', setStatically);
		window.attachEvent('onscroll', setStatically);
		setStatically();
	}
})(this);
<commit_msg>Missing properties<commit_after>(function (global) {
	var doc = global.document;
	var docEl = doc.documentElement;
	var body = doc.body || doc.createElement('body');

	function scrollX() {
		return (docEl.scrollLeft || body.scrollLeft || 0) - (docEl.clientLeft || body.clientLeft || 0);
	}

	function scrollY() {
		return (docEl.scrollTop || body.scrollTop || 0) - (docEl.clientTop || body.clientTop || 0);
	}

	function setStatically() {
		window.scrollX = window.pageXOffset = scrollX();
		window.scrollY = window.pageYOffset = scrollY();
		window.innerWidth = docEl.clientWidth;
		window.innerHeight = docEl.clientHeight;
	}

	try {
		Object.defineProperties(global, {
			'innerWidth': {
				get: function () {
					return docEl.clientWidth;
				}
			},
			'innerHeight': {
				get: function () {
					return docEl.clientHeight;
				}
			},
			'pageXOffset': {
				get: scrollX
			},
			'pageYOffset': {
				get: scrollY
			},
			'scrollX': {
				get: scrollX
			},
			'scrollY': {
				get: scrollY
			}
		});
	} catch(e) {
		window.attachEvent('onresize', setStatically);
		window.attachEvent('onscroll', setStatically);
		setStatically();
	}
})(this);
<|endoftext|>


<commit_before>/*
 * RESTo
 *
 * RESTo - REstful Semantic search Tool for geOspatial
 *
 * Copyright 2013 Jérôme Gasperi <https://github.com/jjrom>
 *
 * jerome[dot]gasperi[at]gmail[dot]com
 *
 *
 * This software is governed by the CeCILL-B license under French law and
 * abiding by the rules of distribution of free software.  You can  use,
 * modify and/ or redistribute the software under the terms of the CeCILL-B
 * license as circulated by CEA, CNRS and INRIA at the following URL
 * "http://www.cecill.info".
 *
 * As a counterpart to the access to the source code and  rights to copy,
 * modify and redistribute granted by the license, users are provided only
 * with a limited warranty  and the software's author,  the holder of the
 * economic rights,  and the successive licensors  have only  limited
 * liability.
 *
 * In this respect, the user's attention is drawn to the risks associated
 * with loading,  using,  modifying and/or developing or reproducing the
 * software by the user in light of its specific status of free software,
 * that may mean  that it is complicated to manipulate,  and  that  also
 * therefore means  that it is reserved for developers  and  experienced
 * professionals having in-depth computer knowledge. Users are therefore
 * encouraged to load and test the software's suitability as regards their
 * requirements in conditions enabling the security of their systems and/or
 * data to be ensured and,  more generally, to use and operate it in the
 * same conditions as regards security.
 *
 * The fact that you are presently reading this means that you have had
 * knowledge of the CeCILL-B license and that you accept its terms.
 *
 */
(function(window) {

    window.R = window.R || {};

    /**
     * Util
     */
    window.R.util = {

        /**
         * Protect user input from XSS attacks by removing html tags
         * from user input
         *
         * @param {Object/String} jqueryObj
         */
        sanitizeValue: function(jqueryObj) {
            if (!jqueryObj || !jqueryObj.length) {
                return '';
            }
            return ($.type(jqueryObj) === 'string' ? jqueryObj : jqueryObj.val()).replace( /<.*?>/g, '' );
        },

        /**
         * Show mask overlay (during loading)
         */
        showMask: function() {
            $('

').appendTo($('body')).css({
                'position': 'fixed',
                'z-index': '100000',
                'top': '0px',
                'left': '0px',
                'background-color': 'rgba(128, 128, 128, 0.7)',
                'color': 'white',
                'text-align': 'center',
                'width': '100%',
                'height': '100%',
                'line-height': $(window).height() + 'px'
            }).show();
        },

        /**
         * Clear mask overlay
         */
        hideMask: function() {
            $('#resto-mask-overlay').remove();
        },

        /**
         * Replace {a:1}, {a:2}, etc within str by array values
         *
         * @param {string} str (e.g. "My name is {a:1} {a:2}")
         * @param {array} values (e.g. ['Jérôme', 'Gasperi'])
         *
         */
        translate: function(str, values) {

            if (!window.R.translation || !window.R.translation[str]) {
                return str;
            }

            var i, l, out = window.R.translation[str];

            /*
             * Replace additional arguments
             */
            if (values && out.indexOf('{a:') !== -1) {
                for (i = 0, l = values.length; i < l; i++) {
                    out = out.replace('{a:' + (i + 1) + '}', values[i]);
                }
            }

            return out;
        },

        /**
         * Update key/value parameters from url by values
         *
         * @param {string} url (e.g. 'http://localhost/resto/?format=json)
         * @param {object} params (e.g. {format:'html'})
         *
         */
        updateUrl: function(url, params) {

            var key, value, i, l, sourceParamsList, sourceParams = {}, newParamsString = "", sourceBase = url.split("?")[0];

            try {
                sourceParamsList = url.split("?")[1].split("&");
            }
            catch (e) {
                sourceParamsList = [];
            }
            for (i = 0, l = sourceParamsList.length; i < l; i++) {
                key = sourceParamsList[i].split('=')[0];
                value = sourceParamsList[i].split('=')[1];
                if (key) {
                    sourceParams[key] = value ? value : '';
                }
            }

            for (key in params) {
                sourceParams[key] = params[key];
            }

            for (key in sourceParams) {
                newParamsString += key + "=" + sourceParams[key] + "&";
            }

            return sourceBase + "?" + newParamsString;
        },

        /**
         * Rewrite URL with new format
         *
         * @param {string} url (e.g. 'http://localhost/resto/?format=json)
         * @param {string} format (e.g. 'html')
         *
         */
        updateUrlFormat: function(url, format) {
           var splitted = url.split("?"), path = splitted[0], params = splitted[1];
           var dotted = path.split(".");
           if (dotted.length > 1) {
               dotted.pop();
               path = dotted.join(".");
           }
           return path + "." + format + "?" + params;
        },

        /**
         * Get gravatar icon url
         *
         * See http://en.gravatar.com
         *
         * @param emailhash : md5 hash of an email adress
         * @param size : size of the returned icon in pixel
         */
        getGravatar: function(emailhash, size) {
            return 'http://www.gravatar.com/avatar/' + (emailhash ? emailhash : '') + '?d=mm' + (!size || !$.isNumeric(size) ? '' : '&s=' + size);
        },

        /**
         * Launch an ajax call
         * This function relies on jquery $.ajax function
         *
         * @param {Object} obj
         * @param {boolean} showMask
         */
        ajax: function(obj, showMask) {

            var self = this;

            /*
             * Paranoid mode
             */
            if (typeof obj !== "object") {
                return null;
            }

            /*
             * Ask for a Mask
             */
            if (showMask) {
                obj['complete'] = function(c) {
                    self.hideMask();
                };
                self.showMask();
            }

            return $.ajax(obj);

        },

        /**
         * Display non intrusive message to user
         *
         * @param {string} message
         * @param {integer} duration
         */
        message: function(message, duration) {
            var $container = $('body'), $d;
            $container.append('

' + message + '

');
            $d = $('.adminMessage', $container);
            $d.fadeIn('slow').delay(duration || 2000).fadeOut('slow', function() {
                $d.remove();
            }).css({
                'left': ($container.width() - $d.width()) / 2,
                'top': 60
            });
            return $d;

        },

        /**
         * Check if a string is a valid email adress
         *
         * @param {String} str
         */
        isEmailAdress: function(str) {
            if (!str || str.length === 0) {
                return false;
            }
            var pattern = /^[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+.[a-zA-Z]{2,4}$/;
            return pattern.test(str);

        },

        /**
         * Align height of a group of elements to the highest element
         *
         * @param {jQueryObject} groupOfItem
         */
        alignHeight: function(groupOfItem){
            var tallest = 0;
            groupOfItem.each(function() {
                var thisHeight = $(this).height() + 20;
                if(thisHeight > tallest){
                    tallest = thisHeight;
                }
            });
            groupOfItem.css('height', tallest);
        }
    };
})(window);
<commit_msg>infiniteScroll<commit_after>/*
 * RESTo
 *
 * RESTo - REstful Semantic search Tool for geOspatial
 *
 * Copyright 2013 Jérôme Gasperi <https://github.com/jjrom>
 *
 * jerome[dot]gasperi[at]gmail[dot]com
 *
 *
 * This software is governed by the CeCILL-B license under French law and
 * abiding by the rules of distribution of free software.  You can  use,
 * modify and/ or redistribute the software under the terms of the CeCILL-B
 * license as circulated by CEA, CNRS and INRIA at the following URL
 * "http://www.cecill.info".
 *
 * As a counterpart to the access to the source code and  rights to copy,
 * modify and redistribute granted by the license, users are provided only
 * with a limited warranty  and the software's author,  the holder of the
 * economic rights,  and the successive licensors  have only  limited
 * liability.
 *
 * In this respect, the user's attention is drawn to the risks associated
 * with loading,  using,  modifying and/or developing or reproducing the
 * software by the user in light of its specific status of free software,
 * that may mean  that it is complicated to manipulate,  and  that  also
 * therefore means  that it is reserved for developers  and  experienced
 * professionals having in-depth computer knowledge. Users are therefore
 * encouraged to load and test the software's suitability as regards their
 * requirements in conditions enabling the security of their systems and/or
 * data to be ensured and,  more generally, to use and operate it in the
 * same conditions as regards security.
 *
 * The fact that you are presently reading this means that you have had
 * knowledge of the CeCILL-B license and that you accept its terms.
 *
 */
(function(window) {

    window.R = window.R || {};

    /**
     * Util
     */
    window.R.util = {

        /*
         * Is ajax ready to do another request ?
         */
        ajaxReady: true,

        /*
         * infinite scrolling offset
         */
        offset: 0,

        /*
         * infinite scrolling limit
         */
        limit: 0,

        /**
         * Protect user input from XSS attacks by removing html tags
         * from user input
         *
         * @param {Object/String} jqueryObj
         */
        sanitizeValue: function(jqueryObj) {
            if (!jqueryObj || !jqueryObj.length) {
                return '';
            }
            return ($.type(jqueryObj) === 'string' ? jqueryObj : jqueryObj.val()).replace( /<.*?>/g, '' );
        },

        /**
         * Show mask overlay (during loading)
         */
        showMask: function() {
            $('

').appendTo($('body')).css({
                'position': 'fixed',
                'z-index': '100000',
                'top': '0px',
                'left': '0px',
                'background-color': 'rgba(128, 128, 128, 0.7)',
                'color': 'white',
                'text-align': 'center',
                'width': '100%',
                'height': '100%',
                'line-height': $(window).height() + 'px'
            }).show();
        },

        /**
         * Clear mask overlay
         */
        hideMask: function() {
            $('#resto-mask-overlay').remove();
        },

        /**
         * Replace {a:1}, {a:2}, etc within str by array values
         *
         * @param {string} str (e.g. "My name is {a:1} {a:2}")
         * @param {array} values (e.g. ['Jérôme', 'Gasperi'])
         *
         */
        translate: function(str, values) {

            if (!window.R.translation || !window.R.translation[str]) {
                return str;
            }

            var i, l, out = window.R.translation[str];

            /*
             * Replace additional arguments
             */
            if (values && out.indexOf('{a:') !== -1) {
                for (i = 0, l = values.length; i < l; i++) {
                    out = out.replace('{a:' + (i + 1) + '}', values[i]);
                }
            }

            return out;
        },

        /**
         * Update key/value parameters from url by values
         *
         * @param {string} url (e.g. 'http://localhost/resto/?format=json)
         * @param {object} params (e.g. {format:'html'})
         *
         */
        updateUrl: function(url, params) {

            var key, value, i, l, sourceParamsList, sourceParams = {}, newParamsString = "", sourceBase = url.split("?")[0];

            try {
                sourceParamsList = url.split("?")[1].split("&");
            }
            catch (e) {
                sourceParamsList = [];
            }
            for (i = 0, l = sourceParamsList.length; i < l; i++) {
                key = sourceParamsList[i].split('=')[0];
                value = sourceParamsList[i].split('=')[1];
                if (key) {
                    sourceParams[key] = value ? value : '';
                }
            }

            for (key in params) {
                sourceParams[key] = params[key];
            }

            for (key in sourceParams) {
                newParamsString += key + "=" + sourceParams[key] + "&";
            }

            return sourceBase + "?" + newParamsString;
        },

        /**
         * Rewrite URL with new format
         *
         * @param {string} url (e.g. 'http://localhost/resto/?format=json)
         * @param {string} format (e.g. 'html')
         *
         */
        updateUrlFormat: function(url, format) {
           var splitted = url.split("?"), path = splitted[0], params = splitted[1];
           var dotted = path.split(".");
           if (dotted.length > 1) {
               dotted.pop();
               path = dotted.join(".");
           }
           return path + "." + format + "?" + params;
        },

        /**
         * Get gravatar icon url
         *
         * See http://en.gravatar.com
         *
         * @param emailhash : md5 hash of an email adress
         * @param size : size of the returned icon in pixel
         */
        getGravatar: function(emailhash, size) {
            return 'http://www.gravatar.com/avatar/' + (emailhash ? emailhash : '') + '?d=mm' + (!size || !$.isNumeric(size) ? '' : '&s=' + size);
        },

        /**
         * Launch an ajax call
         * This function relies on jquery $.ajax function
         *
         * @param {Object} obj
         * @param {boolean} showMask
         */
        ajax: function(obj, showMask) {

            var self = this;

            /*
             * Paranoid mode
             */
            if (typeof obj !== "object") {
                return null;
            }

            /*
             * Ask for a Mask
             */
            if (showMask) {
                obj['complete'] = function(c) {
                    self.hideMask();
                };
                self.showMask();
            }

            return $.ajax(obj);

        },

        /**
         * Display non intrusive message to user
         *
         * @param {string} message
         * @param {integer} duration
         */
        message: function(message, duration) {
            var $container = $('body'), $d;
            $container.append('

' + message + '

');
            $d = $('.adminMessage', $container);
            $d.fadeIn('slow').delay(duration || 2000).fadeOut('slow', function() {
                $d.remove();
            }).css({
                'left': ($container.width() - $d.width()) / 2,
                'top': 60
            });
            return $d;

        },

        /**
         * Check if a string is a valid email adress
         *
         * @param {String} str
         */
        isEmailAdress: function(str) {
            if (!str || str.length === 0) {
                return false;
            }
            var pattern = /^[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+.[a-zA-Z]{2,4}$/;
            return pattern.test(str);

        },

        /**
         * Align height of a group of elements to the highest element
         *
         * @param {jQueryObject} groupOfItem
         */
        alignHeight: function(groupOfItem){
            var tallest = 0;
            groupOfItem.each(function() {
                var thisHeight = $(this).height() + 20;
                if(thisHeight > tallest){
                    tallest = thisHeight;
                }
            });
            groupOfItem.css('height', tallest);
        },

        /**
         * Create an alert button with specific message.
         *
         * @param {jQueryObject} parent
         * @param {String} text
         */
        alert: function(parent, text){
            if(!$('#_alert').length){
                parent.prepend('');

                $("#_alert").on('click', function() {
                    $('#_alert').hide();
                });
            }
            $('#_alert').text(text);
            $('#_alert').show();
        },

        /**
         * Infinite scolling
         *
         * @param {String} url
         * @param {String} dataType
         * @param {Json} data
         * @param {method} callback
         * @param {int} limit
         */
        infiniteScroll: function(url, dataType, data, callback, limit){
            var self = this;
            var lastScrollTop = 0;
            self.limit = limit;

            $(window).scroll(function() {
                var st = $(this).scrollTop();
                if (st > lastScrollTop){
                    if($(window).scrollTop() + $(window).height() > $(document).height() - 100 && self.ajaxReady) {
                        self.ajaxReady = false;
                        self.offset = self.offset + self.limit;
                        data['startIndex'] = self.offset;

                        R.util.showMask();

                        $.ajax({
                            type: "GET",
                            dataType: dataType,
                            url: url,
                            async: true,
                            data: data,
                            success: function(data) {
                                R.util.hideMask();
                                callback(data);
                                self.ajaxReady = true;
                            },
                            error: function(e) {
                                R.util.hideMask();
                                alert('error : ' + e['responseJSON']['ErrorMessage']);
                                self.offset = self.offset - self.limit;
                                self.ajaxReady = true;
                            }
                        });
                    }
                }
                lastScrollTop = st;
             });
        }
    };
})(window);
<|endoftext|>


<commit_before>document.addEventListener("DOMContentLoaded",function(){var e=function(){if("scrollingElement"in document)return document.scrollingElement;var a=document.documentElement,b=a.scrollTop;a.scrollTop=b+1;var c=a.scrollTop;a.scrollTop=b;return c>b?a:document.body}(),g=function(a){var b=e.scrollTop;if(2>a.length)a=-b;else if(a=document.querySelector(a)){a=a.getBoundingClientRect().top;var c=e.scrollHeight-window.innerHeight;a=b+a<c?a:c-b}else a=void 0;if(a)return new Map([["start",b],["delta",a]])},h=function(a){var b=
a.getAttribute("href"),c=g(b);if(c){var d=new Map([["duration",800]]),k=performance.now();requestAnimationFrame(function l(a){d.set("elapsed",a-k);a=d.get("duration");var f=d.get("elapsed"),g=c.get("start"),h=c.get("delta");e.scrollTop=Math.round(h*(-Math.pow(2,-10*f/a)+1)+g);d.get("elapsed")<d.get("duration")?requestAnimationFrame(l):(history.pushState(null,null,b),e.scrollTop=c.get("start")+c.get("delta"))})}},f=document.querySelectorAll("a.scroll");(function b(c,d){var e=c.item(d);e.addEventListener("click",
function(b){b.preventDefault();h(e)});if(d)return b(c,d-1)})(f,f.length-1)});<commit_msg>Delete scroll.min.js<commit_after><|endoftext|>


<commit_before>// # action-creators
//
//  Pure functions for creating action-objects which the platform bots then
//  translates to actual operations on that platform.

const types = require('./action-types');

const creator = {};

creator.sendMessage = (target, text) => ({
  type: types.SEND_MESSAGE,
  payload: { text }
});

module.exports = creator;
<commit_msg>Add also target to sendMessage payload<commit_after>// # action-creators
//
//  Pure functions for creating action-objects which the platform bots then
//  translates to actual operations on that platform.

const types = require('./action-types');

const creator = {};

creator.sendMessage = (target, text) => ({
  type: types.SEND_MESSAGE,
  payload: { text, target }
});

module.exports = creator;
<|endoftext|>


<commit_before>let mod = new ActionObj('Withdraw');
module.exports = mod;
//Should mark
const targetInitFunc = function(creep) {
    const role = creep.memory.role;
    const findSuitableContainer = function(o) {
        if(o.structureType == STRUCTURE_CONTAINER || o.structureType == STRUCTURE_STORAGE) {
            var need = creep.carryCapacity - creep.carry.energy;
            if(o.store[RESOURCE_ENERGY] > need) {
                return true;
            }
        }
        return false;
    };
    const suitableContainers = creep.room.find(FIND_STRUCTURES, {
        filter: findSuitableContainer
    });
    const controllerContainer = creep.room.controller.container;

    if(role == 'upgrader') {
        return controllerContainer;
    } else if(role == 'hauler') {
        //Hauler shouldn't withdraw energy from container/storage which is used by upgrader
        // which will make hauler trapped in withdraw-transfer loop due to action.put is also
        // used by hauler.
        var targets = suitableContainers;
        const controllerContainerId = controllerContainer.id;
        if(controllerContainer) {
            targets = _.filter(targets, o => o.id != controllerContainerId);
        }
        return creep.pos.findClosestByRange(targets);
    } else if(role == 'filler') {
        const spawnLink = creep.room.spawnLink;
        if(spawnLink && spawnLink.energy>0) {
            return spawnLink;
        } else {
            return creep.room.storage;
        }
    } else {
        return creep.pos.findClosestByRange(suitableContainers);
    }
};

mod.nextTarget = function() {
    return Util.Mark.handleMark(this.creep, targetInitFunc, this.actionName);
};

mod.loop = function(creep) {
    return this.loop0(creep, (creep, target) => {
        const result = creep.withdraw(target, RESOURCE_ENERGY);
        if(result == ERR_NOT_IN_RANGE) {
            creep.moveTo(target, {visualizePathStyle: {stroke: '#ffaa00'}});
        } else if(result == OK || result == ERR_NOT_ENOUGH_RESOURCES) {
            Util.Mark.unmarkTarget(creep, this.actionName);
        }
    });
};<commit_msg>filler won't drill energy from storage if there is spawnLink<commit_after>let mod = new ActionObj('Withdraw');
module.exports = mod;
//Should mark
const targetInitFunc = function(creep) {
    const role = creep.memory.role;
    const findSuitableContainer = function(o) {
        if(o.structureType == STRUCTURE_CONTAINER || o.structureType == STRUCTURE_STORAGE) {
            var need = creep.carryCapacity - creep.carry.energy;
            if(o.store[RESOURCE_ENERGY] > need) {
                return true;
            }
        }
        return false;
    };
    const suitableContainers = creep.room.find(FIND_STRUCTURES, {
        filter: findSuitableContainer
    });
    const controllerContainer = creep.room.controller.container;

    if(role == 'upgrader') {
        return controllerContainer;
    } else if(role == 'hauler') {
        //Hauler shouldn't withdraw energy from container/storage which is used by upgrader
        // which will make hauler trapped in withdraw-transfer loop due to action.put is also
        // used by hauler.
        var targets = suitableContainers;
        const controllerContainerId = controllerContainer.id;
        if(controllerContainer) {
            targets = _.filter(targets, o => o.id != controllerContainerId);
        }
        return creep.pos.findClosestByRange(targets);
    } else if(role == 'filler') {
        const spawnLink = creep.room.spawnLink;
        if(spawnLink) {
            return spawnLink;
        } else {
            return creep.room.storage;
        }
    } else {
        return creep.pos.findClosestByRange(suitableContainers);
    }
};

mod.nextTarget = function() {
    return Util.Mark.handleMark(this.creep, targetInitFunc, this.actionName);
};

mod.loop = function(creep) {
    return this.loop0(creep, (creep, target) => {
        const result = creep.withdraw(target, RESOURCE_ENERGY);
        if(result == ERR_NOT_IN_RANGE) {
            creep.moveTo(target, {visualizePathStyle: {stroke: '#ffaa00'}});
        } else if(result == OK || result == ERR_NOT_ENOUGH_RESOURCES) {
            Util.Mark.unmarkTarget(creep, this.actionName);
        }
    });
};<|endoftext|>


<commit_before>let mod = new ActionObj('FromLink');
module.exports = mod;

const targetInitFunc = function(creep) {
    const links = creep.room.links;
    for(const link of links) {
        if(validateFunc(creep, link)) {
            return link;
        }
    }
    return false;
};

mod.nextTarget = function() {
    return Util.Mark.handleMark(this.creep, targetInitFunc, this.actionName, validateFunc);
};

const validateFunc = function(creep, target) {
    return creep.pos.getRangeTo(target)<=4 && target.room && target.room.name===creep.room.name && target.energy>0;
};

mod.word = '⬅Link';

mod.loop = function(creep) {
    return this.loop0(creep, (creep, target) => {
        const result = creep.withdraw(target, RESOURCE_ENERGY);
        if(result == ERR_NOT_IN_RANGE) {
            creep.moveTo(target, {range: 1, maxRooms: 1, visualizePathStyle: {stroke: '#ffaa00'}});
        }
    });
};
<commit_msg>increase range of Action.FromLink<commit_after>let mod = new ActionObj('FromLink');
module.exports = mod;

const targetInitFunc = function(creep) {
    const links = creep.room.links;
    for(const link of links) {
        if(validateFunc(creep, link)) {
            return link;
        }
    }
    return false;
};

mod.nextTarget = function() {
    return Util.Mark.handleMark(this.creep, targetInitFunc, this.actionName, validateFunc);
};

const validateFunc = function(creep, target) {
    return creep.pos.getRangeTo(target)<=6 && target.room && target.room.name===creep.room.name && target.energy>0;
};

mod.word = '⬅Link';

mod.loop = function(creep) {
    return this.loop0(creep, (creep, target) => {
        const result = creep.withdraw(target, RESOURCE_ENERGY);
        if(result == ERR_NOT_IN_RANGE) {
            creep.moveTo(target, {range: 1, maxRooms: 1, visualizePathStyle: {stroke: '#ffaa00'}});
        }
    });
};
<|endoftext|>


<commit_before>import fetch from 'isomorphic-fetch'

export const REQUEST_SCORES = 'REQUEST_SCORES';
function requestScores(user) {
  return {
    type: REQUEST_SCORES,
    user: user
  }
}

export const RECEIVE_SCORES = 'RECEIVE_SCORES';
function receiveScores(user, json) {
  return {
    type: RECEIVE_SCORES,
    user: user,
    scores: json.data.children.map(child => child.data),
    receivedAt: Date.now()
  }
}

export function fetchScores(user) {

  // Thunk middleware knows how to handle functions.
  // It passes the dispatch method as an argument to the function,
  // thus making it able to dispatch actions itself.

  return function (dispatch) {

    // First dispatch: the app state is updated to inform
    // that the API call is starting.

    dispatch(requestScores(user));

    // The function called by the thunk middleware can return a value,
    // that is passed on as the return value of the dispatch method.

    // In this case, we return a promise to wait for.
    // This is not required by thunk middleware, but it is convenient for us.
    var url = 'http://localhost:8888/api/v1.0/scores';
    return fetch(url)
      .then(response => response.json())
      .then(json =>

        // We can dispatch many times!
        // Here, we update the app state with the results of the API call.

        dispatch(receiveScores(user, json))
      );

      // In a real world app, you also want to
      // catch any error in the network call.
  }
}
<commit_msg>parse results properly<commit_after>import fetch from 'isomorphic-fetch'

export const REQUEST_SCORES = 'REQUEST_SCORES';
function requestScores(user) {
  return {
    type: REQUEST_SCORES,
    user: user
  }
}

export const RECEIVE_SCORES = 'RECEIVE_SCORES';
function receiveScores(user, json) {
  return {
    type: RECEIVE_SCORES,
    user: user,
    scores: json.result,
    receivedAt: Date.now()
  }
}

export function fetchScores(user) {

  // Thunk middleware knows how to handle functions.
  // It passes the dispatch method as an argument to the function,
  // thus making it able to dispatch actions itself.

  return function (dispatch) {

    // First dispatch: the app state is updated to inform
    // that the API call is starting.

    dispatch(requestScores(user));

    // The function called by the thunk middleware can return a value,
    // that is passed on as the return value of the dispatch method.

    // In this case, we return a promise to wait for.
    // This is not required by thunk middleware, but it is convenient for us.
    var url = 'http://localhost:8888/api/v1.0/scores';
    return fetch(url)
      .then(response => response.json())
      .then(json =>

        // We can dispatch many times!
        // Here, we update the app state with the results of the API call.

        dispatch(receiveScores(user, json))
      );

      // In a real world app, you also want to
      // catch any error in the network call.
  }
}
<|endoftext|>


<commit_before>/*
switchable

In any group of siblings with the `switchable` attribute, exactly one is
always active.

A `switchable` is given the class `"active"` when a link that references
it is clicked, and all links to it are given the class `"on"`.

Switchables can be used to make tabs, slideshows, accordions and so on.

```
<nav>
    <a class="tab-button button" href="#tab-1">1</a>
    <a class="tab-button button" href="#tab-2">2</a>
    <a class="tab-button button" href="#tab-3">3</a>
</nav>

<section class="tab-block block" switchable id="tab-1">
    Tab 1
</section

><section class="tab-block block" switchable id="tab-2">
    Tab 2
</section

><section class="tab-block block" switchable id="tab-3">
    Tab 3
</section>
```
*/

import { Fn } from '../../fn/module.js';
import { events, trigger, matches, children } from '../module.js';
import { matchers } from './dom-activate.js';

// Define

var match   = matches('.switchable, [switchable]');
var on      = events.on;
var triggerDeactivate = trigger('dom-deactivate');

function activate(e) {
	if (!e.default) { return; }

	var target = e.target;
	if (!match(target)) { return; }

	var nodes = children(target.parentNode).filter(match);
	var i     = nodes.indexOf(target);

	nodes.splice(i, 1);
	var active = nodes.filter(matches('.active'));

	e.default();

	// Deactivate the previous active pane AFTER this pane has been
	// activated. It's important for panes who's style depends on the
	// current active pane, eg: .slide.active ~ .slide
	Fn.from(active).each(triggerDeactivate);
}

function deactivate(e) {
	if (!e.default) { return; }

	var target = e.target;
	if (!match(target)) { return; }

	e.default();
}

on(document, 'dom-activate', activate);
on(document, 'dom-deactivate', deactivate);
matchers.push(match);
<commit_msg>Switchable<commit_after>/*
switchable

In any group of siblings with the `switchable` attribute, exactly one is
always active.

A `switchable` is given the class `"active"` when a link that references
it is clicked, and all links to it are given the class `"on"`.

Switchables can be used to make tabs, slideshows, accordions and so on.

```
<nav>
    <a class="tab-button button" href="#tab-1">1</a>
    <a class="tab-button button" href="#tab-2">2</a>
    <a class="tab-button button" href="#tab-3">3</a>
</nav>

<section class="tab-block block active" switchable id="tab-1">
    Tab 1
</section

><section class="tab-block block" switchable id="tab-2">
    Tab 2
</section

><section class="tab-block block" switchable id="tab-3">
    Tab 3
</section>
```
*/

import { events, trigger, matches, children } from '../module.js';
import { matchers } from './dom-activate.js';

// Define

var match   = matches('.switchable, [switchable]');
var on      = events.on;
var triggerDeactivate = trigger('dom-deactivate');

function activate(e) {
	if (!e.default) { return; }

	var target = e.target;
	if (!match(target)) { return; }

	var nodes = children(target.parentNode).filter(match);
	var i     = nodes.indexOf(target);

	nodes.splice(i, 1);
	var active = nodes.filter(matches('.active'));

	e.default();

	// Deactivate the previous active pane AFTER this pane has been
	// activated. It's important for panes who's style depends on the
	// current active pane, eg: .slide.active ~ .slide
	active.forEach(triggerDeactivate);
}

function deactivate(e) {
	if (!e.default) { return; }

	var target = e.target;
	if (!match(target)) { return; }

	e.default();
}

on(document, 'dom-activate', activate);
on(document, 'dom-deactivate', deactivate);
matchers.push(match);
<|endoftext|>


<commit_before>import {PROFILE_API} from '../config';
import {push} from 'react-router-redux';
import {checkStatus, parseJSON, camelizeProps} from '../util/http';
import * as types from '../constants';

require('isomorphic-fetch');

export function getProfileDataRequest() {
  return {
    type: types.PROFILE_DATA_REQUEST
  };
}

export function getProfileDataSuccess(payload) {
  return {
    type: types.PROFILE_DATA_SUCCESS,
    payload: payload
  };
}

export function getProfileDataFailure(error) {
  return {
    type: types.PROFILE_DATA_FAILURE,
    payload: {
      status: error.response.status,
      statusText: error.response.statusText
    }
  };
}

export function getProfile(userid, token) {
  return dispatch => {
    dispatch(getProfileDataRequest());
    return fetch(PROFILE_API + '/profiles/' + userid + '/', {
      method: 'get',
      headers: {
        'Accept': 'application/json',
        'Content-Type': 'application/json',
        'auth-token': token
      }
    }).then(checkStatus)
      .then(parseJSON)
      .then(camelizeProps)
      .then(json => {
        if (json.firstName) {
          dispatch(getProfileDataSuccess(json));
        } else {
          dispatch(getProfileDataFailure(new Error('Something went wrong.')));
        }
      })
      .catch(error => {
        dispatch(getProfileDataFailure(error));
      });
  };
}

export function createProfileRequest() {
  return {
    type: types.CREATE_PROFILE_REQUEST
  };
}

export function createProfileSuccess() {
  return {
    type: types.CREATE_PROFILE_SUCCESS
  };
}

export function createProfileFailure(error) {
  return {
    type: types.CREATE_PROFILE_FAILURE,
    payload: {
      status: error.response.status,
      statusText: error.response.statusText
    }
  };
}

export function createProfile(userid, firstName, lastName) {
  return dispatch => {
    dispatch(createProfileRequest());
    return fetch(PROFILE_API + '/profiles/', {
      method: 'post',
      headers: {
        'Accept': 'application/json',
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({
        auth_id: userid,
        first_name: firstName,
        last_name: lastName
      })
    }).then(checkStatus)
      .then(parseJSON)
      .then(camelizeProps)
      .then(json => {
        console.log(json);
        if (json.auth_id === userid) {
          dispatch(createProfileSuccess());
          dispatch(push('/confirm-email'));
        } else {
          dispatch(createProfileFailure(new Error('Wrong response from jrc-profile')));
        }
      })
      .catch(error => {
        dispatch(createProfileFailure(error));
      });
  };
}
<commit_msg>Remove unused console.log<commit_after>import {PROFILE_API} from '../config';
import {push} from 'react-router-redux';
import {checkStatus, parseJSON, camelizeProps} from '../util/http';
import * as types from '../constants';

require('isomorphic-fetch');

export function getProfileDataRequest() {
  return {
    type: types.PROFILE_DATA_REQUEST
  };
}

export function getProfileDataSuccess(payload) {
  return {
    type: types.PROFILE_DATA_SUCCESS,
    payload: payload
  };
}

export function getProfileDataFailure(error) {
  return {
    type: types.PROFILE_DATA_FAILURE,
    payload: {
      status: error.response.status,
      statusText: error.response.statusText
    }
  };
}

export function getProfile(userid, token) {
  return dispatch => {
    dispatch(getProfileDataRequest());
    return fetch(PROFILE_API + '/profiles/' + userid + '/', {
      method: 'get',
      headers: {
        'Accept': 'application/json',
        'Content-Type': 'application/json',
        'auth-token': token
      }
    }).then(checkStatus)
      .then(parseJSON)
      .then(camelizeProps)
      .then(json => {
        if (json.firstName) {
          dispatch(getProfileDataSuccess(json));
        } else {
          dispatch(getProfileDataFailure(new Error('Something went wrong.')));
        }
      })
      .catch(error => {
        dispatch(getProfileDataFailure(error));
      });
  };
}

export function createProfileRequest() {
  return {
    type: types.CREATE_PROFILE_REQUEST
  };
}

export function createProfileSuccess() {
  return {
    type: types.CREATE_PROFILE_SUCCESS
  };
}

export function createProfileFailure(error) {
  return {
    type: types.CREATE_PROFILE_FAILURE,
    payload: {
      status: error.response.status,
      statusText: error.response.statusText
    }
  };
}

export function createProfile(userid, firstName, lastName) {
  return dispatch => {
    dispatch(createProfileRequest());
    return fetch(PROFILE_API + '/profiles/', {
      method: 'post',
      headers: {
        'Accept': 'application/json',
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({
        auth_id: userid,
        first_name: firstName,
        last_name: lastName
      })
    }).then(checkStatus)
      .then(parseJSON)
      .then(camelizeProps)
      .then(json => {
        if (json.auth_id === userid) {
          dispatch(createProfileSuccess());
          dispatch(push('/confirm-email'));
        } else {
          dispatch(createProfileFailure(new Error('Wrong response from jrc-profile')));
        }
      })
      .catch(error => {
        dispatch(createProfileFailure(error));
      });
  };
}
<|endoftext|>


<commit_before>function tmp_slider(){

  var width = self.frameElement ? 960 : innerWidth,
      height = self.frameElement ? 500 : innerHeight;

  var data = [800,100]

  var color = d3.scale.category10();

  var drag = d3.behavior.drag()
      // .origin(function(d) {
      //   return {x: d[0], y: d[1]};
      // })
      .on("drag", dragged)
      .on('dragend', function(){
        console.log('dragging has ended\n')
      })

  // var main_svg = d3.select("body")
  //     .on("touchstart", nozoom)
  //     .on("touchmove", nozoom)
  //     .append("svg")
  //     .attr("width", width)
  //     .attr("height", height);

  var main_svg = d3.select('.viz_svg');

  var slider_group = main_svg
      .append('g')
      .attr('transform', function(){ return 'translate(' + data + ')'; })
      .classed('slider_group', true);

    slider_group
      .append("line")
      .style('stroke-width', '10px')
      .style('stroke', 'black')
      .style('opacity', 0.5)
      .attr("y1", 0)
      .attr("y2", 100)

    slider_group
      .append('circle')
      .attr('r', 12)
      // .attr('y', 30)
      .style('fill', 'blue')
      .style('opacity', 0.5)
      .call(drag);

  function dragged() {
    // d[0] = d3.event.x;
    var new_y = d3.event.y;

    if (new_y < 0){
      new_y = 0;
    }

    if (new_y > 100){
      new_y = 100;
    }

    if (this.nextSibling) this.parentNode.appendChild(this);

    new_y = d3.round(new_y, -1);

    // console.log('final y')
    // console.log(new_y)

    d3.select(this).attr("transform", "translate(0, " + new_y + ")");
  }

  function nozoom() {
    d3.event.preventDefault();
  }

}<commit_msg>added dragend event, will use to evade visualization dragging<commit_after>function tmp_slider(){

  var width = self.frameElement ? 960 : innerWidth,
      height = self.frameElement ? 500 : innerHeight;

  var data = [800,100]

  var color = d3.scale.category10();

  var drag = d3.behavior.drag()
      // .origin(function(d) {
      //   return {x: d[0], y: d[1]};
      // })
      .on("drag", dragging)
      .on('dragend', function(){
        console.log('dragging has ended\n')
        cgm.params.slider_drag = false;
      })

  // var main_svg = d3.select("body")
  //     .on("touchstart", nozoom)
  //     .on("touchmove", nozoom)
  //     .append("svg")
  //     .attr("width", width)
  //     .attr("height", height);

  var main_svg = d3.select('.viz_svg');

  var slider_group = main_svg
      .append('g')
      .attr('transform', function(){ return 'translate(' + data + ')'; })
      .classed('slider_group', true);

    slider_group
      .append("line")
      .style('stroke-width', '10px')
      .style('stroke', 'black')
      .style('opacity', 0.5)
      .attr("y1", 0)
      .attr("y2", 100)

    slider_group
      .append('circle')
      .attr('r', 12)
      // .attr('y', 30)
      .style('fill', 'blue')
      .style('opacity', 0.5)
      .call(drag);

  function dragging() {

    cgm.params.slider_drag = true;

    console.log(cgm.params.slider_drag)

    // d[0] = d3.event.x;
    var new_y = d3.event.y;

    if (new_y < 0){
      new_y = 0;
    }

    if (new_y > 100){
      new_y = 100;
    }

    if (this.nextSibling) this.parentNode.appendChild(this);

    new_y = d3.round(new_y, -1);

    // console.log('final y')
    // console.log(new_y)

    d3.select(this).attr("transform", "translate(0, " + new_y + ")");
  }

  function nozoom() {
    d3.event.preventDefault();
  }

}<|endoftext|>


<commit_before>import React from 'react';
import CoreChanneling from 'parser/shared/modules/Channeling';

import SPELLS from 'common/SPELLS';
import SpellLink from 'common/SpellLink';
import SUGGESTION_IMPORTANCE from 'parser/core/ISSUE_IMPORTANCE';
import StatTracker from 'parser/shared/modules/StatTracker';
import { formatPercentage } from 'common/format';

import Analyzer from 'parser/core/Analyzer';

const debug = true;

class SoothingMist extends Analyzer {
  static dependencies = {
    channeling: CoreChanneling,
    statTracker: StatTracker,
  };

  soomTicks = 0;
  gustProc = 0;
  gustsHealing = 0;
  lastSoomTickTimestamp = 0;

  startStamp = 0;
  endStamp = 0;
  soomInProgress = false;
  castsInSoom = 0;
  badSooms = 0;
  totalSoomCasts = 0;

  constructor(...args) {
    super(...args);
    this.assumedGCD = 1500 / (1 + this.statTracker.hastePercentage(this.statTracker.currentHasteRating)) *.95;
  }

  on_byPlayer_heal(event) {
    const spellId = event.ability.guid;

    if (spellId === SPELLS.SOOTHING_MIST.id) {
      this.soomTicks += 1;
      this.lastSoomTickTimestamp = event.timestamp;
    }

    if (spellId === SPELLS.GUSTS_OF_MISTS.id && this.lastSoomTickTimestamp === event.timestamp && this.gustProc < Math.ceil(this.soomTicks/8)) {
      this.gustProc += 1;
      this.gustsHealing += (event.amount || 0) + (event.absorbed || 0);
    }
  }

  on_byPlayer_cast(event) {
    const spellId = event.ability.guid;

    if(this.soomInProgress){
      if(spellId === SPELLS.VIVIFY.id || spellId === SPELLS.ENVELOPING_MIST.id) {
        this.castsInSoom += 1;
      }
      if(spellId === SPELLS.SOOTHING_MIST.id) {//if they refresh soom for some stupid reason
        this.endStamp = event.timestamp;
        this.checkChannelTiming();
        this.castsInSoom = 0;
      }
    }

    if (spellId === SPELLS.SOOTHING_MIST.id) {
      this.startStamp = event.timestamp;
      this.soomInProgress = true;
      const gcd = 1000 / (1 + this.statTracker.hastePercentage(this.statTracker.currentHasteRating));
      this.startGCD = Math.max(750, gcd) * .95;
    }
  }

  on_byPlayer_removebuff(event) {
    const spellId = event.ability.guid;

    if (spellId !== SPELLS.SOOTHING_MIST.id || !this.soomInProgress) {
      return;
    }
    this.endStamp = event.timestamp;
    this.soomInProgress = false;
    this.checkChannelTiming();
    this.castsInSoom = 0;

  }

  checkChannelTiming() {
    this.totalSoomCasts += 1;
    let duration = this.endStamp - this.startStamp;

    if (duration < this.startGCD) {
      return;
    }

    duration -= this.startGCD;
    this.castsInSoom -= 1;

    this.castsInSoom -= parseInt(duration / this.assumedGCD);

    if (this.castsInSoom < 0) {
      this.badSooms += 1;
    }
  }

  on_fightend() {
    if (this.soomInProgress) {
      this.endStamp = this.selectedCombatant.fightDuration;
      this.checkChannelTiming();
    }
    if (debug) {
      console.log(`SooM Ticks: ${this.soomTicks}`);
      console.log('SooM Perc Uptime: ', (this.soomTicks * 2 / this.owner.fightDuration * 1000));
      console.log('SooM Buff Update: ', this.selectedCombatant.getBuffUptime(SPELLS.SOOTHING_MIST.id), ' Percent: ', this.selectedCombatant.getBuffUptime(SPELLS.SOOTHING_MIST.id) / this.owner.fightDuration);
      console.log('soom gusts', this.gustsHealing);

      console.log('Total casts: ', this.totalSoomCasts);
      console.log('Bad casts: ', this.badSooms);
      console.log('Threshold casts: ', this.soomThresholds);
    }
  }

  get soomTicksPerDuration() {
    const soomTicks = (this.soomTicks * 2 / this.owner.fightDuration * 1000) || 0;
    return soomTicks >= 1.5;
  }

  get suggestionThresholds() {
    return {
      actual: this.soomTicksPerDuration,
      isEqual: true,
      style: 'boolean',
    };
  }

  get soomThresholds(){
    return (this.totalSoomCasts - this.badSooms) / this.totalSoomCasts;
  }

  get suggestionThresholdsCasting(){
    return {
      actual: this.soomThresholds,
      isLessThan: {
        minor: 1,
        average: .95,
        major: .9,
      },
      style: 'percentage',
    };
  }

  suggestions(when) {
    when(this.suggestionThresholds).addSuggestion((suggest) => {
        return suggest(
          <>
            You are allowing <SpellLink id={SPELLS.SOOTHING_MIST.id} /> to channel for an extended period of time. <SpellLink id={SPELLS.SOOTHING_MIST.id} /> does little healing, so your time is better spent DPS'ing throug the use of <SpellLink id={SPELLS.TIGER_PALM.id} /> and <SpellLink id={SPELLS.BLACKOUT_KICK.id} />.
          </>
        )
          .icon(SPELLS.SOOTHING_MIST.icon)
          .staticImportance(SUGGESTION_IMPORTANCE.MAJOR);
      });

    when(this.suggestionThresholdsCasting).addSuggestion((suggest, actual, recommended) => {
      return suggest(
        <>
          You were channeling <SpellLink id={SPELLS.SOOTHING_MIST.id} /> without casting spells during it. Replace this channel time with damage abilities like <SpellLink id={SPELLS.RISING_SUN_KICK.id} />.
        </>
      )
        .icon(SPELLS.SOOTHING_MIST.icon)
        .actual(`${formatPercentage(this.badSooms / this.totalSoomCasts)} % of Soothing Mist casts with max spells casted`)
        .recommended(`${recommended} is recommended`);
    });
  }
}

export default SoothingMist;
<commit_msg>Turned debugg off<commit_after>import React from 'react';
import CoreChanneling from 'parser/shared/modules/Channeling';

import SPELLS from 'common/SPELLS';
import SpellLink from 'common/SpellLink';
import SUGGESTION_IMPORTANCE from 'parser/core/ISSUE_IMPORTANCE';
import StatTracker from 'parser/shared/modules/StatTracker';
import { formatPercentage } from 'common/format';

import Analyzer from 'parser/core/Analyzer';

const debug = false;

class SoothingMist extends Analyzer {
  static dependencies = {
    channeling: CoreChanneling,
    statTracker: StatTracker,
  };

  soomTicks = 0;
  gustProc = 0;
  gustsHealing = 0;
  lastSoomTickTimestamp = 0;

  startStamp = 0;
  endStamp = 0;
  soomInProgress = false;
  castsInSoom = 0;
  badSooms = 0;
  totalSoomCasts = 0;

  constructor(...args) {
    super(...args);
    this.assumedGCD = 1500 / (1 + this.statTracker.hastePercentage(this.statTracker.currentHasteRating)) *.95;
  }

  on_byPlayer_heal(event) {
    const spellId = event.ability.guid;

    if (spellId === SPELLS.SOOTHING_MIST.id) {
      this.soomTicks += 1;
      this.lastSoomTickTimestamp = event.timestamp;
    }

    if (spellId === SPELLS.GUSTS_OF_MISTS.id && this.lastSoomTickTimestamp === event.timestamp && this.gustProc < Math.ceil(this.soomTicks/8)) {
      this.gustProc += 1;
      this.gustsHealing += (event.amount || 0) + (event.absorbed || 0);
    }
  }

  on_byPlayer_cast(event) {
    const spellId = event.ability.guid;

    if(this.soomInProgress){
      if(spellId === SPELLS.VIVIFY.id || spellId === SPELLS.ENVELOPING_MIST.id) {
        this.castsInSoom += 1;
      }
      if(spellId === SPELLS.SOOTHING_MIST.id) {//if they refresh soom for some stupid reason
        this.endStamp = event.timestamp;
        this.checkChannelTiming();
        this.castsInSoom = 0;
      }
    }

    if (spellId === SPELLS.SOOTHING_MIST.id) {
      this.startStamp = event.timestamp;
      this.soomInProgress = true;
      const gcd = 1000 / (1 + this.statTracker.hastePercentage(this.statTracker.currentHasteRating));
      this.startGCD = Math.max(750, gcd) * .95;
    }
  }

  on_byPlayer_removebuff(event) {
    const spellId = event.ability.guid;

    if (spellId !== SPELLS.SOOTHING_MIST.id || !this.soomInProgress) {
      return;
    }
    this.endStamp = event.timestamp;
    this.soomInProgress = false;
    this.checkChannelTiming();
    this.castsInSoom = 0;

  }

  checkChannelTiming() {
    this.totalSoomCasts += 1;
    let duration = this.endStamp - this.startStamp;

    if (duration < this.startGCD) {
      return;
    }

    duration -= this.startGCD;
    this.castsInSoom -= 1;

    this.castsInSoom -= parseInt(duration / this.assumedGCD);

    if (this.castsInSoom < 0) {
      this.badSooms += 1;
    }
  }

  on_fightend() {
    if (this.soomInProgress) {
      this.endStamp = this.selectedCombatant.fightDuration;
      this.checkChannelTiming();
    }
    if (debug) {
      console.log(`SooM Ticks: ${this.soomTicks}`);
      console.log('SooM Perc Uptime: ', (this.soomTicks * 2 / this.owner.fightDuration * 1000));
      console.log('SooM Buff Update: ', this.selectedCombatant.getBuffUptime(SPELLS.SOOTHING_MIST.id), ' Percent: ', this.selectedCombatant.getBuffUptime(SPELLS.SOOTHING_MIST.id) / this.owner.fightDuration);
      console.log('soom gusts', this.gustsHealing);

      console.log('Total casts: ', this.totalSoomCasts);
      console.log('Bad casts: ', this.badSooms);
      console.log('Threshold casts: ', this.soomThresholds);
    }
  }

  get soomTicksPerDuration() {
    const soomTicks = (this.soomTicks * 2 / this.owner.fightDuration * 1000) || 0;
    return soomTicks >= 1.5;
  }

  get suggestionThresholds() {
    return {
      actual: this.soomTicksPerDuration,
      isEqual: true,
      style: 'boolean',
    };
  }

  get soomThresholds(){
    return (this.totalSoomCasts - this.badSooms) / this.totalSoomCasts;
  }

  get suggestionThresholdsCasting(){
    return {
      actual: this.soomThresholds,
      isLessThan: {
        minor: 1,
        average: .95,
        major: .9,
      },
      style: 'percentage',
    };
  }

  suggestions(when) {
    when(this.suggestionThresholds).addSuggestion((suggest) => {
        return suggest(
          <>
            You are allowing <SpellLink id={SPELLS.SOOTHING_MIST.id} /> to channel for an extended period of time. <SpellLink id={SPELLS.SOOTHING_MIST.id} /> does little healing, so your time is better spent DPS'ing throug the use of <SpellLink id={SPELLS.TIGER_PALM.id} /> and <SpellLink id={SPELLS.BLACKOUT_KICK.id} />.
          </>
        )
          .icon(SPELLS.SOOTHING_MIST.icon)
          .staticImportance(SUGGESTION_IMPORTANCE.MAJOR);
      });

    when(this.suggestionThresholdsCasting).addSuggestion((suggest, actual, recommended) => {
      return suggest(
        <>
          You were channeling <SpellLink id={SPELLS.SOOTHING_MIST.id} /> without casting spells during it. Replace this channel time with damage abilities like <SpellLink id={SPELLS.RISING_SUN_KICK.id} />.
        </>
      )
        .icon(SPELLS.SOOTHING_MIST.icon)
        .actual(`${formatPercentage(this.badSooms / this.totalSoomCasts)} % of Soothing Mist casts with max spells casted`)
        .recommended(`${recommended} is recommended`);
    });
  }
}

export default SoothingMist;
<|endoftext|>


<commit_before>var CONSTANTS = require('../constants.json');
var utils = require('../utils.js');
var bidfactory = require('../bidfactory.js');
var bidmanager = require('../bidmanager.js');
var adloader = require('../adloader');

/**
 * Adapter for requesting bids from Criteo.
 *
 * @returns {{callBids: _callBids}}
 * @constructor
 */
var CriteoAdapter = function CriteoAdapter() {
	var bids;

	function _callBids(params) {
		bids = params.bids || [];

		// Only make one request per "nid"
		_getUniqueNids(bids).forEach(_requestBid);
	}

	function _getUniqueNids(bids) {
		var key;
		var map = {};
		var nids = [];
		bids.forEach(function(bid) {
			map[bid.params.nid] = bid;
		});
		for (key in map) {
			if (map.hasOwnProperty(key)) {
				nids.push(map[key]);
			}
		}
		return nids;
	}

	function _requestBid(bid) {
		var varname = 'crtg_varname_' + bid.params.nid;
		var scriptUrl = '//rtax.criteo.com/delivery/rta/rta.js?netId=' + encodeURI(bid.params.nid) +
			'&cookieName=' + encodeURI(bid.params.cookiename) +
			'&rnd=' + Math.floor(Math.random() * 99999999999) +
			'&varName=' + encodeURI(varname);

		adloader.loadScript(scriptUrl, function(response) {
			var adResponse;
			var content = window[varname];

			// Add a response for each bid matching the "nid"
			bids.forEach(function(existingBid) {
				if (existingBid.params.nid === bid.params.nid) {
					if (content) {
						adResponse = bidfactory.createBid(1);
						adResponse.bidderCode = 'criteo';

						adResponse.keys = content.split(';');

						bidmanager.addBidResponse(existingBid.placementCode, adResponse);
					} else {
						// Indicate an ad was not returned
						adResponse = bidfactory.createBid(2);
						adResponse.bidderCode = 'criteo';
					}

					bidmanager.addBidResponse(existingBid.placementCode, adResponse);
				}
			});
		});
	}

	return {
		callBids: _callBids
	};
};

module.exports = CriteoAdapter;<commit_msg>fixing issue with criteo creating twice a winning bid<commit_after>var CONSTANTS = require('../constants.json');
var utils = require('../utils.js');
var bidfactory = require('../bidfactory.js');
var bidmanager = require('../bidmanager.js');
var adloader = require('../adloader');

/**
 * Adapter for requesting bids from Criteo.
 *
 * @returns {{callBids: _callBids}}
 * @constructor
 */
var CriteoAdapter = function CriteoAdapter() {
	var bids;

	function _callBids(params) {
		bids = params.bids || [];

		// Only make one request per "nid"
		_getUniqueNids(bids).forEach(_requestBid);
	}

	function _getUniqueNids(bids) {
		var key;
		var map = {};
		var nids = [];
		bids.forEach(function(bid) {
			map[bid.params.nid] = bid;
		});
		for (key in map) {
			if (map.hasOwnProperty(key)) {
				nids.push(map[key]);
			}
		}
		return nids;
	}

	function _requestBid(bid) {
		var varname = 'crtg_varname_' + bid.params.nid;
		var scriptUrl = '//rtax.criteo.com/delivery/rta/rta.js?netId=' + encodeURI(bid.params.nid) +
			'&cookieName=' + encodeURI(bid.params.cookiename) +
			'&rnd=' + Math.floor(Math.random() * 99999999999) +
			'&varName=' + encodeURI(varname);

		adloader.loadScript(scriptUrl, function(response) {
			var adResponse;
			var content = window[varname];

			// Add a response for each bid matching the "nid"
			bids.forEach(function(existingBid) {
				if (existingBid.params.nid === bid.params.nid) {
					if (content) {
						adResponse = bidfactory.createBid(1);
						adResponse.bidderCode = 'criteo';

						adResponse.keys = content.split(';');

						//bidmanager.addBidResponse(existingBid.placementCode, adResponse);
					} else {
						// Indicate an ad was not returned
						adResponse = bidfactory.createBid(2);
						adResponse.bidderCode = 'criteo';
					}

					bidmanager.addBidResponse(existingBid.placementCode, adResponse);
				}
			});
		});
	}

	return {
		callBids: _callBids
	};
};

module.exports = CriteoAdapter;<|endoftext|>