<commit_before>// Hash that expires keys when they have not been used long time ago.
// As data it keeps a key/Value pair with additional info as "last_time_used"
//
// So the interface is as a hash(set, get, delete), but the creation gives a size parameter that
// manages this attributes.
//
if(typeof(AutomaticDictionary) === "undefined" ){
    var AutomaticDictionary = {};
}

AutomaticDictionary.Lib = {}

/**
    LRUHash: Hash with max size and LRU expiration

    Interface:

        set( k, v ) => bool
        get( k, v ) => bool
        size( )  => int
        serialize( ) => string
        load( string ) => void

LRUHash
 * Costs: all operations are O(n)
 * Size of the object is:
   k = average key size
   m = average value size
   n = size of the hash

   Size: k*n + (k+m)*n = (2k+m)*n  => O(n)
*/
AutomaticDictionary.Lib.LRUHash = function( hash, options ){
    options = options || {};
    this.max_size = options.size || null;
    this.sorted_keys = options["sorted_keys"] ||[]; // Its an array with the keys ordered. Oldest first!
    this.hash = hash;
}
AutomaticDictionary.Lib.LRUHash.prototype = {
    max_size: null,
    sorted_keys: [],
    hash: {},

    //Defines or updates
    // O(n)
    set: function( key, value ){
        this.hash[key] = value;
        //Update keys
        this.sk_update_key( key );
        //Expire key if size reached
        if( this.max_size !== null && this.sorted_keys.length > this.max_size ){
            var key = this.sorted_keys[0];
            delete(this.hash[key]);
            this.sk_remove_key( key );
        }
    },
    // O(n)
    get: function( key ){
        //Update keys order
        if( this.hash[key] !== undefined ){
            this.sk_update_key( key );
        }
        return this.hash[key];
    },
    // O(n)
    sk_update_key: function(key){
        this.sk_remove_key( key );
        this.sorted_keys.push( key );
    },
    // O(n)
    sk_remove_key: function( key ){
        var idx = this.sorted_keys.indexOf( key );
        if( idx !== -1 ){
            //Remove element O(n)!! and indexOf can be O(n) too!
            var oldarr = this.sorted_keys;
            this.sorted_keys = new Array();
            for(var i=0; i< oldarr.length; i++ ){
                if( i !== idx ){
                    this.sorted_keys.push( oldarr[ i ] );
                }
            }
        }
    },
    // O(1)
    size: function(){
        return this.sorted_keys.length;
    },
    // O(n)
    serialize: function(){
        var out = this.hash.toSource();
        out += ",";
        out += { "sorted_keys": this.sorted_keys, "size": this.max_size }.toSource();
        return "new AutomaticDictionary.Lib.LRUHash(" + out + ")";
    }
}

/*

SortedSet

    As a set, has no repeated values and stores the order in which they were
    added

    Interface

        add( v ) => bool
            The last() will be v since now
        contains( v ) => bool
        size() => int
        remove( v ) => bool
        toArray() => array
        first() => element
        last() => element

    Costs:
        * All operations are O(logn) or better, except toArray O(n)
*/
AutomaticDictionary.Lib.SortedSet = function(){
    var num_nodes = 0;
    var first = null;
    var last = null;
    var nodes = {};
    return {
        // O(logn)
        contains: function( elem ){
            return nodes[ elem ] != undefined;
        },
        // O(1)
        first: function(){
            if( first ){
                return first.v;
            }
            return null;
        },
        // O(logn)
        push: function( elem ){
            if( this.contains( elem ) ){
                this.remove(elem);
            }
            var node = {p: null, n:null,v:elem};
            nodes[ elem ] = node;
            num_nodes++;
            logger.debug("LL: incrementing num_nodes: "+num_nodes);
            if( !first ){
                first = node;
            }
            if( !last ){
                last = node;
            }else{
                //We add this to the last.
                last.n = node;
                node.p = last;
                last = node;
            }
        },
        // O(1)
        size: function(){
            return num_nodes;
        },
        // O(n)
        toArray: function(){
            var curr = first;
            var res = [];
            while( curr ){
                res.push( curr.v );
                curr = curr.n;
            }
            return res;
        },
        // @return boolean element already existed
        // O(logn)
        remove: function(elem){
            var node = nodes[ elem ];
            //unlink
            if( node === undefined ) return false;
            num_nodes--;
            logger.debug("LL: dec num_nodes: "+num_nodes);
            if( node === last ){
                last = last.p;
            }
            if( node === first ){
                first = first.n;
            }
            if( node.p ){
                node.p.n = node.n;
            }
            if( node.n ){
                node.n.p = node.p;
            }
            delete( nodes[elem] );
            return true;
        }
    }
}

//IMPLEMENT second version

AutomaticDictionary.Lib.LRUHashV2 = function( hash, options ){
    options = options || {};
    this.max_size = options.size || null;
    this.sorted_keys = AutomaticDictionary.Lib.SortedSet();
    var key_base = hash;
    if( options["sorted_keys"] ){
        key_base = options["sorted_keys"];
    }
    //Iterate over hash
    for( var idx in key_base ){
        this.sorted_keys.push( hash[idx] );
    }

    this.hash = hash;
}

AutomaticDictionary.Lib.LRUHashV2.prototype = {
    max_size: null,
    sorted_keys: null,
    hash: {},

    //Defines or updates
    // O(n)
    set: function( key, value ){
        logger.debug("Set "+key );
        //Update keys
        this.sk_update_key( key );
        //Expire key if size reached
        if( this.max_size !== null && this.size() > this.max_size ){
            var key_to_expire = this.sorted_keys.first();
            logger.debug("expiring key "+key_to_expire);
            delete(this.hash[key_to_expire]);
            this.sk_remove_key( key_to_expire );
        }
        this.hash[key] = value;
    },
    // O(n)
    get: function( key ){
        logger.debug("Get "+key );
        //Update keys order
        if( this.hash[key] !== undefined ){
            logger.debug("on a get we get "+this.hash[key]);
            this.sk_update_key( key );
        }
        return this.hash[key];
    },
    // O(n)
    sk_update_key: function(key){
        logger.debug("updating usage of "+key);
        this.sk_remove_key( key );
        this.sorted_keys.push( key );
    },
    // O(n)
    sk_remove_key: function( key ){
        this.sorted_keys.remove( key );
    },
    // O(1)
    size: function(){
        return this.sorted_keys.size();
    },
    // O(n)
    // O(n)
    serialize: function(){
        var out = this.hash.toSource();
        out += ",";
        out += { "sorted_keys": this.sorted_keys.toArray(), "size": this.max_size }.toSource();
        return "new AutomaticDictionary.Lib.LRUHash(" + out + ")";
    }

}

<commit_msg>change logger to be a stub on production or a optional parameter to the data structure<commit_after>// Hash that expires keys when they have not been used long time ago.
// As data it keeps a key/Value pair with additional info as "last_time_used"
//
// So the interface is as a hash(set, get, delete), but the creation gives a size parameter that
// manages this attributes.
//
if(typeof(AutomaticDictionary) === "undefined" ){
    var AutomaticDictionary = {};
}

AutomaticDictionary.Lib = {
    // Stub to call logger when not defined
    LoggerStub: {
        debug: function(){},
        performance: function(){},
        info: function(){},
        warn: function(){},
        error: function(){}
    }
}

/**
    LRUHash: Hash with max size and LRU expiration

    Interface:

        set( k, v ) => bool
        get( k, v ) => bool
        size( )  => int
        serialize( ) => string
        load( string ) => void

LRUHash
 * Costs: all operations are O(n)
 * Size of the object is:
   k = average key size
   m = average value size
   n = size of the hash

   Size: k*n + (k+m)*n = (2k+m)*n  => O(n)
*/
AutomaticDictionary.Lib.LRUHash = function( hash, options ){
    options = options || {};
    this.max_size = options.size || null;
    this.sorted_keys = options["sorted_keys"] ||[]; // Its an array with the keys ordered. Oldest first!
    this.hash = hash;
    this.logger = options["logger"];
}
AutomaticDictionary.Lib.LRUHash.prototype = {
    max_size: null,
    sorted_keys: [],
    hash: {},

    //Defines or updates
    // O(n)
    set: function( key, value ){
        this.hash[key] = value;
        //Update keys
        this.sk_update_key( key );
        //Expire key if size reached
        if( this.max_size !== null && this.sorted_keys.length > this.max_size ){
            var key = this.sorted_keys[0];
            delete(this.hash[key]);
            this.sk_remove_key( key );
        }
    },
    // O(n)
    get: function( key ){
        //Update keys order
        if( this.hash[key] !== undefined ){
            this.sk_update_key( key );
        }
        return this.hash[key];
    },
    // O(n)
    sk_update_key: function(key){
        this.sk_remove_key( key );
        this.sorted_keys.push( key );
    },
    // O(n)
    sk_remove_key: function( key ){
        var idx = this.sorted_keys.indexOf( key );
        if( idx !== -1 ){
            //Remove element O(n)!! and indexOf can be O(n) too!
            var oldarr = this.sorted_keys;
            this.sorted_keys = new Array();
            for(var i=0; i< oldarr.length; i++ ){
                if( i !== idx ){
                    this.sorted_keys.push( oldarr[ i ] );
                }
            }
        }
    },
    // O(1)
    size: function(){
        return this.sorted_keys.length;
    },
    // O(n)
    serialize: function(){
        var out = this.hash.toSource();
        out += ",";
        out += { "sorted_keys": this.sorted_keys, "size": this.max_size }.toSource();
        return "new AutomaticDictionary.Lib.LRUHash(" + out + ")";
    }
}

/*

SortedSet

    As a set, has no repeated values and stores the order in which they were
    added

    Interface

        add( v ) => bool
            The last() will be v since now
        contains( v ) => bool
        size() => int
        remove( v ) => bool
        toArray() => array
        first() => element
        last() => element

    Costs:
        * All operations are O(logn) or better, except toArray O(n)
*/
AutomaticDictionary.Lib.SortedSet = function( options ){
    options = options || {};
    var num_nodes = 0;
    var first = null;
    var last = null;
    var nodes = {};
    var logger = options["logger"] || AutomaticDictionary.Lib.LoggerStub;
    return {
        // O(logn)
        contains: function( elem ){
            return nodes[ elem ] != undefined;
        },
        // O(1)
        first: function(){
            if( first ){
                return first.v;
            }
            return null;
        },
        // O(logn)
        push: function( elem ){
            if( this.contains( elem ) ){
                this.remove(elem);
            }
            var node = {p: null, n:null,v:elem};
            nodes[ elem ] = node;
            num_nodes++;
            logger.debug("LL: incrementing num_nodes: "+num_nodes);
            if( !first ){
                first = node;
            }
            if( !last ){
                last = node;
            }else{
                //We add this to the last.
                last.n = node;
                node.p = last;
                last = node;
            }
        },
        // O(1)
        size: function(){
            return num_nodes;
        },
        // O(n)
        toArray: function(){
            var curr = first;
            var res = [];
            while( curr ){
                res.push( curr.v );
                curr = curr.n;
            }
            return res;
        },
        // @return boolean element already existed
        // O(logn)
        remove: function(elem){
            var node = nodes[ elem ];
            //unlink
            if( node === undefined ) return false;
            num_nodes--;
            logger.debug("LL: dec num_nodes: "+num_nodes);
            if( node === last ){
                last = last.p;
            }
            if( node === first ){
                first = first.n;
            }
            if( node.p ){
                node.p.n = node.n;
            }
            if( node.n ){
                node.n.p = node.p;
            }
            delete( nodes[elem] );
            return true;
        }
    }
}

//IMPLEMENT second version

AutomaticDictionary.Lib.LRUHashV2 = function( hash, options ){
    options = options || {};
    this.max_size = options.size || null;
    this.sorted_keys = AutomaticDictionary.Lib.SortedSet();
    var key_base = hash;
    if( options["sorted_keys"] ){
        key_base = options["sorted_keys"];
    }
    //Iterate over hash
    for( var idx in key_base ){
        this.sorted_keys.push( hash[idx] );
    }

    this.hash = hash;
    this.logger = options["logger"] || AutomaticDictionary.Lib.LoggerStub;
}

AutomaticDictionary.Lib.LRUHashV2.prototype = {
    max_size: null,
    sorted_keys: null,
    hash: {},

    //Defines or updates
    // O(n)
    set: function( key, value ){
        this.logger.debug("Set "+key );
        //Update keys
        this.sk_update_key( key );
        //Expire key if size reached
        if( this.max_size !== null && this.size() > this.max_size ){
            var key_to_expire = this.sorted_keys.first();
            this.logger.debug("expiring key "+key_to_expire);
            delete(this.hash[key_to_expire]);
            this.sk_remove_key( key_to_expire );
        }
        this.hash[key] = value;
    },
    // O(n)
    get: function( key ){
        this.logger.debug("Get "+key );
        //Update keys order
        if( this.hash[key] !== undefined ){
            this.logger.debug("on a get we get "+this.hash[key]);
            this.sk_update_key( key );
        }
        return this.hash[key];
    },
    // O(n)
    sk_update_key: function(key){
        this.logger.debug("updating usage of "+key);
        this.sk_remove_key( key );
        this.sorted_keys.push( key );
    },
    // O(n)
    sk_remove_key: function( key ){
        this.sorted_keys.remove( key );
    },
    // O(1)
    size: function(){
        return this.sorted_keys.size();
    },
    // O(n)
    // O(n)
    serialize: function(){
        var out = this.hash.toSource();
        out += ",";
        out += { "sorted_keys": this.sorted_keys.toArray(), "size": this.max_size }.toSource();
        return "new AutomaticDictionary.Lib.LRUHash(" + out + ")";
    }

}

<|endoftext|>


<commit_before>function AdminGSVCommentView(admin) {
    var self = {};
    self.admin = admin;

    var _init = function() {
        self.panoProp = new PanoProperties();
        _resetModal();
    };

    function _resetModal() {
        var modalText =
            '
' +
                '
' +
                    '
' +
                        '
' +
                            '<button type="button" class="close" data-dismiss="modal" aria-label="Close">×</button>'+
                            '

' +
                        '
' +
                        '
' +
                            '
' +
                        '
' +
                        '
' +
                        '
' +
                    '
' +
                '
' +
            '
';

        self.modal = $(modalText);
        self.panorama = AdminPanorama(self.modal.find("#svholder")[0], self.modal.find("#button-holder"), admin);
    }

    function showCommentGSV(commentGSV, heading, pitch, zoom, labelId) {
        _resetModal();
        self.modal.modal({
            'show': true
        });
        self.panorama.setPano(commentGSV, heading, pitch, zoom);

        if(labelId) {
            var adminLabelUrl = admin ? "/adminapi/label/id/" + labelId : "/label/id/" + labelId;
            $.getJSON(adminLabelUrl, function (data) {
                setLabel(data);
            });
         }
    }

    function setLabel(labelMetadata) {
        var adminPanoramaLabel = AdminPanoramaLabel(labelMetadata['label_id'], labelMetadata['label_type_key'],
            labelMetadata['canvas_x'], labelMetadata['canvas_y'],
            labelMetadata['canvas_width'], labelMetadata['canvas_height'], labelMetadata['heading'],
            labelMetadata['pitch'], labelMetadata['zoom']);
        self.panorama.setLabel(adminPanoramaLabel);
    }

    _init();
    self.showCommentGSV = showCommentGSV;
    return self;
}
<commit_msg>renamed parameter<commit_after>function AdminGSVCommentView(admin) {
    var self = {};
    self.admin = admin;

    var _init = function() {
        self.panoProp = new PanoProperties();
        _resetModal();
    };

    function _resetModal() {
        var modalText =
            '
' +
                '
' +
                    '
' +
                        '
' +
                            '<button type="button" class="close" data-dismiss="modal" aria-label="Close">×</button>'+
                            '

' +
                        '
' +
                        '
' +
                            '
' +
                        '
' +
                        '
' +
                        '
' +
                    '
' +
                '
' +
            '
';

        self.modal = $(modalText);
        self.panorama = AdminPanorama(self.modal.find("#svholder")[0], self.modal.find("#button-holder"), admin);
    }

    function showCommentGSV(panoId, heading, pitch, zoom, labelId) {
        _resetModal();
        self.modal.modal({
            'show': true
        });
        self.panorama.setPano(panoId, heading, pitch, zoom);

        if(labelId) {
            var adminLabelUrl = admin ? "/adminapi/label/id/" + labelId : "/label/id/" + labelId;
            $.getJSON(adminLabelUrl, function (data) {
                setLabel(data);
            });
         }
    }

    function setLabel(labelMetadata) {
        var adminPanoramaLabel = AdminPanoramaLabel(labelMetadata['label_id'], labelMetadata['label_type_key'],
            labelMetadata['canvas_x'], labelMetadata['canvas_y'],
            labelMetadata['canvas_width'], labelMetadata['canvas_height'], labelMetadata['heading'],
            labelMetadata['pitch'], labelMetadata['zoom']);
        self.panorama.setLabel(adminPanoramaLabel);
    }

    _init();
    self.showCommentGSV = showCommentGSV;
    return self;
}
<|endoftext|>


<commit_before>/**
* Copyright 2012-2017, Plotly, Inc.
* All rights reserved.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

'use strict';

var createREGL = require('regl');
var glslify = require('glslify');
var verticalPadding = require('./constants').verticalPadding;
var vertexShaderSource = glslify('./shaders/vertex.glsl');
var pickVertexShaderSource = glslify('./shaders/pick_vertex.glsl');
var fragmentShaderSource = glslify('./shaders/fragment.glsl');

var depthLimitEpsilon = 1e-6; // don't change; otherwise near/far plane lines are lost

var gpuDimensionCount = 64;
var sectionVertexCount = 2;
var vec4NumberCount = 4;

var contextColor = [119, 119, 119]; // middle gray to not drawn the focus; looks good on a black or white background

var dummyPixel = new Uint8Array(4);
var pickPixel = new Uint8Array(4);

function ensureDraw(regl) {
    regl.read({
        x: 0,
        y: 0,
        width: 1,
        height: 1,
        data: dummyPixel
    });
}

function clear(regl, x, y, width, height) {
    var gl = regl._gl;
    gl.enable(gl.SCISSOR_TEST);
    gl.scissor(x, y, width, height);
    regl.clear({color: [0, 0, 0, 0], depth: 1}); // clearing is done in scissored panel only
}

function renderBlock(regl, glAes, renderState, blockLineCount, sampleCount, item) {

    var rafKey = item.key;

    function render(blockNumber) {

        var count;

        count = Math.min(blockLineCount, sampleCount - blockNumber * blockLineCount);

        item.offset = sectionVertexCount * blockNumber * blockLineCount;
        item.count = sectionVertexCount * count;
        if(blockNumber === 0) {
            window.cancelAnimationFrame(renderState.currentRafs[rafKey]); // stop drawing possibly stale glyphs before clearing
            delete renderState.currentRafs[rafKey];
            clear(regl, item.scissorX, item.scissorY, item.scissorWidth, item.viewBoxSize[1]);
        }

        if(renderState.clearOnly) {
            return;
        }

        glAes(item);

        if((blockNumber + 1) * blockLineCount + count < sampleCount) {
            renderState.currentRafs[rafKey] = window.requestAnimationFrame(function() {
                render(blockNumber + 1);
            });
        }

        renderState.drawCompleted = false;
    }

    if(!renderState.drawCompleted) {
        ensureDraw(regl);
        renderState.drawCompleted = true;
    }

    // start with rendering item 0; recursion handles the rest
    render(0);
}

function adjustDepth(d) {
    // WebGL matrix operations use floats with limited precision, potentially causing a number near a border of [0, 1]
    // to end up slightly outside the border. With an epsilon, we reduce the chance that a line gets clipped by the
    // near or the far plane.
    return Math.max(depthLimitEpsilon, Math.min(1 - depthLimitEpsilon, d));
}

function palette(unitToColor, context, opacity) {
    var result = [];
    for(var j = 0; j < 256; j++) {
        var c = unitToColor(j / 255);
        result.push((context ? contextColor : c).concat(opacity));
    }

    return result;
}

// Maps the sample index [0...sampleCount - 1] to a range of [0, 1] as the shader expects colors in the [0, 1] range.
// but first it shifts the sample index by 0, 8 or 16 bits depending on rgbIndex [0..2]
// with the end result that each line will be of a unique color, making it possible for the pick handler
// to uniquely identify which line is hovered over (bijective mapping).
// The inverse, i.e. readPixel is invoked from 'parcoords.js'
function calcPickColor(j, rgbIndex) {
    return (j >>> 8 * rgbIndex) % 256 / 255;
}

function makePoints(sampleCount, dimensionCount, dimensions, color) {

    var points = [];
    for(var j = 0; j < sampleCount; j++) {
        for(var i = 0; i < gpuDimensionCount; i++) {
            points.push(i < dimensionCount ?
                dimensions[i].paddedUnitValues[j] :
                i === (gpuDimensionCount - 1) ?
                    adjustDepth(color[j]) :
                    i >= gpuDimensionCount - 4 ?
                        calcPickColor(j, gpuDimensionCount - 2 - i) :
                        0.5);
        }
    }

    return points;
}

function makeVecAttr(sampleCount, points, vecIndex) {

    var i, j, k;
    var pointPairs = [];

    for(j = 0; j < sampleCount; j++) {
        for(k = 0; k < sectionVertexCount; k++) {
            for(i = 0; i < vec4NumberCount; i++) {
                pointPairs.push(points[j * gpuDimensionCount + vecIndex * vec4NumberCount + i]);
                if(vecIndex * vec4NumberCount + i === gpuDimensionCount - 1 && k % 2 === 0) {
                    pointPairs[pointPairs.length - 1] *= -1;
                }
            }
        }
    }

    return pointPairs;
}

function makeAttributes(sampleCount, points) {

    var vecIndices = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15];
    var vectors = vecIndices.map(function(vecIndex) {return makeVecAttr(sampleCount, points, vecIndex);});

    var attributes = {};
    vectors.forEach(function(v, vecIndex) {
        attributes['p' + vecIndex.toString(16)] = v;
    });

    return attributes;
}

function valid(i, offset, panelCount) {
    return i + offset <= panelCount;
}

module.exports = function(canvasGL, lines, canvasWidth, canvasHeight, initialDimensions, initialPanels, unitToColor, context, pick, scatter) {

    var renderState = {
        currentRafs: {},
        drawCompleted: true,
        clearOnly: false
    };

    var initialDims = initialDimensions.slice();

    var dimensionCount = initialDims.length;
    var sampleCount = initialDims[0] ? initialDims[0].values.length : 0;

    var focusAlphaBlending = context;

    var color = pick ? lines.color.map(function(_, i) {return i / lines.color.length;}) : lines.color;
    var contextOpacity = Math.max(1 / 255, Math.pow(1 / color.length, 1 / 3));
    var overdrag = lines.canvasOverdrag;

    var panelCount = initialPanels.length;

    var points = makePoints(sampleCount, dimensionCount, initialDims, color);
    var attributes = makeAttributes(sampleCount, points);

    var regl = createREGL({
        canvas: canvasGL,
        attributes: {
            preserveDrawingBuffer: true,
            antialias: !pick
        }
    });

    var paletteTexture = regl.texture({
        shape: [256, 1],
        format: 'rgba',
        type: 'uint8',
        mag: 'nearest',
        min: 'nearest',
        data: palette(unitToColor, context, Math.round((context ? contextOpacity : 1) * 255))
    });

    var glAes = regl({

        profile: false,

        blend: {
            enable: focusAlphaBlending,
            func: {
                srcRGB: 'src alpha',
                dstRGB: 'one minus src alpha',
                srcAlpha: 1,
                dstAlpha: 1 // 'one minus src alpha'
            },
            equation: {
                rgb: 'add',
                alpha: 'add'
            },
            color: [0, 0, 0, 0]
        },

        depth: {
            enable: !focusAlphaBlending,
            mask: true,
            func: 'less',
            range: [0, 1]
        },

        // for polygons
        cull: {
            enable: true,
            face: 'back'
        },

        scissor: {
            enable: true,
            box: {
                x: regl.prop('scissorX'),
                y: regl.prop('scissorY'),
                width: regl.prop('scissorWidth'),
                height: regl.prop('scissorHeight')
            }
        },

        dither: false,

        vert: pick ? pickVertexShaderSource : vertexShaderSource,

        frag: fragmentShaderSource,

        primitive: 'lines',
        lineWidth: 1,
        attributes: attributes,
        uniforms: {
            resolution: regl.prop('resolution'),
            viewBoxPosition: regl.prop('viewBoxPosition'),
            viewBoxSize: regl.prop('viewBoxSize'),
            dim1A: regl.prop('dim1A'),
            dim2A: regl.prop('dim2A'),
            dim1B: regl.prop('dim1B'),
            dim2B: regl.prop('dim2B'),
            dim1C: regl.prop('dim1C'),
            dim2C: regl.prop('dim2C'),
            dim1D: regl.prop('dim1D'),
            dim2D: regl.prop('dim2D'),
            loA: regl.prop('loA'),
            hiA: regl.prop('hiA'),
            loB: regl.prop('loB'),
            hiB: regl.prop('hiB'),
            loC: regl.prop('loC'),
            hiC: regl.prop('hiC'),
            loD: regl.prop('loD'),
            hiD: regl.prop('hiD'),
            palette: paletteTexture,
            colorClamp: regl.prop('colorClamp'),
            scatter: regl.prop('scatter')
        },
        offset: regl.prop('offset'),
        count: regl.prop('count')
    });

    var colorClamp = [0, 1];

    function setColorDomain(unitDomain) {
        colorClamp[0] = unitDomain[0];
        colorClamp[1] = unitDomain[1];
    }

    var previousAxisOrder = [];

    function makeItem(i, ii, x, y, panelSizeX, canvasPanelSizeY, crossfilterDimensionIndex, scatter, I, leftmost, rightmost) {
        var loHi, abcd, d, index;
        var leftRight = [i, ii];
        var filterEpsilon = verticalPadding / canvasPanelSizeY;

        var dims = [0, 1].map(function() {return [0, 1, 2, 3].map(function() {return new Float32Array(16);});});
        var lims = [0, 1].map(function() {return [0, 1, 2, 3].map(function() {return new Float32Array(16);});});

        for(loHi = 0; loHi < 2; loHi++) {
            index = leftRight[loHi];
            for(abcd = 0; abcd < 4; abcd++) {
                for(d = 0; d < 16; d++) {
                    var dimP = d + 16 * abcd;
                    dims[loHi][abcd][d] = d + 16 * abcd === index ? 1 : 0;
                    lims[loHi][abcd][d] = (!context && valid(d, 16 * abcd, panelCount) ? initialDims[dimP === 0 ? 0 : 1 + ((dimP - 1) % (initialDims.length - 1))].filter[loHi] : loHi) + (2 * loHi - 1) * filterEpsilon;
                }
            }
        }

        return {
            key: crossfilterDimensionIndex,
            resolution: [canvasWidth, canvasHeight],
            viewBoxPosition: [x + overdrag, y],
            viewBoxSize: [panelSizeX, canvasPanelSizeY],
            i: i,
            ii: ii,

            dim1A: dims[0][0],
            dim1B: dims[0][1],
            dim1C: dims[0][2],
            dim1D: dims[0][3],
            dim2A: dims[1][0],
            dim2B: dims[1][1],
            dim2C: dims[1][2],
            dim2D: dims[1][3],

            loA: lims[0][0],
            loB: lims[0][1],
            loC: lims[0][2],
            loD: lims[0][3],
            hiA: lims[1][0],
            hiB: lims[1][1],
            hiC: lims[1][2],
            hiD: lims[1][3],

            colorClamp: colorClamp,
            scatter: scatter || 0,
            scissorX: I === leftmost ? 0 : x + overdrag,
            scissorWidth: (I === rightmost ? canvasWidth - x + overdrag : panelSizeX + 0.5) + (I === leftmost ? x + overdrag : 0),
            scissorY: y,
            scissorHeight: canvasPanelSizeY
        };
    }

    function renderGLParcoords(panels, setChanged, clearOnly) {

        var I;

        var leftmost, rightmost, lowestX = Infinity, highestX = -Infinity;

        for(I = 0; I < panelCount; I++) {
            if(panels[I].dim2.canvasX > highestX) {
                highestX = panels[I].dim2.canvasX;
                rightmost = I;
            }
            if(panels[I].dim1.canvasX < lowestX) {
                lowestX = panels[I].dim1.canvasX;
                leftmost = I;
            }
        }

        if(panelCount === 0) {
            // clear canvas here, as the panel iteration below will not enter the loop body
            clear(regl, 0, 0, canvasWidth, canvasHeight);
        }

        for(I = 0; I < panelCount; I++) {
            var panel = panels[I];
            var dim1 = panel.dim1;
            var i = dim1.crossfilterDimensionIndex;
            var x = panel.canvasX;
            var y = panel.canvasY;
            var dim2 = panel.dim2;
            var ii = dim2.crossfilterDimensionIndex;
            var panelSizeX = panel.panelSizeX;
            var panelSizeY = panel.panelSizeY;
            var xTo = x + panelSizeX;
            if(setChanged || !previousAxisOrder[i] || previousAxisOrder[i][0] !== x || previousAxisOrder[i][1] !== xTo) {
                previousAxisOrder[i] = [x, xTo];
                var item = makeItem(i, ii, x, y, panelSizeX, panelSizeY, dim1.crossfilterDimensionIndex, scatter || dim1.scatter ? 1 : 0, I, leftmost, rightmost);
                renderState.clearOnly = clearOnly;
                renderBlock(regl, glAes, renderState, setChanged ? lines.blockLineCount : sampleCount, sampleCount, item);
            }
        }
    }

    function readPixel(canvasX, canvasY) {
        regl.read({
            x: canvasX,
            y: canvasY,
            width: 1,
            height: 1,
            data: pickPixel
        });
        return pickPixel;
    }

    function readPixels(canvasX, canvasY, width, height) {
        var pixelArray = new Uint8Array(4 * width * height);
        regl.read({
            x: canvasX,
            y: canvasY,
            width: width,
            height: height,
            data: pixelArray
        });
        return pixelArray;
    }

    return {
        setColorDomain: setColorDomain,
        render: renderGLParcoords,
        readPixel: readPixel,
        readPixels: readPixels,
        destroy: regl.destroy
    };
};
<commit_msg>Parcoords incremental rendering off by one error fix<commit_after>/**
* Copyright 2012-2017, Plotly, Inc.
* All rights reserved.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

'use strict';

var createREGL = require('regl');
var glslify = require('glslify');
var verticalPadding = require('./constants').verticalPadding;
var vertexShaderSource = glslify('./shaders/vertex.glsl');
var pickVertexShaderSource = glslify('./shaders/pick_vertex.glsl');
var fragmentShaderSource = glslify('./shaders/fragment.glsl');

var depthLimitEpsilon = 1e-6; // don't change; otherwise near/far plane lines are lost

var gpuDimensionCount = 64;
var sectionVertexCount = 2;
var vec4NumberCount = 4;

var contextColor = [119, 119, 119]; // middle gray to not drawn the focus; looks good on a black or white background

var dummyPixel = new Uint8Array(4);
var pickPixel = new Uint8Array(4);

function ensureDraw(regl) {
    regl.read({
        x: 0,
        y: 0,
        width: 1,
        height: 1,
        data: dummyPixel
    });
}

function clear(regl, x, y, width, height) {
    var gl = regl._gl;
    gl.enable(gl.SCISSOR_TEST);
    gl.scissor(x, y, width, height);
    regl.clear({color: [0, 0, 0, 0], depth: 1}); // clearing is done in scissored panel only
}

function renderBlock(regl, glAes, renderState, blockLineCount, sampleCount, item) {

    var rafKey = item.key;

    function render(blockNumber) {

        var count;

        count = Math.min(blockLineCount, sampleCount - blockNumber * blockLineCount);

        item.offset = sectionVertexCount * blockNumber * blockLineCount;
        item.count = sectionVertexCount * count;
        if(blockNumber === 0) {
            window.cancelAnimationFrame(renderState.currentRafs[rafKey]); // stop drawing possibly stale glyphs before clearing
            delete renderState.currentRafs[rafKey];
            clear(regl, item.scissorX, item.scissorY, item.scissorWidth, item.viewBoxSize[1]);
        }

        if(renderState.clearOnly) {
            return;
        }

        glAes(item);

        if(blockNumber * blockLineCount + count < sampleCount) {
            renderState.currentRafs[rafKey] = window.requestAnimationFrame(function() {
                render(blockNumber + 1);
            });
        }

        renderState.drawCompleted = false;
    }

    if(!renderState.drawCompleted) {
        ensureDraw(regl);
        renderState.drawCompleted = true;
    }

    // start with rendering item 0; recursion handles the rest
    render(0);
}

function adjustDepth(d) {
    // WebGL matrix operations use floats with limited precision, potentially causing a number near a border of [0, 1]
    // to end up slightly outside the border. With an epsilon, we reduce the chance that a line gets clipped by the
    // near or the far plane.
    return Math.max(depthLimitEpsilon, Math.min(1 - depthLimitEpsilon, d));
}

function palette(unitToColor, context, opacity) {
    var result = [];
    for(var j = 0; j < 256; j++) {
        var c = unitToColor(j / 255);
        result.push((context ? contextColor : c).concat(opacity));
    }

    return result;
}

// Maps the sample index [0...sampleCount - 1] to a range of [0, 1] as the shader expects colors in the [0, 1] range.
// but first it shifts the sample index by 0, 8 or 16 bits depending on rgbIndex [0..2]
// with the end result that each line will be of a unique color, making it possible for the pick handler
// to uniquely identify which line is hovered over (bijective mapping).
// The inverse, i.e. readPixel is invoked from 'parcoords.js'
function calcPickColor(j, rgbIndex) {
    return (j >>> 8 * rgbIndex) % 256 / 255;
}

function makePoints(sampleCount, dimensionCount, dimensions, color) {

    var points = [];
    for(var j = 0; j < sampleCount; j++) {
        for(var i = 0; i < gpuDimensionCount; i++) {
            points.push(i < dimensionCount ?
                dimensions[i].paddedUnitValues[j] :
                i === (gpuDimensionCount - 1) ?
                    adjustDepth(color[j]) :
                    i >= gpuDimensionCount - 4 ?
                        calcPickColor(j, gpuDimensionCount - 2 - i) :
                        0.5);
        }
    }

    return points;
}

function makeVecAttr(sampleCount, points, vecIndex) {

    var i, j, k;
    var pointPairs = [];

    for(j = 0; j < sampleCount; j++) {
        for(k = 0; k < sectionVertexCount; k++) {
            for(i = 0; i < vec4NumberCount; i++) {
                pointPairs.push(points[j * gpuDimensionCount + vecIndex * vec4NumberCount + i]);
                if(vecIndex * vec4NumberCount + i === gpuDimensionCount - 1 && k % 2 === 0) {
                    pointPairs[pointPairs.length - 1] *= -1;
                }
            }
        }
    }

    return pointPairs;
}

function makeAttributes(sampleCount, points) {

    var vecIndices = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15];
    var vectors = vecIndices.map(function(vecIndex) {return makeVecAttr(sampleCount, points, vecIndex);});

    var attributes = {};
    vectors.forEach(function(v, vecIndex) {
        attributes['p' + vecIndex.toString(16)] = v;
    });

    return attributes;
}

function valid(i, offset, panelCount) {
    return i + offset <= panelCount;
}

module.exports = function(canvasGL, lines, canvasWidth, canvasHeight, initialDimensions, initialPanels, unitToColor, context, pick, scatter) {

    var renderState = {
        currentRafs: {},
        drawCompleted: true,
        clearOnly: false
    };

    var initialDims = initialDimensions.slice();

    var dimensionCount = initialDims.length;
    var sampleCount = initialDims[0] ? initialDims[0].values.length : 0;

    var focusAlphaBlending = context;

    var color = pick ? lines.color.map(function(_, i) {return i / lines.color.length;}) : lines.color;
    var contextOpacity = Math.max(1 / 255, Math.pow(1 / color.length, 1 / 3));
    var overdrag = lines.canvasOverdrag;

    var panelCount = initialPanels.length;

    var points = makePoints(sampleCount, dimensionCount, initialDims, color);
    var attributes = makeAttributes(sampleCount, points);

    var regl = createREGL({
        canvas: canvasGL,
        attributes: {
            preserveDrawingBuffer: true,
            antialias: !pick
        }
    });

    var paletteTexture = regl.texture({
        shape: [256, 1],
        format: 'rgba',
        type: 'uint8',
        mag: 'nearest',
        min: 'nearest',
        data: palette(unitToColor, context, Math.round((context ? contextOpacity : 1) * 255))
    });

    var glAes = regl({

        profile: false,

        blend: {
            enable: focusAlphaBlending,
            func: {
                srcRGB: 'src alpha',
                dstRGB: 'one minus src alpha',
                srcAlpha: 1,
                dstAlpha: 1 // 'one minus src alpha'
            },
            equation: {
                rgb: 'add',
                alpha: 'add'
            },
            color: [0, 0, 0, 0]
        },

        depth: {
            enable: !focusAlphaBlending,
            mask: true,
            func: 'less',
            range: [0, 1]
        },

        // for polygons
        cull: {
            enable: true,
            face: 'back'
        },

        scissor: {
            enable: true,
            box: {
                x: regl.prop('scissorX'),
                y: regl.prop('scissorY'),
                width: regl.prop('scissorWidth'),
                height: regl.prop('scissorHeight')
            }
        },

        dither: false,

        vert: pick ? pickVertexShaderSource : vertexShaderSource,

        frag: fragmentShaderSource,

        primitive: 'lines',
        lineWidth: 1,
        attributes: attributes,
        uniforms: {
            resolution: regl.prop('resolution'),
            viewBoxPosition: regl.prop('viewBoxPosition'),
            viewBoxSize: regl.prop('viewBoxSize'),
            dim1A: regl.prop('dim1A'),
            dim2A: regl.prop('dim2A'),
            dim1B: regl.prop('dim1B'),
            dim2B: regl.prop('dim2B'),
            dim1C: regl.prop('dim1C'),
            dim2C: regl.prop('dim2C'),
            dim1D: regl.prop('dim1D'),
            dim2D: regl.prop('dim2D'),
            loA: regl.prop('loA'),
            hiA: regl.prop('hiA'),
            loB: regl.prop('loB'),
            hiB: regl.prop('hiB'),
            loC: regl.prop('loC'),
            hiC: regl.prop('hiC'),
            loD: regl.prop('loD'),
            hiD: regl.prop('hiD'),
            palette: paletteTexture,
            colorClamp: regl.prop('colorClamp'),
            scatter: regl.prop('scatter')
        },
        offset: regl.prop('offset'),
        count: regl.prop('count')
    });

    var colorClamp = [0, 1];

    function setColorDomain(unitDomain) {
        colorClamp[0] = unitDomain[0];
        colorClamp[1] = unitDomain[1];
    }

    var previousAxisOrder = [];

    function makeItem(i, ii, x, y, panelSizeX, canvasPanelSizeY, crossfilterDimensionIndex, scatter, I, leftmost, rightmost) {
        var loHi, abcd, d, index;
        var leftRight = [i, ii];
        var filterEpsilon = verticalPadding / canvasPanelSizeY;

        var dims = [0, 1].map(function() {return [0, 1, 2, 3].map(function() {return new Float32Array(16);});});
        var lims = [0, 1].map(function() {return [0, 1, 2, 3].map(function() {return new Float32Array(16);});});

        for(loHi = 0; loHi < 2; loHi++) {
            index = leftRight[loHi];
            for(abcd = 0; abcd < 4; abcd++) {
                for(d = 0; d < 16; d++) {
                    var dimP = d + 16 * abcd;
                    dims[loHi][abcd][d] = d + 16 * abcd === index ? 1 : 0;
                    lims[loHi][abcd][d] = (!context && valid(d, 16 * abcd, panelCount) ? initialDims[dimP === 0 ? 0 : 1 + ((dimP - 1) % (initialDims.length - 1))].filter[loHi] : loHi) + (2 * loHi - 1) * filterEpsilon;
                }
            }
        }

        return {
            key: crossfilterDimensionIndex,
            resolution: [canvasWidth, canvasHeight],
            viewBoxPosition: [x + overdrag, y],
            viewBoxSize: [panelSizeX, canvasPanelSizeY],
            i: i,
            ii: ii,

            dim1A: dims[0][0],
            dim1B: dims[0][1],
            dim1C: dims[0][2],
            dim1D: dims[0][3],
            dim2A: dims[1][0],
            dim2B: dims[1][1],
            dim2C: dims[1][2],
            dim2D: dims[1][3],

            loA: lims[0][0],
            loB: lims[0][1],
            loC: lims[0][2],
            loD: lims[0][3],
            hiA: lims[1][0],
            hiB: lims[1][1],
            hiC: lims[1][2],
            hiD: lims[1][3],

            colorClamp: colorClamp,
            scatter: scatter || 0,
            scissorX: I === leftmost ? 0 : x + overdrag,
            scissorWidth: (I === rightmost ? canvasWidth - x + overdrag : panelSizeX + 0.5) + (I === leftmost ? x + overdrag : 0),
            scissorY: y,
            scissorHeight: canvasPanelSizeY
        };
    }

    function renderGLParcoords(panels, setChanged, clearOnly) {

        var I;

        var leftmost, rightmost, lowestX = Infinity, highestX = -Infinity;

        for(I = 0; I < panelCount; I++) {
            if(panels[I].dim2.canvasX > highestX) {
                highestX = panels[I].dim2.canvasX;
                rightmost = I;
            }
            if(panels[I].dim1.canvasX < lowestX) {
                lowestX = panels[I].dim1.canvasX;
                leftmost = I;
            }
        }

        if(panelCount === 0) {
            // clear canvas here, as the panel iteration below will not enter the loop body
            clear(regl, 0, 0, canvasWidth, canvasHeight);
        }

        for(I = 0; I < panelCount; I++) {
            var panel = panels[I];
            var dim1 = panel.dim1;
            var i = dim1.crossfilterDimensionIndex;
            var x = panel.canvasX;
            var y = panel.canvasY;
            var dim2 = panel.dim2;
            var ii = dim2.crossfilterDimensionIndex;
            var panelSizeX = panel.panelSizeX;
            var panelSizeY = panel.panelSizeY;
            var xTo = x + panelSizeX;
            if(setChanged || !previousAxisOrder[i] || previousAxisOrder[i][0] !== x || previousAxisOrder[i][1] !== xTo) {
                previousAxisOrder[i] = [x, xTo];
                var item = makeItem(i, ii, x, y, panelSizeX, panelSizeY, dim1.crossfilterDimensionIndex, scatter || dim1.scatter ? 1 : 0, I, leftmost, rightmost);
                renderState.clearOnly = clearOnly;
                renderBlock(regl, glAes, renderState, setChanged ? lines.blockLineCount : sampleCount, sampleCount, item);
            }
        }
    }

    function readPixel(canvasX, canvasY) {
        regl.read({
            x: canvasX,
            y: canvasY,
            width: 1,
            height: 1,
            data: pickPixel
        });
        return pickPixel;
    }

    function readPixels(canvasX, canvasY, width, height) {
        var pixelArray = new Uint8Array(4 * width * height);
        regl.read({
            x: canvasX,
            y: canvasY,
            width: width,
            height: height,
            data: pixelArray
        });
        return pixelArray;
    }

    return {
        setColorDomain: setColorDomain,
        render: renderGLParcoords,
        readPixel: readPixel,
        readPixels: readPixels,
        destroy: regl.destroy
    };
};
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2013 Vanderbilt University, All rights reserved.
 *
 * Author: Tamas Kecskes
 */

define([ "util/assert", "util/guid" ], function (ASSERT, GUID) {
	"use strict";

	var GUID_REGEXP = new RegExp("[a-z0-9]{8}(-[a-z0-9]{4}){3}-[a-z0-9]{12}", 'i');

	function guidCore (_innerCore) {

		//helper functions
		function toInternalGuid (myGuid) {
			return myGuid.replace(/-/g, "");
		}

		function toExternalGuid (myGuid) {
			var out = myGuid.substr(0, 8) + '-' + myGuid.substr(8, 4) + '-' + myGuid.substr(12, 4) + '-' + myGuid.substr(16, 4) + '-' + myGuid.substr(20);
			return out;
		}

		function guidToArray (guid) {
			var array = [];
			for ( var i = 0; i < guid.length / 4; i++) {
				array.push(parseInt(guid.substr(4 * i, 4), 16));
			}
			return array;
		}

		function xorGuids (a, b) {
			var arrayA = guidToArray(a);
			var arrayB = guidToArray(b);

			ASSERT(arrayA.length === arrayB.length);

			var arrayOut = [];
			for ( var i = 0; i < arrayA.length; i++) {
				arrayOut.push(arrayA[i] ^ arrayB[i]);
			}
			for (i = 0; i < arrayOut.length; i++) {
				arrayOut[i] = Number(arrayOut[i]).toString(16);
				var difi = 4 - arrayOut[i].length;
				while (difi > 0) {
					arrayOut[i] = '0' + arrayOut[i];
					difi--;
				}
			}
			return arrayOut.join("");
		}

		var _core = {};
		for ( var i in _innerCore) {
			_core[i] = _innerCore[i];
		}

		//new functions
		_core.getGuid = function (node) {
			var outGuid = _core.getAttribute(node, "_relguid");
			var tempnode = _core.getParent(node);
			while (tempnode) {
				outGuid = xorGuids(outGuid, _core.getAttribute(tempnode, "_relguid"));
				tempnode = _core.getParent(tempnode);
			}
			return toExternalGuid(outGuid);
		};

		_core.setGuid = function (node, guid, callback) {
			ASSERT(GUID_REGEXP.test(guid));
			_core.loadChildren(node, function (err, children) {
				if (err) {
					callback(err);
				} else {
					var newguid = toInternalGuid(guid);
					for ( var i = 0; i < children.length; i++) {
						var oldguid = toInternalGuid(_core.getGuid(children[i]));
						_core.setAttribute(children[i], "_relguid", xorGuids(newguid, oldguid));
					}
					var parent = _core.getParent(node);
					if (parent) {
						_core.setAttribute(node, "_relguid", xorGuids(newguid, toInternalGuid(_core.getGuid(parent))));
					} else {
						_core.setAttribute(node, "_relguid", newguid);
					}
					callback(null);
				}
			});
		};

		//modified functions
		_core.createNode = function (parent, relid, guid) {
			ASSERT(guid === null || guid === undefined || GUID_REGEXP.test(guid));

			var node = _innerCore.createNode(parent, relid);
			guid = guid || GUID();
			guid = toInternalGuid(guid);

			var relguid = "";
			if (parent) {
				relguid = xorGuids(toInternalGuid(_core.getGuid(_core.getParent(node))), guid);
			} else {
				relguid = guid;
			}
			_innerCore.setAttribute(node, "_relguid", relguid);

			return node;
		};

		_core.moveNode = function (node, parent) {
			var newnode = _innerCore.moveNode(node, parent);
			var newguid = toInternalGuid(_core.getGuid(_core.getParent(newnode)));
			newguid = xorGuids(toInternalGuid(_core.getGuid(node)), newguid);
			_core.setAttribute(newnode, "_relguid", newguid);

			return newnode;
		};

		_core.copyNode = function (node, parent) {
			var newnode = _innerCore.copyNode(node, parent);
			_core.setAttribute(newnode, "_relguid", toInternalGuid(GUID()));

			return newnode;
		};

		_core.getAttributeNames = function (node) {
			var names = _innerCore.getAttributeNames(node);
			names.splice(names.indexOf("_relguid"), 1);
			return names;
		};

		return _core;
	}

	return guidCore;
});
<commit_msg>error correction moveNode was imperfect<commit_after>/*
 * Copyright (C) 2013 Vanderbilt University, All rights reserved.
 *
 * Author: Tamas Kecskes
 */

define([ "util/assert", "util/guid" ], function (ASSERT, GUID) {
	"use strict";

	var GUID_REGEXP = new RegExp("[a-z0-9]{8}(-[a-z0-9]{4}){3}-[a-z0-9]{12}", 'i');

	function guidCore (_innerCore) {

		//helper functions
		function toInternalGuid (myGuid) {
			return myGuid.replace(/-/g, "");
		}

		function toExternalGuid (myGuid) {
			var out = myGuid.substr(0, 8) + '-' + myGuid.substr(8, 4) + '-' + myGuid.substr(12, 4) + '-' + myGuid.substr(16, 4) + '-' + myGuid.substr(20);
			return out;
		}

		function guidToArray (guid) {
			var array = [];
			for ( var i = 0; i < guid.length / 4; i++) {
				array.push(parseInt(guid.substr(4 * i, 4), 16));
			}
			return array;
		}

		function xorGuids (a, b) {
			var arrayA = guidToArray(a);
			var arrayB = guidToArray(b);

			ASSERT(arrayA.length === arrayB.length);

			var arrayOut = [];
			for ( var i = 0; i < arrayA.length; i++) {
				arrayOut.push(arrayA[i] ^ arrayB[i]);
			}
			for (i = 0; i < arrayOut.length; i++) {
				arrayOut[i] = Number(arrayOut[i]).toString(16);
				var difi = 4 - arrayOut[i].length;
				while (difi > 0) {
					arrayOut[i] = '0' + arrayOut[i];
					difi--;
				}
			}
			return arrayOut.join("");
		}

		var _core = {};
		for ( var i in _innerCore) {
			_core[i] = _innerCore[i];
		}

		//new functions
		_core.getGuid = function (node) {
			var outGuid = _core.getAttribute(node, "_relguid");
			var tempnode = _core.getParent(node);
			while (tempnode) {
				outGuid = xorGuids(outGuid, _core.getAttribute(tempnode, "_relguid"));
				tempnode = _core.getParent(tempnode);
			}
			return toExternalGuid(outGuid);
		};

		_core.setGuid = function (node, guid, callback) {
			ASSERT(GUID_REGEXP.test(guid));
			_core.loadChildren(node, function (err, children) {
				if (err) {
					callback(err);
				} else {
					var newguid = toInternalGuid(guid);
					for ( var i = 0; i < children.length; i++) {
						var oldguid = toInternalGuid(_core.getGuid(children[i]));
						_core.setAttribute(children[i], "_relguid", xorGuids(newguid, oldguid));
					}
					var parent = _core.getParent(node);
					if (parent) {
						_core.setAttribute(node, "_relguid", xorGuids(newguid, toInternalGuid(_core.getGuid(parent))));
					} else {
						_core.setAttribute(node, "_relguid", newguid);
					}
					callback(null);
				}
			});
		};

		//modified functions
		_core.createNode = function (parent, relid, guid) {
			ASSERT(guid === null || guid === undefined || GUID_REGEXP.test(guid));

			var node = _innerCore.createNode(parent, relid);
			guid = guid || GUID();
			guid = toInternalGuid(guid);

			var relguid = "";
			if (parent) {
				relguid = xorGuids(toInternalGuid(_core.getGuid(_core.getParent(node))), guid);
			} else {
				relguid = guid;
			}
			_innerCore.setAttribute(node, "_relguid", relguid);

			return node;
		};

		_core.moveNode = function (node, parent) {
			var newnode = _innerCore.moveNode(node, parent);
			var newguid = toInternalGuid(_core.getGuid(_core.getParent(newnode)));
			newguid = xorGuids(toInternalGuid(_core.getGuid(newnode)), newguid);
			_core.setAttribute(newnode, "_relguid", newguid);

			return newnode;
		};

		_core.copyNode = function (node, parent) {
			var newnode = _innerCore.copyNode(node, parent);
			_core.setAttribute(newnode, "_relguid", toInternalGuid(GUID()));

			return newnode;
		};

		_core.getAttributeNames = function (node) {
			var names = _innerCore.getAttributeNames(node);
			names.splice(names.indexOf("_relguid"), 1);
			return names;
		};

		return _core;
	}

	return guidCore;
});
<|endoftext|>


<commit_before>import angular from 'angular';
import matches from 'lodash.matches';
import isEmpty from 'lodash.isempty';

import ngModule from '../module';
import { uuid } from '../../core/utils';
import './provider';

class AvDropdownController {

  constructor($element, $attrs, avDropdownConfig, $scope, $timeout, $parse) {

    this.av = { $element, $attrs, avDropdownConfig, $scope, $timeout, $parse };

    this.options = {};
    this.match = null;
    this.ngModel = null;
    this.locals = {};

  }

  init(context) {

    this.ngModel = context.ngModel;

    this.multiple = angular.isDefined(this.av.$attrs.multiple);

    this.options = angular.extend({}, this.av.avDropdownConfig.DEFAULTS, this.av.$scope.$eval(this.av.$attrs.options));

    if (this.isRemoteMultiple()) {
      this.options.multiple = angular.isDefined(this.av.$attrs.multiple);
    }

    if (this.options.query) {

      // Map AvSelectResource into Select2 options
      this.resource = this.options.query;

      // Function used to query results for the search term.
      this.options.query = options => {
        this.query(options);
      };
      // Function used to get the id from the choice object or a string representing the key under which the id is stored.
      this.options.id = this.resource.getId;
      this.options.initSelection = this.resource.initSelection;

    }

    // if element is type input, initSelection is required to use val
    if (this.av.$element.is('input') && !this.options.initSelection) {
      this.options.initSelection = this.initSelection;
    }

  }

  isRemoteMultiple() {
    return angular.isDefined(this.av.$attrs.multiple) && this.av.$element.get(0).tagName.toLowerCase() === 'input';
  }

  initSelection(element, callback) {
    callback();
  }

  setRemoteViewValue(e) {

    let values = this.ngModel.$viewValue;

    if (!angular.isArray(values) || !angular.isObject(values)) {
      values = [];
    }

    if (e.added) {
      // Adding to collection
      values.push(e.added);
    } else {
      // Removing from collection
      const index = values.findIndex(value => matches(e.removed)(value));
      values.splice(index, 1);
    }

    this.ngModel.$setViewValue(values);

  }

  setViewValue(e) {
    this.ngModel.$setViewValue(e.added);
  }

  hashKey(obj, nextUidFn) {
    let key = obj && obj.$$hashKey;

    if (key) {
      if (typeof key === 'function') {
        key = obj.$$hashKey();
      }
      return key;
    }

    const objType = typeof obj;
    if (objType === 'function' || (objType === 'object' && obj !== null)) {
      key = obj.$$hashKey = objType + ':' + (nextUidFn || uuid())();
    } else {
      key = objType + ':' + obj;
    }

    return key;
  }

  getTrackByValueFn(value, locals) {

    if (this.trackBy) {
      return this.trackByFn(this.av.$scope, locals);
    }

    return this.hashKey(value);

  }

  getSelected(model) {

    const self = this;

    if (this.options.query) {
      return 0;
    }

    if (!this.collection) {
      // If we're not using ng-options, the model value is just the raw value of the option,
      // rather than an index, so return it as is.
      return model;
    }

    const optionValues = this.valuesFn(self.av.$scope) || [];
    const optionValuesKeys = this.getOptionValuesKeys(optionValues);

    const index = this.collection.findIndex( item => {
      return angular.equals(model, item);
    });

    const key = (optionValues === optionValuesKeys) ? index : optionValuesKeys[index];
    const value = optionValues[key];
    const locals = self.getLocals(value, key);
    const viewValue = self.viewValueFn(self.av.$scope, locals);
    const selectValue = self.getTrackByValueFn(viewValue, locals);

    return selectValue;

  }

  getMultiSelected(viewValues) {

    const self = this;

    const values = [];

    if (!viewValues) {
      return values;
    }

    if (this.av.$element.get(0).tagName.toLowerCase() !== 'input') {

      viewValues.forEach(viewValue => {

        const selected = self.getSelected(viewValue);

        values.push(selected);

      });

    }

    return values;

  }

  // Wrapper around the query function for Select2.  When the promise resolves
  // the callback
  query(options) {

    this.resource.onQuery(options).then(response => {

      // Callback function that should be called with the result object. The result object:
      //
      // result.results (object) - Array of result objects. The default renderers
      //    expect objects with id and text keys. The id property is required,
      //    even if custom renderers are used. The object may also contain a children
      //    key if hierarchical data is displayed. The object may also contain a disabled
      //    boolean property indicating whether this result can be selected.
      //
      // result.more (boolean) - true if more results are available for the current
      //    search term.
      //
      // results.context (object) - A user-defined object that should be made available
      //    as the context parameter to the query function on subsequent queries to load
      //    more result pages for the same search term. See the description of
      //    options.context parameter.
      options.callback({more: response.more, results: response.results});
    });
  }

  setValue() {

    const self = this;

    const viewValue = this.ngModel.$viewValue;

    let selected = null;
    if (this.multiple) {
      selected = this.getMultiSelected(viewValue);
    } else {
      selected = this.getSelected(viewValue);
    }

    // null === '' for Select2
    selected = (selected === null || selected === 'undefined') ? '' : selected;

    this.av.$timeout(() => {
      self.av.$element.select2('val', selected);
    });
  }

  setValues() {

    let viewValue = this.ngModel.$viewValue;

    if (!angular.isArray(viewValue)) {
      viewValue = [];
    }

    if (!isEmpty(viewValue) && angular.isObject(viewValue[0])) {
      viewValue = this.getMultiSelected(viewValue);
    }

    this.av.$timeout(() => this.av.$element.select2('val', viewValue) );

  }

  ngOptions() {

    this.match = this.av.$attrs.ngOptions.match(this.av.avDropdownConfig.NG_OPTIONS_REGEXP);
    if (!this.match) {
      throw new Error('Invalid ngOptions for avDropdown.  @See https://docs.angularjs.org/api/ng/directive/ngOptions');
    }

    // NG_OPTIONS_REGEXP regex will parse into arrays like below:

    // 1: value expression (valueFn)
    // 2: label expression (displayFn)
    // 3: group by expression (groupByFn)
    // 4: disable when expression (disableWhenFn)
    // 5: array item variable name
    // 6: object item key variable name
    // 7: object item value variable name
    // 8: collection expression
    // 9: track by expression

    // The variable name for the value of the item in the collection
    this.valueName = this.match[5] || this.match[7];

    // An expression that generates the viewValue for an option if there is no label expression
    this.valueFn = this.av.$parse(this.match[2] ? this.match[1] : this.valueName);

    // The variable name for the key of the item in the collection
    this.keyName = this.match[6];

    // An expression that generates the viewValue for an option if there is a label expression
    this.selectAs = / as /.test(this.match[0]) && this.match[1];

    // An expression that generates the viewValue for an option if there is a label expression
    // An expression that is used to track the id of each object in the options collection
    this.trackBy = this.match[9];
    this.selectAsFn = this.selectAs && this.av.$parse(this.selectAs);
    this.viewValueFn = this.selectAsFn || this.valueFn;
    this.trackByFn = this.trackBy && this.av.$parse(this.trackBy);

    this.displayFn = this.av.$parse(this.match[2] || this.match[1]);
    this.groupByFn = this.av.$parse(this.match[3] || '');
    this.disableWhenFn = this.av.$parse(this.match[4] || '');
    this.valuesFn = this.av.$parse(this.match[8]);
    this.collection = this.valuesFn(this.av.$scope);

    this.av.$scope.$watchCollection(this.collection, (newVal, oldVal) => {

      if (angular.equals(newVal, oldVal)) {
        return;
      }

      self.setValue();

    }, true);

  }

  getLocals(value, key) {

    const locals = {};

    if (this.keyName) {
      locals[this.keyName] = key;
      locals[this.valueName] = value;
    } else {
      locals[this.valueName] = value;
    }

    return locals;

  }

  getOptionValuesKeys(optionValues) {

    let optionValuesKeys;

    if (!this.keyName && Array.isArray(optionValues)) {
      optionValuesKeys = optionValues;
    } else {
      // if object, extract keys, in enumeration order, unsorted
      optionValuesKeys = [];
      for (const itemKey in optionValues) {
        if (optionValues.hasOwnProperty(itemKey) && itemKey.charAt(0) !== '$') {
          optionValuesKeys.push(itemKey);
        }
      }
    }
    return optionValuesKeys;
  }

}

ngModule.controller('AvDropdownController', AvDropdownController);

export default AvDropdownController;
<commit_msg>Changing check to better match select2 documentation<commit_after>import angular from 'angular';
import matches from 'lodash.matches';
import isEmpty from 'lodash.isempty';

import ngModule from '../module';
import { uuid } from '../../core/utils';
import './provider';

class AvDropdownController {

  constructor($element, $attrs, avDropdownConfig, $scope, $timeout, $parse) {

    this.av = { $element, $attrs, avDropdownConfig, $scope, $timeout, $parse };

    this.options = {};
    this.match = null;
    this.ngModel = null;
    this.locals = {};

  }

  init(context) {

    this.ngModel = context.ngModel;

    this.multiple = angular.isDefined(this.av.$attrs.multiple);

    this.options = angular.extend({}, this.av.avDropdownConfig.DEFAULTS, this.av.$scope.$eval(this.av.$attrs.options));

    if (this.isRemoteMultiple()) {
      this.options.multiple = angular.isDefined(this.av.$attrs.multiple);
    }

    if (this.options.query) {

      // Map AvSelectResource into Select2 options
      this.resource = this.options.query;

      // Function used to query results for the search term.
      this.options.query = options => {
        this.query(options);
      };
      // Function used to get the id from the choice object or a string representing the key under which the id is stored.
      this.options.id = this.resource.getId;
      this.options.initSelection = this.resource.initSelection;

    }

    // if element is type input, initSelection is required to use val
    if (!this.av.$element.is('select') && !this.options.initSelection) {
      this.options.initSelection = this.initSelection;
    }

  }

  isRemoteMultiple() {
    return angular.isDefined(this.av.$attrs.multiple) && this.av.$element.get(0).tagName.toLowerCase() === 'input';
  }

  initSelection(element, callback) {
    callback();
  }

  setRemoteViewValue(e) {

    let values = this.ngModel.$viewValue;

    if (!angular.isArray(values) || !angular.isObject(values)) {
      values = [];
    }

    if (e.added) {
      // Adding to collection
      values.push(e.added);
    } else {
      // Removing from collection
      const index = values.findIndex(value => matches(e.removed)(value));
      values.splice(index, 1);
    }

    this.ngModel.$setViewValue(values);

  }

  setViewValue(e) {
    this.ngModel.$setViewValue(e.added);
  }

  hashKey(obj, nextUidFn) {
    let key = obj && obj.$$hashKey;

    if (key) {
      if (typeof key === 'function') {
        key = obj.$$hashKey();
      }
      return key;
    }

    const objType = typeof obj;
    if (objType === 'function' || (objType === 'object' && obj !== null)) {
      key = obj.$$hashKey = objType + ':' + (nextUidFn || uuid())();
    } else {
      key = objType + ':' + obj;
    }

    return key;
  }

  getTrackByValueFn(value, locals) {

    if (this.trackBy) {
      return this.trackByFn(this.av.$scope, locals);
    }

    return this.hashKey(value);

  }

  getSelected(model) {

    const self = this;

    if (this.options.query) {
      return 0;
    }

    if (!this.collection) {
      // If we're not using ng-options, the model value is just the raw value of the option,
      // rather than an index, so return it as is.
      return model;
    }

    const optionValues = this.valuesFn(self.av.$scope) || [];
    const optionValuesKeys = this.getOptionValuesKeys(optionValues);

    const index = this.collection.findIndex( item => {
      return angular.equals(model, item);
    });

    const key = (optionValues === optionValuesKeys) ? index : optionValuesKeys[index];
    const value = optionValues[key];
    const locals = self.getLocals(value, key);
    const viewValue = self.viewValueFn(self.av.$scope, locals);
    const selectValue = self.getTrackByValueFn(viewValue, locals);

    return selectValue;

  }

  getMultiSelected(viewValues) {

    const self = this;

    const values = [];

    if (!viewValues) {
      return values;
    }

    if (this.av.$element.get(0).tagName.toLowerCase() !== 'input') {

      viewValues.forEach(viewValue => {

        const selected = self.getSelected(viewValue);

        values.push(selected);

      });

    }

    return values;

  }

  // Wrapper around the query function for Select2.  When the promise resolves
  // the callback
  query(options) {

    this.resource.onQuery(options).then(response => {

      // Callback function that should be called with the result object. The result object:
      //
      // result.results (object) - Array of result objects. The default renderers
      //    expect objects with id and text keys. The id property is required,
      //    even if custom renderers are used. The object may also contain a children
      //    key if hierarchical data is displayed. The object may also contain a disabled
      //    boolean property indicating whether this result can be selected.
      //
      // result.more (boolean) - true if more results are available for the current
      //    search term.
      //
      // results.context (object) - A user-defined object that should be made available
      //    as the context parameter to the query function on subsequent queries to load
      //    more result pages for the same search term. See the description of
      //    options.context parameter.
      options.callback({more: response.more, results: response.results});
    });
  }

  setValue() {

    const self = this;

    const viewValue = this.ngModel.$viewValue;

    let selected = null;
    if (this.multiple) {
      selected = this.getMultiSelected(viewValue);
    } else {
      selected = this.getSelected(viewValue);
    }

    // null === '' for Select2
    selected = (selected === null || selected === 'undefined') ? '' : selected;

    this.av.$timeout(() => {
      self.av.$element.select2('val', selected);
    });
  }

  setValues() {

    let viewValue = this.ngModel.$viewValue;

    if (!angular.isArray(viewValue)) {
      viewValue = [];
    }

    if (!isEmpty(viewValue) && angular.isObject(viewValue[0])) {
      viewValue = this.getMultiSelected(viewValue);
    }

    this.av.$timeout(() => this.av.$element.select2('val', viewValue) );

  }

  ngOptions() {

    this.match = this.av.$attrs.ngOptions.match(this.av.avDropdownConfig.NG_OPTIONS_REGEXP);
    if (!this.match) {
      throw new Error('Invalid ngOptions for avDropdown.  @See https://docs.angularjs.org/api/ng/directive/ngOptions');
    }

    // NG_OPTIONS_REGEXP regex will parse into arrays like below:

    // 1: value expression (valueFn)
    // 2: label expression (displayFn)
    // 3: group by expression (groupByFn)
    // 4: disable when expression (disableWhenFn)
    // 5: array item variable name
    // 6: object item key variable name
    // 7: object item value variable name
    // 8: collection expression
    // 9: track by expression

    // The variable name for the value of the item in the collection
    this.valueName = this.match[5] || this.match[7];

    // An expression that generates the viewValue for an option if there is no label expression
    this.valueFn = this.av.$parse(this.match[2] ? this.match[1] : this.valueName);

    // The variable name for the key of the item in the collection
    this.keyName = this.match[6];

    // An expression that generates the viewValue for an option if there is a label expression
    this.selectAs = / as /.test(this.match[0]) && this.match[1];

    // An expression that generates the viewValue for an option if there is a label expression
    // An expression that is used to track the id of each object in the options collection
    this.trackBy = this.match[9];
    this.selectAsFn = this.selectAs && this.av.$parse(this.selectAs);
    this.viewValueFn = this.selectAsFn || this.valueFn;
    this.trackByFn = this.trackBy && this.av.$parse(this.trackBy);

    this.displayFn = this.av.$parse(this.match[2] || this.match[1]);
    this.groupByFn = this.av.$parse(this.match[3] || '');
    this.disableWhenFn = this.av.$parse(this.match[4] || '');
    this.valuesFn = this.av.$parse(this.match[8]);
    this.collection = this.valuesFn(this.av.$scope);

    this.av.$scope.$watchCollection(this.collection, (newVal, oldVal) => {

      if (angular.equals(newVal, oldVal)) {
        return;
      }

      self.setValue();

    }, true);

  }

  getLocals(value, key) {

    const locals = {};

    if (this.keyName) {
      locals[this.keyName] = key;
      locals[this.valueName] = value;
    } else {
      locals[this.valueName] = value;
    }

    return locals;

  }

  getOptionValuesKeys(optionValues) {

    let optionValuesKeys;

    if (!this.keyName && Array.isArray(optionValues)) {
      optionValuesKeys = optionValues;
    } else {
      // if object, extract keys, in enumeration order, unsorted
      optionValuesKeys = [];
      for (const itemKey in optionValues) {
        if (optionValues.hasOwnProperty(itemKey) && itemKey.charAt(0) !== '$') {
          optionValuesKeys.push(itemKey);
        }
      }
    }
    return optionValuesKeys;
  }

}

ngModule.controller('AvDropdownController', AvDropdownController);

export default AvDropdownController;
<|endoftext|>


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

import {tryDecodeUriComponent_} from './url-try-decode-uri-component';

const regex = /(?:^[#?]?|&)([^=&]+)(?:=([^&]*))?/g;

/**
 * Parses the query string of an URL. This method returns a simple key/value
 * map. If there are duplicate keys the latest value is returned.
 *
 * DO NOT import the function from this file. Instead, import parseQueryString
 * from `src/url.js`.
 *
 * @param {string} queryString
 * @return {!JsonObject}
 */
export function parseQueryString_(queryString) {
  const params = /** @type {!JsonObject} */ (Object.create(null));
  if (!queryString) {
    return params;
  }

  let match;
  while ((match = regex.exec(queryString))) {
    const name = tryDecodeUriComponent_(match[1], match[1]);
    const value = match[2] ? tryDecodeUriComponent_(match[2], match[2]) : '';
    params[name] = value;
  }
  return params;
}
<commit_msg>url: Recognize + as %20 when parsing query string (#24626)<commit_after>/**
 * Copyright 2017 The AMP HTML Authors. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS-IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import {tryDecodeUriComponent_} from './url-try-decode-uri-component';

const regex = /(?:^[#?]?|&)([^=&]+)(?:=([^&]*))?/g;

/**
 * Parses the query string of an URL. This method returns a simple key/value
 * map. If there are duplicate keys the latest value is returned.
 *
 * DO NOT import the function from this file. Instead, import parseQueryString
 * from `src/url.js`.
 *
 * @param {string} queryString
 * @return {!JsonObject}
 */
export function parseQueryString_(queryString) {
  const params = /** @type {!JsonObject} */ (Object.create(null));
  if (!queryString) {
    return params;
  }

  let match;
  while ((match = regex.exec(queryString))) {
    const name = tryDecodeUriComponent_(match[1], match[1]);
    const value = match[2]
      ? tryDecodeUriComponent_(match[2].replace(/\+/g, ' '), match[2])
      : '';
    params[name] = value;
  }
  return params;
}
<|endoftext|>


<commit_before>const translations = {
  en: {
    addUnitTest: 'Add unit test',
    constants: 'Constants',
    delete: 'Delete',
    deleteUnitTest: 'Delete unit test',
    empty: 'None',
    functions: 'Functions',
    tutorial: 'Tutorial',
    library: 'Library',
    loading: 'Loading...',
    next: 'Next',
    noValue: '<NONE>',
    primitives: 'Primitives',
    type: 'Type: ',
    value: 'Value: ',
    workspace: 'Workspace',
    'joyrideSteps.window': 'The window has 3 sections.',
    'joyrideSteps.tutorial':
      '1. Tutorial\

The instructions for following the tutorial
',
    'joyrideSteps.library':
      '2. Library\

The library has 2 components:

Constants

Functions

',
    'joyrideSteps.constants':
      '
Clicking on "Number" will add a ballon to the workspace.
\

This ballon can hold a number.
',
    'joyrideSteps.functions': 'Clicking on a math operation will add a brick to the workspace.',
    'joyrideSteps.workspace':
      '3. Workspace\

The workspace is the playground where you can connect ballons and bricks.
\

Clicking on an element will show a dialog where you can change its properties.
\

Move the elements by drag & drop.
\

The elements are connected through pipes. To create a pipe click on an\
      input and an output slot.',
    'tutorialSteps.1':
"
This is a small tutorial for learning the basics of programming.
\

All programs are composed of two primary components.\
 It doesn't matter if its a smartphone app, a web site, a game, or anything else.\
 This two components are: constants and functions.
\

You already know this components from your math lessons in elementary school.\

The numbers are constants.
\

And the math operators are functions.

\

In general functions process one or more inputs to give a result.\
 For example in the sum '3 + 4', the function '+' has two inputs: the numbers\
 '3' and '4'. And it gives the result of '7'.
\

In the workspace you can do this kind of operations by connecting the constants\
 as inputs to the functions.
\

Try doing a couple of operations now.\
(Here is a video of the steps to do this if you have any doubts)
\

When you finish doing some operations click the button 'Next' to continue.
"
  }
}

export const getMessage = (locale, key) => {
  return translations[locale][key]
}
<commit_msg>Add instructions for steps 2 & 3 of the tutorial<commit_after>const translations = {
  en: {
    addUnitTest: 'Add unit test',
    constants: 'Constants',
    delete: 'Delete',
    deleteUnitTest: 'Delete unit test',
    empty: 'None',
    functions: 'Functions',
    tutorial: 'Tutorial',
    library: 'Library',
    loading: 'Loading...',
    next: 'Next',
    noValue: '<NONE>',
    primitives: 'Primitives',
    type: 'Type: ',
    value: 'Value: ',
    workspace: 'Workspace',
    'joyrideSteps.window': 'The window has 3 sections.',
    'joyrideSteps.tutorial':
      '1. Tutorial\

The instructions for following the tutorial
',
    'joyrideSteps.library':
      '2. Library\

The library has 2 components:

Constants

Functions

',
    'joyrideSteps.constants':
      '
Clicking on "Number" will add a ballon to the workspace.
\

This ballon can hold a number.
',
    'joyrideSteps.functions': 'Clicking on a math operation will add a brick to the workspace.',
    'joyrideSteps.workspace':
      '3. Workspace\

The workspace is the playground where you can connect ballons and bricks.
\

Clicking on an element will show a dialog where you can change its properties.
\

Move the elements by drag & drop.
\

The elements are connected through pipes. To create a pipe click on an\
      input and an output slot.',
    'tutorialSteps.1':
"
This is a small tutorial for learning the basics of programming.
\

All programs are composed of two primary components.\
 It doesn't matter if its a smartphone app, a web site, a game, or anything else.\
 This two components are: constants and functions.
\

You already know this components from your math lessons in elementary school.\

The numbers are constants.
\

And the math operators are functions.

\

In general functions process one or more inputs to give a result.\
 For example in the sum '3 + 4', the function '+' has two inputs: the numbers\
 '3' and '4'. And it gives the result of '7'.
\

In the workspace you can do this kind of operations by connecting the constants\
 as inputs to the functions.
\

Try doing a couple of operations now.\
(Here is a video of the steps to do this if you have any doubts)
\

When you finish doing some operations click the button 'Next' to continue.
",
    'tutorialSteps.2':
"
We can not only manipulate numbers, we can also manipulate letters.\
 Letters are usually referred as 'String' in programming.\
 And as well as the math operations, there are functions to transform them.
\

Try to guess what do the functions in the right do. Then do some operations\
 on the workspace to see if the results of the functions are what you expected.
\

When you finish click the button 'Next' to continue.
",
    'tutorialSteps.3':
"
We have seen two types of constants so far: Numbers & Letters (Strings).\
 Most programs are composed by more than one type of data. And we can mix them\
 according to what we want to achieve. We can even convert from one to the\
 other in case we need it.
\

Have you noticed that constants have different colors depending on their type?\
 Numbers are yellow and strings are orange.\
 The functions have definitions that tell us which type of data they need as input.\
 Hover on the functions of the library and you will see this information.
\

As usual try doing a couple of operations now. See what happens when you connect\
 an input of the wrong type to a function.
\

Try using the functions 'ToNumber' and 'toString' to convert a constant\
 from one type to another.
\

When you finish click the button 'Next' to continue.
"
  }
}

export const getMessage = (locale, key) => {
  return translations[locale][key]
}
<|endoftext|>


<commit_before>import * as types from 'constants/ActionTypes';

export default function example(state = {
  example: 'foo'
}, action) {
  if (types.EXAMPLE_ACTION_TYPE === action.type) {
    return Object.assign({}, state, {
      example: action.example
    });
  }

  return state;
}
<commit_msg>Transferred from Object.assign() to ES6 variable spread<commit_after>import * as types from 'constants/ActionTypes';

export default function example(state = {
  example: 'foo'
}, action) {
  if (types.EXAMPLE_ACTION_TYPE === action.type) {
    return { ...state, example: action.example }
  }

  return state;
}
<|endoftext|>