umap-project 2.3.0__py3-none-any.whl → 2.4.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of umap-project might be problematic. Click here for more details.

Files changed (211) hide show
  1. umap/__init__.py +1 -1
  2. umap/locale/en/LC_MESSAGES/django.po +81 -31
  3. umap/locale/fr/LC_MESSAGES/django.mo +0 -0
  4. umap/locale/fr/LC_MESSAGES/django.po +117 -66
  5. umap/locale/pl/LC_MESSAGES/django.mo +0 -0
  6. umap/locale/pl/LC_MESSAGES/django.po +83 -78
  7. umap/locale/pt/LC_MESSAGES/django.mo +0 -0
  8. umap/locale/pt/LC_MESSAGES/django.po +129 -123
  9. umap/management/commands/run_websocket_server.py +23 -0
  10. umap/models.py +6 -1
  11. umap/settings/base.py +11 -3
  12. umap/static/umap/base.css +68 -186
  13. umap/static/umap/content.css +3 -2
  14. umap/static/umap/css/dialog.css +18 -0
  15. umap/static/umap/css/icon.css +8 -0
  16. umap/static/umap/css/importers.css +51 -0
  17. umap/static/umap/css/panel.css +18 -57
  18. umap/static/umap/css/tooltip.css +59 -0
  19. umap/static/umap/css/window.css +35 -0
  20. umap/static/umap/img/16-white.svg +1 -3
  21. umap/static/umap/img/alert-icon-error.svg +8 -0
  22. umap/static/umap/img/alert-icon-info.svg +4 -0
  23. umap/static/umap/img/alert-icon-success.svg +3 -0
  24. umap/static/umap/img/icon-external-link.svg +3 -0
  25. umap/static/umap/img/importers/communesfr.svg +5 -0
  26. umap/static/umap/img/importers/datasets.svg +13 -0
  27. umap/static/umap/img/importers/geodatamine.svg +10 -0
  28. umap/static/umap/img/importers/overpass.svg +7 -0
  29. umap/static/umap/img/importers/random.svg +18 -0
  30. umap/static/umap/img/importers/random1.svg +4 -0
  31. umap/static/umap/img/importers/random2.svg +4 -0
  32. umap/static/umap/img/source/16-white.svg +2 -4
  33. umap/static/umap/js/components/alerts/alert.css +160 -0
  34. umap/static/umap/js/components/alerts/alert.js +169 -0
  35. umap/static/umap/js/components/base.js +54 -0
  36. umap/static/umap/js/modules/autocomplete.js +347 -0
  37. umap/static/umap/js/modules/browser.js +14 -21
  38. umap/static/umap/js/modules/caption.js +119 -0
  39. umap/static/umap/js/modules/global.js +37 -11
  40. umap/static/umap/js/modules/help.js +255 -0
  41. umap/static/umap/js/modules/importer.js +308 -0
  42. umap/static/umap/js/modules/importers/communesfr.js +44 -0
  43. umap/static/umap/js/modules/importers/datasets.js +42 -0
  44. umap/static/umap/js/modules/importers/geodatamine.js +95 -0
  45. umap/static/umap/js/modules/importers/overpass.js +84 -0
  46. umap/static/umap/js/modules/request.js +12 -14
  47. umap/static/umap/js/modules/rules.js +241 -0
  48. umap/static/umap/js/modules/schema.js +63 -14
  49. umap/static/umap/js/modules/sync/engine.js +93 -0
  50. umap/static/umap/js/modules/sync/updaters.js +109 -0
  51. umap/static/umap/js/modules/sync/websocket.js +25 -0
  52. umap/static/umap/js/modules/ui/dialog.js +52 -0
  53. umap/static/umap/js/modules/{panel.js → ui/panel.js} +37 -20
  54. umap/static/umap/js/modules/ui/tooltip.js +116 -0
  55. umap/static/umap/js/modules/utils.js +25 -18
  56. umap/static/umap/js/umap.controls.js +37 -112
  57. umap/static/umap/js/umap.core.js +1 -327
  58. umap/static/umap/js/umap.features.js +77 -29
  59. umap/static/umap/js/umap.forms.js +17 -19
  60. umap/static/umap/js/umap.js +265 -228
  61. umap/static/umap/js/umap.layer.js +154 -76
  62. umap/static/umap/js/umap.permissions.js +5 -9
  63. umap/static/umap/js/umap.popup.js +2 -1
  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 +52 -16
  86. umap/static/umap/locale/en.json +52 -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 +61 -25
  99. umap/static/umap/locale/fr.json +61 -25
  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 +40 -53
  165. umap/static/umap/unittests/sync.js +105 -0
  166. umap/static/umap/unittests/utils.js +78 -36
  167. umap/static/umap/vars.css +19 -1
  168. umap/static/umap/vendors/dompurify/purify.es.js +50 -15
  169. umap/static/umap/vendors/dompurify/purify.es.mjs.map +1 -1
  170. umap/static/umap/vendors/formbuilder/Leaflet.FormBuilder.js +2 -2
  171. umap/templates/umap/components/alerts/alert.html +89 -0
  172. umap/templates/umap/content.html +4 -3
  173. umap/templates/umap/css.html +4 -0
  174. umap/templates/umap/home.html +3 -0
  175. umap/templates/umap/js.html +0 -3
  176. umap/templates/umap/map_init.html +2 -8
  177. umap/templates/umap/messages.html +9 -11
  178. umap/templates/umap/search.html +3 -0
  179. umap/tests/base.py +3 -0
  180. umap/tests/integration/conftest.py +30 -0
  181. umap/tests/integration/test_anonymous_owned_map.py +8 -13
  182. umap/tests/integration/test_browser.py +81 -6
  183. umap/tests/integration/test_caption.py +27 -0
  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 +32 -3
  188. umap/tests/integration/test_edit_map.py +1 -1
  189. umap/tests/integration/test_facets_browser.py +7 -4
  190. umap/tests/integration/test_import.py +185 -49
  191. umap/tests/integration/test_map.py +31 -17
  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_view_marker.py +19 -2
  197. umap/tests/integration/test_websocket_sync.py +283 -0
  198. umap/tests/settings.py +5 -0
  199. umap/tests/test_datalayer_views.py +0 -1
  200. umap/tests/test_views.py +53 -0
  201. umap/urls.py +5 -0
  202. umap/views.py +40 -11
  203. umap/websocket_server.py +92 -0
  204. {umap_project-2.3.0.dist-info → umap_project-2.4.0.dist-info}/METADATA +13 -11
  205. {umap_project-2.3.0.dist-info → umap_project-2.4.0.dist-info}/RECORD +208 -172
  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.0.dist-info → umap_project-2.4.0.dist-info}/WHEEL +0 -0
  210. {umap_project-2.3.0.dist-info → umap_project-2.4.0.dist-info}/entry_points.txt +0 -0
  211. {umap_project-2.3.0.dist-info → umap_project-2.4.0.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,54 @@
1
+ const EVENT_PREFIX = 'umap'
2
+
3
+ export class uMapElement extends HTMLElement {
4
+ static emit(type, detail = {}) {
5
+ const event = new CustomEvent(`${EVENT_PREFIX}:${type}`, {
6
+ bubbles: true,
7
+ cancelable: true,
8
+ detail: detail,
9
+ })
10
+ return document.dispatchEvent(event)
11
+ }
12
+
13
+ /**
14
+ * Retrieves a clone of the content template either using the `template`
15
+ * attribute or an id mathing the name of the component:
16
+ *
17
+ * `umap-alert` component => `umap-alert-template` template id lookup.
18
+ */
19
+ get template() {
20
+ return document
21
+ .getElementById(this.getAttribute('template') || `${this.localName}-template`)
22
+ .content.cloneNode(true)
23
+ }
24
+
25
+ constructor() {
26
+ super()
27
+ this.append(this.template)
28
+ }
29
+
30
+ /**
31
+ * Special method which allows to easily listen to events
32
+ * and have automated event to component method binding.
33
+ *
34
+ * For instance listening to `alert` will then call `onAlert`.
35
+ */
36
+ handleEvent(event) {
37
+ event.preventDefault()
38
+ // From `umap:alert` to `alert`.
39
+ const eventName = event.type.replace(`${EVENT_PREFIX}:`, '')
40
+ // From `alert` event type to `onAlert` call against that class.
41
+ this[`on${eventName.charAt(0).toUpperCase() + eventName.slice(1)}`](event)
42
+ }
43
+
44
+ listen(eventName) {
45
+ // Using `this` as a listener will call `handleEvent` under the hood.
46
+ document.addEventListener(`${EVENT_PREFIX}:${eventName}`, this)
47
+ }
48
+ }
49
+
50
+ export function register(klass, name) {
51
+ if ('customElements' in globalThis && !customElements.get(name)) {
52
+ customElements.define(name, klass)
53
+ }
54
+ }
@@ -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) {}
@@ -9,18 +9,7 @@ export default class Browser {
9
9
  filter: '',
10
10
  inBbox: false,
11
11
  }
12
- this._mode = 'layers'
13
- }
14
-
15
- set mode(value) {
16
- // Store the mode so we can respect it when we redraw
17
- if (['data', 'filters'].includes(value)) this.map.panel.mode = 'expanded'
18
- else if (value === 'layers') this.map.panel.mode = 'condensed'
19
- this._mode = value
20
- }
21
-
22
- get mode() {
23
- return this._mode
12
+ this.mode = 'layers'
24
13
  }
25
14
 
26
15
  addFeature(feature, parent) {
@@ -73,7 +62,7 @@ export default class Browser {
73
62
 
74
63
  addDataLayer(datalayer, parent) {
75
64
  let className = `datalayer ${datalayer.getHidableClass()}`
76
- if (this.map.panel.mode !== 'condensed') className += ' show-list'
65
+ if (this.mode !== 'layers') className += ' show-list'
77
66
  const container = DomUtil.create('div', className, parent),
78
67
  headline = DomUtil.create('h5', '', container)
79
68
  container.id = this.datalayerId(datalayer)
@@ -87,7 +76,7 @@ export default class Browser {
87
76
  const parent = DomUtil.get(this.datalayerId(datalayer))
88
77
  // Panel is not open
89
78
  if (!parent) return
90
- DomUtil.classIf(parent, 'off', !datalayer.isVisible())
79
+ parent.classList.toggle('off', !datalayer.isVisible())
91
80
  const container = parent.querySelector('ul')
92
81
  const headline = parent.querySelector('h5')
93
82
  const toggleList = () => parent.classList.toggle('show-list')
@@ -154,14 +143,12 @@ export default class Browser {
154
143
  open(mode) {
155
144
  // Force only if mode is known, otherwise keep current mode.
156
145
  if (mode) this.mode = mode
157
- // Get once but use it for each feature later
158
- this.filterKeys = this.map.getFilterKeys()
159
146
  const container = DomUtil.create('div')
160
147
  // HOTFIX. Remove when this is released:
161
148
  // https://github.com/Leaflet/Leaflet/pull/9052
162
149
  DomEvent.disableClickPropagation(container)
163
150
 
164
- DomUtil.createTitle(container, translate('Browse data'), 'icon-layers')
151
+ DomUtil.createTitle(container, translate('Data browser'), 'icon-layers')
165
152
  const formContainer = DomUtil.createFieldset(container, L._('Filters'), {
166
153
  on: this.mode === 'filters',
167
154
  className: 'filters',
@@ -201,7 +188,11 @@ export default class Browser {
201
188
  if (filtersBuilder) filtersBuilder.form.reset()
202
189
  })
203
190
  DomUtil.createIcon(reset, 'icon-restore')
204
- DomUtil.element({ tagName: 'span', parent: reset, textContent: translate('Reset all') })
191
+ DomUtil.element({
192
+ tagName: 'span',
193
+ parent: reset,
194
+ textContent: translate('Reset all'),
195
+ })
205
196
 
206
197
  this.map.panel.open({
207
198
  content: container,
@@ -212,9 +203,11 @@ export default class Browser {
212
203
  }
213
204
 
214
205
  static backButton(map) {
215
- const button = DomUtil.create('li', '')
216
- DomUtil.create('i', 'icon icon-16 icon-back', button)
217
- button.title = translate('Back to browser')
206
+ const button = DomUtil.createButtonIcon(
207
+ DomUtil.create('li', '', undefined),
208
+ 'icon-back',
209
+ translate('Back to browser')
210
+ )
218
211
  // Fixme: remove me when this is merged and released
219
212
  // https://github.com/Leaflet/Leaflet/pull/9052
220
213
  DomEvent.disableClickPropagation(button)
@@ -0,0 +1,119 @@
1
+ import { DomUtil } from '../../vendors/leaflet/leaflet-src.esm.js'
2
+ import { translate } from './i18n.js'
3
+ import * as Utils from './utils.js'
4
+
5
+ export default class Caption {
6
+ constructor(map) {
7
+ this.map = map
8
+ }
9
+
10
+ isOpen() {
11
+ return Boolean(document.querySelector('.on .umap-caption'))
12
+ }
13
+
14
+ refresh() {
15
+ if (!this.isOpen()) return
16
+ this.open()
17
+ }
18
+
19
+ open() {
20
+ const container = DomUtil.create('div', 'umap-caption')
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)
24
+ if (this.map.options.description) {
25
+ const description = DomUtil.element({
26
+ tagName: 'div',
27
+ className: 'umap-map-description text',
28
+ safeHTML: Utils.toHTML(this.map.options.description),
29
+ parent: container,
30
+ })
31
+ }
32
+ const datalayerContainer = DomUtil.create('div', 'datalayer-container', container)
33
+ this.map.eachDataLayerReverse((datalayer) =>
34
+ this.addDataLayer(datalayer, datalayerContainer)
35
+ )
36
+ const creditsContainer = DomUtil.create('div', 'credits-container', container)
37
+ this.addCredits(creditsContainer)
38
+ this.map.panel.open({ content: container })
39
+ }
40
+
41
+ addDataLayer(datalayer, container) {
42
+ if (!datalayer.options.inCaption) return
43
+ const p = DomUtil.create('p', 'datalayer-legend', container),
44
+ legend = DomUtil.create('span', '', p),
45
+ headline = DomUtil.create('strong', '', p)
46
+ datalayer.onceLoaded(() => {
47
+ datalayer.renderLegend(legend)
48
+ if (datalayer.options.description) {
49
+ DomUtil.element({
50
+ tagName: 'span',
51
+ parent: p,
52
+ safeHTML: Utils.toHTML(datalayer.options.description),
53
+ })
54
+ }
55
+ })
56
+ datalayer.renderToolbox(headline)
57
+ DomUtil.add('span', '', headline, `${datalayer.options.name} `)
58
+ }
59
+
60
+ addCredits(container) {
61
+ const credits = DomUtil.createFieldset(container, translate('Credits'))
62
+ let title = DomUtil.add('h5', '', credits, translate('User content credits'))
63
+ if (this.map.options.shortCredit || this.map.options.longCredit) {
64
+ DomUtil.element({
65
+ tagName: 'p',
66
+ parent: credits,
67
+ safeHTML: Utils.toHTML(
68
+ this.map.options.longCredit || this.map.options.shortCredit
69
+ ),
70
+ })
71
+ }
72
+ if (this.map.options.licence) {
73
+ const licence = DomUtil.add(
74
+ 'p',
75
+ '',
76
+ credits,
77
+ `${translate('Map user content has been published under licence')} `
78
+ )
79
+ DomUtil.createLink(
80
+ '',
81
+ licence,
82
+ this.map.options.licence.name,
83
+ this.map.options.licence.url
84
+ )
85
+ } else {
86
+ DomUtil.add('p', '', credits, translate('No licence has been set'))
87
+ }
88
+ title = DomUtil.create('h5', '', credits)
89
+ title.textContent = translate('Map background credits')
90
+ const tilelayerCredit = DomUtil.create('p', '', credits)
91
+ DomUtil.element({
92
+ tagName: 'strong',
93
+ parent: tilelayerCredit,
94
+ textContent: `${this.map.selected_tilelayer.options.name} `,
95
+ })
96
+ DomUtil.element({
97
+ tagName: 'span',
98
+ parent: tilelayerCredit,
99
+ safeHTML: this.map.selected_tilelayer.getAttribution(),
100
+ })
101
+ const urls = {
102
+ leaflet: 'http://leafletjs.com',
103
+ django: 'https://www.djangoproject.com',
104
+ umap: 'https://umap-project.org/',
105
+ changelog: 'https://docs.umap-project.org/en/master/changelog/',
106
+ version: this.map.options.umap_version,
107
+ }
108
+ const creditHTML = translate(
109
+ `
110
+ Powered by <a href="{leaflet}">Leaflet</a> and
111
+ <a href="{django}">Django</a>,
112
+ glued by <a href="{umap}">uMap project</a>
113
+ (version <a href="{changelog}">{version}</a>).
114
+ `,
115
+ urls
116
+ )
117
+ DomUtil.element({ tagName: 'p', innerHTML: creditHTML, parent: credits })
118
+ }
119
+ }
@@ -1,28 +1,54 @@
1
1
  import URLs from './urls.js'
2
2
  import Browser from './browser.js'
3
3
  import Facets from './facets.js'
4
- import { Panel, EditPanel, FullPanel } from './panel.js'
4
+ import Caption from './caption.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'
5
9
  import * as Utils from './utils.js'
6
10
  import { SCHEMA } from './schema.js'
7
11
  import { Request, ServerRequest, RequestError, HTTPError, NOKError } from './request.js'
12
+ import { AjaxAutocomplete, AjaxAutocompleteMultiple } from './autocomplete.js'
8
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'
9
22
 
10
23
  // Import modules and export them to the global scope.
11
24
  // For the not yet module-compatible JS out there.
12
25
 
26
+ // By alphabetic order
13
27
  window.U = {
14
- URLs,
15
- Request,
16
- ServerRequest,
17
- RequestError,
18
- HTTPError,
19
- NOKError,
28
+ Alert,
29
+ AlertCreation,
30
+ AlertConflict,
31
+ AjaxAutocomplete,
32
+ AjaxAutocompleteMultiple,
20
33
  Browser,
21
- Facets,
22
- Panel,
34
+ Caption,
35
+ Dialog,
23
36
  EditPanel,
37
+ Facets,
24
38
  FullPanel,
25
- Utils,
26
- SCHEMA,
39
+ Help,
40
+ HTTPError,
41
+ Importer,
42
+ NOKError,
27
43
  Orderable,
44
+ Panel,
45
+ Request,
46
+ RequestError,
47
+ Rules,
48
+ SCHEMA,
49
+ ServerRequest,
50
+ SyncEngine,
51
+ Tooltip,
52
+ URLs,
53
+ Utils,
28
54
  }