@openglobus/og 0.13.8 → 0.13.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (85) hide show
  1. package/README.md +1 -1
  2. package/css/og.css +388 -129
  3. package/dist/@openglobus/og.css +1 -1
  4. package/dist/@openglobus/og.esm.js +2 -2
  5. package/dist/@openglobus/og.esm.js.map +1 -1
  6. package/dist/@openglobus/og.umd.js +2 -2
  7. package/dist/@openglobus/og.umd.js.map +1 -1
  8. package/package.json +20 -19
  9. package/src/og/Globe.js +15 -16
  10. package/src/og/Object3d.js +33 -0
  11. package/src/og/control/GeoImageDragControl.js +102 -75
  12. package/src/og/control/LayerAnimation.js +306 -38
  13. package/src/og/control/LayerSwitcher.js +506 -159
  14. package/src/og/control/Lighting.js +27 -27
  15. package/src/og/control/RulerSwitcher.js +69 -0
  16. package/src/og/control/UIhelpers.js +146 -0
  17. package/src/og/control/index.js +3 -1
  18. package/src/og/control/ruler/RulerScene.js +6 -2
  19. package/src/og/control/visibleExtent/Segment.js +2 -1
  20. package/src/og/ellipsoid/wgs84.js +1 -1
  21. package/src/og/entity/LabelHandler.js +2 -2
  22. package/src/og/entity/LabelWorker.js +36 -54
  23. package/src/og/layer/BaseGeoImage.js +347 -312
  24. package/src/og/layer/CanvasTiles.js +312 -290
  25. package/src/og/layer/GeoImage.js +222 -214
  26. package/src/og/layer/GeoVideo.js +291 -291
  27. package/src/og/layer/Layer.js +71 -30
  28. package/src/og/layer/Material.js +109 -109
  29. package/src/og/layer/Vector.js +1 -0
  30. package/src/og/layer/XYZ.js +20 -12
  31. package/src/og/light/LightSource.js +364 -363
  32. package/src/og/math.js +2 -2
  33. package/src/og/quadTree/Node.js +7 -5
  34. package/src/og/renderer/Renderer.js +1 -1
  35. package/src/og/scene/Planet.js +103 -42
  36. package/src/og/segment/Segment.js +34 -56
  37. package/src/og/segment/Slice.js +1 -1
  38. package/src/og/shaders/drawnode.js +1 -1
  39. package/src/og/terrain/GlobusTerrain.js +31 -20
  40. package/src/og/terrain/MapboxTerrain.js +1 -1
  41. package/src/og/utils/BaseWorker.js +46 -0
  42. package/src/og/utils/GeoImageCreator.js +164 -161
  43. package/src/og/utils/Loader.js +60 -33
  44. package/src/og/utils/NormalMapCreator.js +403 -412
  45. package/src/og/utils/PlainSegmentWorker.js +29 -55
  46. package/src/og/utils/TerrainWorker.js +572 -589
  47. package/src/og/utils/functionComposition.js +13 -0
  48. package/src/og/webgl/Handler.js +42 -41
  49. package/types/Object3d.d.ts +1 -0
  50. package/types/camera/PlanetCamera.d.ts +4 -0
  51. package/types/control/GeoImageDragControl.d.ts +2 -0
  52. package/types/control/LayerAnimation.d.ts +73 -0
  53. package/types/control/LayerSwitcher.d.ts +55 -11
  54. package/types/control/MouseNavigation.d.ts +1 -0
  55. package/types/control/MouseWheelZoomControl.d.ts +1 -0
  56. package/types/control/Sun.d.ts +1 -0
  57. package/types/control/TouchNavigation.d.ts +1 -0
  58. package/types/control/UIhelpers.d.ts +13 -0
  59. package/types/control/index.d.ts +2 -1
  60. package/types/entity/LabelHandler.d.ts +2 -0
  61. package/types/entity/LabelWorker.d.ts +5 -7
  62. package/types/layer/BaseGeoImage.d.ts +9 -1
  63. package/types/layer/CanvasTiles.d.ts +5 -1
  64. package/types/layer/GeoImage.d.ts +1 -0
  65. package/types/layer/GeoTexture2d.d.ts +1 -0
  66. package/types/layer/Layer.d.ts +5 -1
  67. package/types/layer/Vector.d.ts +7 -0
  68. package/types/layer/WMS.d.ts +1 -0
  69. package/types/layer/XYZ.d.ts +8 -6
  70. package/types/math.d.ts +1 -1
  71. package/types/quadTree/EntityCollectionNode.d.ts +7 -0
  72. package/types/quadTree/Node.d.ts +1 -2
  73. package/types/renderer/Renderer.d.ts +1 -1
  74. package/types/renderer/RendererEvents.d.ts +16 -0
  75. package/types/scene/Planet.d.ts +19 -4
  76. package/types/segment/SegmentLonLat.d.ts +2 -0
  77. package/types/terrain/BilTerrain.d.ts +6 -0
  78. package/types/terrain/GlobusTerrain.d.ts +7 -0
  79. package/types/terrain/MapboxTerrain.d.ts +8 -0
  80. package/types/utils/BaseWorker.d.ts +14 -0
  81. package/types/utils/Loader.d.ts +3 -1
  82. package/types/utils/PlainSegmentWorker.d.ts +4 -6
  83. package/types/utils/TerrainWorker.d.ts +4 -6
  84. package/types/utils/functionComposition.d.ts +5 -0
  85. package/types/webgl/Handler.d.ts +4 -1
@@ -1,159 +1,506 @@
1
- /**
2
- * @module og/control/LayerSwitcher
3
- */
4
-
5
- "use strict";
6
-
7
- import { Control } from "./Control.js";
8
-
9
- /**
10
- * Simple(OpenLayers like)layer switcher, includes base layers, overlays, geo images etc. groups.
11
- * @class
12
- * @extends {Control}
13
- * @param {Object} [options] - Control options.
14
- */
15
- class LayerSwitcher extends Control {
16
- constructor(options) {
17
- super(options);
18
-
19
- this.dialog = null;
20
- this.baseLayersDiv = null;
21
- this.overlaysDiv = null;
22
- this._id = LayerSwitcher.numSwitches++;
23
- }
24
-
25
- static get numSwitches() {
26
- if (!this._counter && this._counter !== 0) {
27
- this._counter = 0;
28
- }
29
- return this._counter;
30
- }
31
-
32
- static set numSwitches(n) {
33
- this._counter = n;
34
- }
35
-
36
- oninit() {
37
- this.planet.events.on("layeradd", this.onLayerAdded, this);
38
- this.planet.events.on("layerremove", this.onLayerRemoved, this);
39
- this.createSwitcher();
40
- this.createDialog();
41
- }
42
-
43
- onLayerAdded(layer) {
44
- if (layer.displayInLayerSwitcher) {
45
- if (layer.isBaseLayer()) {
46
- this.addSwitcher("radio", layer, this.baseLayersDiv);
47
- } else {
48
- this.addSwitcher("checkbox", layer, this.overlaysDiv, this._id);
49
- }
50
- }
51
- }
52
-
53
- onLayerRemoved(layer) {
54
- layer._removeCallback();
55
- layer._removeCallback = null;
56
- }
57
-
58
- addSwitcher(type, obj, container, id = "") {
59
- var lineDiv = document.createElement("div");
60
-
61
- var that = this;
62
- var center = document.createElement("div");
63
- center.classList.add("ogViewExtentBtn");
64
- center.onclick = function () {
65
- that.planet.flyExtent(obj.getExtent());
66
- };
67
-
68
- var inp = document.createElement("input");
69
- inp.type = type;
70
- inp.name = "ogBaseLayerRadiosId" + (id || "");
71
- inp.checked = obj.getVisibility();
72
- inp.className = "ogLayerSwitcherInput";
73
- inp.onclick = function () {
74
- obj.setVisibility(this.checked);
75
- };
76
-
77
- obj.events &&
78
- obj.events.on("visibilitychange", function (e) {
79
- inp.checked = e.getVisibility();
80
- });
81
-
82
- var lbl = document.createElement("label");
83
- lbl.className = "ogLayerSwitcherLabel";
84
- lbl.innerHTML = (obj.name || obj.src || "noname") + "</br>";
85
-
86
- obj._removeCallback = function () {
87
- container.removeChild(lineDiv);
88
- };
89
-
90
- lineDiv.appendChild(center);
91
- lineDiv.appendChild(inp);
92
- lineDiv.appendChild(lbl);
93
-
94
- container.appendChild(lineDiv);
95
- }
96
-
97
- createBaseLayersContainer() {
98
- var layersDiv = document.createElement("div");
99
- layersDiv.className = "layersDiv";
100
- this.dialog.appendChild(layersDiv);
101
-
102
- var baseLayersLbl = document.createElement("div");
103
- baseLayersLbl.className = "layersDiv";
104
- baseLayersLbl.innerHTML = "Base Layer";
105
- layersDiv.appendChild(baseLayersLbl);
106
-
107
- this.baseLayersDiv = document.createElement("div");
108
- layersDiv.appendChild(this.baseLayersDiv);
109
- }
110
-
111
- createOverlaysContainer() {
112
- var overlaysDiv = document.createElement("div");
113
- overlaysDiv.className = "layersDiv";
114
- this.dialog.appendChild(overlaysDiv);
115
-
116
- var overlaysLbl = document.createElement("div");
117
- overlaysLbl.className = "layersDiv";
118
- overlaysLbl.innerHTML = "Overlays";
119
- overlaysDiv.appendChild(overlaysLbl);
120
-
121
- this.overlaysDiv = document.createElement("div");
122
- overlaysDiv.appendChild(this.overlaysDiv);
123
- }
124
-
125
- createDialog() {
126
- this.dialog = document.createElement("div");
127
- this.dialog.id = "ogLayerSwitcherDialog";
128
- this.dialog.className = "displayNone";
129
- this.renderer.div.appendChild(this.dialog);
130
-
131
- this.createBaseLayersContainer();
132
- this.createOverlaysContainer();
133
-
134
- if (this.planet) {
135
- for (var i = 0; i < this.planet.layers.length; i++) {
136
- this.onLayerAdded(this.planet.layers[i]);
137
- }
138
- }
139
- }
140
-
141
- createSwitcher() {
142
- var button = document.createElement("div");
143
- button.className = "ogLayerSwitcherButton";
144
- button.id = "ogLayerSwitcherButtonMaximize";
145
- var that = this;
146
- button.onclick = function (e) {
147
- if (this.id === "ogLayerSwitcherButtonMaximize") {
148
- this.id = "ogLayerSwitcherButtonMinimize";
149
- that.dialog.className = "displayBlock";
150
- } else {
151
- this.id = "ogLayerSwitcherButtonMaximize";
152
- that.dialog.className = "displayNone";
153
- }
154
- };
155
- this.renderer.div.appendChild(button);
156
- }
157
- }
158
-
159
- export { LayerSwitcher };
1
+ /**
2
+ * @module og/control/LayerSwitcher
3
+ */
4
+
5
+ "use strict";
6
+
7
+ import { Control } from "./Control.js";
8
+ import { elementFactory, appendChildren, toggleText, enableElmovement } from "./UIhelpers.js";
9
+ import { compose } from "../utils/functionComposition.js"
10
+ /**
11
+ * Advanced :) layer switcher, includes base layers, overlays, geo images etc. groups.
12
+ * Double click for zoom, drag-and-drop to change zIndex
13
+ * @class
14
+ * @extends {Control}
15
+ * @param {Object} [options] - Control options.
16
+ */
17
+ class LayerSwitcher extends Control {
18
+ constructor(options = {}) {
19
+ super({
20
+ name: "LayerSwitcher",
21
+ ...options
22
+ });
23
+
24
+ this._id = LayerSwitcher.numSwitches++;
25
+ this.switcherDependent = options.switcherDependent
26
+ this.expandedSections = options.expandedSections
27
+ this.switcherInMenu = options.switcherInMenu // none (default)
28
+ this.docListener = options.docListener
29
+ }
30
+
31
+ static get numSwitches() {
32
+ if (!this._counter && this._counter !== 0) {
33
+ this._counter = 0;
34
+ }
35
+ return this._counter;
36
+ }
37
+
38
+ static set numSwitches(n) {
39
+ this._counter = n;
40
+ }
41
+
42
+ oninit() {
43
+ this.setupSwitcher()
44
+ this.planet.events.on("layeradd", this.addNewLayer, this)
45
+ this.planet.events.on("layerremove", this.removeLayer, this)
46
+
47
+ }
48
+
49
+ setupSwitcher () {
50
+ const myData = this.getRecords(this.planet)
51
+ const { $mainContainer } = this.buildBasicDOM()
52
+ this.buildRecords(myData, $mainContainer, 0)
53
+
54
+ }
55
+
56
+ onactivate() {
57
+ this.setupSwitcher()
58
+ }
59
+
60
+ ondeactivate() {
61
+ const $menuBtn = document.getElementById('og-layer-switcher-menu-btn')
62
+ const $dialog = document.getElementById('og-layer-switcher-dialog')
63
+ $menuBtn.remove()
64
+ $dialog.remove()
65
+ document.removeEventListener('click', (e) => this.docListener(e))
66
+ }
67
+
68
+ // BASIC DATA COLLECTION-PREPARATION FUNCTIONS
69
+ planetDataBasic() {
70
+
71
+ const collectTerrains = (planet) => {
72
+ return [...planet._terrainPool]
73
+ }
74
+
75
+ const collectLayers = (planet) => {
76
+ return planet.getLayers()
77
+ }
78
+
79
+ const pickBaseLayers = (layers) => {
80
+ return layers.filter(x => x.isBaseLayer())
81
+ }
82
+
83
+ const pickOverlays = (layers) => {
84
+ return layers.filter(x => !x.isBaseLayer())
85
+ }
86
+
87
+ const classifyObject = (object) => {
88
+ let r = object.isBaseLayer()
89
+ if (r === true || r === false) {
90
+ return r === true ? 'BaseLayers' : 'Overlays'
91
+ }
92
+ else {
93
+ return 'Terrain Providers'
94
+ }
95
+ }
96
+
97
+ const sortByZIndex = (layers) => {
98
+ return layers.sort((a, b) => (a.getZIndex() < b.getZIndex()) ? 1 : -1)
99
+ }
100
+
101
+ const serializeZIndices = (layers) => {
102
+ layers.forEach((el, i) => el.setZIndex(10000 - i * 100))
103
+ return layers
104
+ }
105
+
106
+ const normalizeOverlay = (overlay) => {
107
+ overlay.data = overlay._entities || null
108
+ return overlay
109
+ }
110
+
111
+ return {
112
+ collectTerrains, collectLayers, pickBaseLayers, pickOverlays,
113
+ classifyObject, sortByZIndex, serializeZIndices, normalizeOverlay
114
+ }
115
+ }
116
+
117
+ // OUTPOUT TERRAINS, BASELAYERS, OVERLAYS
118
+ planetDataReturn(planet) {
119
+
120
+ const { collectTerrains, collectLayers, pickBaseLayers, pickOverlays,
121
+ sortByZIndex, serializeZIndices, normalizeOverlay } = this.planetDataBasic()
122
+
123
+ const terrains = collectTerrains(planet)
124
+
125
+ const baseLayers = compose(planet)
126
+ .run(collectLayers)
127
+ .run(pickBaseLayers)
128
+ .end()
129
+
130
+ const overlays = compose(planet)
131
+ .run(collectLayers)
132
+ .run(pickOverlays)
133
+ .run(sortByZIndex)
134
+ .run(serializeZIndices)
135
+ .runForEach(normalizeOverlay)
136
+ .end()
137
+
138
+ return { terrains, baseLayers, overlays }
139
+ }
140
+
141
+ dialogSectionStructure(name, input, data) {
142
+ const section = {}
143
+ section.name = name
144
+ section.input = input
145
+ section.data = data
146
+ return section
147
+ }
148
+
149
+ recordsStructure(terrains, baseLayers, overlays) {
150
+ const myData = {
151
+ data: [
152
+ this.dialogSectionStructure('Terrain Providers', 'radio', terrains),
153
+ this.dialogSectionStructure('Base Layers', 'radio', baseLayers),
154
+ this.dialogSectionStructure('Overlays', 'checkbox', overlays),
155
+ ]
156
+ }
157
+ // console.log(myData) // enable this to have a visual representation of the dialog structure
158
+ return myData
159
+ }
160
+
161
+ getRecords(planet) {
162
+ const { terrains, baseLayers, overlays } = this.planetDataReturn(planet)
163
+ const records = this.recordsStructure(terrains, baseLayers, overlays)
164
+ return records
165
+ }
166
+
167
+ addNewLayer(layer) {
168
+ // Put the data(layer) to the appropriate recordsStructure section and run the build function
169
+ const $dialog = document.getElementById('og-layer-switcher-dialog')
170
+ if($dialog){
171
+ const { classifyObject } = this.planetDataBasic()
172
+ const targets = [...document.body.querySelectorAll('.og-layer-switcher-record.og-depth-0 > details')]
173
+ const type = classifyObject(layer)
174
+ const object = this.recordsStructure().data.filter(x => x.name == type)
175
+ const index = this.recordsStructure().data.findIndex(x => x.name == type)
176
+ object[0].data = [layer]
177
+ this.buildRecords(object[0], targets[index], 1, true)
178
+ }
179
+ }
180
+
181
+ removeLayer(layer) {
182
+ // TODO...Haven't tested it yet
183
+ let id = layer.getID()
184
+ let el = document.body.querySelector('#' + id + ".og-layer-switcher-record.og-depth-1")
185
+ el.remove()
186
+
187
+ if (layer.displayInLayerSwitcher) { // check necessary, or else error with layers not in switcher - e.g rulerScene layers.
188
+ layer._removeCallback();
189
+ layer._removeCallback = null
190
+ }
191
+ }
192
+
193
+ getUserPrefs() {
194
+ const switcherDependency = this.switcherDependent == undefined ? true : this.switcherDependent
195
+ const sectionsOpening = this.expandedSections == undefined ? true : this.expandedSections
196
+ const btnInMenu = this.switcherInMenu == undefined ? true : this.switcherInMenu
197
+
198
+ return { switcherDependency, sectionsOpening, btnInMenu }
199
+ }
200
+
201
+ buildBasicDOM() {
202
+
203
+ const { switcherDependency, sectionsOpening, btnInMenu } = this.getUserPrefs()
204
+
205
+ // Basic DOM creation
206
+ const $menuBtn = elementFactory('div', { id: 'og-layer-switcher-menu-btn', class: 'og-menu-btn og-OFF' },
207
+ elementFactory('div', { id: 'og-layer-switcher-menu-icon', class: 'og-icon-holder' }))
208
+ const $dialog = elementFactory('div', { class: 'og-layer-switcher-dialog og-dialog og-not-visible', id: 'og-layer-switcher-dialog' })
209
+ const $header = elementFactory('div', { class: 'og-layer-switcher-dialog-header' })
210
+ const $header_close = elementFactory('div', { id: 'og-layer-switcher-dialog-close-btn', class: 'og-dialog-header-btn og-OFF' },
211
+ elementFactory('div', { class: 'og-icon-holder' }))
212
+ const $headerMinMax = elementFactory('div', { id: 'og-layer-switcher-dialog-minMax-btn', class: 'og-dialog-header-btn og-OFF' },
213
+ elementFactory('div', { class: 'og-icon-holder' }))
214
+ const $headerTitle = elementFactory('span', { class: 'og-dialog-header-title' }, 'Layer Switcher')
215
+ const $headerPin = elementFactory('div', { id: 'og-layer-switcher-dialog-pin-btn', class: 'og-dialog-header-btn og-OFF' },
216
+ elementFactory('div', { class: 'og-icon-holder' }))
217
+ const $mainContainer = elementFactory('div', { class: 'og-layer-switcher-main-container' })
218
+
219
+ $headerPin.dataset.attachement = 'UNPINNED'
220
+
221
+ // Append children to parents
222
+ appendChildren(this.planet.renderer.div, [$menuBtn, $dialog])
223
+ appendChildren($dialog, [$header, $mainContainer])
224
+ appendChildren($header, [$header_close, $headerMinMax, $headerTitle, $headerPin])
225
+
226
+ // Behaviour according to dependency on switcher
227
+ if (switcherDependency == false) {
228
+ $menuBtn.classList.add('og-hide') // hide menu btn
229
+ $dialog.classList.remove('og-not-visible') // Show dialog when opening webpage
230
+ $headerPin.classList.add('og-not-visible') // hide the pin that attaches the dialog window
231
+ }
232
+
233
+ const dialogBoxInitial = $dialog.getBoundingClientRect() // Initial position of the dialog window
234
+
235
+ // LISTENERS
236
+ const whereClick = (e, wrapper, menuBtn) => {
237
+ if (wrapper.contains(e.target)) { return 'inside' }
238
+ else if (menuBtn.contains(e.target)) { return 'on-btn' }
239
+ else { return 'outside' }
240
+ }
241
+
242
+ var whereClickHandler = null
243
+ whereClickHandler = (e) => { // cannot be a const otherwise cannot be created again in onactivate()
244
+ // Check where I clicked : in dialog, in button, elsewhere
245
+ let whereIclicked = $menuBtn ? whereClick(e, $dialog, $menuBtn) : null
246
+
247
+ // If I clicked elsewhere and the dialog is unpinned, then hide dialog and set menu btn to OFF
248
+ if (whereIclicked === 'outside' && $headerPin.dataset.attachement == 'UNPINNED') {
249
+ $dialog.classList.add('og-not-visible')
250
+ $menuBtn.classList.add('og-OFF')
251
+ // If Iclicked on button, toggle dialog and menu btn
252
+ } else if (whereIclicked === 'on-btn') {
253
+ $dialog.classList.toggle('og-not-visible')
254
+ $menuBtn.classList.toggle('og-OFF')
255
+ }
256
+ }
257
+
258
+ this.docListener = whereClickHandler // Function holder so that it is the same during add/remove
259
+
260
+ // Document
261
+ document.addEventListener('click', (e) => this.docListener(e))
262
+
263
+ // Header pin - here I am using instead of CSS classes, the data-* attribute of the DOM element to store the state
264
+ $headerPin.addEventListener('click', () => {
265
+ let newText = toggleText($headerPin.dataset.attachement, ['PINNED', 'UNPINNED'])
266
+ $headerPin.classList.toggle('og-OFF')
267
+ $headerPin.dataset.attachement = newText
268
+ })
269
+
270
+ // Header double click
271
+ $header.addEventListener('dblclick', (e) => {
272
+ if ($header !== e.target) return
273
+ restoreInitialPos($dialog)
274
+ }, false)
275
+
276
+ const restoreInitialPos = (el) => {
277
+ el.style.top = dialogBoxInitial.top + "px";
278
+ el.style.left = dialogBoxInitial.left + "px";
279
+ }
280
+
281
+ // Header mousedown --> move dialog OR nothing
282
+ const { behaviour, mouseMove } = enableElmovement($dialog, this.planet)
283
+
284
+ $header.addEventListener('mousedown', (e) => {
285
+ if ($headerPin.dataset.attachement == 'UNPINNED') {
286
+ behaviour(e)
287
+ } else {
288
+ document.removeEventListener('mousemove', mouseMove)
289
+ }
290
+ })
291
+
292
+ $header_close.addEventListener('click', () => {
293
+ $dialog.classList.add('og-not-visible')
294
+ $menuBtn.classList.add('og-OFF')
295
+ })
296
+
297
+ $headerMinMax.addEventListener('click', () => {
298
+ $mainContainer.classList.toggle('og-hide')
299
+ $mainContainer.classList.toggle('og-minimized')
300
+ $dialog.classList.toggle('og-minimized')
301
+ })
302
+
303
+ return { $mainContainer, whereClickHandler }
304
+ }
305
+
306
+ buildRecords(myData, $mainContainer, depth, createLastDropZone) {
307
+ let planet = this.planet
308
+
309
+ // Record functions
310
+ const createTitle = (object) => {
311
+ if (object.data) {
312
+ return elementFactory('details', { class: 'og-layer-switcher-record-title' },
313
+ elementFactory('summary', {}, object.name || object.url || 'no-name'))
314
+ } else {
315
+ return elementFactory('label', { class: 'og-layer-switcher-record-title' },
316
+ object.name || object.properties.name || 'no-name')
317
+ }
318
+ }
319
+
320
+ const visibility = (object, nameConcat) => {
321
+ if (
322
+ (nameConcat == 'TerrainProviders' && planet.terrain == object) ||
323
+ (nameConcat == 'BaseLayers' && planet.baseLayer == object) ||
324
+ (nameConcat == 'OverLays' && object.getVisibility() == true)
325
+ ) {
326
+ return true
327
+ }
328
+ }
329
+
330
+ const createInput = (object, depth, type, nameConcat) => {
331
+ if (depth > 0) {
332
+ return elementFactory('input', {
333
+ class: 'og-layer-switcher-record-input ' +
334
+ nameConcat, type: type || 'checkbox',
335
+ ...(visibility(object, nameConcat) ? { checked: true } : null)
336
+ }, '')
337
+ }
338
+ }
339
+
340
+ const createDropZone = (object, depth, type) => {
341
+ if (depth > 0 && type === 'checkbox') {
342
+ return elementFactory('div', { class: 'og-layer-switcher-dropZone' },)
343
+ }
344
+ }
345
+
346
+ // Record listeners
347
+ const inputListener = (input, nameConcat, object, title) => {
348
+ input ? input.addEventListener('click', () => inputClick(input, nameConcat, object, title)) : null
349
+ }
350
+
351
+ const inputClick = (input, nameConcat, object, title) => {
352
+ let siblings = [...document.querySelectorAll('.og-layer-switcher-record-input' + '.' + nameConcat)]
353
+ if (nameConcat == 'BaseLayers') {
354
+ siblings.forEach(sibling => sibling.checked = false)
355
+ input.checked = true
356
+ object.setVisibility(true)
357
+ } else if (nameConcat == 'TerrainProviders') {
358
+ siblings.forEach(sibling => sibling.checked = false)
359
+ input.checked = true
360
+ planet.setTerrain(object)
361
+ } else if (nameConcat == 'Overlays') {
362
+ object.setVisibility(input.checked)
363
+ // Handle any entities contained in layer
364
+ let entities = object._entities
365
+ let inputs = [...title.querySelectorAll('input')]
366
+ entities ? object._entities.forEach((entity, index) => {
367
+ inputs[index].checked = entity.getVisibility()
368
+ }) : null
369
+ } else {
370
+ object.setVisibility(input.checked)
371
+ }
372
+ }
373
+
374
+ const titleListener = (title, object) => {
375
+ title ? title.addEventListener('dblclick', () => titleDoubleClick(object)) : null
376
+ }
377
+
378
+ const titleDoubleClick = (object) => {
379
+ planet.flyExtent(object.getExtent())
380
+ }
381
+
382
+ // Record listeners - dragging behaviour
383
+ const recordDragStart = (record) => {
384
+ record.addEventListener('dragstart', () => {
385
+ record.classList.add('og-dragging');
386
+ })
387
+ }
388
+ const recordDragEnd = (record) => {
389
+ record.addEventListener('dragend', () => {
390
+ record.classList.remove('og-dragging');
391
+ })
392
+ }
393
+
394
+ const recordDrag = (record) => {
395
+ recordDragStart(record)
396
+ recordDragEnd(record)
397
+ }
398
+
399
+
400
+ const dropZonedragOver = (dropZone) => {
401
+ dropZone.addEventListener('dragover', (e) => {
402
+ e.preventDefault()
403
+ dropZone.classList.add('og-drag-over')
404
+ })
405
+ }
406
+
407
+ const dropZonedragLeave = (dropZone) => {
408
+ dropZone.addEventListener('dragleave', (e) => {
409
+ e.preventDefault();
410
+ dropZone.classList.remove('og-drag-over');
411
+ })
412
+ }
413
+
414
+ // TODO in case layer entities will have dropZones I will have to modify this
415
+ const dropZonedrop = (dropZone) => {
416
+ dropZone.addEventListener('drop', (e) => {
417
+ let overlayContainer = document.body.querySelector('div.og-depth-0:nth-child(3) > details:nth-child(1)')
418
+ let dropZones = [...document.querySelectorAll('.og-layer-switcher-dropZone')];
419
+ e.preventDefault();
420
+ dropZone.classList.remove('og-drag-over');
421
+ let selectedLayerRecord = document.querySelector('.og-dragging');
422
+ // Get position of drop zone - last is a special case
423
+ let pos = dropZones.indexOf(dropZone)
424
+ if (pos < dropZones.length - 1) { // not last
425
+ overlayContainer.insertBefore(selectedLayerRecord, dropZone.parentElement); // Appear before the parent element
426
+
427
+ } else { // last
428
+ overlayContainer.insertBefore(selectedLayerRecord, dropZone); // Appear before last (fixed) dropzone element
429
+ }
430
+ indicesPerDialogOrder();
431
+ });
432
+ }
433
+
434
+ const indicesPerDialogOrder = () => { // See how user has placed overlays in switcher and change ZIndexes accordingly (start 10000, go down 100)
435
+ let records = [...document.querySelectorAll('.og-layer-switcher-record.og-depth-1.Overlays')]
436
+ let ids = records.map(x => x.id)
437
+ let layers = planet.layers
438
+
439
+ let overlays = layers.filter(x => !x.isBaseLayer());
440
+ let visible_overlays = [...overlays.filter(x => x.displayInLayerSwitcher)];
441
+ for (let i = 0; i < ids.length; i++) {
442
+ let the_layer = visible_overlays.filter(x => x.getID() == ids[i]);
443
+ //
444
+ //TODO: No need to set zIndexes manually, just change the order in planet container.
445
+ //
446
+ the_layer[0].setZIndex(10000 - i * 100);
447
+ }
448
+ }
449
+
450
+ const dropZoneBehaviour = (dropZone) => {
451
+ dropZonedragOver(dropZone)
452
+ dropZonedragLeave(dropZone)
453
+ dropZonedrop(dropZone)
454
+ }
455
+
456
+ const { sectionsOpening } = this.getUserPrefs()
457
+ // Actual record creation
458
+ const createChildren = (object, $outerWrapper, depth) => {
459
+ let data = object.data || object._entities
460
+ if (data) {
461
+ data.map((x, index) => {
462
+ let nameConcat = object.name ? object.name.replace(/\s/g, '') : null
463
+ let $dropZone = createDropZone(x, depth, object.input)
464
+ let $input = createInput(x, depth, object.input, nameConcat)
465
+ let $title = createTitle(x)
466
+ $input ? inputListener($input, nameConcat, x, $title) : null
467
+ $title && depth > 0 ? titleListener($title, x) : null // need depth > 0 cause to avoid calling on main sections
468
+ $title && depth == 0 && sectionsOpening == true ? $title.setAttribute("open", "") : null // open summaries of depth 0
469
+ let $innerWrapper = elementFactory('div', {
470
+ id: depth == 1 ? x._id : null,
471
+ class: 'og-layer-switcher-record ' + 'og-depth-'
472
+ + depth + (nameConcat ? ' ' + nameConcat : ''),
473
+ draggable: $dropZone ? true : false
474
+ })
475
+
476
+ $dropZone ? dropZoneBehaviour($dropZone) : null
477
+ $dropZone ? recordDrag($innerWrapper) : null
478
+ $dropZone ? $innerWrapper.appendChild($dropZone) : null
479
+
480
+ $input ? $innerWrapper.appendChild($input) : null
481
+ $title ? $innerWrapper.appendChild($title) : null
482
+ $outerWrapper.appendChild($innerWrapper)
483
+
484
+ // Is last time in data array? Make another dropZone - fixed, not movable
485
+ if (index === data.length - 1 && createLastDropZone === true) {
486
+ let $dropZone = createDropZone(x, depth, object.input)
487
+ $dropZone ? dropZoneBehaviour($dropZone) : null
488
+ $dropZone ? recordDrag($innerWrapper) : null
489
+ $dropZone ? $outerWrapper.appendChild($dropZone) : null
490
+ }
491
+
492
+ createChildren(x, $title, depth + 1)
493
+ })
494
+ }
495
+ }
496
+ createChildren(myData, $mainContainer, depth)
497
+
498
+ }
499
+
500
+ removeRecords() {
501
+ let records = [...document.querySelectorAll('.og-layer-switcher-record.og-depth-0')]
502
+ records.forEach(record => record.remove())
503
+ }
504
+ }
505
+
506
+ export { LayerSwitcher }