umap-project 3.4.0b1__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 (202) hide show
  1. umap/__init__.py +1 -1
  2. umap/context_processors.py +1 -1
  3. umap/locale/da/LC_MESSAGES/django.mo +0 -0
  4. umap/locale/da/LC_MESSAGES/django.po +20 -16
  5. umap/locale/en/LC_MESSAGES/django.po +18 -14
  6. umap/locale/es/LC_MESSAGES/django.mo +0 -0
  7. umap/locale/es/LC_MESSAGES/django.po +20 -16
  8. umap/locale/fr/LC_MESSAGES/django.mo +0 -0
  9. umap/locale/fr/LC_MESSAGES/django.po +18 -14
  10. umap/locale/pl/LC_MESSAGES/django.mo +0 -0
  11. umap/locale/pl/LC_MESSAGES/django.po +72 -71
  12. umap/migrations/0018_datalayer_uuid.py +1 -1
  13. umap/models.py +7 -3
  14. umap/settings/local.py.sample +1 -1
  15. umap/static/umap/content.css +0 -3
  16. umap/static/umap/css/bar.css +9 -6
  17. umap/static/umap/css/form.css +27 -11
  18. umap/static/umap/css/popup.css +1 -0
  19. umap/static/umap/js/components/base.js +1 -1
  20. umap/static/umap/js/components/copiable.js +47 -0
  21. umap/static/umap/js/modules/autocomplete.js +31 -58
  22. umap/static/umap/js/modules/browser.js +8 -8
  23. umap/static/umap/js/modules/data/features.js +33 -36
  24. umap/static/umap/js/modules/data/fields.js +446 -0
  25. umap/static/umap/js/modules/data/layer.js +76 -93
  26. umap/static/umap/js/modules/domutils.js +24 -4
  27. umap/static/umap/js/modules/filters.js +20 -47
  28. umap/static/umap/js/modules/form/fields.js +4 -4
  29. umap/static/umap/js/modules/formatter.js +9 -1
  30. umap/static/umap/js/modules/help.js +13 -14
  31. umap/static/umap/js/modules/i18n.js +1 -1
  32. umap/static/umap/js/modules/importer.js +18 -27
  33. umap/static/umap/js/modules/importers/banfr.js +0 -1
  34. umap/static/umap/js/modules/importers/cadastrefr.js +19 -19
  35. umap/static/umap/js/modules/importers/communesfr.js +7 -8
  36. umap/static/umap/js/modules/importers/datasets.js +14 -14
  37. umap/static/umap/js/modules/importers/geodatamine.js +20 -22
  38. umap/static/umap/js/modules/importers/opendata.js +10 -0
  39. umap/static/umap/js/modules/importers/overpass.js +19 -18
  40. umap/static/umap/js/modules/managers.js +1 -265
  41. umap/static/umap/js/modules/permissions.js +5 -3
  42. umap/static/umap/js/modules/rendering/controls.js +6 -4
  43. umap/static/umap/js/modules/rendering/icon.js +5 -9
  44. umap/static/umap/js/modules/rendering/layers/base.js +1 -1
  45. umap/static/umap/js/modules/rendering/layers/classified.js +16 -11
  46. umap/static/umap/js/modules/rendering/layers/heat.js +27 -21
  47. umap/static/umap/js/modules/rendering/map.js +22 -22
  48. umap/static/umap/js/modules/rendering/popup.js +6 -3
  49. umap/static/umap/js/modules/rendering/template.js +31 -37
  50. umap/static/umap/js/modules/rendering/ui.js +1 -2
  51. umap/static/umap/js/modules/rules.js +34 -41
  52. umap/static/umap/js/modules/schema.js +0 -7
  53. umap/static/umap/js/modules/share.js +36 -69
  54. umap/static/umap/js/modules/slideshow.js +3 -3
  55. umap/static/umap/js/modules/tableeditor.js +0 -1
  56. umap/static/umap/js/modules/ui/bar.js +51 -32
  57. umap/static/umap/js/modules/ui/dialog.js +10 -1
  58. umap/static/umap/js/modules/ui/panel.js +28 -23
  59. umap/static/umap/js/modules/ui/tooltip.js +1 -1
  60. umap/static/umap/js/modules/umap.js +84 -84
  61. umap/static/umap/js/modules/utils.js +13 -4
  62. umap/static/umap/js/umap.controls.js +33 -14
  63. umap/static/umap/locale/am_ET.js +19 -8
  64. umap/static/umap/locale/am_ET.json +19 -8
  65. umap/static/umap/locale/ar.js +19 -8
  66. umap/static/umap/locale/ar.json +19 -8
  67. umap/static/umap/locale/ast.js +19 -8
  68. umap/static/umap/locale/ast.json +19 -8
  69. umap/static/umap/locale/bg.js +19 -8
  70. umap/static/umap/locale/bg.json +19 -8
  71. umap/static/umap/locale/br.js +20 -9
  72. umap/static/umap/locale/br.json +20 -9
  73. umap/static/umap/locale/ca.js +19 -8
  74. umap/static/umap/locale/ca.json +19 -8
  75. umap/static/umap/locale/cs_CZ.js +20 -9
  76. umap/static/umap/locale/cs_CZ.json +20 -9
  77. umap/static/umap/locale/da.js +54 -43
  78. umap/static/umap/locale/da.json +54 -43
  79. umap/static/umap/locale/de.js +44 -33
  80. umap/static/umap/locale/de.json +44 -33
  81. umap/static/umap/locale/el.js +20 -9
  82. umap/static/umap/locale/el.json +20 -9
  83. umap/static/umap/locale/en.js +20 -9
  84. umap/static/umap/locale/en.json +20 -9
  85. umap/static/umap/locale/en_US.json +19 -8
  86. umap/static/umap/locale/es.js +55 -44
  87. umap/static/umap/locale/es.json +55 -44
  88. umap/static/umap/locale/et.js +20 -9
  89. umap/static/umap/locale/et.json +20 -9
  90. umap/static/umap/locale/eu.js +25 -14
  91. umap/static/umap/locale/eu.json +25 -14
  92. umap/static/umap/locale/fa_IR.js +20 -9
  93. umap/static/umap/locale/fa_IR.json +20 -9
  94. umap/static/umap/locale/fi.js +19 -8
  95. umap/static/umap/locale/fi.json +19 -8
  96. umap/static/umap/locale/fr.js +21 -10
  97. umap/static/umap/locale/fr.json +21 -10
  98. umap/static/umap/locale/gl.js +147 -136
  99. umap/static/umap/locale/gl.json +147 -136
  100. umap/static/umap/locale/he.js +19 -8
  101. umap/static/umap/locale/he.json +19 -8
  102. umap/static/umap/locale/hr.js +19 -8
  103. umap/static/umap/locale/hr.json +19 -8
  104. umap/static/umap/locale/hu.js +62 -51
  105. umap/static/umap/locale/hu.json +62 -51
  106. umap/static/umap/locale/id.js +19 -8
  107. umap/static/umap/locale/id.json +19 -8
  108. umap/static/umap/locale/is.js +20 -9
  109. umap/static/umap/locale/is.json +20 -9
  110. umap/static/umap/locale/it.js +20 -9
  111. umap/static/umap/locale/it.json +20 -9
  112. umap/static/umap/locale/ja.js +19 -8
  113. umap/static/umap/locale/ja.json +19 -8
  114. umap/static/umap/locale/ko.js +19 -8
  115. umap/static/umap/locale/ko.json +19 -8
  116. umap/static/umap/locale/lt.js +19 -8
  117. umap/static/umap/locale/lt.json +19 -8
  118. umap/static/umap/locale/ms.js +20 -9
  119. umap/static/umap/locale/ms.json +20 -9
  120. umap/static/umap/locale/nl.js +20 -9
  121. umap/static/umap/locale/nl.json +20 -9
  122. umap/static/umap/locale/no.js +19 -8
  123. umap/static/umap/locale/no.json +19 -8
  124. umap/static/umap/locale/pl.js +56 -45
  125. umap/static/umap/locale/pl.json +56 -45
  126. umap/static/umap/locale/pl_PL.json +19 -8
  127. umap/static/umap/locale/pt.js +20 -9
  128. umap/static/umap/locale/pt.json +20 -9
  129. umap/static/umap/locale/pt_BR.js +19 -8
  130. umap/static/umap/locale/pt_BR.json +19 -8
  131. umap/static/umap/locale/pt_PT.js +19 -8
  132. umap/static/umap/locale/pt_PT.json +19 -8
  133. umap/static/umap/locale/ro.js +19 -8
  134. umap/static/umap/locale/ro.json +19 -8
  135. umap/static/umap/locale/ru.js +19 -8
  136. umap/static/umap/locale/ru.json +19 -8
  137. umap/static/umap/locale/si.js +1 -1
  138. umap/static/umap/locale/si.json +1 -1
  139. umap/static/umap/locale/sk_SK.js +19 -8
  140. umap/static/umap/locale/sk_SK.json +19 -8
  141. umap/static/umap/locale/sl.js +19 -8
  142. umap/static/umap/locale/sl.json +19 -8
  143. umap/static/umap/locale/sr.js +19 -8
  144. umap/static/umap/locale/sr.json +19 -8
  145. umap/static/umap/locale/sv.js +19 -8
  146. umap/static/umap/locale/sv.json +19 -8
  147. umap/static/umap/locale/th_TH.js +19 -8
  148. umap/static/umap/locale/th_TH.json +19 -8
  149. umap/static/umap/locale/tr.js +19 -8
  150. umap/static/umap/locale/tr.json +19 -8
  151. umap/static/umap/locale/uk_UA.js +19 -8
  152. umap/static/umap/locale/uk_UA.json +19 -8
  153. umap/static/umap/locale/vi.js +19 -8
  154. umap/static/umap/locale/vi.json +19 -8
  155. umap/static/umap/locale/vi_VN.json +19 -8
  156. umap/static/umap/locale/zh.js +19 -8
  157. umap/static/umap/locale/zh.json +19 -8
  158. umap/static/umap/locale/zh_CN.json +19 -8
  159. umap/static/umap/locale/zh_TW.Big5.json +19 -8
  160. umap/static/umap/locale/zh_TW.js +53 -42
  161. umap/static/umap/locale/zh_TW.json +53 -42
  162. umap/static/umap/map.css +8 -7
  163. umap/static/umap/unittests/utils.js +7 -7
  164. umap/templates/umap/content_footer.html +1 -0
  165. umap/templates/umap/css.html +0 -2
  166. umap/templates/umap/js.html +1 -3
  167. umap/templates/umap/login_popup_end.html +2 -2
  168. umap/tests/integration/conftest.py +11 -2
  169. umap/tests/integration/test_anonymous_owned_map.py +2 -2
  170. umap/tests/integration/test_conditional_rules.py +107 -52
  171. umap/tests/integration/test_draw_polygon.py +4 -0
  172. umap/tests/integration/test_draw_polyline.py +11 -0
  173. umap/tests/integration/test_edit_datalayer.py +1 -1
  174. umap/tests/integration/test_fields.py +19 -0
  175. umap/tests/integration/test_filters.py +6 -7
  176. umap/tests/integration/test_iframe.py +1 -1
  177. umap/tests/integration/test_import.py +23 -0
  178. umap/tests/integration/test_map.py +2 -2
  179. umap/tests/integration/test_map_preview.py +1 -1
  180. umap/tests/integration/test_owned_map.py +2 -2
  181. umap/tests/integration/test_picto.py +1 -1
  182. umap/tests/integration/test_popup.py +31 -0
  183. umap/tests/integration/test_remote_data.py +4 -4
  184. umap/tests/integration/test_save.py +1 -1
  185. umap/tests/integration/test_search.py +41 -0
  186. umap/tests/integration/test_share.py +2 -2
  187. umap/tests/integration/test_team.py +1 -1
  188. umap/tests/integration/test_websocket_sync.py +69 -20
  189. umap/tests/test_dashboard.py +1 -1
  190. umap/tests/test_statics.py +2 -2
  191. umap/tests/test_utils.py +4 -1
  192. umap/tests/test_views.py +1 -1
  193. umap/utils.py +3 -2
  194. {umap_project-3.4.0b1.dist-info → umap_project-3.4.2.dist-info}/METADATA +17 -17
  195. {umap_project-3.4.0b1.dist-info → umap_project-3.4.2.dist-info}/RECORD +198 -199
  196. umap/static/umap/js/umap.core.js +0 -93
  197. umap/static/umap/vendors/editinosm/Leaflet.EditInOSM.css +0 -46
  198. umap/static/umap/vendors/editinosm/Leaflet.EditInOSM.js +0 -240
  199. umap/static/umap/vendors/editinosm/edit-in-osm.png +0 -0
  200. {umap_project-3.4.0b1.dist-info → umap_project-3.4.2.dist-info}/WHEEL +0 -0
  201. {umap_project-3.4.0b1.dist-info → umap_project-3.4.2.dist-info}/entry_points.txt +0 -0
  202. {umap_project-3.4.0b1.dist-info → umap_project-3.4.2.dist-info}/licenses/LICENSE +0 -0
@@ -1,9 +1,9 @@
1
1
  // FIXME: this module should not depend on Leaflet
2
2
  import {
3
3
  DomEvent,
4
- DomUtil,
5
4
  GeoJSON,
6
5
  stamp,
6
+ SVG,
7
7
  } from '../../../vendors/leaflet/leaflet-src.esm.js'
8
8
  import {
9
9
  uMapAlert as Alert,
@@ -19,10 +19,12 @@ import { Heat } from '../rendering/layers/heat.js'
19
19
  import * as Schema from '../schema.js'
20
20
  import TableEditor from '../tableeditor.js'
21
21
  import * as Utils from '../utils.js'
22
+ import * as DOMUtils from '../domutils.js'
22
23
  import { LineString, Point, Polygon } from './features.js'
23
24
  import Rules from '../rules.js'
24
- import { FeatureManager, FieldManager } from '../managers.js'
25
+ import { FeatureManager } from '../managers.js'
25
26
  import { Filters } from '../filters.js'
27
+ import { Fields, getDefaultFields } from './fields.js'
26
28
 
27
29
  export const LAYER_TYPES = [
28
30
  DefaultLayer,
@@ -49,7 +51,7 @@ export class DataLayer {
49
51
  this.parentPane = this._leafletMap.getPane('overlayPane')
50
52
  this.pane = this._leafletMap.createPane(`datalayer${stamp(this)}`, this.parentPane)
51
53
  // FIXME: should be on layer
52
- this.renderer = L.svg({ pane: this.pane })
54
+ this.renderer = new SVG({ pane: this.pane })
53
55
  this.defaultProperties = {
54
56
  displayOnLoad: true,
55
57
  inCaption: true,
@@ -83,20 +85,15 @@ export class DataLayer {
83
85
  }
84
86
  this.connectToMap()
85
87
  this.permissions = new DataLayerPermissions(this._umap, this)
86
- this.rules = new Rules(umap, this)
87
88
 
88
89
  this._needsFetch = this.createdOnServer || this.isRemoteLayer()
90
+ this.fields = new Fields(this, this._umap.dialog)
91
+ this.filters = new Filters(this, this._umap)
92
+ this.rules = new Rules(umap, this)
93
+
89
94
  if (!this.createdOnServer) {
90
95
  if (this.showAtLoad()) this.show()
91
96
  }
92
- if (!this._needsFetch && !this._umap.fields.size) {
93
- this.properties.fields = [
94
- { key: U.DEFAULT_LABEL_KEY, type: 'String' },
95
- { key: 'description', type: 'Text' },
96
- ]
97
- }
98
- this.fields = new FieldManager(this, this._umap.dialog)
99
- this.filters = new Filters(this, this._umap)
100
97
 
101
98
  // Only layers that are displayed on load must be hidden/shown
102
99
  // Automatically, others will be shown manually, and thus will
@@ -148,12 +145,6 @@ export class DataLayer {
148
145
  this.properties.rank = value
149
146
  }
150
147
 
151
- get fieldKeys() {
152
- // Needed to get a similar API from layer and uMap, but
153
- // uMap whould return concat of all datalayers fields
154
- return Array.from(this.fields.keys())
155
- }
156
-
157
148
  get sortKey() {
158
149
  return this.getProperty('sortKey') || U.DEFAULT_LABEL_KEY
159
150
  }
@@ -209,7 +200,7 @@ export class DataLayer {
209
200
  }
210
201
 
211
202
  // This method does a targeted update of the UI,
212
- // it whould be merged with `render`` method and the
203
+ // it would be merged with `render`` method and the
213
204
  // SCHEMA at some point
214
205
  propagate(fields = []) {
215
206
  const impacts = {
@@ -232,27 +223,28 @@ export class DataLayer {
232
223
  }
233
224
 
234
225
  showAtLoad() {
235
- return this.autoLoaded && this.showAtZoom()
226
+ return this.autoVisibility && this.showAtZoom()
236
227
  }
237
228
 
238
- get autoLoaded() {
239
- if (this._autoLoaded === undefined) {
229
+ get autoVisibility() {
230
+ if (this._autoVisibility === undefined) {
240
231
  if (this._umap.datalayersFromQueryString) {
241
232
  const datalayerIds = this._umap.datalayersFromQueryString
242
- this._autoLoaded = datalayerIds.includes(this.id.toString())
233
+ this._autoVisibility = datalayerIds.includes(this.id.toString())
243
234
  if (this.properties.old_id) {
244
- this._autoLoaded =
245
- this._autoLoaded || datalayerIds.includes(this.properties.old_id.toString())
235
+ this._autoVisibility =
236
+ this._autoVisibility ||
237
+ datalayerIds.includes(this.properties.old_id.toString())
246
238
  }
247
239
  } else {
248
- this._autoLoaded = this.properties.displayOnLoad
240
+ this._autoVisibility = this.properties.displayOnLoad
249
241
  }
250
242
  }
251
- return this._autoLoaded
243
+ return this._autoVisibility
252
244
  }
253
245
 
254
- set autoLoaded(value) {
255
- this._autoLoaded = value
246
+ set autoVisibility(value) {
247
+ this._autoVisibility = value
256
248
  }
257
249
 
258
250
  insertBefore(other) {
@@ -453,6 +445,10 @@ export class DataLayer {
453
445
  this._umap.featuresIndex[feature.getSlug()] = feature
454
446
  // TODO: quid for remote data ?
455
447
  this.inferFields(feature)
448
+ if (!this.fields.size && !this._umap.fields.size) {
449
+ this.properties.fields = getDefaultFields()
450
+ this.fields.pull()
451
+ }
456
452
  try {
457
453
  this.showFeature(feature)
458
454
  } catch (error) {
@@ -491,7 +487,7 @@ export class DataLayer {
491
487
 
492
488
  inferFields(feature) {
493
489
  for (const key in feature.properties) {
494
- if (typeof feature.properties[key] !== 'object') {
490
+ if (key && typeof feature.properties[key] !== 'object') {
495
491
  if (key.indexOf('_') === 0) continue
496
492
  if (this.fields.has(key)) continue
497
493
  if (this._umap.fields.has(key)) continue
@@ -529,12 +525,10 @@ export class DataLayer {
529
525
  this.features.forEach((feature) => callback(feature))
530
526
  }
531
527
 
532
- sortedValues(property) {
533
- return this.features
534
- .all()
535
- .map((feature) => feature.properties[property])
536
- .filter((val, idx, arr) => arr.indexOf(val) === idx)
537
- .sort(Utils.naturalSort)
528
+ sortedValues(key) {
529
+ const field = this.fields.get(key) || this._umap.fields.get(key)
530
+ if (!field) return []
531
+ return field.values(this.features.all()).sort(Utils.naturalSort)
538
532
  }
539
533
 
540
534
  addData(geojson, sync) {
@@ -779,7 +773,11 @@ export class DataLayer {
779
773
  },
780
774
  ],
781
775
  ]
782
- DomUtil.createTitle(container, translate('Layer properties'), 'icon-layers')
776
+ container.appendChild(
777
+ DOMUtils.loadTemplate(`
778
+ <h3><i class="icon icon-16 icon-layers"></i>${translate('Layer properties')}</h3>
779
+ `)
780
+ )
783
781
  const builder = new MutatingForm(this, metadataFields)
784
782
  builder.on('set', ({ detail }) => {
785
783
  this._umap.onDataLayersChanged()
@@ -825,7 +823,7 @@ export class DataLayer {
825
823
  const builder = new MutatingForm(this, fields, {
826
824
  id: 'datalayer-advanced-properties',
827
825
  })
828
- const shapeFieldset = DomUtil.createFieldset(
826
+ const shapeFieldset = DOMUtils.createFieldset(
829
827
  container,
830
828
  translate('Shape properties')
831
829
  )
@@ -850,7 +848,7 @@ export class DataLayer {
850
848
  this.reindex()
851
849
  }
852
850
  })
853
- const advancedFieldset = DomUtil.createFieldset(
851
+ const advancedFieldset = DOMUtils.createFieldset(
854
852
  container,
855
853
  translate('Advanced properties')
856
854
  )
@@ -869,7 +867,7 @@ export class DataLayer {
869
867
  'properties.interactive',
870
868
  ]
871
869
  const builder = new MutatingForm(this, fields)
872
- const popupFieldset = DomUtil.createFieldset(
870
+ const popupFieldset = DOMUtils.createFieldset(
873
871
  container,
874
872
  translate('Interaction options')
875
873
  )
@@ -887,7 +885,7 @@ export class DataLayer {
887
885
  'properties.textPathPosition',
888
886
  ]
889
887
  const builder = new MutatingForm(this, fields)
890
- const fieldset = DomUtil.createFieldset(container, translate('Line decoration'))
888
+ const fieldset = DOMUtils.createFieldset(container, translate('Line decoration'))
891
889
  fieldset.appendChild(builder.build())
892
890
  }
893
891
 
@@ -937,23 +935,21 @@ export class DataLayer {
937
935
  fields.push('properties.remoteData.ttl')
938
936
  }
939
937
 
940
- const remoteDataContainer = DomUtil.createFieldset(
938
+ const remoteDataContainer = DOMUtils.createFieldset(
941
939
  container,
942
940
  translate('Remote data')
943
941
  )
944
942
  const builder = new MutatingForm(this, fields)
945
943
  remoteDataContainer.appendChild(builder.build())
946
- DomUtil.createButton(
947
- 'button umap-verify',
948
- remoteDataContainer,
949
- translate('Verify remote URL'),
950
- () => this.fetchRemoteData(true),
951
- this
952
- )
944
+ const button = DOMUtils.loadTemplate(`
945
+ <button class="umap-verify" type="button">${translate('Verify remote URL')}</button>
946
+ `)
947
+ button.addEventListener('click', () => this.fetchRemoteData(true))
948
+ remoteDataContainer.appendChild(button)
953
949
  }
954
950
 
955
951
  _buildAdvancedActions(container) {
956
- const advancedActions = DomUtil.createFieldset(
952
+ const advancedActions = DOMUtils.createFieldset(
957
953
  container,
958
954
  translate('Advanced actions')
959
955
  )
@@ -993,7 +989,7 @@ export class DataLayer {
993
989
  if (!this._umap.editEnabled) {
994
990
  return
995
991
  }
996
- const container = DomUtil.create('div', 'umap-layer-properties-container')
992
+ const container = document.createElement('div')
997
993
  this._editMetadata(container)
998
994
  this._editLayerProperties(container)
999
995
  this._editShapeProperties(container)
@@ -1010,15 +1006,13 @@ export class DataLayer {
1010
1006
 
1011
1007
  this._buildAdvancedActions(container)
1012
1008
 
1013
- const backButton = DomUtil.createButtonIcon(
1014
- undefined,
1015
- 'icon-back',
1016
- translate('Back to layers')
1017
- )
1009
+ const backButton = DOMUtils.loadTemplate(`
1010
+ <button class="icon icon-16 icon-back" type="button" title="${translate('Back to layers')}"></button>
1011
+ `)
1018
1012
  // Fixme: remove me when this is merged and released
1019
1013
  // https://github.com/Leaflet/Leaflet/pull/9052
1020
1014
  DomEvent.disableClickPropagation(backButton)
1021
- DomEvent.on(backButton, 'click', this._umap.editDatalayers, this._umap)
1015
+ backButton.addEventListener('click', () => this._umap.editDatalayers())
1022
1016
 
1023
1017
  return this._umap.editPanel.open({
1024
1018
  content: container,
@@ -1069,14 +1063,14 @@ export class DataLayer {
1069
1063
  button.addEventListener('click', () => this.restore(data.ref))
1070
1064
  }
1071
1065
 
1072
- const versionsContainer = DomUtil.createFieldset(container, translate('Versions'), {
1073
- async callback() {
1066
+ const versionsContainer = DOMUtils.createFieldset(container, translate('Versions'))
1067
+ versionsContainer.closest('details').addEventListener('toggle', async (event) => {
1068
+ if (event.target.open) {
1074
1069
  const [{ versions }, response, error] = await this._umap.server.get(
1075
1070
  this.getVersionsUrl()
1076
1071
  )
1077
1072
  if (!error) versions.forEach(appendVersion)
1078
- },
1079
- context: this,
1073
+ }
1080
1074
  })
1081
1075
  }
1082
1076
 
@@ -1126,7 +1120,7 @@ export class DataLayer {
1126
1120
  // From now on, do not try to how/hide
1127
1121
  // automatically this layer, as user
1128
1122
  // has taken control on this.
1129
- this.autoLoaded = false
1123
+ this.autoVisibility = false
1130
1124
  let display = force
1131
1125
  if (force === undefined) {
1132
1126
  if (!this.isVisible()) display = true
@@ -1347,7 +1341,10 @@ export class DataLayer {
1347
1341
  rules.set(rule.condition, rule)
1348
1342
  }
1349
1343
  for (const rule of this._umap.rules) {
1350
- if (!rules.has(rule.condition) && this.fields.has(rule.key)) {
1344
+ if (
1345
+ !rules.has(rule.condition) &&
1346
+ (this.fields.has(rule.field.key) || this._umap.fields.has(rule.field.key))
1347
+ ) {
1351
1348
  rules.set(rule.condition, rule)
1352
1349
  }
1353
1350
  }
@@ -1366,31 +1363,17 @@ export class DataLayer {
1366
1363
  }
1367
1364
 
1368
1365
  renderToolbox(container) {
1369
- const toggle = DomUtil.createButtonIcon(
1370
- container,
1371
- 'icon-eye',
1372
- translate('Show/hide layer')
1373
- )
1374
- const table = DomUtil.createButtonIcon(
1375
- container,
1376
- 'icon-table show-on-edit',
1377
- translate('Edit properties in a table')
1378
- )
1379
- const zoomTo = DomUtil.createButtonIcon(
1380
- container,
1381
- 'icon-zoom',
1382
- translate('Zoom to layer extent')
1383
- )
1384
- const edit = DomUtil.createButtonIcon(
1385
- container,
1386
- 'icon-edit show-on-edit',
1387
- translate('Edit')
1388
- )
1389
- const remove = DomUtil.createButtonIcon(
1390
- container,
1391
- 'icon-delete show-on-edit',
1392
- translate('Delete layer')
1393
- )
1366
+ const [span, { toggle, table, zoomTo, edit, remove }] =
1367
+ DOMUtils.loadTemplateWithRefs(`
1368
+ <span>
1369
+ <button class="icon icon-16 icon-eye" title="${translate('Show/hide layer')}" type="button" data-ref="toggle"></button>
1370
+ <button class="icon icon-16 icon-table show-on-edit" title="${translate('Edit properties in a table')}" type="button" data-ref="table"></button>
1371
+ <button class="icon icon-16 icon-zoom" title="${translate('Zoom to layer extent')}" type="button" data-ref="zoomTo"></button>
1372
+ <button class="icon icon-16 icon-edit show-on-edit" title="${translate('Edit')}" type="button" data-ref="edit"></button>
1373
+ <button class="icon icon-16 icon-delete show-on-edit" title="${translate('Delete layer')}" type="button" data-ref="remove"></button>
1374
+ </span>
1375
+ `)
1376
+ container.appendChild(span)
1394
1377
  if (this.isReadOnly()) {
1395
1378
  container.classList.add('readonly')
1396
1379
  } else {
@@ -1414,7 +1397,7 @@ export class DataLayer {
1414
1397
  propagateDelete() {
1415
1398
  const els = this.getHidableElements()
1416
1399
  for (const el of els) {
1417
- DomUtil.remove(el)
1400
+ el.remove()
1418
1401
  }
1419
1402
  }
1420
1403
 
@@ -1427,15 +1410,15 @@ export class DataLayer {
1427
1410
 
1428
1411
  propagateHide() {
1429
1412
  const els = this.getHidableElements()
1430
- for (let i = 0; i < els.length; i++) {
1431
- DomUtil.addClass(els[i], 'off')
1413
+ for (const el of els) {
1414
+ el.classList.add('off')
1432
1415
  }
1433
1416
  }
1434
1417
 
1435
1418
  propagateShow() {
1436
1419
  const els = this.getHidableElements()
1437
- for (let i = 0; i < els.length; i++) {
1438
- DomUtil.removeClass(els[i], 'off')
1420
+ for (const el of els) {
1421
+ el.classList.remove('off')
1439
1422
  }
1440
1423
  }
1441
1424
  }
@@ -32,7 +32,7 @@ export const copyToClipboard = (textToCopy) => {
32
32
  tooltip.open({ content: translate('✅ Copied!'), duration: 5000 })
33
33
  }
34
34
 
35
- export const copiableInput = (parent, label, value) => {
35
+ export const copiableInput = (parent, label, value = '') => {
36
36
  const [container, { input, button }] = Utils.loadTemplateWithRefs(`
37
37
  <div class="copiable-input">
38
38
  <label>${label}<input type="text" readOnly value="${value}" data-ref=input /></label>
@@ -83,11 +83,11 @@ export const hexToRGB = (hex) => {
83
83
  .map((x) => Number.parseInt(x, 16))
84
84
  }
85
85
 
86
- const CACHE_CONSTRAST = {}
86
+ const CACHE_CONTRAST = {}
87
87
  export const contrastedColor = (el, bgcolor) => {
88
88
  // Return 0 for black and 1 for white
89
89
  // bgcolor is a human color, it can be a any keyword (purple…)
90
- if (typeof CACHE_CONSTRAST[bgcolor] !== 'undefined') return CACHE_CONSTRAST[bgcolor]
90
+ if (typeof CACHE_CONTRAST[bgcolor] !== 'undefined') return CACHE_CONTRAST[bgcolor]
91
91
  let rgb = window.getComputedStyle(el).getPropertyValue('background-color')
92
92
  rgb = RGBRegex.exec(rgb)
93
93
  if (rgb && rgb.length === 4) {
@@ -104,6 +104,26 @@ export const contrastedColor = (el, bgcolor) => {
104
104
  }
105
105
  if (!rgb) return 1
106
106
  const out = contrastWCAG21(rgb)
107
- if (bgcolor) CACHE_CONSTRAST[bgcolor] = out
107
+ if (bgcolor) CACHE_CONTRAST[bgcolor] = out
108
108
  return out
109
109
  }
110
+
111
+ export const createFieldset = (parent, title, options) => {
112
+ options = options || {}
113
+ const [details, { summary, fieldset }] = loadTemplateWithRefs(`
114
+ <details class="${options.className || ''}">
115
+ <summary data-ref="summary"><h4>${title}</h4></summary>
116
+ <fieldset data-ref="fieldset"></fieldset>
117
+ </details>
118
+ `)
119
+ details.open = options.on === true
120
+ parent.appendChild(details)
121
+ if (options.icon) {
122
+ const icon = loadTemplate(`<i class="icon icon-16 ${options.icon}"></i>`)
123
+ summary.insertBefore(icon, summary.firstChild)
124
+ }
125
+ return fieldset
126
+ }
127
+
128
+ export const loadTemplateWithRefs = Utils.loadTemplateWithRefs
129
+ export const loadTemplate = Utils.loadTemplate
@@ -6,27 +6,6 @@ import { Fields } from './form/fields.js'
6
6
 
7
7
  const EMPTY_VALUE = translate('<empty value>')
8
8
 
9
- const getParser = (type) => {
10
- switch (type) {
11
- case 'Number':
12
- return Number.parseFloat
13
- case 'Datetime':
14
- return (v) => new Date(v)
15
- case 'Date':
16
- return Utils.parseNaiveDate
17
- case 'Boolean':
18
- return Boolean
19
- case 'Enum':
20
- return (v) => {
21
- if (!v) return [EMPTY_VALUE]
22
- return String(v || '')
23
- .split(',')
24
- .map((s) => s.trim())
25
- }
26
- default:
27
- return (v) => String(v || '')
28
- }
29
- }
30
9
  const Widgets = {}
31
10
 
32
11
  class BaseWidget {
@@ -73,13 +52,13 @@ Widgets.MinMax = class extends BaseWidget {
73
52
  return this.userData.min !== undefined || this.userData.max !== undefined
74
53
  }
75
54
  getFormField(field) {
76
- if (field.type === 'Number') {
55
+ if (field.TYPE === 'Number') {
77
56
  return 'FilterByNumber'
78
57
  }
79
- if (field.type === 'Date') {
58
+ if (field.TYPE === 'Date') {
80
59
  return 'FilterByDate'
81
60
  }
82
- if (field.type === 'Datetime') {
61
+ if (field.TYPE === 'Datetime') {
83
62
  return 'FilterByDateTime'
84
63
  }
85
64
  return super.getFormField(field)
@@ -179,23 +158,22 @@ export class Filters {
179
158
  }
180
159
 
181
160
  isActive() {
182
- return this.available.values().some((obj) => obj.isActive())
161
+ return Array.from(this.available.values()).some((obj) => obj.isActive())
183
162
  }
184
163
 
185
164
  // Loop on the data to compute the list of choices, min
186
165
  // and max values.
187
166
  computeInitialData() {
188
167
  const initialData = Object.fromEntries(
189
- this.available.keys().map((name) => [name, {}])
168
+ Array.from(this.available.keys()).map((name) => [name, {}])
190
169
  )
191
170
 
192
171
  for (const [name, filter] of this.available.entries()) {
193
172
  const field = this._parent.fields.get(name)
194
173
  if (!field) continue
195
- const parser = getParser(field.type)
196
174
  this._parent.eachFeature((feature) => {
197
175
  let value = feature.properties[name]
198
- value = parser(value)
176
+ value = field.parse(value)
199
177
  filter.computeInitialData(initialData[name], value)
200
178
  })
201
179
  }
@@ -228,14 +206,8 @@ export class Filters {
228
206
  }
229
207
 
230
208
  load() {
231
- let filters = this._parent.properties.filters || []
232
- // TMP fix for dev server to update map created before changing
233
- // filters to be an array
234
- if (typeof filters === 'object' && !Array.isArray(filters) && filters !== null) {
235
- filters = Object.entries(filters).map(([fieldKey, props]) => {
236
- return { fieldKey, ...props }
237
- })
238
- }
209
+ let filters = this._parent.properties.filters
210
+ if (!Array.isArray(filters)) filters = []
239
211
  for (const filter of filters) {
240
212
  this._add({ ...filter })
241
213
  }
@@ -306,7 +278,7 @@ export class Filters {
306
278
 
307
279
  _listFilters(filters, container, title) {
308
280
  const template = `
309
- <details open>
281
+ <details>
310
282
  <summary>${title}</summary>
311
283
  <ul data-ref=ul></ul>
312
284
  <div>
@@ -322,9 +294,8 @@ export class Filters {
322
294
  `<li>${translate('Add a field prior to create a filter.')}</li>`
323
295
  )
324
296
  )
325
- }
326
- if (!filters.size) {
327
- body.open = false
297
+ } else if (!filters._parent.fields.isDefault()) {
298
+ body.open = true
328
299
  }
329
300
  filters.available.forEach((filter, fieldKey) => {
330
301
  const [li, { edit, remove }] = Utils.loadTemplateWithRefs(
@@ -378,9 +349,9 @@ export class Filters {
378
349
  createFilterForm(fieldKey) {
379
350
  let widget = 'Checkbox'
380
351
  const field = this._parent.fields.get(fieldKey)
381
- if (['Number', 'Date', 'Datetime'].includes(field?.type)) {
352
+ if (['Number', 'Date', 'Datetime'].includes(field?.TYPE)) {
382
353
  widget = 'MinMax'
383
- } else if (field?.type === 'Boolean') {
354
+ } else if (field?.TYPE === 'Boolean') {
384
355
  widget = 'Switch'
385
356
  }
386
357
  const properties = {
@@ -393,7 +364,9 @@ export class Filters {
393
364
  ? [fieldKey]
394
365
  : [
395
366
  '',
396
- ...this._parent.fieldKeys.filter((fieldKey) => !this.available.has(fieldKey)),
367
+ ...Array.from(this._parent.fields.keys()).filter(
368
+ (fieldKey) => !this.available.has(fieldKey)
369
+ ),
397
370
  ]
398
371
  const metadata = [
399
372
  [
@@ -442,8 +415,9 @@ export class Filters {
442
415
  `)
443
416
  body.appendChild(form.build())
444
417
  editField.addEventListener('click', () => {
445
- this._umap.dialog.accept()
446
- this._parent.fields.editField(fieldKey)
418
+ this._umap.dialog.accept().then(() => {
419
+ this._parent.fields.editField(fieldKey)
420
+ })
447
421
  })
448
422
 
449
423
  return this._umap.dialog.open({ template: container }).then(() => {
@@ -474,8 +448,7 @@ export class Filters {
474
448
  // This field may only exist on another layer.
475
449
  if (!field) continue
476
450
  let value = feature.properties[fieldKey]
477
- const parser = getParser(field.type)
478
- value = parser(value)
451
+ value = field.parse(value)
479
452
  if (obj.match(value)) return true
480
453
  }
481
454
  return false
@@ -746,7 +746,7 @@ Fields.PropertyInput = class extends Fields.BlurInput {
746
746
  super.build()
747
747
  const autocomplete = new AutocompleteDatalist(this.input)
748
748
  // Will be used on Umap and DataLayer
749
- const properties = this.builder.obj.fieldKeys
749
+ const properties = Array.from(this.builder.obj.fields.keys())
750
750
  autocomplete.suggestions = properties
751
751
  }
752
752
  }
@@ -1217,7 +1217,7 @@ Fields.ManageOwner = class extends Fields.Base {
1217
1217
  super.build()
1218
1218
  const options = {
1219
1219
  className: 'edit-owner',
1220
- on_select: L.bind(this.onSelect, this),
1220
+ on_select: (choice) => this.onSelect(choice),
1221
1221
  placeholder: translate("Type new owner's username"),
1222
1222
  }
1223
1223
  this.autocomplete = new AjaxAutocomplete(this.container, options)
@@ -1248,8 +1248,8 @@ Fields.ManageEditors = class extends Fields.Base {
1248
1248
  super.build()
1249
1249
  const options = {
1250
1250
  className: 'edit-editors',
1251
- on_select: L.bind(this.onSelect, this),
1252
- on_unselect: L.bind(this.onUnselect, this),
1251
+ on_select: (choice) => this.onSelect(choice),
1252
+ on_unselect: (choice) => this.onUnselect(choice),
1253
1253
  placeholder: translate("Type editor's username"),
1254
1254
  }
1255
1255
  this.autocomplete = new AjaxAutocompleteMultiple(this.container, options)
@@ -71,7 +71,15 @@ export class Formatter {
71
71
  } catch (e) {
72
72
  src = this.toDom(str)
73
73
  }
74
- return osmtogeojson(src, { flatProperties: true })
74
+ const data = osmtogeojson(src, { flatProperties: true })
75
+ // FIXME: make a PR to osmtogeojson when it's more active
76
+ // cf https://github.com/umap-project/umap/issues/3072
77
+ for (const feature of data.features || []) {
78
+ const [osm_type, osm_id] = feature.properties.id.split('/')
79
+ feature.properties.osm_id = osm_id
80
+ feature.properties.osm_type = osm_type
81
+ }
82
+ return data
75
83
  }
76
84
 
77
85
  fromCSV(str, callback) {
@@ -1,7 +1,7 @@
1
- import { DomEvent, DomUtil } from '../../vendors/leaflet/leaflet-src.esm.js'
2
1
  import { translate } from './i18n.js'
3
2
  import Dialog from './ui/dialog.js'
4
3
  import * as Utils from './utils.js'
4
+ import * as DOMUtils from './domutils.js'
5
5
 
6
6
  const SHORTCUTS = {
7
7
  DRAW_MARKER: {
@@ -118,7 +118,7 @@ const ENTRIES = {
118
118
  <dt>KML</dt>
119
119
  <dd>${translate('Properties imported:')}name, description</dd>
120
120
  <dt>CSV</dt>
121
- <dd>${translate('Comma, tab or semi-colon separated values. SRS WGS84 is implied. Only Point geometries are imported. The import will look at the column headers for any mention of «lat» and «lon» at the begining of the header, case insensitive. All other column are imported as properties.')}</dd>
121
+ <dd>${translate('Comma, tab or semi-colon separated values. SRS WGS84 is implied. Only Point geometries are imported. The import will look at the column headers for any mention of «lat» and «lon» at the beginning of the header, case insensitive. All other column are imported as properties.')}</dd>
122
122
  <dt>uMap</dt>
123
123
  <dd>${translate('Imports all umap data, including layers and settings.')}</dd>
124
124
  </div>
@@ -209,15 +209,15 @@ export default class Help {
209
209
  }
210
210
 
211
211
  show(entries) {
212
- const container = DomUtil.add('div')
213
- DomUtil.createTitle(container, translate('Help'))
212
+ const container = DOMUtils.loadTemplate(`
213
+ <div>
214
+ <h3><i class="icon icon-16 icon-help"></i> ${translate('Help')}</h3>
215
+ </div>
216
+ `)
214
217
  for (const name of entries) {
215
- DomUtil.element({
216
- tagName: 'div',
217
- className: 'umap-help-entry',
218
- parent: container,
219
- innerHTML: ENTRIES[name],
220
- })
218
+ container.appendChild(
219
+ DOMUtils.loadTemplate(`<div class="umap-help-entry">${ENTRIES[name]}</div>`)
220
+ )
221
221
  }
222
222
  this.dialog.open({ template: container })
223
223
  }
@@ -253,11 +253,10 @@ export default class Help {
253
253
  }
254
254
 
255
255
  button(container, entries) {
256
- const button = DomUtil.createButton(
257
- 'umap-help-button',
258
- container,
259
- translate('Help')
256
+ const button = DOMUtils.loadTemplate(
257
+ `<button class="umap-help-button" type="button">${translate('Help')}</button>`
260
258
  )
259
+ container.appendChild(button)
261
260
  button.addEventListener('click', () => this.show(entries))
262
261
  return button
263
262
  }