<commit_before>'use strict';
var Geometry = require('famous-webgl-geometries');

/**
 * The Mesh class is responsible for providing the API for how
 * a RenderNode will interact with the WebGL API by adding
 * a set of commands to the renderer.
 *
 * @class Mesh
 * @constructor
 * @renderable
 * @param {LocalDispatch} dispatch LocalDispatch to be retrieved
 * @param {object} Options Optional params for configuring Mesh
 */
function Mesh (node, options) {
    this._node = node;
    this._changeQueue = [];
    this._initialized = false;
    this._requestingUpdate = false;
    this._inDraw = false;
    this.value = {
        drawOptions: {},
        color: null,
        expressions: {},
        geometry: null,
        flatShading: null,
        glossiness: [null, null],
        positionOffset: null,
        normals: null,
    };

    if (options) this.setDrawOptions(options);
    this._id = node.addComponent(this);
}

/**
 * Pass custom options to Mesh, such as a 3 element map
 * which displaces the position of each vertex in world space.
 *
 * @method setDrawOptions
 * @chainable
 *
 * @param {Object} Options
 * @chainable
 */
Mesh.prototype.setDrawOptions = function setOptions (options) {
    this._changeQueue.push('GL_SET_DRAW_OPTIONS');
    this._changeQueue.push(options);
    return this;
};

/**
 * Get the mesh's custom options.
 *
 * @method getDrawOptions
 * @returns {Object} Options
 */
Mesh.prototype.getDrawOptions = function getDrawOptions () {
    return this.value.drawOptions;
};

/**
 * Set the geometry of a mesh.
 *
 * @method setGeometry
 * @chainable
 *
 * @param {Geometry} geometry instance to be associated with the mesh
 * @param {Object} Options Various configurations for geometries.
 * @chainable
 */
Mesh.prototype.setGeometry = function setGeometry (geometry, options) {
    if (typeof geometry === 'string') {
        if (!Geometry[geometry]) throw 'Invalid geometry: "' + geometry + '".';
        else geometry = new Geometry[geometry](options);
    }

    if (this.value.geometry !== geometry || this._inDraw) {
        if (this._initialized) {
            this._changeQueue.push('GL_SET_GEOMETRY');
            this._changeQueue.push(geometry.id);
            this._changeQueue.push(geometry.spec.type);
            this._changeQueue.push(geometry.spec.dynamic);
        }
        this._requestUpdate();
        this.value.geometry = geometry;
    }

    return this;
};

/**
 * Get the geometry of a mesh.
 *
 * @method getGeometry
 * @returns {Geometry} geometry Geometry of mesh
 */
Mesh.prototype.getGeometry = function getGeometry () {
    return this.value.geometry;
};

/**
* Changes the color of Mesh, passing either a material expression or
* color using the 'Color' utility component.
*
* @method setBaseColor
* @param {Object|Color} Material, image, vec3, or Color instance
* @chainable
*/
var baseColorRGBA = [0, 0, 0, 1];
Mesh.prototype.setBaseColor = function setBaseColor (color) {
    var uniformValue;

    if (color._compile) {
        this.value.color = null;
        this.value.expressions.baseColor = color;
        uniformValue = color._compile();
    }
    else if (color.getNormalizedRGB) {
        this.value.expressions.baseColor = null;
        this.value.color = color;
        var color = color.getNormalizedRGB();
        baseColorRGBA[0] = color[0];
        baseColorRGBA[1] = color[1];
        baseColorRGBA[2] = color[2];

        uniformValue = baseColorRGBA;
    }

    if (this._initialized) {

        // If a material expression

        if (color._compile) {
            this._changeQueue.push('MATERIAL_INPUT');
        }

        // If a color component

        else if (color.getNormalizedRGB) {
            this._changeQueue.push('GL_UNIFORMS');
        }

        this._changeQueue.push('baseColor');
        this._changeQueue.push(uniformValue);
    }

    this._requestUpdate();

    return this;
};

/**
 * Returns either the material expression or the color instance of Mesh.
 *
 * @method getBaseColor
 * @returns {MaterialExpress|Color}
 */
Mesh.prototype.getBaseColor = function getBaseColor () {
    return this.value.expressions.baseColor || this.value.color;
};

/**
 * Change whether the Mesh is affected by light. Default is true.
 *
 * @method setFlatShading
 * @param {boolean} Boolean
 * @chainable
 */
Mesh.prototype.setFlatShading = function setFlatShading (bool) {
    if (this._inDraw || this.value.flatShading !== bool) {
        this.value.flatShading = bool;
        if (this._initialized) {
            this._changeQueue.push('GL_UNIFORMS');
            this._changeQueue.push('u_FlatShading');
            this._changeQueue.push(bool ? 1 : 0);
        }
        this._requestUpdate();
    }

    return this;
};

/**
 * Returns a boolean for whether Mesh is affected by light.
 *
 * @method getFlatShading
 * @returns {Boolean} Boolean
 */
Mesh.prototype.getFlatShading = function getFlatShading () {
    return this.value.flatShading;
};

/**
 * Defines a 3-element map which is used to provide significant physical
 * detail to the surface by perturbing the facing direction of each individual
 * pixel.
 *
 * @method normal
 * @chainable
 *
 * @param {Object|Array} Material, Image or vec3
 * @return {Element} current Mesh
 */
Mesh.prototype.setNormals = function setNormals (materialExpression) {
    if (materialExpression._compile) {
        this.value.expressions.normals = materialExpression;
        materialExpression = materialExpression._compile();
    }

    if (this._initialized) {
        this._changeQueue.push(materialExpression._compile ? 'MATERIAL_INPUT' : 'UNIFORM_INPUT');
        this._changeQueue.push('normal');
        this._changeQueue.push(materialExpression);
    }

    this._requestUpdate();

    return this;
};

/**
 * Returns the Normals expression of Mesh
 *
 * @method getNormals
 * @returns The normals expression for Mesh
 */
Mesh.prototype.getNormals = function getNormals (materialExpression) {
    return this.value.expressions.normals;
};

/**
 * Defines the glossiness of the mesh from either a material expression or a
 * scalar value
 *
 * @method setGlossiness
 * @param {MaterialExpression|Color} Accepts either a material expression or Color instance
 * @param {Number} Optional value for changing the strength of the glossiness
 * @chainable
 */
Mesh.prototype.setGlossiness = function setGlossiness(glossiness, strength) {
    var glossiness;

    if (glossiness._compile) {
        this.value.glossiness = [null, null];
        this.value.expressions.glossiness = glossiness;
        glossiness = glossiness._compile();
    }
    else if (glossiness.getNormalizedRGB) {
        this.value.expressions.glossiness = null;
        this.value.glossiness = [glossiness, strength || 20];
        glossiness = glossiness ? glossiness.getNormalizedRGB() : [0, 0, 0];
        glossiness.push(strength);
    }

    if (this._initialized) {
        this._changeQueue.push(glossiness._compile ? 'MATERIAL_INPUT' : 'GL_UNIFORMS');
        this._changeQueue.push('glossiness');
        this._changeQueue.push(glossiness);
    }

    this._requestUpdate();
    return this;
};

/**
 * Returns material expression or scalar value for glossiness.
 *
 * @method getGlossiness
 * @returns {MaterialExpress|Number}
 */
Mesh.prototype.getGlossiness = function getGlossiness() {
    return this.value.expressions.glossiness || this.value.glossiness;
};

/**
 * Defines 3 element map which displaces the position of each vertex in world
 * space.
 *
 * @method setPositionOffset
 * @chainable
 *
 * @param {MaterialExpression|Array}
 * @param {Object} Optional tweening parameter
 * @param {Function} Callback
 * @chainable
 */
Mesh.prototype.setPositionOffset = function positionOffset(materialExpression) {
    var uniformValue;

    if (materialExpression._compile) {
        this.value.expressions.positionOffset = materialExpression;
        uniformValue = materialExpression._compile();
    }
    else {
        this.value.expressions.positionOffset = null;
        this.value.positionOffset = materialExpression;
        uniformValue = this.value.positionOffset;
    }

    if (this._initialized) {
        this._changeQueue.push(materialExpression._compile ? 'MATERIAL_INPUT' : 'GL_UNIFORMS');
        this._changeQueue.push('positionOffset');
        this._changeQueue.push(uniformValue);
    }

    this._requestUpdate();

    return this;
};

/**
 * Returns position offset.
 *
 * @method getPositionOffset
 * @returns {MaterialExpress|Number}
 */
Mesh.prototype.getPositionOffset = function getPositionOffset (materialExpression) {
    return this.value.expressions.positionOffset || this.value.positionOffset;
};

/**
 * Get the mesh's custom options.
 *
 * @method getDrawOptions
 * @returns {Object} Options
 */
Mesh.prototype.getMaterialExpressions = function getMaterialExpressions () {
    return this.value.expressions;
};

Mesh.prototype.getValue = function getValue () {
    return this.value;
};

Mesh.prototype._pushInvalidations = function pushInvalidations (expressionName) {
    var uniformKey;
    var expression = this.value.expressions[expressionName];
    if (expression) {
        var i = expression.invalidations.length;
        while (i--) {
            uniformKey = expression.invalidations.pop();
            this._node.sendDrawCommand('GL_UNIFORMS');
            this._node.sendDrawCommand(uniformKey);
            this._node.sendDrawCommand(expression.uniforms[uniformKey]);
        }
    }
};

/**
* Sends draw commands to the renderer
*
* @private
* @method onUpdate
*/
Mesh.prototype.onUpdate = function onUpdate() {
    var node = this._node;
    var queue = this._changeQueue;

    if (node) {
        node.sendDrawCommand('WITH');
        node.sendDrawCommand(node.getLocation());

        if (this.value.geometry) {
        i = this.value.geometry.spec.invalidations.length;
            while (i--) {
                var bufferIndex = this.value.geometry.spec.invalidations.pop();
                node.sendDrawCommand('GL_BUFFER_DATA');
                node.sendDrawCommand(this.value.geometry.id);
                node.sendDrawCommand(this.value.geometry.spec.bufferNames[i]);
                node.sendDrawCommand(this.value.geometry.spec.bufferValues[i]);
                node.sendDrawCommand(this.value.geometry.spec.bufferSpacings[i]);
            }
        }

        // If any invalidations exist, push them into the queue
        if (this.value.color && this.value.color.isActive()) {
            this._node.sendDrawCommand('GL_UNIFORMS');
            this._node.sendDrawCommand('baseColor');
            this._node.sendDrawCommand(this.value.color.getNormalizedRGB());
            this._node.requestUpdateOnNextTick(this._id);
        }
        if (this.value.glossiness[0] && this.value.glossiness[0].isActive()) {
            this._node.sendDrawCommand('GL_UNIFORMS');
            this._node.sendDrawCommand('glossiness');
            var glossiness = this.value.glossiness[0].getNormalizedRGB();
            glossiness.push(this.value.glossiness[1]);
            this._node.sendDrawCommand(glossiness);
            this._node.requestUpdateOnNextTick(this._id);
        }
        else {
            this._requestingUpdate = false;
        }

        // If any invalidations exist, push them into the queue
        this._pushInvalidations('baseColor');
        this._pushInvalidations('positionOffset');

        for (var i = 0; i < queue.length; i++) {
            node.sendDrawCommand(queue[i]);
        }

        queue.length = 0;
    }

};

Mesh.prototype.onMount = function onMount (node, id) {
    this._node = node;
    this._id = id;

    this.draw();
};

Mesh.prototype.onDismount = function onDismount () {
    this._initialized = false;
    this._changeQueue.push('GL_REMOVE_MESH');

    this._requestUpdate();
};

Mesh.prototype.onShow = function onShow () {
    this._changeQueue.push('GL_SHOW_MESH');
};

Mesh.prototype.onHide = function onHide () {
    this._changeQueue.push('GL_HIDE_MESH');
};

/**
 * Receives transform change updates from the scene graph.
 *
 * @private
 */
Mesh.prototype.onTransformChange = function onTransformChange (transform) {
    if (this._initialized) {
        this._changeQueue.push('GL_UNIFORMS');
        this._changeQueue.push('transform');
        this._changeQueue.push(transform);
    }

    this._requestUpdate();
};

/**
 * Receives size change updates from the scene graph.
 *
 * @private
 */
Mesh.prototype.onSizeChange = function onSizeChange (size) {
    if (this._initialized) {
        this._changeQueue.push('GL_UNIFORMS');
        this._changeQueue.push('size');
        this._changeQueue.push(size);
    }

    this._requestUpdate();
};

/**
 * Receives opacity change updates from the scene graph.
 *
 * @private
 */
Mesh.prototype.onOpacityChange = function onOpacityChange (opacity) {
    if (this._initialized) {
        this._changeQueue.push('GL_UNIFORMS');
        this._changeQueue.push('opacity');
        this._changeQueue.push(opacity);
    }

    this._requestUpdate();
};

Mesh.prototype.onAddUIEvent = function onAddUIEvent (UIEvent, methods, properties) {
    //TODO
};

Mesh.prototype._requestUpdate = function _requestUpdate () {
    if (!this._requestingUpdate) {
        this._node.requestUpdate(this._id);
        this._requestingUpdate = true;
    }
};

Mesh.prototype.init = function init () {
    this._initialized = true;
    this.onTransformChange(this._node.getTransform());
    this.onSizeChange(this._node.getSize());
    this.onOpacityChange(this._node.getOpacity());
    this._requestUpdate();
};

Mesh.prototype.draw = function draw () {
    var key;
    var i;
    var len;

    this._inDraw = true;

    this.init();

    var value = this.getValue();
    if (value.geometry != null) this.setGeometry(value.geometry);
    if (value.color != null) this.setBaseColor(value.color);
    if (value.drawOptions != null) this.setDrawOptions(value.drawOptions);
    if (value.flatShading != null) this.setFlatShading(value.flatShading);
    if (value.expressions.normals != null) this.setNormals(value.expressions.normals);
    if (value.expressions.baseColor != null) this.setBaseColor(value.expressions.baseColor);
    if (value.expressions.glossiness != null) this.setGlossiness(value.expressions.glossiness);
    if (value.expressions.positionOffset != null) this.setPositionOffset(value.expressions.positionOffset);

    this._inDraw = false;
};

module.exports = Mesh;
<commit_msg>fix: fixing bug with baseColor and color component input<commit_after>'use strict';
var Geometry = require('famous-webgl-geometries');

/**
 * The Mesh class is responsible for providing the API for how
 * a RenderNode will interact with the WebGL API by adding
 * a set of commands to the renderer.
 *
 * @class Mesh
 * @constructor
 * @renderable
 * @param {LocalDispatch} dispatch LocalDispatch to be retrieved
 * @param {object} Options Optional params for configuring Mesh
 */
function Mesh (node, options) {
    this._node = node;
    this._changeQueue = [];
    this._initialized = false;
    this._requestingUpdate = false;
    this._inDraw = false;
    this.value = {
        drawOptions: {},
        color: null,
        expressions: {},
        geometry: null,
        flatShading: null,
        glossiness: new Array(2),
        baseColor: new Array(4),
        positionOffset: null,
        normals: null,
    };

    if (options) this.setDrawOptions(options);
    this._id = node.addComponent(this);
}

/**
 * Pass custom options to Mesh, such as a 3 element map
 * which displaces the position of each vertex in world space.
 *
 * @method setDrawOptions
 * @chainable
 *
 * @param {Object} Options
 * @chainable
 */
Mesh.prototype.setDrawOptions = function setOptions (options) {
    this._changeQueue.push('GL_SET_DRAW_OPTIONS');
    this._changeQueue.push(options);
    return this;
};

/**
 * Get the mesh's custom options.
 *
 * @method getDrawOptions
 * @returns {Object} Options
 */
Mesh.prototype.getDrawOptions = function getDrawOptions () {
    return this.value.drawOptions;
};

/**
 * Set the geometry of a mesh.
 *
 * @method setGeometry
 * @chainable
 *
 * @param {Geometry} geometry instance to be associated with the mesh
 * @param {Object} Options Various configurations for geometries.
 * @chainable
 */
Mesh.prototype.setGeometry = function setGeometry (geometry, options) {
    if (typeof geometry === 'string') {
        if (!Geometry[geometry]) throw 'Invalid geometry: "' + geometry + '".';
        else geometry = new Geometry[geometry](options);
    }

    if (this.value.geometry !== geometry || this._inDraw) {
        if (this._initialized) {
            this._changeQueue.push('GL_SET_GEOMETRY');
            this._changeQueue.push(geometry.id);
            this._changeQueue.push(geometry.spec.type);
            this._changeQueue.push(geometry.spec.dynamic);
        }
        this._requestUpdate();
        this.value.geometry = geometry;
    }

    return this;
};

/**
 * Get the geometry of a mesh.
 *
 * @method getGeometry
 * @returns {Geometry} geometry Geometry of mesh
 */
Mesh.prototype.getGeometry = function getGeometry () {
    return this.value.geometry;
};

/**
* Changes the color of Mesh, passing either a material expression or
* color using the 'Color' utility component.
*
* @method setBaseColor
* @param {Object|Color} Material, image, vec3, or Color instance
* @chainable
*/
Mesh.prototype.setBaseColor = function setBaseColor (color) {
    var uniformValue;

    if (color._compile) {
        this.value.color = null;
        this.value.expressions.baseColor = color;
        uniformValue = color._compile();
    }
    else if (color.getNormalizedRGB) {
        this.value.expressions.baseColor = null;
        this.value.color = color;
        var value = color.getNormalizedRGB();
        this.value.baseColor[0] = value[0];
        this.value.baseColor[1] = value[1];
        this.value.baseColor[2] = value[2];

        uniformValue = this.value.baseColor;
    }
    if (this._initialized) {

        // If a material expression

        if (color._compile) {
            this._changeQueue.push('MATERIAL_INPUT');
        }

        // If a color component

        else if (color.getNormalizedRGB) {
            this._changeQueue.push('GL_UNIFORMS');
        }

        this._changeQueue.push('baseColor');
        this._changeQueue.push(uniformValue);
    }

    this._requestUpdate();

    return this;
};

/**
 * Returns either the material expression or the color instance of Mesh.
 *
 * @method getBaseColor
 * @returns {MaterialExpress|Color}
 */
Mesh.prototype.getBaseColor = function getBaseColor () {
    return this.value.expressions.baseColor || this.value.color;
};

/**
 * Change whether the Mesh is affected by light. Default is true.
 *
 * @method setFlatShading
 * @param {boolean} Boolean
 * @chainable
 */
Mesh.prototype.setFlatShading = function setFlatShading (bool) {
    if (this._inDraw || this.value.flatShading !== bool) {
        this.value.flatShading = bool;
        if (this._initialized) {
            this._changeQueue.push('GL_UNIFORMS');
            this._changeQueue.push('u_FlatShading');
            this._changeQueue.push(bool ? 1 : 0);
        }
        this._requestUpdate();
    }

    return this;
};

/**
 * Returns a boolean for whether Mesh is affected by light.
 *
 * @method getFlatShading
 * @returns {Boolean} Boolean
 */
Mesh.prototype.getFlatShading = function getFlatShading () {
    return this.value.flatShading;
};

/**
 * Defines a 3-element map which is used to provide significant physical
 * detail to the surface by perturbing the facing direction of each individual
 * pixel.
 *
 * @method normal
 * @chainable
 *
 * @param {Object|Array} Material, Image or vec3
 * @return {Element} current Mesh
 */
Mesh.prototype.setNormals = function setNormals (materialExpression) {
    if (materialExpression._compile) {
        this.value.expressions.normals = materialExpression;
        materialExpression = materialExpression._compile();
    }

    if (this._initialized) {
        this._changeQueue.push(materialExpression._compile ? 'MATERIAL_INPUT' : 'UNIFORM_INPUT');
        this._changeQueue.push('normal');
        this._changeQueue.push(materialExpression);
    }

    this._requestUpdate();

    return this;
};

/**
 * Returns the Normals expression of Mesh
 *
 * @method getNormals
 * @returns The normals expression for Mesh
 */
Mesh.prototype.getNormals = function getNormals (materialExpression) {
    return this.value.expressions.normals;
};

/**
 * Defines the glossiness of the mesh from either a material expression or a
 * scalar value
 *
 * @method setGlossiness
 * @param {MaterialExpression|Color} Accepts either a material expression or Color instance
 * @param {Number} Optional value for changing the strength of the glossiness
 * @chainable
 */
Mesh.prototype.setGlossiness = function setGlossiness(glossiness, strength) {
    var glossiness;

    if (glossiness._compile) {
        this.value.glossiness = [null, null];
        this.value.expressions.glossiness = glossiness;
        glossiness = glossiness._compile();
    }
    else if (glossiness.getNormalizedRGB) {
        this.value.expressions.glossiness = null;
        this.value.glossiness = [glossiness, strength || 20];
        glossiness = glossiness ? glossiness.getNormalizedRGB() : [0, 0, 0];
        glossiness.push(strength);
    }

    if (this._initialized) {
        this._changeQueue.push(glossiness._compile ? 'MATERIAL_INPUT' : 'GL_UNIFORMS');
        this._changeQueue.push('glossiness');
        this._changeQueue.push(glossiness);
    }

    this._requestUpdate();
    return this;
};

/**
 * Returns material expression or scalar value for glossiness.
 *
 * @method getGlossiness
 * @returns {MaterialExpress|Number}
 */
Mesh.prototype.getGlossiness = function getGlossiness() {
    return this.value.expressions.glossiness || this.value.glossiness;
};

/**
 * Defines 3 element map which displaces the position of each vertex in world
 * space.
 *
 * @method setPositionOffset
 * @chainable
 *
 * @param {MaterialExpression|Array}
 * @param {Object} Optional tweening parameter
 * @param {Function} Callback
 * @chainable
 */
Mesh.prototype.setPositionOffset = function positionOffset(materialExpression) {
    var uniformValue;

    if (materialExpression._compile) {
        this.value.expressions.positionOffset = materialExpression;
        uniformValue = materialExpression._compile();
    }
    else {
        this.value.expressions.positionOffset = null;
        this.value.positionOffset = materialExpression;
        uniformValue = this.value.positionOffset;
    }

    if (this._initialized) {
        this._changeQueue.push(materialExpression._compile ? 'MATERIAL_INPUT' : 'GL_UNIFORMS');
        this._changeQueue.push('positionOffset');
        this._changeQueue.push(uniformValue);
    }

    this._requestUpdate();

    return this;
};

/**
 * Returns position offset.
 *
 * @method getPositionOffset
 * @returns {MaterialExpress|Number}
 */
Mesh.prototype.getPositionOffset = function getPositionOffset (materialExpression) {
    return this.value.expressions.positionOffset || this.value.positionOffset;
};

/**
 * Get the mesh's custom options.
 *
 * @method getDrawOptions
 * @returns {Object} Options
 */
Mesh.prototype.getMaterialExpressions = function getMaterialExpressions () {
    return this.value.expressions;
};

Mesh.prototype.getValue = function getValue () {
    return this.value;
};

Mesh.prototype._pushInvalidations = function pushInvalidations (expressionName) {
    var uniformKey;
    var expression = this.value.expressions[expressionName];
    if (expression) {
        var i = expression.invalidations.length;
        while (i--) {
            uniformKey = expression.invalidations.pop();
            this._node.sendDrawCommand('GL_UNIFORMS');
            this._node.sendDrawCommand(uniformKey);
            this._node.sendDrawCommand(expression.uniforms[uniformKey]);
        }
    }
};

/**
* Sends draw commands to the renderer
*
* @private
* @method onUpdate
*/
Mesh.prototype.onUpdate = function onUpdate() {
    var node = this._node;
    var queue = this._changeQueue;

    if (node) {
        node.sendDrawCommand('WITH');
        node.sendDrawCommand(node.getLocation());

        if (this.value.geometry) {
        i = this.value.geometry.spec.invalidations.length;
            while (i--) {
                var bufferIndex = this.value.geometry.spec.invalidations.pop();
                node.sendDrawCommand('GL_BUFFER_DATA');
                node.sendDrawCommand(this.value.geometry.id);
                node.sendDrawCommand(this.value.geometry.spec.bufferNames[i]);
                node.sendDrawCommand(this.value.geometry.spec.bufferValues[i]);
                node.sendDrawCommand(this.value.geometry.spec.bufferSpacings[i]);
            }
        }

        // If any invalidations exist, push them into the queue
        if (this.value.color && this.value.color.isActive()) {
            this._node.sendDrawCommand('GL_UNIFORMS');
            this._node.sendDrawCommand('baseColor');
            this._node.sendDrawCommand(this.value.color.getNormalizedRGB());
            this._node.requestUpdateOnNextTick(this._id);
        }
        if (this.value.glossiness[0] && this.value.glossiness[0].isActive()) {
            this._node.sendDrawCommand('GL_UNIFORMS');
            this._node.sendDrawCommand('glossiness');
            var glossiness = this.value.glossiness[0].getNormalizedRGB();
            glossiness.push(this.value.glossiness[1]);
            this._node.sendDrawCommand(glossiness);
            this._node.requestUpdateOnNextTick(this._id);
        }
        else {
            this._requestingUpdate = false;
        }

        // If any invalidations exist, push them into the queue
        this._pushInvalidations('baseColor');
        this._pushInvalidations('positionOffset');

        for (var i = 0; i < queue.length; i++) {
            node.sendDrawCommand(queue[i]);
        }

        queue.length = 0;
    }

};

Mesh.prototype.onMount = function onMount (node, id) {
    this._node = node;
    this._id = id;

    this.draw();
};

Mesh.prototype.onDismount = function onDismount () {
    this._initialized = false;
    this._changeQueue.push('GL_REMOVE_MESH');

    this._requestUpdate();
};

Mesh.prototype.onShow = function onShow () {
    this._changeQueue.push('GL_SHOW_MESH');
};

Mesh.prototype.onHide = function onHide () {
    this._changeQueue.push('GL_HIDE_MESH');
};

/**
 * Receives transform change updates from the scene graph.
 *
 * @private
 */
Mesh.prototype.onTransformChange = function onTransformChange (transform) {
    if (this._initialized) {
        this._changeQueue.push('GL_UNIFORMS');
        this._changeQueue.push('transform');
        this._changeQueue.push(transform);
    }

    this._requestUpdate();
};

/**
 * Receives size change updates from the scene graph.
 *
 * @private
 */
Mesh.prototype.onSizeChange = function onSizeChange (size) {
    if (this._initialized) {
        this._changeQueue.push('GL_UNIFORMS');
        this._changeQueue.push('size');
        this._changeQueue.push(size);
    }

    this._requestUpdate();
};

/**
 * Receives opacity change updates from the scene graph.
 *
 * @private
 */
Mesh.prototype.onOpacityChange = function onOpacityChange (opacity) {
    if (this._initialized) {
        this._changeQueue.push('GL_UNIFORMS');
        this._changeQueue.push('opacity');
        this._changeQueue.push(opacity);
    }

    this._requestUpdate();
};

Mesh.prototype.onAddUIEvent = function onAddUIEvent (UIEvent, methods, properties) {
    //TODO
};

Mesh.prototype._requestUpdate = function _requestUpdate () {
    if (!this._requestingUpdate) {
        this._node.requestUpdate(this._id);
        this._requestingUpdate = true;
    }
};

Mesh.prototype.init = function init () {
    this._initialized = true;
    this.onTransformChange(this._node.getTransform());
    this.onSizeChange(this._node.getSize());
    this.onOpacityChange(this._node.getOpacity());
    this._requestUpdate();
};

Mesh.prototype.draw = function draw () {
    var key;
    var i;
    var len;

    this._inDraw = true;

    this.init();

    var value = this.getValue();
    if (value.geometry != null) this.setGeometry(value.geometry);
    if (value.color != null) this.setBaseColor(value.color);
    if (value.drawOptions != null) this.setDrawOptions(value.drawOptions);
    if (value.flatShading != null) this.setFlatShading(value.flatShading);
    if (value.expressions.normals != null) this.setNormals(value.expressions.normals);
    if (value.expressions.baseColor != null) this.setBaseColor(value.expressions.baseColor);
    if (value.expressions.glossiness != null) this.setGlossiness(value.expressions.glossiness);
    if (value.expressions.positionOffset != null) this.setPositionOffset(value.expressions.positionOffset);

    this._inDraw = false;
};

module.exports = Mesh;
<|endoftext|>


<commit_before>"use strict";

const u = require("./UtilFunctions.js"),
	ID = require("./ID.js"),
	Finger = require("./Finger.js");

class Node{
	constructor(chord){
		//TODO
		this.id = chord.id;
		this.chord = chord;
		this.finger = [];
		this.predecessor = this;

		//Create Finger Table
		for(var i=0; i<chord.config.idWidth; i++)
			this.finger[i] = new Finger(this, i);
	}

	//NOTE:
	//These show the algorithms, but they do not account for RPC, promises, callbacks etc...
	//Redo these as time goes on.

	//Promise updated
	getSuccessor(){
		return new Promise((resolve, reject) => {
			resolve(this.finger[0].node)
		});
	}

	//Promise updated
	setSuccessor(val){
		return new Promise((resolve, reject) => {
			this.finger[0].node = val;
			resolve(val)
		});
	}

	//Promise updated
	getPredecessor(){
		return new Promise((resolve, reject) => {
			resolve(this.predecessor)
		});
	}

	//Promise updated
	setPredecessor(val){
		return new Promise((resolve, reject) => {
			this.predecessor = val;
			resolve(val);
		});
	}

	//Promise updated
	initOn(knownNode){
		this.chord.server.connect = true;
		if(knownNode){
			return this.initialiseFingerTable(knownNode)
				.then( () => {
					return this.updateOthers();
				} )
				.then( () => {
					this.chord.server.connect = false;
				} );
			//move keys from successor to self as well.
		} else {
			for(var i=0; i<chord.config.idwidth; i++)
				this.finger[i].node = this;
			this.predecessor = this;
			this.chord.server.connect = false;
			return Promise.resolve();
		}

	}

	//Promise updated
	initialiseFingerTable(knownNode){
		return knownNode.findSuccessor(this.finger[0].start)
			.then(
				succ => {return this.setSuccessor(succ);}
			)
			.then(
				succ => {return succ.getPredecessor();}
			)
			.then(
				pred => {return this.setPredecessor(pred);}
			)
			.then(
				() => {
					let proms = [];

					for(var i=0; i<this.finger.length-1; i++) {
						if(ID.inRightOpenBound(this.finger[i+1].start, this.id, this.finger[i].node.id))
							this.finger[i+1].node = this.finger[i].node;
						else {
							(i=>{
								proms.push(
									knownNode.findSuccessor(this.finger[i+1].start)
										.then(
											succ => {
												this.finger[i+1].node = succ;
											}
										)
								)
							})(i)
						}
					}

					return Promise.all(proms);
				}
			);
	}

	//Promise updated
	updateOthers(){
		//Inform other nodes about our existence.
		let proms = [];

		for (var i = 0; i < this.finger.length; i++) {
			//find last node p whose ith finger might be n
			(i=>{proms.push(
				this.findPredecessor(this.id.subtract(ID.powerOfTwoBuffer(i)))
					.then(
						p => {return p.updateFingerTable(this, i)}
					)
				)
			})(i);
		}

		return Promise.all(proms);
	}

	//Promise updated
	updateFingerTable(foreignNode, index){
		//Update the finger of some remote node
		if(ID.inRightOpenBound(foreignNode.id, this.id, this.finger[index].node.id)){
			this.finger[index].node = foreignNode;
			return this.getPredecessor()
				.then(
					res => {return res.updateFingerTable(foreignNode, index)}
				)
		} else {
			return Promise.resolve();
		}

	}

	//Promise updated
	closestPrecedingFinger(id){
		return new Promise( resolve => {
			for (var i = this.finger.length - 1; i >= 0; i--) {
				if(ID.inOpenBound(this.finger[i].node.id, this.id, id)){
					resolve(this.finger[i].node);
					break;
				}
			}
			resolve(this);
		} );
	}

	//Promise updated
	findSuccessor(id){
		return this.findPredecessor(id)
			.then( nPrime => { return nPrime.getSuccessor() } )
	}

	//Promise updated
	findPredecessor(id){
		return new Promise( (resolve, reject) => {
			let nPrime = this;

			let condUpd = () => {
				nPrime.getSuccessor()
					.then(
						succ => {
							if (!ID.inLeftOpenBound(id, nPrime.id, succ.id)) {
								nPrime.closestPrecedingFinger()
									.then(
										newPrime => {
											nPrime = newPrime;
											condUpd();
										},
										rea => reject(rea)
									)
							} else {
								resolve(nPrime);
							}
						},
						rea => reject(rea)
					)
				};

			condUpd();
		} )
	}

	//Stabilization methods
	//Promise updated
	stableJoin(knownNode){
		return this.setPredecessor(null)
			.then(
				res => {
					this.setSuccessor(knownNode);
					this.chord.server.connect = true;
				}
			);
	}

	//periodically verify n's immediate successor
	//and tell the successor about n.
	//Promise updated
	stabilize(){
		let oSucc;

		return this.getSuccessor()
			.then(succ => {oSucc = succ; return succ.getPredecessor()})
			.then(pred => {
				if(ID.inOpenBound(pred.id, this.id, oSucc.id))
					return this.setSuccessor(pred);
				else
					return Promise.resolve();
			})
			.then(
				res => {return oSucc.notify(this);}
			)

	}

	//Promise updated
	notify(nPrime){
		if(this.predecessor === null || ID.inOpenBound(nPrime.id, this.predecessor.id, this.id))
			this.predecessor = nPrime;
		return Promise.resolve();
	}

	//Promise updated
	fixFingers(){
		let i = Math.floor(Math.random() * (this.finger.length-1) + 1);
		this.finger[i].node = this.findSuccessor(this.finger[i].start);

		return this.findSuccessor(this.finger[i].start)
			.then(
				succ => this.finger[i].node = succ
			)
	}

	//Custom

	message(id, msg){
		//TODO

		// debugger;

		console.log(`Received message at the local node for ${id}: ${msg}
			I am ${this.id}`);

		if(this.chord.server.connect && ID.compare(id, this.id)!== 0) {
			this.chord.server.node.message(id, msg);
		} else if (ID.compare(this.id, id)===0 || ID.inLeftOpenBound(id, this.predecessor.id, this.id)){
			//Pass to appropriate handler - this is our message.
			this.chord.registry.parse(msg);
		} else {
			//Pass along the chain to a responsible node.
			this.closestPrecedingFinger(id).message(id, msg);
		}
	}

	// Item management

	lookup(id){
		//TODO
	}

	add(id, value){
		//TODO
	}
}

module.exports = Node;
<commit_msg>Fixing findPredecessor call, message to use promises.<commit_after>"use strict";

const u = require("./UtilFunctions.js"),
	ID = require("./ID.js"),
	Finger = require("./Finger.js");

class Node{
	constructor(chord){
		//TODO
		this.id = chord.id;
		this.chord = chord;
		this.finger = [];
		this.predecessor = this;

		//Create Finger Table
		for(var i=0; i<chord.config.idWidth; i++)
			this.finger[i] = new Finger(this, i);
	}

	//NOTE:
	//These show the algorithms, but they do not account for RPC, promises, callbacks etc...
	//Redo these as time goes on.

	//Promise updated
	getSuccessor(){
		return new Promise((resolve, reject) => {
			resolve(this.finger[0].node)
		});
	}

	//Promise updated
	setSuccessor(val){
		return new Promise((resolve, reject) => {
			this.finger[0].node = val;
			resolve(val)
		});
	}

	//Promise updated
	getPredecessor(){
		return new Promise((resolve, reject) => {
			resolve(this.predecessor)
		});
	}

	//Promise updated
	setPredecessor(val){
		return new Promise((resolve, reject) => {
			this.predecessor = val;
			resolve(val);
		});
	}

	//Promise updated
	initOn(knownNode){
		this.chord.server.connect = true;
		if(knownNode){
			return this.initialiseFingerTable(knownNode)
				.then( () => {
					return this.updateOthers();
				} )
				.then( () => {
					this.chord.server.connect = false;
				} );
			//move keys from successor to self as well.
		} else {
			for(var i=0; i<chord.config.idwidth; i++)
				this.finger[i].node = this;
			this.predecessor = this;
			this.chord.server.connect = false;
			return Promise.resolve();
		}

	}

	//Promise updated
	initialiseFingerTable(knownNode){
		return knownNode.findSuccessor(this.finger[0].start)
			.then(
				succ => {return this.setSuccessor(succ);}
			)
			.then(
				succ => {return succ.getPredecessor();}
			)
			.then(
				pred => {return this.setPredecessor(pred);}
			)
			.then(
				() => {
					let proms = [];

					for(var i=0; i<this.finger.length-1; i++) {
						if(ID.inRightOpenBound(this.finger[i+1].start, this.id, this.finger[i].node.id))
							this.finger[i+1].node = this.finger[i].node;
						else {
							(i=>{
								proms.push(
									knownNode.findSuccessor(this.finger[i+1].start)
										.then(
											succ => {
												this.finger[i+1].node = succ;
											}
										)
								)
							})(i)
						}
					}

					return Promise.all(proms);
				}
			);
	}

	//Promise updated
	updateOthers(){
		//Inform other nodes about our existence.
		let proms = [];

		for (var i = 0; i < this.finger.length; i++) {
			//find last node p whose ith finger might be n
			(i=>{proms.push(
				this.findPredecessor(this.id.subtract(ID.powerOfTwoBuffer(i)))
					.then(
						p => {return p.updateFingerTable(this, i)}
					)
				)
			})(i);
		}

		return Promise.all(proms);
	}

	//Promise updated
	updateFingerTable(foreignNode, index){
		//Update the finger of some remote node
		if(ID.inRightOpenBound(foreignNode.id, this.id, this.finger[index].node.id)){
			this.finger[index].node = foreignNode;
			return this.getPredecessor()
				.then(
					res => {return res.updateFingerTable(foreignNode, index)}
				)
		} else {
			return Promise.resolve();
		}

	}

	//Promise updated
	closestPrecedingFinger(id){
		return new Promise( resolve => {
			for (var i = this.finger.length - 1; i >= 0; i--) {
				if(ID.inOpenBound(this.finger[i].node.id, this.id, id)){
					resolve(this.finger[i].node);
					break;
				}
			}
			resolve(this);
		} );
	}

	//Promise updated
	findSuccessor(id){
		return this.findPredecessor(id)
			.then( nPrime => { return nPrime.getSuccessor() } )
	}

	//Promise updated
	findPredecessor(id){
		return new Promise( (resolve, reject) => {
			let nPrime = this;

			let condUpd = () => {
				nPrime.getSuccessor()
					.then(
						succ => {
							if (!ID.inLeftOpenBound(id, nPrime.id, succ.id)) {
								nPrime.closestPrecedingFinger(id)
									.then(
										newPrime => {
											nPrime = newPrime;
											condUpd();
										},
										rea => reject(rea)
									)
							} else {
								resolve(nPrime);
							}
						},
						rea => reject(rea)
					)
				};

			condUpd();
		} )
	}

	//Stabilization methods
	//Promise updated
	stableJoin(knownNode){
		return this.setPredecessor(null)
			.then(
				res => {
					this.setSuccessor(knownNode);
					this.chord.server.connect = true;
				}
			);
	}

	//periodically verify n's immediate successor
	//and tell the successor about n.
	//Promise updated
	stabilize(){
		let oSucc;

		return this.getSuccessor()
			.then(succ => {oSucc = succ; return succ.getPredecessor()})
			.then(pred => {
				if(ID.inOpenBound(pred.id, this.id, oSucc.id))
					return this.setSuccessor(pred);
				else
					return Promise.resolve();
			})
			.then(
				res => {return oSucc.notify(this);}
			)

	}

	//Promise updated
	notify(nPrime){
		if(this.predecessor === null || ID.inOpenBound(nPrime.id, this.predecessor.id, this.id))
			this.predecessor = nPrime;
		return Promise.resolve();
	}

	//Promise updated
	fixFingers(){
		let i = Math.floor(Math.random() * (this.finger.length-1) + 1);
		this.finger[i].node = this.findSuccessor(this.finger[i].start);

		return this.findSuccessor(this.finger[i].start)
			.then(
				succ => this.finger[i].node = succ
			)
	}

	//Custom

	message(id, msg){
		//TODO

		// debugger;

		console.log(`Received message at the local node for ${id}: ${msg}
			I am ${this.id}`);

		if(this.chord.server.connect && ID.compare(id, this.id)!== 0) {
			this.chord.server.node.message(id, msg);
		} else if (ID.compare(this.id, id)===0 || ID.inLeftOpenBound(id, this.predecessor.id, this.id)){
			//Pass to appropriate handler - this is our message.
			this.chord.registry.parse(msg);
		} else {
			//Pass along the chain to a responsible node.
			this.closestPrecedingFinger(id)
				.then(
					node => node.message(id, msg)
				)
		}
	}

	// Item management

	lookup(id){
		//TODO
	}

	add(id, value){
		//TODO
	}
}

module.exports = Node;
<|endoftext|>


<commit_before>//region STORY
Parse.Cloud.define("story_create", function (request, response) {
    var content = request.params.content;
    var title = request.params.title;
    var tags = request.params.tags;

    var valid_tags = [];
    var isError = false;
    // Check errors
    if (request.user == null) {
        response.error('Authentication error');
        isError = true;
    }
    else if (content == null || isEmpty(content)) {
        response.error("Syntax error");
        isError = true;
    }
    else if (title == null || isEmpty(title)) {
        response.error("Syntax error");
        isError = true;
    }
    else if (tags == null || !(tags instanceof Array)) {
        response.error("Syntax error");
        isError = true;
    }
    else if (tags instanceof Array) {
        var i;
        for (i = 0; i < tags.length; i++) {
            if (!isEmpty(tags[i]))
                valid_tags.push(tags[i].trim());
        }
        if (valid_tags.length < 1) {
            response.error("Syntax error");
            isError = true;
        }
    }

    // There is no errors
    if(!isError) {
        var StoryClass = Parse.Object.extend("Story");
        var story = new StoryClass();
        story.set("content", content.trim());
        story.set("title", title.trim());
        story.set("owner", request.user);
        story.set("lastEditor", request.user);
        story.set("tagsArray", valid_tags);

        story.save(null, {
            success: function (story) {
                response.success(convertStory(story, true));
            },
            error: function (error) {
                response.error(error);
            }
        });
    }

});
//endregion

//region STORY
Parse.Cloud.define("story_get", function (request, response) {
    var id = request.params.id;
    var StoryClass = Parse.Object.extend("Story");
    var query = new Parse.Query(StoryClass);
    query.get(id, {
        success: function (object) {
            response.success(convertStory(object, true));
        },
        error: function (error) {
            response.error(error);
        }
    });
});
//endregion

//region SEARCH
Parse.Cloud.define("search", function (request, response) {
    // This is our main search API.
    // The search will be parsed here.
    // For now, the search is tag based.
    var queryWord = request.params.query;
    var page = request.params.page;
    var size = request.params.size;

    if (size == null)
        size = 100;

    if (page == null)
        page = 0;

    var tags = queryWord.split(" ");

    var StoryClass = Parse.Object.extend("Story");

    var query = new Parse.Query(StoryClass);
    query.containedIn("tagsArray", tags);
    query.limit(size);
    query.skip(page * size);
    query.find({
        success: function (results) {
            var myRes = [];
            for (var i = 0; i < results.length; i++) {
                myRes.push(
                    convertStory(results[i], false)
                );
            }
            response.success(myRes);
        }
    });
});
//endregion

//region Utility Functions
function isEmpty(str) {
    return str.replace(/^\s+|\s+$/g, '').length == 0;
}
//endregion

//region Object Converters
//Parse object to response object.
//The reason is hide to database structure from API user
function convertStory(story, withContent) {
    var obj = {
        "id": story.id,
        "title": story.get("title"),
        "createdAt": story.createdAt.toISOString(),
        "updatedAt": story.updatedAt.toISOString(),
        "ownerId": story.get("owner").id,
        "lastEditorId": story.get("lastEditor").id,
        "tags": story.get("tagsArray")
    };
    if (withContent == true) {
        obj["content"] = story.get("content");
    }

    return obj;
}
//endregion<commit_msg>Update main.js<commit_after>//region STORY
Parse.Cloud.define("story_create", function (request, response) {
    var content = request.params.content;
    var title = request.params.title;
    var tags = request.params.tags;

    var valid_tags = [];
    var isError = false;
    // Check errors
    if (request.user == null) {
        response.error('Authentication error');
        isError = true;
    }
    else if (content == null || isEmpty(content)) {
        response.error("Syntax error");
        isError = true;
    }
    else if (title == null || isEmpty(title)) {
        response.error("Syntax error");
        isError = true;
    }
    else if (tags == null || !(tags instanceof Array)) {
        response.error("Syntax error");
        isError = true;
    }
    else if (tags instanceof Array) {
        var i;
        for (i = 0; i < tags.length; i++) {
            if (!isEmpty(tags[i]))
                valid_tags.push(tags[i].trim());
        }
        if (valid_tags.length < 1) {
            response.error("Syntax error");
            isError = true;
        }
    }

    // There is no errors
    if(!isError) {
        var StoryClass = Parse.Object.extend("Story");
        var story = new StoryClass();
        story.set("content", content.trim());
        story.set("title", title.trim());
        story.set("owner", request.user);
        story.set("lastEditor", request.user);
        story.set("tagsArray", valid_tags);

        story.save(null, {
            success: function (story) {
                response.success(convertStory(story, true));
            },
            error: function (error) {
                response.error(error);
            }
        });
    }

});
//endregion

//region STORY
Parse.Cloud.define("story_get", function (request, response) {
    var id = request.params.id;
    var StoryClass = Parse.Object.extend("Story");
    var query = new Parse.Query(StoryClass);
    query.get(id, {
        success: function (object) {
            response.success(convertStory(object, true));
        },
        error: function (error) {
            response.error(error);
        }
    });
});
//endregion

//region SEARCH
Parse.Cloud.define("search", function (request, response) {
    // This is our main search API.
    // The search will be parsed here.
    // For now, the search is tag based.
    var queryWord = request.params.query;
    var page = request.params.page;
    var size = request.params.size;

    if (size == null)
        size = 100;

    if (page == null)
        page = 0;

    var tags = queryWord.split(" ");

    var StoryClass = Parse.Object.extend("Story");

    var query = new Parse.Query(StoryClass);
    query.containedIn("tagsArray", tags);
    query.limit(size);
    query.skip(page * size);
    query.find({
        success: function (results) {
            var myRes = [];
            for (var i = 0; i < results.length; i++) {
                myRes.push(
                    convertStory(results[i], false)
                );
            }
            response.success(myRes);
        }
    });
});
//endregion

//region Utility Functions
function isEmpty(str) {
    return str.replace(/^\s+|\s+$/g, '').length == 0;
}
//endregion

//region Object Converters
//Parse object to response object.
//The reason is hide to database structure from API user
function convertStory(story, withContent) {
    var obj = {
        "id": story.id,
        "title": story.get("title"),
        "createdAt": story.createdAt.toISOString(),
        "updatedAt": story.updatedAt.toISOString(),
        "ownerId": story.get("owner").id,
        "lastEditorId": story.get("lastEditor").id,
        "tags": story.get("tagsArray")
    };
    if (withContent == true) {
        obj["content"] = story.get("content");
    }

    return obj;
}
//endregion
<|endoftext|>


<commit_before>import inputData from './data.js';
import infiniteDivs from './lib.js';

let divArray = [];
let root = document.getElementById('root');

for (let value of inputData) {
  let div = document.createElement('div'),
    img = document.createElement('img'),
    span = document.createElement('span');

  img.src = value.img;
  img.style = 'vertical-align: middle; height: 50px; margin: 20px';
  span.innerText = value.name;
  span.setAttribute('style', 'text-decoration: underline');
  div.appendChild(img);
  div.appendChild(span);
  divArray.push(div);

};

let config = {
	root,
	divArray,
	divHeight : 90,
	bufferMultiplier : 3
};

let infinitedivs = new infiniteDivs(config);
<commit_msg>Add viewDoctor to scroll event.<commit_after>import inputData from './data.js';
import infiniteDivs from './lib.js';

let divArray = [];
let root = document.body;

for (let value of inputData) {
  let div = document.createElement('div'),
    img = document.createElement('img'),
    span = document.createElement('span');

  img.src = value.img;
  img.style = 'vertical-align: middle; height: 50px; margin: 20px';
  span.innerText = value.name;
  span.setAttribute('style', 'text-decoration: underline');
  div.appendChild(img);
  div.appendChild(span);
  divArray.push(div);

};

let config = {
  root,
  divArray,
  divHeight: 90,
  bufferMultiplier: 3
};

let infinitedivs = new infiniteDivs(config);

function scrollListener() {
  infinitedivs.viewDoctor(config.root);
};
document.addEventListener('scroll', scrollListener);
<|endoftext|>


<commit_before>/**
 * this infowindow is shown in the map when user clicks on a feature
 */

(function() {

  var MapInfowindow = cdb.geo.ui.Infowindow.extend({
    _TEXTS: {
      _NO_FIELDS_SELECTED: "You haven’t selected any
infowindow fields."
    },

    events: cdb.core.View.extendEvents({
        'click .edit':    '_editGeom',
        'click .remove':  '_removeGeom'
    }),

    initialize: function() {
      var self = this;
      _.bindAll(this, '_removeGeom');
      this.table = this.options.table;
      this.model.set({ content: 'loading...' });
      // call parent
      this.constructor.__super__.initialize.apply(this);
      this.model.set('offset', [28, 0]);
      this.model.bind('change:fields', function() {
        if(!this.hasChanged('content') && self.row) {
          self.renderInfo();
        }
      });

      // Create a help dialog for the infowindows with images
      if (this._containsCover) this._createHelpDialog();

      // Set live tipsy when geom is enabled or disabled
      this.$el.find("a.edit, a.remove").tipsy({
        live:true,
        fade:true,
        gravity: 's',
        offset: -2,
        className: function(){
          return $(this).closest(".cartodb-popup").hasClass('dark') ? 'dark' : ''
        },
        title: function(ev){
          var enabled = !$(this).hasClass("disabled");
          if (enabled) {
            return ''
          } else {
            return 'Not available in SQL view'
          }
        }
      })
    },

    _createHelpDialog: function() {

      var HelpDialog = cdb.admin.BaseDialog.extend({
        render_content: function() {
          return "
This template allows you to add an image to your infowindow. You will only need to put in the first place of the customization list, a column with URL’s to the images that you want.
" +
            "
We higly recommend you to preproccess those images in order to improve your map experience.";
        },
      });

      this.helpDialog = new HelpDialog({
        title: 'Adding images to your infowindows',
        ok_title: 'Ok, close',
        cancel_title: '',
        ok_button_classes: "button grey",
        modal_type: "compressed",
        width: 500
      });

      this.addView(this.helpDialog);

    },

    setFeatureInfo: function(cartodb_id) {
      this.cartodb_id = cartodb_id;
      if(this.row) {
        this.row.unbind();
      }
      this.row = this.table.data().getRow(cartodb_id);
      this.row.bind('change', this.renderInfo, this);
      this.row.fetch();
      this.model.set({ content: 'loading...' });
      // trigger renderInfo now to render the actual contents
      this.renderInfo();
      return this;
    },

    /**
     * renders the infowindows adding some editing features
     */
    render: function() {

      var self = this;

      // render original
      cdb.geo.ui.Infowindow.prototype.render.call(this);

      // render edit and remove buttons
      if(this.model.toJSON().fields && !this.model.toJSON().fields.length) {

        // Add empty fields to the infowindow
        this.$el.find('.cartodb-popup').addClass("no_fields");

        // Check if the infowindow has header or not
        if (this.$('.cartodb-popup-header').length > 0) {
          this.$('.cartodb-popup-header').html('
' + this._TEXTS._NO_FIELDS_SELECTED + '
');
        } else {
          this.$('.cartodb-popup-content').html('
' + this._TEXTS._NO_FIELDS_SELECTED + '
');
        }
      } else {
        this.$el.find('.cartodb-popup').removeClass("no_fields");
      }

      var cartodb_id = '';

      if(this.row) {
        cartodb_id = this.row.attributes['cartodb_id'];
        cartodb_id = cartodb_id? '#'+cartodb_id : '';
      }

      this.$('.cartodb-popup-content-wrapper')
        .append(this.getTemplate('table/views/infowindow_footer')({"cartodb_id": cartodb_id}));

      if(this.table.data().isReadOnly()) {
        this.$('.cartodb-popup-content-wrapper .edit').addClass('disabled');
        this.$('.cartodb-popup-content-wrapper .remove').addClass('disabled');
      }

      if (this._containsCover()) { // bind the help link to the helpDialog
        this.$el.find(".image_not_found a.help").off("click");
        this.$el.find(".image_not_found a.help").on("click", function() {
          $('body').append(self.helpDialog.render().el);
          self.helpDialog.open();
        });
      }

    },

    renderInfo: function() {
      var self = this;
      var fields = _(this.row.attributes).map(function(v, k) {
        // Don't rendering cartodb_id field from the beginning
        // although back is answering us with it
        if(self.model.containsField(k) && !_.contains(self.model.SYSTEM_COLUMNS, k)) {
          var h = {
            title: null,
            value: '',
            position: Number.MAX_INT
          };
          if(self.model.getFieldProperty(k, 'title')) {
            h.title = k;
          }
          h.value = v;
          h.position = self.model.getFieldPos(k);
          return h;
        }
        return null;
      });

      // sort
      fields = _.compact(fields);
      fields.sort(function(a, b) {
        return a.position - b.position;
      });

      // filter and add index
      var render_fields = [];
      for(var i = 0; i < fields.length; ++i) {
        var f = fields[i];
        if(f) {
          f.index = render_fields.length;
          render_fields.push(f);
        }
      }
      this.model.set({ content:  { fields: render_fields } });

      if(this.model.get('visibility')) {
        // Just move the map if need it when fields are already added.
        this._adjustPan();
      }
    },

    /**
     * triggers an editGeom event with the geometry
     * infowindow is currently showing
     */
    _editGeom: function(e) {
      e.preventDefault();

      if (!this.table.data().isReadOnly()) {
        this.model.set("visibility", false);
        this.trigger('editGeom', this.row);
        return false;
      }

    },

    /**
     * triggers an removeGeom event when the geometry
     * is removed from the server
     */
    _removeGeom: function(e) {
      this.killEvent(e);
      if (!this.table.data().isReadOnly()) {
        var self = this;
        this.table.trigger('removing:row');
        this.model.set("visibility", false);
        this.row.destroy({
          success: function() {
            self.table.trigger('remove:row', self.row);
          }
        }, { wait: true });
        return false;
      }
    }
  });

  // export
  cdb.admin.MapInfowindow = MapInfowindow;

})();
<commit_msg>Improves the spelling of the infowindow help dialog<commit_after>/**
 * this infowindow is shown in the map when user clicks on a feature
 */

(function() {

  var MapInfowindow = cdb.geo.ui.Infowindow.extend({
    _TEXTS: {
      _NO_FIELDS_SELECTED: "You haven’t selected any
infowindow fields."
    },

    events: cdb.core.View.extendEvents({
        'click .edit':    '_editGeom',
        'click .remove':  '_removeGeom'
    }),

    initialize: function() {
      var self = this;
      _.bindAll(this, '_removeGeom');
      this.table = this.options.table;
      this.model.set({ content: 'loading...' });
      // call parent
      this.constructor.__super__.initialize.apply(this);
      this.model.set('offset', [28, 0]);
      this.model.bind('change:fields', function() {
        if(!this.hasChanged('content') && self.row) {
          self.renderInfo();
        }
      });

      // Create a help dialog for the infowindows with images
      if (this._containsCover) this._createHelpDialog();

      // Set live tipsy when geom is enabled or disabled
      this.$el.find("a.edit, a.remove").tipsy({
        live:true,
        fade:true,
        gravity: 's',
        offset: -2,
        className: function(){
          return $(this).closest(".cartodb-popup").hasClass('dark') ? 'dark' : ''
        },
        title: function(ev){
          var enabled = !$(this).hasClass("disabled");
          if (enabled) {
            return ''
          } else {
            return 'Not available in SQL view'
          }
        }
      })
    },

    _createHelpDialog: function() {

      var HelpDialog = cdb.admin.BaseDialog.extend({
        render_content: function() {
          return "
This template allows you to add an image to your infowindow. You will only need to put a column with the URL of the image in the first position of the customization field list.
" +
            "
We strongly recommend you to preproccess those images in order to improve your map experience.
";
        },
      });

      this.helpDialog = new HelpDialog({
        title: 'Adding images to your infowindows',
        ok_title: 'Ok, close',
        cancel_title: '',
        ok_button_classes: "button grey",
        modal_type: "compressed",
        width: 500
      });

      this.addView(this.helpDialog);

    },

    setFeatureInfo: function(cartodb_id) {
      this.cartodb_id = cartodb_id;
      if(this.row) {
        this.row.unbind();
      }
      this.row = this.table.data().getRow(cartodb_id);
      this.row.bind('change', this.renderInfo, this);
      this.row.fetch();
      this.model.set({ content: 'loading...' });
      // trigger renderInfo now to render the actual contents
      this.renderInfo();
      return this;
    },

    /**
     * renders the infowindows adding some editing features
     */
    render: function() {

      var self = this;

      // render original
      cdb.geo.ui.Infowindow.prototype.render.call(this);

      // render edit and remove buttons
      if(this.model.toJSON().fields && !this.model.toJSON().fields.length) {

        // Add empty fields to the infowindow
        this.$el.find('.cartodb-popup').addClass("no_fields");

        // Check if the infowindow has header or not
        if (this.$('.cartodb-popup-header').length > 0) {
          this.$('.cartodb-popup-header').html('
' + this._TEXTS._NO_FIELDS_SELECTED + '
');
        } else {
          this.$('.cartodb-popup-content').html('
' + this._TEXTS._NO_FIELDS_SELECTED + '
');
        }
      } else {
        this.$el.find('.cartodb-popup').removeClass("no_fields");
      }

      var cartodb_id = '';

      if(this.row) {
        cartodb_id = this.row.attributes['cartodb_id'];
        cartodb_id = cartodb_id? '#'+cartodb_id : '';
      }

      this.$('.cartodb-popup-content-wrapper')
        .append(this.getTemplate('table/views/infowindow_footer')({"cartodb_id": cartodb_id}));

      if(this.table.data().isReadOnly()) {
        this.$('.cartodb-popup-content-wrapper .edit').addClass('disabled');
        this.$('.cartodb-popup-content-wrapper .remove').addClass('disabled');
      }

      if (this._containsCover()) { // bind the help link to the helpDialog
        this.$el.find(".image_not_found a.help").off("click");
        this.$el.find(".image_not_found a.help").on("click", function() {
          $('body').append(self.helpDialog.render().el);
          self.helpDialog.open();
        });
      }

    },

    renderInfo: function() {
      var self = this;
      var fields = _(this.row.attributes).map(function(v, k) {
        // Don't rendering cartodb_id field from the beginning
        // although back is answering us with it
        if(self.model.containsField(k) && !_.contains(self.model.SYSTEM_COLUMNS, k)) {
          var h = {
            title: null,
            value: '',
            position: Number.MAX_INT
          };
          if(self.model.getFieldProperty(k, 'title')) {
            h.title = k;
          }
          h.value = v;
          h.position = self.model.getFieldPos(k);
          return h;
        }
        return null;
      });

      // sort
      fields = _.compact(fields);
      fields.sort(function(a, b) {
        return a.position - b.position;
      });

      // filter and add index
      var render_fields = [];
      for(var i = 0; i < fields.length; ++i) {
        var f = fields[i];
        if(f) {
          f.index = render_fields.length;
          render_fields.push(f);
        }
      }
      this.model.set({ content:  { fields: render_fields } });

      if(this.model.get('visibility')) {
        // Just move the map if need it when fields are already added.
        this._adjustPan();
      }
    },

    /**
     * triggers an editGeom event with the geometry
     * infowindow is currently showing
     */
    _editGeom: function(e) {
      e.preventDefault();

      if (!this.table.data().isReadOnly()) {
        this.model.set("visibility", false);
        this.trigger('editGeom', this.row);
        return false;
      }

    },

    /**
     * triggers an removeGeom event when the geometry
     * is removed from the server
     */
    _removeGeom: function(e) {
      this.killEvent(e);
      if (!this.table.data().isReadOnly()) {
        var self = this;
        this.table.trigger('removing:row');
        this.model.set("visibility", false);
        this.row.destroy({
          success: function() {
            self.table.trigger('remove:row', self.row);
          }
        }, { wait: true });
        return false;
      }
    }
  });

  // export
  cdb.admin.MapInfowindow = MapInfowindow;

})();
<|endoftext|>


<commit_before>var Bridge  = require('../bridge'),
	PushPullRequest = require('../request');

var PullRequest = PushPullRequest.extend({

	/**
	 */

	'override __construct': function(ops)
	{
		this._super(ops);

		this._writer = this._next;

		//commented out for reference. this screws up any pass-thrus.
		//UPDATE: Dunno why I did this without testing extensivly, but *not* commenting this really fucks shit up, considering when you call
		//a passthru directly, calling this.next() calls back the end call back, which we don't want.
		//fdsnkfanflsadkl comment this out for now
		// this._next = null;
	},

	/**
	 */

	'_callback': function(ops)
	{
		var bridge = new Bridge(this);

		//here's where it gets a little interesting. We need to tie up the ORIGINAL writer, with the current bridge since anything returned
		//to the pass-thru is sent back to the client. By default, doing so usually means an error ocurred (stop in the process).
		this._listen(this.target._writer, this.target.meta, bridge);

		var response = ops.callback.apply(bridge, [bridge]);

		//if there is a response, then we can use that to send data.
		if(response != undefined)
		{
			bridge.end(response);
		}
	}
});

module.exports = PullRequest;
<commit_msg>if target == this, then next = null (bug)<commit_after>var Bridge  = require('../bridge'),
	PushPullRequest = require('../request');

var PullRequest = PushPullRequest.extend({

	/**
	 */

	'override __construct': function(ops)
	{
		this._super(ops);

		this._writer = this._next;

		//it's the root if target == this, and we need next to get back to the pull requestor, otherwise it's a pass thru. IF it's a pass-thru,
		//and next *is* null, then we'll never get to the end callback. IF it's *not* null, and the pass thru is called directly, next will return the final callback, (requestor),
		//and an error will be thrown.
		if(this.target == this) this._next = null;
	},

	/**
	 */

	'_callback': function(ops)
	{
		var bridge = new Bridge(this);

		//here's where it gets a little interesting. We need to tie up the ORIGINAL writer, with the current bridge since anything returned
		//to the pass-thru is sent back to the client. By default, doing so usually means an error ocurred (stop in the process).
		this._listen(this.target._writer, this.target.meta, bridge);

		var response = ops.callback.apply(bridge, [bridge]);

		//if there is a response, then we can use that to send data.
		if(response != undefined)
		{
			bridge.end(response);
		}
	}
});

module.exports = PullRequest;
<|endoftext|>


<commit_before>import Widget from "./base_widget.js";

frappe.provide("frappe.utils");

export default class LinksWidget extends Widget {
	constructor(opts) {
		opts.icon = opts.icon || 'file';
		super(opts);
	}

	get_config() {
		return {
			name: this.name,
			links: JSON.stringify(this.links),
			link_count: this.links.length,
			label: this.label,
			hidden: this.hidden,
		};
	}

	set_body() {

		if (!this.options) {
			this.options = {};
			this.options.links = this.links;
		}
		this.widget.addClass("links-widget-box");
		const is_link_disabled = item => {
			return item.dependencies && item.incomplete_dependencies;
		};
		const disabled_dependent = item => {
			return is_link_disabled(item) ? "disabled-link" : "";
		};

		const get_indicator_color = item => {
			if (item.open_count) {
				return "red";
			}
			if (item.onboard) {
				return item.count ? "blue" : "yellow";
			}
			return "gray";
		};

		const get_link_for_item = item => {
			if (is_link_disabled(item)) {
				return `${
					item.label ? item.label : item.name
				}

${__("You need to create these first: ")}

${item.incomplete_dependencies.join(", ")}

`;
			}

			if (item.youtube_id)
				return `
						${item.label ? item.label : item.name}`;

			return `<span class="link-content ellipsis">${item.label ? item.label : item.name}</span>`;
		};

		this.link_list = this.links.map(item => {
			const opts = {
				name: item.link_to,
				type: item.link_type,
				doctype: item.doctype,
				is_query_report: item.is_query_report
			};

			if (item.link_type.toLowerCase() == "report" && !item.is_query_report) {
				opts.doctype = item.dependencies;
			}

			const route = frappe.utils.generate_route(opts);

			return $(`

					${get_link_for_item(item)}
			`);
		});
		if (this.in_customize_mode) {
			this.body.empty();
		}
		this.link_list.forEach(link => link.appendTo(this.body));
	}

	setup_events() {
		this.link_list.forEach(link => {
			// Bind Popver Event
			const link_label = link.find(".link-content");

			if (link.hasClass("disabled-link")) {
				const popover = link.find(".module-link-popover");

				link_label.mouseover(() => {
					if (this.in_customize_mode) return;
					popover.show();
				});
				link_label.mouseout(() => popover.hide());
			} else {
				link_label.click(event => {
					if (this.in_customize_mode) return;

					if (link_label.hasClass("help-video-link")) {
						let yt_id = event.currentTarget.dataset.youtubeid;
						frappe.help.show_video(yt_id);
					}
				});
			}
		});
	}
}
<commit_msg>fix: added title on card links<commit_after>import Widget from "./base_widget.js";

frappe.provide("frappe.utils");

export default class LinksWidget extends Widget {
	constructor(opts) {
		opts.icon = opts.icon || 'file';
		super(opts);
	}

	get_config() {
		return {
			name: this.name,
			links: JSON.stringify(this.links),
			link_count: this.links.length,
			label: this.label,
			hidden: this.hidden,
		};
	}

	set_body() {

		if (!this.options) {
			this.options = {};
			this.options.links = this.links;
		}
		this.widget.addClass("links-widget-box");
		const is_link_disabled = item => {
			return item.dependencies && item.incomplete_dependencies;
		};
		const disabled_dependent = item => {
			return is_link_disabled(item) ? "disabled-link" : "";
		};

		const get_indicator_color = item => {
			if (item.open_count) {
				return "red";
			}
			if (item.onboard) {
				return item.count ? "blue" : "yellow";
			}
			return "gray";
		};

		const get_link_for_item = item => {
			if (is_link_disabled(item)) {
				return `${
					item.label ? item.label : item.name
				}

${__("You need to create these first: ")}

${item.incomplete_dependencies.join(", ")}

`;
			}

			if (item.youtube_id)
				return `
						${item.label ? item.label : item.name}`;

			return `<span class="link-content ellipsis">${item.label ? item.label : item.name}</span>`;
		};

		this.link_list = this.links.map(item => {
			const opts = {
				name: item.link_to,
				type: item.link_type,
				doctype: item.doctype,
				is_query_report: item.is_query_report
			};

			if (item.link_type.toLowerCase() == "report" && !item.is_query_report) {
				opts.doctype = item.dependencies;
			}

			const route = frappe.utils.generate_route(opts);

			return $(`

					${get_link_for_item(item)}
			`);
		});
		if (this.in_customize_mode) {
			this.body.empty();
		}
		this.link_list.forEach(link => link.appendTo(this.body));
	}

	setup_events() {
		this.link_list.forEach(link => {
			// Bind Popver Event
			const link_label = link.find(".link-content");

			if (link.hasClass("disabled-link")) {
				const popover = link.find(".module-link-popover");

				link_label.mouseover(() => {
					if (this.in_customize_mode) return;
					popover.show();
				});
				link_label.mouseout(() => popover.hide());
			} else {
				link_label.click(event => {
					if (this.in_customize_mode) return;

					if (link_label.hasClass("help-video-link")) {
						let yt_id = event.currentTarget.dataset.youtubeid;
						frappe.help.show_video(yt_id);
					}
				});
			}
		});
	}
}
<|endoftext|>


<commit_before>/**
 * T'MediaArt library for JavaScript
 *  - MajVj extension - frame plugin - mixer -
 * @param options options (See MajVj.prototype.create)
 */
MajVj.frame.mixer = function (options) {
    this._screen = options.screen;
    this._width = options.width;
    this._height = options.height;
    this._aspect = options.aspect;
    this._controller = options.controller;
    this._channel = options.channel || 1;
    var fragmentShader =
            (this._channel == 1) ?  MajVj.frame.mixer._fragment1Shader :
            (this._channel == 2) ?  MajVj.frame.mixer._fragment2Shader :
                                    MajVj.frame.mixer._fragment3Shader;
    this._program = this._screen.createProgram(
            this._screen.compileShader(Tma3DScreen.VERTEX_SHADER,
                    MajVj.frame.mixer._vertexShader),
            this._screen.compileShader(Tma3DScreen.FRAGMENT_SHADER,
                    fragmentShader));
    this._coords = this._screen.createBuffer([0, 0, 0, 1, 1, 1, 1, 0]);

    this._fbo = [];
    for (var ch = 0; ch < this._channel; ++ch) {
        this._fbo[ch] = this._screen.createFrameBuffer(
                this._width, this._height);
    }
};

// Shader programs.
MajVj.frame.mixer._vertexShader = null;
MajVj.frame.mixer._fragment1Shader = null;
MajVj.frame.mixer._fragment2Shader = null;
MajVj.frame.mixer._fragment3Shader = null;

/**
 * Loads resources asynchronously.
 */
MajVj.frame.mixer.load = function () {
    return new Promise(function (resolve, reject) {
        Promise.all([
            MajVj.loadShader('frame', 'mixer', 'shaders.html', 'vertex'),
            MajVj.loadShader('frame', 'mixer', 'shaders.html', 'fragment1'),
            MajVj.loadShader('frame', 'mixer', 'shaders.html', 'fragment2'),
            MajVj.loadShader('frame', 'mixer', 'shaders.html', 'fragment3')
        ]).then(function (shaders) {
            MajVj.frame.mixer._vertexShader = shaders[0];
            MajVj.frame.mixer._fragment1Shader = shaders[1];
            MajVj.frame.mixer._fragment2Shader = shaders[2];
            MajVj.frame.mixer._fragment3Shader = shaders[3];
            resolve();
        }, function () { reject('mixer.load fails'); });
    });
};

/**
 * Handles screen resize.
 * @param aspect screen aspect ratio
 */
MajVj.frame.mixer.prototype.onresize = function (aspect) {
};

/**
 * Draws a frame.
 * @param delta delta time from the last rendering
 */
MajVj.frame.mixer.prototype.draw =
        function (delta, texture0, texture1, texture2) {
    this._program.setAttributeArray('aCoord', this._coords, 0, 2, 0);
    this._program.setTexture('uTexture0', this._fbo[0].texture);
    this._program.setUniformVector('uVolume0', [this._controller.volume[0]]);
    if (this._channel >= 2) {
        this._program.setTexture('uTexture1', this._fbo[1].texture);
        this._program.setUniformVector(
                'uVolume1', [this._controller.volume[1]]);
        if (this._channel >= 3) {
            this._program.setTexture('uTexture2', this._fbo[2].texture);
            this._program.setUniformVector(
                    'uVolume2', [this._controller.volume[2]]);
        }
    }
    this._program.drawArrays(Tma3DScreen.MODE_TRIANGLE_FAN, 0, 4);
};

/**
 * Sets a controller.
 * @param controller a controller object
 */
MajVj.frame.mixer.prototype.setController = function (controller) {
    this._controller = controller;
};

/**
 * Bind an offscreen buffer.
 * @param channel a channel to bind
 */
MajVj.frame.mixer.prototype.bind = function (channel) {
    this._fbo[channel].bind();
};

<commit_msg>MajVj mixer frame: returns a previous FBo on offscreen binding<commit_after>/**
 * T'MediaArt library for JavaScript
 *  - MajVj extension - frame plugin - mixer -
 * @param options options (See MajVj.prototype.create)
 */
MajVj.frame.mixer = function (options) {
    this._screen = options.screen;
    this._width = options.width;
    this._height = options.height;
    this._aspect = options.aspect;
    this._controller = options.controller;
    this._channel = options.channel || 1;
    var fragmentShader =
            (this._channel == 1) ?  MajVj.frame.mixer._fragment1Shader :
            (this._channel == 2) ?  MajVj.frame.mixer._fragment2Shader :
                                    MajVj.frame.mixer._fragment3Shader;
    this._program = this._screen.createProgram(
            this._screen.compileShader(Tma3DScreen.VERTEX_SHADER,
                    MajVj.frame.mixer._vertexShader),
            this._screen.compileShader(Tma3DScreen.FRAGMENT_SHADER,
                    fragmentShader));
    this._coords = this._screen.createBuffer([0, 0, 0, 1, 1, 1, 1, 0]);

    this._fbo = [];
    for (var ch = 0; ch < this._channel; ++ch) {
        this._fbo[ch] = this._screen.createFrameBuffer(
                this._width, this._height);
    }
};

// Shader programs.
MajVj.frame.mixer._vertexShader = null;
MajVj.frame.mixer._fragment1Shader = null;
MajVj.frame.mixer._fragment2Shader = null;
MajVj.frame.mixer._fragment3Shader = null;

/**
 * Loads resources asynchronously.
 */
MajVj.frame.mixer.load = function () {
    return new Promise(function (resolve, reject) {
        Promise.all([
            MajVj.loadShader('frame', 'mixer', 'shaders.html', 'vertex'),
            MajVj.loadShader('frame', 'mixer', 'shaders.html', 'fragment1'),
            MajVj.loadShader('frame', 'mixer', 'shaders.html', 'fragment2'),
            MajVj.loadShader('frame', 'mixer', 'shaders.html', 'fragment3')
        ]).then(function (shaders) {
            MajVj.frame.mixer._vertexShader = shaders[0];
            MajVj.frame.mixer._fragment1Shader = shaders[1];
            MajVj.frame.mixer._fragment2Shader = shaders[2];
            MajVj.frame.mixer._fragment3Shader = shaders[3];
            resolve();
        }, function () { reject('mixer.load fails'); });
    });
};

/**
 * Handles screen resize.
 * @param aspect screen aspect ratio
 */
MajVj.frame.mixer.prototype.onresize = function (aspect) {
};

/**
 * Draws a frame.
 * @param delta delta time from the last rendering
 */
MajVj.frame.mixer.prototype.draw =
        function (delta, texture0, texture1, texture2) {
    this._program.setAttributeArray('aCoord', this._coords, 0, 2, 0);
    this._program.setTexture('uTexture0', this._fbo[0].texture);
    this._program.setUniformVector('uVolume0', [this._controller.volume[0]]);
    if (this._channel >= 2) {
        this._program.setTexture('uTexture1', this._fbo[1].texture);
        this._program.setUniformVector(
                'uVolume1', [this._controller.volume[1]]);
        if (this._channel >= 3) {
            this._program.setTexture('uTexture2', this._fbo[2].texture);
            this._program.setUniformVector(
                    'uVolume2', [this._controller.volume[2]]);
        }
    }
    this._program.drawArrays(Tma3DScreen.MODE_TRIANGLE_FAN, 0, 4);
};

/**
 * Sets a controller.
 * @param controller a controller object
 */
MajVj.frame.mixer.prototype.setController = function (controller) {
    this._controller = controller;
};

/**
 * Bind an offscreen buffer.
 * @param channel a channel to bind
 * @return a previous fbo bount to the context
 */
MajVj.frame.mixer.prototype.bind = function (channel) {
    return this._fbo[channel].bind();
};

<|endoftext|>


<commit_before>// @flow

import * as React from 'react'
import {
	StyleSheet,
	ScrollView,
	View,
	Text,
	RefreshControl,
	Alert,
} from 'react-native'
import {TabBarIcon} from '../components/tabbar-icon'
import {connect} from 'react-redux'
import {Cell, TableView, Section} from 'react-native-tableview-simple'
import {
	hasSeenAcknowledgement,
	type LoginStateType,
} from '../../flux/parts/settings'
import {updateBalances} from '../../flux/parts/balances'
import {type ReduxState} from '../../flux'
import delay from 'delay'
import * as c from '../components/colors'
import type {TopLevelViewPropsType} from '../types'

const DISCLAIMER = 'This data may be outdated or otherwise inaccurate.'
const LONG_DISCLAIMER =
	'This data may be inaccurate.\nBon Appétit is always right.\nThis app is unofficial.'

type ReactProps = TopLevelViewPropsType

type ReduxStateProps = {
	dining: ?string,
	schillers: ?string,
	print: ?string,
	weeklyMeals: ?string,
	dailyMeals: ?string,
	mealPlan: ?string,
	loginState: LoginStateType,
	message: ?string,
	alertSeen: boolean,
}

type ReduxDispatchProps = {
	hasSeenAcknowledgement: () => any,
	updateBalances: boolean => any,
}

type Props = ReactProps & ReduxStateProps & ReduxDispatchProps

type State = {
	loading: boolean,
}

class BalancesView extends React.PureComponent<Props, State> {
	static navigationOptions = {
		tabBarLabel: 'Balances',
		tabBarIcon: TabBarIcon('card'),
	}

	state = {
		loading: false,
	}

	componentDidMount() {
		// calling "refresh" here, to make clear to the user
		// that the data is being updated
		this.refresh()

		if (!this.props.alertSeen) {
			Alert.alert('', LONG_DISCLAIMER, [
				{text: 'I Disagree', onPress: this.goBack, style: 'cancel'},
				{text: 'Okay', onPress: this.props.hasSeenAcknowledgement},
			])
		}
	}

	refresh = async () => {
		let start = Date.now()
		this.setState(() => ({loading: true}))

		await this.fetchData()

		// wait 0.5 seconds – if we let it go at normal speed, it feels broken.
		let elapsed = Date.now() - start
		await delay(500 - elapsed)

		this.setState(() => ({loading: false}))
	}

	fetchData = async () => {
		await this.props.updateBalances(true)
	}

	openSettings = () => {
		this.props.navigation.navigate('SettingsView')
	}

	goBack = () => {
		this.props.navigation.goBack(null)
	}

	render() {
		let {dining, schillers, dailyMeals, weeklyMeals, mealPlan} = this.props
		let {loading} = this.state

		return (
			<ScrollView
				contentContainerStyle={styles.stage}
				refreshControl={
					<RefreshControl
						onRefresh={this.refresh}
						refreshing={this.state.loading}
					/>
				}
			>
				<TableView>

						<View style={styles.balancesRow}>
							<FormattedValueCell
								formatter={getValueOrNa}
								indeterminate={loading}
								label="Schillers"
								value={schillers}
							/>

							<FormattedValueCell
								formatter={getValueOrNa}
								indeterminate={loading}
								label="Dining"
								value={dining}
							/>
						</View>

						<View style={styles.balancesRow}>
							<FormattedValueCell
								formatter={getValueOrNa}
								indeterminate={loading}
								label="Daily Meals Left"
								value={dailyMeals}
							/>

							<FormattedValueCell
								formatter={getValueOrNa}
								indeterminate={loading}
								label="Weekly Meals Left"
								style={styles.finalCell}
								value={weeklyMeals}
							/>
						</View>
						{mealPlan && (
							<Cell cellStyle="Subtitle" detail={mealPlan} title="Meal Plan" />
						)}

					{(this.props.loginState !== 'checking' &&
						this.props.loginState !== 'logged-in') ||
					this.props.message ? (

							{this.props.loginState !== 'logged-in' ? (
								<Cell
									accessory="DisclosureIndicator"
									cellStyle="Basic"
									onPress={this.openSettings}
									title="Log in with Carleton"
								/>
							) : null}

							{this.props.message ? (
								<Cell cellStyle="Basic" title={this.props.message} />
							) : null}

					) : null}
				</TableView>
			</ScrollView>
		)
	}
}

function mapState(state: ReduxState): ReduxStateProps {
	return {
		schillers: state.balances ? state.balances.schillersBalance : null,
		dining: state.balances ? state.balances.diningBalance : null,
		print: state.balances ? state.balances.printBalance : null,
		weeklyMeals: state.balances ? state.balances.mealsRemainingThisWeek : null,
		dailyMeals: state.balances ? state.balances.mealsRemainingToday : null,
		mealPlan: state.balances ? state.balances.mealPlanDescription : null,
		message: state.balances ? state.balances.balancesErrorMessage : null,
		alertSeen: state.settings ? state.settings.unofficiallyAcknowledged : false,

		loginState: state.settings ? state.settings.loginState : 'logged-out',
	}
}

function mapDispatch(dispatch): ReduxDispatchProps {
	return {
		updateBalances: force => dispatch(updateBalances(force)),
		hasSeenAcknowledgement: () => dispatch(hasSeenAcknowledgement()),
	}
}

export default connect(mapState, mapDispatch)(BalancesView)

let cellMargin = 10
let cellSidePadding = 10
let cellEdgePadding = 10

let styles = StyleSheet.create({
	stage: {
		backgroundColor: c.iosLightBackground,
		paddingTop: 20,
		paddingBottom: 20,
	},

	common: {
		backgroundColor: c.white,
	},

	balances: {
		borderRightWidth: StyleSheet.hairlineWidth,
		borderRightColor: c.iosSeparator,
	},

	finalCell: {
		borderRightWidth: 0,
	},

	balancesRow: {
		flexDirection: 'row',
		marginTop: 0,
		marginBottom: -10,
	},

	rectangle: {
		height: 88,
		flex: 1,
		alignItems: 'center',
		paddingTop: cellSidePadding,
		paddingBottom: cellSidePadding,
		paddingRight: cellEdgePadding,
		paddingLeft: cellEdgePadding,
		marginBottom: cellMargin,
	},

	// Text styling
	financialText: {
		paddingTop: 8,
		color: c.iosDisabledText,
		textAlign: 'center',
		fontWeight: '200',
		fontSize: 23,
	},
	rectangleButtonText: {
		paddingTop: 15,
		color: c.black,
		textAlign: 'center',
		fontSize: 16,
	},
})

function getValueOrNa(value: ?string): string {
	// eslint-disable-next-line no-eq-null
	if (value == null) {
		return 'N/A'
	}
	return value
}

function FormattedValueCell({
	indeterminate,
	label,
	value,
	style,
	formatter,
}: {
	indeterminate: boolean,
	label: string,
	value: ?string,
	style?: any,
	formatter: (?string) => string,
}) {
	return (
		<View style={[styles.rectangle, styles.common, styles.balances, style]}>
			<Text
				autoAdjustsFontSize={true}
				selectable={true}
				style={styles.financialText}
			>
				{indeterminate ? '…' : formatter(value)}
			</Text>
			<Text autoAdjustsFontSize={true} style={styles.rectangleButtonText}>
				{label}
			</Text>
		</View>
	)
}
<commit_msg>clean up FormattedValueCell<commit_after>// @flow

import * as React from 'react'
import {
	StyleSheet,
	ScrollView,
	View,
	Text,
	RefreshControl,
	Alert,
} from 'react-native'
import {TabBarIcon} from '../components/tabbar-icon'
import {connect} from 'react-redux'
import {Cell, TableView, Section} from 'react-native-tableview-simple'
import {
	hasSeenAcknowledgement,
	type LoginStateType,
} from '../../flux/parts/settings'
import {updateBalances} from '../../flux/parts/balances'
import {type ReduxState} from '../../flux'
import delay from 'delay'
import * as c from '../components/colors'
import type {TopLevelViewPropsType} from '../types'

const DISCLAIMER = 'This data may be outdated or otherwise inaccurate.'
const LONG_DISCLAIMER =
	'This data may be inaccurate.\nBon Appétit is always right.\nThis app is unofficial.'

type ReactProps = TopLevelViewPropsType

type ReduxStateProps = {
	dining: ?string,
	schillers: ?string,
	print: ?string,
	weeklyMeals: ?string,
	dailyMeals: ?string,
	mealPlan: ?string,
	loginState: LoginStateType,
	message: ?string,
	alertSeen: boolean,
}

type ReduxDispatchProps = {
	hasSeenAcknowledgement: () => any,
	updateBalances: boolean => any,
}

type Props = ReactProps & ReduxStateProps & ReduxDispatchProps

type State = {
	loading: boolean,
}

class BalancesView extends React.PureComponent<Props, State> {
	static navigationOptions = {
		tabBarLabel: 'Balances',
		tabBarIcon: TabBarIcon('card'),
	}

	state = {
		loading: false,
	}

	componentDidMount() {
		// calling "refresh" here, to make clear to the user
		// that the data is being updated
		this.refresh()

		if (!this.props.alertSeen) {
			Alert.alert('', LONG_DISCLAIMER, [
				{text: 'I Disagree', onPress: this.goBack, style: 'cancel'},
				{text: 'Okay', onPress: this.props.hasSeenAcknowledgement},
			])
		}
	}

	refresh = async () => {
		let start = Date.now()
		this.setState(() => ({loading: true}))

		await this.fetchData()

		// wait 0.5 seconds – if we let it go at normal speed, it feels broken.
		let elapsed = Date.now() - start
		await delay(500 - elapsed)

		this.setState(() => ({loading: false}))
	}

	fetchData = async () => {
		await this.props.updateBalances(true)
	}

	openSettings = () => {
		this.props.navigation.navigate('SettingsView')
	}

	goBack = () => {
		this.props.navigation.goBack(null)
	}

	render() {
		let {dining, schillers, dailyMeals, weeklyMeals, mealPlan} = this.props
		let {loading} = this.state

		return (
			<ScrollView
				contentContainerStyle={styles.stage}
				refreshControl={
					<RefreshControl
						onRefresh={this.refresh}
						refreshing={this.state.loading}
					/>
				}
			>
				<TableView>

						<View style={styles.balancesRow}>
							<FormattedValueCell
								formatter={getValueOrNa}
								indeterminate={loading}
								label="Schillers"
								value={schillers}
							/>

							<FormattedValueCell
								formatter={getValueOrNa}
								indeterminate={loading}
								label="Dining"
								value={dining}
							/>
						</View>

						<View style={styles.balancesRow}>
							<FormattedValueCell
								formatter={getValueOrNa}
								indeterminate={loading}
								label="Daily Meals Left"
								value={dailyMeals}
							/>

							<FormattedValueCell
								formatter={getValueOrNa}
								indeterminate={loading}
								label="Weekly Meals Left"
								style={styles.finalCell}
								value={weeklyMeals}
							/>
						</View>
						{mealPlan && (
							<Cell cellStyle="Subtitle" detail={mealPlan} title="Meal Plan" />
						)}

					{(this.props.loginState !== 'checking' &&
						this.props.loginState !== 'logged-in') ||
					this.props.message ? (

							{this.props.loginState !== 'logged-in' ? (
								<Cell
									accessory="DisclosureIndicator"
									cellStyle="Basic"
									onPress={this.openSettings}
									title="Log in with Carleton"
								/>
							) : null}

							{this.props.message ? (
								<Cell cellStyle="Basic" title={this.props.message} />
							) : null}

					) : null}
				</TableView>
			</ScrollView>
		)
	}
}

function mapState(state: ReduxState): ReduxStateProps {
	return {
		schillers: state.balances ? state.balances.schillersBalance : null,
		dining: state.balances ? state.balances.diningBalance : null,
		print: state.balances ? state.balances.printBalance : null,
		weeklyMeals: state.balances ? state.balances.mealsRemainingThisWeek : null,
		dailyMeals: state.balances ? state.balances.mealsRemainingToday : null,
		mealPlan: state.balances ? state.balances.mealPlanDescription : null,
		message: state.balances ? state.balances.balancesErrorMessage : null,
		alertSeen: state.settings ? state.settings.unofficiallyAcknowledged : false,

		loginState: state.settings ? state.settings.loginState : 'logged-out',
	}
}

function mapDispatch(dispatch): ReduxDispatchProps {
	return {
		updateBalances: force => dispatch(updateBalances(force)),
		hasSeenAcknowledgement: () => dispatch(hasSeenAcknowledgement()),
	}
}

export default connect(mapState, mapDispatch)(BalancesView)

let cellMargin = 10
let cellSidePadding = 10
let cellEdgePadding = 10

let styles = StyleSheet.create({
	stage: {
		backgroundColor: c.iosLightBackground,
		paddingTop: 20,
		paddingBottom: 20,
	},

	common: {
		backgroundColor: c.white,
	},

	balances: {
		borderRightWidth: StyleSheet.hairlineWidth,
		borderRightColor: c.iosSeparator,
	},

	finalCell: {
		borderRightWidth: 0,
	},

	balancesRow: {
		flexDirection: 'row',
		marginTop: 0,
		marginBottom: -10,
	},

	rectangle: {
		height: 88,
		flex: 1,
		alignItems: 'center',
		paddingTop: cellSidePadding,
		paddingBottom: cellSidePadding,
		paddingRight: cellEdgePadding,
		paddingLeft: cellEdgePadding,
		marginBottom: cellMargin,
	},

	// Text styling
	financialText: {
		paddingTop: 8,
		color: c.iosDisabledText,
		textAlign: 'center',
		fontWeight: '200',
		fontSize: 23,
	},
	rectangleButtonText: {
		paddingTop: 15,
		color: c.black,
		textAlign: 'center',
		fontSize: 16,
	},
})

function getValueOrNa(value: ?string): string {
	// eslint-disable-next-line no-eq-null
	if (value == null) {
		return 'N/A'
	}
	return value
}

function FormattedValueCell(props: {
	indeterminate: boolean,
	label: string,
	value: ?string,
	style?: any,
	formatter: (?string) => string,
}) {
	let {indeterminate, label, value, style, formatter} = props
	return (
		<View style={[styles.rectangle, styles.common, styles.balances, style]}>
			<Text
				autoAdjustsFontSize={true}
				selectable={true}
				style={styles.financialText}
			>
				{indeterminate ? '…' : formatter(value)}
			</Text>
			<Text autoAdjustsFontSize={true} style={styles.rectangleButtonText}>
				{label}
			</Text>
		</View>
	)
}
<|endoftext|>


<commit_before>import _     from 'lodash';
import error from '../../../lib/error';

const routePartCleanRx = /[\(\/\:\)\*]/g;

export default (route, parentRoute) => {
  let {path} = route;
  let parts = path.match(/\/?[^\/]+/g);
  let {hasSplat, hasOptional, parts: parentParts = []} = parentRoute;
  let names = parentParts.reduce((names, part) => {
    if(!_.isUndefined(part.required)) {
      names.push(part.name);
    }
    return names;
  }, []);

  parts = !parts ? [] : parts.map(part => {
    part = part.replace(/[\s\/]+/g, '');

    if(!part.match(/[\*\:]/g)) {
      if(hasSplat || hasOptional) {
        error.throw('cannot declare a regular route part after a splat or optional parameter', 'routing');
      }

      return {name: part.replace(routePartCleanRx, '')};
    }

    let paramParts = part.split(':');
    let isSplat = part.startsWith('*');
    let required = isSplat || !part.includes(')');

    if(isSplat) {
      if(hasSplat) {
        error.throw('route can have only one splat', 'routing');
      }

      if(hasOptional) {
        error.throw('splat cannot be declared after an optional parameter', 'routing');
      }
    }

    if(required) {
      if(hasSplat || hasOptional) {
        error.throw('required parameter cannot be declared after a splat or optional parameter', 'routing');
      }
    }

    if(!required && hasSplat) {
      error.throw('optional parameter cannot be declared after a splat', 'routing');
    }

    if(!hasSplat && isSplat) {
      route.hasSplat = hasSplat = isSplat;
    }

    if(!hasOptional && !required) {
      route.hasOptional = hasOptional = !required;
    }

    let prefix = isSplat ? '' : paramParts[0];
    let name = isSplat ? part.slice(1) : paramParts[1].replace(routePartCleanRx, '');

    if(names.includes(name)) {
      error.throw(`parameter [${name}] has already been declared`, 'routing');
    }

    names.push(name);

    return {required, prefix, name};
  });

  _.extend(route, {parts: parentParts.concat(parts)});
};
<commit_msg>Make splats optional<commit_after>import _     from 'lodash';
import error from '../../../lib/error';

const routePartCleanRx = /[\(\/\:\)\*]/g;

export default (route, parentRoute) => {
  let {path} = route;
  let parts = path.match(/\/?[^\/]+/g);
  let {hasSplat, hasOptional, parts: parentParts = []} = parentRoute;
  let names = parentParts.reduce((names, part) => {
    if(!_.isUndefined(part.required)) {
      names.push(part.name);
    }
    return names;
  }, []);

  parts = !parts ? [] : parts.map(part => {
    part = part.replace(/[\s\/]+/g, '');

    if(!part.match(/[\*\:]/g)) {
      if(hasSplat || hasOptional) {
        error.throw('cannot declare a regular route part after a splat or optional parameter', 'routing');
      }

      return {name: part.replace(routePartCleanRx, '')};
    }

    let paramParts = part.split(':');
    let isSplat = part.startsWith('*');
    let required = !part.includes(')') && !isSplat;

    if(isSplat) {
      if(hasSplat) {
        error.throw('route can have only one splat', 'routing');
      }

      if(hasOptional) {
        error.throw('splat cannot be declared after an optional parameter', 'routing');
      }
    }

    if(required) {
      if(hasSplat || hasOptional) {
        error.throw('required parameter cannot be declared after a splat or optional parameter', 'routing');
      }
    }

    if(!required && hasSplat) {
      error.throw('optional parameter cannot be declared after a splat', 'routing');
    }

    if(!hasSplat && isSplat) {
      route.hasSplat = hasSplat = isSplat;
    }

    if(!hasOptional && !required) {
      route.hasOptional = hasOptional = !required;
    }

    let prefix = isSplat ? '' : paramParts[0];
    let name = isSplat ? part.slice(1) : paramParts[1].replace(routePartCleanRx, '');

    if(names.includes(name)) {
      error.throw(`parameter [${name}] has already been declared`, 'routing');
    }

    names.push(name);

    return {required, prefix, name};
  });

  _.extend(route, {parts: parentParts.concat(parts)});
};
<|endoftext|>


<commit_before>// requires FreeFlightAnalyticsRecording
// requires FreeFlightAnalyticsRecording.ScreenshotModule
// requires jQuery
FreeFlightAnalyticsRecording.ScreenRecordingModule = {};

// list of screenshot pngs to send to backend to create gifs from
FreeFlightAnalyticsRecording.ScreenRecordingModule.screenshotList = [];

/** start recording by saving a list of pngs
 *  @param duration the number of milliseconds to record for
 *  @param framerate the framerate at which to record at
 */
FreeFlightAnalyticsRecording.ScreenRecordingModule.startRecording = function(duration, framerate) {
  var endTime = Date.now() + duration;
  // take screenshots until duration has been reached
  while(Date.now() < endTime) {
    // push a screenshot to the screenshot list according to the framerate
    setTimeout(function() {
      var canvas;
      FreeFlightAnalyticsRecording.ScreenshotModule.takeScreenshot(function(canvasObj) {
        canvas = canvasObj;
      });
      var image = canvas.toDataURL("image/png");
      FreeFlightAnalyticsRecording.ScreenRecordingModule.screenshotList.push(image);
    }, 1000 / framerate);
  } // end while

  // send data to back end
  $.ajax({
    type: 'POST',
    url: FreeFlightAnalyticsRecording.RouteMap.screenRecordingURL,
    data: {framerate: framerate, screenshotList: FreeFlightAnalyticsRecording.screenshotList}
  });
}

<commit_msg>woops fixed callback<commit_after>// requires FreeFlightAnalyticsRecording
// requires FreeFlightAnalyticsRecording.ScreenshotModule
// requires jQuery
FreeFlightAnalyticsRecording.ScreenRecordingModule = {};

// list of screenshot pngs to send to backend to create gifs from
FreeFlightAnalyticsRecording.ScreenRecordingModule.screenshotList = [];

/** start recording by saving a list of pngs
 *  @param duration the number of milliseconds to record for
 *  @param framerate the framerate at which to record at
 */
FreeFlightAnalyticsRecording.ScreenRecordingModule.startRecording = function(duration, framerate) {
  var endTime = Date.now() + duration;
  // take screenshots until duration has been reached
  while(Date.now() < endTime) {
    // push a screenshot to the screenshot list according to the framerate
    setTimeout(function() {
      FreeFlightAnalyticsRecording.ScreenshotModule.takeScreenshot(function(canvas) {
        var image = canvas.toDataURL("image/png");
        FreeFlightAnalyticsRecording.ScreenRecordingModule.screenshotList.push(image);
      });
    }, 1000 / framerate);
  } // end while

  // send data to back end
  $.ajax({
    type: 'POST',
    url: FreeFlightAnalyticsRecording.RouteMap.screenRecordingURL,
    data: {framerate: framerate, screenshotList: FreeFlightAnalyticsRecording.screenshotList}
  });
}<|endoftext|>