<commit_before>/**
 * Storage内容仓库模块
 *
 * @author Kener (@Kener-林峰, linzhifeng@baidu.com)
 *         errorrik (errorrik@gmail.com)
 */

define(
    function (require) {
        var util = require('./tool/util');
        var log = require('./tool/log');
        var config = require('./config');

        /**
         * 内容仓库 (M)
         *
         */
        function Storage(shape) {
            // 所有常规形状，id索引的map
            this._elements = {};

            // 所有形状的z轴方向排列，提高遍历性能，zElements[0]的形状在zElements[1]形状下方
            this._zElements = [];

            // 高亮层形状，不稳定，动态增删，数组位置也是z轴方向，靠前显示在下方
            this._hoverElements = [];

            // 最大zlevel
            this._maxZlevel = 0;

            // 有数据改变的zlevel
            this._changedZlevel = {};
        }

        /**
         * 遍历迭代器
         *
         * @param {Function} fun 迭代回调函数，return true终止迭代
         * @param {Object=} option 迭代参数，缺省为仅降序遍历常规形状
         *     hover : true 是否迭代高亮层数据
         *     normal : 'down' | 'up' | 'free' 是否迭代常规数据，迭代时是否指定及z轴顺序
         */
        Storage.prototype.iterShape = function (fun, option) {
            if (!option) {
                option = {
                    hover: false,
                    normal: 'down'
                };
            }
            if (option.hover) {
                //高亮层数据遍历
                for (var i = 0, l = this._hoverElements.length; i < l; i++) {
                    if (fun(this._hoverElements[i])) {
                        return this;
                    }
                }
            }

            var zlist;
            var len;
            if (typeof option.normal != 'undefined') {
                //z轴遍历: 'down' | 'up' | 'free'
                switch (option.normal) {
                    case 'down':
                        // 降序遍历，高层优先
                        var l = this._zElements.length;
                        while (l--) {
                            zlist = this._zElements[l];
                            if (zlist) {
                                len = zlist.length;
                                while (len--) {
                                    if (fun(zlist[len])) {
                                        return this;
                                    }
                                }
                            }
                        }
                        break;
                    case 'up':
                        //升序遍历，底层优先
                        for (var i = 0, l = this._zElements.length; i < l; i++) {
                            zlist = this._zElements[i];
                            if (zlist) {
                                len = zlist.length;
                                for (var k = 0; k < len; k++) {
                                    if (fun(zlist[k])) {
                                        return this;
                                    }
                                }
                            }
                        }
                        break;
                    // case 'free':
                    default:
                        //无序遍历
                        for (var i in this._elements) {
                            if (fun(this._elements[i])) {
                                return this;
                            }
                        }
                        break;
                }
            }

            return this;
        };

        /**
         * 修改
         *
         * @param {string} idx 唯一标识
         * @param {Object} params 参数
         * @param {boolean} fast
         */
        Storage.prototype.mod = function (shapeId, params, fast) {
            var shape = this._elements[shapeId];
            if (shape) {
                shape.style.__rect = null;

                this._changedZlevel[shape.zlevel] = true;    // 可能修改前后不在一层
                if (params) {
                    if (fast) {
                        util.mergeFast(
                            shape,
                            params,
                            true,
                            true
                        );
                    } else {
                        util.merge(
                            shape,
                            params,
                            {
                                'overwrite': true,
                                'recursive': true
                            }
                        );
                    }
                }

                this._changedZlevel[shape.zlevel] = true;    // 可能修改前后不在一层
                this._maxZlevel = Math.max(this._maxZlevel, shape.zlevel);
            }

            return this;
        };

        /**
         * 常规形状位置漂移，形状自身定义漂移函数
         *
         * @param {string} idx 形状唯一标识
         */
        Storage.prototype.drift = function (shapeId, dx, dy) {
            var shape = this._elements[shapeId];

            if (shape) {
                shape.__needTransform = true;
                if (!shape.ondrift //ondrift
                    //有onbrush并且调用执行返回false或undefined则继续
                    || (shape.ondrift && !shape.ondrift(dx, dy))
                ) {
                    if (config.catchBrushException) {
                        try {
                            shape.drift(dx, dy);
                        }
                        catch(error) {
                            log(error, 'drift error of ' + shape.type, shape);
                        }
                    }
                    else {
                        shape.drift(dx, dy);
                    }
                }

                this._changedZlevel[shape.zlevel] = true;
            }

            return this;
        };

        /**
         * 添加高亮层数据
         *
         * @param {Object} params 参数
         */
        Storage.prototype.addHover = function (params) {
            if ((params.rotation && Math.abs(params.rotation[0]) > 0.0001)
                || (params.position
                    && (Math.abs(params.position[0]) > 0.0001
                        || Math.abs(params.position[1]) > 0.0001))
                || (params.scale
                    && (Math.abs(params.scale[0] - 1) > 0.0001
                    || Math.abs(params.scale[1] - 1) > 0.0001))
            ) {
                params.__needTransform = true;
            }
            else {
                params.__needTransform = false;
            }

            this._hoverElements.push(params);
            return this;
        };

        /**
         * 删除高亮层数据
         */
        Storage.prototype.delHover = function () {
            this._hoverElements = [];
            return this;
        };

        Storage.prototype.hasHoverShape = function () {
            return this._hoverElements.length > 0;
        };

        /**
         * 添加
         *
         * @param {Shape} shape 参数
         */
        Storage.prototype.add = function (shape) {
            shape.style.__rect = null;
            this._elements[shape.id] = shape;
            this._zElements[shape.zlevel] = this._zElements[shape.zlevel] || [];
            this._zElements[shape.zlevel].push(shape);

            this._maxZlevel = Math.max(this._maxZlevel, shape.zlevel);
            this._changedZlevel[shape.zlevel] = true;

            return this;
        };

        /**
         * 根据指定的shapeId获取相应的shape属性
         *
         * @param {string=} idx 唯一标识
         */
        Storage.prototype.get = function (shapeId) {
            return this._elements[shapeId];
        };

        /**
         * 删除，shapeId不指定则全清空
         *
         * @param {string= | Array} idx 唯一标识
         */
        Storage.prototype.del = function (shapeId) {
            if (typeof shapeId != 'undefined') {
                var delMap = {};
                if (!(shapeId instanceof Array)) {
                    // 单个
                    delMap[shapeId] = true;
                }
                else {
                    // 批量删除
                    if (shapeId.lenth < 1) { // 空数组
                        return;
                    }
                    for (var i = 0, l = shapeId.length; i < l; i++) {
                        delMap[shapeId[i].id] = true;
                    }
                }
                var newList;
                var oldList;
                var zlevel;
                var zChanged = {};
                for (var sId in delMap) {
                    if (this._elements[sId]) {
                        zlevel = this._elements[sId].zlevel;
                        this._changedZlevel[zlevel] = true;
                        if (!zChanged[zlevel]) {
                            oldList = this._zElements[zlevel];
                            newList = [];
                            for (var i = 0, l = oldList.length; i < l; i++){
                                if (!delMap[oldList[i].id]) {
                                    newList.push(oldList[i]);
                                }
                            }
                            this._zElements[zlevel] = newList;
                            zChanged[zlevel] = true;
                        }

                        delete this._elements[sId];
                    }
                }
            }
            else{
                // 不指定shapeId清空
                this._elements = {};
                this._zElements = [];
                this._hoverElements = [];
                this._maxZlevel = 0;         //最大zlevel
                this._changedZlevel = {      //有数据改变的zlevel
                    all : true
                };
            }

            return this;
        };

        Storage.prototype.getMaxZlevel = function () {
            return this._maxZlevel;
        };

        Storage.prototype.getChangedZlevel = function () {
            return this._changedZlevel;
        };

        Storage.prototype.clearChangedZlevel = function () {
            this._changedZlevel = {};
            return this;
        };

        Storage.prototype.setChangedZlevle = function (level) {
            this._changedZlevel[level] = true;
            return this;
        };

        /**
         * 释放
         */
        Storage.prototype.dispose = function () {
            this._elements =
            this._zElements =
            this._hoverElements = null;
        };

        return Storage;
    }
);
<commit_msg>fix bug: rename __needTransform 2 needTransform<commit_after>/**
 * Storage内容仓库模块
 *
 * @author Kener (@Kener-林峰, linzhifeng@baidu.com)
 *         errorrik (errorrik@gmail.com)
 */

define(
    function (require) {
        var util = require('./tool/util');
        var log = require('./tool/log');
        var config = require('./config');

        /**
         * 内容仓库 (M)
         *
         */
        function Storage(shape) {
            // 所有常规形状，id索引的map
            this._elements = {};

            // 所有形状的z轴方向排列，提高遍历性能，zElements[0]的形状在zElements[1]形状下方
            this._zElements = [];

            // 高亮层形状，不稳定，动态增删，数组位置也是z轴方向，靠前显示在下方
            this._hoverElements = [];

            // 最大zlevel
            this._maxZlevel = 0;

            // 有数据改变的zlevel
            this._changedZlevel = {};
        }

        /**
         * 遍历迭代器
         *
         * @param {Function} fun 迭代回调函数，return true终止迭代
         * @param {Object=} option 迭代参数，缺省为仅降序遍历常规形状
         *     hover : true 是否迭代高亮层数据
         *     normal : 'down' | 'up' | 'free' 是否迭代常规数据，迭代时是否指定及z轴顺序
         */
        Storage.prototype.iterShape = function (fun, option) {
            if (!option) {
                option = {
                    hover: false,
                    normal: 'down'
                };
            }
            if (option.hover) {
                //高亮层数据遍历
                for (var i = 0, l = this._hoverElements.length; i < l; i++) {
                    if (fun(this._hoverElements[i])) {
                        return this;
                    }
                }
            }

            var zlist;
            var len;
            if (typeof option.normal != 'undefined') {
                //z轴遍历: 'down' | 'up' | 'free'
                switch (option.normal) {
                    case 'down':
                        // 降序遍历，高层优先
                        var l = this._zElements.length;
                        while (l--) {
                            zlist = this._zElements[l];
                            if (zlist) {
                                len = zlist.length;
                                while (len--) {
                                    if (fun(zlist[len])) {
                                        return this;
                                    }
                                }
                            }
                        }
                        break;
                    case 'up':
                        //升序遍历，底层优先
                        for (var i = 0, l = this._zElements.length; i < l; i++) {
                            zlist = this._zElements[i];
                            if (zlist) {
                                len = zlist.length;
                                for (var k = 0; k < len; k++) {
                                    if (fun(zlist[k])) {
                                        return this;
                                    }
                                }
                            }
                        }
                        break;
                    // case 'free':
                    default:
                        //无序遍历
                        for (var i in this._elements) {
                            if (fun(this._elements[i])) {
                                return this;
                            }
                        }
                        break;
                }
            }

            return this;
        };

        /**
         * 修改
         *
         * @param {string} idx 唯一标识
         * @param {Object} params 参数
         * @param {boolean} fast
         */
        Storage.prototype.mod = function (shapeId, params, fast) {
            var shape = this._elements[shapeId];
            if (shape) {
                shape.style.__rect = null;

                this._changedZlevel[shape.zlevel] = true;    // 可能修改前后不在一层
                if (params) {
                    if (fast) {
                        util.mergeFast(
                            shape,
                            params,
                            true,
                            true
                        );
                    } else {
                        util.merge(
                            shape,
                            params,
                            {
                                'overwrite': true,
                                'recursive': true
                            }
                        );
                    }
                }

                this._changedZlevel[shape.zlevel] = true;    // 可能修改前后不在一层
                this._maxZlevel = Math.max(this._maxZlevel, shape.zlevel);
            }

            return this;
        };

        /**
         * 常规形状位置漂移，形状自身定义漂移函数
         *
         * @param {string} idx 形状唯一标识
         */
        Storage.prototype.drift = function (shapeId, dx, dy) {
            var shape = this._elements[shapeId];

            if (shape) {
                shape.needTransform = true;
                if (!shape.ondrift //ondrift
                    //有onbrush并且调用执行返回false或undefined则继续
                    || (shape.ondrift && !shape.ondrift(dx, dy))
                ) {
                    if (config.catchBrushException) {
                        try {
                            shape.drift(dx, dy);
                        }
                        catch(error) {
                            log(error, 'drift error of ' + shape.type, shape);
                        }
                    }
                    else {
                        shape.drift(dx, dy);
                    }
                }

                this._changedZlevel[shape.zlevel] = true;
            }

            return this;
        };

        /**
         * 添加高亮层数据
         *
         * @param {Object} params 参数
         */
        Storage.prototype.addHover = function (params) {
            if ((params.rotation && Math.abs(params.rotation[0]) > 0.0001)
                || (params.position
                    && (Math.abs(params.position[0]) > 0.0001
                        || Math.abs(params.position[1]) > 0.0001))
                || (params.scale
                    && (Math.abs(params.scale[0] - 1) > 0.0001
                    || Math.abs(params.scale[1] - 1) > 0.0001))
            ) {
                params.needTransform = true;
            }
            else {
                params.needTransform = false;
            }

            this._hoverElements.push(params);
            return this;
        };

        /**
         * 删除高亮层数据
         */
        Storage.prototype.delHover = function () {
            this._hoverElements = [];
            return this;
        };

        Storage.prototype.hasHoverShape = function () {
            return this._hoverElements.length > 0;
        };

        /**
         * 添加
         *
         * @param {Shape} shape 参数
         */
        Storage.prototype.add = function (shape) {
            shape.style.__rect = null;
            this._elements[shape.id] = shape;
            this._zElements[shape.zlevel] = this._zElements[shape.zlevel] || [];
            this._zElements[shape.zlevel].push(shape);

            this._maxZlevel = Math.max(this._maxZlevel, shape.zlevel);
            this._changedZlevel[shape.zlevel] = true;

            return this;
        };

        /**
         * 根据指定的shapeId获取相应的shape属性
         *
         * @param {string=} idx 唯一标识
         */
        Storage.prototype.get = function (shapeId) {
            return this._elements[shapeId];
        };

        /**
         * 删除，shapeId不指定则全清空
         *
         * @param {string= | Array} idx 唯一标识
         */
        Storage.prototype.del = function (shapeId) {
            if (typeof shapeId != 'undefined') {
                var delMap = {};
                if (!(shapeId instanceof Array)) {
                    // 单个
                    delMap[shapeId] = true;
                }
                else {
                    // 批量删除
                    if (shapeId.lenth < 1) { // 空数组
                        return;
                    }
                    for (var i = 0, l = shapeId.length; i < l; i++) {
                        delMap[shapeId[i].id] = true;
                    }
                }
                var newList;
                var oldList;
                var zlevel;
                var zChanged = {};
                for (var sId in delMap) {
                    if (this._elements[sId]) {
                        zlevel = this._elements[sId].zlevel;
                        this._changedZlevel[zlevel] = true;
                        if (!zChanged[zlevel]) {
                            oldList = this._zElements[zlevel];
                            newList = [];
                            for (var i = 0, l = oldList.length; i < l; i++){
                                if (!delMap[oldList[i].id]) {
                                    newList.push(oldList[i]);
                                }
                            }
                            this._zElements[zlevel] = newList;
                            zChanged[zlevel] = true;
                        }

                        delete this._elements[sId];
                    }
                }
            }
            else{
                // 不指定shapeId清空
                this._elements = {};
                this._zElements = [];
                this._hoverElements = [];
                this._maxZlevel = 0;         //最大zlevel
                this._changedZlevel = {      //有数据改变的zlevel
                    all : true
                };
            }

            return this;
        };

        Storage.prototype.getMaxZlevel = function () {
            return this._maxZlevel;
        };

        Storage.prototype.getChangedZlevel = function () {
            return this._changedZlevel;
        };

        Storage.prototype.clearChangedZlevel = function () {
            this._changedZlevel = {};
            return this;
        };

        Storage.prototype.setChangedZlevle = function (level) {
            this._changedZlevel[level] = true;
            return this;
        };

        /**
         * 释放
         */
        Storage.prototype.dispose = function () {
            this._elements =
            this._zElements =
            this._hoverElements = null;
        };

        return Storage;
    }
);
<|endoftext|>


<commit_before>var Platform = require('../../sys/Platform');
var Window = require('../../sys/Window');
var Program = require('../Program');
var Mat4 = require('../../math/Mat4');
var Vec3 = require('../../math/Vec3');

var VERT_SRC = '\
attribute vec3 aPosition; \
attribute vec3 aColor; \
attribute vec3 aTexCoord0; \
varying vec3 vColor; \
uniform mat4 uProjectionMatrix;\
uniform mat4 uViewMatrix;\
uniform mat4 uModelMatrix;\
void main() { \
    vec3 offset = aTexCoord0; \
    gl_Position = uProjectionMatrix * uViewMatrix * uModelMatrix * vec4(aPosition + offset, 1.0); \
    vColor = aColor; \
} \
';

var FRAG_SRC = '' +
'varying vec3 vColor;\n' +
'void main() {\n' +
'    gl_FragColor= vec4(vColor, 1.0);\n' +
'}\n';

if (Platform.isBrowser) {
    FRAG_SRC = 'precision highp float; \n' + FRAG_SRC;
}

Window.create({
    settings: {
        width: 1280,
        height: 720,
        type: '3d'
    },
    init: function() {
        var ctx = this.getContext();

        this.cubeProgram = ctx.createProgram(VERT_SRC, FRAG_SRC);

        this.projectionMatrix = Mat4.perspective(Mat4.create(),45,this.getAspectRatio(),0.001,100.0);
        this.viewMatrix       = Mat4.create();

        this.t = 0;

        this.cubeVao    = this.createInstancedCube();

        ctx.setClearColor(0.2,0.2,0.2,1.0);
        ctx.setDepthTest(true);
        ctx.setProjectionMatrix(this.projectionMatrix);
    },
    createInstancedCube: function() {
        var ctx = this.getContext();
        var positionColorNormalBuffer = ctx.createBuffer(
            ctx.ARRAY_BUFFER,
            new Float32Array([
                 1.0, 1.0, 1.0,   1.0/2+0.5, 1.0/2+0.5, 1.0/2+0.5,   0.0, 0.0, 1.0,
                -1.0, 1.0, 1.0,  -1.0/2+0.5, 1.0/2+0.5, 1.0/2+0.5,   0.0, 0.0, 1.0,
                 1.0,-1.0, 1.0,   1.0/2+0.5,-1.0/2+0.5, 1.0/2+0.5,   0.0, 0.0, 1.0,
                -1.0,-1.0, 1.0,  -1.0/2+0.5,-1.0/2+0.5, 1.0/2+0.5,   0.0, 0.0, 1.0,

                 1.0, 1.0, 1.0,   1.0/2+0.5, 1.0/2+0.5, 1.0/2+0.5,   1.0, 0.0, 0.0,
                 1.0,-1.0, 1.0,   1.0/2+0.5,-1.0/2+0.5, 1.0/2+0.5,   1.0, 0.0, 0.0,
                 1.0, 1.0,-1.0,   1.0/2+0.5, 1.0/2+0.5,-1.0/2+0.5,   1.0, 0.0, 0.0,
                 1.0,-1.0,-1.0,   1.0/2+0.5,-1.0/2+0.5,-1.0/2+0.5,   1.0, 0.0, 0.0,

                 1.0, 1.0, 1.0,   1.0/2+0.5, 1.0/2+0.5, 1.0/2+0.5,   0.0, 1.0, 0.0,
                 1.0, 1.0,-1.0,   1.0/2+0.5, 1.0/2+0.5,-1.0/2+0.5,   0.0, 1.0, 0.0,
                -1.0, 1.0, 1.0,  -1.0/2+0.5, 1.0/2+0.5, 1.0/2+0.5,   0.0, 1.0, 0.0,
                -1.0, 1.0,-1.0,  -1.0/2+0.5, 1.0/2+0.5,-1.0/2+0.5,   0.0, 1.0, 0.0,

                 1.0, 1.0,-1.0,   1.0/2+0.5, 1.0/2+0.5,-1.0/2+0.5,   0.0, 0.0,-1.0,
                 1.0,-1.0,-1.0,   1.0/2+0.5,-1.0/2+0.5,-1.0/2+0.5,   0.0, 0.0,-1.0,
                -1.0, 1.0,-1.0,  -1.0/2+0.5, 1.0/2+0.5,-1.0/2+0.5,   0.0, 0.0,-1.0,
                -1.0,-1.0,-1.0,  -1.0/2+0.5,-1.0/2+0.5,-1.0/2+0.5,   0.0, 0.0,-1.0,

                -1.0, 1.0, 1.0,  -1.0/2+0.5, 1.0/2+0.5, 1.0/2+0.5,  -1.0, 0.0, 0.0,
                -1.0, 1.0,-1.0,  -1.0/2+0.5, 1.0/2+0.5,-1.0/2+0.5,  -1.0, 0.0, 0.0,
                -1.0,-1.0, 1.0,  -1.0/2+0.5,-1.0/2+0.5, 1.0/2+0.5,  -1.0, 0.0, 0.0,
                -1.0,-1.0,-1.0,  -1.0/2+0.5,-1.0/2+0.5,-1.0/2+0.5,  -1.0, 0.0, 0.0,

                 1.0,-1.0, 1.0,   1.0/2+0.5,-1.0/2+0.5, 1.0/2+0.5,   0.0,-1.0, 0.0,
                -1.0,-1.0, 1.0,  -1.0/2+0.5,-1.0/2+0.5, 1.0/2+0.5,   0.0,-1.0, 0.0,
                 1.0,-1.0,-1.0,   1.0/2+0.5,-1.0/2+0.5,-1.0/2+0.5,   0.0,-1.0, 0.0,
                -1.0,-1.0,-1.0,  -1.0/2+0.5,-1.0/2+0.5,-1.0/2+0.5,   0.0,-1.0, 0.0
            ]),
            ctx.STATIC_DRAW
        );

        var offsets = [];
        var r = 60;
        for(var i=0; i<5000; i++) {
            offsets.push(Math.random()*r-r/2, Math.random()*r-r/2, Math.random()*r-r/2);
        }

        var instancedOffsetBuffer = ctx.createBuffer(
            ctx.ARRAY_BUFFER,
            new Float32Array(offsets),
            ctx.STATIC_DRAW
        )

        var indexBuffer = ctx.createBuffer(
            ctx.ELEMENT_ARRAY_BUFFER,
            new Uint16Array([
                0, 1, 2, 2, 1, 3,
                4, 5, 6, 6, 5, 7,
                8, 9,10,10, 9,11,
                12,13,14,14,13,15,
                16,17,18,18,17,19,
                20,21,22,22,21,23
            ]),
            ctx.STATIC_DRAW
        );

        var attributes = [
            { buffer : positionColorNormalBuffer, location : ctx.ATTRIB_POSITION, size : 3, stride : 9 * 4, offset : 0     },
            { buffer : positionColorNormalBuffer, location : ctx.ATTRIB_COLOR,    size : 3, stride : 9 * 4, offset : 3 * 4 },
            { buffer : positionColorNormalBuffer, location : ctx.ATTRIB_NORMAL,   size : 3, stride : 9 * 4, offset : 6 * 4 },
            { buffer : instancedOffsetBuffer, location : ctx.ATTRIB_TEX_COORD_0, size : 3, stride : 3 * 4, offset : 0, divisor: 1 }
        ];
        return ctx.createVertexArray(attributes,indexBuffer);
    },
    draw: function() {
        var ctx = this.getContext();
        var time = this.t / 10;

        ctx.setClearColor(0, 0, 0, 1);
        ctx.clear(ctx.COLOR_BIT | ctx.DEPTH_BIT);

        ctx.setViewMatrix(Mat4.lookAt9(this.viewMatrix,
                16 * Math.cos(time * Math.PI),
                4 * Math.sin(time * 0.5),
                16 * Math.sin(time * Math.PI),
                0,0,0,0,1,0
            )
        );

        var scale = 0.75 + (0.5 + Math.cos(time * Math.PI * 2) * 0.5) * 0.25;

        ctx.bindProgram(this.cubeProgram);
        ctx.bindVertexArray(this.cubeVao);
        ctx.identity();
        ctx.scale([0.2, 0.2, 0.2]);
        ctx.draw(ctx.TRIANGLES, 0, this.cubeVao.getIndexBuffer().getLength());

        this.t += 1 / 60;
    }
});
<commit_msg>06-pex-ctx-00 test Instancing update to use drawElementsIntanced<commit_after>var Platform = require('../../sys/Platform');
var Window = require('../../sys/Window');
var Program = require('../Program');
var Mat4 = require('../../math/Mat4');
var Vec3 = require('../../math/Vec3');

var VERT_SRC = '\
attribute vec3 aPosition; \
attribute vec3 aColor; \
attribute vec3 aTexCoord0; \
varying vec3 vColor; \
uniform mat4 uProjectionMatrix;\
uniform mat4 uViewMatrix;\
uniform mat4 uModelMatrix;\
void main() { \
    vec3 offset = aTexCoord0; \
    gl_Position = uProjectionMatrix * uViewMatrix * uModelMatrix * vec4(aPosition + offset, 1.0); \
    vColor = aColor; \
} \
';

var FRAG_SRC = '' +
'varying vec3 vColor;\n' +
'void main() {\n' +
'    gl_FragColor= vec4(vColor, 1.0);\n' +
'}\n';

if (Platform.isBrowser) {
    FRAG_SRC = 'precision highp float; \n' + FRAG_SRC;
}

Window.create({
    settings: {
        width: 1280,
        height: 720,
        type: '3d'
    },
    init: function() {
        var ctx = this.getContext();

        this.cubeProgram = ctx.createProgram(VERT_SRC, FRAG_SRC);

        this.projectionMatrix = Mat4.perspective(Mat4.create(),45,this.getAspectRatio(),0.001,100.0);
        this.viewMatrix       = Mat4.create();

        this.t = 0;

        this.cubeVao    = this.createInstancedCube();

        ctx.setClearColor(0.2,0.2,0.2,1.0);
        ctx.setDepthTest(true);
        ctx.setProjectionMatrix(this.projectionMatrix);
    },
    createInstancedCube: function() {
        var ctx = this.getContext();
        var positionColorNormalBuffer = ctx.createBuffer(
            ctx.ARRAY_BUFFER,
            new Float32Array([
                 1.0, 1.0, 1.0,   1.0/2+0.5, 1.0/2+0.5, 1.0/2+0.5,   0.0, 0.0, 1.0,
                -1.0, 1.0, 1.0,  -1.0/2+0.5, 1.0/2+0.5, 1.0/2+0.5,   0.0, 0.0, 1.0,
                 1.0,-1.0, 1.0,   1.0/2+0.5,-1.0/2+0.5, 1.0/2+0.5,   0.0, 0.0, 1.0,
                -1.0,-1.0, 1.0,  -1.0/2+0.5,-1.0/2+0.5, 1.0/2+0.5,   0.0, 0.0, 1.0,

                 1.0, 1.0, 1.0,   1.0/2+0.5, 1.0/2+0.5, 1.0/2+0.5,   1.0, 0.0, 0.0,
                 1.0,-1.0, 1.0,   1.0/2+0.5,-1.0/2+0.5, 1.0/2+0.5,   1.0, 0.0, 0.0,
                 1.0, 1.0,-1.0,   1.0/2+0.5, 1.0/2+0.5,-1.0/2+0.5,   1.0, 0.0, 0.0,
                 1.0,-1.0,-1.0,   1.0/2+0.5,-1.0/2+0.5,-1.0/2+0.5,   1.0, 0.0, 0.0,

                 1.0, 1.0, 1.0,   1.0/2+0.5, 1.0/2+0.5, 1.0/2+0.5,   0.0, 1.0, 0.0,
                 1.0, 1.0,-1.0,   1.0/2+0.5, 1.0/2+0.5,-1.0/2+0.5,   0.0, 1.0, 0.0,
                -1.0, 1.0, 1.0,  -1.0/2+0.5, 1.0/2+0.5, 1.0/2+0.5,   0.0, 1.0, 0.0,
                -1.0, 1.0,-1.0,  -1.0/2+0.5, 1.0/2+0.5,-1.0/2+0.5,   0.0, 1.0, 0.0,

                 1.0, 1.0,-1.0,   1.0/2+0.5, 1.0/2+0.5,-1.0/2+0.5,   0.0, 0.0,-1.0,
                 1.0,-1.0,-1.0,   1.0/2+0.5,-1.0/2+0.5,-1.0/2+0.5,   0.0, 0.0,-1.0,
                -1.0, 1.0,-1.0,  -1.0/2+0.5, 1.0/2+0.5,-1.0/2+0.5,   0.0, 0.0,-1.0,
                -1.0,-1.0,-1.0,  -1.0/2+0.5,-1.0/2+0.5,-1.0/2+0.5,   0.0, 0.0,-1.0,

                -1.0, 1.0, 1.0,  -1.0/2+0.5, 1.0/2+0.5, 1.0/2+0.5,  -1.0, 0.0, 0.0,
                -1.0, 1.0,-1.0,  -1.0/2+0.5, 1.0/2+0.5,-1.0/2+0.5,  -1.0, 0.0, 0.0,
                -1.0,-1.0, 1.0,  -1.0/2+0.5,-1.0/2+0.5, 1.0/2+0.5,  -1.0, 0.0, 0.0,
                -1.0,-1.0,-1.0,  -1.0/2+0.5,-1.0/2+0.5,-1.0/2+0.5,  -1.0, 0.0, 0.0,

                 1.0,-1.0, 1.0,   1.0/2+0.5,-1.0/2+0.5, 1.0/2+0.5,   0.0,-1.0, 0.0,
                -1.0,-1.0, 1.0,  -1.0/2+0.5,-1.0/2+0.5, 1.0/2+0.5,   0.0,-1.0, 0.0,
                 1.0,-1.0,-1.0,   1.0/2+0.5,-1.0/2+0.5,-1.0/2+0.5,   0.0,-1.0, 0.0,
                -1.0,-1.0,-1.0,  -1.0/2+0.5,-1.0/2+0.5,-1.0/2+0.5,   0.0,-1.0, 0.0
            ]),
            ctx.STATIC_DRAW
        );

        var offsets = [];
        var r = 60;
        for(var i=0; i<5000; i++) {
            offsets.push(Math.random()*r-r/2, Math.random()*r-r/2, Math.random()*r-r/2);
        }

        var instancedOffsetBuffer = ctx.createBuffer(
            ctx.ARRAY_BUFFER,
            new Float32Array(offsets),
            ctx.STATIC_DRAW
        );

        var indexBuffer = ctx.createBuffer(
            ctx.ELEMENT_ARRAY_BUFFER,
            new Uint16Array([
                0, 1, 2, 2, 1, 3,
                4, 5, 6, 6, 5, 7,
                8, 9,10,10, 9,11,
                12,13,14,14,13,15,
                16,17,18,18,17,19,
                20,21,22,22,21,23
            ]),
            ctx.STATIC_DRAW
        );

        var attributes = [
            { buffer : positionColorNormalBuffer, location : ctx.ATTRIB_POSITION, size : 3, stride : 9 * 4, offset : 0     },
            { buffer : positionColorNormalBuffer, location : ctx.ATTRIB_COLOR,    size : 3, stride : 9 * 4, offset : 3 * 4 },
            { buffer : positionColorNormalBuffer, location : ctx.ATTRIB_NORMAL,   size : 3, stride : 9 * 4, offset : 6 * 4 },
            { buffer : instancedOffsetBuffer, location : ctx.ATTRIB_TEX_COORD_0, size : 3, stride : 3 * 4, offset : 0, divisor: 1 }
        ];
        return ctx.createVertexArray(attributes,indexBuffer);
    },
    draw: function() {
        var ctx = this.getContext();
        var time = this.t / 10;

        ctx.setClearColor(0, 0, 0, 1);
        ctx.clear(ctx.COLOR_BIT | ctx.DEPTH_BIT);

        ctx.setViewMatrix(Mat4.lookAt9(this.viewMatrix,
                16 * Math.cos(time * Math.PI),
                4 * Math.sin(time * 0.5),
                16 * Math.sin(time * Math.PI),
                0,0,0,0,1,0
            )
        );

        var scale = 0.75 + (0.5 + Math.cos(time * Math.PI * 2) * 0.5) * 0.25;

        ctx.bindProgram(this.cubeProgram);
        ctx.bindVertexArray(this.cubeVao);
        ctx.identity();
        ctx.scale([0.2, 0.2, 0.2]);
        ctx.drawElementsInstanced(ctx.TRIANGLES,this.cubeVao.getIndexBuffer().getLength(),0,5000);
        this.t += 1 / 60;
    }
});
<|endoftext|>


<commit_before>'use strict;'

const chai = require('chai');
const sinon = require('sinon');
const sinonChai = require('sinon-chai');
const expect = chai.expect;
const sandbox = sinon.sandbox.create();

const execa = require('execa');
const snapshot = require('snap-shot');
const tmp = require('tmp-promise');

const testCwd = process.cwd();

// Checks out the Gue-test repo
// links the local copy of Gue (this repo)
// runs gue default and returns stdout/stderr
//
// This return value will be snapshotted in the test
function runGueTest() {
  return tmp.dir()
    .then((dir) => {
      let command =
      'cd ' + dir.path + ' && ' +
      'git clone -q ' +
        'https://github.com/skarfacegc/Gue-test.git' + ' && ' +
      'cd Gue-test && ' +
      '(export NODE_ENV=snapshot ;' +
      'yarn > ./foo.yarn 2>&1 ; ' +
      'npm link ' + testCwd + ' > ./foo.link 2>&1 ; ' +
      'gue snapshotTest || exit 0 && exit 1 )' ;
      return execa.shell(command);
    });
}

describe('Gue-test repo tests', function() {
  this.timeout(0);
  it('should run gue snapshotTest successfully', () => {
    return runGueTest().then((result) => {
      result.cmd = '';
      return snapshot(result.stdout);
    });
  });
});
<commit_msg>Remove npm link from integration test<commit_after>'use strict;'

const chai = require('chai');
const sinon = require('sinon');
const sinonChai = require('sinon-chai');
const expect = chai.expect;
const sandbox = sinon.sandbox.create();

const execa = require('execa');
const snapshot = require('snap-shot');
const tmp = require('tmp-promise');

const testCwd = process.cwd();

// Checks out the Gue-test repo
// links the local copy of Gue (this repo)
// runs gue default and returns stdout/stderr
//
// This return value will be snapshotted in the test
function runGueTest() {
  return tmp.dir()
    .then((dir) => {
      let command =
      'yarn link > ' + dir.path + '/foo.linkAdd 2>&1;' +
      'cd ' + dir.path + ' && ' +
      'git clone -q ' +
        'https://github.com/skarfacegc/Gue-test.git' + ' && ' +
      'cd Gue-test && ' +
      '(export NODE_ENV=snapshot ;' +
      'yarn > ./foo.yarn 2>&1 ; ' +
      'yarn link gue > ./foo.link 2>&1 ; ' +
      'gue snapshotTest || exit 0 && exit 1 )' ;
      return execa.shell(command);
    });
}

describe('Gue-test repo tests', function() {
  this.timeout(0);
  it('should run gue snapshotTest successfully', () => {
    return runGueTest().then((result) => {
      result.cmd = '';
      return snapshot(result.stdout);
    });
  });
});
<|endoftext|>


<commit_before>var d3 = require('d3');

var Plotly = require('@lib/index');
var Lib = require('@src/lib');

var createGraphDiv = require('../assets/create_graph_div');
var destroyGraphDiv = require('../assets/destroy_graph_div');
var mouseEvent = require('../assets/mouse_event');

describe('zoom box element', function() {
    var mock = require('@mocks/14.json');

    var gd;
    beforeEach(function(done) {
        gd = createGraphDiv();

        var mockCopy = Lib.extendDeep({}, mock);
        mockCopy.layout.dragmode = 'zoom';

        Plotly.plot(gd, mockCopy.data, mockCopy.layout).then(done);
    });

    afterEach(destroyGraphDiv);

    it('should be appended to the zoom layer', function() {
        var x0 = 100;
        var y0 = 200;
        var x1 = 150;
        var y1 = 200;

        mouseEvent('mousemove', x0, y0);
        expect(d3.selectAll('.zoomlayer > .zoombox').size())
            .toEqual(0);
        expect(d3.selectAll('.zoomlayer > .zoombox-corners').size())
            .toEqual(0);

        mouseEvent('mousedown', x0, y0);
        mouseEvent('mousemove', x1, y1);
        expect(d3.selectAll('.zoomlayer > .zoombox').size())
            .toEqual(1);
        expect(d3.selectAll('.zoomlayer > .zoombox-corners').size())
            .toEqual(1);

        mouseEvent('mouseup', x1, y1);
        expect(d3.selectAll('.zoomlayer > .zoombox').size())
            .toEqual(0);
        expect(d3.selectAll('.zoomlayer > .zoombox-corners').size())
            .toEqual(0);
    });
});

describe('restyle', function() {
    describe('scatter traces', function() {
        var gd;

        beforeEach(function() {
            gd = createGraphDiv();
        });

        afterEach(destroyGraphDiv);

        it('reuses SVG fills', function(done) {
            var fills, firstToZero, secondToZero, firstToNext, secondToNext;
            var mock = Lib.extendDeep({}, require('@mocks/basic_area.json'));

            Plotly.plot(gd, mock.data, mock.layout).then(function() {
                // Assert there are two fills:
                fills = d3.selectAll('g.trace.scatter .js-fill')[0];

                // First is tozero, second is tonext:
                expect(d3.selectAll('g.trace.scatter .js-fill').size()).toEqual(2);
                expect(fills[0].classList.contains('js-tozero')).toBe(true);
                expect(fills[0].classList.contains('js-tonext')).toBe(false);
                expect(fills[1].classList.contains('js-tozero')).toBe(false);
                expect(fills[1].classList.contains('js-tonext')).toBe(true);

                firstToZero = fills[0];
                firstToNext = fills[1];
            }).then(function() {
                return Plotly.restyle(gd, {visible: [false]}, [1]);
            }).then(function() {
                // Trace 1 hidden leaves only trace zero's tozero fill:
                expect(d3.selectAll('g.trace.scatter .js-fill').size()).toEqual(1);
                expect(fills[0].classList.contains('js-tozero')).toBe(true);
                expect(fills[0].classList.contains('js-tonext')).toBe(false);
            }).then(function() {
                return Plotly.restyle(gd, {visible: [true]}, [1]);
            }).then(function() {
                // Reshow means two fills again AND order is preserved:
                fills = d3.selectAll('g.trace.scatter .js-fill')[0];

                // First is tozero, second is tonext:
                expect(d3.selectAll('g.trace.scatter .js-fill').size()).toEqual(2);
                expect(fills[0].classList.contains('js-tozero')).toBe(true);
                expect(fills[0].classList.contains('js-tonext')).toBe(false);
                expect(fills[1].classList.contains('js-tozero')).toBe(false);
                expect(fills[1].classList.contains('js-tonext')).toBe(true);

                secondToZero = fills[0];
                secondToNext = fills[1];

                // The identity of the first is retained:
                expect(firstToZero).toBe(secondToZero);

                // The second has been recreated so is different:
                expect(firstToNext).not.toBe(secondToNext);

                return Plotly.restyle(gd, 'visible', false);
            }).then(function() {
                expect(d3.selectAll('g.trace.scatter').size()).toEqual(0);

            }).then(done);
        });

        it('reuses SVG lines', function(done) {
            var lines, firstLine1, secondLine1, firstLine2, secondLine2;
            var mock = Lib.extendDeep({}, require('@mocks/basic_line.json'));

            Plotly.plot(gd, mock.data, mock.layout).then(function() {
                lines = d3.selectAll('g.scatter.trace .js-line');

                firstLine1 = lines[0][0];
                firstLine2 = lines[0][1];

                // One line for each trace:
                expect(lines.size()).toEqual(2);
            }).then(function() {
                return Plotly.restyle(gd, {visible: [false]}, [0]);
            }).then(function() {
                lines = d3.selectAll('g.scatter.trace .js-line');

                // Only one line now and it's equal to the second trace's line from above:
                expect(lines.size()).toEqual(1);
                expect(lines[0][0]).toBe(firstLine2);
            }).then(function() {
                return Plotly.restyle(gd, {visible: [true]}, [0]);
            }).then(function() {
                lines = d3.selectAll('g.scatter.trace .js-line');
                secondLine1 = lines[0][0];
                secondLine2 = lines[0][1];

                // Two lines once again:
                expect(lines.size()).toEqual(2);

                // First line has been removed and recreated:
                expect(firstLine1).not.toBe(secondLine1);

                // Second line was persisted:
                expect(firstLine2).toBe(secondLine2);
            }).then(done);
        });

        it('can change scatter mode', function(done) {
            var mock = Lib.extendDeep({}, require('@mocks/text_chart_basic.json'));

            function assertScatterModeSizes(lineSize, pointSize, textSize) {
                var gd3 = d3.select(gd),
                    lines = gd3.selectAll('g.scatter.trace .js-line'),
                    points = gd3.selectAll('g.scatter.trace path.point'),
                    texts = gd3.selectAll('g.scatter.trace text');

                expect(lines.size()).toEqual(lineSize);
                expect(points.size()).toEqual(pointSize);
                expect(texts.size()).toEqual(textSize);
            }

            Plotly.plot(gd, mock.data, mock.layout).then(function() {
                assertScatterModeSizes(2, 6, 9);

                return Plotly.restyle(gd, 'mode', 'lines');
            })
            .then(function() {
                assertScatterModeSizes(3, 0, 0);

                return Plotly.restyle(gd, 'mode', 'markers');
            })
            .then(function() {
                assertScatterModeSizes(0, 9, 0);

                return Plotly.restyle(gd, 'mode', 'markers+text');
            })
            .then(function() {
                assertScatterModeSizes(0, 9, 9);

                return Plotly.restyle(gd, 'mode', 'text');
            })
            .then(function() {
                assertScatterModeSizes(0, 0, 9);

                return Plotly.restyle(gd, 'mode', 'markers+text+lines');
            })
            .then(function() {
                assertScatterModeSizes(3, 9, 9);
            })
            .then(done);

        });
    });
});

describe('relayout', function() {

    describe('axis category attributes', function() {
        var mock = require('@mocks/basic_bar.json');

        var gd, mockCopy;

        beforeEach(function() {
            mockCopy = Lib.extendDeep({}, mock);
            gd = createGraphDiv();
        });

        afterEach(destroyGraphDiv);

        it('should response to \'categoryarray\' and \'categoryorder\' updates', function(done) {
            function assertCategories(list) {
                d3.selectAll('g.xtick').each(function(_, i) {
                    var tick = d3.select(this).select('text');
                    expect(tick.html()).toEqual(list[i]);
                });
            }

            Plotly.plot(gd, mockCopy.data, mockCopy.layout).then(function() {
                assertCategories(['giraffes', 'orangutans', 'monkeys']);

                return Plotly.relayout(gd, 'xaxis.categoryorder', 'category descending');
            }).then(function() {
                var list = ['orangutans', 'monkeys', 'giraffes'];

                expect(gd._fullLayout.xaxis._initialCategories).toEqual(list);
                assertCategories(list);

                return Plotly.relayout(gd, 'xaxis.categoryorder', null);
            }).then(function() {
                assertCategories(['giraffes', 'orangutans', 'monkeys']);

                return Plotly.relayout(gd, {
                    'xaxis.categoryarray': ['monkeys', 'giraffes', 'orangutans']
                });
            }).then(function() {
                var list = ['monkeys', 'giraffes', 'orangutans'];

                expect(gd.layout.xaxis.categoryarray).toEqual(list);
                expect(gd._fullLayout.xaxis.categoryarray).toEqual(list);
                expect(gd._fullLayout.xaxis._initialCategories).toEqual(list);
                assertCategories(list);

                done();
            });
        });
    });

});
<commit_msg>test: add cases for point translation on range relayout<commit_after>var d3 = require('d3');

var Plotly = require('@lib/index');
var Lib = require('@src/lib');

var createGraphDiv = require('../assets/create_graph_div');
var destroyGraphDiv = require('../assets/destroy_graph_div');
var mouseEvent = require('../assets/mouse_event');

describe('zoom box element', function() {
    var mock = require('@mocks/14.json');

    var gd;
    beforeEach(function(done) {
        gd = createGraphDiv();

        var mockCopy = Lib.extendDeep({}, mock);
        mockCopy.layout.dragmode = 'zoom';

        Plotly.plot(gd, mockCopy.data, mockCopy.layout).then(done);
    });

    afterEach(destroyGraphDiv);

    it('should be appended to the zoom layer', function() {
        var x0 = 100;
        var y0 = 200;
        var x1 = 150;
        var y1 = 200;

        mouseEvent('mousemove', x0, y0);
        expect(d3.selectAll('.zoomlayer > .zoombox').size())
            .toEqual(0);
        expect(d3.selectAll('.zoomlayer > .zoombox-corners').size())
            .toEqual(0);

        mouseEvent('mousedown', x0, y0);
        mouseEvent('mousemove', x1, y1);
        expect(d3.selectAll('.zoomlayer > .zoombox').size())
            .toEqual(1);
        expect(d3.selectAll('.zoomlayer > .zoombox-corners').size())
            .toEqual(1);

        mouseEvent('mouseup', x1, y1);
        expect(d3.selectAll('.zoomlayer > .zoombox').size())
            .toEqual(0);
        expect(d3.selectAll('.zoomlayer > .zoombox-corners').size())
            .toEqual(0);
    });
});

describe('restyle', function() {
    describe('scatter traces', function() {
        var gd;

        beforeEach(function() {
            gd = createGraphDiv();
        });

        afterEach(destroyGraphDiv);

        it('reuses SVG fills', function(done) {
            var fills, firstToZero, secondToZero, firstToNext, secondToNext;
            var mock = Lib.extendDeep({}, require('@mocks/basic_area.json'));

            Plotly.plot(gd, mock.data, mock.layout).then(function() {
                // Assert there are two fills:
                fills = d3.selectAll('g.trace.scatter .js-fill')[0];

                // First is tozero, second is tonext:
                expect(d3.selectAll('g.trace.scatter .js-fill').size()).toEqual(2);
                expect(fills[0].classList.contains('js-tozero')).toBe(true);
                expect(fills[0].classList.contains('js-tonext')).toBe(false);
                expect(fills[1].classList.contains('js-tozero')).toBe(false);
                expect(fills[1].classList.contains('js-tonext')).toBe(true);

                firstToZero = fills[0];
                firstToNext = fills[1];
            }).then(function() {
                return Plotly.restyle(gd, {visible: [false]}, [1]);
            }).then(function() {
                // Trace 1 hidden leaves only trace zero's tozero fill:
                expect(d3.selectAll('g.trace.scatter .js-fill').size()).toEqual(1);
                expect(fills[0].classList.contains('js-tozero')).toBe(true);
                expect(fills[0].classList.contains('js-tonext')).toBe(false);
            }).then(function() {
                return Plotly.restyle(gd, {visible: [true]}, [1]);
            }).then(function() {
                // Reshow means two fills again AND order is preserved:
                fills = d3.selectAll('g.trace.scatter .js-fill')[0];

                // First is tozero, second is tonext:
                expect(d3.selectAll('g.trace.scatter .js-fill').size()).toEqual(2);
                expect(fills[0].classList.contains('js-tozero')).toBe(true);
                expect(fills[0].classList.contains('js-tonext')).toBe(false);
                expect(fills[1].classList.contains('js-tozero')).toBe(false);
                expect(fills[1].classList.contains('js-tonext')).toBe(true);

                secondToZero = fills[0];
                secondToNext = fills[1];

                // The identity of the first is retained:
                expect(firstToZero).toBe(secondToZero);

                // The second has been recreated so is different:
                expect(firstToNext).not.toBe(secondToNext);

                return Plotly.restyle(gd, 'visible', false);
            }).then(function() {
                expect(d3.selectAll('g.trace.scatter').size()).toEqual(0);

            }).then(done);
        });

        it('reuses SVG lines', function(done) {
            var lines, firstLine1, secondLine1, firstLine2, secondLine2;
            var mock = Lib.extendDeep({}, require('@mocks/basic_line.json'));

            Plotly.plot(gd, mock.data, mock.layout).then(function() {
                lines = d3.selectAll('g.scatter.trace .js-line');

                firstLine1 = lines[0][0];
                firstLine2 = lines[0][1];

                // One line for each trace:
                expect(lines.size()).toEqual(2);
            }).then(function() {
                return Plotly.restyle(gd, {visible: [false]}, [0]);
            }).then(function() {
                lines = d3.selectAll('g.scatter.trace .js-line');

                // Only one line now and it's equal to the second trace's line from above:
                expect(lines.size()).toEqual(1);
                expect(lines[0][0]).toBe(firstLine2);
            }).then(function() {
                return Plotly.restyle(gd, {visible: [true]}, [0]);
            }).then(function() {
                lines = d3.selectAll('g.scatter.trace .js-line');
                secondLine1 = lines[0][0];
                secondLine2 = lines[0][1];

                // Two lines once again:
                expect(lines.size()).toEqual(2);

                // First line has been removed and recreated:
                expect(firstLine1).not.toBe(secondLine1);

                // Second line was persisted:
                expect(firstLine2).toBe(secondLine2);
            }).then(done);
        });

        it('can change scatter mode', function(done) {
            var mock = Lib.extendDeep({}, require('@mocks/text_chart_basic.json'));

            function assertScatterModeSizes(lineSize, pointSize, textSize) {
                var gd3 = d3.select(gd),
                    lines = gd3.selectAll('g.scatter.trace .js-line'),
                    points = gd3.selectAll('g.scatter.trace path.point'),
                    texts = gd3.selectAll('g.scatter.trace text');

                expect(lines.size()).toEqual(lineSize);
                expect(points.size()).toEqual(pointSize);
                expect(texts.size()).toEqual(textSize);
            }

            Plotly.plot(gd, mock.data, mock.layout).then(function() {
                assertScatterModeSizes(2, 6, 9);

                return Plotly.restyle(gd, 'mode', 'lines');
            })
            .then(function() {
                assertScatterModeSizes(3, 0, 0);

                return Plotly.restyle(gd, 'mode', 'markers');
            })
            .then(function() {
                assertScatterModeSizes(0, 9, 0);

                return Plotly.restyle(gd, 'mode', 'markers+text');
            })
            .then(function() {
                assertScatterModeSizes(0, 9, 9);

                return Plotly.restyle(gd, 'mode', 'text');
            })
            .then(function() {
                assertScatterModeSizes(0, 0, 9);

                return Plotly.restyle(gd, 'mode', 'markers+text+lines');
            })
            .then(function() {
                assertScatterModeSizes(3, 9, 9);
            })
            .then(done);

        });
    });
});

describe('relayout', function() {

    describe('axis category attributes', function() {
        var mock = require('@mocks/basic_bar.json');

        var gd, mockCopy;

        beforeEach(function() {
            mockCopy = Lib.extendDeep({}, mock);
            gd = createGraphDiv();
        });

        afterEach(destroyGraphDiv);

        it('should response to \'categoryarray\' and \'categoryorder\' updates', function(done) {
            function assertCategories(list) {
                d3.selectAll('g.xtick').each(function(_, i) {
                    var tick = d3.select(this).select('text');
                    expect(tick.html()).toEqual(list[i]);
                });
            }

            Plotly.plot(gd, mockCopy.data, mockCopy.layout).then(function() {
                assertCategories(['giraffes', 'orangutans', 'monkeys']);

                return Plotly.relayout(gd, 'xaxis.categoryorder', 'category descending');
            }).then(function() {
                var list = ['orangutans', 'monkeys', 'giraffes'];

                expect(gd._fullLayout.xaxis._initialCategories).toEqual(list);
                assertCategories(list);

                return Plotly.relayout(gd, 'xaxis.categoryorder', null);
            }).then(function() {
                assertCategories(['giraffes', 'orangutans', 'monkeys']);

                return Plotly.relayout(gd, {
                    'xaxis.categoryarray': ['monkeys', 'giraffes', 'orangutans']
                });
            }).then(function() {
                var list = ['monkeys', 'giraffes', 'orangutans'];

                expect(gd.layout.xaxis.categoryarray).toEqual(list);
                expect(gd._fullLayout.xaxis.categoryarray).toEqual(list);
                expect(gd._fullLayout.xaxis._initialCategories).toEqual(list);
                assertCategories(list);

                done();
            });
        });

    });

    describe('axis ranges', function() {
        var gd;

        beforeEach(function() {
            gd = createGraphDiv();
        });

        afterEach(destroyGraphDiv);

        it('should translate points and text element', function(done) {
            var mockData = [{
                x: [1],
                y: [1],
                text: ['A'],
                mode: 'markers+text'
            }];

            function assertPointTranslate(pointT, textT) {
                var TOLERANCE = 10;

                var gd3 = d3.select(gd),
                    points = gd3.selectAll('g.scatter.trace path.point'),
                    texts = gd3.selectAll('g.scatter.trace text');

                expect(points.size()).toEqual(1);
                expect(texts.size()).toEqual(1);

                expect(points.attr('x')).toBe(null);
                expect(points.attr('y')).toBe(null);
                expect(texts.attr('transform')).toBe(null);

                var translate = Lib.getTranslate(points);
                expect(Math.abs(translate.x - pointT[0])).toBeLessThan(TOLERANCE);
                expect(Math.abs(translate.y - pointT[1])).toBeLessThan(TOLERANCE);

                expect(Math.abs(texts.attr('x') - textT[0])).toBeLessThan(TOLERANCE);
                expect(Math.abs(texts.attr('y') - textT[1])).toBeLessThan(TOLERANCE);
            }

            Plotly.plot(gd, mockData).then(function() {
                assertPointTranslate([270, 135], [270, 135]);

                return Plotly.relayout(gd, 'xaxis.range', [2, 3]);
            })
            .then(function() {
                assertPointTranslate([0, 540], [-540, 135]);
            })
            .then(done);
        });

    });

});
<|endoftext|>


<commit_before>var keystone = require('../../..'),
	demand = require('must'),
	request = require('supertest'),
	demand = require('must'),
	getExpressApp = require('../../helpers/getExpressApp'),
	app = getExpressApp(),
	frameGuard = require('../../../lib/security/frameGuard');

describe('Keystone "frame guard" setting', function () {
	before(function() {
		app.use(frameGuard(keystone));
		app.get('/', function(req, res) {
			res.send('OK');
		});
	});

	describe('default setting', function() {

		it('should be "sameorigin"', function() {
			demand(keystone.get('frame guard')).to.be('sameorigin');
		});

	});

	describe('keystone.set("frame guard")', function() {

		it('should allow setting to "sameorigin"', function() {
			keystone.set('frame guard', 'sameorigin');
			demand(keystone.get('frame guard')).to.be('sameorigin');
		});

		it('should allow setting to "deny"', function() {
			keystone.set('frame guard', 'deny');
			demand(keystone.get('frame guard')).to.be('deny');
		});

		it('should allow setting to TRUE, converts to "deny"', function() {
			keystone.set('frame guard', true);
			demand(keystone.get('frame guard')).to.be('deny');
		});

		it('should allow setting to FALSE', function() {
			keystone.set('frame guard', false);
			demand(keystone.get('frame guard')).to.be(false);
		});

		it('should translate invalid options to FALSE', function() {
			keystone.set('frame guard', 'xxx');
			demand(keystone.get('frame guard')).to.be(false);
			keystone.set('frame guard', 999);
			demand(keystone.get('frame guard')).to.be(false);
			keystone.set('frame guard', []);
			demand(keystone.get('frame guard')).to.be(false);
			keystone.set('frame guard', {});
			demand(keystone.get('frame guard')).to.be(false);
		});

	});

	describe('X-Frame-Options header', function() {

		it('should be set to "deny" when "frame guard" is "deny"', function(done) {
			keystone.set('frame guard', 'deny');
			request(app)
				.get('/')
				.expect('x-frame-options', 'deny')
				.expect(200, done);
		});

		it('should be set to "sameorigin" when "frame guard" is "sameorigin"', function(done) {
			keystone.set('frame guard', 'sameorigin');
			request(app)
				.get('/')
				.expect('x-frame-options', 'sameorigin')
				.expect(200, done);
		});

		it('should be set to "deny" when "frame guard" is TRUE', function(done) {
			keystone.set('frame guard', true);
			request(app)
				.get('/')
				.expect('x-frame-options', 'deny')
				.expect(200, done);
		});

		it('should not be set when "frame guard" is FALSE', function(done) {
			keystone.set('frame guard', false);
			request(app)
				.get('/')
				.expect(function(res) {
					if (res.headers['x-frame-options']) {
						return 'X-Frame-Options key exists';
					}
				})
				.expect(200, done);
		});

	});

});<commit_msg>adding newline ad end of file<commit_after>var keystone = require('../../..'),
	demand = require('must'),
	request = require('supertest'),
	demand = require('must'),
	getExpressApp = require('../../helpers/getExpressApp'),
	app = getExpressApp(),
	frameGuard = require('../../../lib/security/frameGuard');

describe('Keystone "frame guard" setting', function () {
	before(function() {
		app.use(frameGuard(keystone));
		app.get('/', function(req, res) {
			res.send('OK');
		});
	});

	describe('default setting', function() {

		it('should be "sameorigin"', function() {
			demand(keystone.get('frame guard')).to.be('sameorigin');
		});

	});

	describe('keystone.set("frame guard")', function() {

		it('should allow setting to "sameorigin"', function() {
			keystone.set('frame guard', 'sameorigin');
			demand(keystone.get('frame guard')).to.be('sameorigin');
		});

		it('should allow setting to "deny"', function() {
			keystone.set('frame guard', 'deny');
			demand(keystone.get('frame guard')).to.be('deny');
		});

		it('should allow setting to TRUE, converts to "deny"', function() {
			keystone.set('frame guard', true);
			demand(keystone.get('frame guard')).to.be('deny');
		});

		it('should allow setting to FALSE', function() {
			keystone.set('frame guard', false);
			demand(keystone.get('frame guard')).to.be(false);
		});

		it('should translate invalid options to FALSE', function() {
			keystone.set('frame guard', 'xxx');
			demand(keystone.get('frame guard')).to.be(false);
			keystone.set('frame guard', 999);
			demand(keystone.get('frame guard')).to.be(false);
			keystone.set('frame guard', []);
			demand(keystone.get('frame guard')).to.be(false);
			keystone.set('frame guard', {});
			demand(keystone.get('frame guard')).to.be(false);
		});

	});

	describe('X-Frame-Options header', function() {

		it('should be set to "deny" when "frame guard" is "deny"', function(done) {
			keystone.set('frame guard', 'deny');
			request(app)
				.get('/')
				.expect('x-frame-options', 'deny')
				.expect(200, done);
		});

		it('should be set to "sameorigin" when "frame guard" is "sameorigin"', function(done) {
			keystone.set('frame guard', 'sameorigin');
			request(app)
				.get('/')
				.expect('x-frame-options', 'sameorigin')
				.expect(200, done);
		});

		it('should be set to "deny" when "frame guard" is TRUE', function(done) {
			keystone.set('frame guard', true);
			request(app)
				.get('/')
				.expect('x-frame-options', 'deny')
				.expect(200, done);
		});

		it('should not be set when "frame guard" is FALSE', function(done) {
			keystone.set('frame guard', false);
			request(app)
				.get('/')
				.expect(function(res) {
					if (res.headers['x-frame-options']) {
						return 'X-Frame-Options key exists';
					}
				})
				.expect(200, done);
		});

	});

});
<|endoftext|>


<commit_before>/*! @license
 * Shaka Player
 * Copyright 2016 Google LLC
 * SPDX-License-Identifier: Apache-2.0
 */

goog.require('shaka.media.DrmEngine');
goog.require('shaka.media.MediaSourceEngine');
goog.require('shaka.net.NetworkingEngine');
goog.require('shaka.test.FakeClosedCaptionParser');
goog.require('shaka.test.FakeTextDisplayer');
goog.require('shaka.test.ManifestGenerator');
goog.require('shaka.test.UiUtils');
goog.require('shaka.test.Util');
goog.require('shaka.test.Waiter');
goog.require('shaka.util.EventManager');
goog.require('shaka.util.ManifestParserUtils');
goog.require('shaka.util.Platform');
goog.require('shaka.util.PlayerConfiguration');
goog.require('shaka.util.PublicPromise');

describe('DrmEngine', () => {
  const ContentType = shaka.util.ManifestParserUtils.ContentType;

  // These come from Axinom and use the Axinom license server.
  // TODO: Do not rely on third-party services long-term.
  const videoInitSegmentUri = '/base/test/test/assets/multidrm-video-init.mp4';
  const videoSegmentUri = '/base/test/test/assets/multidrm-video-segment.mp4';
  const audioInitSegmentUri = '/base/test/test/assets/multidrm-audio-init.mp4';
  const audioSegmentUri = '/base/test/test/assets/multidrm-audio-segment.mp4';

  /** @type {!Object.<string, ?shaka.extern.DrmSupportType>} */
  let support = {};

  /** @type {!HTMLVideoElement} */
  let video;
  /** @type {shaka.extern.Manifest} */
  let manifest;

  /** @type {!jasmine.Spy} */
  let onErrorSpy;
  /** @type {!jasmine.Spy} */
  let onKeyStatusSpy;
  /** @type {!jasmine.Spy} */
  let onExpirationSpy;
  /** @type {!jasmine.Spy} */
  let onEventSpy;

  /** @type {!shaka.media.DrmEngine} */
  let drmEngine;
  /** @type {!shaka.media.MediaSourceEngine} */
  let mediaSourceEngine;
  /** @type {!shaka.net.NetworkingEngine} */
  let networkingEngine;
  /** @type {!shaka.util.EventManager} */
  let eventManager;

  /** @type {!ArrayBuffer} */
  let videoInitSegment;
  /** @type {!ArrayBuffer} */
  let audioInitSegment;
  /** @type {!ArrayBuffer} */
  let videoSegment;
  /** @type {!ArrayBuffer} */
  let audioSegment;

  beforeAll(async () => {
    video = shaka.test.UiUtils.createVideoElement();
    document.body.appendChild(video);

    const responses = await Promise.all([
      shaka.media.DrmEngine.probeSupport(),
      shaka.test.Util.fetch(videoInitSegmentUri),
      shaka.test.Util.fetch(videoSegmentUri),
      shaka.test.Util.fetch(audioInitSegmentUri),
      shaka.test.Util.fetch(audioSegmentUri),
    ]);
    support = responses[0];
    videoInitSegment = responses[1];
    videoSegment = responses[2];
    audioInitSegment = responses[3];
    audioSegment = responses[4];
  });

  beforeEach(async () => {
    onErrorSpy = jasmine.createSpy('onError');
    onKeyStatusSpy = jasmine.createSpy('onKeyStatus');
    onExpirationSpy = jasmine.createSpy('onExpirationUpdated');
    onEventSpy = jasmine.createSpy('onEvent');

    networkingEngine = new shaka.net.NetworkingEngine();
    networkingEngine.registerRequestFilter((type, request) => {
      if (type != shaka.net.NetworkingEngine.RequestType.LICENSE) {
        return;
      }

      request.headers['X-AxDRM-Message'] = [
        'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ2ZXJzaW9uIjoxLCJjb21fa2V5X2lk',
        'IjoiNjllNTQwODgtZTllMC00NTMwLThjMWEtMWViNmRjZDBkMTRlIiwibWVzc2FnZSI6e',
        'yJ0eXBlIjoiZW50aXRsZW1lbnRfbWVzc2FnZSIsImtleXMiOlt7ImlkIjoiNmU1YTFkMj',
        'YtMjc1Ny00N2Q3LTgwNDYtZWFhNWQxZDM0YjVhIn1dfX0.yF7PflOPv9qHnu3ZWJNZ12j',
        'gkqTabmwXbDWk_47tLNE',
      ].join('');
    });

    const playerInterface = {
      netEngine: networkingEngine,
      onError: shaka.test.Util.spyFunc(onErrorSpy),
      onKeyStatus: shaka.test.Util.spyFunc(onKeyStatusSpy),
      onExpirationUpdated: shaka.test.Util.spyFunc(onExpirationSpy),
      onEvent: shaka.test.Util.spyFunc(onEventSpy),
    };

    drmEngine = new shaka.media.DrmEngine(playerInterface);
    const config = shaka.util.PlayerConfiguration.createDefault().drm;
    config.servers['com.widevine.alpha'] =
        'https://drm-widevine-licensing.axtest.net/AcquireLicense';
    config.servers['com.microsoft.playready'] =
        'https://drm-playready-licensing.axtest.net/AcquireLicense';
    drmEngine.configure(config);

    manifest = shaka.test.ManifestGenerator.generate((manifest) => {
      manifest.addVariant(0, (variant) => {
        variant.addVideo(1, (stream) => {
          stream.encrypted = true;
          stream.addDrmInfo('com.widevine.alpha');
          stream.addDrmInfo('com.microsoft.playready');
        });
        variant.addAudio(2, (stream) => {
          stream.encrypted = true;
          stream.addDrmInfo('com.widevine.alpha');
          stream.addDrmInfo('com.microsoft.playready');
        });
      });
    });

    const videoStream = manifest.variants[0].video;
    const audioStream = manifest.variants[0].audio;

    eventManager = new shaka.util.EventManager();

    mediaSourceEngine = new shaka.media.MediaSourceEngine(
        video,
        new shaka.test.FakeClosedCaptionParser(),
        new shaka.test.FakeTextDisplayer());

    const expectedObject = new Map();
    expectedObject.set(ContentType.AUDIO, audioStream);
    expectedObject.set(ContentType.VIDEO, videoStream);
    await mediaSourceEngine.init(expectedObject, false);
  });

  afterEach(async () => {
    eventManager.release();

    await mediaSourceEngine.destroy();
    await networkingEngine.destroy();
    await drmEngine.destroy();
  });

  afterAll(() => {
    document.body.removeChild(video);
  });

  function checkSupport() {
    if (shaka.util.Platform.isXboxOne()) {
      // Axinom won't issue a license for an Xbox One.  The error message from
      // the license server says "Your DRM client's security level is 150, but
      // the entitlement message requires 2000 or higher."
      // TODO: Stop using Axinom's license server.  Use
      // https://testweb.playready.microsoft.com/Server/ServiceQueryStringSyntax
      return false;
    }
    return support['com.widevine.alpha'] || support['com.microsoft.playready'];
  }

  describe('basic flow', () => {
    drmIt('gets a license and can play encrypted segments', async () => {
      // The error callback should not be invoked.
      onErrorSpy.and.callFake(fail);

      const originalRequest = networkingEngine.request;
      let requestComplete;
      /** @type {!jasmine.Spy} */
      const requestSpy = jasmine.createSpy('request');
      /** @type {!shaka.util.PublicPromise} */
      const requestMade = new shaka.util.PublicPromise();
      requestSpy.and.callFake((...args) => {
        requestMade.resolve();
        // eslint-disable-next-line no-restricted-syntax
        requestComplete = originalRequest.call(networkingEngine, ...args);
        return requestComplete;
      });
      networkingEngine.request = shaka.test.Util.spyFunc(requestSpy);

      /** @type {!shaka.util.PublicPromise} */
      const encryptedEventSeen = new shaka.util.PublicPromise();
      eventManager.listen(video, 'encrypted', () => {
        encryptedEventSeen.resolve();
      });
      eventManager.listen(video, 'error', () => {
        fail('MediaError code ' + video.error.code);
        let extended = video.error.msExtendedCode;
        if (extended) {
          if (extended < 0) {
            extended += Math.pow(2, 32);
          }
          fail('MediaError msExtendedCode ' + extended.toString(16));
        }
      });

      /** @type {!shaka.util.PublicPromise} */
      const keyStatusEventSeen = new shaka.util.PublicPromise();
      onKeyStatusSpy.and.callFake(() => {
        keyStatusEventSeen.resolve();
      });

      const variants = manifest.variants;

      await drmEngine.initForPlayback(variants, manifest.offlineSessionIds);
      await drmEngine.attach(video);
      await mediaSourceEngine.appendBuffer(
          ContentType.VIDEO, videoInitSegment, null, null,
          /* hasClosedCaptions= */ false);
      await mediaSourceEngine.appendBuffer(
          ContentType.AUDIO, audioInitSegment, null, null,
          /* hasClosedCaptions= */ false);
      await encryptedEventSeen;
      // With PlayReady, a persistent license policy can cause a different
      // chain of events.  In particular, the request is bypassed and we
      // get a usable key right away.
      await Promise.race([requestMade, keyStatusEventSeen]);

      if (requestSpy.calls.count()) {
        // We made a license request.
        // Only one request should have been made.
        expect(requestSpy).toHaveBeenCalledTimes(1);
        // So it's reasonable to assume that this requestComplete Promise
        // is waiting on the correct request.
        await requestComplete;
      } else {
        // This was probably a PlayReady persistent license.
      }

      // Some platforms (notably 2017 Tizen TVs) do not fire key status
      // events.
      const keyStatusTimeout = shaka.test.Util.delay(5);
      await Promise.race([keyStatusTimeout, keyStatusEventSeen]);

      const call = onKeyStatusSpy.calls.mostRecent();
      if (call) {
        const map = /** @type {!Object} */ (call.args[0]);
        expect(Object.keys(map).length).not.toBe(0);
        for (const k in map) {
          expect(map[k]).toBe('usable');
        }
      }

      await mediaSourceEngine.appendBuffer(
          ContentType.VIDEO, videoSegment, 0, 10,
          /* hasClosedCaptions= */ false);
      await mediaSourceEngine.appendBuffer(
          ContentType.AUDIO, audioSegment, 0, 10,
          /* hasClosedCaptions= */ false);

      expect(video.buffered.end(0)).toBeGreaterThan(0);
      video.play();

      const waiter = new shaka.test.Waiter(eventManager).timeoutAfter(15);
      await waiter.waitForMovement(video);

      // Something should have played by now.
      expect(video.readyState).toBeGreaterThan(1);
      expect(video.currentTime).toBeGreaterThan(0);
    });
  });  // describe('basic flow')
});
<commit_msg>test: Fix filtering of DRM tests by key system<commit_after>/*! @license
 * Shaka Player
 * Copyright 2016 Google LLC
 * SPDX-License-Identifier: Apache-2.0
 */

goog.require('shaka.media.DrmEngine');
goog.require('shaka.media.MediaSourceEngine');
goog.require('shaka.net.NetworkingEngine');
goog.require('shaka.test.FakeClosedCaptionParser');
goog.require('shaka.test.FakeTextDisplayer');
goog.require('shaka.test.ManifestGenerator');
goog.require('shaka.test.UiUtils');
goog.require('shaka.test.Util');
goog.require('shaka.test.Waiter');
goog.require('shaka.util.EventManager');
goog.require('shaka.util.ManifestParserUtils');
goog.require('shaka.util.Platform');
goog.require('shaka.util.PlayerConfiguration');
goog.require('shaka.util.PublicPromise');

describe('DrmEngine', () => {
  const ContentType = shaka.util.ManifestParserUtils.ContentType;

  // These come from Axinom and use the Axinom license server.
  // TODO: Do not rely on third-party services long-term.
  const videoInitSegmentUri = '/base/test/test/assets/multidrm-video-init.mp4';
  const videoSegmentUri = '/base/test/test/assets/multidrm-video-segment.mp4';
  const audioInitSegmentUri = '/base/test/test/assets/multidrm-audio-init.mp4';
  const audioSegmentUri = '/base/test/test/assets/multidrm-audio-segment.mp4';

  /** @type {!Object.<string, ?shaka.extern.DrmSupportType>} */
  let support = {};

  /** @type {!HTMLVideoElement} */
  let video;
  /** @type {shaka.extern.Manifest} */
  let manifest;

  /** @type {!jasmine.Spy} */
  let onErrorSpy;
  /** @type {!jasmine.Spy} */
  let onKeyStatusSpy;
  /** @type {!jasmine.Spy} */
  let onExpirationSpy;
  /** @type {!jasmine.Spy} */
  let onEventSpy;

  /** @type {!shaka.media.DrmEngine} */
  let drmEngine;
  /** @type {!shaka.media.MediaSourceEngine} */
  let mediaSourceEngine;
  /** @type {!shaka.net.NetworkingEngine} */
  let networkingEngine;
  /** @type {!shaka.util.EventManager} */
  let eventManager;

  /** @type {!ArrayBuffer} */
  let videoInitSegment;
  /** @type {!ArrayBuffer} */
  let audioInitSegment;
  /** @type {!ArrayBuffer} */
  let videoSegment;
  /** @type {!ArrayBuffer} */
  let audioSegment;

  beforeAll(async () => {
    video = shaka.test.UiUtils.createVideoElement();
    document.body.appendChild(video);

    const responses = await Promise.all([
      shaka.media.DrmEngine.probeSupport(),
      shaka.test.Util.fetch(videoInitSegmentUri),
      shaka.test.Util.fetch(videoSegmentUri),
      shaka.test.Util.fetch(audioInitSegmentUri),
      shaka.test.Util.fetch(audioSegmentUri),
    ]);
    support = responses[0];
    videoInitSegment = responses[1];
    videoSegment = responses[2];
    audioInitSegment = responses[3];
    audioSegment = responses[4];
  });

  beforeEach(async () => {
    onErrorSpy = jasmine.createSpy('onError');
    onKeyStatusSpy = jasmine.createSpy('onKeyStatus');
    onExpirationSpy = jasmine.createSpy('onExpirationUpdated');
    onEventSpy = jasmine.createSpy('onEvent');

    networkingEngine = new shaka.net.NetworkingEngine();
    networkingEngine.registerRequestFilter((type, request) => {
      if (type != shaka.net.NetworkingEngine.RequestType.LICENSE) {
        return;
      }

      request.headers['X-AxDRM-Message'] = [
        'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ2ZXJzaW9uIjoxLCJjb21fa2V5X2lk',
        'IjoiNjllNTQwODgtZTllMC00NTMwLThjMWEtMWViNmRjZDBkMTRlIiwibWVzc2FnZSI6e',
        'yJ0eXBlIjoiZW50aXRsZW1lbnRfbWVzc2FnZSIsImtleXMiOlt7ImlkIjoiNmU1YTFkMj',
        'YtMjc1Ny00N2Q3LTgwNDYtZWFhNWQxZDM0YjVhIn1dfX0.yF7PflOPv9qHnu3ZWJNZ12j',
        'gkqTabmwXbDWk_47tLNE',
      ].join('');
    });

    const playerInterface = {
      netEngine: networkingEngine,
      onError: shaka.test.Util.spyFunc(onErrorSpy),
      onKeyStatus: shaka.test.Util.spyFunc(onKeyStatusSpy),
      onExpirationUpdated: shaka.test.Util.spyFunc(onExpirationSpy),
      onEvent: shaka.test.Util.spyFunc(onEventSpy),
    };

    drmEngine = new shaka.media.DrmEngine(playerInterface);
    const config = shaka.util.PlayerConfiguration.createDefault().drm;
    config.servers['com.widevine.alpha'] =
        'https://drm-widevine-licensing.axtest.net/AcquireLicense';
    config.servers['com.microsoft.playready'] =
        'https://drm-playready-licensing.axtest.net/AcquireLicense';
    drmEngine.configure(config);

    manifest = shaka.test.ManifestGenerator.generate((manifest) => {
      manifest.addVariant(0, (variant) => {
        variant.addVideo(1, (stream) => {
          stream.encrypted = true;
          stream.addDrmInfo('com.widevine.alpha');
          stream.addDrmInfo('com.microsoft.playready');
        });
        variant.addAudio(2, (stream) => {
          stream.encrypted = true;
          stream.addDrmInfo('com.widevine.alpha');
          stream.addDrmInfo('com.microsoft.playready');
        });
      });
    });

    const videoStream = manifest.variants[0].video;
    const audioStream = manifest.variants[0].audio;

    eventManager = new shaka.util.EventManager();

    mediaSourceEngine = new shaka.media.MediaSourceEngine(
        video,
        new shaka.test.FakeClosedCaptionParser(),
        new shaka.test.FakeTextDisplayer());

    const expectedObject = new Map();
    expectedObject.set(ContentType.AUDIO, audioStream);
    expectedObject.set(ContentType.VIDEO, videoStream);
    await mediaSourceEngine.init(expectedObject, false);
  });

  afterEach(async () => {
    eventManager.release();

    await mediaSourceEngine.destroy();
    await networkingEngine.destroy();
    await drmEngine.destroy();
  });

  afterAll(() => {
    document.body.removeChild(video);
  });

  function checkSupport() {
    if (shaka.util.Platform.isXboxOne()) {
      // Axinom won't issue a license for an Xbox One.  The error message from
      // the license server says "Your DRM client's security level is 150, but
      // the entitlement message requires 2000 or higher."
      // TODO: Stop using Axinom's license server.  Use
      // https://testweb.playready.microsoft.com/Server/ServiceQueryStringSyntax
      return false;
    }
    return support['com.widevine.alpha'] || support['com.microsoft.playready'];
  }

  filterDescribe('basic flow', checkSupport, () => {
    drmIt('gets a license and can play encrypted segments', async () => {
      // The error callback should not be invoked.
      onErrorSpy.and.callFake(fail);

      const originalRequest = networkingEngine.request;
      let requestComplete;
      /** @type {!jasmine.Spy} */
      const requestSpy = jasmine.createSpy('request');
      /** @type {!shaka.util.PublicPromise} */
      const requestMade = new shaka.util.PublicPromise();
      requestSpy.and.callFake((...args) => {
        requestMade.resolve();
        // eslint-disable-next-line no-restricted-syntax
        requestComplete = originalRequest.call(networkingEngine, ...args);
        return requestComplete;
      });
      networkingEngine.request = shaka.test.Util.spyFunc(requestSpy);

      /** @type {!shaka.util.PublicPromise} */
      const encryptedEventSeen = new shaka.util.PublicPromise();
      eventManager.listen(video, 'encrypted', () => {
        encryptedEventSeen.resolve();
      });
      eventManager.listen(video, 'error', () => {
        fail('MediaError code ' + video.error.code);
        let extended = video.error.msExtendedCode;
        if (extended) {
          if (extended < 0) {
            extended += Math.pow(2, 32);
          }
          fail('MediaError msExtendedCode ' + extended.toString(16));
        }
      });

      /** @type {!shaka.util.PublicPromise} */
      const keyStatusEventSeen = new shaka.util.PublicPromise();
      onKeyStatusSpy.and.callFake(() => {
        keyStatusEventSeen.resolve();
      });

      const variants = manifest.variants;

      await drmEngine.initForPlayback(variants, manifest.offlineSessionIds);
      await drmEngine.attach(video);
      await mediaSourceEngine.appendBuffer(
          ContentType.VIDEO, videoInitSegment, null, null,
          /* hasClosedCaptions= */ false);
      await mediaSourceEngine.appendBuffer(
          ContentType.AUDIO, audioInitSegment, null, null,
          /* hasClosedCaptions= */ false);
      await encryptedEventSeen;
      // With PlayReady, a persistent license policy can cause a different
      // chain of events.  In particular, the request is bypassed and we
      // get a usable key right away.
      await Promise.race([requestMade, keyStatusEventSeen]);

      if (requestSpy.calls.count()) {
        // We made a license request.
        // Only one request should have been made.
        expect(requestSpy).toHaveBeenCalledTimes(1);
        // So it's reasonable to assume that this requestComplete Promise
        // is waiting on the correct request.
        await requestComplete;
      } else {
        // This was probably a PlayReady persistent license.
      }

      // Some platforms (notably 2017 Tizen TVs) do not fire key status
      // events.
      const keyStatusTimeout = shaka.test.Util.delay(5);
      await Promise.race([keyStatusTimeout, keyStatusEventSeen]);

      const call = onKeyStatusSpy.calls.mostRecent();
      if (call) {
        const map = /** @type {!Object} */ (call.args[0]);
        expect(Object.keys(map).length).not.toBe(0);
        for (const k in map) {
          expect(map[k]).toBe('usable');
        }
      }

      await mediaSourceEngine.appendBuffer(
          ContentType.VIDEO, videoSegment, 0, 10,
          /* hasClosedCaptions= */ false);
      await mediaSourceEngine.appendBuffer(
          ContentType.AUDIO, audioSegment, 0, 10,
          /* hasClosedCaptions= */ false);

      expect(video.buffered.end(0)).toBeGreaterThan(0);
      video.play();

      const waiter = new shaka.test.Waiter(eventManager).timeoutAfter(15);
      await waiter.waitForMovement(video);

      // Something should have played by now.
      expect(video.readyState).toBeGreaterThan(1);
      expect(video.currentTime).toBeGreaterThan(0);
    });
  });  // describe('basic flow')
});
<|endoftext|>


<commit_before>/**
 * @module  web
 * methods for browsers and business requirement

 * - domready
 * - data storage
 * - getLocation
 * - UA
 */

;(function(K, undef){

// --- method for KM.data ----------------------- *\
function setData(data){
	data && K.mix(stored_data, data);
};

function cloneData(){
	return K.clone(stored_data);
};

function getData(name){
	return stored_data[name];
};
// --- /method for KM.data ----------------------- *\

/**
 * get the readable location object of current page
 */
function getCurrentLocation(){
	var L, H;

	// IE may throw an exception when accessing
	// a field from document.location if document.domain has been set
	// ref:
	// stackoverflow.com/questions/1498788/reading-window-location-after-setting-document-domain-in-ie6
	try {
		L = DOC.location;
		H = L.href;
	} catch(e) {

		// Use the href attribute of an A element
		// since IE will modify it given document.location
		H = DOC.createElement('a');
		H.href = EMPTY;
		H = H.href;

		L = parseLocation(H);
	}

	return L;
};

/**
 * parse a link to location object
 * @param {string} H
 * @return {Object} custom location object
 */
function parseLocation(H){
	var E = nullOrEmpty, port;

	H = H.match(REGEX_URL);

	port = H[3];

	return {
		href: 		H[0],
		protocol:	H[1],
		host:		H[2] + (port ? ':' + port : EMPTY),
		hostname:	H[2],
		port:		E( port ),
		pathname:	E( H[4] ),
		search:		E( H[5] ),
		hash:		E( H[6] )
	};
};

function nullOrEmpty(str){
	return str || EMPTY;
};

	// @type {Object}
var stored_data = {},

	// @const
	WIN = K.__HOST,
	DOC = WIN.document,
	EMPTY = '',

    /*
    REGEX_URL = /^
    	([\w\+\.\-]+:)		// protocol
    	\/\/
    	([^\/?#:]+)			// domain
    (?::
    	(\d+)				// port
    )?
    	(/[^?#]*)?			// pathname
    	(\?[^?#]*)?			// search
    	(#.*)?				// hash
    $/
    */

    REGEX_URL = /^([\w\+\.\-]+:)\/\/([^\/?#:]+)(?::(\d+))?(\/[^?#]*)?(\?[^#]*)?(#.*)?$/;

/**
 * module  data

 * setter
 * getter

 * usage:
 * 1. KM.data()                     returns the shadow copy of the current data stack
 * 2. KM.data('abc')                returns the data named 'abc'
 * 3. KM.data('abc', 123)           set the value of 'abc' as 123
 * 4. KM.data({abc: 123, edf:456})  batch setter
 *
 * @param {all=} value
 */
K.data = function(name, value){
	var type = K._type(name),
		empty_obj = {},
		is_getter, ret;

	if(name === undef){
		ret = cloneData(); // get: return shadow copy
		is_getter = true;

	}else if(type === 'string'){
		if(value === undef){
			ret = getData(name); // get: return the value of name
			is_getter = true;
		}else{
			empty_obj[name] = value;
			setData(empty_obj) // set
		}

	}else if(type === 'object'){
		setData(name); // set
	}

	return is_getter ? ret : K;
};

/**
 * @param {string} href
 * @return {Object}
 *	- if href undefined, returns the current location
 *	- if href is a string, returns the parsed loaction
 */
K.getLocation = function(href){
	return href ?
		parseLocation(href)
	:	getCurrentLocation();
};

})(KM);

/**
 TODO:
 - add more user-agent datas.
 - add constructor for DP.data

 change log:
 2011-07-05  Kael:
 - remove UA.fullversion
 - add adapter for Browser.Platform

 2011-06-12  Kael:
 - fix a bug about the regular expression of location pattern that more than one question mark should be allowed in search query
 - add UA.chrome.

 2011-04-26  Kael:
 - adapt mootools.Browser
 - remove ua.chrome, ua.safari, add ua.webkit

 2011-04-12  Kael Zhang:
 - fix a bug that domready could not be properly fired
 - add KM.getLocation method,
 	1. to fix the bug of ie6, which will cause an exception when fetching the value of window.location if document.domain is already specified
 	2. which can split an specified uri into different parts

 2010-12-31  Kael Zhang:
 - migrate domready event out from mootools to here, and change some way
 - migrate .data and .delay methods from core/lang to here

 */<commit_msg>add KM.bizRequire method to automatically provide a batch of specified modules<commit_after><|endoftext|>


<commit_before>import tableroPedidos from "../../app/reducers/tableroPedidos";
import {
  AGREGAR_PEDIDO,
  ELIMINAR_PEDIDO,
  AUMENTAR_PRIORIDAD_PEDIDO,
  SOLICITAR_PEDIDOS,
  CARGAR_PEDIDOS
} from "../../app/actions/tableroPedidos";

describe("Reducer Tablero Pedidos", () => {
  it("Agregar un pedido al ejecutar AGREGAR_PEDIDO", () => {
    const estadoInicial = {
      pedidos: [
        { id: 1, nombre: "Pedido 1" }
      ],
      estaCargando: true
    };
    const nuevoPedido = {
      id: 2,
      nombre: "Pedido 2"
    };
    const nuevoEstado = {
      pedidos: [
        { id: 1, nombre: "Pedido 1" },
        { id: 2, nombre: "Pedido 2" }
      ],
      estaCargando: true
    };
    expect(tableroPedidos(estadoInicial, { type: AGREGAR_PEDIDO, pedido: nuevoPedido }))
      .toEqual(nuevoEstado);
  });
  it("Eliminar un pedido al ejecutar ELIMINAR_PEDIDO", () => {
    const estadoInicial = {
      pedidos: [
        { id: 1, nombre: "Pedido 1" },
        { id: 2, nombre: "Pedido 2" },
        { id: 3, nombre: "Pedido 3" }
      ],
      estaCargando: true
    };
    const idPedidoEliminar = 2;
    const nuevoEstado = {
      pedidos: [
        { id: 1, nombre: "Pedido 1" },
        { id: 3, nombre: "Pedido 3" }
      ],
      estaCargando: true
    };
    expect(tableroPedidos(estadoInicial, { type: ELIMINAR_PEDIDO, id: idPedidoEliminar }))
      .toEqual(nuevoEstado);
  });
  it("Debe aumentar en 1 la prioridad de un pedido al ejecutar ELIMINAR_PEDIDO", () => {
    const estadoInicial = {
      pedidos: [
        { id: 1, prioridad: 0 },
        { id: 2, prioridad: 1 }
      ],
      estaCargando: true
    };
    const idPedidoPrioridad = 2;
    const nuevoEstado = {
      pedidos: [
        { id: 1, prioridad: 0 },
        { id: 2, prioridad: 2 }
      ],
      estaCargando: true
    };
    expect(tableroPedidos(estadoInicial, { type: AUMENTAR_PRIORIDAD_PEDIDO, id: idPedidoPrioridad }))
      .toEqual(nuevoEstado);
  });
  it("No debe agregar un pedido si es que el id existe en el store", () => {
    const estadoInicial = {
      pedidos: [
        { id: 1, nombre: "Pedido 1" },
        { id: 2, nombre: "Pedido 2" },
        { id: 3, nombre: "Pedido 3" }
      ],
      estaCargando: true
    };
    const nuevoPedido = {
      id: 2,
      nombre: "Pedido 2"
    };
    const nuevoEstado = {
      pedidos: [
        { id: 1, nombre: "Pedido 1" },
        { id: 2, nombre: "Pedido 2" },
        { id: 3, nombre: "Pedido 3" }
      ],
      estaCargando: true
    };
    expect(tableroPedidos(estadoInicial, { type: AGREGAR_PEDIDO, pedido: nuevoPedido }))
      .toEqual(nuevoEstado);
  });
  it("Solicitar un pedidos debe dejar cargando en verdadero", () => {
    const estadoInicial = {
      pedidos: [],
      estaCargando: false
    };
    const nuevoEstado = {
      pedidos: [],
      estaCargando: true
    };
    expect(tableroPedidos(estadoInicial, { type: SOLICITAR_PEDIDOS }))
      .toEqual(nuevoEstado);
  });
  it("Cargar pedidos debe sobrescribir los pedidos y dejar cargando en falso", () => {
    const estadoInicial = {
      pedidos: [
        { id: 1, nombre: "Pedido 1" },
        { id: 2, nombre: "Pedido 2" },
        { id: 3, nombre: "Pedido 3" }
      ],
      estaCargando: true
    };
    const pedidosCarga = [
      { id: 4, nombre: "Pedido 4" },
      { id: 5, nombre: "Pedido 5" },
      { id: 6, nombre: "Pedido 6" }
    ];
    const nuevoEstado = {
      pedidos: [
        { id: 4, nombre: "Pedido 4" },
        { id: 5, nombre: "Pedido 5" },
        { id: 6, nombre: "Pedido 6" }
      ],
      estaCargando: false
    };
    expect(tableroPedidos(estadoInicial, { type: CARGAR_PEDIDOS, pedidos: pedidosCarga }))
      .toEqual(nuevoEstado);
  });
});
<commit_msg>Se corrigue error en descripcion de prueba<commit_after>import tableroPedidos from "../../app/reducers/tableroPedidos";
import {
  AGREGAR_PEDIDO,
  ELIMINAR_PEDIDO,
  AUMENTAR_PRIORIDAD_PEDIDO,
  SOLICITAR_PEDIDOS,
  CARGAR_PEDIDOS
} from "../../app/actions/tableroPedidos";

describe("Reducer Tablero Pedidos", () => {
  it("Agregar un pedido al ejecutar AGREGAR_PEDIDO", () => {
    const estadoInicial = {
      pedidos: [
        { id: 1, nombre: "Pedido 1" }
      ],
      estaCargando: true
    };
    const nuevoPedido = {
      id: 2,
      nombre: "Pedido 2"
    };
    const nuevoEstado = {
      pedidos: [
        { id: 1, nombre: "Pedido 1" },
        { id: 2, nombre: "Pedido 2" }
      ],
      estaCargando: true
    };
    expect(tableroPedidos(estadoInicial, { type: AGREGAR_PEDIDO, pedido: nuevoPedido }))
      .toEqual(nuevoEstado);
  });
  it("Eliminar un pedido al ejecutar ELIMINAR_PEDIDO", () => {
    const estadoInicial = {
      pedidos: [
        { id: 1, nombre: "Pedido 1" },
        { id: 2, nombre: "Pedido 2" },
        { id: 3, nombre: "Pedido 3" }
      ],
      estaCargando: true
    };
    const idPedidoEliminar = 2;
    const nuevoEstado = {
      pedidos: [
        { id: 1, nombre: "Pedido 1" },
        { id: 3, nombre: "Pedido 3" }
      ],
      estaCargando: true
    };
    expect(tableroPedidos(estadoInicial, { type: ELIMINAR_PEDIDO, id: idPedidoEliminar }))
      .toEqual(nuevoEstado);
  });
  it("Debe aumentar en 1 la prioridad de un pedido al ejecutar ELIMINAR_PEDIDO", () => {
    const estadoInicial = {
      pedidos: [
        { id: 1, prioridad: 0 },
        { id: 2, prioridad: 1 }
      ],
      estaCargando: true
    };
    const idPedidoPrioridad = 2;
    const nuevoEstado = {
      pedidos: [
        { id: 1, prioridad: 0 },
        { id: 2, prioridad: 2 }
      ],
      estaCargando: true
    };
    expect(tableroPedidos(estadoInicial, { type: AUMENTAR_PRIORIDAD_PEDIDO, id: idPedidoPrioridad }))
      .toEqual(nuevoEstado);
  });
  it("No debe agregar un pedido si es que el id existe en el store", () => {
    const estadoInicial = {
      pedidos: [
        { id: 1, nombre: "Pedido 1" },
        { id: 2, nombre: "Pedido 2" },
        { id: 3, nombre: "Pedido 3" }
      ],
      estaCargando: true
    };
    const nuevoPedido = {
      id: 2,
      nombre: "Pedido 2"
    };
    const nuevoEstado = {
      pedidos: [
        { id: 1, nombre: "Pedido 1" },
        { id: 2, nombre: "Pedido 2" },
        { id: 3, nombre: "Pedido 3" }
      ],
      estaCargando: true
    };
    expect(tableroPedidos(estadoInicial, { type: AGREGAR_PEDIDO, pedido: nuevoPedido }))
      .toEqual(nuevoEstado);
  });
  it("Solicitar pedidos debe dejar cargando en verdadero", () => {
    const estadoInicial = {
      pedidos: [],
      estaCargando: false
    };
    const nuevoEstado = {
      pedidos: [],
      estaCargando: true
    };
    expect(tableroPedidos(estadoInicial, { type: SOLICITAR_PEDIDOS }))
      .toEqual(nuevoEstado);
  });
  it("Cargar pedidos debe sobrescribir los pedidos y dejar cargando en falso", () => {
    const estadoInicial = {
      pedidos: [
        { id: 1, nombre: "Pedido 1" },
        { id: 2, nombre: "Pedido 2" },
        { id: 3, nombre: "Pedido 3" }
      ],
      estaCargando: true
    };
    const pedidosCarga = [
      { id: 4, nombre: "Pedido 4" },
      { id: 5, nombre: "Pedido 5" },
      { id: 6, nombre: "Pedido 6" }
    ];
    const nuevoEstado = {
      pedidos: [
        { id: 4, nombre: "Pedido 4" },
        { id: 5, nombre: "Pedido 5" },
        { id: 6, nombre: "Pedido 6" }
      ],
      estaCargando: false
    };
    expect(tableroPedidos(estadoInicial, { type: CARGAR_PEDIDOS, pedidos: pedidosCarga }))
      .toEqual(nuevoEstado);
  });
});
<|endoftext|>