BR.LayersTab = BR.ControlLayers.extend({ previewLayer: null, previewBounds: null, saveLayers: [], initialize: function (layersConfig, baseLayers, overlays, options) { L.Control.Layers.prototype.initialize.call(this, baseLayers, overlays, options); this.layersConfig = layersConfig; }, addTo: function (map) { this._map = map; this.onAdd(map); L.DomUtil.get('layers-control-wrapper').appendChild(this._section); this.initButtons(); this.initJsTree(); return this; }, initButtons: function () { var expandTree = function (e) { this.jstree.open_all(); }; var collapseTree = function (e) { this.jstree.close_all(); }; var toggleOptionalLayers = function (e) { var button = L.DomUtil.get('optional_layers_button'); var treeButtons = L.DomUtil.get('tree-button-group'); var div = L.DomUtil.get('optional-layers-tree'); div.hidden = !div.hidden; treeButtons.hidden = !treeButtons.hidden; button.classList.toggle('active'); if (div.hidden) { this.deselectNode(); } }; L.DomUtil.get('expand_tree_button').onclick = L.bind(expandTree, this); L.DomUtil.get('collapse_tree_button').onclick = L.bind(collapseTree, this); L.DomUtil.get('optional_layers_button').onclick = L.bind(toggleOptionalLayers, this); }, initJsTree: function () { var layerIndex = BR.layerIndex; var treeData = this.toJsTree(BR.confLayers.tree); var oldSelected = null; var onSelectNode = function (e, data) { var layerData = layerIndex[data.node.id]; var selected = data.selected[0]; if (selected !== oldSelected) { this.showPreview(layerData); oldSelected = selected; } else { data.instance.deselect_node(data.node); } }; var onDeselectNode = function (e, data) { this.hidePreview(); oldSelected = null; }; var onCheckNode = function (e, data) { var layerData = layerIndex[data.node.id]; var layer = this.createLayer(layerData); var name = layerData.properties.name; var overlay = layerData.properties.overlay; if (overlay) { this.addOverlay(layer, name); } else { this.addBaseLayer(layer, name); } this.storeDefaultLayers(); }; var onUncheckNode = function (e, data) { var obj = this.getLayerById(data.node.id); if (!obj) return; this.removeLayer(obj.layer); if (this._map.hasLayer(obj.layer)) { this._map.removeLayer(obj.layer); if (!obj.overlay) { this.activateFirstLayer(); } } this.storeDefaultLayers(); }; $('#optional-layers-tree') .on('select_node.jstree', L.bind(onSelectNode, this)) .on('deselect_node.jstree', L.bind(onDeselectNode, this)) .on('check_node.jstree', L.bind(onCheckNode, this)) .on('uncheck_node.jstree', L.bind(onUncheckNode, this)) .on('ready.jstree', function (e, data) { data.instance.open_all(); }) .jstree({ plugins: [ 'checkbox' ], checkbox: { whole_node: false, tie_selection: false }, core: { 'multiple': false, 'themes': { 'icons': false, dots : false }, 'data' : treeData } }); this.jstree = $('#optional-layers-tree').jstree(true); }, toJsTree: function (layerTree) { var data = { children: [] }; var self = this; function createRootNode(name) { var rootNode = { 'text': i18next.t('sidebar.layers.category.' + name, name), 'state': { 'disabled': true }, 'children': [] }; return rootNode; } function getText(props, parent) { var text = ''; var code = props.country_code || props.language_code; if (code && parent.text !== code) { text += '' + code + ''; } text += props.name; return text; } function createNode(id, layerData, parent) { var props = layerData.properties; var url = props.url; var keyObj = self.layersConfig.getKeyName(url); var childNode = null; // when key required only add if configured if (!keyObj || keyObj && BR.keys[keyObj.name]) { childNode = { 'id': id, 'text': getText(props, parent), 'state': { 'checked': self.layersConfig.isDefaultLayer(id, props.overlay) } }; } return childNode; } function walkTree(inTree, outTree) { function walkObject(obj) { for (name in obj) { var value = obj[name]; var rootNode = createRootNode(name) outTree.children.push(rootNode); walkTree(value, rootNode); } } if (Array.isArray(inTree)) { for (var i = 0; i < inTree.length; i++) { var entry = inTree[i]; if (typeof entry === 'object') { walkObject(entry); } else { var layer = BR.layerIndex[entry]; if (layer) { var childNode = createNode(entry, layer, outTree); if (childNode) { outTree.children.push(childNode); } } else { console.error('Layer "' + entry + '" not found'); } } } } else { walkObject(inTree); } } walkTree(layerTree, data); return data.children; }, storeDefaultLayers: function () { var baseLayers = []; var overlays = []; for (var i = 0; i < this._layers.length; i++) { var obj = this._layers[i]; // id set in LayersConfig.createLayer var id = obj.layer.id; if (id) { if (obj.overlay) { overlays.push(id); } else { baseLayers.push(id); } } } this.layersConfig.storeDefaultLayers(baseLayers, overlays); }, createLayer: function (layerData) { var layer = this.layersConfig.createLayer(layerData); var overlay = layerData.properties.overlay; // preview z-index, like in BR.ControlLayers._addLayer layer.options.zIndex = overlay ? this._lastZIndex + 1 : 0; return layer; }, getLayerById: function (id) { for (var i = 0; i < this._layers.length; i++) { var obj = this._layers[i]; if (obj.layer.id === id) { return obj; } } return null; }, getLayerByLegacyName: function (legacyName) { var obj = null; var id = this.layersConfig.legacyNameToIdMap[legacyName]; if (id) { obj = this.getLayerById(id); } return obj; }, activateDefaultBaseLayer: function () { var index = BR.conf.defaultBaseLayerIndex || 0; var activeBaseLayer = this.getActiveBaseLayer(); if (!activeBaseLayer) { this.activateBaseLayerIndex(index); } }, saveRemoveActiveLayers: function () { this.saveLayers = this.removeActiveLayers(); }, restoreActiveLayers: function (overlaysOnly) { for (var i = 0; i < this.saveLayers.length; i++) { var obj = this.saveLayers[i]; if (!overlaysOnly || (overlaysOnly && obj.overlay)) { var hasLayer = !!this._getLayer(L.Util.stamp(obj.layer)); if (hasLayer) { if (!this._map.hasLayer(obj.layer)) { this._map.addLayer(obj.layer); } } else if (!obj.overlay) { // saved base layer has been removed during preview, select first this.activateFirstLayer(); } } } this.saveLayers = []; }, removePreviewLayer: function () { if (this.previewLayer && this._map.hasLayer(this.previewLayer)) { this._map.removeLayer(this.previewLayer); this.previewLayer = null; return true; } return false; }, showPreviewBounds: function (layerData) { if (layerData.geometry) { this.previewBounds = L.geoJson(layerData.geometry, { // fill/mask outside of bounds polygon with Leaflet.snogylop invert: true, // reduce unmasked areas appearing due to clipping while panning and zooming out renderer: L.svg({ padding: 1 }), color: '#333', fillOpacity: 0.4, weight: 2 }).addTo(this._map); } }, removePreviewBounds: function () { if (this.previewBounds && this._map.hasLayer(this.previewBounds)) { this._map.removeLayer(this.previewBounds); this.previewBounds = null; } }, deselectNode: function () { var selected = this.jstree.get_selected(); if (selected.length > 0) { this.jstree.deselect_node(selected[0]); } }, onBaselayerchange: function () { // execute after current input click handler, // otherwise added overlay checkbox state doesn't update setTimeout(L.Util.bind(function () { this.removePreviewBounds(); this.removePreviewLayer(); this.restoreActiveLayers(true); this.deselectNode(); }, this), 0); }, showPreview: function (layerData) { var layer = this.createLayer(layerData); this._map.addLayer(layer); this.removePreviewBounds(); if (!this.removePreviewLayer()) { this.saveRemoveActiveLayers(); this._map.once('baselayerchange', this.onBaselayerchange, this); } this.previewLayer = layer; this.showPreviewBounds(layerData); }, hidePreview: function (layer) { this._map.off('baselayerchange', this.onBaselayerchange, this); this.removePreviewBounds(); this.removePreviewLayer(); this.restoreActiveLayers(); } }); BR.layersTab = function (baseLayers, overlays, options) { return new BR.LayersTab(baseLayers, overlays, options); };