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.