umap-project 2.3.1__py3-none-any.whl → 2.4.0b0__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 (210) hide show
  1. umap/.DS_Store +0 -0
  2. umap/__init__.py +1 -1
  3. umap/locale/en/LC_MESSAGES/django.po +81 -31
  4. umap/locale/fr/LC_MESSAGES/django.mo +0 -0
  5. umap/locale/fr/LC_MESSAGES/django.po +109 -59
  6. umap/management/commands/run_websocket_server.py +23 -0
  7. umap/models.py +6 -1
  8. umap/settings/base.py +11 -3
  9. umap/static/.DS_Store +0 -0
  10. umap/static/umap/.DS_Store +0 -0
  11. umap/static/umap/base.css +53 -162
  12. umap/static/umap/content.css +3 -2
  13. umap/static/umap/css/dialog.css +18 -0
  14. umap/static/umap/css/icon.css +8 -0
  15. umap/static/umap/css/importers.css +44 -0
  16. umap/static/umap/css/panel.css +19 -57
  17. umap/static/umap/css/tooltip.css +59 -0
  18. umap/static/umap/css/window.css +35 -0
  19. umap/static/umap/favicons/.DS_Store +0 -0
  20. umap/static/umap/fonts/.DS_Store +0 -0
  21. umap/static/umap/img/.DS_Store +0 -0
  22. umap/static/umap/img/alert-icon-error.svg +8 -0
  23. umap/static/umap/img/alert-icon-info.svg +4 -0
  24. umap/static/umap/img/alert-icon-success.svg +3 -0
  25. umap/static/umap/img/icon-external-link.svg +3 -0
  26. umap/static/umap/img/importers/communesfr.svg +5 -0
  27. umap/static/umap/img/importers/datasets.svg +13 -0
  28. umap/static/umap/img/importers/geodatamine.svg +10 -0
  29. umap/static/umap/img/importers/overpass.svg +7 -0
  30. umap/static/umap/img/importers/random.svg +18 -0
  31. umap/static/umap/img/importers/random1.svg +4 -0
  32. umap/static/umap/img/importers/random2.svg +4 -0
  33. umap/static/umap/img/source/.DS_Store +0 -0
  34. umap/static/umap/js/components/alerts/alert.css +160 -0
  35. umap/static/umap/js/components/alerts/alert.js +169 -0
  36. umap/static/umap/js/components/base.js +54 -0
  37. umap/static/umap/js/modules/autocomplete.js +347 -0
  38. umap/static/umap/js/modules/browser.js +1 -1
  39. umap/static/umap/js/modules/caption.js +4 -3
  40. umap/static/umap/js/modules/global.js +36 -12
  41. umap/static/umap/js/modules/help.js +255 -0
  42. umap/static/umap/js/modules/importer.js +280 -0
  43. umap/static/umap/js/modules/importers/communesfr.js +44 -0
  44. umap/static/umap/js/modules/importers/datasets.js +41 -0
  45. umap/static/umap/js/modules/importers/geodatamine.js +95 -0
  46. umap/static/umap/js/modules/importers/overpass.js +84 -0
  47. umap/static/umap/js/modules/request.js +12 -14
  48. umap/static/umap/js/modules/rules.js +241 -0
  49. umap/static/umap/js/modules/schema.js +63 -14
  50. umap/static/umap/js/modules/sync/engine.js +93 -0
  51. umap/static/umap/js/modules/sync/updaters.js +109 -0
  52. umap/static/umap/js/modules/sync/websocket.js +25 -0
  53. umap/static/umap/js/modules/ui/dialog.js +52 -0
  54. umap/static/umap/js/modules/{panel.js → ui/panel.js} +25 -14
  55. umap/static/umap/js/modules/ui/tooltip.js +116 -0
  56. umap/static/umap/js/modules/utils.js +25 -18
  57. umap/static/umap/js/umap.controls.js +13 -14
  58. umap/static/umap/js/umap.core.js +1 -324
  59. umap/static/umap/js/umap.features.js +67 -27
  60. umap/static/umap/js/umap.forms.js +9 -13
  61. umap/static/umap/js/umap.js +220 -180
  62. umap/static/umap/js/umap.layer.js +142 -74
  63. umap/static/umap/js/umap.permissions.js +5 -9
  64. umap/static/umap/js/umap.tableeditor.js +8 -8
  65. umap/static/umap/locale/am_ET.js +51 -16
  66. umap/static/umap/locale/am_ET.json +51 -16
  67. umap/static/umap/locale/ar.js +51 -16
  68. umap/static/umap/locale/ar.json +51 -16
  69. umap/static/umap/locale/ast.js +51 -16
  70. umap/static/umap/locale/ast.json +51 -16
  71. umap/static/umap/locale/bg.js +51 -16
  72. umap/static/umap/locale/bg.json +51 -16
  73. umap/static/umap/locale/br.js +55 -20
  74. umap/static/umap/locale/br.json +55 -20
  75. umap/static/umap/locale/ca.js +51 -16
  76. umap/static/umap/locale/ca.json +51 -16
  77. umap/static/umap/locale/cs_CZ.js +93 -58
  78. umap/static/umap/locale/cs_CZ.json +93 -58
  79. umap/static/umap/locale/da.js +51 -16
  80. umap/static/umap/locale/da.json +51 -16
  81. umap/static/umap/locale/de.js +56 -21
  82. umap/static/umap/locale/de.json +56 -21
  83. umap/static/umap/locale/el.js +51 -16
  84. umap/static/umap/locale/el.json +51 -16
  85. umap/static/umap/locale/en.js +51 -16
  86. umap/static/umap/locale/en.json +51 -16
  87. umap/static/umap/locale/en_US.json +51 -16
  88. umap/static/umap/locale/es.js +51 -16
  89. umap/static/umap/locale/es.json +51 -16
  90. umap/static/umap/locale/et.js +51 -16
  91. umap/static/umap/locale/et.json +51 -16
  92. umap/static/umap/locale/eu.js +51 -16
  93. umap/static/umap/locale/eu.json +51 -16
  94. umap/static/umap/locale/fa_IR.js +51 -16
  95. umap/static/umap/locale/fa_IR.json +51 -16
  96. umap/static/umap/locale/fi.js +51 -16
  97. umap/static/umap/locale/fi.json +51 -16
  98. umap/static/umap/locale/fr.js +52 -17
  99. umap/static/umap/locale/fr.json +52 -17
  100. umap/static/umap/locale/gl.js +51 -16
  101. umap/static/umap/locale/gl.json +51 -16
  102. umap/static/umap/locale/he.js +51 -16
  103. umap/static/umap/locale/he.json +51 -16
  104. umap/static/umap/locale/hr.js +51 -16
  105. umap/static/umap/locale/hr.json +51 -16
  106. umap/static/umap/locale/hu.js +51 -16
  107. umap/static/umap/locale/hu.json +51 -16
  108. umap/static/umap/locale/id.js +51 -16
  109. umap/static/umap/locale/id.json +51 -16
  110. umap/static/umap/locale/is.js +51 -16
  111. umap/static/umap/locale/is.json +51 -16
  112. umap/static/umap/locale/it.js +51 -16
  113. umap/static/umap/locale/it.json +51 -16
  114. umap/static/umap/locale/ja.js +51 -16
  115. umap/static/umap/locale/ja.json +51 -16
  116. umap/static/umap/locale/ko.js +51 -16
  117. umap/static/umap/locale/ko.json +51 -16
  118. umap/static/umap/locale/lt.js +51 -16
  119. umap/static/umap/locale/lt.json +51 -16
  120. umap/static/umap/locale/ms.js +51 -16
  121. umap/static/umap/locale/ms.json +51 -16
  122. umap/static/umap/locale/nl.js +51 -16
  123. umap/static/umap/locale/nl.json +51 -16
  124. umap/static/umap/locale/no.js +51 -16
  125. umap/static/umap/locale/no.json +51 -16
  126. umap/static/umap/locale/pl.js +93 -58
  127. umap/static/umap/locale/pl.json +93 -58
  128. umap/static/umap/locale/pl_PL.json +51 -16
  129. umap/static/umap/locale/pt.js +215 -180
  130. umap/static/umap/locale/pt.json +215 -180
  131. umap/static/umap/locale/pt_BR.js +51 -16
  132. umap/static/umap/locale/pt_BR.json +51 -16
  133. umap/static/umap/locale/pt_PT.js +51 -16
  134. umap/static/umap/locale/pt_PT.json +51 -16
  135. umap/static/umap/locale/ro.js +51 -16
  136. umap/static/umap/locale/ro.json +51 -16
  137. umap/static/umap/locale/ru.js +51 -16
  138. umap/static/umap/locale/ru.json +51 -16
  139. umap/static/umap/locale/si.js +51 -16
  140. umap/static/umap/locale/si.json +51 -16
  141. umap/static/umap/locale/sk_SK.js +51 -16
  142. umap/static/umap/locale/sk_SK.json +51 -16
  143. umap/static/umap/locale/sl.js +51 -16
  144. umap/static/umap/locale/sl.json +51 -16
  145. umap/static/umap/locale/sr.js +51 -16
  146. umap/static/umap/locale/sr.json +51 -16
  147. umap/static/umap/locale/sv.js +51 -16
  148. umap/static/umap/locale/sv.json +51 -16
  149. umap/static/umap/locale/th_TH.js +51 -16
  150. umap/static/umap/locale/th_TH.json +51 -16
  151. umap/static/umap/locale/tr.js +51 -16
  152. umap/static/umap/locale/tr.json +51 -16
  153. umap/static/umap/locale/uk_UA.js +51 -16
  154. umap/static/umap/locale/uk_UA.json +51 -16
  155. umap/static/umap/locale/vi.js +51 -16
  156. umap/static/umap/locale/vi.json +51 -16
  157. umap/static/umap/locale/vi_VN.json +51 -16
  158. umap/static/umap/locale/zh.js +51 -16
  159. umap/static/umap/locale/zh.json +51 -16
  160. umap/static/umap/locale/zh_CN.json +51 -16
  161. umap/static/umap/locale/zh_TW.Big5.json +51 -16
  162. umap/static/umap/locale/zh_TW.js +51 -16
  163. umap/static/umap/locale/zh_TW.json +51 -16
  164. umap/static/umap/map.css +27 -41
  165. umap/static/umap/unittests/sync.js +105 -0
  166. umap/static/umap/unittests/utils.js +76 -34
  167. umap/static/umap/vars.css +18 -1
  168. umap/static/umap/vendors/dompurify/purify.es.js +5 -59
  169. umap/static/umap/vendors/dompurify/purify.es.mjs.map +1 -1
  170. umap/templates/umap/components/alerts/alert.html +89 -0
  171. umap/templates/umap/content.html +4 -3
  172. umap/templates/umap/css.html +4 -0
  173. umap/templates/umap/home.html +3 -0
  174. umap/templates/umap/js.html +0 -3
  175. umap/templates/umap/map_init.html +2 -8
  176. umap/templates/umap/messages.html +9 -11
  177. umap/templates/umap/search.html +3 -0
  178. umap/tests/.DS_Store +0 -0
  179. umap/tests/base.py +2 -0
  180. umap/tests/integration/.DS_Store +0 -0
  181. umap/tests/integration/conftest.py +30 -0
  182. umap/tests/integration/test_anonymous_owned_map.py +8 -13
  183. umap/tests/integration/test_browser.py +1 -1
  184. umap/tests/integration/test_conditional_rules.py +201 -0
  185. umap/tests/integration/test_dashboard.py +1 -1
  186. umap/tests/integration/test_datalayer.py +2 -3
  187. umap/tests/integration/test_edit_datalayer.py +4 -4
  188. umap/tests/integration/test_edit_map.py +1 -1
  189. umap/tests/integration/test_facets_browser.py +3 -3
  190. umap/tests/integration/test_import.py +138 -49
  191. umap/tests/integration/test_map.py +2 -2
  192. umap/tests/integration/{test_collaborative_editing.py → test_optimistic_merge.py} +7 -7
  193. umap/tests/integration/test_owned_map.py +1 -1
  194. umap/tests/integration/test_picto.py +2 -2
  195. umap/tests/integration/test_statics.py +1 -1
  196. umap/tests/integration/test_websocket_sync.py +283 -0
  197. umap/tests/settings.py +5 -0
  198. umap/tests/test_datalayer_views.py +0 -1
  199. umap/tests/test_views.py +53 -0
  200. umap/urls.py +5 -0
  201. umap/views.py +40 -11
  202. umap/websocket_server.py +92 -0
  203. {umap_project-2.3.1.dist-info → umap_project-2.4.0b0.dist-info}/METADATA +11 -9
  204. {umap_project-2.3.1.dist-info → umap_project-2.4.0b0.dist-info}/RECORD +207 -164
  205. {umap_project-2.3.1.dist-info → umap_project-2.4.0b0.dist-info}/WHEEL +1 -1
  206. umap/static/umap/js/umap.autocomplete.js +0 -341
  207. umap/static/umap/js/umap.importer.js +0 -187
  208. umap/static/umap/js/umap.ui.js +0 -190
  209. {umap_project-2.3.1.dist-info → umap_project-2.4.0b0.dist-info}/entry_points.txt +0 -0
  210. {umap_project-2.3.1.dist-info → umap_project-2.4.0b0.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,347 @@
1
+ import {
2
+ DomUtil,
3
+ DomEvent,
4
+ setOptions,
5
+ Util,
6
+ } from '../../vendors/leaflet/leaflet-src.esm.js'
7
+ import { translate } from './i18n.js'
8
+ import { Request, ServerRequest } from './request.js'
9
+
10
+ export class BaseAutocomplete {
11
+ constructor(el, options) {
12
+ this.el = el
13
+ this.options = {
14
+ placeholder: translate('Start typing...'),
15
+ emptyMessage: translate('No result'),
16
+ allowFree: true,
17
+ minChar: 2,
18
+ maxResults: 5,
19
+ }
20
+ this.cache = ''
21
+ this.results = []
22
+ this._current = null
23
+ setOptions(this, options)
24
+ this.createInput()
25
+ this.createContainer()
26
+ this.selectedContainer = this.initSelectedContainer()
27
+ }
28
+
29
+ get current() {
30
+ return this._current
31
+ }
32
+
33
+ set current(index) {
34
+ if (typeof index === 'object') {
35
+ index = this.resultToIndex(index)
36
+ }
37
+ this._current = index
38
+ }
39
+
40
+ createInput() {
41
+ this.input = DomUtil.element({
42
+ tagName: 'input',
43
+ type: 'text',
44
+ parent: this.el,
45
+ placeholder: this.options.placeholder,
46
+ autocomplete: 'off',
47
+ className: this.options.className,
48
+ })
49
+ DomEvent.on(this.input, 'keydown', this.onKeyDown, this)
50
+ DomEvent.on(this.input, 'keyup', this.onKeyUp, this)
51
+ DomEvent.on(this.input, 'blur', this.onBlur, this)
52
+ }
53
+
54
+ createContainer() {
55
+ this.container = DomUtil.element({
56
+ tagName: 'ul',
57
+ parent: document.body,
58
+ className: 'umap-autocomplete',
59
+ })
60
+ }
61
+
62
+ resizeContainer() {
63
+ const l = this.getLeft(this.input)
64
+ const t = this.getTop(this.input) + this.input.offsetHeight
65
+ this.container.style.left = `${l}px`
66
+ this.container.style.top = `${t}px`
67
+ const width = this.options.width ? this.options.width : this.input.offsetWidth - 2
68
+ this.container.style.width = `${width}px`
69
+ }
70
+
71
+ onKeyDown(e) {
72
+ switch (e.key) {
73
+ case 'Tab':
74
+ if (this.current !== null) this.setChoice()
75
+ DomEvent.stop(e)
76
+ break
77
+ case 'Enter':
78
+ DomEvent.stop(e)
79
+ this.setChoice()
80
+ break
81
+ case 'Escape':
82
+ DomEvent.stop(e)
83
+ this.hide()
84
+ break
85
+ case 'ArrowDown':
86
+ if (this.results.length > 0) {
87
+ if (this.current !== null && this.current < this.results.length - 1) {
88
+ // what if one result?
89
+ this.current++
90
+ this.highlight()
91
+ } else if (this.current === null) {
92
+ this.current = 0
93
+ this.highlight()
94
+ }
95
+ }
96
+ break
97
+ case 'ArrowUp':
98
+ if (this.current !== null) {
99
+ DomEvent.stop(e)
100
+ }
101
+ if (this.results.length > 0) {
102
+ if (this.current > 0) {
103
+ this.current--
104
+ this.highlight()
105
+ } else if (this.current === 0) {
106
+ this.current = null
107
+ this.highlight()
108
+ }
109
+ }
110
+ break
111
+ }
112
+ }
113
+
114
+ onKeyUp(e) {
115
+ const special = [
116
+ 'Tab',
117
+ 'Enter',
118
+ 'ArrowLeft',
119
+ 'ArrowRight',
120
+ 'ArrowDown',
121
+ 'ArrowUp',
122
+ 'Meta',
123
+ 'Shift',
124
+ 'Alt',
125
+ 'Control',
126
+ ]
127
+ if (!special.includes(e.key)) {
128
+ this.search()
129
+ }
130
+ }
131
+
132
+ onBlur() {
133
+ setTimeout(() => this.hide(), 100)
134
+ }
135
+
136
+ clear() {
137
+ this.results = []
138
+ this.current = null
139
+ this.cache = ''
140
+ this.container.innerHTML = ''
141
+ }
142
+
143
+ hide() {
144
+ this.clear()
145
+ this.container.style.display = 'none'
146
+ this.input.value = ''
147
+ }
148
+
149
+ setChoice(choice) {
150
+ choice = choice || this.results[this.current]
151
+ if (choice) {
152
+ this.input.value = choice.item.label
153
+ this.options.on_select(choice)
154
+ this.displaySelected(choice)
155
+ this.hide()
156
+ if (this.options.callback) {
157
+ this.options.callback.bind(this)(choice)
158
+ }
159
+ }
160
+ }
161
+
162
+ createResult(item) {
163
+ const el = DomUtil.element({
164
+ tagName: 'li',
165
+ parent: this.container,
166
+ textContent: item.label,
167
+ })
168
+ const result = {
169
+ item: item,
170
+ el: el,
171
+ }
172
+ DomEvent.on(el, 'mouseover', () => {
173
+ this.current = result
174
+ this.highlight()
175
+ })
176
+ DomEvent.on(el, 'mousedown', () => this.setChoice())
177
+ return result
178
+ }
179
+
180
+ resultToIndex(result) {
181
+ return this.results.findIndex((item) => item.item.value === result.item.value)
182
+ }
183
+
184
+ handleResults(data) {
185
+ this.clear()
186
+ this.container.style.display = 'block'
187
+ this.resizeContainer()
188
+ data.forEach((item) => {
189
+ this.results.push(this.createResult(item))
190
+ })
191
+ this.current = 0
192
+ this.highlight()
193
+ //TODO manage no results
194
+ }
195
+
196
+ highlight() {
197
+ this.results.forEach((result, index) => {
198
+ if (index === this.current) DomUtil.addClass(result.el, 'on')
199
+ else DomUtil.removeClass(result.el, 'on')
200
+ })
201
+ }
202
+
203
+ getLeft(el) {
204
+ let tmp = el.offsetLeft
205
+ el = el.offsetParent
206
+ while (el) {
207
+ tmp += el.offsetLeft
208
+ el = el.offsetParent
209
+ }
210
+ return tmp
211
+ }
212
+
213
+ getTop(el) {
214
+ let tmp = el.offsetTop
215
+ el = el.offsetParent
216
+ while (el) {
217
+ tmp += el.offsetTop
218
+ el = el.offsetParent
219
+ }
220
+ return tmp
221
+ }
222
+ }
223
+
224
+ export class BaseAjax extends BaseAutocomplete {
225
+ constructor(el, options) {
226
+ super(el, options)
227
+ this.setUrl()
228
+ this.initRequest()
229
+ }
230
+
231
+ setUrl() {
232
+ this.url = this.options?.url
233
+ }
234
+
235
+ initRequest() {
236
+ this.request = new Request()
237
+ }
238
+
239
+ optionToResult(option) {
240
+ return {
241
+ value: option.value,
242
+ label: option.innerHTML,
243
+ }
244
+ }
245
+
246
+ async search() {
247
+ let val = this.input.value
248
+ if (val.length < this.options.minChar) {
249
+ this.clear()
250
+ return
251
+ }
252
+ if (val === this.cache) return
253
+ else this.cache = val
254
+ val = val.toLowerCase()
255
+ const url = Util.template(this.url, { q: encodeURIComponent(val) })
256
+ this.handleResults(await this._search(url))
257
+ }
258
+
259
+ async _search(url) {
260
+ const response = await this.request.get(url)
261
+ if (response && response.ok) {
262
+ return await response.json()
263
+ }
264
+ }
265
+ }
266
+
267
+ class BaseServerAjax extends BaseAjax {
268
+ setUrl() {
269
+ this.url = '/agnocomplete/AutocompleteUser/?q={q}'
270
+ }
271
+
272
+ initRequest() {
273
+ this.server = new ServerRequest()
274
+ }
275
+ async _search(url) {
276
+ const [{ data }, response] = await this.server.get(url)
277
+ return data
278
+ }
279
+ }
280
+
281
+ export const SingleMixin = (Base) =>
282
+ class extends Base {
283
+ initSelectedContainer() {
284
+ return DomUtil.after(
285
+ this.input,
286
+ DomUtil.element({ tagName: 'div', className: 'umap-singleresult' })
287
+ )
288
+ }
289
+
290
+ displaySelected(result) {
291
+ const result_el = DomUtil.element({
292
+ tagName: 'div',
293
+ parent: this.selectedContainer,
294
+ })
295
+ result_el.textContent = result.item.label
296
+ const close = DomUtil.element({
297
+ tagName: 'span',
298
+ parent: result_el,
299
+ className: 'close',
300
+ textContent: '×',
301
+ })
302
+ this.input.style.display = 'none'
303
+ DomEvent.on(
304
+ close,
305
+ 'click',
306
+ function () {
307
+ this.selectedContainer.innerHTML = ''
308
+ this.input.style.display = 'block'
309
+ },
310
+ this
311
+ )
312
+ this.hide()
313
+ }
314
+ }
315
+
316
+ export const MultipleMixin = (Base) =>
317
+ class extends Base {
318
+ initSelectedContainer() {
319
+ return DomUtil.after(
320
+ this.input,
321
+ DomUtil.element({ tagName: 'ul', className: 'umap-multiresult' })
322
+ )
323
+ }
324
+
325
+ displaySelected(result) {
326
+ const result_el = DomUtil.element({
327
+ tagName: 'li',
328
+ parent: this.selectedContainer,
329
+ })
330
+ result_el.textContent = result.item.label
331
+ const close = DomUtil.element({
332
+ tagName: 'span',
333
+ parent: result_el,
334
+ className: 'close',
335
+ textContent: '×',
336
+ })
337
+ DomEvent.on(close, 'click', () => {
338
+ this.selectedContainer.removeChild(result_el)
339
+ this.options.on_unselect(result)
340
+ })
341
+ this.hide()
342
+ }
343
+ }
344
+
345
+ export class AjaxAutocompleteMultiple extends MultipleMixin(BaseServerAjax) {}
346
+
347
+ export class AjaxAutocomplete extends SingleMixin(BaseServerAjax) {}
@@ -150,7 +150,7 @@ export default class Browser {
150
150
  // https://github.com/Leaflet/Leaflet/pull/9052
151
151
  DomEvent.disableClickPropagation(container)
152
152
 
153
- DomUtil.createTitle(container, translate('Browse data'), 'icon-layers')
153
+ DomUtil.createTitle(container, translate('Data browser'), 'icon-layers')
154
154
  const formContainer = DomUtil.createFieldset(container, L._('Filters'), {
155
155
  on: this.mode === 'filters',
156
156
  className: 'filters',
@@ -18,8 +18,9 @@ export default class Caption {
18
18
 
19
19
  open() {
20
20
  const container = DomUtil.create('div', 'umap-caption')
21
- DomUtil.createTitle(container, this.map.options.name, 'icon-caption')
22
- this.map.permissions.addOwnerLink('h5', container)
21
+ const hgroup = DomUtil.element({tagName: 'hgroup', parent: container})
22
+ DomUtil.createTitle(hgroup, this.map.options.name, 'icon-caption icon-block')
23
+ this.map.permissions.addOwnerLink('h4', hgroup)
23
24
  if (this.map.options.description) {
24
25
  const description = DomUtil.element({
25
26
  tagName: 'div',
@@ -29,7 +30,7 @@ export default class Caption {
29
30
  })
30
31
  }
31
32
  const datalayerContainer = DomUtil.create('div', 'datalayer-container', container)
32
- this.map.eachDataLayer((datalayer) =>
33
+ this.map.eachDataLayerReverse((datalayer) =>
33
34
  this.addDataLayer(datalayer, datalayerContainer)
34
35
  )
35
36
  const creditsContainer = DomUtil.create('div', 'credits-container', container)
@@ -2,29 +2,53 @@ import URLs from './urls.js'
2
2
  import Browser from './browser.js'
3
3
  import Facets from './facets.js'
4
4
  import Caption from './caption.js'
5
- import { Panel, EditPanel, FullPanel } from './panel.js'
5
+ import { Panel, EditPanel, FullPanel } from './ui/panel.js'
6
+ import Dialog from './ui/dialog.js'
7
+ import Tooltip from './ui/tooltip.js'
8
+ import Rules from './rules.js'
6
9
  import * as Utils from './utils.js'
7
10
  import { SCHEMA } from './schema.js'
8
11
  import { Request, ServerRequest, RequestError, HTTPError, NOKError } from './request.js'
12
+ import { AjaxAutocomplete, AjaxAutocompleteMultiple } from './autocomplete.js'
9
13
  import Orderable from './orderable.js'
14
+ import Importer from './importer.js'
15
+ import Help from './help.js'
16
+ import { SyncEngine } from './sync/engine.js'
17
+ import {
18
+ uMapAlert as Alert,
19
+ uMapAlertCreation as AlertCreation,
20
+ uMapAlertConflict as AlertConflict,
21
+ } from '../components/alerts/alert.js'
10
22
 
11
23
  // Import modules and export them to the global scope.
12
24
  // For the not yet module-compatible JS out there.
13
25
 
26
+ // By alphabetic order
14
27
  window.U = {
15
- URLs,
16
- Request,
17
- ServerRequest,
18
- RequestError,
19
- HTTPError,
20
- NOKError,
28
+ Alert,
29
+ AlertCreation,
30
+ AlertConflict,
31
+ AjaxAutocomplete,
32
+ AjaxAutocompleteMultiple,
21
33
  Browser,
22
- Facets,
23
- Panel,
34
+ Caption,
35
+ Dialog,
24
36
  EditPanel,
37
+ Facets,
25
38
  FullPanel,
26
- Utils,
27
- SCHEMA,
39
+ Help,
40
+ HTTPError,
41
+ Importer,
42
+ NOKError,
28
43
  Orderable,
29
- Caption,
44
+ Panel,
45
+ Request,
46
+ RequestError,
47
+ Rules,
48
+ SCHEMA,
49
+ ServerRequest,
50
+ SyncEngine,
51
+ Tooltip,
52
+ URLs,
53
+ Utils,
30
54
  }
@@ -0,0 +1,255 @@
1
+ import { DomUtil, DomEvent } from '../../vendors/leaflet/leaflet-src.esm.js'
2
+ import { translate } from './i18n.js'
3
+
4
+ const SHORTCUTS = {
5
+ DRAW_MARKER: {
6
+ shortcut: 'Modifier+M',
7
+ label: translate('Draw a marker'),
8
+ },
9
+ DRAW_LINE: {
10
+ shortcut: 'Modifier+L',
11
+ label: translate('Draw a polyline'),
12
+ },
13
+ DRAW_POLYGON: {
14
+ shortcut: 'Modifier+P',
15
+ label: translate('Draw a polygon'),
16
+ },
17
+ TOGGLE_EDIT: {
18
+ shortcut: 'Modifier+E',
19
+ label: translate('Toggle edit mode'),
20
+ },
21
+ STOP_EDIT: {
22
+ shortcut: 'Modifier+E',
23
+ label: translate('Stop editing'),
24
+ },
25
+ SAVE_MAP: {
26
+ shortcut: 'Modifier+S',
27
+ label: translate('Save map'),
28
+ },
29
+ IMPORT_PANEL: {
30
+ shortcut: 'Modifier+I',
31
+ label: translate('Import data'),
32
+ },
33
+ SEARCH: {
34
+ shortcut: 'Modifier+F',
35
+ label: translate('Search location'),
36
+ },
37
+ CANCEL: {
38
+ shortcut: 'Modifier+Z',
39
+ label: translate('Cancel edits'),
40
+ },
41
+ PREVIEW: {
42
+ shortcut: 'Modifier+E',
43
+ label: translate('Back to preview'),
44
+ },
45
+ SAVE: {
46
+ shortcut: 'Modifier+S',
47
+ label: translate('Save current edits'),
48
+ },
49
+ EDIT_FEATURE_LAYER: {
50
+ shortcut: 'Modifier+⇧+Click',
51
+ label: translate("Edit feature's layer"),
52
+ },
53
+ CONTINUE_LINE: {
54
+ shortcut: 'Modifier+Click',
55
+ label: translate('Continue line'),
56
+ },
57
+ }
58
+
59
+ const ENTRIES = {
60
+ formatURL: `${translate(
61
+ 'Supported variables that will be dynamically replaced'
62
+ )}: {bbox}, {lat}, {lng}, {zoom}, {east}, {north}..., {left}, {top}..., locale, lang`,
63
+ colorValue: translate('Must be a valid CSS value (eg.: DarkBlue or #123456)'),
64
+ smoothFactor: translate(
65
+ 'How much to simplify the polyline on each zoom level (more = better performance and smoother look, less = more accurate)'
66
+ ),
67
+ dashArray: translate(
68
+ 'A comma separated list of numbers that defines the stroke dash pattern. Ex.: "5, 10, 15".'
69
+ ),
70
+ zoomTo: translate('Zoom level for automatic zooms'),
71
+ labelKey: translate(
72
+ 'The name of the property to use as feature label (eg.: "nom"). You can also use properties inside brackets to use more than one or mix with static content (eg.: "&lcub;name&rcub; in &lcub;place&rcub;")'
73
+ ),
74
+ stroke: translate('Whether to display or not polygons paths.'),
75
+ fill: translate('Whether to fill polygons with color.'),
76
+ fillColor: translate('Optional. Same as color if not set.'),
77
+ shortCredit: translate('Will be displayed in the bottom right corner of the map'),
78
+ longCredit: translate('Will be visible in the caption of the map'),
79
+ permanentCredit: translate(
80
+ 'Will be permanently visible in the bottom left corner of the map'
81
+ ),
82
+ sortKey: translate(
83
+ 'Comma separated list of properties to use for sorting features. To reverse the sort, put a minus sign (-) before. Eg. mykey,-otherkey.'
84
+ ),
85
+ slugKey: translate('The name of the property to use as feature unique identifier.'),
86
+ filterKey: translate(
87
+ 'Comma separated list of properties to use when filtering features by text input'
88
+ ),
89
+ facetKey: translate(
90
+ 'Comma separated list of properties to use for filters (eg.: mykey,otherkey). To control label, add it after a | (eg.: mykey|My Key,otherkey|Other Key). To control input field type, add it after another | (eg.: mykey|My Key|checkbox,otherkey|Other Key|datetime). Allowed values for the input field type are checkbox (default), radio, number, date and datetime.'
91
+ ),
92
+ interactive: translate(
93
+ 'If false, the polygon or line will act as a part of the underlying map.'
94
+ ),
95
+ outlink: translate('Define link to open in a new window on polygon click.'),
96
+ dynamicRemoteData: translate('Fetch data each time map view changes.'),
97
+ proxyRemoteData: translate(
98
+ "To use if remote server doesn't allow cross domain (slower)"
99
+ ),
100
+ browsable: translate(
101
+ 'Set it to false to hide this layer from the slideshow, the data browser, the popup navigation…'
102
+ ),
103
+ importMode: translate(
104
+ 'When providing an URL, uMap can copy the remote data in a layer, or add this URL as remote source of the layer. In that case, data will always be fetched from that URL, and thus be up to date, but it will not be possible to edit it inside uMap.'
105
+ ),
106
+ importFormats: `
107
+ <div>
108
+ <dt>GeoJSON</dt>
109
+ <dd>${translate('All properties are imported.')}</dd>
110
+ <dt>GPX</dt>
111
+ <dd>${translate('Properties imported:')}name, desc</dd>
112
+ <dt>KML</dt>
113
+ <dd>${translate('Properties imported:')}name, description</dd>
114
+ <dt>CSV</dt>
115
+ <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>
116
+ <dt>uMap</dt>
117
+ <dd>${translate('Imports all umap data, including layers and settings.')}</dd>
118
+ </div>
119
+ `,
120
+ dynamicProperties: `
121
+ <div>
122
+ <h4>${translate('Dynamic properties')}</h4>
123
+ <p>${translate('Use placeholders with feature properties between brackets, eg. &#123;name&#125;, they will be dynamically replaced by the corresponding values.')}</p>
124
+ </div>
125
+ `,
126
+
127
+ textFormatting: `
128
+ <div>
129
+ <h4>${translate('Text formatting')}</h4>
130
+ <ul>
131
+ <li>${translate('*single star for italic*')}</li>
132
+ <li>${translate('**double star for bold**')}</li>
133
+ <li>${translate('# one hash for main heading')}</li>
134
+ <li>${translate('## two hashes for second heading')}</li>
135
+ <li>${translate('### three hashes for third heading')}</li>
136
+ <li>${translate('Simple link: [[http://example.com]]')}</li>
137
+ <li>${translate('Link with text: [[http://example.com|text of the link]]')}</li>
138
+ <li>${translate('Image: {{http://image.url.com}}')}</li>
139
+ <li>${translate('Image with custom width (in px): {{http://image.url.com|width}}')}</li>
140
+ <li>${translate('Iframe: {{{http://iframe.url.com}}}')}</li>
141
+ <li>${translate('Iframe with custom height (in px): {{{http://iframe.url.com|height}}}')}</li>
142
+ <li>${translate('Iframe with custom height and width (in px): {{{http://iframe.url.com|height*width}}}')}</li>
143
+ <li>${translate('--- for a horizontal rule')}</li>
144
+ </ul>
145
+ </div>
146
+ `,
147
+
148
+ overpassImporter: `
149
+ <div>
150
+ <h4>${translate('Overpass supported expressions')}</h4>
151
+ <ul>
152
+ <li>${translate('key (eg. building)')}</li>
153
+ <li>${translate('!key (eg. !name)')}</li>
154
+ <li>${translate('key=value (eg. building=yes)')}</li>
155
+ <li>${translate('key!=value (eg. building!=yes)')}</li>
156
+ <li>${translate('key~value (eg. name~Grisy)')}</li>
157
+ <li>${translate('key="value|value2" (eg. name="Paris|Berlin")')}</li>
158
+ </ul>
159
+ <div>${translate('More info about Overpass syntax')}: <a href="https://wiki.openstreetmap.org/wiki/Overpass_API/Language_Guide">https://wiki.openstreetmap.org/wiki/Overpass_API/Language_Guide</a></div>
160
+ <div>${translate('For more complex needs, see')} <a href="https://overpass-turbo.eu/">https://overpass-turbo.eu/</a></div>
161
+ </div>
162
+ `,
163
+ }
164
+
165
+ export default class Help {
166
+ constructor(map) {
167
+ this.map = map
168
+ this.isMacOS = /mac/i.test(
169
+ // eslint-disable-next-line compat/compat -- Fallback available.
170
+ navigator.userAgentData ? navigator.userAgentData.platform : navigator.platform
171
+ )
172
+ }
173
+
174
+ displayLabel(action, withKbdTag = true) {
175
+ let { shortcut, label } = SHORTCUTS[action]
176
+ const modifier = this.isMacOS ? 'Cmd' : 'Ctrl'
177
+ shortcut = shortcut.replace('Modifier', modifier)
178
+ if (withKbdTag) {
179
+ shortcut = shortcut
180
+ .split('+')
181
+ .map((el) => `<kbd>${el}</kbd>`)
182
+ .join('+')
183
+ label += ` ${shortcut}`
184
+ } else {
185
+ label += ` (${shortcut})`
186
+ }
187
+ return label
188
+ }
189
+
190
+ show(entries) {
191
+ const container = DomUtil.add('div')
192
+ DomUtil.createTitle(container, translate('Help'))
193
+ // Special dynamic case. Do we still think this dialog is usefull ?
194
+ if (entries == 'edit') {
195
+ DomUtil.element({
196
+ tagName: 'div',
197
+ className: 'umap-help-entry',
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
+ }
209
+ }
210
+ this.map.dialog.open({ content: container, className: 'dark' })
211
+ }
212
+
213
+ button(container, entries, classname) {
214
+ const button = DomUtil.createButton(
215
+ classname || 'umap-help-button',
216
+ container,
217
+ translate('Help')
218
+ )
219
+ entries = typeof entries === 'string' ? [entries] : entries
220
+ DomEvent.on(button, 'click', DomEvent.stop).on(button, 'click', () =>
221
+ this.show(entries)
222
+ )
223
+ return button
224
+ }
225
+
226
+ link(container, entries) {
227
+ const button = this.button(container, entries, 'umap-help-link')
228
+ button.textContent = translate('Help')
229
+ return button
230
+ }
231
+
232
+ parse(container) {
233
+ for (const element of container.querySelectorAll('[data-help]')) {
234
+ this.button(element, element.dataset.help.split(','))
235
+ }
236
+ }
237
+
238
+ _buildEditEntry() {
239
+ const container = DomUtil.create('div', '')
240
+ const title = DomUtil.create('h4', '', container)
241
+ const actionsContainer = DomUtil.create('ul', 'umap-edit-actions', container)
242
+ const addAction = (action) => {
243
+ const actionContainer = DomUtil.add('li', '', actionsContainer)
244
+ DomUtil.add('i', action.options.className, actionContainer),
245
+ DomUtil.add('span', '', actionContainer, action.options.tooltip)
246
+ DomEvent.on(actionContainer, 'click', action.addHooks, action)
247
+ DomEvent.on(actionContainer, 'click', this.map.dialog.close, this.map.dialog)
248
+ }
249
+ title.textContent = translate('Where do we go from here?')
250
+ for (const id in this.map.helpMenuActions) {
251
+ addAction(this.map.helpMenuActions[id])
252
+ }
253
+ return container
254
+ }
255
+ }