umap-project 2.1.2__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 (211) 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 +10 -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 +248 -361
  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 +65 -43
  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 -58
  43. umap/static/umap/js/umap.js +206 -192
  44. umap/static/umap/js/umap.layer.js +86 -46
  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 +1 -1
  151. umap/static/umap/test/Feature.js +0 -226
  152. umap/static/umap/test/Map.js +0 -304
  153. umap/static/umap/test/Polygon.js +0 -256
  154. umap/static/umap/test/Polyline.js +0 -116
  155. umap/static/umap/test/TableEditor.js +10 -10
  156. umap/static/umap/test/Util.js +0 -521
  157. umap/static/umap/test/index.html +1 -5
  158. umap/static/umap/unittests/URLs.js +1 -1
  159. umap/static/umap/unittests/utils.js +610 -0
  160. umap/static/umap/vars.css +9 -0
  161. umap/static/umap/vendors/dompurify/purify.es.mjs +1525 -0
  162. umap/static/umap/vendors/formbuilder/Leaflet.FormBuilder.js +1 -0
  163. umap/static/umap/vendors/iconlayers/iconLayers.js +1 -1
  164. umap/templates/umap/css.html +2 -0
  165. umap/templates/umap/js.html +0 -1
  166. umap/templates/umap/map_detail.html +4 -0
  167. umap/templates/umap/map_table.html +12 -10
  168. umap/templatetags/umap_tags.py +5 -0
  169. umap/tests/conftest.py +9 -0
  170. umap/tests/fixtures/test_upload_data.csv +2 -1
  171. umap/tests/fixtures/test_upload_data.umap +171 -0
  172. umap/tests/fixtures/test_upload_data_osm.json +33 -0
  173. umap/tests/integration/conftest.py +16 -0
  174. umap/tests/integration/test_anonymous_owned_map.py +30 -5
  175. umap/tests/integration/test_basics.py +21 -0
  176. umap/tests/integration/test_browser.py +16 -36
  177. umap/tests/integration/test_choropleth.py +89 -0
  178. umap/tests/integration/test_collaborative_editing.py +30 -1
  179. umap/tests/integration/test_dashboard.py +10 -0
  180. umap/tests/integration/test_datalayer.py +132 -0
  181. umap/tests/integration/test_draw_polygon.py +363 -0
  182. umap/tests/integration/test_draw_polyline.py +325 -0
  183. umap/tests/integration/test_edit_datalayer.py +145 -6
  184. umap/tests/integration/test_edit_map.py +202 -0
  185. umap/tests/integration/test_edit_marker.py +120 -0
  186. umap/tests/integration/test_edit_polygon.py +122 -0
  187. umap/tests/integration/test_facets_browser.py +132 -11
  188. umap/tests/integration/test_import.py +407 -10
  189. umap/tests/integration/test_map.py +36 -54
  190. umap/tests/integration/test_map_list.py +28 -0
  191. umap/tests/integration/test_owned_map.py +24 -6
  192. umap/tests/integration/test_picto.py +25 -38
  193. umap/tests/integration/test_querystring.py +9 -15
  194. umap/tests/integration/test_slideshow.py +0 -5
  195. umap/tests/integration/test_statics.py +3 -2
  196. umap/tests/integration/test_tableeditor.py +23 -0
  197. umap/tests/integration/test_tilelayer.py +10 -0
  198. umap/tests/integration/test_view_marker.py +64 -0
  199. umap/tests/integration/test_view_polygon.py +59 -0
  200. umap/tests/integration/test_view_polyline.py +51 -0
  201. umap/tests/test_map_views.py +13 -0
  202. {umap_project-2.1.2.dist-info → umap_project-2.2.0.dist-info}/METADATA +12 -12
  203. {umap_project-2.1.2.dist-info → umap_project-2.2.0.dist-info}/RECORD +206 -187
  204. {umap_project-2.1.2.dist-info → umap_project-2.2.0.dist-info}/WHEEL +1 -1
  205. umap/static/umap/test/Choropleth.js +0 -245
  206. umap/static/umap/test/Permissions.js +0 -74
  207. umap/static/umap/vendors/dompurify/purify.min.js +0 -3
  208. umap/static/umap/vendors/dompurify/purify.min.js.map +0 -1
  209. umap/tests/integration/test_drawing.py +0 -243
  210. {umap_project-2.1.2.dist-info → umap_project-2.2.0.dist-info}/entry_points.txt +0 -0
  211. {umap_project-2.1.2.dist-info → umap_project-2.2.0.dist-info}/licenses/LICENSE +0 -0
@@ -59,15 +59,14 @@ U.FeatureMixin = {
59
59
  getPermalink: function () {
60
60
  const slug = this.getSlug()
61
61
  if (slug)
62
- return `${L.Util.getBaseUrl()}?${L.Util.buildQueryString({ feature: slug })}${
62
+ return `${U.Utils.getBaseUrl()}?${U.Utils.buildQueryString({ feature: slug })}${
63
63
  window.location.hash
64
64
  }`
65
65
  },
66
66
 
67
67
  view: function (e) {
68
- if (this.map.editEnabled) return
69
- const outlink = this.getOption('outlink'),
70
- target = this.getOption('outlinkTarget')
68
+ const outlink = this.getOption('outlink')
69
+ const target = this.getOption('outlinkTarget')
71
70
  if (outlink) {
72
71
  switch (target) {
73
72
  case 'self':
@@ -77,31 +76,38 @@ U.FeatureMixin = {
77
76
  window.top.location = outlink
78
77
  break
79
78
  default:
80
- const win = window.open(this.properties._umap_options.outlink)
79
+ window.open(this.properties._umap_options.outlink)
81
80
  }
82
81
  return
83
82
  }
84
83
  // TODO deal with an event instead?
85
- if (this.map.slideshow) this.map.slideshow.current = this
84
+ if (this.map.slideshow) {
85
+ this.map.slideshow.current = this
86
+ }
86
87
  this.map.currentFeature = this
87
88
  this.attachPopup()
88
- this.openPopup((e && e.latlng) || this.getCenter())
89
+ this.openPopup(e?.latlng || this.getCenter())
90
+ },
91
+
92
+ render: function (fields) {
93
+ const impactData = fields.some((field) => {
94
+ return field.startsWith('properties.')
95
+ })
96
+ if (impactData) {
97
+ if (this.map.currentFeature === this) {
98
+ this.view()
99
+ }
100
+ }
89
101
  },
90
102
 
91
103
  openPopup: function () {
92
- if (this.map.editEnabled) return
93
104
  this.parentClass.prototype.openPopup.apply(this, arguments)
94
105
  },
95
106
 
96
107
  edit: function (e) {
97
108
  if (!this.map.editEnabled || this.isReadOnly()) return
98
109
  const container = L.DomUtil.create('div', 'umap-feature-container')
99
- L.DomUtil.add(
100
- 'h3',
101
- `umap-feature-properties ${this.getClassName()}`,
102
- container,
103
- L._('Feature properties')
104
- )
110
+ L.DomUtil.createTitle(container, L._('Feature properties'), this.getClassName())
105
111
 
106
112
  let builder = new U.FormBuilder(
107
113
  this,
@@ -131,13 +137,13 @@ U.FeatureMixin = {
131
137
  callback: this._redraw, // In case we have dynamic options…
132
138
  })
133
139
  container.appendChild(builder.build())
134
- this.map.ui.once('panel:ready', () => {
135
- builder.helpers['properties.name'].input.focus()
136
- })
137
140
  this.appendEditFieldsets(container)
138
141
  const advancedActions = L.DomUtil.createFieldset(container, L._('Advanced actions'))
139
142
  this.getAdvancedEditActions(advancedActions)
140
- this.map.ui.openPanel({ data: { html: container }, className: 'dark' })
143
+ const onLoad = this.map.editPanel.open({ content: container })
144
+ onLoad.then(() => {
145
+ builder.helpers['properties.name'].input.focus()
146
+ })
141
147
  this.map.editedFeature = this
142
148
  if (!this.isOnScreen()) this.zoomTo(e)
143
149
  },
@@ -149,7 +155,7 @@ U.FeatureMixin = {
149
155
  L._('Delete'),
150
156
  function (e) {
151
157
  L.DomEvent.stop(e)
152
- if (this.confirmDelete()) this.map.ui.closePanel()
158
+ if (this.confirmDelete()) this.map.editPanel.close()
153
159
  },
154
160
  this
155
161
  )
@@ -204,7 +210,8 @@ U.FeatureMixin = {
204
210
  if (fallback === undefined) fallback = this.datalayer.options.name
205
211
  const key = this.getOption('labelKey') || 'name'
206
212
  // Variables mode.
207
- if (L.Util.hasVar(key)) return L.Util.greedyTemplate(key, this.extendedProperties())
213
+ if (U.Utils.hasVar(key))
214
+ return U.Utils.greedyTemplate(key, this.extendedProperties())
208
215
  // Simple mode.
209
216
  return this.properties[key] || this.properties.title || fallback
210
217
  },
@@ -291,7 +298,7 @@ U.FeatureMixin = {
291
298
  let value = fallback
292
299
  if (typeof this.staticOptions[option] !== 'undefined') {
293
300
  value = this.staticOptions[option]
294
- } else if (L.Util.usableOption(this.properties._umap_options, option)) {
301
+ } else if (U.Utils.usableOption(this.properties._umap_options, option)) {
295
302
  value = this.properties._umap_options[option]
296
303
  } else if (this.datalayer) {
297
304
  value = this.datalayer.getOption(option, this)
@@ -304,23 +311,22 @@ U.FeatureMixin = {
304
311
  getDynamicOption: function (option, fallback) {
305
312
  let value = this.getOption(option, fallback)
306
313
  // There is a variable inside.
307
- if (L.Util.hasVar(value)) {
308
- value = L.Util.greedyTemplate(value, this.properties, true)
309
- if (L.Util.hasVar(value)) value = this.map.getDefaultOption(option)
314
+ if (U.Utils.hasVar(value)) {
315
+ value = U.Utils.greedyTemplate(value, this.properties, true)
316
+ if (U.Utils.hasVar(value)) value = this.map.getDefaultOption(option)
310
317
  }
311
318
  return value
312
319
  },
313
320
 
314
- zoomTo: function (e) {
315
- e = e || {}
316
- const easing = e.easing !== undefined ? e.easing : this.map.getOption('easing')
321
+ zoomTo: function ({ easing, latlng, callback } = {}) {
322
+ if (easing === undefined) easing = this.map.getOption('easing')
323
+ if (callback) this.map.once('moveend', callback.call(this))
317
324
  if (easing) {
318
325
  this.map.flyTo(this.getCenter(), this.getBestZoom())
319
326
  } else {
320
- const latlng = e.latlng || this.getCenter()
327
+ latlng = latlng || this.getCenter()
321
328
  this.map.setView(latlng, this.getBestZoom() || this.map.getZoom())
322
329
  }
323
- if (e.callback) e.callback.call(this)
324
330
  },
325
331
 
326
332
  getBestZoom: function () {
@@ -442,7 +448,7 @@ U.FeatureMixin = {
442
448
  }
443
449
  items = items.concat(
444
450
  {
445
- text: L._("Edit feature's layer") + ' (Ctrl+⇧+Click)',
451
+ text: this.map.help.displayLabel('EDIT_FEATURE_LAYER'),
446
452
  callback: this.datalayer.edit,
447
453
  context: this.datalayer,
448
454
  iconCls: 'umap-edit',
@@ -466,7 +472,7 @@ U.FeatureMixin = {
466
472
  this.parentClass.prototype.onRemove.call(this, map)
467
473
  if (this.map.editedFeature === this) {
468
474
  this.endEdit()
469
- this.map.ui.closePanel()
475
+ this.map.editPanel.close()
470
476
  }
471
477
  },
472
478
 
@@ -485,7 +491,7 @@ U.FeatureMixin = {
485
491
  options.permanent = showLabel === true
486
492
  this.unbindTooltip()
487
493
  if ((showLabel === true || showLabel === null) && displayName)
488
- this.bindTooltip(L.Util.escapeHTML(displayName), options)
494
+ this.bindTooltip(U.Utils.escapeHTML(displayName), options)
489
495
  },
490
496
 
491
497
  matchFilter: function (filter, keys) {
@@ -498,11 +504,24 @@ U.FeatureMixin = {
498
504
  },
499
505
 
500
506
  matchFacets: function () {
501
- const facets = this.map.facets
502
- for (const [property, expected] of Object.entries(facets)) {
503
- if (expected.length) {
504
- let value = this.properties[property]
505
- if (!value || !expected.includes(value)) return false
507
+ const selected = this.map.facets.selected
508
+ for (let [name, { type, min, max, choices }] of Object.entries(selected)) {
509
+ let value = this.properties[name]
510
+ let parser = this.map.facets.getParser(type)
511
+ value = parser(value)
512
+ switch (type) {
513
+ case 'date':
514
+ case 'datetime':
515
+ case 'number':
516
+ min = parser(min)
517
+ max = parser(max)
518
+ if (!isNaN(min) && !isNaN(value) && min > value) return false
519
+ if (!isNaN(max) && !isNaN(value) && max < value) return false
520
+ break
521
+ default:
522
+ value = value || L._('<empty value>')
523
+ if (choices?.length && !choices.includes(value)) return false
524
+ break
506
525
  }
507
526
  }
508
527
  return true
@@ -533,7 +552,8 @@ U.FeatureMixin = {
533
552
  extendedProperties: function () {
534
553
  // Include context properties
535
554
  properties = this.map.getGeoContext()
536
- if (L.locale) properties.locale = L.locale
555
+ const locale = L.getLocale()
556
+ if (locale) properties.locale = locale
537
557
  if (L.lang) properties.lang = L.lang
538
558
  properties.rank = this.getRank() + 1
539
559
  if (this.hasGeom()) {
@@ -757,7 +777,7 @@ U.PathMixin = {
757
777
  if (this.map.editEnabled) {
758
778
  if (this.editEnabled()) {
759
779
  this.endEdit()
760
- this.map.ui.closePanel()
780
+ this.map.editPanel.close()
761
781
  } else {
762
782
  this.edit(e)
763
783
  }
@@ -888,7 +908,9 @@ U.PathMixin = {
888
908
  const other = new (this instanceof U.Polyline ? U.Polyline : U.Polygon)(
889
909
  this.map,
890
910
  shape,
891
- { geojson: { properties: properties } }
911
+ {
912
+ geojson: { properties },
913
+ }
892
914
  )
893
915
  this.datalayer.addLayer(other)
894
916
  other.edit()
@@ -1033,7 +1055,7 @@ U.Polyline = L.Polyline.extend({
1033
1055
  })
1034
1056
  } else if (index === 0 || index === e.vertex.getLastIndex()) {
1035
1057
  items.push({
1036
- text: L._('Continue line (Ctrl+Click)'),
1058
+ text: this.map.help.displayLabel('CONTINUE_LINE'),
1037
1059
  callback: e.vertex.continue,
1038
1060
  context: e.vertex.continue,
1039
1061
  })
@@ -1056,7 +1078,7 @@ U.Polyline = L.Polyline.extend({
1056
1078
  const geojson = this.toGeoJSON()
1057
1079
  geojson.geometry.type = 'Polygon'
1058
1080
  geojson.geometry.coordinates = [
1059
- L.Util.flattenCoordinates(geojson.geometry.coordinates),
1081
+ U.Utils.flattenCoordinates(geojson.geometry.coordinates),
1060
1082
  ]
1061
1083
  const polygon = this.datalayer.geojsonToFeatures(geojson)
1062
1084
  polygon.edit()
@@ -1196,7 +1218,7 @@ U.Polygon = L.Polygon.extend({
1196
1218
  toPolyline: function () {
1197
1219
  const geojson = this.toGeoJSON()
1198
1220
  geojson.geometry.type = 'LineString'
1199
- geojson.geometry.coordinates = L.Util.flattenCoordinates(
1221
+ geojson.geometry.coordinates = U.Utils.flattenCoordinates(
1200
1222
  geojson.geometry.coordinates
1201
1223
  )
1202
1224
  const polyline = this.datalayer.geojsonToFeatures(geojson)
@@ -475,7 +475,7 @@ L.FormBuilder.IconUrl = L.FormBuilder.BlurInput.extend({
475
475
  build: function () {
476
476
  L.FormBuilder.BlurInput.prototype.build.call(this)
477
477
  this.buttons = L.DomUtil.create('div', '', this.parentNode)
478
- this.tabs = L.DomUtil.create('div', 'pictogram-tabs', this.parentNode)
478
+ this.tabs = L.DomUtil.create('div', 'flat-tabs', this.parentNode)
479
479
  this.body = L.DomUtil.create('div', 'umap-pictogram-body', this.parentNode)
480
480
  this.footer = L.DomUtil.create('div', '', this.parentNode)
481
481
  this.updatePreview()
@@ -492,8 +492,8 @@ L.FormBuilder.IconUrl = L.FormBuilder.BlurInput.extend({
492
492
  this.buildTabs()
493
493
  const value = this.value()
494
494
  if (U.Icon.RECENT.length) this.showRecentTab()
495
- else if (!value || L.Util.isPath(value)) this.showSymbolsTab()
496
- else if (L.Util.isRemoteUrl(value) || L.Util.isDataImage(value)) this.showURLTab()
495
+ else if (!value || U.Utils.isPath(value)) this.showSymbolsTab()
496
+ else if (U.Utils.isRemoteUrl(value) || U.Utils.isDataImage(value)) this.showURLTab()
497
497
  else this.showCharsTab()
498
498
  const closeButton = L.DomUtil.createButton(
499
499
  'button action-button',
@@ -567,7 +567,7 @@ L.FormBuilder.IconUrl = L.FormBuilder.BlurInput.extend({
567
567
  updatePreview: function () {
568
568
  this.buttons.innerHTML = ''
569
569
  if (this.isDefault()) return
570
- if (!L.Util.hasVar(this.value())) {
570
+ if (!U.Utils.hasVar(this.value())) {
571
571
  // Do not try to render URL with variables
572
572
  const box = L.DomUtil.create('div', 'umap-pictogram-choice', this.buttons)
573
573
  L.DomEvent.on(box, 'click', this.onDefine, this)
@@ -585,11 +585,11 @@ L.FormBuilder.IconUrl = L.FormBuilder.BlurInput.extend({
585
585
  addIconPreview: function (pictogram, parent) {
586
586
  const baseClass = 'umap-pictogram-choice',
587
587
  value = pictogram.src,
588
- search = L.Util.normalize(this.searchInput.value),
588
+ search = U.Utils.normalize(this.searchInput.value),
589
589
  title = pictogram.attribution
590
590
  ? `${pictogram.name} — © ${pictogram.attribution}`
591
591
  : pictogram.name || pictogram.src
592
- if (search && L.Util.normalize(title).indexOf(search) === -1) return
592
+ if (search && U.Utils.normalize(title).indexOf(search) === -1) return
593
593
  const className = value === this.value() ? `${baseClass} selected` : baseClass,
594
594
  container = L.DomUtil.create('div', className, parent)
595
595
  U.Icon.makeIconElement(value, container)
@@ -637,7 +637,7 @@ L.FormBuilder.IconUrl = L.FormBuilder.BlurInput.extend({
637
637
  categories[category].push(props)
638
638
  }
639
639
  const sorted = Object.entries(categories).toSorted(([a], [b]) =>
640
- L.Util.naturalSort(a, b)
640
+ U.Utils.naturalSort(a, b, L.lang)
641
641
  )
642
642
  for (let [name, items] of sorted) {
643
643
  this.addCategory(items, name)
@@ -688,7 +688,7 @@ L.FormBuilder.IconUrl = L.FormBuilder.BlurInput.extend({
688
688
  showURLTab: function () {
689
689
  this.openTab('url')
690
690
  const value =
691
- L.Util.isRemoteUrl(this.value()) || L.Util.isDataImage(this.value())
691
+ U.Utils.isRemoteUrl(this.value()) || U.Utils.isDataImage(this.value())
692
692
  ? this.value()
693
693
  : null
694
694
  const input = this.buildInput(this.body, value)
@@ -744,34 +744,127 @@ L.FormBuilder.Switch = L.FormBuilder.CheckBox.extend({
744
744
  },
745
745
  })
746
746
 
747
- L.FormBuilder.FacetSearch = L.FormBuilder.Element.extend({
747
+ L.FormBuilder.FacetSearchChoices = L.FormBuilder.Element.extend({
748
748
  build: function () {
749
- this.container = L.DomUtil.create('div', 'umap-facet', this.parentNode)
749
+ this.container = L.DomUtil.create('fieldset', 'umap-facet', this.parentNode)
750
+ this.container.appendChild(this.label)
750
751
  this.ul = L.DomUtil.create('ul', '', this.container)
751
- const choices = this.options.choices
752
+ this.type = this.options.criteria['type']
753
+
754
+ const choices = this.options.criteria['choices']
752
755
  choices.sort()
753
756
  choices.forEach((value) => this.buildLi(value))
754
757
  },
755
758
 
756
759
  buildLabel: function () {
757
- this.label = L.DomUtil.add('h5', '', this.parentNode, this.options.label)
760
+ this.label = L.DomUtil.element({
761
+ tagName: 'legend',
762
+ textContent: this.options.label,
763
+ })
758
764
  },
759
765
 
760
766
  buildLi: function (value) {
761
- const property_li = L.DomUtil.create('li', '', this.ul),
762
- input = L.DomUtil.create('input', '', property_li),
763
- label = L.DomUtil.create('label', '', property_li)
764
- input.type = 'checkbox'
765
- input.id = `checkbox_${this.name}_${value}`
766
- input.checked = this.get().includes(value)
767
+ const property_li = L.DomUtil.create('li', '', this.ul)
768
+ const label = L.DomUtil.create('label', '', property_li)
769
+ const input = L.DomUtil.create('input', '', label)
770
+ L.DomUtil.add('span', '', label, value)
771
+
772
+ input.type = this.type
773
+ input.name = `${this.type}_${this.name}`
774
+ input.checked = this.get()['choices'].includes(value)
767
775
  input.dataset.value = value
768
- label.htmlFor = `checkbox_${this.name}_${value}`
769
- label.innerHTML = value
776
+
770
777
  L.DomEvent.on(input, 'change', (e) => this.sync())
771
778
  },
772
779
 
773
780
  toJS: function () {
774
- return [...this.ul.querySelectorAll('input:checked')].map((i) => i.dataset.value)
781
+ return {
782
+ type: this.type,
783
+ choices: [...this.ul.querySelectorAll('input:checked')].map(
784
+ (i) => i.dataset.value
785
+ ),
786
+ }
787
+ },
788
+ })
789
+
790
+ L.FormBuilder.MinMaxBase = L.FormBuilder.Element.extend({
791
+ getInputType: function (type) {
792
+ return type
793
+ },
794
+
795
+ getLabels: function () {
796
+ return [L._('Min'), L._('Max')]
797
+ },
798
+
799
+ castValue: function (value) {
800
+ return value.valueOf()
801
+ },
802
+
803
+ build: function () {
804
+ this.container = L.DomUtil.create('fieldset', 'umap-facet', this.parentNode)
805
+ this.container.appendChild(this.label)
806
+ const { min, max, type } = this.options.criteria
807
+ this.type = type
808
+ this.inputType = this.getInputType(this.type)
809
+
810
+ const [minLabel, maxLabel] = this.getLabels()
811
+
812
+ this.minLabel = L.DomUtil.create('label', '', this.container)
813
+ this.minLabel.textContent = minLabel
814
+
815
+ this.minInput = L.DomUtil.create('input', '', this.minLabel)
816
+ this.minInput.type = this.inputType
817
+ this.minInput.step = 'any'
818
+ if (min != null) {
819
+ this.minInput.valueAsNumber = this.castValue(min)
820
+ this.minInput.dataset.value = min
821
+ }
822
+
823
+ this.maxLabel = L.DomUtil.create('label', '', this.container)
824
+ this.maxLabel.textContent = maxLabel
825
+
826
+ this.maxInput = L.DomUtil.create('input', '', this.maxLabel)
827
+ this.maxInput.type = this.inputType
828
+ this.maxInput.step = 'any'
829
+ if (max != null) {
830
+ this.maxInput.valueAsNumber = this.castValue(max)
831
+ this.maxInput.dataset.value = max
832
+ }
833
+
834
+ L.DomEvent.on(this.minInput, 'change', (e) => this.sync())
835
+ L.DomEvent.on(this.maxInput, 'change', (e) => this.sync())
836
+ },
837
+
838
+ buildLabel: function () {
839
+ this.label = L.DomUtil.element({
840
+ tagName: 'legend',
841
+ textContent: this.options.label,
842
+ })
843
+ },
844
+
845
+ toJS: function () {
846
+ return {
847
+ type: this.type,
848
+ min: this.minInput.value,
849
+ max: this.maxInput.value,
850
+ }
851
+ },
852
+ })
853
+
854
+ L.FormBuilder.FacetSearchNumber = L.FormBuilder.MinMaxBase.extend({})
855
+
856
+ L.FormBuilder.FacetSearchDate = L.FormBuilder.MinMaxBase.extend({
857
+ castValue: function (value) {
858
+ return value.valueOf() - value.getTimezoneOffset() * 60000
859
+ },
860
+ getLabels: function () {
861
+ return [L._('From'), L._('Until')]
862
+ },
863
+ })
864
+
865
+ L.FormBuilder.FacetSearchDateTime = L.FormBuilder.FacetSearchDate.extend({
866
+ getInputType: function (type) {
867
+ return 'datetime-local'
775
868
  },
776
869
  })
777
870
 
@@ -886,22 +979,23 @@ L.FormBuilder.Range = L.FormBuilder.FloatInput.extend({
886
979
  },
887
980
 
888
981
  buildHelpText: function () {
889
- const datalist = L.DomUtil.create(
890
- 'datalist',
891
- 'umap-field-datalist',
892
- this.getHelpTextParent()
893
- )
894
- datalist.id = `range-${this.options.label || this.name}`
895
- this.input.setAttribute('list', datalist.id)
896
982
  let options = ''
897
- const step = this.options.step || 1,
898
- digits = step < 1 ? 1 : 0
983
+ const step = this.options.step || 1
984
+ const digits = step < 1 ? 1 : 0
985
+ const id = `range-${this.options.label || this.name}`
899
986
  for (let i = this.options.min; i <= this.options.max; i += this.options.step) {
900
987
  options += `<option value="${i.toFixed(digits)}" label="${i.toFixed(
901
988
  digits
902
989
  )}"></option>`
903
990
  }
904
- datalist.innerHTML = options
991
+ const datalist = L.DomUtil.element({
992
+ tagName: 'datalist',
993
+ parent: this.getHelpTextParent(),
994
+ className: 'umap-field-datalist',
995
+ safeHTML: options,
996
+ id: id,
997
+ })
998
+ this.input.setAttribute('list', id)
905
999
  L.FormBuilder.Input.prototype.buildHelpText.call(this)
906
1000
  },
907
1001
  })
@@ -1009,9 +1103,6 @@ U.FormBuilder = L.FormBuilder.extend({
1009
1103
  case 'iconUrl':
1010
1104
  schema.handler = 'IconUrl'
1011
1105
  break
1012
- case 'datalayersControl':
1013
- schema.handler = 'DataLayersControl'
1014
- break
1015
1106
  case 'licence':
1016
1107
  schema.handler = 'LicenceChooser'
1017
1108
  break
@@ -1032,10 +1123,11 @@ U.FormBuilder = L.FormBuilder.extend({
1032
1123
 
1033
1124
  setter: function (field, value) {
1034
1125
  L.FormBuilder.prototype.setter.call(this, field, value)
1035
- if (this.options.makeDirty !== false) this.obj.isDirty = true
1126
+ this.obj.isDirty = true
1127
+ if ('render' in this.obj) this.obj.render([field], this)
1036
1128
  },
1037
1129
 
1038
1130
  finish: function () {
1039
- this.map.ui.closePanel()
1131
+ this.map.editPanel.close()
1040
1132
  },
1041
1133
  })
@@ -18,7 +18,7 @@ U.Icon = L.DivIcon.extend({
18
18
  },
19
19
 
20
20
  _setRecent: function (url) {
21
- if (L.Util.hasVar(url)) return
21
+ if (U.Utils.hasVar(url)) return
22
22
  if (url === U.SCHEMA.iconUrl.default) return
23
23
  if (U.Icon.RECENT.indexOf(url) === -1) {
24
24
  U.Icon.RECENT.push(url)
@@ -50,7 +50,10 @@ U.Icon = L.DivIcon.extend({
50
50
  },
51
51
 
52
52
  formatUrl: function (url, feature) {
53
- return L.Util.greedyTemplate(url || '', feature ? feature.extendedProperties() : {})
53
+ return U.Utils.greedyTemplate(
54
+ url || '',
55
+ feature ? feature.extendedProperties() : {}
56
+ )
54
57
  },
55
58
 
56
59
  onAdd: function () {},
@@ -206,7 +209,7 @@ U.Icon.Cluster = L.DivIcon.extend({
206
209
  })
207
210
 
208
211
  U.Icon.isImg = function (src) {
209
- return L.Util.isPath(src) || L.Util.isRemoteUrl(src) || L.Util.isDataImage(src)
212
+ return U.Utils.isPath(src) || U.Utils.isRemoteUrl(src) || U.Utils.isDataImage(src)
210
213
  }
211
214
 
212
215
  U.Icon.makeIconElement = function (src, parent) {
@@ -236,7 +239,11 @@ U.Icon.setIconContrast = function (icon, parent, src, bgcolor) {
236
239
  if (L.DomUtil.contrastedColor(parent, bgcolor)) {
237
240
  // Decide whether to switch svg to white or not, but do it
238
241
  // only for internal SVG, as invert could do weird things
239
- if (L.Util.isPath(src) && src.endsWith('.svg') && src !== U.SCHEMA.iconUrl.default) {
242
+ if (
243
+ U.Utils.isPath(src) &&
244
+ src.endsWith('.svg') &&
245
+ src !== U.SCHEMA.iconUrl.default
246
+ ) {
240
247
  // Must be called after icon container is added to the DOM
241
248
  // An image
242
249
  icon.style.filter = 'invert(1)'