<commit_before>/*
 *  MainScene.js
 *  2014/06/04
 *  @auther minimo
 *  This Program is MIT license.
 *
 */

tm.define("kumabang.MainScene", {
    superClass: tm.app.Scene,

    //マルチタッチ補助クラス
    touches: null,
    touchID: -1,

    //エンドレスモード
    endless: false,

    //現在ステージデータ
    stageNumber: 1,
    stageData: null,

    //スタート＆ゴールパネル座標
    startX: 0,
    startY: 0,
    goalX: 0,
    goalY: 0,
    startPattern: 0,

    //状態フラグ
    ready: false,   //準備ＯＫ
    start: false,   //ゲームスタート

    //パネル配列
    panels: null,

    //選択中パネル
    selectPanel: null,

    //タッチ情報
    moveX: 0,
    moveY: 0,
    beforeX: 0,
    beforeY: 0,
    offsetX: 0,
    offsetY: 0,

    //経過時間
    time: 0,

    init: function() {
        this.superInit();
        this.background = "rgba(0, 0, 0, 0.0)";

        //バックグラウンド
        this.bg = tm.display.Sprite("bg", SC_W*2, SC_H*2).addChildTo(this);

        //マルチタッチ初期化
        this.touches = tm.input.TouchesEx(this);

        //レイヤー準備
        this.lowerLayer = tm.app.Object2D().addChildTo(this);
        this.panelLayer = tm.app.Object2D().addChildTo(this);
        this.playerLayer = tm.app.Object2D().addChildTo(this);
        this.itemLayer = tm.app.Object2D().addChildTo(this);

        //プレイヤー準備
        kumabang.createSpriteSheet();
        this.player = kumabang.Player().addChildTo(this.playerLayer);
        this.player.setPosition(PN_OFFX, PN_OFFY);
        this.player.visible = false;

        //ステージ開始時演出用
        this.egg = kumabang.Egg();
        this.egg.player = this.player;
        this.egg.setPosition(PN_OFFX, PN_OFFY);

        //状態フラグ初期化
        this.ready = true;
        this.start = false;
    },

    update: function() {
        if (this.ready) {
            //パネル初期化
            this.initStage();
            this.ready = false;
            this.start = false;
        }
        if (!this.start) return;
        var player = this.player;
        var px = ~~((player.x-PN_OFFX+PN_W/2)/PN_W), py = ~~((player.y-PN_OFFY+PN_H/2)/PN_H);
        if (player.mapX != px || player.mapY != py) {
            var p = this.checkMapPanel(px, py);
            if (p) {
                player.tweener.clear().moveBy(60, 0, 2000);
                player.mapX = px;
                player.mapY = py;
            } else {
                //ミス！！
            }
        }

        var kb = app.keyboard;
        this.time++;
    },

    //ステージ初期化
    initStage: function() {
        //ステージデータコピー
        this.stageData = kumabang.stageData[this.stageNumber-1];

        //パネル全消去
        if (this.panels) {
            for (i in this.panels) {
                this.panels[i].remove();
            }
        }

        //マップ構築
        this.panels = [];
        for (var y = 0; y < MAP_H; y++){
            for (var x = 0; x < MAP_W; x++){
                var p = kumabang.Panel().addChildTo(this.panelLayer);
                p.x = x*PN_W+PN_OFFX;
                p.y = y*PN_H+PN_OFFY;
                p.mapX = x;
                p.mapY = y;
                p.pattern = this.stageData.map[y][x];
                var item = this.stageData.item[y][x];
                if (item != 0) p.shuffle = false;
                if (item != 0 && item != 6)p.onItem = true;
                //スタート位置
                if (item == 8) {
                    this.startX = x;
                    this.startY = y;
                    this.startPattern = p.pattern;
                }
                //ゴール位置
                if (item == 9) {
                    this.goalX = x;
                    this.goalY = y;
                }

                this.panels.push(p);
            }
        }
        //パネルシャッフル
        for (var i = 0; i < rand(15, 20); i++){
            var a = rand(0, this.panels.length-1);
            var b = rand(0, this.panels.length-1);
            var p1 = this.panels[a];
            var p2 = this.panels[b];
            if (a == b || !p1.shuffle || !p2.shuffle) {i--; continue;}
            var tx = p1.mapX, ty = p1.mapY;
            p1.move(p2.mapX, p2.mapY);
            p2.move(tx, ty);
        }

        //プレイヤー準備
        var sx = PN_OFFX+this.startX*PN_W;
        var sy = PN_OFFY+this.startY*PN_H;
        this.player.setPosition(sx, sy);
        this.player.scaleX = -1;
        this.player.visible = false;
        this.egg.addChildTo(this.playerLayer);
        this.egg.scaleX = -1;
        this.egg.setPosition(sx, sy);
        this.egg.gotoAndPlay("enter");

        //ゴール準備
        if (!this.endless) {
            var gx = PN_OFFX+this.goalX*PN_W;
            var gy = PN_OFFY+this.goalY*PN_H;
        }

        //スタートメッセージ
        var that = this;
        var lb = tm.display.OutlineLabel("３", 30).addChildTo(this);
        lb.setPosition(SC_W/2, -SC_H/2);
        lb.alpha = 0;
        lb.fontFamily = "'KS-Kohichi-FeltPen'";
        lb.align     = "center";
        lb.baseline  = "middle";
        lb.fontSize = 25;
        lb.outlineWidth = 2;
        lb.tweener.clear();
        lb.tweener.call(function(){lb.text = "３";}).to({x: SC_W/2, y: -SC_H}, 1).fadeIn(1).move(SC_W/2, SC_H/2, 400, "easeOutQuint").fadeOut(200);
        lb.tweener.call(function(){lb.text = "２";}).to({x: SC_W/2, y: -SC_H}, 1).fadeIn(1).move(SC_W/2, SC_H/2, 400, "easeOutQuint").fadeOut(200);
        lb.tweener.call(function(){lb.text = "１";}).to({x: SC_W/2, y: -SC_H}, 1).fadeIn(1).move(SC_W/2, SC_H/2, 400, "easeOutQuint").fadeOut(200);
        lb.tweener.call(function(){lb.text = "スタート！";}).to({x: SC_W/2, y: -SC_H}, 1).fadeIn(1).move(SC_W/2, SC_H/2, 500, "easeOutQuint").fadeOut(200);
        lb.tweener.move(SC_W/2, SC_H/2, 500, "easeOutQuint").fadeOut(200).call(function(){that.start = true;lb.remove();});
    },

    //スクリーン座標上のパネル判定
    checkScreenPanel: function(x, y) {
        var len = this.panels.length;
        for (var i = 0; i< len; i++) {
            var p = this.panels[i];
            if (p.disable)continue;
            var px = p.x-PN_W_HALF;
            var py = p.y-PN_H_HALF;
            if (px < x && x < px+PN_W && py < y && y < py+PN_H) return p;
        }
        return null;
    },

    //マップ座標上のパネル判定
    checkMapPanel: function(x, y) {
        var len = this.panels.length;
        for (var i = 0; i< len; i++) {
            var p = this.panels[i];
            if (p.select)continue;
            if (p.mapX == x && p.mapY == y) return p;
        }
        return null;
    },

    //タッチorクリック開始処理
    ontouchesstart: function(e) {
        if (this.touchID > 0)return;
        this.touchID = e.ID;
        var sx = this.moveX = this.beforeX = e.pointing.x;
        var sy = this.moveY = this.beforeY = e.pointing.y;

        var p = this.checkScreenPanel(sx, sy);
        if (p) {
            p.select = true;
            p.tweener.clear().scale(0.9, 100);
            p.remove().addChildTo(this.panelLayer); //一番手前に持ってくる
            this.selectPanel = p;

            //パネルの座標とタッチ座標のオフセットを計算
            this.offsetX = sx-p.x;
            this.offsetY = sy-p.y;
        }
    },

    //タッチorクリック移動処理
    ontouchesmove: function(e) {
        if (this.touchID != e.ID) return;
        var sx = this.moveX = e.pointing.x;
        var sy = this.moveY = e.pointing.y;
        if (this.selectPanel) {
            var p = this.selectPanel;
            //パネルが領域外に行かない様に制限
            p.x = clamp(sx-this.offsetX, PN_OFFX, PN_OFFX+PN_W*(MAP_W-1));
            p.y = clamp(sy-this.offsetY, PN_OFFY, PN_OFFY+PN_H*(MAP_H-1));

            //選択中パネルの位置に他のパネルが合ったら場所を交換
            var mx = clamp(~~((p.x-PN_OFFX+PN_W_HALF)/PN_W), 0, MAP_W-1);
            var my = clamp(~~((p.y-PN_OFFY+PN_H_HALF)/PN_H), 0, MAP_H-1);
            if (p.mapX != mx || p.mapY != my) {
                var mp = this.checkMapPanel(mx, my);
                if (mp && !mp.disable) {
                    //行き先にパネルが無ければ移動
                    var fp = this.checkMapPanel(p.mapX, p.MapY);
                    if (!fp) mp.move(p.mapX, p.mapY);
                    p.mapX = mx;
                    p.mapY = my;
                }
                if (mp == null) {
                    p.mapX = mx;
                    p.mapY = my;
                }
            }
        }
        this.beforeX = sx;
        this.beforeY = sy;
    },

    //タッチorクリック終了処理
    ontouchesend: function(e) {
        if (this.touchID != e.ID) return;
        this.touchID = -1;
        if (this.selectPanel) {
            var p = this.selectPanel;
            p.select = false;
            p.reverse();
            this.selectPanel = null;
        }
    },

});

<commit_msg>tick player<commit_after>/*
 *  MainScene.js
 *  2014/06/04
 *  @auther minimo
 *  This Program is MIT license.
 *
 */

tm.define("kumabang.MainScene", {
    superClass: tm.app.Scene,

    //マルチタッチ補助クラス
    touches: null,
    touchID: -1,

    //エンドレスモード
    endless: false,

    //現在ステージデータ
    stageNumber: 1,
    stageData: null,

    //スタート＆ゴールパネル座標
    startX: 0,
    startY: 0,
    goalX: 0,
    goalY: 0,
    startPattern: 0,

    //状態フラグ
    ready: false,   //準備ＯＫ
    start: false,   //ゲームスタート

    //１つのパネルを通る時間
    speed: 1500,

    //パネル配列
    panels: null,

    //選択中パネル
    selectPanel: null,

    //タッチ情報
    moveX: 0,
    moveY: 0,
    beforeX: 0,
    beforeY: 0,
    offsetX: 0,
    offsetY: 0,

    //経過時間
    time: 0,

    init: function() {
        this.superInit();
        this.background = "rgba(0, 0, 0, 0.0)";

        //バックグラウンド
        this.bg = tm.display.Sprite("bg", SC_W*2, SC_H*2).addChildTo(this);

        //マルチタッチ初期化
        this.touches = tm.input.TouchesEx(this);

        //レイヤー準備
        this.lowerLayer = tm.app.Object2D().addChildTo(this);
        this.panelLayer = tm.app.Object2D().addChildTo(this);
        this.playerLayer = tm.app.Object2D().addChildTo(this);
        this.itemLayer = tm.app.Object2D().addChildTo(this);

        //プレイヤー準備
        kumabang.createSpriteSheet();
        this.player = kumabang.Player().addChildTo(this.playerLayer);
        this.player.setPosition(PN_OFFX, PN_OFFY);
        this.player.visible = false;

        //ステージ開始時演出用
        this.egg = kumabang.Egg();
        this.egg.player = this.player;
        this.egg.setPosition(PN_OFFX, PN_OFFY);

        //状態フラグ初期化
        this.ready = true;
        this.start = false;
    },

    update: function() {
        if (this.ready) {
            //パネル初期化
            this.initStage();
            this.ready = false;
            this.start = false;
        }
        if (!this.start) return;

        this.tickPlayer();

        var kb = app.keyboard;
        this.time++;
    },

    //ステージ初期化
    initStage: function() {
        //ステージデータコピー
        this.stageData = kumabang.stageData[this.stageNumber-1];

        //パネル全消去
        if (this.panels) {
            for (i in this.panels) {
                this.panels[i].remove();
            }
        }

        //マップ構築
        this.panels = [];
        for (var y = 0; y < MAP_H; y++){
            for (var x = 0; x < MAP_W; x++){
                var p = kumabang.Panel().addChildTo(this.panelLayer);
                p.x = x*PN_W+PN_OFFX;
                p.y = y*PN_H+PN_OFFY;
                p.mapX = x;
                p.mapY = y;
                p.pattern = this.stageData.map[y][x];
                var item = this.stageData.item[y][x];
                if (item != 0) p.shuffle = false;
                if (item != 0 && item != 6)p.onItem = true;
                //スタート位置
                if (item == 8) {
                    this.startX = x;
                    this.startY = y;
                    this.startPattern = p.pattern;
                }
                //ゴール位置
                if (item == 9) {
                    this.goalX = x;
                    this.goalY = y;
                }

                this.panels.push(p);
            }
        }
        //パネルシャッフル
        for (var i = 0; i < rand(15, 20); i++){
            var a = rand(0, this.panels.length-1);
            var b = rand(0, this.panels.length-1);
            var p1 = this.panels[a];
            var p2 = this.panels[b];
            if (a == b || !p1.shuffle || !p2.shuffle) {i--; continue;}
            var tx = p1.mapX, ty = p1.mapY;
            p1.move(p2.mapX, p2.mapY);
            p2.move(tx, ty);
        }

        //プレイヤー準備
        var sx = PN_OFFX+this.startX*PN_W;
        var sy = PN_OFFY+this.startY*PN_H;
        this.player.setPosition(sx, sy);
        this.player.scaleX = -1;
        this.player.visible = false;
        this.egg.addChildTo(this.playerLayer);
        this.egg.scaleX = -1;
        this.egg.setPosition(sx, sy);
        this.egg.gotoAndPlay("enter");

        //ゴール準備
        if (!this.endless) {
            var gx = PN_OFFX+this.goalX*PN_W;
            var gy = PN_OFFY+this.goalY*PN_H;
        }

        //スタートメッセージ
        var that = this;
        var lb = tm.display.OutlineLabel("３", 30).addChildTo(this);
        lb.setPosition(SC_W/2, -SC_H/2);
        lb.alpha = 0;
        lb.fontFamily = "'KS-Kohichi-FeltPen'";
        lb.align     = "center";
        lb.baseline  = "middle";
        lb.fontSize = 25;
        lb.outlineWidth = 2;
        lb.tweener.clear();
        lb.tweener.call(function(){lb.text = "３";}).to({x: SC_W/2, y: -SC_H}, 1).fadeIn(1).move(SC_W/2, SC_H/2, 400, "easeOutQuint").fadeOut(200);
        lb.tweener.call(function(){lb.text = "２";}).to({x: SC_W/2, y: -SC_H}, 1).fadeIn(1).move(SC_W/2, SC_H/2, 400, "easeOutQuint").fadeOut(200);
        lb.tweener.call(function(){lb.text = "１";}).to({x: SC_W/2, y: -SC_H}, 1).fadeIn(1).move(SC_W/2, SC_H/2, 400, "easeOutQuint").fadeOut(200);
        lb.tweener.call(function(){lb.text = "スタート！";}).to({x: SC_W/2, y: -SC_H}, 1).fadeIn(1).move(SC_W/2, SC_H/2, 500, "easeOutQuint").fadeOut(200);
        lb.tweener.move(SC_W/2, SC_H/2, 500, "easeOutQuint").fadeOut(200).call(function(){that.start = true;lb.remove();});
    },

    //スクリーン座標上のパネル判定
    checkScreenPanel: function(x, y) {
        var len = this.panels.length;
        for (var i = 0; i< len; i++) {
            var p = this.panels[i];
            if (p.disable)continue;
            var px = p.x-PN_W_HALF;
            var py = p.y-PN_H_HALF;
            if (px < x && x < px+PN_W && py < y && y < py+PN_H) return p;
        }
        return null;
    },

    //マップ座標上のパネル判定
    checkMapPanel: function(x, y) {
        var len = this.panels.length;
        for (var i = 0; i< len; i++) {
            var p = this.panels[i];
            if (p.select)continue;
            if (p.mapX == x && p.mapY == y) return p;
        }
        return null;
    },

    //プレイヤー処理（パネル＆アイテム）
    tickPlayer: function() {
        var player = this.player;
        var px = ~~((player.x-PN_OFFX+PN_W/2)/PN_W), py = ~~((player.y-PN_OFFY+PN_H/2)/PN_H);
        if (player.mapX != px || player.mapY != py) {
            var p = this.checkMapPanel(px, py);
            if (p) {
                var vx = px-player.mapX;
                var vy = py-player.mapY;
                switch (p.pattern) {
                    case 1: //横
                        player.tweener.moveBy(60*vx, 0, this.speed);
                        break;
                    case 2: //縦
                        player.tweener.moveBy(0, 60*vy, this.speed);
                        break;
                    case 3: //十字
                        if (vx != 0) {
                            player.tweener.moveBy(60*vx, 0, this.speed);
                        } else {
                            player.tweener.moveBy(0, 60*vy, this.speed);
                        }
                        break;
                    case 4: //右－下
                        if (vx != 0) {
                            //右から進入
                            player.tweener.moveBy(-30, 0, this.speed/2);
                            player.tweener.moveBy(0, 30, this.speed/2);
                        } else {
                            //下から進入
                            player.tweener.moveBy(0, -30, this.speed/2);
                            player.tweener.moveBy(30, 0, this.speed/2);
                        }
                        break;
                    case 5: //左－下
                        if (vx != 0) {
                            //左から進入
                            player.tweener.moveBy(30, 0, this.speed/2);
                            player.tweener.moveBy(0, 30, this.speed/2);
                        } else {
                            //下から進入
                            player.tweener.moveBy(0, -30, this.speed/2);
                            player.tweener.moveBy(-30, 0, this.speed/2);
                        }
                        break;
                    case 6: //右－上
                        if (vx != 0) {
                            //右から進入
                            player.tweener.moveBy(-30, 0, this.speed/2);
                            player.tweener.moveBy(0, -31, this.speed/2);
                        } else {
                            //上から進入
                            player.tweener.moveBy(0, 30, this.speed/2);
                            player.tweener.moveBy(31, 0, this.speed/2);
                        }
                        break;
                    case 7: //左－下
                        if (vx != 0) {
                            //左から進入
                            player.tweener.moveBy(30, 0, this.speed/2);
                            player.tweener.moveBy(0, -30, this.speed/2);
                        } else {
                            //上から進入
                            player.tweener.moveBy(0, 30, this.speed/2);
                            player.tweener.moveBy(-30, 0, this.speed/2);
                        }
                        break;

                    //スタート地点用パネル
                    case 8:
                        player.tweener.moveBy(30, 0, this.speed/2);
                        break;
                    case 9:
                        player.tweener.moveBy(0, 30,  this.speed/2);
                        break;
                    case 10:
                        player.tweener.moveBy(-30, 0,  this.speed/2);
                        break;
                    case 11:
                        player.tweener.moveBy(0, -30,  this.speed/2);
                        break;
                }
                player.mapX = px;
                player.mapY = py;
            } else {
                //ミス！！
            }
        }
    },

    //タッチorクリック開始処理
    ontouchesstart: function(e) {
        if (this.touchID > 0)return;
        this.touchID = e.ID;
        var sx = this.moveX = this.beforeX = e.pointing.x;
        var sy = this.moveY = this.beforeY = e.pointing.y;

        var p = this.checkScreenPanel(sx, sy);
        if (p) {
            p.select = true;
            p.tweener.clear().scale(0.9, 100);
            p.remove().addChildTo(this.panelLayer); //一番手前に持ってくる
            this.selectPanel = p;

            //パネルの座標とタッチ座標のオフセットを計算
            this.offsetX = sx-p.x;
            this.offsetY = sy-p.y;
        }
    },

    //タッチorクリック移動処理
    ontouchesmove: function(e) {
        if (this.touchID != e.ID) return;
        var sx = this.moveX = e.pointing.x;
        var sy = this.moveY = e.pointing.y;
        if (this.selectPanel) {
            var p = this.selectPanel;
            //パネルが領域外に行かない様に制限
            p.x = clamp(sx-this.offsetX, PN_OFFX, PN_OFFX+PN_W*(MAP_W-1));
            p.y = clamp(sy-this.offsetY, PN_OFFY, PN_OFFY+PN_H*(MAP_H-1));

            //選択中パネルの位置に他のパネルが合ったら場所を交換
            var mx = clamp(~~((p.x-PN_OFFX+PN_W_HALF)/PN_W), 0, MAP_W-1);
            var my = clamp(~~((p.y-PN_OFFY+PN_H_HALF)/PN_H), 0, MAP_H-1);
            if (p.mapX != mx || p.mapY != my) {
                var mp = this.checkMapPanel(mx, my);
                if (mp && !mp.disable) {
                    //行き先にパネルが無ければ移動
                    var fp = this.checkMapPanel(p.mapX, p.MapY);
                    if (!fp) mp.move(p.mapX, p.mapY);
                    p.mapX = mx;
                    p.mapY = my;
                }
                if (mp == null) {
                    p.mapX = mx;
                    p.mapY = my;
                }
            }
        }
        this.beforeX = sx;
        this.beforeY = sy;
    },

    //タッチorクリック終了処理
    ontouchesend: function(e) {
        if (this.touchID != e.ID) return;
        this.touchID = -1;
        if (this.selectPanel) {
            var p = this.selectPanel;
            p.select = false;
            p.reverse();
            this.selectPanel = null;
        }
    },

});

<|endoftext|>


<commit_before>pc.extend(pc.Application.prototype, function () {

    var lineVertexFormat = null;
    var lineBatches = [];
    var quadMesh = null;
    var cubeLocalPos = null;
    var cubeWorldPos = null;
    var tempGraphNode = new pc.GraphNode();
    var identityGraphNode = new pc.GraphNode();
    var meshInstanceArray = [];

    var lineBatch = function () {
        // Sensible default value; buffers will be doubled and reallocated when it's not enough
        this.numLinesAllocated = 128;

        this.vb = null;
        this.vbRam = null;
        this.mesh = null;
        this.linesUsed = 0;
        this.material = null;
        this.meshInstance = null;
    };

    lineBatch.prototype = {
        init: function (device, linesToAdd) {
            // Allocate basic stuff once per batch
            if (!this.mesh) {
                this.mesh = new pc.Mesh();
                this.mesh.primitive[0].type = pc.PRIMITIVE_LINES;
                this.mesh.primitive[0].base = 0;
                this.mesh.primitive[0].indexed = false;

                this.material = new pc.BasicMaterial();
                this.material.vertexColors = true;
                this.material.blend = true;
                this.material.blendType = pc.BLEND_NORMAL;
                this.material.update();
            }

            // Increase buffer size, if it's not enough
            while((this.linesUsed + linesToAdd) > this.numLinesAllocated) {
                this.vb = null;
                this.numLinesAllocated *= 2;
            }

            // (Re)allocate line buffer
            if (!this.vb) {
                this.vb = new pc.VertexBuffer(device, lineVertexFormat, this.numLinesAllocated * 2, pc.BUFFER_DYNAMIC);
                this.mesh.vertexBuffer = this.vb;
                this.vbRam = new DataView(this.vb.lock());

                if (!this.meshInstance) {
                    identityGraphNode.worldTransform = pc.Mat4.IDENTITY;
                    identityGraphNode._dirtyWorld = identityGraphNode._dirtyNormal = false;
                    this.meshInstance = new pc.MeshInstance(identityGraphNode, this.mesh, this.material);
                    this.meshInstance.cull = false;
                }
            }
        },

        addLines: function(position, color) {
            // Append lines to buffer
            var multiColor = !!color.length;
            var offset = this.linesUsed * 2 * lineVertexFormat.size;
            var clr;
            for(var i=0; i<position.length; i++) {
                this.vbRam.setFloat32(offset, position[i].x, true); offset += 4;
                this.vbRam.setFloat32(offset, position[i].y, true); offset += 4;
                this.vbRam.setFloat32(offset, position[i].z, true); offset += 4;
                clr = multiColor? color[i] : color;
                this.vbRam.setUint8(offset, clr.r * 255); offset += 1;
                this.vbRam.setUint8(offset, clr.g * 255); offset += 1;
                this.vbRam.setUint8(offset, clr.b * 255); offset += 1;
                this.vbRam.setUint8(offset, clr.a * 255); offset += 1;
            }
            this.linesUsed += position.length / 2;
        },

        finalize: function(layer) {
            // Update batch vertex buffer/issue drawcall if there are any lines
            if (this.linesUsed > 0) {
                this.vb.setData(this.vbRam.buffer);
                this.mesh.primitive[0].count = this.linesUsed * 2;
                meshInstanceArray[0] = this.meshInstance;
                layer.addMeshInstances(meshInstanceArray, true);
                this.linesUsed = 0;
            }
        }
    };

    function _initImmediate() {
        // Init global line drawing data once
        if (!lineVertexFormat) {
            lineVertexFormat = new pc.VertexFormat(this.graphicsDevice, [
                    { semantic: pc.SEMANTIC_POSITION, components: 3, type: pc.TYPE_FLOAT32 },
                    { semantic: pc.SEMANTIC_COLOR, components: 4, type: pc.TYPE_UINT8, normalize: true }
                ]);
            this.on('prerender', this._preRenderImmediate, this);
            this.on('postrender', this._postRenderImmediate, this);
        }
    }

    function _addLines(batchId, position, color) {
        this._initImmediate();
        if (!lineBatches[batchId]) {
            // Init used batch once
            lineBatches[batchId] = new lineBatch();
            lineBatches[batchId].init(this.graphicsDevice, position.length / 2);
            if (batchId===pc.LINEBATCH_OVERLAY) {
                lineBatches[batchId].material.depthTest = false;
            }
        } else {
            // Possibly reallocate buffer if it's small
            lineBatches[batchId].init(this.graphicsDevice, position.length / 2);
        }
        // Append
        lineBatches[batchId].addLines(position, color);
    }

    /**
     * @function
     * @name pc.Application#renderLine
     * @description Draw a line in one color
     * @param {pc.Vec3} start The start of the line
     * @param {pc.Vec3} end The end of the line
     * @param {pc.Color} color The color of the line
     * @example
     * var start = new pc.Vec3(0,0,0);
     * var end = new pc.Vec3(1,0,0);
     * var color = new pc.Color(1,1,1);
     * app.renderLine(start, end, color);
     */
    /**
     * @function
     * @name pc.Application#renderLine^2
     * @description Draw a line which blends between two colors
     * @param {pc.Vec3} start The start of the line
     * @param {pc.Vec3} end The end of the line
     * @param {pc.Color} startColor The start color of the line
     * @param {pc.Color} endColor The end color of the line
     * @example
     * var start = new pc.Vec3(0,0,0);
     * var end = new pc.Vec3(1,0,0);
     * var startColor = new pc.Color(1,1,1);
     * var endColor = new pc.Color(1,0,0);
     * app.renderLine(start, end, startColor, endColor);
     */
    /**
     * @function
     * @name pc.Application#renderLine^3
     * @description Draw a line of one color with specified line type
     * @param {pc.Vec3} start The start of the line
     * @param {pc.Vec3} end The end of the line
     * @param {pc.Color} color The color of the line
     * @param {Number} lineType The type of rendering to use: pc.LINEBATCH_WORLD, pc.LINEBATCH_OVERLAY, pc.LINEBATCH_GIZMO. Default is pc.LINEBATCH_WORLD
     * @example
     * var start = new pc.Vec3(0,0,0);
     * var end = new pc.Vec3(1,0,0);
     * var color = new pc.Color(1,1,1);
     * app.renderLine(start, end, color, pc.LINEBATCH_OVERLAY);
     */
    /**
     * @function
     * @name pc.Application#renderLine^4
     * @description Draw a line which blends between two colors with specified line type
     * @param {pc.Vec3} start The start of the line
     * @param {pc.Vec3} end The end of the line
     * @param {pc.Color} startColor The start color of the line
     * @param {pc.Color} endColor The end color of the line
     * @param {Number} lineType The type of rendering to use: pc.LINEBATCH_WORLD, pc.LINEBATCH_OVERLAY, pc.LINEBATCH_GIZMO. Default is pc.LINEBATCH_WORLD
     * @example
     * var start = new pc.Vec3(0,0,0);
     * var end = new pc.Vec3(1,0,0);
     * var startColor = new pc.Color(1,1,1);
     * var endColor = new pc.Color(1,0,0);
     * app.renderLine(start, end, startColor, endColor, pc.LINEBATCH_OVERLAY);
     */
    function renderLine(start, end, color, arg3, arg4) {
        var endColor = color;
        var lineType = pc.LINEBATCH_WORLD;
        if (arg3) {
            if (typeof(arg3) === 'number') {
                lineType = arg3;
            } else {
                endColor = arg3;
                if (arg4) lineType = arg4;
            }
        }
        this._addLines(lineType, [start, end], [color, endColor]);
    }

    /**
     * @function
     * @name pc.Application#renderLines
     * @description Draw an array of lines.
     * @param {pc.Vec3[]} position An array of points to draw lines between
     * @param {pc.Color[]} color An array of colors to color the lines. This must be the same size as the position array
     * @param {Number} [lineType] The type of rendering to use: pc.LINEBATCH_WORLD, pc.LINEBATCH_OVERLAY, pc.LINEBATCH_GIZMO. Default is pc.LINEBATCH_WORLD
     * @example
     * var points = [new pc.Vec3(0,0,0), new pc.Vec3(1,0,0), new pc.Vec3(1,1,0), new pc.Vec3(1,1,1)];
     * var colors = [new pc.Color(1,0,0), new pc.Color(1,1,0), new pc.Color(0,1,1), new pc.Color(0,0,1)];
     * app.renderLines(points, colors);
    */
    function renderLines(position, color, lineType) {
        if (lineType===undefined) lineType = pc.LINEBATCH_WORLD;
        var multiColor = !!color.length;
        if (multiColor) {
            if (position.length !== color.length) {
                pc.log.error("renderLines: position/color arrays have different lengths");
                return;
            }
        }
        if (position.length % 2 !== 0) {
            pc.log.error("renderLines: array length is not divisible by 2");
            return;
        }
        this._addLines(lineType, position, color);
    }

    // Draw lines forming a transformed unit-sized cube at this frame
    // lineType is optional
    function renderWireCube(matrix, color, lineType) {
        if (lineType===undefined) lineType = pc.LINEBATCH_WORLD;
        var i;
        // Init cube data once
        if (!cubeLocalPos) {
            var x = 0.5;
            cubeLocalPos = [new pc.Vec3(-x, -x, -x), new pc.Vec3(-x, x, -x), new pc.Vec3(x, x, -x), new pc.Vec3(x, -x, -x),
                            new pc.Vec3(-x, -x, x), new pc.Vec3(-x, x, x), new pc.Vec3(x, x, x), new pc.Vec3(x, -x, x)];
            cubeWorldPos = [new pc.Vec3(), new pc.Vec3(), new pc.Vec3(), new pc.Vec3(),
                            new pc.Vec3(), new pc.Vec3(), new pc.Vec3(), new pc.Vec3()];
        }
        // Transform and append lines
        for(i=0; i<8; i++) {
            matrix.transformPoint(cubeLocalPos[i], cubeWorldPos[i]);
        }
        this.renderLines([cubeWorldPos[0],cubeWorldPos[1],
                     cubeWorldPos[1],cubeWorldPos[2],
                     cubeWorldPos[2],cubeWorldPos[3],
                     cubeWorldPos[3],cubeWorldPos[0],

                     cubeWorldPos[4],cubeWorldPos[5],
                     cubeWorldPos[5],cubeWorldPos[6],
                     cubeWorldPos[6],cubeWorldPos[7],
                     cubeWorldPos[7],cubeWorldPos[4],

                     cubeWorldPos[0],cubeWorldPos[4],
                     cubeWorldPos[1],cubeWorldPos[5],
                     cubeWorldPos[2],cubeWorldPos[6],
                     cubeWorldPos[3],cubeWorldPos[7]
                     ], color, lineType);
    }

    function _preRenderImmediate() {
        for(var i=0; i<3; i++) {
            if (lineBatches[i]) {
                lineBatches[i].finalize(this._immediateLayer);
            }
        }
    }

    function _postRenderImmediate() {
        this._immediateLayer.clearMeshInstances(true);
    }

    // Draw meshInstance at this frame
    function renderMeshInstance(meshInstance) {
        this._initImmediate();
        meshInstanceArray[0] = meshInstance;
        this._immediateLayer.addMeshInstances(meshInstanceArray, true);
    }

    // Draw mesh at this frame
    function renderMesh(mesh, material, matrix) {
        this._initImmediate();
        tempGraphNode.worldTransform = matrix;
        tempGraphNode._dirtyWorld = tempGraphNode._dirtyNormal = false;
        var instance = new pc.MeshInstance(tempGraphNode, mesh, material);
        instance.cull = false;
        meshInstanceArray[0] = instance;
        this._immediateLayer.addMeshInstances(meshInstanceArray, true);
    }

    // Draw quad of size [-0.5, 0.5] at this frame
    function renderQuad(matrix, material) {
        // Init quad data once
        if (!quadMesh) {
            var format = new pc.VertexFormat(this.graphicsDevice, [
                    { semantic: pc.SEMANTIC_POSITION, components: 3, type: pc.TYPE_FLOAT32 }
                ]);
            var quadVb = new pc.VertexBuffer(this.graphicsDevice, format, 4);
            var iterator = new pc.VertexIterator(quadVb);
            iterator.element[pc.SEMANTIC_POSITION].set(-0.5, -0.5, 0);
            iterator.next();
            iterator.element[pc.SEMANTIC_POSITION].set(0.5, -0.5, 0);
            iterator.next();
            iterator.element[pc.SEMANTIC_POSITION].set(-0.5, 0.5, 0);
            iterator.next();
            iterator.element[pc.SEMANTIC_POSITION].set(0.5, 0.5, 0);
            iterator.end();
            quadMesh = new pc.Mesh();
            quadMesh.vertexBuffer = quadVb;
            quadMesh.primitive[0].type = pc.PRIMITIVE_TRISTRIP;
            quadMesh.primitive[0].base = 0;
            quadMesh.primitive[0].count = 4;
            quadMesh.primitive[0].indexed = false;
            this._initImmediate();
        }
        // Issue quad drawcall
        tempGraphNode.worldTransform = matrix;
        tempGraphNode._dirtyWorld = tempGraphNode._dirtyNormal = false;
        var quad = new pc.MeshInstance(tempGraphNode, quadMesh, material);
        quad.cull = false;
        meshInstanceArray[0] = quad;
        this._immediateLayer.addMeshInstances(meshInstanceArray, true);
    }

    return {
        renderMeshInstance: renderMeshInstance,
        renderMesh: renderMesh,
        renderLine: renderLine,
        renderLines: renderLines,
        renderQuad: renderQuad,
        renderWireCube: renderWireCube,
        _addLines: _addLines,
        _initImmediate: _initImmediate,
        _preRenderImmediate: _preRenderImmediate,
        _postRenderImmediate: _postRenderImmediate
    };
}());
<commit_msg>Update immediate mode rendering to take layer options argument [BREAKING] pc.LINEBATCH_GIZMO no longer supported use options.layers instead<commit_after>pc.extend(pc.Application.prototype, function () {

    var lineVertexFormat = null;
    var lineBatches = [];
    var layerToBatch = {};
    var quadMesh = null;
    var cubeLocalPos = null;
    var cubeWorldPos = null;
    var tempGraphNode = new pc.GraphNode();
    var identityGraphNode = new pc.GraphNode();
    var meshInstanceArray = [];

    var _deprecationWarning = false;

    var LineBatch = function () {
        // Sensible default value; buffers will be doubled and reallocated when it's not enough
        this.numLinesAllocated = 128;

        this.vb = null;
        this.vbRam = null;
        this.mesh = null;
        this.linesUsed = 0;
        this.material = null;
        this.meshInstance = null;

        this.layer = null;
    };

    LineBatch.prototype = {
        init: function (device, layer, linesToAdd) {
            // Allocate basic stuff once per batch
            if (!this.mesh) {
                this.mesh = new pc.Mesh();
                this.mesh.primitive[0].type = pc.PRIMITIVE_LINES;
                this.mesh.primitive[0].base = 0;
                this.mesh.primitive[0].indexed = false;

                this.material = new pc.BasicMaterial();
                this.material.vertexColors = true;
                this.material.blend = true;
                this.material.blendType = pc.BLEND_NORMAL;
                this.material.update();
            }

            this.layer = layer;

            // Increase buffer size, if it's not enough
            while((this.linesUsed + linesToAdd) > this.numLinesAllocated) {
                this.vb = null;
                this.numLinesAllocated *= 2;
            }

            // (Re)allocate line buffer
            if (!this.vb) {
                this.vb = new pc.VertexBuffer(device, lineVertexFormat, this.numLinesAllocated * 2, pc.BUFFER_DYNAMIC);
                this.mesh.vertexBuffer = this.vb;
                this.vbRam = new DataView(this.vb.lock());

                if (!this.meshInstance) {
                    identityGraphNode.worldTransform = pc.Mat4.IDENTITY;
                    identityGraphNode._dirtyWorld = identityGraphNode._dirtyNormal = false;
                    this.meshInstance = new pc.MeshInstance(identityGraphNode, this.mesh, this.material);
                    this.meshInstance.cull = false;
                }
            }
        },

        addLines: function(position, color) {
            // Append lines to buffer
            var multiColor = !!color.length;
            var offset = this.linesUsed * 2 * lineVertexFormat.size;
            var clr;
            for(var i=0; i<position.length; i++) {
                this.vbRam.setFloat32(offset, position[i].x, true); offset += 4;
                this.vbRam.setFloat32(offset, position[i].y, true); offset += 4;
                this.vbRam.setFloat32(offset, position[i].z, true); offset += 4;
                clr = multiColor? color[i] : color;
                this.vbRam.setUint8(offset, clr.r * 255); offset += 1;
                this.vbRam.setUint8(offset, clr.g * 255); offset += 1;
                this.vbRam.setUint8(offset, clr.b * 255); offset += 1;
                this.vbRam.setUint8(offset, clr.a * 255); offset += 1;
            }
            this.linesUsed += position.length / 2;
        },

        finalize: function() {
            // Update batch vertex buffer/issue drawcall if there are any lines
            if (this.linesUsed > 0) {
                this.vb.setData(this.vbRam.buffer);
                this.mesh.primitive[0].count = this.linesUsed * 2;
                meshInstanceArray[0] = this.meshInstance;
                this.layer.addMeshInstances(meshInstanceArray, true);
                this.linesUsed = 0;
            }
        }
    };

    function _initImmediate() {
        // Init global line drawing data once
        if (!lineVertexFormat) {
            lineVertexFormat = new pc.VertexFormat(this.graphicsDevice, [
                    { semantic: pc.SEMANTIC_POSITION, components: 3, type: pc.TYPE_FLOAT32 },
                    { semantic: pc.SEMANTIC_COLOR, components: 4, type: pc.TYPE_UINT8, normalize: true }
                ]);
            this.on('prerender', this._preRenderImmediate, this);
            this.on('postrender', this._postRenderImmediate, this);
        }
    }

    function _getLayerIdx (layer) {
        return layerToBatch[layer.id]
    }

    function _addLayerIdx (idx, layer) {
        layerToBatch[layer.id] = idx;
    }

    function _addLines(position, color, options) {
        if (options.layer === undefined) options.layer = this.scene.layers.getLayerById(pc.LAYERID_IMMEDIATE);
        if (options.depthTest === undefined) options.depthTest = true;

        this._initImmediate();

        var layer = options.layer;

        var idx = _getLayerIdx(layer);
        if (idx === undefined) {
            // Init used batch once
            var batch = new LineBatch();
            batch.init(this.graphicsDevice, layer, position.length / 2);
            batch.material.depthTest = options.depthTest;

            idx = lineBatches.push(batch)-1; // push into list and get index
            _addLayerIdx(idx, layer);
        } else {
            // Possibly reallocate buffer if it's small
            lineBatches[idx].init(this.graphicsDevice, layer, position.length / 2);
            lineBatches[idx].material.depthTest = options.depthTest;
        }
        // Append
        lineBatches[idx].addLines(position, color);
    }

    /**
     * @function
     * @name pc.Application#renderLine
     * @description Draw a line in one color
     * @param {pc.Vec3} start The start of the line
     * @param {pc.Vec3} end The end of the line
     * @param {pc.Color} color The color of the line
     * @example
     * var start = new pc.Vec3(0,0,0);
     * var end = new pc.Vec3(1,0,0);
     * var color = new pc.Color(1,1,1);
     * app.renderLine(start, end, color);
     */
    /**
     * @function
     * @name pc.Application#renderLine^2
     * @description Draw a line which blends between two colors
     * @param {pc.Vec3} start The start of the line
     * @param {pc.Vec3} end The end of the line
     * @param {pc.Color} startColor The start color of the line
     * @param {pc.Color} endColor The end color of the line
     * @example
     * var start = new pc.Vec3(0,0,0);
     * var end = new pc.Vec3(1,0,0);
     * var startColor = new pc.Color(1,1,1);
     * var endColor = new pc.Color(1,0,0);
     * app.renderLine(start, end, startColor, endColor);
     */
    /**
     * @function
     * @name pc.Application#renderLine^3
     * @description Draw a line of one color with specified line type
     * @param {pc.Vec3} start The start of the line
     * @param {pc.Vec3} end The end of the line
     * @param {pc.Color} color The color of the line
     * @param {Object} options Options to set rendering properties
     * @param {pc.Layer} [options.layer] The layer to render the line into
     * @example
     * var start = new pc.Vec3(0,0,0);
     * var end = new pc.Vec3(1,0,0);
     * var color = new pc.Color(1,1,1);
     * app.renderLine(start, end, color, {
     *   layer: app.scene.layers.getLayerById(pc.LAYERID_WORLD)
     * });
     */
    /**
     * @function
     * @name pc.Application#renderLine^4
     * @description Draw a line which blends between two colors with specified line type
     * @param {pc.Vec3} start The start of the line
     * @param {pc.Vec3} end The end of the line
     * @param {pc.Color} startColor The start color of the line
     * @param {pc.Color} endColor The end color of the line
     * @param {Object} options Options to set rendering properties
     * @param {pc.Layer} [options.layer] The layer to render the line into
     * @example
     * var start = new pc.Vec3(0,0,0);
     * var end = new pc.Vec3(1,0,0);
     * var startColor = new pc.Color(1,1,1);
     * var endColor = new pc.Color(1,0,0);
     * app.renderLine(start, end, color, {
     *   layer: app.scene.layers.getLayerById(pc.LAYERID_WORLD)
     * });
     */
    function renderLine(start, end, color, arg3, arg4) {
        var endColor = color;
        var options;

        if (arg3 instanceof pc.Color) {
            // passed in end color
            endColor = arg3;

            if (typeof(arg4) === 'number') {
                if (!_deprecationWarning) {
                    console.warn("lineBatch argument is deprecated for renderLine. Use options.layer instead");
                    _deprecationWarning = true;
                }
                // compatibility: convert linebatch id into options
                if (arg4 === pc.LINEBATCH_OVERLAY) {
                    options = {
                        layer: this.scene.layers.getLayerById(pc.LAYERID_IMMEDIATE),
                        depthTest: false
                    };
                } else {
                    options = {
                        layer: this.scene.layers.getLayerById(pc.LAYERID_IMMEDIATE),
                        depthTest: true
                    }
                }
            } else {
                // use passed in options
                options = arg4;
            }
        } else if (typeof(arg3) === 'number') {
            if (!_deprecationWarning) {
                console.warn("lineBatch argument is deprecated for renderLine. Use options.layer instead");
                _deprecationWarning = true;
            }

            endColor = color;

            // compatibility: convert linebatch id into options
            if (arg3 === pc.LINEBATCH_OVERLAY) {
                options = {
                    layer: this.scene.layers.getLayerById(pc.LAYERID_IMMEDIATE),
                    depthTest: false
                };
            } else {
                options = {
                    layer: this.scene.layers.getLayerById(pc.LAYERID_IMMEDIATE),
                    depthTest: true
                }
            }
        } else if (arg3) {
            // options passed in
            options = arg3;
        } else {
            // no arg3, use default options
            options = {
                layer: this.scene.layers.getLayerById(pc.LAYERID_IMMEDIATE),
                depthTest: true
            }
        }

        this._addLines([start, end], [color, endColor], options);
    }

    /**
     * @function
     * @name pc.Application#renderLines
     * @description Draw an array of lines.
     * @param {pc.Vec3[]} position An array of points to draw lines between
     * @param {pc.Color[]} color An array of colors to color the lines. This must be the same size as the position array
     * @param {Object} [options] Options to set rendering properties
     * @param {pc.Layer} [options.layer] The layer to render the line into
     * @example
     * var points = [new pc.Vec3(0,0,0), new pc.Vec3(1,0,0), new pc.Vec3(1,1,0), new pc.Vec3(1,1,1)];
     * var colors = [new pc.Color(1,0,0), new pc.Color(1,1,0), new pc.Color(0,1,1), new pc.Color(0,0,1)];
     * app.renderLines(points, colors);
    */
    function renderLines(position, color, options) {
        if (!options) {
            // default option
            options = {
                layer: this.scene.layers.getLayerById(pc.LAYERID_IMMEDIATE),
                depthTest: true
            };
        } else if (typeof(options) === 'number') {
            if (!_deprecationWarning) {
                console.warn("lineBatch argument is deprecated for renderLine. Use options.layer instead");
                _deprecationWarning = true;
            }

            // backwards compatibility, LINEBATCH_OVERLAY lines have depthtest disabled
            if (options === pc.LINEBATCH_OVERLAY) {
                options = {
                    layer: this.scene.layers.getLayerById(pc.LAYERID_IMMEDIATE),
                    depthTest: false
                };
            } else {
                options = {
                    layer: this.scene.layers.getLayerById(pc.LAYERID_IMMEDIATE),
                    depthTest: true
                }
            }
        }

        var multiColor = !!color.length;
        if (multiColor) {
            if (position.length !== color.length) {
                pc.log.error("renderLines: position/color arrays have different lengths");
                return;
            }
        }
        if (position.length % 2 !== 0) {
            pc.log.error("renderLines: array length is not divisible by 2");
            return;
        }
        this._addLines(position, color, options);
    }

    // Draw lines forming a transformed unit-sized cube at this frame
    // lineType is optional
    function renderWireCube(matrix, color, options) {
        // if (lineType===undefined) lineType = pc.LINEBATCH_WORLD;

        var i;
        // Init cube data once
        if (!cubeLocalPos) {
            var x = 0.5;
            cubeLocalPos = [new pc.Vec3(-x, -x, -x), new pc.Vec3(-x, x, -x), new pc.Vec3(x, x, -x), new pc.Vec3(x, -x, -x),
                            new pc.Vec3(-x, -x, x), new pc.Vec3(-x, x, x), new pc.Vec3(x, x, x), new pc.Vec3(x, -x, x)];
            cubeWorldPos = [new pc.Vec3(), new pc.Vec3(), new pc.Vec3(), new pc.Vec3(),
                            new pc.Vec3(), new pc.Vec3(), new pc.Vec3(), new pc.Vec3()];
        }
        // Transform and append lines
        for(i=0; i<8; i++) {
            matrix.transformPoint(cubeLocalPos[i], cubeWorldPos[i]);
        }
        this.renderLines([cubeWorldPos[0],cubeWorldPos[1],
                     cubeWorldPos[1],cubeWorldPos[2],
                     cubeWorldPos[2],cubeWorldPos[3],
                     cubeWorldPos[3],cubeWorldPos[0],

                     cubeWorldPos[4],cubeWorldPos[5],
                     cubeWorldPos[5],cubeWorldPos[6],
                     cubeWorldPos[6],cubeWorldPos[7],
                     cubeWorldPos[7],cubeWorldPos[4],

                     cubeWorldPos[0],cubeWorldPos[4],
                     cubeWorldPos[1],cubeWorldPos[5],
                     cubeWorldPos[2],cubeWorldPos[6],
                     cubeWorldPos[3],cubeWorldPos[7]
                     ], color, options);
    }

    function _preRenderImmediate() {
        for(var i = 0; i < lineBatches.length; i++) {
            if (lineBatches[i]) {
                lineBatches[i].finalize();
            }
        }
    }

    function _postRenderImmediate() {
        for(var i = 0; i < lineBatches.length; i++) {
            if (lineBatches[i]) {
                lineBatches[i].layer.clearMeshInstances(true);
            }
        }
    }

    // Draw meshInstance at this frame
    function renderMeshInstance(meshInstance, options) {
        if (!options) {
            options = {
                layer: this.scene.layers.getLayerById(pc.LAYERID_IMMEDIATE),
                depthTest: true
            };
        }

        this._initImmediate();
        meshInstanceArray[0] = meshInstance;
        options.layer.addMeshInstances(meshInstanceArray, true);
    }

    // Draw mesh at this frame
    function renderMesh(mesh, material, matrix, options) {
        if (!options) {
            options = {
                layer: this.scene.layers.getLayerById(pc.LAYERID_IMMEDIATE),
                depthTest: true
            };
        }

        this._initImmediate();
        tempGraphNode.worldTransform = matrix;
        tempGraphNode._dirtyWorld = tempGraphNode._dirtyNormal = false;
        var instance = new pc.MeshInstance(tempGraphNode, mesh, material);
        instance.cull = false;
        meshInstanceArray[0] = instance;
        options.layer.addMeshInstances(meshInstanceArray, true);
    }

    // Draw quad of size [-0.5, 0.5] at this frame
    function renderQuad(matrix, material, option) {
        if (!options) {
            options = {
                layer: this.scene.layers.getLayerById(pc.LAYERID_IMMEDIATE),
                depthTest: true
            };
        }

        // Init quad data once
        if (!quadMesh) {
            var format = new pc.VertexFormat(this.graphicsDevice, [
                    { semantic: pc.SEMANTIC_POSITION, components: 3, type: pc.TYPE_FLOAT32 }
                ]);
            var quadVb = new pc.VertexBuffer(this.graphicsDevice, format, 4);
            var iterator = new pc.VertexIterator(quadVb);
            iterator.element[pc.SEMANTIC_POSITION].set(-0.5, -0.5, 0);
            iterator.next();
            iterator.element[pc.SEMANTIC_POSITION].set(0.5, -0.5, 0);
            iterator.next();
            iterator.element[pc.SEMANTIC_POSITION].set(-0.5, 0.5, 0);
            iterator.next();
            iterator.element[pc.SEMANTIC_POSITION].set(0.5, 0.5, 0);
            iterator.end();
            quadMesh = new pc.Mesh();
            quadMesh.vertexBuffer = quadVb;
            quadMesh.primitive[0].type = pc.PRIMITIVE_TRISTRIP;
            quadMesh.primitive[0].base = 0;
            quadMesh.primitive[0].count = 4;
            quadMesh.primitive[0].indexed = false;
            this._initImmediate();
        }
        // Issue quad drawcall
        tempGraphNode.worldTransform = matrix;
        tempGraphNode._dirtyWorld = tempGraphNode._dirtyNormal = false;
        var quad = new pc.MeshInstance(tempGraphNode, quadMesh, material);
        quad.cull = false;
        meshInstanceArray[0] = quad;
        options.layer.addMeshInstances(meshInstanceArray, true);
    }

    return {
        renderMeshInstance: renderMeshInstance,
        renderMesh: renderMesh,
        renderLine: renderLine,
        renderLines: renderLines,
        renderQuad: renderQuad,
        renderWireCube: renderWireCube,
        _addLines: _addLines,
        _initImmediate: _initImmediate,
        _preRenderImmediate: _preRenderImmediate,
        _postRenderImmediate: _postRenderImmediate
    };
}());
<|endoftext|>


<commit_before>/* Automatic generetad by require_map */
define([
  "scripts/article/ArticleCtrl.js",
  "scripts/article/ArticleCtrl.spec.js",
  "scripts/article/index.js",
  "scripts/common/apiMocks.js",
  "scripts/common/confProvider.js",
  "scripts/common/confProvider.spec.js",
  "scripts/common/index.js",
  "scripts/common/resolverProvider.js",
  "scripts/common/security/Auth.js",
  "scripts/common/security/index.js",
  "scripts/common/services/Article.js",
  "scripts/common/services/Notifications.js",
  "scripts/common/services/Page.js",
  "scripts/common/services/index.js",
  "scripts/common/sessionStorage.js",
  "scripts/controllers/AppCtrl.js",
  "scripts/controllers/LoginCtrl.js",
  "scripts/controllers/NavbarCtrl.js",
  "scripts/controllers/UsersCtrl.js",
  "scripts/controllers/index.js",
  "scripts/directives/index.js"
])<commit_msg>rm src.map<commit_after><|endoftext|>


<commit_before>import { EventEmitter } from 'events';
import servers from './servers';
import sidebar from './sidebar';
import { desktopCapturer, ipcRenderer } from 'electron';

class WebView extends EventEmitter {
    constructor () {
        super();

        this.webviewParentElement = document.body;

        servers.forEach((host) => {
            this.add(host);
        });

        servers.on('host-added', (hostUrl) => {
            this.add(servers.get(hostUrl));
        });

        servers.on('host-removed', (hostUrl) => {
            this.remove(hostUrl);
        });

        servers.on('active-setted', (hostUrl) => {
            this.setActive(hostUrl);
        });

        servers.on('active-cleared', (hostUrl) => {
            this.deactiveAll(hostUrl);
        });

        servers.once('loaded', () => {
            this.loaded();
        });

        ipcRenderer.on('screenshare-result', (e, result) => {
            const webviewObj = this.getActive();
            webviewObj.executeJavaScript(`
                window.parent.postMessage({
                    sourceId: '${result}'
                }, '*')
            `);
        });
    }

    loaded () {
        document.querySelector('#loading').style.display = 'none';
        document.querySelector('#login-card').style.display = 'block';
        document.querySelector('footer').style.display = 'block';
    }

    loading () {
        document.querySelector('#loading').style.display = 'block';
        document.querySelector('#login-card').style.display = 'none';
        document.querySelector('footer').style.display = 'none';
    }

    add (host) {
        var webviewObj = this.getByUrl(host.url);
        if (webviewObj) {
            return;
        }

        webviewObj = document.createElement('webview');
        webviewObj.setAttribute('server', host.url);
        webviewObj.setAttribute('preload', './preload.js');
        webviewObj.setAttribute('allowpopups', 'on');
        webviewObj.setAttribute('disablewebsecurity', 'on');

        webviewObj.addEventListener('did-navigate-in-page', (lastPath) => {
            this.saveLastPath(host.url, lastPath.url);
        });

        webviewObj.addEventListener('console-message', function (e) {
            console.log('webview:', e.message);
        });

        webviewObj.addEventListener('ipc-message', (event) => {
            this.emit('ipc-message-'+event.channel, host.url, event.args);

            switch (event.channel) {
                case 'title-changed':
                    servers.setHostTitle(host.url, event.args[0]);
                    break;
                case 'unread-changed':
                    sidebar.setBadge(host.url, event.args[0]);
                    break;
                case 'focus':
                    servers.setActive(host.url);
                    break;
                case 'get-sourceId':
                    desktopCapturer.getSources({types: ['window', 'screen']}, (error, sources) => {
                        if (error) {
                            throw error;
                        }

                        sources = sources.map(source => {
                            source.thumbnail = source.thumbnail.toDataURL();
                            return source;
                        });
                        ipcRenderer.send('screenshare', sources);
                    });
                    break;
                case 'reload-server':
                    const active = this.getActive();
                    const server = active.getAttribute('server');
                    this.loading();
                    active.loadURL(server);
                    break;
            }
        });

        webviewObj.addEventListener('dom-ready', () => {
            this.emit('dom-ready', host.url);
        });

        webviewObj.addEventListener('did-fail-load', () => {
            webviewObj.loadURL('file://' + __dirname + '/loading-error.html');
        });

        this.webviewParentElement.appendChild(webviewObj);

        webviewObj.src = host.lastPath || host.url;
    }

    remove (hostUrl) {
        var el = this.getByUrl(hostUrl);
        if (el) {
            el.remove();
        }
    }

    saveLastPath (hostUrl, lastPathUrl) {
        var hosts = servers.hosts;
        hosts[hostUrl].lastPath = lastPathUrl;
        servers.hosts = hosts;
    }

    getByUrl (hostUrl) {
        return this.webviewParentElement.querySelector(`webview[server="${hostUrl}"]`);
    }

    getActive () {
        return document.querySelector('webview.active');
    }

    isActive (hostUrl) {
        return !!this.webviewParentElement.querySelector(`webview.active[server="${hostUrl}"]`);
    }

    deactiveAll () {
        var item;
        while (!(item = this.getActive()) === false) {
            item.classList.remove('active');
        }
    }

    setActive (hostUrl) {
        console.log('active setted', hostUrl);
        if (this.isActive(hostUrl)) {
            return;
        }

        this.deactiveAll();
        var item = this.getByUrl(hostUrl);
        if (item) {
            item.classList.add('active');
        }

        this.focusActive();
    }

    focusActive () {
        var active = this.getActive();
        if (active) {
            active.focus();
            return true;
        }
        return false;
    }
}

export default new WebView();
<commit_msg>Only show reload screen if main webview error<commit_after>import { EventEmitter } from 'events';
import servers from './servers';
import sidebar from './sidebar';
import { desktopCapturer, ipcRenderer } from 'electron';

class WebView extends EventEmitter {
    constructor () {
        super();

        this.webviewParentElement = document.body;

        servers.forEach((host) => {
            this.add(host);
        });

        servers.on('host-added', (hostUrl) => {
            this.add(servers.get(hostUrl));
        });

        servers.on('host-removed', (hostUrl) => {
            this.remove(hostUrl);
        });

        servers.on('active-setted', (hostUrl) => {
            this.setActive(hostUrl);
        });

        servers.on('active-cleared', (hostUrl) => {
            this.deactiveAll(hostUrl);
        });

        servers.once('loaded', () => {
            this.loaded();
        });

        ipcRenderer.on('screenshare-result', (e, result) => {
            const webviewObj = this.getActive();
            webviewObj.executeJavaScript(`
                window.parent.postMessage({
                    sourceId: '${result}'
                }, '*')
            `);
        });
    }

    loaded () {
        document.querySelector('#loading').style.display = 'none';
        document.querySelector('#login-card').style.display = 'block';
        document.querySelector('footer').style.display = 'block';
    }

    loading () {
        document.querySelector('#loading').style.display = 'block';
        document.querySelector('#login-card').style.display = 'none';
        document.querySelector('footer').style.display = 'none';
    }

    add (host) {
        var webviewObj = this.getByUrl(host.url);
        if (webviewObj) {
            return;
        }

        webviewObj = document.createElement('webview');
        webviewObj.setAttribute('server', host.url);
        webviewObj.setAttribute('preload', './preload.js');
        webviewObj.setAttribute('allowpopups', 'on');
        webviewObj.setAttribute('disablewebsecurity', 'on');

        webviewObj.addEventListener('did-navigate-in-page', (lastPath) => {
            this.saveLastPath(host.url, lastPath.url);
        });

        webviewObj.addEventListener('console-message', function (e) {
            console.log('webview:', e.message);
        });

        webviewObj.addEventListener('ipc-message', (event) => {
            this.emit('ipc-message-'+event.channel, host.url, event.args);

            switch (event.channel) {
                case 'title-changed':
                    servers.setHostTitle(host.url, event.args[0]);
                    break;
                case 'unread-changed':
                    sidebar.setBadge(host.url, event.args[0]);
                    break;
                case 'focus':
                    servers.setActive(host.url);
                    break;
                case 'get-sourceId':
                    desktopCapturer.getSources({types: ['window', 'screen']}, (error, sources) => {
                        if (error) {
                            throw error;
                        }

                        sources = sources.map(source => {
                            source.thumbnail = source.thumbnail.toDataURL();
                            return source;
                        });
                        ipcRenderer.send('screenshare', sources);
                    });
                    break;
                case 'reload-server':
                    const active = this.getActive();
                    const server = active.getAttribute('server');
                    this.loading();
                    active.loadURL(server);
                    break;
            }
        });

        webviewObj.addEventListener('dom-ready', () => {
            this.emit('dom-ready', host.url);
        });

        webviewObj.addEventListener('did-fail-load', (e) => {
            if (e.isMainFrame) {
                webviewObj.loadURL('file://' + __dirname + '/loading-error.html');
            }
        });

        this.webviewParentElement.appendChild(webviewObj);

        webviewObj.src = host.lastPath || host.url;
    }

    remove (hostUrl) {
        var el = this.getByUrl(hostUrl);
        if (el) {
            el.remove();
        }
    }

    saveLastPath (hostUrl, lastPathUrl) {
        var hosts = servers.hosts;
        hosts[hostUrl].lastPath = lastPathUrl;
        servers.hosts = hosts;
    }

    getByUrl (hostUrl) {
        return this.webviewParentElement.querySelector(`webview[server="${hostUrl}"]`);
    }

    getActive () {
        return document.querySelector('webview.active');
    }

    isActive (hostUrl) {
        return !!this.webviewParentElement.querySelector(`webview.active[server="${hostUrl}"]`);
    }

    deactiveAll () {
        var item;
        while (!(item = this.getActive()) === false) {
            item.classList.remove('active');
        }
    }

    setActive (hostUrl) {
        console.log('active setted', hostUrl);
        if (this.isActive(hostUrl)) {
            return;
        }

        this.deactiveAll();
        var item = this.getByUrl(hostUrl);
        if (item) {
            item.classList.add('active');
        }

        this.focusActive();
    }

    focusActive () {
        var active = this.getActive();
        if (active) {
            active.focus();
            return true;
        }
        return false;
    }
}

export default new WebView();
<|endoftext|>


<commit_before>(function ($) {

    /**
     * Fuzzy search logic
     * @param search
     * @param text
     * @returns {boolean}
     */
    function match(search, text) {
        search = search.toUpperCase();
        text = text.toUpperCase();

        var j = -1;

        for (var i = 0; i < search.length; i++) {
            var l = search[i];
            if (l == ' ') continue;

            j = text.indexOf(l, j + 1);
            if (j == -1) return false;
        }
        return true;
    }

    $.anywhere = function (data, shortcuts, limitPerGroup, showGroupCount) {

        // default value for limitPerGroup setting
        limitPerGroup = limitPerGroup || 5;

        var plugin = this;
        plugin.data = [];
        plugin.lastSet = [];
        plugin.isActive = false;

        /**
         * Internal event handling system
         */
        plugin.events = {_events: {}};
        plugin.on = plugin.events.on = function (event, callback, context) {
            if (!_.has(this._events, event)) {
                this._events[event] = [];
            }
            this._events[event].push({callback: callback, context: context});
        };
        plugin.events.trigger = function (event) {
            var args = Array.prototype.slice.call(arguments, 1);
            if (_.has(this._events, event)) {
                var events = this._events[event];
                _.each(events, function (e) {
                    var cbk = _.bind(e["callback"], e["context"]);
                    cbk.apply(this, args);
                });
            }
        };
        plugin.events.off = function (event) {
            if (_.has(this._events, event)) {
                delete this._events[event];
            }
        };

        /**
         * Show search box
         */
        plugin.showSearchBox = function () {
            plugin.isActive = true;
            $(".sensei-anywhere").toggle();
            var $input = $(".sensei-anywhere input");
            $input.focus();
            plugin.updateResults($input.val().trim());
            plugin.events.trigger("show");
        };

        /**
         * Hide search box
         */
        plugin.hideSearchBox = function () {

            // hide search box itself
            $(".sensei-anywhere").hide();

            // clear search box
            $(".sensei-anywhere").find("input").val(null);

            plugin.isActive = false;
            plugin.events.trigger("close");
        };

        /**
         * Update search results
         * @param term
         */
        plugin.updateResults = function (term) {

            var head;
            var group_size = limitPerGroup;
            var $wrapper = $(".sensei-anywhere .sensei-anywhere-list");
            $wrapper.html("");

            _.each(plugin.data, function (group) {

                if (term) {
                    head = _.filter(group.items, function (item) {
                        return match(term, item);
                    });
                } else {
                    head = group.items;
                }

                if (head.length > 0) {
                    var $li = $("<li>").addClass("item-group").text(group.group);
                    $wrapper.append($li);

                    if (showGroupCount) {
                        var $span = $("<span>").addClass("group-count").text(" " + head.length);
                        $li.append($span);
                    }

                    _.each(head.slice(0, group_size), function (item) {
                        $wrapper.append($("<li>").addClass("item").text(item).data("group", group.group));
                    });
                }

            });

            $(".sensei-anywhere .sensei-anywhere-list li.item:first").addClass("active");
        };

        /**
         * Move selection up
         */
        plugin.moveActiveItemUp = function () {
            var $curr = $(".sensei-anywhere .sensei-anywhere-list li.item.active");
            $curr.removeClass("active");
            if ($curr.prevAll("li.item").length > 0) {
                var $item = $curr.prevAll("li.item").first().addClass("active");
            } else {
                var $item = $(".sensei-anywhere .sensei-anywhere-list li.item:last").addClass("active");
            }
            plugin.events.trigger("highlight", $item.text());
        };

        /**
         * Move selection down
         */
        plugin.moveActiveItemDown = function () {
            var $curr = $(".sensei-anywhere .sensei-anywhere-list li.item.active");
            $curr.removeClass("active");
            if ($curr.nextAll("li.item").length > 0) {
                var $item = $curr.nextAll("li.item").first().addClass("active");
            } else {
                var $item = $(".sensei-anywhere .sensei-anywhere-list li.item:first").addClass("active");
            }
            plugin.events.trigger("highlight", $item.text());
        };

        /**
         * Choose active item
         */
        plugin.chooseItem = function () {
            var $item = $(".sensei-anywhere .sensei-anywhere-list li.item.active");
            var item = $item.text();
            plugin.hideSearchBox();
            plugin.events.trigger("select", item, $item.data());
        };

        /**
         * Initialize plugin
         * @param data
         * @param shortcuts
         * @returns {jQuery}
         */
        plugin.init = function (data, shortcuts) {

            // render html
            if ($(".sensei-anywhere").length === 0) {
                var $el = $("<div>").addClass("sensei-anywhere")
                    .append($("<input>").addClass("mousetrap"))
                    .append($("<ul>").addClass("sensei-anywhere-list"));
                $("body").append($el);
            }

            plugin.data = data;
            Mousetrap.bind(shortcuts, plugin.showSearchBox);
            Mousetrap.bind(["esc"], function (e) {
                if (plugin.isActive) {
                    e.preventDefault();
                    plugin.hideSearchBox();
                }
            });
            Mousetrap.bind(["up", "shift+tab"], function (e) {
                if (plugin.isActive) {
                    e.preventDefault();
                    plugin.moveActiveItemUp();
                }
            });
            Mousetrap.bind(["down", "tab"], function (e) {
                if (plugin.isActive) {
                    e.preventDefault();
                    plugin.moveActiveItemDown();
                }
            });
            Mousetrap.bind(["enter"], function (e) {
                if (plugin.isActive) {
                    e.preventDefault();
                    plugin.chooseItem();
                }
            });
            $(".sensei-anywhere input").on("keyup", function (e) {
                var usedKeyCodes = [9, 13, 38, 40];
                if (!_.contains(usedKeyCodes, e.which)) {
                    var term = $(this).val().trim();
                    plugin.updateResults(term);
                }
            });
            $(".sensei-anywhere .sensei-anywhere-list").on("click", "li.item", function (e) {
                e.preventDefault();
                $(this).addClass("active").siblings().removeClass("active");
                plugin.chooseItem();
            });
            plugin.events.trigger("init");
            return plugin;
        };

        /**
         * Set data
         * @param data
         */
        plugin.setData = function (data) {
            plugin.data = data;
        };

        return plugin.init(data, shortcuts);

    }

})(jQuery);
<commit_msg>Add fuzzy matching algorithm: String Scoring Algorithm 0.1.21, github.com/joshaven/string_score<commit_after>(function ($) {

    /*!
     * String Scoring Algorithm 0.1.21
     *
     * http://joshaven.com/string_score
     * https://github.com/joshaven/string_score
     *
     * Copyright (C) 2009-2014 Joshaven Potter <yourtech@gmail.com>
     * Special thanks to all of the contributors listed here https://github.com/joshaven/string_score
     * MIT License: http://opensource.org/licenses/MIT
     *
     * Date: Tue Mar 1 2011
     * Updated: Wed Jun 18 2014
    */
    String.prototype.score = function (word, fuzziness) {
        'use strict';

        // If the string is equal to the word, perfect match.
        if (this === word) { return 1; }

        //if it's not a perfect match and is empty return 0
        if (word === "") { return 0; }

        var runningScore = 0,
            charScore,
            finalScore,
            string = this,
            lString = string.toLowerCase(),
            strLength = string.length,
            lWord = word.toLowerCase(),
            wordLength = word.length,
            idxOf,
            startAt = 0,
            fuzzies = 1,
            fuzzyFactor,
            i;

        // Cache fuzzyFactor for speed increase
        if (fuzziness) fuzzyFactor = 1 - fuzziness;

        // Walk through word and add up scores.
        // Code duplication occurs to prevent checking fuzziness inside for loop
        if (fuzziness) {
            for (i = 0; i < wordLength; ++i) {

                // Find next first case-insensitive match of a character.
                idxOf = lString.indexOf(lWord[i], startAt);

                if (-1 === idxOf) {
                    fuzzies += fuzzyFactor;
                    continue;
                } else if (startAt === idxOf) {
                    // Consecutive letter & start-of-string Bonus
                    charScore = 0.7;
                } else {
                    charScore = 0.1;

                    // Acronym Bonus
                    // Weighing Logic: Typing the first character of an acronym is as if you
                    // preceded it with two perfect character matches.
                    if (string[idxOf - 1] === ' ') charScore += 0.8;
                }

                // Same case bonus.
                if (string[idxOf] === word[i]) charScore += 0.1;

                // Update scores and startAt position for next round of indexOf
                runningScore += charScore;
                startAt = idxOf + 1;
            }
        } else {
            for (i = 0; i < wordLength; ++i) {
                idxOf = lString.indexOf(lWord[i], startAt);
                if (-1 === idxOf) {
                    return 0;
                } else if (startAt === idxOf) {
                    charScore = 0.7;
                } else {
                    charScore = 0.1;
                    if (string[idxOf - 1] === ' ') charScore += 0.8;
                }
                if (string[idxOf] === word[i]) charScore += 0.1;
                runningScore += charScore;
                startAt = idxOf + 1;
            }
        }

        // Reduce penalty for longer strings.
        finalScore = 0.5 * (runningScore / strLength    + runningScore / wordLength) / fuzzies;

        if ((lWord[0] === lString[0]) && (finalScore < 0.85)) {
            finalScore += 0.15;
        }

        return finalScore;
    };

    $.anywhere = function (data, shortcuts, limitPerGroup, showGroupCount) {

        // default value for limitPerGroup setting
        limitPerGroup = limitPerGroup || 5;

        var plugin = this;
        plugin.data = [];
        plugin.lastSet = [];
        plugin.isActive = false;

        /**
         * Internal event handling system
         */
        plugin.events = {_events: {}};
        plugin.on = plugin.events.on = function (event, callback, context) {
            if (!_.has(this._events, event)) {
                this._events[event] = [];
            }
            this._events[event].push({callback: callback, context: context});
        };
        plugin.events.trigger = function (event) {
            var args = Array.prototype.slice.call(arguments, 1);
            if (_.has(this._events, event)) {
                var events = this._events[event];
                _.each(events, function (e) {
                    var cbk = _.bind(e["callback"], e["context"]);
                    cbk.apply(this, args);
                });
            }
        };
        plugin.events.off = function (event) {
            if (_.has(this._events, event)) {
                delete this._events[event];
            }
        };

        /**
         * Show search box
         */
        plugin.showSearchBox = function () {
            plugin.isActive = true;
            $(".sensei-anywhere").toggle();
            var $input = $(".sensei-anywhere input");
            $input.focus();
            plugin.updateResults($input.val().trim());
            plugin.events.trigger("show");
        };

        /**
         * Hide search box
         */
        plugin.hideSearchBox = function () {

            // hide search box itself
            $(".sensei-anywhere").hide();

            // clear search box
            $(".sensei-anywhere").find("input").val(null);

            plugin.isActive = false;
            plugin.events.trigger("close");
        };

        /**
         * Update search results
         * @param term
         */
        plugin.updateResults = function (term) {

            var head;
            var group_size = limitPerGroup;
            var $wrapper = $(".sensei-anywhere .sensei-anywhere-list");
            $wrapper.html("");

            _.each(plugin.data, function (group) {

                if (term) {

                    head = _.sortBy(_.filter(group.items, function (item) {
                        return item.score(term) > 0;
                    }), function (item) {
                        return item.score(term) * -1;
                    });

                } else {
                    head = group.items;
                }

                if (head.length > 0) {
                    var $li = $("<li>").addClass("item-group").text(group.group);
                    $wrapper.append($li);

                    if (showGroupCount) {
                        var $span = $("<span>").addClass("group-count").text(" " + head.length);
                        $li.append($span);
                    }

                    _.each(head.slice(0, group_size), function (item) {
                        $wrapper.append($("<li>").addClass("item").text(item).data("group", group.group));
                    });
                }

            });

            $(".sensei-anywhere .sensei-anywhere-list li.item:first").addClass("active");
        };

        /**
         * Move selection up
         */
        plugin.moveActiveItemUp = function () {
            var $curr = $(".sensei-anywhere .sensei-anywhere-list li.item.active");
            $curr.removeClass("active");
            if ($curr.prevAll("li.item").length > 0) {
                var $item = $curr.prevAll("li.item").first().addClass("active");
            } else {
                var $item = $(".sensei-anywhere .sensei-anywhere-list li.item:last").addClass("active");
            }
            plugin.events.trigger("highlight", $item.text());
        };

        /**
         * Move selection down
         */
        plugin.moveActiveItemDown = function () {
            var $curr = $(".sensei-anywhere .sensei-anywhere-list li.item.active");
            $curr.removeClass("active");
            if ($curr.nextAll("li.item").length > 0) {
                var $item = $curr.nextAll("li.item").first().addClass("active");
            } else {
                var $item = $(".sensei-anywhere .sensei-anywhere-list li.item:first").addClass("active");
            }
            plugin.events.trigger("highlight", $item.text());
        };

        /**
         * Choose active item
         */
        plugin.chooseItem = function () {
            var $item = $(".sensei-anywhere .sensei-anywhere-list li.item.active");
            var item = $item.text();
            plugin.hideSearchBox();
            plugin.events.trigger("select", item, $item.data());
        };

        /**
         * Initialize plugin
         * @param data
         * @param shortcuts
         * @returns {jQuery}
         */
        plugin.init = function (data, shortcuts) {

            // render html
            if ($(".sensei-anywhere").length === 0) {
                var $el = $("<div>").addClass("sensei-anywhere")
                    .append($("<input>").addClass("mousetrap"))
                    .append($("<ul>").addClass("sensei-anywhere-list"));
                $("body").append($el);
            }

            plugin.data = data;
            Mousetrap.bind(shortcuts, plugin.showSearchBox);
            Mousetrap.bind(["esc"], function (e) {
                if (plugin.isActive) {
                    e.preventDefault();
                    plugin.hideSearchBox();
                }
            });
            Mousetrap.bind(["up", "shift+tab"], function (e) {
                if (plugin.isActive) {
                    e.preventDefault();
                    plugin.moveActiveItemUp();
                }
            });
            Mousetrap.bind(["down", "tab"], function (e) {
                if (plugin.isActive) {
                    e.preventDefault();
                    plugin.moveActiveItemDown();
                }
            });
            Mousetrap.bind(["enter"], function (e) {
                if (plugin.isActive) {
                    e.preventDefault();
                    plugin.chooseItem();
                }
            });
            $(".sensei-anywhere input").on("keyup", function (e) {
                var usedKeyCodes = [9, 13, 38, 40];
                if (!_.contains(usedKeyCodes, e.which)) {
                    var term = $(this).val().trim();
                    plugin.updateResults(term);
                }
            });
            $(".sensei-anywhere .sensei-anywhere-list").on("click", "li.item", function (e) {
                e.preventDefault();
                $(this).addClass("active").siblings().removeClass("active");
                plugin.chooseItem();
            });
            plugin.events.trigger("init");
            return plugin;
        };

        /**
         * Set data
         * @param data
         */
        plugin.setData = function (data) {
            plugin.data = data;
        };

        return plugin.init(data, shortcuts);

    }

})(jQuery);
<|endoftext|>


<commit_before>import _ from "lodash"
import EventEmitter from "eventemitter3"
import assert from "assert"

import MIDIControlEvents from "../constants/MIDIControlEvents"
import MIDIChannelEvents from "../constants/MIDIChannelEvents"
import { eventToBytes } from "../helpers/midiHelper"
import { deassemble } from "../helpers/noteAssembler"

function firstByte(eventType, channel) {
  return (MIDIChannelEvents[eventType] << 4) + channel
}

function collectAllEvents(song) {
  return _.chain(song.tracks)
    .map(t => t.getEvents())
    .flatten()
    .map(deassemble)
    .flatten()
    .value()
}

function filterEventsRange(events, startTick, endTick) {
  return events
    .filter(e => e && e.tick >= startTick && e.tick <= endTick)
}

// 同じ名前のタスクを描画タイマーごとに一度だけ実行する
class DisplayTask {
  constructor() {
    this.tasks = {}
    setInterval(() => this.perform(), 50)
  }

  add(name, func) {
    this.tasks[name] = func
  }

  perform() {
    _.values(this.tasks).forEach(t => t())
    this.tasks = {}
  }
}

const displayTask = new DisplayTask()

export default class Player extends EventEmitter {
  _playing = false
  _currentTempo = 120
  _currentTick = 0
  _prevTime = 0
  _eventsToPlay = []

  constructor(timebase, output, trackMute) {
    super()

    this._output = output
    this._timebase = timebase
    this._trackMute = trackMute
  }

  set song(song) {
    this._song = song
  }

  play() {
    assert(this._song, "you must set song before play")
    this._playing = true
    this._prevTime = window.performance.now()
    this._eventsToPlay = collectAllEvents(this._song)

    const loop = () => {
      if (this._playing) {
        this._onTimer()
      }
    }
    setInterval(loop, 33)
  }

  set position(tick) {
    this._currentTick = Math.max(0, tick)
    this.emitChangePosition()

    if (this.isPlaying) {
      this.allSoundsOff()
    }
  }

  get position() {
    return this._currentTick
  }

  get isPlaying() {
    return this._playing
  }

  get timebase() {
    return this._timebase
  }

  get numberOfChannels() {
    return 0xf
  }

  allSoundsOffChannel(ch) {
    this._sendMessage([0xb0 + ch, MIDIControlEvents.ALL_SOUNDS_OFF, 0], window.performance.now())
  }

  allSoundsOff() {
    for (const ch of _.range(0, this.numberOfChannels)) {
      this.allSoundsOffChannel(ch)
    }
  }

  allSoundsOffExclude(channel) {
    for (const ch of _.range(0, this.numberOfChannels)) {
      if (ch !== channel) {
        this.allSoundsOffChannel(ch)
      }
    }
  }

  stop() {
    this._playing = false
    this._eventsToPlay = []
    this.allSoundsOff()
  }

  reset() {
    const time = window.performance.now()
    for (const ch of _.range(0, this.numberOfChannels)) {
      // reset controllers
      this._sendMessage([firstByte("controller", ch), MIDIControlEvents.RESET_CONTROLLERS, 0x7f], time)
    }
    this.stop()
    this.position = 0
  }

  get currentTempo() {
    return this._currentTempo
  }

  _sendMessage(msg, timestamp) {
    this._output.send(msg, Math.round(timestamp))
  }

  playNote({ channel, noteNumber, velocity, duration }) {
    const timestamp = window.performance.now()
    this._sendMessage([firstByte("noteOn", channel), noteNumber, velocity], timestamp)
    this._sendMessage([firstByte("noteOff", channel), noteNumber, 0], timestamp + this.tickToMillisec(duration))
  }

  secToTick(sec) {
    // timebase: 1/4拍子ごとのtick数
    return sec * this._currentTempo / 60 * this._timebase
  }

  tickToMillisec(tick) {
    return tick / (this._timebase / 60) / this._currentTempo * 1000
  }

  _shouldPlayChannel(channel) {
    const trackId = this._song.trackIdOfChannel(channel)
    return trackId ? this._trackMute.shouldPlayTrack(trackId) : true
  }

  _onTimer() {
    const timestamp = window.performance.now()
    const deltaTime = timestamp - this._prevTime
    const deltaTick = this.secToTick(deltaTime / 1000)
    const endTick = this._currentTick + deltaTick

    const events = filterEventsRange(this._eventsToPlay, this._currentTick, endTick)

    // channel イベントを MIDI Output に送信
    events
      .filter(e => e.type === "channel" && this._shouldPlayChannel(e.channel))
      .forEach(e => {
        const bytes = eventToBytes(e, false)
        const waitTick = e.tick - this._currentTick
        this._sendMessage(bytes, timestamp + this.tickToMillisec(waitTick))
      })

    // channel イベント以外を実行
    events
      .filter(e => e.type !== "channel")
      .forEach(e => {
        switch (e.subtype) {
          case "setTempo":
            this._currentTempo = 60000000 / e.microsecondsPerBeat
            this.emit("change-tempo", this._currentTempo)
            break
          case "endOfTrack":
            break
          default:
            break
        }
      })

    if (this._currentTick >= this._song.endOfSong) {
      this.stop()
    }

    this._prevTime = timestamp
    this._currentTick = endTick
    this.emitChangePosition()
  }

  emitChangePosition() {
    displayTask.add("changePosition", () => {
      this.emit("change-position", this._currentTick)
    })
  }
}
<commit_msg>Fix note preview<commit_after>import _ from "lodash"
import EventEmitter from "eventemitter3"
import assert from "assert"

import MIDIControlEvents from "../constants/MIDIControlEvents"
import MIDIChannelEvents from "../constants/MIDIChannelEvents"
import { eventToBytes } from "../helpers/midiHelper"
import { deassemble } from "../helpers/noteAssembler"

function firstByte(eventType, channel) {
  return (MIDIChannelEvents[eventType] << 4) + channel
}

function collectAllEvents(song) {
  return _.chain(song.tracks)
    .map(t => t.getEvents())
    .flatten()
    .map(deassemble)
    .flatten()
    .value()
}

function filterEventsRange(events, startTick, endTick) {
  return events
    .filter(e => e && e.tick >= startTick && e.tick <= endTick)
}

// 同じ名前のタスクを描画タイマーごとに一度だけ実行する
class DisplayTask {
  constructor() {
    this.tasks = {}
    setInterval(() => this.perform(), 50)
  }

  add(name, func) {
    this.tasks[name] = func
  }

  perform() {
    _.values(this.tasks).forEach(t => t())
    this.tasks = {}
  }
}

const displayTask = new DisplayTask()

export default class Player extends EventEmitter {
  _playing = false
  _currentTempo = 120
  _currentTick = 0
  _prevTime = 0
  _eventsToPlay = []

  constructor(timebase, output, trackMute) {
    super()

    this._output = output
    this._timebase = timebase
    this._trackMute = trackMute
  }

  set song(song) {
    this._song = song
  }

  play() {
    assert(this._song, "you must set song before play")
    this._playing = true
    this._prevTime = window.performance.now()
    this._eventsToPlay = collectAllEvents(this._song)

    const loop = () => {
      if (this._playing) {
        this._onTimer()
      }
    }
    setInterval(loop, 33)
  }

  set position(tick) {
    this._currentTick = Math.max(0, tick)
    this.emitChangePosition()

    if (this.isPlaying) {
      this.allSoundsOff()
    }
  }

  get position() {
    return this._currentTick
  }

  get isPlaying() {
    return this._playing
  }

  get timebase() {
    return this._timebase
  }

  get numberOfChannels() {
    return 0xf
  }

  allSoundsOffChannel(ch) {
    this._sendMessage([0xb0 + ch, MIDIControlEvents.ALL_SOUNDS_OFF, 0], window.performance.now())
  }

  allSoundsOff() {
    for (const ch of _.range(0, this.numberOfChannels)) {
      this.allSoundsOffChannel(ch)
    }
  }

  allSoundsOffExclude(channel) {
    for (const ch of _.range(0, this.numberOfChannels)) {
      if (ch !== channel) {
        this.allSoundsOffChannel(ch)
      }
    }
  }

  stop() {
    this._playing = false
    this._eventsToPlay = []
    this.allSoundsOff()
  }

  reset() {
    const time = window.performance.now()
    for (const ch of _.range(0, this.numberOfChannels)) {
      // reset controllers
      this._sendMessage([firstByte("controller", ch), MIDIControlEvents.RESET_CONTROLLERS, 0x7f], time)
    }
    this.stop()
    this.position = 0
  }

  get currentTempo() {
    return this._currentTempo
  }

  _sendMessage(msg, timestamp) {
    this._output.send(msg, Math.round(timestamp))
  }

  playNote({ channel, noteNumber, velocity, duration }) {
    const timestamp = window.performance.now() + 100
    this._sendMessage([firstByte("noteOn", channel), noteNumber, velocity], timestamp)
    this._sendMessage([firstByte("noteOff", channel), noteNumber, 0], timestamp + this.tickToMillisec(duration))
  }

  secToTick(sec) {
    // timebase: 1/4拍子ごとのtick数
    return sec * this._currentTempo / 60 * this._timebase
  }

  tickToMillisec(tick) {
    return tick / (this._timebase / 60) / this._currentTempo * 1000
  }

  _shouldPlayChannel(channel) {
    const trackId = this._song.trackIdOfChannel(channel)
    return trackId ? this._trackMute.shouldPlayTrack(trackId) : true
  }

  _onTimer() {
    const timestamp = window.performance.now()
    const deltaTime = timestamp - this._prevTime
    const deltaTick = this.secToTick(deltaTime / 1000)
    const endTick = this._currentTick + deltaTick

    const events = filterEventsRange(this._eventsToPlay, this._currentTick, endTick)

    // channel イベントを MIDI Output に送信
    events
      .filter(e => e.type === "channel" && this._shouldPlayChannel(e.channel))
      .forEach(e => {
        const bytes = eventToBytes(e, false)
        const waitTick = e.tick - this._currentTick
        this._sendMessage(bytes, timestamp + this.tickToMillisec(waitTick))
      })

    // channel イベント以外を実行
    events
      .filter(e => e.type !== "channel")
      .forEach(e => {
        switch (e.subtype) {
          case "setTempo":
            this._currentTempo = 60000000 / e.microsecondsPerBeat
            this.emit("change-tempo", this._currentTempo)
            break
          case "endOfTrack":
            break
          default:
            break
        }
      })

    if (this._currentTick >= this._song.endOfSong) {
      this.stop()
    }

    this._prevTime = timestamp
    this._currentTick = endTick
    this.emitChangePosition()
  }

  emitChangePosition() {
    displayTask.add("changePosition", () => {
      this.emit("change-position", this._currentTick)
    })
  }
}
<|endoftext|>


<commit_before>const config = require('../config/config.json').PREFIX
let channelsArr = [];
exports.run = async (client, guild) => {

  let Id = guild.channels.get(guild.channels.filter(i=> i.type === "text").map(i => i)[0].id);
  let p = guild.channels.filter(o => o.type === 'text').map(o => o)
  Id.send(`Hey :wave:, Thank you very much for adding me to \`${guild.name}\` to get access to the command list do \`${config}help\``);

  /**
  * Check if there's a channel with the name of may-log
  * @type {Array}
  */
  for (var i = 0; i < p.length; i++) {
    channelsArr.push(p[i].name)
  }

  /**
  * Create a channel for may to print out punishments
  * @returns {Error}
  * @returns {Promise}
  */
  if(!channelsArr.includes('may-log')) {
    channelsArr.shift()
    guild.createChannel("may-log", "text").then(channel => channel.overwritePermissions(guild.id, {
      SEND_MESSAGES: false
    }))
    .catch(e => {
      logger.error(e)
    })
  }

  /**
  * Create muted role
  * @param {Object} data
  * @param {String} name
  * @param {String} color
  * @param {Array} permissions
  * @param {String} reason
  * @returns {Error}
  * @returns {Promise}
  */
  guild.createRole({
      name: 'may-muted',
      permissions: ["USE_VAD", "ADD_REACTIONS", "READ_MESSAGE_HISTORY", "CREATE_INSTANT_INVITE", "READ_MESSAGES"],
    reason: 'we needed a role for muted',
  })
  .then(role => logger.info(`Created role ${role.name}`))
  .catch(e => {
    logger.error(e)
  })

  /*
    Loop through all the channels and block muted people to talk there
  */
  setTimeout(function () {
    guild.channels.filter(textchannel => textchannel.type === 'text').forEach(cnl => {
      cnl.overwritePermissions(guild.roles.find('name', 'may-muted').id, {
          SEND_MESSAGES: false
      })
        .catch(e => logger.error(e))
  });
  }, 2000);
}
<commit_msg>PLEASE USE LET AND NOT VAR<commit_after>const config = require('../config/config.json').PREFIX;
let channelsArr = [];
exports.run = async (client, guild) => {

  let Id = guild.channels.get(guild.channels.filter(i=> i.type === "text").map(i => i)[0].id);
  let p = guild.channels.filter(o => o.type === 'text').map(o => o);
  Id.send(`Hey :wave:, Thank you very much for adding me to \`${guild.name}\` to get access to the command list do \`${config}help\``);

  /**
  * Check if there's a channel with the name of may-log
  * @type {Array}
  */
  for (let i = 0; i < p.length; i++) {
    channelsArr.push(p[i].name)
  }

  /**
  * Create a channel for may to print out punishments
  * @returns {Error}
  * @returns {Promise}
  */
  if(!channelsArr.includes('may-log')) {
    channelsArr.shift();
    guild.createChannel("may-log", "text").then(channel => channel.overwritePermissions(guild.id, {
      SEND_MESSAGES: false
    }))
    .catch(e => {
      logger.error(e)
    })
  }

  /**
  * Create muted role
  * @param {Object} data
  * @param {String} name
  * @param {String} color
  * @param {Array} permissions
  * @param {String} reason
  * @returns {Error}
  * @returns {Promise}
  */
  guild.createRole({
      name: 'may-muted',
      permissions: ["USE_VAD", "ADD_REACTIONS", "READ_MESSAGE_HISTORY", "CREATE_INSTANT_INVITE", "READ_MESSAGES"],
    reason: 'we needed a role for muted',
  })
  .then(role => logger.info(`Created role ${role.name}`))
  .catch(e => {
    logger.error(e)
  });

  /*
    Loop through all the channels and block muted people to talk there
  */
  setTimeout(function () {
    guild.channels.filter(textchannel => textchannel.type === 'text').forEach(cnl => {
      cnl.overwritePermissions(guild.roles.find('name', 'may-muted').id, {
          SEND_MESSAGES: false
      })
        .catch(e => logger.error(e))
  });
  }, 2000);
};
<|endoftext|>