umap-project 3.3.2__py3-none-any.whl → 3.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.
Files changed (242) hide show
  1. umap/__init__.py +1 -1
  2. umap/context_processors.py +4 -1
  3. umap/locale/cs_CZ/LC_MESSAGES/django.mo +0 -0
  4. umap/locale/cs_CZ/LC_MESSAGES/django.po +43 -33
  5. umap/locale/da/LC_MESSAGES/django.mo +0 -0
  6. umap/locale/da/LC_MESSAGES/django.po +43 -33
  7. umap/locale/de/LC_MESSAGES/django.mo +0 -0
  8. umap/locale/de/LC_MESSAGES/django.po +64 -53
  9. umap/locale/el/LC_MESSAGES/django.mo +0 -0
  10. umap/locale/el/LC_MESSAGES/django.po +35 -29
  11. umap/locale/en/LC_MESSAGES/django.po +47 -41
  12. umap/locale/es/LC_MESSAGES/django.mo +0 -0
  13. umap/locale/es/LC_MESSAGES/django.po +43 -33
  14. umap/locale/et/LC_MESSAGES/django.mo +0 -0
  15. umap/locale/et/LC_MESSAGES/django.po +58 -54
  16. umap/locale/eu/LC_MESSAGES/django.mo +0 -0
  17. umap/locale/eu/LC_MESSAGES/django.po +43 -33
  18. umap/locale/fa_IR/LC_MESSAGES/django.mo +0 -0
  19. umap/locale/fa_IR/LC_MESSAGES/django.po +43 -33
  20. umap/locale/fr/LC_MESSAGES/django.mo +0 -0
  21. umap/locale/fr/LC_MESSAGES/django.po +36 -30
  22. umap/locale/gl/LC_MESSAGES/django.mo +0 -0
  23. umap/locale/gl/LC_MESSAGES/django.po +43 -33
  24. umap/locale/hu/LC_MESSAGES/django.mo +0 -0
  25. umap/locale/hu/LC_MESSAGES/django.po +35 -29
  26. umap/locale/is/LC_MESSAGES/django.mo +0 -0
  27. umap/locale/is/LC_MESSAGES/django.po +43 -33
  28. umap/locale/it/LC_MESSAGES/django.mo +0 -0
  29. umap/locale/it/LC_MESSAGES/django.po +43 -33
  30. umap/locale/nl/LC_MESSAGES/django.mo +0 -0
  31. umap/locale/nl/LC_MESSAGES/django.po +35 -29
  32. umap/locale/pl/LC_MESSAGES/django.mo +0 -0
  33. umap/locale/pl/LC_MESSAGES/django.po +114 -103
  34. umap/locale/pt/LC_MESSAGES/django.mo +0 -0
  35. umap/locale/pt/LC_MESSAGES/django.po +43 -33
  36. umap/locale/th_TH/LC_MESSAGES/django.mo +0 -0
  37. umap/locale/th_TH/LC_MESSAGES/django.po +310 -109
  38. umap/locale/zh_TW/LC_MESSAGES/django.mo +0 -0
  39. umap/locale/zh_TW/LC_MESSAGES/django.po +80 -70
  40. umap/management/commands/switch_user.py +2 -2
  41. umap/migrations/0018_datalayer_uuid.py +1 -1
  42. umap/models.py +7 -3
  43. umap/settings/local.py.sample +1 -1
  44. umap/static/umap/base.css +89 -32
  45. umap/static/umap/content.css +129 -33
  46. umap/static/umap/css/bar.css +82 -20
  47. umap/static/umap/css/browser.css +163 -0
  48. umap/static/umap/css/contextmenu.css +15 -0
  49. umap/static/umap/css/dialog.css +36 -16
  50. umap/static/umap/css/form.css +123 -33
  51. umap/static/umap/css/icon.css +46 -3
  52. umap/static/umap/css/panel.css +7 -3
  53. umap/static/umap/css/popup.css +34 -8
  54. umap/static/umap/css/tooltip.css +8 -4
  55. umap/static/umap/img/16-white.svg +26 -8
  56. umap/static/umap/img/16.svg +1 -1
  57. umap/static/umap/img/source/16-white.svg +36 -18
  58. umap/static/umap/img/source/16.svg +1 -1
  59. umap/static/umap/js/components/alerts/alert.css +69 -31
  60. umap/static/umap/js/components/alerts/alert.js +20 -2
  61. umap/static/umap/js/components/base.js +1 -1
  62. umap/static/umap/js/modules/browser.js +69 -61
  63. umap/static/umap/js/modules/caption.js +10 -7
  64. umap/static/umap/js/modules/data/features.js +89 -63
  65. umap/static/umap/js/modules/data/fields.js +446 -0
  66. umap/static/umap/js/modules/data/layer.js +116 -196
  67. umap/static/umap/js/modules/domutils.js +109 -0
  68. umap/static/umap/js/modules/filters.js +780 -0
  69. umap/static/umap/js/modules/form/builder.js +8 -5
  70. umap/static/umap/js/modules/form/fields.js +111 -221
  71. umap/static/umap/js/modules/formatter.js +24 -1
  72. umap/static/umap/js/modules/help.js +4 -3
  73. umap/static/umap/js/modules/i18n.js +1 -1
  74. umap/static/umap/js/modules/importer.js +1 -1
  75. umap/static/umap/js/modules/importers/opendata.js +15 -0
  76. umap/static/umap/js/modules/importers/openrouteservice.js +6 -1
  77. umap/static/umap/js/modules/managers.js +2 -1
  78. umap/static/umap/js/modules/permissions.js +39 -31
  79. umap/static/umap/js/modules/rendering/controls.js +11 -9
  80. umap/static/umap/js/modules/rendering/icon.js +3 -8
  81. umap/static/umap/js/modules/rendering/layers/base.js +3 -3
  82. umap/static/umap/js/modules/rendering/layers/classified.js +18 -11
  83. umap/static/umap/js/modules/rendering/layers/cluster.js +23 -11
  84. umap/static/umap/js/modules/rendering/layers/heat.js +27 -21
  85. umap/static/umap/js/modules/rendering/map.js +1 -0
  86. umap/static/umap/js/modules/rendering/template.js +50 -23
  87. umap/static/umap/js/modules/rendering/ui.js +33 -25
  88. umap/static/umap/js/modules/rules.js +38 -44
  89. umap/static/umap/js/modules/schema.js +3 -6
  90. umap/static/umap/js/modules/share.js +5 -4
  91. umap/static/umap/js/modules/tableeditor.js +50 -38
  92. umap/static/umap/js/modules/templates.js +2 -3
  93. umap/static/umap/js/modules/ui/bar.js +55 -23
  94. umap/static/umap/js/modules/ui/dialog.js +38 -27
  95. umap/static/umap/js/modules/ui/panel.js +23 -8
  96. umap/static/umap/js/modules/ui/tooltip.js +6 -5
  97. umap/static/umap/js/modules/umap.js +158 -51
  98. umap/static/umap/js/modules/utils.js +24 -2
  99. umap/static/umap/js/umap.core.js +1 -110
  100. umap/static/umap/locale/am_ET.js +52 -17
  101. umap/static/umap/locale/am_ET.json +52 -17
  102. umap/static/umap/locale/ar.js +52 -17
  103. umap/static/umap/locale/ar.json +52 -17
  104. umap/static/umap/locale/ast.js +52 -17
  105. umap/static/umap/locale/ast.json +52 -17
  106. umap/static/umap/locale/bg.js +52 -17
  107. umap/static/umap/locale/bg.json +52 -17
  108. umap/static/umap/locale/br.js +48 -22
  109. umap/static/umap/locale/br.json +48 -22
  110. umap/static/umap/locale/ca.js +52 -17
  111. umap/static/umap/locale/ca.json +52 -17
  112. umap/static/umap/locale/cs_CZ.js +52 -17
  113. umap/static/umap/locale/cs_CZ.json +52 -17
  114. umap/static/umap/locale/da.js +54 -17
  115. umap/static/umap/locale/da.json +54 -17
  116. umap/static/umap/locale/de.js +102 -67
  117. umap/static/umap/locale/de.json +102 -67
  118. umap/static/umap/locale/el.js +52 -17
  119. umap/static/umap/locale/el.json +52 -17
  120. umap/static/umap/locale/en.js +54 -16
  121. umap/static/umap/locale/en.json +54 -16
  122. umap/static/umap/locale/en_US.json +52 -17
  123. umap/static/umap/locale/es.js +54 -17
  124. umap/static/umap/locale/es.json +54 -17
  125. umap/static/umap/locale/et.js +91 -56
  126. umap/static/umap/locale/et.json +91 -56
  127. umap/static/umap/locale/eu.js +84 -49
  128. umap/static/umap/locale/eu.json +84 -49
  129. umap/static/umap/locale/fa_IR.js +52 -17
  130. umap/static/umap/locale/fa_IR.json +52 -17
  131. umap/static/umap/locale/fi.js +52 -17
  132. umap/static/umap/locale/fi.json +52 -17
  133. umap/static/umap/locale/fr.js +54 -17
  134. umap/static/umap/locale/fr.json +54 -17
  135. umap/static/umap/locale/gl.js +52 -17
  136. umap/static/umap/locale/gl.json +52 -17
  137. umap/static/umap/locale/he.js +52 -17
  138. umap/static/umap/locale/he.json +52 -17
  139. umap/static/umap/locale/hr.js +52 -17
  140. umap/static/umap/locale/hr.json +52 -17
  141. umap/static/umap/locale/hu.js +59 -24
  142. umap/static/umap/locale/hu.json +59 -24
  143. umap/static/umap/locale/id.js +52 -17
  144. umap/static/umap/locale/id.json +52 -17
  145. umap/static/umap/locale/is.js +52 -17
  146. umap/static/umap/locale/is.json +52 -17
  147. umap/static/umap/locale/it.js +52 -17
  148. umap/static/umap/locale/it.json +52 -17
  149. umap/static/umap/locale/ja.js +52 -17
  150. umap/static/umap/locale/ja.json +52 -17
  151. umap/static/umap/locale/ko.js +52 -17
  152. umap/static/umap/locale/ko.json +52 -17
  153. umap/static/umap/locale/lt.js +52 -17
  154. umap/static/umap/locale/lt.json +52 -17
  155. umap/static/umap/locale/ms.js +52 -17
  156. umap/static/umap/locale/ms.json +52 -17
  157. umap/static/umap/locale/nl.js +52 -17
  158. umap/static/umap/locale/nl.json +52 -17
  159. umap/static/umap/locale/no.js +52 -17
  160. umap/static/umap/locale/no.json +52 -17
  161. umap/static/umap/locale/pl.js +53 -17
  162. umap/static/umap/locale/pl.json +53 -17
  163. umap/static/umap/locale/pl_PL.json +52 -17
  164. umap/static/umap/locale/pt.js +52 -17
  165. umap/static/umap/locale/pt.json +52 -17
  166. umap/static/umap/locale/pt_BR.js +52 -17
  167. umap/static/umap/locale/pt_BR.json +52 -17
  168. umap/static/umap/locale/pt_PT.js +52 -17
  169. umap/static/umap/locale/pt_PT.json +52 -17
  170. umap/static/umap/locale/ro.js +52 -17
  171. umap/static/umap/locale/ro.json +52 -17
  172. umap/static/umap/locale/ru.js +52 -17
  173. umap/static/umap/locale/ru.json +52 -17
  174. umap/static/umap/locale/si.js +1 -1
  175. umap/static/umap/locale/si.json +1 -1
  176. umap/static/umap/locale/sk_SK.js +52 -17
  177. umap/static/umap/locale/sk_SK.json +52 -17
  178. umap/static/umap/locale/sl.js +52 -17
  179. umap/static/umap/locale/sl.json +52 -17
  180. umap/static/umap/locale/sr.js +52 -17
  181. umap/static/umap/locale/sr.json +52 -17
  182. umap/static/umap/locale/sv.js +52 -17
  183. umap/static/umap/locale/sv.json +52 -17
  184. umap/static/umap/locale/th_TH.js +52 -17
  185. umap/static/umap/locale/th_TH.json +52 -17
  186. umap/static/umap/locale/tr.js +52 -17
  187. umap/static/umap/locale/tr.json +52 -17
  188. umap/static/umap/locale/uk_UA.js +52 -17
  189. umap/static/umap/locale/uk_UA.json +52 -17
  190. umap/static/umap/locale/vi.js +52 -17
  191. umap/static/umap/locale/vi.json +52 -17
  192. umap/static/umap/locale/vi_VN.json +52 -17
  193. umap/static/umap/locale/zh.js +52 -17
  194. umap/static/umap/locale/zh.json +52 -17
  195. umap/static/umap/locale/zh_CN.json +52 -17
  196. umap/static/umap/locale/zh_TW.Big5.json +52 -17
  197. umap/static/umap/locale/zh_TW.js +53 -17
  198. umap/static/umap/locale/zh_TW.json +53 -17
  199. umap/static/umap/map.css +63 -226
  200. umap/static/umap/unittests/utils.js +18 -0
  201. umap/static/umap/vars.css +23 -5
  202. umap/templates/umap/components/alerts/alert.html +32 -29
  203. umap/templates/umap/css.html +2 -1
  204. umap/templates/umap/login_popup_end.html +18 -9
  205. umap/templates/umap/user_map_table.html +7 -2
  206. umap/tests/integration/conftest.py +10 -6
  207. umap/tests/integration/test_anonymous_owned_map.py +90 -37
  208. umap/tests/integration/test_basics.py +25 -1
  209. umap/tests/integration/test_browser.py +37 -0
  210. umap/tests/integration/test_cluster.py +110 -0
  211. umap/tests/integration/test_conditional_rules.py +107 -52
  212. umap/tests/integration/test_datalayer.py +9 -16
  213. umap/tests/integration/test_draw_polygon.py +6 -0
  214. umap/tests/integration/test_draw_polyline.py +11 -0
  215. umap/tests/integration/test_edit_marker.py +12 -1
  216. umap/tests/integration/test_export_map.py +19 -0
  217. umap/tests/integration/test_fields.py +541 -0
  218. umap/tests/integration/test_filters.py +616 -0
  219. umap/tests/integration/test_iframe.py +1 -1
  220. umap/tests/integration/test_import.py +38 -42
  221. umap/tests/integration/test_map_preview.py +1 -1
  222. umap/tests/integration/test_picto.py +1 -1
  223. umap/tests/integration/test_popup.py +31 -0
  224. umap/tests/integration/test_remote_data.py +60 -4
  225. umap/tests/integration/test_save.py +1 -1
  226. umap/tests/integration/test_share.py +4 -4
  227. umap/tests/integration/test_tableeditor.py +31 -7
  228. umap/tests/integration/test_websocket_sync.py +71 -20
  229. umap/tests/test_dashboard.py +11 -1
  230. umap/tests/test_statics.py +2 -2
  231. umap/tests/test_utils.py +19 -2
  232. umap/tests/test_views.py +1 -1
  233. umap/urls.py +1 -0
  234. umap/utils.py +8 -1
  235. umap/views.py +5 -0
  236. {umap_project-3.3.2.dist-info → umap_project-3.4.0.dist-info}/METADATA +15 -15
  237. {umap_project-3.3.2.dist-info → umap_project-3.4.0.dist-info}/RECORD +240 -236
  238. umap/static/umap/js/modules/facets.js +0 -164
  239. umap/tests/integration/test_facets_browser.py +0 -279
  240. {umap_project-3.3.2.dist-info → umap_project-3.4.0.dist-info}/WHEEL +0 -0
  241. {umap_project-3.3.2.dist-info → umap_project-3.4.0.dist-info}/entry_points.txt +0 -0
  242. {umap_project-3.3.2.dist-info → umap_project-3.4.0.dist-info}/licenses/LICENSE +0 -0
@@ -78,8 +78,7 @@ export default class Dialog extends WithTemplate {
78
78
  if (!this.dialogSupported) {
79
79
  this.elements.form.addEventListener('submit', (event) => {
80
80
  event.preventDefault()
81
- this.dialog.returnValue = 'accept'
82
- this.close()
81
+ this.accept()
83
82
  })
84
83
  }
85
84
  this.dialog.addEventListener('keydown', (e) => {
@@ -118,7 +117,6 @@ export default class Dialog extends WithTemplate {
118
117
  this.elements.cancel.hidden = !dialog.cancel
119
118
  this.elements.message.textContent = dialog.message
120
119
  this.elements.message.hidden = !dialog.message
121
- this.elements.target = dialog.target || ''
122
120
  this.elements.template.innerHTML = ''
123
121
  if (dialog.template?.nodeType === 1) {
124
122
  this.elements.template.appendChild(dialog.template)
@@ -137,12 +135,13 @@ export default class Dialog extends WithTemplate {
137
135
  if (currentZIndex) {
138
136
  this.dialog.style.zIndex = currentZIndex + 1
139
137
  }
140
-
141
- this.toggle(true)
142
-
138
+ if (this.dialogSupported) {
139
+ this.dialog.show()
140
+ } else {
141
+ this.dialog.hidden = false
142
+ }
143
143
  if (this.hasFormData) this.focusable[0].focus()
144
144
  else this.elements.accept.focus()
145
-
146
145
  return this.waitForUser()
147
146
  }
148
147
 
@@ -151,37 +150,49 @@ export default class Dialog extends WithTemplate {
151
150
  }
152
151
 
153
152
  close() {
154
- this.toggle(false)
155
- this.dialog.returnValue = undefined
156
- }
157
-
158
- toggle(open = false) {
153
+ this._closing = true
159
154
  if (this.dialogSupported) {
160
- if (open) this.dialog.show()
161
- else this.dialog.close()
155
+ this.dialog.close()
162
156
  } else {
163
- this.dialog.hidden = !open
164
- if (this.elements.target && !open) {
165
- this.elements.target.focus()
166
- }
167
- if (!open) {
168
- this.dialog.dispatchEvent(new CustomEvent('close'))
169
- }
157
+ this.dialog.hidden = true
158
+ this.dialog.dispatchEvent(new CustomEvent('close'))
170
159
  }
171
160
  }
172
161
 
173
- waitForUser() {
162
+ accept() {
163
+ this.dialog.returnValue = 'accept'
174
164
  return new Promise((resolve) => {
175
165
  this.dialog.addEventListener(
176
166
  'close',
177
- (event) => {
178
- if (this.dialog.returnValue === 'accept') {
179
- const value = this.hasFormData ? this.collectFormData() : true
180
- resolve(value)
181
- }
167
+ () => {
168
+ resolve()
182
169
  },
183
170
  { once: true }
184
171
  )
172
+ this.close()
173
+ })
174
+ }
175
+
176
+ waitForUser() {
177
+ return new Promise((resolve) => {
178
+ const onClose = () => {
179
+ this._closing = false
180
+ if (this.dialog.returnValue === 'accept') {
181
+ const value = this.hasFormData ? this.collectFormData() : true
182
+ resolve(value)
183
+ }
184
+ }
185
+ const waitForClose = () => {
186
+ this.dialog.returnValue = undefined
187
+ this.dialog.addEventListener('close', () => onClose(), { once: true })
188
+ }
189
+ if (this._closing) {
190
+ // We are opening a new dialog while another is not fully closed,
191
+ // so let's first wait for that one to be fully closed
192
+ this.dialog.addEventListener('close', () => waitForClose(), { once: true })
193
+ } else {
194
+ waitForClose()
195
+ }
185
196
  })
186
197
  }
187
198
 
@@ -7,7 +7,7 @@ export class Panel {
7
7
  this._umap = umap
8
8
  this._leafletMap = leafletMap
9
9
  this.container = DomUtil.create('div', '', this.parent)
10
- // This will be set once according to the panel configurated at load
10
+ // This will be set once according to the panel configured at load
11
11
  // or by using panels as popups
12
12
  this.mode = null
13
13
  this.className = 'left'
@@ -26,12 +26,12 @@ export class Panel {
26
26
  }
27
27
 
28
28
  open({ content, className, highlight, actions = [] } = {}) {
29
+ let isOpen = false
29
30
  if (this.isOpen()) {
31
+ isOpen = true
30
32
  this.onClose()
31
33
  }
32
- this.container.className = `with-transition panel window ${this.className} ${
33
- this.mode || ''
34
- }`
34
+ this.container.className = `with-transition panel window ${this.className} ${this.mode || ''} ${isOpen ? 'on' : ''}`
35
35
  if (highlight) {
36
36
  this.container.dataset.highlight = highlight
37
37
  }
@@ -56,8 +56,22 @@ export class Panel {
56
56
  }
57
57
  if (className) DomUtil.addClass(body, className)
58
58
  const promise = new Promise((resolve, reject) => {
59
- DomUtil.addClass(this.container, 'on')
60
- resolve(this)
59
+ if (isOpen) {
60
+ resolve(this)
61
+ } else {
62
+ this.container.classList.add('on')
63
+ Promise.all(
64
+ this.container.getAnimations?.().map((animation) => animation.finished)
65
+ )
66
+ .then(() => {
67
+ resolve(this)
68
+ })
69
+ .catch(() => {
70
+ // Panel has been removed, so the DOM has changed, so the animations
71
+ // were cancelled, we want the new panel callback to be called anyway.
72
+ resolve(this)
73
+ })
74
+ }
61
75
  })
62
76
  DomEvent.on(closeButton, 'click', this.close, this)
63
77
  DomEvent.on(resizeButton, 'click', this.resize, this)
@@ -83,11 +97,12 @@ export class Panel {
83
97
  }
84
98
 
85
99
  onClose() {
86
- if (DomUtil.hasClass(this.container, 'on')) {
87
- DomUtil.removeClass(this.container, 'on')
100
+ if (this.container.classList.contains('on')) {
101
+ this.container.classList.remove('on')
88
102
  this._leafletMap.invalidateSize({ pan: false })
89
103
  }
90
104
  }
105
+
91
106
  scrollTo(selector) {
92
107
  const fieldset = this.container.querySelector(selector)
93
108
  if (!fieldset) return
@@ -4,11 +4,10 @@ import * as Utils from '../utils.js'
4
4
  import { Positioned } from './base.js'
5
5
 
6
6
  export default class Tooltip extends Positioned {
7
- constructor(parent) {
7
+ constructor() {
8
8
  super()
9
- this.parent = parent
9
+ this.parent = document.body
10
10
  this.container = Utils.loadTemplate('<div class="umap-tooltip-container"></div>')
11
- this.parent.appendChild(this.container)
12
11
  DomEvent.disableClickPropagation(this.container)
13
12
  this.container.addEventListener('contextmenu', (event) => event.stopPropagation()) // Do not activate our custom context menu.
14
13
  this.container.addEventListener('wheel', (event) => event.stopPropagation())
@@ -25,7 +24,7 @@ export default class Tooltip extends Positioned {
25
24
  } else {
26
25
  this.container.innerHTML = Utils.escapeHTML(opts.content)
27
26
  }
28
- this.parent.classList.add('umap-tooltip')
27
+ this.parent.appendChild(this.container)
29
28
  this.openAt(opts)
30
29
  }
31
30
  this.TOOLTIP_ID = window.setTimeout(L.bind(showIt, this), opts.delay || 0)
@@ -49,6 +48,8 @@ export default class Tooltip extends Positioned {
49
48
  this.toggleClassPosition()
50
49
  this.container.innerHTML = ''
51
50
  this.setPosition({})
52
- this.parent.classList.remove('umap-tooltip')
51
+ if (this.parent.contains(this.container)) {
52
+ this.parent.removeChild(this.container)
53
+ }
53
54
  }
54
55
  }
@@ -10,7 +10,8 @@ import {
10
10
  import Browser from './browser.js'
11
11
  import Caption from './caption.js'
12
12
  import { DataLayer } from './data/layer.js'
13
- import Facets from './facets.js'
13
+ import { Fields } from './data/fields.js'
14
+ import { Filters, migrateLegacyFilters } from './filters.js'
14
15
  import { MutatingForm } from './form/builder.js'
15
16
  import { Formatter } from './formatter.js'
16
17
  import Help from './help.js'
@@ -32,6 +33,7 @@ import { EditPanel, FullPanel, Panel } from './ui/panel.js'
32
33
  import Tooltip from './ui/tooltip.js'
33
34
  import URLs from './urls.js'
34
35
  import * as Utils from './utils.js'
36
+ import * as DOMUtils from './domutils.js'
35
37
  import { DataLayerManager } from './managers.js'
36
38
  import { Importer as OpenRouteService } from './importers/openrouteservice.js'
37
39
 
@@ -48,6 +50,7 @@ export default class Umap {
48
50
  }
49
51
 
50
52
  async init(element, geojson) {
53
+ this.migrateLegacyProperties(geojson.properties)
51
54
  this.properties = Object.assign(
52
55
  {
53
56
  enableMarkerDraw: true,
@@ -131,7 +134,8 @@ export default class Umap {
131
134
  this.contextmenu = new ContextMenu()
132
135
  this.server = new ServerRequest()
133
136
  this.request = new Request()
134
- this.facets = new Facets(this)
137
+ this.fields = new Fields(this, this.dialog)
138
+ this.filters = new Filters(this, this)
135
139
  this.browser = new Browser(this, this._leafletMap)
136
140
  this.caption = new Caption(this, this._leafletMap)
137
141
  this.importer = new Importer(this)
@@ -160,10 +164,6 @@ export default class Umap {
160
164
  ) {
161
165
  this.properties.slideshow.active = true
162
166
  }
163
- if (this.properties.advancedFilterKey) {
164
- this.properties.facetKey = this.properties.advancedFilterKey
165
- delete this.properties.advancedFilterKey
166
- }
167
167
 
168
168
  // Global storage for retrieving datalayers and features.
169
169
  this.datalayers = new DataLayerManager()
@@ -261,17 +261,6 @@ export default class Umap {
261
261
  return window.self !== window.top
262
262
  }
263
263
 
264
- get fields() {
265
- if (!this.properties.fields) this.properties.fields = []
266
- return this.properties.fields
267
- }
268
-
269
- get fieldKeys() {
270
- return this.fields
271
- .map((field) => field.key)
272
- .concat(...this.datalayers.active().map((dl) => dl.fieldKeys))
273
- }
274
-
275
264
  setPropertiesFromQueryString() {
276
265
  const asBoolean = (key) => {
277
266
  const value = this.searchParams.get(key)
@@ -391,6 +380,17 @@ export default class Umap {
391
380
  }
392
381
  }
393
382
 
383
+ hasFilters() {
384
+ return this.filters.size || this.datalayers.active().some((d) => d.filters.size)
385
+ }
386
+
387
+ hasActiveFilters() {
388
+ return (
389
+ this.filters.isActive() ||
390
+ this.datalayers.active().some((d) => d.filters.isActive())
391
+ )
392
+ }
393
+
394
394
  getOwnContextMenu(event) {
395
395
  const items = []
396
396
  if (this.editEnabled) {
@@ -424,7 +424,7 @@ export default class Umap {
424
424
  action: () => this.openBrowser('data'),
425
425
  }
426
426
  )
427
- if (this.properties.facetKey) {
427
+ if (this.hasFilters()) {
428
428
  items.push({
429
429
  label: translate('Filter data'),
430
430
  action: () => this.openBrowser('filters'),
@@ -444,7 +444,13 @@ export default class Umap {
444
444
  }
445
445
 
446
446
  getSharedContextMenu(event) {
447
- const items = []
447
+ const latlng = `${event.latlng.lat.toFixed(6)},${event.latlng.lng.toFixed(6)}`
448
+ const items = [
449
+ {
450
+ label: latlng,
451
+ action: () => DOMUtils.copyToClipboard(latlng),
452
+ },
453
+ ]
448
454
  if (this.properties.urls.routing) {
449
455
  items.push({
450
456
  label: translate('Directions from here'),
@@ -481,7 +487,7 @@ export default class Umap {
481
487
  }
482
488
 
483
489
  search() {
484
- if (this._leafletMap._controls.search) this._leafletMap._controls.search.open()
490
+ if (this._leafletMap._controls.search) this._leafletMap._controls.search.onClick()
485
491
  }
486
492
 
487
493
  hasEditMode() {
@@ -666,7 +672,7 @@ export default class Umap {
666
672
  const parent = this._leafletMap.getPane('overlayPane')
667
673
  const datalayers = Object.values(this.datalayers)
668
674
  .filter((datalayer) => !datalayer._isDeleted)
669
- .sort((datalayer1, datalayer2) => datalayer1.rank > datalayer2.rank)
675
+ .sort((datalayer1, datalayer2) => datalayer1.rank - datalayer2.rank)
670
676
  for (const datalayer of datalayers) {
671
677
  const child = parent.querySelector(`[data-id="${datalayer.id}"]`)
672
678
  parent.appendChild(child)
@@ -720,7 +726,14 @@ export default class Umap {
720
726
  return this.properties.name || translate('Untitled map')
721
727
  }
722
728
 
729
+ migrateLegacyProperties(properties) {
730
+ if (migrateLegacyFilters(properties)) {
731
+ this._migrated = true
732
+ }
733
+ }
734
+
723
735
  setProperties(newProperties) {
736
+ this.migrateLegacyProperties(newProperties)
724
737
  for (const key of Object.keys(SCHEMA)) {
725
738
  if (newProperties[key] !== undefined) {
726
739
  this.properties[key] = newProperties[key]
@@ -758,7 +771,7 @@ export default class Umap {
758
771
  'properties.is_template',
759
772
  ]
760
773
 
761
- DomUtil.createTitle(container, translate('Edit map details'), 'icon-caption')
774
+ DomUtil.createTitle(container, translate('Edit map details'), 'icon-info')
762
775
  const builder = new MutatingForm(this, metadataFields, {
763
776
  className: 'map-metadata',
764
777
  umap: this,
@@ -868,23 +881,22 @@ export default class Umap {
868
881
  defaultShapeProperties.appendChild(builder.build())
869
882
  }
870
883
 
871
- _editDefaultProperties(container) {
872
- const optionsFields = [
884
+ _editDefaultKeys(container) {
885
+ const shapeOptions = [
873
886
  'properties.zoomTo',
874
887
  'properties.easing',
875
888
  'properties.labelKey',
876
889
  'properties.sortKey',
877
890
  'properties.filterKey',
878
- 'properties.facetKey',
879
891
  'properties.slugKey',
880
892
  ]
881
893
 
882
- const builder = new MutatingForm(this, optionsFields, { umap: this })
883
- const defaultProperties = DomUtil.createFieldset(
894
+ const builder = new MutatingForm(this, shapeOptions, { umap: this })
895
+ const defaultShapeProperties = DomUtil.createFieldset(
884
896
  container,
885
897
  translate('Default properties')
886
898
  )
887
- defaultProperties.appendChild(builder.build())
899
+ defaultShapeProperties.appendChild(builder.build())
888
900
  }
889
901
 
890
902
  _editInteractionsProperties(container) {
@@ -1159,7 +1171,8 @@ export default class Umap {
1159
1171
  )
1160
1172
  this._editControls(container)
1161
1173
  this._editShapeProperties(container)
1162
- this._editDefaultProperties(container)
1174
+ this._editDefaultKeys(container)
1175
+ this.fields.edit(container)
1163
1176
  this._editInteractionsProperties(container)
1164
1177
  this.rules.edit(container)
1165
1178
  this._editTilelayer(container)
@@ -1178,6 +1191,16 @@ export default class Umap {
1178
1191
  })
1179
1192
  }
1180
1193
 
1194
+ onAnonymousSave(editUrl) {
1195
+ AlertCreation.info(
1196
+ this,
1197
+ translate('Hey, you created a map without an account!'),
1198
+ Number.Infinity,
1199
+ editUrl,
1200
+ this.properties.urls.map_send_edit_link ? this.sendEditLinkEmail.bind(this) : null
1201
+ )
1202
+ }
1203
+
1181
1204
  async save() {
1182
1205
  const geojson = {
1183
1206
  type: 'Feature',
@@ -1197,11 +1220,10 @@ export default class Umap {
1197
1220
  if (error) {
1198
1221
  return
1199
1222
  }
1200
- // TOOD: map.save may not always be the first call during save process
1223
+ // TODO: map.save may not always be the first call during save process
1201
1224
  // since SAVEMANAGER refactor
1202
1225
  if (data.login_required) {
1203
- window.onLogin = () => this.saveAll()
1204
- window.open(data.login_required)
1226
+ this.askForLogin().then(() => this.saveAll())
1205
1227
  return
1206
1228
  }
1207
1229
  this.properties.user = data.user
@@ -1209,17 +1231,9 @@ export default class Umap {
1209
1231
  this.properties.id = data.id
1210
1232
  this.permissions.setProperties(data.permissions)
1211
1233
  this.permissions.commit()
1212
- if (data.permissions?.anonymous_edit_url) {
1213
- this._leafletMap.once('saved', () => {
1214
- AlertCreation.info(
1215
- translate('Your map has been created with an anonymous account!'),
1216
- Number.Infinity,
1217
- data.permissions.anonymous_edit_url,
1218
- this.properties.urls.map_send_edit_link
1219
- ? this.sendEditLinkEmail.bind(this)
1220
- : null
1221
- )
1222
- })
1234
+ const anonymousEditUrl = data.permissions?.anonymous_edit_url
1235
+ if (anonymousEditUrl) {
1236
+ this._leafletMap.once('saved', () => this.onAnonymousSave(anonymousEditUrl))
1223
1237
  } else {
1224
1238
  this._leafletMap.once('saved', () => {
1225
1239
  Alert.success(translate('Congratulations, your map has been created!'))
@@ -1241,6 +1255,29 @@ export default class Umap {
1241
1255
  return true
1242
1256
  }
1243
1257
 
1258
+ askForLogin() {
1259
+ const promise = new Promise((resolve) => {
1260
+ const bc = new BroadcastChannel('auth')
1261
+ bc.onmessage = (event) => {
1262
+ if (event.data === 'auth:ok') {
1263
+ bc.postMessage('auth:close')
1264
+ const url = this.urls.get('whoami', { map_id: this.id })
1265
+ this.server.get(url).then(([data]) => {
1266
+ this.properties.user = data.user
1267
+ if (!this.id) {
1268
+ this.properties.permissions.owner = { ...data.user }
1269
+ }
1270
+ this.permissions.pull()
1271
+ this.render(['user', 'properties.permissions'])
1272
+ resolve()
1273
+ })
1274
+ }
1275
+ }
1276
+ })
1277
+ window.open(this.urls.get('login'))
1278
+ return promise
1279
+ }
1280
+
1244
1281
  exportProperties() {
1245
1282
  const properties = {}
1246
1283
  for (const key of Object.keys(SCHEMA)) {
@@ -1251,6 +1288,18 @@ export default class Umap {
1251
1288
  return properties
1252
1289
  }
1253
1290
 
1291
+ renameField(oldName, newName) {
1292
+ for (const datalayer of this.datalayers.active()) {
1293
+ datalayer.renameFeaturesField(oldName, newName)
1294
+ }
1295
+ }
1296
+
1297
+ deleteField(name) {
1298
+ for (const datalayer of this.datalayers.active()) {
1299
+ datalayer.deleteFeaturesField(name)
1300
+ }
1301
+ }
1302
+
1254
1303
  geometry() {
1255
1304
  /* Return a GeoJSON geometry Object */
1256
1305
  const latlng = this._leafletMap.latLng(
@@ -1269,6 +1318,51 @@ export default class Umap {
1269
1318
  this.drop.enable()
1270
1319
  this.fire('edit:enabled')
1271
1320
  this.initSyncEngine()
1321
+ this.checkForLegacy()
1322
+ this.checkForAnonymous()
1323
+ }
1324
+
1325
+ checkForAnonymous() {
1326
+ if (
1327
+ this.permissions.isAnonymousMap() &&
1328
+ this.permissions.isOwner() &&
1329
+ this.permissions.userIsAuth()
1330
+ ) {
1331
+ this.dialog
1332
+ .confirm(
1333
+ translate('This map is anonymous, do you want to attach it to your account?')
1334
+ )
1335
+ .then(() => {
1336
+ this.permissions.attach()
1337
+ })
1338
+ }
1339
+ }
1340
+
1341
+ checkForLegacy() {
1342
+ let needSaveAlert = false
1343
+ if (this._migrated) {
1344
+ needSaveAlert = true
1345
+ delete this._migrated
1346
+ // Force user to save
1347
+ this.sync.update('properties.name', this.properties.name, this.properties.name)
1348
+ }
1349
+ for (const datalayer of this.datalayers.active()) {
1350
+ if (!datalayer.isReadOnly() && datalayer._migrated) {
1351
+ datalayer._migrated = false
1352
+ // Force user to resave those datalayers
1353
+ datalayer.sync.update(
1354
+ 'properties.name',
1355
+ datalayer.properties.name,
1356
+ datalayer.properties.name
1357
+ )
1358
+ needSaveAlert = true
1359
+ }
1360
+ }
1361
+ if (needSaveAlert) {
1362
+ Alert.warning(
1363
+ translate('The map has been upgraded to latest version, please save it.')
1364
+ )
1365
+ }
1272
1366
  }
1273
1367
 
1274
1368
  disableEdit() {
@@ -1312,6 +1406,12 @@ export default class Umap {
1312
1406
  // Propagate will remove the fields it has already
1313
1407
  // processed
1314
1408
  fields = this.propagate(fields)
1409
+ if (fields.includes('properties.filters')) {
1410
+ this.filters.load()
1411
+ if (this.browser.isOpen()) {
1412
+ this.browser.buildFilters()
1413
+ }
1414
+ }
1315
1415
 
1316
1416
  const impacts = Utils.getImpactsFromSchema(fields)
1317
1417
  for (const impact of impacts) {
@@ -1353,7 +1453,7 @@ export default class Umap {
1353
1453
  }
1354
1454
 
1355
1455
  // This method does a targeted update of the UI,
1356
- // it whould be merged with `render`` method and the
1456
+ // it would be merged with `render`` method and the
1357
1457
  // SCHEMA at some point
1358
1458
  propagate(fields = []) {
1359
1459
  const impacts = {
@@ -1364,7 +1464,7 @@ export default class Umap {
1364
1464
  },
1365
1465
  user: () => {
1366
1466
  Utils.eachElement('.umap-user .username', (el) => {
1367
- if (this.properties.user?.id) {
1467
+ if (this.permissions.userIsAuth()) {
1368
1468
  el.textContent = this.properties.user.name
1369
1469
  }
1370
1470
  })
@@ -1471,17 +1571,22 @@ export default class Umap {
1471
1571
  `
1472
1572
  const [container, { ul }] = Utils.loadTemplateWithRefs(template)
1473
1573
  this.datalayers.reverse().map((datalayer) => {
1474
- const row = Utils.loadTemplate(
1475
- `<li class="orderable"><i class="icon icon-16 icon-drag" title="${translate('Drag to reorder')}"></i></li>`
1476
- )
1477
- datalayer.renderToolbox(row)
1574
+ const [row, { toolbox, formbox }] = Utils.loadTemplateWithRefs(`
1575
+ <li class="orderable with-toolbox ${datalayer.cssId}">
1576
+ <span data-ref=formbox class="datalayer-editable-title truncate"></span>
1577
+ <span data-ref=toolbox>
1578
+ <i class="icon icon-16 icon-drag" title="${translate('Drag to reorder')}"></i>
1579
+ </span>
1580
+ </li>
1581
+ `)
1582
+ datalayer.renderToolbox(toolbox)
1478
1583
  const builder = new MutatingForm(
1479
1584
  datalayer,
1480
1585
  [['properties.name', { handler: 'EditableText' }]],
1481
1586
  { className: 'umap-form-inline' }
1482
1587
  )
1483
1588
  const form = builder.build()
1484
- row.appendChild(form)
1589
+ formbox.appendChild(form)
1485
1590
  row.classList.toggle('off', !datalayer.isVisible())
1486
1591
  row.dataset.id = datalayer.id
1487
1592
  ul.appendChild(row)
@@ -1624,6 +1729,8 @@ export default class Umap {
1624
1729
  const fields = Object.keys(importedData.properties).map(
1625
1730
  (field) => `properties.${field}`
1626
1731
  )
1732
+ this.fields.pull()
1733
+ this.filters.load()
1627
1734
  this.render(fields)
1628
1735
  this._leafletMap._setDefaultCenter()
1629
1736
  }
@@ -1637,7 +1744,7 @@ export default class Umap {
1637
1744
  this.importRaw(rawData)
1638
1745
  } catch (e) {
1639
1746
  console.error('Error importing data', e)
1640
- U.Alert.error(
1747
+ Alert.error(
1641
1748
  translate('Invalid umap data in {filename}', { filename: file.name })
1642
1749
  )
1643
1750
  }
@@ -40,7 +40,7 @@ function _getPropertyName(field) {
40
40
  * Return an array of unique impacts.
41
41
  *
42
42
  * @param {fields} list[fields]
43
- * @param object schema object. If ommited, global U.SCHEMA will be used.
43
+ * @param object schema object. If omitted, global U.SCHEMA will be used.
44
44
  * @returns Array[string]
45
45
  */
46
46
  export function getImpactsFromSchema(fields, schema) {
@@ -400,8 +400,30 @@ export function template(str, data) {
400
400
  })
401
401
  }
402
402
 
403
+ const DATE_REGEX = [
404
+ // Format 1: "YYYY-MM-DD"
405
+ /^(?<year>\d{4})[\-\/](?<month>\d{2})[\-\/](?<day>\d{2})$/,
406
+ // Format2 : "DD-MM-YYYY"
407
+ /^(?<day>0[1-9]|[12][0-9]|3[01])[\-\/](?<month>0[1-9]|1[0-2])[\-\/](?<year>\d{4})/,
408
+ ]
409
+
403
410
  export function parseNaiveDate(value) {
404
- const naive = new Date(value)
411
+ let naive
412
+ if (!value) return undefined
413
+ value = String(value)
414
+ for (const regex of DATE_REGEX) {
415
+ const parsed = value.match(regex)
416
+ if (parsed) {
417
+ const { year, month, day } = parsed.groups
418
+ naive = new Date(year, Number.parseInt(month, 10) - 1, Number.parseInt(day, 10))
419
+ break
420
+ }
421
+ }
422
+ if (!naive) {
423
+ naive = new Date(value)
424
+ }
425
+ // Number.isNaN will always return false for invalid date
426
+ if (isNaN(naive)) return undefined
405
427
  // Let's pretend naive date are UTC, and remove time…
406
428
  return new Date(Date.UTC(naive.getFullYear(), naive.getMonth(), naive.getDate()))
407
429
  }