umap-project 2.8.1__py3-none-any.whl → 2.9.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 (262) hide show
  1. umap/__init__.py +1 -1
  2. umap/admin.py +15 -2
  3. umap/asgi.py +12 -7
  4. umap/context_processors.py +1 -0
  5. umap/locale/br/LC_MESSAGES/django.mo +0 -0
  6. umap/locale/br/LC_MESSAGES/django.po +111 -67
  7. umap/locale/cs_CZ/LC_MESSAGES/django.mo +0 -0
  8. umap/locale/cs_CZ/LC_MESSAGES/django.po +110 -66
  9. umap/locale/el/LC_MESSAGES/django.mo +0 -0
  10. umap/locale/el/LC_MESSAGES/django.po +129 -85
  11. umap/locale/en/LC_MESSAGES/django.po +103 -60
  12. umap/locale/es/LC_MESSAGES/django.mo +0 -0
  13. umap/locale/es/LC_MESSAGES/django.po +114 -69
  14. umap/locale/fr/LC_MESSAGES/django.mo +0 -0
  15. umap/locale/fr/LC_MESSAGES/django.po +105 -61
  16. umap/locale/gl/LC_MESSAGES/django.mo +0 -0
  17. umap/locale/gl/LC_MESSAGES/django.po +216 -171
  18. umap/locale/hu/LC_MESSAGES/django.mo +0 -0
  19. umap/locale/hu/LC_MESSAGES/django.po +10 -10
  20. umap/locale/it/LC_MESSAGES/django.mo +0 -0
  21. umap/locale/it/LC_MESSAGES/django.po +142 -98
  22. umap/locale/nl/LC_MESSAGES/django.mo +0 -0
  23. umap/locale/nl/LC_MESSAGES/django.po +196 -151
  24. umap/locale/pt/LC_MESSAGES/django.mo +0 -0
  25. umap/locale/pt/LC_MESSAGES/django.po +115 -71
  26. umap/locale/zh_TW/LC_MESSAGES/django.mo +0 -0
  27. umap/locale/zh_TW/LC_MESSAGES/django.po +109 -65
  28. umap/management/commands/empty_trash.py +12 -1
  29. umap/migrations/0026_datalayer_modified_at_datalayer_share_status.py +26 -0
  30. umap/models.py +43 -13
  31. umap/settings/base.py +5 -2
  32. umap/static/umap/base.css +5 -2
  33. umap/static/umap/content.css +2 -22
  34. umap/static/umap/css/bar.css +39 -10
  35. umap/static/umap/css/contextmenu.css +14 -2
  36. umap/static/umap/css/form.css +33 -39
  37. umap/static/umap/css/icon.css +47 -5
  38. umap/static/umap/css/panel.css +20 -2
  39. umap/static/umap/css/popup.css +0 -1
  40. umap/static/umap/css/tooltip.css +33 -31
  41. umap/static/umap/img/16-white.svg +5 -3
  42. umap/static/umap/img/16.svg +1 -1
  43. umap/static/umap/img/24-white.svg +17 -16
  44. umap/static/umap/img/24.svg +29 -18
  45. umap/static/umap/img/providers/bitbucket.png +0 -0
  46. umap/static/umap/img/providers/github.png +0 -0
  47. umap/static/umap/img/providers/keycloak.png +0 -0
  48. umap/static/umap/img/providers/openstreetmap-oauth2.png +0 -0
  49. umap/static/umap/img/providers/twitter-oauth2.png +0 -0
  50. umap/static/umap/img/source/16-white.svg +6 -4
  51. umap/static/umap/img/source/16.svg +1 -1
  52. umap/static/umap/img/source/24-white.svg +20 -18
  53. umap/static/umap/img/source/24.svg +30 -19
  54. umap/static/umap/js/components/alerts/alert.js +4 -1
  55. umap/static/umap/js/modules/browser.js +8 -8
  56. umap/static/umap/js/modules/caption.js +30 -7
  57. umap/static/umap/js/modules/data/features.js +101 -56
  58. umap/static/umap/js/modules/data/layer.js +108 -83
  59. umap/static/umap/js/modules/form/builder.js +242 -0
  60. umap/static/umap/js/modules/form/fields.js +1346 -0
  61. umap/static/umap/js/modules/formatter.js +9 -8
  62. umap/static/umap/js/modules/help.js +20 -24
  63. umap/static/umap/js/modules/importer.js +6 -3
  64. umap/static/umap/js/modules/permissions.js +11 -6
  65. umap/static/umap/js/modules/rendering/icon.js +5 -1
  66. umap/static/umap/js/modules/rendering/layers/classified.js +12 -8
  67. umap/static/umap/js/modules/rendering/layers/cluster.js +11 -1
  68. umap/static/umap/js/modules/rendering/map.js +1 -23
  69. umap/static/umap/js/modules/rendering/ui.js +20 -38
  70. umap/static/umap/js/modules/rules.js +3 -2
  71. umap/static/umap/js/modules/saving.js +5 -0
  72. umap/static/umap/js/modules/schema.js +8 -6
  73. umap/static/umap/js/modules/share.js +3 -3
  74. umap/static/umap/js/modules/sync/engine.js +56 -26
  75. umap/static/umap/js/modules/sync/updaters.js +15 -6
  76. umap/static/umap/js/modules/sync/websocket.js +50 -37
  77. umap/static/umap/js/modules/tableeditor.js +3 -2
  78. umap/static/umap/js/modules/ui/bar.js +101 -9
  79. umap/static/umap/js/modules/ui/base.js +7 -24
  80. umap/static/umap/js/modules/ui/contextmenu.js +9 -2
  81. umap/static/umap/js/modules/ui/panel.js +5 -1
  82. umap/static/umap/js/modules/ui/tooltip.js +19 -11
  83. umap/static/umap/js/modules/umap.js +124 -71
  84. umap/static/umap/js/modules/utils.js +196 -12
  85. umap/static/umap/js/umap.controls.js +12 -354
  86. umap/static/umap/locale/am_ET.js +17 -5
  87. umap/static/umap/locale/am_ET.json +17 -5
  88. umap/static/umap/locale/ar.js +17 -5
  89. umap/static/umap/locale/ar.json +17 -5
  90. umap/static/umap/locale/ast.js +17 -5
  91. umap/static/umap/locale/ast.json +17 -5
  92. umap/static/umap/locale/bg.js +17 -5
  93. umap/static/umap/locale/bg.json +17 -5
  94. umap/static/umap/locale/br.js +33 -20
  95. umap/static/umap/locale/br.json +33 -20
  96. umap/static/umap/locale/ca.js +17 -5
  97. umap/static/umap/locale/ca.json +17 -5
  98. umap/static/umap/locale/cs_CZ.js +15 -5
  99. umap/static/umap/locale/cs_CZ.json +15 -5
  100. umap/static/umap/locale/da.js +17 -5
  101. umap/static/umap/locale/da.json +17 -5
  102. umap/static/umap/locale/de.js +17 -5
  103. umap/static/umap/locale/de.json +17 -5
  104. umap/static/umap/locale/el.js +63 -51
  105. umap/static/umap/locale/el.json +63 -51
  106. umap/static/umap/locale/en.js +15 -5
  107. umap/static/umap/locale/en.json +15 -5
  108. umap/static/umap/locale/en_US.json +17 -5
  109. umap/static/umap/locale/es.js +25 -13
  110. umap/static/umap/locale/es.json +25 -13
  111. umap/static/umap/locale/et.js +17 -5
  112. umap/static/umap/locale/et.json +17 -5
  113. umap/static/umap/locale/eu.js +17 -5
  114. umap/static/umap/locale/eu.json +17 -5
  115. umap/static/umap/locale/fa_IR.js +17 -5
  116. umap/static/umap/locale/fa_IR.json +17 -5
  117. umap/static/umap/locale/fi.js +17 -5
  118. umap/static/umap/locale/fi.json +17 -5
  119. umap/static/umap/locale/fr.js +16 -6
  120. umap/static/umap/locale/fr.json +16 -6
  121. umap/static/umap/locale/gl.js +357 -345
  122. umap/static/umap/locale/gl.json +357 -345
  123. umap/static/umap/locale/he.js +17 -5
  124. umap/static/umap/locale/he.json +17 -5
  125. umap/static/umap/locale/hr.js +17 -5
  126. umap/static/umap/locale/hr.json +17 -5
  127. umap/static/umap/locale/hu.js +39 -27
  128. umap/static/umap/locale/hu.json +39 -27
  129. umap/static/umap/locale/id.js +17 -5
  130. umap/static/umap/locale/id.json +17 -5
  131. umap/static/umap/locale/is.js +17 -5
  132. umap/static/umap/locale/is.json +17 -5
  133. umap/static/umap/locale/it.js +125 -113
  134. umap/static/umap/locale/it.json +125 -113
  135. umap/static/umap/locale/ja.js +17 -5
  136. umap/static/umap/locale/ja.json +17 -5
  137. umap/static/umap/locale/ko.js +17 -5
  138. umap/static/umap/locale/ko.json +17 -5
  139. umap/static/umap/locale/lt.js +17 -5
  140. umap/static/umap/locale/lt.json +17 -5
  141. umap/static/umap/locale/ms.js +17 -5
  142. umap/static/umap/locale/ms.json +17 -5
  143. umap/static/umap/locale/nl.js +132 -119
  144. umap/static/umap/locale/nl.json +132 -119
  145. umap/static/umap/locale/no.js +17 -5
  146. umap/static/umap/locale/no.json +17 -5
  147. umap/static/umap/locale/pl.js +17 -5
  148. umap/static/umap/locale/pl.json +17 -5
  149. umap/static/umap/locale/pl_PL.json +17 -5
  150. umap/static/umap/locale/pt.js +38 -25
  151. umap/static/umap/locale/pt.json +38 -25
  152. umap/static/umap/locale/pt_BR.js +17 -5
  153. umap/static/umap/locale/pt_BR.json +17 -5
  154. umap/static/umap/locale/pt_PT.js +17 -5
  155. umap/static/umap/locale/pt_PT.json +17 -5
  156. umap/static/umap/locale/ro.js +17 -5
  157. umap/static/umap/locale/ro.json +17 -5
  158. umap/static/umap/locale/ru.js +17 -5
  159. umap/static/umap/locale/ru.json +17 -5
  160. umap/static/umap/locale/sk_SK.js +17 -5
  161. umap/static/umap/locale/sk_SK.json +17 -5
  162. umap/static/umap/locale/sl.js +17 -5
  163. umap/static/umap/locale/sl.json +17 -5
  164. umap/static/umap/locale/sr.js +17 -5
  165. umap/static/umap/locale/sr.json +17 -5
  166. umap/static/umap/locale/sv.js +17 -5
  167. umap/static/umap/locale/sv.json +17 -5
  168. umap/static/umap/locale/th_TH.js +17 -5
  169. umap/static/umap/locale/th_TH.json +17 -5
  170. umap/static/umap/locale/tr.js +17 -5
  171. umap/static/umap/locale/tr.json +17 -5
  172. umap/static/umap/locale/uk_UA.js +17 -5
  173. umap/static/umap/locale/uk_UA.json +17 -5
  174. umap/static/umap/locale/vi.js +17 -5
  175. umap/static/umap/locale/vi.json +17 -5
  176. umap/static/umap/locale/vi_VN.json +17 -5
  177. umap/static/umap/locale/zh.js +17 -5
  178. umap/static/umap/locale/zh.json +17 -5
  179. umap/static/umap/locale/zh_CN.json +17 -5
  180. umap/static/umap/locale/zh_TW.Big5.json +17 -5
  181. umap/static/umap/locale/zh_TW.js +15 -5
  182. umap/static/umap/locale/zh_TW.json +15 -5
  183. umap/static/umap/map.css +29 -76
  184. umap/static/umap/nav.css +6 -3
  185. umap/static/umap/unittests/utils.js +14 -0
  186. umap/static/umap/vars.css +3 -0
  187. umap/static/umap/vendors/dompurify/purify.es.js +138 -354
  188. umap/static/umap/vendors/dompurify/purify.es.mjs.map +1 -1
  189. umap/static/umap/vendors/editable/Leaflet.Editable.js +1 -0
  190. umap/sync/__init__.py +0 -0
  191. umap/sync/app.py +187 -0
  192. umap/sync/payloads.py +56 -0
  193. umap/templates/auth/user_detail.html +4 -0
  194. umap/templates/auth/user_form.html +9 -6
  195. umap/templates/auth/user_stars.html +4 -0
  196. umap/templates/base.html +1 -1
  197. umap/templates/registration/login.html +2 -5
  198. umap/templates/umap/about.html +5 -0
  199. umap/templates/umap/about_summary.html +2 -2
  200. umap/templates/umap/components/provider.html +8 -0
  201. umap/templates/umap/content_footer.html +1 -1
  202. umap/templates/umap/css.html +0 -2
  203. umap/templates/umap/js.html +0 -4
  204. umap/templates/umap/map_detail.html +1 -1
  205. umap/templates/umap/password_change.html +4 -0
  206. umap/templates/umap/password_change_done.html +4 -0
  207. umap/templates/umap/search.html +4 -0
  208. umap/templates/umap/search_bar.html +1 -0
  209. umap/templates/umap/team_confirm_delete.html +4 -0
  210. umap/templates/umap/team_detail.html +4 -0
  211. umap/templates/umap/team_form.html +4 -0
  212. umap/templates/umap/user_dashboard.html +1 -1
  213. umap/templates/umap/user_teams.html +4 -0
  214. umap/tests/base.py +3 -1
  215. umap/tests/integration/conftest.py +16 -23
  216. umap/tests/integration/test_anonymous_owned_map.py +2 -2
  217. umap/tests/integration/test_basics.py +4 -7
  218. umap/tests/integration/test_caption.py +1 -0
  219. umap/tests/integration/test_categorized_layer.py +4 -8
  220. umap/tests/integration/test_choropleth.py +1 -1
  221. umap/tests/integration/test_conditional_rules.py +3 -3
  222. umap/tests/integration/test_draw_polygon.py +14 -22
  223. umap/tests/integration/test_draw_polyline.py +6 -14
  224. umap/tests/integration/test_edit_datalayer.py +11 -11
  225. umap/tests/integration/test_edit_map.py +30 -4
  226. umap/tests/integration/test_edit_marker.py +5 -5
  227. umap/tests/integration/test_edit_polygon.py +6 -6
  228. umap/tests/integration/test_features_id_generation.py +2 -6
  229. umap/tests/integration/test_import.py +115 -29
  230. umap/tests/integration/test_optimistic_merge.py +1 -0
  231. umap/tests/integration/test_owned_map.py +1 -1
  232. umap/tests/integration/test_picto.py +8 -8
  233. umap/tests/integration/test_save.py +3 -2
  234. umap/tests/integration/test_star.py +13 -9
  235. umap/tests/integration/test_tableeditor.py +8 -7
  236. umap/tests/integration/test_view_marker.py +10 -0
  237. umap/tests/integration/test_websocket_sync.py +239 -64
  238. umap/tests/settings.py +2 -0
  239. umap/tests/test_datalayer.py +2 -3
  240. umap/tests/test_datalayer_views.py +20 -1
  241. umap/tests/test_empty_trash.py +10 -3
  242. umap/tests/test_map_views.py +11 -0
  243. umap/utils.py +27 -11
  244. umap/views.py +37 -6
  245. {umap_project-2.8.1.dist-info → umap_project-2.9.0.dist-info}/METADATA +22 -22
  246. {umap_project-2.8.1.dist-info → umap_project-2.9.0.dist-info}/RECORD +249 -250
  247. {umap_project-2.8.1.dist-info → umap_project-2.9.0.dist-info}/WHEEL +1 -1
  248. umap/management/commands/run_websocket_server.py +0 -23
  249. umap/settings/local_s3.py +0 -45
  250. umap/static/umap/bitbucket.png +0 -0
  251. umap/static/umap/github.png +0 -0
  252. umap/static/umap/js/umap.forms.js +0 -1242
  253. umap/static/umap/keycloak.png +0 -0
  254. umap/static/umap/openstreetmap.png +0 -0
  255. umap/static/umap/twitter.png +0 -0
  256. umap/static/umap/vendors/formbuilder/Leaflet.FormBuilder.js +0 -468
  257. umap/static/umap/vendors/toolbar/leaflet.toolbar.css +0 -1
  258. umap/static/umap/vendors/toolbar/leaflet.toolbar.js +0 -1
  259. umap/tests/test_websocket_server.py +0 -22
  260. umap/websocket_server.py +0 -202
  261. {umap_project-2.8.1.dist-info → umap_project-2.9.0.dist-info}/entry_points.txt +0 -0
  262. {umap_project-2.8.1.dist-info → umap_project-2.9.0.dist-info}/licenses/LICENSE +0 -0
@@ -1,5 +1,6 @@
1
1
  /* Uses globals for: csv2geojson, osmtogeojson (not available as ESM) */
2
2
  import { translate } from './i18n.js'
3
+ import { uMapAlert as Alert } from '../components/alerts/alert.js'
3
4
 
4
5
  export const EXPORT_FORMATS = {
5
6
  geojson: {
@@ -58,11 +59,7 @@ export class Formatter {
58
59
  }
59
60
 
60
61
  async fromGeoJSON(str) {
61
- try {
62
- return JSON.parse(str)
63
- } catch (err) {
64
- U.Alert.error(`Invalid JSON file: ${err}`)
65
- }
62
+ return JSON.parse(str)
66
63
  }
67
64
 
68
65
  async fromOSM(str) {
@@ -106,8 +103,12 @@ export class Formatter {
106
103
  message: err[0].message,
107
104
  })
108
105
  }
109
- U.Alert.error(message, 10000)
110
- console.error(err)
106
+ if (str.split(/\r\n|\r|\n/).length <= 2) {
107
+ // Seems like a blank CSV, let's not warn
108
+ console.debug(err)
109
+ } else {
110
+ Alert.error(message, 10000)
111
+ }
111
112
  }
112
113
  if (result?.features.length) {
113
114
  callback(result)
@@ -127,7 +128,7 @@ export class Formatter {
127
128
  const doc = new DOMParser().parseFromString(x, 'text/xml')
128
129
  const errorNode = doc.querySelector('parsererror')
129
130
  if (errorNode) {
130
- U.Alert.error(translate('Cannot parse data'))
131
+ Alert.error(translate('Cannot parse data'))
131
132
  }
132
133
  return doc
133
134
  }
@@ -206,13 +206,23 @@ export default class Help {
206
206
 
207
207
  // Special dynamic case. Do we still think this dialog is useful?
208
208
  showGetStarted() {
209
- const container = DomUtil.add('div')
210
- DomUtil.createTitle(container, translate('Where do we go from here?'))
211
- DomUtil.element({
212
- tagName: 'div',
213
- className: 'umap-help-entry',
214
- parent: container,
215
- }).appendChild(this._buildEditEntry())
209
+ const [container, { ul }] = Utils.loadTemplateWithRefs(`
210
+ <div>
211
+ <h3><i class="icon icon-16 icon-help"></i>${translate('Where do we go from here?')}</h3>
212
+ <ul data-ref=ul class="umap-getstarted"></ul>
213
+ </div>
214
+ `)
215
+ const elements = document.querySelectorAll('[data-getstarted]')
216
+ for (const el of elements) {
217
+ const [node, { button }] = Utils.loadTemplateWithRefs(
218
+ `<li><button data-ref=button type="button" title="${el.title}">${el.innerHTML}${el.title}</button></li>`
219
+ )
220
+ ul.appendChild(node)
221
+ button.addEventListener('click', () => {
222
+ el.click()
223
+ this.dialog.close()
224
+ })
225
+ }
216
226
  this.dialog.open({ template: container })
217
227
  }
218
228
 
@@ -228,24 +238,10 @@ export default class Help {
228
238
 
229
239
  parse(container) {
230
240
  for (const element of container.querySelectorAll('[data-help]')) {
231
- this.button(element, element.dataset.help.split(','))
232
- }
233
- }
234
-
235
- _buildEditEntry() {
236
- const container = DomUtil.create('div', '')
237
- const actionsContainer = DomUtil.create('ul', 'umap-edit-actions', container)
238
- const addAction = (action) => {
239
- const actionContainer = DomUtil.add('li', '', actionsContainer)
240
- DomUtil.add('i', action.options.className, actionContainer)
241
- DomUtil.add('span', '', actionContainer, action.options.tooltip)
242
- DomEvent.on(actionContainer, 'click', action.addHooks, action)
243
- DomEvent.on(actionContainer, 'click', this.dialog.close, this.dialog)
244
- }
245
- for (const action of Object.values(Help.MENU_ACTIONS)) {
246
- addAction(action)
241
+ if (element.dataset.help) {
242
+ this.button(element, element.dataset.help.split(','))
243
+ }
247
244
  }
248
- return container
249
245
  }
250
246
  }
251
247
 
@@ -10,7 +10,7 @@ import Dialog from './ui/dialog.js'
10
10
  import * as Utils from './utils.js'
11
11
 
12
12
  const TEMPLATE = `
13
- <div class="umap-upload">
13
+ <div class="umap-import">
14
14
  <h3><i class="icon icon-16 icon-upload"></i><span>${translate('Import data')}</span></h3>
15
15
  <fieldset class="formbox">
16
16
  <legend class="counter">${translate('Choose data')}</legend>
@@ -261,7 +261,10 @@ export default class Importer extends Utils.WithTemplate {
261
261
 
262
262
  open() {
263
263
  if (!this.container) this.build()
264
- const onLoad = this._umap.editPanel.open({ content: this.container })
264
+ const onLoad = this._umap.editPanel.open({
265
+ content: this.container,
266
+ highlight: 'import',
267
+ })
265
268
  onLoad.then(() => this.onLoad())
266
269
  }
267
270
 
@@ -305,7 +308,7 @@ export default class Importer extends Utils.WithTemplate {
305
308
  this.onSuccess()
306
309
  } catch (e) {
307
310
  this.onError(translate('Invalid umap data'))
308
- console.error(e)
311
+ console.debug(e)
309
312
  return false
310
313
  }
311
314
  }
@@ -3,6 +3,7 @@ import { translate } from './i18n.js'
3
3
  import { uMapAlert as Alert } from '../components/alerts/alert.js'
4
4
  import { ServerStored } from './saving.js'
5
5
  import * as Utils from './utils.js'
6
+ import { MutatingForm } from './form/builder.js'
6
7
 
7
8
  // Dedicated object so we can deal with a separate dirty status, and thus
8
9
  // call the endpoint only when needed, saving one call at each save.
@@ -58,7 +59,7 @@ export class MapPermissions extends ServerStored {
58
59
  selectOptions: this._umap.properties.share_statuses,
59
60
  },
60
61
  ])
61
- const builder = new U.FormBuilder(this, fields)
62
+ const builder = new MutatingForm(this, fields)
62
63
  const form = builder.build()
63
64
  container.appendChild(form)
64
65
 
@@ -133,7 +134,7 @@ export class MapPermissions extends ServerStored {
133
134
  { handler: 'ManageEditors', label: translate("Map's editors") },
134
135
  ])
135
136
 
136
- const builder = new U.FormBuilder(this, topFields)
137
+ const builder = new MutatingForm(this, topFields)
137
138
  const form = builder.build()
138
139
  container.appendChild(form)
139
140
  if (collaboratorsFields.length) {
@@ -141,7 +142,7 @@ export class MapPermissions extends ServerStored {
141
142
  `<fieldset class="separator"><legend>${translate('Manage collaborators')}</legend></fieldset>`
142
143
  )
143
144
  container.appendChild(fieldset)
144
- const builder = new U.FormBuilder(this, collaboratorsFields)
145
+ const builder = new MutatingForm(this, collaboratorsFields)
145
146
  const form = builder.build()
146
147
  container.appendChild(form)
147
148
  }
@@ -165,12 +166,16 @@ export class MapPermissions extends ServerStored {
165
166
  Alert.info(translate('Please save the map first'))
166
167
  return
167
168
  }
168
- const container = DomUtil.create('div', 'permissions-panel')
169
+ const container = DomUtil.create('div', 'umap-edit-permissions')
169
170
  DomUtil.createTitle(container, translate('Update permissions'), 'icon-key')
170
171
  if (this.isAnonymousMap()) this._editAnonymous(container)
171
172
  else this._editWithOwner(container)
172
173
  this._editDatalayers(container)
173
- this._umap.editPanel.open({ content: container, className: 'dark' })
174
+ this._umap.editPanel.open({
175
+ content: container,
176
+ className: 'dark',
177
+ highlight: 'permissions',
178
+ })
174
179
  }
175
180
 
176
181
  async attach() {
@@ -269,7 +274,7 @@ export class DataLayerPermissions extends ServerStored {
269
274
  },
270
275
  ],
271
276
  ]
272
- const builder = new U.FormBuilder(this, fields, {
277
+ const builder = new MutatingForm(this, fields, {
273
278
  className: 'umap-form datalayer-permissions',
274
279
  })
275
280
  const form = builder.build()
@@ -70,6 +70,11 @@ const BaseIcon = DivIcon.extend({
70
70
  },
71
71
 
72
72
  onAdd: () => {},
73
+
74
+ _setIconStyles: function (img, name) {
75
+ if (this.feature.isActive()) this.options.className += ' umap-icon-active'
76
+ DivIcon.prototype._setIconStyles.call(this, img, name)
77
+ },
73
78
  })
74
79
 
75
80
  const DefaultIcon = BaseIcon.extend({
@@ -86,7 +91,6 @@ const DefaultIcon = BaseIcon.extend({
86
91
  },
87
92
 
88
93
  _setIconStyles: function (img, name) {
89
- if (this.feature.isActive()) this.options.className += ' umap-icon-active'
90
94
  BaseIcon.prototype._setIconStyles.call(this, img, name)
91
95
  const color = this._getColor()
92
96
  const opacity = this._getOpacity()
@@ -75,7 +75,7 @@ const ClassifiedMixin = {
75
75
  },
76
76
 
77
77
  renderLegend: function (container) {
78
- if (!this.datalayer.hasDataLoaded()) return
78
+ if (!this.datalayer.isLoaded()) return
79
79
  const parent = DomUtil.create('ul', '', container)
80
80
  const items = this.getLegendItems()
81
81
  for (const [color, label] of items) {
@@ -88,7 +88,11 @@ const ClassifiedMixin = {
88
88
  },
89
89
 
90
90
  getColorSchemes: function (classes) {
91
- return this.colorSchemes.filter((scheme) => Boolean(colorbrewer[scheme][classes]))
91
+ const found = this.colorSchemes.filter((scheme) =>
92
+ Boolean(colorbrewer[scheme][classes])
93
+ )
94
+ if (found.length) return found
95
+ return [['', translate('Default')]]
92
96
  },
93
97
  }
94
98
 
@@ -191,7 +195,7 @@ export const Choropleth = FeatureGroup.extend({
191
195
  'options.choropleth.property',
192
196
  {
193
197
  handler: 'Select',
194
- selectOptions: this.datalayer._propertiesIndex,
198
+ selectOptions: this.datalayer.allProperties(),
195
199
  label: translate('Choropleth property value'),
196
200
  },
197
201
  ],
@@ -300,7 +304,7 @@ export const Circles = FeatureGroup.extend({
300
304
  'options.circles.property',
301
305
  {
302
306
  handler: 'Select',
303
- selectOptions: this.datalayer._propertiesIndex,
307
+ selectOptions: this.datalayer.allProperties(),
304
308
  label: translate('Property name to compute circles'),
305
309
  },
306
310
  ],
@@ -377,7 +381,7 @@ export const Categorized = FeatureGroup.extend({
377
381
 
378
382
  _getValue: function (feature) {
379
383
  const key =
380
- this.datalayer.options.categorized.property || this.datalayer._propertiesIndex[0]
384
+ this.datalayer.options.categorized.property || this.datalayer.allProperties()[0]
381
385
  return feature.properties[key]
382
386
  },
383
387
 
@@ -420,7 +424,7 @@ export const Categorized = FeatureGroup.extend({
420
424
  } else {
421
425
  this.options.colors = colorbrewer?.Accent[this._classes]
422
426
  ? colorbrewer?.Accent[this._classes]
423
- : U.COLORS // Fixme: move COLORS to modules/
427
+ : Utils.COLORS
424
428
  }
425
429
  },
426
430
 
@@ -430,7 +434,7 @@ export const Categorized = FeatureGroup.extend({
430
434
  'options.categorized.property',
431
435
  {
432
436
  handler: 'Select',
433
- selectOptions: this.datalayer._propertiesIndex,
437
+ selectOptions: this.datalayer.allProperties(),
434
438
  label: translate('Category property'),
435
439
  },
436
440
  ],
@@ -464,7 +468,7 @@ export const Categorized = FeatureGroup.extend({
464
468
 
465
469
  onEdit: function (field, builder) {
466
470
  // Only compute the categories if we're dealing with categorized
467
- if (!field.startsWith('options.categorized')) return
471
+ if (!field.startsWith('options.categorized') && field !== 'options.type') return
468
472
  // If user touches the categories, then force manual mode
469
473
  if (field === 'options.categorized.categories') {
470
474
  this.datalayer.options.categorized.mode = 'manual'
@@ -63,7 +63,17 @@ export const Cluster = L.MarkerClusterGroup.extend({
63
63
 
64
64
  addLayer: function (layer) {
65
65
  this._layers.push(layer)
66
- return L.MarkerClusterGroup.prototype.addLayer.call(this, layer)
66
+ try {
67
+ return L.MarkerClusterGroup.prototype.addLayer.call(this, layer)
68
+ } catch (error) {
69
+ console.debug(error)
70
+ // Certainly a race condition when loading a clustered layer
71
+ // while zooming (this for example can happen at load, when the
72
+ // initial zoom is changed by uMap).
73
+ // FIXME: remove when this is merged:
74
+ // https://github.com/Leaflet/Leaflet.markercluster/pull/1048/files
75
+ return this
76
+ }
67
77
  },
68
78
 
69
79
  removeLayer: function (layer) {
@@ -32,7 +32,6 @@ const ControlsMixin = {
32
32
  'locate',
33
33
  'measure',
34
34
  'editinosm',
35
- 'star',
36
35
  'tilelayers',
37
36
  ],
38
37
 
@@ -41,20 +40,6 @@ const ControlsMixin = {
41
40
 
42
41
  if (this._umap.hasEditMode() && !this.options.noControl) {
43
42
  new U.EditControl(this).addTo(this)
44
-
45
- new U.DrawToolbar({ map: this }).addTo(this)
46
- const editActions = [
47
- U.EditCaptionAction,
48
- U.ImportAction,
49
- U.EditLayersAction,
50
- U.ChangeTileLayerAction,
51
- U.UpdateExtentAction,
52
- U.UpdatePermsAction,
53
- U.EditPropertiesAction,
54
- ]
55
- if (this.options.editMode === 'advanced') {
56
- new U.SettingsToolbar({ actions: editActions }).addTo(this)
57
- }
58
43
  }
59
44
  this._controls.zoom = new Control.Zoom({
60
45
  zoomInTitle: translate('Zoom in'),
@@ -84,7 +69,6 @@ const ControlsMixin = {
84
69
  this._controls.search = new U.SearchControl()
85
70
  this._controls.embed = new Control.Embed(this._umap)
86
71
  this._controls.tilelayersChooser = new U.TileLayerChooser(this)
87
- if (this.options.user?.id) this._controls.star = new U.StarControl(this._umap)
88
72
  this._controls.editinosm = new Control.EditInOSM({
89
73
  position: 'topleft',
90
74
  widgetOptions: {
@@ -233,7 +217,7 @@ const ManageTilelayerMixin = {
233
217
  }
234
218
  },
235
219
 
236
- updateTileLayers: function () {
220
+ editTileLayers: function () {
237
221
  if (this._controls.tilelayersChooser) {
238
222
  this._controls.tilelayersChooser.openSwitcher({ edit: true })
239
223
  }
@@ -266,7 +250,6 @@ export const LeafletMap = BaseMap.extend({
266
250
  DomEvent.on(document.body, 'dataload', (event) =>
267
251
  this.fire('dataload', event.detail)
268
252
  )
269
- this.on('click', this.closeInplaceToolbar)
270
253
  }
271
254
 
272
255
  this.on('baselayerchange', (e) => {
@@ -300,11 +283,6 @@ export const LeafletMap = BaseMap.extend({
300
283
  this.handleLimitBounds()
301
284
  },
302
285
 
303
- closeInplaceToolbar: function () {
304
- const toolbar = this._toolbars[L.Toolbar.Popup._toolbar_class_id]
305
- if (toolbar) toolbar.remove()
306
- },
307
-
308
286
  latLng: (a, b, c) => {
309
287
  // manage geojson case and call original method
310
288
  if (!(a instanceof L.LatLng) && a.coordinates) {
@@ -29,12 +29,9 @@ const FeatureMixin = {
29
29
 
30
30
  onRemove: function (map) {
31
31
  this.parentClass.prototype.onRemove.call(this, map)
32
- if (map.editedFeature === this.feature) {
33
- this.feature._marked_for_deletion = true
32
+ if (map._umap.editedFeature === this.feature) {
34
33
  this.feature.endEdit()
35
- if (map.editedFeature === this.feature) {
36
- map.editPanel.close()
37
- }
34
+ map._umap.editPanel.close()
38
35
  }
39
36
  },
40
37
 
@@ -47,6 +44,7 @@ const FeatureMixin = {
47
44
  addInteractions: function () {
48
45
  this.on('contextmenu editable:vertex:contextmenu', this.onContextMenu)
49
46
  this.on('click', this.onClick)
47
+ this.on('editable:edited', this.onCommit)
50
48
  },
51
49
 
52
50
  onClick: function (event) {
@@ -59,15 +57,13 @@ const FeatureMixin = {
59
57
  if (event.originalEvent.ctrlKey || event.originalEvent.metaKey) {
60
58
  this.feature.datalayer.edit(event)
61
59
  } else {
62
- if (this.feature._toggleEditing) this.feature._toggleEditing(event)
63
- else this.feature.edit(event)
60
+ this.feature.toggleEditing(event)
64
61
  }
65
62
  } else if (!this._map.editTools?.drawing()) {
66
- new L.Toolbar.Popup(event.latlng, {
67
- className: 'leaflet-inplace-toolbar',
68
- anchor: this.getPopupToolbarAnchor(),
69
- actions: this.feature.getInplaceToolbarActions(event),
70
- }).addTo(this._map, this.feature, event.latlng)
63
+ this._map._umap.editContextmenu.open(
64
+ event.originalEvent,
65
+ this.feature.getInplaceEditMenu(event)
66
+ )
71
67
  }
72
68
  }
73
69
  DomEvent.stop(event)
@@ -104,8 +100,6 @@ const FeatureMixin = {
104
100
  this.feature.pullGeometry(false)
105
101
  this.feature.onCommit()
106
102
  },
107
-
108
- getPopupToolbarAnchor: () => [0, 0],
109
103
  }
110
104
 
111
105
  const PointMixin = {
@@ -118,9 +112,8 @@ const PointMixin = {
118
112
  this.on('dragend', (event) => {
119
113
  this.isDirty = true
120
114
  this.feature.edit(event)
121
- this.feature.pullGeometry()
115
+ this.feature.pullGeometry(false)
122
116
  })
123
- this.on('editable:drawing:commit', this.onCommit)
124
117
  if (!this.feature.isReadOnly()) this.on('mouseover', this._enableDragging)
125
118
  this.on('mouseout', this._onMouseOut)
126
119
  },
@@ -248,10 +241,6 @@ export const LeafletMarker = Marker.extend({
248
241
  this._redraw()
249
242
  this._resetZIndex()
250
243
  },
251
-
252
- getPopupToolbarAnchor: function () {
253
- return this.options.icon.options.popupAnchor
254
- },
255
244
  })
256
245
 
257
246
  const PathMixin = {
@@ -267,6 +256,8 @@ const PathMixin = {
267
256
  },
268
257
 
269
258
  makeGeometryEditable: function () {
259
+ // Feature has been removed since then?
260
+ if (!this._map) return
270
261
  if (this._map._umap.editedFeature !== this.feature) {
271
262
  this.disableEdit()
272
263
  return
@@ -285,13 +276,17 @@ const PathMixin = {
285
276
 
286
277
  addInteractions: function () {
287
278
  FeatureMixin.addInteractions.call(this)
288
- this.on('editable:disable', this.onCommit)
289
279
  this.on('mouseover', this._onMouseOver)
290
280
  this.on('drag editable:drag', this._onDrag)
291
281
  this.on('popupopen', this.highlightPath)
292
282
  this.on('popupclose', this._redraw)
293
283
  },
294
284
 
285
+ bindTooltip: function (content, options) {
286
+ options.sticky = !options.permanent
287
+ this.parentClass.prototype.bindTooltip.call(this, content, options)
288
+ },
289
+
295
290
  highlightPath: function () {
296
291
  this.parentClass.prototype.setStyle.call(this, {
297
292
  fillOpacity: Math.sqrt(this.feature.getDynamicOption('fillOpacity', 1.0)),
@@ -335,13 +330,11 @@ const PathMixin = {
335
330
  this.resetTooltip()
336
331
  },
337
332
 
338
- getVertexActions: () => [U.DeleteVertexAction],
339
-
340
333
  onVertexRawClick: function (event) {
341
- new L.Toolbar.Popup(event.latlng, {
342
- className: 'leaflet-inplace-toolbar',
343
- actions: this.getVertexActions(event),
344
- }).addTo(this._map, this, event.latlng, event.vertex)
334
+ this._map._umap.editContextmenu.open(
335
+ event.originalEvent,
336
+ this.feature.getInplaceEditVertexMenu(event)
337
+ )
345
338
  },
346
339
 
347
340
  isolateShape: function (atLatLng) {
@@ -377,17 +370,6 @@ export const LeafletPolyline = Polyline.extend({
377
370
 
378
371
  getClass: () => LeafletPolyline,
379
372
 
380
- getVertexActions: function (event) {
381
- const actions = PathMixin.getVertexActions.call(this, event)
382
- const index = event.vertex.getIndex()
383
- if (index === 0 || index === event.vertex.getLastIndex()) {
384
- actions.push(U.ContinueLineAction)
385
- } else {
386
- actions.push(U.SplitLineAction)
387
- }
388
- return actions
389
- },
390
-
391
373
  getMeasure: function (shape) {
392
374
  let shapes
393
375
  if (shape) {
@@ -3,6 +3,7 @@ import { translate } from './i18n.js'
3
3
  import * as Utils from './utils.js'
4
4
  import { AutocompleteDatalist } from './autocomplete.js'
5
5
  import Orderable from './orderable.js'
6
+ import { MutatingForm } from './form/builder.js'
6
7
 
7
8
  const EMPTY_VALUES = ['', undefined, null]
8
9
 
@@ -129,7 +130,7 @@ class Rule {
129
130
  'options.dashArray',
130
131
  ]
131
132
  const container = DomUtil.create('div')
132
- const builder = new U.FormBuilder(this, options)
133
+ const builder = new MutatingForm(this, options)
133
134
  const defaultShapeProperties = DomUtil.add('div', '', container)
134
135
  defaultShapeProperties.appendChild(builder.build())
135
136
  const autocomplete = new AutocompleteDatalist(builder.helpers.condition.input)
@@ -146,7 +147,7 @@ class Rule {
146
147
  .map((str) => `${value}${str || ''}`)
147
148
  }
148
149
  })
149
- this._umap.editPanel.open({ content: container })
150
+ this._umap.editPanel.open({ content: container, highlight: 'settings' })
150
151
  }
151
152
 
152
153
  renderToolbox(row) {
@@ -10,6 +10,11 @@ export async function save() {
10
10
  }
11
11
  }
12
12
 
13
+ export function clear() {
14
+ _queue.clear()
15
+ onUpdate()
16
+ }
17
+
13
18
  function add(obj) {
14
19
  _queue.add(obj)
15
20
  onUpdate()
@@ -278,6 +278,7 @@ export const SCHEMA = {
278
278
  impacts: ['ui'],
279
279
  nullable: true,
280
280
  label: translate('Display the locate control'),
281
+ default: null,
281
282
  },
282
283
  longCredit: {
283
284
  type: 'Text',
@@ -290,6 +291,7 @@ export const SCHEMA = {
290
291
  impacts: ['ui'],
291
292
  nullable: true,
292
293
  label: translate('Display the measure control'),
294
+ default: null,
293
295
  },
294
296
  mask: {
295
297
  type: Boolean,
@@ -447,6 +449,11 @@ export const SCHEMA = {
447
449
  label: translate('Display label'),
448
450
  inheritable: true,
449
451
  default: false,
452
+ choices: [
453
+ [true, translate('always')],
454
+ [false, translate('never')],
455
+ [null, translate('on hover')],
456
+ ],
450
457
  },
451
458
  slideshow: {
452
459
  type: Object,
@@ -478,12 +485,6 @@ export const SCHEMA = {
478
485
  label: translate('Sort key'),
479
486
  inheritable: true,
480
487
  },
481
- starControl: {
482
- type: Boolean,
483
- impacts: ['ui'],
484
- nullable: true,
485
- label: translate('Display the star map button'),
486
- },
487
488
  stroke: {
488
489
  type: Boolean,
489
490
  impacts: ['data'],
@@ -508,6 +509,7 @@ export const SCHEMA = {
508
509
  impacts: ['ui'],
509
510
  nullable: true,
510
511
  label: translate('Display the tile layers control'),
512
+ default: null,
511
513
  },
512
514
  toZoom: {
513
515
  type: Number,
@@ -2,6 +2,7 @@ import { DomUtil } from '../../vendors/leaflet/leaflet-src.esm.js'
2
2
  import { EXPORT_FORMATS } from './formatter.js'
3
3
  import { translate } from './i18n.js'
4
4
  import * as Utils from './utils.js'
5
+ import { MutatingForm } from './form/builder.js'
5
6
 
6
7
  export default class Share {
7
8
  constructor(umap) {
@@ -125,9 +126,8 @@ export default class Share {
125
126
  exportUrl.value = window.location.protocol + iframeExporter.buildUrl()
126
127
  }
127
128
  buildIframeCode()
128
- const builder = new U.FormBuilder(iframeExporter, UIFields, {
129
- callback: buildIframeCode,
130
- })
129
+ const builder = new MutatingForm(iframeExporter, UIFields)
130
+ builder.on('set', buildIframeCode)
131
131
  const iframeOptions = DomUtil.createFieldset(
132
132
  this.container,
133
133
  translate('Embed and link options')