umap-project 2.1.3__py3-none-any.whl → 2.2.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.

Files changed (200) hide show
  1. umap/__init__.py +1 -1
  2. umap/context_processors.py +1 -0
  3. umap/locale/br/LC_MESSAGES/django.mo +0 -0
  4. umap/locale/en/LC_MESSAGES/django.po +32 -32
  5. umap/locale/hu/LC_MESSAGES/django.mo +0 -0
  6. umap/locale/it/LC_MESSAGES/django.mo +0 -0
  7. umap/locale/ms/LC_MESSAGES/django.mo +0 -0
  8. umap/migrations/0020_alter_tilelayer_url_template.py +19 -0
  9. umap/migrations/0021_remove_map_description.py +16 -0
  10. umap/models.py +8 -6
  11. umap/settings/base.py +1 -0
  12. umap/static/umap/base.css +43 -156
  13. umap/static/umap/content.css +7 -25
  14. umap/static/umap/css/icon.css +112 -0
  15. umap/static/umap/css/panel.css +140 -0
  16. umap/static/umap/img/16-white.svg +5 -1
  17. umap/static/umap/img/16.svg +7 -4
  18. umap/static/umap/img/24-white.svg +3 -1
  19. umap/static/umap/img/24.svg +3 -4
  20. umap/static/umap/img/source/16-white.svg +176 -940
  21. umap/static/umap/img/source/16.svg +8 -5
  22. umap/static/umap/img/source/24-white.svg +5 -3
  23. umap/static/umap/img/source/24.svg +6 -7
  24. umap/static/umap/js/modules/browser.js +97 -73
  25. umap/static/umap/js/modules/dompurify.js +12 -0
  26. umap/static/umap/js/modules/facets.js +149 -0
  27. umap/static/umap/js/modules/global.js +9 -1
  28. umap/static/umap/js/modules/i18n.js +7 -0
  29. umap/static/umap/js/modules/orderable.js +84 -0
  30. umap/static/umap/js/modules/panel.js +76 -0
  31. umap/static/umap/js/modules/request.js +0 -1
  32. umap/static/umap/js/modules/schema.js +324 -223
  33. umap/static/umap/js/modules/urls.js +1 -16
  34. umap/static/umap/js/modules/utils.js +340 -0
  35. umap/static/umap/js/umap.autocomplete.js +40 -25
  36. umap/static/umap/js/umap.controls.js +227 -369
  37. umap/static/umap/js/umap.core.js +77 -366
  38. umap/static/umap/js/umap.datalayer.permissions.js +1 -1
  39. umap/static/umap/js/umap.features.js +62 -42
  40. umap/static/umap/js/umap.forms.js +128 -36
  41. umap/static/umap/js/umap.icon.js +11 -4
  42. umap/static/umap/js/umap.importer.js +78 -57
  43. umap/static/umap/js/umap.js +179 -156
  44. umap/static/umap/js/umap.layer.js +79 -40
  45. umap/static/umap/js/umap.permissions.js +13 -9
  46. umap/static/umap/js/umap.popup.js +26 -30
  47. umap/static/umap/js/umap.share.js +12 -9
  48. umap/static/umap/js/umap.tableeditor.js +4 -6
  49. umap/static/umap/js/umap.ui.js +10 -60
  50. umap/static/umap/locale/am_ET.js +243 -227
  51. umap/static/umap/locale/am_ET.json +21 -9
  52. umap/static/umap/locale/ar.js +243 -227
  53. umap/static/umap/locale/ar.json +21 -9
  54. umap/static/umap/locale/ast.js +243 -227
  55. umap/static/umap/locale/ast.json +21 -9
  56. umap/static/umap/locale/bg.js +243 -227
  57. umap/static/umap/locale/bg.json +21 -9
  58. umap/static/umap/locale/br.js +253 -237
  59. umap/static/umap/locale/br.json +25 -13
  60. umap/static/umap/locale/ca.js +243 -227
  61. umap/static/umap/locale/ca.json +21 -9
  62. umap/static/umap/locale/cs_CZ.js +243 -227
  63. umap/static/umap/locale/cs_CZ.json +21 -9
  64. umap/static/umap/locale/da.js +243 -227
  65. umap/static/umap/locale/da.json +21 -9
  66. umap/static/umap/locale/de.js +243 -227
  67. umap/static/umap/locale/de.json +21 -9
  68. umap/static/umap/locale/el.js +243 -227
  69. umap/static/umap/locale/el.json +21 -9
  70. umap/static/umap/locale/en.js +243 -234
  71. umap/static/umap/locale/en.json +22 -10
  72. umap/static/umap/locale/en_US.json +21 -9
  73. umap/static/umap/locale/es.js +243 -227
  74. umap/static/umap/locale/es.json +21 -9
  75. umap/static/umap/locale/et.js +243 -227
  76. umap/static/umap/locale/et.json +21 -9
  77. umap/static/umap/locale/eu.js +227 -199
  78. umap/static/umap/locale/eu.json +1 -1
  79. umap/static/umap/locale/fa_IR.js +243 -227
  80. umap/static/umap/locale/fa_IR.json +21 -9
  81. umap/static/umap/locale/fi.js +243 -227
  82. umap/static/umap/locale/fi.json +21 -9
  83. umap/static/umap/locale/fr.js +243 -234
  84. umap/static/umap/locale/fr.json +21 -9
  85. umap/static/umap/locale/gl.js +243 -227
  86. umap/static/umap/locale/gl.json +21 -9
  87. umap/static/umap/locale/he.js +243 -227
  88. umap/static/umap/locale/he.json +21 -9
  89. umap/static/umap/locale/hr.js +243 -227
  90. umap/static/umap/locale/hr.json +21 -9
  91. umap/static/umap/locale/hu.js +243 -234
  92. umap/static/umap/locale/hu.json +21 -9
  93. umap/static/umap/locale/id.js +243 -227
  94. umap/static/umap/locale/id.json +21 -9
  95. umap/static/umap/locale/is.js +243 -227
  96. umap/static/umap/locale/is.json +21 -9
  97. umap/static/umap/locale/it.js +243 -234
  98. umap/static/umap/locale/it.json +21 -9
  99. umap/static/umap/locale/ja.js +243 -227
  100. umap/static/umap/locale/ja.json +21 -9
  101. umap/static/umap/locale/ko.js +243 -227
  102. umap/static/umap/locale/ko.json +21 -9
  103. umap/static/umap/locale/lt.js +243 -227
  104. umap/static/umap/locale/lt.json +21 -9
  105. umap/static/umap/locale/ms.js +243 -234
  106. umap/static/umap/locale/ms.json +22 -10
  107. umap/static/umap/locale/nl.js +246 -230
  108. umap/static/umap/locale/nl.json +21 -9
  109. umap/static/umap/locale/no.js +243 -227
  110. umap/static/umap/locale/no.json +21 -9
  111. umap/static/umap/locale/pl.js +243 -227
  112. umap/static/umap/locale/pl.json +21 -9
  113. umap/static/umap/locale/pl_PL.json +21 -9
  114. umap/static/umap/locale/pt.js +243 -227
  115. umap/static/umap/locale/pt.json +21 -9
  116. umap/static/umap/locale/pt_BR.js +243 -227
  117. umap/static/umap/locale/pt_BR.json +21 -9
  118. umap/static/umap/locale/pt_PT.js +243 -227
  119. umap/static/umap/locale/pt_PT.json +21 -9
  120. umap/static/umap/locale/ro.js +243 -227
  121. umap/static/umap/locale/ro.json +21 -9
  122. umap/static/umap/locale/ru.js +243 -227
  123. umap/static/umap/locale/ru.json +21 -9
  124. umap/static/umap/locale/si.js +1 -1
  125. umap/static/umap/locale/si.json +1 -1
  126. umap/static/umap/locale/sk_SK.js +243 -227
  127. umap/static/umap/locale/sk_SK.json +21 -9
  128. umap/static/umap/locale/sl.js +243 -227
  129. umap/static/umap/locale/sl.json +21 -9
  130. umap/static/umap/locale/sr.js +243 -227
  131. umap/static/umap/locale/sr.json +21 -9
  132. umap/static/umap/locale/sv.js +243 -227
  133. umap/static/umap/locale/sv.json +21 -9
  134. umap/static/umap/locale/th_TH.js +243 -227
  135. umap/static/umap/locale/th_TH.json +21 -9
  136. umap/static/umap/locale/tr.js +243 -227
  137. umap/static/umap/locale/tr.json +21 -9
  138. umap/static/umap/locale/uk_UA.js +243 -227
  139. umap/static/umap/locale/uk_UA.json +21 -9
  140. umap/static/umap/locale/vi.js +243 -227
  141. umap/static/umap/locale/vi.json +21 -9
  142. umap/static/umap/locale/vi_VN.json +21 -9
  143. umap/static/umap/locale/zh.js +243 -227
  144. umap/static/umap/locale/zh.json +21 -9
  145. umap/static/umap/locale/zh_CN.json +21 -9
  146. umap/static/umap/locale/zh_TW.Big5.json +21 -9
  147. umap/static/umap/locale/zh_TW.js +243 -234
  148. umap/static/umap/locale/zh_TW.json +21 -9
  149. umap/static/umap/map.css +124 -264
  150. umap/static/umap/test/DataLayer.js +463 -0
  151. umap/static/umap/test/Feature.js +0 -226
  152. umap/static/umap/test/TableEditor.js +104 -0
  153. umap/static/umap/test/Util.js +0 -521
  154. umap/static/umap/test/index.html +0 -1
  155. umap/static/umap/unittests/URLs.js +1 -1
  156. umap/static/umap/unittests/utils.js +610 -0
  157. umap/static/umap/vars.css +9 -0
  158. umap/static/umap/vendors/dompurify/purify.es.mjs +1525 -0
  159. umap/static/umap/vendors/formbuilder/Leaflet.FormBuilder.js +1 -0
  160. umap/static/umap/vendors/iconlayers/iconLayers.js +1 -1
  161. umap/templates/umap/css.html +2 -0
  162. umap/templates/umap/js.html +0 -1
  163. umap/templates/umap/map_detail.html +4 -0
  164. umap/templates/umap/map_table.html +12 -10
  165. umap/templatetags/umap_tags.py +5 -0
  166. umap/tests/integration/conftest.py +12 -1
  167. umap/tests/integration/test_anonymous_owned_map.py +27 -5
  168. umap/tests/integration/test_basics.py +21 -0
  169. umap/tests/integration/test_browser.py +12 -25
  170. umap/tests/integration/test_choropleth.py +1 -1
  171. umap/tests/integration/test_dashboard.py +10 -0
  172. umap/tests/integration/test_datalayer.py +8 -6
  173. umap/tests/integration/test_edit_datalayer.py +24 -19
  174. umap/tests/integration/test_edit_map.py +189 -2
  175. umap/tests/integration/test_edit_marker.py +120 -0
  176. umap/tests/integration/test_edit_polygon.py +122 -0
  177. umap/tests/integration/test_facets_browser.py +104 -14
  178. umap/tests/integration/test_import.py +72 -20
  179. umap/tests/integration/test_map.py +19 -17
  180. umap/tests/integration/test_map_list.py +28 -0
  181. umap/tests/integration/test_owned_map.py +10 -10
  182. umap/tests/integration/test_picto.py +5 -5
  183. umap/tests/integration/test_querystring.py +9 -15
  184. umap/tests/integration/test_slideshow.py +0 -5
  185. umap/tests/integration/test_statics.py +3 -2
  186. umap/tests/integration/test_tableeditor.py +1 -5
  187. umap/tests/integration/test_tilelayer.py +10 -0
  188. umap/tests/integration/test_view_marker.py +64 -0
  189. umap/tests/integration/test_view_polygon.py +59 -0
  190. umap/tests/integration/test_view_polyline.py +51 -0
  191. umap/tests/test_map_views.py +13 -0
  192. {umap_project-2.1.3.dist-info → umap_project-2.2.0.dist-info}/METADATA +12 -12
  193. {umap_project-2.1.3.dist-info → umap_project-2.2.0.dist-info}/RECORD +198 -182
  194. {umap_project-2.1.3.dist-info → umap_project-2.2.0.dist-info}/WHEEL +1 -1
  195. umap/static/umap/vendors/dompurify/purify.min.js +0 -3
  196. umap/static/umap/vendors/dompurify/purify.min.js.map +0 -1
  197. /umap/tests/integration/{test_polygon.py → test_draw_polygon.py} +0 -0
  198. /umap/tests/integration/{test_polyline.py → test_draw_polyline.py} +0 -0
  199. {umap_project-2.1.3.dist-info → umap_project-2.2.0.dist-info}/entry_points.txt +0 -0
  200. {umap_project-2.1.3.dist-info → umap_project-2.2.0.dist-info}/licenses/LICENSE +0 -0
@@ -56,6 +56,11 @@ U.Map = L.Map.extend({
56
56
  if (geojson.geometry) this.options.center = this.latLng(geojson.geometry)
57
57
  this.urls = new U.URLs(this.options.urls)
58
58
 
59
+ this.panel = new U.Panel(this)
60
+ if (this.hasEditMode()) {
61
+ this.editPanel = new U.EditPanel(this)
62
+ this.fullPanel = new U.FullPanel(this)
63
+ }
59
64
  this.ui = new U.UI(this._container)
60
65
  this.ui.on('dataloading', (e) => this.fire('dataloading', e))
61
66
  this.ui.on('dataload', (e) => this.fire('dataload', e))
@@ -69,6 +74,7 @@ U.Map = L.Map.extend({
69
74
  this.options.zoomControl = zoomControl !== undefined ? zoomControl : true
70
75
  this.options.fullscreenControl =
71
76
  fullscreenControl !== undefined ? fullscreenControl : true
77
+
72
78
  this.datalayersFromQueryString = L.Util.queryString('datalayers')
73
79
  if (this.datalayersFromQueryString) {
74
80
  this.datalayersFromQueryString = this.datalayersFromQueryString
@@ -100,17 +106,19 @@ U.Map = L.Map.extend({
100
106
  this.options.slideshow &&
101
107
  this.options.slideshow.delay &&
102
108
  this.options.slideshow.active === undefined
103
- )
109
+ ) {
104
110
  this.options.slideshow.active = true
105
- if (this.options.advancedFilterKey)
111
+ }
112
+ if (this.options.advancedFilterKey) {
106
113
  this.options.facetKey = this.options.advancedFilterKey
114
+ delete this.options.advancedFilterKey
115
+ }
107
116
 
108
117
  // Global storage for retrieving datalayers and features
109
118
  this.datalayers = {}
110
119
  this.datalayers_index = []
111
120
  this.dirty_datalayers = []
112
121
  this.features_index = {}
113
- this.facets = {}
114
122
 
115
123
  // Needed for actions labels
116
124
  this.help = new U.Help(this)
@@ -138,14 +146,9 @@ U.Map = L.Map.extend({
138
146
  }
139
147
  delete this.options.displayDataBrowserOnLoad
140
148
  }
141
-
142
- this.ui.on(
143
- 'panel:closed',
144
- function () {
145
- this.invalidateSize({ pan: false })
146
- },
147
- this
148
- )
149
+ if (this.options.datalayersControl === 'expanded') {
150
+ this.options.onLoadPanel = 'datalayers'
151
+ }
149
152
 
150
153
  let isDirty = false // self status
151
154
  try {
@@ -195,30 +198,29 @@ U.Map = L.Map.extend({
195
198
 
196
199
  this.slideshow = new U.Slideshow(this, this.options.slideshow)
197
200
  this.permissions = new U.MapPermissions(this)
198
- this.initCaptionBar()
199
201
  if (this.hasEditMode()) {
200
202
  this.editTools = new U.Editable(this)
201
- this.ui.on(
202
- 'panel:closed panel:open',
203
- function () {
204
- this.editedFeature = null
205
- },
206
- this
207
- )
208
203
  this.renderEditToolbar()
209
204
  }
210
205
  this.initShortcuts()
211
206
  this.onceDataLoaded(function () {
212
- if (L.Util.queryString('share')) this.share.open()
213
- else if (this.options.onLoadPanel === 'databrowser') this.openBrowser()
214
- else if (this.options.onLoadPanel === 'caption') this.displayCaption()
215
- else if (
216
- this.options.onLoadPanel === 'facet' ||
217
- this.options.onLoadPanel === 'datafilters'
218
- )
219
- this.openFacet()
220
207
  const slug = L.Util.queryString('feature')
221
208
  if (slug && this.features_index[slug]) this.features_index[slug].view()
209
+ if (this.options.noControl) return
210
+ this.initCaptionBar()
211
+ if (L.Util.queryString('share')) {
212
+ this.share.open()
213
+ } else if (this.options.onLoadPanel === 'databrowser') {
214
+ this.openBrowser('expanded')
215
+ } else if (this.options.onLoadPanel === 'datalayers') {
216
+ this.openBrowser('condensed')
217
+ } else if (this.options.onLoadPanel === 'caption') {
218
+ this.panel.mode = 'condensed'
219
+ this.displayCaption()
220
+ } else if (['facet', 'datafilters'].includes(this.options.onLoadPanel)) {
221
+ this.panel.mode = 'expanded'
222
+ this.openFacet()
223
+ }
222
224
  if (L.Util.queryString('edit')) {
223
225
  if (this.hasEditMode()) this.enableEdit()
224
226
  // Sometimes users share the ?edit link by mistake, let's remove
@@ -241,6 +243,44 @@ U.Map = L.Map.extend({
241
243
  this.on('click contextmenu.show', this.closeInplaceToolbar)
242
244
  },
243
245
 
246
+ render: function (fields) {
247
+ let impacts = U.Utils.getImpactsFromSchema(fields)
248
+
249
+ for (let impact of impacts) {
250
+ switch (impact) {
251
+ case 'ui':
252
+ this.initCaptionBar()
253
+ this.renderEditToolbar()
254
+ this.renderControls()
255
+ this.facets.redraw()
256
+ break
257
+ case 'data':
258
+ this.redrawVisibleDataLayers()
259
+ break
260
+ case 'datalayer-index':
261
+ this.reindexDataLayers()
262
+ break
263
+ case 'background':
264
+ this.initTileLayers()
265
+ break
266
+ case 'bounds':
267
+ this.handleLimitBounds()
268
+ break
269
+ }
270
+ }
271
+ },
272
+
273
+ reindexDataLayers: function () {
274
+ this.eachDataLayer((datalayer) => datalayer.reindex())
275
+ this.onDataLayersChanged()
276
+ },
277
+
278
+ redrawVisibleDataLayers: function () {
279
+ this.eachVisibleDataLayer((datalayer) => {
280
+ datalayer.redraw()
281
+ })
282
+ },
283
+
244
284
  setOptionsFromQueryString: function (options) {
245
285
  // This is not an editable option
246
286
  L.Util.setFromQueryString(options, 'editMode')
@@ -264,10 +304,12 @@ U.Map = L.Map.extend({
264
304
  // Specific case for datalayersControl
265
305
  // which accepts "expanded" value, on top of true/false/null
266
306
  if (L.Util.queryString('datalayersControl') === 'expanded') {
267
- L.Util.setFromQueryString(options, 'datalayersControl')
307
+ options.onLoadPanel = 'datalayers'
268
308
  }
269
309
  },
270
310
 
311
+ // Merge the given schema with the default one
312
+ // Missing keys inside the schema are merged with the default ones.
271
313
  overrideSchema: function (schema) {
272
314
  for (const [key, extra] of Object.entries(schema)) {
273
315
  U.SCHEMA[key] = L.extend({}, U.SCHEMA[key], extra)
@@ -282,22 +324,25 @@ U.Map = L.Map.extend({
282
324
  new U.EditControl(this).addTo(this)
283
325
 
284
326
  new U.DrawToolbar({ map: this }).addTo(this)
285
-
286
327
  const editActions = [
287
- U.ImportAction,
328
+ U.EditCaptionAction,
288
329
  U.EditPropertiesAction,
289
- U.ManageDatalayersAction,
330
+ U.EditLayersAction,
290
331
  U.ChangeTileLayerAction,
291
332
  U.UpdateExtentAction,
292
333
  U.UpdatePermsAction,
334
+ U.ImportAction,
293
335
  ]
294
- new U.SettingsToolbar({ actions: editActions }).addTo(this)
336
+ if (this.options.editMode === 'advanced') {
337
+ new U.SettingsToolbar({ actions: editActions }).addTo(this)
338
+ }
295
339
  }
296
340
  this._controls.zoom = new L.Control.Zoom({
297
341
  zoomInTitle: L._('Zoom in'),
298
342
  zoomOutTitle: L._('Zoom out'),
299
343
  })
300
344
  this._controls.datalayers = new U.DataLayersControl(this)
345
+ this._controls.caption = new U.CaptionControl(this)
301
346
  this._controls.locate = new U.Locate(this, {
302
347
  strings: {
303
348
  title: L._('Center map on your location'),
@@ -333,6 +378,7 @@ U.Map = L.Map.extend({
333
378
  if (this.options.scrollWheelZoom) this.scrollWheelZoom.enable()
334
379
  else this.scrollWheelZoom.disable()
335
380
  this.browser = new U.Browser(this)
381
+ this.facets = new U.Facets(this)
336
382
  this.importer = new U.Importer(this)
337
383
  this.drop = new U.DropControl(this)
338
384
  this.share = new U.Share(this)
@@ -420,7 +466,11 @@ U.Map = L.Map.extend({
420
466
  if (!pane.dataset || !pane.dataset.id) continue
421
467
  this.datalayers_index.push(this.datalayers[pane.dataset.id])
422
468
  }
423
- this.updateDatalayersControl()
469
+ this.onDataLayersChanged()
470
+ },
471
+
472
+ onDataLayersChanged: function () {
473
+ if (this.browser) this.browser.update()
424
474
  },
425
475
 
426
476
  ensurePanesOrder: function () {
@@ -449,10 +499,6 @@ U.Map = L.Map.extend({
449
499
  return this
450
500
  },
451
501
 
452
- updateDatalayersControl: function () {
453
- if (this._controls.datalayers) this._controls.datalayers.update()
454
- },
455
-
456
502
  backupOptions: function () {
457
503
  this._backupOptions = L.extend({}, this.options)
458
504
  this._backupOptions.tilelayer = L.extend({}, this.options.tilelayer)
@@ -476,8 +522,13 @@ U.Map = L.Map.extend({
476
522
  L.DomEvent.stop(e)
477
523
  this.search()
478
524
  } else if (e.keyCode === U.Keys.ESC) {
479
- if (this.help.visible()) this.help.hide()
480
- else this.ui.closePanel()
525
+ if (this.help.visible()) {
526
+ this.help.hide()
527
+ } else {
528
+ this.panel.close()
529
+ this.editPanel?.close()
530
+ this.fullPanel?.close()
531
+ }
481
532
  }
482
533
 
483
534
  if (!this.hasEditMode()) return
@@ -489,7 +540,6 @@ U.Map = L.Map.extend({
489
540
  } else if (key === U.Keys.E && modifierKey && this.editEnabled && !this.isDirty) {
490
541
  L.DomEvent.stop(e)
491
542
  this.disableEdit()
492
- this.ui.closePanel()
493
543
  }
494
544
  if (key === U.Keys.S && modifierKey) {
495
545
  L.DomEvent.stop(e)
@@ -668,7 +718,7 @@ U.Map = L.Map.extend({
668
718
  if (datalayer) {
669
719
  const feature = datalayer.getFeatureByIndex(-1)
670
720
  if (feature) {
671
- feature.zoomTo()
721
+ feature.zoomTo({ callback: this.options.noControl ? null : feature.view })
672
722
  return
673
723
  }
674
724
  }
@@ -727,12 +777,17 @@ U.Map = L.Map.extend({
727
777
  return new U.DataLayer(this, datalayer)
728
778
  },
729
779
 
780
+ newDataLayer: function () {
781
+ const datalayer = this.createDataLayer({})
782
+ datalayer.edit()
783
+ },
784
+
730
785
  getDefaultOption: function (option) {
731
786
  return U.SCHEMA[option] && U.SCHEMA[option].default
732
787
  },
733
788
 
734
789
  getOption: function (option) {
735
- if (L.Util.usableOption(this.options, option)) return this.options[option]
790
+ if (U.Utils.usableOption(this.options, option)) return this.options[option]
736
791
  return this.getDefaultOption(option)
737
792
  },
738
793
 
@@ -764,10 +819,6 @@ U.Map = L.Map.extend({
764
819
  })
765
820
  },
766
821
 
767
- manageDatalayers: function () {
768
- if (this._controls.datalayers) this._controls.datalayers.openPanel()
769
- },
770
-
771
822
  toGeoJSON: function () {
772
823
  let features = []
773
824
  this.eachDataLayer((datalayer) => {
@@ -789,7 +840,7 @@ U.Map = L.Map.extend({
789
840
  },
790
841
 
791
842
  processFileToImport: function (file, layer, type) {
792
- type = type || L.Util.detectFileType(file)
843
+ type = type || U.Utils.detectFileType(file)
793
844
  if (!type) {
794
845
  this.ui.alert({
795
846
  content: L._('Unable to detect format of file {filename}', {
@@ -856,7 +907,8 @@ U.Map = L.Map.extend({
856
907
  }
857
908
  },
858
909
 
859
- openBrowser: function () {
910
+ openBrowser: function (mode) {
911
+ if (mode) this.panel.mode = mode
860
912
  this.onceDatalayersLoaded(function () {
861
913
  this.browser.open()
862
914
  })
@@ -911,9 +963,9 @@ U.Map = L.Map.extend({
911
963
  })
912
964
  this.ensurePanesOrder()
913
965
  this.dirty_datalayers = []
914
- this.updateDatalayersControl()
915
966
  this.initTileLayers()
916
967
  this.isDirty = false
968
+ this.onDataLayersChanged()
917
969
  },
918
970
 
919
971
  checkDirty: function () {
@@ -971,11 +1023,7 @@ U.Map = L.Map.extend({
971
1023
  this.options.umap_id = data.id
972
1024
  this.permissions.setOptions(data.permissions)
973
1025
  this.permissions.commit()
974
- if (
975
- data.permissions &&
976
- data.permissions.anonymous_edit_url &&
977
- this.options.urls.map_send_edit_link
978
- ) {
1026
+ if (data.permissions && data.permissions.anonymous_edit_url) {
979
1027
  alert.duration = Infinity
980
1028
  alert.content =
981
1029
  L._(
@@ -983,12 +1031,6 @@ U.Map = L.Map.extend({
983
1031
  ) + `<br>${data.permissions.anonymous_edit_url}`
984
1032
 
985
1033
  alert.actions = [
986
- {
987
- label: L._('Send me the link'),
988
- input: L._('Email'),
989
- callback: this.sendEditLink,
990
- callbackContext: this,
991
- },
992
1034
  {
993
1035
  label: L._('Copy link'),
994
1036
  callback: () => {
@@ -1001,6 +1043,14 @@ U.Map = L.Map.extend({
1001
1043
  callbackContext: this,
1002
1044
  },
1003
1045
  ]
1046
+ if (this.options.urls.map_send_edit_link) {
1047
+ alert.actions.push({
1048
+ label: L._('Send me the link'),
1049
+ input: L._('Email'),
1050
+ callback: this.sendEditLink,
1051
+ callbackContext: this,
1052
+ })
1053
+ }
1004
1054
  }
1005
1055
  } else if (!this.permissions.isDirty) {
1006
1056
  // Do not override local changes to permissions,
@@ -1014,7 +1064,7 @@ U.Map = L.Map.extend({
1014
1064
  else window.location = data.url
1015
1065
  alert.content = data.info || alert.content
1016
1066
  this.once('saved', () => this.ui.alert(alert))
1017
- this.ui.closePanel()
1067
+ this.editPanel.close()
1018
1068
  this.permissions.save()
1019
1069
  }
1020
1070
  },
@@ -1127,13 +1177,7 @@ U.Map = L.Map.extend({
1127
1177
  'options.captionBar',
1128
1178
  'options.captionMenus',
1129
1179
  ])
1130
- builder = new U.FormBuilder(this, UIFields, {
1131
- callback: function () {
1132
- this.renderControls()
1133
- this.initCaptionBar()
1134
- },
1135
- callbackContext: this,
1136
- })
1180
+ builder = new U.FormBuilder(this, UIFields)
1137
1181
  const controlsOptions = L.DomUtil.createFieldset(
1138
1182
  container,
1139
1183
  L._('User interface options')
@@ -1156,14 +1200,7 @@ U.Map = L.Map.extend({
1156
1200
  'options.dashArray',
1157
1201
  ]
1158
1202
 
1159
- builder = new U.FormBuilder(this, shapeOptions, {
1160
- callback: function (e) {
1161
- if (this._controls.miniMap) this.renderControls()
1162
- this.eachVisibleDataLayer((datalayer) => {
1163
- datalayer.redraw()
1164
- })
1165
- },
1166
- })
1203
+ builder = new U.FormBuilder(this, shapeOptions)
1167
1204
  const defaultShapeProperties = L.DomUtil.createFieldset(
1168
1205
  container,
1169
1206
  L._('Default shape properties')
@@ -1199,9 +1236,9 @@ U.Map = L.Map.extend({
1199
1236
  [
1200
1237
  'options.facetKey',
1201
1238
  {
1202
- handler: 'Input',
1239
+ handler: 'BlurInput',
1203
1240
  helpEntries: 'facetKey',
1204
- placeholder: L._('Example: key1,key2,key3'),
1241
+ placeholder: L._('Example: key1,key2|Label 2,key3|Label 3|checkbox'),
1205
1242
  label: L._('Facet keys'),
1206
1243
  },
1207
1244
  ],
@@ -1216,14 +1253,7 @@ U.Map = L.Map.extend({
1216
1253
  ],
1217
1254
  ]
1218
1255
 
1219
- builder = new U.FormBuilder(this, optionsFields, {
1220
- callback: function (e) {
1221
- this.initCaptionBar()
1222
- if (e.helper.field === 'options.sortKey') {
1223
- this.eachDataLayer((datalayer) => datalayer.reindex())
1224
- }
1225
- },
1226
- })
1256
+ builder = new U.FormBuilder(this, optionsFields)
1227
1257
  const defaultProperties = L.DomUtil.createFieldset(
1228
1258
  container,
1229
1259
  L._('Default properties')
@@ -1241,20 +1271,7 @@ U.Map = L.Map.extend({
1241
1271
  'options.labelInteractive',
1242
1272
  'options.outlinkTarget',
1243
1273
  ]
1244
- builder = new U.FormBuilder(this, popupFields, {
1245
- callback: function (e) {
1246
- if (
1247
- e.helper.field === 'options.popupTemplate' ||
1248
- e.helper.field === 'options.popupContentTemplate' ||
1249
- e.helper.field === 'options.popupShape' ||
1250
- e.helper.field === 'options.outlinkTarget'
1251
- )
1252
- return
1253
- this.eachVisibleDataLayer((datalayer) => {
1254
- datalayer.redraw()
1255
- })
1256
- },
1257
- })
1274
+ builder = new U.FormBuilder(this, popupFields)
1258
1275
  const popupFieldset = L.DomUtil.createFieldset(
1259
1276
  container,
1260
1277
  L._('Default interaction options')
@@ -1263,7 +1280,7 @@ U.Map = L.Map.extend({
1263
1280
  },
1264
1281
 
1265
1282
  _editTilelayer: function (container) {
1266
- if (!L.Util.isObject(this.options.tilelayer)) {
1283
+ if (!U.Utils.isObject(this.options.tilelayer)) {
1267
1284
  this.options.tilelayer = {}
1268
1285
  }
1269
1286
  const tilelayerFields = [
@@ -1308,15 +1325,12 @@ U.Map = L.Map.extend({
1308
1325
  container,
1309
1326
  L._('Custom background')
1310
1327
  )
1311
- builder = new U.FormBuilder(this, tilelayerFields, {
1312
- callback: this.initTileLayers,
1313
- callbackContext: this,
1314
- })
1328
+ builder = new U.FormBuilder(this, tilelayerFields)
1315
1329
  customTilelayer.appendChild(builder.build())
1316
1330
  },
1317
1331
 
1318
1332
  _editOverlay: function (container) {
1319
- if (!L.Util.isObject(this.options.overlay)) {
1333
+ if (!U.Utils.isObject(this.options.overlay)) {
1320
1334
  this.options.overlay = {}
1321
1335
  }
1322
1336
  const overlayFields = [
@@ -1359,15 +1373,12 @@ U.Map = L.Map.extend({
1359
1373
  ['options.overlay.tms', { handler: 'Switch', label: L._('TMS format') }],
1360
1374
  ]
1361
1375
  const overlay = L.DomUtil.createFieldset(container, L._('Custom overlay'))
1362
- builder = new U.FormBuilder(this, overlayFields, {
1363
- callback: this.initTileLayers,
1364
- callbackContext: this,
1365
- })
1376
+ builder = new U.FormBuilder(this, overlayFields)
1366
1377
  overlay.appendChild(builder.build())
1367
1378
  },
1368
1379
 
1369
1380
  _editBounds: function (container) {
1370
- if (!L.Util.isObject(this.options.limitBounds)) {
1381
+ if (!U.Utils.isObject(this.options.limitBounds)) {
1371
1382
  this.options.limitBounds = {}
1372
1383
  }
1373
1384
  const limitBounds = L.DomUtil.createFieldset(container, L._('Limit bounds'))
@@ -1389,10 +1400,7 @@ U.Map = L.Map.extend({
1389
1400
  { handler: 'BlurFloatInput', placeholder: L._('max East') },
1390
1401
  ],
1391
1402
  ]
1392
- const boundsBuilder = new U.FormBuilder(this, boundsFields, {
1393
- callback: this.handleLimitBounds,
1394
- callbackContext: this,
1395
- })
1403
+ const boundsBuilder = new U.FormBuilder(this, boundsFields)
1396
1404
  limitBounds.appendChild(boundsBuilder.build())
1397
1405
  const boundsButtons = L.DomUtil.create('div', 'button-bar half', limitBounds)
1398
1406
  L.DomUtil.createButton(
@@ -1453,7 +1461,6 @@ U.Map = L.Map.extend({
1453
1461
  ]
1454
1462
  const slideshowHandler = function () {
1455
1463
  this.slideshow.setOptions(this.options.slideshow)
1456
- this.renderControls()
1457
1464
  }
1458
1465
  const slideshowBuilder = new U.FormBuilder(this, slideshowFields, {
1459
1466
  callback: slideshowHandler,
@@ -1462,22 +1469,6 @@ U.Map = L.Map.extend({
1462
1469
  slideshow.appendChild(slideshowBuilder.build())
1463
1470
  },
1464
1471
 
1465
- _editCredits: function (container) {
1466
- const credits = L.DomUtil.createFieldset(container, L._('Credits'))
1467
- const creditsFields = [
1468
- 'options.licence',
1469
- 'options.shortCredit',
1470
- 'options.longCredit',
1471
- 'options.permanentCredit',
1472
- 'options.permanentCreditBackground',
1473
- ]
1474
- const creditsBuilder = new U.FormBuilder(this, creditsFields, {
1475
- callback: this.renderControls,
1476
- callbackContext: this,
1477
- })
1478
- credits.appendChild(creditsBuilder.build())
1479
- },
1480
-
1481
1472
  _advancedActions: function (container) {
1482
1473
  const advancedActions = L.DomUtil.createFieldset(container, L._('Advanced actions'))
1483
1474
  const advancedButtons = L.DomUtil.create('div', 'button-bar half', advancedActions)
@@ -1520,18 +1511,37 @@ U.Map = L.Map.extend({
1520
1511
  )
1521
1512
  },
1522
1513
 
1523
- edit: function () {
1514
+ editCaption: function () {
1524
1515
  if (!this.editEnabled) return
1525
1516
  if (this.options.editMode !== 'advanced') return
1526
1517
  const container = L.DomUtil.create('div', 'umap-edit-container'),
1527
1518
  metadataFields = ['options.name', 'options.description'],
1528
1519
  title = L.DomUtil.create('h3', '', container)
1529
- title.textContent = L._('Edit map properties')
1520
+ title.textContent = L._('Edit map details')
1530
1521
  const builder = new U.FormBuilder(this, metadataFields, {
1531
1522
  className: 'map-metadata',
1532
1523
  })
1533
1524
  const form = builder.build()
1534
1525
  container.appendChild(form)
1526
+
1527
+ const credits = L.DomUtil.createFieldset(container, L._('Credits'))
1528
+ const creditsFields = [
1529
+ 'options.licence',
1530
+ 'options.shortCredit',
1531
+ 'options.longCredit',
1532
+ 'options.permanentCredit',
1533
+ 'options.permanentCreditBackground',
1534
+ ]
1535
+ const creditsBuilder = new U.FormBuilder(this, creditsFields)
1536
+ credits.appendChild(creditsBuilder.build())
1537
+ this.editPanel.open({ content: container })
1538
+ },
1539
+
1540
+ edit: function () {
1541
+ if (!this.editEnabled) return
1542
+ if (this.options.editMode !== 'advanced') return
1543
+ const container = L.DomUtil.create('div')
1544
+ L.DomUtil.createTitle(container, L._('Map advanced properties'), 'icon-settings')
1535
1545
  this._editControls(container)
1536
1546
  this._editShapeProperties(container)
1537
1547
  this._editDefaultProperties(container)
@@ -1540,10 +1550,9 @@ U.Map = L.Map.extend({
1540
1550
  this._editOverlay(container)
1541
1551
  this._editBounds(container)
1542
1552
  this._editSlideshow(container)
1543
- this._editCredits(container)
1544
1553
  this._advancedActions(container)
1545
1554
 
1546
- this.ui.openPanel({ data: { html: container }, className: 'dark' })
1555
+ this.editPanel.open({ content: container, className: 'dark' })
1547
1556
  },
1548
1557
 
1549
1558
  enableEdit: function () {
@@ -1560,6 +1569,8 @@ U.Map = L.Map.extend({
1560
1569
  this.editedFeature = null
1561
1570
  this.editEnabled = false
1562
1571
  this.fire('edit:disabled')
1572
+ this.editPanel.close()
1573
+ this.fullPanel.close()
1563
1574
  },
1564
1575
 
1565
1576
  hasEditMode: function () {
@@ -1591,8 +1602,7 @@ U.Map = L.Map.extend({
1591
1602
  'umap-open-browser-link flat',
1592
1603
  container,
1593
1604
  L._('Browse data'),
1594
- this.openBrowser,
1595
- this
1605
+ () => this.openBrowser('expanded')
1596
1606
  )
1597
1607
  if (this.options.facetKey) {
1598
1608
  L.DomUtil.createButton(
@@ -1617,8 +1627,7 @@ U.Map = L.Map.extend({
1617
1627
  askForReset: function (e) {
1618
1628
  if (!confirm(L._('Are you sure you want to cancel your changes?'))) return
1619
1629
  this.reset()
1620
- this.disableEdit(e)
1621
- this.ui.closePanel()
1630
+ this.disableEdit()
1622
1631
  },
1623
1632
 
1624
1633
  startMarker: function () {
@@ -1734,10 +1743,17 @@ U.Map = L.Map.extend({
1734
1743
  })
1735
1744
  }
1736
1745
  }
1737
- items.push('-', {
1738
- text: L._('Browse data'),
1739
- callback: this.openBrowser,
1740
- })
1746
+ items.push(
1747
+ '-',
1748
+ {
1749
+ text: L._('See layers'),
1750
+ callback: () => this.openBrowser('condensed'),
1751
+ },
1752
+ {
1753
+ text: L._('Browse data'),
1754
+ callback: () => this.openBrowser('expanded'),
1755
+ }
1756
+ )
1741
1757
  if (this.options.facetKey) {
1742
1758
  items.push({
1743
1759
  text: L._('Facet search'),
@@ -1760,14 +1776,29 @@ U.Map = L.Map.extend({
1760
1776
  callback: this.openExternalRouting,
1761
1777
  })
1762
1778
  }
1779
+ if (this.options.urls.edit_in_osm) {
1780
+ items.push('-', {
1781
+ text: L._('Edit in OpenStreetMap'),
1782
+ callback: this.editInOSM,
1783
+ })
1784
+ }
1763
1785
  this.options.contextmenuItems = items
1764
1786
  },
1765
1787
 
1788
+ editInOSM: function (e) {
1789
+ const url = this.urls.get('edit_in_osm', {
1790
+ lat: e.latlng.lat,
1791
+ lng: e.latlng.lng,
1792
+ zoom: Math.max(this.getZoom(), 16),
1793
+ })
1794
+ if (url) window.open(url)
1795
+ },
1796
+
1766
1797
  openExternalRouting: function (e) {
1767
1798
  const url = this.urls.get('routing', {
1768
1799
  lat: e.latlng.lat,
1769
1800
  lng: e.latlng.lng,
1770
- locale: L.locale,
1801
+ locale: L.getLocale(),
1771
1802
  zoom: this.getZoom(),
1772
1803
  })
1773
1804
  if (url) window.open(url)
@@ -1796,12 +1827,12 @@ U.Map = L.Map.extend({
1796
1827
  },
1797
1828
 
1798
1829
  localizeUrl: function (url) {
1799
- return L.Util.greedyTemplate(url, this.getGeoContext(), true)
1830
+ return U.Utils.greedyTemplate(url, this.getGeoContext(), true)
1800
1831
  },
1801
1832
 
1802
1833
  proxyUrl: function (url, ttl) {
1803
1834
  if (this.options.urls.ajax_proxy) {
1804
- url = L.Util.greedyTemplate(this.options.urls.ajax_proxy, {
1835
+ url = U.Utils.greedyTemplate(this.options.urls.ajax_proxy, {
1805
1836
  url: encodeURIComponent(url),
1806
1837
  ttl: ttl,
1807
1838
  })
@@ -1815,21 +1846,13 @@ U.Map = L.Map.extend({
1815
1846
  },
1816
1847
 
1817
1848
  search: function () {
1818
- if (this._controls.search) this._controls.search.openPanel(this)
1849
+ if (this._controls.search) this._controls.search.open()
1819
1850
  },
1820
1851
 
1821
1852
  getFilterKeys: function () {
1822
1853
  return (this.options.filterKey || this.options.sortKey || 'name').split(',')
1823
1854
  },
1824
1855
 
1825
- getFacetKeys: function () {
1826
- return (this.options.facetKey || '').split(',').reduce((acc, curr) => {
1827
- const els = curr.split('|')
1828
- acc[els[0]] = els[1] || els[0]
1829
- return acc
1830
- }, {})
1831
- },
1832
-
1833
1856
  getLayersBounds: function () {
1834
1857
  const bounds = new L.latLngBounds()
1835
1858
  this.eachBrowsableDataLayer((d) => {