umap-project 1.14.0a1__py3-none-any.whl → 1.14.0a2__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.

Files changed (182) hide show
  1. umap/__init__.py +1 -1
  2. umap/static/.gitignore +0 -0
  3. umap/static/umap/base.css +994 -0
  4. umap/static/umap/bitbucket.png +0 -0
  5. umap/static/umap/content.css +395 -0
  6. umap/static/umap/favicons/apple-touch-icon.png +0 -0
  7. umap/static/umap/favicons/favicon.ico +0 -0
  8. umap/static/umap/favicons/icon-192.png +0 -0
  9. umap/static/umap/favicons/icon-512.png +0 -0
  10. umap/static/umap/favicons/icon.svg +5 -0
  11. umap/static/umap/font/FiraSans-Light.woff +0 -0
  12. umap/static/umap/font/FiraSans-Light.woff2 +0 -0
  13. umap/static/umap/font/FiraSans-LightItalic.woff +0 -0
  14. umap/static/umap/font/FiraSans-LightItalic.woff2 +0 -0
  15. umap/static/umap/font/FiraSans-SemiBold.woff +0 -0
  16. umap/static/umap/font/FiraSans-SemiBold.woff2 +0 -0
  17. umap/static/umap/font.css +33 -0
  18. umap/static/umap/github.png +0 -0
  19. umap/static/umap/img/16-white.svg +190 -0
  20. umap/static/umap/img/16.svg +182 -0
  21. umap/static/umap/img/24-white.svg +62 -0
  22. umap/static/umap/img/24.svg +90 -0
  23. umap/static/umap/img/edit.svg +7 -0
  24. umap/static/umap/img/icon-bg.png +0 -0
  25. umap/static/umap/img/logo.svg +4 -0
  26. umap/static/umap/img/logo_filigree.png +0 -0
  27. umap/static/umap/img/logo_small.svg +14 -0
  28. umap/static/umap/img/marker.png +0 -0
  29. umap/static/umap/img/opensource.svg +7 -0
  30. umap/static/umap/img/osm.svg +7 -0
  31. umap/static/umap/img/search.gif +0 -0
  32. umap/static/umap/img/source/16-white.svg +980 -0
  33. umap/static/umap/img/source/16.svg +201 -0
  34. umap/static/umap/img/source/24-white.svg +83 -0
  35. umap/static/umap/img/source/24.svg +110 -0
  36. umap/static/umap/js/components/fragment.js +13 -0
  37. umap/static/umap/js/modules/global.js +8 -0
  38. umap/static/umap/js/modules/urls.js +29 -0
  39. umap/static/umap/js/umap.autocomplete.js +336 -0
  40. umap/static/umap/js/umap.browser.js +148 -0
  41. umap/static/umap/js/umap.controls.js +1542 -0
  42. umap/static/umap/js/umap.core.js +851 -0
  43. umap/static/umap/js/umap.datalayer.permissions.js +72 -0
  44. umap/static/umap/js/umap.features.js +1216 -0
  45. umap/static/umap/js/umap.forms.js +1267 -0
  46. umap/static/umap/js/umap.icon.js +234 -0
  47. umap/static/umap/js/umap.importer.js +166 -0
  48. umap/static/umap/js/umap.js +2010 -0
  49. umap/static/umap/js/umap.layer.js +1636 -0
  50. umap/static/umap/js/umap.permissions.js +212 -0
  51. umap/static/umap/js/umap.popup.js +340 -0
  52. umap/static/umap/js/umap.share.js +254 -0
  53. umap/static/umap/js/umap.slideshow.js +165 -0
  54. umap/static/umap/js/umap.tableeditor.js +120 -0
  55. umap/static/umap/js/umap.ui.js +240 -0
  56. umap/static/umap/js/umap.xhr.js +304 -0
  57. umap/static/umap/locale/am_ET.js +447 -0
  58. umap/static/umap/locale/am_ET.json +445 -0
  59. umap/static/umap/locale/ar.js +447 -0
  60. umap/static/umap/locale/ar.json +445 -0
  61. umap/static/umap/locale/ast.js +447 -0
  62. umap/static/umap/locale/ast.json +445 -0
  63. umap/static/umap/locale/bg.js +447 -0
  64. umap/static/umap/locale/bg.json +445 -0
  65. umap/static/umap/locale/br.js +447 -0
  66. umap/static/umap/locale/br.json +445 -0
  67. umap/static/umap/locale/ca.js +447 -0
  68. umap/static/umap/locale/ca.json +445 -0
  69. umap/static/umap/locale/cs_CZ.js +447 -0
  70. umap/static/umap/locale/cs_CZ.json +445 -0
  71. umap/static/umap/locale/da.js +447 -0
  72. umap/static/umap/locale/da.json +445 -0
  73. umap/static/umap/locale/de.js +447 -0
  74. umap/static/umap/locale/de.json +445 -0
  75. umap/static/umap/locale/el.js +447 -0
  76. umap/static/umap/locale/el.json +445 -0
  77. umap/static/umap/locale/en.js +447 -0
  78. umap/static/umap/locale/en.json +445 -0
  79. umap/static/umap/locale/en_US.json +445 -0
  80. umap/static/umap/locale/es.js +447 -0
  81. umap/static/umap/locale/es.json +445 -0
  82. umap/static/umap/locale/et.js +447 -0
  83. umap/static/umap/locale/et.json +445 -0
  84. umap/static/umap/locale/eu.js +413 -0
  85. umap/static/umap/locale/eu.json +411 -0
  86. umap/static/umap/locale/fa_IR.js +447 -0
  87. umap/static/umap/locale/fa_IR.json +445 -0
  88. umap/static/umap/locale/fi.js +447 -0
  89. umap/static/umap/locale/fi.json +445 -0
  90. umap/static/umap/locale/fr.js +447 -0
  91. umap/static/umap/locale/fr.json +445 -0
  92. umap/static/umap/locale/gl.js +447 -0
  93. umap/static/umap/locale/gl.json +445 -0
  94. umap/static/umap/locale/he.js +447 -0
  95. umap/static/umap/locale/he.json +445 -0
  96. umap/static/umap/locale/hr.js +447 -0
  97. umap/static/umap/locale/hr.json +445 -0
  98. umap/static/umap/locale/hu.js +447 -0
  99. umap/static/umap/locale/hu.json +445 -0
  100. umap/static/umap/locale/id.js +447 -0
  101. umap/static/umap/locale/id.json +445 -0
  102. umap/static/umap/locale/is.js +447 -0
  103. umap/static/umap/locale/is.json +445 -0
  104. umap/static/umap/locale/it.js +447 -0
  105. umap/static/umap/locale/it.json +445 -0
  106. umap/static/umap/locale/ja.js +447 -0
  107. umap/static/umap/locale/ja.json +445 -0
  108. umap/static/umap/locale/ko.js +447 -0
  109. umap/static/umap/locale/ko.json +445 -0
  110. umap/static/umap/locale/lt.js +447 -0
  111. umap/static/umap/locale/lt.json +445 -0
  112. umap/static/umap/locale/ms.js +447 -0
  113. umap/static/umap/locale/ms.json +445 -0
  114. umap/static/umap/locale/nl.js +447 -0
  115. umap/static/umap/locale/nl.json +445 -0
  116. umap/static/umap/locale/no.js +447 -0
  117. umap/static/umap/locale/no.json +445 -0
  118. umap/static/umap/locale/pl.js +447 -0
  119. umap/static/umap/locale/pl.json +445 -0
  120. umap/static/umap/locale/pl_PL.json +445 -0
  121. umap/static/umap/locale/pt.js +447 -0
  122. umap/static/umap/locale/pt.json +445 -0
  123. umap/static/umap/locale/pt_BR.js +447 -0
  124. umap/static/umap/locale/pt_BR.json +445 -0
  125. umap/static/umap/locale/pt_PT.js +447 -0
  126. umap/static/umap/locale/pt_PT.json +445 -0
  127. umap/static/umap/locale/ro.js +447 -0
  128. umap/static/umap/locale/ro.json +445 -0
  129. umap/static/umap/locale/ru.js +447 -0
  130. umap/static/umap/locale/ru.json +445 -0
  131. umap/static/umap/locale/si.js +439 -0
  132. umap/static/umap/locale/si.json +437 -0
  133. umap/static/umap/locale/sk_SK.js +447 -0
  134. umap/static/umap/locale/sk_SK.json +445 -0
  135. umap/static/umap/locale/sl.js +447 -0
  136. umap/static/umap/locale/sl.json +445 -0
  137. umap/static/umap/locale/sr.js +447 -0
  138. umap/static/umap/locale/sr.json +445 -0
  139. umap/static/umap/locale/sv.js +447 -0
  140. umap/static/umap/locale/sv.json +445 -0
  141. umap/static/umap/locale/th_TH.js +447 -0
  142. umap/static/umap/locale/th_TH.json +445 -0
  143. umap/static/umap/locale/tr.js +447 -0
  144. umap/static/umap/locale/tr.json +445 -0
  145. umap/static/umap/locale/uk_UA.js +447 -0
  146. umap/static/umap/locale/uk_UA.json +445 -0
  147. umap/static/umap/locale/vi.js +447 -0
  148. umap/static/umap/locale/vi.json +445 -0
  149. umap/static/umap/locale/vi_VN.json +445 -0
  150. umap/static/umap/locale/zh.js +447 -0
  151. umap/static/umap/locale/zh.json +445 -0
  152. umap/static/umap/locale/zh_CN.json +445 -0
  153. umap/static/umap/locale/zh_TW.Big5.json +445 -0
  154. umap/static/umap/locale/zh_TW.js +447 -0
  155. umap/static/umap/locale/zh_TW.json +445 -0
  156. umap/static/umap/map.css +1843 -0
  157. umap/static/umap/nav.css +81 -0
  158. umap/static/umap/openstreetmap.png +0 -0
  159. umap/static/umap/test/.eslintrc +22 -0
  160. umap/static/umap/test/Choropleth.js +243 -0
  161. umap/static/umap/test/Controls.js +100 -0
  162. umap/static/umap/test/DataLayer.js +495 -0
  163. umap/static/umap/test/Feature.js +382 -0
  164. umap/static/umap/test/Map.Export.js +106 -0
  165. umap/static/umap/test/Map.Init.js +46 -0
  166. umap/static/umap/test/Map.js +342 -0
  167. umap/static/umap/test/Marker.js +122 -0
  168. umap/static/umap/test/Permissions.js +74 -0
  169. umap/static/umap/test/Polygon.js +367 -0
  170. umap/static/umap/test/Polyline.js +402 -0
  171. umap/static/umap/test/TableEditor.js +100 -0
  172. umap/static/umap/test/URLs.js +54 -0
  173. umap/static/umap/test/Util.js +549 -0
  174. umap/static/umap/test/_pre.js +460 -0
  175. umap/static/umap/test/index.html +135 -0
  176. umap/static/umap/theme.css +1 -0
  177. umap/static/umap/twitter.png +0 -0
  178. {umap_project-1.14.0a1.dist-info → umap_project-1.14.0a2.dist-info}/METADATA +1 -1
  179. {umap_project-1.14.0a1.dist-info → umap_project-1.14.0a2.dist-info}/RECORD +182 -6
  180. {umap_project-1.14.0a1.dist-info → umap_project-1.14.0a2.dist-info}/WHEEL +0 -0
  181. {umap_project-1.14.0a1.dist-info → umap_project-1.14.0a2.dist-info}/entry_points.txt +0 -0
  182. {umap_project-1.14.0a1.dist-info → umap_project-1.14.0a2.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,2010 @@
1
+ L.Map.mergeOptions({
2
+ overlay: null,
3
+ datalayers: [],
4
+ hash: true,
5
+ default_color: 'DarkBlue',
6
+ default_smoothFactor: 1.0,
7
+ default_opacity: 0.5,
8
+ default_fillOpacity: 0.3,
9
+ default_stroke: true,
10
+ default_fill: true,
11
+ default_weight: 3,
12
+ default_iconOpacity: 1,
13
+ default_iconClass: 'Default',
14
+ default_popupContentTemplate: '# {name}\n{description}',
15
+ default_interactive: true,
16
+ default_labelDirection: 'auto',
17
+ maxZoomLimit: 24,
18
+ attributionControl: false,
19
+ editMode: 'advanced',
20
+ embedControl: true,
21
+ zoomControl: true,
22
+ datalayersControl: true,
23
+ searchControl: true,
24
+ editInOSMControl: false,
25
+ editInOSMControlOptions: false,
26
+ scaleControl: true,
27
+ noControl: false, // Do not render any control.
28
+ miniMap: false,
29
+ name: '',
30
+ description: '',
31
+ displayPopupFooter: false,
32
+ // When a TileLayer is in TMS mode, it needs -y instead of y.
33
+ // This is usually handled by the TileLayer instance itself, but
34
+ // we cannot rely on this because of the y is overriden by Leaflet
35
+ // See https://github.com/Leaflet/Leaflet/pull/9201
36
+ // And let's remove this -y when this PR is merged and released.
37
+ demoTileInfos: { s: 'a', z: 9, x: 265, y: 181, '-y': 181, r: '' },
38
+ licences: [],
39
+ licence: '',
40
+ enableMarkerDraw: true,
41
+ enablePolygonDraw: true,
42
+ enablePolylineDraw: true,
43
+ limitBounds: {},
44
+ importPresets: [
45
+ // {url: 'http://localhost:8019/en/datalayer/1502/', label: 'Simplified World Countries', format: 'geojson'}
46
+ ],
47
+ moreControl: true,
48
+ captionBar: false,
49
+ captionMenus: true,
50
+ slideshow: {},
51
+ clickable: true,
52
+ easing: false,
53
+ permissions: {},
54
+ permanentCreditBackground: true,
55
+ featuresHaveOwner: false,
56
+ })
57
+
58
+ L.U.Map.include({
59
+ HIDDABLE_CONTROLS: [
60
+ 'zoom',
61
+ 'search',
62
+ 'fullscreen',
63
+ 'embed',
64
+ 'locate',
65
+ 'measure',
66
+ 'editinosm',
67
+ 'datalayers',
68
+ 'star',
69
+ 'tilelayers',
70
+ ],
71
+
72
+ initialize: function (el, geojson) {
73
+ // Locale name (pt_PT, en_US…)
74
+ // To be used for Django localization
75
+ if (geojson.properties.locale) L.setLocale(geojson.properties.locale)
76
+
77
+ // Language code (pt-pt, en-us…)
78
+ // To be used in javascript APIs
79
+ if (geojson.properties.lang) L.lang = geojson.properties.lang
80
+
81
+ // Don't let default autocreation of controls
82
+ const zoomControl =
83
+ typeof geojson.properties.zoomControl !== 'undefined'
84
+ ? geojson.properties.zoomControl
85
+ : true
86
+ geojson.properties.zoomControl = false
87
+ const fullscreenControl =
88
+ typeof geojson.properties.fullscreenControl !== 'undefined'
89
+ ? geojson.properties.fullscreenControl
90
+ : true
91
+ geojson.properties.fullscreenControl = false
92
+ L.Util.setBooleanFromQueryString(geojson.properties, 'scrollWheelZoom')
93
+
94
+ L.Map.prototype.initialize.call(this, el, geojson.properties)
95
+
96
+ // After calling parent initialize, as we are doing initCenter our-selves
97
+ if (geojson.geometry) this.options.center = this.latLng(geojson.geometry)
98
+ this.urls = new window.umap.URLs(this.options.urls)
99
+
100
+ this.ui = new L.U.UI(this._container)
101
+ this.xhr = new L.U.Xhr(this.ui)
102
+ this.xhr.on('dataloading', (e) => this.fire('dataloading', e))
103
+ this.xhr.on('dataload', (e) => this.fire('dataload', e))
104
+
105
+ this.initLoader()
106
+ this.name = this.options.name
107
+ this.description = this.options.description
108
+ this.demoTileInfos = this.options.demoTileInfos
109
+ this.options.zoomControl = zoomControl
110
+ this.options.fullscreenControl = fullscreenControl
111
+ L.Util.setBooleanFromQueryString(this.options, 'moreControl')
112
+ L.Util.setBooleanFromQueryString(this.options, 'scaleControl')
113
+ L.Util.setBooleanFromQueryString(this.options, 'miniMap')
114
+ L.Util.setFromQueryString(this.options, 'editMode')
115
+ L.Util.setBooleanFromQueryString(this.options, 'displayDataBrowserOnLoad')
116
+ L.Util.setBooleanFromQueryString(this.options, 'displayCaptionOnLoad')
117
+ L.Util.setBooleanFromQueryString(this.options, 'captionBar')
118
+ L.Util.setBooleanFromQueryString(this.options, 'captionMenus')
119
+ for (let i = 0; i < this.HIDDABLE_CONTROLS.length; i++) {
120
+ L.Util.setNullableBooleanFromQueryString(
121
+ this.options,
122
+ `${this.HIDDABLE_CONTROLS[i]}Control`
123
+ )
124
+ }
125
+ this.datalayersOnLoad = L.Util.queryString('datalayers')
126
+ this.options.onLoadPanel = L.Util.queryString(
127
+ 'onLoadPanel',
128
+ this.options.onLoadPanel
129
+ )
130
+ if (this.datalayersOnLoad)
131
+ this.datalayersOnLoad = this.datalayersOnLoad.toString().split(',')
132
+
133
+ if (L.Browser.ielt9) this.options.editMode = 'disabled' // TODO include ie9
134
+
135
+ let editedFeature = null
136
+ const self = this
137
+ try {
138
+ Object.defineProperty(this, 'editedFeature', {
139
+ get: function () {
140
+ return editedFeature
141
+ },
142
+ set: function (feature) {
143
+ if (editedFeature && editedFeature !== feature) {
144
+ editedFeature.endEdit()
145
+ }
146
+ editedFeature = feature
147
+ self.fire('seteditedfeature')
148
+ },
149
+ })
150
+ } catch (e) {
151
+ // Certainly IE8, which has a limited version of defineProperty
152
+ }
153
+
154
+ // Retrocompat
155
+ if (
156
+ this.options.slideshow &&
157
+ this.options.slideshow.delay &&
158
+ this.options.slideshow.active === undefined
159
+ )
160
+ this.options.slideshow.active = true
161
+ if (this.options.advancedFilterKey)
162
+ this.options.facetKey = this.options.advancedFilterKey
163
+
164
+ // Global storage for retrieving datalayers and features
165
+ this.datalayers = {}
166
+ this.datalayers_index = []
167
+ this.dirty_datalayers = []
168
+ this.features_index = {}
169
+ this.facets = {}
170
+
171
+ // Needed for actions labels
172
+ this.help = new L.U.Help(this)
173
+
174
+ if (this.options.hash) this.addHash()
175
+ this.initTileLayers()
176
+ // Needs tilelayer to exist for minimap
177
+ this.initControls()
178
+ // Needs locate control and hash to exist
179
+ this.initCenter()
180
+ this.handleLimitBounds()
181
+ this.initDatalayers()
182
+
183
+ if (this.options.displayCaptionOnLoad) {
184
+ // Retrocompat
185
+ if (!this.options.onLoadPanel) {
186
+ this.options.onLoadPanel = 'caption'
187
+ }
188
+ delete this.options.displayCaptionOnLoad
189
+ }
190
+ if (this.options.displayDataBrowserOnLoad) {
191
+ // Retrocompat
192
+ if (!this.options.onLoadPanel) {
193
+ this.options.onLoadPanel = 'databrowser'
194
+ }
195
+ delete this.options.displayDataBrowserOnLoad
196
+ }
197
+
198
+ this.ui.on(
199
+ 'panel:closed',
200
+ function () {
201
+ this.invalidateSize({ pan: false })
202
+ },
203
+ this
204
+ )
205
+
206
+ let isDirty = false // self status
207
+ try {
208
+ Object.defineProperty(this, 'isDirty', {
209
+ get: function () {
210
+ return isDirty
211
+ },
212
+ set: function (status) {
213
+ isDirty = status
214
+ this.checkDirty()
215
+ },
216
+ })
217
+ } catch (e) {
218
+ // Certainly IE8, which has a limited version of defineProperty
219
+ }
220
+ this.on(
221
+ 'baselayerchange',
222
+ function (e) {
223
+ if (this._controls.miniMap) this._controls.miniMap.onMainMapBaseLayerChange(e)
224
+ },
225
+ this
226
+ )
227
+
228
+ // Creation mode
229
+ if (!this.options.umap_id) {
230
+ this.isDirty = true
231
+ this._default_extent = true
232
+ this.options.name = L._('Untitled map')
233
+ this.options.editMode = 'advanced'
234
+ const datalayer = this.createDataLayer()
235
+ datalayer.connectToMap()
236
+ this.enableEdit()
237
+ let dataUrl = L.Util.queryString('dataUrl', null)
238
+ const dataFormat = L.Util.queryString('dataFormat', 'geojson')
239
+ if (dataUrl) {
240
+ dataUrl = decodeURIComponent(dataUrl)
241
+ dataUrl = this.localizeUrl(dataUrl)
242
+ dataUrl = this.proxyUrl(dataUrl)
243
+ datalayer.importFromUrl(dataUrl, dataFormat)
244
+ }
245
+ }
246
+
247
+ this.slideshow = new L.U.Slideshow(this, this.options.slideshow)
248
+ this.permissions = new L.U.MapPermissions(this)
249
+ this.initCaptionBar()
250
+ if (this.hasEditMode()) {
251
+ this.editTools = new L.U.Editable(this)
252
+ this.ui.on(
253
+ 'panel:closed panel:open',
254
+ function () {
255
+ this.editedFeature = null
256
+ },
257
+ this
258
+ )
259
+ this.renderEditToolbar()
260
+ }
261
+ this.initShortcuts()
262
+ this.onceDataLoaded(function () {
263
+ if (L.Util.queryString('share')) this.share.open()
264
+ else if (this.options.onLoadPanel === 'databrowser') this.openBrowser()
265
+ else if (this.options.onLoadPanel === 'caption') this.displayCaption()
266
+ else if (
267
+ this.options.onLoadPanel === 'facet' ||
268
+ this.options.onLoadPanel === 'datafilters'
269
+ )
270
+ this.openFacet()
271
+ })
272
+ this.onceDataLoaded(function () {
273
+ const slug = L.Util.queryString('feature')
274
+ if (slug && this.features_index[slug]) this.features_index[slug].view()
275
+ if (L.Util.queryString('edit')) {
276
+ if (this.hasEditMode()) this.enableEdit()
277
+ // Sometimes users share the ?edit link by mistake, let's remove
278
+ // this search parameter from URL to prevent this
279
+ const url = new URL(window.location)
280
+ url.searchParams.delete('edit')
281
+ history.pushState({}, '', url)
282
+ }
283
+ if (L.Util.queryString('download')) {
284
+ const download_url = this.urls.get('map_download', {
285
+ map_id: this.options.umap_id,
286
+ })
287
+ window.location = download_url
288
+ }
289
+ })
290
+
291
+ window.onbeforeunload = () => this.isDirty || null
292
+ this.backup()
293
+ this.initContextMenu()
294
+ this.on('click contextmenu.show', this.closeInplaceToolbar)
295
+ },
296
+
297
+ initControls: function () {
298
+ this.helpMenuActions = {}
299
+ this._controls = {}
300
+
301
+ if (this.hasEditMode() && !this.options.noControl) {
302
+ new L.U.EditControl(this).addTo(this)
303
+
304
+ new L.U.DrawToolbar({ map: this }).addTo(this)
305
+
306
+ const editActions = [
307
+ L.U.ImportAction,
308
+ L.U.EditPropertiesAction,
309
+ L.U.ManageDatalayersAction,
310
+ L.U.ChangeTileLayerAction,
311
+ L.U.UpdateExtentAction,
312
+ L.U.UpdatePermsAction,
313
+ ]
314
+ new L.U.SettingsToolbar({ actions: editActions }).addTo(this)
315
+ }
316
+ this._controls.zoom = new L.Control.Zoom({
317
+ zoomInTitle: L._('Zoom in'),
318
+ zoomOutTitle: L._('Zoom out'),
319
+ })
320
+ this._controls.datalayers = new L.U.DataLayersControl(this)
321
+ this._controls.locate = L.control.locate({
322
+ strings: {
323
+ title: L._('Center map on your location'),
324
+ },
325
+ showPopup: false,
326
+ // We style this control in our own CSS for consistency with other controls,
327
+ // but the control breaks if we don't specify a class here, so a fake class
328
+ // will do.
329
+ icon: 'umap-fake-class',
330
+ iconLoading: 'umap-fake-class',
331
+ flyTo: this.options.easing,
332
+ onLocationError: (err) => this.ui.alert({ content: err.message }),
333
+ })
334
+ this._controls.fullscreen = new L.Control.Fullscreen({
335
+ title: { false: L._('View Fullscreen'), true: L._('Exit Fullscreen') },
336
+ })
337
+ this._controls.search = new L.U.SearchControl()
338
+ this._controls.embed = new L.Control.Embed(this, this.options.embedOptions)
339
+ this._controls.tilelayersChooser = new L.U.TileLayerChooser(this)
340
+ this._controls.star = new L.U.StarControl(this)
341
+ this._controls.editinosm = new L.Control.EditInOSM({
342
+ position: 'topleft',
343
+ widgetOptions: {
344
+ helpText: L._(
345
+ 'Open this map extent in a map editor to provide more accurate data to OpenStreetMap'
346
+ ),
347
+ },
348
+ })
349
+ this._controls.measure = new L.MeasureControl().initHandler(this)
350
+ this._controls.more = new L.U.MoreControls()
351
+ this._controls.scale = L.control.scale()
352
+ this._controls.permanentCredit = new L.U.PermanentCreditsControl(this)
353
+ if (this.options.scrollWheelZoom) this.scrollWheelZoom.enable()
354
+ else this.scrollWheelZoom.disable()
355
+ this.browser = new L.U.Browser(this)
356
+ this.importer = new L.U.Importer(this)
357
+ this.drop = new L.U.DropControl(this)
358
+ this.share = new L.U.Share(this)
359
+ this._controls.tilelayers = new L.U.TileLayerControl(this)
360
+ this._controls.tilelayers.setLayers()
361
+
362
+ this.renderControls()
363
+ },
364
+
365
+ renderControls: function () {
366
+ L.DomUtil.classIf(
367
+ document.body,
368
+ 'umap-caption-bar-enabled',
369
+ this.options.captionBar ||
370
+ (this.options.slideshow && this.options.slideshow.active)
371
+ )
372
+ L.DomUtil.classIf(
373
+ document.body,
374
+ 'umap-slideshow-enabled',
375
+ this.options.slideshow && this.options.slideshow.active
376
+ )
377
+ for (const i in this._controls) {
378
+ this.removeControl(this._controls[i])
379
+ }
380
+ if (this.options.noControl) return
381
+
382
+ this._controls.attribution = new L.U.AttributionControl().addTo(this)
383
+ if (this.options.miniMap && !this.options.noControl) {
384
+ this.whenReady(function () {
385
+ if (this.selected_tilelayer) {
386
+ this._controls.miniMap = new L.Control.MiniMap(this.selected_tilelayer, {
387
+ aimingRectOptions: {
388
+ color: this.getOption('color'),
389
+ fillColor: this.getOption('fillColor'),
390
+ stroke: this.getOption('stroke'),
391
+ fill: this.getOption('fill'),
392
+ weight: this.getOption('weight'),
393
+ opacity: this.getOption('opacity'),
394
+ fillOpacity: this.getOption('fillOpacity'),
395
+ },
396
+ }).addTo(this)
397
+ this._controls.miniMap._miniMap.invalidateSize()
398
+ }
399
+ })
400
+ }
401
+ let name, status, control
402
+ for (let i = 0; i < this.HIDDABLE_CONTROLS.length; i++) {
403
+ name = this.HIDDABLE_CONTROLS[i]
404
+ status = this.options[`${name}Control`]
405
+ if (status === false) continue
406
+ control = this._controls[name]
407
+ control.addTo(this)
408
+ if (status === undefined || status === null)
409
+ L.DomUtil.addClass(control._container, 'display-on-more')
410
+ else L.DomUtil.removeClass(control._container, 'display-on-more')
411
+ }
412
+ if (this.options.permanentCredit) this._controls.permanentCredit.addTo(this)
413
+ if (this.options.moreControl) this._controls.more.addTo(this)
414
+ if (this.options.scaleControl) this._controls.scale.addTo(this)
415
+ },
416
+
417
+ initDatalayers: function () {
418
+ for (let j = 0; j < this.options.datalayers.length; j++) {
419
+ this.createDataLayer(this.options.datalayers[j])
420
+ }
421
+ this.loadDatalayers()
422
+ },
423
+
424
+ loadDatalayers: function (force) {
425
+ const total = this.datalayers_index.length
426
+ // toload => datalayer metadata remaining to load (synchronous)
427
+ // dataToload => datalayer data remaining to load (asynchronous)
428
+ let toload = total,
429
+ dataToload = total
430
+ let datalayer
431
+ const loaded = () => {
432
+ this.datalayersLoaded = true
433
+ this.fire('datalayersloaded')
434
+ }
435
+ const decrementToLoad = () => {
436
+ toload--
437
+ if (toload === 0) loaded()
438
+ }
439
+ const dataLoaded = () => {
440
+ this.dataLoaded = true
441
+ this.fire('dataloaded')
442
+ }
443
+ const decrementDataToLoad = () => {
444
+ dataToload--
445
+ if (dataToload === 0) dataLoaded()
446
+ }
447
+ this.eachDataLayer(function (datalayer) {
448
+ if (force && !datalayer.hasDataLoaded()) {
449
+ datalayer.show()
450
+ }
451
+ if (datalayer.showAtLoad() || force) {
452
+ datalayer.onceLoaded(decrementToLoad)
453
+ } else {
454
+ decrementToLoad()
455
+ }
456
+ if (datalayer.showAtLoad() || force) {
457
+ datalayer.onceDataLoaded(decrementDataToLoad)
458
+ } else {
459
+ decrementDataToLoad({ sourceTarget: datalayer })
460
+ }
461
+ })
462
+ if (total === 0) {
463
+ // no datalayer
464
+ loaded()
465
+ dataLoaded()
466
+ }
467
+ },
468
+
469
+ indexDatalayers: function () {
470
+ const panes = this.getPane('overlayPane')
471
+ let pane
472
+ this.datalayers_index = []
473
+ for (let i = 0; i < panes.children.length; i++) {
474
+ pane = panes.children[i]
475
+ if (!pane.dataset || !pane.dataset.id) continue
476
+ this.datalayers_index.push(this.datalayers[pane.dataset.id])
477
+ }
478
+ this.updateDatalayersControl()
479
+ },
480
+
481
+ ensurePanesOrder: function () {
482
+ this.eachDataLayer((datalayer) => {
483
+ datalayer.bringToTop()
484
+ })
485
+ },
486
+
487
+ onceDatalayersLoaded: function (callback, context) {
488
+ // Once datalayers **metadata** have been loaded
489
+ if (this.datalayersLoaded) {
490
+ callback.call(context || this, this)
491
+ } else {
492
+ this.once('datalayersloaded', callback, context)
493
+ }
494
+ return this
495
+ },
496
+
497
+ onceDataLoaded: function (callback, context) {
498
+ // Once datalayers **data** have been loaded
499
+ if (this.dataLoaded) {
500
+ callback.call(context || this, this)
501
+ } else {
502
+ this.once('dataloaded', callback, context)
503
+ }
504
+ return this
505
+ },
506
+
507
+ updateDatalayersControl: function () {
508
+ if (this._controls.datalayers) this._controls.datalayers.update()
509
+ },
510
+
511
+ backupOptions: function () {
512
+ this._backupOptions = L.extend({}, this.options)
513
+ this._backupOptions.tilelayer = L.extend({}, this.options.tilelayer)
514
+ this._backupOptions.limitBounds = L.extend({}, this.options.limitBounds)
515
+ this._backupOptions.permissions = L.extend({}, this.permissions.options)
516
+ },
517
+
518
+ resetOptions: function () {
519
+ this.options = L.extend({}, this._backupOptions)
520
+ this.options.tilelayer = L.extend({}, this._backupOptions.tilelayer)
521
+ this.permissions.options = L.extend({}, this._backupOptions.permissions)
522
+ },
523
+
524
+ initShortcuts: function () {
525
+ const globalShortcuts = function (e) {
526
+ const key = e.keyCode,
527
+ modifierKey = e.ctrlKey || e.metaKey
528
+
529
+ /* Generic shortcuts */
530
+ if (key === L.U.Keys.F && modifierKey) {
531
+ L.DomEvent.stop(e)
532
+ this.search()
533
+ } else if (e.keyCode === L.U.Keys.ESC) {
534
+ if (this.help.visible()) this.help.hide()
535
+ else this.ui.closePanel()
536
+ }
537
+
538
+ if (!this.hasEditMode()) return
539
+
540
+ /* Edit mode only shortcuts */
541
+ if (key === L.U.Keys.E && modifierKey && !this.editEnabled) {
542
+ L.DomEvent.stop(e)
543
+ this.enableEdit()
544
+ } else if (
545
+ key === L.U.Keys.E &&
546
+ modifierKey &&
547
+ this.editEnabled &&
548
+ !this.isDirty
549
+ ) {
550
+ L.DomEvent.stop(e)
551
+ this.disableEdit()
552
+ this.ui.closePanel()
553
+ }
554
+ if (key === L.U.Keys.S && modifierKey) {
555
+ L.DomEvent.stop(e)
556
+ if (this.isDirty) {
557
+ this.save()
558
+ }
559
+ }
560
+ if (key === L.U.Keys.Z && modifierKey && this.isDirty) {
561
+ L.DomEvent.stop(e)
562
+ this.askForReset()
563
+ }
564
+ if (key === L.U.Keys.M && modifierKey && this.editEnabled) {
565
+ L.DomEvent.stop(e)
566
+ this.editTools.startMarker()
567
+ }
568
+ if (key === L.U.Keys.P && modifierKey && this.editEnabled) {
569
+ L.DomEvent.stop(e)
570
+ this.editTools.startPolygon()
571
+ }
572
+ if (key === L.U.Keys.L && modifierKey && this.editEnabled) {
573
+ L.DomEvent.stop(e)
574
+ this.editTools.startPolyline()
575
+ }
576
+ if (key === L.U.Keys.I && modifierKey && this.editEnabled) {
577
+ L.DomEvent.stop(e)
578
+ this.importer.open()
579
+ }
580
+ if (key === L.U.Keys.O && modifierKey && this.editEnabled) {
581
+ L.DomEvent.stop(e)
582
+ this.importer.openFiles()
583
+ }
584
+ if (key === L.U.Keys.H && modifierKey && this.editEnabled) {
585
+ L.DomEvent.stop(e)
586
+ this.help.show('edit')
587
+ }
588
+ if (e.keyCode === L.U.Keys.ESC) {
589
+ if (this.editEnabled && this.editTools.drawing()) {
590
+ this.editTools.stopDrawing()
591
+ }
592
+ if (this.measureTools.enabled()) this.measureTools.stopDrawing()
593
+ }
594
+ }
595
+ L.DomEvent.addListener(document, 'keydown', globalShortcuts, this)
596
+ },
597
+
598
+ initTileLayers: function () {
599
+ this.tilelayers = []
600
+ for (const props of this.options.tilelayers) {
601
+ let layer = this.createTileLayer(props)
602
+ this.tilelayers.push(layer)
603
+ if (
604
+ this.options.tilelayer &&
605
+ this.options.tilelayer.url_template === props.url_template
606
+ ) {
607
+ // Keep control over the displayed attribution for non custom tilelayers
608
+ this.options.tilelayer.attribution = props.attribution
609
+ }
610
+ }
611
+ if (
612
+ this.options.tilelayer &&
613
+ this.options.tilelayer.url_template &&
614
+ this.options.tilelayer.attribution
615
+ ) {
616
+ this.customTilelayer = this.createTileLayer(this.options.tilelayer)
617
+ this.selectTileLayer(this.customTilelayer)
618
+ } else {
619
+ this.selectTileLayer(this.tilelayers[0])
620
+ }
621
+ if (this._controls) this._controls.tilelayers.setLayers()
622
+ },
623
+
624
+ createTileLayer: function (tilelayer) {
625
+ return new L.TileLayer(tilelayer.url_template, tilelayer)
626
+ },
627
+
628
+ selectTileLayer: function (tilelayer) {
629
+ if (tilelayer === this.selected_tilelayer) {
630
+ return
631
+ }
632
+ try {
633
+ this.addLayer(tilelayer)
634
+ this.fire('baselayerchange', { layer: tilelayer })
635
+ if (this.selected_tilelayer) {
636
+ this.removeLayer(this.selected_tilelayer)
637
+ }
638
+ this.selected_tilelayer = tilelayer
639
+ if (
640
+ !isNaN(this.selected_tilelayer.options.minZoom) &&
641
+ this.getZoom() < this.selected_tilelayer.options.minZoom
642
+ ) {
643
+ this.setZoom(this.selected_tilelayer.options.minZoom)
644
+ }
645
+ if (
646
+ !isNaN(this.selected_tilelayer.options.maxZoom) &&
647
+ this.getZoom() > this.selected_tilelayer.options.maxZoom
648
+ ) {
649
+ this.setZoom(this.selected_tilelayer.options.maxZoom)
650
+ }
651
+ } catch (e) {
652
+ console.error(e)
653
+ this.removeLayer(tilelayer)
654
+ this.ui.alert({
655
+ content: `${L._('Error in the tilelayer URL')}: ${tilelayer._url}`,
656
+ level: 'error',
657
+ })
658
+ // Users can put tilelayer URLs by hand, and if they add wrong {variable},
659
+ // Leaflet throw an error, and then the map is no more editable
660
+ }
661
+ this.setOverlay()
662
+ },
663
+
664
+ eachTileLayer: function (callback, context) {
665
+ const urls = []
666
+ const callOne = (layer) => {
667
+ // Prevent adding a duplicate background,
668
+ // while adding selected/custom on top of the list
669
+ const url = layer.options.url_template
670
+ if (urls.indexOf(url) !== -1) return
671
+ callback.call(context, layer)
672
+ urls.push(url)
673
+ }
674
+ if (this.selected_tilelayer) callOne(this.selected_tilelayer)
675
+ if (this.customTilelayer) callOne(this.customTilelayer)
676
+ this.tilelayers.forEach(callOne)
677
+ },
678
+
679
+ setOverlay: function () {
680
+ if (!this.options.overlay || !this.options.overlay.url_template) return
681
+ const overlay = this.createTileLayer(this.options.overlay)
682
+ try {
683
+ this.addLayer(overlay)
684
+ if (this.overlay) this.removeLayer(this.overlay)
685
+ this.overlay = overlay
686
+ } catch (e) {
687
+ this.removeLayer(overlay)
688
+ console.error(e)
689
+ this.ui.alert({
690
+ content: `${L._('Error in the overlay URL')}: ${overlay._url}`,
691
+ level: 'error',
692
+ })
693
+ }
694
+ },
695
+
696
+ _setDefaultCenter: function () {
697
+ this.options.center = this.latLng(this.options.center)
698
+ this.setView(this.options.center, this.options.zoom)
699
+ },
700
+
701
+ hasData: function () {
702
+ for (const datalayer of this.datalayers_index) {
703
+ if (datalayer.hasData()) return true
704
+ }
705
+ },
706
+
707
+ fitDataBounds: function () {
708
+ const bounds = this.getLayersBounds()
709
+ if (!this.hasData() || !bounds.isValid()) return false
710
+ this.fitBounds(bounds)
711
+ },
712
+
713
+ initCenter: function () {
714
+ if (this.options.hash && this._hash.parseHash(location.hash)) {
715
+ // FIXME An invalid hash will cause the load to fail
716
+ this._hash.update()
717
+ } else if (this.options.defaultView === 'locate' && !this.options.noControl) {
718
+ // When using locate as default map view AND activating easing
719
+ // Leaflet.locate will ask the map view to compute transition to user
720
+ // position, so in this case we do need a default center, so let's
721
+ // set it anyway
722
+ this._setDefaultCenter()
723
+ this._controls.locate.start()
724
+ } else if (this.options.defaultView === 'data') {
725
+ this.onceDataLoaded(() => {
726
+ if (!this.fitDataBounds()) return this._setDefaultCenter()
727
+ })
728
+ } else if (this.options.defaultView === 'latest') {
729
+ this.onceDataLoaded(() => {
730
+ if (!this.hasData()) {
731
+ this._setDefaultCenter()
732
+ return
733
+ }
734
+ const datalayer = this.firstVisibleDatalayer()
735
+ let feature
736
+ if (datalayer) {
737
+ const feature = datalayer.getFeatureByIndex(-1)
738
+ if (feature) {
739
+ feature.zoomTo()
740
+ return
741
+ }
742
+ }
743
+ // Fallback, no datalayer or no feature found
744
+ this._setDefaultCenter()
745
+ })
746
+ } else {
747
+ this._setDefaultCenter()
748
+ }
749
+ },
750
+
751
+ latLng: function (a, b, c) {
752
+ // manage geojson case and call original method
753
+ if (!(a instanceof L.LatLng) && a.coordinates) {
754
+ // Guess it's a geojson
755
+ a = [a.coordinates[1], a.coordinates[0]]
756
+ }
757
+ return L.latLng(a, b, c)
758
+ },
759
+
760
+ handleLimitBounds: function () {
761
+ const south = parseFloat(this.options.limitBounds.south),
762
+ west = parseFloat(this.options.limitBounds.west),
763
+ north = parseFloat(this.options.limitBounds.north),
764
+ east = parseFloat(this.options.limitBounds.east)
765
+ if (!isNaN(south) && !isNaN(west) && !isNaN(north) && !isNaN(east)) {
766
+ const bounds = L.latLngBounds([
767
+ [south, west],
768
+ [north, east],
769
+ ])
770
+ this.options.minZoom = this.getBoundsZoom(bounds, false)
771
+ try {
772
+ this.setMaxBounds(bounds)
773
+ } catch (e) {
774
+ // Unusable bounds, like -2 -2 -2 -2?
775
+ console.error('Error limiting bounds', e)
776
+ }
777
+ } else {
778
+ this.options.minZoom = 0
779
+ this.setMaxBounds()
780
+ }
781
+ },
782
+
783
+ setMaxBounds: function (bounds) {
784
+ // Hack. Remove me when fix is released:
785
+ // https://github.com/Leaflet/Leaflet/pull/4494
786
+ bounds = L.latLngBounds(bounds)
787
+
788
+ if (!bounds.isValid()) {
789
+ this.options.maxBounds = null
790
+ return this.off('moveend', this._panInsideMaxBounds)
791
+ }
792
+ return L.Map.prototype.setMaxBounds.call(this, bounds)
793
+ },
794
+
795
+ createDataLayer: function (datalayer) {
796
+ datalayer = datalayer || {
797
+ name: `${L._('Layer')} ${this.datalayers_index.length + 1}`,
798
+ }
799
+ return new L.U.DataLayer(this, datalayer)
800
+ },
801
+
802
+ getDefaultOption: function (option) {
803
+ return this.options[`default_${option}`]
804
+ },
805
+
806
+ getOption: function (option) {
807
+ if (L.Util.usableOption(this.options, option)) return this.options[option]
808
+ return this.getDefaultOption(option)
809
+ },
810
+
811
+ updateExtent: function () {
812
+ this.options.center = this.getCenter()
813
+ this.options.zoom = this.getZoom()
814
+ this.isDirty = true
815
+ this._default_extent = false
816
+ if (this.options.umap_id) {
817
+ // We do not want an extra message during the map creation
818
+ // to avoid the double notification/alert.
819
+ this.ui.alert({
820
+ content: L._('The zoom and center have been modified.'),
821
+ level: 'info',
822
+ })
823
+ }
824
+ },
825
+
826
+ updateTileLayers: function () {
827
+ const self = this,
828
+ callback = (tilelayer) => {
829
+ self.options.tilelayer = tilelayer.toJSON()
830
+ self.isDirty = true
831
+ }
832
+ if (this._controls.tilelayersChooser)
833
+ this._controls.tilelayersChooser.openSwitcher({ callback: callback, className: 'dark' })
834
+ },
835
+
836
+ manageDatalayers: function () {
837
+ if (this._controls.datalayers) this._controls.datalayers.openPanel()
838
+ },
839
+
840
+ toGeoJSON: function () {
841
+ let features = []
842
+ this.eachDataLayer((datalayer) => {
843
+ if (datalayer.isVisible()) {
844
+ features = features.concat(datalayer.featuresToGeoJSON())
845
+ }
846
+ })
847
+ const geojson = {
848
+ type: 'FeatureCollection',
849
+ features: features,
850
+ }
851
+ return geojson
852
+ },
853
+
854
+ eachFeature: function (callback, context) {
855
+ this.eachDataLayer((datalayer) => {
856
+ if (datalayer.isVisible()) datalayer.eachFeature(callback, context)
857
+ })
858
+ },
859
+
860
+ processFileToImport: function (file, layer, type) {
861
+ type = type || L.Util.detectFileType(file)
862
+ if (!type) {
863
+ this.ui.alert({
864
+ content: L._('Unable to detect format of file {filename}', {
865
+ filename: file.name,
866
+ }),
867
+ level: 'error',
868
+ })
869
+ return
870
+ }
871
+ if (type === 'umap') {
872
+ this.importFromFile(file, 'umap')
873
+ } else {
874
+ if (!layer) layer = this.createDataLayer({ name: file.name })
875
+ layer.importFromFile(file, type)
876
+ }
877
+ },
878
+
879
+ importRaw: function (rawData) {
880
+ const importedData = JSON.parse(rawData)
881
+
882
+ let mustReindex = false
883
+
884
+ for (let i = 0; i < this.editableOptions.length; i++) {
885
+ const option = this.editableOptions[i]
886
+ if (typeof importedData.properties[option] !== 'undefined') {
887
+ this.options[option] = importedData.properties[option]
888
+ if (option === 'sortKey') mustReindex = true
889
+ }
890
+ }
891
+
892
+ if (importedData.geometry) this.options.center = this.latLng(importedData.geometry)
893
+ const self = this
894
+ importedData.layers.forEach((geojson) => {
895
+ delete geojson._umap_options['id'] // Never trust an id at this stage
896
+ const dataLayer = self.createDataLayer(geojson._umap_options)
897
+ dataLayer.fromUmapGeoJSON(geojson)
898
+ })
899
+
900
+ this.initTileLayers()
901
+ this.renderControls()
902
+ this.handleLimitBounds()
903
+ this.eachDataLayer((datalayer) => {
904
+ if (mustReindex) datalayer.reindex()
905
+ datalayer.redraw()
906
+ })
907
+ this.fire('postsync')
908
+ this.isDirty = true
909
+ },
910
+
911
+ importFromFile: function (file) {
912
+ const reader = new FileReader()
913
+ reader.readAsText(file)
914
+ const self = this
915
+ reader.onload = (e) => {
916
+ const rawData = e.target.result
917
+ try {
918
+ self.importRaw(rawData)
919
+ } catch (e) {
920
+ console.error('Error importing data', e)
921
+ self.ui.alert({
922
+ content: L._('Invalid umap data in {filename}', { filename: file.name }),
923
+ level: 'error',
924
+ })
925
+ }
926
+ }
927
+ },
928
+
929
+ openBrowser: function () {
930
+ this.onceDatalayersLoaded(function () {
931
+ this.browser.open()
932
+ })
933
+ },
934
+
935
+ openFacet: function () {
936
+ this.onceDataLoaded(function () {
937
+ this._openFacet()
938
+ })
939
+ },
940
+
941
+ eachDataLayer: function (method, context) {
942
+ for (let i = 0; i < this.datalayers_index.length; i++) {
943
+ method.call(context, this.datalayers_index[i])
944
+ }
945
+ },
946
+
947
+ eachDataLayerReverse: function (method, context, filter) {
948
+ for (let i = this.datalayers_index.length - 1; i >= 0; i--) {
949
+ if (filter && !filter.call(context, this.datalayers_index[i])) continue
950
+ method.call(context, this.datalayers_index[i])
951
+ }
952
+ },
953
+
954
+ eachBrowsableDataLayer: function (method, context) {
955
+ this.eachDataLayerReverse(method, context, (d) => d.allowBrowse())
956
+ },
957
+
958
+ eachVisibleDataLayer: function (method, context) {
959
+ this.eachDataLayerReverse(method, context, (d) => d.isVisible())
960
+ },
961
+
962
+ findDataLayer: function (method, context) {
963
+ for (let i = this.datalayers_index.length - 1; i >= 0; i--) {
964
+ if (method.call(context, this.datalayers_index[i]))
965
+ return this.datalayers_index[i]
966
+ }
967
+ },
968
+
969
+ backup: function () {
970
+ this.backupOptions()
971
+ this._datalayers_index_bk = [].concat(this.datalayers_index)
972
+ },
973
+
974
+ reset: function () {
975
+ if (this.editTools) this.editTools.stopDrawing()
976
+ this.resetOptions()
977
+ this.datalayers_index = [].concat(this._datalayers_index_bk)
978
+ this.dirty_datalayers.slice().forEach((datalayer) => {
979
+ if (datalayer.isDeleted) datalayer.connectToMap()
980
+ datalayer.reset()
981
+ })
982
+ this.ensurePanesOrder()
983
+ this.dirty_datalayers = []
984
+ this.updateDatalayersControl()
985
+ this.initTileLayers()
986
+ this.isDirty = false
987
+ },
988
+
989
+ checkDirty: function () {
990
+ L.DomUtil.classIf(this._container, 'umap-is-dirty', this.isDirty)
991
+ },
992
+
993
+ addDirtyDatalayer: function (datalayer) {
994
+ if (this.dirty_datalayers.indexOf(datalayer) === -1) {
995
+ this.dirty_datalayers.push(datalayer)
996
+ this.isDirty = true
997
+ }
998
+ },
999
+
1000
+ removeDirtyDatalayer: function (datalayer) {
1001
+ if (this.dirty_datalayers.indexOf(datalayer) !== -1) {
1002
+ this.dirty_datalayers.splice(this.dirty_datalayers.indexOf(datalayer), 1)
1003
+ this.checkDirty()
1004
+ }
1005
+ },
1006
+
1007
+ continueSaving: function () {
1008
+ if (this.dirty_datalayers.length) this.dirty_datalayers[0].save()
1009
+ else this.fire('saved')
1010
+ },
1011
+
1012
+ editableOptions: [
1013
+ 'zoom',
1014
+ 'scrollWheelZoom',
1015
+ 'scaleControl',
1016
+ 'moreControl',
1017
+ 'miniMap',
1018
+ 'displayPopupFooter',
1019
+ 'onLoadPanel',
1020
+ 'defaultView',
1021
+ 'name',
1022
+ 'description',
1023
+ 'licence',
1024
+ 'tilelayer',
1025
+ 'overlay',
1026
+ 'limitBounds',
1027
+ 'color',
1028
+ 'iconClass',
1029
+ 'iconUrl',
1030
+ 'smoothFactor',
1031
+ 'iconOpacity',
1032
+ 'opacity',
1033
+ 'weight',
1034
+ 'fill',
1035
+ 'fillColor',
1036
+ 'fillOpacity',
1037
+ 'dashArray',
1038
+ 'popupShape',
1039
+ 'popupTemplate',
1040
+ 'popupContentTemplate',
1041
+ 'zoomTo',
1042
+ 'captionBar',
1043
+ 'captionMenus',
1044
+ 'slideshow',
1045
+ 'sortKey',
1046
+ 'labelKey',
1047
+ 'filterKey',
1048
+ 'facetKey',
1049
+ 'slugKey',
1050
+ 'showLabel',
1051
+ 'labelDirection',
1052
+ 'labelInteractive',
1053
+ 'outlinkTarget',
1054
+ 'shortCredit',
1055
+ 'longCredit',
1056
+ 'permanentCredit',
1057
+ 'permanentCreditBackground',
1058
+ 'zoomControl',
1059
+ 'datalayersControl',
1060
+ 'searchControl',
1061
+ 'locateControl',
1062
+ 'fullscreenControl',
1063
+ 'editinosmControl',
1064
+ 'embedControl',
1065
+ 'measureControl',
1066
+ 'tilelayersControl',
1067
+ 'starControl',
1068
+ 'easing',
1069
+ ],
1070
+
1071
+ exportOptions: function () {
1072
+ const properties = {}
1073
+ for (let i = this.editableOptions.length - 1; i >= 0; i--) {
1074
+ if (typeof this.options[this.editableOptions[i]] !== 'undefined') {
1075
+ properties[this.editableOptions[i]] = this.options[this.editableOptions[i]]
1076
+ }
1077
+ }
1078
+ return properties
1079
+ },
1080
+
1081
+ saveSelf: function () {
1082
+ const geojson = {
1083
+ type: 'Feature',
1084
+ geometry: this.geometry(),
1085
+ properties: this.exportOptions(),
1086
+ }
1087
+ const formData = new FormData()
1088
+ formData.append('name', this.options.name)
1089
+ formData.append('center', JSON.stringify(this.geometry()))
1090
+ formData.append('settings', JSON.stringify(geojson))
1091
+ this.post(this.urls.get('map_save', { map_id: this.options.umap_id }), {
1092
+ data: formData,
1093
+ context: this,
1094
+ callback: function (data) {
1095
+ let duration = 3000,
1096
+ alert = { content: L._('Map has been saved!'), level: 'info' }
1097
+ if (!this.options.umap_id) {
1098
+ alert.content = L._('Congratulations, your map has been created!')
1099
+ this.options.umap_id = data.id
1100
+ this.permissions.setOptions(data.permissions)
1101
+ this.permissions.commit()
1102
+ if (
1103
+ data.permissions &&
1104
+ data.permissions.anonymous_edit_url &&
1105
+ this.options.urls.map_send_edit_link
1106
+ ) {
1107
+ alert.duration = Infinity
1108
+ alert.content =
1109
+ L._(
1110
+ 'Your map has been created! As you are not logged in, here is your secret link to edit the map, please keep it safe:'
1111
+ ) + `<br>${data.permissions.anonymous_edit_url}`
1112
+
1113
+ alert.actions = [
1114
+ {
1115
+ label: L._('Send me the link'),
1116
+ input: L._('Email'),
1117
+ callback: this.sendEditLink,
1118
+ callbackContext: this,
1119
+ },
1120
+ {
1121
+ label: L._('Copy link'),
1122
+ callback: () => {
1123
+ L.Util.copyToClipboard(data.permissions.anonymous_edit_url)
1124
+ this.ui.alert({
1125
+ content: L._('Secret edit link copied to clipboard!'),
1126
+ level: 'info',
1127
+ })
1128
+ },
1129
+ callbackContext: this,
1130
+ },
1131
+ ]
1132
+ }
1133
+ } else if (!this.permissions.isDirty) {
1134
+ // Do not override local changes to permissions,
1135
+ // but update in case some other editors changed them in the meantime.
1136
+ this.permissions.setOptions(data.permissions)
1137
+ this.permissions.commit()
1138
+ }
1139
+ // Update URL in case the name has changed.
1140
+ if (history && history.pushState)
1141
+ history.pushState({}, this.options.name, data.url)
1142
+ else window.location = data.url
1143
+ alert.content = data.info || alert.content
1144
+ this.once('saved', () => this.ui.alert(alert))
1145
+ this.ui.closePanel()
1146
+ this.permissions.save()
1147
+ },
1148
+ })
1149
+ },
1150
+
1151
+ save: function () {
1152
+ if (!this.isDirty) return
1153
+ if (this._default_extent) this.updateExtent()
1154
+ this.backup()
1155
+ this.once('saved', () => {
1156
+ this.isDirty = false
1157
+ })
1158
+ if (this.options.editMode === 'advanced') {
1159
+ // Only save the map if the user has the rights to do so.
1160
+ this.saveSelf()
1161
+ } else {
1162
+ this.permissions.save()
1163
+ }
1164
+ },
1165
+
1166
+ sendEditLink: function () {
1167
+ const input = this.ui._alert.querySelector('input')
1168
+ const email = input.value
1169
+
1170
+ const formData = new FormData()
1171
+ formData.append('email', email)
1172
+
1173
+ const url = this.urls.get('map_send_edit_link', { map_id: this.options.umap_id })
1174
+ this.post(url, {
1175
+ data: formData,
1176
+ })
1177
+ },
1178
+
1179
+ star: function () {
1180
+ if (!this.options.umap_id)
1181
+ return this.ui.alert({
1182
+ content: L._('Please save the map first'),
1183
+ level: 'error',
1184
+ })
1185
+ const url = this.urls.get('map_star', { map_id: this.options.umap_id })
1186
+ this.post(url, {
1187
+ context: this,
1188
+ callback: function (data) {
1189
+ this.options.starred = data.starred
1190
+ let msg = data.starred
1191
+ ? L._('Map has been starred')
1192
+ : L._('Map has been unstarred')
1193
+ this.ui.alert({ content: msg, level: 'info' })
1194
+ this.renderControls()
1195
+ },
1196
+ })
1197
+ },
1198
+
1199
+ geometry: function () {
1200
+ /* Return a GeoJSON geometry Object */
1201
+ const latlng = this.latLng(this.options.center || this.getCenter())
1202
+ return {
1203
+ type: 'Point',
1204
+ coordinates: [latlng.lng, latlng.lat],
1205
+ }
1206
+ },
1207
+
1208
+ firstVisibleDatalayer: function () {
1209
+ return this.findDataLayer((datalayer) => {
1210
+ if (datalayer.isVisible()) return true
1211
+ })
1212
+ },
1213
+
1214
+ // TODO: allow to control the default datalayer
1215
+ // (edit and viewing)
1216
+ // cf https://github.com/umap-project/umap/issues/585
1217
+ defaultEditDataLayer: function () {
1218
+ let datalayer, fallback
1219
+ datalayer = this.lastUsedDataLayer
1220
+ if (
1221
+ datalayer &&
1222
+ !datalayer.isDataReadOnly() &&
1223
+ datalayer.canBrowse() &&
1224
+ datalayer.isVisible()
1225
+ ) {
1226
+ return datalayer
1227
+ }
1228
+ datalayer = this.findDataLayer((datalayer) => {
1229
+ if (!datalayer.isDataReadOnly() && datalayer.canBrowse()) {
1230
+ fallback = datalayer
1231
+ if (datalayer.isVisible()) return true
1232
+ }
1233
+ })
1234
+ if (datalayer) return datalayer
1235
+ if (fallback) {
1236
+ // No datalayer visible, let's force one
1237
+ this.addLayer(fallback.layer)
1238
+ return fallback
1239
+ }
1240
+ return this.createDataLayer()
1241
+ },
1242
+
1243
+ getDataLayerByUmapId: function (umap_id) {
1244
+ return this.findDataLayer((d) => d.umap_id == umap_id)
1245
+ },
1246
+
1247
+ _editControls: function (container) {
1248
+ let UIFields = []
1249
+ for (let i = 0; i < this.HIDDABLE_CONTROLS.length; i++) {
1250
+ UIFields.push(`options.${this.HIDDABLE_CONTROLS[i]}Control`)
1251
+ }
1252
+ UIFields = UIFields.concat([
1253
+ 'options.moreControl',
1254
+ 'options.scrollWheelZoom',
1255
+ 'options.miniMap',
1256
+ 'options.scaleControl',
1257
+ 'options.onLoadPanel',
1258
+ 'options.defaultView',
1259
+ 'options.displayPopupFooter',
1260
+ 'options.captionBar',
1261
+ 'options.captionMenus',
1262
+ ])
1263
+ builder = new L.U.FormBuilder(this, UIFields, {
1264
+ callback: function () {
1265
+ this.renderControls()
1266
+ this.initCaptionBar()
1267
+ },
1268
+ callbackContext: this,
1269
+ })
1270
+ const controlsOptions = L.DomUtil.createFieldset(
1271
+ container,
1272
+ L._('User interface options')
1273
+ )
1274
+ controlsOptions.appendChild(builder.build())
1275
+ },
1276
+
1277
+ _editShapeProperties: function (container) {
1278
+ const shapeOptions = [
1279
+ 'options.color',
1280
+ 'options.iconClass',
1281
+ 'options.iconUrl',
1282
+ 'options.iconOpacity',
1283
+ 'options.opacity',
1284
+ 'options.weight',
1285
+ 'options.fill',
1286
+ 'options.fillColor',
1287
+ 'options.fillOpacity',
1288
+ 'options.smoothFactor',
1289
+ 'options.dashArray',
1290
+ ]
1291
+
1292
+ builder = new L.U.FormBuilder(this, shapeOptions, {
1293
+ callback: function (e) {
1294
+ if (this._controls.miniMap) this.renderControls()
1295
+ this.eachVisibleDataLayer((datalayer) => {
1296
+ datalayer.redraw()
1297
+ })
1298
+ },
1299
+ })
1300
+ const defaultShapeProperties = L.DomUtil.createFieldset(
1301
+ container,
1302
+ L._('Default shape properties')
1303
+ )
1304
+ defaultShapeProperties.appendChild(builder.build())
1305
+ },
1306
+
1307
+ _editDefaultProperties: function (container) {
1308
+ const optionsFields = [
1309
+ 'options.zoomTo',
1310
+ ['options.easing', { handler: 'Switch', label: L._('Animated transitions') }],
1311
+ 'options.labelKey',
1312
+ [
1313
+ 'options.sortKey',
1314
+ {
1315
+ handler: 'BlurInput',
1316
+ helpEntries: 'sortKey',
1317
+ placeholder: L._('Default: name'),
1318
+ label: L._('Sort key'),
1319
+ inheritable: true,
1320
+ },
1321
+ ],
1322
+ [
1323
+ 'options.filterKey',
1324
+ {
1325
+ handler: 'Input',
1326
+ helpEntries: 'filterKey',
1327
+ placeholder: L._('Default: name'),
1328
+ label: L._('Filter keys'),
1329
+ inheritable: true,
1330
+ },
1331
+ ],
1332
+ [
1333
+ 'options.facetKey',
1334
+ {
1335
+ handler: 'Input',
1336
+ helpEntries: 'facetKey',
1337
+ placeholder: L._('Example: key1,key2,key3'),
1338
+ label: L._('Facet keys'),
1339
+ },
1340
+ ],
1341
+ [
1342
+ 'options.slugKey',
1343
+ {
1344
+ handler: 'BlurInput',
1345
+ helpEntries: 'slugKey',
1346
+ placeholder: L._('Default: name'),
1347
+ label: L._('Feature identifier key'),
1348
+ },
1349
+ ],
1350
+ ]
1351
+
1352
+ builder = new L.U.FormBuilder(this, optionsFields, {
1353
+ callback: function (e) {
1354
+ this.initCaptionBar()
1355
+ if (e.helper.field === 'options.sortKey') {
1356
+ this.eachDataLayer((datalayer) => datalayer.reindex())
1357
+ }
1358
+ },
1359
+ })
1360
+ const defaultProperties = L.DomUtil.createFieldset(
1361
+ container,
1362
+ L._('Default properties')
1363
+ )
1364
+ defaultProperties.appendChild(builder.build())
1365
+ },
1366
+
1367
+ _editInteractionsProperties: function (container) {
1368
+ const popupFields = [
1369
+ 'options.popupShape',
1370
+ 'options.popupTemplate',
1371
+ 'options.popupContentTemplate',
1372
+ 'options.showLabel',
1373
+ 'options.labelDirection',
1374
+ 'options.labelInteractive',
1375
+ 'options.outlinkTarget',
1376
+ ]
1377
+ builder = new L.U.FormBuilder(this, popupFields, {
1378
+ callback: function (e) {
1379
+ if (
1380
+ e.helper.field === 'options.popupTemplate' ||
1381
+ e.helper.field === 'options.popupContentTemplate' ||
1382
+ e.helper.field === 'options.popupShape' ||
1383
+ e.helper.field === 'options.outlinkTarget'
1384
+ )
1385
+ return
1386
+ this.eachVisibleDataLayer((datalayer) => {
1387
+ datalayer.redraw()
1388
+ })
1389
+ },
1390
+ })
1391
+ const popupFieldset = L.DomUtil.createFieldset(
1392
+ container,
1393
+ L._('Default interaction options')
1394
+ )
1395
+ popupFieldset.appendChild(builder.build())
1396
+ },
1397
+
1398
+ _editTilelayer: function (container) {
1399
+ if (!L.Util.isObject(this.options.tilelayer)) {
1400
+ this.options.tilelayer = {}
1401
+ }
1402
+ const tilelayerFields = [
1403
+ [
1404
+ 'options.tilelayer.name',
1405
+ { handler: 'BlurInput', placeholder: L._('display name') },
1406
+ ],
1407
+ [
1408
+ 'options.tilelayer.url_template',
1409
+ {
1410
+ handler: 'BlurInput',
1411
+ helpText: `${L._('Supported scheme')}: http://{s}.domain.com/{z}/{x}/{y}.png`,
1412
+ placeholder: 'url',
1413
+ type: 'url',
1414
+ },
1415
+ ],
1416
+ [
1417
+ 'options.tilelayer.maxZoom',
1418
+ {
1419
+ handler: 'BlurIntInput',
1420
+ placeholder: L._('max zoom'),
1421
+ min: 0,
1422
+ max: this.options.maxZoomLimit,
1423
+ },
1424
+ ],
1425
+ [
1426
+ 'options.tilelayer.minZoom',
1427
+ {
1428
+ handler: 'BlurIntInput',
1429
+ placeholder: L._('min zoom'),
1430
+ min: 0,
1431
+ max: this.options.maxZoomLimit,
1432
+ },
1433
+ ],
1434
+ [
1435
+ 'options.tilelayer.attribution',
1436
+ { handler: 'BlurInput', placeholder: L._('attribution') },
1437
+ ],
1438
+ ['options.tilelayer.tms', { handler: 'Switch', label: L._('TMS format') }],
1439
+ ]
1440
+ const customTilelayer = L.DomUtil.createFieldset(
1441
+ container,
1442
+ L._('Custom background')
1443
+ )
1444
+ builder = new L.U.FormBuilder(this, tilelayerFields, {
1445
+ callback: this.initTileLayers,
1446
+ callbackContext: this,
1447
+ })
1448
+ customTilelayer.appendChild(builder.build())
1449
+ },
1450
+
1451
+ _editOverlay: function (container) {
1452
+ if (!L.Util.isObject(this.options.overlay)) {
1453
+ this.options.overlay = {}
1454
+ }
1455
+ const overlayFields = [
1456
+ [
1457
+ 'options.overlay.url_template',
1458
+ {
1459
+ handler: 'BlurInput',
1460
+ helpText: `${L._('Supported scheme')}: http://{s}.domain.com/{z}/{x}/{y}.png`,
1461
+ placeholder: 'url',
1462
+ helpText: L._('Background overlay url'),
1463
+ type: 'url',
1464
+ },
1465
+ ],
1466
+ [
1467
+ 'options.overlay.maxZoom',
1468
+ {
1469
+ handler: 'BlurIntInput',
1470
+ placeholder: L._('max zoom'),
1471
+ min: 0,
1472
+ max: this.options.maxZoomLimit,
1473
+ },
1474
+ ],
1475
+ [
1476
+ 'options.overlay.minZoom',
1477
+ {
1478
+ handler: 'BlurIntInput',
1479
+ placeholder: L._('min zoom'),
1480
+ min: 0,
1481
+ max: this.options.maxZoomLimit,
1482
+ },
1483
+ ],
1484
+ [
1485
+ 'options.overlay.attribution',
1486
+ { handler: 'BlurInput', placeholder: L._('attribution') },
1487
+ ],
1488
+ [
1489
+ 'options.overlay.opacity',
1490
+ { handler: 'Range', min: 0, max: 1, step: 0.1, label: L._('Opacity') },
1491
+ ],
1492
+ ['options.overlay.tms', { handler: 'Switch', label: L._('TMS format') }],
1493
+ ]
1494
+ const overlay = L.DomUtil.createFieldset(container, L._('Custom overlay'))
1495
+ builder = new L.U.FormBuilder(this, overlayFields, {
1496
+ callback: this.initTileLayers,
1497
+ callbackContext: this,
1498
+ })
1499
+ overlay.appendChild(builder.build())
1500
+ },
1501
+
1502
+ _editBounds: function (container) {
1503
+ if (!L.Util.isObject(this.options.limitBounds)) {
1504
+ this.options.limitBounds = {}
1505
+ }
1506
+ const limitBounds = L.DomUtil.createFieldset(container, L._('Limit bounds'))
1507
+ const boundsFields = [
1508
+ [
1509
+ 'options.limitBounds.south',
1510
+ { handler: 'BlurFloatInput', placeholder: L._('max South') },
1511
+ ],
1512
+ [
1513
+ 'options.limitBounds.west',
1514
+ { handler: 'BlurFloatInput', placeholder: L._('max West') },
1515
+ ],
1516
+ [
1517
+ 'options.limitBounds.north',
1518
+ { handler: 'BlurFloatInput', placeholder: L._('max North') },
1519
+ ],
1520
+ [
1521
+ 'options.limitBounds.east',
1522
+ { handler: 'BlurFloatInput', placeholder: L._('max East') },
1523
+ ],
1524
+ ]
1525
+ const boundsBuilder = new L.U.FormBuilder(this, boundsFields, {
1526
+ callback: this.handleLimitBounds,
1527
+ callbackContext: this,
1528
+ })
1529
+ limitBounds.appendChild(boundsBuilder.build())
1530
+ const boundsButtons = L.DomUtil.create('div', 'button-bar half', limitBounds)
1531
+ L.DomUtil.createButton(
1532
+ 'button',
1533
+ boundsButtons,
1534
+ L._('Use current bounds'),
1535
+ function () {
1536
+ const bounds = this.getBounds()
1537
+ this.options.limitBounds.south = L.Util.formatNum(bounds.getSouth())
1538
+ this.options.limitBounds.west = L.Util.formatNum(bounds.getWest())
1539
+ this.options.limitBounds.north = L.Util.formatNum(bounds.getNorth())
1540
+ this.options.limitBounds.east = L.Util.formatNum(bounds.getEast())
1541
+ boundsBuilder.fetchAll()
1542
+ this.isDirty = true
1543
+ this.handleLimitBounds()
1544
+ },
1545
+ this
1546
+ )
1547
+ L.DomUtil.createButton(
1548
+ 'button',
1549
+ boundsButtons,
1550
+ L._('Empty'),
1551
+ function () {
1552
+ this.options.limitBounds.south = null
1553
+ this.options.limitBounds.west = null
1554
+ this.options.limitBounds.north = null
1555
+ this.options.limitBounds.east = null
1556
+ boundsBuilder.fetchAll()
1557
+ this.isDirty = true
1558
+ this.handleLimitBounds()
1559
+ },
1560
+ this
1561
+ )
1562
+ },
1563
+
1564
+ _editSlideshow: function (container) {
1565
+ const slideshow = L.DomUtil.createFieldset(container, L._('Slideshow'))
1566
+ const slideshowFields = [
1567
+ [
1568
+ 'options.slideshow.active',
1569
+ { handler: 'Switch', label: L._('Activate slideshow mode') },
1570
+ ],
1571
+ [
1572
+ 'options.slideshow.delay',
1573
+ {
1574
+ handler: 'SlideshowDelay',
1575
+ helpText: L._('Delay between two transitions when in play mode'),
1576
+ },
1577
+ ],
1578
+ [
1579
+ 'options.slideshow.easing',
1580
+ { handler: 'Switch', label: L._('Animated transitions'), inheritable: true },
1581
+ ],
1582
+ [
1583
+ 'options.slideshow.autoplay',
1584
+ { handler: 'Switch', label: L._('Autostart when map is loaded') },
1585
+ ],
1586
+ ]
1587
+ const slideshowHandler = function () {
1588
+ this.slideshow.setOptions(this.options.slideshow)
1589
+ this.renderControls()
1590
+ }
1591
+ const slideshowBuilder = new L.U.FormBuilder(this, slideshowFields, {
1592
+ callback: slideshowHandler,
1593
+ callbackContext: this,
1594
+ })
1595
+ slideshow.appendChild(slideshowBuilder.build())
1596
+ },
1597
+
1598
+ _editCredits: function (container) {
1599
+ const credits = L.DomUtil.createFieldset(container, L._('Credits'))
1600
+ const creditsFields = [
1601
+ ['options.licence', { handler: 'LicenceChooser', label: L._('licence') }],
1602
+ [
1603
+ 'options.shortCredit',
1604
+ {
1605
+ handler: 'Input',
1606
+ label: L._('Short credits'),
1607
+ helpEntries: ['shortCredit', 'textFormatting'],
1608
+ },
1609
+ ],
1610
+ [
1611
+ 'options.longCredit',
1612
+ {
1613
+ handler: 'Textarea',
1614
+ label: L._('Long credits'),
1615
+ helpEntries: ['longCredit', 'textFormatting'],
1616
+ },
1617
+ ],
1618
+ [
1619
+ 'options.permanentCredit',
1620
+ {
1621
+ handler: 'Textarea',
1622
+ label: L._('Permanent credits'),
1623
+ helpEntries: ['permanentCredit', 'textFormatting'],
1624
+ },
1625
+ ],
1626
+ [
1627
+ 'options.permanentCreditBackground',
1628
+ { handler: 'Switch', label: L._('Permanent credits background') },
1629
+ ],
1630
+ ]
1631
+ const creditsBuilder = new L.U.FormBuilder(this, creditsFields, {
1632
+ callback: this.renderControls,
1633
+ callbackContext: this,
1634
+ })
1635
+ credits.appendChild(creditsBuilder.build())
1636
+ },
1637
+
1638
+ _advancedActions: function (container) {
1639
+ const advancedActions = L.DomUtil.createFieldset(container, L._('Advanced actions'))
1640
+ const advancedButtons = L.DomUtil.create('div', 'button-bar half', advancedActions)
1641
+ if (this.permissions.isOwner()) {
1642
+ L.DomUtil.createButton(
1643
+ 'button umap-delete',
1644
+ advancedButtons,
1645
+ L._('Delete'),
1646
+ this.del,
1647
+ this
1648
+ )
1649
+ L.DomUtil.createButton(
1650
+ 'button umap-empty',
1651
+ advancedButtons,
1652
+ L._('Empty'),
1653
+ this.empty,
1654
+ this
1655
+ )
1656
+ }
1657
+ L.DomUtil.createButton(
1658
+ 'button umap-clone',
1659
+ advancedButtons,
1660
+ L._('Clone this map'),
1661
+ this.clone,
1662
+ this
1663
+ )
1664
+ L.DomUtil.createButton(
1665
+ 'button umap-empty',
1666
+ advancedButtons,
1667
+ L._('Delete all layers'),
1668
+ this.empty,
1669
+ this
1670
+ )
1671
+ L.DomUtil.createButton(
1672
+ 'button umap-download',
1673
+ advancedButtons,
1674
+ L._('Open share & download panel'),
1675
+ this.share.open,
1676
+ this.share
1677
+ )
1678
+ },
1679
+
1680
+ edit: function () {
1681
+ if (!this.editEnabled) return
1682
+ if (this.options.editMode !== 'advanced') return
1683
+ const container = L.DomUtil.create('div', 'umap-edit-container'),
1684
+ metadataFields = ['options.name', 'options.description'],
1685
+ title = L.DomUtil.create('h3', '', container)
1686
+ title.textContent = L._('Edit map properties')
1687
+ const builder = new L.U.FormBuilder(this, metadataFields)
1688
+ const form = builder.build()
1689
+ container.appendChild(form)
1690
+ this._editControls(container)
1691
+ this._editShapeProperties(container)
1692
+ this._editDefaultProperties(container)
1693
+ this._editInteractionsProperties(container)
1694
+ this._editTilelayer(container)
1695
+ this._editOverlay(container)
1696
+ this._editBounds(container)
1697
+ this._editSlideshow(container)
1698
+ this._editCredits(container)
1699
+ this._advancedActions(container)
1700
+
1701
+ this.ui.openPanel({ data: { html: container }, className: 'dark' })
1702
+ },
1703
+
1704
+ enableEdit: function () {
1705
+ L.DomUtil.addClass(document.body, 'umap-edit-enabled')
1706
+ this.editEnabled = true
1707
+ this.drop.enable()
1708
+ this.fire('edit:enabled')
1709
+ },
1710
+
1711
+ disableEdit: function () {
1712
+ if (this.isDirty) return
1713
+ this.drop.disable()
1714
+ L.DomUtil.removeClass(document.body, 'umap-edit-enabled')
1715
+ this.editedFeature = null
1716
+ this.editEnabled = false
1717
+ this.fire('edit:disabled')
1718
+ },
1719
+
1720
+ hasEditMode: function () {
1721
+ return this.options.editMode === 'simple' || this.options.editMode === 'advanced'
1722
+ },
1723
+
1724
+ getDisplayName: function () {
1725
+ return this.options.name || L._('Untitled map')
1726
+ },
1727
+
1728
+ initCaptionBar: function () {
1729
+ const container = L.DomUtil.create(
1730
+ 'div',
1731
+ 'umap-caption-bar',
1732
+ this._controlContainer
1733
+ ),
1734
+ name = L.DomUtil.create('h3', '', container)
1735
+ L.DomEvent.disableClickPropagation(container)
1736
+ this.permissions.addOwnerLink('span', container)
1737
+ if (this.options.captionMenus) {
1738
+ L.DomUtil.createButton(
1739
+ 'umap-about-link flat',
1740
+ container,
1741
+ L._('About'),
1742
+ this.displayCaption,
1743
+ this
1744
+ )
1745
+ L.DomUtil.createButton(
1746
+ 'umap-open-browser-link flat',
1747
+ container,
1748
+ L._('Browse data'),
1749
+ this.openBrowser,
1750
+ this
1751
+ )
1752
+ if (this.options.facetKey) {
1753
+ L.DomUtil.createButton(
1754
+ 'umap-open-filter-link flat',
1755
+ container,
1756
+ L._('Select data'),
1757
+ this.openFacet,
1758
+ this
1759
+ )
1760
+ }
1761
+ }
1762
+ const setName = function () {
1763
+ name.textContent = this.getDisplayName()
1764
+ }
1765
+ L.bind(setName, this)()
1766
+ this.on('postsync', L.bind(setName, this))
1767
+ this.onceDatalayersLoaded(function () {
1768
+ this.slideshow.renderToolbox(container)
1769
+ })
1770
+ },
1771
+
1772
+ askForReset: function (e) {
1773
+ if (!confirm(L._('Are you sure you want to cancel your changes?'))) return
1774
+ this.reset()
1775
+ this.disableEdit(e)
1776
+ this.ui.closePanel()
1777
+ },
1778
+
1779
+ startMarker: function () {
1780
+ return this.editTools.startMarker()
1781
+ },
1782
+
1783
+ startPolyline: function () {
1784
+ return this.editTools.startPolyline()
1785
+ },
1786
+
1787
+ startPolygon: function () {
1788
+ return this.editTools.startPolygon()
1789
+ },
1790
+
1791
+ del: function () {
1792
+ if (confirm(L._('Are you sure you want to delete this map?'))) {
1793
+ const url = this.urls.get('map_delete', { map_id: this.options.umap_id })
1794
+ this.post(url)
1795
+ }
1796
+ },
1797
+
1798
+ clone: function () {
1799
+ if (
1800
+ confirm(L._('Are you sure you want to clone this map and all its datalayers?'))
1801
+ ) {
1802
+ const url = this.urls.get('map_clone', { map_id: this.options.umap_id })
1803
+ this.post(url)
1804
+ }
1805
+ },
1806
+
1807
+ empty: function () {
1808
+ this.eachDataLayerReverse((datalayer) => {
1809
+ datalayer._delete()
1810
+ })
1811
+ },
1812
+
1813
+ initLoader: function () {
1814
+ this.loader = new L.Control.Loading()
1815
+ this.loader.onAdd(this)
1816
+ },
1817
+
1818
+ post: function (url, options) {
1819
+ options = options || {}
1820
+ options.listener = this
1821
+ this.xhr.post(url, options)
1822
+ },
1823
+
1824
+ get: function (url, options) {
1825
+ options = options || {}
1826
+ options.listener = this
1827
+ this.xhr.get(url, options)
1828
+ },
1829
+
1830
+ ajax: function (options) {
1831
+ options.listener = this
1832
+ this.xhr._ajax(options)
1833
+ },
1834
+
1835
+ initContextMenu: function () {
1836
+ this.contextmenu = new L.U.ContextMenu(this)
1837
+ this.contextmenu.enable()
1838
+ },
1839
+
1840
+ setContextMenuItems: function (e) {
1841
+ let items = []
1842
+ if (this._zoom !== this.getMaxZoom()) {
1843
+ items.push({
1844
+ text: L._('Zoom in'),
1845
+ callback: function () {
1846
+ this.zoomIn()
1847
+ },
1848
+ })
1849
+ }
1850
+ if (this._zoom !== this.getMinZoom()) {
1851
+ items.push({
1852
+ text: L._('Zoom out'),
1853
+ callback: function () {
1854
+ this.zoomOut()
1855
+ },
1856
+ })
1857
+ }
1858
+ if (e && e.relatedTarget) {
1859
+ if (e.relatedTarget.getContextMenuItems) {
1860
+ items = items.concat(e.relatedTarget.getContextMenuItems(e))
1861
+ }
1862
+ }
1863
+ if (this.hasEditMode()) {
1864
+ items.push('-')
1865
+ if (this.editEnabled) {
1866
+ if (!this.isDirty) {
1867
+ items.push({
1868
+ text: this.help.displayLabel('STOP_EDIT'),
1869
+ callback: this.disableEdit,
1870
+ })
1871
+ }
1872
+ if (this.options.enableMarkerDraw) {
1873
+ items.push({
1874
+ text: this.help.displayLabel('DRAW_MARKER'),
1875
+ callback: this.startMarker,
1876
+ context: this,
1877
+ })
1878
+ }
1879
+ if (this.options.enablePolylineDraw) {
1880
+ items.push({
1881
+ text: this.help.displayLabel('DRAW_POLYGON'),
1882
+ callback: this.startPolygon,
1883
+ context: this,
1884
+ })
1885
+ }
1886
+ if (this.options.enablePolygonDraw) {
1887
+ items.push({
1888
+ text: this.help.displayLabel('DRAW_LINE'),
1889
+ callback: this.startPolyline,
1890
+ context: this,
1891
+ })
1892
+ }
1893
+ items.push('-')
1894
+ items.push({
1895
+ text: L._('Help'),
1896
+ callback: function () {
1897
+ this.help.show('edit')
1898
+ },
1899
+ })
1900
+ } else {
1901
+ items.push({
1902
+ text: this.help.displayLabel('TOGGLE_EDIT'),
1903
+ callback: this.enableEdit,
1904
+ })
1905
+ }
1906
+ }
1907
+ items.push('-', {
1908
+ text: L._('Browse data'),
1909
+ callback: this.openBrowser,
1910
+ })
1911
+ if (this.options.facetKey) {
1912
+ items.push({
1913
+ text: L._('Facet search'),
1914
+ callback: this.openFacet,
1915
+ })
1916
+ }
1917
+ items.push(
1918
+ {
1919
+ text: L._('About'),
1920
+ callback: this.displayCaption,
1921
+ },
1922
+ {
1923
+ text: this.help.displayLabel('SEARCH'),
1924
+ callback: this.search,
1925
+ }
1926
+ )
1927
+ if (this.options.urls.routing) {
1928
+ items.push('-', {
1929
+ text: L._('Directions from here'),
1930
+ callback: this.openExternalRouting,
1931
+ })
1932
+ }
1933
+ this.options.contextmenuItems = items
1934
+ },
1935
+
1936
+ openExternalRouting: function (e) {
1937
+ const url = this.urls.get('routing', {
1938
+ lat: e.latlng.lat,
1939
+ lng: e.latlng.lng,
1940
+ locale: L.locale,
1941
+ zoom: this.getZoom(),
1942
+ })
1943
+ if (url) window.open(url)
1944
+ },
1945
+
1946
+ getMap: function () {
1947
+ return this
1948
+ },
1949
+
1950
+ getGeoContext: function () {
1951
+ const context = {
1952
+ bbox: this.getBounds().toBBoxString(),
1953
+ north: this.getBounds().getNorthEast().lat,
1954
+ east: this.getBounds().getNorthEast().lng,
1955
+ south: this.getBounds().getSouthWest().lat,
1956
+ west: this.getBounds().getSouthWest().lng,
1957
+ lat: this.getCenter().lat,
1958
+ lng: this.getCenter().lng,
1959
+ zoom: this.getZoom(),
1960
+ }
1961
+ context.left = context.west
1962
+ context.bottom = context.south
1963
+ context.right = context.east
1964
+ context.top = context.north
1965
+ return context
1966
+ },
1967
+
1968
+ localizeUrl: function (url) {
1969
+ return L.Util.greedyTemplate(url, this.getGeoContext(), true)
1970
+ },
1971
+
1972
+ proxyUrl: function (url, ttl) {
1973
+ if (this.options.urls.ajax_proxy) {
1974
+ url = L.Util.greedyTemplate(this.options.urls.ajax_proxy, {
1975
+ url: encodeURIComponent(url),
1976
+ ttl: ttl,
1977
+ })
1978
+ }
1979
+ return url
1980
+ },
1981
+
1982
+ closeInplaceToolbar: function () {
1983
+ const toolbar = this._toolbars[L.Toolbar.Popup._toolbar_class_id]
1984
+ if (toolbar) toolbar.remove()
1985
+ },
1986
+
1987
+ search: function () {
1988
+ if (this._controls.search) this._controls.search.openPanel(this)
1989
+ },
1990
+
1991
+ getFilterKeys: function () {
1992
+ return (this.options.filterKey || this.options.sortKey || 'name').split(',')
1993
+ },
1994
+
1995
+ getFacetKeys: function () {
1996
+ return (this.options.facetKey || '').split(',').reduce((acc, curr) => {
1997
+ const els = curr.split('|')
1998
+ acc[els[0]] = els[1] || els[0]
1999
+ return acc
2000
+ }, {})
2001
+ },
2002
+
2003
+ getLayersBounds: function () {
2004
+ const bounds = new L.latLngBounds()
2005
+ this.eachBrowsableDataLayer((d) => {
2006
+ if (d.isVisible()) bounds.extend(d.layer.getBounds())
2007
+ })
2008
+ return bounds
2009
+ },
2010
+ })