<commit_before>var EventEmitter = require('../utils/EventEmitter'),
    Rectangle = require('../math/Rectangle'),
    inherit = require('../utils/inherit'),
    Texture = require('./Texture'),
    math = require('../math/math'),
    PIXI = require('../vendor/pixi');

/**
 * The base Sprite class. This class is the base for all images on the screen
 *
 * @class Sprite
 * @extends PIXI.Sprite
 * @uses EventEmitter
 * @constructor
 * @param textures {Texture|Array<Texture>|Object} The texture for the sprite to display, an array of texture to animation through, or an animation object.
 *      The later looks like: `{ animationName: { frames: [frame1, frame2], rate: 2 } }` where each frame is a Texture object
 * @param [speed] {Number} The speed of the animations (can be overriden on a specific animations)
 * @param [start] {String} The animation to start with, defaults to the first found key otherwise
 * @example
 *      var spr = new gf.Sprite(texture);
 */
var Sprite = module.exports = function(anims, speed, start) {
    EventEmitter.call(this);

    //parse tx into correct format
    if(anims instanceof Texture) {
        anims = { _default: { frames: [anims] } };
        speed = 1;
        start = '_default';
    }
    else if(anims instanceof Array) {
        anims = { _default: { frames: anims } };
        speed = 1;
        start = '_default';
    } else {
        //massage animations into full format
        for(var a in anims) {
            if(start === undefined)
                start = a;

            var anim = anims[a];

            if(anim instanceof Array)
                anims[a] = { frames: anim };
            else if(anim instanceof Texture)
                anims[a] = { frames: [anim] };
        }
    }

    PIXI.Sprite.call(this, anims[start].frames[0]);

    /**
     * The name of the sprite
     *
     * @property name
     * @type String
     * @default ''
     */
    this.name = '';

    /**
     * The lifetime of the sprite. Once it reaches 0 (after being set)
     * the sprite's visible property is set to false, so that it will
     * no longer be rendered. NOT YET IMPLEMENTED
     *
     * @property lifetime
     * @type Number
     * @default 0
     * @private
     */
    this.lifetime = 0;

    /**
     * The animation speed for this sprite
     *
     * @property speed
     * @type Number
     * @default 1
     */
    this.speed = speed || 1;

    /**
     * Whether or not to loop the animations. This can be overriden
     * on a per-animation level
     *
     * @property loop
     * @type Boolean
     * @default false
     */
    this.loop = false;

    /**
     * The registerd animations for this AnimatedSprite
     *
     * @property animations
     * @type Object
     * @readOnly
     */
    this.animations = anims;

    /**
     * The currently playing animation
     *
     * @property currentAnimation
     * @type String
     * @readOnly
     */
    this.currentAnimation = start;

    /**
     * The current frame being shown
     *
     * @property currentFrame
     * @type Number
     * @readOnly
     */
    this.currentFrame = 0;

    /**
     * Whether or not the animation is currently playing
     *
     * @property playing
     * @type Boolean
     * @readOnly
     */
    this.playing = false;

    this.hitArea = this.hitArea || new Rectangle(0, 0, this.width, this.height);

    //start playing
    this.gotoAndPlay(this.currentAnimation);
};

inherit(Sprite, PIXI.Sprite, {
    /**
     * Adds a new animation to this animated sprite
     *
     * @method addAnimation
     * @param name {String} The string name of the animation
     * @param frames {Array<Texture>} The array of texture frames
     * @param [speed] {Number} The animation speed
     * @param [loop] {Boolean} Loop the animation or not
     */
    addAnimation: function(name, frames, speed, loop) {
        if(typeof name === 'object') {
            this.animations[name.name] = name;
        } else {
            this.animations[name] = {
                name: name,
                frames: frames,
                speed: speed,
                loop: loop
            };
        }
    },
    /**
     * Goes to a frame and starts playing the animation from there
     *
     * @method gotoAndPlay
     * @param [name] {String} The string name of the animation to play
     * @param frame {Number} The index of the frame to start on
     */
    gotoAndPlay: function(anim, frame) {
        if(typeof anim === 'number') {
            this.currentFrame = anim;
        } else {
            this.currentFrame = frame || 0;
            this.lastRound = math.round(frame || 0);
            this.currentAnimation = anim;
        }
        this.playing = true;

        this.setTexture(this.animations[this.currentAnimation].frames[this.currentFrame]);
        this.emit('frame', this.currentAnimation, this.lastRound);
    },
    /**
     * Goes to a frame and stops playing the animation
     *
     * @method gotoAndStop
     * @param [name] {String} The string name of the animation to go to
     * @param frame {Number} The index of the frame to stop on
     */
    gotoAndStop: function(anim, frame) {
        if(typeof anim === 'number') {
            this.currentFrame = anim;
        } else {
            this.currentFrame = frame || 0;
            this.lastRound = math.round(frame || 0);
            this.currentAnimation = anim;
        }
        this.playing = false;

        this.setTexture(this.animations[this.currentAnimation].frames[this.currentFrame]);
        this.emit('frame', this.currentAnimation, this.lastRound);
    },
    /**
     * Starts playing the currently active animation
     *
     * @method play
     */
    play: function() {
        this.playing = true;
    },
    /**
     * Stops playing the currently active animation
     *
     * @method stop
     */
    stop: function() {
        this.playing = false;
    },
    /**
     * Removes this sprite from the stage and the physics system
     *
     * @method destroy
     */
    destroy: function() {
        this.stop();

        if(this._physics) {
            this._physics.removeSprite(this);
            this._physics = null;
        }

        if(this.parent)
            this.parent.removeChild(this);

        this.name = null;
        this.lifetime = null;
        this.speed = null;
        this.loop = null;
        this.animations = null;
        this.currentAnimation = null;
        this.currentFrame = null;
        this.playing = null;
        this.hitArea = null;
    },
    /**
     * Called by PIXI to update our textures and do the actual animation
     *
     * @method updateTransform
     * @private
     */
    updateTransform: function() {
        Sprite.prototype.updateTransform.call(this);

        if(!this.playing) return;

        var anim = this.animations[this.currentAnimation],
            round,
            loop = anim.loop !== undefined ? anim.loop : this.loop;

        this.currentFrame += anim.speed || this.speed;
        round = math.round(this.currentFrame);

        if(round < anim.frames.length) {
            if(round !== this.lastRound) {
                this.lastRound = round;
                this.setTexture(anim.frames[round]);
                this.emit('frame', this.currentAnimation, round);
            }
        }
        else {
            if(loop) {
                this.gotoAndPlay(0);
            } else {
                this.stop();
                this.emit('complete', this.currentAnimation);
            }
        }
    }
});

//Add event echos
/*
['click', 'mousedown', 'mouseup', 'mouseupoutside', 'mouseover', 'mouseout', 'mousemove', 'tap', 'touchstart', 'touchend', 'touchendoutside'].forEach(function(evtname) {
    Sprite.prototype[evtname] = module.exports = function(e) {
        this.emit(evtname, e);
    };
});
*/

/*
 * MOUSE Callbacks
 */

/**
 * A callback that is used when the users clicks on the sprite with their mouse
 *
 * @event click
 * @param interactionData {InteractionData}
 */

/**
 * A callback that is used when the user clicks the mouse down over the sprite
 *
 * @event mousedown
 * @param interactionData {InteractionData}
 */

/**
 * A callback that is used when the user releases the mouse that was over the sprite
 * for this callback to be fired the mouse must have been pressed down over the sprite
 *
 * @event mouseup
 * @param interactionData {InteractionData}
 */

/**
 * A callback that is used when the user releases the mouse that was over the sprite but is no longer over the sprite
 * for this callback to be fired, The touch must have started over the sprite
 *
 * @event mouseupoutside
 * @param interactionData {InteractionData}
 */

/**
 * A callback that is used when the users mouse rolls over the sprite
 *
 * @event mouseover
 * @param interactionData {InteractionData}
 */

/**
 * A callback that is used when the users mouse leaves the sprite
 *
 * @event mouseout
 * @param interactionData {InteractionData}
 */

/**
 * A callback that is used when the user moves the mouse while over the sprite
 *
 * @event mousemove
 * @param interactionData {InteractionData}
 */

/*
 * TOUCH Callbacks
 */

/**
 * A callback that is used when the users taps on the sprite with their finger
 * basically a touch version of click
 *
 * @event tap
 * @param interactionData {InteractionData}
 */

/**
 * A callback that is used when the user touch's over the sprite
 *
 * @event touchstart
 * @param interactionData {InteractionData}
 */

/**
 * A callback that is used when the user releases a touch over the sprite
 *
 * @event touchend
 * @param interactionData {InteractionData}
 */

/**
 * A callback that is used when the user releases the touch that was over the sprite
 * for this callback to be fired, The touch must have started over the sprite
 *
 * @event touchendoutside
 * @param interactionData {InteractionData}
 */
<commit_msg>sprite functions now chain<commit_after>var EventEmitter = require('../utils/EventEmitter'),
    Rectangle = require('../math/Rectangle'),
    inherit = require('../utils/inherit'),
    Texture = require('./Texture'),
    math = require('../math/math'),
    PIXI = require('../vendor/pixi');

/**
 * The base Sprite class. This class is the base for all images on the screen
 *
 * @class Sprite
 * @extends PIXI.Sprite
 * @uses EventEmitter
 * @constructor
 * @param textures {Texture|Array<Texture>|Object} The texture for the sprite to display, an array of texture to animation through, or an animation object.
 *      The later looks like: `{ animationName: { frames: [frame1, frame2], rate: 2 } }` where each frame is a Texture object
 * @param [speed] {Number} The speed of the animations (can be overriden on a specific animations)
 * @param [start] {String} The animation to start with, defaults to the first found key otherwise
 * @example
 *      var spr = new gf.Sprite(texture);
 */
var Sprite = module.exports = function(anims, speed, start) {
    EventEmitter.call(this);

    //parse tx into correct format
    if(anims instanceof Texture) {
        anims = { _default: { frames: [anims] } };
        speed = 1;
        start = '_default';
    }
    else if(anims instanceof Array) {
        anims = { _default: { frames: anims } };
        speed = 1;
        start = '_default';
    } else {
        //massage animations into full format
        for(var a in anims) {
            if(start === undefined)
                start = a;

            var anim = anims[a];

            if(anim instanceof Array)
                anims[a] = { frames: anim };
            else if(anim instanceof Texture)
                anims[a] = { frames: [anim] };
        }
    }

    PIXI.Sprite.call(this, anims[start].frames[0]);

    /**
     * The name of the sprite
     *
     * @property name
     * @type String
     * @default ''
     */
    this.name = '';

    /**
     * The lifetime of the sprite. Once it reaches 0 (after being set)
     * the sprite's visible property is set to false, so that it will
     * no longer be rendered. NOT YET IMPLEMENTED
     *
     * @property lifetime
     * @type Number
     * @default 0
     * @private
     */
    this.lifetime = 0;

    /**
     * The animation speed for this sprite
     *
     * @property speed
     * @type Number
     * @default 1
     */
    this.speed = speed || 1;

    /**
     * Whether or not to loop the animations. This can be overriden
     * on a per-animation level
     *
     * @property loop
     * @type Boolean
     * @default false
     */
    this.loop = false;

    /**
     * The registerd animations for this AnimatedSprite
     *
     * @property animations
     * @type Object
     * @readOnly
     */
    this.animations = anims;

    /**
     * The currently playing animation
     *
     * @property currentAnimation
     * @type String
     * @readOnly
     */
    this.currentAnimation = start;

    /**
     * The current frame being shown
     *
     * @property currentFrame
     * @type Number
     * @readOnly
     */
    this.currentFrame = 0;

    /**
     * Whether or not the animation is currently playing
     *
     * @property playing
     * @type Boolean
     * @readOnly
     */
    this.playing = false;

    this.hitArea = this.hitArea || new Rectangle(0, 0, this.width, this.height);

    //start playing
    this.gotoAndPlay(this.currentAnimation);
};

inherit(Sprite, PIXI.Sprite, {
    /**
     * Adds a new animation to this animated sprite
     *
     * @method addAnimation
     * @param name {String} The string name of the animation
     * @param frames {Array<Texture>} The array of texture frames
     * @param [speed] {Number} The animation speed
     * @param [loop] {Boolean} Loop the animation or not
     */
    addAnimation: function(name, frames, speed, loop) {
        if(typeof name === 'object') {
            this.animations[name.name] = name;
        } else {
            this.animations[name] = {
                name: name,
                frames: frames,
                speed: speed,
                loop: loop
            };
        }

        return this;
    },
    /**
     * Goes to a frame and starts playing the animation from there. You can optionally
     * pass the name of a new aniamtion to start playing.
     *
     * @method goto
     * @param frame {Number} The index of the frame to start on
     * @param [name] {String} The string name of the animation to go to
     */
    goto: function(frame, anim) {
        this.currentFrame = frame || 0;
        this.lastRound = math.round(frame || 0);

        if(anim) {
            this.currentAnimation = anim;
        }

        this.setTexture(this.animations[this.currentAnimation].frames[this.currentFrame]);
        this.emit('frame', this.currentAnimation, this.lastRound);

        return this;
    },
    /**
     * Starts playing the currently active animation
     *
     * @method play
     */
    play: function() {
        this.playing = true;
        return this;
    },
    /**
     * Stops playing the currently active animation
     *
     * @method stop
     */
    stop: function() {
        this.playing = false;
        return this;
    },
    /**
     * Removes this sprite from the stage and the physics system
     *
     * @method destroy
     */
    destroy: function() {
        this.stop();

        if(this._physics) {
            this._physics.removeSprite(this);
            this._physics = null;
        }

        if(this.parent)
            this.parent.removeChild(this);

        this.name = null;
        this.lifetime = null;
        this.speed = null;
        this.loop = null;
        this.animations = null;
        this.currentAnimation = null;
        this.currentFrame = null;
        this.playing = null;
        this.hitArea = null;
    },
    /**
     * Called by PIXI to update our textures and do the actual animation
     *
     * @method updateTransform
     * @private
     */
    updateTransform: function() {
        Sprite.prototype.updateTransform.call(this);

        if(!this.playing) return;

        var anim = this.animations[this.currentAnimation],
            round,
            loop = anim.loop !== undefined ? anim.loop : this.loop;

        this.currentFrame += anim.speed || this.speed;
        round = math.round(this.currentFrame);

        if(round < anim.frames.length) {
            if(round !== this.lastRound) {
                this.lastRound = round;
                this.setTexture(anim.frames[round]);
                this.emit('frame', this.currentAnimation, round);
            }
        }
        else {
            if(loop) {
                this.gotoAndPlay(0);
            } else {
                this.stop();
                this.emit('complete', this.currentAnimation);
            }
        }
    }
});

//Add event echos
/*
['click', 'mousedown', 'mouseup', 'mouseupoutside', 'mouseover', 'mouseout', 'mousemove', 'tap', 'touchstart', 'touchend', 'touchendoutside'].forEach(function(evtname) {
    Sprite.prototype[evtname] = module.exports = function(e) {
        this.emit(evtname, e);
    };
});
*/

/*
 * MOUSE Callbacks
 */

/**
 * A callback that is used when the users clicks on the sprite with their mouse
 *
 * @event click
 * @param interactionData {InteractionData}
 */

/**
 * A callback that is used when the user clicks the mouse down over the sprite
 *
 * @event mousedown
 * @param interactionData {InteractionData}
 */

/**
 * A callback that is used when the user releases the mouse that was over the sprite
 * for this callback to be fired the mouse must have been pressed down over the sprite
 *
 * @event mouseup
 * @param interactionData {InteractionData}
 */

/**
 * A callback that is used when the user releases the mouse that was over the sprite but is no longer over the sprite
 * for this callback to be fired, The touch must have started over the sprite
 *
 * @event mouseupoutside
 * @param interactionData {InteractionData}
 */

/**
 * A callback that is used when the users mouse rolls over the sprite
 *
 * @event mouseover
 * @param interactionData {InteractionData}
 */

/**
 * A callback that is used when the users mouse leaves the sprite
 *
 * @event mouseout
 * @param interactionData {InteractionData}
 */

/**
 * A callback that is used when the user moves the mouse while over the sprite
 *
 * @event mousemove
 * @param interactionData {InteractionData}
 */

/*
 * TOUCH Callbacks
 */

/**
 * A callback that is used when the users taps on the sprite with their finger
 * basically a touch version of click
 *
 * @event tap
 * @param interactionData {InteractionData}
 */

/**
 * A callback that is used when the user touch's over the sprite
 *
 * @event touchstart
 * @param interactionData {InteractionData}
 */

/**
 * A callback that is used when the user releases a touch over the sprite
 *
 * @event touchend
 * @param interactionData {InteractionData}
 */

/**
 * A callback that is used when the user releases the touch that was over the sprite
 * for this callback to be fired, The touch must have started over the sprite
 *
 * @event touchendoutside
 * @param interactionData {InteractionData}
 */
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2017 Bruce Schubert.
 * The MIT License
 * http://www.opensource.org/licenses/mit-license
 */

/* global define, WorldWind */

/**
 * The EnhancedWmsLayer provides vendor parameters to the GeoServer WMS GetMap
 * requests.
 *
 * @exports EnhancedWmsLayer
 * @author Bruce Schubert
 */
define(['worldwind'],
    function () {
        "use strict";

        var EnhancedWmsLayer = function (config, timeString) {
            WorldWind.WmsLayer.call(this, config, timeString);

            // Extract the bbox out of the WMS layer configuration
            this.bbox = config.sector;

            // Override the default WmsLayer 36x36 level set with one that
            // matches the GeoServer EPSG:4326 Gridset
            this.levels = new WorldWind.LevelSet(WorldWind.Sector.FULL_SPHERE,
                new WorldWind.Location(180, 180),
                config.numLevels || 22,
                256,
                256);

            // "tiled=true" is a hint for the GeoServer WMS to use the GeoWebCache
            this.vendorParms = '&tiled=true&tilesorigin=-180,-90';

            // The WW tileCache is too small to accomodate large screen
            // full of tiles at an oblique view from the surface.
            // Increase the size to prevent trashing of the tileCache.
            this.tileCache = new WorldWind.MemoryCache(4.0e6, 3.2e6);

        };

        // Inherit the WmsLayer methods
        EnhancedWmsLayer.prototype = Object.create(WorldWind.WmsLayer.prototype);

        /**
         * Returns the URL string for the resource. Overrides the TiledImageLayer
         * resourceUrlForTile method by appending the vendor paramerters to the URL.
         * @param {ImageTile} tile The tile whose image is returned
         * @param {String} imageFormat The mime type of the image format desired.
         * @returns {String} The URL string, or null if the string can not be formed.
         */
        EnhancedWmsLayer.prototype.resourceUrlForTile = function (tile, imageFormat) {
            var url = WorldWind.TiledImageLayer.prototype.resourceUrlForTile.call(this, tile, imageFormat);
            if (url) {
                url = url + this.vendorParms;
            }
            return url;
        };

        return EnhancedWmsLayer;
    }
);<commit_msg>Using WorldWindFixes constants in EnhancedWmsLayer. Re: issue #106.<commit_after>/*
 * Copyright (c) 2017 Bruce Schubert.
 * The MIT License
 * http://www.opensource.org/licenses/mit-license
 */

/* global define, WorldWind */

/**
 * The EnhancedWmsLayer provides vendor parameters to the GeoServer WMS GetMap
 * requests.
 *
 * @exports EnhancedWmsLayer
 * @author Bruce Schubert
 */
define(['WorldWindFixes', 'worldwind'],
    function (WorldWindFixes) {
        "use strict";

        var EnhancedWmsLayer = function (config, timeString) {
            WorldWind.WmsLayer.call(this, config, timeString);

            // Extract the bbox out of the WMS layer configuration
            this.bbox = config.sector;

            // Override the default WmsLayer 36x36 level set with one that
            // matches the GeoServer EPSG:4326 Gridset
            this.levels = new WorldWind.LevelSet(WorldWind.Sector.FULL_SPHERE,
                new WorldWind.Location(180, 180),
                config.numLevels || 22,
                256,
                256);

            // "tiled=true" is a hint for the GeoServer WMS to use the GeoWebCache
            this.vendorParms = '&tiled=true&tilesorigin=-180,-90';

            // The WW tileCache is too small to accomodate large screen
            // full of tiles at an oblique view from the surface.
            // Increase the size to prevent trashing of the tileCache.
            this.tileCache = new WorldWind.MemoryCache(
                WorldWindFixes.TILE_CACHE_CAPACITY,
                WorldWindFixes.TILE_CACHE_LOW_WATER);

        };

        // Inherit the WmsLayer methods
        EnhancedWmsLayer.prototype = Object.create(WorldWind.WmsLayer.prototype);

        /**
         * Returns the URL string for the resource. Overrides the TiledImageLayer
         * resourceUrlForTile method by appending the vendor paramerters to the URL.
         * @param {ImageTile} tile The tile whose image is returned
         * @param {String} imageFormat The mime type of the image format desired.
         * @returns {String} The URL string, or null if the string can not be formed.
         */
        EnhancedWmsLayer.prototype.resourceUrlForTile = function (tile, imageFormat) {
            var url = WorldWind.TiledImageLayer.prototype.resourceUrlForTile.call(this, tile, imageFormat);
            if (url) {
                url = url + this.vendorParms;
            }
            return url;
        };

        return EnhancedWmsLayer;
    }
);<|endoftext|>


<commit_before>/* jshint latedef:nofunc */

'use strict';

var isObject = require('lodash/lang/isObject');
var isFunction = require('lodash/lang/isFunction');
// WORKAROUND: using the phantomjs Function.prototype.bind polyfill
// the implementation in this file does not work strangely.
// it works however if we are using lodash bind here
// For whatever reason, the polyfill works alright in other places.
var bind = require('lodash/function/bind');

/**
 * Helpers for oo programming.
 *
 * @module
 */
var oo = {};

/**
  Initialize a class.

  After initializing a class it has a `static` scope which should be used for static properties
  and functions, which will be available in subclasses as well. This way we are as close as
  possible to ES6.

  Also, an initialized class has an `extend` function which can be used to derive subclasses.

  @param {Constructor} clazz

  @example

  ```
  function MyClass() {
    ...
  }
  oo.initClass(MyClass);
  ```

  The simplest way to create a subclass is

  ```
    var Foo = MyClass.extend()
  ```

  This is the disadvantage, that the created class is anonymous, i.e., in a debugger it
  does not have a senseful name.

  The preferred way is to extend a subclass this way:

  ```
    function Foo() {
      Foo.super.apply(this, arguments);
    }
    MyClass.extend(Foo);
  ```

  This correnponds to what would do in ES6 with

  ```
    class Foo extends MyClass {}
  ```

  It is also possible to derive a class and provide a prototype as an object:

  ```
    var Foo = MyClass.extend({
      bla: function() { return "bla"; }
    });
  ```

  Again the result is an anonymous class, without the ability to show a meaningful name in a
  debugger.

  If you want to define a prototype, the preferred way is extending an already defined class:

  ```
    function Foo() {
      Foo.super.apply(this, arguments);
    }
    MyClass.extend(Foo, {
      bla: function() { return "bla"; }
    });
  ```

  If you prefer to write prototypes as functions you should do it this way:

  ```
    function Foo() {
      Foo.super.apply(this, arguments);
    }
    MyClass.extend(Foo, function() {
      this.bla = function() { return "bla"; };
    });
  ```

  In that case the protoype is an anonymous class, i.e. it won't have a meaningful name in the debugger.

  To overcome this you can give the prototype function a name:

  ```
    function Foo() {
      Foo.super.apply(this, arguments);
    }
    MyClass.extend(Foo, function FooPrototype() {
      this.bla = function() { return "bla"; };
    });
  ```

  **Static variables**

  Static variables can either be set directly on the `static` scope:

  ```
    var Foo = MyClass.extend();
    Foo.static.foo = "foo"
  ```

  Or with a prototype you can provide them in a `static` object property of the prototype:

  ```
    MyClass.extend({
      static: {
        foo: "foo"
      }
    });
    MyClass.static.foo -> "foo"
  ```

  A static scope of a class comes with a reference to its owning class. I.e.,

  ```
    MyClass.static.__class__
  ```

  Gives gives access to `MyClass`.

  **Mix-ins**

  Mixins must be plain objects. They get merged into the created prototype.

  ```
    var MyMixin = {
      foo: "foo";
    };
    function Foo() {
      Foo.super.apply(this, arguments);
    }
    MyClass.extend(Foo, MyMixin);
  ```

  This is also possible in combination with prototype functions.

  ```
    var MyMixin = {
      foo: "foo";
      bar: "bar";
    };
    function Foo() {
      Foo.super.apply(this, arguments);
    }
    MyClass.extend(Foo, MyMixin, function() {
      this.bar = "this wins"
    });
  ```
  Mixins never override existing prototype functions, or already other mixed in members.
*/
oo.initClass = function(clazz) {
  _initClass(clazz);
  _makeExtensible(clazz);
};

/**
 * Inherit from a parent class.
 *
 * @param {Constructor} clazz class constructor
 * @param {Constructor} parentClazz parent constructor
 *
 * @example
 *
 * ```js
 * var oo = require('substance/basics/oo');
 * var Parent = function() {};
 * Parent.Prototype = function() {
 *   this.foo = function() { return 'foo'; }
 * }
 * var Child = function() {
 *   Parent.apply(this, arguments);
 * }
 * oo.inherit(Child, Parent);
 * ```
 */
oo.inherit =  function(clazz, parentClazz) {
  if (!clazz.__is_initialized__) {
    oo.initClass(clazz);
  }
  _inherit(clazz, parentClazz);
  _afterClassInitHook(clazz);
};

/*
 * @param clazz {Constructor} class constructor
 * @param mixinClazz {Constructor} parent constructor
 */
oo.mixin = function(Clazz, mixin) {
  if (!Clazz.__is_initialized__) {
    oo.initClass(Clazz);
  }
  _mixin(Clazz, mixin);
};

// ### Internal implementations

function _initClass(clazz) {
  if (clazz.__is_initialized__) return;
  if (clazz.Prototype && !(clazz.prototype instanceof clazz.Prototype)) {
    clazz.prototype = new clazz.Prototype();
    clazz.prototype.constructor = clazz;
  }
  var StaticScope = _StaticScope();
  clazz.static = clazz.static || new StaticScope(clazz);
  clazz.__is_initialized__ = true;
}

function _inherit(ChildClass, ParentClass) {
  if (ChildClass.prototype instanceof ParentClass) {
    throw new Error('Target already inherits from origin');
  }
  // Customization: supporting a prototype constructor function
  // defined as a static member 'Prototype' of the target function.
  var PrototypeCtor = ChildClass.Prototype;
  // Provide a shortcut to the parent constructor
  ChildClass.super = ParentClass;
  if (PrototypeCtor) {
    PrototypeCtor.prototype = ParentClass.prototype;
    ChildClass.prototype = new PrototypeCtor();
    ChildClass.prototype.constructor = ChildClass;
  } else {
    ChildClass.prototype = Object.create(ParentClass.prototype, {
      // Restore constructor property of clazz
      constructor: {
        value: ChildClass,
        enumerable: false,
        writable: true,
        configurable: true
      }
    });
  }
  // provide a shortcut to the parent prototype
  ChildClass.prototype.super = ParentClass.prototype;
  // Extend static properties - always initialize both sides
  var StaticScope = _StaticScope();
  if (ParentClass.static) {
    StaticScope.prototype = ParentClass.static;
  }
  ChildClass.static = new StaticScope(ChildClass);
  if (ChildClass.static._makeExtendFunction) {
    ChildClass.extend = ChildClass.static._makeExtendFunction(ChildClass);
  } else {
    _makeExtensible(ChildClass);
  }
}

function _afterClassInitHook(childClazz) {
  var proto = childClazz.prototype;
  for(var key in proto) {
    if (key === "constructor" ||
        key === "__class__" ||
        !proto.hasOwnProperty(key)) continue;
    // built-in: extend class.static with prototype.static
    if (key === 'static') {
      _copyStaticProps(childClazz.static, proto.static);
      continue;
    }
  }
}

/*
  extend() -> lazy inheritance without a proto
  extend({...}) -> lazy inheritance with a proto
  extend(Function) -> inheritance without a proto
  extend(Function, {}) -> inherit with a proto
  extend(Function, Function) -> inheritance with prototype function
*/
function _extendClass(ParentClass) {

  // this ctor is used when extend is not provided with a constructor function.
  function AnonymousClass() {
    ParentClass.apply(this, arguments);
  }

  var args = Array.prototype.slice.call(arguments, 1);
  //var childOrProto = args[args.length-1];
  var ChildClass;
  var mixins = [];

  // the first argument must be a Class constructor, otherwise we will use an anonymous ctor.
  var idx = 0;
  if (isFunction(args[0])) {
    ChildClass = args[0];
    idx++;
  } else {
    ChildClass = AnonymousClass;
  }
  // the remaining arguments should be Objects used as a mixin for the created prototype
  // the last argument may be a prototype constructor function.
  for (; idx < args.length; idx++) {
    if (isFunction(args[idx])) {
      if (idx !== args.length-1) {
        throw new Error('Illegal use of Class.extend(): Prototype function must be last argument.');
      }
      if (ChildClass.hasOwnProperty('Prototype')) {
        throw new Error('Class ' + ChildClass.name + ' has defined ' + ChildClass.name +
         '.Prototype which would be overwritten by Class.extend().\n' +
         'You provided a prototype function when calling Class.extend().');
      } else {
        ChildClass.Prototype = args[idx];
      }
      break;
    } else if (isObject(args[idx])) {
      mixins.push(args[idx]);
    } else {
      throw new Error('Illegal use of Class.extend');
    }
  }
  _inherit(ChildClass, ParentClass);

  // from right to left copy all mixins into the prototype
  // but never overwrite
  // like with lodash/object/extend, the mixin later in the args list 'wins'
  for (var i = mixins.length - 1; i >= 0; i--) {
    _mixin(ChildClass, mixins[i]);
  }

  return ChildClass;
}

function _mixin(Clazz, mixin) {
  var proto = Clazz.prototype;
  for(var key in mixin) {
    if (mixin.hasOwnProperty(key)) {
      // built-in: extend class.static with prototype.static
      if (key === 'static') {
        _copyStaticProps(Clazz.static, mixin.static);
        continue;
      } else {
        if (!proto.hasOwnProperty(key)) {
          proto[key] = mixin[key];
        }
      }
    }
  }
}

function _makeExtensible(clazz) {
  if (!clazz.static) {
    oo.initClass(clazz);
  }
  clazz.static._makeExtendFunction = function(parentClazz) {
    return bind(_extendClass, clazz, parentClazz);
  };
  clazz.extend = clazz.static._makeExtendFunction(clazz);
}

oo.makeExtensible = _makeExtensible;

function _StaticScope() {
  return function StaticScope(clazz) {
    this.__class__ = clazz;
  };
}

function _copyStaticProps(staticProps, parentStaticProps) {
  for ( var key in parentStaticProps ) {
    if ( key === 'constructor' ||
         key === '__class__' ||
         !parentStaticProps.hasOwnProperty( key ) ||
         // don't overwrite static properties
         staticProps.hasOwnProperty(key) ) {
      continue;
    }
    staticProps[key] = parentStaticProps[key];
  }
}

module.exports = oo;
<commit_msg>Improved document for Class.extend<commit_after>/* jshint latedef:nofunc */

'use strict';

var isObject = require('lodash/lang/isObject');
var isFunction = require('lodash/lang/isFunction');
// WORKAROUND: using the phantomjs Function.prototype.bind polyfill
// the implementation in this file does not work strangely.
// it works however if we are using lodash bind here
// For whatever reason, the polyfill works alright in other places.
var bind = require('lodash/function/bind');

/**
 * Helpers for oo programming.
 *
 * @module
 */
var oo = {};

/**
  Initialize a class.

  After initializing a class has a `static` scope which can be used for static properties
  and functions. These static members get inherited by subclasses, which makes this approach
  as close as possible to ES6.

  An initialized class has an `extend` function which can be used to derive subclasses.

  @param {Constructor} clazz

  @example

  ```
  function MyClass() {
    ...
  }
  oo.initClass(MyClass);
  ```

  #### Extending a class

  The simplest way to create a subclass is

  ```
  var Foo = MyClass.extend()
  ```

  This is the disadvantage, that the created class is anonymous, i.e., in a debugger it
  does not have a senseful name.

  The preferred way is to extend a subclass this way:

  ```
  function Foo() {
    Foo.super.apply(this, arguments);
  }
  MyClass.extend(Foo);
  ```

  This correnponds to what would do in ES6 with

  ```
  class Foo extends MyClass {}
  ```

  It is also possible to derive a class and provide a prototype as an object:

  ```
  var Foo = MyClass.extend({
    bla: function() { return "bla"; }
  });
  ```

  Again the result is an anonymous class, without the ability to show a meaningful name in a
  debugger.

  If you want to define a prototype, the preferred way is extending an already defined class:

  ```
  function Foo() {
    Foo.super.apply(this, arguments);
  }
  MyClass.extend(Foo, {
    bla: function() { return "bla"; }
  });
  ```

  If you prefer to write prototypes as functions you should do it this way:

  ```
  function Foo() {
    Foo.super.apply(this, arguments);
  }
  MyClass.extend(Foo, function() {
    this.bla = function() { return "bla"; };
  });
  ```

  In that case the protoype is an anonymous class, i.e. it won't have a meaningful name in the debugger.

  To overcome this you can give the prototype function a name:

  ```
  function Foo() {
    Foo.super.apply(this, arguments);
  }
  MyClass.extend(Foo, function FooPrototype() {
    this.bla = function() { return "bla"; };
  });
  ```

  #### Static variables

  Static variables can either be set directly on the `static` scope:

  ```
  var Foo = MyClass.extend();
  Foo.static.foo = "foo"
  ```

  Or with a prototype you can provide them in a `static` object property of the prototype:

  ```
  MyClass.extend({
    static: {
      foo: "foo"
    }
  });
  MyClass.static.foo -> "foo"
  ```

  A static scope of a class comes with a reference to its owning class. I.e.,

  ```
  MyClass.static.__class__
  ```

  Gives gives access to `MyClass`.

  #### Mix-ins

  Mixins must be plain objects. They get merged into the created prototype.

  ```
  var MyMixin = {
    foo: "foo";
  };
  function Foo() {
    Foo.super.apply(this, arguments);
  }
  MyClass.extend(Foo, MyMixin);
  ```

  This is also possible in combination with prototype functions.

  ```
  var MyMixin = {
    foo: "foo";
    bar: "bar";
  };
  function Foo() {
    Foo.super.apply(this, arguments);
  }
  MyClass.extend(Foo, MyMixin, function() {
    this.bar = "this wins"
  });
  ```
  Mixins never override existing prototype functions, or already other mixed in members.

*/
oo.initClass = function(clazz) {
  _initClass(clazz);
  _makeExtensible(clazz);
};

/**
 * Inherit from a parent class.
 *
 * @param {Constructor} clazz class constructor
 * @param {Constructor} parentClazz parent constructor
 *
 * @example
 *
 * ```js
 * var oo = require('substance/basics/oo');
 * var Parent = function() {};
 * Parent.Prototype = function() {
 *   this.foo = function() { return 'foo'; }
 * }
 * var Child = function() {
 *   Parent.apply(this, arguments);
 * }
 * oo.inherit(Child, Parent);
 * ```
 */
oo.inherit =  function(clazz, parentClazz) {
  if (!clazz.__is_initialized__) {
    oo.initClass(clazz);
  }
  _inherit(clazz, parentClazz);
  _afterClassInitHook(clazz);
};

/*
 * @param clazz {Constructor} class constructor
 * @param mixinClazz {Constructor} parent constructor
 */
oo.mixin = function(Clazz, mixin) {
  if (!Clazz.__is_initialized__) {
    oo.initClass(Clazz);
  }
  _mixin(Clazz, mixin);
};

// ### Internal implementations

function _initClass(clazz) {
  if (clazz.__is_initialized__) return;
  if (clazz.Prototype && !(clazz.prototype instanceof clazz.Prototype)) {
    clazz.prototype = new clazz.Prototype();
    clazz.prototype.constructor = clazz;
  }
  var StaticScope = _StaticScope();
  clazz.static = clazz.static || new StaticScope(clazz);
  clazz.__is_initialized__ = true;
}

function _inherit(ChildClass, ParentClass) {
  if (ChildClass.prototype instanceof ParentClass) {
    throw new Error('Target already inherits from origin');
  }
  // Customization: supporting a prototype constructor function
  // defined as a static member 'Prototype' of the target function.
  var PrototypeCtor = ChildClass.Prototype;
  // Provide a shortcut to the parent constructor
  ChildClass.super = ParentClass;
  if (PrototypeCtor) {
    PrototypeCtor.prototype = ParentClass.prototype;
    ChildClass.prototype = new PrototypeCtor();
    ChildClass.prototype.constructor = ChildClass;
  } else {
    ChildClass.prototype = Object.create(ParentClass.prototype, {
      // Restore constructor property of clazz
      constructor: {
        value: ChildClass,
        enumerable: false,
        writable: true,
        configurable: true
      }
    });
  }
  // provide a shortcut to the parent prototype
  ChildClass.prototype.super = ParentClass.prototype;
  // Extend static properties - always initialize both sides
  var StaticScope = _StaticScope();
  if (ParentClass.static) {
    StaticScope.prototype = ParentClass.static;
  }
  ChildClass.static = new StaticScope(ChildClass);
  if (ChildClass.static._makeExtendFunction) {
    ChildClass.extend = ChildClass.static._makeExtendFunction(ChildClass);
  } else {
    _makeExtensible(ChildClass);
  }
}

function _afterClassInitHook(childClazz) {
  var proto = childClazz.prototype;
  for(var key in proto) {
    if (key === "constructor" ||
        key === "__class__" ||
        !proto.hasOwnProperty(key)) continue;
    // built-in: extend class.static with prototype.static
    if (key === 'static') {
      _copyStaticProps(childClazz.static, proto.static);
      continue;
    }
  }
}

/*
  extend() -> lazy inheritance without a proto
  extend({...}) -> lazy inheritance with a proto
  extend(Function) -> inheritance without a proto
  extend(Function, {}) -> inherit with a proto
  extend(Function, Function) -> inheritance with prototype function
*/
function _extendClass(ParentClass) {

  // this ctor is used when extend is not provided with a constructor function.
  function AnonymousClass() {
    ParentClass.apply(this, arguments);
  }

  var args = Array.prototype.slice.call(arguments, 1);
  //var childOrProto = args[args.length-1];
  var ChildClass;
  var mixins = [];

  // the first argument must be a Class constructor, otherwise we will use an anonymous ctor.
  var idx = 0;
  if (isFunction(args[0])) {
    ChildClass = args[0];
    idx++;
  } else {
    ChildClass = AnonymousClass;
  }
  // the remaining arguments should be Objects used as a mixin for the created prototype
  // the last argument may be a prototype constructor function.
  for (; idx < args.length; idx++) {
    if (isFunction(args[idx])) {
      if (idx !== args.length-1) {
        throw new Error('Illegal use of Class.extend(): Prototype function must be last argument.');
      }
      if (ChildClass.hasOwnProperty('Prototype')) {
        throw new Error('Class ' + ChildClass.name + ' has defined ' + ChildClass.name +
         '.Prototype which would be overwritten by Class.extend().\n' +
         'You provided a prototype function when calling Class.extend().');
      } else {
        ChildClass.Prototype = args[idx];
      }
      break;
    } else if (isObject(args[idx])) {
      mixins.push(args[idx]);
    } else {
      throw new Error('Illegal use of Class.extend');
    }
  }
  _inherit(ChildClass, ParentClass);

  // from right to left copy all mixins into the prototype
  // but never overwrite
  // like with lodash/object/extend, the mixin later in the args list 'wins'
  for (var i = mixins.length - 1; i >= 0; i--) {
    _mixin(ChildClass, mixins[i]);
  }

  return ChildClass;
}

function _mixin(Clazz, mixin) {
  var proto = Clazz.prototype;
  for(var key in mixin) {
    if (mixin.hasOwnProperty(key)) {
      // built-in: extend class.static with prototype.static
      if (key === 'static') {
        _copyStaticProps(Clazz.static, mixin.static);
        continue;
      } else {
        if (!proto.hasOwnProperty(key)) {
          proto[key] = mixin[key];
        }
      }
    }
  }
}

function _makeExtensible(clazz) {
  if (!clazz.static) {
    oo.initClass(clazz);
  }
  clazz.static._makeExtendFunction = function(parentClazz) {
    return bind(_extendClass, clazz, parentClazz);
  };
  clazz.extend = clazz.static._makeExtendFunction(clazz);
}

oo.makeExtensible = _makeExtensible;

function _StaticScope() {
  return function StaticScope(clazz) {
    this.__class__ = clazz;
  };
}

function _copyStaticProps(staticProps, parentStaticProps) {
  for ( var key in parentStaticProps ) {
    if ( key === 'constructor' ||
         key === '__class__' ||
         !parentStaticProps.hasOwnProperty( key ) ||
         // don't overwrite static properties
         staticProps.hasOwnProperty(key) ) {
      continue;
    }
    staticProps[key] = parentStaticProps[key];
  }
}

module.exports = oo;
<|endoftext|>


<commit_before>'use strict';
var async = require('async');
var config = require('./config.json');

exports.getFlavor = function getFlavor(value, arr) {
    var key = Object.keys(arr).filter(function (a) {
            return a <= value;
        }),
        res;
    key = key[key.length - 1];
    res = arr[key];
    if (typeof res !== 'string' && res !== undefined) {
        res = res[Math.floor(Math.random() * res.length)];
    }
    return res;
};

exports.getScore = function getScore(check) {
    var code = check.responseCode,
        time = check.responseTime;
    if ((code !== 200 && code !== 204) || time > 12) {
        return 0;
    } else if (time > 3) {
        return 50;
    }
    return 100;
};

exports.range = function range(num) {
    return Array.apply(null, Array(num)).map(function (_, i) {
        return i;
    });
};

exports.round = function round(num, places) {
    if (!places) {
        places = 0;
    }
    places = Math.pow(10, places);
    return Math.round(num * places) / places;
};

exports.sum = function sum(arr, mapper) {
    if (!mapper || !mapper.call) {
        mapper = function mapper(data) {
            return data;
        };
    }
    return arr.map(mapper).reduce(function (a, b) {
        return a + b;
    }, 0);
};

exports.average = function average(arr, mapper) {
    if (!mapper || !mapper.call) {
        mapper = function mapper(data) {
            return data;
        };
    }
    return exports.sum(arr, mapper) / arr.length;
};

exports.formattedRowGenerator = function formattedRowGenerator() {
    function format(row) {
        row.score = exports.getScore(row);
        row.response = exports.getFlavor(row.score, config.scoreCode);
        row.polledAt = new Date(row.checkedAt).toGMTString();
    }
    var avg = [];
    return function (row, callback) {
        var res = [row];
        avg.unshift(row);
        format(row);
        if (avg.length === config.checks.length) {
            var overall = {
                checkName: 'overall',
                checkId: -1,
                responseCode: exports.round(exports.average(avg, function (n) {
                    return n.responseCode;
                })),
                responseTime: exports.round(exports.average(avg, function (n) {
                    return n.responseTime;
                }), 3),
                checkedAt: avg[0].checkedAt
            };
            format(overall);
            res.push(overall);
            avg.pop();
        }
        process.nextTick(function () {
            callback(null, res);
        });
    };
};

exports.parseData = function parseData(data, formatter, callback) {
    var result = {
        'overall': []
    };
    config.checks.forEach(function (key) {
        result[key.replace(/^https?:\/\//, '')] = [];
    });
    async.eachSeries(data, function (row, next) {
        formatter(row, function (_, rows) {
            rows.forEach(function (r) {
                result[r.checkName].unshift(r);
            });
            next();
        });
    }, function () {
        callback(null, result);
    });
};

exports.truncateData = function truncateData(data, filter, minimum) {
    if (!data){
        return [];
    }
    if (typeof filter === 'number') {
        minimum = filter;
        filter = function () {
            return true;
        };
    }
    var res = data.filter(filter);
    if (res.length < minimum) {
        res = data.slice(0, minimum);
    }
    return res;
};

exports.roundAverage = function roundAverage(data, reduce, places) {
    return exports.round(exports.average(data, reduce), places);
};
<commit_msg>Overall HTTP status code is now the maximum<commit_after>'use strict';
var async = require('async');
var config = require('./config.json');

exports.getFlavor = function getFlavor(value, arr) {
    var key = Object.keys(arr).filter(function (a) {
            return a <= value;
        }),
        res;
    key = key[key.length - 1];
    res = arr[key];
    if (typeof res !== 'string' && res !== undefined) {
        res = res[Math.floor(Math.random() * res.length)];
    }
    return res;
};

exports.getScore = function getScore(check) {
    var code = check.responseCode,
        time = check.responseTime;
    if ((code !== 200 && code !== 204) || time > 12) {
        return 0;
    } else if (time > 3) {
        return 50;
    }
    return 100;
};

exports.range = function range(num) {
    return Array.apply(null, Array(num)).map(function (_, i) {
        return i;
    });
};

exports.round = function round(num, places) {
    if (!places) {
        places = 0;
    }
    places = Math.pow(10, places);
    return Math.round(num * places) / places;
};

exports.sum = function sum(arr, mapper) {
    if (!mapper || !mapper.call) {
        mapper = function mapper(data) {
            return data;
        };
    }
    return arr.map(mapper).reduce(function (a, b) {
        return a + b;
    }, 0);
};

exports.average = function average(arr, mapper) {
    if (!mapper || !mapper.call) {
        mapper = function mapper(data) {
            return data;
        };
    }
    return exports.sum(arr, mapper) / arr.length;
};

exports.max = function max(arr, mapper) {
    if (!mapper || !mapper.call) {
        mapper = function mapper(data) {
            return data;
        };
    }
    return Math.max(arr.map(mapper));
};

exports.formattedRowGenerator = function formattedRowGenerator() {
    function format(row) {
        row.score = exports.getScore(row);
        row.response = exports.getFlavor(row.score, config.scoreCode);
        row.polledAt = new Date(row.checkedAt).toGMTString();
    }
    var avg = [];
    return function (row, callback) {
        var res = [row];
        avg.unshift(row);
        format(row);
        if (avg.length === config.checks.length) {
            var overall = {
                checkName: 'overall',
                checkId: -1,
                responseCode: exports.round(exports.max(avg, function (n) {
                    return n.responseCode;
                })),
                responseTime: exports.round(exports.average(avg, function (n) {
                    return n.responseTime;
                }), 3),
                checkedAt: avg[0].checkedAt
            };
            format(overall);
            res.push(overall);
            avg.pop();
        }
        process.nextTick(function () {
            callback(null, res);
        });
    };
};

exports.parseData = function parseData(data, formatter, callback) {
    var result = {
        'overall': []
    };
    config.checks.forEach(function (key) {
        result[key.replace(/^https?:\/\//, '')] = [];
    });
    async.eachSeries(data, function (row, next) {
        formatter(row, function (_, rows) {
            rows.forEach(function (r) {
                result[r.checkName].unshift(r);
            });
            next();
        });
    }, function () {
        callback(null, result);
    });
};

exports.truncateData = function truncateData(data, filter, minimum) {
    if (!data){
        return [];
    }
    if (typeof filter === 'number') {
        minimum = filter;
        filter = function () {
            return true;
        };
    }
    var res = data.filter(filter);
    if (res.length < minimum) {
        res = data.slice(0, minimum);
    }
    return res;
};

exports.roundAverage = function roundAverage(data, reduce, places) {
    return exports.round(exports.average(data, reduce), places);
};
<|endoftext|>


<commit_before>if(!Hummingbird) { var Hummingbird = {}; }

Hummingbird.Graph = function(canvas) {
  if ( !(this instanceof Hummingbird.Graph) ) {
    return new Hummingbird.Graph(canvas);
  }

  this.canvas = canvas;
  this.trafficLog = [];

  this.init();
};

Hummingbird.Graph.prototype = {
  init: function() {
    this.setupContext();

    this.lineColors = [ "#FFFFFF", "#3BA496", "#65B88A",
                        "#F1E29F", "#C44939", "#983839" ];
    this.bgLineColor = "#555";
    this.canvasHeight = $(this.canvas).height();
    this.canvasWidth = $(this.canvas).width();
  },

  addValue: function(value) {
    this.trafficLog.push(value);
    if(this.trafficLog.length > 20) {
      this.trafficLog.shift();
    }
  },

  shiftCanvas: function(x, y) {
    this.context.putImageData(this.context.getImageData(x, y, this.canvas.width, this.canvas.height), 0, 0);
  },

  setupContext: function() {
    if(this.canvas.getContext) {
      this.context = this.canvas.getContext('2d');
    } else {
      alert("Sorry, this browser doesn't support canvas");
    }

    this.context.lineWidth = 3;
  },

  runningAverage: function() {
    return this.trafficLog.sum() / this.trafficLog.length;
  },

  drawLogPath: function(percent) {
    this.addValue(percent);
    var average = this.runningAverage();
    var height = Math.max(average * this.canvasHeight, 1);
    var colorIndex = Math.min(Math.ceil((average / 2) * 10), 5);
    var color = this.lineColors[colorIndex];
    var endingPoint = this.canvasHeight - height;

    this.shiftCanvas(6, 0);
    this.context.beginPath();
    this.context.strokeStyle = color;
    this.context.moveTo(this.canvasWidth - 10, this.canvasHeight);
    this.context.lineTo(this.canvasWidth - 10, endingPoint);
    this.context.stroke();
    this.context.closePath();

    this.context.beginPath();
    this.context.strokeStyle = this.bgLineColor;
    this.context.moveTo(this.canvasWidth - 10, endingPoint);
    this.context.lineTo(this.canvasWidth - 10, 0);
    this.context.stroke();
    this.context.closePath();
  }
};
<commit_msg>Draw empty graph to start<commit_after>if(!Hummingbird) { var Hummingbird = {}; }

Hummingbird.Graph = function(canvas) {
  if ( !(this instanceof Hummingbird.Graph) ) {
    return new Hummingbird.Graph(canvas);
  }

  this.canvas = canvas;
  this.trafficLog = [];

  this.init();
};

Hummingbird.Graph.prototype = {
  init: function() {
    this.setupContext();

    this.lineColors = [ "#FFFFFF", "#3BA496", "#65B88A",
                        "#F1E29F", "#C44939", "#983839" ];
    this.bgLineColor = "#555";
    this.canvasHeight = $(this.canvas).height();
    this.canvasWidth = $(this.canvas).width();

    this.drawEmptyGraph();
  },

  addValue: function(value) {
    this.trafficLog.push(value);
    if(this.trafficLog.length > 20) {
      this.trafficLog.shift();
    }
  },

  shiftCanvas: function(x, y) {
    this.context.putImageData(this.context.getImageData(x, y, this.canvas.width, this.canvas.height), 0, 0);
  },

  setupContext: function() {
    if(this.canvas.getContext) {
      this.context = this.canvas.getContext('2d');
    } else {
      alert("Sorry, this browser doesn't support canvas");
    }

    this.context.lineWidth = 3;
  },

  runningAverage: function() {
    return this.trafficLog.sum() / this.trafficLog.length;
  },

  drawEmptyGraph: function() {
    var dataPoints = Math.ceil(this.canvasWidth / 6)
    while(dataPoints--) {
      this.drawLogPath(0);
    }
  },

  drawLogPath: function(percent) {
    this.addValue(percent);
    var average = this.runningAverage();
    var height = Math.max(average * this.canvasHeight, 1);
    var colorIndex = Math.min(Math.ceil((average / 2) * 10), 5);
    var color = this.lineColors[colorIndex];
    var endingPoint = this.canvasHeight - height;

    this.shiftCanvas(6, 0);
    this.context.beginPath();
    this.context.strokeStyle = color;
    this.context.moveTo(this.canvasWidth - 10, this.canvasHeight);
    this.context.lineTo(this.canvasWidth - 10, endingPoint);
    this.context.stroke();
    this.context.closePath();

    this.context.beginPath();
    this.context.strokeStyle = this.bgLineColor;
    this.context.moveTo(this.canvasWidth - 10, endingPoint);
    this.context.lineTo(this.canvasWidth - 10, 0);
    this.context.stroke();
    this.context.closePath();
  }
};
<|endoftext|>


<commit_before>
var maxValue = 100;
var all = [];

var template;

$(function(){
  $('#send-url').on('click', fetchAndRefresh);
  $('#get-rss').on('click', fetchRSS);
  template = Handlebars.compile($("#metric-template").html());
});

function blockState(){
  $('#url').attr('disabled', true);
  $('#send-url, #get-rss').button('loading');
}

function resetState(){
  $('#send-url, #get-rss').button('reset');
  $('#url').val("").attr('disabled', false);
}

function fetchAndRefresh(){
  var url = $('#url').val();

  if (url.length === 0){
    window.alert('enter a url!');
  }

  blockState();

  $.ajax({
    url: '/counts?url=' + url,
    dataType: 'json',
  })
  .done(function(counters) {
    createCounter(url, setScore(counters));
    resetState();
  })
  .error(function(err,a,b){
    console.log('ERROR on fetch > ' + err);
    resetState();
  });

}

function fetchRSS(){
  blockState();

  $.ajax({
    url: '/rss_counts',
    dataType: 'json',
  })
  .done(function(res) {

    res.data.forEach(function(data){
      createCounter(data.url, setScore(data.counters));
    });

    resetState();
  })
  .error(function(err,a,b){
    console.log('ERROR on fetch > ' + err);
    resetState();
  });
}

function setScore(counters){
  var sum = 0;
  counters.socialScore = 0;

  for (var p in counters){
    if (p !== "googleanalytics" && counters[p].total > 0){
      sum += counters[p].total;
    }
  }

  var tGA = counters.googleanalytics.total;
  if (tGA > 0){
    counters.socialScore = (sum * 100) / tGA;
  }

  return counters;
}

function createCounter(url, counters){
  var parsed = url.split('/'),
    tail = parsed[parsed.length-1] || parsed[parsed.length-2];

  var template = Handlebars.compile($("#metric-template").html());
  var html = template({
    url: url,
    tail: tail,
    counters: counters
  });

  all.push({
    url: url,
    tail: tail,
    counters: counters,
    svg: null,
    html: html
  });

  if (updateMax(counters)){
    $('.metric').remove();
    all.forEach(draw);
    return;
  }

  draw(all[all.length-1]);
}

function updateMax(counter){
  var updated = false;

  function checkCounter(c){
    for (var p in c){
      if (p !== "googleanalytics" && c[p].total > maxValue){
        maxValue = c[p].total;
        updated = true;
      }
    }
  }

  if (counter)
    checkCounter(counter);
  else
    all.forEach(function(item){
      checkCounter(item.counters);
    });

  return updated;
}

function removeItem(item){
  all.forEach(function(_item, i){
    if (item.url === _item.url){
      all.splice(i, 1);
    }
  });

  maxValue = 100;
  updateMax();

  $('.metric').remove();
  all.forEach(draw);
}

var social_textures_in = {};
var social_textures_out = {};

var networks = [
    'twitter'
  , 'googleplus'
  , 'linkedin'
  , 'facebook'
];

function getTexture(nt, _in){

  function getBy(){
    var args = arguments;

    var txt = textures.lines()
      .orientation(args[0])
      .size(args[1]);

    if (_in){
      return txt
        .strokeWidth(args[2])
        .background(args[3])
        .stroke(args[4]);
    }

    return txt
      .stroke('#F0F3F0')
      .background(args[3]);
  }

  switch (nt){
    case 'facebook': return getBy('diagonal', 10, 5, '#ffffff','#3b5998');
    case 'twitter': return getBy('3/8', 12, 5, '#ffffff','#00aced');
    case 'linkedin': return getBy('vertical', 6, 5, '#ffffff','#007bb6');
    case 'googleplus': return getBy('7/8', 15, 6, '#ffffff','#dd4b39');
  }
}

function createTextures(svg){

  networks.forEach(function(network){
    social_textures_in[network] = getTexture(network, true);
    social_textures_out[network] = getTexture(network);

    svg.call(social_textures_in[network])
    svg.call(social_textures_out[network])
  });

}

function sortMetrics($metricParent){
  var $list = $('.data > .list-group', $metricParent);

  var ntwksSort = [
      'socialScore'
    , 'googleanalytics'
    , 'facebook'
    , 'twitter'
    , 'linkedin'
    , 'googleplus'
  ];

  ntwksSort.forEach(function(name){
    $('>.item-' + name, $list).appendTo($list);
  });
}

function draw(item){
  // taken from http://bl.ocks.org/bbest/2de0e25d4840c68f2db1

  var width = 300,
    height = 300,
    radius = Math.min(width, height) / 2,
    innerRadius = 0.3 * radius;

  var tip = d3.tip()
    .attr('class', 'd3-tip')
    .offset([0, 0])
    .html(function(d) {
      return d.data.label + ": " + d.data.score + "";
    });

  var pie = d3.layout.pie().sort(null).value(function(d) { return d.width; });

  var arc = d3.svg.arc()
    .innerRadius(innerRadius)
    .outerRadius(function (d) {
      return (radius - innerRadius) * (d.data.per / 100.0) + innerRadius;
    });

  var outlineArc = d3.svg.arc()
          .innerRadius(innerRadius)
          .outerRadius(radius);

  var $metricParent = $(item.html).appendTo("#ctn");
  sortMetrics($metricParent);
  var $metric = $('.score', $metricParent);

  var svg = d3.select($metric[0]).append("svg")
      .attr("width", width)
      .attr("height", height)
      .append("g")
      .attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");

  svg.call(tip);
  createTextures(svg);

  item.svg = svg;

  var data = convertData(item);

  var outerPath = svg.selectAll(".outlineArc")
      .data(pie(data))
      .enter().append("path")
      .attr("fill", function(d) { return social_textures_out[d.data.id].url(); })
      .attr("stroke", "#E6EBE6")
      .attr("class", "outlineArc")
      .attr("d", outlineArc);

  var path = svg.selectAll(".solidArc")
      .data(pie(data))
      .enter().append("path")
      .attr("fill", function(d) { return social_textures_in[d.data.id].url(); })
      .attr("class", "solidArc")
      .attr("d", arc)
      .on('mouseover', tip.show)
      .on('mouseout', tip.hide);

  var sc = item.counters.socialScore;

  svg.append("svg:text")
    .attr("class", "aster-score")
    .attr("dy", ".35em")
    .attr("text-anchor", "middle")
    .text(sc ? sc.toFixed(1) + "%" : "-");

  var close = $('x').on('click', function(){
    return (function(_item){
      removeItem(_item);
    })(item);
  });

  $metricParent
    .prepend('' + item.tail + '')
    .children('.nav-tabs').append(close)

  $('.collapser', $metricParent).on('click', function(){
    $('.details', $(this).parents('.list-group-item')).collapse('toggle');
  });

  $('.nav-tabs a', $metricParent).click(function (e) {
    e.preventDefault();
    var ele = $(this);
    var target = $('.' + ele.attr('data-target'), $metricParent);
    ele.tab('show');

    $('.tab-pane', $metricParent).removeClass('active');
    target.addClass('active');
  });
}

function convertData(item){
  var counters = item.counters,
    svg = item.svg,
    data = [];

  for (var p in counters){
    var network = counters[p];

    var per = (network.total*100 / maxValue);

    data[networks.indexOf(p)] = {
      id: p,
      order: 1,
      color: getColor(p),
      weight: 1,
      width: 1,
      score: network.total,
      per: (per > 0 && per < 5 ? 5 : per), // calculated per over a global maxValue
      label: p
    };
  }

  return data;
}

function getColor(network){
  switch (network){
    case 'facebook': return '#3b5998';
    case 'twitter': return '#00aced';
    case 'linkedin': return '#007bb6';
    case 'googleplus': return '#dd4b39';
  }
}

Handlebars.registerHelper('fancyName', function(name) {

  if (name.indexOf('ga:') > -1){
    name = name.replace('ga:', '');
  }

  switch(name){
    case 'googleplus':
      return 'Google Plus';

    case 'googleanalytics':
      return 'Google Analytics';

    case 'sessions':
      return 'Sessions (visits)';
    case 'avgSessionDuration':
      return 'Session Duration (average)';
    case 'sessionDuration':
      return 'Session Duration (total)';
    case 'sessionsPerUser':
      return 'Session Per User';
    case 'bouncerate':
      return 'Bounce Rate';

    case 'socialScore':
      return 'Social Score';
  }

  return name.charAt(0).toUpperCase() + name.slice(1);
});

Handlebars.registerHelper('parseValue', function(key, value) {

  if (value.total >= 0){
    value = value.total;
  }

  switch(key){
    case 'ga:avgSessionDuration':
    case 'ga:sessionDuration':
    case 'ga:sessionsPerUser':
      return parseFloat(value).toFixed(1) + " s";
    case 'ga:bouncerate':
    case 'socialScore':
      return parseFloat(value).toFixed(1) + " %";
  }

  return value;
});<commit_msg>fixed js error<commit_after>
var maxValue = 100;
var all = [];

var template;

$(function(){
  $('#send-url').on('click', fetchAndRefresh);
  $('#get-rss').on('click', fetchRSS);
  template = Handlebars.compile($("#metric-template").html());
});

function blockState(){
  $('#url').attr('disabled', true);
  $('#send-url, #get-rss').button('loading');
}

function resetState(){
  $('#send-url, #get-rss').button('reset');
  $('#url').val("").attr('disabled', false);
}

function fetchAndRefresh(){
  var url = $('#url').val();

  if (url.length === 0){
    window.alert('enter a url!');
  }

  blockState();

  $.ajax({
    url: '/counts?url=' + url,
    dataType: 'json',
  })
  .done(function(counters) {
    createCounter(url, setScore(counters));
    resetState();
  })
  .error(function(err,a,b){
    console.log('ERROR on fetch > ' + err);
    resetState();
  });

}

function fetchRSS(){
  blockState();

  $.ajax({
    url: '/rss_counts',
    dataType: 'json',
  })
  .done(function(res) {

    res.data.forEach(function(data){
      createCounter(data.url, setScore(data.counters));
    });

    resetState();
  })
  .error(function(err,a,b){
    console.log('ERROR on fetch > ' + err);
    resetState();
  });
}

function setScore(counters){
  var sum = 0;
  counters.socialScore = 0;

  for (var p in counters){
    if (p !== "googleanalytics" && counters[p].total > 0){
      sum += counters[p].total;
    }
  }

  var tGA = (counters.googleanalytics && counters.googleanalytics.total) || 0;
  if (tGA > 0){
    counters.socialScore = (sum * 100) / tGA;
  }

  return counters;
}

function createCounter(url, counters){
  var parsed = url.split('/'),
    tail = parsed[parsed.length-1] || parsed[parsed.length-2];

  var template = Handlebars.compile($("#metric-template").html());
  var html = template({
    url: url,
    tail: tail,
    counters: counters
  });

  all.push({
    url: url,
    tail: tail,
    counters: counters,
    svg: null,
    html: html
  });

  if (updateMax(counters)){
    $('.metric').remove();
    all.forEach(draw);
    return;
  }

  draw(all[all.length-1]);
}

function updateMax(counter){
  var updated = false;

  function checkCounter(c){
    for (var p in c){
      if (p !== "googleanalytics" && c[p].total > maxValue){
        maxValue = c[p].total;
        updated = true;
      }
    }
  }

  if (counter)
    checkCounter(counter);
  else
    all.forEach(function(item){
      checkCounter(item.counters);
    });

  return updated;
}

function removeItem(item){
  all.forEach(function(_item, i){
    if (item.url === _item.url){
      all.splice(i, 1);
    }
  });

  maxValue = 100;
  updateMax();

  $('.metric').remove();
  all.forEach(draw);
}

var social_textures_in = {};
var social_textures_out = {};

var networks = [
    'twitter'
  , 'googleplus'
  , 'linkedin'
  , 'facebook'
];

function getTexture(nt, _in){

  function getBy(){
    var args = arguments;

    var txt = textures.lines()
      .orientation(args[0])
      .size(args[1]);

    if (_in){
      return txt
        .strokeWidth(args[2])
        .background(args[3])
        .stroke(args[4]);
    }

    return txt
      .stroke('#F0F3F0')
      .background(args[3]);
  }

  switch (nt){
    case 'facebook': return getBy('diagonal', 10, 5, '#ffffff','#3b5998');
    case 'twitter': return getBy('3/8', 12, 5, '#ffffff','#00aced');
    case 'linkedin': return getBy('vertical', 6, 5, '#ffffff','#007bb6');
    case 'googleplus': return getBy('7/8', 15, 6, '#ffffff','#dd4b39');
  }
}

function createTextures(svg){

  networks.forEach(function(network){
    social_textures_in[network] = getTexture(network, true);
    social_textures_out[network] = getTexture(network);

    svg.call(social_textures_in[network])
    svg.call(social_textures_out[network])
  });

}

function sortMetrics($metricParent){
  var $list = $('.data > .list-group', $metricParent);

  var ntwksSort = [
      'socialScore'
    , 'googleanalytics'
    , 'facebook'
    , 'twitter'
    , 'linkedin'
    , 'googleplus'
  ];

  ntwksSort.forEach(function(name){
    $('>.item-' + name, $list).appendTo($list);
  });
}

function draw(item){
  // taken from http://bl.ocks.org/bbest/2de0e25d4840c68f2db1

  var width = 300,
    height = 300,
    radius = Math.min(width, height) / 2,
    innerRadius = 0.3 * radius;

  var tip = d3.tip()
    .attr('class', 'd3-tip')
    .offset([0, 0])
    .html(function(d) {
      return d.data.label + ": " + d.data.score + "";
    });

  var pie = d3.layout.pie().sort(null).value(function(d) { return d.width; });

  var arc = d3.svg.arc()
    .innerRadius(innerRadius)
    .outerRadius(function (d) {
      return (radius - innerRadius) * (d.data.per / 100.0) + innerRadius;
    });

  var outlineArc = d3.svg.arc()
          .innerRadius(innerRadius)
          .outerRadius(radius);

  var $metricParent = $(item.html).appendTo("#ctn");
  sortMetrics($metricParent);
  var $metric = $('.score', $metricParent);

  var svg = d3.select($metric[0]).append("svg")
      .attr("width", width)
      .attr("height", height)
      .append("g")
      .attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");

  svg.call(tip);
  createTextures(svg);

  item.svg = svg;

  var data = convertData(item);

  var outerPath = svg.selectAll(".outlineArc")
      .data(pie(data))
      .enter().append("path")
      .attr("fill", function(d) { return social_textures_out[d.data.id].url(); })
      .attr("stroke", "#E6EBE6")
      .attr("class", "outlineArc")
      .attr("d", outlineArc);

  var path = svg.selectAll(".solidArc")
      .data(pie(data))
      .enter().append("path")
      .attr("fill", function(d) { return social_textures_in[d.data.id].url(); })
      .attr("class", "solidArc")
      .attr("d", arc)
      .on('mouseover', tip.show)
      .on('mouseout', tip.hide);

  var sc = item.counters.socialScore;

  svg.append("svg:text")
    .attr("class", "aster-score")
    .attr("dy", ".35em")
    .attr("text-anchor", "middle")
    .text(sc ? sc.toFixed(1) + "%" : "-");

  var close = $('x').on('click', function(){
    return (function(_item){
      removeItem(_item);
    })(item);
  });

  $metricParent
    .prepend('' + item.tail + '')
    .children('.nav-tabs').append(close)

  $('.collapser', $metricParent).on('click', function(){
    $('.details', $(this).parents('.list-group-item')).collapse('toggle');
  });

  $('.nav-tabs a', $metricParent).click(function (e) {
    e.preventDefault();
    var ele = $(this);
    var target = $('.' + ele.attr('data-target'), $metricParent);
    ele.tab('show');

    $('.tab-pane', $metricParent).removeClass('active');
    target.addClass('active');
  });
}

function convertData(item){
  var counters = item.counters,
    svg = item.svg,
    data = [];

  for (var p in counters){
    var network = counters[p];

    var per = (network.total*100 / maxValue);

    data[networks.indexOf(p)] = {
      id: p,
      order: 1,
      color: getColor(p),
      weight: 1,
      width: 1,
      score: network.total,
      per: (per > 0 && per < 5 ? 5 : per), // calculated per over a global maxValue
      label: p
    };
  }

  return data;
}

function getColor(network){
  switch (network){
    case 'facebook': return '#3b5998';
    case 'twitter': return '#00aced';
    case 'linkedin': return '#007bb6';
    case 'googleplus': return '#dd4b39';
  }
}

Handlebars.registerHelper('fancyName', function(name) {

  if (name.indexOf('ga:') > -1){
    name = name.replace('ga:', '');
  }

  switch(name){
    case 'googleplus':
      return 'Google Plus';

    case 'googleanalytics':
      return 'Google Analytics';

    case 'sessions':
      return 'Sessions (visits)';
    case 'avgSessionDuration':
      return 'Session Duration (average)';
    case 'sessionDuration':
      return 'Session Duration (total)';
    case 'sessionsPerUser':
      return 'Session Per User';
    case 'bouncerate':
      return 'Bounce Rate';

    case 'socialScore':
      return 'Social Score';
  }

  return name.charAt(0).toUpperCase() + name.slice(1);
});

Handlebars.registerHelper('parseValue', function(key, value) {

  if (value.total >= 0){
    value = value.total;
  }

  switch(key){
    case 'ga:avgSessionDuration':
    case 'ga:sessionDuration':
    case 'ga:sessionsPerUser':
      return parseFloat(value).toFixed(1) + " s";
    case 'ga:bouncerate':
    case 'socialScore':
      return parseFloat(value).toFixed(1) + " %";
  }

  return value;
});<|endoftext|>


<commit_before>
var mySpeed = 8;
var SHEEP_SPEED = 8;
var WOLF_SPEED = 6;
var pixel = 64;
var fps = 30;
var DIRS = ['up', 'right', 'down', 'left'];
var DX   = [0, 1, 0, -1];
var DY   = [-1, 0, 1, 0];
var sheepId;
var isInvincible;

function initPlayScene(userObj, mapObj, normalItemObj, powerItemObj, game) {
    isInvincible = false;
	var scene = new Scene();

	var map = initDynamicMap(game,mapObj);
	scene.addChild(map);

    //init all item before character
    var normalItemList = {};
    for(let i=0;i<normalItemObj.length;i++){
        //init item obj and assign to list for checking when sheep hit
        const item = initNormalItem(game,normalItemObj[i]);
        normalItemList[normalItemObj[i].id] = item;
        scene.addChild(item);
    }

    var powerItemList = {};
    for(let i=0;i<powerItemObj.length;i++){
        //init item obj and assign to list for checking when sheep hit
        const item = initPowerItem(game,powerItemObj[i]);
        powerItemList[powerItemObj[i].id] = item;
        scene.addChild(item);
    }

	//init all character
	var character = {};
	for(let i=0;i<userObj.length;i++){
		var objId = userObj[i].id;
		if(myId == objId){
			//set speed every move
            mySpeed = userObj[i].isEnemy ? WOLF_SPEED : SHEEP_SPEED;
		}
		character[objId] = initPlayer(game,map,socket,userObj[i]);
        if(myId == objId) {
            initPlayerMove(game, map, socket, character, userObj, mapObj, normalItemObj, normalItemList, powerItemObj, powerItemList);
        }
		// Add elements to scene.
    	scene.addChild(character[objId]);

    	//set sheep id for killing
    	if(!userObj[i].isEnemy){
			sheepId = objId;
		}
	}

    socket.on('movePlayer', (req) => {
        const {x, y} = req.player.coordinate;
        var objId = req.player.id;

        //rotate head before change position
        //rotate character
        if(character[objId].x<x){ //right
        	character[objId].scaleX  = -1;
        	character[objId].rotation = 0;
        }else if(character[objId].x>x){ // left
        	character[objId].scaleX  = 1;
        	character[objId].rotation = 0;
        }else if(character[objId].y<y){ // down
        	character[objId].scaleX  = -1;
        	character[objId].rotation = 90;
        }else if(character[objId].y>y){ // up
        	character[objId].scaleX  = -1;
        	character[objId].rotation = 270;
        }

        //change position
        character[objId].x = x;
        character[objId].y = y;
    });

    socket.on('killSheep', (req) => {
        const sheep = userObj.find(x => !x.isEnemy);
        sheep.isAlive = false;
        scene.removeChild(character[sheep.id]);
    });

    socket.on('killWolf', (req) => {
        const wolf = userObj.find(x => x.id === req.player.id);
        wolf.isAlive = false;
        scene.removeChild(character[wolf.id]);
    });

    socket.on('takeNormalItem', (req) => {
        const targetItemObj = normalItemObj.find(x => x.id === req.normalItem.id);
        targetItemObj.enabled = false;

        const item = normalItemList[targetItemObj.id];
        scene.removeChild(item);
    });

    socket.on('takePowerItem', (req) => {
        const targetItemObj = powerItemObj.find(x => x.id === req.powerItem.id);
        targetItemObj.enabled = false;

        const item = powerItemList[targetItemObj.id];
        scene.removeChild(item);
    });

    socket.on('startInvincible', () => {
        console.log('start invincible');
        isInvincible = true;

        // enable super mode
        character[sheepId].frame = [0, 1];
    });

    socket.on('endInvincible', () => {
        console.log('end invincible');
        isInvincible = false;

        // disable super mode
        character[sheepId].frame = [0, 0, 0, 0, 1, 1, 1, 1];
    });

    console.log("push scene");
    game.replaceScene(scene);
}

function initPlayer(game,map,socket,userObj){
	 // Player for now will be a pixel x pixel.
    var player = new Sprite(pixel, pixel);
    player.image = game.assets[charImg];

    //starting point
    player.x = userObj.coordinate.x;
    player.y = userObj.coordinate.y;

    //if enemy = wolf
    if(userObj.isEnemy){
    	player.frame = [3, 3, 3, 3, 4, 4, 4, 4]; // wolf
    }else{
    	player.frame = [0, 0, 0, 0, 1, 1, 1, 1]; // sheep
    }

    return player;
}

function initPlayerMove(game, map, socket, character, userObj, mapObj, normalItemObj, normalItemList, powerItemObj, powerItemList)  {
    const myUserObj = userObj.find(x => x.id === myId);
    const myCharacter = character[myId];

    // Let player move within bounds.
    myCharacter.addEventListener(Event.ENTER_FRAME, function () {
        // First move the player. If the player's new location has resulted
        // in the player being in a "hit" zone, then back the player up to
        // its original location. Tweak "hits" by "offset" pixels.

        let {x, y} = myCharacter;
        DIRS.forEach((dir, i) => {
            if (!game.input[dir]) { return; }
            x += DX[i] * mySpeed;
            y += DY[i] * mySpeed;

            //check the wall
            if (!myHitTest(map, x, y)) {
            	//check for warp portal at the border of map
            	warpPortal(x, y, mapObj);
            }
        });

        // kill by sheepId
        if (myId === sheepId && myUserObj.isAlive) {
            for (let i = 0; i < userObj.length; i++) {
                if (userObj[i] === myUserObj) { continue; }
                if (!userObj[i].isAlive) { continue; }
                if (character[userObj[i].id].intersect(myCharacter)) {
                    console.log(isInvincible);
                    if (isInvincible) {
                        socket.killWolf({wolfId: userObj[i].id});
                        userObj[i].isAlive = false;
                    } else {
                        socket.killSheep();
                        myUserObj.isAlive = false;
                        break;
                    }
                }
            }

            //check sheep intersect with item
            for(let i=0;i<normalItemObj.length;i++){
                if (!normalItemObj[i].enabled) { continue; }

                const item = normalItemList[normalItemObj[i].id];
                if(myCharacter.intersect(item)){
                    normalItemObj[i].enabled = false;
                    socket.takeNormalItem({itemId: normalItemObj[i].id});
                }
            }

            for(let i=0;i<powerItemObj.length;i++){
                if (!powerItemObj[i].enabled) { continue; }

                const item = powerItemList[powerItemObj[i].id];
                if(myCharacter.intersect(item)){
                    powerItemObj[i].enabled = false;
                    console.log(`take ${powerItemObj[i].id}`);
                    socket.takePowerItem({itemId: powerItemObj[i].id});
                }
            }
        }
    });
}

function myHitTest (map, x, y) {
    return map.hitTest(x, y) ||
        map.hitTest(x + pixel - 1, y) ||
        map.hitTest(x, y + pixel - 1) ||
        map.hitTest(x + pixel - 1, y + pixel - 1);
}

function warpPortal(x, y, mapObj){
	var mapXLimit = (mapObj.width * pixel)-(pixel/2);
	var mapYLimit = (mapObj.height * pixel)-(pixel/2);
	if(x>mapXLimit){
		x = pixel/2;
	}else if(x<pixel/2){
		x = mapXLimit;
	}else if(y>mapYLimit){
		y = pixel/2;
	}else if(y<pixel/2){
		y = mapYLimit;
	}
	socket.movePlayer({x, y});
}
<commit_msg>remove console log<commit_after>
var mySpeed = 8;
var SHEEP_SPEED = 8;
var WOLF_SPEED = 6;
var pixel = 64;
var fps = 30;
var DIRS = ['up', 'right', 'down', 'left'];
var DX   = [0, 1, 0, -1];
var DY   = [-1, 0, 1, 0];
var sheepId;
var isInvincible;

function initPlayScene(userObj, mapObj, normalItemObj, powerItemObj, game) {
    isInvincible = false;
	var scene = new Scene();

	var map = initDynamicMap(game,mapObj);
	scene.addChild(map);

    //init all item before character
    var normalItemList = {};
    for(let i=0;i<normalItemObj.length;i++){
        //init item obj and assign to list for checking when sheep hit
        const item = initNormalItem(game,normalItemObj[i]);
        normalItemList[normalItemObj[i].id] = item;
        scene.addChild(item);
    }

    var powerItemList = {};
    for(let i=0;i<powerItemObj.length;i++){
        //init item obj and assign to list for checking when sheep hit
        const item = initPowerItem(game,powerItemObj[i]);
        powerItemList[powerItemObj[i].id] = item;
        scene.addChild(item);
    }

	//init all character
	var character = {};
	for(let i=0;i<userObj.length;i++){
		var objId = userObj[i].id;
		if(myId == objId){
			//set speed every move
            mySpeed = userObj[i].isEnemy ? WOLF_SPEED : SHEEP_SPEED;
		}
		character[objId] = initPlayer(game,map,socket,userObj[i]);
        if(myId == objId) {
            initPlayerMove(game, map, socket, character, userObj, mapObj, normalItemObj, normalItemList, powerItemObj, powerItemList);
        }
		// Add elements to scene.
    	scene.addChild(character[objId]);

    	//set sheep id for killing
    	if(!userObj[i].isEnemy){
			sheepId = objId;
		}
	}

    socket.on('movePlayer', (req) => {
        const {x, y} = req.player.coordinate;
        var objId = req.player.id;

        //rotate head before change position
        //rotate character
        if(character[objId].x<x){ //right
        	character[objId].scaleX  = -1;
        	character[objId].rotation = 0;
        }else if(character[objId].x>x){ // left
        	character[objId].scaleX  = 1;
        	character[objId].rotation = 0;
        }else if(character[objId].y<y){ // down
        	character[objId].scaleX  = -1;
        	character[objId].rotation = 90;
        }else if(character[objId].y>y){ // up
        	character[objId].scaleX  = -1;
        	character[objId].rotation = 270;
        }

        //change position
        character[objId].x = x;
        character[objId].y = y;
    });

    socket.on('killSheep', (req) => {
        const sheep = userObj.find(x => !x.isEnemy);
        sheep.isAlive = false;
        scene.removeChild(character[sheep.id]);
    });

    socket.on('killWolf', (req) => {
        const wolf = userObj.find(x => x.id === req.player.id);
        wolf.isAlive = false;
        scene.removeChild(character[wolf.id]);
    });

    socket.on('takeNormalItem', (req) => {
        const targetItemObj = normalItemObj.find(x => x.id === req.normalItem.id);
        targetItemObj.enabled = false;

        const item = normalItemList[targetItemObj.id];
        scene.removeChild(item);
    });

    socket.on('takePowerItem', (req) => {
        const targetItemObj = powerItemObj.find(x => x.id === req.powerItem.id);
        targetItemObj.enabled = false;

        const item = powerItemList[targetItemObj.id];
        scene.removeChild(item);
    });

    socket.on('startInvincible', () => {
        isInvincible = true;

        // enable super mode
        character[sheepId].frame = [0, 1];
    });

    socket.on('endInvincible', () => {
        isInvincible = false;

        // disable super mode
        character[sheepId].frame = [0, 0, 0, 0, 1, 1, 1, 1];
    });

    game.replaceScene(scene);
}

function initPlayer(game,map,socket,userObj){
	 // Player for now will be a pixel x pixel.
    var player = new Sprite(pixel, pixel);
    player.image = game.assets[charImg];

    //starting point
    player.x = userObj.coordinate.x;
    player.y = userObj.coordinate.y;

    //if enemy = wolf
    if(userObj.isEnemy){
    	player.frame = [3, 3, 3, 3, 4, 4, 4, 4]; // wolf
    }else{
    	player.frame = [0, 0, 0, 0, 1, 1, 1, 1]; // sheep
    }

    return player;
}

function initPlayerMove(game, map, socket, character, userObj, mapObj, normalItemObj, normalItemList, powerItemObj, powerItemList)  {
    const myUserObj = userObj.find(x => x.id === myId);
    const myCharacter = character[myId];

    // Let player move within bounds.
    myCharacter.addEventListener(Event.ENTER_FRAME, function () {
        // First move the player. If the player's new location has resulted
        // in the player being in a "hit" zone, then back the player up to
        // its original location. Tweak "hits" by "offset" pixels.

        let {x, y} = myCharacter;
        DIRS.forEach((dir, i) => {
            if (!game.input[dir]) { return; }
            x += DX[i] * mySpeed;
            y += DY[i] * mySpeed;

            //check the wall
            if (!myHitTest(map, x, y)) {
            	//check for warp portal at the border of map
            	warpPortal(x, y, mapObj);
            }
        });

        // kill by sheepId
        if (myId === sheepId && myUserObj.isAlive) {
            for (let i = 0; i < userObj.length; i++) {
                if (userObj[i] === myUserObj) { continue; }
                if (!userObj[i].isAlive) { continue; }
                if (character[userObj[i].id].intersect(myCharacter)) {
                    if (isInvincible) {
                        socket.killWolf({wolfId: userObj[i].id});
                        userObj[i].isAlive = false;
                    } else {
                        socket.killSheep();
                        myUserObj.isAlive = false;
                        break;
                    }
                }
            }

            //check sheep intersect with item
            for(let i=0;i<normalItemObj.length;i++){
                if (!normalItemObj[i].enabled) { continue; }

                const item = normalItemList[normalItemObj[i].id];
                if(myCharacter.intersect(item)){
                    normalItemObj[i].enabled = false;
                    socket.takeNormalItem({itemId: normalItemObj[i].id});
                }
            }

            for(let i=0;i<powerItemObj.length;i++){
                if (!powerItemObj[i].enabled) { continue; }

                const item = powerItemList[powerItemObj[i].id];
                if(myCharacter.intersect(item)){
                    powerItemObj[i].enabled = false;
                    socket.takePowerItem({itemId: powerItemObj[i].id});
                }
            }
        }
    });
}

function myHitTest (map, x, y) {
    return map.hitTest(x, y) ||
        map.hitTest(x + pixel - 1, y) ||
        map.hitTest(x, y + pixel - 1) ||
        map.hitTest(x + pixel - 1, y + pixel - 1);
}

function warpPortal(x, y, mapObj){
	var mapXLimit = (mapObj.width * pixel)-(pixel/2);
	var mapYLimit = (mapObj.height * pixel)-(pixel/2);
	if(x>mapXLimit){
		x = pixel/2;
	}else if(x<pixel/2){
		x = mapXLimit;
	}else if(y>mapYLimit){
		y = pixel/2;
	}else if(y<pixel/2){
		y = mapYLimit;
	}
	socket.movePlayer({x, y});
}
<|endoftext|>


<commit_before>/**
 *
 * i18nSource: a form field to contain values for referenced language fields;
 * it acts as a conduit for other fields in the form (they are grouped in i18nFieldsets).
 * e.g. on 'setValue' all referenced fields are 'setValue'd with their respective language value:
 *
 *  field.name.setValue(obj) -->> field._name_nl.setValue(str);
 *                                field._name_en.setValue(str);
 *
 */
Garp.i18nSource = Ext.extend(Ext.form.Field, {

	ref: '../',
	style: 'display:none; height: 0; margin: 0; padding: 0;',
	hideLabel: true,

	originalValue: null,

	initComponent: function(ct){
		Garp.i18nSource.superclass.initComponent.call(this, ct);
		Garp.i18nSource.superclass.hide.call(this);
	},

	/**
	 * Get referenced field
	 * @param {String} lang
	 */
	getRefField: function(lang){
		return this.refOwner.find('name', ('_' + this.name + '_' + lang))[0];
	},

	setValue: function(v){
		Ext.each(LANGUAGES, function(lang){
			if (this.getRefField(lang)) {
				this.getRefField(lang).setValue(v[lang]);
			}
		}, this);
		Garp.i18nSource.superclass.setValue.call(this, v);
	},

	setRawValue: function(v){
		return this.setValue(v);
	},

	getValue: function(v){
		var out = {};
		Ext.each(LANGUAGES, function(lang){
			field = this.getRefField(lang);
			if (field && field.isDirty()) {
				out[lang] = field.getValue() === '' ? null : field.getValue();
			}
		}, this);
		return out;
	},

	getRawValue: function(v){
		return this.getValue(v);
	},

	isDirty: function(v){
		var out = false;
		Ext.each(LANGUAGES, function(lang){
			if (this.getRefField(lang) && this.getRefField(lang).isDirty()) {
				out = true;
				return false;
			}
		}, this);
		return out;
	},

	/**
	 * Perform function on all referenced fields
	 * @param {String} function to perform
	 * @param {Object} [optional] param
	 * @param {Bool} skipSelf, do not perform the function on 'this'
	 */
	_setAll: function(func, param, skipSelf){
		Ext.each(LANGUAGES, function(lang){
			var f = this.getRefField(lang);
			if (f && f[func]) {
				f[func](param);
			}
		}, this);
		if (!skipSelf === true) {
			return Garp.i18nSource.superclass[func].call(this, param);
		}
	},

	setVisible: function(state){
		return this._setAll('setVisible', state, true);
	},
	setDisabled: function(state){
		return this._setAll('setDisabled', state);
	},
	show: function(state){
		return this._setAll('show', state, true);
	},
	hide: function(state){
		return this._setAll('hide', state, true);
	},
	enable: function(state){
		return this._setAll('enable', state);
	},
	disable: function(state){
		return this._setAll('disable', state);
	}

});
Ext.reg('i18nsource', Garp.i18nSource);

/**
 * Simple Fieldset to hold i18n fields:
 */
Garp.i18nFieldSet = Ext.extend(Ext.form.FieldSet, {
	cls: 'i18n-fieldset',
	collapsed: true,
	initComponent: function(ct){
		Garp.i18nFieldSet.superclass.initComponent.call(this, ct);
	}
});
Ext.reg('i18nfieldset', Garp.i18nFieldSet);<commit_msg>null issue: WWC Ticket #930<commit_after>/**
 *
 * i18nSource: a form field to contain values for referenced language fields;
 * it acts as a conduit for other fields in the form (they are grouped in i18nFieldsets).
 * e.g. on 'setValue' all referenced fields are 'setValue'd with their respective language value:
 *
 *  field.name.setValue(obj) -->> field._name_nl.setValue(str);
 *                                field._name_en.setValue(str);
 *
 */
Garp.i18nSource = Ext.extend(Ext.form.Field, {

	ref: '../',
	style: 'display:none; height: 0; margin: 0; padding: 0;',
	hideLabel: true,

	originalValue: null,

	initComponent: function(ct){
		Garp.i18nSource.superclass.initComponent.call(this, ct);
		Garp.i18nSource.superclass.hide.call(this);
	},

	/**
	 * Get referenced field
	 * @param {String} lang
	 */
	getRefField: function(lang){
		return this.refOwner.find('name', ('_' + this.name + '_' + lang))[0];
	},

	setValue: function(v){
		Ext.each(LANGUAGES, function(lang){
			if (this.getRefField(lang)) {
				if (v !== null) {
					this.getRefField(lang).setValue(v[lang]);
				}
			}
		}, this);
		Garp.i18nSource.superclass.setValue.call(this, v);
	},

	setRawValue: function(v){
		return this.setValue(v);
	},

	getValue: function(v){
		var out = {};
		Ext.each(LANGUAGES, function(lang){
			field = this.getRefField(lang);
			if (field && field.isDirty()) {
				out[lang] = field.getValue() === '' ? null : field.getValue();
			}
		}, this);
		return out;
	},

	getRawValue: function(v){
		return this.getValue(v);
	},

	isDirty: function(v){
		var out = false;
		Ext.each(LANGUAGES, function(lang){
			if (this.getRefField(lang) && this.getRefField(lang).isDirty()) {
				out = true;
				return false;
			}
		}, this);
		return out;
	},

	/**
	 * Perform function on all referenced fields
	 * @param {String} function to perform
	 * @param {Object} [optional] param
	 * @param {Bool} skipSelf, do not perform the function on 'this'
	 */
	_setAll: function(func, param, skipSelf){
		Ext.each(LANGUAGES, function(lang){
			var f = this.getRefField(lang);
			if (f && f[func]) {
				f[func](param);
			}
		}, this);
		if (!skipSelf === true) {
			return Garp.i18nSource.superclass[func].call(this, param);
		}
	},

	setVisible: function(state){
		return this._setAll('setVisible', state, true);
	},
	setDisabled: function(state){
		return this._setAll('setDisabled', state);
	},
	show: function(state){
		return this._setAll('show', state, true);
	},
	hide: function(state){
		return this._setAll('hide', state, true);
	},
	enable: function(state){
		return this._setAll('enable', state);
	},
	disable: function(state){
		return this._setAll('disable', state);
	}

});
Ext.reg('i18nsource', Garp.i18nSource);

/**
 * Simple Fieldset to hold i18n fields:
 */
Garp.i18nFieldSet = Ext.extend(Ext.form.FieldSet, {
	cls: 'i18n-fieldset',
	collapsed: true,
	initComponent: function(ct){
		Garp.i18nFieldSet.superclass.initComponent.call(this, ct);
	}
});
Ext.reg('i18nfieldset', Garp.i18nFieldSet);<|endoftext|>