<commit_before>var test_helper = require('../support/test_helper');

var assert = require('assert');
var RedisPool = require('redis-mpool');
var TemplateMaps = require('../../lib/cartodb/template_maps.js');
var PgConnection = require(__dirname + '/../../lib/cartodb/backends/pg_connection');
var MapConfigNamedLayersAdapter = require('../../lib/cartodb/models/mapconfig_named_layers_adapter');

var Step = require('step');
var _ = require('underscore');

// configure redis pool instance to use in tests
var redisPool = RedisPool(global.environment.redis);
var pgConnection = new PgConnection(require('cartodb-redis')({ pool: redisPool }));

var templateMaps = new TemplateMaps(redisPool, {
    max_user_templates: global.environment.maxUserTemplates
});

var mapConfigNamedLayersAdapter = new MapConfigNamedLayersAdapter(templateMaps);

var wadusSql = 'select 1 wadusLayer, null::geometry the_geom_webmercator';
var wadusLayer = {
    type: 'cartodb',
    options: {
        sql: wadusSql,
        cartocss: '#layer { marker-fill: black; }',
        cartocss_version: '2.3.0'
    }
};

var wadusTemplateSql = 'select 1 wadusTemplateLayer, null::geometry the_geom_webmercator';
var wadusTemplateLayer = {
    type: 'cartodb',
    options: {
        sql: wadusTemplateSql,
        cartocss: '#layer { marker-fill: <%= color %>; }',
        cartocss_version: '2.3.0'
    }
};

var wadusMapnikSql = 'select 1 wadusMapnikLayer, null::geometry the_geom_webmercator';
var wadusMapnikLayer = {
    type: 'mapnik',
    options: {
        sql: wadusMapnikSql,
        cartocss: '#layer { polygon-fill: <%= polygon_color %>; }',
        cartocss_version: '2.3.0'
    }
};

var username = 'localhost';

var templateName = 'valid_template';
var template = {
    version: '0.0.1',
    name: templateName,
    auth: {
        method: 'open'
    },
    "placeholders": {
        "color": {
            "type": "css_color",
            "default": "#cc3300"
        }
    },
    layergroup: {
        layers: [
            wadusTemplateLayer
        ]
    }
};

var multipleLayersTemplateName = 'multiple_valid_template';
var multipleLayersTemplate = {
    version: '0.0.1',
    name: multipleLayersTemplateName,
    auth: {
        method: 'token',
        valid_tokens: ['valid1', 'valid2']
    },
    "placeholders": {
        "polygon_color": {
            "type": "css_color",
            "default": "green"
        },
        "color": {
            "type": "css_color",
            "default": "red"
        }
    },
    layergroup: {
        layers: [
            wadusMapnikLayer,
            wadusTemplateLayer
        ]
    }
};

suite('named_layers datasources', function() {
    suiteSetup(function(done) {
        templateMaps.addTemplate(username, template, function(err) {
            if (err) {
                return done(err);
            }
            templateMaps.addTemplate(username, multipleLayersTemplate, done);
        });
    });

    function makeNamedMapLayerConfig(layers) {
        return {
            version: '1.3.0',
            layers: layers
        };
    }

    var simpleNamedLayer = {
        type: 'named',
        options: {
            name: templateName
        }
    };

    var multipleLayersNamedLayer = {
        type: 'named',
        options: {
            name: multipleLayersTemplateName,
            auth_tokens: ['valid2']
        }
    };

    var testScenarios = [
        {
            desc: 'without datasource for non-named layers',
            config: makeNamedMapLayerConfig([wadusLayer]),
            test: function(err, layers, datasource, done) {
                assert.ok(!err);
                assert.equal(layers.length, 1);

                assert.equal(layers[0].type, 'cartodb');
                assert.equal(layers[0].options.sql, wadusSql);
                assert.equal(datasource.getLayerDatasource(0), undefined);

                done();
            }
        },

        {
            desc: 'with datasource for the named layer but not for the normal',
            config: makeNamedMapLayerConfig([wadusLayer, simpleNamedLayer]),
            test: function(err, layers, datasource, done) {
                assert.ok(!err);
                assert.equal(layers.length, 2);

                assert.equal(layers[0].type, 'cartodb');
                assert.equal(layers[0].options.sql, wadusSql);
                assert.equal(datasource.getLayerDatasource(0), undefined);

                assert.equal(layers[1].type, 'cartodb');
                assert.equal(layers[1].options.sql, wadusTemplateSql);
                var layerDatasource = datasource.getLayerDatasource(1);
                assert.notEqual(layerDatasource, undefined);
                assert.ok(layerDatasource.user);

                done();
            }
        },

        {
            desc: 'with datasource for the multiple layers in the named but not for the normal',
            config: makeNamedMapLayerConfig([wadusLayer, multipleLayersNamedLayer]),
            test: function(err, layers, datasource, done) {
                assert.ok(!err);
                assert.equal(layers.length, 3);

                assert.equal(layers[0].type, 'cartodb');
                assert.equal(layers[0].options.sql, wadusSql);
                assert.equal(datasource.getLayerDatasource(0), undefined);

                assert.equal(layers[1].type, 'mapnik');
                assert.equal(layers[1].options.sql, wadusMapnikSql);
                var layerDatasource = datasource.getLayerDatasource(1);
                assert.notEqual(layerDatasource, undefined);
                assert.ok(layerDatasource.user);

                assert.equal(layers[2].type, 'cartodb');
                assert.equal(layers[2].options.sql, wadusTemplateSql);
                layerDatasource = datasource.getLayerDatasource(2);
                assert.notEqual(layerDatasource, undefined);
                assert.ok(layerDatasource.user);

                done();
            }
        },

        {
            desc: 'all with datasource because all are named',
            config: makeNamedMapLayerConfig([multipleLayersNamedLayer, simpleNamedLayer]),
            test: function(err, layers, datasource, done) {
                assert.ok(!err);
                assert.equal(layers.length, 3);

                assert.equal(layers[0].type, 'mapnik');
                assert.equal(layers[0].options.sql, wadusMapnikSql);
                var layerDatasource = datasource.getLayerDatasource(0);
                assert.notEqual(layerDatasource, undefined);
                assert.ok(layerDatasource.user);

                assert.equal(layers[1].type, 'cartodb');
                assert.equal(layers[1].options.sql, wadusTemplateSql);
                layerDatasource = datasource.getLayerDatasource(1);
                assert.notEqual(layerDatasource, undefined);
                assert.ok(layerDatasource.user);

                assert.equal(layers[2].type, 'cartodb');
                assert.equal(layers[2].options.sql, wadusTemplateSql);
                layerDatasource = datasource.getLayerDatasource(2);
                assert.notEqual(layerDatasource, undefined);
                assert.ok(layerDatasource.user);

                done();
            }
        },

        {
            desc: 'with a mix of datasource and no datasource depending if layers are named or not',
            config: makeNamedMapLayerConfig([simpleNamedLayer, multipleLayersNamedLayer, wadusLayer, simpleNamedLayer, wadusLayer, multipleLayersNamedLayer]),
            test: function(err, layers, datasource, done) {

                assert.ok(!err);
                assert.equal(layers.length, 8);

                assert.equal(layers[0].type, 'cartodb');
                assert.equal(layers[0].options.sql, wadusTemplateSql);
                var layerDatasource = datasource.getLayerDatasource(0);
                assert.notEqual(layerDatasource, undefined);
                assert.ok(layerDatasource.user);

                assert.equal(layers[1].type, 'mapnik');
                assert.equal(layers[1].options.sql, wadusMapnikSql);
                layerDatasource = datasource.getLayerDatasource(1);
                assert.notEqual(layerDatasource, undefined);
                assert.ok(layerDatasource.user);

                assert.equal(layers[2].type, 'cartodb');
                assert.equal(layers[2].options.sql, wadusTemplateSql);
                layerDatasource = datasource.getLayerDatasource(2);
                assert.notEqual(layerDatasource, undefined);
                assert.ok(layerDatasource.user);

                assert.equal(layers[3].type, 'cartodb');
                assert.equal(layers[3].options.sql, wadusSql);
                assert.equal(datasource.getLayerDatasource(3), undefined);

                assert.equal(layers[4].type, 'cartodb');
                assert.equal(layers[4].options.sql, wadusTemplateSql);
                layerDatasource = datasource.getLayerDatasource(4);
                assert.notEqual(layerDatasource, undefined);
                assert.ok(layerDatasource.user);

                assert.equal(layers[5].type, 'cartodb');
                assert.equal(layers[5].options.sql, wadusSql);
                assert.equal(datasource.getLayerDatasource(5), undefined);

                assert.equal(layers[6].type, 'mapnik');
                assert.equal(layers[6].options.sql, wadusMapnikSql);
                layerDatasource = datasource.getLayerDatasource(6);
                assert.notEqual(layerDatasource, undefined);
                assert.ok(layerDatasource.user);

                assert.equal(layers[7].type, 'cartodb');
                assert.equal(layers[7].options.sql, wadusTemplateSql);
                layerDatasource = datasource.getLayerDatasource(7);
                assert.notEqual(layerDatasource, undefined);
                assert.ok(layerDatasource.user);

                done();
            }
        }
    ];

    testScenarios.forEach(function(testScenario) {
        test('should return a list of layers ' + testScenario.desc, function(done) {
            mapConfigNamedLayersAdapter.getLayers(username, testScenario.config.layers, pgConnection, function(err, layers, datasource) {
                testScenario.test(err, layers, datasource, done);
            });
        });
    });

    suiteTeardown(function(done) {
        templateMaps.delTemplate(username, templateName, function(err) {
            if (err) {
                return done(err);
            }
            templateMaps.delTemplate(username, multipleLayersTemplateName, done);
        });
    });
});
<commit_msg>fix jshint<commit_after>require('../support/test_helper');

var assert = require('assert');
var RedisPool = require('redis-mpool');
var TemplateMaps = require('../../lib/cartodb/template_maps.js');
var PgConnection = require(__dirname + '/../../lib/cartodb/backends/pg_connection');
var MapConfigNamedLayersAdapter = require('../../lib/cartodb/models/mapconfig_named_layers_adapter');

// configure redis pool instance to use in tests
var redisPool = new RedisPool(global.environment.redis);
var pgConnection = new PgConnection(require('cartodb-redis')({ pool: redisPool }));

var templateMaps = new TemplateMaps(redisPool, {
    max_user_templates: global.environment.maxUserTemplates
});

var mapConfigNamedLayersAdapter = new MapConfigNamedLayersAdapter(templateMaps);

var wadusSql = 'select 1 wadusLayer, null::geometry the_geom_webmercator';
var wadusLayer = {
    type: 'cartodb',
    options: {
        sql: wadusSql,
        cartocss: '#layer { marker-fill: black; }',
        cartocss_version: '2.3.0'
    }
};

var wadusTemplateSql = 'select 1 wadusTemplateLayer, null::geometry the_geom_webmercator';
var wadusTemplateLayer = {
    type: 'cartodb',
    options: {
        sql: wadusTemplateSql,
        cartocss: '#layer { marker-fill: <%= color %>; }',
        cartocss_version: '2.3.0'
    }
};

var wadusMapnikSql = 'select 1 wadusMapnikLayer, null::geometry the_geom_webmercator';
var wadusMapnikLayer = {
    type: 'mapnik',
    options: {
        sql: wadusMapnikSql,
        cartocss: '#layer { polygon-fill: <%= polygon_color %>; }',
        cartocss_version: '2.3.0'
    }
};

var username = 'localhost';

var templateName = 'valid_template';
var template = {
    version: '0.0.1',
    name: templateName,
    auth: {
        method: 'open'
    },
    "placeholders": {
        "color": {
            "type": "css_color",
            "default": "#cc3300"
        }
    },
    layergroup: {
        layers: [
            wadusTemplateLayer
        ]
    }
};

var multipleLayersTemplateName = 'multiple_valid_template';
var multipleLayersTemplate = {
    version: '0.0.1',
    name: multipleLayersTemplateName,
    auth: {
        method: 'token',
        valid_tokens: ['valid1', 'valid2']
    },
    "placeholders": {
        "polygon_color": {
            "type": "css_color",
            "default": "green"
        },
        "color": {
            "type": "css_color",
            "default": "red"
        }
    },
    layergroup: {
        layers: [
            wadusMapnikLayer,
            wadusTemplateLayer
        ]
    }
};

describe('named_layers datasources', function() {
    before(function(done) {
        templateMaps.addTemplate(username, template, function(err) {
            if (err) {
                return done(err);
            }
            templateMaps.addTemplate(username, multipleLayersTemplate, done);
        });
    });

    function makeNamedMapLayerConfig(layers) {
        return {
            version: '1.3.0',
            layers: layers
        };
    }

    var simpleNamedLayer = {
        type: 'named',
        options: {
            name: templateName
        }
    };

    var multipleLayersNamedLayer = {
        type: 'named',
        options: {
            name: multipleLayersTemplateName,
            auth_tokens: ['valid2']
        }
    };

    var testScenarios = [
        {
            desc: 'without datasource for non-named layers',
            config: makeNamedMapLayerConfig([wadusLayer]),
            test: function(err, layers, datasource, done) {
                assert.ok(!err);
                assert.equal(layers.length, 1);

                assert.equal(layers[0].type, 'cartodb');
                assert.equal(layers[0].options.sql, wadusSql);
                assert.equal(datasource.getLayerDatasource(0), undefined);

                done();
            }
        },

        {
            desc: 'with datasource for the named layer but not for the normal',
            config: makeNamedMapLayerConfig([wadusLayer, simpleNamedLayer]),
            test: function(err, layers, datasource, done) {
                assert.ok(!err);
                assert.equal(layers.length, 2);

                assert.equal(layers[0].type, 'cartodb');
                assert.equal(layers[0].options.sql, wadusSql);
                assert.equal(datasource.getLayerDatasource(0), undefined);

                assert.equal(layers[1].type, 'cartodb');
                assert.equal(layers[1].options.sql, wadusTemplateSql);
                var layerDatasource = datasource.getLayerDatasource(1);
                assert.notEqual(layerDatasource, undefined);
                assert.ok(layerDatasource.user);

                done();
            }
        },

        {
            desc: 'with datasource for the multiple layers in the named but not for the normal',
            config: makeNamedMapLayerConfig([wadusLayer, multipleLayersNamedLayer]),
            test: function(err, layers, datasource, done) {
                assert.ok(!err);
                assert.equal(layers.length, 3);

                assert.equal(layers[0].type, 'cartodb');
                assert.equal(layers[0].options.sql, wadusSql);
                assert.equal(datasource.getLayerDatasource(0), undefined);

                assert.equal(layers[1].type, 'mapnik');
                assert.equal(layers[1].options.sql, wadusMapnikSql);
                var layerDatasource = datasource.getLayerDatasource(1);
                assert.notEqual(layerDatasource, undefined);
                assert.ok(layerDatasource.user);

                assert.equal(layers[2].type, 'cartodb');
                assert.equal(layers[2].options.sql, wadusTemplateSql);
                layerDatasource = datasource.getLayerDatasource(2);
                assert.notEqual(layerDatasource, undefined);
                assert.ok(layerDatasource.user);

                done();
            }
        },

        {
            desc: 'all with datasource because all are named',
            config: makeNamedMapLayerConfig([multipleLayersNamedLayer, simpleNamedLayer]),
            test: function(err, layers, datasource, done) {
                assert.ok(!err);
                assert.equal(layers.length, 3);

                assert.equal(layers[0].type, 'mapnik');
                assert.equal(layers[0].options.sql, wadusMapnikSql);
                var layerDatasource = datasource.getLayerDatasource(0);
                assert.notEqual(layerDatasource, undefined);
                assert.ok(layerDatasource.user);

                assert.equal(layers[1].type, 'cartodb');
                assert.equal(layers[1].options.sql, wadusTemplateSql);
                layerDatasource = datasource.getLayerDatasource(1);
                assert.notEqual(layerDatasource, undefined);
                assert.ok(layerDatasource.user);

                assert.equal(layers[2].type, 'cartodb');
                assert.equal(layers[2].options.sql, wadusTemplateSql);
                layerDatasource = datasource.getLayerDatasource(2);
                assert.notEqual(layerDatasource, undefined);
                assert.ok(layerDatasource.user);

                done();
            }
        },

        {
            desc: 'with a mix of datasource and no datasource depending if layers are named or not',
            config: makeNamedMapLayerConfig([
                simpleNamedLayer,
                multipleLayersNamedLayer,
                wadusLayer,
                simpleNamedLayer,
                wadusLayer,
                multipleLayersNamedLayer
            ]),
            test: function(err, layers, datasource, done) {

                assert.ok(!err);
                assert.equal(layers.length, 8);

                assert.equal(layers[0].type, 'cartodb');
                assert.equal(layers[0].options.sql, wadusTemplateSql);
                var layerDatasource = datasource.getLayerDatasource(0);
                assert.notEqual(layerDatasource, undefined);
                assert.ok(layerDatasource.user);

                assert.equal(layers[1].type, 'mapnik');
                assert.equal(layers[1].options.sql, wadusMapnikSql);
                layerDatasource = datasource.getLayerDatasource(1);
                assert.notEqual(layerDatasource, undefined);
                assert.ok(layerDatasource.user);

                assert.equal(layers[2].type, 'cartodb');
                assert.equal(layers[2].options.sql, wadusTemplateSql);
                layerDatasource = datasource.getLayerDatasource(2);
                assert.notEqual(layerDatasource, undefined);
                assert.ok(layerDatasource.user);

                assert.equal(layers[3].type, 'cartodb');
                assert.equal(layers[3].options.sql, wadusSql);
                assert.equal(datasource.getLayerDatasource(3), undefined);

                assert.equal(layers[4].type, 'cartodb');
                assert.equal(layers[4].options.sql, wadusTemplateSql);
                layerDatasource = datasource.getLayerDatasource(4);
                assert.notEqual(layerDatasource, undefined);
                assert.ok(layerDatasource.user);

                assert.equal(layers[5].type, 'cartodb');
                assert.equal(layers[5].options.sql, wadusSql);
                assert.equal(datasource.getLayerDatasource(5), undefined);

                assert.equal(layers[6].type, 'mapnik');
                assert.equal(layers[6].options.sql, wadusMapnikSql);
                layerDatasource = datasource.getLayerDatasource(6);
                assert.notEqual(layerDatasource, undefined);
                assert.ok(layerDatasource.user);

                assert.equal(layers[7].type, 'cartodb');
                assert.equal(layers[7].options.sql, wadusTemplateSql);
                layerDatasource = datasource.getLayerDatasource(7);
                assert.notEqual(layerDatasource, undefined);
                assert.ok(layerDatasource.user);

                done();
            }
        }
    ];

    testScenarios.forEach(function(testScenario) {
        it('should return a list of layers ' + testScenario.desc, function(done) {
            mapConfigNamedLayersAdapter.getLayers(username, testScenario.config.layers, pgConnection,
                function(err, layers, datasource) {
                    testScenario.test(err, layers, datasource, done);
                }
            );
        });
    });

    after(function(done) {
        templateMaps.delTemplate(username, templateName, function(err) {
            if (err) {
                return done(err);
            }
            templateMaps.delTemplate(username, multipleLayersTemplateName, done);
        });
    });
});
<|endoftext|>


<commit_before>var map;
var hilites;
var pu_layer;
var markers;
var selectFeatureControl;
var keyboardControl;
var selectGeographyControl;

function getCfFields() {
    // Find the set of conservation features represented in ALL of the selected planning units.
    if (pu_layer.selectedFeatures.length >= 1) {
        var tmpList = pu_layer.selectedFeatures[0].attributes.cf_fields;
        $.each( pu_layer.selectedFeatures, function(idx, feat) {
            fieldList = feat.attributes.cf_fields;
            tmpList = tmpList.intersect(fieldList);
        });
    } else {
        return [];
    };
    return tmpList;
}

function getCostFields() {
    // Find the set of costs represented in ALL of the selected planning units.
    if (pu_layer.selectedFeatures.length >= 1) {
        var tmpList = pu_layer.selectedFeatures[0].attributes.cost_fields;
        $.each( pu_layer.selectedFeatures, function(idx, feat) {
            fieldList = feat.attributes.cost_fields;
            tmpList = tmpList.intersect(fieldList);
        });
    } else {
        return [];
    };
    return tmpList;
}

function init_map() {
    map = new OpenLayers.Map({
        div: "map",
        projection: "EPSG:900913",
        displayProjection: "EPSG:4326",
        controls: [
            new OpenLayers.Control.Navigation(),
            new OpenLayers.Control.Zoom(),
            new OpenLayers.Control.LayerSwitcher({
                'div': OpenLayers.Util.getElement('layerswitcher')
            })
        ],
        numZoomLevels: 18
    });

    markers = new OpenLayers.Layer.Markers( "Markers" );
    //var lonLat = new OpenLayers.LonLat( -14277165.0, 7002116.0 );
    //markers.addMarker(new OpenLayers.Marker(lonLat));

    var osm = new OpenLayers.Layer.OSM();

    var esri_physical = new OpenLayers.Layer.XYZ( "ESRI World Physical Map",
        "http://server.arcgisonline.com/ArcGIS/rest/services/World_Physical_Map/MapServer/tile/${z}/${y}/${x}",
        {sphericalMercator: true}
    );

    var esri_shade = new OpenLayers.Layer.XYZ( "ESRI Shaded Relief Map",
        "http://server.arcgisonline.com/ArcGIS/rest/services/World_Shaded_Relief/MapServer/tile/${z}/${y}/${x}",
        {sphericalMercator: true}
    );

    var esri_topo = new OpenLayers.Layer.XYZ( "ESRI World Topo Map",
        "http://server.arcgisonline.com/ArcGIS/rest/services/World_Topo_Map/MapServer/tile/${z}/${y}/${x}",
        {sphericalMercator: true}
    );

    var pu_utfgrid = new OpenLayers.Layer.UTFGrid({
         url: "/tiles/utfgrid/${z}/${x}/${y}.json", //dynamic, use once mapnik is figured out
         //url: "/media/tiles/planning_units/${z}/${x}/${y}.json",
         utfgridResolution: 4,
         sphericalMercator: true,
         displayInLayerSwitcher: false
        }
    );
var pu_tiles = new OpenLayers.Layer.OSM( "Planning Units",
        "/tiles/planning_units/${z}/${x}/${y}.png",
        {
         sphericalMercator: true,
         isBaseLayer: false
        }
    );

    var google_terrain = new OpenLayers.Layer.Google(
        "Google Terrain",
        {type: google.maps.MapTypeId.TERRAIN, opacity: 0.6}
    );

    var myStyles = new OpenLayers.StyleMap({
        "default": new OpenLayers.Style({
            display: "none",  /* needs to be set temporarily to true for selection to work */
            strokeWidth: 0,
            fillOpacity: 0
        }),
        "select": new OpenLayers.Style({
            display: true,
            strokeWidth: 0,
            fillColor: "#ffff00",
            fillOpacity: 0.5,
            graphicZIndex: 2
        }),
        "select_geography": new OpenLayers.Style({
            display: true,
            fillColor: "#777777",
            fillOpacity: 0.5
        })
    });

    // allow testing of specific renderers via "?renderer=Canvas", etc
    var renderer = OpenLayers.Util.getParameters(window.location.href).renderer;
    renderer = (renderer) ? [renderer] : OpenLayers.Layer.Vector.prototype.renderers;
    pu_layer = new OpenLayers.Layer.Vector("Planning Units", {
        styleMap: myStyles,
        renderers: renderer,
        displayInLayerSwitcher: false
    });

    var url = "/seak/planning_units.geojson";
    var jqxhr = $.ajax({
        url: url,
        cache: true,
        dataType: 'json',
        success: function(data) {
            var gformat = new OpenLayers.Format.GeoJSON();
            try {
                var feats = gformat.read(data);
                pu_layer.addFeatures(feats);
            } catch(err) {
                console.log(err.message);
                app.scenarios.viewModel.planningUnitsLoadError(true);
            }
        }
    })
    .error(function() { app.scenarios.viewModel.planningUnitsLoadError(true); })
    .complete(function() { app.scenarios.viewModel.planningUnitsLoadComplete(true); });

    map.addLayers([esri_shade, esri_topo, esri_physical, osm, google_terrain, pu_layer, pu_tiles, pu_utfgrid, markers]);

    selectFeatureControl = new OpenLayers.Control.SelectFeature(
        pu_layer,
        {
            multiple: true
        }
    );

    var geographySelectCallback = function(){
        $('#geographySelectionCount').html(pu_layer.selectedFeatures.length);
    };

    selectGeographyControl = new OpenLayers.Control.SelectFeature(
        pu_layer,
        {
            clickout: true,
            toggle: false,
            multiple: true,
            hover: false,
            toggleKey: "ctrlKey", // ctrl key removes from selection
            multipleKey: "shiftKey", // shift key adds to selection
            renderIntent: "select_geography",
            box: true,
            onSelect: geographySelectCallback,
            onUnselect: geographySelectCallback
        }
    );

    keyboardControl = new OpenLayers.Control.KeyboardDefaults();

    selectFeatureControl.deactivate();
    keyboardControl.deactivate();
    selectGeographyControl.deactivate();
    map.addControls([selectFeatureControl, selectGeographyControl, keyboardControl]);

    var url = "/seak/field_lookup.json";
    var fieldLookup;
    var jqxhr = $.ajax({
        url: url,
        cache: true,
        dataType: 'json',
        success: function(data) { fieldLookup = data; }
    })
    .error(function() { fieldLookup = null; })

    var callback = function(infoLookup) {
        var msg = "";
        if (infoLookup) {
            var info;
            for (var idx in infoLookup) {
                info = infoLookup[idx];
                if (info && info.data) {
                    msg = "
";
                    $.each(info.data, function(idx, val) {
                        if (val >= 0) { // Assume negative is null
                            try {
                                msg += "
"+ fieldLookup[idx] + "	" + val.toPrecision(6) + "
";
                            } catch (err) {
                                msg += "
"+ idx + "	" + val + "
";
                            }

                        }
                    });
                    msg += "
";
                }
            }
        }
        document.getElementById("info").innerHTML = msg;
    };

    var ctl = new OpenLayers.Control.UTFGrid({
        callback: callback,
        handlerMode: "click"
    });

    map.addControl(ctl);

    map.setCenter(new OpenLayers.LonLat(-15400000, 6700000), 4);
}
<commit_msg>control miz/max zoom levels, fixes #20<commit_after>var map;
var hilites;
var pu_layer;
var markers;
var selectFeatureControl;
var keyboardControl;
var selectGeographyControl;

function getCfFields() {
    // Find the set of conservation features represented in ALL of the selected planning units.
    if (pu_layer.selectedFeatures.length >= 1) {
        var tmpList = pu_layer.selectedFeatures[0].attributes.cf_fields;
        $.each( pu_layer.selectedFeatures, function(idx, feat) {
            fieldList = feat.attributes.cf_fields;
            tmpList = tmpList.intersect(fieldList);
        });
    } else {
        return [];
    };
    return tmpList;
}

function getCostFields() {
    // Find the set of costs represented in ALL of the selected planning units.
    if (pu_layer.selectedFeatures.length >= 1) {
        var tmpList = pu_layer.selectedFeatures[0].attributes.cost_fields;
        $.each( pu_layer.selectedFeatures, function(idx, feat) {
            fieldList = feat.attributes.cost_fields;
            tmpList = tmpList.intersect(fieldList);
        });
    } else {
        return [];
    };
    return tmpList;
}

function init_map() {
    map = new OpenLayers.Map({
        div: "map",
        projection: "EPSG:900913",
        displayProjection: "EPSG:4326",
        controls: [
            new OpenLayers.Control.Navigation(),
            new OpenLayers.Control.Zoom(),
            new OpenLayers.Control.LayerSwitcher({
                'div': OpenLayers.Util.getElement('layerswitcher')
            })
        ],
        minZoomLevel: 3,
        restrictedExtent: new OpenLayers.Bounds(-19140016, 2626698, -10262137, 11307047),
        numZoomLevels: 6
    });

    markers = new OpenLayers.Layer.Markers( "Markers", {displayInLayerSwitcher: false});

    var osm = new OpenLayers.Layer.OSM();

    var esri_physical = new OpenLayers.Layer.XYZ( "ESRI World Physical Map",
        "http://server.arcgisonline.com/ArcGIS/rest/services/World_Physical_Map/MapServer/tile/${z}/${y}/${x}",
        {sphericalMercator: true}
    );

    var esri_shade = new OpenLayers.Layer.XYZ( "ESRI Shaded Relief Map",
        "http://server.arcgisonline.com/ArcGIS/rest/services/World_Shaded_Relief/MapServer/tile/${z}/${y}/${x}",
        {sphericalMercator: true}
    );

    var esri_topo = new OpenLayers.Layer.XYZ( "ESRI World Topo Map",
        "http://server.arcgisonline.com/ArcGIS/rest/services/World_Topo_Map/MapServer/tile/${z}/${y}/${x}",
        {sphericalMercator: true}
    );

    var pu_utfgrid = new OpenLayers.Layer.UTFGrid({
         url: "/tiles/utfgrid/${z}/${x}/${y}.json", //dynamic, use once mapnik is figured out
         //url: "/media/tiles/planning_units/${z}/${x}/${y}.json",
         utfgridResolution: 4,
         sphericalMercator: true,
         displayInLayerSwitcher: false
        }
    );

    var pu_tiles = new OpenLayers.Layer.OSM( "Planning Units",
        "/tiles/planning_units/${z}/${x}/${y}.png",
        {
         sphericalMercator: true,
         isBaseLayer: false
        }
    );

    var google_terrain = new OpenLayers.Layer.Google(
        "Google Terrain",
        {type: google.maps.MapTypeId.TERRAIN, opacity: 0.6}
    );

    var myStyles = new OpenLayers.StyleMap({
        "default": new OpenLayers.Style({
            display: "none",  /* needs to be set temporarily to true for selection to work */
            strokeWidth: 0,
            fillOpacity: 0
        }),
        "select": new OpenLayers.Style({
            display: true,
            strokeWidth: 0,
            fillColor: "#ffff00",
            fillOpacity: 0.5,
            graphicZIndex: 2
        }),
        "select_geography": new OpenLayers.Style({
            display: true,
            fillColor: "#777777",
            fillOpacity: 0.5
        })
    });

    // allow testing of specific renderers via "?renderer=Canvas", etc
    var renderer = OpenLayers.Util.getParameters(window.location.href).renderer;
    renderer = (renderer) ? [renderer] : OpenLayers.Layer.Vector.prototype.renderers;
    pu_layer = new OpenLayers.Layer.Vector("Planning Units", {
        styleMap: myStyles,
        renderers: renderer,
        displayInLayerSwitcher: false
    });

    var url = "/seak/planning_units.geojson";
    var jqxhr = $.ajax({
        url: url,
        cache: true,
        dataType: 'json',
        success: function(data) {
            var gformat = new OpenLayers.Format.GeoJSON();
            try {
                var feats = gformat.read(data);
                pu_layer.addFeatures(feats);
            } catch(err) {
                console.log(err.message);
                app.scenarios.viewModel.planningUnitsLoadError(true);
            }
        }
    })
    .error(function() { app.scenarios.viewModel.planningUnitsLoadError(true); })
    .complete(function() { app.scenarios.viewModel.planningUnitsLoadComplete(true); });

    map.addLayers([esri_shade, esri_topo, esri_physical, osm, google_terrain, pu_layer, pu_tiles, pu_utfgrid, markers]);

    map.isValidZoomLevel = function(zoomLevel) {
        // Why is this even necessary OpenLayers?.. grrr
        // http://stackoverflow.com/questions/4240610/min-max-zoom-level-in-openlayers
        return ( (zoomLevel != null) &&
            (zoomLevel >= this.minZoomLevel) &&
            (zoomLevel < this.minZoomLevel + this.numZoomLevels));
    }

    selectFeatureControl = new OpenLayers.Control.SelectFeature(
        pu_layer,
        {
            multiple: true
        }
    );

    var geographySelectCallback = function(){
        $('#geographySelectionCount').html(pu_layer.selectedFeatures.length);
    };

    selectGeographyControl = new OpenLayers.Control.SelectFeature(
        pu_layer,
        {
            clickout: true,
            toggle: false,
            multiple: true,
            hover: false,
            toggleKey: "ctrlKey", // ctrl key removes from selection
            multipleKey: "shiftKey", // shift key adds to selection
            renderIntent: "select_geography",
            box: true,
            onSelect: geographySelectCallback,
            onUnselect: geographySelectCallback
        }
    );

    keyboardControl = new OpenLayers.Control.KeyboardDefaults();

    selectFeatureControl.deactivate();
    keyboardControl.deactivate();
    selectGeographyControl.deactivate();
    map.addControls([selectFeatureControl, selectGeographyControl, keyboardControl]);

    var url = "/seak/field_lookup.json";
    var fieldLookup;
    var jqxhr = $.ajax({
        url: url,
        cache: true,
        dataType: 'json',
        success: function(data) { fieldLookup = data; }
    })
    .error(function() { fieldLookup = null; })

    var callback = function(infoLookup) {
        var msg = "";
        if (infoLookup) {
            var info;
            for (var idx in infoLookup) {
                info = infoLookup[idx];
                if (info && info.data) {
                    msg = "
";
                    $.each(info.data, function(idx, val) {
                        if (val >= 0) { // Assume negative is null
                            try {
                                msg += "
"+ fieldLookup[idx] + "	" + val.toPrecision(6) + "
";
                            } catch (err) {
                                msg += "
"+ idx + "	" + val + "
";
                            }

                        }
                    });
                    msg += "
";
                }
            }
        }
        document.getElementById("info").innerHTML = msg;
    };

    var ctl = new OpenLayers.Control.UTFGrid({
        callback: callback,
        handlerMode: "click"
    });

    map.addControl(ctl);

    map.setCenter(new OpenLayers.LonLat(-15400000, 6700000), 4);
}
<|endoftext|>


<commit_before>var Lines=require('../lines.js');
var LodShape=require('./lodshape.js');

var Gasket=function(elementIndexBits,hasReflections,hasColorsPerVertex,hasColorsPerFace,colorAttrs,lod){
	LodShape.apply(this,arguments);
};
Gasket.prototype=Object.create(LodShape.prototype);
Gasket.prototype.constructor=Gasket;
Gasket.prototype.getDistinctVertexCount=function(lodSymbol){
	return "(Math.pow(3,"+lodSymbol+"+1)+3)/2";
};
Gasket.prototype.getFaceVertexCount=function(lodSymbol){
	return "Math.pow(3,"+lodSymbol+")*3";
};
Gasket.prototype.getTotalVertexCount=function(lodSymbol){
	return "Math.pow(3,"+lodSymbol+")*3";
};
Gasket.prototype.writeStoreShape=function(){
	var writePushVertex=function(){
		var lines=new Lines;
		lines.a(
			"vertices[nextIndexIntoVertices++]=p[0];",
			"vertices[nextIndexIntoVertices++]=p[1];"
		);
		if (this.hasColorsPerVertex || this.hasColorsPerFace) {
			if (!this.usesElements() || this.hasColorsPerFace) {
				lines.a(
					"var c=colors[nextIndexIntoColors];"
				);
			} else {
				lines.a(
					"var c=colors[nextElement%colors.length];"
				);
			}
			// TODO more than 3 color components
			/*
			lines.a(
				"vertices[nextIndexIntoVertices++]=c[0];",
				"vertices[nextIndexIntoVertices++]=c[1];",
				"vertices[nextIndexIntoVertices++]=c[2];"
			);
			*/
			lines.a(
				"c.forEach(function(cc){",
				"	vertices[nextIndexIntoVertices++]=cc;",
				"});"
			);
		}
		if (this.usesElements()) {
			lines.a(
				"return nextElement++;"
			);
		} else if (this.hasColorsPerVertex) {
			lines.a(
				"nextIndexIntoColors=(nextIndexIntoColors+1)%colors.length;"
			);
		}
		return lines.wrap(
			"function pushVertex(p) {",
			"}"
		);
	}.bind(this);
	var writeTriangle=function(){
		var lines=new Lines;
		lines.a(
			"if (depth<=0) {"
		);
		if (!this.usesElements()) {
			if (this.hasColorsPerFace) {
				lines.a(
					"	pushVertex(p0);",
					"	pushVertex(p1);",
					"	pushVertex(p2);",
					"	nextIndexIntoColors=(nextIndexIntoColors+1)%colors.length;"
				);
			} else {
				lines.a(
					"	pushVertex(p0);",
					"	pushVertex(p1);",
					"	pushVertex(p2);"
				);
			}
		} else {
			if (this.hasColorsPerFace) {
				lines.a(
					"	pushElement(pushVertex(p0));",
					"	pushElement(pushVertex(p1));",
					"	pushElement(pushVertex(p2));",
					"	nextIndexIntoColors=(nextIndexIntoColors+1)%colors.length;"
				);
			} else {
				lines.a(
					"	if (es[0]===null) es[0]=pushVertex(p0);",
					"	if (es[1]===null) es[1]=pushVertex(p1);",
					"	if (es[2]===null) es[2]=pushVertex(p2);",
					"	pushElement(es[0]);",
					"	pushElement(es[1]);",
					"	pushElement(es[2]);",
					"	return es;"
				);
			}
		}
		lines.a(
			"} else {",
			"	var p01=mix(p0,p1);",
			"	var p12=mix(p1,p2);",
			"	var p20=mix(p2,p0);"
		);
		if (!this.usesElements()) {
			lines.a(
				"	triangle(depth-1,p0,p01,p20);",
				"	triangle(depth-1,p1,p12,p01);",
				"	triangle(depth-1,p2,p20,p12);"
			);
		} else if (this.hasColorsPerFace) {
			lines.a(
				"	var es0=triangle(depth-1,p0,p01,p20);",
				"	var es1=triangle(depth-1,p1,p12,p01);",
				"	var es2=triangle(depth-1,p2,p20,p12);"
			);
		} else {
			lines.a(
				"	var es0=triangle(depth-1,p0,p01,p20,[es[0],null,null]);",
				"	var es1=triangle(depth-1,p1,p12,p01,[es[1],null,es0[1]]);",
				"	var es2=triangle(depth-1,p2,p20,p12,[es[2],es0[2],es1[1]]);",
				"	return [es0[0],es1[0],es2[0]];"
			);
		}
		lines.a(
			"}"
		);
		if (!this.usesElements() || this.hasColorsPerFace) {
			return lines.wrap(
				"function triangle(depth,p0,p1,p2) {",
				"}"
			);
		} else {
			return lines.wrap(
				"function triangle(depth,p0,p1,p2,es) {",
				"}"
			);
		}
	}.bind(this);
	var writeInitialTriangleCall=function(){
		var lines=new Lines;
		lines.a(
			"shapeLod,",
			"[-Math.sin(0/3*Math.PI),Math.cos(0/3*Math.PI)],",
			"[-Math.sin(2/3*Math.PI),Math.cos(2/3*Math.PI)],",
			"[-Math.sin(4/3*Math.PI),Math.cos(4/3*Math.PI)]"
		);
		if (this.usesElements() && this.hasColorsPerFace) {
			lines.t(
				",","[null,null,null]"
			);
		}
		return lines.wrap(
			"triangle(",
			");"
		);
	}.bind(this);
	var lines=new Lines;
	lines.a(
		"var nextIndexIntoVertices=0;"
	);
	if (this.usesElements()) {
		lines.a(
			"var nextIndexIntoElements=0;",
			"var nextElement=0;"
		);
	}
	if (this.hasColorsPerVertex || this.hasColorsPerFace) {
		lines.a("// p = position, c = color, e = element, es = elements");
		lines.a(this.writeColorData());
		if (!this.usesElements() || this.hasColorsPerFace) {
			lines.a(
				"var nextIndexIntoColors=0;"
			);
		}
	} else {
		lines.a(
			"// p = position, e = element, es = elements"
		);
	}
	lines.a(writePushVertex());
	if (this.usesElements()) {
		lines.a(
			"function pushElement(e) {",
			"	elements[nextIndexIntoElements++]=e;",
			"}"
		);
	}
	lines.a(
		"function mix(pa,pb) {",
		"	return [",
		"		(pa[0]+pb[0])/2,",
		"		(pa[1]+pb[1])/2,",
		"	];",
		"}",
		writeTriangle(),
		writeInitialTriangleCall()
	);
	return lines;
}

module.exports=Gasket;
<commit_msg>fix: gasket colors per vertex when using elements<commit_after>var Lines=require('../lines.js');
var LodShape=require('./lodshape.js');

var Gasket=function(elementIndexBits,hasReflections,hasColorsPerVertex,hasColorsPerFace,colorAttrs,lod){
	LodShape.apply(this,arguments);
};
Gasket.prototype=Object.create(LodShape.prototype);
Gasket.prototype.constructor=Gasket;
Gasket.prototype.getDistinctVertexCount=function(lodSymbol){
	return "(Math.pow(3,"+lodSymbol+"+1)+3)/2";
};
Gasket.prototype.getFaceVertexCount=function(lodSymbol){
	return "Math.pow(3,"+lodSymbol+")*3";
};
Gasket.prototype.getTotalVertexCount=function(lodSymbol){
	return "Math.pow(3,"+lodSymbol+")*3";
};
Gasket.prototype.writeStoreShape=function(){
	var writePushVertex=function(){
		var lines=new Lines;
		lines.a(
			"vertices[nextIndexIntoVertices++]=p[0];",
			"vertices[nextIndexIntoVertices++]=p[1];"
		);
		if (this.hasColorsPerVertex || this.hasColorsPerFace) {
			if (!this.usesElements() || this.hasColorsPerFace) {
				lines.a(
					"colors[nextIndexIntoColors]"
				);
			} else {
				lines.a(
					"colors[nextElement%colors.length]"
				);
			}
			lines.t(
				".forEach(function(c){",
				"	vertices[nextIndexIntoVertices++]=c;",
				"});"
			);
		}
		if (this.usesElements()) {
			lines.a(
				"return nextElement++;"
			);
		} else if (this.hasColorsPerVertex) {
			lines.a(
				"nextIndexIntoColors=(nextIndexIntoColors+1)%colors.length;"
			);
		}
		return lines.wrap(
			"function pushVertex(p) {",
			"}"
		);
	}.bind(this);
	var writeTriangle=function(){
		var lines=new Lines;
		lines.a(
			"if (depth<=0) {"
		);
		if (!this.usesElements()) {
			if (this.hasColorsPerFace) {
				lines.a(
					"	pushVertex(p0);",
					"	pushVertex(p1);",
					"	pushVertex(p2);",
					"	nextIndexIntoColors=(nextIndexIntoColors+1)%colors.length;"
				);
			} else {
				lines.a(
					"	pushVertex(p0);",
					"	pushVertex(p1);",
					"	pushVertex(p2);"
				);
			}
		} else {
			if (this.hasColorsPerFace) {
				lines.a(
					"	pushElement(pushVertex(p0));",
					"	pushElement(pushVertex(p1));",
					"	pushElement(pushVertex(p2));",
					"	nextIndexIntoColors=(nextIndexIntoColors+1)%colors.length;"
				);
			} else {
				lines.a(
					"	if (es[0]===null) es[0]=pushVertex(p0);",
					"	if (es[1]===null) es[1]=pushVertex(p1);",
					"	if (es[2]===null) es[2]=pushVertex(p2);",
					"	pushElement(es[0]);",
					"	pushElement(es[1]);",
					"	pushElement(es[2]);",
					"	return es;"
				);
			}
		}
		lines.a(
			"} else {",
			"	var p01=mix(p0,p1);",
			"	var p12=mix(p1,p2);",
			"	var p20=mix(p2,p0);"
		);
		if (!this.usesElements()) {
			lines.a(
				"	triangle(depth-1,p0,p01,p20);",
				"	triangle(depth-1,p1,p12,p01);",
				"	triangle(depth-1,p2,p20,p12);"
			);
		} else if (this.hasColorsPerFace) {
			lines.a(
				"	var es0=triangle(depth-1,p0,p01,p20);",
				"	var es1=triangle(depth-1,p1,p12,p01);",
				"	var es2=triangle(depth-1,p2,p20,p12);"
			);
		} else {
			lines.a(
				"	var es0=triangle(depth-1,p0,p01,p20,[es[0],null,null]);",
				"	var es1=triangle(depth-1,p1,p12,p01,[es[1],null,es0[1]]);",
				"	var es2=triangle(depth-1,p2,p20,p12,[es[2],es0[2],es1[1]]);",
				"	return [es0[0],es1[0],es2[0]];"
			);
		}
		lines.a(
			"}"
		);
		if (!this.usesElements() || this.hasColorsPerFace) {
			return lines.wrap(
				"function triangle(depth,p0,p1,p2) {",
				"}"
			);
		} else {
			return lines.wrap(
				"function triangle(depth,p0,p1,p2,es) {",
				"}"
			);
		}
	}.bind(this);
	var writeInitialTriangleCall=function(){
		var lines=new Lines;
		lines.a(
			"shapeLod,",
			"[-Math.sin(0/3*Math.PI),Math.cos(0/3*Math.PI)],",
			"[-Math.sin(2/3*Math.PI),Math.cos(2/3*Math.PI)],",
			"[-Math.sin(4/3*Math.PI),Math.cos(4/3*Math.PI)]"
		);
		if (this.usesElements() && !this.hasColorsPerFace) {
			lines.t(
				",","[null,null,null]"
			);
		}
		return lines.wrap(
			"triangle(",
			");"
		);
	}.bind(this);
	var lines=new Lines;
	lines.a(
		"var nextIndexIntoVertices=0;"
	);
	if (this.usesElements()) {
		lines.a(
			"var nextIndexIntoElements=0;",
			"var nextElement=0;"
		);
	}
	if (this.hasColorsPerVertex || this.hasColorsPerFace) {
		lines.a("// p = position, c = color, e = element, es = elements");
		lines.a(this.writeColorData());
		if (!this.usesElements() || this.hasColorsPerFace) {
			lines.a(
				"var nextIndexIntoColors=0;"
			);
		}
	} else {
		lines.a(
			"// p = position, e = element, es = elements"
		);
	}
	lines.a(writePushVertex());
	if (this.usesElements()) {
		lines.a(
			"function pushElement(e) {",
			"	elements[nextIndexIntoElements++]=e;",
			"}"
		);
	}
	lines.a(
		"function mix(pa,pb) {",
		"	return [",
		"		(pa[0]+pb[0])/2,",
		"		(pa[1]+pb[1])/2,",
		"	];",
		"}",
		writeTriangle(),
		writeInitialTriangleCall()
	);
	return lines;
}

module.exports=Gasket;
<|endoftext|>


<commit_before>import BaseShape from './base-shape';

/**
 * A shape to display a marker.
 *
 * [example usage](./examples/layer-marker.html)
 */
export default class Marker extends BaseShape {
  getClassName() { return 'marker'; }

  _getAccessorList() {
    return { x: 0, color: '#ff0000' };
  }

  _getDefaults() {
    return {
      handlerWidth: 7,
      handlerHeight: 10,
      displayHandlers: true,
      opacity: 1,
      color: 'red',
    };
  }

  render(renderingContext) {
    if (this.$el) { return this.$el; }

    const height = renderingContext.height;

    this.$el = document.createElementNS(this.ns, 'g');
    this.$line = document.createElementNS(this.ns, 'line');

    // draw line
    this.$line.setAttributeNS(null, 'x', 0);
    this.$line.setAttributeNS(null, 'y1', 0);
    this.$line.setAttributeNS(null, 'y2', height);
    this.$line.setAttributeNS(null, 'shape-rendering', 'crispEdges');

    this.$el.appendChild(this.$line);

    if (this.params.displayHandlers) {
      this.$handler = document.createElementNS(this.ns, 'rect');

      this.$handler.setAttributeNS(null, 'x', -((this.params.handlerWidth) / 2 ));
      this.$handler.setAttributeNS(null, 'y', renderingContext.height - this.params.handlerHeight);
      this.$handler.setAttributeNS(null, 'width', this.params.handlerWidth);
      this.$handler.setAttributeNS(null, 'height', this.params.handlerHeight);
      this.$handler.setAttributeNS(null, 'shape-rendering', 'crispEdges');

      this.$el.appendChild(this.$handler);
    }

    this.$el.style.opacity = this.params.opacity;

    return this.$el;
  }

  update(renderingContext, datum) {
    const x = renderingContext.timeToPixel(this.x(datum)) - 0.5;
    const color = this.color(datum);

    this.$el.setAttributeNS(null, 'transform', `translate(${x}, 0)`);
    this.$line.style.stroke = color;

    if (this.params.displayHandlers) {
      this.$handler.style.fill = color;
    }
  }

  inArea(renderingContext, datum, x1, y1, x2, y2) {
    // handlers only are selectable
    const x = renderingContext.timeToPixel(this.x(datum));
    const shapeX1 = x - (this.params.handlerWidth - 1) / 2;
    const shapeX2 = shapeX1 + this.params.handlerWidth;
    const shapeY1 = renderingContext.height - this.params.handlerHeight;
    const shapeY2 = renderingContext.height;

    const xOverlap = Math.max(0, Math.min(x2, shapeX2) - Math.max(x1, shapeX1));
    const yOverlap = Math.max(0, Math.min(y2, shapeY2) - Math.max(y1, shapeY1));
    const area = xOverlap * yOverlap;

    return area > 0;
  }
}
<commit_msg>Make markers out of range be hidden. Not sure this is any improvement.<commit_after>import BaseShape from './base-shape';

/**
 * A shape to display a marker.
 *
 * [example usage](./examples/layer-marker.html)
 */
export default class Marker extends BaseShape {
  getClassName() { return 'marker'; }

  _getAccessorList() {
    return { x: 0, color: '#ff0000' };
  }

  _getDefaults() {
    return {
      handlerWidth: 7,
      handlerHeight: 10,
      displayHandlers: true,
      opacity: 1,
      color: 'red',
    };
  }

  render(renderingContext) {
    if (this.$el) { return this.$el; }

    console.log("marker render");

    const height = renderingContext.height;

    this.$el = document.createElementNS(this.ns, 'g');
    this.$line = document.createElementNS(this.ns, 'line');

    // draw line
    this.$line.setAttributeNS(null, 'x', 0);
    this.$line.setAttributeNS(null, 'y1', 0);
    this.$line.setAttributeNS(null, 'y2', height);
    this.$line.setAttributeNS(null, 'shape-rendering', 'crispEdges');

    this.$el.appendChild(this.$line);

    if (this.params.displayHandlers) {
      this.$handler = document.createElementNS(this.ns, 'rect');

      this.$handler.setAttributeNS(null, 'x', -((this.params.handlerWidth) / 2 ));
      this.$handler.setAttributeNS(null, 'y', renderingContext.height - this.params.handlerHeight);
      this.$handler.setAttributeNS(null, 'width', this.params.handlerWidth);
      this.$handler.setAttributeNS(null, 'height', this.params.handlerHeight);
      this.$handler.setAttributeNS(null, 'shape-rendering', 'crispEdges');

      this.$el.appendChild(this.$handler);
    }

    this.$el.style.opacity = this.params.opacity;

    return this.$el;
  }

  update(renderingContext, datum) {

    console.log("marker update");

    const x = renderingContext.timeToPixel(this.x(datum)) - 0.5;

    const minX = renderingContext.minX;
    const maxX = renderingContext.maxX;

    if (x < minX || x > maxX) {

      this.$el.style.visibility = 'hidden';

    } else {

      this.$el.style.visibility = 'visible';

      const color = this.color(datum);

      this.$el.setAttributeNS(null, 'transform', `translate(${x}, 0)`);
      this.$line.style.stroke = color;

      if (this.params.displayHandlers) {
        this.$handler.style.fill = color;
      }
    }
  }

  inArea(renderingContext, datum, x1, y1, x2, y2) {
    // handlers only are selectable
    const x = renderingContext.timeToPixel(this.x(datum));
    const shapeX1 = x - (this.params.handlerWidth - 1) / 2;
    const shapeX2 = shapeX1 + this.params.handlerWidth;
    const shapeY1 = renderingContext.height - this.params.handlerHeight;
    const shapeY2 = renderingContext.height;

    const xOverlap = Math.max(0, Math.min(x2, shapeX2) - Math.max(x1, shapeX1));
    const yOverlap = Math.max(0, Math.min(y2, shapeY2) - Math.max(y1, shapeY1));
    const area = xOverlap * yOverlap;

    return area > 0;
  }
}
<|endoftext|>


<commit_before>(function (module) {
	"use strict";

	if( ! window.skrollr) {
		console.error('`skrollr` not found');
	} else {
		window.skrollr.decks = module(window, document, window.skrollr);
	}

} (function (window, document, skrollr) {
	"use strict";

	var defaults = {
		decks: '.skrollr-deck',
		offset: 15,
		duration: 600,
		easing: 'quadratic',
		delay: 500,
		autoscroll: true
	}, callbacks = {
		render: [],
		change: []
	};

	var setTimeout = window.setTimeout,
		clearTimeout = window.clearTimeout,
		isInitialized = false,
		settings = {},
		segments = {},
		segmentsList = [],
		nav = document.createElement('ul'),
		currentDeck;

	// Stop animating on scroll keys
	document.addEventListener('keydown', function (e) {
		var inst = skrollr.get(),
			keys = [38, 40,
					33, 34,
					36, 35];

		if(keys.indexOf(e.keyCode) > -1) {
			if(inst && inst.isAnimatingTo()) {
				inst.stopAnimateTo();
			}
		}
	}, false);

	// Auto initialize
	document.addEventListener('DOMContentLoaded', function () {
		var el = document.querySelector('.skrollr-decks-init');
		if(el && el.tagName === 'BODY') {
			init();
		}
	}, false);

	// Auto resize
	window.addEventListener('load', resizeDecks, false);
	window.addEventListener('load', update, false);
	window.addEventListener('resize', resizeDecks, false);
	window.addEventListener('resize', update, false);

	return {
		init: init,
		animateTo: animateTo,
		refresh: resizeDecks,
		on: on
	};

	function on(name, cb) {
		if((name = name.toLowerCase()) in callbacks && typeof cb === 'function') {
			callbacks[name].unshift(cb);
		}
	}

	function trigger(name, data) {
		var collection = callbacks[name], i;

		for(i = collection.length - 1; i > -1; i--) {
			collection[i].apply({}, data);
		}
	}

	// Initialize
	function init (user) {
		if(isInitialized) {
			return false;
		} else {
			isInitialized = true;
		}

		var key, inst, renderTimer;

		user = typeof user === 'object' ? user : {};
		for(key in defaults) if(defaults.hasOwnProperty(key)) {
			settings[key] = user[key] || defaults[key];
		}

		var onRender = settings.onRender,
			onChange = settings.onChange,

		inst = skrollr.init({
			forceHeight: false
		});

		segments = findDecks(settings.decks, segmentsList);
		nav = createNav(segmentsList);

		resizeDecks();

		document.body.appendChild(nav);

		inst.refresh(nav.children);

		inst.on('render', function (e) {
			var el = update(e);

			clearTimeout(renderTimer);
			renderTimer = setTimeout(function () {
				settings.autoscroll && el && inst.animateTo(inst.relativeToAbsolute(el, 'top', 'top') + 1, settings);
			}, settings.delay);

			trigger('render', [e]);
		});
	}

	// Goto
	function animateTo(anchor, noAnim) {
		var el = segments[anchor],
			inst = skrollr.get(),
			top, active, index;

		if(inst) {
			inst.stopAnimateTo();

			if( ! el) {
				active = nav.getElementsByClassName('skrollable-between');
				if(active.length === 1) {
					index = Number(active[0].getAttribute('data-skrollr-decks-index'));
					el = segmentsList[anchor === 'up' ? index - 1 : anchor === 'down' ? index + 1 : -1];
				}

				if(active.length === 2) {
					active = active[anchor === 'up' ? 0 : anchor === 'down' ? 1 : -1];
					el = active ? segments[active.getAttribute('data-anchor-target')] : null;
				}
			}

			if(el) {
				top = inst.relativeToAbsolute(el, 'top', 'top') + 1;
				if(noAnim) {
					inst.setScrollTop(top, true);
				} else {
					inst.animateTo(top, settings);
				}

				return true;
			}
		}

		return false;
	}

	// Update decks size
	function resizeDecks() {
		var wndHeight = window.innerHeight + 2,
			deck, deckHeight, i;
		for(i = segmentsList.length - 1; i > -1; i--) {
			deck = segmentsList[i];
			deck.style.height = 'auto';
			deckHeight = deck.offsetHeight;
			deck.style.height = (deckHeight < wndHeight ? wndHeight : deckHeight) + 'px';
		}
		deck.offsetHeight;
	}

	// Autoscroll
	function update(e) {
		var inst = skrollr.get(),
			active = nav.getElementsByClassName('skrollable-between'),
			el, before, after;

		if(active.length === 1) {
			el = segments[active[0].getAttribute('data-anchor-target')];
			currentDeck !== el && trigger('change', [el])
			currentDeck = el;
		}

		if(active.length === 2) {
			before = e.direction === 'up' ? 0 : 1;
			after = e.direction === 'up' ? 1 : 0;

			el = segments[active[before].getAttribute('data-anchor-target')];
			if( ! isVol(el)) {
				el = segments[active[after].getAttribute('data-anchor-target')];
			}

			currentDeck !== el && trigger('change', [el])
			currentDeck = el;

			return el;
		}
	}

	function isVol(el) {
		var inst = skrollr.get(),
			top = inst.getScrollTop(),
			height = window.innerHeight,
			bottom = top + height,
			elTop = inst.relativeToAbsolute(el, 'top', 'top'),
			elBottom = inst.relativeToAbsolute(el, 'top', 'bottom'),
			offset =  height * settings.offset / 100;

		return elTop + offset < bottom  && elBottom - offset > top;
	}

	// Layout
	function findDecks(selector, list) {
		var items = {},
			start = +new Date(),
			el, i, id;

		if(typeof selector === 'string') {
			selector = document.querySelectorAll(selector);
			for(i = selector.length - 1; i > -1; i--) {
				el = selector[i];
				list.unshift(el);
				el.id = !! el.id ? el.id : 'skroll-deck-id-' + i;
				items['#' + el.id] = el;
			}
		}

		return items;
	}

	function createNav(list) {
		var frag = document.createElement('ul'),
			item = document.createElement('li'),
			i, max, el;

		frag.style.display = 'none';
		item.setAttribute('data-top-bottom', '');
		item.setAttribute('data-bottom-top', '');

		for(i = 0, max = list.length; i < max; i++) {
			el = item.cloneNode();
			el.setAttribute('data-skrollr-decks-index', i);
			el.setAttribute('data-anchor-target', '#' + list[i].id);
			frag.appendChild(el);
		}

		return frag;
	}

}));
<commit_msg>Normalized autoscroll<commit_after>(function (module) {
	"use strict";

	if( ! window.skrollr) {
		console.error('`skrollr` not found');
	} else {
		window.skrollr.decks = module(window, document, window.skrollr);
	}

} (function (window, document, skrollr) {
	"use strict";

	var defaults = {
		decks: '.skrollr-deck',
		offset: 15,
		duration: 600,
		easing: 'quadratic',
		delay: 500,
		autoscroll: true
	}, callbacks = {
		render: [],
		change: []
	};

	var setTimeout = window.setTimeout,
		clearTimeout = window.clearTimeout,
		isInitialized = false,
		settings = {},
		segments = {},
		segmentsList = [],
		nav = document.createElement('ul'),
		currentDeck;

	// Stop animating on scroll keys
	document.addEventListener('keydown', function (e) {
		var inst = skrollr.get(),
			keys = [38, 40,
					33, 34,
					36, 35];

		if(keys.indexOf(e.keyCode) > -1) {
			if(inst && inst.isAnimatingTo()) {
				inst.stopAnimateTo();
			}
		}
	}, false);

	// Auto initialize
	document.addEventListener('DOMContentLoaded', function () {
		var el = document.querySelector('.skrollr-decks-init');
		if(el && el.tagName === 'BODY') {
			init();
		}
	}, false);

	// Auto resize
	window.addEventListener('load', resizeDecks, false);
	window.addEventListener('load', update, false);
	window.addEventListener('resize', resizeDecks, false);
	window.addEventListener('resize', update, false);

	return {
		init: init,
		animateTo: animateTo,
		refresh: resizeDecks,
		on: on
	};

	function on(name, cb) {
		if((name = name.toLowerCase()) in callbacks && typeof cb === 'function') {
			callbacks[name].unshift(cb);
		}
	}

	function trigger(name, data) {
		var collection = callbacks[name], i;

		for(i = collection.length - 1; i > -1; i--) {
			collection[i].apply({}, data);
		}
	}

	// Initialize
	function init (user) {
		if(isInitialized) {
			return false;
		} else {
			isInitialized = true;
		}

		var key, inst, renderTimer;

		user = typeof user === 'object' ? user : {};
		for(key in defaults) if(defaults.hasOwnProperty(key)) {
			settings[key] = user[key] || defaults[key];
		}

		var onRender = settings.onRender,
			onChange = settings.onChange,

		inst = skrollr.init({
			forceHeight: false
		});

		segments = findDecks(settings.decks, segmentsList);
		nav = createNav(segmentsList);

		resizeDecks();

		document.body.appendChild(nav);

		inst.refresh(nav.children);

		inst.on('render', function (e) {
			var el = update(e);

			clearTimeout(renderTimer);
			if(el && settings.autoscroll) {
				renderTimer = setTimeout(function () {
					var up = e.direction === 'up',
						offset = (up ? - window.innerHeight - 1 : 1);

					offset += inst.relativeToAbsolute(el, 'top', up ? 'bottom' : 'top');
					inst.animateTo(offset, settings);
				}, settings.delay);
			}

			trigger('render', [e]);
		});
	}

	// Goto
	function animateTo(anchor, noAnim) {
		var el = segments[anchor],
			inst = skrollr.get(),
			top, active, index;

		if(inst) {
			inst.stopAnimateTo();

			if( ! el) {
				active = nav.getElementsByClassName('skrollable-between');
				if(active.length === 1) {
					index = Number(active[0].getAttribute('data-skrollr-decks-index'));
					el = segmentsList[anchor === 'up' ? index - 1 : anchor === 'down' ? index + 1 : -1];
				}

				if(active.length === 2) {
					active = active[anchor === 'up' ? 0 : anchor === 'down' ? 1 : -1];
					el = active ? segments[active.getAttribute('data-anchor-target')] : null;
				}
			}

			if(el) {
				top = inst.relativeToAbsolute(el, 'top', 'top') + 1;
				if(noAnim) {
					inst.setScrollTop(top, true);
				} else {
					inst.animateTo(top, settings);
				}

				return true;
			}
		}

		return false;
	}

	// Update decks size
	function resizeDecks() {
		var wndHeight = window.innerHeight + 2,
			deck, deckHeight, i;
		for(i = segmentsList.length - 1; i > -1; i--) {
			deck = segmentsList[i];
			deck.style.height = 'auto';
			deckHeight = deck.offsetHeight;
			deck.style.height = (deckHeight < wndHeight ? wndHeight : deckHeight) + 'px';
		}
		deck.offsetHeight;
	}

	// Autoscroll
	function update(e) {
		var inst = skrollr.get(),
			active = nav.getElementsByClassName('skrollable-between'),
			el, before, after;

		if(active.length === 1) {
			el = segments[active[0].getAttribute('data-anchor-target')];
			currentDeck !== el && trigger('change', [el])
			currentDeck = el;
		}

		if(active.length === 2) {
			before = e.direction === 'up' ? 0 : 1;
			after = e.direction === 'up' ? 1 : 0;

			el = segments[active[before].getAttribute('data-anchor-target')];
			if( ! isVol(el)) {
				el = segments[active[after].getAttribute('data-anchor-target')];
			}

			currentDeck !== el && trigger('change', [el])
			currentDeck = el;

			return el;
		}
	}

	function isVol(el) {
		var inst = skrollr.get(),
			top = inst.getScrollTop(),
			height = window.innerHeight,
			bottom = top + height,
			elTop = inst.relativeToAbsolute(el, 'top', 'top'),
			elBottom = inst.relativeToAbsolute(el, 'top', 'bottom'),
			offset =  height * settings.offset / 100;

		return elTop + offset < bottom  && elBottom - offset > top;
	}

	// Layout
	function findDecks(selector, list) {
		var items = {},
			start = +new Date(),
			el, i, id;

		if(typeof selector === 'string') {
			selector = document.querySelectorAll(selector);
			for(i = selector.length - 1; i > -1; i--) {
				el = selector[i];
				list.unshift(el);
				el.id = !! el.id ? el.id : 'skroll-deck-id-' + i;
				items['#' + el.id] = el;
			}
		}

		return items;
	}

	function createNav(list) {
		var frag = document.createElement('ul'),
			item = document.createElement('li'),
			i, max, el;

		frag.style.display = 'none';
		item.setAttribute('data-top-bottom', '');
		item.setAttribute('data-bottom-top', '');

		for(i = 0, max = list.length; i < max; i++) {
			el = item.cloneNode();
			el.setAttribute('data-skrollr-decks-index', i);
			el.setAttribute('data-anchor-target', '#' + list[i].id);
			frag.appendChild(el);
		}

		return frag;
	}

}));
<|endoftext|>


<commit_before>import { customElement, inlineView } from 'aurelia-templating';
import { inject } from 'aurelia-dependency-injection';

@customElement('md-slider')
@inject(Element)
@inlineView(`
  <template class="slider">
  <require from="./slider.css"></require>

    <content select="li"></content>

  </template>
`)
export class MdSlider {
  constructor(element) {
    this.element = element;
  }

  attached() {
    // $(this.element).slider({full_width: true});
    $(this.element).slider();
  }
}
<commit_msg>feat(slider): add fullscreen/fillContainer option<commit_after>import { bindable, customElement, inlineView } from 'aurelia-templating';
import { inject } from 'aurelia-dependency-injection';
import { getBooleanFromAttributeValue } from '../common/attributes';

@customElement('md-slider')
@inject(Element)
@inlineView(`
  <template class="slider">
  <require from="./slider.css"></require>

    <content select="li"></content>

  </template>
`)
export class MdSlider {
  @bindable() mdFillContainer = false;
  @bindable() mdHeight = 400;
  @bindable() mdIndicators = true;
  @bindable() mdInterval = 6000;
  @bindable() mdTransition = 500;

  constructor(element) {
    this.element = element;
  }

  attached() {
    if (getBooleanFromAttributeValue(this.mdFillContainer)) {
      this.element.classList.add('fullscreen');
    }
    // $(this.element).slider({full_width: true});
    $(this.element).slider({
      height: parseInt(this.mdHeight, 10),
      indicators: getBooleanFromAttributeValue(this.mdIndicators),
      interval: parseInt(this.mdInterval, 10),
      transition: parseInt(this.mdTransition, 10)
    });
  }
}
<|endoftext|>


<commit_before>/**
 * @license
 * resol-vbus - A JavaScript library for processing RESOL VBus data
 */
'use strict';

var _ = require('lodash');
var moment = require('moment');
var sprintf = require('sprintf').sprintf;

var extend = require('./extend');

var createVBusSpecificationData = require('./specification-data');

var globalSpecificationData = createVBusSpecificationData();
var globalSpecification;

var optionKeys = [
    'language'
];

var Specification = extend(null, {

    language: 'en',

    deviceSpecCache: null,

    packetSpecCache: null,

    specificationData: null,

    constructor: function(options) {
        var _this = this;

        _.extend(this, _.pick(options, optionKeys));

        this.deviceSpecCache = {};
        this.packetSpecCache = {};

        this.specificationData = Specification.loadSpecificationData(options && options.specificationData);
    },

    getDeviceSpecification: function(selfAddress, peerAddress, channel) {
        if (typeof selfAddress === 'object') {
            if (peerAddress === 'source') {
                channel = selfAddress.channel;
                peerAddress = selfAddress.destinationAddress;
                selfAddress = selfAddress.sourceAddress;
            } else if (peerAddress === 'destination') {
                channel = selfAddress.channel;
                peerAddress = selfAddress.sourceAddress;
                selfAddress = selfAddress.destinationAddress;
            } else {
                throw new Error('Invalid arguments');
            }
        }

        if (channel === undefined) {
            channel = 0;
        }

        var deviceId = sprintf('%02X_%04X_%04X', channel, selfAddress, peerAddress);

        if (!_.has(this.deviceSpecCache, deviceId)) {
            var origDeviceSpec;
            if (!origDeviceSpec && this.specificationData.getDeviceSpecification) {
                origDeviceSpec = this.specificationData.getDeviceSpecification(selfAddress, peerAddress);
            }
            if (!origDeviceSpec && this.specificationData.deviceSpecs) {
                origDeviceSpec = this.specificationData.deviceSpecs ['_' + deviceId];
            }

            var deviceSpec = _.extend({}, origDeviceSpec, {
                deviceId: deviceId,
            });

            if (!_.has(deviceSpec, 'name')) {
                deviceSpec.name = sprintf('Unknown Device (0x%04X)', selfAddress);
            }

            if (!_.has(deviceSpec, 'fullName')) {
                var fullNameFormatter;
                if (channel) {
                    fullNameFormatter = 'VBus #%1$d: %2$s';
                } else {
                    fullNameFormatter = '%2$s';
                }
                deviceSpec.fullName = sprintf(fullNameFormatter, channel, deviceSpec.name);
            }

            this.deviceSpecCache [deviceId] = deviceSpec;
        }

        return this.deviceSpecCache [deviceId];
    },

    getPacketSpecification: function(headerOrChannel, destinationAddress, sourceAddress, command) {
        if (typeof headerOrChannel === 'object') {
            command = headerOrChannel.command;
            sourceAddress = headerOrChannel.sourceAddress;
            destinationAddress = headerOrChannel.destinationAddress;
            headerOrChannel = headerOrChannel.channel;
        } else if (typeof headerOrChannel === 'string') {
            var md = headerOrChannel.match(/^([0-9a-f]{2})_([0-9a-f]{4})_([0-9a-f]{4})_10_([0-9a-f]{4})/i);
            if (!md) {
                throw new Error('Invalid packet ID');
            }

            command = parseInt(md [4], 16);
            sourceAddress = parseInt(md [3], 16);
            destinationAddress = parseInt(md [2], 16);
            headerOrChannel = parseInt(md [1], 16);
        }

        var packetId = sprintf('%02X_%04X_%04X_10_%04X', headerOrChannel, destinationAddress, sourceAddress, command);

        if (!_.has(this.packetSpecCache, packetId)) {
            var origPacketSpec;
            if (!origPacketSpec && this.specificationData.getPacketSpecification) {
                origPacketSpec = this.specificationData.getPacketSpecification(destinationAddress, sourceAddress, command);
            }
            if (!origPacketSpec && this.specificationData.packetSpecs) {
                origPacketSpec = this.specificationData.packetSpecs ['_' + packetId];
            }

            var destinationDeviceSpec = this.getDeviceSpecification(destinationAddress, sourceAddress, headerOrChannel);
            var sourceDeviceSpec = this.getDeviceSpecification(sourceAddress, destinationAddress, headerOrChannel);

            var packetSpec = _.extend({}, origPacketSpec, {
                packetId: packetId,
                destinationDevice: destinationDeviceSpec,
                sourceDevice: sourceDeviceSpec,
            });

            if (!_.has(packetSpec, 'packetFields')) {
                packetSpec.packetFields = [];
            }

            this.packetSpecCache [packetId] = packetSpec;
        }

        return this.packetSpecCache [packetId];
    },

    getPacketFieldSpecification: function(packetSpecOrId, fieldId) {
        var packetFieldSpec;
        if (typeof packetSpecOrId === 'string') {
            if (this.specificationData.filteredPacketFieldSpecs) {
                packetFieldSpec = _.find(this.specificationData.filteredPacketFieldSpecs, { filteredPacketFieldId: packetSpecOrId });
            }

            if (!packetFieldSpec) {
                var md = packetSpecOrId.match(/^([0-9a-f]{2}_[0-9a-f]{4}_[0-9a-f]{4}_10_[0-9a-f]{4})_(.*)$/i);
                if (!md) {
                    throw new Error('Invalid packet field ID');
                }

                fieldId = md [2];
                packetSpecOrId = this.getPacketSpecification(md [1]);
            }
        }

        if (!packetFieldSpec && packetSpecOrId) {
            packetFieldSpec = _.find(packetSpecOrId.packetFields, { fieldId: fieldId });
        }

        return packetFieldSpec;
    },

    getRawValue: function(packetField, buffer, start, end) {
        if (start === undefined) {
            start = 0;
        }
        if (end === undefined) {
            end = buffer ? buffer.length : 0;
        }

        var rawValue;
        if (packetField && packetField.getRawValue) {
            rawValue = packetField.getRawValue(buffer, start, end);
        } else {
            rawValue = null;
        }

        return rawValue;
    },

    formatTextValueFromRawValue: function(packetField, rawValue, unit) {
        var textValue;

        if ((rawValue !== undefined) && (rawValue !== null)) {
            if (typeof unit === 'string') {
                if (_.has(this.specificationData.units, unit)) {
                    unit = this.specificationData.units [unit];
                } else {
                    throw new Error('Unknown unit named "' + unit + '"');
                }
            }

            if (packetField && packetField.type) {
                var type = packetField.type;
                textValue = this.formatTextValueFromRawValueInternal(rawValue, unit, type.rootTypeId, type.precision, type.unit);
            } else {
                textValue = rawValue.toString();
                if (unit && unit.unitText) {
                    textValue += unit.unitText;
                }
            }
        } else {
            textValue = '';
        }

        return textValue;
    },

    formatTextValueFromRawValueInternal: function(rawValue, unit, rootType, precision, defaultUnit) {
        var unitText = unit ? unit.unitText : defaultUnit ? defaultUnit.unitText : '';

        var result, textValue, format;
        if (rootType === 'Time') {
            textValue = moment(rawValue * 60000).lang(this.language).utc().format('HH:mm');
            result = textValue + unitText;
        } else if (rootType === 'Weektime') {
            textValue = moment((rawValue + 5760) * 60000).lang(this.language).utc().format('dd,HH:mm');
            result = textValue + unitText;
        } else if (rootType === 'Datetime') {
            textValue = moment((rawValue + 978307200) * 1000).lang(this.language).utc().format('L HH:mm:ss');
            result = textValue + unitText;
        } else if (precision === 0) {
            result = sprintf('%.0f%s', rawValue, unitText);
        } else if (precision === 1) {
            result = sprintf('%.1f%s', rawValue, unitText);
        } else if (precision === 2) {
            result = sprintf('%.2f%s', rawValue, unitText);
        } else if (precision === 3) {
            result = sprintf('%.3f%s', rawValue, unitText);
        } else if (precision === 4) {
            result = sprintf('%.4f%s', rawValue, unitText);
        } else {
            format = '%.' + precision + 'f%s';
            result = sprintf(format, rawValue, unitText);
        }

        return result;
    },

    getPacketFieldsForHeaders: function(headers) {
        var _this = this;

        // filter out all packets
        var packets = _.reduce(headers, function(memo, header) {
            if ((header.getProtocolVersion() & 0xF0) === 0x10) {
                memo.push(header);
            }
            return memo;
        }, []);

        var packetFields = [];

        var filteredPacketFieldSpecs = this.specificationData.filteredPacketFieldSpecs;
        if (filteredPacketFieldSpecs) {
            var packetById = _.reduce(packets, function(memo, packet) {
                var packetSpec = _this.getPacketSpecification(packet);
                memo [packetSpec.packetId] = packet;
                return memo;
            }, {});

            _.forEach(filteredPacketFieldSpecs, function(fpfs) {
                var packetField = _.extend({}, {
                    id: fpfs.filteredPacketFieldId,
                    packet: packetById [fpfs.packetId],
                    packetSpec: fpfs.packetSpec,
                    packetFieldSpec: fpfs,
                    origPacketFieldSpec: fpfs.packetFieldSpec,
                });
                packetFields.push(packetField);
            });
        } else {
            _.forEach(packets, function(packet) {
                var packetSpec = _this.getPacketSpecification(packet);
                if (packetSpec) {
                    _.forEach(packetSpec.packetFields, function(packetFieldSpec) {
                        var packetField = {
                            id: packetSpec.packetId + '_' + packetFieldSpec.fieldId,
                            packet: packet,
                            packetSpec: packetSpec,
                            packetFieldSpec: packetFieldSpec,
                            origPacketFieldSpec: packetFieldSpec,
                        };
                        packetFields.push(packetField);
                    });
                }
            });
        }

        var language = this.language;

        _.forEach(packetFields, function(packetField) {
            var names = packetField.packetFieldSpec.name;
            var name = names [language] || names.en || names.de || names.ref;

            var rawValue;
            if (packetField.packetFieldSpec && packetField.packet) {
                rawValue = _this.getRawValue(packetField.packetFieldSpec, packetField.packet.frameData);
            }

            _.extend(packetField, {

                name: name,

                rawValue: rawValue,

                formatTextValue: function(unit) {
                    return _this.formatTextValueFromRawValue(packetField.packetFieldSpec, rawValue, unit);
                },

            });
        });

        return packetFields;
    },

    getFilteredPacketFieldSpecificationsForHeaders: function(headers) {
        var filteredPacketFieldSpecs = [];

        var packetFields = this.getPacketFieldsForHeaders(headers);

        _.forEach(packetFields, function(packetField) {
            var packetSpec = packetField.packetSpec;
            var packetFieldSpec = packetField.packetFieldSpec;

            if (packetSpec && packetFieldSpec) {
                var filteredPacketFieldSpec = _.extend({}, packetFieldSpec, {
                    filteredPacketFieldId: packetSpec.packetId + '_' + packetFieldSpec.fieldId,
                    packetId: packetSpec.packetId,
                    name: packetField.name,
                });

                filteredPacketFieldSpecs.push(filteredPacketFieldSpec);
            }
        });

        return filteredPacketFieldSpecs;
    },

}, {

    loadSpecificationData: function(rawSpecificationData, options) {
        if (rawSpecificationData === undefined) {
            rawSpecificationData = {};
        }
        if (options === undefined) {
            options = {};
        }

        var rawFilteredPacketFieldSpecs = rawSpecificationData.filteredPacketFieldSpecs;
        var specification = options.specification || globalSpecification || {};
        var specificationData = options.specificationData || specification.specificationData || globalSpecificationData || {};

        var filteredPacketFieldSpecs;
        if (rawFilteredPacketFieldSpecs) {
            var resolve = function(value, collectionKey) {
                var collection = specificationData [collectionKey];

                if (_.has(collection, value)) {
                    value = collection [value];
                }

                return value;
            };

            filteredPacketFieldSpecs = _.map(rawFilteredPacketFieldSpecs, function(rfpfs) {
                var packetSpec = specification.getPacketSpecification(rfpfs.packetId);
                var packetFieldSpec = specification.getPacketFieldSpecification(packetSpec, rfpfs.fieldId);

                var name = rfpfs.name;
                if (typeof name === 'string') {
                    name = { ref: name };
                }

                return _.extend({}, rfpfs, {
                    packetSpec: packetSpec,
                    packetFieldSpec: packetFieldSpec,
                    name: name,
                    type: resolve(rfpfs.type, 'types'),
                    getRawValue: resolve(rfpfs.getRawValue, 'getRawValueFunctions'),
                });
            });
        }

        var result = _.extend({}, specificationData, {
            filteredPacketFieldSpecs: filteredPacketFieldSpecs,
        });

        return result;
    },

    storeSpecificationData: function(options) {
        if (options === undefined) {
            options = {};
        }
        if (options instanceof Specification) {
            options = { specification: options };
        }

        var specification = options.specification || globalSpecification || {};
        var specificationData = options.specificationData || specification.specificationData || globalSpecificationData || {};
        var filteredPacketFieldSpecs = options.filteredPacketFieldSpecs || specificationData.filteredPacketFieldSpecs;

        var rawFilteredPacketFieldSpecs;
        if (filteredPacketFieldSpecs) {
            var link = function(value, valueIdKey, collectionKey) {
                var collection = specificationData [collectionKey];

                var valueId;
                if (valueIdKey) {
                    valueId = value [valueIdKey];
                }
                if (!valueId) {
                    valueId = _.findKey(collection, function(refValue) {
                        return (value === refValue);
                    });
                }
                if (valueId && _.has(collection, valueId) && (collection [valueId] === value)) {
                    value = valueId;
                }

                return value;
            };

            rawFilteredPacketFieldSpecs = _.map(filteredPacketFieldSpecs, function(fpfs) {
                return {
                    filteredPacketFieldId: fpfs.filteredPacketFieldId,
                    packetId: fpfs.packetId,
                    fieldId: fpfs.fieldId,
                    name: fpfs.name,
                    type: link(fpfs.type, 'typeId', 'types'),
                    getRawValue: link(fpfs.getRawValue, null, 'getRawValueFunctions'),
                };
            });
        }

        var rawSpecificationData = {
            filteredPacketFieldSpecs: rawFilteredPacketFieldSpecs,
        };

        return rawSpecificationData;
    },

});

globalSpecification = new Specification();

module.exports = Specification;
<commit_msg>- Refactored VBus specification to make use of the I18N helper class<commit_after>/**
 * @license
 * resol-vbus - A JavaScript library for processing RESOL VBus data
 */
'use strict';

var _ = require('lodash');
var sprintf = require('sprintf').sprintf;

var extend = require('./extend');
var I18N = require('./i18n');

var createVBusSpecificationData = require('./specification-data');

var globalSpecificationData = createVBusSpecificationData();
var globalSpecification;

var optionKeys = [
    'language'
];

var numberFormatCache = {};

var Specification = extend(null, {

    language: 'en',

    deviceSpecCache: null,

    packetSpecCache: null,

    i18n: null,

    specificationData: null,

    constructor: function(options) {
        var _this = this;

        _.extend(this, _.pick(options, optionKeys));

        this.i18n = new I18N(this.language);

        this.deviceSpecCache = {};
        this.packetSpecCache = {};

        this.specificationData = Specification.loadSpecificationData(options && options.specificationData);
    },

    getDeviceSpecification: function(selfAddress, peerAddress, channel) {
        if (typeof selfAddress === 'object') {
            if (peerAddress === 'source') {
                channel = selfAddress.channel;
                peerAddress = selfAddress.destinationAddress;
                selfAddress = selfAddress.sourceAddress;
            } else if (peerAddress === 'destination') {
                channel = selfAddress.channel;
                peerAddress = selfAddress.sourceAddress;
                selfAddress = selfAddress.destinationAddress;
            } else {
                throw new Error('Invalid arguments');
            }
        }

        if (channel === undefined) {
            channel = 0;
        }

        var deviceId = sprintf('%02X_%04X_%04X', channel, selfAddress, peerAddress);

        if (!_.has(this.deviceSpecCache, deviceId)) {
            var origDeviceSpec;
            if (!origDeviceSpec && this.specificationData.getDeviceSpecification) {
                origDeviceSpec = this.specificationData.getDeviceSpecification(selfAddress, peerAddress);
            }
            if (!origDeviceSpec && this.specificationData.deviceSpecs) {
                origDeviceSpec = this.specificationData.deviceSpecs ['_' + deviceId];
            }

            var deviceSpec = _.extend({}, origDeviceSpec, {
                deviceId: deviceId,
            });

            if (!_.has(deviceSpec, 'name')) {
                deviceSpec.name = sprintf('Unknown Device (0x%04X)', selfAddress);
            }

            if (!_.has(deviceSpec, 'fullName')) {
                var fullNameFormatter;
                if (channel) {
                    fullNameFormatter = 'VBus #%1$d: %2$s';
                } else {
                    fullNameFormatter = '%2$s';
                }
                deviceSpec.fullName = sprintf(fullNameFormatter, channel, deviceSpec.name);
            }

            this.deviceSpecCache [deviceId] = deviceSpec;
        }

        return this.deviceSpecCache [deviceId];
    },

    getPacketSpecification: function(headerOrChannel, destinationAddress, sourceAddress, command) {
        if (typeof headerOrChannel === 'object') {
            command = headerOrChannel.command;
            sourceAddress = headerOrChannel.sourceAddress;
            destinationAddress = headerOrChannel.destinationAddress;
            headerOrChannel = headerOrChannel.channel;
        } else if (typeof headerOrChannel === 'string') {
            var md = headerOrChannel.match(/^([0-9a-f]{2})_([0-9a-f]{4})_([0-9a-f]{4})_10_([0-9a-f]{4})/i);
            if (!md) {
                throw new Error('Invalid packet ID');
            }

            command = parseInt(md [4], 16);
            sourceAddress = parseInt(md [3], 16);
            destinationAddress = parseInt(md [2], 16);
            headerOrChannel = parseInt(md [1], 16);
        }

        var packetId = sprintf('%02X_%04X_%04X_10_%04X', headerOrChannel, destinationAddress, sourceAddress, command);

        if (!_.has(this.packetSpecCache, packetId)) {
            var origPacketSpec;
            if (!origPacketSpec && this.specificationData.getPacketSpecification) {
                origPacketSpec = this.specificationData.getPacketSpecification(destinationAddress, sourceAddress, command);
            }
            if (!origPacketSpec && this.specificationData.packetSpecs) {
                origPacketSpec = this.specificationData.packetSpecs ['_' + packetId];
            }

            var destinationDeviceSpec = this.getDeviceSpecification(destinationAddress, sourceAddress, headerOrChannel);
            var sourceDeviceSpec = this.getDeviceSpecification(sourceAddress, destinationAddress, headerOrChannel);

            var packetSpec = _.extend({}, origPacketSpec, {
                packetId: packetId,
                destinationDevice: destinationDeviceSpec,
                sourceDevice: sourceDeviceSpec,
            });

            if (!_.has(packetSpec, 'packetFields')) {
                packetSpec.packetFields = [];
            }

            this.packetSpecCache [packetId] = packetSpec;
        }

        return this.packetSpecCache [packetId];
    },

    getPacketFieldSpecification: function(packetSpecOrId, fieldId) {
        var packetFieldSpec;
        if (typeof packetSpecOrId === 'string') {
            if (this.specificationData.filteredPacketFieldSpecs) {
                packetFieldSpec = _.find(this.specificationData.filteredPacketFieldSpecs, { filteredPacketFieldId: packetSpecOrId });
            }

            if (!packetFieldSpec) {
                var md = packetSpecOrId.match(/^([0-9a-f]{2}_[0-9a-f]{4}_[0-9a-f]{4}_10_[0-9a-f]{4})_(.*)$/i);
                if (!md) {
                    throw new Error('Invalid packet field ID');
                }

                fieldId = md [2];
                packetSpecOrId = this.getPacketSpecification(md [1]);
            }
        }

        if (!packetFieldSpec && packetSpecOrId) {
            packetFieldSpec = _.find(packetSpecOrId.packetFields, { fieldId: fieldId });
        }

        return packetFieldSpec;
    },

    getRawValue: function(packetField, buffer, start, end) {
        if (start === undefined) {
            start = 0;
        }
        if (end === undefined) {
            end = buffer ? buffer.length : 0;
        }

        var rawValue;
        if (packetField && packetField.getRawValue) {
            rawValue = packetField.getRawValue(buffer, start, end);
        } else {
            rawValue = null;
        }

        return rawValue;
    },

    formatTextValueFromRawValue: function(packetField, rawValue, unit) {
        var textValue;

        if ((rawValue !== undefined) && (rawValue !== null)) {
            if (typeof unit === 'string') {
                if (_.has(this.specificationData.units, unit)) {
                    unit = this.specificationData.units [unit];
                } else {
                    throw new Error('Unknown unit named "' + unit + '"');
                }
            }

            if (packetField && packetField.type) {
                var type = packetField.type;
                textValue = this.formatTextValueFromRawValueInternal(rawValue, unit, type.rootTypeId, type.precision, type.unit);
            } else {
                textValue = rawValue.toString();
                if (unit && unit.unitText) {
                    textValue += unit.unitText;
                }
            }
        } else {
            textValue = '';
        }

        return textValue;
    },

    formatTextValueFromRawValueInternal: function(rawValue, unit, rootType, precision, defaultUnit) {
        var unitText = unit ? unit.unitText : defaultUnit ? defaultUnit.unitText : '';

        var result, textValue, format;
        if (rootType === 'Time') {
            textValue = this.i18n.moment(rawValue * 60000).utc().format('HH:mm');
            result = textValue + unitText;
        } else if (rootType === 'Weektime') {
            textValue = this.i18n.moment((rawValue + 5760) * 60000).utc().format('dd,HH:mm');
            result = textValue + unitText;
        } else if (rootType === 'Datetime') {
            textValue = this.i18n.moment((rawValue + 978307200) * 1000).utc().format('L HH:mm:ss');
            result = textValue + unitText;
        } else if (precision === 0) {
            textValue = this.i18n.numeral(rawValue).format('0');
            result = textValue + unitText;
        } else if (precision === 1) {
            textValue = this.i18n.numeral(rawValue).format('0.0');
            result = textValue + unitText;
        } else if (precision === 2) {
            textValue = this.i18n.numeral(rawValue).format('0.00');
            result = textValue + unitText;
        } else if (precision === 3) {
            textValue = this.i18n.numeral(rawValue).format('0.000');
            result = textValue + unitText;
        } else if (precision === 4) {
            textValue = this.i18n.numeral(rawValue).format('0.0000');
            result = textValue + unitText;
        } else {
            if (!_.has(numberFormatCache, precision)) {
                format = '0.';
                for (var i = 0; i < precision; i++) {
                    format = format + '0';
                }
                numberFormatCache [precision] = format;
            }

            textValue = this.i18n.numeral(rawValue).format(numberFormatCache [precision]);
            result = textValue + unitText;
        }

        return result;
    },

    getPacketFieldsForHeaders: function(headers) {
        var _this = this;

        // filter out all packets
        var packets = _.reduce(headers, function(memo, header) {
            if ((header.getProtocolVersion() & 0xF0) === 0x10) {
                memo.push(header);
            }
            return memo;
        }, []);

        var packetFields = [];

        var filteredPacketFieldSpecs = this.specificationData.filteredPacketFieldSpecs;
        if (filteredPacketFieldSpecs) {
            var packetById = _.reduce(packets, function(memo, packet) {
                var packetSpec = _this.getPacketSpecification(packet);
                memo [packetSpec.packetId] = packet;
                return memo;
            }, {});

            _.forEach(filteredPacketFieldSpecs, function(fpfs) {
                var packetField = _.extend({}, {
                    id: fpfs.filteredPacketFieldId,
                    packet: packetById [fpfs.packetId],
                    packetSpec: fpfs.packetSpec,
                    packetFieldSpec: fpfs,
                    origPacketFieldSpec: fpfs.packetFieldSpec,
                });
                packetFields.push(packetField);
            });
        } else {
            _.forEach(packets, function(packet) {
                var packetSpec = _this.getPacketSpecification(packet);
                if (packetSpec) {
                    _.forEach(packetSpec.packetFields, function(packetFieldSpec) {
                        var packetField = {
                            id: packetSpec.packetId + '_' + packetFieldSpec.fieldId,
                            packet: packet,
                            packetSpec: packetSpec,
                            packetFieldSpec: packetFieldSpec,
                            origPacketFieldSpec: packetFieldSpec,
                        };
                        packetFields.push(packetField);
                    });
                }
            });
        }

        var language = this.language;

        _.forEach(packetFields, function(packetField) {
            var names = packetField.packetFieldSpec.name;
            var name = names [language] || names.en || names.de || names.ref;

            var rawValue;
            if (packetField.packetFieldSpec && packetField.packet) {
                rawValue = _this.getRawValue(packetField.packetFieldSpec, packetField.packet.frameData);
            }

            _.extend(packetField, {

                name: name,

                rawValue: rawValue,

                formatTextValue: function(unit) {
                    return _this.formatTextValueFromRawValue(packetField.packetFieldSpec, rawValue, unit);
                },

            });
        });

        return packetFields;
    },

    getFilteredPacketFieldSpecificationsForHeaders: function(headers) {
        var filteredPacketFieldSpecs = [];

        var packetFields = this.getPacketFieldsForHeaders(headers);

        _.forEach(packetFields, function(packetField) {
            var packetSpec = packetField.packetSpec;
            var packetFieldSpec = packetField.packetFieldSpec;

            if (packetSpec && packetFieldSpec) {
                var filteredPacketFieldSpec = _.extend({}, packetFieldSpec, {
                    filteredPacketFieldId: packetSpec.packetId + '_' + packetFieldSpec.fieldId,
                    packetId: packetSpec.packetId,
                    name: packetField.name,
                });

                filteredPacketFieldSpecs.push(filteredPacketFieldSpec);
            }
        });

        return filteredPacketFieldSpecs;
    },

}, {

    loadSpecificationData: function(rawSpecificationData, options) {
        if (rawSpecificationData === undefined) {
            rawSpecificationData = {};
        }
        if (options === undefined) {
            options = {};
        }

        var rawFilteredPacketFieldSpecs = rawSpecificationData.filteredPacketFieldSpecs;
        var specification = options.specification || globalSpecification || {};
        var specificationData = options.specificationData || specification.specificationData || globalSpecificationData || {};

        var filteredPacketFieldSpecs;
        if (rawFilteredPacketFieldSpecs) {
            var resolve = function(value, collectionKey) {
                var collection = specificationData [collectionKey];

                if (_.has(collection, value)) {
                    value = collection [value];
                }

                return value;
            };

            filteredPacketFieldSpecs = _.map(rawFilteredPacketFieldSpecs, function(rfpfs) {
                var packetSpec = specification.getPacketSpecification(rfpfs.packetId);
                var packetFieldSpec = specification.getPacketFieldSpecification(packetSpec, rfpfs.fieldId);

                var name = rfpfs.name;
                if (typeof name === 'string') {
                    name = { ref: name };
                }

                return _.extend({}, rfpfs, {
                    packetSpec: packetSpec,
                    packetFieldSpec: packetFieldSpec,
                    name: name,
                    type: resolve(rfpfs.type, 'types'),
                    getRawValue: resolve(rfpfs.getRawValue, 'getRawValueFunctions'),
                });
            });
        }

        var result = _.extend({}, specificationData, {
            filteredPacketFieldSpecs: filteredPacketFieldSpecs,
        });

        return result;
    },

    storeSpecificationData: function(options) {
        if (options === undefined) {
            options = {};
        }
        if (options instanceof Specification) {
            options = { specification: options };
        }

        var specification = options.specification || globalSpecification || {};
        var specificationData = options.specificationData || specification.specificationData || globalSpecificationData || {};
        var filteredPacketFieldSpecs = options.filteredPacketFieldSpecs || specificationData.filteredPacketFieldSpecs;

        var rawFilteredPacketFieldSpecs;
        if (filteredPacketFieldSpecs) {
            var link = function(value, valueIdKey, collectionKey) {
                var collection = specificationData [collectionKey];

                var valueId;
                if (valueIdKey) {
                    valueId = value [valueIdKey];
                }
                if (!valueId) {
                    valueId = _.findKey(collection, function(refValue) {
                        return (value === refValue);
                    });
                }
                if (valueId && _.has(collection, valueId) && (collection [valueId] === value)) {
                    value = valueId;
                }

                return value;
            };

            rawFilteredPacketFieldSpecs = _.map(filteredPacketFieldSpecs, function(fpfs) {
                return {
                    filteredPacketFieldId: fpfs.filteredPacketFieldId,
                    packetId: fpfs.packetId,
                    fieldId: fpfs.fieldId,
                    name: fpfs.name,
                    type: link(fpfs.type, 'typeId', 'types'),
                    getRawValue: link(fpfs.getRawValue, null, 'getRawValueFunctions'),
                };
            });
        }

        var rawSpecificationData = {
            filteredPacketFieldSpecs: rawFilteredPacketFieldSpecs,
        };

        return rawSpecificationData;
    },

});

globalSpecification = new Specification();

module.exports = Specification;
<|endoftext|>