| |
|
|
| let controlContainer; |
|
|
| |
| |
| L.Control.GroupedLayers = L.Control.extend({ |
| options: { |
| sortLayers: true, |
| sortGroups: true, |
| sortBaseLayers: false, |
| collapsed: true, |
| position: 'topright', |
| autoZIndex: true, |
| exclusiveGroups: [], |
| exclusiveOptionalGroups: [], |
| groupCheckboxes: false, |
| groupsCollapsable: false, |
| groupsExpandedClass: "leaflet-control-layers-group-collapse-default", |
| groupsCollapsedClass: "leaflet-control-layers-group-expand-default", |
| sortFunction: function (nameA, nameB) { |
| return nameA.localeCompare(nameB); |
| } |
| }, |
| |
| initialize: function (baseLayers, groupedOverlays, options) { |
| var i, j; |
| L.Util.setOptions(this, options); |
| |
| this._layers = []; |
| this._lastZIndex = 0; |
| this._handlingClick = false; |
| this._groupList = []; |
| this._domGroups = []; |
| |
| for (i in baseLayers) { |
| this._addLayer(baseLayers[i], i); |
| } |
| |
| for (i in groupedOverlays) { |
| for (j in groupedOverlays[i]) { |
| this._addLayer(groupedOverlays[i][j], j, i, true); |
| } |
| } |
| }, |
| |
| onAdd: function (map) { |
| this._initLayout(); |
| this._update(); |
| |
| map |
| .on('layeradd', this._onLayerChange, this) |
| .on('layerremove', this._onLayerChange, this); |
| |
| return this._container; |
| }, |
| |
| addTo: function (map) { |
| L.Control.prototype.addTo.call(this, map); |
| |
| return this._expandIfNotCollapsed(); |
| }, |
| |
| onRemove: function (map) { |
| map |
| .off('layeradd', this._onLayerChange, this) |
| .off('layerremove', this._onLayerChange, this); |
| }, |
| |
| addBaseLayer: function (layer, name) { |
| this._addLayer(layer, name); |
| this._update(); |
| return this; |
| }, |
| |
| addOverlay: function (layer, name, options) { |
| this._addLayer(layer, name, {...options, overlay: true}); |
| this._update(); |
| return this; |
| }, |
| |
| removeLayer: function (layer) { |
| var id = L.Util.stamp(layer); |
| var _layer = this._getLayer(id); |
| if (_layer) { |
| this._layers.splice(this._layers.indexOf(_layer), 1); |
| } |
| this._update(); |
| return this; |
| }, |
| |
| _getLayer: function (id) { |
| for (var layer of this._layers) { |
| if (layer && L.stamp(layer.layer) === id) { |
| return layer; |
| } |
| } |
| }, |
| |
| _initLayout: function () { |
| var className = 'leaflet-control-layers', |
| container = this._container = L.DomUtil.create('div', className), |
| collapsed = this.options.collapsed; |
| |
| |
| container.setAttribute('aria-haspopup', true); |
| |
| L.DomEvent.disableClickPropagation(container); |
| L.DomEvent.disableScrollPropagation(container); |
| |
| var form = this._form = L.DomUtil.create('form', className + '-list'); |
| |
| if (collapsed) { |
| this._map.on('click', this._collapse, this); |
| |
| if (!L.Browser.android) { |
| L.DomEvent.on(container, { |
| mouseenter: this._expand, |
| mouseleave: this._collapse |
| }, this); |
| } |
| } |
| |
| var link = this._layersLink = L.DomUtil.create('a', className + '-toggle', container); |
| link.href = '#'; |
| link.title = 'Layers'; |
| |
| if (L.Browser.touch) { |
| L.DomEvent.on(link, 'click', L.DomEvent.stop); |
| L.DomEvent.on(link, 'click', this._expand, this); |
| } else { |
| L.DomEvent.on(link, 'focus', this._expand, this); |
| } |
| |
| if (!collapsed) { |
| this._expand(); |
| } |
| |
| this._baseLayersList = L.DomUtil.create('div', className + '-base', form); |
| this._separator = L.DomUtil.create('div', className + '-separator', form); |
| this._overlaysList = L.DomUtil.create('div', className + '-overlays', form); |
| |
| container.appendChild(form); |
|
|
| controlContainer = container; |
| }, |
| |
| _addLayer: function (layer, name, options) { |
| options = {...options}; |
| if (options.image) { |
| name = `<img src="${options.image}" class='control-item-image' /> ${name}`; |
| } |
|
|
| var _layer = { |
| layer: layer, |
| name: name, |
| overlay: options.overlay, |
| key: options.layerKey, |
| }; |
| this._layers.push(_layer); |
| |
| const group = options.groupName || ''; |
| var groupId = this._indexOf(this._groupList, group); |
| |
| if (groupId === -1) { |
| groupId = this._groupList.push(group) - 1; |
| } |
| |
| var exclusive = (this._indexOf(this.options.exclusiveGroups, group) !== -1); |
| var exclusiveOptional = (this._indexOf(this.options.exclusiveOptionalGroups, group) !== -1); |
| |
| _layer.group = { |
| name: group, |
| id: groupId, |
| exclusive: exclusive, |
| exclusiveOptional: exclusiveOptional, |
| key: options.groupKey, |
| collapsed: options.groupCollapsed, |
| }; |
| |
| if (this.options.autoZIndex && layer.setZIndex) { |
| this._lastZIndex++; |
| layer.setZIndex(this._lastZIndex); |
| } |
| |
| if (this.options.sortLayers) { |
| this._layers.sort(L.bind(function (a, b) { |
| if (a.overlay === true && b.overlay === true) { |
| return this.options.sortFunction(a.name, b.name); |
| } |
| }, this)); |
| } |
| |
| if (this.options.sortBaseLayers) { |
| this._layers.sort(L.bind(function (a, b) { |
| if (a.overlay === undefined && b.overlay === undefined) { |
| return this.options.sortFunction(a.name, b.name); |
| } |
| }, this)); |
| } |
| |
| if (this.options.sortGroups) { |
| this._layers.sort(L.bind(function (a, b) { |
| if (a.group.exclusiveOptional && !b.group.exclusiveOptional) { |
| return -1; |
| } |
| if (!a.group.exclusiveOptional && b.group.exclusiveOptional) { |
| return 1; |
| } |
| return this.options.sortFunction(a.group.name, b.group.name); |
| }, this)); |
| } |
| |
| this._expandIfNotCollapsed(); |
| }, |
| |
| _update: function () { |
| if (!this._container) { |
| return; |
| } |
| |
| this._baseLayersList.innerHTML = ''; |
| this._overlaysList.innerHTML = ''; |
| this._domGroups.length = 0; |
| |
| var baseLayersPresent = false, |
| overlaysPresent = false; |
| |
| for (var obj of this._layers) { |
| this._addItem(obj); |
| overlaysPresent = overlaysPresent || obj.overlay; |
| baseLayersPresent = baseLayersPresent || !obj.overlay; |
| } |
| |
| if (this.options.groupCheckboxes) { |
| this._refreshGroupsCheckStates(); |
| } |
| |
| this._separator.style.display = overlaysPresent && baseLayersPresent ? '' : 'none'; |
| }, |
| |
| _onLayerChange: function (e) { |
| var obj = this._getLayer(L.Util.stamp(e.layer)), |
| type; |
| |
| if (!obj) { |
| return; |
| } |
| |
| if (!this._handlingClick) { |
| this._update(); |
| } |
| |
| if (obj.overlay) { |
| type = e.type === 'layeradd' ? 'overlayadd' : 'overlayremove'; |
| } else { |
| type = e.type === 'layeradd' ? 'baselayerchange' : null; |
| } |
| |
| if (type) { |
| this._map.fire(type, obj); |
| } |
| }, |
| |
| _createRadioElement: function (name, checked) { |
| var radio = document.createElement('input'); |
| radio.type = 'radio'; |
| radio.name = name; |
| radio.className = 'leaflet-control-layers-selector'; |
| radio.checked = checked; |
| |
| return radio; |
| }, |
|
|
| _createExclusiveCheckElement: function (name, checked) { |
| var radio = document.createElement('input'); |
| radio.type = 'checkbox'; |
| radio.name = name; |
| radio.className = `leaflet-control-layers-selector ${name}`; |
| radio.checked = checked; |
| radio.onchange = e => { |
| if (!e.target.checked) { |
| return; |
| } |
| var radios = document.getElementsByClassName(name); |
| for (var r of radios) { |
| if (r === e.target || !r.checked) { |
| continue; |
| } |
| r.checked = false; |
| this._onInputClick(); |
| } |
| }; |
| |
| return radio; |
| }, |
| |
| _addItem: function (obj) { |
| var label = document.createElement('label'), |
| input, |
| checked = this._map.hasLayer(obj.layer), |
| container, |
| groupRadioName; |
| |
| if (obj.overlay) { |
| if (obj.group.exclusive) { |
| groupRadioName = 'leaflet-exclusive-group-layer-' + obj.group.id; |
| input = this._createRadioElement(groupRadioName, checked); |
| } else if (obj.group.exclusiveOptional) { |
| groupRadioName = 'leaflet-exclusive-group-layer-' + obj.group.id; |
| input = this._createExclusiveCheckElement(groupRadioName, checked); |
| } else { |
| input = document.createElement('input'); |
| input.type = 'checkbox'; |
| input.className = 'leaflet-control-layers-selector'; |
| input.defaultChecked = checked; |
| } |
| } else { |
| input = this._createRadioElement('leaflet-base-layers', checked); |
| } |
| |
| input.layerId = L.Util.stamp(obj.layer); |
| input.groupID = obj.group.id; |
| L.DomEvent.on(input, 'click', this._onInputClick, this); |
| L.DomEvent.on(input, 'click', () => { |
| if (obj.layer.key && !obj.layer.options.overlay) { |
| controlContainer.dispatchEvent(new CustomEvent('layerToggle', { |
| bubbles: false, |
| detail: { |
| key: obj.layer.key, |
| checked: input.checked, |
| }, |
| })); |
| } else if (obj.layer.options.overlay) { |
| controlContainer.dispatchEvent(new CustomEvent('overlayToggle', { |
| bubbles: false, |
| detail: { |
| key: obj.layer.options.name, |
| checked: input.checked, |
| }, |
| })); |
| } |
| }, this); |
| |
| var name = document.createElement('span'); |
| name.innerHTML = ' ' + obj.name; |
| |
| label.appendChild(input); |
| label.appendChild(name); |
| |
| if (obj.overlay) { |
| container = this._overlaysList; |
| |
| var groupContainer = this._domGroups[obj.group.id]; |
| |
| |
| if (!groupContainer) { |
| groupContainer = document.createElement('div'); |
| groupContainer.className = 'leaflet-control-layers-group'; |
| groupContainer.id = 'leaflet-control-layers-group-' + obj.group.id; |
| groupContainer.dataset.key = obj.group.key; |
| |
| var groupLabel = document.createElement('label'); |
| groupLabel.className = 'leaflet-control-layers-group-label'; |
| |
| if (obj.group.name !== '' && !obj.group.exclusive && !obj.group.exclusiveOptional) { |
| |
| if (this.options.groupCheckboxes) { |
| var groupInput = document.createElement('input'); |
| groupInput.type = 'checkbox'; |
| groupInput.className = 'leaflet-control-layers-group-selector'; |
| groupInput.groupID = obj.group.id; |
| groupInput.legend = this; |
| groupInput.dataset.key = obj.group.key; |
| L.DomEvent.on(groupInput, 'click', this._onGroupInputClick, groupInput); |
| groupLabel.appendChild(groupInput); |
| } |
| } |
| |
| if (this.options.groupsCollapsable){ |
| groupContainer.classList.add("group-collapsable"); |
| if (obj.group.collapsed) { |
| groupContainer.classList.add("collapsed"); |
| } |
| |
| var groupMin = document.createElement('span'); |
| groupMin.className = 'leaflet-control-layers-group-collapse '+this.options.groupsExpandedClass; |
| groupLabel.appendChild(groupMin); |
| |
| var groupMax = document.createElement('span'); |
| groupMax.className = 'leaflet-control-layers-group-expand '+this.options.groupsCollapsedClass; |
| groupLabel.appendChild(groupMax); |
| |
| L.DomEvent.on(groupLabel, 'click', this._onGroupCollapseToggle, groupContainer); |
| } |
| |
| var groupName = document.createElement('span'); |
| groupName.className = 'leaflet-control-layers-group-name'; |
| groupName.innerHTML = obj.group.name; |
| groupLabel.appendChild(groupName); |
| |
| groupContainer.appendChild(groupLabel); |
| container.appendChild(groupContainer); |
| |
| this._domGroups[obj.group.id] = groupContainer; |
| } |
| |
| container = groupContainer; |
| } else { |
| container = this._baseLayersList; |
| } |
| |
| container.appendChild(label); |
| |
| return label; |
| }, |
| |
| _onGroupCollapseToggle: function (event) { |
| L.DomEvent.stopPropagation(event); |
| L.DomEvent.preventDefault(event); |
| if (this.classList.contains("group-collapsable") && this.classList.contains("collapsed")){ |
| this.classList.remove("collapsed"); |
| } else if (this.classList.contains("group-collapsable") && !this.classList.contains("collapsed")) { |
| this.classList.add("collapsed"); |
| } |
| if (this.dataset.key) { |
| controlContainer.dispatchEvent(new CustomEvent('groupCollapseToggle', { |
| bubbles: false, |
| detail: { |
| key: this.dataset.key, |
| collapsed: this.classList.contains("collapsed"), |
| }, |
| })); |
| } |
| }, |
| |
| _onGroupInputClick: function (event) { |
| L.DomEvent.stopPropagation(event); |
| var obj; |
| |
| var this_legend = this.legend; |
| this_legend._handlingClick = true; |
| |
| var inputs = this_legend._form.getElementsByTagName('input'); |
| |
| for (var input of inputs) { |
| if (input.groupID === this.groupID && input.className === 'leaflet-control-layers-selector') { |
| input.checked = this.checked; |
| obj = this_legend._getLayer(input.layerId); |
| if (input.checked && !this_legend._map.hasLayer(obj.layer)) { |
| this_legend._map.addLayer(obj.layer); |
| } else if (!input.checked && this_legend._map.hasLayer(obj.layer)) { |
| this_legend._map.removeLayer(obj.layer); |
| } |
| } |
| } |
|
|
| if (this.dataset.key) { |
| controlContainer.dispatchEvent(new CustomEvent('groupToggle', { |
| bubbles: false, |
| detail: { |
| key: this.dataset.key, |
| checked: this.checked, |
| }, |
| })); |
| } |
| |
| this_legend._handlingClick = false; |
| }, |
| |
| _onInputClick: function () { |
| var obj, |
| inputs = this._form.getElementsByClassName('leaflet-control-layers-selector'), |
| toBeRemoved, |
| toBeAdded; |
| |
| this._handlingClick = true; |
| |
| for (var input of inputs) { |
| obj = this._getLayer(input.layerId); |
| if (input.checked && !this._map.hasLayer(obj.layer)) { |
| toBeAdded = obj.layer; |
| } else if (!input.checked && this._map.hasLayer(obj.layer)) { |
| toBeRemoved = obj.layer; |
| } |
| } |
| |
| if (toBeRemoved !== undefined) { |
| this._map.removeLayer(toBeRemoved); |
| } |
| if (toBeAdded !== undefined) { |
| this._map.addLayer(toBeAdded); |
| } |
| |
| if (this.options.groupCheckboxes) { |
| this._refreshGroupsCheckStates(); |
| } |
| |
| this._handlingClick = false; |
| }, |
| |
| _refreshGroupsCheckStates: function () { |
| for (var i = 0; i < this._domGroups.length; i++) { |
| var groupContainer = this._domGroups[i]; |
| if (groupContainer) { |
| var groupInput = groupContainer.getElementsByClassName('leaflet-control-layers-group-selector')[0]; |
| var groupItemInputs = groupContainer.querySelectorAll('input.leaflet-control-layers-selector'); |
| var checkedGroupItemInputs = groupContainer.querySelectorAll('input.leaflet-control-layers-selector:checked'); |
| |
| if (groupInput) { |
| groupInput.indeterminate = false; |
| if (checkedGroupItemInputs.length === groupItemInputs.length) { |
| groupInput.checked = true; |
| } else if (checkedGroupItemInputs.length === 0) { |
| groupInput.checked = false; |
| } else { |
| groupInput.indeterminate = true; |
| } |
| } |
| } |
| } |
| }, |
| |
| _expand: function () { |
| L.DomUtil.addClass(this._container, 'leaflet-control-layers-expanded'); |
| this._form.style.height = null; |
| var acceptableHeight = this._map.getSize().y - (this._container.offsetTop + 50); |
| if (acceptableHeight < this._form.clientHeight) { |
| L.DomUtil.addClass(this._form, 'leaflet-control-layers-scrollbar'); |
| this._form.style.height = acceptableHeight + 'px'; |
| } else { |
| L.DomUtil.removeClass(this._form, 'leaflet-control-layers-scrollbar'); |
| } |
| |
| return this; |
| }, |
| |
| _expandIfNotCollapsed: function () { |
| if (this._map && !this.options.collapsed) { |
| this._expand(); |
| } |
| return this; |
| }, |
| |
| _collapse: function () { |
| this._container.className = this._container.className.replace(' leaflet-control-layers-expanded', ''); |
| }, |
| |
| _indexOf: function (arr, obj) { |
| for (var i = 0, j = arr.length; i < j; i++) { |
| if (arr[i] === obj) { |
| return i; |
| } |
| } |
| return -1; |
| }, |
| on: (eventName, handler, options) => { |
| controlContainer.addEventListener(eventName, handler, options); |
| }, |
| once: (eventName, handler, options) => { |
| controlContainer.addEventListener(eventName, handler, {...options, once: true}); |
| }, |
| off: (eventName, handler, options) => { |
| controlContainer.removeEventListener(eventName, handler, options); |
| }, |
| }); |
| |
| L.control.groupedLayers = function (baseLayers, groupedOverlays, options) { |
| return new L.Control.GroupedLayers(baseLayers, groupedOverlays, options); |
| }; |
| |
|
|