umap-project 1.14.0a5__py3-none-any.whl → 2.0.0a0__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 (224) hide show
  1. umap/__init__.py +1 -1
  2. umap/decorators.py +0 -14
  3. umap/locale/br/LC_MESSAGES/django.mo +0 -0
  4. umap/locale/br/LC_MESSAGES/django.po +137 -85
  5. umap/locale/cs_CZ/LC_MESSAGES/django.mo +0 -0
  6. umap/locale/cs_CZ/LC_MESSAGES/django.po +136 -84
  7. umap/locale/el/LC_MESSAGES/django.mo +0 -0
  8. umap/locale/el/LC_MESSAGES/django.po +136 -84
  9. umap/locale/en/LC_MESSAGES/django.po +128 -88
  10. umap/locale/es/LC_MESSAGES/django.mo +0 -0
  11. umap/locale/es/LC_MESSAGES/django.po +136 -84
  12. umap/locale/fr/LC_MESSAGES/django.mo +0 -0
  13. umap/locale/fr/LC_MESSAGES/django.po +131 -91
  14. umap/locale/hu/LC_MESSAGES/django.mo +0 -0
  15. umap/locale/hu/LC_MESSAGES/django.po +137 -85
  16. umap/locale/it/LC_MESSAGES/django.mo +0 -0
  17. umap/locale/it/LC_MESSAGES/django.po +136 -84
  18. umap/locale/ms/LC_MESSAGES/django.mo +0 -0
  19. umap/locale/ms/LC_MESSAGES/django.po +136 -84
  20. umap/locale/pl/LC_MESSAGES/django.mo +0 -0
  21. umap/locale/pl/LC_MESSAGES/django.po +136 -84
  22. umap/locale/sv/LC_MESSAGES/django.mo +0 -0
  23. umap/locale/sv/LC_MESSAGES/django.po +135 -83
  24. umap/locale/zh_TW/LC_MESSAGES/django.mo +0 -0
  25. umap/locale/zh_TW/LC_MESSAGES/django.po +143 -91
  26. umap/models.py +23 -1
  27. umap/settings/__init__.py +1 -4
  28. umap/settings/base.py +1 -0
  29. umap/static/umap/base.css +5 -0
  30. umap/static/umap/content.css +185 -13
  31. umap/static/umap/favicons/icon.svg +2 -2
  32. umap/static/umap/img/edit.svg +3 -3
  33. umap/static/umap/img/icon-delete.svg +4 -0
  34. umap/static/umap/img/icon-download.svg +13 -0
  35. umap/static/umap/img/icon-duplicate.svg +5 -0
  36. umap/static/umap/img/icon-edit.svg +12 -0
  37. umap/static/umap/img/icon-share.svg +11 -0
  38. umap/static/umap/img/icon-view.svg +12 -0
  39. umap/static/umap/img/logo.svg +2 -2
  40. umap/static/umap/img/logo_small.svg +2 -2
  41. umap/static/umap/img/marker.svg +4 -0
  42. umap/static/umap/img/opensource.svg +2 -2
  43. umap/static/umap/img/osm.svg +2 -2
  44. umap/static/umap/js/components/fragment.js +1 -1
  45. umap/static/umap/js/modules/browser.js +159 -0
  46. umap/static/umap/js/modules/global.js +3 -1
  47. umap/static/umap/js/modules/request.js +155 -0
  48. umap/static/umap/js/umap.autocomplete.js +28 -38
  49. umap/static/umap/js/umap.controls.js +73 -58
  50. umap/static/umap/js/umap.core.js +4 -9
  51. umap/static/umap/js/umap.datalayer.permissions.js +13 -12
  52. umap/static/umap/js/umap.features.js +51 -49
  53. umap/static/umap/js/umap.forms.js +19 -19
  54. umap/static/umap/js/umap.icon.js +17 -17
  55. umap/static/umap/js/umap.importer.js +2 -1
  56. umap/static/umap/js/umap.js +242 -291
  57. umap/static/umap/js/umap.layer.js +173 -141
  58. umap/static/umap/js/umap.permissions.js +24 -25
  59. umap/static/umap/js/umap.popup.js +14 -14
  60. umap/static/umap/js/umap.share.js +4 -4
  61. umap/static/umap/js/umap.slideshow.js +4 -4
  62. umap/static/umap/js/umap.tableeditor.js +2 -2
  63. umap/static/umap/js/umap.ui.js +1 -1
  64. umap/static/umap/locale/am_ET.js +1 -11
  65. umap/static/umap/locale/am_ET.json +1 -11
  66. umap/static/umap/locale/ar.js +1 -11
  67. umap/static/umap/locale/ar.json +1 -11
  68. umap/static/umap/locale/ast.js +1 -11
  69. umap/static/umap/locale/ast.json +1 -11
  70. umap/static/umap/locale/bg.js +1 -11
  71. umap/static/umap/locale/bg.json +1 -11
  72. umap/static/umap/locale/br.js +1 -11
  73. umap/static/umap/locale/br.json +1 -11
  74. umap/static/umap/locale/ca.js +1 -11
  75. umap/static/umap/locale/ca.json +1 -11
  76. umap/static/umap/locale/cs_CZ.js +1 -11
  77. umap/static/umap/locale/cs_CZ.json +1 -11
  78. umap/static/umap/locale/da.js +1 -11
  79. umap/static/umap/locale/da.json +1 -11
  80. umap/static/umap/locale/de.js +1 -11
  81. umap/static/umap/locale/de.json +1 -11
  82. umap/static/umap/locale/el.js +1 -11
  83. umap/static/umap/locale/el.json +1 -11
  84. umap/static/umap/locale/en.js +1 -11
  85. umap/static/umap/locale/en.json +1 -11
  86. umap/static/umap/locale/en_US.json +1 -11
  87. umap/static/umap/locale/es.js +1 -11
  88. umap/static/umap/locale/es.json +1 -11
  89. umap/static/umap/locale/et.js +1 -11
  90. umap/static/umap/locale/et.json +1 -11
  91. umap/static/umap/locale/fa_IR.js +6 -16
  92. umap/static/umap/locale/fa_IR.json +6 -16
  93. umap/static/umap/locale/fi.js +1 -11
  94. umap/static/umap/locale/fi.json +1 -11
  95. umap/static/umap/locale/fr.js +1 -11
  96. umap/static/umap/locale/fr.json +1 -11
  97. umap/static/umap/locale/gl.js +1 -11
  98. umap/static/umap/locale/gl.json +1 -11
  99. umap/static/umap/locale/he.js +1 -11
  100. umap/static/umap/locale/he.json +1 -11
  101. umap/static/umap/locale/hr.js +1 -11
  102. umap/static/umap/locale/hr.json +1 -11
  103. umap/static/umap/locale/hu.js +1 -11
  104. umap/static/umap/locale/hu.json +1 -11
  105. umap/static/umap/locale/id.js +1 -11
  106. umap/static/umap/locale/id.json +1 -11
  107. umap/static/umap/locale/is.js +1 -11
  108. umap/static/umap/locale/is.json +1 -11
  109. umap/static/umap/locale/it.js +1 -11
  110. umap/static/umap/locale/it.json +1 -11
  111. umap/static/umap/locale/ja.js +1 -11
  112. umap/static/umap/locale/ja.json +1 -11
  113. umap/static/umap/locale/ko.js +1 -11
  114. umap/static/umap/locale/ko.json +1 -11
  115. umap/static/umap/locale/lt.js +1 -11
  116. umap/static/umap/locale/lt.json +1 -11
  117. umap/static/umap/locale/ms.js +1 -11
  118. umap/static/umap/locale/ms.json +1 -11
  119. umap/static/umap/locale/nl.js +1 -11
  120. umap/static/umap/locale/nl.json +1 -11
  121. umap/static/umap/locale/no.js +1 -11
  122. umap/static/umap/locale/no.json +1 -11
  123. umap/static/umap/locale/pl.js +1 -11
  124. umap/static/umap/locale/pl.json +1 -11
  125. umap/static/umap/locale/pl_PL.json +1 -11
  126. umap/static/umap/locale/pt.js +1 -11
  127. umap/static/umap/locale/pt.json +1 -11
  128. umap/static/umap/locale/pt_BR.js +1 -11
  129. umap/static/umap/locale/pt_BR.json +1 -11
  130. umap/static/umap/locale/pt_PT.js +1 -11
  131. umap/static/umap/locale/pt_PT.json +1 -11
  132. umap/static/umap/locale/ro.js +1 -11
  133. umap/static/umap/locale/ro.json +1 -11
  134. umap/static/umap/locale/ru.js +1 -11
  135. umap/static/umap/locale/ru.json +1 -11
  136. umap/static/umap/locale/sk_SK.js +1 -11
  137. umap/static/umap/locale/sk_SK.json +1 -11
  138. umap/static/umap/locale/sl.js +1 -11
  139. umap/static/umap/locale/sl.json +1 -11
  140. umap/static/umap/locale/sr.js +1 -11
  141. umap/static/umap/locale/sr.json +1 -11
  142. umap/static/umap/locale/sv.js +1 -11
  143. umap/static/umap/locale/sv.json +1 -11
  144. umap/static/umap/locale/th_TH.js +1 -11
  145. umap/static/umap/locale/th_TH.json +1 -11
  146. umap/static/umap/locale/tr.js +1 -11
  147. umap/static/umap/locale/tr.json +1 -11
  148. umap/static/umap/locale/uk_UA.js +1 -11
  149. umap/static/umap/locale/uk_UA.json +1 -11
  150. umap/static/umap/locale/vi.js +1 -11
  151. umap/static/umap/locale/vi.json +1 -11
  152. umap/static/umap/locale/vi_VN.json +1 -11
  153. umap/static/umap/locale/zh.js +1 -11
  154. umap/static/umap/locale/zh.json +1 -11
  155. umap/static/umap/locale/zh_CN.json +1 -11
  156. umap/static/umap/locale/zh_TW.Big5.json +1 -11
  157. umap/static/umap/locale/zh_TW.js +17 -27
  158. umap/static/umap/locale/zh_TW.json +17 -27
  159. umap/static/umap/map.css +2 -2
  160. umap/static/umap/nav.css +2 -1
  161. umap/static/umap/test/.eslintrc +0 -1
  162. umap/static/umap/test/Choropleth.js +29 -27
  163. umap/static/umap/test/DataLayer.js +207 -239
  164. umap/static/umap/test/Feature.js +33 -58
  165. umap/static/umap/test/Map.Export.js +11 -11
  166. umap/static/umap/test/Map.js +66 -67
  167. umap/static/umap/test/Marker.js +36 -32
  168. umap/static/umap/test/Polygon.js +95 -95
  169. umap/static/umap/test/Polyline.js +31 -31
  170. umap/static/umap/test/TableEditor.js +29 -25
  171. umap/static/umap/test/_pre.js +2 -7
  172. umap/static/umap/test/index.html +4 -4
  173. umap/static/umap/vendors/contextmenu/leaflet.contextmenu.css +54 -0
  174. umap/static/umap/vendors/contextmenu/leaflet.contextmenu.js +586 -0
  175. umap/static/umap/vendors/csv2geojson/index.js +259 -0
  176. umap/static/umap/vendors/dompurify/purify.js +1633 -0
  177. umap/static/umap/vendors/locatecontrol/L.Control.Locate.css +63 -0
  178. umap/static/umap/vendors/locatecontrol/L.Control.Locate.js +950 -0
  179. umap/static/umap/vendors/minimap/Control.MiniMap.css +88 -0
  180. umap/static/umap/vendors/minimap/Control.MiniMap.js +352 -0
  181. umap/static/umap/vendors/togeojson/togeojson.js +2 -0
  182. umap/templates/auth/user_form.html +3 -2
  183. umap/templates/base.html +1 -0
  184. umap/templates/registration/login.html +51 -36
  185. umap/templates/umap/about_summary.html +1 -1
  186. umap/templates/umap/branding.html +3 -0
  187. umap/templates/umap/content.html +15 -39
  188. umap/templates/umap/header.html +0 -0
  189. umap/templates/umap/home.html +4 -2
  190. umap/templates/umap/js.html +0 -1
  191. umap/templates/umap/map_detail.html +9 -0
  192. umap/templates/umap/map_init.html +1 -1
  193. umap/templates/umap/map_messages.html +4 -2
  194. umap/templates/umap/map_table.html +130 -69
  195. umap/templates/umap/navigation.html +2 -4
  196. umap/templates/umap/user_dashboard.html +29 -6
  197. umap/tests/base.py +1 -1
  198. umap/tests/integration/conftest.py +18 -0
  199. umap/tests/integration/test_anonymous_owned_map.py +6 -3
  200. umap/tests/integration/test_browser.py +166 -6
  201. umap/tests/integration/test_collaborative_editing.py +23 -5
  202. umap/tests/integration/test_dashboard.py +17 -0
  203. umap/tests/integration/test_edit_datalayer.py +4 -3
  204. umap/tests/integration/test_export_map.py +1 -1
  205. umap/tests/integration/test_import.py +9 -4
  206. umap/tests/integration/test_map.py +64 -0
  207. umap/tests/integration/test_map_preview.py +75 -0
  208. umap/tests/integration/test_owned_map.py +11 -25
  209. umap/tests/integration/test_picto.py +3 -3
  210. umap/tests/integration/test_querystring.py +52 -0
  211. umap/tests/integration/test_share.py +22 -0
  212. umap/tests/test_map_views.py +157 -14
  213. umap/tests/test_views.py +50 -11
  214. umap/urls.py +6 -12
  215. umap/views.py +170 -47
  216. {umap_project-1.14.0a5.dist-info → umap_project-2.0.0a0.dist-info}/METADATA +13 -15
  217. {umap_project-1.14.0a5.dist-info → umap_project-2.0.0a0.dist-info}/RECORD +220 -199
  218. umap/static/umap/js/umap.browser.js +0 -148
  219. umap/static/umap/js/umap.xhr.js +0 -304
  220. umap/static/umap/test/Controls.js +0 -100
  221. umap/static/umap/test/Map.Init.js +0 -46
  222. {umap_project-1.14.0a5.dist-info → umap_project-2.0.0a0.dist-info}/WHEEL +0 -0
  223. {umap_project-1.14.0a5.dist-info → umap_project-2.0.0a0.dist-info}/entry_points.txt +0 -0
  224. {umap_project-1.14.0a5.dist-info → umap_project-2.0.0a0.dist-info}/licenses/LICENSE +0 -0
@@ -1,5 +1,5 @@
1
- L.U.Layer = {
2
- canBrowse: true,
1
+ U.Layer = {
2
+ browsable: true,
3
3
 
4
4
  getType: function () {
5
5
  const proto = Object.getPrototypeOf(this)
@@ -26,12 +26,12 @@ L.U.Layer = {
26
26
  },
27
27
  }
28
28
 
29
- L.U.Layer.Default = L.FeatureGroup.extend({
29
+ U.Layer.Default = L.FeatureGroup.extend({
30
30
  statics: {
31
31
  NAME: L._('Default'),
32
32
  TYPE: 'Default',
33
33
  },
34
- includes: [L.U.Layer],
34
+ includes: [U.Layer],
35
35
 
36
36
  initialize: function (datalayer) {
37
37
  this.datalayer = datalayer
@@ -39,7 +39,7 @@ L.U.Layer.Default = L.FeatureGroup.extend({
39
39
  },
40
40
  })
41
41
 
42
- L.U.MarkerCluster = L.MarkerCluster.extend({
42
+ U.MarkerCluster = L.MarkerCluster.extend({
43
43
  // Custom class so we can call computeTextColor
44
44
  // when element is already on the DOM.
45
45
 
@@ -51,12 +51,12 @@ L.U.MarkerCluster = L.MarkerCluster.extend({
51
51
  },
52
52
  })
53
53
 
54
- L.U.Layer.Cluster = L.MarkerClusterGroup.extend({
54
+ U.Layer.Cluster = L.MarkerClusterGroup.extend({
55
55
  statics: {
56
56
  NAME: L._('Clustered'),
57
57
  TYPE: 'Cluster',
58
58
  },
59
- includes: [L.U.Layer],
59
+ includes: [U.Layer],
60
60
 
61
61
  initialize: function (datalayer) {
62
62
  this.datalayer = datalayer
@@ -65,14 +65,14 @@ L.U.Layer.Cluster = L.MarkerClusterGroup.extend({
65
65
  color: this.datalayer.getColor(),
66
66
  },
67
67
  iconCreateFunction: function (cluster) {
68
- return new L.U.Icon.Cluster(datalayer, cluster)
68
+ return new U.Icon.Cluster(datalayer, cluster)
69
69
  },
70
70
  }
71
71
  if (this.datalayer.options.cluster && this.datalayer.options.cluster.radius) {
72
72
  options.maxClusterRadius = this.datalayer.options.cluster.radius
73
73
  }
74
74
  L.MarkerClusterGroup.prototype.initialize.call(this, options)
75
- this._markerCluster = L.U.MarkerCluster
75
+ this._markerCluster = U.MarkerCluster
76
76
  this._layers = []
77
77
  },
78
78
 
@@ -132,13 +132,12 @@ L.U.Layer.Cluster = L.MarkerClusterGroup.extend({
132
132
  },
133
133
  })
134
134
 
135
- L.U.Layer.Choropleth = L.FeatureGroup.extend({
135
+ U.Layer.Choropleth = L.FeatureGroup.extend({
136
136
  statics: {
137
137
  NAME: L._('Choropleth'),
138
138
  TYPE: 'Choropleth',
139
139
  },
140
- includes: [L.U.Layer],
141
- canBrowse: true,
140
+ includes: [U.Layer],
142
141
  // Have defaults that better suit the choropleth mode.
143
142
  defaults: {
144
143
  color: 'white',
@@ -194,6 +193,7 @@ L.U.Layer.Choropleth = L.FeatureGroup.extend({
194
193
  let mode = this.datalayer.options.choropleth.mode,
195
194
  classes = +this.datalayer.options.choropleth.classes || 5,
196
195
  breaks
196
+ classes = Math.min(classes, values.length)
197
197
  if (mode === 'manual') {
198
198
  const manualBreaks = this.datalayer.options.choropleth.breaks
199
199
  if (manualBreaks) {
@@ -338,13 +338,13 @@ L.U.Layer.Choropleth = L.FeatureGroup.extend({
338
338
  },
339
339
  })
340
340
 
341
- L.U.Layer.Heat = L.HeatLayer.extend({
341
+ U.Layer.Heat = L.HeatLayer.extend({
342
342
  statics: {
343
343
  NAME: L._('Heatmap'),
344
344
  TYPE: 'Heat',
345
345
  },
346
- includes: [L.U.Layer],
347
- canBrowse: false,
346
+ includes: [U.Layer],
347
+ browsable: false,
348
348
 
349
349
  initialize: function (datalayer) {
350
350
  this.datalayer = datalayer
@@ -505,7 +505,7 @@ L.U.Layer.Heat = L.HeatLayer.extend({
505
505
  },
506
506
  })
507
507
 
508
- L.U.DataLayer = L.Evented.extend({
508
+ U.DataLayer = L.Evented.extend({
509
509
  options: {
510
510
  displayOnLoad: true,
511
511
  inCaption: true,
@@ -579,9 +579,11 @@ L.U.DataLayer = L.Evented.extend({
579
579
  }
580
580
  this.backupOptions()
581
581
  this.connectToMap()
582
- this.permissions = new L.U.DataLayerPermissions(this)
583
- if (this.showAtLoad()) this.show()
584
- if (!this.umap_id) this.isDirty = true
582
+ this.permissions = new U.DataLayerPermissions(this)
583
+ if (!this.umap_id) {
584
+ if (this.showAtLoad()) this.show()
585
+ this.isDirty = true
586
+ }
585
587
 
586
588
  this.onceLoaded(function () {
587
589
  this.map.on('moveend', this.onMoveEnd, this)
@@ -646,7 +648,7 @@ L.U.DataLayer = L.Evented.extend({
646
648
  if (this.layer) this.layer.clearLayers()
647
649
  // delete this.layer?
648
650
  if (visible) this.map.removeLayer(this.layer)
649
- const Class = L.U.Layer[this.options.type] || L.U.Layer.Default
651
+ const Class = U.Layer[this.options.type] || U.Layer.Default
650
652
  this.layer = new Class(this)
651
653
  this.eachLayer(this.showFeature)
652
654
  if (visible) this.show()
@@ -661,7 +663,7 @@ L.U.DataLayer = L.Evented.extend({
661
663
  },
662
664
 
663
665
  eachFeature: function (method, context) {
664
- if (this.layer && this.layer.canBrowse) {
666
+ if (this.isBrowsable()) {
665
667
  for (let i = 0; i < this._index.length; i++) {
666
668
  method.call(context || this, this._layers[this._index[i]])
667
669
  }
@@ -669,30 +671,28 @@ L.U.DataLayer = L.Evented.extend({
669
671
  return this
670
672
  },
671
673
 
672
- fetchData: function () {
674
+ fetchData: async function () {
673
675
  if (!this.umap_id) return
674
676
  if (this._loading) return
675
677
  this._loading = true
676
- this.map.get(this._dataUrl(), {
677
- callback: function (geojson, response) {
678
- this._last_modified = response.getResponseHeader('Last-Modified')
679
- // FIXME: for now this property is set dynamically from backend
680
- // And thus it's not in the geojson file in the server
681
- // So do not let all options to be reset
682
- // Fix is a proper migration so all datalayers settings are
683
- // in DB, and we remove it from geojson flat files.
684
- if (geojson._umap_options) {
685
- geojson._umap_options.editMode = this.options.editMode
686
- }
687
- // In case of maps pre 1.0 still around
688
- if (geojson._storage) geojson._storage.editMode = this.options.editMode
689
- this.fromUmapGeoJSON(geojson)
690
- this.backupOptions()
691
- this.fire('loaded')
692
- this._loading = false
693
- },
694
- context: this,
695
- })
678
+ const [geojson, response, error] = await this.map.server.get(this._dataUrl())
679
+ if (!error) {
680
+ this._last_modified = response.headers.get('last-modified')
681
+ // FIXME: for now this property is set dynamically from backend
682
+ // And thus it's not in the geojson file in the server
683
+ // So do not let all options to be reset
684
+ // Fix is a proper migration so all datalayers settings are
685
+ // in DB, and we remove it from geojson flat files.
686
+ if (geojson._umap_options) {
687
+ geojson._umap_options.editMode = this.options.editMode
688
+ }
689
+ // In case of maps pre 1.0 still around
690
+ if (geojson._storage) geojson._storage.editMode = this.options.editMode
691
+ this.fromUmapGeoJSON(geojson)
692
+ this.backupOptions()
693
+ this.fire('loaded')
694
+ this._loading = false
695
+ }
696
696
  },
697
697
 
698
698
  fromGeoJSON: function (geojson) {
@@ -743,23 +743,27 @@ L.U.DataLayer = L.Evented.extend({
743
743
  return !((!isNaN(from) && zoom < from) || (!isNaN(to) && zoom > to))
744
744
  },
745
745
 
746
- fetchRemoteData: function (force) {
746
+ hasDynamicData: function () {
747
+ return !!(this.options.remoteData && this.options.remoteData.dynamic)
748
+ },
749
+
750
+ fetchRemoteData: async function (force) {
747
751
  if (!this.isRemoteLayer()) return
748
- if (!this.options.remoteData.dynamic && this.hasDataLoaded() && !force) return
752
+ if (!this.hasDynamicData() && this.hasDataLoaded() && !force) return
749
753
  if (!this.isVisible()) return
750
754
  let url = this.map.localizeUrl(this.options.remoteData.url)
751
- if (this.options.remoteData.proxy)
755
+ if (this.options.remoteData.proxy) {
752
756
  url = this.map.proxyUrl(url, this.options.remoteData.ttl)
753
- this.map.ajax({
754
- uri: url,
755
- verb: 'GET',
756
- callback: (raw) => {
757
- this.clear()
758
- this.rawToGeoJSON(raw, this.options.remoteData.format, (geojson) =>
759
- this.fromGeoJSON(geojson)
760
- )
761
- },
762
- })
757
+ }
758
+ const response = await this.map.request.get(url)
759
+ if (response && response.ok) {
760
+ this.clear()
761
+ this.rawToGeoJSON(
762
+ await response.text(),
763
+ this.options.remoteData.format,
764
+ (geojson) => this.fromGeoJSON(geojson)
765
+ )
766
+ }
763
767
  },
764
768
 
765
769
  onceLoaded: function (callback, context) {
@@ -796,7 +800,8 @@ L.U.DataLayer = L.Evented.extend({
796
800
  },
797
801
 
798
802
  setOptions: function (options) {
799
- this.options = L.Util.CopyJSON(L.U.DataLayer.prototype.options) // Start from fresh.
803
+ delete options.geojson
804
+ this.options = L.Util.CopyJSON(U.DataLayer.prototype.options) // Start from fresh.
800
805
  this.updateOptions(options)
801
806
  },
802
807
 
@@ -924,7 +929,7 @@ L.U.DataLayer = L.Evented.extend({
924
929
  })
925
930
  }
926
931
  this.map.ui.alert({ content: message, level: 'error', duration: 10000 })
927
- console.log(err)
932
+ console.error(err)
928
933
  }
929
934
  if (result && result.features.length) {
930
935
  callback(result)
@@ -1022,11 +1027,11 @@ L.U.DataLayer = L.Evented.extend({
1022
1027
  },
1023
1028
 
1024
1029
  _pointToLayer: function (geojson, latlng) {
1025
- return new L.U.Marker(this.map, latlng, { geojson: geojson, datalayer: this })
1030
+ return new U.Marker(this.map, latlng, { geojson: geojson, datalayer: this })
1026
1031
  },
1027
1032
 
1028
1033
  _lineToLayer: function (geojson, latlngs) {
1029
- return new L.U.Polyline(this.map, latlngs, {
1034
+ return new U.Polyline(this.map, latlngs, {
1030
1035
  geojson: geojson,
1031
1036
  datalayer: this,
1032
1037
  color: null,
@@ -1038,7 +1043,7 @@ L.U.DataLayer = L.Evented.extend({
1038
1043
  // for (let i = latlngs.length - 1; i > 0; i--) {
1039
1044
  // if (!latlngs.slice()[i].length) latlngs.splice(i, 1);
1040
1045
  // }
1041
- return new L.U.Polygon(this.map, latlngs, { geojson: geojson, datalayer: this })
1046
+ return new U.Polygon(this.map, latlngs, { geojson: geojson, datalayer: this })
1042
1047
  },
1043
1048
 
1044
1049
  importRaw: function (raw, type) {
@@ -1060,13 +1065,12 @@ L.U.DataLayer = L.Evented.extend({
1060
1065
  reader.onload = (e) => this.importRaw(e.target.result, type)
1061
1066
  },
1062
1067
 
1063
- importFromUrl: function (url, type) {
1064
- url = this.map.localizeUrl(url)
1065
- this.map.xhr._ajax({
1066
- verb: 'GET',
1067
- uri: url,
1068
- callback: (data) => this.importRaw(data, type),
1069
- })
1068
+ importFromUrl: async function (uri, type) {
1069
+ uri = this.map.localizeUrl(uri)
1070
+ const response = await this.map.request.get(uri)
1071
+ if (response && response.ok) {
1072
+ this.importRaw(await response.text(), type)
1073
+ }
1070
1074
  },
1071
1075
 
1072
1076
  getColor: function () {
@@ -1182,7 +1186,7 @@ L.U.DataLayer = L.Evented.extend({
1182
1186
  ],
1183
1187
  ]
1184
1188
  const title = L.DomUtil.add('h3', '', container, L._('Layer properties'))
1185
- let builder = new L.U.FormBuilder(this, metadataFields, {
1189
+ let builder = new U.FormBuilder(this, metadataFields, {
1186
1190
  callback: function (e) {
1187
1191
  this.map.updateDatalayersControl()
1188
1192
  if (e.helper.field === 'options.type') {
@@ -1204,7 +1208,7 @@ L.U.DataLayer = L.Evented.extend({
1204
1208
  const layerOptions = this.layer.getEditableOptions()
1205
1209
 
1206
1210
  if (layerOptions.length) {
1207
- builder = new L.U.FormBuilder(this, layerOptions, {
1211
+ builder = new U.FormBuilder(this, layerOptions, {
1208
1212
  id: 'datalayer-layer-properties',
1209
1213
  callback: redrawCallback,
1210
1214
  })
@@ -1228,7 +1232,7 @@ L.U.DataLayer = L.Evented.extend({
1228
1232
  'options.fillOpacity',
1229
1233
  ]
1230
1234
 
1231
- builder = new L.U.FormBuilder(this, shapeOptions, {
1235
+ builder = new U.FormBuilder(this, shapeOptions, {
1232
1236
  id: 'datalayer-advanced-properties',
1233
1237
  callback: redrawCallback,
1234
1238
  })
@@ -1244,7 +1248,7 @@ L.U.DataLayer = L.Evented.extend({
1244
1248
  'options.labelKey',
1245
1249
  ]
1246
1250
 
1247
- builder = new L.U.FormBuilder(this, optionsFields, {
1251
+ builder = new U.FormBuilder(this, optionsFields, {
1248
1252
  id: 'datalayer-advanced-properties',
1249
1253
  callback: redrawCallback,
1250
1254
  })
@@ -1264,7 +1268,7 @@ L.U.DataLayer = L.Evented.extend({
1264
1268
  'options.outlinkTarget',
1265
1269
  'options.interactive',
1266
1270
  ]
1267
- builder = new L.U.FormBuilder(this, popupFields, { callback: redrawCallback })
1271
+ builder = new U.FormBuilder(this, popupFields, { callback: redrawCallback })
1268
1272
  const popupFieldset = L.DomUtil.createFieldset(
1269
1273
  container,
1270
1274
  L._('Interaction options')
@@ -1310,7 +1314,7 @@ L.U.DataLayer = L.Evented.extend({
1310
1314
  }
1311
1315
 
1312
1316
  const remoteDataContainer = L.DomUtil.createFieldset(container, L._('Remote data'))
1313
- builder = new L.U.FormBuilder(this, remoteDataFields)
1317
+ builder = new U.FormBuilder(this, remoteDataFields)
1314
1318
  remoteDataContainer.appendChild(builder.build())
1315
1319
  L.DomUtil.createButton(
1316
1320
  'button umap-verify',
@@ -1383,8 +1387,8 @@ L.U.DataLayer = L.Evented.extend({
1383
1387
  }
1384
1388
  },
1385
1389
 
1386
- buildVersionsFieldset: function (container) {
1387
- const appendVersion = function (data) {
1390
+ buildVersionsFieldset: async function (container) {
1391
+ const appendVersion = (data) => {
1388
1392
  const date = new Date(parseInt(data.at, 10))
1389
1393
  const content = `${date.toLocaleString(L.lang)} (${parseInt(data.size) / 1000}Kb)`
1390
1394
  const el = L.DomUtil.create('div', 'umap-datalayer-version', versionsContainer)
@@ -1400,34 +1404,30 @@ L.U.DataLayer = L.Evented.extend({
1400
1404
  }
1401
1405
 
1402
1406
  const versionsContainer = L.DomUtil.createFieldset(container, L._('Versions'), {
1403
- callback: function () {
1404
- this.map.xhr.get(this.getVersionsUrl(), {
1405
- callback: function (data) {
1406
- for (let i = 0; i < data.versions.length; i++) {
1407
- appendVersion.call(this, data.versions[i])
1408
- }
1409
- },
1410
- context: this,
1411
- })
1407
+ callback: async function () {
1408
+ const [{ versions }, response, error] = await this.map.server.get(
1409
+ this.getVersionsUrl()
1410
+ )
1411
+ if (!error) versions.forEach(appendVersion)
1412
1412
  },
1413
1413
  context: this,
1414
1414
  })
1415
1415
  },
1416
1416
 
1417
- restore: function (version) {
1417
+ restore: async function (version) {
1418
1418
  if (!this.map.editEnabled) return
1419
1419
  if (!confirm(L._('Are you sure you want to restore this version?'))) return
1420
- this.map.xhr.get(this.getVersionUrl(version), {
1421
- callback: function (geojson) {
1422
- if (geojson._storage) geojson._umap_options = geojson._storage // Retrocompat.
1423
- if (geojson._umap_options) this.setOptions(geojson._umap_options)
1424
- this.empty()
1425
- if (this.isRemoteLayer()) this.fetchRemoteData()
1426
- else this.addData(geojson)
1427
- this.isDirty = true
1428
- },
1429
- context: this,
1430
- })
1420
+ const [geojson, response, error] = await this.map.server.get(
1421
+ this.getVersionUrl(version)
1422
+ )
1423
+ if (!error) {
1424
+ if (geojson._storage) geojson._umap_options = geojson._storage // Retrocompat.
1425
+ if (geojson._umap_options) this.setOptions(geojson._umap_options)
1426
+ this.empty()
1427
+ if (this.isRemoteLayer()) this.fetchRemoteData()
1428
+ else this.addData(geojson)
1429
+ this.isDirty = true
1430
+ }
1431
1431
  },
1432
1432
 
1433
1433
  featuresToGeoJSON: function () {
@@ -1436,9 +1436,9 @@ L.U.DataLayer = L.Evented.extend({
1436
1436
  return features
1437
1437
  },
1438
1438
 
1439
- show: function () {
1440
- if (!this.isLoaded()) this.fetchData()
1439
+ show: async function () {
1441
1440
  this.map.addLayer(this.layer)
1441
+ if (!this.isLoaded()) await this.fetchData()
1442
1442
  this.fire('show')
1443
1443
  },
1444
1444
 
@@ -1461,8 +1461,22 @@ L.U.DataLayer = L.Evented.extend({
1461
1461
  if (bounds.isValid()) this.map.fitBounds(bounds)
1462
1462
  },
1463
1463
 
1464
+ // Is this layer type browsable in theorie
1465
+ isBrowsable: function () {
1466
+ return this.layer && this.layer.browsable
1467
+ },
1468
+
1469
+ // Is this layer browsable in theorie
1470
+ // AND the user allows it
1464
1471
  allowBrowse: function () {
1465
- return !!this.options.browsable && this.canBrowse() && this.isVisible()
1472
+ return !!this.options.browsable && this.isBrowsable()
1473
+ },
1474
+
1475
+ // Is this layer browsable in theorie
1476
+ // AND the user allows it
1477
+ // AND it makes actually sense (is visible, it has data…)
1478
+ canBrowse: function () {
1479
+ return this.allowBrowse() && this.isVisible() && this.hasData()
1466
1480
  },
1467
1481
 
1468
1482
  count: function () {
@@ -1477,10 +1491,6 @@ L.U.DataLayer = L.Evented.extend({
1477
1491
  return this.layer && this.map.hasLayer(this.layer)
1478
1492
  },
1479
1493
 
1480
- canBrowse: function () {
1481
- return this.layer && this.layer.canBrowse
1482
- },
1483
-
1484
1494
  getFeatureByIndex: function (index) {
1485
1495
  if (index === -1) index = this._index.length - 1
1486
1496
  const id = this._index[index]
@@ -1509,7 +1519,7 @@ L.U.DataLayer = L.Evented.extend({
1509
1519
  let next
1510
1520
  const index = this.map.datalayers_index
1511
1521
  while (((id = index[++id] ? id : 0), (next = index[id]))) {
1512
- if (next === this || (next.allowBrowse() && next.hasData())) break
1522
+ if (next === this || next.canBrowse()) break
1513
1523
  }
1514
1524
  return next
1515
1525
  },
@@ -1519,7 +1529,7 @@ L.U.DataLayer = L.Evented.extend({
1519
1529
  let prev
1520
1530
  const index = this.map.datalayers_index
1521
1531
  while (((id = index[--id] ? id : index.length - 1), (prev = index[id]))) {
1522
- if (prev === this || (prev.allowBrowse() && prev.hasData())) break
1532
+ if (prev === this || prev.canBrowse()) break
1523
1533
  }
1524
1534
  return prev
1525
1535
  },
@@ -1546,7 +1556,7 @@ L.U.DataLayer = L.Evented.extend({
1546
1556
  return this.isReadOnly() || this.isRemoteLayer()
1547
1557
  },
1548
1558
 
1549
- save: function () {
1559
+ save: async function () {
1550
1560
  if (this.isDeleted) return this.saveDelete()
1551
1561
  if (!this.isLoaded()) {
1552
1562
  return
@@ -1564,44 +1574,66 @@ L.U.DataLayer = L.Evented.extend({
1564
1574
  map_id: this.map.options.umap_id,
1565
1575
  pk: this.umap_id,
1566
1576
  })
1567
- this.map.post(saveUrl, {
1568
- data: formData,
1569
- callback: function (data, response) {
1570
- // Response contains geojson only if save has conflicted and conflicts have
1571
- // been resolved. So we need to reload to get extra data (saved from someone else)
1572
- if (data.geojson) {
1573
- this.clear()
1574
- this.fromGeoJSON(data.geojson)
1575
- delete data.geojson
1576
- }
1577
- this._geojson = geojson
1578
- this._last_modified = response.getResponseHeader('Last-Modified')
1579
- this.setUmapId(data.id)
1580
- this.updateOptions(data)
1581
- this.backupOptions()
1582
- this.connectToMap()
1583
- this._loaded = true
1584
- this.redraw() // Needed for reordering features
1585
- this.isDirty = false
1586
- this.permissions.save()
1587
- },
1588
- context: this,
1589
- headers: this._last_modified
1590
- ? { 'If-Unmodified-Since': this._last_modified }
1591
- : {},
1592
- })
1577
+ const headers = this._last_modified
1578
+ ? { 'If-Unmodified-Since': this._last_modified }
1579
+ : {}
1580
+ await this._trySave(saveUrl, headers, formData)
1581
+ this._geojson = geojson
1593
1582
  },
1594
1583
 
1595
- saveDelete: function () {
1596
- const callback = function () {
1584
+ _trySave: async function (url, headers, formData) {
1585
+ const [data, response, error] = await this.map.server.post(url, headers, formData)
1586
+ if (error) {
1587
+ if (response && response.status === 412) {
1588
+ const msg = L._(
1589
+ 'Woops! Someone else seems to have edited the data. You can save anyway, but this will erase the changes made by others.'
1590
+ )
1591
+ const actions = [
1592
+ {
1593
+ label: L._('Save anyway'),
1594
+ callback: async () => {
1595
+ // Save again,
1596
+ // but do not pass If-Unmodified-Since this time
1597
+ await this._trySave(url, {}, formData)
1598
+ },
1599
+ },
1600
+ {
1601
+ label: L._('Cancel'),
1602
+ },
1603
+ ]
1604
+ this.map.ui.alert({
1605
+ content: msg,
1606
+ level: 'error',
1607
+ duration: 100000,
1608
+ actions: actions,
1609
+ })
1610
+ }
1611
+ } else {
1612
+ // Response contains geojson only if save has conflicted and conflicts have
1613
+ // been resolved. So we need to reload to get extra data (added by someone else)
1614
+ if (data.geojson) {
1615
+ this.clear()
1616
+ this.fromGeoJSON(data.geojson)
1617
+ delete data.geojson
1618
+ }
1619
+ this._last_modified = response.headers.get('last-modified')
1620
+ this.setUmapId(data.id)
1621
+ this.updateOptions(data)
1622
+ this.backupOptions()
1623
+ this.connectToMap()
1624
+ this._loaded = true
1625
+ this.redraw() // Needed for reordering features
1597
1626
  this.isDirty = false
1598
- this.map.continueSaving()
1627
+ this.permissions.save()
1599
1628
  }
1600
- if (!this.umap_id) return callback.call(this)
1601
- this.map.xhr.post(this.getDeleteUrl(), {
1602
- callback: callback,
1603
- context: this,
1604
- })
1629
+ },
1630
+
1631
+ saveDelete: async function () {
1632
+ if (this.umap_id) {
1633
+ await this.map.server.post(this.getDeleteUrl())
1634
+ }
1635
+ this.isDirty = false
1636
+ this.map.continueSaving()
1605
1637
  },
1606
1638
 
1607
1639
  getMap: function () {
@@ -1614,7 +1646,7 @@ L.U.DataLayer = L.Evented.extend({
1614
1646
 
1615
1647
  tableEdit: function () {
1616
1648
  if (this.isRemoteLayer() || !this.isVisible()) return
1617
- const editor = new L.U.TableEditor(this)
1649
+ const editor = new U.TableEditor(this)
1618
1650
  editor.edit()
1619
1651
  },
1620
1652
  })
@@ -1,6 +1,6 @@
1
1
  // Dedicated object so we can deal with a separate dirty status, and thus
2
2
  // call the endpoint only when needed, saving one call at each save.
3
- L.U.MapPermissions = L.Class.extend({
3
+ U.MapPermissions = L.Class.extend({
4
4
  options: {
5
5
  owner: null,
6
6
  editors: [],
@@ -105,7 +105,7 @@ L.U.MapPermissions = L.Class.extend({
105
105
  ])
106
106
  }
107
107
  title.textContent = L._('Update permissions')
108
- const builder = new L.U.FormBuilder(this, fields)
108
+ const builder = new U.FormBuilder(this, fields)
109
109
  const form = builder.build()
110
110
  container.appendChild(form)
111
111
  if (this.isAnonymousMap() && this.map.options.user) {
@@ -131,21 +131,19 @@ L.U.MapPermissions = L.Class.extend({
131
131
  this.map.ui.openPanel({ data: { html: container }, className: 'dark' })
132
132
  },
133
133
 
134
- attach: function () {
135
- this.map.post(this.getAttachUrl(), {
136
- callback: function () {
137
- this.options.owner = this.map.options.user
138
- this.map.ui.alert({
139
- content: L._('Map has been attached to your account'),
140
- level: 'info',
141
- })
142
- this.map.ui.closePanel()
143
- },
144
- context: this,
145
- })
134
+ attach: async function () {
135
+ const [data, response, error] = await this.map.server.post(this.getAttachUrl())
136
+ if (!error) {
137
+ this.options.owner = this.map.options.user
138
+ this.map.ui.alert({
139
+ content: L._('Map has been attached to your account'),
140
+ level: 'info',
141
+ })
142
+ this.map.ui.closePanel()
143
+ }
146
144
  },
147
145
 
148
- save: function () {
146
+ save: async function () {
149
147
  if (!this.isDirty) return this.map.continueSaving()
150
148
  const formData = new FormData()
151
149
  if (!this.isAnonymousMap() && this.options.editors) {
@@ -159,16 +157,17 @@ L.U.MapPermissions = L.Class.extend({
159
157
  formData.append('owner', this.options.owner && this.options.owner.id)
160
158
  formData.append('share_status', this.options.share_status)
161
159
  }
162
- this.map.post(this.getUrl(), {
163
- data: formData,
164
- context: this,
165
- callback: function (data) {
166
- this.commit()
167
- this.isDirty = false
168
- this.map.continueSaving()
169
- this.map.fire('postsync')
170
- },
171
- })
160
+ const [data, response, error] = await this.map.server.post(
161
+ this.getUrl(),
162
+ {},
163
+ formData
164
+ )
165
+ if (!error) {
166
+ this.commit()
167
+ this.isDirty = false
168
+ this.map.continueSaving()
169
+ this.map.fire('postsync')
170
+ }
172
171
  },
173
172
 
174
173
  getUrl: function () {