umap-project 2.4.2__py3-none-any.whl → 2.5.1__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 (177) hide show
  1. umap/__init__.py +1 -1
  2. umap/locale/el/LC_MESSAGES/django.mo +0 -0
  3. umap/locale/eu/LC_MESSAGES/django.mo +0 -0
  4. umap/locale/hu/LC_MESSAGES/django.mo +0 -0
  5. umap/locale/hu/LC_MESSAGES/django.po +100 -50
  6. umap/static/umap/base.css +4 -1
  7. umap/static/umap/css/contextmenu.css +11 -0
  8. umap/static/umap/css/dialog.css +24 -3
  9. umap/static/umap/css/panel.css +4 -2
  10. umap/static/umap/css/slideshow.css +69 -0
  11. umap/static/umap/css/tableeditor.css +69 -0
  12. umap/static/umap/css/tooltip.css +3 -3
  13. umap/static/umap/img/16-white.svg +4 -0
  14. umap/static/umap/img/source/16-white.svg +5 -1
  15. umap/static/umap/js/components/alerts/alert.css +10 -10
  16. umap/static/umap/js/modules/autocomplete.js +23 -1
  17. umap/static/umap/js/modules/browser.js +14 -8
  18. umap/static/umap/js/modules/facets.js +40 -10
  19. umap/static/umap/js/modules/formatter.js +153 -0
  20. umap/static/umap/js/modules/global.js +10 -1
  21. umap/static/umap/js/modules/help.js +25 -25
  22. umap/static/umap/js/modules/importer.js +4 -4
  23. umap/static/umap/js/modules/importers/communesfr.js +3 -1
  24. umap/static/umap/js/modules/importers/datasets.js +8 -6
  25. umap/static/umap/js/modules/importers/geodatamine.js +10 -10
  26. umap/static/umap/js/modules/importers/overpass.js +18 -14
  27. umap/static/umap/js/modules/rules.js +13 -1
  28. umap/static/umap/js/modules/schema.js +16 -12
  29. umap/static/umap/js/{umap.share.js → modules/share.js} +60 -99
  30. umap/static/umap/js/modules/slideshow.js +141 -0
  31. umap/static/umap/js/modules/tableeditor.js +329 -0
  32. umap/static/umap/js/modules/ui/base.js +93 -0
  33. umap/static/umap/js/modules/ui/contextmenu.js +50 -0
  34. umap/static/umap/js/modules/ui/dialog.js +169 -31
  35. umap/static/umap/js/modules/ui/panel.js +6 -4
  36. umap/static/umap/js/modules/ui/tooltip.js +5 -75
  37. umap/static/umap/js/modules/utils.js +20 -0
  38. umap/static/umap/js/umap.controls.js +1 -1
  39. umap/static/umap/js/umap.features.js +22 -14
  40. umap/static/umap/js/umap.forms.js +157 -154
  41. umap/static/umap/js/umap.js +48 -34
  42. umap/static/umap/js/umap.layer.js +232 -164
  43. umap/static/umap/js/umap.permissions.js +1 -1
  44. umap/static/umap/js/umap.popup.js +1 -1
  45. umap/static/umap/locale/am_ET.js +22 -5
  46. umap/static/umap/locale/am_ET.json +19 -5
  47. umap/static/umap/locale/ar.js +22 -5
  48. umap/static/umap/locale/ar.json +19 -5
  49. umap/static/umap/locale/ast.js +22 -5
  50. umap/static/umap/locale/ast.json +19 -5
  51. umap/static/umap/locale/bg.js +22 -5
  52. umap/static/umap/locale/bg.json +19 -5
  53. umap/static/umap/locale/br.js +22 -5
  54. umap/static/umap/locale/br.json +19 -5
  55. umap/static/umap/locale/ca.js +56 -39
  56. umap/static/umap/locale/ca.json +53 -39
  57. umap/static/umap/locale/cs_CZ.js +22 -5
  58. umap/static/umap/locale/cs_CZ.json +19 -5
  59. umap/static/umap/locale/da.js +22 -5
  60. umap/static/umap/locale/da.json +19 -5
  61. umap/static/umap/locale/de.js +22 -5
  62. umap/static/umap/locale/de.json +19 -5
  63. umap/static/umap/locale/el.js +27 -10
  64. umap/static/umap/locale/el.json +19 -5
  65. umap/static/umap/locale/en.js +22 -6
  66. umap/static/umap/locale/en.json +19 -5
  67. umap/static/umap/locale/en_US.json +19 -5
  68. umap/static/umap/locale/es.js +22 -6
  69. umap/static/umap/locale/es.json +19 -5
  70. umap/static/umap/locale/et.js +22 -5
  71. umap/static/umap/locale/et.json +19 -5
  72. umap/static/umap/locale/eu.js +167 -150
  73. umap/static/umap/locale/eu.json +167 -150
  74. umap/static/umap/locale/fa_IR.js +22 -5
  75. umap/static/umap/locale/fa_IR.json +19 -5
  76. umap/static/umap/locale/fi.js +22 -5
  77. umap/static/umap/locale/fi.json +19 -5
  78. umap/static/umap/locale/fr.js +22 -6
  79. umap/static/umap/locale/fr.json +19 -5
  80. umap/static/umap/locale/gl.js +22 -5
  81. umap/static/umap/locale/gl.json +19 -5
  82. umap/static/umap/locale/he.js +22 -5
  83. umap/static/umap/locale/he.json +19 -5
  84. umap/static/umap/locale/hr.js +22 -5
  85. umap/static/umap/locale/hr.json +19 -5
  86. umap/static/umap/locale/hu.js +89 -72
  87. umap/static/umap/locale/hu.json +89 -75
  88. umap/static/umap/locale/id.js +22 -5
  89. umap/static/umap/locale/id.json +19 -5
  90. umap/static/umap/locale/is.js +22 -5
  91. umap/static/umap/locale/is.json +19 -5
  92. umap/static/umap/locale/it.js +22 -5
  93. umap/static/umap/locale/it.json +19 -5
  94. umap/static/umap/locale/ja.js +22 -5
  95. umap/static/umap/locale/ja.json +19 -5
  96. umap/static/umap/locale/ko.js +22 -5
  97. umap/static/umap/locale/ko.json +19 -5
  98. umap/static/umap/locale/lt.js +22 -5
  99. umap/static/umap/locale/lt.json +19 -5
  100. umap/static/umap/locale/ms.js +22 -5
  101. umap/static/umap/locale/ms.json +19 -5
  102. umap/static/umap/locale/nl.js +22 -5
  103. umap/static/umap/locale/nl.json +19 -5
  104. umap/static/umap/locale/no.js +22 -5
  105. umap/static/umap/locale/no.json +19 -5
  106. umap/static/umap/locale/pl.js +22 -5
  107. umap/static/umap/locale/pl.json +19 -5
  108. umap/static/umap/locale/pl_PL.json +19 -5
  109. umap/static/umap/locale/pt.js +22 -6
  110. umap/static/umap/locale/pt.json +21 -7
  111. umap/static/umap/locale/pt_BR.js +22 -5
  112. umap/static/umap/locale/pt_BR.json +19 -5
  113. umap/static/umap/locale/pt_PT.js +22 -5
  114. umap/static/umap/locale/pt_PT.json +19 -5
  115. umap/static/umap/locale/ro.js +22 -5
  116. umap/static/umap/locale/ro.json +19 -5
  117. umap/static/umap/locale/ru.js +22 -5
  118. umap/static/umap/locale/ru.json +19 -5
  119. umap/static/umap/locale/sk_SK.js +22 -5
  120. umap/static/umap/locale/sk_SK.json +19 -5
  121. umap/static/umap/locale/sl.js +22 -5
  122. umap/static/umap/locale/sl.json +19 -5
  123. umap/static/umap/locale/sr.js +22 -5
  124. umap/static/umap/locale/sr.json +19 -5
  125. umap/static/umap/locale/sv.js +22 -5
  126. umap/static/umap/locale/sv.json +19 -5
  127. umap/static/umap/locale/th_TH.js +22 -5
  128. umap/static/umap/locale/th_TH.json +19 -5
  129. umap/static/umap/locale/tr.js +22 -5
  130. umap/static/umap/locale/tr.json +19 -5
  131. umap/static/umap/locale/uk_UA.js +22 -5
  132. umap/static/umap/locale/uk_UA.json +19 -5
  133. umap/static/umap/locale/vi.js +22 -5
  134. umap/static/umap/locale/vi.json +19 -5
  135. umap/static/umap/locale/vi_VN.json +19 -5
  136. umap/static/umap/locale/zh.js +22 -5
  137. umap/static/umap/locale/zh.json +19 -5
  138. umap/static/umap/locale/zh_CN.json +19 -5
  139. umap/static/umap/locale/zh_TW.Big5.json +19 -5
  140. umap/static/umap/locale/zh_TW.js +22 -5
  141. umap/static/umap/locale/zh_TW.json +19 -5
  142. umap/static/umap/map.css +2 -145
  143. umap/static/umap/vars.css +5 -0
  144. umap/static/umap/vendors/formbuilder/Leaflet.FormBuilder.js +410 -428
  145. umap/static/umap/vendors/geojson-to-gpx/index.js +155 -0
  146. umap/static/umap/vendors/osmtogeojson/osmtogeojson.js +1 -2
  147. umap/static/umap/vendors/togeojson/togeojson.es.js +1109 -0
  148. umap/static/umap/vendors/togeojson/{togeojson.umd.js.map → togeojson.es.mjs.map} +1 -1
  149. umap/static/umap/vendors/tokml/tokml.es.js +895 -0
  150. umap/static/umap/vendors/tokml/tokml.es.mjs.map +1 -0
  151. umap/storage.py +6 -2
  152. umap/templates/umap/components/alerts/alert.html +3 -3
  153. umap/templates/umap/css.html +3 -0
  154. umap/templates/umap/js.html +0 -6
  155. umap/tests/fixtures/categorized_highway.geojson +1 -0
  156. umap/tests/fixtures/test_import_osm_relation.json +130 -0
  157. umap/tests/integration/conftest.py +8 -1
  158. umap/tests/integration/test_browser.py +3 -2
  159. umap/tests/integration/test_categorized_layer.py +141 -0
  160. umap/tests/integration/test_conditional_rules.py +21 -0
  161. umap/tests/integration/test_datalayer.py +9 -4
  162. umap/tests/integration/test_edit_datalayer.py +1 -0
  163. umap/tests/integration/test_edit_polygon.py +1 -1
  164. umap/tests/integration/test_export_map.py +2 -3
  165. umap/tests/integration/test_import.py +22 -0
  166. umap/tests/integration/test_tableeditor.py +158 -4
  167. umap/tests/integration/test_websocket_sync.py +2 -2
  168. {umap_project-2.4.2.dist-info → umap_project-2.5.1.dist-info}/METADATA +8 -8
  169. {umap_project-2.4.2.dist-info → umap_project-2.5.1.dist-info}/RECORD +172 -162
  170. umap/static/umap/js/umap.slideshow.js +0 -163
  171. umap/static/umap/js/umap.tableeditor.js +0 -118
  172. umap/static/umap/vendors/togeojson/togeojson.umd.js +0 -2
  173. umap/static/umap/vendors/togpx/togpx.js +0 -547
  174. umap/static/umap/vendors/tokml/tokml.js +0 -343
  175. {umap_project-2.4.2.dist-info → umap_project-2.5.1.dist-info}/WHEEL +0 -0
  176. {umap_project-2.4.2.dist-info → umap_project-2.5.1.dist-info}/entry_points.txt +0 -0
  177. {umap_project-2.4.2.dist-info → umap_project-2.5.1.dist-info}/licenses/LICENSE +0 -0
@@ -1,4 +1,4 @@
1
- [role="dialog"] {
1
+ .umap-alert[role="dialog"] {
2
2
  box-sizing: border-box;
3
3
  min-height: 46px;
4
4
  line-height: 46px;
@@ -20,36 +20,36 @@
20
20
  width: max-content;
21
21
  z-index: var(--zindex-alert);
22
22
  }
23
- [role="dialog"] > div {
23
+ .umap-alert[role="dialog"] > div {
24
24
  margin: 0 auto;
25
25
  min-width: 60%;
26
26
  background-size: 20px;
27
27
  background-position: 0 15px;
28
28
  padding-left: 28px;
29
29
  }
30
- [role="dialog"][data-level="info"] > div {
30
+ .umap-alert[role="dialog"][data-level="info"] > div {
31
31
  background-image: url('../../../img/alert-icon-info.svg');
32
32
  background-repeat: no-repeat;
33
33
  }
34
- [role="dialog"][data-level="success"] > div {
34
+ .umap-alert[role="dialog"][data-level="success"] > div {
35
35
  background-image: url('../../../img/alert-icon-success.svg');
36
36
  background-repeat: no-repeat;
37
37
  }
38
- [role="dialog"][data-level="error"] > div {
38
+ .umap-alert[role="dialog"][data-level="error"] > div {
39
39
  background-image: url('../../../img/alert-icon-error.svg');
40
40
  background-repeat: no-repeat;
41
41
  }
42
- [role="dialog"][data-level="error"] {
42
+ .umap-alert[role="dialog"][data-level="error"] {
43
43
  background-color: var(--color-darkRed);
44
44
  }
45
- [role="dialog"] a {
45
+ .umap-alert[role="dialog"] a {
46
46
  text-decoration: underline;
47
47
  }
48
- [role="dialog"] label {
48
+ .umap-alert[role="dialog"] label {
49
49
  font-size: .8rem;
50
50
  font-weight: normal;
51
51
  }
52
- [role="dialog"] a[target="_blank"] {
52
+ .umap-alert[role="dialog"] a[target="_blank"] {
53
53
  background: url('../../../img/icon-external-link.svg') no-repeat right center;
54
54
  padding-right: 14px;
55
55
  background-size: 12px;
@@ -127,7 +127,7 @@ h3[role="alert"] + p {
127
127
  #link-wrapper {
128
128
  margin-bottom: 1rem;
129
129
  }
130
- [role="dialog"] #conflict-wrapper a[target="_blank"] {
130
+ .umap-alert[role="dialog"] #conflict-wrapper a[target="_blank"] {
131
131
  background-position-y: 16px;
132
132
  }
133
133
 
@@ -6,6 +6,7 @@ import {
6
6
  } from '../../vendors/leaflet/leaflet-src.esm.js'
7
7
  import { translate } from './i18n.js'
8
8
  import { Request, ServerRequest } from './request.js'
9
+ import { escapeHTML, generateId } from './utils.js'
9
10
 
10
11
  export class BaseAutocomplete {
11
12
  constructor(el, options) {
@@ -16,6 +17,7 @@ export class BaseAutocomplete {
16
17
  allowFree: true,
17
18
  minChar: 2,
18
19
  maxResults: 5,
20
+ throttling: 300,
19
21
  }
20
22
  this.cache = ''
21
23
  this.results = []
@@ -45,6 +47,7 @@ export class BaseAutocomplete {
45
47
  placeholder: this.options.placeholder,
46
48
  autocomplete: 'off',
47
49
  className: this.options.className,
50
+ name: this.options.name || 'autocomplete',
48
51
  })
49
52
  DomEvent.on(this.input, 'keydown', this.onKeyDown, this)
50
53
  DomEvent.on(this.input, 'keyup', this.onKeyUp, this)
@@ -125,7 +128,10 @@ export class BaseAutocomplete {
125
128
  'Control',
126
129
  ]
127
130
  if (!special.includes(e.key)) {
128
- this.search()
131
+ if (this._typing) window.clearTimeout(this._typing)
132
+ this._typing = window.setTimeout(() => {
133
+ this.search()
134
+ }, this.options.throttling)
129
135
  }
130
136
  }
131
137
 
@@ -345,3 +351,19 @@ export const MultipleMixin = (Base) =>
345
351
  export class AjaxAutocompleteMultiple extends MultipleMixin(BaseServerAjax) {}
346
352
 
347
353
  export class AjaxAutocomplete extends SingleMixin(BaseServerAjax) {}
354
+
355
+ export class AutocompleteDatalist {
356
+ constructor(input) {
357
+ this.input = input
358
+ this.datalist = document.createElement('datalist')
359
+ this.datalist.id = generateId()
360
+ this.input.setAttribute('list', this.datalist.id)
361
+ this.input.parentElement.appendChild(this.datalist)
362
+ }
363
+
364
+ set suggestions(values) {
365
+ this.datalist.innerHTML = values
366
+ .map((value) => `<option>${escapeHTML(value)}</option>`)
367
+ .join('')
368
+ }
369
+ }
@@ -37,7 +37,7 @@ export default class Browser {
37
37
  ? U.Icon.prototype.formatUrl(feature._getIconUrl(), feature)
38
38
  : null
39
39
  title.textContent = feature.getDisplayName() || '—'
40
- const bgcolor = feature.getDynamicOption('color')
40
+ const bgcolor = feature.getPreviewColor()
41
41
  colorBox.style.backgroundColor = bgcolor
42
42
  if (symbol && symbol !== U.SCHEMA.iconUrl.default) {
43
43
  const icon = U.Icon.makeIconElement(symbol, colorBox)
@@ -107,6 +107,7 @@ export default class Browser {
107
107
  this.map.eachBrowsableDataLayer((datalayer) => {
108
108
  datalayer.resetLayer(true)
109
109
  this.updateDatalayer(datalayer)
110
+ if (this.map.fullPanel?.isOpen()) datalayer.tableEdit()
110
111
  })
111
112
  this.toggleBadge()
112
113
  }
@@ -149,7 +150,7 @@ export default class Browser {
149
150
  DomEvent.disableClickPropagation(container)
150
151
 
151
152
  DomUtil.createTitle(container, translate('Data browser'), 'icon-layers')
152
- const formContainer = DomUtil.createFieldset(container, L._('Filters'), {
153
+ this.formContainer = DomUtil.createFieldset(container, L._('Filters'), {
153
154
  on: this.mode === 'filters',
154
155
  className: 'filters',
155
156
  icon: 'icon-filters',
@@ -169,7 +170,7 @@ export default class Browser {
169
170
  callback: () => this.onFormChange(),
170
171
  })
171
172
  let filtersBuilder
172
- formContainer.appendChild(builder.build())
173
+ this.formContainer.appendChild(builder.build())
173
174
  DomEvent.on(builder.form, 'reset', () => {
174
175
  window.setTimeout(builder.syncAll.bind(builder))
175
176
  })
@@ -181,12 +182,11 @@ export default class Browser {
181
182
  DomEvent.on(filtersBuilder.form, 'reset', () => {
182
183
  window.setTimeout(filtersBuilder.syncAll.bind(filtersBuilder))
183
184
  })
184
- formContainer.appendChild(filtersBuilder.build())
185
+ this.formContainer.appendChild(filtersBuilder.build())
185
186
  }
186
- const reset = DomUtil.createButton('flat', formContainer, '', () => {
187
- builder.form.reset()
188
- if (filtersBuilder) filtersBuilder.form.reset()
189
- })
187
+ const reset = DomUtil.createButton('flat', this.formContainer, '', () =>
188
+ this.resetFilters()
189
+ )
190
190
  DomUtil.createIcon(reset, 'icon-restore')
191
191
  DomUtil.element({
192
192
  tagName: 'span',
@@ -202,6 +202,12 @@ export default class Browser {
202
202
  this.update()
203
203
  }
204
204
 
205
+ resetFilters() {
206
+ for (const form of this.formContainer?.querySelectorAll('form') || []) {
207
+ form.reset()
208
+ }
209
+ }
210
+
205
211
  static backButton(map) {
206
212
  const button = DomUtil.createButtonIcon(
207
213
  DomUtil.create('li', '', undefined),
@@ -12,8 +12,8 @@ export default class Facets {
12
12
  const properties = {}
13
13
  let selected
14
14
 
15
- names.forEach((name) => {
16
- const type = defined[name].type
15
+ for (const name of names) {
16
+ const type = defined.get(name).type
17
17
  properties[name] = { type: type }
18
18
  selected = this.selected[name] || {}
19
19
  selected.type = type
@@ -22,13 +22,13 @@ export default class Facets {
22
22
  selected.choices = selected.choices || []
23
23
  }
24
24
  this.selected[name] = selected
25
- })
25
+ }
26
26
 
27
27
  this.map.eachBrowsableDataLayer((datalayer) => {
28
28
  datalayer.eachFeature((feature) => {
29
- names.forEach((name) => {
29
+ for (const name of names) {
30
30
  let value = feature.properties[name]
31
- const type = defined[name].type
31
+ const type = defined.get(name).type
32
32
  const parser = this.getParser(type)
33
33
  value = parser(value)
34
34
  switch (type) {
@@ -56,7 +56,7 @@ export default class Facets {
56
56
  properties[name].choices.push(value)
57
57
  }
58
58
  }
59
- })
59
+ }
60
60
  })
61
61
  })
62
62
  return properties
@@ -73,7 +73,7 @@ export default class Facets {
73
73
 
74
74
  build() {
75
75
  const defined = this.getDefined()
76
- const names = Object.keys(defined)
76
+ const names = [...defined.keys()]
77
77
  const facetProperties = this.compute(names, defined)
78
78
 
79
79
  const fields = names.map((name) => {
@@ -90,7 +90,7 @@ export default class Facets {
90
90
  handler = 'FacetSearchDateTime'
91
91
  break
92
92
  }
93
- const label = defined[name].label
93
+ const label = defined.get(name).label
94
94
  return [
95
95
  `selected.${name}`,
96
96
  {
@@ -107,12 +107,14 @@ export default class Facets {
107
107
  getDefined() {
108
108
  const defaultType = 'checkbox'
109
109
  const allowedTypes = [defaultType, 'radio', 'number', 'date', 'datetime']
110
+ const defined = new Map()
111
+ if (!this.map.options.facetKey) return defined
110
112
  return (this.map.options.facetKey || '').split(',').reduce((acc, curr) => {
111
113
  let [name, label, type] = curr.split('|')
112
114
  type = allowedTypes.includes(type) ? type : defaultType
113
- acc[name] = { label: label || name, type: type }
115
+ acc.set(name, { label: label || name, type: type })
114
116
  return acc
115
- }, {})
117
+ }, defined)
116
118
  }
117
119
 
118
120
  getParser(type) {
@@ -127,4 +129,32 @@ export default class Facets {
127
129
  return (v) => String(v || '')
128
130
  }
129
131
  }
132
+
133
+ dumps(parsed) {
134
+ const dumped = []
135
+ for (const [property, { label, type }] of parsed) {
136
+ dumped.push([property, label, type].filter(Boolean).join('|'))
137
+ }
138
+ return dumped.join(',')
139
+ }
140
+
141
+ has(property) {
142
+ return this.getDefined().has(property)
143
+ }
144
+
145
+ add(property, label, type) {
146
+ const defined = this.getDefined()
147
+ if (!defined.has(property)) {
148
+ defined.set(property, { label, type })
149
+ this.map.options.facetKey = this.dumps(defined)
150
+ this.map.isDirty = true
151
+ }
152
+ }
153
+
154
+ remove(property) {
155
+ const defined = this.getDefined()
156
+ defined.delete(property)
157
+ this.map.options.facetKey = this.dumps(defined)
158
+ this.map.isDirty = true
159
+ }
130
160
  }
@@ -0,0 +1,153 @@
1
+ /* Uses globals for: csv2geojson, osmtogeojson, GeoRSSToGeoJSON (not available as ESM) */
2
+ import { translate } from './i18n.js'
3
+
4
+ export const EXPORT_FORMATS = {
5
+ geojson: {
6
+ formatter: async (map) => JSON.stringify(map.toGeoJSON(), null, 2),
7
+ ext: '.geojson',
8
+ filetype: 'application/json',
9
+ },
10
+ gpx: {
11
+ formatter: async (map) => await map.formatter.toGPX(map.toGeoJSON()),
12
+ ext: '.gpx',
13
+ filetype: 'application/gpx+xml',
14
+ },
15
+ kml: {
16
+ formatter: async (map) => await map.formatter.toKML(map.toGeoJSON()),
17
+ ext: '.kml',
18
+ filetype: 'application/vnd.google-earth.kml+xml',
19
+ },
20
+ csv: {
21
+ formatter: async (map) => {
22
+ const table = []
23
+ map.eachFeature((feature) => {
24
+ const row = feature.toGeoJSON().properties
25
+ const center = feature.getCenter()
26
+ delete row._umap_options
27
+ row.Latitude = center.lat
28
+ row.Longitude = center.lng
29
+ table.push(row)
30
+ })
31
+ return csv2geojson.dsv.csvFormat(table)
32
+ },
33
+ ext: '.csv',
34
+ filetype: 'text/csv',
35
+ },
36
+ }
37
+
38
+ export class Formatter {
39
+ async fromGPX(str) {
40
+ const togeojson = await import('../../vendors/togeojson/togeojson.es.js')
41
+ return togeojson.gpx(this.toDom(str))
42
+ }
43
+
44
+ async fromKML(str) {
45
+ const togeojson = await import('../../vendors/togeojson/togeojson.es.js')
46
+ return togeojson.kml(this.toDom(str), {
47
+ skipNullGeometry: true,
48
+ })
49
+ }
50
+
51
+ async fromGeoJSON(str) {
52
+ try {
53
+ return JSON.parse(str)
54
+ } catch (err) {
55
+ U.Alert.error(`Invalid JSON file: ${err}`)
56
+ }
57
+ }
58
+
59
+ async fromOSM(str) {
60
+ let src
61
+ try {
62
+ src = JSON.parse(str)
63
+ } catch (e) {
64
+ src = this.toDom(str)
65
+ }
66
+ return osmtogeojson(src, { flatProperties: true })
67
+ }
68
+
69
+ fromCSV(str, callback) {
70
+ csv2geojson.csv2geojson(
71
+ str,
72
+ {
73
+ delimiter: 'auto',
74
+ includeLatLon: false,
75
+ },
76
+ (err, result) => {
77
+ // csv2geojson fallback to null geometries when it cannot determine
78
+ // lat or lon columns. This is valid geojson, but unwanted from a user
79
+ // point of view.
80
+ if (result?.features.length) {
81
+ if (result.features[0].geometry === null) {
82
+ err = {
83
+ type: 'Error',
84
+ message: translate('Cannot determine latitude and longitude columns.'),
85
+ }
86
+ }
87
+ }
88
+ if (err) {
89
+ let message
90
+ if (err.type === 'Error') {
91
+ message = err.message
92
+ } else {
93
+ message = translate('{count} errors during import: {message}', {
94
+ count: err.length,
95
+ message: err[0].message,
96
+ })
97
+ }
98
+ U.Alert.error(message, 10000)
99
+ console.error(err)
100
+ }
101
+ if (result?.features.length) {
102
+ callback(result)
103
+ }
104
+ }
105
+ )
106
+ }
107
+
108
+ async fromGeoRSS(str) {
109
+ return GeoRSSToGeoJSON(this.toDom(c))
110
+ }
111
+
112
+ toDom(x) {
113
+ const doc = new DOMParser().parseFromString(x, 'text/xml')
114
+ const errorNode = doc.querySelector('parsererror')
115
+ if (errorNode) {
116
+ U.Alert.error(translate('Cannot parse data'))
117
+ }
118
+ return doc
119
+ }
120
+
121
+ async parse(str, format) {
122
+ switch (format) {
123
+ case 'csv':
124
+ return new Promise((resolve, reject) => {
125
+ return this.fromCSV(str, (data) => resolve(data))
126
+ })
127
+ case 'gpx':
128
+ return await this.fromGPX(str)
129
+ case 'kml':
130
+ return await this.fromKML(str)
131
+ case 'osm':
132
+ return await this.fromOSM(str)
133
+ case 'georss':
134
+ return await this.fromGeoRSS(str)
135
+ case 'geojson':
136
+ return await this.fromGeoJSON(str)
137
+ }
138
+ }
139
+
140
+ async toGPX(geojson) {
141
+ const togpx = await import('../../vendors/geojson-to-gpx/index.js')
142
+ for (const feature of geojson.features) {
143
+ feature.properties.desc = feature.properties.description
144
+ }
145
+ const gpx = togpx.default(geojson)
146
+ return new XMLSerializer().serializeToString(gpx)
147
+ }
148
+
149
+ async toKML(geojson) {
150
+ const tokml = await import('../../vendors/tokml/tokml.es.js')
151
+ return tokml.toKML(geojson)
152
+ }
153
+ }
@@ -3,19 +3,23 @@ import {
3
3
  uMapAlertConflict as AlertConflict,
4
4
  uMapAlertCreation as AlertCreation,
5
5
  } from '../components/alerts/alert.js'
6
- import { AjaxAutocomplete, AjaxAutocompleteMultiple } from './autocomplete.js'
6
+ import { AjaxAutocomplete, AjaxAutocompleteMultiple, AutocompleteDatalist } from './autocomplete.js'
7
7
  import Browser from './browser.js'
8
8
  import Caption from './caption.js'
9
9
  import Facets from './facets.js'
10
+ import { Formatter } from './formatter.js'
10
11
  import Help from './help.js'
11
12
  import Importer from './importer.js'
12
13
  import Orderable from './orderable.js'
13
14
  import { HTTPError, NOKError, Request, RequestError, ServerRequest } from './request.js'
14
15
  import Rules from './rules.js'
15
16
  import { SCHEMA } from './schema.js'
17
+ import Share from './share.js'
18
+ import Slideshow from './slideshow.js'
16
19
  import { SyncEngine } from './sync/engine.js'
17
20
  import Dialog from './ui/dialog.js'
18
21
  import { EditPanel, FullPanel, Panel } from './ui/panel.js'
22
+ import TableEditor from './tableeditor.js'
19
23
  import Tooltip from './ui/tooltip.js'
20
24
  import URLs from './urls.js'
21
25
  import * as Utils from './utils.js'
@@ -30,11 +34,13 @@ window.U = {
30
34
  AlertConflict,
31
35
  AjaxAutocomplete,
32
36
  AjaxAutocompleteMultiple,
37
+ AutocompleteDatalist,
33
38
  Browser,
34
39
  Caption,
35
40
  Dialog,
36
41
  EditPanel,
37
42
  Facets,
43
+ Formatter,
38
44
  FullPanel,
39
45
  Help,
40
46
  HTTPError,
@@ -47,7 +53,10 @@ window.U = {
47
53
  Rules,
48
54
  SCHEMA,
49
55
  ServerRequest,
56
+ Share,
57
+ Slideshow,
50
58
  SyncEngine,
59
+ TableEditor,
51
60
  Tooltip,
52
61
  URLs,
53
62
  Utils,
@@ -165,6 +165,7 @@ const ENTRIES = {
165
165
  export default class Help {
166
166
  constructor(map) {
167
167
  this.map = map
168
+ this.dialog = new U.Dialog()
168
169
  this.isMacOS = /mac/i.test(
169
170
  // eslint-disable-next-line compat/compat -- Fallback available.
170
171
  navigator.userAgentData ? navigator.userAgentData.platform : navigator.platform
@@ -190,42 +191,43 @@ export default class Help {
190
191
  show(entries) {
191
192
  const container = DomUtil.add('div')
192
193
  DomUtil.createTitle(container, translate('Help'))
193
- // Special dynamic case. Do we still think this dialog is usefull ?
194
- if (entries === 'edit') {
194
+ for (const name of entries) {
195
195
  DomUtil.element({
196
196
  tagName: 'div',
197
197
  className: 'umap-help-entry',
198
198
  parent: container,
199
- }).appendChild(this._buildEditEntry())
200
- } else {
201
- for (const name of entries) {
202
- DomUtil.element({
203
- tagName: 'div',
204
- className: 'umap-help-entry',
205
- parent: container,
206
- innerHTML: ENTRIES[name],
207
- })
208
- }
199
+ innerHTML: ENTRIES[name],
200
+ })
209
201
  }
202
+ this.dialog.open({ template: container, className: 'dark', cancel: false, accept: false })
203
+ }
204
+
205
+ // Special dynamic case. Do we still think this dialog is useful?
206
+ showGetStarted() {
207
+ const container = DomUtil.add('div')
208
+ DomUtil.createTitle(container, translate('Where do we go from here?'))
209
+ DomUtil.element({
210
+ tagName: 'div',
211
+ className: 'umap-help-entry',
212
+ parent: container,
213
+ }).appendChild(this._buildEditEntry())
210
214
  this.map.dialog.open({ content: container, className: 'dark' })
211
215
  }
212
216
 
213
- button(container, entries, classname) {
217
+ button(container, entries) {
214
218
  const button = DomUtil.createButton(
215
- classname || 'umap-help-button',
219
+ 'umap-help-button',
216
220
  container,
217
221
  translate('Help')
218
222
  )
219
- entries = typeof entries === 'string' ? [entries] : entries
220
- DomEvent.on(button, 'click', DomEvent.stop).on(button, 'click', () =>
221
- this.show(entries)
222
- )
223
+ button.addEventListener('click', () => this.show(entries))
223
224
  return button
224
225
  }
225
226
 
226
- link(container, entries) {
227
- const button = this.button(container, entries, 'umap-help-link')
227
+ getStartedLink(container) {
228
+ const button = DomUtil.createButton('umap-help-link', container, translate('Help'))
228
229
  button.textContent = translate('Help')
230
+ button.addEventListener('click', () => this.showGetStarted())
229
231
  return button
230
232
  }
231
233
 
@@ -237,16 +239,14 @@ export default class Help {
237
239
 
238
240
  _buildEditEntry() {
239
241
  const container = DomUtil.create('div', '')
240
- const title = DomUtil.create('h4', '', container)
241
242
  const actionsContainer = DomUtil.create('ul', 'umap-edit-actions', container)
242
243
  const addAction = (action) => {
243
244
  const actionContainer = DomUtil.add('li', '', actionsContainer)
244
- DomUtil.add('i', action.options.className, actionContainer),
245
- DomUtil.add('span', '', actionContainer, action.options.tooltip)
245
+ DomUtil.add('i', action.options.className, actionContainer)
246
+ DomUtil.add('span', '', actionContainer, action.options.tooltip)
246
247
  DomEvent.on(actionContainer, 'click', action.addHooks, action)
247
- DomEvent.on(actionContainer, 'click', this.map.dialog.close, this.map.dialog)
248
+ DomEvent.on(actionContainer, 'click', this.dialog.close, this.dialog)
248
249
  }
249
- title.textContent = translate('Where do we go from here?')
250
250
  for (const id in this.map.helpMenuActions) {
251
251
  addAction(this.map.helpMenuActions[id])
252
252
  }
@@ -53,7 +53,7 @@ export default class Importer {
53
53
  this.TYPES = ['geojson', 'csv', 'gpx', 'kml', 'osm', 'georss', 'umap']
54
54
  this.IMPORTERS = []
55
55
  this.loadImporters()
56
- this.dialog = new Dialog(this.map._controlContainer)
56
+ this.dialog = new Dialog()
57
57
  }
58
58
 
59
59
  loadImporters() {
@@ -114,7 +114,7 @@ export default class Importer {
114
114
  }
115
115
 
116
116
  get action() {
117
- return this.qs('[name=action]:checked').value
117
+ return this.qs('[name=action]:checked')?.value
118
118
  }
119
119
 
120
120
  get layerId() {
@@ -234,7 +234,7 @@ export default class Importer {
234
234
  }
235
235
 
236
236
  submit() {
237
- let hasErrors = false
237
+ let hasErrors
238
238
  if (this.format === 'umap') {
239
239
  hasErrors = !this.full()
240
240
  } else if (!this.url) {
@@ -242,7 +242,7 @@ export default class Importer {
242
242
  } else if (this.action) {
243
243
  hasErrors = !this[this.action]()
244
244
  }
245
- if (!hasErrors) {
245
+ if (hasErrors === false) {
246
246
  Alert.info(translate('Data successfully imported!'))
247
247
  }
248
248
  }
@@ -37,8 +37,10 @@ export class Importer {
37
37
  this.autocomplete = new Autocomplete(container, options)
38
38
 
39
39
  importer.dialog.open({
40
- content: container,
40
+ template: container,
41
41
  className: `${this.id} importer dark`,
42
+ cancel: false,
43
+ accept: false,
42
44
  })
43
45
  }
44
46
  }
@@ -30,13 +30,15 @@ export class Importer {
30
30
  importer.format = select.options[select.selectedIndex].dataset.format
31
31
  importer.layerName = select.options[select.selectedIndex].textContent
32
32
  }
33
- importer.dialog.close()
34
33
  }
35
- L.DomUtil.createButton('', container, translate('Choose this dataset'), confirm)
36
34
 
37
- importer.dialog.open({
38
- content: container,
39
- className: `${this.id} importer dark`,
40
- })
35
+ importer.dialog
36
+ .open({
37
+ template: container,
38
+ className: `${this.id} importer dark`,
39
+ accept: translate('Choose this dataset'),
40
+ cancel: false,
41
+ })
42
+ .then(confirm)
41
43
  }
42
44
  }