<commit_before>var math = require('../math'),
    utils = require('../utils'),
    RenderTexture = require('../textures/RenderTexture'),
    _tempMatrix = new math.Matrix();

/**
 * The base class for all objects that are rendered on the screen.
 * This is an abstract class and should not be used on its own rather it should be extended.
 *
 * @class
 * @namespace PIXI
 */
function DisplayObject()
{
    /**
     * The coordinate of the object relative to the local coordinates of the parent.
     *
     * @member {Point}
     */
    this.position = new math.Point();

    /**
     * The scale factor of the object.
     *
     * @member {Point}
     */
    this.scale = new math.Point(1, 1);

    /**
     * The pivot point of the displayObject that it rotates around
     *
     * @member {Point}
     */
    this.pivot = new math.Point(0, 0);

    /**
     * The rotation of the object in radians.
     *
     * @member {number}
     */
    this.rotation = 0;

    /**
     * The opacity of the object.
     *
     * @member {number}
     */
    this.alpha = 1;

    /**
     * The visibility of the object. If false the object will not be drawn, and
     * the updateTransform function will not be called.
     *
     * @member {boolean}
     */
    this.visible = true;

    /**
     * Can this object be rendered, if false the object will not be drawn but the updateTransform
     * methods will still be called.
     *
     * @member {boolean}
     */
    this.renderable = true;

    /**
     * The display object container that contains this display object.
     *
     * @member {Container}
     * @readOnly
     */
    this.parent = null;

    /**
     * The multiplied alpha of the displayObject
     *
     * @member {number}
     * @readOnly
     */
    this.worldAlpha = 1;

    /**
     * Current transform of the object based on world (parent) factors
     *
     * @member {Matrix}
     * @readOnly
     */
    this.worldTransform = new math.Matrix();

    /**
     * The area the filter is applied to. This is used as more of an optimisation
     * rather than figuring out the dimensions of the displayObject each frame you can set this rectangle
     *
     * @member {Rectangle}
     */
    this.filterArea = null;

    /**
     * cached sin rotation
     *
     * @member {number}
     * @private
     */
    this._sr = 0;

    /**
     * cached cos rotation
     *
     * @member {number}
     * @private
     */
    this._cr = 1;

    /**
     * The original, cached bounds of the object
     *
     * @member {Rectangle}
     * @private
     */
    this._bounds = new math.Rectangle(0, 0, 1, 1);

    /**
     * The most up-to-date bounds of the object
     *
     * @member {Rectangle}
     * @private
     */
    this._currentBounds = null;

    /**
     * The original, cached mask of the object
     *
     * @member {Rectangle}
     * @private
     */
    this._mask = null;

    //TODO rename to _isMask
    this.isMask = false;

    /**
     * Cached internal flag.
     *
     * @member {boolean}
     * @private
     */
    this._cacheAsBitmap = false;
    this._cachedObject = null;
}

// constructor
DisplayObject.prototype.constructor = DisplayObject;
utils.eventTarget.mixin(DisplayObject.prototype);
module.exports = DisplayObject;

Object.defineProperties(DisplayObject.prototype, {
    /**
     * The position of the displayObject on the x axis relative to the local coordinates of the parent.
     *
     * @member {number}
     * @memberof DisplayObject#
     */
    x: {
        get: function ()
        {
            return this.position.x;
        },
        set: function (value)
        {
            this.position.x = value;
        }
    },

    /**
     * The position of the displayObject on the y axis relative to the local coordinates of the parent.
     *
     * @member {number}
     * @memberof DisplayObject#
     */
    y: {
        get: function ()
        {
            return this.position.y;
        },
        set: function (value)
        {
            this.position.y = value;
        }
    },

    /**
     * Indicates if the sprite is globally visible.
     *
     * @member {boolean}
     * @memberof DisplayObject#
     * @readonly
     */
    worldVisible: {
        get: function ()
        {
            var item = this;

            do {
                if (!item.visible)
                {
                    return false;
                }

                item = item.parent;
            } while (item);

            return true;
        }
    },

    /**
     * Sets a mask for the displayObject. A mask is an object that limits the visibility of an object to the shape of the mask applied to it.
     * In PIXI a regular mask must be a PIXI.Graphics object. This allows for much faster masking in canvas as it utilises shape clipping.
     * To remove a mask, set this property to null.
     *
     * @member {Graphics}
     * @memberof DisplayObject#
     */
    mask: {
        get: function ()
        {
            return this._mask;
        },
        set: function (value)
        {
            if (this._mask)
            {
                this._mask.isMask = false;
            }

            this._mask = value;

            if (this._mask)
            {
                this._mask.isMask = true;
            }
        }
    },

    /**
     * Sets the filters for the displayObject.
     * * IMPORTANT: This is a webGL only feature and will be ignored by the canvas renderer.
     * To remove filters simply set this property to 'null'
     *
     * @member {Filter[]}
     * @memberof DisplayObject#
     */
    filters: {
        get: function ()
        {
            return this._filters && this._filters.slice();
        },
        set: function (value)
        {
            this._filters = value && value.slice();
        }
    },

    cacheAsBitmap: {
        get: function ()
        {
            return this._cacheAsBitmap;
        },
        set: function (value)
        {
            if(this._cacheAsBitmap === value)
            {
                return;
            }

            this._cacheAsBitmap = value;

            if(!value)
            {
                if(this._cachedObject)
                {
                    this._destroyCachedDisplayObject();
                }
            }
        }
    }
});

/*
 * Updates the object transform for rendering
 *
 * TODO - Optimization pass!
 *
 * @private
 */
DisplayObject.prototype.updateTransform = function ()
{

    // create some matrix refs for easy access
    var pt = this.parent.worldTransform;
    var wt = this.worldTransform;

    // temporary matrix variables
    var a, b, c, d, tx, ty;

    // so if rotation is between 0 then we can simplify the multiplication process..
    if (this.rotation % math.PI_2)
    {
        // check to see if the rotation is the same as the previous render. This means we only need to use sin and cos when rotation actually changes
        if (this.rotation !== this.rotationCache)
        {
            this.rotationCache = this.rotation;
            this._sr = Math.sin(this.rotation);
            this._cr = Math.cos(this.rotation);
        }

        // get the matrix values of the displayobject based on its transform properties..
        a  =  this._cr * this.scale.x;
        b  =  this._sr * this.scale.x;
        c  = -this._sr * this.scale.y;
        d  =  this._cr * this.scale.y;
        tx =  this.position.x;
        ty =  this.position.y;

        // check for pivot.. not often used so geared towards that fact!
        if (this.pivot.x || this.pivot.y)
        {
            tx -= this.pivot.x * a + this.pivot.y * c;
            ty -= this.pivot.x * b + this.pivot.y * d;
        }

        // concat the parent matrix with the objects transform.
        wt.a  = a  * pt.a + b  * pt.c;
        wt.b  = a  * pt.b + b  * pt.d;
        wt.c  = c  * pt.a + d  * pt.c;
        wt.d  = c  * pt.b + d  * pt.d;
        wt.tx = tx * pt.a + ty * pt.c + pt.tx;
        wt.ty = tx * pt.b + ty * pt.d + pt.ty;
    }
    else
    {
        // lets do the fast version as we know there is no rotation..
        a  = this.scale.x;
        d  = this.scale.y;

        tx = this.position.x - this.pivot.x * a;
        ty = this.position.y - this.pivot.y * d;

        wt.a  = a  * pt.a;
        wt.b  = a  * pt.b;
        wt.c  = d  * pt.c;
        wt.d  = d  * pt.d;
        wt.tx = tx * pt.a + ty * pt.c + pt.tx;
        wt.ty = tx * pt.b + ty * pt.d + pt.ty;
    }

    // multiply the alphas..
    this.worldAlpha = this.alpha * this.parent.worldAlpha;
};

// performance increase to avoid using call.. (10x faster)
DisplayObject.prototype.displayObjectUpdateTransform = DisplayObject.prototype.updateTransform;

/**
 *
 *
 *
 * Retrieves the bounds of the displayObject as a rectangle object
 *
 * @param matrix {Matrix}
 * @return {Rectangle} the rectangular bounding area
 */
DisplayObject.prototype.getBounds = function (/* matrix */)
{
    return math.Rectangle.EMPTY;
};

/**
 * Retrieves the local bounds of the displayObject as a rectangle object
 *
 * @return {Rectangle} the rectangular bounding area
 */
DisplayObject.prototype.getLocalBounds = function ()
{
    return this.getBounds(math.Matrix.IDENTITY);
};

/**
 * Calculates the global position of the display object
 *
 * @param position {Point} The world origin to calculate from
 * @return {Point} A point object representing the position of this object
 */
DisplayObject.prototype.toGlobal = function (position)
{
    // don't need to u[date the lot
    this.displayObjectUpdateTransform();
    return this.worldTransform.apply(position);
};

/**
 * Calculates the local position of the display object relative to another point
 *
 * @param position {Point} The world origin to calculate from
 * @param [from] {DisplayObject} The DisplayObject to calculate the global position from
 * @return {Point} A point object representing the position of this object
 */
DisplayObject.prototype.toLocal = function (position, from)
{
    if (from)
    {
        position = from.toGlobal(position);
    }

    // don't need to update the lot
    this.displayObjectUpdateTransform();
    return this.worldTransform.applyInverse(position);
};

/**
 * Renders the object using the WebGL renderer
 *
 * @param renderer {WebGLRenderer} The renderer
 * @private
 */
DisplayObject.prototype.renderWebGL = function (/* renderer */)
{
    // OVERWRITE;
};

/**
 * Renders the object using the Canvas renderer
 *
 * @param renderer {CanvasRenderer} The renderer
 * @private
 */
DisplayObject.prototype.renderCanvas = function (/* renderer */)
{
    // OVERWRITE;
};

DisplayObject.prototype.generateTexture = function (renderer, resolution, scaleMode)
{
    var bounds = this.getLocalBounds();

    var renderTexture = new RenderTexture(renderer, bounds.width | 0, bounds.height | 0, renderer, scaleMode, resolution);

    _tempMatrix.tx = -bounds.x;
    _tempMatrix.ty = -bounds.y;

    renderTexture.render(this, _tempMatrix);

    return renderTexture;
};

DisplayObject.prototype._generateCachedDisplayObject = function( renderer )
{
    if(this._cachedObject)
    {
        return;
    }

    var bounds = this.getLocalBounds();

    var cachedRenderTarget = renderer.currentRenderTarget;

    var renderTexture = new RenderTexture(renderer, bounds.width | 0, bounds.height | 0);//, renderSession.renderer);

    // need to set //
    var m = new math.Matrix();

    m.tx = -bounds.x;
    m.ty = -bounds.y;

    this.cacheAsBitmap = false;

    renderTexture.render(this, m, true);

    renderer.setRenderTarget(cachedRenderTarget);

    this.cacheAsBitmap = true;

    this._cachedObject = {
                            worldTransform:this.worldTransform,
                            anchor:new math.Point(-( bounds.x / bounds.width ), -( bounds.y / bounds.height )),
                            _texture:renderTexture,
                            blendMode:0,
                            worldAlpha:1,
                            tint:0xFFFFFF
                        };
};

DisplayObject.prototype._destroyCachedDisplayObject = function( renderer )
{
    this._cachedObject._texture.destroy();
    this._cachedObject = null;
};

DisplayObject.prototype._renderCached = function( renderer )
{
    this._generateCachedDisplayObject( renderer );

    this._cachedObject.worldAlpha = this.worldAlpha;
   // this._cachedObject.tint = this.tint;

    renderer.plugins.sprite.render( this._cachedObject );
};

<commit_msg>cacheAsBitmap tweaks<commit_after>var math = require('../math'),
    utils = require('../utils'),
    RenderTexture = require('../textures/RenderTexture'),
    _tempMatrix = new math.Matrix();

/**
 * The base class for all objects that are rendered on the screen.
 * This is an abstract class and should not be used on its own rather it should be extended.
 *
 * @class
 * @namespace PIXI
 */
function DisplayObject()
{
    /**
     * The coordinate of the object relative to the local coordinates of the parent.
     *
     * @member {Point}
     */
    this.position = new math.Point();

    /**
     * The scale factor of the object.
     *
     * @member {Point}
     */
    this.scale = new math.Point(1, 1);

    /**
     * The pivot point of the displayObject that it rotates around
     *
     * @member {Point}
     */
    this.pivot = new math.Point(0, 0);

    /**
     * The rotation of the object in radians.
     *
     * @member {number}
     */
    this.rotation = 0;

    /**
     * The opacity of the object.
     *
     * @member {number}
     */
    this.alpha = 1;

    /**
     * The visibility of the object. If false the object will not be drawn, and
     * the updateTransform function will not be called.
     *
     * @member {boolean}
     */
    this.visible = true;

    /**
     * Can this object be rendered, if false the object will not be drawn but the updateTransform
     * methods will still be called.
     *
     * @member {boolean}
     */
    this.renderable = true;

    /**
     * The display object container that contains this display object.
     *
     * @member {Container}
     * @readOnly
     */
    this.parent = null;

    /**
     * The multiplied alpha of the displayObject
     *
     * @member {number}
     * @readOnly
     */
    this.worldAlpha = 1;

    /**
     * Current transform of the object based on world (parent) factors
     *
     * @member {Matrix}
     * @readOnly
     */
    this.worldTransform = new math.Matrix();

    /**
     * The area the filter is applied to. This is used as more of an optimisation
     * rather than figuring out the dimensions of the displayObject each frame you can set this rectangle
     *
     * @member {Rectangle}
     */
    this.filterArea = null;

    /**
     * cached sin rotation
     *
     * @member {number}
     * @private
     */
    this._sr = 0;

    /**
     * cached cos rotation
     *
     * @member {number}
     * @private
     */
    this._cr = 1;

    /**
     * The original, cached bounds of the object
     *
     * @member {Rectangle}
     * @private
     */
    this._bounds = new math.Rectangle(0, 0, 1, 1);

    /**
     * The most up-to-date bounds of the object
     *
     * @member {Rectangle}
     * @private
     */
    this._currentBounds = null;

    /**
     * The original, cached mask of the object
     *
     * @member {Rectangle}
     * @private
     */
    this._mask = null;

    //TODO rename to _isMask
    this.isMask = false;

    /**
     * Cached internal flag.
     *
     * @member {boolean}
     * @private
     */
    this._cacheAsBitmap = false;
    this._cachedObject = null;
}

// constructor
DisplayObject.prototype.constructor = DisplayObject;
utils.eventTarget.mixin(DisplayObject.prototype);
module.exports = DisplayObject;

Object.defineProperties(DisplayObject.prototype, {
    /**
     * The position of the displayObject on the x axis relative to the local coordinates of the parent.
     *
     * @member {number}
     * @memberof DisplayObject#
     */
    x: {
        get: function ()
        {
            return this.position.x;
        },
        set: function (value)
        {
            this.position.x = value;
        }
    },

    /**
     * The position of the displayObject on the y axis relative to the local coordinates of the parent.
     *
     * @member {number}
     * @memberof DisplayObject#
     */
    y: {
        get: function ()
        {
            return this.position.y;
        },
        set: function (value)
        {
            this.position.y = value;
        }
    },

    /**
     * Indicates if the sprite is globally visible.
     *
     * @member {boolean}
     * @memberof DisplayObject#
     * @readonly
     */
    worldVisible: {
        get: function ()
        {
            var item = this;

            do {
                if (!item.visible)
                {
                    return false;
                }

                item = item.parent;
            } while (item);

            return true;
        }
    },

    /**
     * Sets a mask for the displayObject. A mask is an object that limits the visibility of an object to the shape of the mask applied to it.
     * In PIXI a regular mask must be a PIXI.Graphics object. This allows for much faster masking in canvas as it utilises shape clipping.
     * To remove a mask, set this property to null.
     *
     * @member {Graphics}
     * @memberof DisplayObject#
     */
    mask: {
        get: function ()
        {
            return this._mask;
        },
        set: function (value)
        {
            if (this._mask)
            {
                this._mask.isMask = false;
            }

            this._mask = value;

            if (this._mask)
            {
                this._mask.isMask = true;
            }
        }
    },

    /**
     * Sets the filters for the displayObject.
     * * IMPORTANT: This is a webGL only feature and will be ignored by the canvas renderer.
     * To remove filters simply set this property to 'null'
     *
     * @member {Filter[]}
     * @memberof DisplayObject#
     */
    filters: {
        get: function ()
        {
            return this._filters && this._filters.slice();
        },
        set: function (value)
        {
            this._filters = value && value.slice();
        }
    },

    cacheAsBitmap: {
        get: function ()
        {
            return this._cacheAsBitmap;
        },
        set: function (value)
        {
            if(this._cacheAsBitmap === value)
            {
                return;
            }

            this._cacheAsBitmap = value;

            if(!value)
            {
                if(this._cachedObject)
                {
                    this._destroyCachedDisplayObject();
                }
            }
        }
    }
});

/*
 * Updates the object transform for rendering
 *
 * TODO - Optimization pass!
 *
 * @private
 */
DisplayObject.prototype.updateTransform = function ()
{

    // create some matrix refs for easy access
    var pt = this.parent.worldTransform;
    var wt = this.worldTransform;

    // temporary matrix variables
    var a, b, c, d, tx, ty;

    // so if rotation is between 0 then we can simplify the multiplication process..
    if (this.rotation % math.PI_2)
    {
        // check to see if the rotation is the same as the previous render. This means we only need to use sin and cos when rotation actually changes
        if (this.rotation !== this.rotationCache)
        {
            this.rotationCache = this.rotation;
            this._sr = Math.sin(this.rotation);
            this._cr = Math.cos(this.rotation);
        }

        // get the matrix values of the displayobject based on its transform properties..
        a  =  this._cr * this.scale.x;
        b  =  this._sr * this.scale.x;
        c  = -this._sr * this.scale.y;
        d  =  this._cr * this.scale.y;
        tx =  this.position.x;
        ty =  this.position.y;

        // check for pivot.. not often used so geared towards that fact!
        if (this.pivot.x || this.pivot.y)
        {
            tx -= this.pivot.x * a + this.pivot.y * c;
            ty -= this.pivot.x * b + this.pivot.y * d;
        }

        // concat the parent matrix with the objects transform.
        wt.a  = a  * pt.a + b  * pt.c;
        wt.b  = a  * pt.b + b  * pt.d;
        wt.c  = c  * pt.a + d  * pt.c;
        wt.d  = c  * pt.b + d  * pt.d;
        wt.tx = tx * pt.a + ty * pt.c + pt.tx;
        wt.ty = tx * pt.b + ty * pt.d + pt.ty;
    }
    else
    {
        // lets do the fast version as we know there is no rotation..
        a  = this.scale.x;
        d  = this.scale.y;

        tx = this.position.x - this.pivot.x * a;
        ty = this.position.y - this.pivot.y * d;

        wt.a  = a  * pt.a;
        wt.b  = a  * pt.b;
        wt.c  = d  * pt.c;
        wt.d  = d  * pt.d;
        wt.tx = tx * pt.a + ty * pt.c + pt.tx;
        wt.ty = tx * pt.b + ty * pt.d + pt.ty;
    }

    // multiply the alphas..
    this.worldAlpha = this.alpha * this.parent.worldAlpha;
};

// performance increase to avoid using call.. (10x faster)
DisplayObject.prototype.displayObjectUpdateTransform = DisplayObject.prototype.updateTransform;

/**
 *
 *
 *
 * Retrieves the bounds of the displayObject as a rectangle object
 *
 * @param matrix {Matrix}
 * @return {Rectangle} the rectangular bounding area
 */
DisplayObject.prototype.getBounds = function (/* matrix */)
{
    return math.Rectangle.EMPTY;
};

/**
 * Retrieves the local bounds of the displayObject as a rectangle object
 *
 * @return {Rectangle} the rectangular bounding area
 */
DisplayObject.prototype.getLocalBounds = function ()
{
    return this.getBounds(math.Matrix.IDENTITY);
};

/**
 * Calculates the global position of the display object
 *
 * @param position {Point} The world origin to calculate from
 * @return {Point} A point object representing the position of this object
 */
DisplayObject.prototype.toGlobal = function (position)
{
    // don't need to u[date the lot
    this.displayObjectUpdateTransform();
    return this.worldTransform.apply(position);
};

/**
 * Calculates the local position of the display object relative to another point
 *
 * @param position {Point} The world origin to calculate from
 * @param [from] {DisplayObject} The DisplayObject to calculate the global position from
 * @return {Point} A point object representing the position of this object
 */
DisplayObject.prototype.toLocal = function (position, from)
{
    if (from)
    {
        position = from.toGlobal(position);
    }

    // don't need to update the lot
    this.displayObjectUpdateTransform();
    return this.worldTransform.applyInverse(position);
};

/**
 * Renders the object using the WebGL renderer
 *
 * @param renderer {WebGLRenderer} The renderer
 * @private
 */
DisplayObject.prototype.renderWebGL = function (/* renderer */)
{
    // OVERWRITE;
};

/**
 * Renders the object using the Canvas renderer
 *
 * @param renderer {CanvasRenderer} The renderer
 * @private
 */
DisplayObject.prototype.renderCanvas = function (/* renderer */)
{
    // OVERWRITE;
};

DisplayObject.prototype.generateTexture = function (renderer, resolution, scaleMode)
{
    var bounds = this.getLocalBounds();

    var renderTexture = new RenderTexture(renderer, bounds.width | 0, bounds.height | 0, renderer, scaleMode, resolution);

    _tempMatrix.tx = -bounds.x;
    _tempMatrix.ty = -bounds.y;

    renderTexture.render(this, _tempMatrix);

    return renderTexture;
};

DisplayObject.prototype._generateCachedDisplayObject = function( renderer )
{
    if(this._cachedObject)
    {
        return;
    }

    var bounds = this.getLocalBounds();

    var cachedRenderTarget = renderer.currentRenderTarget;

    var renderTexture = new RenderTexture(renderer, bounds.width | 0, bounds.height | 0);//, renderSession.renderer);

    // need to set //
    var m = new _tempMatrix;

    m.tx = -bounds.x;
    m.ty = -bounds.y;

    this._cacheAsBitmap = false;

    renderTexture.render(this, m, true);

    renderer.setRenderTarget(cachedRenderTarget);

    this._cacheAsBitmap = true;

    this._cachedObject = {
                            worldTransform:this.worldTransform,
                            anchor:new math.Point(-( bounds.x / bounds.width ), -( bounds.y / bounds.height )),
                            _texture:renderTexture,
                            blendMode:0,
                            worldAlpha:1,
                            tint:0xFFFFFF
                        };
};

DisplayObject.prototype._destroyCachedDisplayObject = function()
{
    this._cachedObject._texture.destroy();
    this._cachedObject = null;
};

DisplayObject.prototype._renderCached = function( renderer )
{
    this._generateCachedDisplayObject( renderer );

    this._cachedObject.worldAlpha = this.worldAlpha;

    renderer.plugins.sprite.render( this._cachedObject );
};

<|endoftext|>


<commit_before>import config from '../../__dev/buuk-config.js'
import { store } from '../store/index'
import ConfigPrimitive from './config-primitive'

function configLoader() {
    // Array of supported options.
    const validOptions = [ "name", "short_name", "sub"]

    Object.entries(config).forEach(item => {
        if (validOptions.includes(item[0])) {

            // Load the supported option into our store.
            store.commit('core/setConfig', new ConfigPrimitive(item[0], item[1]))

            // Dispatch an event of type 'config/${config_key}' every time we successfully load an event.
            window.EventBus.$emit(`config:${item[0]}`, item[1])
        }
    })
}

export default configLoader<commit_msg>Updated comment.<commit_after>import config from '../../__dev/buuk-config.js'
import { store } from '../store/index'
import ConfigPrimitive from './config-primitive'

function configLoader() {
    // Array of supported options.
    const validOptions = [ "name", "short_name", "sub"]

    Object.entries(config).forEach(item => {
        if (validOptions.includes(item[0])) {

            // Load the supported option into our store.
            store.commit('core/setConfig', new ConfigPrimitive(item[0], item[1]))

            // Dispatch an event of type 'config:${config_key}' every time we successfully load an event.
            window.EventBus.$emit(`config:${item[0]}`, item[1])
        }
    })
}

export default configLoader<|endoftext|>


<commit_before>class OrderedMap {

  constructor() {
    this.entries = [];
  }

  clear() {
    this.entries.length = 0;
    return this;
  }

  add(keyArg) {
    return this.set(keyArg);
  }

  set(key, value) {
    for (let i = 0; i < this.entries.length; i++) {
      const entry = this.entries[i];

      if (key === entry.key) {
        entry.value = value;
        return this;
      }
    }
    this.entries.push({key, value, visited: false});
    return this;
  }

  remove(key) {
    for (let i = 0; i < this.entries.length; i++) {
      const entry = this.entries[i];
      if (key === entry.key) {
        this.entries.splice(i, 1);
        return entry.value;
      }
    }
  }

  move(key, afterKey) {
    if (key === afterKey) return this;
    let fromIndex = null;
    let toIndex = null;
    for (let i = 0; i < this.entries.length; i++) {
      const entry = this.entries[i];
      if (key === entry.key) {
        fromIndex = i;
        break;
      }
    }
    if (fromIndex == null) return this;
    if (afterKey == null) {
      toIndex = 0;
    } else {
      for (let i = 0; i < this.entries.length; i++) {
        const entry = this.entries[i];
        if (afterKey === entry.key) {
          toIndex = i;
          break;
        }
      }
    }
    if (toIndex == null) return this;
    if (fromIndex > toIndex) toIndex++;
    const [entry] = this.entries.splice(fromIndex, 1);
    this.entries.splice(toIndex, 0, entry);
    return this;
  }

  get(key) {
    for (let i = 0; i < this.entries.length; i++) {
      const entry = this.entries[i];
      if (key === entry.key) return entry.value;
    }
  }

  has(key) {
    for (let i = 0; i < this.entries.length; i++) {
      const entry = this.entries[i];
      if (key === entry.key) return true;
    }
    return false;
  }

  *[Symbol.iterator]() {
    for (let i = 0; i < this.entries.length; i++) {
      const entry = this.entries[i];
      yield [entry.key, entry.value];
    }
  }

  getSize() {
    return this.entries.length;
  }

  visit(key) {
    for (let i = 0; i < this.entries.length; i++) {
      const entry = this.entries[i];

      if (key === entry.key) {
        entry.visited = true;
        return this;
      }
    }
    return this;
  }

  *clearVisitedAndIterateUnvisited() {
    for (let i = 0; i < this.entries.length; i++) {
      const entry = this.entries[i];
      if (!entry.visited) yield entry.key;
      entry.visited = false;
    }
  }
}

export default function createOrderedMap() {
  return new OrderedMap();
}
<commit_msg>Make ordered map use keysEqual function<commit_after>import keysEqual from '../keysEqual';

class OrderedMap {

  constructor() {
    this.entries = [];
  }

  clear() {
    this.entries.length = 0;
    return this;
  }

  add(keyArg) {
    return this.set(keyArg);
  }

  set(key, value) {
    for (let i = 0; i < this.entries.length; i++) {
      const entry = this.entries[i];

      if (keysEqual(key, entry.key)) {
        entry.value = value;
        return this;
      }
    }
    this.entries.push({key, value, visited: false});
    return this;
  }

  remove(key) {
    for (let i = 0; i < this.entries.length; i++) {
      const entry = this.entries[i];
      if (keysEqual(key, entry.key)) {
        this.entries.splice(i, 1);
        return entry.value;
      }
    }
  }

  move(key, afterKey) {
    if (keysEqual(key, afterKey)) return this;
    let fromIndex = null;
    let toIndex = null;
    for (let i = 0; i < this.entries.length; i++) {
      const entry = this.entries[i];
      if (keysEqual(key, entry.key)) {
        fromIndex = i;
        break;
      }
    }
    if (fromIndex == null) return this;
    if (afterKey == null) {
      toIndex = 0;
    } else {
      for (let i = 0; i < this.entries.length; i++) {
        const entry = this.entries[i];
        if (keysEqual(afterKey, entry.key)) {
          toIndex = i;
          break;
        }
      }
    }
    if (toIndex == null) return this;
    if (fromIndex > toIndex) toIndex++;
    const [entry] = this.entries.splice(fromIndex, 1);
    this.entries.splice(toIndex, 0, entry);
    return this;
  }

  get(key) {
    for (let i = 0; i < this.entries.length; i++) {
      const entry = this.entries[i];
      if (keysEqual(key, entry.key)) return entry.value;
    }
  }

  has(key) {
    for (let i = 0; i < this.entries.length; i++) {
      const entry = this.entries[i];
      if (keysEqual(key, entry.key)) return true;
    }
    return false;
  }

  *[Symbol.iterator]() {
    for (let i = 0; i < this.entries.length; i++) {
      const entry = this.entries[i];
      yield [entry.key, entry.value];
    }
  }

  getSize() {
    return this.entries.length;
  }

  visit(key) {
    for (let i = 0; i < this.entries.length; i++) {
      const entry = this.entries[i];

      if (keysEqual(key, entry.key)) {
        entry.visited = true;
        return this;
      }
    }
    return this;
  }

  *clearVisitedAndIterateUnvisited() {
    for (let i = 0; i < this.entries.length; i++) {
      const entry = this.entries[i];
      if (!entry.visited) yield entry.key;
      entry.visited = false;
    }
  }
}

export default function createOrderedMap() {
  return new OrderedMap();
}
<|endoftext|>


<commit_before>/* @flow */
import invariant from 'invariant';
import {
  oFilterMap,
  oForEach,
  oMap,
  oMerge,
  oReduce,
} from '../utils/ObjectUtils';
import Store from '../store/Store';

export type CompoundDependency = {
  propTypes?: Object,
  stores: Array<Store>;
  deref: (
    props: Object,
    state: ?Object,
    stores: Array<Store>
  ) => any;
};

export type Dependency = CompoundDependency | Store;

export type DependencyIndexEntry = {
  dispatchTokens: {[key:string]: bool};
  fields: {[key:string]: bool};
};

export type DependencyIndex = {
  [key:string]: DependencyIndexEntry;
};

export type DependencyMap = {
  [key:string]: Dependency;
};

export type PropTypes = {
  [key:string]: Function;
};

export function enforceValidDependencies(
  dependencies: DependencyMap
): DependencyMap {
  invariant(
    dependencies && typeof dependencies === 'object',
    'expected `dependencies` to be an `object` but got `%s`',
    dependencies
  );
  oForEach(dependencies, (dependency, field) => {
    if (dependency instanceof Store) {
      return;
    }
    invariant(
      dependency && typeof dependency === 'object',
      'expected `%s` to be an `object` but got `%s`',
      field,
      dependency
    );
    const { deref, stores } = dependency;
    invariant(
      typeof deref === 'function',
      'expected `%s.deref` to be a function but got `%s`',
      field,
      deref
    );
    invariant(
      Array.isArray(stores),
      'expected `%s.stores` to be an Array but got `%s`',
      field,
      dependency.stores
    );
    stores.forEach((store, index) => {
      invariant(
        store instanceof Store,
        'expected `%s.stores.%s` to be a `Store` but got `%s`',
        field,
        index,
        store
      );
    });
  });
  return dependencies;
}

export function dependencyPropTypes(dependencies: DependencyMap): PropTypes {
  return oReduce(dependencies, (types, dependency) => {
    const { propTypes } = dependency;
    if (!propTypes || typeof propTypes !== 'object') {
      return types;
    }
    return oMerge(types, propTypes);
  }, {});
}

export function calculate(
  dependency: Dependency,
  props: Object,
  state: ?Object
): any {
  if (dependency instanceof Store) {
    return dependency.get();
  }
  const { deref, stores } = dependency;
  if (deref.length === 0) {
    return deref();
  }
  if (deref.length === 1) {
    return deref(props);
  }
  return deref(props, state, stores);
}

export function calculateInitial(
  dependencies: DependencyMap,
  props: Object,
  state: ?Object
): Object {
  return oMap(
    dependencies,
    (dependency) => calculate(dependency, props, state)
  );
}

export function calculateForDispatch(
  dependencies: DependencyMap,
  dependencyIndexEntry: DependencyIndexEntry,
  props: Object,
  state: ?Object
): Object {
  return oMap(
    dependencyIndexEntry.fields,
    (_, field) => calculate(dependencies[field], props, state),
  );
}

export function calculateForPropsChange(
  dependencies: DependencyMap,
  props: Object,
  state: ?Object
): Object {
  return oFilterMap(
    dependencies,
    (dep) => dep.deref && dep.deref.length > 0,
    (dep) => {
      if (dep.deref.length === 1) {
        return calculate(dep, props);
      }
      return calculate(dep, props, state);
    }
  );
}

export function calculateForStateChange(
  dependencies: DependencyMap,
  props: Object,
  state: ?Object
): Object {
  return oFilterMap(
    dependencies,
    (dep) => dep.deref && dep.deref.length > 1,
    (dep) => calculate(dep, props, state)
  );
}

function makeIndexEntry(): DependencyIndexEntry {
  return {
    fields: {},
    dispatchTokens: {},
  };
}

export function makeDependencyIndex(
  dependencies: DependencyMap
): DependencyIndex {
  enforceValidDependencies(dependencies);
  return oReduce(dependencies, (index, dep, field) => {
    const stores = dep instanceof Store ? [dep] : dep.stores;
    stores.forEach((store) => {
      store.getActionTypes().forEach((actionType) => {
        let entry = index[actionType];
        if (!entry) {
          entry = index[actionType] = makeIndexEntry();
        }
        const token = store.getDispatchToken();
        entry.dispatchTokens[token] = true;
        entry.fields[field] = true;
      });
    });
    return index;
  }, {});
}

export function dependenciesUseState(dependencies: DependencyMap): bool {
  for (const field in dependencies) {
    if (!dependencies.hasOwnProperty(field)) {
      continue;
    }
    const dep = dependencies[field];
    if (!(dep instanceof Store) && dep.deref.length > 1) {
      return true;
    }
  }
  return false;
}
<commit_msg>whoops, calculate does the arity check<commit_after>/* @flow */
import invariant from 'invariant';
import {
  oFilterMap,
  oForEach,
  oMap,
  oMerge,
  oReduce,
} from '../utils/ObjectUtils';
import Store from '../store/Store';

export type CompoundDependency = {
  propTypes?: Object,
  stores: Array<Store>;
  deref: (
    props: Object,
    state: ?Object,
    stores: Array<Store>
  ) => any;
};

export type Dependency = CompoundDependency | Store;

export type DependencyIndexEntry = {
  dispatchTokens: {[key:string]: bool};
  fields: {[key:string]: bool};
};

export type DependencyIndex = {
  [key:string]: DependencyIndexEntry;
};

export type DependencyMap = {
  [key:string]: Dependency;
};

export type PropTypes = {
  [key:string]: Function;
};

export function enforceValidDependencies(
  dependencies: DependencyMap
): DependencyMap {
  invariant(
    dependencies && typeof dependencies === 'object',
    'expected `dependencies` to be an `object` but got `%s`',
    dependencies
  );
  oForEach(dependencies, (dependency, field) => {
    if (dependency instanceof Store) {
      return;
    }
    invariant(
      dependency && typeof dependency === 'object',
      'expected `%s` to be an `object` but got `%s`',
      field,
      dependency
    );
    const { deref, stores } = dependency;
    invariant(
      typeof deref === 'function',
      'expected `%s.deref` to be a function but got `%s`',
      field,
      deref
    );
    invariant(
      Array.isArray(stores),
      'expected `%s.stores` to be an Array but got `%s`',
      field,
      dependency.stores
    );
    stores.forEach((store, index) => {
      invariant(
        store instanceof Store,
        'expected `%s.stores.%s` to be a `Store` but got `%s`',
        field,
        index,
        store
      );
    });
  });
  return dependencies;
}

export function dependencyPropTypes(dependencies: DependencyMap): PropTypes {
  return oReduce(dependencies, (types, dependency) => {
    const { propTypes } = dependency;
    if (!propTypes || typeof propTypes !== 'object') {
      return types;
    }
    return oMerge(types, propTypes);
  }, {});
}

export function calculate(
  dependency: Dependency,
  props: Object,
  state: ?Object
): any {
  if (dependency instanceof Store) {
    return dependency.get();
  }
  const { deref, stores } = dependency;
  if (deref.length === 0) {
    return deref();
  }
  if (deref.length === 1) {
    return deref(props);
  }
  return deref(props, state, stores);
}

export function calculateInitial(
  dependencies: DependencyMap,
  props: Object,
  state: ?Object
): Object {
  return oMap(
    dependencies,
    (dependency) => calculate(dependency, props, state)
  );
}

export function calculateForDispatch(
  dependencies: DependencyMap,
  dependencyIndexEntry: DependencyIndexEntry,
  props: Object,
  state: ?Object
): Object {
  return oMap(
    dependencyIndexEntry.fields,
    (_, field) => calculate(dependencies[field], props, state),
  );
}

export function calculateForPropsChange(
  dependencies: DependencyMap,
  props: Object,
  state: ?Object
): Object {
  return oFilterMap(
    dependencies,
    (dep) => dep.deref && dep.deref.length > 0,
    (dep) => calculate(dep, props, state)
  );
}

export function calculateForStateChange(
  dependencies: DependencyMap,
  props: Object,
  state: ?Object
): Object {
  return oFilterMap(
    dependencies,
    (dep) => dep.deref && dep.deref.length > 1,
    (dep) => calculate(dep, props, state)
  );
}

function makeIndexEntry(): DependencyIndexEntry {
  return {
    fields: {},
    dispatchTokens: {},
  };
}

export function makeDependencyIndex(
  dependencies: DependencyMap
): DependencyIndex {
  enforceValidDependencies(dependencies);
  return oReduce(dependencies, (index, dep, field) => {
    const stores = dep instanceof Store ? [dep] : dep.stores;
    stores.forEach((store) => {
      store.getActionTypes().forEach((actionType) => {
        let entry = index[actionType];
        if (!entry) {
          entry = index[actionType] = makeIndexEntry();
        }
        const token = store.getDispatchToken();
        entry.dispatchTokens[token] = true;
        entry.fields[field] = true;
      });
    });
    return index;
  }, {});
}

export function dependenciesUseState(dependencies: DependencyMap): bool {
  for (const field in dependencies) {
    if (!dependencies.hasOwnProperty(field)) {
      continue;
    }
    const dep = dependencies[field];
    if (!(dep instanceof Store) && dep.deref.length > 1) {
      return true;
    }
  }
  return false;
}
<|endoftext|>


<commit_before>module.exports = [
  'Formio',
  'formioComponents',
  function(
    Formio,
    formioComponents
  ) {
    return {
      replace: true,
      restrict: 'E',
      require: '?^formio',
      scope: {
        component: '=',
        data: '=',
        formio: '=',
        formioForm: '=',
        readOnly: '=',
        gridRow: '=',
        gridCol: '='
      },
      templateUrl: 'formio/component.html',
      link: function(scope, el, attrs, formioCtrl) {
        if (formioCtrl) {
          scope.showAlerts = formioCtrl.showAlerts.bind(formioCtrl);
        }
        else {
          scope.showAlerts = function() {
            throw new Error('Cannot call $scope.showAlerts unless this component is inside a formio directive.');
          };
        }
      },
      controller: [
        '$scope',
        '$http',
        '$controller',
        function(
          $scope,
          $http,
          $controller
        ) {
          // Options to match jquery.maskedinput masks
          $scope.uiMaskOptions = {
            maskDefinitions: {
              '9': /\d/,
              'a': /[a-zA-Z]/,
              '*': /[a-zA-Z0-9]/
            },
            clearOnBlur: false,
            eventsToHandle: ['input', 'keyup', 'click', 'focus'],
            silentEvents: ['click', 'focus']
          };

          // Get the settings.
          var component = formioComponents.components[$scope.component.type] || formioComponents.components['custom'];

          // Set the component with the defaults from the component settings.
          angular.forEach(component.settings, function(value, key) {
            if (!$scope.component.hasOwnProperty(key)) {
              $scope.component[key] = angular.copy(value);
            }
          });

          // Add a new field value.
          $scope.addFieldValue = function() {
            var value = $scope.component.hasOwnProperty('defaultValue') ? $scope.component.defaultValue : '';
            $scope.data[$scope.component.key] = $scope.data[$scope.component.key] || [];
            $scope.data[$scope.component.key].push(value);
          };

          // Remove a field value.
          $scope.removeFieldValue = function(index) {
            $scope.data[$scope.component.key].splice(index, 1);
          };

          // Set the template for the component.
          if (typeof component.template === 'function') {
            $scope.template = component.template($scope);
          }
          else {
            $scope.template = component.template;
          }

          // Allow component keys to look like "settings[username]"
          if ($scope.component.key && $scope.component.key.indexOf('[') !== -1) {
            var matches = $scope.component.key.match(/([^\[]+)\[([^]+)\]/);
            if ((matches.length === 3) && $scope.data.hasOwnProperty(matches[1])) {
              $scope.data = $scope.data[matches[1]];
              $scope.component.key = matches[2];
            }
          }

          // If the component has a controller.
          if (component.controller) {
            // Maintain reverse compatibility by executing the old method style.
            if (typeof component.controller === 'function') {
              component.controller($scope.component, $scope, $http, Formio);
            }
            else {
              $controller(component.controller, {$scope: $scope});
            }
          }

          $scope.$watch('component.multiple', function(_new, _old) {
            if (!_new && !_old) return;

            // Establish a default for data.
            $scope.data = $scope.data || {};
            if ($scope.component.multiple) {
              // Use the current data or default.
              $scope.data[$scope.component.key] = ($scope.data[$scope.component.key] ? [$scope.data[$scope.component.key]] : false)
                || ($scope.component.hasOwnProperty('defaultValue') ? [$scope.component.defaultValue] : []);
            }
            else {
              // Use the current data or default.
              $scope.data[$scope.component.key] = ($scope.data[$scope.component.key] ? $scope.data[$scope.component.key] : false)
                || ($scope.component.hasOwnProperty('defaultValue') ? $scope.component.defaultValue : '');
            }
          });

          // Set the component name.
          $scope.componentId = $scope.component.key;
          if ($scope.gridRow !== undefined) {
            $scope.componentId += ('-' + $scope.gridRow);
          }
          if ($scope.gridCol !== undefined) {
            $scope.componentId += ('-' + $scope.gridCol);
          }
        }
      ]
    };
  }
];
<commit_msg>Fixing issue with the default values and multiple.<commit_after>module.exports = [
  'Formio',
  'formioComponents',
  function(
    Formio,
    formioComponents
  ) {
    return {
      replace: true,
      restrict: 'E',
      require: '?^formio',
      scope: {
        component: '=',
        data: '=',
        formio: '=',
        formioForm: '=',
        readOnly: '=',
        gridRow: '=',
        gridCol: '='
      },
      templateUrl: 'formio/component.html',
      link: function(scope, el, attrs, formioCtrl) {
        if (formioCtrl) {
          scope.showAlerts = formioCtrl.showAlerts.bind(formioCtrl);
        }
        else {
          scope.showAlerts = function() {
            throw new Error('Cannot call $scope.showAlerts unless this component is inside a formio directive.');
          };
        }
      },
      controller: [
        '$scope',
        '$http',
        '$controller',
        function(
          $scope,
          $http,
          $controller
        ) {
          // Options to match jquery.maskedinput masks
          $scope.uiMaskOptions = {
            maskDefinitions: {
              '9': /\d/,
              'a': /[a-zA-Z]/,
              '*': /[a-zA-Z0-9]/
            },
            clearOnBlur: false,
            eventsToHandle: ['input', 'keyup', 'click', 'focus'],
            silentEvents: ['click', 'focus']
          };

          // Get the settings.
          var component = formioComponents.components[$scope.component.type] || formioComponents.components['custom'];

          // Set the component with the defaults from the component settings.
          angular.forEach(component.settings, function(value, key) {
            if (!$scope.component.hasOwnProperty(key)) {
              $scope.component[key] = angular.copy(value);
            }
          });

          // Add a new field value.
          $scope.addFieldValue = function() {
            var value = $scope.component.hasOwnProperty('defaultValue') ? $scope.component.defaultValue : '';
            $scope.data[$scope.component.key] = $scope.data[$scope.component.key] || [];
            $scope.data[$scope.component.key].push(value);
          };

          // Remove a field value.
          $scope.removeFieldValue = function(index) {
            $scope.data[$scope.component.key].splice(index, 1);
          };

          // Set the template for the component.
          if (typeof component.template === 'function') {
            $scope.template = component.template($scope);
          }
          else {
            $scope.template = component.template;
          }

          // Allow component keys to look like "settings[username]"
          if ($scope.component.key && $scope.component.key.indexOf('[') !== -1) {
            var matches = $scope.component.key.match(/([^\[]+)\[([^]+)\]/);
            if ((matches.length === 3) && $scope.data.hasOwnProperty(matches[1])) {
              $scope.data = $scope.data[matches[1]];
              $scope.component.key = matches[2];
            }
          }

          // If the component has a controller.
          if (component.controller) {
            // Maintain reverse compatibility by executing the old method style.
            if (typeof component.controller === 'function') {
              component.controller($scope.component, $scope, $http, Formio);
            }
            else {
              $controller(component.controller, {$scope: $scope});
            }
          }

          $scope.$watch('component.multiple', function(_new, _old) {
            if (!_new && !_old) return;

            // Establish a default for data.
            $scope.data = $scope.data || {};
            if ($scope.component.multiple) {
              var value = null;
              if ($scope.data[$scope.component.key]) {
                // If a value is present, and its an array, assign it to the value.
                if ($scope.data[$scope.component.key] instanceof Array) {
                  value = $scope.data[$scope.component.key];
                }
                // If a value is present and it is not an array, wrap the value.
                else {
                  value = [$scope.data[$scope.component.key]];
                }
              }
              else if ($scope.component.hasOwnProperty('defaultValue')) {
                // If there is a default value and it is an array, assign it to the value.
                if ($scope.component.defaultValue instanceof Array) {
                  value = $scope.component.defaultValue;
                }
                // If there is a default value and it is not an array, wrap the value.
                else {
                  value = [$scope.component.defaultValue];
                }
              }
              else {
                // Couldn't safely default, make it a simple array. Possibly add a single obj or string later.
                value = [];
              }

              // Use the current data or default.
              $scope.data[$scope.component.key] = value;
            }
            else {
              // Use the current data or default.
              $scope.data[$scope.component.key] = ($scope.data[$scope.component.key] ? $scope.data[$scope.component.key] : false)
                || ($scope.component.hasOwnProperty('defaultValue') ? $scope.component.defaultValue : '');
            }
          });

          // Set the component name.
          $scope.componentId = $scope.component.key;
          if ($scope.gridRow !== undefined) {
            $scope.componentId += ('-' + $scope.gridRow);
          }
          if ($scope.gridCol !== undefined) {
            $scope.componentId += ('-' + $scope.gridCol);
          }
        }
      ]
    };
  }
];
<|endoftext|>


<commit_before>/* globals TileTools */
(class Pen extends DTile.Tool {
    static get toolName() { return "pen"; }
    static get icon() { return "tool:pen"; }

    onMove({ tileX, tileY, shiftKey, button, previewTiles }) {
        this.doTap(tileX, tileY, shiftKey, button);
        previewTiles(this._tiles);
        this._tiles = {};
    }

    onTap({ tileX, tileY, shiftKey, button, commitTiles }) {
        this.doTap(tileX, tileY, shiftKey, button);
        commitTiles(this._tiles);
        this._tiles = {};
    }

    doTap(tileX, tileY, shiftKey, button) {
        const shouldRemove = button === 2;

        this._paintOrigin = { x: tileX, y: tileY };
        if (!shiftKey || !this._lastPaintPosition) {
            this._lastPaintPosition = { x: tileX, y: tileY };
        }
        this._paintTo(tileX, tileY, shouldRemove);
        this._lastPaintPosition = { x: tileX, y: tileY };
    }

    onTrack({ tileX, tileY, state, button, previewTiles, commitTiles }) {
        const shouldRemove = button === 2;

        if (state === "start") {
            this._tiles = {};
            this._paintOrigin = this._lastPaintPosition = { x: tileX, y: tileY };
            this._paintTo(tileX, tileY, shouldRemove);
        } else if (state === "track") {
            this._paintTo(tileX, tileY, shouldRemove);
            this._lastPaintPosition = { x: tileX, y: tileY };
            previewTiles(this._tiles);
        } else if (state === "end") {
            commitTiles(this._tiles);
            this._tiles = {};
        }
    }

    _paintAt(tileX, tileY, shouldRemove) {
        const map = this.currentMap;
        const tileArea = this.tileArea;

        const layerTileArea = this.layerAsTileArea;
        const newTiles = { ...this._tiles };

        for (let lx = 0; lx < (shouldRemove ? 1 : tileArea.width); lx++) {
            for (let ly = 0; ly < (shouldRemove ? 1 : tileArea.height); ly++) {
                const x = tileX + lx;
                const y = tileY + ly;
                const tile = shouldRemove
                    ? { tileId: -1, tilesetId: -1 }
                    : TileTools.getTilingTileData(
                        tileArea,
                        this._paintOrigin.x, this._paintOrigin.y,
                        x, y
                    );

                if (x < 0 || y < 0 || x >= map.width || y >= map.height ||
                    (!shouldRemove && (tile.tileId === -1 || tile.tilesetId === -1))) continue;

                const tileMeta = this.getTileMeta(tile.tilesetId, tile.tileId);
                let layerIndex;
                if (tileMeta["@map-to-name"]) {
                    layerIndex = this.currentMap.layers.findIndex(layer => {
                        return layer.name === tileMeta["@map-to-name"];
                    });
                } else if (tileMeta["@map-to-index"]) {
                    layerIndex = tileMeta["@map-to-index"];
                } else {
                    layerIndex = this.currentLayerIndex;
                }

                const tileIndex = TileTools.getTileIndex(layerTileArea, x, y);
                if (!newTiles[layerIndex]) newTiles[layerIndex] = [];
                newTiles[layerIndex][tileIndex] = { ...tile };
            }
        }

        this._tiles = newTiles;
    }

    _paintTo(tileX, tileY, shouldRemove) {
        let x0 = this._lastPaintPosition.x,
            y0 = this._lastPaintPosition.y,
            x1 = tileX, y1 = tileY;
        const dx = Math.abs(x1 - x0), dy = Math.abs(y1 - y0);
        const sx = (x0 < x1) ? 1 : -1, sy = (y0 < y1) ? 1 : -1;

        let err = dx - dy;

        while (true) {
            this._paintAt(x0, y0, shouldRemove);

            if ((x0 === x1) && (y0 === y1)) break;
            const e2 = err * 2;
            if (e2 > -dy) {
                err -= dy;
                x0 += sx;
            }
            if (e2 < dx) {
                err += dx;
                y0 += sy;
            }
        }
    }
}).register();
<commit_msg>Fix new tiles type for pen<commit_after>/* globals TileTools */
(class Pen extends DTile.Tool {
    static get toolName() { return "pen"; }
    static get icon() { return "tool:pen"; }

    onMove({ tileX, tileY, shiftKey, button, previewTiles }) {
        this.doTap(tileX, tileY, shiftKey, button);
        previewTiles(this._tiles);
        this._tiles = {};
    }

    onTap({ tileX, tileY, shiftKey, button, commitTiles }) {
        this.doTap(tileX, tileY, shiftKey, button);
        commitTiles(this._tiles);
        this._tiles = {};
    }

    doTap(tileX, tileY, shiftKey, button) {
        const shouldRemove = button === 2;

        this._paintOrigin = { x: tileX, y: tileY };
        if (!shiftKey || !this._lastPaintPosition) {
            this._lastPaintPosition = { x: tileX, y: tileY };
        }
        this._paintTo(tileX, tileY, shouldRemove);
        this._lastPaintPosition = { x: tileX, y: tileY };
    }

    onTrack({ tileX, tileY, state, button, previewTiles, commitTiles }) {
        const shouldRemove = button === 2;

        if (state === "start") {
            this._tiles = {};
            this._paintOrigin = this._lastPaintPosition = { x: tileX, y: tileY };
            this._paintTo(tileX, tileY, shouldRemove);
        } else if (state === "track") {
            this._paintTo(tileX, tileY, shouldRemove);
            this._lastPaintPosition = { x: tileX, y: tileY };
            previewTiles(this._tiles);
        } else if (state === "end") {
            commitTiles(this._tiles);
            this._tiles = {};
        }
    }

    _paintAt(tileX, tileY, shouldRemove) {
        const map = this.currentMap;
        const tileArea = this.tileArea;

        const layerTileArea = this.layerAsTileArea;
        const newTiles = { ...this._tiles };

        for (let lx = 0; lx < (shouldRemove ? 1 : tileArea.width); lx++) {
            for (let ly = 0; ly < (shouldRemove ? 1 : tileArea.height); ly++) {
                const x = tileX + lx;
                const y = tileY + ly;
                const tile = shouldRemove
                    ? { tileId: -1, tilesetId: -1 }
                    : TileTools.getTilingTileData(
                        tileArea,
                        this._paintOrigin.x, this._paintOrigin.y,
                        x, y
                    );

                if (x < 0 || y < 0 || x >= map.width || y >= map.height ||
                    (!shouldRemove && (tile.tileId === -1 || tile.tilesetId === -1))) continue;

                const tileMeta = this.getTileMeta(tile.tilesetId, tile.tileId);
                let layerIndex;
                if (tileMeta["@map-to-name"]) {
                    layerIndex = this.currentMap.layers.findIndex(layer => {
                        return layer.name === tileMeta["@map-to-name"];
                    });
                } else if (tileMeta["@map-to-index"]) {
                    layerIndex = tileMeta["@map-to-index"];
                } else {
                    layerIndex = this.currentLayerIndex;
                }

                const tileIndex = TileTools.getTileIndex(layerTileArea, x, y);
                if (!newTiles[layerIndex]) newTiles[layerIndex] = {};
                newTiles[layerIndex][tileIndex] = { ...tile };
            }
        }

        this._tiles = newTiles;
    }

    _paintTo(tileX, tileY, shouldRemove) {
        let x0 = this._lastPaintPosition.x,
            y0 = this._lastPaintPosition.y,
            x1 = tileX, y1 = tileY;
        const dx = Math.abs(x1 - x0), dy = Math.abs(y1 - y0);
        const sx = (x0 < x1) ? 1 : -1, sy = (y0 < y1) ? 1 : -1;

        let err = dx - dy;

        while (true) {
            this._paintAt(x0, y0, shouldRemove);

            if ((x0 === x1) && (y0 === y1)) break;
            const e2 = err * 2;
            if (e2 > -dy) {
                err -= dy;
                x0 += sx;
            }
            if (e2 < dx) {
                err += dx;
                y0 += sy;
            }
        }
    }
}).register();
<|endoftext|>


<commit_before>/**
 * Validation.validateItems
 *
 * @param {object} instance
 * @param {object} schema
 * @param {string} path
 * @param {function} callback
 */
Validation.prototype.validateItems = function(instance, schema, path, callback) {

  // Save a reference to the ‘this’
  var self = this;

  /**
   * {
   *   type: 'array'
   *   items: [
   *     {
   *       type: 'string'
   *     },
   *     {
   *       type: 'number'
   *     },
   *     ...
   *   ],
   *   ...
   * }
   */
  if (isArray(schema.items)) {

    // Additional items are allowed
    if (isUndefined(schema.additionalItems) || schema.additionalItems === true) {
      return each(schema.items, function(itemIndex, itemSchema, callback) {
        return self.validateSchema(
          instance[itemIndex],
          itemSchema,
          self.joinPath(path, itemIndex),
          callback
        );
      }, callback);
    }

    return each(instance, function(itemIndex, itemValue, callback) {

      // The ‘additionalItems’ attribute is a schema that defines
      // the schema of the additional items
      if (schema.items[itemIndex] || isObject(schema.additionalItems)) {
        return self.validateSchema(
          itemValue,
          schema.items[itemIndex],
          self.joinPath(path, itemIndex),
          callback
        );
      }

      // Additional items are disallowed
      if (schema.additionalItems === false) {
        self.errors.push({
          property: self.joinPath(path, itemIndex),
          propertyValue: itemValue,
          attributeName: 'additionalItems',
          attributeValue: false
        });
        return callback();
      }

    }, callback);

  }

  /**
   * {
   *   type: 'array'
   *   items: {
   *     type: 'string'
   *   },
   *   ...
   * }
   */
  if (isObject(schema.items) && instance && !isEmpty(instance)) {
    return each(instance, function(itemIndex, itemValue, callback) {
      return self.validateSchema(
        instance[itemIndex],
        schema.items,
        self.joinPath(path, itemIndex),
        callback
      );
    }, callback);
  } else {
    return callback();
  }

};<commit_msg>fix of TypeError: Cannot read property '0' of undefined<commit_after>/**
 * Validation.validateItems
 *
 * @param {object} instance
 * @param {object} schema
 * @param {string} path
 * @param {function} callback
 */
Validation.prototype.validateItems = function(instance, schema, path, callback) {

  // Save a reference to the ‘this’
  var self = this;

  /**
   * {
   *   type: 'array'
   *   items: [
   *     {
   *       type: 'string'
   *     },
   *     {
   *       type: 'number'
   *     },
   *     ...
   *   ],
   *   ...
   * }
   */

  if (isUndefined(instance)) {
    instance = []
  }

  if (isArray(schema.items)) {

    // Additional items are allowed
    if (isUndefined(schema.additionalItems) || schema.additionalItems === true) {
      return each(schema.items, function(itemIndex, itemSchema, callback) {
        return self.validateSchema(
          instance[itemIndex],
          itemSchema,
          self.joinPath(path, itemIndex),
          callback
        );
      }, callback);
    }

    return each(instance, function(itemIndex, itemValue, callback) {

      // The ‘additionalItems’ attribute is a schema that defines
      // the schema of the additional items
      if (schema.items[itemIndex] || isObject(schema.additionalItems)) {
        return self.validateSchema(
          itemValue,
          schema.items[itemIndex],
          self.joinPath(path, itemIndex),
          callback
        );
      }

      // Additional items are disallowed
      if (schema.additionalItems === false) {
        self.errors.push({
          property: self.joinPath(path, itemIndex),
          propertyValue: itemValue,
          attributeName: 'additionalItems',
          attributeValue: false
        });
        return callback();
      }

    }, callback);

  }

  /**
   * {
   *   type: 'array'
   *   items: {
   *     type: 'string'
   *   },
   *   ...
   * }
   */
  if (isObject(schema.items) && instance && !isEmpty(instance)) {
    return each(instance, function(itemIndex, itemValue, callback) {
      return self.validateSchema(
        instance[itemIndex],
        schema.items,
        self.joinPath(path, itemIndex),
        callback
      );
    }, callback);
  } else {
    return callback();
  }

};<|endoftext|>


<commit_before>/**
 * Copyright 2017 The __PROJECT__ Authors. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS-IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import {SubscriptionMarkup} from '../runtime/subscription-markup';
import {setImportantStyles} from '../utils/style';

const USE_SANDBOX = true;
const PAY_SERVICE =
    USE_SANDBOX ?
    {
      origin: 'https://sandbox.google.com',
      path: '/payments/apis/instantbuy/serving/proxy.html',
    } :
    {
      origin: 'https://subs-pay.googleplex.com',
      path: '/proxy.html',
    };

/**
 */
export class PaymentsView {
  constructor(win, context, paymentRequestBlob) {
    /** @const @private {!Window} */
    this.win_ = win;

    /** @const @private {!PopupContext} */
    this.context_ = context;

    this.paymentRequestBlob_ = paymentRequestBlob;

    /** @private @const {!Element} */
    this.iframe_ = this.win_.document.createElement('iframe');
    this.iframe_.setAttribute('frameborder', '0');
    this.iframe_.setAttribute('scrolling', 'no');
    setImportantStyles(this.iframe_, {
      'width': '100%',
      'min-height': '80px',
    });

    /** @private {?function(number, number)} */
    this.onResize_ = null;

    /** @private {?function()} */
    this.onComplete_ = null;
  }

  /**
   * @param {function()} callback
   * @return {!PaymentsView}
   */
  onComplete(callback) {
    this.onComplete_ = callback;
    return this;
  }

  /**
   * @return {!Element}
   */
  getElement() {
    return this.iframe_;
  }

  /**
   * @return {!Promise}
   */
  init() {
    const readyPromise = new Promise(resolve => {
      // Wait for the first resize.
      this.onResize_ = resolve;
    });
    const loc = PAY_SERVICE;
    const markup = new SubscriptionMarkup(this.win_);
    const themeColor = markup.getThemeColor();
    this.win_.addEventListener('message', e => {
      if (e.source == this.iframe_.contentWindow &&
          e.origin == loc.origin &&
          e.data && e.data.type) {
        this.onMessage_(e.data.type, e.data.payload);
      }
    });

    this.iframe_.src = loc.origin + loc.path
        + '?pc=' + (themeColor ? encodeURIComponent(themeColor) : '')
        + '&ep=' + encodeURIComponent(this.paymentRequestBlob_);
    return readyPromise;
  }

  /**
   * @param {string} type
   * @param {!Object} payload
   * @private
   */
  onMessage_(type, payload) {
    console.log('onMessage_: ', type, payload);
    switch (type) {
      case 'init':
      case 'ready':
        break;
      case 'resize':
        // Notify the context about resize required.
        this.context_.resizeView(this, payload.height);

        if (this.onResize_) {
          this.onResize_(payload.width, payload.height);
        }
        break;
      case 'busy':
        this.context_.setBusy(payload.busy);
        break;
      case 'complete':
        if (this.onComplete_) {
          this.onComplete_();
        }
        break;
      default:
        throw new Error(`unknown message type: "${type}"`);
    }
  }
}
<commit_msg>Remove busy call for Payments view process to remove duplicate loading indicator (#114)<commit_after>/**
 * Copyright 2017 The __PROJECT__ Authors. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS-IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import {SubscriptionMarkup} from '../runtime/subscription-markup';
import {setImportantStyles} from '../utils/style';

const USE_SANDBOX = true;
const PAY_SERVICE =
    USE_SANDBOX ?
    {
      origin: 'https://sandbox.google.com',
      path: '/payments/apis/instantbuy/serving/proxy.html',
    } :
    {
      origin: 'https://subs-pay.googleplex.com',
      path: '/proxy.html',
    };

/**
 */
export class PaymentsView {
  constructor(win, context, paymentRequestBlob) {
    /** @const @private {!Window} */
    this.win_ = win;

    /** @const @private {!PopupContext} */
    this.context_ = context;

    this.paymentRequestBlob_ = paymentRequestBlob;

    /** @private @const {!Element} */
    this.iframe_ = this.win_.document.createElement('iframe');
    this.iframe_.setAttribute('frameborder', '0');
    this.iframe_.setAttribute('scrolling', 'no');
    setImportantStyles(this.iframe_, {
      'width': '100%',
      'min-height': '80px',
    });

    /** @private {?function(number, number)} */
    this.onResize_ = null;

    /** @private {?function()} */
    this.onComplete_ = null;
  }

  /**
   * @param {function()} callback
   * @return {!PaymentsView}
   */
  onComplete(callback) {
    this.onComplete_ = callback;
    return this;
  }

  /**
   * @return {!Element}
   */
  getElement() {
    return this.iframe_;
  }

  /**
   * @return {!Promise}
   */
  init() {
    const readyPromise = new Promise(resolve => {
      // Wait for the first resize.
      this.onResize_ = resolve;
    });
    const loc = PAY_SERVICE;
    const markup = new SubscriptionMarkup(this.win_);
    const themeColor = markup.getThemeColor();
    this.win_.addEventListener('message', e => {
      if (e.source == this.iframe_.contentWindow &&
          e.origin == loc.origin &&
          e.data && e.data.type) {
        this.onMessage_(e.data.type, e.data.payload);
      }
    });

    this.iframe_.src = loc.origin + loc.path
        + '?pc=' + (themeColor ? encodeURIComponent(themeColor) : '')
        + '&ep=' + encodeURIComponent(this.paymentRequestBlob_);
    return readyPromise;
  }

  /**
   * @param {string} type
   * @param {!Object} payload
   * @private
   */
  onMessage_(type, payload) {
    console.log('onMessage_: ', type, payload);
    switch (type) {
      case 'init':
      case 'ready':
        break;
      case 'resize':
        // Notify the context about resize required.
        this.context_.resizeView(this, payload.height);

        if (this.onResize_) {
          this.onResize_(payload.width, payload.height);
        }
        break;
      case 'busy':
        // No need to call content.setBusy() since, Payments UI manages it's
        // own loading state.
        break;
      case 'complete':
        if (this.onComplete_) {
          this.onComplete_();
        }
        break;
      default:
        throw new Error(`unknown message type: "${type}"`);
    }
  }
}
<|endoftext|>


<commit_before>/**
 * @license
 * Copyright 2017 The FOAM Authors. All Rights Reserved.
 * http://www.apache.org/licenses/LICENSE-2.0
 */

foam.CLASS({
  package: 'foam.nanos.session',
  name: 'Session',

  implements: [
    'foam.nanos.auth.CreatedAware',
    'foam.nanos.auth.CreatedByAware',
    'foam.nanos.medusa.Clusterable'
  ],

  javaImports: [
    'foam.core.X',
    'foam.dao.DAO',
    'static foam.mlang.MLang.*',
    'foam.nanos.app.AppConfig',
    'foam.nanos.auth.*',
    'foam.nanos.auth.Subject',
    'foam.nanos.boot.NSpec',
    'foam.nanos.logger.Logger',
    'foam.nanos.logger.PrefixLogger',
    'foam.nanos.theme.Theme',
    'foam.nanos.theme.ThemeDomain',
    'foam.nanos.theme.Themes',
    'foam.util.SafetyUtil',
    'java.util.Date',
    'java.util.Random',
    'java.util.UUID',
    'javax.servlet.http.HttpServletRequest',
    'org.eclipse.jetty.server.Request'
  ],

  tableColumns: [
    'userId',
    'agentId',
    'created',
    'lastUsed',
    'ttl',
    'uses',
    'remoteHost'
  ],

  properties: [
    {
      class: 'String',
      name: 'id',
      includeInDigest: true,
      visibility: 'RO'
    },
    {
      class: 'Long',
      name: 'userId',
      includeInDigest: true,
      tableCellFormatter: function(value, obj) {
        this.add(value);
        this.__context__.userDAO.find(value).then(function(user) {
          this.add(' ', user && user.toSummary());
        }.bind(this));
      },
      required: true,
      updateVisibility: 'RO'
    },
    {
      class: 'Long',
      name: 'agentId',
      includeInDigest: true,
      tableCellFormatter: function(value, obj) {
        if ( ! value ) return;
        this.add(value);
        this.__context__.userDAO.find(value).then(function(user) {
          this.add(' ', user.toSummary());
        }.bind(this));
      },
      visibility: 'RO'
    },
    {
      class: 'DateTime',
      name: 'created',
      includeInDigest: true,
      visibility: 'RO'
    },
    {
      class: 'Reference',
      of: 'foam.nanos.auth.User',
      name: 'createdBy',
      includeInDigest: true,
      visibility: 'RO'
    },
    {
      class: 'Reference',
      of: 'foam.nanos.auth.User',
      name: 'createdByAgent',
      includeInDigest: true,
      visibility: 'RO'
    },
    {
      class: 'DateTime',
      name: 'lastUsed',
      visibility: 'RO',
      storageTransient: true
    },
    {
      class: 'Duration',
      name: 'ttl',
      label: 'TTL',
      documentation: 'The "time to live" of the session. The amount of time in milliseconds that the session should be kept alive after its last use before being destroyed. Must be a positive value or zero.',
      value: 28800000, // 1000 * 60 * 60 * 8 = number of milliseconds in 8 hours
      includeInDigest: true,
      tableWidth: 70,
      validationPredicates: [
        {
          args: ['ttl'],
          predicateFactory: function(e) {
            return e.GTE(foam.nanos.session.Session.TTL, 0);
          },
          errorString: 'TTL must be 0 or greater.'
        }
      ]
    },
    {
      class: 'Long',
      name: 'uses',
      tableWidth: 70,
      storageTransient: true
    },
    {
      class: 'String',
      name: 'remoteHost',
      includeInDigest: true,
      visibility: 'RO',
      tableWidth: 120
    },
    {
      documentation: `Restrict access to long TTL session to particular IP address range.

@see https://en.wikipedia.org/wiki/Classless_Inter-Domain_Routing
List entries are of the form: 172.0.0.0/24 - this would restrict logins to the 172 network.`,
      class: 'FObjectArray',
      of: 'foam.net.CIDR',
      name: 'cidrWhiteList',
      includeInDigest: true
    },
    {
      class: 'Object',
      name: 'context',
      type: 'Context',
      javaFactory: 'return reset(getX());',
      visibility: 'HIDDEN',
      transient: true,
      networkTransient: true,
      clusterTransient: true
    },
    {
      class: 'Boolean',
      name: 'clusterable',
      value: true,
      visibility: 'HIDDEN',
      storageTransient: true,
      clusterTransient: true
    }
  ],

  methods: [
    // Disable cloneing and freezing so that Sessions can be mutated while
    // in the SessionDAO.
    {
      name: 'fclone',
      type: 'foam.core.FObject',
      javaCode: 'return this;'
    },
    {
      name: 'freeze',
      type: 'foam.core.FObject',
      javaCode: ' return this; '
    },
    {
      name: 'touch',
      documentation: 'Called when session used to track usage statistics.',
      javaCode: `
        synchronized ( this ) {
          setLastUsed(new Date());
          setUses(getUses()+1);
        }
      `
    },
    {
      name: 'validateRemoteHost',
      args: [
        {
          name: 'x',
          type: 'Context'
        }
      ],
      javaThrows: ['foam.core.ValidationException'],
      javaCode: `
      String remoteIp = foam.net.IPSupport.instance().getRemoteIp(x);
      foam.net.CIDR[] cidrs = getCidrWhiteList();
      if ( remoteIp == null ||
           SafetyUtil.isEmpty(getRemoteHost()) ||
           SafetyUtil.equals(getRemoteHost(), remoteIp) ||
           cidrs == null ||
           cidrs.length == 0 ) {
        return;
      }
      if ( ! SafetyUtil.isEmpty(getRemoteHost()) &&
           ! SafetyUtil.equals(getRemoteHost(), remoteIp) ) {
        throw new foam.core.ValidationException("IP changed");
      }
      for ( foam.net.CIDR cidr : cidrs ) {
        try {
          if ( cidr.inRange(x, remoteIp) ) {
            return;
          }
        } catch (java.net.UnknownHostException e) {
          ((foam.nanos.logger.Logger) x.get("logger")).warning(this.getClass().getSimpleName(), "validateRemoteHost", remoteIp, e.getMessage());
        }
      }
      throw new foam.core.ValidationException("Restricted IP");
      `
    },
    {
      name: 'reset',
      type: 'Context',
      args: [
        { type: 'Context', name: 'x' }
      ],
      documentation: `
        Return a subcontext of the given context where the security-relevant
        entries have been reset to their empty default values.
      `,
      javaCode: `
      Subject subject = new Subject.Builder(x).setUser(null).build();
        return x
          .put(Session.class, this)
          .put("spid", null)
          .put("subject", subject)
          .put("group", null)
          .put("twoFactorSuccess", false)
          .put(CachingAuthService.CACHE_KEY, null)
          .put(
            "logger",
            new PrefixLogger(
              new Object[] { "Unauthenticated session" },
              (Logger) x.get("logger")
            )
          );
      `
    },
    {
      name: 'applyTo',
      type: 'Context',
      args: [
        { type: 'Context', name: 'x' }
      ],
      documentation: `
        Returns a subcontext of the given context with the user, group, and
        other information relevant to this session filled in if it's appropriate
        to do so.
      `,
      javaCode: `
        // We null out the security-relevant entries in the context since we
        // don't want whatever was there before to leak through, especially
        // since the system context (which has full admin privileges) is often
        // used as the argument to this method.
        X rtn = reset(x);

        if ( getUserId() == 0 ) {
          HttpServletRequest req = x.get(HttpServletRequest.class);
          if ( req == null ) {
            // null during test runs
            return rtn;
          }
          AppConfig appConfig = (AppConfig) x.get("appConfig");
          appConfig = (AppConfig) appConfig.fclone();

          Theme theme = ((Themes) x.get("themes")).findTheme(x);
          rtn = rtn.put("theme", theme);

          AppConfig themeAppConfig = theme.getAppConfig();
          if ( themeAppConfig != null ) {
            appConfig.copyFrom(themeAppConfig);
          }
          appConfig = appConfig.configure(x, null);

          rtn = rtn.put("appConfig", appConfig);

          return rtn;
        }

        // Validate
        validate(x);

        DAO localUserDAO  = (DAO) x.get("localUserDAO");
        DAO localGroupDAO = (DAO) x.get("localGroupDAO");
        AuthService auth  = (AuthService) x.get("auth");
        User user         = (User) localUserDAO.find(getUserId());
        User agent        = (User) localUserDAO.find(getAgentId());
        Object[] prefix   = agent == null
          ? new Object[] { String.format("%s (%d)", user.toSummary(), user.getId()) }
          : new Object[] { String.format("%s (%d) acting as %s (%d)", agent.toSummary(), agent.getId(), user.toSummary(), user.getId()) };

        Subject subject = new Subject();
        subject.setUser(agent);
        subject.setUser(user);
        rtn = rtn
          .put("subject", subject)
          .put("spid", user.getSpid())
          .put("logger", new PrefixLogger(prefix, (Logger) x.get("logger")))
          .put("twoFactorSuccess", getContext().get("twoFactorSuccess"))
          .put(CachingAuthService.CACHE_KEY, getContext().get(CachingAuthService.CACHE_KEY));

        // We need to do this after the user and agent have been put since
        // 'getCurrentGroup' depends on them being in the context.
        Group group = auth.getCurrentGroup(rtn);

        if ( group != null ) {
          rtn = rtn
            .put("group", group)
            .put("appConfig", group.getAppConfig(rtn));
        }
        rtn = rtn.put("theme", ((Themes) x.get("themes")).findTheme(rtn));

        return rtn;
      `
    },
    {
      name: 'validate',
      args: [
        { name: 'x', type: 'Context' }
      ],
      javaCode: `
        if ( getUserId() < 0 ) {
          throw new IllegalStateException("User id is invalid.");
        }

        if ( getAgentId() < 0 ) {
          throw new IllegalStateException("Agent id is invalid.");
        }

        if ( getUserId() > 0 ) {
          checkUserEnabled(x, getUserId());
        }

        if ( getAgentId() > 0 ) {
          checkUserEnabled(x, getAgentId());
        }
      `
    },
    {
      name: 'checkUserEnabled',
      args: [
        { name: 'x', type: 'Context' },
        { name: 'userId', type: 'Long' }
      ],
      javaCode: `
        User user = (User) ((DAO) x.get("localUserDAO")).find(userId);

        if ( user == null
         || (user instanceof LifecycleAware && ((LifecycleAware)user).getLifecycleState() != LifecycleState.ACTIVE)
       ) {
          ((Logger) x.get("logger")).warning("Session", "User not found.", userId);
          throw new RuntimeException(String.format("User with id '%d' not found.", userId));
        }

        if ( ! user.getEnabled() ) {
          throw new RuntimeException(String.format("The user with id '%d' has been disabled.", userId));
        }
      `
    }
  ]
});
<commit_msg>Updating CIDR Whitelisting (#4951)<commit_after>/**
 * @license
 * Copyright 2017 The FOAM Authors. All Rights Reserved.
 * http://www.apache.org/licenses/LICENSE-2.0
 */

foam.CLASS({
  package: 'foam.nanos.session',
  name: 'Session',

  implements: [
    'foam.nanos.auth.CreatedAware',
    'foam.nanos.auth.CreatedByAware',
    'foam.nanos.medusa.Clusterable'
  ],

  javaImports: [
    'foam.core.X',
    'foam.dao.DAO',
    'static foam.mlang.MLang.*',
    'foam.nanos.app.AppConfig',
    'foam.nanos.auth.*',
    'foam.nanos.auth.Subject',
    'foam.nanos.boot.NSpec',
    'foam.nanos.logger.Logger',
    'foam.nanos.logger.PrefixLogger',
    'foam.nanos.theme.Theme',
    'foam.nanos.theme.ThemeDomain',
    'foam.nanos.theme.Themes',
    'foam.util.SafetyUtil',
    'java.util.Date',
    'java.util.Random',
    'java.util.UUID',
    'javax.servlet.http.HttpServletRequest',
    'org.eclipse.jetty.server.Request'
  ],

  tableColumns: [
    'userId',
    'agentId',
    'created',
    'lastUsed',
    'ttl',
    'uses',
    'remoteHost'
  ],

  properties: [
    {
      class: 'String',
      name: 'id',
      includeInDigest: true,
      visibility: 'RO'
    },
    {
      class: 'Long',
      name: 'userId',
      includeInDigest: true,
      tableCellFormatter: function(value, obj) {
        this.add(value);
        this.__context__.userDAO.find(value).then(function(user) {
          this.add(' ', user && user.toSummary());
        }.bind(this));
      },
      required: true,
      updateVisibility: 'RO'
    },
    {
      class: 'Long',
      name: 'agentId',
      includeInDigest: true,
      tableCellFormatter: function(value, obj) {
        if ( ! value ) return;
        this.add(value);
        this.__context__.userDAO.find(value).then(function(user) {
          this.add(' ', user.toSummary());
        }.bind(this));
      },
      visibility: 'RO'
    },
    {
      class: 'DateTime',
      name: 'created',
      includeInDigest: true,
      visibility: 'RO'
    },
    {
      class: 'Reference',
      of: 'foam.nanos.auth.User',
      name: 'createdBy',
      includeInDigest: true,
      visibility: 'RO'
    },
    {
      class: 'Reference',
      of: 'foam.nanos.auth.User',
      name: 'createdByAgent',
      includeInDigest: true,
      visibility: 'RO'
    },
    {
      class: 'DateTime',
      name: 'lastUsed',
      visibility: 'RO',
      storageTransient: true
    },
    {
      class: 'Duration',
      name: 'ttl',
      label: 'TTL',
      documentation: 'The "time to live" of the session. The amount of time in milliseconds that the session should be kept alive after its last use before being destroyed. Must be a positive value or zero.',
      value: 28800000, // 1000 * 60 * 60 * 8 = number of milliseconds in 8 hours
      includeInDigest: true,
      tableWidth: 70,
      validationPredicates: [
        {
          args: ['ttl'],
          predicateFactory: function(e) {
            return e.GTE(foam.nanos.session.Session.TTL, 0);
          },
          errorString: 'TTL must be 0 or greater.'
        }
      ]
    },
    {
      class: 'Long',
      name: 'uses',
      tableWidth: 70,
      storageTransient: true
    },
    {
      class: 'String',
      name: 'remoteHost',
      includeInDigest: true,
      visibility: 'RO',
      tableWidth: 120
    },
    {
      documentation: `Restrict access to long TTL session to particular IP address range.

@see https://en.wikipedia.org/wiki/Classless_Inter-Domain_Routing
List entries are of the form: 172.0.0.0/24 - this would restrict logins to the 172 network.`,
      class: 'FObjectArray',
      of: 'foam.net.CIDR',
      name: 'cidrWhiteList',
      includeInDigest: true
    },
    {
      class: 'Object',
      name: 'context',
      type: 'Context',
      javaFactory: 'return reset(getX());',
      visibility: 'HIDDEN',
      transient: true,
      networkTransient: true,
      clusterTransient: true
    },
    {
      class: 'Boolean',
      name: 'clusterable',
      value: true,
      visibility: 'HIDDEN',
      storageTransient: true,
      clusterTransient: true
    }
  ],

  methods: [
    // Disable cloneing and freezing so that Sessions can be mutated while
    // in the SessionDAO.
    {
      name: 'fclone',
      type: 'foam.core.FObject',
      javaCode: 'return this;'
    },
    {
      name: 'freeze',
      type: 'foam.core.FObject',
      javaCode: ' return this; '
    },
    {
      name: 'touch',
      documentation: 'Called when session used to track usage statistics.',
      javaCode: `
        synchronized ( this ) {
          setLastUsed(new Date());
          setUses(getUses()+1);
        }
      `
    },
    {
      name: 'validateRemoteHost',
      args: [
        {
          name: 'x',
          type: 'Context'
        }
      ],
      javaThrows: ['foam.core.ValidationException'],
      javaCode: `
      String remoteIp = foam.net.IPSupport.instance().getRemoteIp(x);
      foam.net.CIDR[] cidrs = getCidrWhiteList();
      if ( remoteIp == null ||
           SafetyUtil.isEmpty(getRemoteHost()) ||
           SafetyUtil.equals(getRemoteHost(), remoteIp) ||
           cidrs == null ||
           cidrs.length == 0 ) {
        return;
      }

      // Check whitelisted IP addresses
      for ( foam.net.CIDR cidr : cidrs ) {
        try {
          if ( cidr.inRange(x, remoteIp) ) {
            return;
          }
        } catch (java.net.UnknownHostException e) {
          ((foam.nanos.logger.Logger) x.get("logger")).warning(this.getClass().getSimpleName(), "validateRemoteHost", remoteIp, e.getMessage());
        }
      }

      // Do not allow IP to change if not in whitelist
      if ( ! SafetyUtil.isEmpty(getRemoteHost()) &&
           ! SafetyUtil.equals(getRemoteHost(), remoteIp) ) {
        throw new foam.core.ValidationException("IP changed");
      }

      throw new foam.core.ValidationException("Restricted IP");
      `
    },
    {
      name: 'reset',
      type: 'Context',
      args: [
        { type: 'Context', name: 'x' }
      ],
      documentation: `
        Return a subcontext of the given context where the security-relevant
        entries have been reset to their empty default values.
      `,
      javaCode: `
      Subject subject = new Subject.Builder(x).setUser(null).build();
        return x
          .put(Session.class, this)
          .put("spid", null)
          .put("subject", subject)
          .put("group", null)
          .put("twoFactorSuccess", false)
          .put(CachingAuthService.CACHE_KEY, null)
          .put(
            "logger",
            new PrefixLogger(
              new Object[] { "Unauthenticated session" },
              (Logger) x.get("logger")
            )
          );
      `
    },
    {
      name: 'applyTo',
      type: 'Context',
      args: [
        { type: 'Context', name: 'x' }
      ],
      documentation: `
        Returns a subcontext of the given context with the user, group, and
        other information relevant to this session filled in if it's appropriate
        to do so.
      `,
      javaCode: `
        // We null out the security-relevant entries in the context since we
        // don't want whatever was there before to leak through, especially
        // since the system context (which has full admin privileges) is often
        // used as the argument to this method.
        X rtn = reset(x);

        if ( getUserId() == 0 ) {
          HttpServletRequest req = x.get(HttpServletRequest.class);
          if ( req == null ) {
            // null during test runs
            return rtn;
          }
          AppConfig appConfig = (AppConfig) x.get("appConfig");
          appConfig = (AppConfig) appConfig.fclone();

          Theme theme = ((Themes) x.get("themes")).findTheme(x);
          rtn = rtn.put("theme", theme);

          AppConfig themeAppConfig = theme.getAppConfig();
          if ( themeAppConfig != null ) {
            appConfig.copyFrom(themeAppConfig);
          }
          appConfig = appConfig.configure(x, null);

          rtn = rtn.put("appConfig", appConfig);

          return rtn;
        }

        // Validate
        validate(x);

        DAO localUserDAO  = (DAO) x.get("localUserDAO");
        DAO localGroupDAO = (DAO) x.get("localGroupDAO");
        AuthService auth  = (AuthService) x.get("auth");
        User user         = (User) localUserDAO.find(getUserId());
        User agent        = (User) localUserDAO.find(getAgentId());
        Object[] prefix   = agent == null
          ? new Object[] { String.format("%s (%d)", user.toSummary(), user.getId()) }
          : new Object[] { String.format("%s (%d) acting as %s (%d)", agent.toSummary(), agent.getId(), user.toSummary(), user.getId()) };

        Subject subject = new Subject();
        subject.setUser(agent);
        subject.setUser(user);
        rtn = rtn
          .put("subject", subject)
          .put("spid", user.getSpid())
          .put("logger", new PrefixLogger(prefix, (Logger) x.get("logger")))
          .put("twoFactorSuccess", getContext().get("twoFactorSuccess"))
          .put(CachingAuthService.CACHE_KEY, getContext().get(CachingAuthService.CACHE_KEY));

        // We need to do this after the user and agent have been put since
        // 'getCurrentGroup' depends on them being in the context.
        Group group = auth.getCurrentGroup(rtn);

        if ( group != null ) {
          rtn = rtn
            .put("group", group)
            .put("appConfig", group.getAppConfig(rtn));
        }
        rtn = rtn.put("theme", ((Themes) x.get("themes")).findTheme(rtn));

        return rtn;
      `
    },
    {
      name: 'validate',
      args: [
        { name: 'x', type: 'Context' }
      ],
      javaCode: `
        if ( getUserId() < 0 ) {
          throw new IllegalStateException("User id is invalid.");
        }

        if ( getAgentId() < 0 ) {
          throw new IllegalStateException("Agent id is invalid.");
        }

        if ( getUserId() > 0 ) {
          checkUserEnabled(x, getUserId());
        }

        if ( getAgentId() > 0 ) {
          checkUserEnabled(x, getAgentId());
        }
      `
    },
    {
      name: 'checkUserEnabled',
      args: [
        { name: 'x', type: 'Context' },
        { name: 'userId', type: 'Long' }
      ],
      javaCode: `
        User user = (User) ((DAO) x.get("localUserDAO")).find(userId);

        if ( user == null
         || (user instanceof LifecycleAware && ((LifecycleAware)user).getLifecycleState() != LifecycleState.ACTIVE)
       ) {
          ((Logger) x.get("logger")).warning("Session", "User not found.", userId);
          throw new RuntimeException(String.format("User with id '%d' not found.", userId));
        }

        if ( ! user.getEnabled() ) {
          throw new RuntimeException(String.format("The user with id '%d' has been disabled.", userId));
        }
      `
    }
  ]
});
<|endoftext|>