|
1
|
|
|
(function (global, factory) { |
|
2
|
|
|
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('ol/control/Control'), require('ol/Observable'), require('ol/layer/Group')) : |
|
3
|
|
|
typeof define === 'function' && define.amd ? define(['ol/control/Control', 'ol/Observable', 'ol/layer/Group'], factory) : |
|
|
|
|
|
|
4
|
|
|
(global.LayerSwitcher = factory(global.ol.control.Control,global.ol.Observable,global.ol.layer.Group)); |
|
5
|
|
|
}(this, (function (Control,ol_Observable,LayerGroup) { 'use strict'; |
|
6
|
|
|
|
|
7
|
|
|
Control = 'default' in Control ? Control['default'] : Control; |
|
8
|
|
|
LayerGroup = 'default' in LayerGroup ? LayerGroup['default'] : LayerGroup; |
|
9
|
|
|
|
|
10
|
|
|
var classCallCheck = function (instance, Constructor) { |
|
11
|
|
|
if (!(instance instanceof Constructor)) { |
|
12
|
|
|
throw new TypeError("Cannot call a class as a function"); |
|
13
|
|
|
} |
|
14
|
|
|
}; |
|
15
|
|
|
|
|
16
|
|
|
var createClass = function () { |
|
17
|
|
|
function defineProperties(target, props) { |
|
18
|
|
|
for (var i = 0; i < props.length; i++) { |
|
19
|
|
|
var descriptor = props[i]; |
|
20
|
|
|
descriptor.enumerable = descriptor.enumerable || false; |
|
21
|
|
|
descriptor.configurable = true; |
|
22
|
|
|
if ("value" in descriptor) descriptor.writable = true; |
|
|
|
|
|
|
23
|
|
|
Object.defineProperty(target, descriptor.key, descriptor); |
|
24
|
|
|
} |
|
25
|
|
|
} |
|
26
|
|
|
|
|
27
|
|
|
return function (Constructor, protoProps, staticProps) { |
|
28
|
|
|
if (protoProps) defineProperties(Constructor.prototype, protoProps); |
|
|
|
|
|
|
29
|
|
|
if (staticProps) defineProperties(Constructor, staticProps); |
|
|
|
|
|
|
30
|
|
|
return Constructor; |
|
31
|
|
|
}; |
|
32
|
|
|
}(); |
|
33
|
|
|
|
|
34
|
|
|
|
|
35
|
|
|
|
|
36
|
|
|
|
|
37
|
|
|
|
|
38
|
|
|
|
|
39
|
|
|
|
|
40
|
|
|
var get = function get(object, property, receiver) { |
|
41
|
|
|
if (object === null) object = Function.prototype; |
|
|
|
|
|
|
42
|
|
|
var desc = Object.getOwnPropertyDescriptor(object, property); |
|
43
|
|
|
|
|
44
|
|
|
if (desc === undefined) { |
|
45
|
|
|
var parent = Object.getPrototypeOf(object); |
|
46
|
|
|
|
|
47
|
|
|
if (parent === null) { |
|
48
|
|
|
return undefined; |
|
49
|
|
|
} else { |
|
|
|
|
|
|
50
|
|
|
return get(parent, property, receiver); |
|
51
|
|
|
} |
|
52
|
|
|
} else if ("value" in desc) { |
|
53
|
|
|
return desc.value; |
|
54
|
|
|
} else { |
|
55
|
|
|
var getter = desc.get; |
|
56
|
|
|
|
|
57
|
|
|
if (getter === undefined) { |
|
58
|
|
|
return undefined; |
|
59
|
|
|
} |
|
60
|
|
|
|
|
61
|
|
|
return getter.call(receiver); |
|
62
|
|
|
} |
|
63
|
|
|
}; |
|
64
|
|
|
|
|
65
|
|
|
var inherits = function (subClass, superClass) { |
|
66
|
|
|
if (typeof superClass !== "function" && superClass !== null) { |
|
67
|
|
|
throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); |
|
68
|
|
|
} |
|
69
|
|
|
|
|
70
|
|
|
subClass.prototype = Object.create(superClass && superClass.prototype, { |
|
71
|
|
|
constructor: { |
|
72
|
|
|
value: subClass, |
|
73
|
|
|
enumerable: false, |
|
74
|
|
|
writable: true, |
|
75
|
|
|
configurable: true |
|
76
|
|
|
} |
|
77
|
|
|
}); |
|
78
|
|
|
if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; |
|
|
|
|
|
|
79
|
|
|
}; |
|
80
|
|
|
|
|
81
|
|
|
|
|
82
|
|
|
|
|
83
|
|
|
|
|
84
|
|
|
|
|
85
|
|
|
|
|
86
|
|
|
|
|
87
|
|
|
|
|
88
|
|
|
|
|
89
|
|
|
|
|
90
|
|
|
|
|
91
|
|
|
var possibleConstructorReturn = function (self, call) { |
|
92
|
|
|
if (!self) { |
|
93
|
|
|
throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); |
|
94
|
|
|
} |
|
95
|
|
|
|
|
96
|
|
|
return call && (typeof call === "object" || typeof call === "function") ? call : self; |
|
97
|
|
|
}; |
|
98
|
|
|
|
|
99
|
|
|
/** |
|
100
|
|
|
* @protected |
|
101
|
|
|
*/ |
|
102
|
|
|
var CSS_PREFIX = 'layer-switcher-'; |
|
103
|
|
|
/** |
|
104
|
|
|
* OpenLayers LayerSwitcher Control, displays a list of layers and groups |
|
105
|
|
|
* associated with a map which have a `title` property. |
|
106
|
|
|
* |
|
107
|
|
|
* To be shown in the LayerSwitcher panel layers must have a `title` property; |
|
108
|
|
|
* base map layers should have a `type` property set to `base`. Group layers |
|
109
|
|
|
* (`LayerGroup`) can be used to visually group layers together; a group |
|
110
|
|
|
* with a `fold` property set to either `'open'` or `'close'` will be displayed |
|
111
|
|
|
* with a toggle. |
|
112
|
|
|
* |
|
113
|
|
|
* See [BaseLayerOptions](#baselayeroptions) for a full list of LayerSwitcher |
|
114
|
|
|
* properties for layers (`TileLayer`, `ImageLayer`, `VectorTile` etc.) and |
|
115
|
|
|
* [GroupLayerOptions](#grouplayeroptions) for group layer (`LayerGroup`) |
|
116
|
|
|
* LayerSwitcher properties. |
|
117
|
|
|
* |
|
118
|
|
|
* Layer and group properties can either be set by adding extra properties |
|
119
|
|
|
* to their options when they are created or via their set method. |
|
120
|
|
|
* |
|
121
|
|
|
* Specify a `title` for a Layer by adding a `title` property to it's options object: |
|
122
|
|
|
* ```javascript |
|
123
|
|
|
* var lyr = new ol.layer.Tile({ |
|
124
|
|
|
* // Specify a title property which will be displayed by the layer switcher |
|
125
|
|
|
* title: 'OpenStreetMap', |
|
126
|
|
|
* visible: true, |
|
127
|
|
|
* source: new ol.source.OSM() |
|
128
|
|
|
* }) |
|
129
|
|
|
* ``` |
|
130
|
|
|
* |
|
131
|
|
|
* Alternatively the properties can be set via the `set` method after a layer has been created: |
|
132
|
|
|
* ```javascript |
|
133
|
|
|
* var lyr = new ol.layer.Tile({ |
|
134
|
|
|
* visible: true, |
|
135
|
|
|
* source: new ol.source.OSM() |
|
136
|
|
|
* }) |
|
137
|
|
|
* // Specify a title property which will be displayed by the layer switcher |
|
138
|
|
|
* lyr.set('title', 'OpenStreetMap'); |
|
139
|
|
|
* ``` |
|
140
|
|
|
* |
|
141
|
|
|
* 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). |
|
142
|
|
|
* ```javascript |
|
143
|
|
|
* var layerSwitcher = new LayerSwitcher({ |
|
144
|
|
|
* reverse: true, |
|
145
|
|
|
* groupSelectStyle: 'group' |
|
146
|
|
|
* }); |
|
147
|
|
|
* map.addControl(layerSwitcher); |
|
148
|
|
|
* ``` |
|
149
|
|
|
* |
|
150
|
|
|
* @constructor |
|
151
|
|
|
* @extends {ol/control/Control~Control} |
|
152
|
|
|
* @param opt_options LayerSwitcher options, see [LayerSwitcher Options](#options) and [RenderOptions](#renderoptions) which LayerSwitcher `Options` extends for more details. |
|
153
|
|
|
*/ |
|
154
|
|
|
|
|
155
|
|
|
var LayerSwitcher = function (_Control) { |
|
156
|
|
|
inherits(LayerSwitcher, _Control); |
|
157
|
|
|
|
|
158
|
|
|
function LayerSwitcher(opt_options) { |
|
159
|
|
|
classCallCheck(this, LayerSwitcher); |
|
160
|
|
|
|
|
161
|
|
|
var options = Object.assign({}, opt_options); |
|
162
|
|
|
// TODO Next: Rename to showButtonTitle |
|
163
|
|
|
var tipLabel = options.tipLabel ? options.tipLabel : 'Legend'; |
|
164
|
|
|
// TODO Next: Rename to hideButtonTitle |
|
165
|
|
|
var collapseTipLabel = options.collapseTipLabel ? options.collapseTipLabel : 'Collapse legend'; |
|
166
|
|
|
var element = document.createElement('div'); |
|
167
|
|
|
|
|
168
|
|
|
var _this = possibleConstructorReturn(this, (LayerSwitcher.__proto__ || Object.getPrototypeOf(LayerSwitcher)).call(this, { element: element, target: options.target })); |
|
169
|
|
|
|
|
170
|
|
|
_this.activationMode = options.activationMode || 'mouseover'; |
|
171
|
|
|
_this.startActive = options.startActive === true; |
|
172
|
|
|
// TODO Next: Rename to showButtonContent |
|
173
|
|
|
var label = options.label !== undefined ? options.label : ''; |
|
174
|
|
|
// TODO Next: Rename to hideButtonContent |
|
175
|
|
|
var collapseLabel = options.collapseLabel !== undefined ? options.collapseLabel : '\xBB'; |
|
176
|
|
|
_this.groupSelectStyle = LayerSwitcher.getGroupSelectStyle(options.groupSelectStyle); |
|
177
|
|
|
_this.reverse = options.reverse !== false; |
|
178
|
|
|
_this.mapListeners = []; |
|
179
|
|
|
_this.hiddenClassName = 'ol-unselectable ol-control layer-switcher'; |
|
180
|
|
|
if (LayerSwitcher.isTouchDevice_()) { |
|
181
|
|
|
_this.hiddenClassName += ' touch'; |
|
182
|
|
|
} |
|
183
|
|
|
_this.shownClassName = 'shown'; |
|
184
|
|
|
element.className = _this.hiddenClassName; |
|
185
|
|
|
var button = document.createElement('button'); |
|
186
|
|
|
button.setAttribute('title', tipLabel); |
|
187
|
|
|
button.setAttribute('aria-label', tipLabel); |
|
188
|
|
|
element.appendChild(button); |
|
189
|
|
|
_this.panel = document.createElement('div'); |
|
190
|
|
|
_this.panel.className = 'panel'; |
|
191
|
|
|
element.appendChild(_this.panel); |
|
192
|
|
|
LayerSwitcher.enableTouchScroll_(_this.panel); |
|
193
|
|
|
button.textContent = label; |
|
194
|
|
|
element.classList.add(CSS_PREFIX + 'group-select-style-' + _this.groupSelectStyle); |
|
195
|
|
|
element.classList.add(CSS_PREFIX + 'activation-mode-' + _this.activationMode); |
|
196
|
|
|
if (_this.activationMode === 'click') { |
|
197
|
|
|
// TODO Next: Remove in favour of layer-switcher-activation-mode-click |
|
198
|
|
|
element.classList.add('activationModeClick'); |
|
199
|
|
|
if (_this.startActive) { |
|
200
|
|
|
button.textContent = collapseLabel; |
|
201
|
|
|
button.setAttribute('title', collapseTipLabel); |
|
202
|
|
|
button.setAttribute('aria-label', collapseTipLabel); |
|
203
|
|
|
} |
|
204
|
|
|
button.onclick = function (e) { |
|
205
|
|
|
var evt = e || window.event; |
|
206
|
|
|
if (_this.element.classList.contains(_this.shownClassName)) { |
|
207
|
|
|
_this.hidePanel(); |
|
208
|
|
|
button.textContent = label; |
|
209
|
|
|
button.setAttribute('title', tipLabel); |
|
210
|
|
|
button.setAttribute('aria-label', tipLabel); |
|
211
|
|
|
} else { |
|
212
|
|
|
_this.showPanel(); |
|
213
|
|
|
button.textContent = collapseLabel; |
|
214
|
|
|
button.setAttribute('title', collapseTipLabel); |
|
215
|
|
|
button.setAttribute('aria-label', collapseTipLabel); |
|
216
|
|
|
} |
|
217
|
|
|
evt.preventDefault(); |
|
218
|
|
|
}; |
|
219
|
|
|
} else { |
|
220
|
|
|
button.onmouseover = function () { |
|
221
|
|
|
_this.showPanel(); |
|
222
|
|
|
}; |
|
223
|
|
|
button.onclick = function (e) { |
|
224
|
|
|
var evt = e || window.event; |
|
225
|
|
|
_this.showPanel(); |
|
226
|
|
|
evt.preventDefault(); |
|
227
|
|
|
}; |
|
228
|
|
|
_this.panel.onmouseout = function (evt) { |
|
229
|
|
|
if (!_this.panel.contains(evt.relatedTarget)) { |
|
230
|
|
|
_this.hidePanel(); |
|
231
|
|
|
} |
|
232
|
|
|
}; |
|
233
|
|
|
} |
|
234
|
|
|
return _this; |
|
235
|
|
|
} |
|
236
|
|
|
/** |
|
237
|
|
|
* Set the map instance the control is associated with. |
|
238
|
|
|
* @param map The map instance. |
|
239
|
|
|
*/ |
|
240
|
|
|
|
|
241
|
|
|
|
|
242
|
|
|
createClass(LayerSwitcher, [{ |
|
243
|
|
|
key: 'setMap', |
|
244
|
|
|
value: function setMap(map) { |
|
245
|
|
|
var _this2 = this; |
|
246
|
|
|
|
|
247
|
|
|
// Clean up listeners associated with the previous map |
|
248
|
|
|
for (var i = 0; i < this.mapListeners.length; i++) { |
|
249
|
|
|
ol_Observable.unByKey(this.mapListeners[i]); |
|
250
|
|
|
} |
|
251
|
|
|
this.mapListeners.length = 0; |
|
252
|
|
|
// Wire up listeners etc. and store reference to new map |
|
253
|
|
|
get(LayerSwitcher.prototype.__proto__ || Object.getPrototypeOf(LayerSwitcher.prototype), 'setMap', this).call(this, map); |
|
254
|
|
|
if (map) { |
|
255
|
|
|
if (this.startActive) { |
|
256
|
|
|
this.showPanel(); |
|
257
|
|
|
} else { |
|
258
|
|
|
this.renderPanel(); |
|
259
|
|
|
} |
|
260
|
|
|
if (this.activationMode !== 'click') { |
|
261
|
|
|
this.mapListeners.push(map.on('pointerdown', function () { |
|
262
|
|
|
_this2.hidePanel(); |
|
263
|
|
|
})); |
|
264
|
|
|
} |
|
265
|
|
|
} |
|
266
|
|
|
} |
|
267
|
|
|
/** |
|
268
|
|
|
* Show the layer panel. |
|
269
|
|
|
*/ |
|
270
|
|
|
|
|
271
|
|
|
}, { |
|
272
|
|
|
key: 'showPanel', |
|
273
|
|
|
value: function showPanel() { |
|
274
|
|
|
if (!this.element.classList.contains(this.shownClassName)) { |
|
275
|
|
|
this.element.classList.add(this.shownClassName); |
|
276
|
|
|
this.renderPanel(); |
|
277
|
|
|
} |
|
278
|
|
|
} |
|
279
|
|
|
/** |
|
280
|
|
|
* Hide the layer panel. |
|
281
|
|
|
*/ |
|
282
|
|
|
|
|
283
|
|
|
}, { |
|
284
|
|
|
key: 'hidePanel', |
|
285
|
|
|
value: function hidePanel() { |
|
286
|
|
|
if (this.element.classList.contains(this.shownClassName)) { |
|
287
|
|
|
this.element.classList.remove(this.shownClassName); |
|
288
|
|
|
} |
|
289
|
|
|
} |
|
290
|
|
|
/** |
|
291
|
|
|
* Re-draw the layer panel to represent the current state of the layers. |
|
292
|
|
|
*/ |
|
293
|
|
|
|
|
294
|
|
|
}, { |
|
295
|
|
|
key: 'renderPanel', |
|
296
|
|
|
value: function renderPanel() { |
|
297
|
|
|
this.dispatchEvent('render'); |
|
298
|
|
|
LayerSwitcher.renderPanel(this.getMap(), this.panel, { |
|
299
|
|
|
groupSelectStyle: this.groupSelectStyle, |
|
300
|
|
|
reverse: this.reverse |
|
301
|
|
|
}); |
|
302
|
|
|
this.dispatchEvent('rendercomplete'); |
|
303
|
|
|
} |
|
304
|
|
|
/** |
|
305
|
|
|
* **_[static]_** - Re-draw the layer panel to represent the current state of the layers. |
|
306
|
|
|
* @param map The OpenLayers Map instance to render layers for |
|
307
|
|
|
* @param panel The DOM Element into which the layer tree will be rendered |
|
308
|
|
|
* @param options Options for panel, group, and layers |
|
309
|
|
|
*/ |
|
310
|
|
|
|
|
311
|
|
|
}], [{ |
|
312
|
|
|
key: 'renderPanel', |
|
313
|
|
|
value: function renderPanel(map, panel, options) { |
|
314
|
|
|
// Create the event. |
|
315
|
|
|
var render_event = new Event('render'); |
|
|
|
|
|
|
316
|
|
|
// Dispatch the event. |
|
317
|
|
|
panel.dispatchEvent(render_event); |
|
318
|
|
|
options = options || {}; |
|
319
|
|
|
options.groupSelectStyle = LayerSwitcher.getGroupSelectStyle(options.groupSelectStyle); |
|
320
|
|
|
LayerSwitcher.ensureTopVisibleBaseLayerShown(map, options.groupSelectStyle); |
|
321
|
|
|
while (panel.firstChild) { |
|
322
|
|
|
panel.removeChild(panel.firstChild); |
|
323
|
|
|
} |
|
324
|
|
|
// Reset indeterminate state for all layers and groups before |
|
325
|
|
|
// applying based on groupSelectStyle |
|
326
|
|
|
LayerSwitcher.forEachRecursive(map, function (l, _idx, _a) { |
|
|
|
|
|
|
327
|
|
|
l.set('indeterminate', false); |
|
328
|
|
|
}); |
|
329
|
|
|
if (options.groupSelectStyle === 'children' || options.groupSelectStyle === 'none') { |
|
330
|
|
|
// Set visibile and indeterminate state of groups based on |
|
331
|
|
|
// their children's visibility |
|
332
|
|
|
LayerSwitcher.setGroupVisibility(map); |
|
333
|
|
|
} else if (options.groupSelectStyle === 'group') { |
|
334
|
|
|
// Set child indetermiate state based on their parent's visibility |
|
335
|
|
|
LayerSwitcher.setChildVisibility(map); |
|
336
|
|
|
} |
|
337
|
|
|
var ul = document.createElement('ul'); |
|
338
|
|
|
panel.appendChild(ul); |
|
339
|
|
|
// passing two map arguments instead of lyr as we're passing the map as the root of the layers tree |
|
340
|
|
|
LayerSwitcher.renderLayers_(map, map, ul, options, function render(_changedLyr) { |
|
|
|
|
|
|
341
|
|
|
LayerSwitcher.renderPanel(map, panel, options); |
|
342
|
|
|
}); |
|
343
|
|
|
// Create the event. |
|
344
|
|
|
var rendercomplete_event = new Event('rendercomplete'); |
|
345
|
|
|
// Dispatch the event. |
|
346
|
|
|
panel.dispatchEvent(rendercomplete_event); |
|
347
|
|
|
} |
|
348
|
|
|
/** |
|
349
|
|
|
* **_[static]_** - Determine if a given layer group contains base layers |
|
350
|
|
|
* @param grp Group to test |
|
351
|
|
|
*/ |
|
352
|
|
|
|
|
353
|
|
|
}, { |
|
354
|
|
|
key: 'isBaseGroup', |
|
355
|
|
|
value: function isBaseGroup(grp) { |
|
356
|
|
|
if (grp instanceof LayerGroup) { |
|
357
|
|
|
var lyrs = grp.getLayers().getArray(); |
|
358
|
|
|
return lyrs.length && lyrs[0].get('type') === 'base'; |
|
359
|
|
|
} else { |
|
|
|
|
|
|
360
|
|
|
return false; |
|
361
|
|
|
} |
|
362
|
|
|
} |
|
363
|
|
|
}, { |
|
364
|
|
|
key: 'setGroupVisibility', |
|
365
|
|
|
value: function setGroupVisibility(map) { |
|
366
|
|
|
// Get a list of groups, with the deepest first |
|
367
|
|
|
var groups = LayerSwitcher.getGroupsAndLayers(map, function (l) { |
|
368
|
|
|
return l instanceof LayerGroup && !l.get('combine') && !LayerSwitcher.isBaseGroup(l); |
|
369
|
|
|
}).reverse(); |
|
370
|
|
|
// console.log(groups.map(g => g.get('title'))); |
|
371
|
|
|
groups.forEach(function (grp) { |
|
372
|
|
|
// TODO Can we use getLayersArray, is it public in the esm build? |
|
373
|
|
|
var descendantVisibility = grp.getLayersArray().map(function (l) { |
|
374
|
|
|
var state = l.getVisible(); |
|
375
|
|
|
// console.log('>', l.get('title'), state); |
|
376
|
|
|
return state; |
|
377
|
|
|
}); |
|
378
|
|
|
// console.log(descendantVisibility); |
|
379
|
|
|
if (descendantVisibility.every(function (v) { |
|
380
|
|
|
return v === true; |
|
381
|
|
|
})) { |
|
382
|
|
|
grp.setVisible(true); |
|
383
|
|
|
grp.set('indeterminate', false); |
|
384
|
|
|
} else if (descendantVisibility.every(function (v) { |
|
385
|
|
|
return v === false; |
|
386
|
|
|
})) { |
|
387
|
|
|
grp.setVisible(false); |
|
388
|
|
|
grp.set('indeterminate', false); |
|
389
|
|
|
} else { |
|
390
|
|
|
grp.setVisible(true); |
|
391
|
|
|
grp.set('indeterminate', true); |
|
392
|
|
|
} |
|
393
|
|
|
}); |
|
394
|
|
|
} |
|
395
|
|
|
}, { |
|
396
|
|
|
key: 'setChildVisibility', |
|
397
|
|
|
value: function setChildVisibility(map) { |
|
398
|
|
|
// console.log('setChildVisibility'); |
|
399
|
|
|
var groups = LayerSwitcher.getGroupsAndLayers(map, function (l) { |
|
400
|
|
|
return l instanceof LayerGroup && !l.get('combine') && !LayerSwitcher.isBaseGroup(l); |
|
401
|
|
|
}); |
|
402
|
|
|
groups.forEach(function (grp) { |
|
403
|
|
|
var group = grp; |
|
404
|
|
|
// console.log(group.get('title')); |
|
405
|
|
|
var groupVisible = group.getVisible(); |
|
406
|
|
|
var groupIndeterminate = group.get('indeterminate'); |
|
407
|
|
|
group.getLayers().getArray().forEach(function (l) { |
|
408
|
|
|
l.set('indeterminate', false); |
|
409
|
|
|
if ((!groupVisible || groupIndeterminate) && l.getVisible()) { |
|
410
|
|
|
l.set('indeterminate', true); |
|
411
|
|
|
} |
|
412
|
|
|
}); |
|
413
|
|
|
}); |
|
414
|
|
|
} |
|
415
|
|
|
/** |
|
416
|
|
|
* Ensure only the top-most base layer is visible if more than one is visible. |
|
417
|
|
|
* @param map The map instance. |
|
418
|
|
|
* @param groupSelectStyle |
|
419
|
|
|
* @protected |
|
420
|
|
|
*/ |
|
421
|
|
|
|
|
422
|
|
|
}, { |
|
423
|
|
|
key: 'ensureTopVisibleBaseLayerShown', |
|
424
|
|
|
value: function ensureTopVisibleBaseLayerShown(map, groupSelectStyle) { |
|
425
|
|
|
var lastVisibleBaseLyr = void 0; |
|
|
|
|
|
|
426
|
|
|
LayerSwitcher.forEachRecursive(map, function (lyr, _idx, _arr) { |
|
|
|
|
|
|
427
|
|
|
if (lyr.get('type') === 'base' && lyr.getVisible()) { |
|
428
|
|
|
lastVisibleBaseLyr = lyr; |
|
429
|
|
|
} |
|
430
|
|
|
}); |
|
431
|
|
|
if (lastVisibleBaseLyr) LayerSwitcher.setVisible_(map, lastVisibleBaseLyr, true, groupSelectStyle); |
|
|
|
|
|
|
432
|
|
|
} |
|
433
|
|
|
/** |
|
434
|
|
|
* **_[static]_** - Get an Array of all layers and groups displayed by the LayerSwitcher (has a `'title'` property) |
|
435
|
|
|
* contained by the specified map or layer group; optionally filtering via `filterFn` |
|
436
|
|
|
* @param grp The map or layer group for which layers are found. |
|
437
|
|
|
* @param filterFn Optional function used to filter the returned layers |
|
438
|
|
|
*/ |
|
439
|
|
|
|
|
440
|
|
|
}, { |
|
441
|
|
|
key: 'getGroupsAndLayers', |
|
442
|
|
|
value: function getGroupsAndLayers(grp, filterFn) { |
|
443
|
|
|
var layers = []; |
|
444
|
|
|
filterFn = filterFn || function (_lyr, _idx, _arr) { |
|
|
|
|
|
|
445
|
|
|
return true; |
|
446
|
|
|
}; |
|
447
|
|
|
LayerSwitcher.forEachRecursive(grp, function (lyr, idx, arr) { |
|
448
|
|
|
if (lyr.get('title')) { |
|
449
|
|
|
if (filterFn(lyr, idx, arr)) { |
|
450
|
|
|
layers.push(lyr); |
|
451
|
|
|
} |
|
452
|
|
|
} |
|
453
|
|
|
}); |
|
454
|
|
|
return layers; |
|
455
|
|
|
} |
|
456
|
|
|
/** |
|
457
|
|
|
* Toggle the visible state of a layer. |
|
458
|
|
|
* Takes care of hiding other layers in the same exclusive group if the layer |
|
459
|
|
|
* is toggle to visible. |
|
460
|
|
|
* @protected |
|
461
|
|
|
* @param map The map instance. |
|
462
|
|
|
* @param lyr layer whose visibility will be toggled. |
|
463
|
|
|
* @param visible Set whether the layer is shown |
|
464
|
|
|
* @param groupSelectStyle |
|
465
|
|
|
* @protected |
|
466
|
|
|
*/ |
|
467
|
|
|
|
|
468
|
|
|
}, { |
|
469
|
|
|
key: 'setVisible_', |
|
470
|
|
|
value: function setVisible_(map, lyr, visible, groupSelectStyle) { |
|
471
|
|
|
// console.log(lyr.get('title'), visible, groupSelectStyle); |
|
472
|
|
|
lyr.setVisible(visible); |
|
473
|
|
|
if (visible && lyr.get('type') === 'base') { |
|
474
|
|
|
// Hide all other base layers regardless of grouping |
|
475
|
|
|
LayerSwitcher.forEachRecursive(map, function (l, _idx, _arr) { |
|
|
|
|
|
|
476
|
|
|
if (l != lyr && l.get('type') === 'base') { |
|
477
|
|
|
l.setVisible(false); |
|
478
|
|
|
} |
|
479
|
|
|
}); |
|
480
|
|
|
} |
|
481
|
|
|
if (lyr instanceof LayerGroup && !lyr.get('combine') && groupSelectStyle === 'children') { |
|
482
|
|
|
lyr.getLayers().forEach(function (l) { |
|
483
|
|
|
LayerSwitcher.setVisible_(map, l, lyr.getVisible(), groupSelectStyle); |
|
484
|
|
|
}); |
|
485
|
|
|
} |
|
486
|
|
|
} |
|
487
|
|
|
/** |
|
488
|
|
|
* Render all layers that are children of a group. |
|
489
|
|
|
* @param map The map instance. |
|
490
|
|
|
* @param lyr Layer to be rendered (should have a title property). |
|
491
|
|
|
* @param idx Position in parent group list. |
|
492
|
|
|
* @param options Options for groups and layers |
|
493
|
|
|
* @protected |
|
494
|
|
|
*/ |
|
495
|
|
|
|
|
496
|
|
|
}, { |
|
497
|
|
|
key: 'renderLayer_', |
|
498
|
|
|
value: function renderLayer_(map, lyr, idx, options, render) { |
|
499
|
|
|
var li = document.createElement('li'); |
|
500
|
|
|
var lyrTitle = lyr.get('title'); |
|
501
|
|
|
var checkboxId = LayerSwitcher.uuid(); |
|
502
|
|
|
var label = document.createElement('label'); |
|
503
|
|
|
if (lyr instanceof LayerGroup && !lyr.get('combine')) { |
|
504
|
|
|
var isBaseGroup = LayerSwitcher.isBaseGroup(lyr); |
|
505
|
|
|
li.classList.add('group'); |
|
506
|
|
|
if (isBaseGroup) { |
|
507
|
|
|
li.classList.add(CSS_PREFIX + 'base-group'); |
|
508
|
|
|
} |
|
509
|
|
|
// Group folding |
|
510
|
|
|
if (lyr.get('fold')) { |
|
511
|
|
|
li.classList.add(CSS_PREFIX + 'fold'); |
|
512
|
|
|
li.classList.add(CSS_PREFIX + lyr.get('fold')); |
|
513
|
|
|
var btn = document.createElement('button'); |
|
514
|
|
|
btn.onclick = function (e) { |
|
515
|
|
|
var evt = e || window.event; |
|
516
|
|
|
LayerSwitcher.toggleFold_(lyr, li); |
|
517
|
|
|
evt.preventDefault(); |
|
518
|
|
|
}; |
|
519
|
|
|
li.appendChild(btn); |
|
520
|
|
|
} |
|
521
|
|
|
if (!isBaseGroup && options.groupSelectStyle != 'none') { |
|
522
|
|
|
var input = document.createElement('input'); |
|
523
|
|
|
input.type = 'checkbox'; |
|
524
|
|
|
input.id = checkboxId; |
|
525
|
|
|
input.checked = lyr.getVisible(); |
|
526
|
|
|
input.indeterminate = lyr.get('indeterminate'); |
|
527
|
|
|
input.onchange = function (e) { |
|
528
|
|
|
var target = e.target; |
|
529
|
|
|
LayerSwitcher.setVisible_(map, lyr, target.checked, options.groupSelectStyle); |
|
530
|
|
|
render(lyr); |
|
531
|
|
|
}; |
|
532
|
|
|
li.appendChild(input); |
|
533
|
|
|
label.htmlFor = checkboxId; |
|
534
|
|
|
} |
|
535
|
|
|
label.innerHTML = lyrTitle; |
|
536
|
|
|
li.appendChild(label); |
|
537
|
|
|
var ul = document.createElement('ul'); |
|
538
|
|
|
li.appendChild(ul); |
|
539
|
|
|
LayerSwitcher.renderLayers_(map, lyr, ul, options, render); |
|
540
|
|
|
} else { |
|
541
|
|
|
li.className = 'layer'; |
|
542
|
|
|
var _input = document.createElement('input'); |
|
543
|
|
|
if (lyr.get('type') === 'base') { |
|
544
|
|
|
_input.type = 'radio'; |
|
545
|
|
|
_input.name = 'base'; |
|
546
|
|
|
} else { |
|
547
|
|
|
_input.type = 'checkbox'; |
|
548
|
|
|
} |
|
549
|
|
|
_input.id = checkboxId; |
|
550
|
|
|
_input.checked = lyr.get('visible'); |
|
551
|
|
|
_input.indeterminate = lyr.get('indeterminate'); |
|
552
|
|
|
_input.onchange = function (e) { |
|
553
|
|
|
var target = e.target; |
|
554
|
|
|
LayerSwitcher.setVisible_(map, lyr, target.checked, options.groupSelectStyle); |
|
555
|
|
|
render(lyr); |
|
556
|
|
|
}; |
|
557
|
|
|
li.appendChild(_input); |
|
558
|
|
|
label.htmlFor = checkboxId; |
|
559
|
|
|
label.innerHTML = lyrTitle; |
|
560
|
|
|
var rsl = map.getView().getResolution(); |
|
561
|
|
|
if (rsl > lyr.getMaxResolution() || rsl < lyr.getMinResolution()) { |
|
562
|
|
|
label.className += ' disabled'; |
|
563
|
|
|
} |
|
564
|
|
|
li.appendChild(label); |
|
565
|
|
|
} |
|
566
|
|
|
return li; |
|
567
|
|
|
} |
|
568
|
|
|
/** |
|
569
|
|
|
* Render all layers that are children of a group. |
|
570
|
|
|
* @param map The map instance. |
|
571
|
|
|
* @param lyr Group layer whose children will be rendered. |
|
572
|
|
|
* @param elm DOM element that children will be appended to. |
|
573
|
|
|
* @param options Options for groups and layers |
|
574
|
|
|
* @protected |
|
575
|
|
|
*/ |
|
576
|
|
|
|
|
577
|
|
|
}, { |
|
578
|
|
|
key: 'renderLayers_', |
|
579
|
|
|
value: function renderLayers_(map, lyr, elm, options, render) { |
|
580
|
|
|
var lyrs = lyr.getLayers().getArray().slice(); |
|
581
|
|
|
if (options.reverse) lyrs = lyrs.reverse(); |
|
|
|
|
|
|
582
|
|
|
for (var i = 0, l; i < lyrs.length; i++) { |
|
583
|
|
|
l = lyrs[i]; |
|
584
|
|
|
if (l.get('title')) { |
|
585
|
|
|
elm.appendChild(LayerSwitcher.renderLayer_(map, l, i, options, render)); |
|
586
|
|
|
} |
|
587
|
|
|
} |
|
588
|
|
|
} |
|
589
|
|
|
/** |
|
590
|
|
|
* **_[static]_** - Call the supplied function for each layer in the passed layer group |
|
591
|
|
|
* recursing nested groups. |
|
592
|
|
|
* @param lyr The layer group to start iterating from. |
|
593
|
|
|
* @param fn Callback which will be called for each layer |
|
594
|
|
|
* found under `lyr`. |
|
595
|
|
|
*/ |
|
596
|
|
|
|
|
597
|
|
|
}, { |
|
598
|
|
|
key: 'forEachRecursive', |
|
599
|
|
|
value: function forEachRecursive(lyr, fn) { |
|
600
|
|
|
lyr.getLayers().forEach(function (lyr, idx, a) { |
|
601
|
|
|
fn(lyr, idx, a); |
|
602
|
|
|
if (lyr instanceof LayerGroup) { |
|
603
|
|
|
LayerSwitcher.forEachRecursive(lyr, fn); |
|
604
|
|
|
} |
|
605
|
|
|
}); |
|
606
|
|
|
} |
|
607
|
|
|
/** |
|
608
|
|
|
* **_[static]_** - Generate a UUID |
|
609
|
|
|
* Adapted from http://stackoverflow.com/a/2117523/526860 |
|
610
|
|
|
* @returns {String} UUID |
|
611
|
|
|
*/ |
|
612
|
|
|
|
|
613
|
|
|
}, { |
|
614
|
|
|
key: 'uuid', |
|
615
|
|
|
value: function uuid() { |
|
616
|
|
|
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) { |
|
617
|
|
|
var r = Math.random() * 16 | 0, |
|
618
|
|
|
v = c == 'x' ? r : r & 0x3 | 0x8; |
|
619
|
|
|
return v.toString(16); |
|
620
|
|
|
}); |
|
621
|
|
|
} |
|
622
|
|
|
/** |
|
623
|
|
|
* Apply workaround to enable scrolling of overflowing content within an |
|
624
|
|
|
* element. Adapted from https://gist.github.com/chrismbarr/4107472 |
|
625
|
|
|
* @param elm Element on which to enable touch scrolling |
|
626
|
|
|
* @protected |
|
627
|
|
|
*/ |
|
628
|
|
|
|
|
629
|
|
|
}, { |
|
630
|
|
|
key: 'enableTouchScroll_', |
|
631
|
|
|
value: function enableTouchScroll_(elm) { |
|
632
|
|
|
if (LayerSwitcher.isTouchDevice_()) { |
|
633
|
|
|
var scrollStartPos = 0; |
|
634
|
|
|
elm.addEventListener('touchstart', function (event) { |
|
635
|
|
|
scrollStartPos = this.scrollTop + event.touches[0].pageY; |
|
636
|
|
|
}, false); |
|
637
|
|
|
elm.addEventListener('touchmove', function (event) { |
|
638
|
|
|
this.scrollTop = scrollStartPos - event.touches[0].pageY; |
|
639
|
|
|
}, false); |
|
640
|
|
|
} |
|
641
|
|
|
} |
|
642
|
|
|
/** |
|
643
|
|
|
* Determine if the current browser supports touch events. Adapted from |
|
644
|
|
|
* https://gist.github.com/chrismbarr/4107472 |
|
645
|
|
|
* @returns {Boolean} True if client can have 'TouchEvent' event |
|
646
|
|
|
* @protected |
|
647
|
|
|
*/ |
|
648
|
|
|
|
|
649
|
|
|
}, { |
|
650
|
|
|
key: 'isTouchDevice_', |
|
651
|
|
|
value: function isTouchDevice_() { |
|
652
|
|
|
try { |
|
653
|
|
|
document.createEvent('TouchEvent'); |
|
654
|
|
|
return true; |
|
655
|
|
|
} catch (e) { |
|
656
|
|
|
return false; |
|
657
|
|
|
} |
|
658
|
|
|
} |
|
659
|
|
|
/** |
|
660
|
|
|
* Fold/unfold layer group |
|
661
|
|
|
* @param lyr Layer group to fold/unfold |
|
662
|
|
|
* @param li List item containing layer group |
|
663
|
|
|
* @protected |
|
664
|
|
|
*/ |
|
665
|
|
|
|
|
666
|
|
|
}, { |
|
667
|
|
|
key: 'toggleFold_', |
|
668
|
|
|
value: function toggleFold_(lyr, li) { |
|
669
|
|
|
li.classList.remove(CSS_PREFIX + lyr.get('fold')); |
|
670
|
|
|
lyr.set('fold', lyr.get('fold') === 'open' ? 'close' : 'open'); |
|
671
|
|
|
li.classList.add(CSS_PREFIX + lyr.get('fold')); |
|
672
|
|
|
} |
|
673
|
|
|
/** |
|
674
|
|
|
* If a valid groupSelectStyle value is not provided then return the default |
|
675
|
|
|
* @param groupSelectStyle The string to check for validity |
|
676
|
|
|
* @returns The value groupSelectStyle, if valid, the default otherwise |
|
677
|
|
|
* @protected |
|
678
|
|
|
*/ |
|
679
|
|
|
|
|
680
|
|
|
}, { |
|
681
|
|
|
key: 'getGroupSelectStyle', |
|
682
|
|
|
value: function getGroupSelectStyle(groupSelectStyle) { |
|
683
|
|
|
return ['none', 'children', 'group'].indexOf(groupSelectStyle) >= 0 ? groupSelectStyle : 'children'; |
|
684
|
|
|
} |
|
685
|
|
|
}]); |
|
686
|
|
|
return LayerSwitcher; |
|
687
|
|
|
}(Control); |
|
688
|
|
|
if (window['ol'] && window['ol']['control']) { |
|
689
|
|
|
window['ol']['control']['LayerSwitcher'] = LayerSwitcher; |
|
690
|
|
|
} |
|
691
|
|
|
|
|
692
|
|
|
return LayerSwitcher; |
|
693
|
|
|
|
|
694
|
|
|
}))); |
|
695
|
|
|
|
This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.
To learn more about declaring variables in Javascript, see the MDN.