umap-project 2.3.1__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 (204) 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/management/commands/run_websocket_server.py +23 -0
  6. umap/models.py +6 -1
  7. umap/settings/base.py +11 -3
  8. umap/static/umap/base.css +64 -184
  9. umap/static/umap/content.css +3 -2
  10. umap/static/umap/css/dialog.css +18 -0
  11. umap/static/umap/css/icon.css +8 -0
  12. umap/static/umap/css/importers.css +51 -0
  13. umap/static/umap/css/panel.css +18 -57
  14. umap/static/umap/css/tooltip.css +59 -0
  15. umap/static/umap/css/window.css +35 -0
  16. umap/static/umap/img/16-white.svg +1 -3
  17. umap/static/umap/img/alert-icon-error.svg +8 -0
  18. umap/static/umap/img/alert-icon-info.svg +4 -0
  19. umap/static/umap/img/alert-icon-success.svg +3 -0
  20. umap/static/umap/img/icon-external-link.svg +3 -0
  21. umap/static/umap/img/importers/communesfr.svg +5 -0
  22. umap/static/umap/img/importers/datasets.svg +13 -0
  23. umap/static/umap/img/importers/geodatamine.svg +10 -0
  24. umap/static/umap/img/importers/overpass.svg +7 -0
  25. umap/static/umap/img/importers/random.svg +18 -0
  26. umap/static/umap/img/importers/random1.svg +4 -0
  27. umap/static/umap/img/importers/random2.svg +4 -0
  28. umap/static/umap/img/source/16-white.svg +2 -4
  29. umap/static/umap/js/components/alerts/alert.css +160 -0
  30. umap/static/umap/js/components/alerts/alert.js +169 -0
  31. umap/static/umap/js/components/base.js +54 -0
  32. umap/static/umap/js/modules/autocomplete.js +347 -0
  33. umap/static/umap/js/modules/browser.js +6 -6
  34. umap/static/umap/js/modules/caption.js +5 -4
  35. umap/static/umap/js/modules/global.js +36 -12
  36. umap/static/umap/js/modules/help.js +255 -0
  37. umap/static/umap/js/modules/importer.js +308 -0
  38. umap/static/umap/js/modules/importers/communesfr.js +44 -0
  39. umap/static/umap/js/modules/importers/datasets.js +42 -0
  40. umap/static/umap/js/modules/importers/geodatamine.js +95 -0
  41. umap/static/umap/js/modules/importers/overpass.js +84 -0
  42. umap/static/umap/js/modules/request.js +12 -14
  43. umap/static/umap/js/modules/rules.js +241 -0
  44. umap/static/umap/js/modules/schema.js +63 -14
  45. umap/static/umap/js/modules/sync/engine.js +93 -0
  46. umap/static/umap/js/modules/sync/updaters.js +109 -0
  47. umap/static/umap/js/modules/sync/websocket.js +25 -0
  48. umap/static/umap/js/modules/ui/dialog.js +52 -0
  49. umap/static/umap/js/modules/{panel.js → ui/panel.js} +25 -14
  50. umap/static/umap/js/modules/ui/tooltip.js +116 -0
  51. umap/static/umap/js/modules/utils.js +25 -18
  52. umap/static/umap/js/umap.controls.js +13 -14
  53. umap/static/umap/js/umap.core.js +1 -324
  54. umap/static/umap/js/umap.features.js +77 -29
  55. umap/static/umap/js/umap.forms.js +9 -13
  56. umap/static/umap/js/umap.js +254 -215
  57. umap/static/umap/js/umap.layer.js +152 -74
  58. umap/static/umap/js/umap.permissions.js +5 -9
  59. umap/static/umap/js/umap.popup.js +1 -1
  60. umap/static/umap/js/umap.tableeditor.js +8 -8
  61. umap/static/umap/locale/am_ET.js +51 -16
  62. umap/static/umap/locale/am_ET.json +51 -16
  63. umap/static/umap/locale/ar.js +51 -16
  64. umap/static/umap/locale/ar.json +51 -16
  65. umap/static/umap/locale/ast.js +51 -16
  66. umap/static/umap/locale/ast.json +51 -16
  67. umap/static/umap/locale/bg.js +51 -16
  68. umap/static/umap/locale/bg.json +51 -16
  69. umap/static/umap/locale/br.js +55 -20
  70. umap/static/umap/locale/br.json +55 -20
  71. umap/static/umap/locale/ca.js +51 -16
  72. umap/static/umap/locale/ca.json +51 -16
  73. umap/static/umap/locale/cs_CZ.js +93 -58
  74. umap/static/umap/locale/cs_CZ.json +93 -58
  75. umap/static/umap/locale/da.js +51 -16
  76. umap/static/umap/locale/da.json +51 -16
  77. umap/static/umap/locale/de.js +56 -21
  78. umap/static/umap/locale/de.json +56 -21
  79. umap/static/umap/locale/el.js +51 -16
  80. umap/static/umap/locale/el.json +51 -16
  81. umap/static/umap/locale/en.js +52 -16
  82. umap/static/umap/locale/en.json +52 -16
  83. umap/static/umap/locale/en_US.json +51 -16
  84. umap/static/umap/locale/es.js +51 -16
  85. umap/static/umap/locale/es.json +51 -16
  86. umap/static/umap/locale/et.js +51 -16
  87. umap/static/umap/locale/et.json +51 -16
  88. umap/static/umap/locale/eu.js +51 -16
  89. umap/static/umap/locale/eu.json +51 -16
  90. umap/static/umap/locale/fa_IR.js +51 -16
  91. umap/static/umap/locale/fa_IR.json +51 -16
  92. umap/static/umap/locale/fi.js +51 -16
  93. umap/static/umap/locale/fi.json +51 -16
  94. umap/static/umap/locale/fr.js +61 -25
  95. umap/static/umap/locale/fr.json +61 -25
  96. umap/static/umap/locale/gl.js +51 -16
  97. umap/static/umap/locale/gl.json +51 -16
  98. umap/static/umap/locale/he.js +51 -16
  99. umap/static/umap/locale/he.json +51 -16
  100. umap/static/umap/locale/hr.js +51 -16
  101. umap/static/umap/locale/hr.json +51 -16
  102. umap/static/umap/locale/hu.js +51 -16
  103. umap/static/umap/locale/hu.json +51 -16
  104. umap/static/umap/locale/id.js +51 -16
  105. umap/static/umap/locale/id.json +51 -16
  106. umap/static/umap/locale/is.js +51 -16
  107. umap/static/umap/locale/is.json +51 -16
  108. umap/static/umap/locale/it.js +51 -16
  109. umap/static/umap/locale/it.json +51 -16
  110. umap/static/umap/locale/ja.js +51 -16
  111. umap/static/umap/locale/ja.json +51 -16
  112. umap/static/umap/locale/ko.js +51 -16
  113. umap/static/umap/locale/ko.json +51 -16
  114. umap/static/umap/locale/lt.js +51 -16
  115. umap/static/umap/locale/lt.json +51 -16
  116. umap/static/umap/locale/ms.js +51 -16
  117. umap/static/umap/locale/ms.json +51 -16
  118. umap/static/umap/locale/nl.js +51 -16
  119. umap/static/umap/locale/nl.json +51 -16
  120. umap/static/umap/locale/no.js +51 -16
  121. umap/static/umap/locale/no.json +51 -16
  122. umap/static/umap/locale/pl.js +93 -58
  123. umap/static/umap/locale/pl.json +93 -58
  124. umap/static/umap/locale/pl_PL.json +51 -16
  125. umap/static/umap/locale/pt.js +215 -180
  126. umap/static/umap/locale/pt.json +215 -180
  127. umap/static/umap/locale/pt_BR.js +51 -16
  128. umap/static/umap/locale/pt_BR.json +51 -16
  129. umap/static/umap/locale/pt_PT.js +51 -16
  130. umap/static/umap/locale/pt_PT.json +51 -16
  131. umap/static/umap/locale/ro.js +51 -16
  132. umap/static/umap/locale/ro.json +51 -16
  133. umap/static/umap/locale/ru.js +51 -16
  134. umap/static/umap/locale/ru.json +51 -16
  135. umap/static/umap/locale/si.js +51 -16
  136. umap/static/umap/locale/si.json +51 -16
  137. umap/static/umap/locale/sk_SK.js +51 -16
  138. umap/static/umap/locale/sk_SK.json +51 -16
  139. umap/static/umap/locale/sl.js +51 -16
  140. umap/static/umap/locale/sl.json +51 -16
  141. umap/static/umap/locale/sr.js +51 -16
  142. umap/static/umap/locale/sr.json +51 -16
  143. umap/static/umap/locale/sv.js +51 -16
  144. umap/static/umap/locale/sv.json +51 -16
  145. umap/static/umap/locale/th_TH.js +51 -16
  146. umap/static/umap/locale/th_TH.json +51 -16
  147. umap/static/umap/locale/tr.js +51 -16
  148. umap/static/umap/locale/tr.json +51 -16
  149. umap/static/umap/locale/uk_UA.js +51 -16
  150. umap/static/umap/locale/uk_UA.json +51 -16
  151. umap/static/umap/locale/vi.js +51 -16
  152. umap/static/umap/locale/vi.json +51 -16
  153. umap/static/umap/locale/vi_VN.json +51 -16
  154. umap/static/umap/locale/zh.js +51 -16
  155. umap/static/umap/locale/zh.json +51 -16
  156. umap/static/umap/locale/zh_CN.json +51 -16
  157. umap/static/umap/locale/zh_TW.Big5.json +51 -16
  158. umap/static/umap/locale/zh_TW.js +51 -16
  159. umap/static/umap/locale/zh_TW.json +51 -16
  160. umap/static/umap/map.css +40 -53
  161. umap/static/umap/unittests/sync.js +105 -0
  162. umap/static/umap/unittests/utils.js +78 -36
  163. umap/static/umap/vars.css +19 -1
  164. umap/static/umap/vendors/formbuilder/Leaflet.FormBuilder.js +2 -2
  165. umap/templates/umap/components/alerts/alert.html +89 -0
  166. umap/templates/umap/content.html +4 -3
  167. umap/templates/umap/css.html +4 -0
  168. umap/templates/umap/home.html +3 -0
  169. umap/templates/umap/js.html +0 -3
  170. umap/templates/umap/map_init.html +2 -8
  171. umap/templates/umap/messages.html +9 -11
  172. umap/templates/umap/search.html +3 -0
  173. umap/tests/base.py +2 -0
  174. umap/tests/integration/conftest.py +30 -0
  175. umap/tests/integration/test_anonymous_owned_map.py +8 -13
  176. umap/tests/integration/test_browser.py +77 -4
  177. umap/tests/integration/test_conditional_rules.py +201 -0
  178. umap/tests/integration/test_dashboard.py +1 -1
  179. umap/tests/integration/test_datalayer.py +2 -3
  180. umap/tests/integration/test_edit_datalayer.py +4 -4
  181. umap/tests/integration/test_edit_map.py +1 -1
  182. umap/tests/integration/test_facets_browser.py +3 -3
  183. umap/tests/integration/test_import.py +185 -49
  184. umap/tests/integration/test_map.py +31 -2
  185. umap/tests/integration/{test_collaborative_editing.py → test_optimistic_merge.py} +7 -7
  186. umap/tests/integration/test_owned_map.py +1 -1
  187. umap/tests/integration/test_picto.py +2 -2
  188. umap/tests/integration/test_statics.py +1 -1
  189. umap/tests/integration/test_view_marker.py +2 -2
  190. umap/tests/integration/test_websocket_sync.py +283 -0
  191. umap/tests/settings.py +5 -0
  192. umap/tests/test_datalayer_views.py +0 -1
  193. umap/tests/test_views.py +53 -0
  194. umap/urls.py +5 -0
  195. umap/views.py +40 -11
  196. umap/websocket_server.py +92 -0
  197. {umap_project-2.3.1.dist-info → umap_project-2.4.0.dist-info}/METADATA +10 -8
  198. {umap_project-2.3.1.dist-info → umap_project-2.4.0.dist-info}/RECORD +201 -167
  199. umap/static/umap/js/umap.autocomplete.js +0 -341
  200. umap/static/umap/js/umap.importer.js +0 -187
  201. umap/static/umap/js/umap.ui.js +0 -190
  202. {umap_project-2.3.1.dist-info → umap_project-2.4.0.dist-info}/WHEEL +0 -0
  203. {umap_project-2.3.1.dist-info → umap_project-2.4.0.dist-info}/entry_points.txt +0 -0
  204. {umap_project-2.3.1.dist-info → umap_project-2.4.0.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,169 @@
1
+ import { translate } from '../../modules/i18n.js'
2
+ import { uMapElement } from '../base.js'
3
+
4
+ class uMapAlert extends uMapElement {
5
+ static get observedAttributes() {
6
+ return ['open']
7
+ }
8
+
9
+ attributeChangedCallback(name, oldValue, newValue) {
10
+ switch (name) {
11
+ case 'open':
12
+ newValue === 'open' ? this._show() : this._hide()
13
+ break
14
+ }
15
+ }
16
+
17
+ static info(message, duration = 5000) {
18
+ uMapAlert.emit('alert', { message, duration })
19
+ }
20
+
21
+ static success(message, duration = 5000) {
22
+ uMapAlert.emit('alert', { level: 'success', message, duration })
23
+ }
24
+
25
+ // biome-ignore lint/style/useNumberNamespace: Number.Infinity returns undefined by default
26
+ static error(message, duration = Infinity) {
27
+ uMapAlert.emit('alert', { level: 'error', message, duration })
28
+ }
29
+
30
+ constructor() {
31
+ super()
32
+ this._hide()
33
+ this.container = this.querySelector('[role="dialog"]')
34
+ this.element = this.container.querySelector('[role="alert"]')
35
+ }
36
+
37
+ _hide() {
38
+ this.setAttribute('hidden', 'hidden')
39
+ this.removeAttribute('open')
40
+ }
41
+
42
+ _show() {
43
+ this.removeAttribute('hidden')
44
+ }
45
+
46
+ _handleClose() {
47
+ this.addEventListener('click', (event) => {
48
+ if (event.target.closest('[data-close]')) {
49
+ this._hide()
50
+ }
51
+ })
52
+ }
53
+
54
+ onAlert(event) {
55
+ const { level = 'info', duration = 5000, message = '' } = event.detail
56
+ this.container.dataset.level = level
57
+ this.container.dataset.duration = duration
58
+ this.element.textContent = message
59
+ this.setAttribute('open', 'open')
60
+ if (Number.isFinite(duration)) {
61
+ setTimeout(() => {
62
+ this._hide()
63
+ }, duration)
64
+ }
65
+ }
66
+
67
+ connectedCallback() {
68
+ this._handleClose()
69
+ this.listen('alert')
70
+ }
71
+ }
72
+
73
+ class uMapAlertCreation extends uMapAlert {
74
+ static info(
75
+ message,
76
+ // biome-ignore lint/style/useNumberNamespace: Number.Infinity returns undefined by default
77
+ duration = Infinity,
78
+ editLink = undefined,
79
+ sendCallback = undefined
80
+ ) {
81
+ uMapAlertCreation.emit('alertCreation', {
82
+ message,
83
+ duration,
84
+ editLink,
85
+ sendCallback,
86
+ })
87
+ }
88
+
89
+ constructor() {
90
+ super()
91
+ this.linkWrapper = this.container.querySelector('#link-wrapper')
92
+ this.formWrapper = this.container.querySelector('#form-wrapper')
93
+ }
94
+
95
+ onAlertCreation(event) {
96
+ const {
97
+ level = 'info',
98
+ duration = 5000,
99
+ message = '',
100
+ editLink = undefined,
101
+ sendCallback = undefined,
102
+ } = event.detail
103
+ uMapAlert.prototype.onAlert.call(this, { detail: { level, duration, message } })
104
+ this.linkWrapper.querySelector('input[type="url"]').value = editLink
105
+ const button = this.linkWrapper.querySelector('input[type="button"]')
106
+ button.addEventListener('click', (event) => {
107
+ event.preventDefault()
108
+ L.Util.copyToClipboard(editLink)
109
+ event.target.value = translate('✅ Copied!')
110
+ })
111
+ if (sendCallback) {
112
+ this.formWrapper.removeAttribute('hidden')
113
+ const form = this.formWrapper.querySelector('form')
114
+ form.addEventListener('submit', async (event) => {
115
+ event.preventDefault()
116
+ const formData = new FormData(form)
117
+ sendCallback(formData)
118
+ this.removeAttribute('open')
119
+ })
120
+ }
121
+ }
122
+
123
+ connectedCallback() {
124
+ this._handleClose()
125
+ this.listen('alertCreation')
126
+ }
127
+ }
128
+
129
+ class uMapAlertConflict extends uMapAlert {
130
+ static error(message, forceCallback) {
131
+ uMapAlertConflict.emit('alertConflict', { level: 'error', message, forceCallback })
132
+ }
133
+
134
+ constructor() {
135
+ super()
136
+ this.conflictWrapper = this.container.querySelector('#conflict-wrapper')
137
+ }
138
+
139
+ onAlertConflict(event) {
140
+ // biome-ignore lint/style/useNumberNamespace: Number.Infinity returns undefined by default
141
+ const {
142
+ level = 'info',
143
+ duration = Infinity,
144
+ message = '',
145
+ forceCallback = undefined,
146
+ } = event.detail
147
+ uMapAlert.prototype.onAlert.call(this, { detail: { level, duration, message } })
148
+ const form = this.conflictWrapper.querySelector('form')
149
+ form.addEventListener('submit', (event) => {
150
+ event.preventDefault()
151
+ switch (event.submitter.id) {
152
+ case 'your-changes':
153
+ forceCallback()
154
+ break
155
+ case 'their-changes':
156
+ window.location.reload()
157
+ break
158
+ }
159
+ this.removeAttribute('open')
160
+ })
161
+ }
162
+
163
+ connectedCallback() {
164
+ this._handleClose()
165
+ this.listen('alertConflict')
166
+ }
167
+ }
168
+
169
+ export { uMapAlert, uMapAlertCreation, uMapAlertConflict }
@@ -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) {}
@@ -143,14 +143,12 @@ export default class Browser {
143
143
  open(mode) {
144
144
  // Force only if mode is known, otherwise keep current mode.
145
145
  if (mode) this.mode = mode
146
- // Get once but use it for each feature later
147
- this.filterKeys = this.map.getFilterKeys()
148
146
  const container = DomUtil.create('div')
149
147
  // HOTFIX. Remove when this is released:
150
148
  // https://github.com/Leaflet/Leaflet/pull/9052
151
149
  DomEvent.disableClickPropagation(container)
152
150
 
153
- DomUtil.createTitle(container, translate('Browse data'), 'icon-layers')
151
+ DomUtil.createTitle(container, translate('Data browser'), 'icon-layers')
154
152
  const formContainer = DomUtil.createFieldset(container, L._('Filters'), {
155
153
  on: this.mode === 'filters',
156
154
  className: 'filters',
@@ -205,9 +203,11 @@ export default class Browser {
205
203
  }
206
204
 
207
205
  static backButton(map) {
208
- const button = DomUtil.create('li', '')
209
- DomUtil.create('i', 'icon icon-16 icon-back', button)
210
- 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
+ )
211
211
  // Fixme: remove me when this is merged and released
212
212
  // https://github.com/Leaflet/Leaflet/pull/9052
213
213
  DomEvent.disableClickPropagation(button)
@@ -18,18 +18,19 @@ 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',
26
- className: 'umap-map-description',
27
+ className: 'umap-map-description text',
27
28
  safeHTML: Utils.toHTML(this.map.options.description),
28
29
  parent: container,
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
  }