umap-project 3.4.0b3__py3-none-any.whl → 3.4.2__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.
Files changed (185) hide show
  1. umap/__init__.py +1 -1
  2. umap/locale/da/LC_MESSAGES/django.mo +0 -0
  3. umap/locale/da/LC_MESSAGES/django.po +18 -14
  4. umap/locale/en/LC_MESSAGES/django.po +5 -1
  5. umap/locale/es/LC_MESSAGES/django.mo +0 -0
  6. umap/locale/es/LC_MESSAGES/django.po +20 -16
  7. umap/locale/fr/LC_MESSAGES/django.mo +0 -0
  8. umap/locale/fr/LC_MESSAGES/django.po +18 -14
  9. umap/locale/pl/LC_MESSAGES/django.mo +0 -0
  10. umap/locale/pl/LC_MESSAGES/django.po +72 -71
  11. umap/static/umap/content.css +0 -3
  12. umap/static/umap/css/bar.css +9 -6
  13. umap/static/umap/css/form.css +25 -9
  14. umap/static/umap/css/popup.css +1 -0
  15. umap/static/umap/js/components/copiable.js +47 -0
  16. umap/static/umap/js/modules/autocomplete.js +31 -58
  17. umap/static/umap/js/modules/browser.js +4 -4
  18. umap/static/umap/js/modules/data/features.js +32 -35
  19. umap/static/umap/js/modules/data/fields.js +189 -23
  20. umap/static/umap/js/modules/data/layer.js +72 -87
  21. umap/static/umap/js/modules/domutils.js +21 -1
  22. umap/static/umap/js/modules/filters.js +13 -40
  23. umap/static/umap/js/modules/form/fields.js +4 -4
  24. umap/static/umap/js/modules/formatter.js +9 -1
  25. umap/static/umap/js/modules/help.js +12 -13
  26. umap/static/umap/js/modules/importer.js +17 -26
  27. umap/static/umap/js/modules/importers/banfr.js +0 -1
  28. umap/static/umap/js/modules/importers/cadastrefr.js +19 -19
  29. umap/static/umap/js/modules/importers/communesfr.js +7 -8
  30. umap/static/umap/js/modules/importers/datasets.js +14 -14
  31. umap/static/umap/js/modules/importers/geodatamine.js +20 -22
  32. umap/static/umap/js/modules/importers/opendata.js +10 -0
  33. umap/static/umap/js/modules/importers/overpass.js +19 -18
  34. umap/static/umap/js/modules/managers.js +1 -1
  35. umap/static/umap/js/modules/permissions.js +5 -3
  36. umap/static/umap/js/modules/rendering/controls.js +2 -2
  37. umap/static/umap/js/modules/rendering/icon.js +5 -9
  38. umap/static/umap/js/modules/rendering/layers/base.js +1 -1
  39. umap/static/umap/js/modules/rendering/layers/classified.js +15 -10
  40. umap/static/umap/js/modules/rendering/layers/heat.js +1 -0
  41. umap/static/umap/js/modules/rendering/map.js +22 -22
  42. umap/static/umap/js/modules/rendering/popup.js +6 -3
  43. umap/static/umap/js/modules/rendering/template.js +28 -34
  44. umap/static/umap/js/modules/rendering/ui.js +1 -2
  45. umap/static/umap/js/modules/rules.js +34 -41
  46. umap/static/umap/js/modules/schema.js +0 -7
  47. umap/static/umap/js/modules/share.js +36 -69
  48. umap/static/umap/js/modules/slideshow.js +3 -3
  49. umap/static/umap/js/modules/tableeditor.js +0 -1
  50. umap/static/umap/js/modules/ui/bar.js +51 -32
  51. umap/static/umap/js/modules/ui/panel.js +26 -21
  52. umap/static/umap/js/modules/ui/tooltip.js +1 -1
  53. umap/static/umap/js/modules/umap.js +75 -80
  54. umap/static/umap/js/modules/utils.js +12 -3
  55. umap/static/umap/js/umap.controls.js +33 -14
  56. umap/static/umap/locale/am_ET.js +6 -4
  57. umap/static/umap/locale/am_ET.json +6 -4
  58. umap/static/umap/locale/ar.js +6 -4
  59. umap/static/umap/locale/ar.json +6 -4
  60. umap/static/umap/locale/ast.js +6 -4
  61. umap/static/umap/locale/ast.json +6 -4
  62. umap/static/umap/locale/bg.js +6 -4
  63. umap/static/umap/locale/bg.json +6 -4
  64. umap/static/umap/locale/br.js +19 -8
  65. umap/static/umap/locale/br.json +19 -8
  66. umap/static/umap/locale/ca.js +6 -4
  67. umap/static/umap/locale/ca.json +6 -4
  68. umap/static/umap/locale/cs_CZ.js +7 -5
  69. umap/static/umap/locale/cs_CZ.json +7 -5
  70. umap/static/umap/locale/da.js +8 -6
  71. umap/static/umap/locale/da.json +8 -6
  72. umap/static/umap/locale/de.js +38 -36
  73. umap/static/umap/locale/de.json +38 -36
  74. umap/static/umap/locale/el.js +7 -5
  75. umap/static/umap/locale/el.json +7 -5
  76. umap/static/umap/locale/en.js +7 -5
  77. umap/static/umap/locale/en.json +7 -5
  78. umap/static/umap/locale/en_US.json +6 -4
  79. umap/static/umap/locale/es.js +19 -17
  80. umap/static/umap/locale/es.json +19 -17
  81. umap/static/umap/locale/et.js +7 -5
  82. umap/static/umap/locale/et.json +7 -5
  83. umap/static/umap/locale/eu.js +23 -21
  84. umap/static/umap/locale/eu.json +23 -21
  85. umap/static/umap/locale/fa_IR.js +7 -5
  86. umap/static/umap/locale/fa_IR.json +7 -5
  87. umap/static/umap/locale/fi.js +6 -4
  88. umap/static/umap/locale/fi.json +6 -4
  89. umap/static/umap/locale/fr.js +8 -6
  90. umap/static/umap/locale/fr.json +8 -6
  91. umap/static/umap/locale/gl.js +147 -145
  92. umap/static/umap/locale/gl.json +147 -145
  93. umap/static/umap/locale/he.js +6 -4
  94. umap/static/umap/locale/he.json +6 -4
  95. umap/static/umap/locale/hr.js +6 -4
  96. umap/static/umap/locale/hr.json +6 -4
  97. umap/static/umap/locale/hu.js +7 -5
  98. umap/static/umap/locale/hu.json +7 -5
  99. umap/static/umap/locale/id.js +6 -4
  100. umap/static/umap/locale/id.json +6 -4
  101. umap/static/umap/locale/is.js +7 -5
  102. umap/static/umap/locale/is.json +7 -5
  103. umap/static/umap/locale/it.js +7 -5
  104. umap/static/umap/locale/it.json +7 -5
  105. umap/static/umap/locale/ja.js +6 -4
  106. umap/static/umap/locale/ja.json +6 -4
  107. umap/static/umap/locale/ko.js +6 -4
  108. umap/static/umap/locale/ko.json +6 -4
  109. umap/static/umap/locale/lt.js +6 -4
  110. umap/static/umap/locale/lt.json +6 -4
  111. umap/static/umap/locale/ms.js +7 -5
  112. umap/static/umap/locale/ms.json +7 -5
  113. umap/static/umap/locale/nl.js +7 -5
  114. umap/static/umap/locale/nl.json +7 -5
  115. umap/static/umap/locale/no.js +6 -4
  116. umap/static/umap/locale/no.json +6 -4
  117. umap/static/umap/locale/pl.js +53 -51
  118. umap/static/umap/locale/pl.json +53 -51
  119. umap/static/umap/locale/pl_PL.json +6 -4
  120. umap/static/umap/locale/pt.js +7 -5
  121. umap/static/umap/locale/pt.json +7 -5
  122. umap/static/umap/locale/pt_BR.js +6 -4
  123. umap/static/umap/locale/pt_BR.json +6 -4
  124. umap/static/umap/locale/pt_PT.js +6 -4
  125. umap/static/umap/locale/pt_PT.json +6 -4
  126. umap/static/umap/locale/ro.js +6 -4
  127. umap/static/umap/locale/ro.json +6 -4
  128. umap/static/umap/locale/ru.js +6 -4
  129. umap/static/umap/locale/ru.json +6 -4
  130. umap/static/umap/locale/sk_SK.js +6 -4
  131. umap/static/umap/locale/sk_SK.json +6 -4
  132. umap/static/umap/locale/sl.js +6 -4
  133. umap/static/umap/locale/sl.json +6 -4
  134. umap/static/umap/locale/sr.js +6 -4
  135. umap/static/umap/locale/sr.json +6 -4
  136. umap/static/umap/locale/sv.js +6 -4
  137. umap/static/umap/locale/sv.json +6 -4
  138. umap/static/umap/locale/th_TH.js +6 -4
  139. umap/static/umap/locale/th_TH.json +6 -4
  140. umap/static/umap/locale/tr.js +6 -4
  141. umap/static/umap/locale/tr.json +6 -4
  142. umap/static/umap/locale/uk_UA.js +6 -4
  143. umap/static/umap/locale/uk_UA.json +6 -4
  144. umap/static/umap/locale/vi.js +6 -4
  145. umap/static/umap/locale/vi.json +6 -4
  146. umap/static/umap/locale/vi_VN.json +6 -4
  147. umap/static/umap/locale/zh.js +6 -4
  148. umap/static/umap/locale/zh.json +6 -4
  149. umap/static/umap/locale/zh_CN.json +6 -4
  150. umap/static/umap/locale/zh_TW.Big5.json +6 -4
  151. umap/static/umap/locale/zh_TW.js +20 -18
  152. umap/static/umap/locale/zh_TW.json +20 -18
  153. umap/static/umap/map.css +5 -4
  154. umap/static/umap/unittests/utils.js +7 -7
  155. umap/templates/umap/content_footer.html +1 -0
  156. umap/templates/umap/css.html +0 -2
  157. umap/templates/umap/js.html +1 -3
  158. umap/tests/integration/conftest.py +3 -2
  159. umap/tests/integration/test_anonymous_owned_map.py +1 -1
  160. umap/tests/integration/test_conditional_rules.py +106 -51
  161. umap/tests/integration/test_draw_polygon.py +4 -0
  162. umap/tests/integration/test_draw_polyline.py +11 -0
  163. umap/tests/integration/test_edit_datalayer.py +1 -1
  164. umap/tests/integration/test_fields.py +19 -0
  165. umap/tests/integration/test_iframe.py +1 -1
  166. umap/tests/integration/test_import.py +23 -0
  167. umap/tests/integration/test_map.py +2 -2
  168. umap/tests/integration/test_owned_map.py +2 -2
  169. umap/tests/integration/test_popup.py +31 -0
  170. umap/tests/integration/test_remote_data.py +4 -4
  171. umap/tests/integration/test_search.py +41 -0
  172. umap/tests/integration/test_share.py +2 -2
  173. umap/tests/integration/test_team.py +1 -1
  174. umap/tests/integration/test_websocket_sync.py +6 -1
  175. umap/tests/test_utils.py +4 -1
  176. umap/utils.py +1 -0
  177. {umap_project-3.4.0b3.dist-info → umap_project-3.4.2.dist-info}/METADATA +15 -15
  178. {umap_project-3.4.0b3.dist-info → umap_project-3.4.2.dist-info}/RECORD +181 -183
  179. umap/static/umap/js/umap.core.js +0 -93
  180. umap/static/umap/vendors/editinosm/Leaflet.EditInOSM.css +0 -46
  181. umap/static/umap/vendors/editinosm/Leaflet.EditInOSM.js +0 -240
  182. umap/static/umap/vendors/editinosm/edit-in-osm.png +0 -0
  183. {umap_project-3.4.0b3.dist-info → umap_project-3.4.2.dist-info}/WHEEL +0 -0
  184. {umap_project-3.4.0b3.dist-info → umap_project-3.4.2.dist-info}/entry_points.txt +0 -0
  185. {umap_project-3.4.0b3.dist-info → umap_project-3.4.2.dist-info}/licenses/LICENSE +0 -0
@@ -1,13 +1,9 @@
1
- import {
2
- DomEvent,
3
- DomUtil,
4
- Util,
5
- setOptions,
6
- } from '../../vendors/leaflet/leaflet-src.esm.js'
1
+ import { DomEvent, Util, setOptions } from '../../vendors/leaflet/leaflet-src.esm.js'
7
2
  import { translate } from './i18n.js'
8
3
  import { Request, ServerRequest } from './request.js'
9
4
  import { escapeHTML, generateId } from './utils.js'
10
5
  import * as Utils from './utils.js'
6
+ import * as DOMUtils from './domutils.js'
11
7
 
12
8
  export class BaseAutocomplete {
13
9
  constructor(parent, options) {
@@ -41,26 +37,18 @@ export class BaseAutocomplete {
41
37
  }
42
38
 
43
39
  createInput() {
44
- this.input = DomUtil.element({
45
- tagName: 'input',
46
- type: 'text',
47
- parent: this.parent,
48
- placeholder: this.options.placeholder,
49
- autocomplete: 'off',
50
- className: this.options.className,
51
- name: this.options.name || 'autocomplete',
52
- })
53
- DomEvent.on(this.input, 'keydown', this.onKeyDown, this)
54
- DomEvent.on(this.input, 'keyup', this.onKeyUp, this)
55
- DomEvent.on(this.input, 'blur', this.onBlur, this)
40
+ this.input = DOMUtils.loadTemplate(`
41
+ <input type="text" placeholder="${this.options.placeholder}" autocomplete="off" class="${this.options.className}" name="${this.options.name || 'autocomplete'}">
42
+ `)
43
+ this.parent.appendChild(this.input)
44
+ this.input.addEventListener('keydown', (event) => this.onKeyDown(event))
45
+ this.input.addEventListener('keyup', (event) => this.onKeyUp(event))
46
+ this.input.addEventListener('blur', (event) => this.onBlur(event))
56
47
  }
57
48
 
58
49
  createContainer() {
59
- this.container = DomUtil.element({
60
- tagName: 'ul',
61
- parent: document.body,
62
- className: 'umap-autocomplete',
63
- })
50
+ this.container = DOMUtils.loadTemplate('<ul class="umap-autocomplete"></ul>')
51
+ document.body.appendChild(this.container)
64
52
  }
65
53
 
66
54
  resizeContainer() {
@@ -167,20 +155,17 @@ export class BaseAutocomplete {
167
155
  }
168
156
 
169
157
  createResult(item) {
170
- const el = DomUtil.element({
171
- tagName: 'li',
172
- parent: this.container,
173
- textContent: item.label,
174
- })
158
+ const li = DOMUtils.loadTemplate(`<li>${item.label}</li>`)
159
+ this.container.appendChild(li)
175
160
  const result = {
176
161
  item: item,
177
- el: el,
162
+ el: li,
178
163
  }
179
- DomEvent.on(el, 'mouseover', () => {
164
+ li.addEventListener('mouseover', () => {
180
165
  this.current = result
181
166
  this.highlight()
182
167
  })
183
- DomEvent.on(el, 'mousedown', () => this.setChoice())
168
+ li.addEventListener('mousedown', () => this.setChoice())
184
169
  return result
185
170
  }
186
171
 
@@ -202,8 +187,7 @@ export class BaseAutocomplete {
202
187
 
203
188
  highlight() {
204
189
  this.results.forEach((result, index) => {
205
- if (index === this.current) DomUtil.addClass(result.el, 'on')
206
- else DomUtil.removeClass(result.el, 'on')
190
+ result.el.classList.toggle('on', index === this.current)
207
191
  })
208
192
  }
209
193
 
@@ -298,19 +282,15 @@ export const SingleMixin = (Base) =>
298
282
  }
299
283
 
300
284
  displaySelected(result) {
301
- const result_el = DomUtil.element({
302
- tagName: 'div',
303
- parent: this.selectedContainer,
304
- })
305
- result_el.textContent = result.item.label
306
- const close = DomUtil.element({
307
- tagName: 'span',
308
- parent: result_el,
309
- className: 'close',
310
- textContent: '×',
311
- })
285
+ const [root, { close }] = DOMUtils.loadTemplateWithRefs(`
286
+ <div class="with-toolbox">
287
+ ${result.item.label}
288
+ <button type="button" class="icon icon-16 icon-close" title="${translate('Close')}" data-ref="close"></button>
289
+ </div>
290
+ `)
291
+ this.selectedContainer.appendChild(root)
312
292
  this.input.style.display = 'none'
313
- DomEvent.on(close, 'click', () => {
293
+ close.addEventListener('click', () => {
314
294
  this.selectedContainer.innerHTML = ''
315
295
  this.input.style.display = 'block'
316
296
  this.options.on_unselect?.(result)
@@ -328,19 +308,12 @@ export const MultipleMixin = (Base) =>
328
308
  }
329
309
 
330
310
  displaySelected(result) {
331
- const result_el = DomUtil.element({
332
- tagName: 'li',
333
- parent: this.selectedContainer,
334
- })
335
- result_el.textContent = result.item.label
336
- const close = DomUtil.element({
337
- tagName: 'span',
338
- parent: result_el,
339
- className: 'close',
340
- textContent: '×',
341
- })
342
- DomEvent.on(close, 'click', () => {
343
- this.selectedContainer.removeChild(result_el)
311
+ const [li, { close }] = DOMUtils.loadTemplateWithRefs(`
312
+ <li class="with-toolbox">${result.item.label} <button class="icon icon-16 icon-close" type="button" data-ref="close"></button></li>
313
+ `)
314
+ this.selectedContainer.appendChild(li)
315
+ close.addEventListener('click', () => {
316
+ this.selectedContainer.removeChild(li)
344
317
  this.options.on_unselect?.(result)
345
318
  })
346
319
  this.hide()
@@ -1,4 +1,4 @@
1
- import { DomEvent, DomUtil, stamp } from '../../vendors/leaflet/leaflet-src.esm.js'
1
+ import { DomEvent, stamp } from '../../vendors/leaflet/leaflet-src.esm.js'
2
2
  import { Form } from './form/builder.js'
3
3
  import { EXPORT_FORMATS } from './formatter.js'
4
4
  import { translate } from './i18n.js'
@@ -133,9 +133,9 @@ export default class Browser {
133
133
  update() {
134
134
  if (!this.isOpen()) return
135
135
  this.dataContainer.innerHTML = ''
136
- this._umap.datalayers.browsable().map((datalayer) => {
136
+ for (const datalayer of this._umap.datalayers.browsable()) {
137
137
  this.addDataLayer(datalayer, this.dataContainer)
138
- })
138
+ }
139
139
  }
140
140
 
141
141
  open(mode) {
@@ -260,7 +260,7 @@ export default class Browser {
260
260
  if (datalayer.isVisible()) allHidden = false
261
261
  })
262
262
  this._umap.datalayers.browsable().map((datalayer) => {
263
- datalayer.autoLoaded = false
263
+ datalayer.autoVisibility = false
264
264
  if (allHidden) {
265
265
  datalayer.show()
266
266
  } else {
@@ -1,12 +1,7 @@
1
- import {
2
- DomEvent,
3
- DomUtil,
4
- GeoJSON,
5
- LineUtil,
6
- } from '../../../vendors/leaflet/leaflet-src.esm.js'
1
+ import { GeoJSON, LineUtil } from '../../../vendors/leaflet/leaflet-src.esm.js'
7
2
  import { uMapAlert as Alert } from '../../components/alerts/alert.js'
8
3
  import { MutatingForm } from '../form/builder.js'
9
- import { translate } from '../i18n.js'
4
+ import { translate, getLocale } from '../i18n.js'
10
5
  import {
11
6
  PREFERENCES as ORS_PREFERENCES,
12
7
  PROFILES as ORS_PROFILES,
@@ -243,12 +238,11 @@ class Feature {
243
238
  if (this._umap.editedFeature === this && !event?.force) return
244
239
  // If this feature is active (popup open), let's close it.
245
240
  this.deactivate()
246
- const container = DomUtil.create('div', 'umap-feature-container')
247
- DomUtil.createTitle(
248
- container,
249
- translate('Feature properties'),
250
- `icon-${this.getClassName()}`
251
- )
241
+ const container = DOMUtils.loadTemplate(`
242
+ <div class="umap-feature-container">
243
+ <h3><i class="icon icon-16 icon-${this.getClassName()}"></i> ${translate('Feature properties')}</h3>
244
+ </div>
245
+ `)
252
246
 
253
247
  let builder = new MutatingForm(this, [
254
248
  ['datalayer', { handler: 'EditableDataLayerSwitcher' }],
@@ -260,17 +254,19 @@ class Feature {
260
254
  const properties = []
261
255
  for (const field of this.fields) {
262
256
  const options = { handler: 'Input', label: field.key }
263
- if (field.key === 'description' || field.type === 'Text') {
257
+ if (field.key === 'description' || field.TYPE === 'Text') {
264
258
  options.handler = 'Textarea'
265
259
  options.helpEntries = ['textFormatting']
266
- } else if (field.type === 'Number') {
260
+ } else if (field.TYPE === 'Number') {
267
261
  options.handler = 'FloatInput'
268
- } else if (field.type === 'Date') {
262
+ } else if (field.TYPE === 'Date') {
269
263
  options.handler = 'DateInput'
270
- } else if (field.type === 'Datetime') {
264
+ } else if (field.TYPE === 'Datetime') {
271
265
  options.handler = 'DateTimeInput'
272
- } else if (field.type === 'Boolean') {
266
+ } else if (field.TYPE === 'Boolean') {
273
267
  options.handler = 'Switch'
268
+ } else if (field.TYPE === 'Enum') {
269
+ options.helpText = translate('Comma separated list of values')
274
270
  }
275
271
  properties.push([`properties.${field.key}`, options])
276
272
  }
@@ -331,7 +327,7 @@ class Feature {
331
327
  let builder = new MutatingForm(this, optionsFields, {
332
328
  id: 'umap-feature-shape-properties',
333
329
  })
334
- const shapeProperties = DomUtil.createFieldset(
330
+ const shapeProperties = DOMUtils.createFieldset(
335
331
  container,
336
332
  translate('Shape properties')
337
333
  )
@@ -343,7 +339,7 @@ class Feature {
343
339
  builder = new MutatingForm(this, advancedOptions, {
344
340
  id: 'umap-feature-advanced-properties',
345
341
  })
346
- const advancedProperties = DomUtil.createFieldset(
342
+ const advancedProperties = DOMUtils.createFieldset(
347
343
  container,
348
344
  translate('Advanced properties')
349
345
  )
@@ -351,7 +347,7 @@ class Feature {
351
347
 
352
348
  const interactionOptions = this.getInteractionOptions()
353
349
  builder = new MutatingForm(this, interactionOptions)
354
- const popupFieldset = DomUtil.createFieldset(
350
+ const popupFieldset = DOMUtils.createFieldset(
355
351
  container,
356
352
  translate('Interaction options')
357
353
  )
@@ -442,7 +438,7 @@ class Feature {
442
438
  this.properties = Object.fromEntries(
443
439
  Object.entries(geojson.properties || {}).map(this.cleanProperty)
444
440
  )
445
- this.properties._umap_options = L.extend(
441
+ this.properties._umap_options = Object.assign(
446
442
  {},
447
443
  this.properties._storage_options,
448
444
  this.properties._umap_options
@@ -514,8 +510,8 @@ class Feature {
514
510
  }
515
511
 
516
512
  cloneProperties() {
517
- const properties = L.extend({}, this.properties)
518
- properties._umap_options = L.extend({}, properties._umap_options)
513
+ const properties = Object.assign({}, this.properties)
514
+ properties._umap_options = Object.assign({}, properties._umap_options)
519
515
  if (Object.keys && Object.keys(properties._umap_options).length === 0) {
520
516
  delete properties._umap_options // It can make a difference on big data sets
521
517
  }
@@ -591,9 +587,10 @@ class Feature {
591
587
  }
592
588
 
593
589
  extendedProperties() {
590
+ console.trace()
594
591
  // Include context properties
595
592
  const properties = this._umap.getGeoContext()
596
- const locale = L.getLocale()
593
+ const locale = getLocale()
597
594
  if (locale) properties.locale = locale
598
595
  if (U.lang) properties.lang = U.lang
599
596
  properties.rank = this.getRank() + 1
@@ -788,7 +785,7 @@ export class Point extends Feature {
788
785
  ]
789
786
  const builder = new MutatingForm(this, coordinatesOptions)
790
787
  builder.on('set', () => {
791
- if (!this.ui._latlng.isValid()) {
788
+ if (!Utils.LatLngIsValid(this.ui._latlng)) {
792
789
  Alert.error(translate('Invalid latitude or longitude'))
793
790
  builder.restoreField('ui._latlng.lat')
794
791
  builder.restoreField('ui._latlng.lng')
@@ -796,7 +793,7 @@ export class Point extends Feature {
796
793
  this.pullGeometry()
797
794
  this.zoomTo({ easing: false })
798
795
  })
799
- const fieldset = DomUtil.createFieldset(container, translate('Coordinates'))
796
+ const fieldset = DOMUtils.createFieldset(container, translate('Coordinates'))
800
797
  fieldset.appendChild(builder.build())
801
798
  }
802
799
 
@@ -964,7 +961,7 @@ class Path extends Feature {
964
961
  const builder = new MutatingForm(this, options, {
965
962
  id: 'umap-feature-line-decoration',
966
963
  })
967
- const fieldset = DomUtil.createFieldset(container, translate('Line decoration'))
964
+ const fieldset = DOMUtils.createFieldset(container, translate('Line decoration'))
968
965
  fieldset.appendChild(builder.build())
969
966
  }
970
967
  }
@@ -1077,7 +1074,7 @@ export class LineString extends Path {
1077
1074
  getAdvancedEditActions(container) {
1078
1075
  super.getAdvancedEditActions(container)
1079
1076
  const button = Utils.loadTemplate(`
1080
- <button class="button" type="button"><i class="icon icon-24 icon-polygon"></i>${translate('Transform to polygon')}</button>
1077
+ <button type="button"><i class="icon icon-24 icon-polygon"></i>${translate('Transform to polygon')}</button>
1081
1078
  `)
1082
1079
  container.appendChild(button)
1083
1080
  button.addEventListener('click', () => this.toPolygon())
@@ -1383,13 +1380,13 @@ export class Polygon extends Path {
1383
1380
 
1384
1381
  getAdvancedEditActions(container) {
1385
1382
  super.getAdvancedEditActions(container)
1386
- const toLineString = DomUtil.createButton(
1387
- 'button umap-to-polyline',
1388
- container,
1389
- translate('Transform to lines'),
1390
- this.toLineString,
1391
- this
1383
+ const toLineString = DOMUtils.loadTemplate(
1384
+ `<button type="button" class="umap-to-polyline">
1385
+ <i class="icon icon-24 icon-polyline"></i>${translate('Transform to lines')}
1386
+ </button>`
1392
1387
  )
1388
+ container.appendChild(toLineString)
1389
+ toLineString.addEventListener('click', () => this.toLineString())
1393
1390
  }
1394
1391
 
1395
1392
  isMulti() {
@@ -9,6 +9,180 @@ export const getDefaultFields = () => [
9
9
  { key: 'description', type: 'Text' },
10
10
  ]
11
11
 
12
+ export const Registry = {}
13
+
14
+ class BaseField {
15
+ constructor(key) {
16
+ this.key = key
17
+ }
18
+
19
+ values(features) {
20
+ return features
21
+ .map((feature) => this.parse(feature.properties[this.key]))
22
+ .filter((val, idx, arr) => arr.indexOf(val) === idx)
23
+ }
24
+
25
+ equal(expected, other) {
26
+ return expected === other
27
+ }
28
+
29
+ not_equal(expected, other) {
30
+ return expected !== other
31
+ }
32
+
33
+ gt(expected, other) {
34
+ return other > expected
35
+ }
36
+
37
+ lt(expected, other) {
38
+ return other < expected
39
+ }
40
+
41
+ render(value) {
42
+ return Utils.escapeHTML(value).trim()
43
+ }
44
+
45
+ dumps() {
46
+ return {
47
+ key: this.key,
48
+ type: this.TYPE,
49
+ }
50
+ }
51
+ }
52
+
53
+ Registry.String = class extends BaseField {
54
+ constructor(key) {
55
+ super(key)
56
+ // FIXME make it dynamic from class name
57
+ this.TYPE = 'String'
58
+ this.LABEL = translate('Short text')
59
+ }
60
+
61
+ parse(value) {
62
+ return String(value ?? '')
63
+ }
64
+
65
+ render(value) {
66
+ value = super.render(value)
67
+ // TODO, manage links (url, mailto, wikipedia...)
68
+ if (value.indexOf('http') === 0) {
69
+ value = `<a href="${value}" target="_blank">${value}</a>`
70
+ }
71
+ return value
72
+ }
73
+ }
74
+
75
+ Registry.Text = class extends Registry.String {
76
+ constructor(key) {
77
+ super(key)
78
+ // FIXME make it dynamic from class name
79
+ this.TYPE = 'Text'
80
+ this.LABEL = translate('Text')
81
+ }
82
+
83
+ render(value) {
84
+ return Utils.toHTML(value)
85
+ }
86
+ }
87
+
88
+ Registry.Number = class extends BaseField {
89
+ constructor(key) {
90
+ super(key)
91
+ // FIXME make it dynamic from class name
92
+ this.TYPE = 'Number'
93
+ this.LABEL = translate('Number')
94
+ }
95
+
96
+ parse(value) {
97
+ return Number.parseFloat(value)
98
+ }
99
+ }
100
+
101
+ Registry.Datetime = class extends BaseField {
102
+ constructor(key) {
103
+ super(key)
104
+ // FIXME make it dynamic from class name
105
+ this.TYPE = 'Datetime'
106
+ this.LABEL = translate('Date and time')
107
+ }
108
+
109
+ parse(value) {
110
+ return new Date(value)
111
+ }
112
+ }
113
+
114
+ Registry.Date = class extends BaseField {
115
+ constructor(key) {
116
+ super(key)
117
+ // FIXME make it dynamic from class name
118
+ this.TYPE = 'Date'
119
+ this.LABEL = translate('Date')
120
+ }
121
+
122
+ parse(value) {
123
+ return Utils.parseNaiveDate(value)
124
+ }
125
+ }
126
+
127
+ Registry.Boolean = class extends BaseField {
128
+ constructor(key) {
129
+ super(key)
130
+ // FIXME make it dynamic from class name
131
+ this.TYPE = 'Boolean'
132
+ this.LABEL = translate('Yes / No')
133
+ }
134
+
135
+ parse(value) {
136
+ // 'yes' is used in OpenStreetMap data
137
+ return ['true', '1', 'yes'].includes(`${value}`.toLowerCase())
138
+ }
139
+ }
140
+
141
+ Registry.Enum = class extends BaseField {
142
+ constructor(key) {
143
+ super(key)
144
+ // FIXME make it dynamic from class name
145
+ this.TYPE = 'Enum'
146
+ this.LABEL = translate('List of values')
147
+ }
148
+
149
+ parse(value) {
150
+ return String(value || '')
151
+ .split(',')
152
+ .map((s) => s.trim())
153
+ }
154
+
155
+ values(features) {
156
+ return features
157
+ .reduce(
158
+ (acc, feature) => acc.concat(this.parse(feature.properties[this.key])),
159
+ []
160
+ )
161
+ .filter((val, idx, arr) => arr.indexOf(val) === idx)
162
+ }
163
+
164
+ equal(expected, other) {
165
+ return Boolean(new Set(other).intersection(new Set(expected)).size)
166
+ }
167
+
168
+ not_equal(expected, other) {
169
+ return !new Set(other).intersection(new Set(expected)).size
170
+ }
171
+
172
+ gt(expected, other) {
173
+ return false
174
+ }
175
+
176
+ lt(expected, other) {
177
+ return false
178
+ }
179
+ }
180
+
181
+ const FIELD_TYPES = Object.entries(Registry).map(([name, klass]) => [
182
+ name,
183
+ new klass().LABEL,
184
+ ])
185
+
12
186
  export class Fields extends Map {
13
187
  constructor(parent, dialog) {
14
188
  super()
@@ -38,7 +212,7 @@ export class Fields extends Map {
38
212
  this.parent.properties.fields = this.all().map((field) => {
39
213
  // We don't want to keep the reference, otherwise editing
40
214
  // it will also change the old value
41
- return { ...field }
215
+ return { ...field.dumps() }
42
216
  })
43
217
  }
44
218
 
@@ -60,10 +234,8 @@ export class Fields extends Map {
60
234
  console.error('Invalid field', field)
61
235
  return
62
236
  }
63
- field.type ??= 'String'
64
- // Copy object, so not to affect original
65
- // when edited.
66
- this.set(field.key, { ...field })
237
+ const klass = Registry[field.type] || Registry.String
238
+ this.set(field.key, new klass(field.key))
67
239
  this.push()
68
240
  }
69
241
 
@@ -103,7 +275,7 @@ export class Fields extends Map {
103
275
  const [row, { edit, del, addFilter, editFilter }] = Utils.loadTemplateWithRefs(
104
276
  `<li class="orderable with-toolbox" data-key="${field.key}">
105
277
  <span>
106
- <i class="icon icon-16 icon-field-${field.type}" title="${field.type}"></i>
278
+ <i class="icon icon-16 icon-field-${field.TYPE}" title="${field.LABEL}"></i>
107
279
  ${field.key}
108
280
  </span>
109
281
  <span>
@@ -161,16 +333,8 @@ export class Fields extends Map {
161
333
 
162
334
  async editField(name) {
163
335
  if (!name && this.parent.isRemoteLayer?.()) return
164
- const FIELD_TYPES = [
165
- ['String', translate('Short text')],
166
- ['Text', translate('Text')],
167
- ['Number', translate('Number')],
168
- ['Date', translate('Date')],
169
- ['Datetime', translate('Date and time')],
170
- ['Enum', translate('List of values')],
171
- ['Boolean', translate('Yes / No')],
172
- ]
173
- const field = this.get(name) || {}
336
+ const field = this.get(name)
337
+ const data = field?.dumps() || {}
174
338
  const metadatas = [
175
339
  [
176
340
  'key',
@@ -189,7 +353,7 @@ export class Fields extends Map {
189
353
  },
190
354
  ],
191
355
  ]
192
- const form = new Form(field, metadatas)
356
+ const form = new Form(data, metadatas)
193
357
 
194
358
  const [container, { body, addFilter }] = Utils.loadTemplateWithRefs(`
195
359
  <div>
@@ -202,32 +366,34 @@ export class Fields extends Map {
202
366
  if (this.parent.filters) {
203
367
  addFilter.addEventListener('click', () => {
204
368
  this.dialog.accept().then(() => {
205
- this.parent.filters.createFilterForm(field.key)
369
+ this.parent.filters.createFilterForm(data.key)
206
370
  })
207
371
  })
208
372
  addFilter.hidden = false
209
373
  }
210
374
 
211
375
  return this.dialog.open({ template: container }).then(() => {
212
- if (!this.validateName(field.key, field.key !== name)) {
376
+ if (!this.validateName(data.key, data.key !== name)) {
213
377
  this.pull()
214
378
  return
215
379
  }
216
380
  this.parent.sync.startBatch()
217
381
  const oldFields = Utils.CopyJSON(this.parent.properties.fields)
218
382
  if (!name) {
219
- this.add(field)
220
- } else if (name !== field.key) {
383
+ this.add(data)
384
+ } else if (name !== data.key || field.TYPE !== data.type) {
221
385
  this.clear()
222
386
  // Keep order on rename
223
387
  for (const old of oldFields) {
224
388
  if (old.key === name) {
225
- this.add(field)
389
+ this.add(data)
226
390
  } else {
227
391
  this.add(old)
228
392
  }
229
393
  }
230
- this.parent.renameField(name, field.key)
394
+ if (name !== data.key) {
395
+ this.parent.renameField(name, data.key)
396
+ }
231
397
  } else {
232
398
  this.push()
233
399
  }