umap-project 2.5.0__py3-none-any.whl → 2.6.0__py3-none-any.whl
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.
Potentially problematic release.
This version of umap-project might be problematic. Click here for more details.
- umap/__init__.py +1 -1
- umap/admin.py +6 -1
- umap/context_processors.py +2 -1
- umap/decorators.py +13 -2
- umap/forms.py +26 -2
- umap/locale/br/LC_MESSAGES/django.mo +0 -0
- umap/locale/br/LC_MESSAGES/django.po +252 -146
- umap/locale/ca/LC_MESSAGES/django.mo +0 -0
- umap/locale/ca/LC_MESSAGES/django.po +274 -162
- umap/locale/cs_CZ/LC_MESSAGES/django.mo +0 -0
- umap/locale/cs_CZ/LC_MESSAGES/django.po +261 -150
- umap/locale/de/LC_MESSAGES/django.mo +0 -0
- umap/locale/de/LC_MESSAGES/django.po +299 -187
- umap/locale/el/LC_MESSAGES/django.mo +0 -0
- umap/locale/el/LC_MESSAGES/django.po +215 -159
- umap/locale/en/LC_MESSAGES/django.po +211 -155
- umap/locale/es/LC_MESSAGES/django.mo +0 -0
- umap/locale/es/LC_MESSAGES/django.po +255 -144
- umap/locale/eu/LC_MESSAGES/django.mo +0 -0
- umap/locale/eu/LC_MESSAGES/django.po +254 -198
- umap/locale/fa_IR/LC_MESSAGES/django.mo +0 -0
- umap/locale/fa_IR/LC_MESSAGES/django.po +347 -235
- umap/locale/fr/LC_MESSAGES/django.mo +0 -0
- umap/locale/fr/LC_MESSAGES/django.po +216 -160
- umap/locale/hu/LC_MESSAGES/django.mo +0 -0
- umap/locale/hu/LC_MESSAGES/django.po +215 -159
- umap/locale/it/LC_MESSAGES/django.mo +0 -0
- umap/locale/it/LC_MESSAGES/django.po +252 -146
- umap/locale/ms/LC_MESSAGES/django.mo +0 -0
- umap/locale/ms/LC_MESSAGES/django.po +252 -146
- umap/locale/pl/LC_MESSAGES/django.mo +0 -0
- umap/locale/pl/LC_MESSAGES/django.po +254 -148
- umap/locale/pt/LC_MESSAGES/django.mo +0 -0
- umap/locale/pt/LC_MESSAGES/django.po +215 -159
- umap/locale/sv/LC_MESSAGES/django.mo +0 -0
- umap/locale/sv/LC_MESSAGES/django.po +254 -143
- umap/locale/th_TH/LC_MESSAGES/django.mo +0 -0
- umap/locale/th_TH/LC_MESSAGES/django.po +125 -70
- umap/locale/zh_TW/LC_MESSAGES/django.mo +0 -0
- umap/locale/zh_TW/LC_MESSAGES/django.po +256 -145
- umap/migrations/0022_add_team.py +94 -0
- umap/models.py +45 -10
- umap/settings/__init__.py +2 -0
- umap/settings/base.py +9 -2
- umap/static/umap/base.css +32 -41
- umap/static/umap/content.css +19 -25
- umap/static/umap/css/icon.css +63 -37
- umap/static/umap/css/importers.css +1 -1
- umap/static/umap/css/slideshow.css +7 -5
- umap/static/umap/css/tableeditor.css +4 -3
- umap/static/umap/img/16-white.svg +1 -4
- umap/static/umap/img/16.svg +2 -6
- umap/static/umap/img/24-white.svg +4 -4
- umap/static/umap/img/24.svg +6 -6
- umap/static/umap/img/source/16-white.svg +2 -5
- umap/static/umap/img/source/16.svg +3 -7
- umap/static/umap/img/source/24-white.svg +7 -14
- umap/static/umap/img/source/24.svg +10 -17
- umap/static/umap/js/components/alerts/alert.css +20 -8
- umap/static/umap/js/modules/autocomplete.js +8 -12
- umap/static/umap/js/modules/browser.js +4 -3
- umap/static/umap/js/modules/caption.js +9 -11
- umap/static/umap/js/modules/data/features.js +993 -0
- umap/static/umap/js/modules/data/layer.js +1210 -0
- umap/static/umap/js/modules/formatter.js +12 -3
- umap/static/umap/js/modules/global.js +21 -5
- umap/static/umap/js/modules/importers/overpass.js +22 -8
- umap/static/umap/js/modules/permissions.js +280 -0
- umap/static/umap/js/{umap.icon.js → modules/rendering/icon.js} +77 -56
- umap/static/umap/js/modules/rendering/layers/base.js +105 -0
- umap/static/umap/js/modules/rendering/layers/classified.js +484 -0
- umap/static/umap/js/modules/rendering/layers/cluster.js +103 -0
- umap/static/umap/js/modules/rendering/layers/heat.js +182 -0
- umap/static/umap/js/modules/rendering/popup.js +99 -0
- umap/static/umap/js/modules/rendering/template.js +217 -0
- umap/static/umap/js/modules/rendering/ui.js +610 -0
- umap/static/umap/js/modules/rules.js +16 -3
- umap/static/umap/js/modules/schema.js +25 -1
- umap/static/umap/js/modules/share.js +66 -45
- umap/static/umap/js/modules/sync/updaters.js +9 -10
- umap/static/umap/js/modules/tableeditor.js +7 -7
- umap/static/umap/js/modules/ui/dialog.js +8 -4
- umap/static/umap/js/modules/utils.js +22 -13
- umap/static/umap/js/umap.controls.js +80 -146
- umap/static/umap/js/umap.core.js +9 -9
- umap/static/umap/js/umap.forms.js +41 -17
- umap/static/umap/js/umap.js +72 -65
- umap/static/umap/locale/am_ET.js +8 -2
- umap/static/umap/locale/am_ET.json +8 -2
- umap/static/umap/locale/ar.js +8 -2
- umap/static/umap/locale/ar.json +8 -2
- umap/static/umap/locale/ast.js +8 -2
- umap/static/umap/locale/ast.json +8 -2
- umap/static/umap/locale/bg.js +8 -2
- umap/static/umap/locale/bg.json +8 -2
- umap/static/umap/locale/br.js +42 -36
- umap/static/umap/locale/br.json +42 -36
- umap/static/umap/locale/ca.js +67 -61
- umap/static/umap/locale/ca.json +67 -61
- umap/static/umap/locale/cs_CZ.js +8 -2
- umap/static/umap/locale/cs_CZ.json +8 -2
- umap/static/umap/locale/da.js +8 -2
- umap/static/umap/locale/da.json +8 -2
- umap/static/umap/locale/de.js +143 -137
- umap/static/umap/locale/de.json +143 -137
- umap/static/umap/locale/el.js +54 -48
- umap/static/umap/locale/el.json +54 -48
- umap/static/umap/locale/en.js +10 -2
- umap/static/umap/locale/en.json +10 -2
- umap/static/umap/locale/en_US.json +8 -2
- umap/static/umap/locale/es.js +8 -2
- umap/static/umap/locale/es.json +8 -2
- umap/static/umap/locale/et.js +8 -2
- umap/static/umap/locale/et.json +8 -2
- umap/static/umap/locale/eu.js +346 -338
- umap/static/umap/locale/eu.json +346 -338
- umap/static/umap/locale/fa_IR.js +415 -407
- umap/static/umap/locale/fa_IR.json +415 -407
- umap/static/umap/locale/fi.js +8 -2
- umap/static/umap/locale/fi.json +8 -2
- umap/static/umap/locale/fr.js +11 -3
- umap/static/umap/locale/fr.json +11 -3
- umap/static/umap/locale/gl.js +8 -2
- umap/static/umap/locale/gl.json +8 -2
- umap/static/umap/locale/he.js +8 -2
- umap/static/umap/locale/he.json +8 -2
- umap/static/umap/locale/hr.js +8 -2
- umap/static/umap/locale/hr.json +8 -2
- umap/static/umap/locale/hu.js +31 -23
- umap/static/umap/locale/hu.json +31 -23
- umap/static/umap/locale/id.js +8 -2
- umap/static/umap/locale/id.json +8 -2
- umap/static/umap/locale/is.js +8 -2
- umap/static/umap/locale/is.json +8 -2
- umap/static/umap/locale/it.js +8 -2
- umap/static/umap/locale/it.json +8 -2
- umap/static/umap/locale/ja.js +8 -2
- umap/static/umap/locale/ja.json +8 -2
- umap/static/umap/locale/ko.js +8 -2
- umap/static/umap/locale/ko.json +8 -2
- umap/static/umap/locale/lt.js +8 -2
- umap/static/umap/locale/lt.json +8 -2
- umap/static/umap/locale/ms.js +8 -2
- umap/static/umap/locale/ms.json +8 -2
- umap/static/umap/locale/nl.js +8 -2
- umap/static/umap/locale/nl.json +8 -2
- umap/static/umap/locale/no.js +8 -2
- umap/static/umap/locale/no.json +8 -2
- umap/static/umap/locale/pl.js +54 -48
- umap/static/umap/locale/pl.json +54 -48
- umap/static/umap/locale/pl_PL.json +8 -2
- umap/static/umap/locale/pt.js +24 -18
- umap/static/umap/locale/pt.json +24 -18
- umap/static/umap/locale/pt_BR.js +8 -2
- umap/static/umap/locale/pt_BR.json +8 -2
- umap/static/umap/locale/pt_PT.js +214 -208
- umap/static/umap/locale/pt_PT.json +214 -208
- umap/static/umap/locale/ro.js +8 -2
- umap/static/umap/locale/ro.json +8 -2
- umap/static/umap/locale/ru.js +8 -2
- umap/static/umap/locale/ru.json +8 -2
- umap/static/umap/locale/sk_SK.js +8 -2
- umap/static/umap/locale/sk_SK.json +8 -2
- umap/static/umap/locale/sl.js +8 -2
- umap/static/umap/locale/sl.json +8 -2
- umap/static/umap/locale/sr.js +8 -2
- umap/static/umap/locale/sr.json +8 -2
- umap/static/umap/locale/sv.js +8 -2
- umap/static/umap/locale/sv.json +8 -2
- umap/static/umap/locale/th_TH.js +33 -27
- umap/static/umap/locale/th_TH.json +33 -27
- umap/static/umap/locale/tr.js +8 -2
- umap/static/umap/locale/tr.json +8 -2
- umap/static/umap/locale/uk_UA.js +8 -2
- umap/static/umap/locale/uk_UA.json +8 -2
- umap/static/umap/locale/vi.js +8 -2
- umap/static/umap/locale/vi.json +8 -2
- umap/static/umap/locale/vi_VN.json +8 -2
- umap/static/umap/locale/zh.js +8 -2
- umap/static/umap/locale/zh.json +8 -2
- umap/static/umap/locale/zh_CN.json +8 -2
- umap/static/umap/locale/zh_TW.Big5.json +8 -2
- umap/static/umap/locale/zh_TW.js +102 -96
- umap/static/umap/locale/zh_TW.json +102 -96
- umap/static/umap/map.css +111 -108
- umap/static/umap/nav.css +19 -10
- umap/static/umap/unittests/utils.js +230 -107
- umap/static/umap/vars.css +1 -0
- umap/static/umap/vendors/csv2geojson/csv2geojson.js +62 -40
- umap/static/umap/vendors/editable/Leaflet.Editable.js +2079 -1937
- umap/storage.py +4 -3
- umap/templates/404.html +5 -1
- umap/templates/500.html +3 -1
- umap/templates/auth/user_detail.html +8 -2
- umap/templates/auth/user_form.html +19 -10
- umap/templates/auth/user_stars.html +8 -2
- umap/templates/base.html +1 -0
- umap/templates/registration/login.html +18 -3
- umap/templates/umap/about.html +1 -0
- umap/templates/umap/about_summary.html +22 -7
- umap/templates/umap/components/alerts/alert.html +42 -21
- umap/templates/umap/content.html +2 -0
- umap/templates/umap/content_footer.html +7 -3
- umap/templates/umap/css.html +1 -0
- umap/templates/umap/dashboard_menu.html +15 -0
- umap/templates/umap/home.html +14 -4
- umap/templates/umap/js.html +4 -9
- umap/templates/umap/login_popup_end.html +10 -4
- umap/templates/umap/map_detail.html +8 -2
- umap/templates/umap/map_fragment.html +3 -1
- umap/templates/umap/map_init.html +2 -1
- umap/templates/umap/map_list.html +6 -3
- umap/templates/umap/map_table.html +36 -12
- umap/templates/umap/messages.html +0 -1
- umap/templates/umap/navigation.html +2 -1
- umap/templates/umap/password_change.html +5 -1
- umap/templates/umap/password_change_done.html +8 -2
- umap/templates/umap/search.html +8 -2
- umap/templates/umap/search_bar.html +1 -0
- umap/templates/umap/team_confirm_delete.html +19 -0
- umap/templates/umap/team_detail.html +27 -0
- umap/templates/umap/team_form.html +60 -0
- umap/templates/umap/user_dashboard.html +7 -9
- umap/templates/umap/user_teams.html +51 -0
- umap/tests/base.py +8 -1
- umap/tests/conftest.py +6 -0
- umap/tests/fixtures/test_circles_layer.geojson +219 -0
- umap/tests/fixtures/test_upload_georss.xml +20 -0
- umap/tests/integration/conftest.py +18 -4
- umap/tests/integration/helpers.py +12 -0
- umap/tests/integration/test_anonymous_owned_map.py +23 -0
- umap/tests/integration/test_basics.py +29 -0
- umap/tests/integration/test_browser.py +20 -0
- umap/tests/integration/test_caption.py +20 -0
- umap/tests/integration/test_circles_layer.py +69 -0
- umap/tests/integration/test_conditional_rules.py +102 -17
- umap/tests/integration/test_draw_polygon.py +138 -13
- umap/tests/integration/test_draw_polyline.py +8 -18
- umap/tests/integration/test_edit_datalayer.py +3 -3
- umap/tests/integration/test_import.py +124 -5
- umap/tests/integration/test_owned_map.py +21 -13
- umap/tests/integration/test_querystring.py +7 -0
- umap/tests/integration/test_team.py +47 -0
- umap/tests/integration/test_tilelayer.py +19 -2
- umap/tests/integration/test_view_marker.py +28 -1
- umap/tests/integration/test_websocket_sync.py +5 -5
- umap/tests/test_datalayer.py +32 -7
- umap/tests/test_datalayer_views.py +1 -1
- umap/tests/test_map.py +30 -4
- umap/tests/test_map_views.py +2 -2
- umap/tests/test_statics.py +40 -0
- umap/tests/test_team_views.py +131 -0
- umap/tests/test_views.py +15 -1
- umap/urls.py +23 -13
- umap/views.py +116 -10
- {umap_project-2.5.0.dist-info → umap_project-2.6.0.dist-info}/METADATA +14 -14
- {umap_project-2.5.0.dist-info → umap_project-2.6.0.dist-info}/RECORD +260 -253
- umap/static/umap/js/umap.datalayer.permissions.js +0 -70
- umap/static/umap/js/umap.features.js +0 -1290
- umap/static/umap/js/umap.layer.js +0 -1837
- umap/static/umap/js/umap.permissions.js +0 -208
- umap/static/umap/js/umap.popup.js +0 -341
- umap/static/umap/test/TableEditor.js +0 -104
- umap/static/umap/vendors/leaflet/leaflet-src.js +0 -14512
- umap/static/umap/vendors/leaflet/leaflet-src.js.map +0 -1
- umap/static/umap/vendors/leaflet/leaflet.js +0 -6
- umap/static/umap/vendors/leaflet/leaflet.js.map +0 -1
- umap/static/umap/vendors/markercluster/WhereAreTheJavascriptFiles.txt +0 -5
- umap/static/umap/vendors/markercluster/leaflet.markercluster-src.js +0 -2718
- umap/static/umap/vendors/markercluster/leaflet.markercluster-src.js.map +0 -1
- umap/static/umap/vendors/toolbar/leaflet.toolbar-src.css +0 -117
- umap/static/umap/vendors/toolbar/leaflet.toolbar-src.js +0 -365
- umap/tests/integration/test_statics.py +0 -47
- {umap_project-2.5.0.dist-info → umap_project-2.6.0.dist-info}/WHEEL +0 -0
- {umap_project-2.5.0.dist-info → umap_project-2.6.0.dist-info}/entry_points.txt +0 -0
- {umap_project-2.5.0.dist-info → umap_project-2.6.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,1210 @@
|
|
|
1
|
+
// Uses U.FormBuilder not available as ESM
|
|
2
|
+
|
|
3
|
+
// FIXME: this module should not depend on Leaflet
|
|
4
|
+
import {
|
|
5
|
+
DomUtil,
|
|
6
|
+
DomEvent,
|
|
7
|
+
stamp,
|
|
8
|
+
GeoJSON,
|
|
9
|
+
} from '../../../vendors/leaflet/leaflet-src.esm.js'
|
|
10
|
+
import * as Utils from '../utils.js'
|
|
11
|
+
import { Default as DefaultLayer } from '../rendering/layers/base.js'
|
|
12
|
+
import { Cluster } from '../rendering/layers/cluster.js'
|
|
13
|
+
import { Heat } from '../rendering/layers/heat.js'
|
|
14
|
+
import { Categorized, Choropleth, Circles } from '../rendering/layers/classified.js'
|
|
15
|
+
import {
|
|
16
|
+
uMapAlert as Alert,
|
|
17
|
+
uMapAlertConflict as AlertConflict,
|
|
18
|
+
} from '../../components/alerts/alert.js'
|
|
19
|
+
import { translate } from '../i18n.js'
|
|
20
|
+
import { DataLayerPermissions } from '../permissions.js'
|
|
21
|
+
import { Point, LineString, Polygon } from './features.js'
|
|
22
|
+
import TableEditor from '../tableeditor.js'
|
|
23
|
+
|
|
24
|
+
export const LAYER_TYPES = [
|
|
25
|
+
DefaultLayer,
|
|
26
|
+
Cluster,
|
|
27
|
+
Heat,
|
|
28
|
+
Choropleth,
|
|
29
|
+
Categorized,
|
|
30
|
+
Circles,
|
|
31
|
+
]
|
|
32
|
+
|
|
33
|
+
const LAYER_MAP = LAYER_TYPES.reduce((acc, klass) => {
|
|
34
|
+
acc[klass.TYPE] = klass
|
|
35
|
+
return acc
|
|
36
|
+
}, {})
|
|
37
|
+
|
|
38
|
+
export class DataLayer {
|
|
39
|
+
constructor(map, data) {
|
|
40
|
+
this.map = map
|
|
41
|
+
this.sync = map.sync_engine.proxy(this)
|
|
42
|
+
this._index = Array()
|
|
43
|
+
this._features = {}
|
|
44
|
+
this._geojson = null
|
|
45
|
+
this._propertiesIndex = []
|
|
46
|
+
this._loaded = false // Are layer metadata loaded
|
|
47
|
+
this._dataloaded = false // Are layer data loaded
|
|
48
|
+
|
|
49
|
+
this.parentPane = this.map.getPane('overlayPane')
|
|
50
|
+
this.pane = this.map.createPane(`datalayer${stamp(this)}`, this.parentPane)
|
|
51
|
+
this.pane.dataset.id = stamp(this)
|
|
52
|
+
// FIXME: should be on layer
|
|
53
|
+
this.renderer = L.svg({ pane: this.pane })
|
|
54
|
+
this.defaultOptions = {
|
|
55
|
+
displayOnLoad: true,
|
|
56
|
+
inCaption: true,
|
|
57
|
+
browsable: true,
|
|
58
|
+
editMode: 'advanced',
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
this._isDirty = false
|
|
62
|
+
this._isDeleted = false
|
|
63
|
+
this.setUmapId(data.id)
|
|
64
|
+
this.setOptions(data)
|
|
65
|
+
|
|
66
|
+
if (!Utils.isObject(this.options.remoteData)) {
|
|
67
|
+
this.options.remoteData = {}
|
|
68
|
+
}
|
|
69
|
+
// Retrocompat
|
|
70
|
+
if (this.options.remoteData?.from) {
|
|
71
|
+
this.options.fromZoom = this.options.remoteData.from
|
|
72
|
+
delete this.options.remoteData.from
|
|
73
|
+
}
|
|
74
|
+
if (this.options.remoteData?.to) {
|
|
75
|
+
this.options.toZoom = this.options.remoteData.to
|
|
76
|
+
delete this.options.remoteData.to
|
|
77
|
+
}
|
|
78
|
+
this.backupOptions()
|
|
79
|
+
this.connectToMap()
|
|
80
|
+
this.permissions = new DataLayerPermissions(this)
|
|
81
|
+
if (!this.umap_id) {
|
|
82
|
+
if (this.showAtLoad()) this.show()
|
|
83
|
+
this.isDirty = true
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Only layers that are displayed on load must be hidden/shown
|
|
87
|
+
// Automatically, others will be shown manually, and thus will
|
|
88
|
+
// be in the "forced visibility" mode
|
|
89
|
+
if (this.isVisible()) this.propagateShow()
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
set isDirty(status) {
|
|
93
|
+
this._isDirty = status
|
|
94
|
+
if (status) {
|
|
95
|
+
this.map.addDirtyDatalayer(this)
|
|
96
|
+
// A layer can be made dirty by indirect action (like dragging layers)
|
|
97
|
+
// we need to have it loaded before saving it.
|
|
98
|
+
if (!this.isLoaded()) this.fetchData()
|
|
99
|
+
} else {
|
|
100
|
+
this.map.removeDirtyDatalayer(this)
|
|
101
|
+
this.isDeleted = false
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
get isDirty() {
|
|
106
|
+
return this._isDirty
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
set isDeleted(status) {
|
|
110
|
+
this._isDeleted = status
|
|
111
|
+
if (status) this.isDirty = status
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
get isDeleted() {
|
|
115
|
+
return this._isDeleted
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
getSyncMetadata() {
|
|
119
|
+
return {
|
|
120
|
+
subject: 'datalayer',
|
|
121
|
+
metadata: {
|
|
122
|
+
id: this.umap_id || null,
|
|
123
|
+
},
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
render(fields, builder) {
|
|
128
|
+
const impacts = Utils.getImpactsFromSchema(fields)
|
|
129
|
+
|
|
130
|
+
for (const impact of impacts) {
|
|
131
|
+
switch (impact) {
|
|
132
|
+
case 'ui':
|
|
133
|
+
this.map.onDataLayersChanged()
|
|
134
|
+
break
|
|
135
|
+
case 'data':
|
|
136
|
+
if (fields.includes('options.type')) {
|
|
137
|
+
this.resetLayer()
|
|
138
|
+
}
|
|
139
|
+
this.hide()
|
|
140
|
+
for (const field of fields) {
|
|
141
|
+
this.layer.onEdit(field, builder)
|
|
142
|
+
}
|
|
143
|
+
this.redraw()
|
|
144
|
+
this.show()
|
|
145
|
+
break
|
|
146
|
+
case 'remote-data':
|
|
147
|
+
this.fetchRemoteData()
|
|
148
|
+
break
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
showAtLoad() {
|
|
154
|
+
return this.autoLoaded() && this.showAtZoom()
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
autoLoaded() {
|
|
158
|
+
if (!this.map.datalayersFromQueryString) return this.options.displayOnLoad
|
|
159
|
+
const datalayerIds = this.map.datalayersFromQueryString
|
|
160
|
+
let loadMe = datalayerIds.includes(this.umap_id.toString())
|
|
161
|
+
if (this.options.old_id) {
|
|
162
|
+
loadMe = loadMe || datalayerIds.includes(this.options.old_id.toString())
|
|
163
|
+
}
|
|
164
|
+
return loadMe
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
insertBefore(other) {
|
|
168
|
+
if (!other) return
|
|
169
|
+
this.parentPane.insertBefore(this.pane, other.pane)
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
insertAfter(other) {
|
|
173
|
+
if (!other) return
|
|
174
|
+
this.parentPane.insertBefore(this.pane, other.pane.nextSibling)
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
bringToTop() {
|
|
178
|
+
this.parentPane.appendChild(this.pane)
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
hasDataVisible() {
|
|
182
|
+
return this.layer.hasDataVisible()
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
resetLayer(force) {
|
|
186
|
+
// Only reset if type is defined (undefined is the default) and different from current type
|
|
187
|
+
if (
|
|
188
|
+
this.layer &&
|
|
189
|
+
(!this.options.type || this.options.type === this.layer.getType()) &&
|
|
190
|
+
!force
|
|
191
|
+
) {
|
|
192
|
+
return
|
|
193
|
+
}
|
|
194
|
+
const visible = this.isVisible()
|
|
195
|
+
if (this.layer) this.layer.clearLayers()
|
|
196
|
+
// delete this.layer?
|
|
197
|
+
if (visible) this.map.removeLayer(this.layer)
|
|
198
|
+
const Class = LAYER_MAP[this.options.type] || DefaultLayer
|
|
199
|
+
this.layer = new Class(this)
|
|
200
|
+
// Rendering layer changed, so let's force reset the feature rendering too.
|
|
201
|
+
this.eachFeature((feature) => feature.makeUI())
|
|
202
|
+
this.eachFeature(this.showFeature)
|
|
203
|
+
if (visible) this.show()
|
|
204
|
+
this.propagateRemote()
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
eachFeature(method, context) {
|
|
208
|
+
for (const idx of this._index) {
|
|
209
|
+
method.call(context || this, this._features[idx])
|
|
210
|
+
}
|
|
211
|
+
return this
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
async fetchData() {
|
|
215
|
+
if (!this.umap_id) return
|
|
216
|
+
if (this._loading) return
|
|
217
|
+
this._loading = true
|
|
218
|
+
const [geojson, response, error] = await this.map.server.get(this._dataUrl())
|
|
219
|
+
if (!error) {
|
|
220
|
+
this._reference_version = response.headers.get('X-Datalayer-Version')
|
|
221
|
+
// FIXME: for now this property is set dynamically from backend
|
|
222
|
+
// And thus it's not in the geojson file in the server
|
|
223
|
+
// So do not let all options to be reset
|
|
224
|
+
// Fix is a proper migration so all datalayers settings are
|
|
225
|
+
// in DB, and we remove it from geojson flat files.
|
|
226
|
+
if (geojson._umap_options) {
|
|
227
|
+
geojson._umap_options.editMode = this.options.editMode
|
|
228
|
+
}
|
|
229
|
+
// In case of maps pre 1.0 still around
|
|
230
|
+
if (geojson._storage) geojson._storage.editMode = this.options.editMode
|
|
231
|
+
await this.fromUmapGeoJSON(geojson)
|
|
232
|
+
this.backupOptions()
|
|
233
|
+
this._loading = false
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
dataChanged() {
|
|
238
|
+
this.map.onDataLayersChanged()
|
|
239
|
+
this.layer.dataChanged()
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
fromGeoJSON(geojson, sync = true) {
|
|
243
|
+
this.addData(geojson, sync)
|
|
244
|
+
this._geojson = geojson
|
|
245
|
+
this._dataloaded = true
|
|
246
|
+
this.dataChanged()
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
async fromUmapGeoJSON(geojson) {
|
|
250
|
+
if (geojson._storage) geojson._umap_options = geojson._storage // Retrocompat
|
|
251
|
+
if (geojson._umap_options) this.setOptions(geojson._umap_options)
|
|
252
|
+
if (this.isRemoteLayer()) await this.fetchRemoteData()
|
|
253
|
+
else this.fromGeoJSON(geojson, false)
|
|
254
|
+
this._loaded = true
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
clear() {
|
|
258
|
+
this.layer.clearLayers()
|
|
259
|
+
this._features = {}
|
|
260
|
+
this._index = Array()
|
|
261
|
+
if (this._geojson) {
|
|
262
|
+
this.backupData()
|
|
263
|
+
this._geojson = null
|
|
264
|
+
}
|
|
265
|
+
this.dataChanged()
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
backupData() {
|
|
269
|
+
this._geojson_bk = Utils.CopyJSON(this._geojson)
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
reindex() {
|
|
273
|
+
const features = Object.values(this._features)
|
|
274
|
+
Utils.sortFeatures(features, this.map.getOption('sortKey'), L.lang)
|
|
275
|
+
this._index = features.map((feature) => stamp(feature))
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
showAtZoom() {
|
|
279
|
+
const from = Number.parseInt(this.options.fromZoom, 10)
|
|
280
|
+
const to = Number.parseInt(this.options.toZoom, 10)
|
|
281
|
+
const zoom = this.map.getZoom()
|
|
282
|
+
return !((!Number.isNaN(from) && zoom < from) || (!Number.isNaN(to) && zoom > to))
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
hasDynamicData() {
|
|
286
|
+
return this.isRemoteLayer() && Boolean(this.options.remoteData?.dynamic)
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
async fetchRemoteData(force) {
|
|
290
|
+
if (!this.isRemoteLayer()) return
|
|
291
|
+
if (!this.hasDynamicData() && this.hasDataLoaded() && !force) return
|
|
292
|
+
if (!this.isVisible()) return
|
|
293
|
+
let url = this.map.localizeUrl(this.options.remoteData.url)
|
|
294
|
+
if (this.options.remoteData.proxy) {
|
|
295
|
+
url = this.map.proxyUrl(url, this.options.remoteData.ttl)
|
|
296
|
+
}
|
|
297
|
+
const response = await this.map.request.get(url)
|
|
298
|
+
if (response?.ok) {
|
|
299
|
+
this.clear()
|
|
300
|
+
this.map.formatter
|
|
301
|
+
.parse(await response.text(), this.options.remoteData.format)
|
|
302
|
+
.then((geojson) => this.fromGeoJSON(geojson))
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
isLoaded() {
|
|
307
|
+
return !this.umap_id || this._loaded
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
hasDataLoaded() {
|
|
311
|
+
return this._dataloaded
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
setUmapId(id) {
|
|
315
|
+
// Datalayer is null when listening creation form
|
|
316
|
+
if (!this.umap_id && id) this.umap_id = id
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
backupOptions() {
|
|
320
|
+
this._backupOptions = Utils.CopyJSON(this.options)
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
resetOptions() {
|
|
324
|
+
this.options = Utils.CopyJSON(this._backupOptions)
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
setOptions(options) {
|
|
328
|
+
delete options.geojson
|
|
329
|
+
this.options = Utils.CopyJSON(this.defaultOptions) // Start from fresh.
|
|
330
|
+
this.updateOptions(options)
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
updateOptions(options) {
|
|
334
|
+
this.options = Object.assign(this.options, options)
|
|
335
|
+
this.resetLayer()
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
connectToMap() {
|
|
339
|
+
const id = stamp(this)
|
|
340
|
+
if (!this.map.datalayers[id]) {
|
|
341
|
+
this.map.datalayers[id] = this
|
|
342
|
+
if (!this.map.datalayers_index.includes(this)) {
|
|
343
|
+
this.map.datalayers_index.push(this)
|
|
344
|
+
}
|
|
345
|
+
this.map.onDataLayersChanged()
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
_dataUrl() {
|
|
350
|
+
const template = this.map.options.urls.datalayer_view
|
|
351
|
+
|
|
352
|
+
let url = Utils.template(template, {
|
|
353
|
+
pk: this.umap_id,
|
|
354
|
+
map_id: this.map.options.umap_id,
|
|
355
|
+
})
|
|
356
|
+
|
|
357
|
+
// No browser cache for owners/editors.
|
|
358
|
+
if (this.map.hasEditMode()) url = `${url}?${Date.now()}`
|
|
359
|
+
return url
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
isRemoteLayer() {
|
|
363
|
+
return Boolean(this.options.remoteData?.url && this.options.remoteData.format)
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
isClustered() {
|
|
367
|
+
return this.options.type === 'Cluster'
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
showFeature(feature) {
|
|
371
|
+
if (feature.isFiltered()) return
|
|
372
|
+
this.layer.addLayer(feature.ui)
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
hideFeature(feature) {
|
|
376
|
+
this.layer.removeLayer(feature.ui)
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
addFeature(feature) {
|
|
380
|
+
const id = stamp(feature)
|
|
381
|
+
feature.connectToDataLayer(this)
|
|
382
|
+
this._index.push(id)
|
|
383
|
+
this._features[id] = feature
|
|
384
|
+
this.indexProperties(feature)
|
|
385
|
+
this.map.features_index[feature.getSlug()] = feature
|
|
386
|
+
this.showFeature(feature)
|
|
387
|
+
if (this.hasDataLoaded()) this.dataChanged()
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
removeFeature(feature, sync) {
|
|
391
|
+
const id = stamp(feature)
|
|
392
|
+
if (sync !== false) feature.sync.delete()
|
|
393
|
+
this.hideFeature(feature)
|
|
394
|
+
delete this.map.features_index[feature.getSlug()]
|
|
395
|
+
feature.disconnectFromDataLayer(this)
|
|
396
|
+
this._index.splice(this._index.indexOf(id), 1)
|
|
397
|
+
delete this._features[id]
|
|
398
|
+
if (this.hasDataLoaded() && this.isVisible()) this.dataChanged()
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
indexProperties(feature) {
|
|
402
|
+
for (const i in feature.properties)
|
|
403
|
+
if (typeof feature.properties[i] !== 'object') this.indexProperty(i)
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
indexProperty(name) {
|
|
407
|
+
if (!name) return
|
|
408
|
+
if (name.indexOf('_') === 0) return
|
|
409
|
+
if (!this._propertiesIndex.includes(name)) {
|
|
410
|
+
this._propertiesIndex.push(name)
|
|
411
|
+
this._propertiesIndex.sort()
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
deindexProperty(name) {
|
|
416
|
+
const idx = this._propertiesIndex.indexOf(name)
|
|
417
|
+
if (idx !== -1) this._propertiesIndex.splice(idx, 1)
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
sortedValues(property) {
|
|
421
|
+
return Object.values(this._features)
|
|
422
|
+
.map((feature) => feature.properties[property])
|
|
423
|
+
.filter((val, idx, arr) => arr.indexOf(val) === idx)
|
|
424
|
+
.sort(Utils.naturalSort)
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
addData(geojson, sync) {
|
|
428
|
+
try {
|
|
429
|
+
// Do not fail if remote data is somehow invalid,
|
|
430
|
+
// otherwise the layer becomes uneditable.
|
|
431
|
+
this.makeFeatures(geojson, sync)
|
|
432
|
+
} catch (err) {
|
|
433
|
+
console.log('Error with DataLayer', this.umap_id)
|
|
434
|
+
console.error(err)
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
makeFeatures(geojson = {}, sync = true) {
|
|
439
|
+
if (geojson.type === 'Feature' || geojson.coordinates) {
|
|
440
|
+
geojson = [geojson]
|
|
441
|
+
}
|
|
442
|
+
const collection = Array.isArray(geojson)
|
|
443
|
+
? geojson
|
|
444
|
+
: geojson.features || geojson.geometries
|
|
445
|
+
Utils.sortFeatures(collection, this.map.getOption('sortKey'), L.lang)
|
|
446
|
+
for (const feature of collection) {
|
|
447
|
+
this.makeFeature(feature, sync)
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
makeFeature(geojson = {}, sync = true, id = null) {
|
|
452
|
+
// Both Feature and Geometry are valid geojson objects.
|
|
453
|
+
const geometry = geojson.geometry || geojson
|
|
454
|
+
let feature
|
|
455
|
+
|
|
456
|
+
switch (geometry.type) {
|
|
457
|
+
case 'Point':
|
|
458
|
+
// FIXME: deal with MultiPoint
|
|
459
|
+
feature = new Point(this, geojson, id)
|
|
460
|
+
break
|
|
461
|
+
case 'MultiLineString':
|
|
462
|
+
case 'LineString':
|
|
463
|
+
feature = new LineString(this, geojson, id)
|
|
464
|
+
break
|
|
465
|
+
case 'MultiPolygon':
|
|
466
|
+
case 'Polygon':
|
|
467
|
+
feature = new Polygon(this, geojson, id)
|
|
468
|
+
break
|
|
469
|
+
default:
|
|
470
|
+
console.log(geojson)
|
|
471
|
+
Alert.error(
|
|
472
|
+
translate('Skipping unknown geometry.type: {type}', {
|
|
473
|
+
type: geometry.type || 'undefined',
|
|
474
|
+
})
|
|
475
|
+
)
|
|
476
|
+
}
|
|
477
|
+
if (feature) {
|
|
478
|
+
this.addFeature(feature)
|
|
479
|
+
if (sync) feature.onCommit()
|
|
480
|
+
return feature
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
async importRaw(raw, format) {
|
|
485
|
+
this.map.formatter
|
|
486
|
+
.parse(raw, format)
|
|
487
|
+
.then((geojson) => this.addData(geojson))
|
|
488
|
+
.then(() => this.zoomTo())
|
|
489
|
+
this.isDirty = true
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
importFromFiles(files, type) {
|
|
493
|
+
for (const f of files) {
|
|
494
|
+
this.importFromFile(f, type)
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
importFromFile(f, type) {
|
|
499
|
+
const reader = new FileReader()
|
|
500
|
+
type = type || Utils.detectFileType(f)
|
|
501
|
+
reader.readAsText(f)
|
|
502
|
+
reader.onload = (e) => this.importRaw(e.target.result, type)
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
async importFromUrl(uri, type) {
|
|
506
|
+
uri = this.map.localizeUrl(uri)
|
|
507
|
+
const response = await this.map.request.get(uri)
|
|
508
|
+
if (response?.ok) {
|
|
509
|
+
this.importRaw(await response.text(), type)
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
getColor() {
|
|
514
|
+
return this.options.color || this.map.getOption('color')
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
getDeleteUrl() {
|
|
518
|
+
return Utils.template(this.map.options.urls.datalayer_delete, {
|
|
519
|
+
pk: this.umap_id,
|
|
520
|
+
map_id: this.map.options.umap_id,
|
|
521
|
+
})
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
getVersionsUrl() {
|
|
525
|
+
return Utils.template(this.map.options.urls.datalayer_versions, {
|
|
526
|
+
pk: this.umap_id,
|
|
527
|
+
map_id: this.map.options.umap_id,
|
|
528
|
+
})
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
getVersionUrl(name) {
|
|
532
|
+
return Utils.template(this.map.options.urls.datalayer_version, {
|
|
533
|
+
pk: this.umap_id,
|
|
534
|
+
map_id: this.map.options.umap_id,
|
|
535
|
+
name: name,
|
|
536
|
+
})
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
_delete() {
|
|
540
|
+
this.isDeleted = true
|
|
541
|
+
this.erase()
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
empty() {
|
|
545
|
+
if (this.isRemoteLayer()) return
|
|
546
|
+
this.clear()
|
|
547
|
+
this.isDirty = true
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
clone() {
|
|
551
|
+
const options = Utils.CopyJSON(this.options)
|
|
552
|
+
options.name = translate('Clone of {name}', { name: this.options.name })
|
|
553
|
+
delete options.id
|
|
554
|
+
const geojson = Utils.CopyJSON(this._geojson)
|
|
555
|
+
const datalayer = this.map.createDataLayer(options)
|
|
556
|
+
datalayer.fromGeoJSON(geojson)
|
|
557
|
+
return datalayer
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
erase() {
|
|
561
|
+
this.hide()
|
|
562
|
+
delete this.map.datalayers[stamp(this)]
|
|
563
|
+
this.map.datalayers_index.splice(this.getRank(), 1)
|
|
564
|
+
this.parentPane.removeChild(this.pane)
|
|
565
|
+
this.map.onDataLayersChanged()
|
|
566
|
+
this.layer.onDelete(this.map)
|
|
567
|
+
this.propagateDelete()
|
|
568
|
+
this._leaflet_events_bk = this._leaflet_events
|
|
569
|
+
this.clear()
|
|
570
|
+
delete this._loaded
|
|
571
|
+
delete this._dataloaded
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
reset() {
|
|
575
|
+
if (!this.umap_id) this.erase()
|
|
576
|
+
|
|
577
|
+
this.resetOptions()
|
|
578
|
+
this.parentPane.appendChild(this.pane)
|
|
579
|
+
if (this._leaflet_events_bk && !this._leaflet_events) {
|
|
580
|
+
this._leaflet_events = this._leaflet_events_bk
|
|
581
|
+
}
|
|
582
|
+
this.clear()
|
|
583
|
+
this.hide()
|
|
584
|
+
if (this.isRemoteLayer()) this.fetchRemoteData()
|
|
585
|
+
else if (this._geojson_bk) this.fromGeoJSON(this._geojson_bk)
|
|
586
|
+
this._loaded = true
|
|
587
|
+
this.show()
|
|
588
|
+
this.isDirty = false
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
redraw() {
|
|
592
|
+
if (!this.isVisible()) return
|
|
593
|
+
this.hide()
|
|
594
|
+
this.show()
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
edit() {
|
|
598
|
+
if (!this.map.editEnabled || !this.isLoaded()) {
|
|
599
|
+
return
|
|
600
|
+
}
|
|
601
|
+
const container = DomUtil.create('div', 'umap-layer-properties-container')
|
|
602
|
+
const metadataFields = [
|
|
603
|
+
'options.name',
|
|
604
|
+
'options.description',
|
|
605
|
+
[
|
|
606
|
+
'options.type',
|
|
607
|
+
{ handler: 'LayerTypeChooser', label: translate('Type of layer') },
|
|
608
|
+
],
|
|
609
|
+
[
|
|
610
|
+
'options.displayOnLoad',
|
|
611
|
+
{ label: translate('Display on load'), handler: 'Switch' },
|
|
612
|
+
],
|
|
613
|
+
[
|
|
614
|
+
'options.browsable',
|
|
615
|
+
{
|
|
616
|
+
label: translate('Data is browsable'),
|
|
617
|
+
handler: 'Switch',
|
|
618
|
+
helpEntries: 'browsable',
|
|
619
|
+
},
|
|
620
|
+
],
|
|
621
|
+
[
|
|
622
|
+
'options.inCaption',
|
|
623
|
+
{
|
|
624
|
+
label: translate('Show this layer in the caption'),
|
|
625
|
+
handler: 'Switch',
|
|
626
|
+
},
|
|
627
|
+
],
|
|
628
|
+
]
|
|
629
|
+
DomUtil.createTitle(container, translate('Layer properties'), 'icon-layers')
|
|
630
|
+
let builder = new U.FormBuilder(this, metadataFields, {
|
|
631
|
+
callback(e) {
|
|
632
|
+
this.map.onDataLayersChanged()
|
|
633
|
+
if (e.helper.field === 'options.type') {
|
|
634
|
+
this.edit()
|
|
635
|
+
}
|
|
636
|
+
},
|
|
637
|
+
})
|
|
638
|
+
container.appendChild(builder.build())
|
|
639
|
+
|
|
640
|
+
const layerOptions = this.layer.getEditableOptions()
|
|
641
|
+
|
|
642
|
+
if (layerOptions.length) {
|
|
643
|
+
builder = new U.FormBuilder(this, layerOptions, {
|
|
644
|
+
id: 'datalayer-layer-properties',
|
|
645
|
+
})
|
|
646
|
+
const layerProperties = DomUtil.createFieldset(
|
|
647
|
+
container,
|
|
648
|
+
`${this.layer.getName()}: ${translate('settings')}`
|
|
649
|
+
)
|
|
650
|
+
layerProperties.appendChild(builder.build())
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
const shapeOptions = [
|
|
654
|
+
'options.color',
|
|
655
|
+
'options.iconClass',
|
|
656
|
+
'options.iconUrl',
|
|
657
|
+
'options.iconOpacity',
|
|
658
|
+
'options.opacity',
|
|
659
|
+
'options.stroke',
|
|
660
|
+
'options.weight',
|
|
661
|
+
'options.fill',
|
|
662
|
+
'options.fillColor',
|
|
663
|
+
'options.fillOpacity',
|
|
664
|
+
]
|
|
665
|
+
|
|
666
|
+
builder = new U.FormBuilder(this, shapeOptions, {
|
|
667
|
+
id: 'datalayer-advanced-properties',
|
|
668
|
+
})
|
|
669
|
+
const shapeProperties = DomUtil.createFieldset(
|
|
670
|
+
container,
|
|
671
|
+
translate('Shape properties')
|
|
672
|
+
)
|
|
673
|
+
shapeProperties.appendChild(builder.build())
|
|
674
|
+
|
|
675
|
+
const optionsFields = [
|
|
676
|
+
'options.smoothFactor',
|
|
677
|
+
'options.dashArray',
|
|
678
|
+
'options.zoomTo',
|
|
679
|
+
'options.fromZoom',
|
|
680
|
+
'options.toZoom',
|
|
681
|
+
'options.labelKey',
|
|
682
|
+
]
|
|
683
|
+
|
|
684
|
+
builder = new U.FormBuilder(this, optionsFields, {
|
|
685
|
+
id: 'datalayer-advanced-properties',
|
|
686
|
+
})
|
|
687
|
+
const advancedProperties = DomUtil.createFieldset(
|
|
688
|
+
container,
|
|
689
|
+
translate('Advanced properties')
|
|
690
|
+
)
|
|
691
|
+
advancedProperties.appendChild(builder.build())
|
|
692
|
+
|
|
693
|
+
const popupFields = [
|
|
694
|
+
'options.popupShape',
|
|
695
|
+
'options.popupTemplate',
|
|
696
|
+
'options.popupContentTemplate',
|
|
697
|
+
'options.showLabel',
|
|
698
|
+
'options.labelDirection',
|
|
699
|
+
'options.labelInteractive',
|
|
700
|
+
'options.outlinkTarget',
|
|
701
|
+
'options.interactive',
|
|
702
|
+
]
|
|
703
|
+
builder = new U.FormBuilder(this, popupFields)
|
|
704
|
+
const popupFieldset = DomUtil.createFieldset(
|
|
705
|
+
container,
|
|
706
|
+
translate('Interaction options')
|
|
707
|
+
)
|
|
708
|
+
popupFieldset.appendChild(builder.build())
|
|
709
|
+
|
|
710
|
+
// XXX I'm not sure **why** this is needed (as it's set during `this.initialize`)
|
|
711
|
+
// but apparently it's needed.
|
|
712
|
+
if (!Utils.isObject(this.options.remoteData)) {
|
|
713
|
+
this.options.remoteData = {}
|
|
714
|
+
}
|
|
715
|
+
|
|
716
|
+
const remoteDataFields = [
|
|
717
|
+
[
|
|
718
|
+
'options.remoteData.url',
|
|
719
|
+
{ handler: 'Url', label: translate('Url'), helpEntries: 'formatURL' },
|
|
720
|
+
],
|
|
721
|
+
[
|
|
722
|
+
'options.remoteData.format',
|
|
723
|
+
{ handler: 'DataFormat', label: translate('Format') },
|
|
724
|
+
],
|
|
725
|
+
'options.fromZoom',
|
|
726
|
+
'options.toZoom',
|
|
727
|
+
[
|
|
728
|
+
'options.remoteData.dynamic',
|
|
729
|
+
{
|
|
730
|
+
handler: 'Switch',
|
|
731
|
+
label: translate('Dynamic'),
|
|
732
|
+
helpEntries: 'dynamicRemoteData',
|
|
733
|
+
},
|
|
734
|
+
],
|
|
735
|
+
[
|
|
736
|
+
'options.remoteData.licence',
|
|
737
|
+
{
|
|
738
|
+
label: translate('Licence'),
|
|
739
|
+
helpText: translate('Please be sure the licence is compliant with your use.'),
|
|
740
|
+
},
|
|
741
|
+
],
|
|
742
|
+
]
|
|
743
|
+
if (this.map.options.urls.ajax_proxy) {
|
|
744
|
+
remoteDataFields.push([
|
|
745
|
+
'options.remoteData.proxy',
|
|
746
|
+
{
|
|
747
|
+
handler: 'Switch',
|
|
748
|
+
label: translate('Proxy request'),
|
|
749
|
+
helpEntries: 'proxyRemoteData',
|
|
750
|
+
},
|
|
751
|
+
])
|
|
752
|
+
remoteDataFields.push('options.remoteData.ttl')
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
const remoteDataContainer = DomUtil.createFieldset(
|
|
756
|
+
container,
|
|
757
|
+
translate('Remote data')
|
|
758
|
+
)
|
|
759
|
+
builder = new U.FormBuilder(this, remoteDataFields)
|
|
760
|
+
remoteDataContainer.appendChild(builder.build())
|
|
761
|
+
DomUtil.createButton(
|
|
762
|
+
'button umap-verify',
|
|
763
|
+
remoteDataContainer,
|
|
764
|
+
translate('Verify remote URL'),
|
|
765
|
+
() => this.fetchRemoteData(true),
|
|
766
|
+
this
|
|
767
|
+
)
|
|
768
|
+
|
|
769
|
+
if (this.map.options.urls.datalayer_versions) this.buildVersionsFieldset(container)
|
|
770
|
+
|
|
771
|
+
const advancedActions = DomUtil.createFieldset(
|
|
772
|
+
container,
|
|
773
|
+
translate('Advanced actions')
|
|
774
|
+
)
|
|
775
|
+
const advancedButtons = DomUtil.create('div', 'button-bar half', advancedActions)
|
|
776
|
+
const deleteButton = Utils.loadTemplate(`
|
|
777
|
+
<button class="button" type="button">
|
|
778
|
+
<i class="icon icon-24 icon-delete"></i>${translate('Delete')}
|
|
779
|
+
</button>`)
|
|
780
|
+
deleteButton.addEventListener('click', () => {
|
|
781
|
+
this._delete()
|
|
782
|
+
this.map.editPanel.close()
|
|
783
|
+
})
|
|
784
|
+
advancedButtons.appendChild(deleteButton)
|
|
785
|
+
|
|
786
|
+
if (!this.isRemoteLayer()) {
|
|
787
|
+
const emptyLink = DomUtil.createButton(
|
|
788
|
+
'button umap-empty',
|
|
789
|
+
advancedButtons,
|
|
790
|
+
translate('Empty'),
|
|
791
|
+
this.empty,
|
|
792
|
+
this
|
|
793
|
+
)
|
|
794
|
+
}
|
|
795
|
+
const cloneLink = DomUtil.createButton(
|
|
796
|
+
'button umap-clone',
|
|
797
|
+
advancedButtons,
|
|
798
|
+
translate('Clone'),
|
|
799
|
+
function () {
|
|
800
|
+
const datalayer = this.clone()
|
|
801
|
+
datalayer.edit()
|
|
802
|
+
},
|
|
803
|
+
this
|
|
804
|
+
)
|
|
805
|
+
if (this.umap_id) {
|
|
806
|
+
const download = DomUtil.createLink(
|
|
807
|
+
'button umap-download',
|
|
808
|
+
advancedButtons,
|
|
809
|
+
translate('Download'),
|
|
810
|
+
this._dataUrl(),
|
|
811
|
+
'_blank'
|
|
812
|
+
)
|
|
813
|
+
}
|
|
814
|
+
const backButton = DomUtil.createButtonIcon(
|
|
815
|
+
undefined,
|
|
816
|
+
'icon-back',
|
|
817
|
+
translate('Back to layers')
|
|
818
|
+
)
|
|
819
|
+
// Fixme: remove me when this is merged and released
|
|
820
|
+
// https://github.com/Leaflet/Leaflet/pull/9052
|
|
821
|
+
DomEvent.disableClickPropagation(backButton)
|
|
822
|
+
DomEvent.on(backButton, 'click', this.map.editDatalayers, this.map)
|
|
823
|
+
|
|
824
|
+
this.map.editPanel.open({
|
|
825
|
+
content: container,
|
|
826
|
+
actions: [backButton],
|
|
827
|
+
})
|
|
828
|
+
}
|
|
829
|
+
|
|
830
|
+
getOwnOption(option) {
|
|
831
|
+
if (Utils.usableOption(this.options, option)) return this.options[option]
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
getOption(option, feature) {
|
|
835
|
+
if (this.layer?.getOption) {
|
|
836
|
+
const value = this.layer.getOption(option, feature)
|
|
837
|
+
if (value !== undefined) return value
|
|
838
|
+
}
|
|
839
|
+
if (this.getOwnOption(option) !== undefined) {
|
|
840
|
+
return this.getOwnOption(option)
|
|
841
|
+
}
|
|
842
|
+
if (this.layer?.defaults?.[option]) {
|
|
843
|
+
return this.layer.defaults[option]
|
|
844
|
+
}
|
|
845
|
+
return this.map.getOption(option, feature)
|
|
846
|
+
}
|
|
847
|
+
|
|
848
|
+
async buildVersionsFieldset(container) {
|
|
849
|
+
const appendVersion = (data) => {
|
|
850
|
+
const date = new Date(Number.parseInt(data.at, 10))
|
|
851
|
+
const content = `${date.toLocaleString(L.lang)} (${Number.parseInt(data.size) / 1000}Kb)`
|
|
852
|
+
const el = DomUtil.create('div', 'umap-datalayer-version', versionsContainer)
|
|
853
|
+
const button = DomUtil.createButton(
|
|
854
|
+
'',
|
|
855
|
+
el,
|
|
856
|
+
'',
|
|
857
|
+
() => this.restore(data.name),
|
|
858
|
+
this
|
|
859
|
+
)
|
|
860
|
+
button.title = translate('Restore this version')
|
|
861
|
+
DomUtil.add('span', '', el, content)
|
|
862
|
+
}
|
|
863
|
+
|
|
864
|
+
const versionsContainer = DomUtil.createFieldset(container, translate('Versions'), {
|
|
865
|
+
async callback() {
|
|
866
|
+
const [{ versions }, response, error] = await this.map.server.get(
|
|
867
|
+
this.getVersionsUrl()
|
|
868
|
+
)
|
|
869
|
+
if (!error) versions.forEach(appendVersion)
|
|
870
|
+
},
|
|
871
|
+
context: this,
|
|
872
|
+
})
|
|
873
|
+
}
|
|
874
|
+
|
|
875
|
+
async restore(version) {
|
|
876
|
+
if (!this.map.editEnabled) return
|
|
877
|
+
if (!confirm(translate('Are you sure you want to restore this version?'))) return
|
|
878
|
+
const [geojson, response, error] = await this.map.server.get(
|
|
879
|
+
this.getVersionUrl(version)
|
|
880
|
+
)
|
|
881
|
+
if (!error) {
|
|
882
|
+
if (geojson._storage) geojson._umap_options = geojson._storage // Retrocompat.
|
|
883
|
+
if (geojson._umap_options) this.setOptions(geojson._umap_options)
|
|
884
|
+
this.empty()
|
|
885
|
+
if (this.isRemoteLayer()) this.fetchRemoteData()
|
|
886
|
+
else this.addData(geojson)
|
|
887
|
+
this.isDirty = true
|
|
888
|
+
}
|
|
889
|
+
}
|
|
890
|
+
|
|
891
|
+
featuresToGeoJSON() {
|
|
892
|
+
const features = []
|
|
893
|
+
this.eachFeature((feature) => features.push(feature.toGeoJSON()))
|
|
894
|
+
return features
|
|
895
|
+
}
|
|
896
|
+
|
|
897
|
+
async show() {
|
|
898
|
+
this.map.addLayer(this.layer)
|
|
899
|
+
if (!this.isLoaded()) await this.fetchData()
|
|
900
|
+
this.propagateShow()
|
|
901
|
+
}
|
|
902
|
+
|
|
903
|
+
hide() {
|
|
904
|
+
this.map.removeLayer(this.layer)
|
|
905
|
+
this.propagateHide()
|
|
906
|
+
}
|
|
907
|
+
|
|
908
|
+
toggle() {
|
|
909
|
+
// From now on, do not try to how/hidedataChanged
|
|
910
|
+
// automatically this layer.
|
|
911
|
+
this._forcedVisibility = true
|
|
912
|
+
if (!this.isVisible()) this.show()
|
|
913
|
+
else this.hide()
|
|
914
|
+
}
|
|
915
|
+
|
|
916
|
+
zoomTo() {
|
|
917
|
+
if (!this.isVisible()) return
|
|
918
|
+
const bounds = this.layer.getBounds()
|
|
919
|
+
if (bounds.isValid()) {
|
|
920
|
+
const options = { maxZoom: this.getOption('zoomTo') }
|
|
921
|
+
this.map.fitBounds(bounds, options)
|
|
922
|
+
}
|
|
923
|
+
}
|
|
924
|
+
|
|
925
|
+
// Is this layer type browsable in theorie
|
|
926
|
+
isBrowsable() {
|
|
927
|
+
return this.layer?.browsable
|
|
928
|
+
}
|
|
929
|
+
|
|
930
|
+
// Is this layer browsable in theorie
|
|
931
|
+
// AND the user allows it
|
|
932
|
+
allowBrowse() {
|
|
933
|
+
return !!this.options.browsable && this.isBrowsable()
|
|
934
|
+
}
|
|
935
|
+
|
|
936
|
+
// Is this layer browsable in theorie
|
|
937
|
+
// AND the user allows it
|
|
938
|
+
// AND it makes actually sense (is visible, it has data…)
|
|
939
|
+
canBrowse() {
|
|
940
|
+
return this.allowBrowse() && this.isVisible() && this.hasData()
|
|
941
|
+
}
|
|
942
|
+
|
|
943
|
+
count() {
|
|
944
|
+
return this._index.length
|
|
945
|
+
}
|
|
946
|
+
|
|
947
|
+
hasData() {
|
|
948
|
+
return !!this._index.length
|
|
949
|
+
}
|
|
950
|
+
|
|
951
|
+
isVisible() {
|
|
952
|
+
return Boolean(this.layer && this.map.hasLayer(this.layer))
|
|
953
|
+
}
|
|
954
|
+
|
|
955
|
+
getFeatureByIndex(index) {
|
|
956
|
+
if (index === -1) index = this._index.length - 1
|
|
957
|
+
const id = this._index[index]
|
|
958
|
+
return this._features[id]
|
|
959
|
+
}
|
|
960
|
+
|
|
961
|
+
// TODO Add an index
|
|
962
|
+
// For now, iterate on all the features.
|
|
963
|
+
getFeatureById(id) {
|
|
964
|
+
return Object.values(this._features).find((feature) => feature.id === id)
|
|
965
|
+
}
|
|
966
|
+
|
|
967
|
+
getNextFeature(feature) {
|
|
968
|
+
const id = this._index.indexOf(stamp(feature))
|
|
969
|
+
const nextId = this._index[id + 1]
|
|
970
|
+
return nextId
|
|
971
|
+
? this._features[nextId]
|
|
972
|
+
: this.getNextBrowsable().getFeatureByIndex(0)
|
|
973
|
+
}
|
|
974
|
+
|
|
975
|
+
getPreviousFeature(feature) {
|
|
976
|
+
if (this._index <= 1) {
|
|
977
|
+
return null
|
|
978
|
+
}
|
|
979
|
+
const id = this._index.indexOf(stamp(feature))
|
|
980
|
+
const previousId = this._index[id - 1]
|
|
981
|
+
return previousId
|
|
982
|
+
? this._features[previousId]
|
|
983
|
+
: this.getPreviousBrowsable().getFeatureByIndex(-1)
|
|
984
|
+
}
|
|
985
|
+
|
|
986
|
+
getPreviousBrowsable() {
|
|
987
|
+
let id = this.getRank()
|
|
988
|
+
let next
|
|
989
|
+
const index = this.map.datalayers_index
|
|
990
|
+
while (((id = index[++id] ? id : 0), (next = index[id]))) {
|
|
991
|
+
if (next === this || next.canBrowse()) break
|
|
992
|
+
}
|
|
993
|
+
return next
|
|
994
|
+
}
|
|
995
|
+
|
|
996
|
+
getNextBrowsable() {
|
|
997
|
+
let id = this.getRank()
|
|
998
|
+
let prev
|
|
999
|
+
const index = this.map.datalayers_index
|
|
1000
|
+
while (((id = index[--id] ? id : index.length - 1), (prev = index[id]))) {
|
|
1001
|
+
if (prev === this || prev.canBrowse()) break
|
|
1002
|
+
}
|
|
1003
|
+
return prev
|
|
1004
|
+
}
|
|
1005
|
+
|
|
1006
|
+
umapGeoJSON() {
|
|
1007
|
+
return {
|
|
1008
|
+
type: 'FeatureCollection',
|
|
1009
|
+
features: this.isRemoteLayer() ? [] : this.featuresToGeoJSON(),
|
|
1010
|
+
_umap_options: this.options,
|
|
1011
|
+
}
|
|
1012
|
+
}
|
|
1013
|
+
|
|
1014
|
+
getRank() {
|
|
1015
|
+
return this.map.datalayers_index.indexOf(this)
|
|
1016
|
+
}
|
|
1017
|
+
|
|
1018
|
+
isReadOnly() {
|
|
1019
|
+
// isReadOnly must return true if unset
|
|
1020
|
+
return this.options.editMode === 'disabled'
|
|
1021
|
+
}
|
|
1022
|
+
|
|
1023
|
+
isDataReadOnly() {
|
|
1024
|
+
// This layer cannot accept features
|
|
1025
|
+
return this.isReadOnly() || this.isRemoteLayer()
|
|
1026
|
+
}
|
|
1027
|
+
|
|
1028
|
+
async save() {
|
|
1029
|
+
if (this.isDeleted) return this.saveDelete()
|
|
1030
|
+
if (!this.isLoaded()) {
|
|
1031
|
+
return
|
|
1032
|
+
}
|
|
1033
|
+
const geojson = this.umapGeoJSON()
|
|
1034
|
+
const formData = new FormData()
|
|
1035
|
+
formData.append('name', this.options.name)
|
|
1036
|
+
formData.append('display_on_load', !!this.options.displayOnLoad)
|
|
1037
|
+
formData.append('rank', this.getRank())
|
|
1038
|
+
formData.append('settings', JSON.stringify(this.options))
|
|
1039
|
+
// Filename support is shaky, don't do it for now.
|
|
1040
|
+
const blob = new Blob([JSON.stringify(geojson)], { type: 'application/json' })
|
|
1041
|
+
formData.append('geojson', blob)
|
|
1042
|
+
const saveUrl = this.map.urls.get('datalayer_save', {
|
|
1043
|
+
map_id: this.map.options.umap_id,
|
|
1044
|
+
pk: this.umap_id,
|
|
1045
|
+
})
|
|
1046
|
+
const headers = this._reference_version
|
|
1047
|
+
? { 'X-Datalayer-Reference': this._reference_version }
|
|
1048
|
+
: {}
|
|
1049
|
+
await this._trySave(saveUrl, headers, formData)
|
|
1050
|
+
this._geojson = geojson
|
|
1051
|
+
}
|
|
1052
|
+
|
|
1053
|
+
async _trySave(url, headers, formData) {
|
|
1054
|
+
const [data, response, error] = await this.map.server.post(url, headers, formData)
|
|
1055
|
+
if (error) {
|
|
1056
|
+
if (response && response.status === 412) {
|
|
1057
|
+
AlertConflict.error(
|
|
1058
|
+
translate(
|
|
1059
|
+
'Whoops! Other contributor(s) changed some of the same map elements as you. ' +
|
|
1060
|
+
'This situation is tricky, you have to choose carefully which version is pertinent.'
|
|
1061
|
+
),
|
|
1062
|
+
async () => {
|
|
1063
|
+
await this._trySave(url, {}, formData)
|
|
1064
|
+
}
|
|
1065
|
+
)
|
|
1066
|
+
}
|
|
1067
|
+
} else {
|
|
1068
|
+
// Response contains geojson only if save has conflicted and conflicts have
|
|
1069
|
+
// been resolved. So we need to reload to get extra data (added by someone else)
|
|
1070
|
+
if (data.geojson) {
|
|
1071
|
+
this.clear()
|
|
1072
|
+
this.fromGeoJSON(data.geojson)
|
|
1073
|
+
delete data.geojson
|
|
1074
|
+
}
|
|
1075
|
+
this._reference_version = response.headers.get('X-Datalayer-Version')
|
|
1076
|
+
this.sync.update('_reference_version', this._reference_version)
|
|
1077
|
+
|
|
1078
|
+
this.setUmapId(data.id)
|
|
1079
|
+
this.updateOptions(data)
|
|
1080
|
+
this.backupOptions()
|
|
1081
|
+
this.connectToMap()
|
|
1082
|
+
this._loaded = true
|
|
1083
|
+
this.redraw() // Needed for reordering features
|
|
1084
|
+
this.isDirty = false
|
|
1085
|
+
this.permissions.save()
|
|
1086
|
+
}
|
|
1087
|
+
}
|
|
1088
|
+
|
|
1089
|
+
async saveDelete() {
|
|
1090
|
+
if (this.umap_id) {
|
|
1091
|
+
await this.map.server.post(this.getDeleteUrl())
|
|
1092
|
+
}
|
|
1093
|
+
this.isDirty = false
|
|
1094
|
+
this.map.continueSaving()
|
|
1095
|
+
}
|
|
1096
|
+
|
|
1097
|
+
getMap() {
|
|
1098
|
+
return this.map
|
|
1099
|
+
}
|
|
1100
|
+
|
|
1101
|
+
getName() {
|
|
1102
|
+
return this.options.name || translate('Untitled layer')
|
|
1103
|
+
}
|
|
1104
|
+
|
|
1105
|
+
tableEdit() {
|
|
1106
|
+
if (!this.isVisible()) return
|
|
1107
|
+
const editor = new TableEditor(this)
|
|
1108
|
+
editor.open()
|
|
1109
|
+
}
|
|
1110
|
+
|
|
1111
|
+
getFilterKeys() {
|
|
1112
|
+
// This keys will be used to filter feature from the browser text input.
|
|
1113
|
+
// By default, it will we use the "name" property, which is also the one used as label in the features list.
|
|
1114
|
+
// When map owner has configured another label or sort key, we try to be smart and search in the same keys.
|
|
1115
|
+
if (this.map.options.filterKey) return this.map.options.filterKey
|
|
1116
|
+
if (this.getOption('labelKey')) return this.getOption('labelKey')
|
|
1117
|
+
if (this.map.options.sortKey) return this.map.options.sortKey
|
|
1118
|
+
return 'displayName'
|
|
1119
|
+
}
|
|
1120
|
+
|
|
1121
|
+
renderLegend(container) {
|
|
1122
|
+
if (this.layer.renderLegend) return this.layer.renderLegend(container)
|
|
1123
|
+
const color = DomUtil.create('span', 'datalayer-color', container)
|
|
1124
|
+
color.style.backgroundColor = this.getColor()
|
|
1125
|
+
}
|
|
1126
|
+
|
|
1127
|
+
renderToolbox(container) {
|
|
1128
|
+
const toggle = DomUtil.createButtonIcon(
|
|
1129
|
+
container,
|
|
1130
|
+
'icon-eye',
|
|
1131
|
+
translate('Show/hide layer')
|
|
1132
|
+
)
|
|
1133
|
+
const zoomTo = DomUtil.createButtonIcon(
|
|
1134
|
+
container,
|
|
1135
|
+
'icon-zoom',
|
|
1136
|
+
translate('Zoom to layer extent')
|
|
1137
|
+
)
|
|
1138
|
+
const edit = DomUtil.createButtonIcon(
|
|
1139
|
+
container,
|
|
1140
|
+
'icon-edit show-on-edit',
|
|
1141
|
+
translate('Edit')
|
|
1142
|
+
)
|
|
1143
|
+
const table = DomUtil.createButtonIcon(
|
|
1144
|
+
container,
|
|
1145
|
+
'icon-table show-on-edit',
|
|
1146
|
+
translate('Edit properties in a table')
|
|
1147
|
+
)
|
|
1148
|
+
const remove = DomUtil.createButtonIcon(
|
|
1149
|
+
container,
|
|
1150
|
+
'icon-delete show-on-edit',
|
|
1151
|
+
translate('Delete layer')
|
|
1152
|
+
)
|
|
1153
|
+
if (this.isReadOnly()) {
|
|
1154
|
+
DomUtil.addClass(container, 'readonly')
|
|
1155
|
+
} else {
|
|
1156
|
+
DomEvent.on(edit, 'click', this.edit, this)
|
|
1157
|
+
DomEvent.on(table, 'click', this.tableEdit, this)
|
|
1158
|
+
DomEvent.on(
|
|
1159
|
+
remove,
|
|
1160
|
+
'click',
|
|
1161
|
+
function () {
|
|
1162
|
+
if (!this.isVisible()) return
|
|
1163
|
+
if (!confirm(translate('Are you sure you want to delete this layer?'))) return
|
|
1164
|
+
this._delete()
|
|
1165
|
+
},
|
|
1166
|
+
this
|
|
1167
|
+
)
|
|
1168
|
+
}
|
|
1169
|
+
DomEvent.on(toggle, 'click', this.toggle, this)
|
|
1170
|
+
DomEvent.on(zoomTo, 'click', this.zoomTo, this)
|
|
1171
|
+
container.classList.add(this.getHidableClass())
|
|
1172
|
+
container.classList.toggle('off', !this.isVisible())
|
|
1173
|
+
}
|
|
1174
|
+
|
|
1175
|
+
getHidableElements() {
|
|
1176
|
+
return document.querySelectorAll(`.${this.getHidableClass()}`)
|
|
1177
|
+
}
|
|
1178
|
+
|
|
1179
|
+
getHidableClass() {
|
|
1180
|
+
return `show_with_datalayer_${stamp(this)}`
|
|
1181
|
+
}
|
|
1182
|
+
|
|
1183
|
+
propagateDelete() {
|
|
1184
|
+
const els = this.getHidableElements()
|
|
1185
|
+
for (const el of els) {
|
|
1186
|
+
DomUtil.remove(el)
|
|
1187
|
+
}
|
|
1188
|
+
}
|
|
1189
|
+
|
|
1190
|
+
propagateRemote() {
|
|
1191
|
+
const els = this.getHidableElements()
|
|
1192
|
+
for (const el of els) {
|
|
1193
|
+
el.classList.toggle('remotelayer', this.isRemoteLayer())
|
|
1194
|
+
}
|
|
1195
|
+
}
|
|
1196
|
+
|
|
1197
|
+
propagateHide() {
|
|
1198
|
+
const els = this.getHidableElements()
|
|
1199
|
+
for (let i = 0; i < els.length; i++) {
|
|
1200
|
+
DomUtil.addClass(els[i], 'off')
|
|
1201
|
+
}
|
|
1202
|
+
}
|
|
1203
|
+
|
|
1204
|
+
propagateShow() {
|
|
1205
|
+
const els = this.getHidableElements()
|
|
1206
|
+
for (let i = 0; i < els.length; i++) {
|
|
1207
|
+
DomUtil.removeClass(els[i], 'off')
|
|
1208
|
+
}
|
|
1209
|
+
}
|
|
1210
|
+
}
|