<commit_before>(function(){

    xtag.register('widget-DAQ', {
        extends: 'div',
        lifecycle: {
            created: function() {
                var xString, option, title, deckWrap;

                this.width = this.offsetWidth;
                this.height = window.innerHeight*0.6;
                this.showing = 0;

                //get the DAQ structure
                XHR('http://' + this.MIDAS + '/?cmd=jcopy&odb=/DAQ&encoding=json-nokeys',
                    function(res){
                        this.buildDAQ(res);
                    }.bind(this),
                    'application/json');

                //build DOM
                this.navBlock = document.createElement('div');
                this.navBlock.setAttribute('class', 'DAQnav');
                this.appendChild(this.navBlock);

                title = document.createElement('h1');
                title.innerHTML = 'DAQ';
                this.navBlock.appendChild(title);

                this.cardNav = document.createElement('select');
                this.cardNav.setAttribute('id', 'DAQnav')
                this.cardNav.setAttribute('class', 'stdin');
                this.cardNav.onchange = function(){
                    var targetIndex = parseInt(selected('DAQnav'), 10);
                    document.getElementById('DAQdeck').shuffleTo(targetIndex);
                    this.writeCollectorTooltip(-1);
                    this.showing = targetIndex;
                }.bind(this)
                this.navBlock.appendChild(this.cardNav);

                option = document.createElement('option');
                option.value = 0;
                option.innerHTML = 'Master'
                this.cardNav.appendChild(option);

                deckWrap = document.createElement('div');
                this.appendChild(deckWrap);

                xString = '<x-deck id="DAQdeck" selected-index=0>';
                xString += '<x-card id="DAQmasterCard"></x-card></x-deck>';
                xtag.innerHTML(deckWrap, xString);
                this.nCards = 1

                this.masterBlock = document.createElement('div');
                this.masterBlock.setAttribute('class', 'DAQheadNode');
                document.getElementById('DAQmasterCard').appendChild(this.masterBlock);

                this.collectorBlock = document.createElement('div');
                this.collectorBlock.setAttribute('id', 'collectorBlock');
                document.getElementById('DAQmasterCard').appendChild(this.collectorBlock);

                this.tooltip = document.createElement('div');
                this.tooltip.setAttribute('class', 'tooltip hidden');

                ////////////////////////////
                //Kinetic.js setup
                ////////////////////////////

                //indices for these arrays correspond to the x-card index on display
                this.stage = [];
                this.mainLayer = [];
                this.scaleLayer = [];
                this.tooltipLayer = [];
                this.TTbkg = [];
                this.text = [];
            },
            inserted: function() {},
            removed: function() {},
            attributeChanged: function() {}
        },
        events: {

        },
        accessors: {
            'MIDAS':{
                attribute: {} //this just needs to be declared
            }
        },
        methods: {
            'buildDAQ' : function(response){
                var data = JSON.parse(response),
                    i, j, k, option,
                    collectorGutter = this.width*0.02,
                    collectorWidth = (this.width - collectorGutter*16) / 16,
                    xLength = collectorGutter/2,
                    xLeft, xRight, M, S, C, MSCstring;

                this.collectors = [];
                this.digitizers = [];
                this.collectorCells = [];
                this.localMSC = [];

                //determine what collectors are present and instantiate x-cards for each one
                for(i=0; i<16; i++){
                    this.collectors[i] = data.hosts['collector0x' + i.toString(16)];

                    if(this.collectors[i]){
                        document.getElementById('DAQdeck').innerHTML += '<x-card id="collector'+i+'">

</x-card>';
                        option = document.createElement('option');
                        option.value = this.nCards;
                        option.innerHTML = 'Collector 0x' + i.toString(16).toUpperCase();
                        this.cardNav.appendChild(option);
                        this.nCards++;

                    }
                }

                //now that the xdeck is built, paint master canvas:
                //collectors:
                this.setupKinetic('collectorBlock');
                for(i=0; i<16; i++){
                    if(this.collectors[i]){
                        this.collectorCells[i] = new Kinetic.Rect({
                            x:collectorGutter/2 + i*this.width/16,
                            y:this.height*0.6,
                            width: collectorWidth,
                            height:this.height*0.2,
                            fill:'#555555',
                            stroke: '#000000',
                            strokeWidth: 4
                        });
                        this.collectorCells[i].on('click', this.clickCollector.bind(this, i));
                        this.collectorCells[i].on('mousemove', this.moveTooltip.bind(this) );
                        this.collectorCells[i].on('mouseover', this.writeCollectorTooltip.bind(this, i) );
                        this.collectorCells[i].on('mouseout', this.writeCollectorTooltip.bind(this, -1));
                        this.mainLayer[0].add(this.collectorCells[i]);

                        this.localMSC[i] = []
                    } else{
                        //terminate loose cord with red x
                        xLeft = new Kinetic.Line({
                            points: [(collectorGutter + collectorWidth)/2 + i*(collectorGutter+collectorWidth) - xLength, 0.6*this.height - xLength, (collectorGutter + collectorWidth)/2 + i*(collectorGutter+collectorWidth) + xLength, 0.6*this.height + xLength],
                            stroke: '#FF0000',
                            strokeWidth: 8
                        });
                        xRight = new Kinetic.Line({
                            points: [(collectorGutter + collectorWidth)/2 + i*(collectorGutter+collectorWidth) + xLength, 0.6*this.height - xLength, (collectorGutter + collectorWidth)/2 + i*(collectorGutter+collectorWidth) - xLength, 0.6*this.height + xLength],
                            stroke: '#FF0000',
                            strokeWidth: 8
                        });
                        this.mainLayer[0].add(xLeft);
                        this.mainLayer[0].add(xRight);
                    }
                }

                //cabling:
                this.masterCables = [[],[],[],[]]; //1-to-4 cables: outer index counts master port, inner index counts collector
                for(i=0; i<4; i++){
                    this.masterCables[i][0] = new Kinetic.Line({
                        points: [collectorWidth*2 + collectorGutter*1.75 + i*(collectorWidth + collectorGutter)*4,0, collectorWidth*2 + collectorGutter*1.75 + i*(collectorWidth + collectorGutter)*4, 0.3*this.height],
                        stroke: '#000000',
                        strokeWidth: 4
                    });
                    this.mainLayer[0].add(this.masterCables[i][0]);
                    this.masterCables[i][0].moveToBottom();
                    for(j=1; j<5; j++){
                        this.masterCables[i][j] = new Kinetic.Line({
                            points: [collectorWidth*2 + collectorGutter*1.75 + i*(collectorWidth + collectorGutter)*4, 0.3*this.height, (collectorGutter + collectorWidth)/2 + (4*i+j-1)*(collectorGutter+collectorWidth), 0.6*this.height],
                            stroke: '#000000',
                            strokeWidth: 4
                        });
                        this.mainLayer[0].add(this.masterCables[i][j]);
                        this.masterCables[i][j].moveToBottom();
                    }
                }
                this.mainLayer[0].draw();

                //and again for each collector card
                this.digitizerCells = [];
                this.collectorCables = [];
                for(i=0; i<16; i++){
                    this.digitizerCells[i] = [];

                    if(!this.collectors[i]) continue;

                    this.setupKinetic('digitizerBlock'+i);
                    for(j=0; j<16; j++){
                        if(data.hosts['collector0x' + i.toString(16)].digitizers[j]){
                            this.digitizerCells[i][j] = new Kinetic.Rect({
                                x:collectorGutter/2 + j*this.width/16,
                                y:this.height*0.6,
                                width: collectorWidth,
                                height:this.height*0.2,
                                fill:'#555555',
                                stroke: '#000000',
                                strokeWidth: 4
                            });
                            this.digitizerCells[i][j].on('mousemove', this.moveTooltip.bind(this) );
                            this.digitizerCells[i][j].on('mouseover', this.writeDigitizerTooltip.bind(this, j) );
                            this.digitizerCells[i][j].on('mouseout', this.writeDigitizerTooltip.bind(this, -1));
                            this.mainLayer[i+1].add(this.digitizerCells[i][j]);

                            this.localMSC[i][j] = {};
                        } else{
                            //terminate loose cord with red x
                            xLeft = new Kinetic.Line({
                                points: [(collectorGutter + collectorWidth)/2 + j*(collectorGutter+collectorWidth) - xLength, 0.6*this.height - xLength, (collectorGutter + collectorWidth)/2 + j*(collectorGutter+collectorWidth) + xLength, 0.6*this.height + xLength],
                                stroke: '#FF0000',
                                strokeWidth: 8
                            });
                            xRight = new Kinetic.Line({
                                points: [(collectorGutter + collectorWidth)/2 + j*(collectorGutter+collectorWidth) + xLength, 0.6*this.height - xLength, (collectorGutter + collectorWidth)/2 + j*(collectorGutter+collectorWidth) - xLength, 0.6*this.height + xLength],
                                stroke: '#FF0000',
                                strokeWidth: 8
                            });
                            this.mainLayer[i+1].add(xLeft);
                            this.mainLayer[i+1].add(xRight);
                        }

                    }

                    //cabling:
                    this.collectorCables[i] = [];
                    for(j=0; j<4; j++){
                        this.collectorCables[i][j] = [];
                        this.collectorCables[i][j][0] = new Kinetic.Line({
                            points: [collectorWidth*2 + collectorGutter*1.75 + j*(collectorWidth + collectorGutter)*4,0, collectorWidth*2 + collectorGutter*1.75 + j*(collectorWidth + collectorGutter)*4, 0.3*this.height],
                            stroke: '#000000',
                            strokeWidth: 4
                        });
                        this.mainLayer[i+1].add(this.collectorCables[i][j][0]);
                        this.collectorCables[i][j][0].moveToBottom();
                        for(k=1; k<5; k++){
                            this.collectorCables[i][j][k] = new Kinetic.Line({
                                points: [collectorWidth*2 + collectorGutter*1.75 + j*(collectorWidth + collectorGutter)*4, 0.3*this.height, (collectorGutter + collectorWidth)/2 + (4*j+k-1)*(collectorGutter+collectorWidth), 0.6*this.height],
                                stroke: '#000000',
                                strokeWidth: 4
                            });
                            this.mainLayer[i+1].add(this.collectorCables[i][j][k]);
                            this.collectorCables[i][j][k].moveToBottom();
                        }
                    }
                    this.mainLayer[i+1].draw();

                }

                //build the MSC table in per-digitizer chunks
                //this.localMSC[collector index][digitizer index][ADC index] = channel name
                for(i=0; i<data.MSC.MSC.length; i++){
                    M = (parseInt(data.MSC.MSC[i],10) & 0xF000) >> 12;
                    S = (parseInt(data.MSC.MSC[i],10) & 0x0F00) >> 8;
                    C = parseInt(data.MSC.MSC[i],10) & 0x00FF;
                    MSCstring = data.MSC.MSC[i].toString(16).toUpperCase();
                        if(MSCstring.length == 1)
                            MSCstring = '000' + MSCstring;
                        else if(MSCstring.length == 2)
                            MSCstring = '00' + MSCstring;
                        else if(MSCstring.length == 3)
                            MSCstring = '0' + MSCstring
                    this.localMSC[M][S][data.MSC.chan[i]] = '0x'+MSCstring;
                }

            },

            'setupKinetic' : function(targetID){
                var i = this.stage.length;
                //point kinetic at the div and set up the staging and layers:
                this.stage[i] = new Kinetic.Stage({
                    container: targetID,
                    width: this.width,
                    height: this.height
                });
                this.mainLayer[i] = new Kinetic.Layer();       //main rendering layer
                this.scaleLayer[i] = new Kinetic.Layer();      //layer for scales / legends
                this.tooltipLayer[i] = new Kinetic.Layer();    //layer for tooltip info

                //tooltip background:
                this.TTbkg[i] = new Kinetic.Rect({
                    x:-1000,
                    y:-1000,
                    width:100,
                    height:100,
                    fill:'rgba(0,0,0,0.8)',
                    stroke: 'rgba(0,0,0,0)',
                    listening: false
                });
                this.tooltipLayer[i].add(this.TTbkg[i]);

                //tooltip text:
                this.text[i] = new Kinetic.Text({
                    x: -1000,
                    y: -1000,
                    fontFamily: 'Arial',
                    fontSize: 16,
                    text: '',
                    lineHeight: 1.2,
                    fill: '#EEEEEE',
                    listening: false
                });
                this.tooltipLayer[i].add(this.text[i]);

                this.stage[i].add(this.mainLayer[i]);
                this.stage[i].add(this.scaleLayer[i]);
                this.stage[i].add(this.tooltipLayer[i]);
            },

            'clickCollector' : function(index){
                //document.getElementById('DAQdeck').shuffleTo(index+1);
                document.getElementById('DAQnav').value = index+1;
                document.getElementById('DAQnav').onchange();
            },

            'moveTooltip': function(){
                var mousePos = this.stage[this.showing].getPointerPosition(),
                    TTwidth = this.TTbkg[this.showing].getAttr('width'),
                    TTheight = this.TTbkg[this.showing].getAttr('height');

                //adjust the background size & position
                this.TTbkg[this.showing].setAttr( 'x', Math.min(mousePos.x + 10, this.width - TTwidth) );
                this.TTbkg[this.showing].setAttr( 'y', Math.min(mousePos.y + 10, this.height - TTheight) );
                //make text follow the mouse too
                this.text[this.showing].setAttr( 'x', Math.min(mousePos.x + 20, this.width - TTwidth + 10) );
                this.text[this.showing].setAttr( 'y', Math.min(mousePos.y + 20, this.height - TTheight + 10) );

                this.tooltipLayer[this.showing].draw();

                this.tooltip.setAttribute('style', 'left: ' + (Math.min(mousePos.x + 20, this.width - TTwidth + 10)) + 'px; top: ' + Math.min(mousePos.y + 20, this.height - TTheight + 10) + 'px;' );
                this.tooltip.setAttribute('class', 'tooltip')
            },

            'writeCollectorTooltip' : function(i){
                var text;

                if(i!=-1){
                    text = 'Collector 0x' + i.toString(16);
                } else {
                    text = '';
                }
                this.text[this.showing].setText(text);
                if(text != ''){
                    //adjust the background size
                    this.TTbkg[this.showing].setAttr( 'width', this.text[this.showing].getAttr('width') + 20 );
                    this.TTbkg[this.showing].setAttr( 'height', this.text[this.showing].getAttr('height') + 20 );
                } else {
                    this.TTbkg[this.showing].setAttr('width', 0);
                    this.TTbkg[this.showing].setAttr('height', 0);
                }
                this.tooltipLayer[this.showing].draw();
            },

            'writeDigitizerTooltip' : function(i){
                var text, key;

                if(i!=-1){
                    text = 'Digitizer 0x' + i.toString(16).toUpperCase();
                    for(key in this.localMSC[this.showing-1][i]){
                        text += '\n' + key + ' ' + this.localMSC[this.showing-1][i][key];
                    }
                } else {
                    text = '';
                }
                this.text[this.showing].setText(text);
                if(text != ''){
                    //adjust the background size
                    this.TTbkg[this.showing].setAttr( 'width', this.text[this.showing].getAttr('width') + 20 );
                    this.TTbkg[this.showing].setAttr( 'height', this.text[this.showing].getAttr('height') + 20 );
                } else {
                    this.TTbkg[this.showing].setAttr('width', 0);
                    this.TTbkg[this.showing].setAttr('height', 0);
                }
                this.tooltipLayer[this.showing].draw();
            }
        }
    });

})();<commit_msg>new tooltip for DAQ<commit_after>(function(){

    xtag.register('widget-DAQ', {
        extends: 'div',
        lifecycle: {
            created: function() {
                var xString, option, title, deckWrap;

                this.width = this.offsetWidth;
                this.height = window.innerHeight*0.6;
                this.showing = 0;
                this.lastCollectorTTindex = null;
                this.lastDigitizerTTindex = null;

                //get the DAQ structure
                XHR('http://' + this.MIDAS + '/?cmd=jcopy&odb=/DAQ&encoding=json-nokeys',
                    function(res){
                        this.buildDAQ(res);
                    }.bind(this),
                    'application/json');

                //build DOM
                this.navBlock = document.createElement('div');
                this.navBlock.setAttribute('class', 'DAQnav');
                this.appendChild(this.navBlock);

                title = document.createElement('h1');
                title.innerHTML = 'DAQ';
                this.navBlock.appendChild(title);

                this.cardNav = document.createElement('select');
                this.cardNav.setAttribute('id', 'DAQnav')
                this.cardNav.setAttribute('class', 'stdin');
                this.cardNav.onchange = function(){
                    var targetIndex = parseInt(selected('DAQnav'), 10);
                    document.getElementById('DAQdeck').shuffleTo(targetIndex);
                    this.writeCollectorTooltip(-1);
                    this.showing = targetIndex;
                }.bind(this)
                this.navBlock.appendChild(this.cardNav);

                option = document.createElement('option');
                option.value = 0;
                option.innerHTML = 'Master'
                this.cardNav.appendChild(option);

                deckWrap = document.createElement('div');
                this.appendChild(deckWrap);

                xString = '<x-deck id="DAQdeck" selected-index=0>';
                xString += '<x-card id="DAQmasterCard"></x-card></x-deck>';
                xtag.innerHTML(deckWrap, xString);
                this.nCards = 1

                this.masterBlock = document.createElement('div');
                this.masterBlock.setAttribute('class', 'DAQheadNode');
                document.getElementById('DAQmasterCard').appendChild(this.masterBlock);

                this.collectorBlock = document.createElement('div');
                this.collectorBlock.setAttribute('id', 'collectorBlock');
                document.getElementById('DAQmasterCard').appendChild(this.collectorBlock);

                this.tooltip = document.createElement('div');
                this.tooltip.setAttribute('id', 'tooltip');
                this.appendChild(this.tooltip);

                ////////////////////////////
                //Kinetic.js setup
                ////////////////////////////

                //indices for these arrays correspond to the x-card index on display
                this.stage = [];
                this.mainLayer = [];
                this.scaleLayer = [];

                //let repopulate know that the detector would like to be updated every loop:
                if(!window.refreshTargets)
                    window.refreshTargets = [];
                window.refreshTargets[window.refreshTargets.length] = this;
            },
            inserted: function() {},
            removed: function() {},
            attributeChanged: function() {}
        },
        events: {

        },
        accessors: {
            'MIDAS':{
                attribute: {} //this just needs to be declared
            }
        },
        methods: {
            'buildDAQ' : function(response){
                var data = JSON.parse(response),
                    i, j, k, option,
                    collectorGutter = this.width*0.02,
                    collectorWidth = (this.width - collectorGutter*16) / 16,
                    xLength = collectorGutter/2,
                    xLeft, xRight, M, S, C, MSCstring;

                this.collectors = [];
                this.digitizers = [];
                this.collectorCells = [];
                this.localMSC = [];

                //determine what collectors are present and instantiate x-cards for each one
                for(i=0; i<16; i++){
                    this.collectors[i] = data.hosts['collector0x' + i.toString(16)];

                    if(this.collectors[i]){
                        document.getElementById('DAQdeck').innerHTML += '<x-card id="collector'+i+'">

</x-card>';
                        option = document.createElement('option');
                        option.value = this.nCards;
                        option.innerHTML = 'Collector 0x' + i.toString(16).toUpperCase();
                        this.cardNav.appendChild(option);
                        this.nCards++;

                    }
                }

                //now that the xdeck is built, paint master canvas:
                //collectors:
                this.setupKinetic('collectorBlock');
                for(i=0; i<16; i++){
                    if(this.collectors[i]){
                        this.collectorCells[i] = new Kinetic.Rect({
                            x:collectorGutter/2 + i*this.width/16,
                            y:this.height*0.6,
                            width: collectorWidth,
                            height:this.height*0.2,
                            fill:'#555555',
                            stroke: '#000000',
                            strokeWidth: 4
                        });
                        this.collectorCells[i].on('click', this.clickCollector.bind(this, i));
                        this.collectorCells[i].on('mousemove', this.moveTooltip.bind(this) );
                        this.collectorCells[i].on('mouseover', this.writeCollectorTooltip.bind(this, i) );
                        this.collectorCells[i].on('mouseout', this.writeCollectorTooltip.bind(this, -1));
                        this.mainLayer[0].add(this.collectorCells[i]);

                        this.localMSC[i] = []
                    } else{
                        //terminate loose cord with red x
                        xLeft = new Kinetic.Line({
                            points: [(collectorGutter + collectorWidth)/2 + i*(collectorGutter+collectorWidth) - xLength, 0.6*this.height - xLength, (collectorGutter + collectorWidth)/2 + i*(collectorGutter+collectorWidth) + xLength, 0.6*this.height + xLength],
                            stroke: '#FF0000',
                            strokeWidth: 8
                        });
                        xRight = new Kinetic.Line({
                            points: [(collectorGutter + collectorWidth)/2 + i*(collectorGutter+collectorWidth) + xLength, 0.6*this.height - xLength, (collectorGutter + collectorWidth)/2 + i*(collectorGutter+collectorWidth) - xLength, 0.6*this.height + xLength],
                            stroke: '#FF0000',
                            strokeWidth: 8
                        });
                        this.mainLayer[0].add(xLeft);
                        this.mainLayer[0].add(xRight);
                    }
                }

                //cabling:
                this.masterCables = [[],[],[],[]]; //1-to-4 cables: outer index counts master port, inner index counts collector
                for(i=0; i<4; i++){
                    this.masterCables[i][0] = new Kinetic.Line({
                        points: [collectorWidth*2 + collectorGutter*1.75 + i*(collectorWidth + collectorGutter)*4,0, collectorWidth*2 + collectorGutter*1.75 + i*(collectorWidth + collectorGutter)*4, 0.3*this.height],
                        stroke: '#000000',
                        strokeWidth: 4
                    });
                    this.mainLayer[0].add(this.masterCables[i][0]);
                    this.masterCables[i][0].moveToBottom();
                    for(j=1; j<5; j++){
                        this.masterCables[i][j] = new Kinetic.Line({
                            points: [collectorWidth*2 + collectorGutter*1.75 + i*(collectorWidth + collectorGutter)*4, 0.3*this.height, (collectorGutter + collectorWidth)/2 + (4*i+j-1)*(collectorGutter+collectorWidth), 0.6*this.height],
                            stroke: '#000000',
                            strokeWidth: 4
                        });
                        this.mainLayer[0].add(this.masterCables[i][j]);
                        this.masterCables[i][j].moveToBottom();
                    }
                }
                this.mainLayer[0].draw();

                //and again for each collector card
                this.digitizerCells = [];
                this.collectorCables = [];
                for(i=0; i<16; i++){
                    this.digitizerCells[i] = [];

                    if(!this.collectors[i]) continue;

                    this.setupKinetic('digitizerBlock'+i);
                    for(j=0; j<16; j++){
                        if(data.hosts['collector0x' + i.toString(16)].digitizers[j]){
                            this.digitizerCells[i][j] = new Kinetic.Rect({
                                x:collectorGutter/2 + j*this.width/16,
                                y:this.height*0.6,
                                width: collectorWidth,
                                height:this.height*0.2,
                                fill:'#555555',
                                stroke: '#000000',
                                strokeWidth: 4
                            });
                            this.digitizerCells[i][j].on('mousemove', this.moveTooltip.bind(this) );
                            this.digitizerCells[i][j].on('mouseover', this.writeDigitizerTooltip.bind(this, j) );
                            this.digitizerCells[i][j].on('mouseout', this.writeDigitizerTooltip.bind(this, -1));
                            this.mainLayer[i+1].add(this.digitizerCells[i][j]);

                            this.localMSC[i][j] = {};
                        } else{
                            //terminate loose cord with red x
                            xLeft = new Kinetic.Line({
                                points: [(collectorGutter + collectorWidth)/2 + j*(collectorGutter+collectorWidth) - xLength, 0.6*this.height - xLength, (collectorGutter + collectorWidth)/2 + j*(collectorGutter+collectorWidth) + xLength, 0.6*this.height + xLength],
                                stroke: '#FF0000',
                                strokeWidth: 8
                            });
                            xRight = new Kinetic.Line({
                                points: [(collectorGutter + collectorWidth)/2 + j*(collectorGutter+collectorWidth) + xLength, 0.6*this.height - xLength, (collectorGutter + collectorWidth)/2 + j*(collectorGutter+collectorWidth) - xLength, 0.6*this.height + xLength],
                                stroke: '#FF0000',
                                strokeWidth: 8
                            });
                            this.mainLayer[i+1].add(xLeft);
                            this.mainLayer[i+1].add(xRight);
                        }

                    }

                    //cabling:
                    this.collectorCables[i] = [];
                    for(j=0; j<4; j++){
                        this.collectorCables[i][j] = [];
                        this.collectorCables[i][j][0] = new Kinetic.Line({
                            points: [collectorWidth*2 + collectorGutter*1.75 + j*(collectorWidth + collectorGutter)*4,0, collectorWidth*2 + collectorGutter*1.75 + j*(collectorWidth + collectorGutter)*4, 0.3*this.height],
                            stroke: '#000000',
                            strokeWidth: 4
                        });
                        this.mainLayer[i+1].add(this.collectorCables[i][j][0]);
                        this.collectorCables[i][j][0].moveToBottom();
                        for(k=1; k<5; k++){
                            this.collectorCables[i][j][k] = new Kinetic.Line({
                                points: [collectorWidth*2 + collectorGutter*1.75 + j*(collectorWidth + collectorGutter)*4, 0.3*this.height, (collectorGutter + collectorWidth)/2 + (4*j+k-1)*(collectorGutter+collectorWidth), 0.6*this.height],
                                stroke: '#000000',
                                strokeWidth: 4
                            });
                            this.mainLayer[i+1].add(this.collectorCables[i][j][k]);
                            this.collectorCables[i][j][k].moveToBottom();
                        }
                    }
                    this.mainLayer[i+1].draw();

                }

                //build the MSC table in per-digitizer chunks
                //this.localMSC[collector index][digitizer index][ADC index] = channel name
                for(i=0; i<data.MSC.MSC.length; i++){
                    M = (parseInt(data.MSC.MSC[i],10) & 0xF000) >> 12;
                    S = (parseInt(data.MSC.MSC[i],10) & 0x0F00) >> 8;
                    C = parseInt(data.MSC.MSC[i],10) & 0x00FF;
                    MSCstring = data.MSC.MSC[i].toString(16).toUpperCase();
                        if(MSCstring.length == 1)
                            MSCstring = '000' + MSCstring;
                        else if(MSCstring.length == 2)
                            MSCstring = '00' + MSCstring;
                        else if(MSCstring.length == 3)
                            MSCstring = '0' + MSCstring
                    this.localMSC[M][S][data.MSC.chan[i]] = '0x'+MSCstring;
                }

            },

            'setupKinetic' : function(targetID){
                var i = this.stage.length;
                //point kinetic at the div and set up the staging and layers:
                this.stage[i] = new Kinetic.Stage({
                    container: targetID,
                    width: this.width,
                    height: this.height
                });
                this.mainLayer[i] = new Kinetic.Layer();       //main rendering layer
                this.scaleLayer[i] = new Kinetic.Layer();      //layer for scales / legends

                this.stage[i].add(this.mainLayer[i]);
                this.stage[i].add(this.scaleLayer[i]);
            },

            'clickCollector' : function(index){
                //document.getElementById('DAQdeck').shuffleTo(index+1);
                document.getElementById('DAQnav').value = index+1;
                document.getElementById('DAQnav').onchange();
            },

            //move the tooltip around
            'moveTooltip' : function(){
                var tt = document.getElementById('tooltip'),
                    mousePos = this.stage[this.showing].getPointerPosition(),
                    offsetTop = 0, offsetLeft = 0,
                    left = mousePos.x,
                    top = mousePos.y,
                    element = this,
                    position = '';

                do{
                    position = window.getComputedStyle(element).getPropertyValue('position')
                    offsetTop += element.offsetTop || 0;
                    offsetLeft += element.offsetLeft || 0;
                    element = element.offsetParent;
                } while(element && position != 'absolute' && position != 'relative')

                left += offsetLeft;
                top += offsetTop;

                tt.setAttribute('style', 'display:block; z-index:10; position: absolute; left:' + left + '; top:' + top + ';');
            },

            //formulate the tooltip text for cell i and write it on the tooltip layer.
            'writeCollectorTooltip': function(i){
                var text;

                if(i!=-1){
                    text = 'Collector 0x' + i.toString(16);
                } else {
                    text = '';
                }

                this.lastCollectorTTindex = i;
                if(text != '')
                    document.getElementById('tooltip').innerHTML = text;
                else
                    document.getElementById('tooltip').setAttribute('style', '');
            },

            'writeDigitizerTooltip': function(i){
                var text, key;

                if(i!=-1){
                    text = 'Digitizer 0x' + i.toString(16).toUpperCase();
                    for(key in this.localMSC[this.showing-1][i]){
                        text += '\n' + key + ' ' + this.localMSC[this.showing-1][i][key];
                    }
                } else {
                    text = '';
                }

                this.lastDigitizerTTindex = i;
                if(text != '')
                    document.getElementById('tooltip').innerHTML = text;
                else
                    document.getElementById('tooltip').setAttribute('style', '');
            }

            'update' : function(){
                //keep the tooltip updated:
                if(this.showing == 0 && (this.lastCollectorTTindex || this.lastCollectorTTindex==0)){
                    this.writeCollectorTooltip(this.lastCollectorTTindex);
                } else if(this.lastDigitizerTTindex || this.lastDigitizerTTindex==0){
                    this.writeDigitizerTooltip(this.lastDigitizerTTindex);
                }

            }
        }
    });

})();<|endoftext|>


<commit_before><commit_msg>ajax pattern is handling forms<commit_after><|endoftext|>


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

'use strict';

module.exports = {
  opts: {
    readme: './README.md',
    package: './package.json',
    template: './node_modules/jsdoc-fresh',
    recurse: true,
    verbose: true,
    destination: './docs/'
  },
  plugins: [
    'plugins/markdown',
    'jsdoc-region-tag'
  ],
  source: {
    excludePattern: '(^|\\/|\\\\)[._]',
    include: [
      'src',
      'protos'
    ],
    includePattern: '\\.js$'
  },
  templates: {
    copyright: 'Copyright 2019 Google, LLC.',
    includeDate: false,
    sourceFiles: false,
    systemName: '@google/repo',
    theme: 'lumen',
    default: {
      outputSourceFiles: false
    }
  },
  markdown: {
    idInHeadings: true
  }
};
<commit_msg>chore: correct .jsdoc.js protos and double quotes (#373)<commit_after>// Copyright 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

'use strict';

module.exports = {
  opts: {
    readme: './README.md',
    package: './package.json',
    template: './node_modules/jsdoc-fresh',
    recurse: true,
    verbose: true,
    destination: './docs/'
  },
  plugins: [
    'plugins/markdown',
    'jsdoc-region-tag'
  ],
  source: {
    excludePattern: '(^|\\/|\\\\)[._]',
    include: [
      'src',
    ],
    includePattern: '\\.js$'
  },
  templates: {
    copyright: 'Copyright 2019 Google, LLC.',
    includeDate: false,
    sourceFiles: false,
    systemName: '@google/repo',
    theme: 'lumen',
    default: {
      outputSourceFiles: false
    }
  },
  markdown: {
    idInHeadings: true
  }
};
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2020 - present Instructure, Inc.
 *
 * This file is part of Canvas.
 *
 * Canvas is free software: you can redistribute it and/or modify it under
 * the terms of the GNU Affero General Public License as published by the Free
 * Software Foundation, version 3 of the License.
 *
 * Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
 * A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
 * details.
 *
 * You should have received a copy of the GNU Affero General Public License along
 * with this program. If not, see <http://www.gnu.org/licenses/>.
 */
import React, {useCallback, useEffect, useRef, useState} from 'react'
import {func, string} from 'prop-types'
import formatMessage from 'format-message'
import {CodeEditor} from '@instructure/ui-code-editor'
import beautify from 'js-beautify'

// html inline elements allowed by canvas
// (less 'a', 'img', 'span', and 'br', which I want rendered on a new line the editor)
const inline_elems = [
  'abbr',
  'area',
  'b',
  'bdi',
  'bdo',
  'cite',
  'code',
  'del',
  'dfn',
  'em',
  'embed',
  'i',
  'ins',
  'kbd',
  'label',
  'map',
  'mark',
  'math',
  'object',
  'q',
  'samp',
  'small',
  'strong',
  'sub',
  'sup',
  'time',
  'u',
  'var',
  'acronym',
  'big',
  'tt'
]

const RceHtmlEditor = React.forwardRef(({onFocus, ...props}, editorRef) => {
  const [code, setCode] = useState(props.code)
  const label = formatMessage('html code editor')
  const [dir, setDir] = useState(getComputedStyle(document.body, null).direction)

  useEffect(() => {
    // INSTUI sets the CodeEditor's surrounding label's
    // display inline-block so it doesn't fill the width
    // of its container unless there's wide content.
    // Override that.
    // It would be nice to use webpack's style-loader
    // but babel doesn't copy css files to its output
    // dir, and the instui babel plugin mangles class names
    // the simplest approach is to manually inject the stylesheet
    if (!document.getElementById('RceHtmlEditorStyle')) {
      const stylesheet = document.createElement('style')
      stylesheet.setAttribute('id', 'RceHtmlEditorStyle')
      stylesheet.setAttribute('type', 'text/css')
      stylesheet.textContent = `
        .RceHtmlEditor label {
          display: block;
          margin-bottom: 0;  /* get rid of the margin on CodeEditor's label */
        }
      `
      document.head.appendChild(stylesheet)
    }
    // odds are, this won't change the dir.
    setDir(getComputedStyle(editorRef.current || document.body, null).direction)
  }, [dir, editorRef])

  useEffect(() => {
    // scoping querySelector to the container div makes sure we're targeting this CodeEditor
    // The CodeMirror docs (https://codemirror.net/doc/manual.html#styling)
    // say this is the element we use to set the editor's height
    const editor = editorRef.current.querySelector('.CodeMirror')
    editor.CodeMirror.setSize(null, props.height)
    editor.style.margin = '0'
    editor.style.border = '0'
  }, [props.height, editorRef])

  useEffect(() => {
    setCode(beautify.html(props.code, {inline: inline_elems}))
  }, [props.code])

  const isFocused = useRef(false)

  // move cursor to the top of the html code when the editor is focused for the first time
  const handleFocus = useCallback(
    (editor, event) => {
      if (!isFocused.current) {
        editor.setCursor(0, 0)
        isFocused.current = true
      }
      onFocus(event)
    },
    [onFocus]
  )

  // setting height on the container keeps the RCE toolbar from jumping
  return (
    <div
      ref={editorRef}
      className="RceHtmlEditor"
      style={{height: props.height, overflow: 'hidden', textAlign: 'start'}}
    >
      <CodeEditor
        label={label}
        language="html"
        options={{
          lineNumbers: true,
          lineWrapping: true,
          autofocus: false,
          spellcheck: true,
          extraKeys: {Tab: false, 'Shift-Tab': false},
          screenReaderLabel: label,
          direction: dir,
          rtlMoveVisually: true
        }}
        value={code}
        onChange={value => {
          setCode(value)
          props.onChange(value)
        }}
        onFocus={handleFocus}
      />
    </div>
  )
})
RceHtmlEditor.propTypes = {
  code: string.isRequired,
  height: string,
  onChange: func,
  onFocus: func
}
RceHtmlEditor.defaultProps = {
  height: 'auto',
  onChange: _value => {},
  onFocus: () => {}
}

export default RceHtmlEditor
<commit_msg>RCE Pretty HTML Editor cursor and formatting is correct<commit_after>/*
 * Copyright (C) 2020 - present Instructure, Inc.
 *
 * This file is part of Canvas.
 *
 * Canvas is free software: you can redistribute it and/or modify it under
 * the terms of the GNU Affero General Public License as published by the Free
 * Software Foundation, version 3 of the License.
 *
 * Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
 * A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
 * details.
 *
 * You should have received a copy of the GNU Affero General Public License along
 * with this program. If not, see <http://www.gnu.org/licenses/>.
 */
import React, {useCallback, useEffect, useRef, useState} from 'react'
import {func, string} from 'prop-types'
import formatMessage from 'format-message'
import {CodeEditor} from '@instructure/ui-code-editor'
import beautify from 'js-beautify'

// html inline elements allowed by canvas
// (less 'a', 'img', 'span', and 'br', which I want rendered on a new line the editor)
const inline_elems = [
  'abbr',
  'area',
  'b',
  'bdi',
  'bdo',
  'cite',
  'code',
  'del',
  'dfn',
  'em',
  'embed',
  'i',
  'ins',
  'kbd',
  'label',
  'map',
  'mark',
  'math',
  'object',
  'q',
  'samp',
  'small',
  'strong',
  'sub',
  'sup',
  'time',
  'u',
  'var',
  'acronym',
  'big',
  'tt'
]

const RceHtmlEditor = React.forwardRef(({onFocus, ...props}, editorRef) => {
  const [code, setCode] = useState(props.code)
  const label = formatMessage('html code editor')
  const [dir, setDir] = useState(getComputedStyle(document.body, null).direction)

  useEffect(() => {
    setCode(beautify.html(props.code, {inline: inline_elems}))
  }, [])

  useEffect(() => {
    // INSTUI sets the CodeEditor's surrounding label's
    // display inline-block so it doesn't fill the width
    // of its container unless there's wide content.
    // Override that.
    // It would be nice to use webpack's style-loader
    // but babel doesn't copy css files to its output
    // dir, and the instui babel plugin mangles class names
    // the simplest approach is to manually inject the stylesheet
    if (!document.getElementById('RceHtmlEditorStyle')) {
      const stylesheet = document.createElement('style')
      stylesheet.setAttribute('id', 'RceHtmlEditorStyle')
      stylesheet.setAttribute('type', 'text/css')
      stylesheet.textContent = `
        .RceHtmlEditor label {
          display: block;
          margin-bottom: 0;  /* get rid of the margin on CodeEditor's label */
        }
      `
      document.head.appendChild(stylesheet)
    }
    // odds are, this won't change the dir.
    setDir(getComputedStyle(editorRef.current || document.body, null).direction)
  }, [dir, editorRef])

  useEffect(() => {
    // scoping querySelector to the container div makes sure we're targeting this CodeEditor
    // The CodeMirror docs (https://codemirror.net/doc/manual.html#styling)
    // say this is the element we use to set the editor's height
    const editor = editorRef.current.querySelector('.CodeMirror')
    editor.CodeMirror.setSize(null, props.height)
    editor.style.margin = '0'
    editor.style.border = '0'
  }, [props.height, editorRef])

  const isFocused = useRef(false)

  // move cursor to the top of the html code when the editor is focused for the first time
  const handleFocus = useCallback(
    (editor, event) => {
      if (!isFocused.current) {
        editor.setCursor(0, 0)
        isFocused.current = true
      }
      onFocus(event)
    },
    [onFocus]
  )

  // setting height on the container keeps the RCE toolbar from jumping
  return (
    <div
      ref={editorRef}
      className="RceHtmlEditor"
      style={{height: props.height, overflow: 'hidden', textAlign: 'start'}}
    >
      <CodeEditor
        label={label}
        language="html"
        options={{
          lineNumbers: true,
          lineWrapping: true,
          autofocus: false,
          spellcheck: true,
          extraKeys: {Tab: false, 'Shift-Tab': false},
          screenReaderLabel: label,
          direction: dir,
          rtlMoveVisually: true
        }}
        value={code}
        onChange={value => {
          setCode(value)
          props.onChange(value)
        }}
        onFocus={handleFocus}
      />
    </div>
  )
})
RceHtmlEditor.propTypes = {
  code: string.isRequired,
  height: string,
  onChange: func,
  onFocus: func
}
RceHtmlEditor.defaultProps = {
  height: 'auto',
  onChange: _value => {},
  onFocus: () => {}
}

export default RceHtmlEditor
<|endoftext|>


<commit_before>(function(){

    xtag.register('widget-PPG', {
        extends: 'div',
        lifecycle: {
            created: function() {
                var xString,
                    title = document.createElement('h1'),
                    controlWrap = document.createElement('form'),
                    savePPG = document.createElement('button'),
                    saveLoadPPG = document.createElement('button'),
                    encodedCycle = document.createElement('input'),
                    applyCycle = document.createElement('input'),
                    cycleNameLabel = document.createElement('label'),
                    cycleName = document.createElement('input'),
                    chooseCycleLabel = document.createElement('label'),
                    chooseCycle = document.createElement('select'),
                    controlRows = [],
                    loadPPG = document.createElement('button');
                    deletePPG = document.createElement('button');
                    loadTarget = document.createElement('input');
                    deleteTarget = document.createElement('input');

                XHR('http://'+this.MIDAS+'/?cmd=jcopy&odb=/PPG&encoding=json-nokeys', this.registerPPGODB.bind(this));

                xString = '
Cycle Configuration
';
                xString += '<input id="ppgSummary" type="radio" name="ppgSummary" class="stdin" value="Summary" checked></button>';
                xString += 'Summary'
                xString += '<input id="ppgEdit" type="radio" name="ppgSummary" class="stdin" value="Edit"></button>';
                xString += 'Edit'
                xString += '<x-ribbon id="PPGribbon"></x-ribbon>';
                xtag.innerHTML(this,xString);
                this.ribbon = document.getElementById('PPGribbon')
                document.getElementById('ppgSummary').onchange = this.toggleSummary.bind(this);
                document.getElementById('ppgEdit').onchange = this.toggleSummary.bind(this);

                this.ribbon.wrapperForm.onchange = function(){
                    document.getElementById('cycleName').value = '';
                };

                controlWrap.setAttribute('class', 'PPGcontrol');
                controlWrap.setAttribute('method', 'POST');
                controlWrap.setAttribute('action', 'registerCycle');
                this.appendChild(controlWrap);

                controlRows[0] = document.createElement('span')
                controlWrap.appendChild(controlRows[0]);

                encodedCycle.setAttribute('type', 'text');
                encodedCycle.setAttribute('id', 'encodedCycle');
                encodedCycle.setAttribute('style', 'display:none');
                encodedCycle.setAttribute('name', 'cycleString');
                controlRows[0].appendChild(encodedCycle);

                applyCycle.setAttribute('type', 'checkbox');
                applyCycle.setAttribute('id', 'applyCycle');
                applyCycle.setAttribute('style', 'display:none');
                applyCycle.setAttribute('name', 'applyCycle');
                controlRows[0].appendChild(applyCycle);

                cycleNameLabel.innerHTML = 'Cycle Name:';
                cycleName.setAttribute('class', 'stdin');
                cycleName.setAttribute('type', 'text');
                cycleName.setAttribute('id', 'cycleName');
                cycleName.setAttribute('name', 'cycleName');
                controlRows[0].appendChild(cycleNameLabel);
                controlRows[0].appendChild(cycleName);

                savePPG.setAttribute('class', 'stdin');
                savePPG.innerHTML = 'Save Cycle Definition';
                savePPG.onclick = this.registerNewCycle.bind(this);
                controlRows[0].appendChild(savePPG);

                saveLoadPPG.setAttribute('class', 'stdin');
                saveLoadPPG.innerHTML = 'Save & Apply Cycle Definition';
                saveLoadPPG.onclick = function(){
                    this.registerNewCycle();
                    document.getElementById('applyCycle').checked = true;
                }.bind(this);
                controlRows[0].appendChild(saveLoadPPG);

                controlRows[1] = document.createElement('span');
                controlWrap.appendChild(controlRows[1]);

                chooseCycleLabel.innerHTML = 'Load / Delete Cycle:'
                controlRows[1].appendChild(chooseCycleLabel);
                chooseCycle.setAttribute('class', 'stdin');
                chooseCycle.setAttribute('id', 'cycleList');
                controlRows[1].appendChild(chooseCycle);

                loadTarget.setAttribute('type', 'text');
                loadTarget.setAttribute('name', 'loadTarget');
                loadTarget.setAttribute('id', 'loadTarget');
                loadTarget.setAttribute('value', 'null');
                loadTarget.setAttribute('style', 'display:none');
                controlRows[1].appendChild(loadTarget);
                loadPPG.setAttribute('class', 'stdin');
                loadPPG.innerHTML = 'Load'
                loadPPG.onclick = function(){
                    document.getElementById('loadTarget').value = selected('cycleList')
                }
                controlRows[1].appendChild(loadPPG);

                deleteTarget.setAttribute('type', 'text');
                deleteTarget.setAttribute('name', 'deleteTarget');
                deleteTarget.setAttribute('id', 'deleteTarget');
                deleteTarget.setAttribute('value', 'null');
                deleteTarget.setAttribute('style', 'display:none');
                controlRows[1].appendChild(deleteTarget);
                deletePPG.setAttribute('class', 'stdin');
                deletePPG.innerHTML = 'Delete'
                deletePPG.onclick = function(){
                    document.getElementById('deleteTarget').value = selected('cycleList')
                }
                controlRows[1].appendChild(deletePPG);

            },
            inserted: function() {},
            removed: function() {},
            attributeChanged: function() {}
        },
        events: {

        },
        accessors: {
            'MIDAS':{
                attribute: {} //this just needs to be declared
            }
        },
        methods: {
            'loadPPG' : function(ppgTable, durations){
                var i, j, lastStep, options, lastDuration, durationString;

                for(i=0; i<ppgTable.length; i++){
                    this.ribbon.endRibbon.onclick();

                    lastStep = this.ribbon.getElementsByTagName('ul');
                    lastStep = lastStep[lastStep.length-1];
                    options = lastStep.querySelectorAll('input[type="checkbox"]');

                    lastDuration = this.ribbon.querySelectorAll('input[type="number"]');
                    lastDuration = lastDuration[lastDuration.length-1];
                    durationString = this.ribbon.querySelectorAll('span');
                    durationString = durationString[durationString.length -1];
                    if(durations[i]%60000 == 0){
                        lastDuration.value = durations[i] / 60000;
                        lastDuration.nextSibling.value = 60000;
                        durationString.innerHTML = 'Duration: ' + durations[i] / 60000 + ' min';
                    }
                    else if(durations[i]%1000 == 0){
                        lastDuration.value = durations[i] / 1000;
                        lastDuration.nextSibling.value = 1000;
                        durationString.innerHTML = 'Duration: ' + durations[i] / 1000 + ' s';
                    } else{
                        lastDuration.value = durations[i];
                        durationString.innerHTML = 'Duration: ' + durations[i] + ' ms';
                    }

                    for(j=0; j<16; j++){
                        if( (1 << j) & ppgTable[i]){
                            options[j].checked = true;
                        }
                    }
                }
            },

            'traversePPGribbon' : function(){
                var steps = this.ribbon.getElementsByTagName('ul'),
                    durations = this.ribbon.querySelectorAll('input[type="number"]'),
                    units = this.ribbon.querySelectorAll('select'),
                    duration = 0,
                    options,
                    ppgConfig = [],
                    i, j;

                for(i=0; i<steps.length; i++){
                    duration = parseInt(durations[i].value) * selected(units[i].id);

                    options = steps[i].querySelectorAll('input[type="checkbox"]:checked');
                    if(options.length > 0){
                        ppgConfig.push({'PPGcode' : 0, 'duration' : duration});
                        for(j=0; j<options.length; j++){
                            ppgConfig[ppgConfig.length-1].PPGcode = ppgConfig[ppgConfig.length-1].PPGcode | parseInt(options[j].value,10);
                        }
                    }
                }

                return ppgConfig;
            },

            'registerNewCycle' : function(){
                document.getElementById('encodedCycle').value = JSON.stringify(this.traversePPGribbon());
            },

            'registerPPGODB' : function(responseText){
                var data = JSON.parse(responseText),
                    currentName = data.Current,
                    currentPPG = (data.Cycles[currentName]) ? data.Cycles[currentName].PPGcodes : [],
                    currentDuration = (data.Cycles[currentName]) ? data.Cycles[currentName].durations : [],
                    cycleSelect = document.getElementById('cycleList'),
                    cycleOptions, key;

                this.PPGrecord = data;

                this.loadPPG(currentPPG, currentDuration);
                document.getElementById('cycleName').value = currentName;

                for(key in data.Cycles){
                    cycleOptions = document.createElement('option');
                    cycleOptions.innerHTML = key;
                    cycleOptions.value = key;
                    cycleSelect.appendChild(cycleOptions);

                }

                cycleSelect.value = currentName;
            },

            'toggleSummary' : function(){
                var ribbonCards = this.querySelectorAll('div.ribbonCard'),
                    checkboxes, timingUI, durationSummary, duration, durationScale,
                    currentState = this.querySelectorAll('input[type="radio"]:checked').value,
                    i, j;

console.log(ribbonCards)
console.log(currentState)
                for(j=0; j<ribbonCards.length; j++){
                    checkboxes = ribbonCards[j].querySelectorAll('input[type="checkbox"]');
                    timingUI = ribbonCards[j].querySelectorAll('div#timingWrap');
                    durationSummary = ribbonCards[j].querySelectorAll('span#durationSummary');
                    duration = parseFloat(ribbonCards[j].querySelectorAll('input[type="number"]')[0].value);
                    durationScale = parseInt(selected(ribbonCards[j].querySelectorAll('select')[0].id),10);

                    if(currentState == 'Edit'){
                        for(i=0; i<checkboxes.length; i++){
                            checkboxes[i].setAttribute('class', 'edit');
                        }
                        timingUI[0].setAttribute('class', 'edit');
                        durationSummary[0].setAttribute('class', 'edit');
                    } else {
                        for(i=0; i<checkboxes.length; i++){
                            checkboxes[i].setAttribute('class', 'summary');
                        }
                        timingUI[0].setAttribute('class', 'summary');
                        durationSummary[0].setAttribute('class', 'summary');

                        if(durationScale == 60000){
                            durationSummary[0].innerHTML = 'Duration: ' + duration + ' min';
                        }
                        else if(durationScale == 1000){
                            durationSummary[0].innerHTML = 'Duration: ' + duration + ' s';
                        } else{
                            durationSummary[0].innerHTML = 'Duration: ' + duration + ' ms';
                        }
                    }
                }
            }
        }
    });

})();<commit_msg>cycle detail toggle<commit_after>(function(){

    xtag.register('widget-PPG', {
        extends: 'div',
        lifecycle: {
            created: function() {
                var xString,
                    title = document.createElement('h1'),
                    controlWrap = document.createElement('form'),
                    savePPG = document.createElement('button'),
                    saveLoadPPG = document.createElement('button'),
                    encodedCycle = document.createElement('input'),
                    applyCycle = document.createElement('input'),
                    cycleNameLabel = document.createElement('label'),
                    cycleName = document.createElement('input'),
                    chooseCycleLabel = document.createElement('label'),
                    chooseCycle = document.createElement('select'),
                    controlRows = [],
                    loadPPG = document.createElement('button');
                    deletePPG = document.createElement('button');
                    loadTarget = document.createElement('input');
                    deleteTarget = document.createElement('input');

                XHR('http://'+this.MIDAS+'/?cmd=jcopy&odb=/PPG&encoding=json-nokeys', this.registerPPGODB.bind(this));

                xString = '
Cycle Configuration
';
                xString += '<input id="ppgSummary" type="radio" name="ppgSummary" class="stdin" value="Summary" checked></button>';
                xString += 'Summary'
                xString += '<input id="ppgEdit" type="radio" name="ppgSummary" class="stdin" value="Edit"></button>';
                xString += 'Edit'
                xString += '<x-ribbon id="PPGribbon"></x-ribbon>';
                xtag.innerHTML(this,xString);
                this.ribbon = document.getElementById('PPGribbon')
                document.getElementById('ppgSummary').onchange = this.toggleSummary.bind(this);
                document.getElementById('ppgEdit').onchange = this.toggleSummary.bind(this);

                this.ribbon.wrapperForm.onchange = function(){
                    document.getElementById('cycleName').value = '';
                };

                controlWrap.setAttribute('class', 'PPGcontrol');
                controlWrap.setAttribute('method', 'POST');
                controlWrap.setAttribute('action', 'registerCycle');
                this.appendChild(controlWrap);

                controlRows[0] = document.createElement('span')
                controlWrap.appendChild(controlRows[0]);

                encodedCycle.setAttribute('type', 'text');
                encodedCycle.setAttribute('id', 'encodedCycle');
                encodedCycle.setAttribute('style', 'display:none');
                encodedCycle.setAttribute('name', 'cycleString');
                controlRows[0].appendChild(encodedCycle);

                applyCycle.setAttribute('type', 'checkbox');
                applyCycle.setAttribute('id', 'applyCycle');
                applyCycle.setAttribute('style', 'display:none');
                applyCycle.setAttribute('name', 'applyCycle');
                controlRows[0].appendChild(applyCycle);

                cycleNameLabel.innerHTML = 'Cycle Name:';
                cycleName.setAttribute('class', 'stdin');
                cycleName.setAttribute('type', 'text');
                cycleName.setAttribute('id', 'cycleName');
                cycleName.setAttribute('name', 'cycleName');
                controlRows[0].appendChild(cycleNameLabel);
                controlRows[0].appendChild(cycleName);

                savePPG.setAttribute('class', 'stdin');
                savePPG.innerHTML = 'Save Cycle Definition';
                savePPG.onclick = this.registerNewCycle.bind(this);
                controlRows[0].appendChild(savePPG);

                saveLoadPPG.setAttribute('class', 'stdin');
                saveLoadPPG.innerHTML = 'Save & Apply Cycle Definition';
                saveLoadPPG.onclick = function(){
                    this.registerNewCycle();
                    document.getElementById('applyCycle').checked = true;
                }.bind(this);
                controlRows[0].appendChild(saveLoadPPG);

                controlRows[1] = document.createElement('span');
                controlWrap.appendChild(controlRows[1]);

                chooseCycleLabel.innerHTML = 'Load / Delete Cycle:'
                controlRows[1].appendChild(chooseCycleLabel);
                chooseCycle.setAttribute('class', 'stdin');
                chooseCycle.setAttribute('id', 'cycleList');
                controlRows[1].appendChild(chooseCycle);

                loadTarget.setAttribute('type', 'text');
                loadTarget.setAttribute('name', 'loadTarget');
                loadTarget.setAttribute('id', 'loadTarget');
                loadTarget.setAttribute('value', 'null');
                loadTarget.setAttribute('style', 'display:none');
                controlRows[1].appendChild(loadTarget);
                loadPPG.setAttribute('class', 'stdin');
                loadPPG.innerHTML = 'Load'
                loadPPG.onclick = function(){
                    document.getElementById('loadTarget').value = selected('cycleList')
                }
                controlRows[1].appendChild(loadPPG);

                deleteTarget.setAttribute('type', 'text');
                deleteTarget.setAttribute('name', 'deleteTarget');
                deleteTarget.setAttribute('id', 'deleteTarget');
                deleteTarget.setAttribute('value', 'null');
                deleteTarget.setAttribute('style', 'display:none');
                controlRows[1].appendChild(deleteTarget);
                deletePPG.setAttribute('class', 'stdin');
                deletePPG.innerHTML = 'Delete'
                deletePPG.onclick = function(){
                    document.getElementById('deleteTarget').value = selected('cycleList')
                }
                controlRows[1].appendChild(deletePPG);

            },
            inserted: function() {},
            removed: function() {},
            attributeChanged: function() {}
        },
        events: {

        },
        accessors: {
            'MIDAS':{
                attribute: {} //this just needs to be declared
            }
        },
        methods: {
            'loadPPG' : function(ppgTable, durations){
                var i, j, lastStep, options, lastDuration, durationString;

                for(i=0; i<ppgTable.length; i++){
                    this.ribbon.endRibbon.onclick();

                    lastStep = this.ribbon.getElementsByTagName('ul');
                    lastStep = lastStep[lastStep.length-1];
                    options = lastStep.querySelectorAll('input[type="checkbox"]');

                    lastDuration = this.ribbon.querySelectorAll('input[type="number"]');
                    lastDuration = lastDuration[lastDuration.length-1];
                    durationString = this.ribbon.querySelectorAll('span');
                    durationString = durationString[durationString.length -1];
                    if(durations[i]%60000 == 0){
                        lastDuration.value = durations[i] / 60000;
                        lastDuration.nextSibling.value = 60000;
                        durationString.innerHTML = 'Duration: ' + durations[i] / 60000 + ' min';
                    }
                    else if(durations[i]%1000 == 0){
                        lastDuration.value = durations[i] / 1000;
                        lastDuration.nextSibling.value = 1000;
                        durationString.innerHTML = 'Duration: ' + durations[i] / 1000 + ' s';
                    } else{
                        lastDuration.value = durations[i];
                        durationString.innerHTML = 'Duration: ' + durations[i] + ' ms';
                    }

                    for(j=0; j<16; j++){
                        if( (1 << j) & ppgTable[i]){
                            options[j].checked = true;
                        }
                    }
                }
            },

            'traversePPGribbon' : function(){
                var steps = this.ribbon.getElementsByTagName('ul'),
                    durations = this.ribbon.querySelectorAll('input[type="number"]'),
                    units = this.ribbon.querySelectorAll('select'),
                    duration = 0,
                    options,
                    ppgConfig = [],
                    i, j;

                for(i=0; i<steps.length; i++){
                    duration = parseInt(durations[i].value) * selected(units[i].id);

                    options = steps[i].querySelectorAll('input[type="checkbox"]:checked');
                    if(options.length > 0){
                        ppgConfig.push({'PPGcode' : 0, 'duration' : duration});
                        for(j=0; j<options.length; j++){
                            ppgConfig[ppgConfig.length-1].PPGcode = ppgConfig[ppgConfig.length-1].PPGcode | parseInt(options[j].value,10);
                        }
                    }
                }

                return ppgConfig;
            },

            'registerNewCycle' : function(){
                document.getElementById('encodedCycle').value = JSON.stringify(this.traversePPGribbon());
            },

            'registerPPGODB' : function(responseText){
                var data = JSON.parse(responseText),
                    currentName = data.Current,
                    currentPPG = (data.Cycles[currentName]) ? data.Cycles[currentName].PPGcodes : [],
                    currentDuration = (data.Cycles[currentName]) ? data.Cycles[currentName].durations : [],
                    cycleSelect = document.getElementById('cycleList'),
                    cycleOptions, key;

                this.PPGrecord = data;

                this.loadPPG(currentPPG, currentDuration);
                document.getElementById('cycleName').value = currentName;

                for(key in data.Cycles){
                    cycleOptions = document.createElement('option');
                    cycleOptions.innerHTML = key;
                    cycleOptions.value = key;
                    cycleSelect.appendChild(cycleOptions);

                }

                cycleSelect.value = currentName;
            },

            'toggleSummary' : function(){
                var ribbonCards = this.querySelectorAll('div.ribbonCard'),
                    checkboxes, timingUI, durationSummary, duration, durationScale,
                    currentState = this.querySelectorAll('input[type="radio"]:checked')[0].value,
                    i, j;

console.log(ribbonCards)
console.log(currentState)
                for(j=0; j<ribbonCards.length; j++){
                    checkboxes = ribbonCards[j].querySelectorAll('input[type="checkbox"]');
                    timingUI = ribbonCards[j].querySelectorAll('div#timingWrap');
                    durationSummary = ribbonCards[j].querySelectorAll('span#durationSummary');
                    duration = parseFloat(ribbonCards[j].querySelectorAll('input[type="number"]')[0].value);
                    durationScale = parseInt(selected(ribbonCards[j].querySelectorAll('select')[0].id),10);

                    if(currentState == 'Edit'){
                        for(i=0; i<checkboxes.length; i++){
                            checkboxes[i].setAttribute('class', 'edit');
                        }
                        timingUI[0].setAttribute('class', 'edit');
                        durationSummary[0].setAttribute('class', 'edit');
                    } else {
                        for(i=0; i<checkboxes.length; i++){
                            checkboxes[i].setAttribute('class', 'summary');
                        }
                        timingUI[0].setAttribute('class', 'summary');
                        durationSummary[0].setAttribute('class', 'summary');

                        if(durationScale == 60000){
                            durationSummary[0].innerHTML = 'Duration: ' + duration + ' min';
                        }
                        else if(durationScale == 1000){
                            durationSummary[0].innerHTML = 'Duration: ' + duration + ' s';
                        } else{
                            durationSummary[0].innerHTML = 'Duration: ' + duration + ' ms';
                        }
                    }
                }
            }
        }
    });

})();<|endoftext|>


<commit_before>var exec = require("child_process").exec;
var fs = require("fs");
var rm = require("rimraf");
var helpers = require("../helpers/utils");
var should = require('chai').should();

describe("Command `em new`", function() {
  it("should require a dirName argument", function(done){
    exec("./bin/em new", function(error, stdout, stderr) {
      // need to test the program should successfully shut down
      stdout.should.include('[-Error:]');
      stdout.should.include('Missing directory name.');
      stdout.should.include('See \'em new --help\'');
      done();
    });
  });
});

describe("Created directory", function() {
  afterEach(function(done) {
    rm("./test-app", done);
  });

  it("should exit the program when the dirName has been existed", function(done){
    fs.mkdirSync('test-app');
    exec("./bin/em new test-app", function(error, stdout, stderr) {
      // need to test the program should successfully shut down
      stdout.should.include('[-Error:]');
      stdout.should.include('The folder name test-app has existed in this directory tree!');
      done();
    });
  });

  it("should scaffold a bunch of directories", function(done) {
    exec("./bin/em new test-app --test", function() {
      helpers.assertFoldersExist([
        // server folder
        "test-app/server",
        "test-app/server/mocks",
        "test-app/server/routes",
        "test-app/server/views",

        // client folder
        "test-app/client",
        "test-app/client/assets",
        "test-app/client/assets/images",
        "test-app/client/assets/images/touch",
        "test-app/client/assets/styles",
        "test-app/client/assets/styles/sass",
        "test-app/client/assets/vendors",

        // ember application folder
        "test-app/client/app",
        "test-app/client/app/adapters",
        "test-app/client/app/components",
        "test-app/client/app/controllers",
        "test-app/client/app/helpers",
        "test-app/client/app/models",
        "test-app/client/app/routes",
        "test-app/client/app/store",
        "test-app/client/app/templates",
        "test-app/client/app/templates/components",
        "test-app/client/app/templates/users",
        "test-app/client/app/utils",
        "test-app/client/app/views",
        "test-app/client/app/views",
      ], done);
    });
  });

  it("should scaffold a bunch of files", function(done) {
    exec("./bin/em new test-app --test", function() {
      helpers.assertPathsExist([
        "test-app/.bowerrc",
        "test-app/.editorconfig",
        "test-app/.gitattributes",
        "test-app/.gitignore",
        "test-app/.jshintrc",
        "test-app/.npmignore",
        "test-app/bower.json",
        "test-app/config.rb",
        "test-app/gulpfile.js",
        "test-app/package.json",
        "test-app/README.md",

        // Server Folder
        "test-app/server/app.js",
        "test-app/server/index.js",
        "test-app/server/views/base.jade",
        "test-app/server/views/error.jade",
        "test-app/server/routes/index.js",
        "test-app/server/routes/mock_users.js",
        "test-app/server/mocks/users.json",

        // Client Folder
        "test-app/client/index.html",
        "test-app/client/assets/images/touch/chrome-touch-icon-196x196.png",
        "test-app/client/assets/images/touch/icon-128x128.png",
        "test-app/client/assets/styles/reset.css",
        "test-app/client/assets/styles/sass/base.sass",
        "test-app/client/assets/vendors/globalizer.js"
      ], done);
    });
  });

  it("should have an path option for fetching a valid git repo", function(done){
    exec("./bin/em new test-app --test --path github.com/mattma/Ember-Rocks-Template-Basic",
      function(error, stdout, stderr) {
        stdout.should.include('Going to fetch the app template from');
        stdout.should.include('github.com/mattma/Ember-Rocks-Template-Basic');
        stdout.should.include('[-done:] Successfully fetched and installed the app template');
        done();
    });
  });

});
<commit_msg>fix the failing unit test<commit_after>var exec = require("child_process").exec;
var fs = require("fs");
var rm = require("rimraf");
var helpers = require("../helpers/utils");
var should = require('chai').should();

describe("Command `em new`", function() {
  it("should require a dirName argument", function(done){
    exec("./bin/em new", function(error, stdout, stderr) {
      // need to test the program should successfully shut down
      stdout.should.include('[-Error:]');
      stdout.should.include('Missing directory name.');
      stdout.should.include('See \'em new --help\'');
      done();
    });
  });
});

describe("Created directory", function() {
  afterEach(function(done) {
    rm("./test-app", done);
  });

  it("should exit the program when the dirName has been existed", function(done){
    fs.mkdirSync('test-app');
    exec("./bin/em new test-app", function(error, stdout, stderr) {
      // need to test the program should successfully shut down
      stdout.should.include('[-Error:]');
      stdout.should.include('The folder name test-app has existed in this directory tree!');
      done();
    });
  });

  it("should scaffold a bunch of directories", function(done) {
    exec("./bin/em new test-app --test", function() {
      helpers.assertFoldersExist([
        // server folder
        "test-app/server",
        "test-app/server/mocks",
        "test-app/server/routes",
        "test-app/server/views",

        // client folder
        "test-app/client",
        "test-app/client/assets",
        "test-app/client/assets/images",
        "test-app/client/assets/images/touch",
        "test-app/client/assets/styles",
        "test-app/client/assets/styles/sass",
        "test-app/client/assets/vendors",

        // ember application folder
        "test-app/client/app",
        "test-app/client/app/adapters",
        "test-app/client/app/components",
        "test-app/client/app/controllers",
        "test-app/client/app/models",
        "test-app/client/app/routes",
        "test-app/client/app/store",
        "test-app/client/app/templates",
        "test-app/client/app/templates/components",
        "test-app/client/app/templates/users",
        "test-app/client/app/utils",
        "test-app/client/app/views",
        "test-app/client/app/views",
      ], done);
    });
  });

  it("should scaffold a bunch of files", function(done) {
    exec("./bin/em new test-app --test", function() {
      helpers.assertPathsExist([
        "test-app/.bowerrc",
        "test-app/.editorconfig",
        "test-app/.gitattributes",
        "test-app/.gitignore",
        "test-app/.jshintrc",
        "test-app/.npmignore",
        "test-app/bower.json",
        "test-app/config.rb",
        "test-app/gulpfile.js",
        "test-app/package.json",
        "test-app/README.md",

        // Server Folder
        "test-app/server/app.js",
        "test-app/server/index.js",
        "test-app/server/views/base.jade",
        "test-app/server/views/error.jade",
        "test-app/server/routes/index.js",
        "test-app/server/routes/mock_users.js",
        "test-app/server/mocks/users.json",

        // Client Folder
        "test-app/client/index.html",
        "test-app/client/assets/images/touch/chrome-touch-icon-196x196.png",
        "test-app/client/assets/images/touch/icon-128x128.png",
        "test-app/client/assets/styles/reset.css",
        "test-app/client/assets/styles/sass/base.sass",
        "test-app/client/assets/vendors/globalizer.js"
      ], done);
    });
  });

  it("should have an path option for fetching a valid git repo", function(done){
    exec("./bin/em new test-app --test --path github.com/mattma/Ember-Rocks-Template-Basic",
      function(error, stdout, stderr) {
        stdout.should.include('Going to fetch the app template from');
        stdout.should.include('github.com/mattma/Ember-Rocks-Template-Basic');
        stdout.should.include('[-done:] Successfully fetched and installed the app template');
        done();
    });
  });

});
<|endoftext|>


<commit_before>
'use strict';

import * as fs from 'fs';
import * as path from 'path';
import * as glob from 'glob';
import * as async from 'async';
import { DepGraph } from 'dependency-graph';

/**
 * Plugin class.
 * @class PluginManager
 */
export default class {

  /**
   * Construct plugin manager.
   *
   * @param {App} app
   */
  constructor(app) {
    this.app = app;
    this.graph = new DepGraph();

    // ObjectArray
    this.plugins = {};
    // Array of plugin ids in order for starting.
    this.order = [];
  }

  /**
   * Load all plugins in order of requirements/dependencies.
   *
   * @returns {Promise}
   */
  loadPlugins() {
    this.app.log.debug("Loading plugins from configuration...");

    return new Promise((resolve, reject) => {
      // Get all plugin info's first
      let plugins = this.app.config.plugins && this.app.config.plugins !== null ? this.app.config.plugins : [];
      let pluginIds = Object.keys(plugins);

      // Init plugins.
      pluginIds.forEach((pluginId) => {
        this.app.log.debug("Loading plugin '" + pluginId + "'...");

        // Import plugin.
        try {
          // Plugin config.
          let config = this.app.config.plugins[pluginId];

          // Load plugin (require).
          var PluginClass = require(pluginId).default;

          // Save plugin details to plugin array.
          this.plugins[pluginId] = new PluginClass();

          // Inject App, options and child logger.
          this.plugins[pluginId].inject(this.app, config, this.app.log.child({plugin: pluginId}));

          // Register node
          this.graph.addNode(pluginId);
        } catch  (err) {
          return reject(err);
        }
      });

      // Set plugins to app plugins.
      this.app.plugins = this.plugins;

      return resolve();
    });
  }

  /**
   * Start all plugins, this will first determinate the start order, then ask the plugins to init, async and in order.
   *
   * @return {Promise}
   */
  startPlugins() {
    let self = this;
    this.app.log.debug("Starting all plugins... Determinate order...");

    // Determinate order.
    try {
      this.determinateOrder();
    } catch (err) {
      console.error(err);
      return;
    }

    this.app.log.debug("Starting all plugins... Calling init...");

    return new Promise((resolve, reject) => {
      async.eachSeries(self.order, (id, callback) => {
        self.plugins[id].init()
          .then(() => {callback();})
          .catch((err) => {callback(err);});
      }, (err) => {
        if (err) {
          return reject(err);
        }
        return resolve();
        });
    });
  }

  /**
   * Determinate start order.
   */
  determinateOrder() {
    let ids = Object.keys(this.plugins);

    // Loop and register dependencies to nodes.
    for (var i = 0; i < ids.length; i++) {
      let id =     ids[i];
      let plugin = this.plugins[id];

      if (plugin.hasOwnProperty('dependencies')) {
        let dependencies = plugin.dependencies;
        if (dependencies.length > 0) {
          // Parse, add node and go on.
          this.graph.addDependency(plugin);
        }
      }
    }
    this.order = this.graph.overallOrder();
  }

  /**
   * Load all models from plugins.
   *
   * @param sequelize
   *
   * @returns {Promise}
   */
  loadModels(sequelize) {
    if (this.order.length === 0) {
      this.determinateOrder();
    }

    this.order.forEach((id) => {
      if (this.plugins[id].directory) {
        let modelDirectory = path.normalize(this.plugins[id].directory + '/models/');

        try {
          if (modelDirectory && fs.existsSync(modelDirectory)) {
            let list = glob.sync(modelDirectory + '*.js');

            if (list.length > 0) {
              list.forEach((file) => {
                // Import sequelize model.
                let model = sequelize.import(file);

                // Set in the plugin.
                if (! this.plugins[id].models) {
                  this.plugins[id].models = {};
                }
                this.plugins[id].models[model.name] = model;

                // Set in the global models.
                if (! this.app.models.hasOwnProperty(id)) {
                  this.app.models[id] = {};
                }
                this.app.models[id][model.name] = model;
              });
            }
          }

        } catch (err) {
          this.app.log.warn('Warning, can\'t load models for plugin ' + id, err.stack);
        }
      }
    });
  }

  /**
   * Emit event on all plugins!
   * Will be in the order calculated on dependencies.
   *
   * @param {string} event
   * @param {object} params
   * @param {object} options
   */
  emitAll (event, params, options) {
    params = params || {};
    options = options || {};

    // Always do it on the specific order we always use!
    this.order.forEach((id) => {
      this.plugins[id].emit(event, params);
    });
  }

}
<commit_msg>Fixing reject call on error with order determination.<commit_after>
'use strict';

import * as fs from 'fs';
import * as path from 'path';
import * as glob from 'glob';
import * as async from 'async';
import { DepGraph } from 'dependency-graph';

/**
 * Plugin class.
 * @class PluginManager
 */
export default class {

  /**
   * Construct plugin manager.
   *
   * @param {App} app
   */
  constructor(app) {
    this.app = app;
    this.graph = new DepGraph();

    // ObjectArray
    this.plugins = {};
    // Array of plugin ids in order for starting.
    this.order = [];
  }

  /**
   * Load all plugins in order of requirements/dependencies.
   *
   * @returns {Promise}
   */
  loadPlugins() {
    this.app.log.debug("Loading plugins from configuration...");

    return new Promise((resolve, reject) => {
      // Get all plugin info's first
      let plugins = this.app.config.plugins && this.app.config.plugins !== null ? this.app.config.plugins : [];
      let pluginIds = Object.keys(plugins);

      // Init plugins.
      pluginIds.forEach((pluginId) => {
        this.app.log.debug("Loading plugin '" + pluginId + "'...");

        // Import plugin.
        try {
          // Plugin config.
          let config = this.app.config.plugins[pluginId];

          // Load plugin (require).
          var PluginClass = require(pluginId).default;

          // Save plugin details to plugin array.
          this.plugins[pluginId] = new PluginClass();

          // Inject App, options and child logger.
          this.plugins[pluginId].inject(this.app, config, this.app.log.child({plugin: pluginId}));

          // Register node
          this.graph.addNode(pluginId);
        } catch  (err) {
          return reject(err);
        }
      });

      // Set plugins to app plugins.
      this.app.plugins = this.plugins;

      return resolve();
    });
  }

  /**
   * Start all plugins, this will first determinate the start order, then ask the plugins to init, async and in order.
   *
   * @return {Promise}
   */
  startPlugins() {
    let self = this;
    this.app.log.debug("Starting all plugins... Determinate order...");

    // Determinate order.
    try {
      this.determinateOrder();
    } catch (err) {
      return Promise.reject(err);
    }

    this.app.log.debug("Starting all plugins... Calling init...");

    return new Promise((resolve, reject) => {
      async.eachSeries(self.order, (id, callback) => {
        self.plugins[id].init()
          .then(() => {callback();})
          .catch((err) => {callback(err);});
      }, (err) => {
        if (err) {
          return reject(err);
        }
        return resolve();
        });
    });
  }

  /**
   * Determinate start order.
   */
  determinateOrder() {
    let ids = Object.keys(this.plugins);

    // Loop and register dependencies to nodes.
    for (var i = 0; i < ids.length; i++) {
      let id =     ids[i];
      let plugin = this.plugins[id];

      if (plugin.hasOwnProperty('dependencies')) {
        let dependencies = plugin.dependencies;
        if (dependencies.length > 0) {
          // Parse, add node and go on.
          this.graph.addDependency(plugin);
        }
      }
    }
    this.order = this.graph.overallOrder();
  }

  /**
   * Load all models from plugins.
   *
   * @param sequelize
   *
   * @returns {Promise}
   */
  loadModels(sequelize) {
    if (this.order.length === 0) {
      this.determinateOrder();
    }

    this.order.forEach((id) => {
      if (this.plugins[id].directory) {
        let modelDirectory = path.normalize(this.plugins[id].directory + '/models/');

        try {
          if (modelDirectory && fs.existsSync(modelDirectory)) {
            let list = glob.sync(modelDirectory + '*.js');

            if (list.length > 0) {
              list.forEach((file) => {
                // Import sequelize model.
                let model = sequelize.import(file);

                // Set in the plugin.
                if (! this.plugins[id].models) {
                  this.plugins[id].models = {};
                }
                this.plugins[id].models[model.name] = model;

                // Set in the global models.
                if (! this.app.models.hasOwnProperty(id)) {
                  this.app.models[id] = {};
                }
                this.app.models[id][model.name] = model;
              });
            }
          }

        } catch (err) {
          this.app.log.warn('Warning, can\'t load models for plugin ' + id, err.stack);
        }
      }
    });
  }

  /**
   * Emit event on all plugins!
   * Will be in the order calculated on dependencies.
   *
   * @param {string} event
   * @param {object} params
   * @param {object} options
   */
  emitAll (event, params, options) {
    params = params || {};
    options = options || {};

    // Always do it on the specific order we always use!
    this.order.forEach((id) => {
      this.plugins[id].emit(event, params);
    });
  }

}
<|endoftext|>


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

'use strict';

module.exports = {
  opts: {
    readme: './README.md',
    package: './package.json',
    template: './node_modules/jsdoc-baseline',
    recurse: true,
    verbose: true,
    destination: './docs/'
  },
  plugins: [
    'plugins/markdown'
  ],
  source: {
    excludePattern: '(^|\\/|\\\\)[._]',
    include: [
      'src'
    ],
    includePattern: '\\.js$'
  },
  templates: {
    copyright: 'Copyright 2018 Google, LLC.',
    includeDate: false,
    sourceFiles: false,
    systemName: 'gtoken',
    theme: 'lumen'
  }
};
<commit_msg>fix(docs): make anchors work in jsdoc (#215)<commit_after>/*!
 * Copyright 2018 Google LLC. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

'use strict';

module.exports = {
  opts: {
    readme: './README.md',
    package: './package.json',
    template: './node_modules/jsdoc-baseline',
    recurse: true,
    verbose: true,
    destination: './docs/'
  },
  plugins: [
    'plugins/markdown'
  ],
  source: {
    excludePattern: '(^|\\/|\\\\)[._]',
    include: [
      'src'
    ],
    includePattern: '\\.js$'
  },
  templates: {
    copyright: 'Copyright 2018 Google, LLC.',
    includeDate: false,
    sourceFiles: false,
    systemName: 'gtoken',
    theme: 'lumen'
  },
  markdown: {
    idInHeadings: true
  }
};
<|endoftext|>