<commit_before>/*
 * Copyright (C) 2012-2013 B3Partners B.V.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
/**
 * Graph component
 * Creates a Graph component
 * @author Meine Toonen
 */
Ext.define("viewer.components.Graph", {
    extend: "viewer.components.Component",
    panel: null,
    initialized: false,
    toolMapClick:null,
    deActivatedTools:null,
    layers:null,
    config: {
        title: null,
        iconUrl: null,
        tooltip: null,
        graphs: null
    },
    constructor: function(conf) {
        viewer.components.Graph.superclass.constructor.call(this, conf);
        this.initConfig(conf);
        var me = this;
        this.renderButton({
            handler: function() {
                 me.buttonClick();
            },
            text: me.title,
            icon: me.iconUrl,
            tooltip: me.tooltip,
            label: me.label
        });
        this.viewerController.addListener(viewer.viewercontroller.controller.Event.ON_LAYERS_INITIALIZED, this.initialize, this);
        return this;
    },
    initialize: function() {
        this.initialized = true;

        this.toolMapClick =  this.viewerController.mapComponent.createTool({
            type: viewer.viewercontroller.controller.Tool.MAP_CLICK,
            id: this.name + "toolMapClick",
            handler:{
                fn: this.mapClicked,
                scope:this
            },
            viewerController: this.viewerController
        });

        this.layers = [];
        for (var i = 0 ; i < this.graphs.length ;i ++){
            var graph = this.graphs[i];
            this.layers.push(graph.layer);
        }
        this.loadWindow();
    },
     loadWindow : function (){
        var me =this;
        this.maincontainer = Ext.create('Ext.container.Container', {
            id: this.name + 'Container',
            width: '100%',
            height: '100%',
            autoScroll: true,
            layout: {
                type: 'vbox',
                align: 'stretch'
            },
            style: {
                backgroundColor: 'White'
            },
            renderTo: this.getContentDiv(),
            items: [
                {
                    id: this.name + 'LayerSelectorPanel',
                    xtype: "container",
                    padding: 4,
                    height: 36
                },
                {
                    xtype: "container",
                    items: [{
                        xtype: 'button',
                        text: 'Selecteer punt voor grafiek',
                        listeners: {
                            click: {
                                scope: me,
                                fn: me.activateMapClick
                            }
                        }
                    }],
                    height: 25
                },
                 {
                    id: this.name + 'graphPanel',
                    xtype: this.graphs.length > 1 ? "tabpanel" : "container",
                    padding: 4,
                    flex: 1,
                    layout: 'fit'
                }
            ]
        });
        this.createLayerSelector();
    },
    createLayerSelector: function(){
        var config = {
            viewerController : this.viewerController,
            restriction : "attribute",
            id : this.name + "layerSelector",
            layers: this.layers,
            div: this.name + 'LayerSelectorPanel'
        };
        this.layerSelector = Ext.create("viewer.components.LayerSelector",config);
        if(this.layers.length === 1){
            this.layerSelector.setValue(this.layers[0]);
        }
    },
    mapClicked : function(tool, comp){
       this.deactivateMapClick();
        //Ext.get(this.getContentDiv()).mask("Haalt features op...")
        var coords = comp.coord;
        var x = coords.x;
        var y = coords.y;

        var appLayer = this.layerSelector.getValue();
        if(appLayer === null) {
            Ext.Msg.alert('Let op', 'Selecteer eerst een kaartlaag');
            return;
        }
        var graphConfig = this.getConfigByAppLayer(appLayer.id);
        var attributes = [];
        if(!graphConfig){
            return;
        }
        var me = this;
        var graphPanel = Ext.getCmp(this.name + 'graphPanel');
        graphPanel.removeAll();
        // We create placeholders to be able to insert graphs in correct order in the tabpanel
        if(graphConfig.length > 1) {
            for(var i = 0; i < graphConfig.length; i++) {
                graphPanel.add({
                    xtype: 'container',
                    title: graphConfig[i].title,
                    html: 'Bezig met laden...',
                    padding: 10,
                    id: 'placeholderContainer' + i
                });
            }
        }
        for(var i = 0; i < graphConfig.length; i++) {
            (function(config, configId){
                for(var j = 0; j < config.serieAttribute.length; j++) {
                    attributes.push(config.serieAttribute[j]);
                }
                attributes.push(config.categoryAttribute);
                var extraParams = {
                    attributesToInclude : attributes,
                    graph:true
                };
                this.viewerController.mapComponent.getMap().setMarker("edit",x,y);
                var featureInfo = Ext.create("viewer.FeatureInfo", {
                    viewerController: me.viewerController
                });
                featureInfo.editFeatureInfo(x,y,me.viewerController.mapComponent.getMap().getResolution() * 4,appLayer, function (features){
                    me.featuresReceived(features, attributes, configId);
                },function(msg){me.failed(msg);},extraParams);
            })(graphConfig[i], i);
        }
    },
    failed: function(msg) {
        Ext.Msg.alert('Fout', 'Fout bij het ophalen van de grafiek gegevens');
    },
    featuresReceived : function (features,attributes, configId){
        var json = {};
        for (var i = 0 ; i < features.length ;i++){
            var feature = features[i];
            json = feature;
            if(json.related_featuretypes){
                for (var j = 0 ; j < json.related_featuretypes.length ;j++){
                    var linked = this.getLinkedData(json.related_featuretypes[i], attributes, configId);
                    json.linkedData = linked;
                    break;
                }
            }
        }
    },
    getLinkedData : function (related_feature,attributes, configId){
        var appLayer = this.layerSelector.getValue();
        var options = {};

        var filter = "&filter="+encodeURIComponent(related_feature.filter);

        var featureType="&featureType="+related_feature.id;

        options.application = this.appId;
        options.appLayer = appLayer.id;
        options.limit = 1000;
        options.filter = filter;
        options.graph = true;
        options.arrays = false;
        options.attributesToInclude = attributes;
        Ext.Ajax.request({
            url: appLayer.featureService.getStoreUrl() + featureType+filter,
            params: options,
            scope: this,
            success: function(result) {
                var response = Ext.JSON.decode(result.responseText);
                var features = response.features;
                this.createGraph(appLayer, features, configId);
            },
            failure: function(result) {
               this.viewerController.logger.error(result);
            }
        });
    },
    buttonClick: function(){
        this.popup.show();
    },
    createGraph : function (appLayer,  data, configId){
        var gco = this.graphs[configId];
        var me = this;
        var fields = this.getAttributeTitle(appLayer,gco.serieAttribute);
        if(!(fields instanceof Array)){
            fields = [fields];
        }
        fields.push(this.getAttributeTitle(appLayer,gco.categoryAttribute));
        var store = Ext.create('Ext.data.JsonStore', {
            fields: fields,
            data: data
        });

        var graphType = gco.type.toLowerCase();
        // Line charts need to have multiple series
        // Bar/Column charts need 1 serie with multiple yFields
        // For line charts we can iterate over serieAttribute array,
        // for bar/column we wrap it in an array so 1 serie is added only.
        var series = [], serieAttributes = gco.serieAttribute;
        if(graphType === 'bar' || graphType === 'column') {
            serieAttributes = [ gco.serieAttribute ];
        }
        for(var i = 0; i < serieAttributes.length; i++) {
            (function(serieAttribute) {
                series.push({
                    type: graphType,
                    axis: 'left',
                    xField: me.getAttributeTitle(appLayer, gco.categoryAttribute),
                    yField: me.getAttributeTitle(appLayer, serieAttribute),
                    markerConfig: {
                        type: 'circle',
                        size: 4,
                        radius: 4,
                        'stroke-width': 0
                    },
                    tips: {
                        trackMouse: true,
                        minWidth: 140,
                        height: 28,
                        renderer: function(storeItem, item) {
                            if(graphType === 'bar' || graphType === 'column') {
                                this.setTitle(item.value[0] + ': ' + item.value[1]);
                            } else {
                                this.setTitle(storeItem.get(me.getAttributeName(appLayer,gco.categoryAttribute)) + ': ' + storeItem.get(me.getAttributeName(appLayer,serieAttribute)));
                            }
                        }
                    },
                    highlight: {
                        size: 7,
                        radius: 7
                    }
                });
            })(serieAttributes[i]);
        }
        /*
         * The setup for a Theme, we could use this in the future to control the color of the lines/bars
         *
         * Ext.define('Ext.chart.theme.Flamingo', {
            extend: 'Ext.chart.theme.Base',
            constructor: function(config) {
                var themeConfig = [Ext.apply({
                    series: {
                        'stroke-width': 1
                    },
                    colors: ['#FF0000', '#00FF00', '#0000FF', '#4292C6', '#2171B5', '#084594'],
                    seriesThemes: [{
                        fill: "#FF0000"
                        }, {
                        fill: "#00FF00"
                        }, {
                        fill: "#0000FF"
                        }, {
                        fill: '#4292C6'
                        },{
                        fill: '#2171B5'
                        },{
                        fill: '#084594'
                        }
                    ],
                    stacked: false
                }, config)];
                this.callParent(themeConfig);
            }
        }); */
        var axes = [
            {
                type: 'Numeric',
                position: 'left',
                fields: this.getAttributeName(appLayer, gco.serieAttribute),
                label: {
                    renderer: Ext.util.Format.numberRenderer('0,0')
                },
                title: this.isArray(gco.serieAttribute) ? '' : this.getAttributeTitle(appLayer,gco.serieAttribute),
                grid: true,
                minimum: 0
            },{
                type: 'Category',
                position: 'bottom',
                fields: [this.getAttributeName(appLayer,gco.categoryAttribute)],
                title: this.getAttributeTitle(appLayer,gco.categoryAttribute)
            }
        ];
        if(graphType === 'bar') {
            axes[0]['position'] = 'bottom';
            axes[1]['position'] = 'left';
        }
        var chart = Ext.create('Ext.chart.Chart', {
            //theme: 'Flamingo',
            animate: true,
            store: store,
            axes: axes,
            series: series,
            title: gco.title,
            legend: true
        });
        var graphPanel = Ext.getCmp(this.name + 'graphPanel');
        // remove placeholder
        graphPanel.remove('placeholderContainer' + configId, true);
        // add graph in placeholder place
        graphPanel.insert(configId, chart);
        // Always select first tab
        if(graphPanel.setActiveTab){
            graphPanel.setActiveTab(0);
        }
    },
    activateMapClick: function(){
        this.deActivatedTools = this.viewerController.mapComponent.deactivateTools();
        this.toolMapClick.activateTool();
    },
    deactivateMapClick: function(){
        for (var i=0; i < this.deActivatedTools.length; i++){
            this.deActivatedTools[i].activate();
        }
        this.deActivatedTools = [];
        this.toolMapClick.deactivateTool();
    },
    getConfigByAppLayer : function (appLayerId) {
        var configs = [];
        for (var i = 0 ; i < this.graphs.length ;i++){
            if(this.graphs[i].layer === appLayerId){
                configs.push(this.graphs[i]);
            }
        }
        if(!configs.length) return null;
        return configs;
    },
    getAttribute : function (appLayer, attributeId){
        for(var i = 0 ; i < appLayer.attributes.length;i++){
            if(appLayer.attributes[i].id === attributeId){
                return appLayer.attributes[i];
            }
        }
        return null;
    },
    getAttributeName: function(appLayer, attributeId) {
        return this.getAttributeTitleName(appLayer, attributeId, false);
    },
    getAttributeTitle: function(appLayer, attributeId) {
        return this.getAttributeTitleName(appLayer, attributeId, true);
    },
    getAttributeTitleName: function(appLayer, attributeId, allowAlias) {
        var attributes = this.wrapArray(attributeId),
            attributeTitles = [],
            attribute = null;
        for(var i = 0; i < attributes.length; i++) {
            attribute = this.getAttribute(appLayer, attributes[i]);
            if(attribute === null) {
                attributeTitles.push('');
                continue;
            }
            attributeTitles.push(allowAlias && attribute.alias ? attribute.alias : attribute.name);
        }
        if(attributeTitles.length === 1) return attributeTitles[0];
        return attributeTitles;
    },
    wrapArray: function(item) {
        if(this.isArray(item)) return item;
        return [ item ];
    },
    isArray: function(item) {
        return Object.prototype.toString.call(item) === '[object Array]';
    },
    getExtComponents: function() {
        return [ this.maincontainer.getId() ];
    }
});
<commit_msg>getAttributeName removed: always use the title: the backend always gives the alias, so the name doesn't really matter. Fixed nested iteration: correct index<commit_after>/*
 * Copyright (C) 2012-2013 B3Partners B.V.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
/**
 * Graph component
 * Creates a Graph component
 * @author Meine Toonen
 */
Ext.define("viewer.components.Graph", {
    extend: "viewer.components.Component",
    panel: null,
    initialized: false,
    toolMapClick:null,
    deActivatedTools:null,
    layers:null,
    config: {
        title: null,
        iconUrl: null,
        tooltip: null,
        graphs: null
    },
    constructor: function(conf) {
        viewer.components.Graph.superclass.constructor.call(this, conf);
        this.initConfig(conf);
        var me = this;
        this.renderButton({
            handler: function() {
                 me.buttonClick();
            },
            text: me.title,
            icon: me.iconUrl,
            tooltip: me.tooltip,
            label: me.label
        });
        this.viewerController.addListener(viewer.viewercontroller.controller.Event.ON_LAYERS_INITIALIZED, this.initialize, this);
        return this;
    },
    initialize: function() {
        this.initialized = true;

        this.toolMapClick =  this.viewerController.mapComponent.createTool({
            type: viewer.viewercontroller.controller.Tool.MAP_CLICK,
            id: this.name + "toolMapClick",
            handler:{
                fn: this.mapClicked,
                scope:this
            },
            viewerController: this.viewerController
        });

        this.layers = [];
        for (var i = 0 ; i < this.graphs.length ;i ++){
            var graph = this.graphs[i];
            this.layers.push(graph.layer);
        }
        this.loadWindow();
    },
     loadWindow : function (){
        var me =this;
        this.maincontainer = Ext.create('Ext.container.Container', {
            id: this.name + 'Container',
            width: '100%',
            height: '100%',
            autoScroll: true,
            layout: {
                type: 'vbox',
                align: 'stretch'
            },
            style: {
                backgroundColor: 'White'
            },
            renderTo: this.getContentDiv(),
            items: [
                {
                    id: this.name + 'LayerSelectorPanel',
                    xtype: "container",
                    padding: 4,
                    height: 36
                },
                {
                    xtype: "container",
                    items: [{
                        xtype: 'button',
                        text: 'Selecteer punt voor grafiek',
                        listeners: {
                            click: {
                                scope: me,
                                fn: me.activateMapClick
                            }
                        }
                    }],
                    height: 25
                },
                 {
                    id: this.name + 'graphPanel',
                    xtype: this.graphs.length > 1 ? "tabpanel" : "container",
                    padding: 4,
                    flex: 1,
                    layout: 'fit'
                }
            ]
        });
        this.createLayerSelector();
    },
    createLayerSelector: function(){
        var config = {
            viewerController : this.viewerController,
            restriction : "attribute",
            id : this.name + "layerSelector",
            layers: this.layers,
            div: this.name + 'LayerSelectorPanel'
        };
        this.layerSelector = Ext.create("viewer.components.LayerSelector",config);
        if(this.layers.length === 1){
            this.layerSelector.setValue(this.layers[0]);
        }
    },
    mapClicked : function(tool, comp){
       this.deactivateMapClick();
        //Ext.get(this.getContentDiv()).mask("Haalt features op...")
        var coords = comp.coord;
        var x = coords.x;
        var y = coords.y;

        var appLayer = this.layerSelector.getValue();
        if(appLayer === null) {
            Ext.Msg.alert('Let op', 'Selecteer eerst een kaartlaag');
            return;
        }
        var graphConfig = this.getConfigByAppLayer(appLayer.id);
        var attributes = [];
        if(!graphConfig){
            return;
        }
        var me = this;
        var graphPanel = Ext.getCmp(this.name + 'graphPanel');
        graphPanel.removeAll();
        // We create placeholders to be able to insert graphs in correct order in the tabpanel
        if(graphConfig.length > 1) {
            for(var i = 0; i < graphConfig.length; i++) {
                graphPanel.add({
                    xtype: 'container',
                    title: graphConfig[i].title,
                    html: 'Bezig met laden...',
                    padding: 10,
                    id: 'placeholderContainer' + i
                });
            }
        }
        for(var i = 0; i < graphConfig.length; i++) {
            (function(config, configId){
                for(var j = 0; j < config.serieAttribute.length; j++) {
                    attributes.push(config.serieAttribute[j]);
                }
                attributes.push(config.categoryAttribute);
                var extraParams = {
                    attributesToInclude : attributes,
                    graph:true
                };
                this.viewerController.mapComponent.getMap().setMarker("edit",x,y);
                var featureInfo = Ext.create("viewer.FeatureInfo", {
                    viewerController: me.viewerController
                });
                featureInfo.editFeatureInfo(x,y,me.viewerController.mapComponent.getMap().getResolution() * 4,appLayer, function (features){
                    me.featuresReceived(features, attributes, configId);
                },function(msg){me.failed(msg);},extraParams);
            })(graphConfig[i], i);
        }
    },
    failed: function(msg) {
        Ext.Msg.alert('Fout', 'Fout bij het ophalen van de grafiek gegevens');
    },
    featuresReceived : function (features,attributes, configId){
        var json = {};
        for (var i = 0 ; i < features.length ;i++){
            var feature = features[i];
            json = feature;
            if(json.related_featuretypes){
                for (var j = 0 ; j < json.related_featuretypes.length ;j++){
                    var linked = this.getLinkedData(json.related_featuretypes[j], attributes, configId);
                    json.linkedData = linked;
                    break;
                }
            }
        }
    },
    getLinkedData : function (related_feature,attributes, configId){
        var appLayer = this.layerSelector.getValue();
        var options = {};

        var filter = "&filter="+encodeURIComponent(related_feature.filter);

        var featureType="&featureType="+related_feature.id;

        options.application = this.appId;
        options.appLayer = appLayer.id;
        options.limit = 1000;
        options.filter = filter;
        options.graph = true;
        options.arrays = false;
        options.attributesToInclude = attributes;
        Ext.Ajax.request({
            url: appLayer.featureService.getStoreUrl() + featureType+filter,
            params: options,
            scope: this,
            success: function(result) {
                var response = Ext.JSON.decode(result.responseText);
                var features = response.features;
                this.createGraph(appLayer, features, configId);
            },
            failure: function(result) {
               this.viewerController.logger.error(result);
            }
        });
    },
    buttonClick: function(){
        this.popup.show();
    },
    createGraph : function (appLayer,  data, configId){
        var gco = this.graphs[configId];
        var me = this;
        var fields = this.getAttributeTitle(appLayer,gco.serieAttribute);
        if(!(fields instanceof Array)){
            fields = [fields];
        }
        fields.push(this.getAttributeTitle(appLayer,gco.categoryAttribute));
        var store = Ext.create('Ext.data.JsonStore', {
            fields: fields,
            data: data
        });

        var graphType = gco.type.toLowerCase();
        // Line charts need to have multiple series
        // Bar/Column charts need 1 serie with multiple yFields
        // For line charts we can iterate over serieAttribute array,
        // for bar/column we wrap it in an array so 1 serie is added only.
        var series = [], serieAttributes = gco.serieAttribute;
        if(graphType === 'bar' || graphType === 'column') {
            serieAttributes = [ gco.serieAttribute ];
        }
        for(var i = 0; i < serieAttributes.length; i++) {
            (function(serieAttribute) {
                series.push({
                    type: graphType,
                    axis: 'left',
                    xField: me.getAttributeTitle(appLayer, gco.categoryAttribute),
                    yField: me.getAttributeTitle(appLayer, serieAttribute),
                    markerConfig: {
                        type: 'circle',
                        size: 4,
                        radius: 4,
                        'stroke-width': 0
                    },
                    tips: {
                        trackMouse: true,
                        minWidth: 140,
                        height: 28,
                        renderer: function(storeItem, item) {
                            if(graphType === 'bar' || graphType === 'column') {
                                this.setTitle(item.value[0] + ': ' + item.value[1]);
                            } else {
                                this.setTitle(storeItem.get(me.getAttributeTitle(appLayer,gco.categoryAttribute)) + ': ' + storeItem.get(me.getAttributeTitle(appLayer,serieAttribute)));
                            }
                        }
                    },
                    highlight: {
                        size: 7,
                        radius: 7
                    }
                });
            })(serieAttributes[i]);
        }
        /*
         * The setup for a Theme, we could use this in the future to control the color of the lines/bars
         *
         * Ext.define('Ext.chart.theme.Flamingo', {
            extend: 'Ext.chart.theme.Base',
            constructor: function(config) {
                var themeConfig = [Ext.apply({
                    series: {
                        'stroke-width': 1
                    },
                    colors: ['#FF0000', '#00FF00', '#0000FF', '#4292C6', '#2171B5', '#084594'],
                    seriesThemes: [{
                        fill: "#FF0000"
                        }, {
                        fill: "#00FF00"
                        }, {
                        fill: "#0000FF"
                        }, {
                        fill: '#4292C6'
                        },{
                        fill: '#2171B5'
                        },{
                        fill: '#084594'
                        }
                    ],
                    stacked: false
                }, config)];
                this.callParent(themeConfig);
            }
        }); */
        var axes = [
            {
                type: 'Numeric',
                position: 'left',
                fields: this.getAttributeTitle(appLayer, gco.serieAttribute),
                label: {
                    renderer: Ext.util.Format.numberRenderer('0,0')
                },
                title: this.isArray(gco.serieAttribute) ? '' : this.getAttributeTitle(appLayer,gco.serieAttribute),
                grid: true,
                minimum: 0
            },{
                type: 'Category',
                position: 'bottom',
                fields: [this.getAttributeTitle(appLayer,gco.categoryAttribute)],
                title: this.getAttributeTitle(appLayer,gco.categoryAttribute)
            }
        ];
        if(graphType === 'bar') {
            axes[0]['position'] = 'bottom';
            axes[1]['position'] = 'left';
        }
        var chart = Ext.create('Ext.chart.Chart', {
            //theme: 'Flamingo',
            animate: true,
            store: store,
            axes: axes,
            series: series,
            title: gco.title,
            legend: true
        });
        var graphPanel = Ext.getCmp(this.name + 'graphPanel');
        // remove placeholder
        graphPanel.remove('placeholderContainer' + configId, true);
        // add graph in placeholder place
        graphPanel.insert(configId, chart);
        // Always select first tab
        if(graphPanel.setActiveTab){
            graphPanel.setActiveTab(0);
        }
    },
    activateMapClick: function(){
        this.deActivatedTools = this.viewerController.mapComponent.deactivateTools();
        this.toolMapClick.activateTool();
    },
    deactivateMapClick: function(){
        for (var i=0; i < this.deActivatedTools.length; i++){
            this.deActivatedTools[i].activate();
        }
        this.deActivatedTools = [];
        this.toolMapClick.deactivateTool();
    },
    getConfigByAppLayer : function (appLayerId) {
        var configs = [];
        for (var i = 0 ; i < this.graphs.length ;i++){
            if(this.graphs[i].layer === appLayerId){
                configs.push(this.graphs[i]);
            }
        }
        if(!configs.length) return null;
        return configs;
    },
    getAttribute : function (appLayer, attributeId){
        for(var i = 0 ; i < appLayer.attributes.length;i++){
            if(appLayer.attributes[i].id === attributeId){
                return appLayer.attributes[i];
            }
        }
        return null;
    },
    getAttributeTitle: function(appLayer, attributeId) {
        return this.getAttributeTitleName(appLayer, attributeId, true);
    },
    getAttributeTitleName: function(appLayer, attributeId, allowAlias) {
        var attributes = this.wrapArray(attributeId),
            attributeTitles = [],
            attribute = null;
        for(var i = 0; i < attributes.length; i++) {
            attribute = this.getAttribute(appLayer, attributes[i]);
            if(attribute === null) {
                attributeTitles.push('');
                continue;
            }
            attributeTitles.push(allowAlias && attribute.alias ? attribute.alias : attribute.name);
        }
        if(attributeTitles.length === 1) return attributeTitles[0];
        return attributeTitles;
    },
    wrapArray: function(item) {
        if(this.isArray(item)) return item;
        return [ item ];
    },
    isArray: function(item) {
        return Object.prototype.toString.call(item) === '[object Array]';
    },
    getExtComponents: function() {
        return [ this.maincontainer.getId() ];
    }
});
<|endoftext|>


<commit_before>/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2020 Looker Data Sciences, Inc.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

import React, { useEffect, useState, useContext } from 'react'
import { Space, ComponentsProvider, Text } from '@looker/components'
import { ExtensionContext } from '@looker/extension-sdk-react'

export const HelloWorld = () => {
  const { core40SDK } = useContext(ExtensionContext)
  const [message, setMessage] = useState()

  useEffect(() => {
    const initialize = async () => {
      try {
        const value = await core40SDK.ok(core40SDK.me())
        setMessage(`Hello, ${value.display_name}`)
      } catch (error) {
        setMessage('Error occured getting information about me!')
        console.error(error)
      }
    }
    initialize()
  }, [])

  return (
    <>
      <ComponentsProvider>
        <Space p="xxxxxlarge" width="100%" height="50vh" around>
          <Text p="xxxxxlarge" fontSize="xxxxxlarge">
            {message}
          </Text>
        </Space>
      </ComponentsProvider>
      )}
    </>
  )
}
<commit_msg>remove extra bracket<commit_after>/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2020 Looker Data Sciences, Inc.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

import React, { useEffect, useState, useContext } from 'react'
import { Space, ComponentsProvider, Text } from '@looker/components'
import { ExtensionContext } from '@looker/extension-sdk-react'

export const HelloWorld = () => {
  const { core40SDK } = useContext(ExtensionContext)
  const [message, setMessage] = useState()

  useEffect(() => {
    const initialize = async () => {
      try {
        const value = await core40SDK.ok(core40SDK.me())
        setMessage(`Hello, ${value.display_name}`)
      } catch (error) {
        setMessage('Error occured getting information about me!')
        console.error(error)
      }
    }
    initialize()
  }, [])

  return (
    <>
      <ComponentsProvider>
        <Space p="xxxxxlarge" width="100%" height="50vh" around>
          <Text p="xxxxxlarge" fontSize="xxxxxlarge">
            {message}
          </Text>
        </Space>
      </ComponentsProvider>
    </>
  )
}
<|endoftext|>


<commit_before>var Vec3 = require("../src/math/Vec3");
var Quaternion = require("../src/math/Quaternion");
var Box = require('../src/shapes/Box');
var Heightfield = require('../src/shapes/heightfield');
var Narrowphase = require('../src/world/Narrowphase');
var Sphere = require('../src/shapes/Sphere');
var Body = require('../src/objects/Body');
var ContactMaterial = require('../src/material/ContactMaterial');
var World = require('../src/world/World');

module.exports = {

    sphereSphere : function(test){
        var world = new World();
        var cg = new Narrowphase(world);
        var result = [];
        var sphereShape = new Sphere(1);

        var bodyA = new Body({ mass: 1 });
        bodyA.addShape(sphereShape);
        var bodyB = new Body({ mass: 1 });
        bodyB.addShape(sphereShape);

        cg.currentContactMaterial = new ContactMaterial();
        cg.result = result;
        cg.sphereSphere(
            sphereShape,
            sphereShape,
            new Vec3(0.5, 0, 0),
            new Vec3(-0.5, 0, 0),
            new Quaternion(),
            new Quaternion(),
            bodyA,
            bodyB
        );

        test.equal(result.length, 1);

        test.done();
    },

    sphereHeightfield : function(test){
        var world = new World();
        var cg = new Narrowphase(world);
        var result = [];
        var hfShape = createHeightfield();
        var sphereShape = new Sphere(0.1);
        cg.currentContactMaterial = new ContactMaterial();
        cg.result = result;
        cg.sphereHeightfield(
            sphereShape,
            hfShape,
            new Vec3(0.25, 0.25, 0.05), // hit the first triangle in the field
            new Vec3(0, 0, 0),
            new Quaternion(),
            new Quaternion(),
            new Body(1, sphereShape),
            new Body(1, hfShape)
        );

        test.equal(result.length, 1);

        test.done();
    },

};

function createHeightfield(){
    var matrix = [];
    var size = 20;
    for (var i = 0; i < size; i++) {
        matrix.push([]);
        for (var j = 0; j < size; j++) {
            matrix[i].push(0);
        }
    }
    var hfShape = new Heightfield(matrix, {
        elementSize: 1,
    });

    return hfShape;
}<commit_msg>fixed require typo<commit_after>var Vec3 = require("../src/math/Vec3");
var Quaternion = require("../src/math/Quaternion");
var Box = require('../src/shapes/Box');
var Heightfield = require('../src/shapes/Heightfield');
var Narrowphase = require('../src/world/Narrowphase');
var Sphere = require('../src/shapes/Sphere');
var Body = require('../src/objects/Body');
var ContactMaterial = require('../src/material/ContactMaterial');
var World = require('../src/world/World');

module.exports = {

    sphereSphere : function(test){
        var world = new World();
        var cg = new Narrowphase(world);
        var result = [];
        var sphereShape = new Sphere(1);

        var bodyA = new Body({ mass: 1 });
        bodyA.addShape(sphereShape);
        var bodyB = new Body({ mass: 1 });
        bodyB.addShape(sphereShape);

        cg.currentContactMaterial = new ContactMaterial();
        cg.result = result;
        cg.sphereSphere(
            sphereShape,
            sphereShape,
            new Vec3(0.5, 0, 0),
            new Vec3(-0.5, 0, 0),
            new Quaternion(),
            new Quaternion(),
            bodyA,
            bodyB
        );

        test.equal(result.length, 1);

        test.done();
    },

    sphereHeightfield : function(test){
        var world = new World();
        var cg = new Narrowphase(world);
        var result = [];
        var hfShape = createHeightfield();
        var sphereShape = new Sphere(0.1);
        cg.currentContactMaterial = new ContactMaterial();
        cg.result = result;
        cg.sphereHeightfield(
            sphereShape,
            hfShape,
            new Vec3(0.25, 0.25, 0.05), // hit the first triangle in the field
            new Vec3(0, 0, 0),
            new Quaternion(),
            new Quaternion(),
            new Body(1, sphereShape),
            new Body(1, hfShape)
        );

        test.equal(result.length, 1);

        test.done();
    },

};

function createHeightfield(){
    var matrix = [];
    var size = 20;
    for (var i = 0; i < size; i++) {
        matrix.push([]);
        for (var j = 0; j < size; j++) {
            matrix[i].push(0);
        }
    }
    var hfShape = new Heightfield(matrix, {
        elementSize: 1,
    });

    return hfShape;
}<|endoftext|>


<commit_before>// Copyright (c) 2012 Titanium I.T. LLC. All rights reserved. See LICENSE.txt for details.
/*global jQuery, describe, it, expect, dump, $, wwp, beforeEach, afterEach, Raphael*/

(function() {
	"use strict";

	describe("Drawing area", function() {

		var drawingArea;
		var paper;

		afterEach(function() {
			drawingArea.remove();
		});

		it("should have the same dimensions as its enclosing div", function() {
			drawingArea = $("
hi
");
			$(document.body).append(drawingArea);
			paper = wwp.initializeDrawingArea(drawingArea[0]);

			expect(paper.height).to.equal(300);
			expect(paper.width).to.equal(600);
		});

		it("should draw a line", function() {
			drawingArea = $("
hi
");
			$(document.body).append(drawingArea);
			paper = wwp.initializeDrawingArea(drawingArea[0]);

			wwp.drawLine(20, 30, 30, 300);
			var elements = drawingElements(paper);
			expect(elements.length).to.equal(1);
			expect(pathFor(elements[0])).to.equal("M20,30L30,300");
		});

		it("draws line segments in response to clicks", function() {
			drawingArea = $("
hi
");
			$(document.body).append(drawingArea);
			paper = wwp.initializeDrawingArea(drawingArea[0]);

			clickMouse(20, 30);
			clickMouse(50, 60);
//			clickMouse(40, 20);

			expect(paperPaths(paper)).to.eql([ [20, 30, 50, 60] ]);//, [50, 60, 40, 20] ]);
		});

		function paperPaths(paper) {
			var box;
			var result = [];
			for (var i = 0; i < drawingElements(paper).length; i++) {
				box = drawingElements(paper)[i].getBBox();
				result.push([ box.x, box.y, box.x2, box.y2 ]);
				dump(JSON.stringify(box));
			}
			return result;
		}

//		it("considers border when calculating mouse target", function() {
//			drawingArea = $("
hi
");
//			$(document.body).append(drawingArea);
//			paper = wwp.initializeDrawingArea(drawingArea[0]);
//
//			var eventData = new jQuery.Event();
//			eventData.pageX = 20;
//			eventData.pageY = 30;
//			eventData.type = "click";
//
//			drawingArea.trigger(eventData);
//
//			var topLeftOfDrawingArea = drawingArea.offset();
//			var borderWidth = 13;
//			var expectedX = 20 - topLeftOfDrawingArea.left - borderWidth;
//			var expectedY = 30 - topLeftOfDrawingArea.top - borderWidth;
//
//			var elements = drawingElements(paper);
//			expect(elements.length).to.equal(1);
//			expect(pathFor(elements[0])).to.equal("M0,0L" + expectedX + "," + expectedY);
//		});

		// TODO: test that em is converted px

		function clickMouse(relativeX, relativeY) {
			var topLeftOfDrawingArea = drawingArea.offset();
			var pageX = relativeX + topLeftOfDrawingArea.left;
			var pageY = relativeY + topLeftOfDrawingArea.top;

			var eventData = new jQuery.Event();
			eventData.pageX = pageX;
			eventData.pageY = pageY;
			eventData.type = "click";
			drawingArea.trigger(eventData);
		}

		function drawingElements(paper) {
			var result = [];
			paper.forEach(function(element) {
				result.push(element);
			});
			return result;
		}

		function pathFor(element) {
			// Use 'Element.getBBox()' here instead of low-level DOM inspection?
			// (thanks to Vlad Gurdiga for the suggestion - http://www.letscodejavascript.com/v1/comments/tdjs49.html)

			var box = element.getBBox();
			return "M" + box.x + "," + box.y + "L" + box.x2 + "," + box.y2;

			// Superceded by getBBox()?
//			if (Raphael.vml) return vmlPathFor(element);
//			else if (Raphael.svg) return svgPathFor(element);
//			else throw new Error("Unknown Raphael type");
		}

		function svgPathFor(element) {
			var path = element.node.attributes.d.value;
			if (path.indexOf(",") !== -1) {
				// We're in Firefox, Safari, Chrome, which uses format "M20,30L30,300"
				return path;
			}
			else {
				// We're in IE9, which uses format "M 20 30 L 30 300"
				var ie9PathRegex = /M (\d+) (\d+) L (\d+) (\d+)/;
				var ie9 = path.match(ie9PathRegex);

				return "M" + ie9[1] + "," + ie9[2] + "L" + ie9[3] + "," + ie9[4];
			}
			return path;
		}

		function vmlPathFor(element) {
			// We're in IE 8, which uses format "m432000,648000 l648000,67456800 e"
			var VML_MAGIC_NUMBER = 21600;

			var path = element.node.path.value;

			var ie8PathRegex = /m(\d+),(\d+) l(\d+),(\d+) e/;
			var ie8 = path.match(ie8PathRegex);

			var startX = ie8[1] / VML_MAGIC_NUMBER;
			var startY = ie8[2] / VML_MAGIC_NUMBER;
			var endX = ie8[3] / VML_MAGIC_NUMBER;
			var endY = ie8[4] / VML_MAGIC_NUMBER;

			return "M" + startX + "," + startY + "L" + endX + "," + endY;
		}

	});
}());
<commit_msg>Switched back to inspecting DOM to discover paper paths, as an experiment<commit_after>// Copyright (c) 2012 Titanium I.T. LLC. All rights reserved. See LICENSE.txt for details.
/*global jQuery, describe, it, expect, dump, $, wwp, beforeEach, afterEach, Raphael*/

(function() {
	"use strict";

	describe("Drawing area", function() {

		var drawingArea;
		var paper;

		afterEach(function() {
			drawingArea.remove();
		});

		it("should have the same dimensions as its enclosing div", function() {
			drawingArea = $("
hi
");
			$(document.body).append(drawingArea);
			paper = wwp.initializeDrawingArea(drawingArea[0]);

			expect(paper.height).to.equal(300);
			expect(paper.width).to.equal(600);
		});

		it("should draw a line", function() {
			drawingArea = $("
hi
");
			$(document.body).append(drawingArea);
			paper = wwp.initializeDrawingArea(drawingArea[0]);

			wwp.drawLine(20, 30, 30, 300);
			expect(paperPaths(paper)).to.eql([ [20, 30, 30, 300] ]);
		});

		it("draws line segments in response to clicks", function() {
			drawingArea = $("
hi
");
			$(document.body).append(drawingArea);
			paper = wwp.initializeDrawingArea(drawingArea[0]);

			clickMouse(20, 30);
			clickMouse(50, 60);
			clickMouse(40, 20);

			expect(paperPaths(paper)).to.eql([ [20, 30, 50, 60], [50, 60, 40, 20] ]);
		});

		function paperPaths(paper) {
			// Note: Paths are normalized with left side first in all cases
			var box;
			var result = [];
			for (var i = 0; i < drawingElements(paper).length; i++) {
				box = pathFor(drawingElements(paper)[i]);
				result.push([ box.x, box.y, box.x2, box.y2 ]);
				dump(JSON.stringify(box));
			}
			return result;
		}

//		it("considers border when calculating mouse target", function() {
//			drawingArea = $("
hi
");
//			$(document.body).append(drawingArea);
//			paper = wwp.initializeDrawingArea(drawingArea[0]);
//
//			var eventData = new jQuery.Event();
//			eventData.pageX = 20;
//			eventData.pageY = 30;
//			eventData.type = "click";
//
//			drawingArea.trigger(eventData);
//
//			var topLeftOfDrawingArea = drawingArea.offset();
//			var borderWidth = 13;
//			var expectedX = 20 - topLeftOfDrawingArea.left - borderWidth;
//			var expectedY = 30 - topLeftOfDrawingArea.top - borderWidth;
//
//			var elements = drawingElements(paper);
//			expect(elements.length).to.equal(1);
//			expect(pathFor(elements[0])).to.equal("M0,0L" + expectedX + "," + expectedY);
//		});

		// TODO: test that em is converted px

		function clickMouse(relativeX, relativeY) {
			var topLeftOfDrawingArea = drawingArea.offset();
			var pageX = relativeX + topLeftOfDrawingArea.left;
			var pageY = relativeY + topLeftOfDrawingArea.top;

			var eventData = new jQuery.Event();
			eventData.pageX = pageX;
			eventData.pageY = pageY;
			eventData.type = "click";
			drawingArea.trigger(eventData);
		}

		function drawingElements(paper) {
			var result = [];
			paper.forEach(function(element) {
				result.push(element);
			});
			return result;
		}

		function pathFor(element) {
			// Use 'Element.getBBox()' here instead of low-level DOM inspection?
			// (thanks to Vlad Gurdiga for the suggestion - http://www.letscodejavascript.com/v1/comments/tdjs49.html)

//			var box = element.getBBox();
//			return "M" + box.x + "," + box.y + "L" + box.x2 + "," + box.y2;

			// Superceded by getBBox()?
			if (Raphael.vml) return vmlPathFor(element);
			else if (Raphael.svg) return svgPathFor(element);
			else throw new Error("Unknown Raphael type");
		}

		function svgPathFor(element) {
			var path = element.node.attributes.d.value;
			if (path.indexOf(",") !== -1) {
				// We're in Firefox, Safari, Chrome, which uses format "M20,30L30,300"
				return path;
			}
			else {
				// We're in IE9, which uses format "M 20 30 L 30 300"
				var ie9PathRegex = /M (\d+) (\d+) L (\d+) (\d+)/;
				var ie9 = path.match(ie9PathRegex);

				return "M" + ie9[1] + "," + ie9[2] + "L" + ie9[3] + "," + ie9[4];
			}
			return path;
		}

		function vmlPathFor(element) {
			// We're in IE 8, which uses format "m432000,648000 l648000,67456800 e"
			var VML_MAGIC_NUMBER = 21600;

			var path = element.node.path.value;

			var ie8PathRegex = /m(\d+),(\d+) l(\d+),(\d+) e/;
			var ie8 = path.match(ie8PathRegex);

			var startX = ie8[1] / VML_MAGIC_NUMBER;
			var startY = ie8[2] / VML_MAGIC_NUMBER;
			var endX = ie8[3] / VML_MAGIC_NUMBER;
			var endY = ie8[4] / VML_MAGIC_NUMBER;

			return {
				x: startX,
				y: startY,
				x2: endX,
				y2: endY
			};
		}

	});
}());
<|endoftext|>


<commit_before>
import _ from 'lodash'

export function appendBranch (path, branch) {
  return path.concat([branch])
}

export function pathToString (path, itemToId) {
  return (path.length === 0)
    ? ''
    : path.map(itemToId).join(':')
}

/**
 * queries all information on compound nodes.
 */
export function queryNode (node, resolveFn, resolved = {}, resolvePath = []) {
  return resolveFn(node.meta, node.version)
  .then((resNode) => {
    resolved[(node.name) ? node.name : node.meta] = resNode
    var branchName = (node.name) ? node.name : resNode.id
    var nodeIdentifier = {meta: resNode.id, branch: branchName, version: resNode.version, uniqueId: resNode.uniqueId, path: resolvePath}
    var newPath = appendBranch(resolvePath, nodeIdentifier)
    var branchPath = pathToString(newPath, (b) => b.branch)

    resNode.path = resolvePath
    resNode.params = node.params
    if (resolvePath.length !== 0) {
      resNode.parent = pathToString(resolvePath, (b) => b.branch)
    }
    resNode.branchPath = branchPath
    resNode.branch = branchName
    if (node.values) {
      resNode.values = node.values
    }
    var branchIdx = _.findIndex(resolvePath, (n) => n.meta === node.meta && n.branch === node.name)
    if (branchIdx !== -1) {
      resNode.recursive = true
    }
    if (resNode.atomic || resNode.recursive) {
      return resNode
    } else {
      var queryNextNode = _.partial(queryNode, _, resolveFn, resolved, newPath)
      return Promise.all(_.map(resNode.implementation.nodes, queryNextNode))
      .then((implNodes) => {
        resNode.implementation.nodes = implNodes
        return resNode
      })
    }
  })
}

/**
 * gets a compound node and returns an array of atomics + compound nodes
 * @param {any} node The node to process
 * @return {Array} An array containing all the atomics of the compound node.
 */
export function flattenNode (node) {
  if (node.atomic || node.recursive) {
    return [node]
  } else {
    var impls = _(node.implementation.nodes)
      .map(flattenNode)
      .flatten()
      .value()
    return [node].concat(impls)
  }
}

export function flattenEdges (node) {
  if (node.atomic || node.recursive) {
    return []
  } else {
    var subEdges = _(node.implementation.nodes)
      .map(flattenEdges)
      .flatten()
      .value()
    var edges = _(node.implementation.edges)
      .map((e) => {
        var e2 = _.cloneDeep(e)
        var pathId = pathToString(node.path, (b) => b.branch)
        if (node.path.length !== 0) {
          pathId += ':'
        }
        var nodeId = node.branch
        e2.from = pathId + nodeId + ':' + e2.from
        e2.to = pathId + nodeId + ':' + e2.to
        return e2
      })
      .value()
    return edges.concat(subEdges)
  }
}
<commit_msg>(feature) Support type hints.<commit_after>
import _ from 'lodash'

export function appendBranch (path, branch) {
  return path.concat([branch])
}

export function pathToString (path, itemToId) {
  return (path.length === 0)
    ? ''
    : path.map(itemToId).join(':')
}

/**
 * queries all information on compound nodes.
 */
export function queryNode (node, resolveFn, resolved = {}, resolvePath = []) {
  return resolveFn(node.meta, node.version)
  .then((resNode) => {
    resolved[(node.name) ? node.name : node.meta] = resNode
    var branchName = (node.name) ? node.name : resNode.id
    var nodeIdentifier = {meta: resNode.id, branch: branchName, version: resNode.version, uniqueId: resNode.uniqueId, path: resolvePath}
    var newPath = appendBranch(resolvePath, nodeIdentifier)
    var branchPath = pathToString(newPath, (b) => b.branch)

    resNode.path = resolvePath
    resNode.params = node.params
    resNode.typeHint = node.typeHint
    if (resolvePath.length !== 0) {
      resNode.parent = pathToString(resolvePath, (b) => b.branch)
    }
    resNode.branchPath = branchPath
    resNode.branch = branchName
    if (node.values) {
      resNode.values = node.values
    }
    var branchIdx = _.findIndex(resolvePath, (n) => n.meta === node.meta && n.branch === node.name)
    if (branchIdx !== -1) {
      resNode.recursive = true
    }
    if (resNode.atomic || resNode.recursive) {
      return resNode
    } else {
      var queryNextNode = _.partial(queryNode, _, resolveFn, resolved, newPath)
      return Promise.all(_.map(resNode.implementation.nodes, queryNextNode))
      .then((implNodes) => {
        resNode.implementation.nodes = implNodes
        return resNode
      })
    }
  })
}

/**
 * gets a compound node and returns an array of atomics + compound nodes
 * @param {any} node The node to process
 * @return {Array} An array containing all the atomics of the compound node.
 */
export function flattenNode (node) {
  if (node.atomic || node.recursive) {
    return [node]
  } else {
    var impls = _(node.implementation.nodes)
      .map(flattenNode)
      .flatten()
      .value()
    return [node].concat(impls)
  }
}

export function flattenEdges (node) {
  if (node.atomic || node.recursive) {
    return []
  } else {
    var subEdges = _(node.implementation.nodes)
      .map(flattenEdges)
      .flatten()
      .value()
    var edges = _(node.implementation.edges)
      .map((e) => {
        var e2 = _.cloneDeep(e)
        var pathId = pathToString(node.path, (b) => b.branch)
        if (node.path.length !== 0) {
          pathId += ':'
        }
        var nodeId = node.branch
        e2.from = pathId + nodeId + ':' + e2.from
        e2.to = pathId + nodeId + ':' + e2.to
        return e2
      })
      .value()
    return edges.concat(subEdges)
  }
}
<|endoftext|>


<commit_before>(function() {

module("PEG.compiler.checks");

test("reports missing referenced rules", function() {
  var grammars = [
    'start = missing',
    'start = missing / "a" / "b"',
    'start = "a" / "b" / missing',
    'start = missing "a" "b"',
    'start = "a" "b" missing',
    'start = label:missing',
    'start = &missing',
    'start = !missing',
    'start = missing?',
    'start = missing*',
    'start = missing+',
    'start = missing { }'
  ];

  for (var i = 0; i < grammars.length; i++) {
    raises(
      function() {
        var ast = PEG.parser.parse(grammars[i]);
        PEG.compiler.checks.missingReferencedRules(ast);
      },
      function(e) {
        return e instanceof PEG.GrammarError
          && e.message === "Referenced rule \"missing\" does not exist.";
      }
    );
  }
});

test("reports left recursion", function() {
  var grammars = [
    /* Direct */
    'start = start',
    'start = start / "a" / "b"',
    'start = "a" / "b" / start',
    'start = start "a" "b"',
    'start = label:start',
    'start = &start',
    'start = !start',
    'start = start?',
    'start = start*',
    'start = start+',
    'start = start { }',

    /* Indirect */
    'start = stop; stop = start'
  ];

  for (var i = 0; i < grammars.length; i++) {
    raises(
      function() {
        var ast = PEG.parser.parse(grammars[i]);
        PEG.compiler.checks.leftRecursion(ast);
      },
      function(e) {
        return e instanceof PEG.GrammarError
          && e.message === "Left recursion detected for rule \"start\".";
      }
    );
  }
});

})();
<commit_msg>test/checks-test.js: Avoid function definitions in loops<commit_after>(function() {

module("PEG.compiler.checks");

test("reports missing referenced rules", function() {
  function testGrammar(grammar) {
    raises(
      function() {
        var ast = PEG.parser.parse(grammar);
        PEG.compiler.checks.missingReferencedRules(ast);
      },
      function(e) {
        return e instanceof PEG.GrammarError
          && e.message === "Referenced rule \"missing\" does not exist.";
      }
    );
  }

  var grammars = [
    'start = missing',
    'start = missing / "a" / "b"',
    'start = "a" / "b" / missing',
    'start = missing "a" "b"',
    'start = "a" "b" missing',
    'start = label:missing',
    'start = &missing',
    'start = !missing',
    'start = missing?',
    'start = missing*',
    'start = missing+',
    'start = missing { }'
  ];

  for (var i = 0; i < grammars.length; i++) { testGrammar(grammars[i]); }
});

test("reports left recursion", function() {
  function testGrammar(grammar) {
    raises(
      function() {
        var ast = PEG.parser.parse(grammar);
        PEG.compiler.checks.leftRecursion(ast);
      },
      function(e) {
        return e instanceof PEG.GrammarError
          && e.message === "Left recursion detected for rule \"start\".";
      }
    );
  }

  var grammars = [
    /* Direct */
    'start = start',
    'start = start / "a" / "b"',
    'start = "a" / "b" / start',
    'start = start "a" "b"',
    'start = label:start',
    'start = &start',
    'start = !start',
    'start = start?',
    'start = start*',
    'start = start+',
    'start = start { }',

    /* Indirect */
    'start = stop; stop = start'
  ];

  for (var i = 0; i < grammars.length; i++) { testGrammar(grammars[i]); }
});

})();
<|endoftext|>


<commit_before>var config = require('../config');
var router = require('express').Router();
var request = require('request');
var moment = require('moment');
var Entities = require('html-entities').AllHtmlEntities;
var entities = new Entities();
var schedule = require('node-schedule');
var slack = require('../lib/slack');
var utils = require('../lib/utils');

// Scheduled task for Slack notifications
var rule = new schedule.RecurrenceRule();
rule.minute = [0, 30];
var j = schedule.scheduleJob(rule, function () {
	utils.conlog('Scheduled job - underperforming');
	getLatestPosts(null, true);
});

// JSON output of results
router.get('/', function (req, res) {
	utils.conlog('URL request for JSON');
	getLatestPosts(res, false);
});

// Triggers Slack notification on demand
router.get('/slack', function (req, res) {
	utils.conlog('URL request for Slack notification');
	getLatestPosts(null, true);
	res.status(200).send('SLACK');
});

// Grab latest posts from the newsfeed API
function getLatestPosts(res, tellSlack) {
	request(config.postsApiUrl, function (apiError, apiResponse, apiBody) {
		utils.conlog('Newsfeed API response ' + apiResponse.statusCode);
		if (!apiError) {
			var outPosts = getList(apiBody);
			if (res) {
				// Output JSON response
				res.setHeader('Content-Type', 'application/json');
				res.header('Access-Control-Allow-Origin', '*');
  				res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept');
				res.send(outPosts);
			}
			if (tellSlack && outPosts.posts.length) {
				// Tell Slack
				utils.conlog('Slack notification');
				slack.send(makeSlackPayload(outPosts.posts));
			}
		} else {
			utils.conlog('Newsfeed API error ' + apiError);
		}
	});
}

// Pick the posts between 60 and 90 mins old with views less than 1,000
function getList(body) {
	var postsJSON = JSON.parse(body),
		inPosts = postsJSON.posts,
		outPosts = {},
		now = moment(),
		earlier = now.clone(),
		hourAgo = now.clone();

	outPosts.posts = [];
	earlier = earlier.subtract(90, 'minutes');
	hourAgo = hourAgo.subtract(1, 'hour');

	for (var i = 0; i < 10; i++) {
		var postDate = moment(inPosts[i].date);
		if (postDate.isBetween(earlier, hourAgo) && inPosts[i].metrics.views < 1000) {
			inPosts[i].age = now.diff(postDate, 'minutes') + ' mins';
			outPosts.posts.push(inPosts[i]);
		}
	}
	outPosts.metadata = {};
	outPosts.metadata.timestamp = now.format('D MMM YYYY HH:mm:ss');
	return outPosts;
}

// Build payload for Slack notification
function makeSlackPayload(posts) {
	var articles = '',
		payload;
	posts.forEach(function (item) {
		articles += '<' + item.URL + '|' + entities.decode(item.title) + '>\n';
		articles += 'Age: ' + item.age + '\n';
		articles += 'Views: ' + utils.addCommas(item.metrics.views) + '\n\n';
	});
	payload = {
		"text"       : "Articles waiting to take off:",
		"attachments": [
			{
				"fallback": "List of articles that haven't taken off yet.",
				"color"   : "warning",
				"fields"  : [
					{
						"title": "",
						"value": articles,
						"short": false
					}
				]
			}
		],
		"icon_emoji": ":turtle:"
	};
	return JSON.stringify(payload);
}

module.exports = router;
<commit_msg>Added response for newsfeed API error.<commit_after>var config = require('../config');
var router = require('express').Router();
var request = require('request');
var moment = require('moment');
var Entities = require('html-entities').AllHtmlEntities;
var entities = new Entities();
var schedule = require('node-schedule');
var slack = require('../lib/slack');
var utils = require('../lib/utils');

// Scheduled task for Slack notifications
var rule = new schedule.RecurrenceRule();
rule.minute = [0, 30];
var j = schedule.scheduleJob(rule, function () {
	utils.conlog('Scheduled job - underperforming');
	getLatestPosts(null, true);
});

// JSON output of results
router.get('/', function (req, res) {
	utils.conlog('URL request for JSON');
	getLatestPosts(res, false);
});

// Triggers Slack notification on demand
router.get('/slack', function (req, res) {
	utils.conlog('URL request for Slack notification');
	getLatestPosts(null, true);
	res.status(200).send('SLACK');
});

// Grab latest posts from the newsfeed API
function getLatestPosts(res, tellSlack) {
	request(config.postsApiUrl, function (apiError, apiResponse, apiBody) {
		utils.conlog('Newsfeed API response ' + apiResponse.statusCode);
		if (!apiError) {
			var outPosts = getList(apiBody);
			if (res) {
				// Output JSON response
				res.setHeader('Content-Type', 'application/json');
				res.header('Access-Control-Allow-Origin', '*');
  				res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept');
				res.send(outPosts);
			}
			if (tellSlack && outPosts.posts.length) {
				// Tell Slack
				utils.conlog('Slack notification');
				slack.send(makeSlackPayload(outPosts.posts));
			}
		} else {
			utils.conlog('Newsfeed API error ' + apiError);
			res.status(500).send('Newsfeed API error');
		}
	});
}

// Pick the posts between 60 and 90 mins old with views less than 1,000
function getList(body) {
	var postsJSON = JSON.parse(body),
		inPosts = postsJSON.posts,
		outPosts = {},
		now = moment(),
		earlier = now.clone(),
		hourAgo = now.clone();

	outPosts.posts = [];
	earlier = earlier.subtract(90, 'minutes');
	hourAgo = hourAgo.subtract(1, 'hour');

	for (var i = 0; i < 10; i++) {
		var postDate = moment(inPosts[i].date);
		if (postDate.isBetween(earlier, hourAgo) && inPosts[i].metrics.views < 1000) {
			inPosts[i].age = now.diff(postDate, 'minutes') + ' mins';
			outPosts.posts.push(inPosts[i]);
		}
	}
	outPosts.metadata = {};
	outPosts.metadata.timestamp = now.format('D MMM YYYY HH:mm:ss');
	return outPosts;
}

// Build payload for Slack notification
function makeSlackPayload(posts) {
	var articles = '',
		payload;
	posts.forEach(function (item) {
		articles += '<' + item.URL + '|' + entities.decode(item.title) + '>\n';
		articles += 'Age: ' + item.age + '\n';
		articles += 'Views: ' + utils.addCommas(item.metrics.views) + '\n\n';
	});
	payload = {
		"text"       : "Articles waiting to take off:",
		"attachments": [
			{
				"fallback": "List of articles that haven't taken off yet.",
				"color"   : "warning",
				"fields"  : [
					{
						"title": "",
						"value": articles,
						"short": false
					}
				]
			}
		],
		"icon_emoji": ":turtle:"
	};
	return JSON.stringify(payload);
}

module.exports = router;
<|endoftext|>


<commit_before>/*
 * jQuery Expander plugin
 * Version 0.4  (12/09/2008)
 * @requires jQuery v1.1.1+
 *
 * Dual licensed under the MIT and GPL licenses:
 * http://www.opensource.org/licenses/mit-license.php
 * http://www.gnu.org/licenses/gpl.html
 *
 */

(function($) {

  $.fn.expander = function(options) {

    var opts = $.extend({}, $.fn.expander.defaults, options);
    var delayedCollapse;
    return this.each(function() {
      var $this = $(this);
      var o = $.meta ? $.extend({}, opts, $this.data()) : opts;
     	var cleanedTag, startTags, endTags;
     	var allText = $this.html();
     	var startText = allText.slice(0, o.slicePoint).replace(/\w+$/,'');
     	startTags = startText.match(/<\w[^>]*>/g);
   	  if (startTags) {startText = allText.slice(0,o.slicePoint + startTags.join('').length).replace(/\w+$/,'');}

     	if (startText.lastIndexOf('<') > startText.lastIndexOf('>') ) {
     	  startText = startText.slice(0,startText.lastIndexOf('<'));
     	}
     	var endText = allText.slice(startText.length);
     	// create necessary expand/collapse elements if they don't already exist
   	  if (!$('span.details', this).length) {
        // end script if text length isn't long enough.
       	if ( endText.replace(/\s+$/,'').split(' ').length < o.widow ) { return; }
       	// otherwise, continue...
       	if (endText.indexOf('</') > -1) {
         	endTags = endText.match(/<(\/)?[^>]*>/g);
          for (var i=0; i < endTags.length; i++) {

            if (endTags[i].indexOf('</') > -1) {
              var startTag, startTagExists = false;
              for (var j=0; j ');
                if (startTag == rSlash(endTags[i])) {
                  startTagExists = true;
                }
              }
              if (!startTagExists) {
                startText = startText + endTags[i];
                var matched = false;
                for (var s=startTags.length - 1; s >= 0; s--) {
                  if (startTags[s].slice(0, startTags[s].indexOf(' ')).replace(/(\w)$/,'$1>') == rSlash(endTags[i])
                  && matched == false) {
                    cleanedTag = cleanedTag ? startTags[s] + cleanedTag : startTags[s];
                    matched = true;
                  }
                };
              }
            }
          }

          endText = cleanedTag && cleanedTag + endText || endText;
        }
     	  $this.html([
     		startText,
     		'',
     		o.expandPrefix,
       		'',
       		  o.expandText,
       		'',
        '',
     		'',
     		  endText,
     		''
     		].join('')
     	  );
      }
      var $thisDetails = $('span.details', this),
        $readMore = $('span.read-more', this);
   	  $thisDetails.hide();
 	    $readMore.find('a').click(function() {
 	      $readMore.hide();

 	      if (o.expandEffect === 'show' && !o.expandSpeed) {
          o.beforeExpand($this);
 	        $thisDetails.show();
          o.afterExpand($this);
          delayCollapse(o, $thisDetails);
 	      } else {
          o.beforeExpand($this);
 	        $thisDetails[o.expandEffect](o.expandSpeed, function() {
            $thisDetails.css({zoom: ''});
            o.afterExpand($this);
            delayCollapse(o, $thisDetails);
 	        });
 	      }
        return false;
 	    });
      if (o.userCollapse) {
        $this
        .find('span.details').append('' + o.userCollapsePrefix + '' + o.userCollapseText + '');
        $this.find('span.re-collapse a').click(function() {

          clearTimeout(delayedCollapse);
          var $detailsCollapsed = $(this).parents('span.details');
          reCollapse($detailsCollapsed);
          o.onCollapse($this, true);
          return false;
        });
      }
    });
    function reCollapse(el) {
       el.hide()
        .prev('span.read-more').show();
    }
    function delayCollapse(option, $collapseEl) {
      if (option.collapseTimer) {
        delayedCollapse = setTimeout(function() {
          reCollapse($collapseEl);
          option.onCollapse($collapseEl.parent(), false);
          },
          option.collapseTimer
        );
      }
    }
    function rSlash(rString) {
      return rString.replace(/\//,'');
    }
  };
    // plugin defaults
  $.fn.expander.defaults = {
    slicePoint:       100,  // the number of characters at which the contents will be sliced into two parts.
                            // Note: any tag names in the HTML that appear inside the sliced element before
                            // the slicePoint will be counted along with the text characters.
    widow:            4,  // a threshold of sorts for whether to initially hide/collapse part of the element's contents.
                          // If after slicing the contents in two there are fewer words in the second part than
                          // the value set by widow, we won't bother hiding/collapsing anything.
    expandText:       'read more', // text displayed in a link instead of the hidden part of the element.
                                      // clicking this will expand/show the hidden/collapsed text
    expandPrefix:     '… ',
    collapseTimer:    0, // number of milliseconds after text has been expanded at which to collapse the text again
    expandEffect:     'fadeIn',
    expandSpeed:      '',   // speed in milliseconds of the animation effect for expanding the text
    userCollapse:     true, // allow the user to re-collapse the expanded text.
    userCollapseText: '[collapse expanded text]',  // text to use for the link to re-collapse the text
    userCollapsePrefix: ' ',
    beforeExpand: function($thisEl) {},
    afterExpand: function($thisEl) {},
    onCollapse: function($thisEl, byUser) {}
  };
})(jQuery);
<commit_msg>Update to a newer jquery.expander.js<commit_after>/*!
 * jQuery Expander Plugin v1.4
 *
 * Date: Sun Dec 11 15:08:42 2011 EST
 * Requires: jQuery v1.3+
 *
 * Copyright 2011, Karl Swedberg
 * Dual licensed under the MIT and GPL licenses (just like jQuery):
 * http://www.opensource.org/licenses/mit-license.php
 * http://www.gnu.org/licenses/gpl.html
 *
 *
 *
 *
*/
(function(d){d.expander={version:"1.4",defaults:{slicePoint:100,preserveWords:true,widow:4,expandText:"read more",expandPrefix:"… ",expandAfterSummary:false,summaryClass:"summary",detailClass:"details",moreClass:"read-more",lessClass:"read-less",collapseTimer:0,expandEffect:"fadeIn",expandSpeed:250,collapseEffect:"fadeOut",collapseSpeed:200,userCollapse:true,userCollapseText:"read less",userCollapsePrefix:" ",onSlice:null,beforeExpand:null,afterExpand:null,onCollapse:null}};d.fn.expander=function(k){function H(a,
c){var g="span",h=a.summary;if(c){g="div";if(w.test(h)&&!a.expandAfterSummary)h=h.replace(w,a.moreLabel+"$1");else h+=a.moreLabel;h='
'+h+"
"}else h+=a.moreLabel;return[h,"<",g+' class="'+a.detailClass+'"',">",a.details,"</"+g+">"].join("")}function I(a){var c=''+a.expandPrefix;c+=''+a.expandText+"";return c}function x(a,c){if(a.lastIndexOf("<")>a.lastIndexOf(">"))a=a.slice(0,a.lastIndexOf("<"));if(c)a=a.replace(J,
"");return a}function y(a,c){c.stop(true,true)[a.collapseEffect](a.collapseSpeed,function(){c.prev("span."+a.moreClass).show().length||c.parent().children("div."+a.summaryClass).show().find("span."+a.moreClass).show()})}function K(a,c,g){if(a.collapseTimer)z=setTimeout(function(){y(a,c);d.isFunction(a.onCollapse)&&a.onCollapse.call(g,false)},a.collapseTimer)}var u="init";if(typeof k=="string"){u=k;k={}}var r=d.extend({},d.expander.defaults,k),L=/^<(?:area|br|col|embed|hr|img|input|link|meta|param).*>$/i,
J=/(&(?:[^;]+;)?|\w+)$/,M=/<\/?(\w+)[^>]*>/g,A=/<(\w+)[^>]*>/g,B=/<\/(\w+)>/g,w=/(<\/[^>]+>)\s*$/,N=/^<[^>]+>.?/,z;k={init:function(){this.each(function(){var a,c,g,h,l,n,v,C=[],s=[],o={},p=this,f=d(this),D=d([]),b=d.meta?d.extend({},r,f.data()):r,O=!!f.find("."+b.detailClass).length,q=!!f.find("*").filter(function(){return/^block|table|list/.test(d(this).css("display"))}).length,t=(q?"div":"span")+"."+b.detailClass,E="span."+b.moreClass,P=b.expandSpeed||0,m=d.trim(f.html());d.trim(f.text());var e=
m.slice(0,b.slicePoint);if(!d.data(this,"expander")){d.data(this,"expander",true);d.each(["onSlice","beforeExpand","afterExpand","onCollapse"],function(i,j){o[j]=d.isFunction(b[j])});e=x(e);for(summTagless=e.replace(M,"").length;summTagless<b.slicePoint;){newChar=m.charAt(e.length);if(newChar=="<")newChar=m.slice(e.length).match(N)[0];e+=newChar;summTagless++}e=x(e,b.preserveWords);h=e.match(A)||[];l=e.match(B)||[];g=[];d.each(h,function(i,j){L.test(j)||g.push(j)});h=g;c=l.length;for(a=0;a<c;a++)l[a]=
l[a].replace(B,"$1");d.each(h,function(i,j){var F=j.replace(A,"$1"),G=d.inArray(F,l);if(G===-1){C.push(j);s.push("</"+F+">")}else l.splice(G,1)});s.reverse();if(O){c=f.find(t).remove().html();e=f.html();m=e+c;a=""}else{c=m.slice(e.length);if(c===""||c.split(/\s+/).length<b.widow)return;a=s.pop()||"";e+=s.join("");c=C.join("")+c}b.moreLabel=f.find(E).length?"":I(b);if(q)c=m;e+=a;b.summary=e;b.details=c;b.lastCloseTag=a;if(o.onSlice)b=(g=b.onSlice.call(p,b))&&g.details?g:b;q=H(b,q);f.html(q);n=f.find(t);
v=f.find(E);n.hide();v.find("a").unbind("click.expander").bind("click.expander",function(i){i.preventDefault();v.hide();D.hide();o.beforeExpand&&b.beforeExpand.call(p);n.stop(false,true)[b.expandEffect](P,function(){n.css({zoom:""});o.afterExpand&&b.afterExpand.call(p);K(b,n,p)})});D=f.find("div."+b.summaryClass);b.userCollapse&&!f.find("span."+b.lessClass).length&&f.find(t).append(''+b.userCollapsePrefix+''+b.userCollapseText+"");f.find("span."+
b.lessClass+" a").unbind("click.expander").bind("click.expander",function(i){i.preventDefault();clearTimeout(z);i=d(this).closest(t);y(b,i);o.onCollapse&&b.onCollapse.call(p,true)})}})},destroy:function(){if(this.data("expander")){this.removeData("expander");this.each(function(){var a=d(this),c=d.meta?d.extend({},r,a.data()):r,g=a.find("."+c.detailClass).contents();a.find("."+c.moreClass).remove();a.find("."+c.summaryClass).remove();a.find("."+c.detailClass).after(g).remove();a.find("."+c.lessClass).remove()})}}};
k[u]&&k[u].call(this);return this};d.fn.expander.defaults=d.expander.defaults})(jQuery);
<|endoftext|>


<commit_before>// Copyright 2014-2015, University of Colorado Boulder

/**
 * A node that represents a 2D surface on which energy chunks reside.  The
 * surface contains z-dimension information, and can thus be used to create an
 * effect of layering in order to get a bit of a 3D appearance.
 *
 * @author John Blanco
 */

define( function( require ) {
  'use strict';

  // modules

  var EnergyChunkNode = require( 'ENERGY_FORMS_AND_CHANGES/common/view/EnergyChunkNode' );
  var energyFormsAndChanges = require( 'ENERGY_FORMS_AND_CHANGES/energyFormsAndChanges' );
  var inherit = require( 'PHET_CORE/inherit' );
  var Node = require( 'SCENERY/nodes/Node' );
  var Path = require( 'SCENERY/nodes/Path' );

  // constants
  var ALWAYS_SHOW_OUTLINE = false; // DEBUG

  /**
   * *
   * @param {EnergyChunkContainerSlice} energyChunkContainerSlice
   * @param {ModelViewTransform2} modelViewTransform
   * @constructor
   */
  function EnergyChunkContainerSliceNode( energyChunkContainerSlice, modelViewTransform ) {

    var self = this;
    this.modelViewTransform = modelViewTransform;
    this.energyChunkContainerSlice = energyChunkContainerSlice;
    Node.call( this );

    // Add the initial energy chunks.
    // this.addInitialEnergyChunks(); // AA: No!

    energyChunkContainerSlice.energyChunkList.addItemAddedListener( function( addedEnergyChunk ) {
      var energyChunkNode = new EnergyChunkNode( addedEnergyChunk, modelViewTransform );
      // console.log( 'ECCSN: Adding energyChunkNode' );
      self.addChild( energyChunkNode );
      energyChunkContainerSlice.energyChunkList.addItemRemovedListener( function removalListener( removedEnergyChunk ) {
        if ( removedEnergyChunk === addedEnergyChunk ) {
          // console.log( 'ECCSN: Removing energyChunkNode' );
          self.removeChild( energyChunkNode );
          energyChunkContainerSlice.energyChunkList.removeItemRemovedListener( removalListener );
        }
      } );
    } );

    // For debug.
    if ( ALWAYS_SHOW_OUTLINE ) {
      this.addChild( new Path( modelViewTransform.modelToViewShape( energyChunkContainerSlice.shape ), {
        lineWidth: 1,
        stroke: 'red'
      } ) );
    }
  }

  energyFormsAndChanges.register( 'EnergyChunkContainerSliceNode', EnergyChunkContainerSliceNode );

  return inherit( Node, EnergyChunkContainerSliceNode, {

    // Note (AA): this function does not belong here. See #23. Leaving intact until more progress
    // is made on that issue.
    addInitialEnergyChunks: function() {
      var self = this;
      this.energyChunkContainerSlice.energyChunkList.forEach( function( energyChunk ) {
        self.addChild( new EnergyChunkNode( energyChunk, self.modelViewTransform ) );
      } );
    }
  } );

} );

<commit_msg>Add listeners to existing as well as future chunks (#23)<commit_after>// Copyright 2014-2015, University of Colorado Boulder

/**
 * A node that represents a 2D surface on which energy chunks reside.  The
 * surface contains z-dimension information, and can thus be used to create an
 * effect of layering in order to get a bit of a 3D appearance.
 *
 * @author John Blanco
 * @author Andrew Adare
 */

define( function( require ) {
  'use strict';

  // modules
  var EnergyChunkNode = require( 'ENERGY_FORMS_AND_CHANGES/common/view/EnergyChunkNode' );
  var energyFormsAndChanges = require( 'ENERGY_FORMS_AND_CHANGES/energyFormsAndChanges' );
  var inherit = require( 'PHET_CORE/inherit' );
  var Node = require( 'SCENERY/nodes/Node' );
  var Path = require( 'SCENERY/nodes/Path' );

  // constants
  var ALWAYS_SHOW_OUTLINE = false; // DEBUG

  /**
   * @param {EnergyChunkContainerSlice} slice
   * @param {ModelViewTransform2} modelViewTransform
   * @constructor
   */
  function EnergyChunkContainerSliceNode( slice, modelViewTransform ) {

    var self = this;
    this.modelViewTransform = modelViewTransform;
    this.energyChunkContainerSlice = slice;
    Node.call( this );

    var listener = function( addedChunk ) {
      var energyChunkNode = new EnergyChunkNode( addedChunk, modelViewTransform );
      self.addChild( energyChunkNode );
      slice.energyChunkList.addItemRemovedListener( function removalListener( removedChunk ) {
        if ( removedChunk === addedChunk ) {
          self.removeChild( energyChunkNode );
          slice.energyChunkList.removeItemRemovedListener( removalListener );
        }
      } );
    };

    // Add listeners to any existing chunks in the slice
    slice.energyChunkList.forEach( listener );

    // Add listeners to future chunks when added
    slice.energyChunkList.addItemAddedListener( listener );

    // For debug.
    if ( ALWAYS_SHOW_OUTLINE ) {
      this.addChild( new Path( modelViewTransform.modelToViewShape( slice.shape ), {
        lineWidth: 1,
        stroke: 'red'
      } ) );
    }
  }

  energyFormsAndChanges.register( 'EnergyChunkContainerSliceNode', EnergyChunkContainerSliceNode );

  return inherit( Node, EnergyChunkContainerSliceNode );
} );
<|endoftext|>


<commit_before>/*
 * Catalog web/REST frontend - search API
 *
 * Copyright 2014 Commons Machinery http://commonsmachinery.se/
 *
 * Distributed under an AGPL_v3 license, please see LICENSE in the top dir.
 */

'use strict';

var debug = require('debug')('catalog:frontend:api:search'); // jshint ignore:line

// External libs
var Promise = require('bluebird');

// Components
var search = require('../../../modules/search/search');

// Frontend libs
var respond = require('./respond');
var request = require('./request');
var uris = require('../uris');

var transformSearchResult = function(lookup, context) {
    if (lookup.object_type === 'core.Work') {
        var workId = lookup.object_id;
        var result;

        result = {
            href: uris.buildWorkURI(workId),
            uri: lookup.uri,
            text: lookup.text,
            property: lookup.property_type,
            score: lookup.score,
            distance: lookup.distance,
        };

        return result;
    }
    else {
        throw new Error('Unable to transform search result for %s', lookup.object_type);
    }
};

/* Return promise handler to transform search results for JSON responses.
 */
var transformResults = function(req) {
    return function(results) {
        var promiseStack = [];
        for (var i = 0; i < results.length; i++) {
            promiseStack.push(transformSearchResult(results[i], req.context));
        }
        return Promise.all(promiseStack);
    };
};

exports.lookupURI = function lookupURI(req, res, next) {
    search.lookupURI(req.query.uri,
                     {
                         context: req.query.context,
                         skip: request.getSkip(req),
                         limit: request.getLimit(req),
                     })
        .then(transformResults(req))
        .then(function(results) {
            respond.setPagingLinks(req, res, results);
            res.status(200).json(results);
        })
        .catch(function(err) {
            next(err);
        });
};

exports.lookupHash = function lookupHash(req, res, next) {
    search.lookupHash(req.query.hash,
                      {
                          context: req.query.context,
                          skip: request.getSkip(req),
                          limit: request.getLimit(req),
                      })
        .then(transformResults(req))
        .then(function(results) {
            respond.setPagingLinks(req, res, results);
            res.status(200).json(results);
        })
        .catch(function(err) {
            next(err);
        });
};
<commit_msg>Allow CORS requests for search results.<commit_after>/*
 * Catalog web/REST frontend - search API
 *
 * Copyright 2014 Commons Machinery http://commonsmachinery.se/
 *
 * Distributed under an AGPL_v3 license, please see LICENSE in the top dir.
 */

'use strict';

var debug = require('debug')('catalog:frontend:api:search'); // jshint ignore:line

// External libs
var Promise = require('bluebird');

// Components
var search = require('../../../modules/search/search');

// Frontend libs
var respond = require('./respond');
var request = require('./request');
var uris = require('../uris');

var transformSearchResult = function(lookup, context) {
    if (lookup.object_type === 'core.Work') {
        var workId = lookup.object_id;
        var result;

        result = {
            href: uris.buildWorkURI(workId),
            uri: lookup.uri,
            text: lookup.text,
            property: lookup.property_type,
            score: lookup.score,
            distance: lookup.distance,
        };

        return result;
    }
    else {
        throw new Error('Unable to transform search result for %s', lookup.object_type);
    }
};

/* Return promise handler to transform search results for JSON responses.
 */
var transformResults = function(req) {
    return function(results) {
        var promiseStack = [];
        for (var i = 0; i < results.length; i++) {
            promiseStack.push(transformSearchResult(results[i], req.context));
        }
        return Promise.all(promiseStack);
    };
};

exports.lookupURI = function lookupURI(req, res, next) {
    search.lookupURI(req.query.uri,
                     {
                         context: req.query.context,
                         skip: request.getSkip(req),
                         limit: request.getLimit(req),
                     })
        .then(transformResults(req))
        .then(function(results) {
            respond.setPagingLinks(req, res, results);
            // TODO: should we do this?
            // always allow CORS for search results
            res.set('Access-Control-Allow-Origin', '*');
            res.status(200).json(results);
        })
        .catch(function(err) {
            next(err);
        });
};

exports.lookupHash = function lookupHash(req, res, next) {
    search.lookupHash(req.query.hash,
                      {
                          context: req.query.context,
                          skip: request.getSkip(req),
                          limit: request.getLimit(req),
                      })
        .then(transformResults(req))
        .then(function(results) {
            respond.setPagingLinks(req, res, results);
            // TODO: should we do this?
            // always allow CORS for search results
            res.set('Access-Control-Allow-Origin', '*');
            res.status(200).json(results);
        })
        .catch(function(err) {
            next(err);
        });
};
<|endoftext|>


<commit_before>(function(){
	const CLIENT_ID ="02gUJC0hH2ct1EGOcYXQIzRFU91c72Ea";

	function get(url, callback){
		var d = new XMLHttpRequest;
		d.addEventListener("load", function(a) {
			callback(a.target.responseText)
		});
		d.open("GET", url);
		d.send();
	}

	function downloadInBufferArray(url, callback){
		var d = new XMLHttpRequest();
		d.responseType = "arraybuffer";
		d.addEventListener("load", function(a) {
			callback(d.response);
		});
		d.open("GET", url, true);
		d.send();
	}

	function download(file, name) {
	    var link = document.createElement('a');
	    link.download = name;
	    link.href = file;
	    link.click();
	}

	function fetchSound(trackInfo, callback){
		var url = "https://api.soundcloud.com/i1/tracks/"
			+ trackInfo.id
			+ "/streams?client_id="
			+ CLIENT_ID
			+ "&app_version=cc53575";
		get(url, function(data){
			var url = JSON.parse(data).http_mp3_128_url;
			downloadInBufferArray(url, function(blob){
				var result = {};
				result.info = trackInfo;
				result.data = blob;
				callback(result);
			});
		});

	}

	function fetchSoundFromUrl(url, callback){
		//fetch track id
		var encodedURL = encodeURI(url);
		get("https://api.soundcloud.com/resolve.json?url="
				+ encodedURL
				+ "&client_id="
				+ CLIENT_ID
			, function(data){
				var resolveData = JSON.parse(data);
				fetchSound(resolveData, callback);
		});
	}

	var buttonContainer = document.querySelector(".sc-button-group.sc-button-group-medium");
	if(buttonContainer === null){
		return;
	}
	var existingButtons = buttonContainer.childNodes;
	for(var i=0; i<existingButtons.length; i++){
		if(existingButtons[i].title === "Download"){
			return;
		}
	}

	var downloadButton = document.createElement("button");
	downloadButton.className = "sc-button sc-button-medium sc-button-responsive sc-button-download sc-button-klau";
	downloadButton.title = "Download";
	downloadButton.innerHTML = "Download";
	buttonContainer.appendChild(downloadButton);

	fetchSoundFromUrl(document.location.href, function(result){
		var writer = new ID3Writer(result.data);
		writer.setFrame('TIT2', result.info.title);
		writer.addTag();
		downloadButton.addEventListener('click', function(){
			console.log(result.info);
			var url = writer.getURL();
			download(url, result.info.title + ".mp3");
		});
	});

})();<commit_msg>add albumcover to file<commit_after>(function(){
	const CLIENT_ID ="02gUJC0hH2ct1EGOcYXQIzRFU91c72Ea";

	function get(url, callback){
		var d = new XMLHttpRequest;
		d.addEventListener("load", function(a) {
			callback(a.target.responseText)
		});
		d.open("GET", url);
		d.send();
	}

	function downloadInBufferArray(url, callback){
		var d = new XMLHttpRequest();
		d.responseType = "arraybuffer";
		d.addEventListener("load", function(a) {
			callback(d.response);
		});
		d.open("GET", url, true);
		d.send();
	}

	function download(file, name) {
	    var link = document.createElement('a');
	    link.download = name;
	    link.href = file;
	    link.click();
	}

	function fetchSound(trackInfo, callback){
		var url = "https://api.soundcloud.com/i1/tracks/"
			+ trackInfo.id
			+ "/streams?client_id="
			+ CLIENT_ID
			+ "&app_version=cc53575";
		get(url, function(data){
			var url = JSON.parse(data).http_mp3_128_url;
			downloadInBufferArray(url, function(blob){
				var result = {};
				result.info = trackInfo;
				result.data = blob;
				callback(result);
			});
		});

	}

	function fetchSoundFromUrl(url, callback){
		//fetch track id
		var encodedURL = encodeURI(url);
		get("https://api.soundcloud.com/resolve.json?url="
				+ encodedURL
				+ "&client_id="
				+ CLIENT_ID
			, function(data){
				var resolveData = JSON.parse(data);
				fetchSound(resolveData, callback);
		});
	}

	var buttonContainer = document.querySelector(".sc-button-group.sc-button-group-medium");
	if(buttonContainer === null){
		return;
	}
	var existingButtons = buttonContainer.childNodes;
	for(var i=0; i<existingButtons.length; i++){
		if(existingButtons[i].title === "Download"){
			return;
		}
	}

	var downloadButton = document.createElement("button");
	downloadButton.className = "sc-button sc-button-medium sc-button-responsive sc-button-download sc-button-klau";
	downloadButton.title = "Download";
	downloadButton.innerHTML = "Download";
	buttonContainer.appendChild(downloadButton);

	fetchSoundFromUrl(document.location.href, function(result){
		console.log(result.info);
		if(result.info.artwork_url !== undefined && result.info.artwork_url !== null){
			var coverUrl = result.info.artwork_url.replace("large", "original");
			downloadInBufferArray(coverUrl, function(cover){
				result.cover = cover;
				processTagsAndDownload(result);
			});
		}else{
			processTagsAndDownload(result);
		}

	});

	function processTagsAndDownload(result){
		var writer = new ID3Writer(result.data);
		writer.setFrame('TIT2', result.info.title);
		if(result.cover !== undefined){
			writer.setFrame('APIC', result.cover);
		}
		writer.addTag();
		downloadButton.addEventListener('click', function(){
			var url = writer.getURL();
			download(url, result.info.title + ".mp3");
		});
	}

})();<|endoftext|>


<commit_before>$(function () {
  $(".datetimepicker").datetimepicker({
    debug: false,
    format: "DD-MM-YYYY",
  });
});
<commit_msg>agora sim<commit_after>$(function () {
  $(".datetimepicker").datetimepicker({
    debug: false,
    format: "DD/MM/YYYY",
  });
});
<|endoftext|>


<commit_before>(function(global, undefined) {
  'use strict';

  global.ados_results = global.ados_results || null;

  var NETWORK = global.ADS_GLOBALS.network;
  var SITE = global.ADS_GLOBALS.site;
  var PLACEMENT_TYPES = {
    main: 5,
    sponsorship: 8,
  };

  function getConfig() {
    // Accessing `location.hash` directly does different
    // things in different browsers:
    //    > location.hash = "#%30";
    //    > location.hash === "#0"; // This is wrong, it should be "#%30"
    //    > true
    // see http://stackoverflow.com/a/1704842/704286
    var hash = location.href.split('#')[1] || '';

    // Firefox automatically encodes thing the fragment, but not other browsers.
    if (/^\{%22/.test(hash)) {
      hash = decodeURIComponent((hash));
    }

    try {
      return $.parseJSON(hash);
    } catch (e) {
      return {};
    }
  }

  var config = getConfig();
  var properties = config.properties || {};

  // Allows the yield manager to target a percentage of users
  // with specific SSPs.
  if (!properties.hasOwnProperty('percentage')) {
    properties.percentage = Math.round(Math.random());
  }

  // Display a random image in lieu of an ad for certain keywords.
  // This reduces the number of ad requests for low-fill targets.
  if (global.SKIP_AD_PROBABILITY && Math.random() <= global.SKIP_AD_PROBABILITY) {
    var keywords = config.keywords ? config.keywords : [];
    var skipAd = false;

    if (global.SKIP_AD_KEYWORDS && keywords) {
      for (var i = 0; i < keywords.length; i++) {
        if ($.inArray(keywords[i], global.SKIP_AD_KEYWORDS) !== -1) {
          skipAd = true;
          break;
        }
      }
    }

    if (skipAd) {
      var adframe = document.getElementById('main');
      var img = document.createElement('img');
      var randomImgIndex = Math.floor(Math.random() * global.SKIP_AD_IMAGES.length);
      img.height = 250;
      img.width = 300;
      img.src = global.SKIP_AD_IMAGES[randomImgIndex];

      adframe.appendChild(img);

      return;
    }
  }

  ados.run.push(function() {
    ados.isAsync = true;
    var placement = null;
    var request = {
      keywords: config.keywords,
      properties: config.properties,
      placement_types: [],
    };

    if (config.placements) {
      var placements = config.placements.split(',');

      for (var i = 0; i < placements.length; i++) {
        var kvp = placements[i].split(':');
        var type = kvp[0];
        var creative = kvp[1];

        placement = ados_add_placement(NETWORK, SITE, type, PLACEMENT_TYPES[type]);
        placement.setFlightCreativeId(creative);
        placement.setProperties(properties);

        request.placement_types.push(PLACEMENT_TYPES[type]);
      }
    } else {
      for (var type in PLACEMENT_TYPES) {
        placement = ados_add_placement(NETWORK, SITE, type, PLACEMENT_TYPES[type]);
        placement.setProperties(properties);

        request.placement_types.push(PLACEMENT_TYPES[type]);
      }
    }

    ados_setWriteResults(true);

    if (config.keywords) {
      ados_setKeywords(config.keywords);
    }

    r.frames.postMessage(global.parent, 'request.adzerk', request);

    ados_load();

    var load = setInterval(function() {
      if (global.ados_results) {
        clearInterval(load);

        for (var key in global.ados_ads) {
          if (!global.ados_ads.hasOwnProperty(key)) {
            continue;
          }

          r.frames.postMessage(global.parent, 'response.adzerk', {
            keywords: request.keywords,
            properties: request.properties,
            placement_types: request.placement_types,
            placement_name: 'banner_' + key,
            campaign_id: global.ados_ads[key].flight.campaign.id,
            flight_id: global.ados_ads[key].flight.id,
            creative_id: global.ados_ads[key].creative.id,
            ad_id: global.ados_ads[key].id,
            priority_id: global.ados_ads[key].flight.priorityId,
            ad_type: global.ados_ads[key].creative.adType,
          });
        }

        // Load companion
        if (global.ados_results.sponsorship) {
          if (global.postMessage) {
            global.parent.postMessage('ados.createAdFrame:sponsorship', config.origin);
          } else {
            iframe = document.createElement('iframe');
            iframe.src = '/static/createadframe.html';
            iframe.style.display = 'none';
            document.documentElement.appendChild(iframe);
          }
        }
      }
    }, 50);
  });
})(this);
<commit_msg>Ensure percentages are from 0-100<commit_after>(function(global, undefined) {
  'use strict';

  global.ados_results = global.ados_results || null;

  var NETWORK = global.ADS_GLOBALS.network;
  var SITE = global.ADS_GLOBALS.site;
  var PLACEMENT_TYPES = {
    main: 5,
    sponsorship: 8,
  };

  function getConfig() {
    // Accessing `location.hash` directly does different
    // things in different browsers:
    //    > location.hash = "#%30";
    //    > location.hash === "#0"; // This is wrong, it should be "#%30"
    //    > true
    // see http://stackoverflow.com/a/1704842/704286
    var hash = location.href.split('#')[1] || '';

    // Firefox automatically encodes thing the fragment, but not other browsers.
    if (/^\{%22/.test(hash)) {
      hash = decodeURIComponent((hash));
    }

    try {
      return $.parseJSON(hash);
    } catch (e) {
      return {};
    }
  }

  var config = getConfig();
  var properties = config.properties || {};

  // Allows the yield manager to target a percentage of users
  // with specific SSPs.
  if (!properties.hasOwnProperty('percentage')) {
    properties.percentage = Math.round(Math.random() * 100);
  }

  // Display a random image in lieu of an ad for certain keywords.
  // This reduces the number of ad requests for low-fill targets.
  if (global.SKIP_AD_PROBABILITY && Math.random() <= global.SKIP_AD_PROBABILITY) {
    var keywords = config.keywords ? config.keywords : [];
    var skipAd = false;

    if (global.SKIP_AD_KEYWORDS && keywords) {
      for (var i = 0; i < keywords.length; i++) {
        if ($.inArray(keywords[i], global.SKIP_AD_KEYWORDS) !== -1) {
          skipAd = true;
          break;
        }
      }
    }

    if (skipAd) {
      var adframe = document.getElementById('main');
      var img = document.createElement('img');
      var randomImgIndex = Math.floor(Math.random() * global.SKIP_AD_IMAGES.length);
      img.height = 250;
      img.width = 300;
      img.src = global.SKIP_AD_IMAGES[randomImgIndex];

      adframe.appendChild(img);

      return;
    }
  }

  ados.run.push(function() {
    ados.isAsync = true;
    var placement = null;
    var request = {
      keywords: config.keywords,
      properties: config.properties,
      placement_types: [],
    };

    if (config.placements) {
      var placements = config.placements.split(',');

      for (var i = 0; i < placements.length; i++) {
        var kvp = placements[i].split(':');
        var type = kvp[0];
        var creative = kvp[1];

        placement = ados_add_placement(NETWORK, SITE, type, PLACEMENT_TYPES[type]);
        placement.setFlightCreativeId(creative);
        placement.setProperties(properties);

        request.placement_types.push(PLACEMENT_TYPES[type]);
      }
    } else {
      for (var type in PLACEMENT_TYPES) {
        placement = ados_add_placement(NETWORK, SITE, type, PLACEMENT_TYPES[type]);
        placement.setProperties(properties);

        request.placement_types.push(PLACEMENT_TYPES[type]);
      }
    }

    ados_setWriteResults(true);

    if (config.keywords) {
      ados_setKeywords(config.keywords);
    }

    r.frames.postMessage(global.parent, 'request.adzerk', request);

    ados_load();

    var load = setInterval(function() {
      if (global.ados_results) {
        clearInterval(load);

        for (var key in global.ados_ads) {
          if (!global.ados_ads.hasOwnProperty(key)) {
            continue;
          }

          r.frames.postMessage(global.parent, 'response.adzerk', {
            keywords: request.keywords,
            properties: request.properties,
            placement_types: request.placement_types,
            placement_name: 'banner_' + key,
            campaign_id: global.ados_ads[key].flight.campaign.id,
            flight_id: global.ados_ads[key].flight.id,
            creative_id: global.ados_ads[key].creative.id,
            ad_id: global.ados_ads[key].id,
            priority_id: global.ados_ads[key].flight.priorityId,
            ad_type: global.ados_ads[key].creative.adType,
          });
        }

        // Load companion
        if (global.ados_results.sponsorship) {
          if (global.postMessage) {
            global.parent.postMessage('ados.createAdFrame:sponsorship', config.origin);
          } else {
            iframe = document.createElement('iframe');
            iframe.src = '/static/createadframe.html';
            iframe.style.display = 'none';
            document.documentElement.appendChild(iframe);
          }
        }
      }
    }, 50);
  });
})(this);
<|endoftext|>