diff options
Diffstat (limited to 'ishtar_common/static/ol-layerswitcher/ol-layerswitcher.js')
-rw-r--r-- | ishtar_common/static/ol-layerswitcher/ol-layerswitcher.js | 725 |
1 files changed, 725 insertions, 0 deletions
diff --git a/ishtar_common/static/ol-layerswitcher/ol-layerswitcher.js b/ishtar_common/static/ol-layerswitcher/ol-layerswitcher.js new file mode 100644 index 000000000..d5ca3c7aa --- /dev/null +++ b/ishtar_common/static/ol-layerswitcher/ol-layerswitcher.js @@ -0,0 +1,725 @@ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('ol/control/Control'), require('ol/Observable'), require('ol/layer/Group')) : + typeof define === 'function' && define.amd ? define(['ol/control/Control', 'ol/Observable', 'ol/layer/Group'], factory) : + (global.LayerSwitcher = factory(global.ol.control.Control,global.ol.Observable,global.ol.layer.Group)); +}(this, (function (Control,ol_Observable,LayerGroup) { 'use strict'; + +Control = 'default' in Control ? Control['default'] : Control; +LayerGroup = 'default' in LayerGroup ? LayerGroup['default'] : LayerGroup; + +var classCallCheck = function (instance, Constructor) { + if (!(instance instanceof Constructor)) { + throw new TypeError("Cannot call a class as a function"); + } +}; + +var createClass = function () { + function defineProperties(target, props) { + for (var i = 0; i < props.length; i++) { + var descriptor = props[i]; + descriptor.enumerable = descriptor.enumerable || false; + descriptor.configurable = true; + if ("value" in descriptor) descriptor.writable = true; + Object.defineProperty(target, descriptor.key, descriptor); + } + } + + return function (Constructor, protoProps, staticProps) { + if (protoProps) defineProperties(Constructor.prototype, protoProps); + if (staticProps) defineProperties(Constructor, staticProps); + return Constructor; + }; +}(); + + + + + + + +var get = function get(object, property, receiver) { + if (object === null) object = Function.prototype; + var desc = Object.getOwnPropertyDescriptor(object, property); + + if (desc === undefined) { + var parent = Object.getPrototypeOf(object); + + if (parent === null) { + return undefined; + } else { + return get(parent, property, receiver); + } + } else if ("value" in desc) { + return desc.value; + } else { + var getter = desc.get; + + if (getter === undefined) { + return undefined; + } + + return getter.call(receiver); + } +}; + +var inherits = function (subClass, superClass) { + if (typeof superClass !== "function" && superClass !== null) { + throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); + } + + subClass.prototype = Object.create(superClass && superClass.prototype, { + constructor: { + value: subClass, + enumerable: false, + writable: true, + configurable: true + } + }); + if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; +}; + + + + + + + + + + + +var possibleConstructorReturn = function (self, call) { + if (!self) { + throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); + } + + return call && (typeof call === "object" || typeof call === "function") ? call : self; +}; + +/** + * @protected + */ +var CSS_PREFIX = 'layer-switcher-'; +/** + * OpenLayers LayerSwitcher Control, displays a list of layers and groups + * associated with a map which have a `title` property. + * + * To be shown in the LayerSwitcher panel layers must have a `title` property; + * base map layers should have a `type` property set to `base`. Group layers + * (`LayerGroup`) can be used to visually group layers together; a group + * with a `fold` property set to either `'open'` or `'close'` will be displayed + * with a toggle. + * + * See [BaseLayerOptions](#baselayeroptions) for a full list of LayerSwitcher + * properties for layers (`TileLayer`, `ImageLayer`, `VectorTile` etc.) and + * [GroupLayerOptions](#grouplayeroptions) for group layer (`LayerGroup`) + * LayerSwitcher properties. + * + * Layer and group properties can either be set by adding extra properties + * to their options when they are created or via their set method. + * + * Specify a `title` for a Layer by adding a `title` property to it's options object: + * ```javascript + * var lyr = new ol.layer.Tile({ + * // Specify a title property which will be displayed by the layer switcher + * title: 'OpenStreetMap', + * visible: true, + * source: new ol.source.OSM() + * }) + * ``` + * + * Alternatively the properties can be set via the `set` method after a layer has been created: + * ```javascript + * var lyr = new ol.layer.Tile({ + * visible: true, + * source: new ol.source.OSM() + * }) + * // Specify a title property which will be displayed by the layer switcher + * lyr.set('title', 'OpenStreetMap'); + * ``` + * + * To create a LayerSwitcher and add it to a map, create a new instance then pass it to the map's [`addControl` method](https://openlayers.org/en/latest/apidoc/module-ol_PluggableMap-PluggableMap.html#addControl). + * ```javascript + * var layerSwitcher = new LayerSwitcher({ + * reverse: true, + * groupSelectStyle: 'group' + * }); + * map.addControl(layerSwitcher); + * ``` + * + * @constructor + * @extends {ol/control/Control~Control} + * @param opt_options LayerSwitcher options, see [LayerSwitcher Options](#options) and [RenderOptions](#renderoptions) which LayerSwitcher `Options` extends for more details. + */ + +var LayerSwitcher = function (_Control) { + inherits(LayerSwitcher, _Control); + + function LayerSwitcher(opt_options) { + classCallCheck(this, LayerSwitcher); + + var options = Object.assign({}, opt_options); + var element = document.createElement('div'); + + var _this = possibleConstructorReturn(this, (LayerSwitcher.__proto__ || Object.getPrototypeOf(LayerSwitcher)).call(this, { element: element, target: options.target })); + + _this.activationMode = options.activationMode || 'mouseover'; + _this.startActive = options.startActive === true; + // TODO Next: Rename to showButtonContent + _this.label = options.label !== undefined ? options.label : ''; + // TODO Next: Rename to hideButtonContent + _this.collapseLabel = options.collapseLabel !== undefined ? options.collapseLabel : '\xBB'; + // TODO Next: Rename to showButtonTitle + _this.tipLabel = options.tipLabel ? options.tipLabel : 'Legend'; + // TODO Next: Rename to hideButtonTitle + _this.collapseTipLabel = options.collapseTipLabel ? options.collapseTipLabel : 'Collapse legend'; + _this.groupSelectStyle = LayerSwitcher.getGroupSelectStyle(options.groupSelectStyle); + _this.reverse = options.reverse !== false; + _this.mapListeners = []; + _this.hiddenClassName = 'ol-unselectable ol-control layer-switcher'; + if (LayerSwitcher.isTouchDevice_()) { + _this.hiddenClassName += ' touch'; + } + _this.shownClassName = 'shown'; + element.className = _this.hiddenClassName; + _this.button = document.createElement('button'); + element.appendChild(_this.button); + _this.panel = document.createElement('div'); + _this.panel.className = 'panel'; + element.appendChild(_this.panel); + LayerSwitcher.enableTouchScroll_(_this.panel); + element.classList.add(CSS_PREFIX + 'group-select-style-' + _this.groupSelectStyle); + element.classList.add(CSS_PREFIX + 'activation-mode-' + _this.activationMode); + if (_this.activationMode === 'click') { + // TODO Next: Remove in favour of layer-switcher-activation-mode-click + element.classList.add('activationModeClick'); + _this.button.onclick = function (e) { + var evt = e || window.event; + if (_this.element.classList.contains(_this.shownClassName)) { + _this.hidePanel(); + } else { + _this.showPanel(); + } + evt.preventDefault(); + }; + } else { + _this.button.onmouseover = function () { + _this.showPanel(); + }; + _this.button.onclick = function (e) { + var evt = e || window.event; + _this.showPanel(); + evt.preventDefault(); + }; + _this.panel.onmouseout = function (evt) { + if (!_this.panel.contains(evt.relatedTarget)) { + _this.hidePanel(); + } + }; + } + _this.updateButton(); + return _this; + } + /** + * Set the map instance the control is associated with. + * @param map The map instance. + */ + + + createClass(LayerSwitcher, [{ + key: 'setMap', + value: function setMap(map) { + var _this2 = this; + + // Clean up listeners associated with the previous map + for (var i = 0; i < this.mapListeners.length; i++) { + ol_Observable.unByKey(this.mapListeners[i]); + } + this.mapListeners.length = 0; + // Wire up listeners etc. and store reference to new map + get(LayerSwitcher.prototype.__proto__ || Object.getPrototypeOf(LayerSwitcher.prototype), 'setMap', this).call(this, map); + if (map) { + if (this.startActive) { + this.showPanel(); + } else { + this.renderPanel(); + } + if (this.activationMode !== 'click') { + this.mapListeners.push(map.on('pointerdown', function () { + _this2.hidePanel(); + })); + } + } + } + /** + * Show the layer panel. Fires `'show'` event. + * @fires LayerSwitcher#show + */ + + }, { + key: 'showPanel', + value: function showPanel() { + if (!this.element.classList.contains(this.shownClassName)) { + this.element.classList.add(this.shownClassName); + this.updateButton(); + this.renderPanel(); + } + /** + * Event triggered after the panel has been shown. + * Listen to the event via the `on` or `once` methods; for example: + * ```js + * var layerSwitcher = new LayerSwitcher(); + * map.addControl(layerSwitcher); + * + * layerSwitcher.on('show', evt => { + * console.log('show', evt); + * }); + * @event LayerSwitcher#show + */ + this.dispatchEvent('show'); + } + /** + * Hide the layer panel. Fires `'hide'` event. + * @fires LayerSwitcher#hide + */ + + }, { + key: 'hidePanel', + value: function hidePanel() { + if (this.element.classList.contains(this.shownClassName)) { + this.element.classList.remove(this.shownClassName); + this.updateButton(); + } + /** + * Event triggered after the panel has been hidden. + * @event LayerSwitcher#hide + */ + this.dispatchEvent('hide'); + } + /** + * Update button text content and attributes based on current + * state + */ + + }, { + key: 'updateButton', + value: function updateButton() { + if (this.element.classList.contains(this.shownClassName)) { + this.button.textContent = this.collapseLabel; + this.button.setAttribute('title', this.collapseTipLabel); + this.button.setAttribute('aria-label', this.collapseTipLabel); + } else { + this.button.textContent = this.label; + this.button.setAttribute('title', this.tipLabel); + this.button.setAttribute('aria-label', this.tipLabel); + } + } + /** + * Re-draw the layer panel to represent the current state of the layers. + */ + + }, { + key: 'renderPanel', + value: function renderPanel() { + this.dispatchEvent('render'); + LayerSwitcher.renderPanel(this.getMap(), this.panel, { + groupSelectStyle: this.groupSelectStyle, + reverse: this.reverse + }); + this.dispatchEvent('rendercomplete'); + } + /** + * **_[static]_** - Re-draw the layer panel to represent the current state of the layers. + * @param map The OpenLayers Map instance to render layers for + * @param panel The DOM Element into which the layer tree will be rendered + * @param options Options for panel, group, and layers + */ + + }], [{ + key: 'renderPanel', + value: function renderPanel(map, panel, options) { + // Create the event. + var render_event = new Event('render'); + // Dispatch the event. + panel.dispatchEvent(render_event); + options = options || {}; + options.groupSelectStyle = LayerSwitcher.getGroupSelectStyle(options.groupSelectStyle); + LayerSwitcher.ensureTopVisibleBaseLayerShown(map, options.groupSelectStyle); + while (panel.firstChild) { + panel.removeChild(panel.firstChild); + } + // Reset indeterminate state for all layers and groups before + // applying based on groupSelectStyle + LayerSwitcher.forEachRecursive(map, function (l, _idx, _a) { + l.set('indeterminate', false); + }); + if (options.groupSelectStyle === 'children' || options.groupSelectStyle === 'none') { + // Set visibile and indeterminate state of groups based on + // their children's visibility + LayerSwitcher.setGroupVisibility(map); + } else if (options.groupSelectStyle === 'group') { + // Set child indetermiate state based on their parent's visibility + LayerSwitcher.setChildVisibility(map); + } + var ul = document.createElement('ul'); + panel.appendChild(ul); + // passing two map arguments instead of lyr as we're passing the map as the root of the layers tree + LayerSwitcher.renderLayers_(map, map, ul, options, function render(_changedLyr) { + LayerSwitcher.renderPanel(map, panel, options); + }); + // Create the event. + var rendercomplete_event = new Event('rendercomplete'); + // Dispatch the event. + panel.dispatchEvent(rendercomplete_event); + } + /** + * **_[static]_** - Determine if a given layer group contains base layers + * @param grp Group to test + */ + + }, { + key: 'isBaseGroup', + value: function isBaseGroup(grp) { + if (grp instanceof LayerGroup) { + var lyrs = grp.getLayers().getArray(); + return lyrs.length && lyrs[0].get('type') === 'base'; + } else { + return false; + } + } + }, { + key: 'setGroupVisibility', + value: function setGroupVisibility(map) { + // Get a list of groups, with the deepest first + var groups = LayerSwitcher.getGroupsAndLayers(map, function (l) { + return l instanceof LayerGroup && !l.get('combine') && !LayerSwitcher.isBaseGroup(l); + }).reverse(); + // console.log(groups.map(g => g.get('title'))); + groups.forEach(function (grp) { + // TODO Can we use getLayersArray, is it public in the esm build? + var descendantVisibility = grp.getLayersArray().map(function (l) { + var state = l.getVisible(); + // console.log('>', l.get('title'), state); + return state; + }); + // console.log(descendantVisibility); + if (descendantVisibility.every(function (v) { + return v === true; + })) { + grp.setVisible(true); + grp.set('indeterminate', false); + } else if (descendantVisibility.every(function (v) { + return v === false; + })) { + grp.setVisible(false); + grp.set('indeterminate', false); + } else { + grp.setVisible(true); + grp.set('indeterminate', true); + } + }); + } + }, { + key: 'setChildVisibility', + value: function setChildVisibility(map) { + // console.log('setChildVisibility'); + var groups = LayerSwitcher.getGroupsAndLayers(map, function (l) { + return l instanceof LayerGroup && !l.get('combine') && !LayerSwitcher.isBaseGroup(l); + }); + groups.forEach(function (grp) { + var group = grp; + // console.log(group.get('title')); + var groupVisible = group.getVisible(); + var groupIndeterminate = group.get('indeterminate'); + group.getLayers().getArray().forEach(function (l) { + l.set('indeterminate', false); + if ((!groupVisible || groupIndeterminate) && l.getVisible()) { + l.set('indeterminate', true); + } + }); + }); + } + /** + * Ensure only the top-most base layer is visible if more than one is visible. + * @param map The map instance. + * @param groupSelectStyle + * @protected + */ + + }, { + key: 'ensureTopVisibleBaseLayerShown', + value: function ensureTopVisibleBaseLayerShown(map, groupSelectStyle) { + var lastVisibleBaseLyr = void 0; + LayerSwitcher.forEachRecursive(map, function (lyr, _idx, _arr) { + if (lyr.get('type') === 'base' && lyr.getVisible()) { + lastVisibleBaseLyr = lyr; + } + }); + if (lastVisibleBaseLyr) LayerSwitcher.setVisible_(map, lastVisibleBaseLyr, true, groupSelectStyle); + } + /** + * **_[static]_** - Get an Array of all layers and groups displayed by the LayerSwitcher (has a `'title'` property) + * contained by the specified map or layer group; optionally filtering via `filterFn` + * @param grp The map or layer group for which layers are found. + * @param filterFn Optional function used to filter the returned layers + */ + + }, { + key: 'getGroupsAndLayers', + value: function getGroupsAndLayers(grp, filterFn) { + var layers = []; + filterFn = filterFn || function (_lyr, _idx, _arr) { + return true; + }; + LayerSwitcher.forEachRecursive(grp, function (lyr, idx, arr) { + if (lyr.get('title')) { + if (filterFn(lyr, idx, arr)) { + layers.push(lyr); + } + } + }); + return layers; + } + /** + * Toggle the visible state of a layer. + * Takes care of hiding other layers in the same exclusive group if the layer + * is toggle to visible. + * @protected + * @param map The map instance. + * @param lyr layer whose visibility will be toggled. + * @param visible Set whether the layer is shown + * @param groupSelectStyle + * @protected + */ + + }, { + key: 'setVisible_', + value: function setVisible_(map, lyr, visible, groupSelectStyle) { + // console.log(lyr.get('title'), visible, groupSelectStyle); + lyr.setVisible(visible); + if (visible && lyr.get('type') === 'base') { + // Hide all other base layers regardless of grouping + LayerSwitcher.forEachRecursive(map, function (l, _idx, _arr) { + if (l != lyr && l.get('type') === 'base') { + l.setVisible(false); + } + }); + } + if (lyr instanceof LayerGroup && !lyr.get('combine') && groupSelectStyle === 'children') { + lyr.getLayers().forEach(function (l) { + LayerSwitcher.setVisible_(map, l, lyr.getVisible(), groupSelectStyle); + }); + } + } + /** + * Render all layers that are children of a group. + * @param map The map instance. + * @param lyr Layer to be rendered (should have a title property). + * @param idx Position in parent group list. + * @param options Options for groups and layers + * @protected + */ + + }, { + key: 'renderLayer_', + value: function renderLayer_(map, lyr, idx, options, render) { + var li = document.createElement('li'); + var lyrTitle = lyr.get('title'); + var checkboxId = LayerSwitcher.uuid(); + var label = document.createElement('label'); + if (lyr instanceof LayerGroup && !lyr.get('combine')) { + var isBaseGroup = LayerSwitcher.isBaseGroup(lyr); + li.classList.add('group'); + if (isBaseGroup) { + li.classList.add(CSS_PREFIX + 'base-group'); + } + // Group folding + if (lyr.get('fold')) { + li.classList.add(CSS_PREFIX + 'fold'); + li.classList.add(CSS_PREFIX + lyr.get('fold')); + var btn = document.createElement('button'); + btn.onclick = function (e) { + var evt = e || window.event; + LayerSwitcher.toggleFold_(lyr, li); + evt.preventDefault(); + }; + li.appendChild(btn); + } + if (!isBaseGroup && options.groupSelectStyle != 'none') { + var input = document.createElement('input'); + input.type = 'checkbox'; + input.id = checkboxId; + input.checked = lyr.getVisible(); + input.indeterminate = lyr.get('indeterminate'); + input.onchange = function (e) { + var target = e.target; + LayerSwitcher.setVisible_(map, lyr, target.checked, options.groupSelectStyle); + render(lyr); + }; + li.appendChild(input); + label.htmlFor = checkboxId; + } + label.innerHTML = lyrTitle; + li.appendChild(label); + var ul = document.createElement('ul'); + li.appendChild(ul); + LayerSwitcher.renderLayers_(map, lyr, ul, options, render); + } else { + li.className = 'layer'; + var _input = document.createElement('input'); + if (lyr.get('type') === 'base') { + _input.type = 'radio'; + } else { + _input.type = 'checkbox'; + } + _input.id = checkboxId; + _input.checked = lyr.get('visible'); + _input.indeterminate = lyr.get('indeterminate'); + _input.onchange = function (e) { + var target = e.target; + LayerSwitcher.setVisible_(map, lyr, target.checked, options.groupSelectStyle); + render(lyr); + }; + li.appendChild(_input); + label.htmlFor = checkboxId; + label.innerHTML = lyrTitle; + var rsl = map.getView().getResolution(); + if (rsl >= lyr.getMaxResolution() || rsl < lyr.getMinResolution()) { + label.className += ' disabled'; + } else if (lyr.getMinZoom && lyr.getMaxZoom) { + var zoom = map.getView().getZoom(); + if (zoom <= lyr.getMinZoom() || zoom > lyr.getMaxZoom()) { + label.className += ' disabled'; + } + } + li.appendChild(label); + } + return li; + } + /** + * Render all layers that are children of a group. + * @param map The map instance. + * @param lyr Group layer whose children will be rendered. + * @param elm DOM element that children will be appended to. + * @param options Options for groups and layers + * @protected + */ + + }, { + key: 'renderLayers_', + value: function renderLayers_(map, lyr, elm, options, render) { + var lyrs = lyr.getLayers().getArray().slice(); + if (options.reverse) lyrs = lyrs.reverse(); + for (var i = 0, l; i < lyrs.length; i++) { + l = lyrs[i]; + if (l.get('title')) { + elm.appendChild(LayerSwitcher.renderLayer_(map, l, i, options, render)); + } + } + } + /** + * **_[static]_** - Call the supplied function for each layer in the passed layer group + * recursing nested groups. + * @param lyr The layer group to start iterating from. + * @param fn Callback which will be called for each layer + * found under `lyr`. + */ + + }, { + key: 'forEachRecursive', + value: function forEachRecursive(lyr, fn) { + lyr.getLayers().forEach(function (lyr, idx, a) { + fn(lyr, idx, a); + if (lyr instanceof LayerGroup) { + LayerSwitcher.forEachRecursive(lyr, fn); + } + }); + } + /** + * **_[static]_** - Generate a UUID + * Adapted from http://stackoverflow.com/a/2117523/526860 + * @returns {String} UUID + */ + + }, { + key: 'uuid', + value: function uuid() { + return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) { + var r = Math.random() * 16 | 0, + v = c == 'x' ? r : r & 0x3 | 0x8; + return v.toString(16); + }); + } + /** + * Apply workaround to enable scrolling of overflowing content within an + * element. Adapted from https://gist.github.com/chrismbarr/4107472 + * @param elm Element on which to enable touch scrolling + * @protected + */ + + }, { + key: 'enableTouchScroll_', + value: function enableTouchScroll_(elm) { + if (LayerSwitcher.isTouchDevice_()) { + var scrollStartPos = 0; + elm.addEventListener('touchstart', function (event) { + scrollStartPos = this.scrollTop + event.touches[0].pageY; + }, false); + elm.addEventListener('touchmove', function (event) { + this.scrollTop = scrollStartPos - event.touches[0].pageY; + }, false); + } + } + /** + * Determine if the current browser supports touch events. Adapted from + * https://gist.github.com/chrismbarr/4107472 + * @returns {Boolean} True if client can have 'TouchEvent' event + * @protected + */ + + }, { + key: 'isTouchDevice_', + value: function isTouchDevice_() { + try { + document.createEvent('TouchEvent'); + return true; + } catch (e) { + return false; + } + } + /** + * Fold/unfold layer group + * @param lyr Layer group to fold/unfold + * @param li List item containing layer group + * @protected + */ + + }, { + key: 'toggleFold_', + value: function toggleFold_(lyr, li) { + li.classList.remove(CSS_PREFIX + lyr.get('fold')); + lyr.set('fold', lyr.get('fold') === 'open' ? 'close' : 'open'); + li.classList.add(CSS_PREFIX + lyr.get('fold')); + } + /** + * If a valid groupSelectStyle value is not provided then return the default + * @param groupSelectStyle The string to check for validity + * @returns The value groupSelectStyle, if valid, the default otherwise + * @protected + */ + + }, { + key: 'getGroupSelectStyle', + value: function getGroupSelectStyle(groupSelectStyle) { + return ['none', 'children', 'group'].indexOf(groupSelectStyle) >= 0 ? groupSelectStyle : 'children'; + } + }]); + return LayerSwitcher; +}(Control); +if (window['ol'] && window['ol']['control']) { + window['ol']['control']['LayerSwitcher'] = LayerSwitcher; +} + +return LayerSwitcher; + +}))); |