umap-project 2.6.3__py3-none-any.whl → 2.7.0b0__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 (104) hide show
  1. umap/__init__.py +1 -1
  2. umap/admin.py +64 -1
  3. umap/context_processors.py +1 -0
  4. umap/locale/cs_CZ/LC_MESSAGES/django.mo +0 -0
  5. umap/locale/cs_CZ/LC_MESSAGES/django.po +96 -92
  6. umap/locale/de/LC_MESSAGES/django.mo +0 -0
  7. umap/locale/de/LC_MESSAGES/django.po +19 -18
  8. umap/locale/en/LC_MESSAGES/django.po +47 -43
  9. umap/locale/fr/LC_MESSAGES/django.mo +0 -0
  10. umap/locale/fr/LC_MESSAGES/django.po +51 -47
  11. umap/locale/pt/LC_MESSAGES/django.mo +0 -0
  12. umap/locale/pt/LC_MESSAGES/django.po +64 -60
  13. umap/management/commands/clean_tilelayer.py +152 -0
  14. umap/management/commands/purge_purgatory.py +28 -0
  15. umap/models.py +27 -2
  16. umap/settings/base.py +2 -0
  17. umap/static/umap/base.css +4 -4
  18. umap/static/umap/css/contextmenu.css +5 -0
  19. umap/static/umap/css/icon.css +7 -2
  20. umap/static/umap/img/16-white.svg +9 -2
  21. umap/static/umap/img/16.svg +3 -0
  22. umap/static/umap/img/source/16-white.svg +10 -3
  23. umap/static/umap/img/source/16.svg +4 -1
  24. umap/static/umap/js/modules/autocomplete.js +7 -3
  25. umap/static/umap/js/modules/browser.js +7 -1
  26. umap/static/umap/js/modules/caption.js +6 -1
  27. umap/static/umap/js/modules/data/features.js +176 -2
  28. umap/static/umap/js/modules/data/layer.js +31 -26
  29. umap/static/umap/js/modules/formatter.js +3 -2
  30. umap/static/umap/js/modules/global.js +2 -0
  31. umap/static/umap/js/modules/importers/communesfr.js +13 -1
  32. umap/static/umap/js/modules/permissions.js +123 -93
  33. umap/static/umap/js/modules/rendering/ui.js +37 -212
  34. umap/static/umap/js/modules/sync/engine.js +365 -14
  35. umap/static/umap/js/modules/sync/hlc.js +106 -0
  36. umap/static/umap/js/modules/sync/updaters.js +4 -4
  37. umap/static/umap/js/modules/sync/websocket.js +1 -1
  38. umap/static/umap/js/modules/ui/base.js +2 -2
  39. umap/static/umap/js/modules/ui/contextmenu.js +34 -17
  40. umap/static/umap/js/modules/urls.js +5 -1
  41. umap/static/umap/js/modules/utils.js +5 -1
  42. umap/static/umap/js/umap.controls.js +47 -47
  43. umap/static/umap/js/umap.core.js +3 -3
  44. umap/static/umap/js/umap.forms.js +3 -1
  45. umap/static/umap/js/umap.js +95 -112
  46. umap/static/umap/locale/br.js +13 -4
  47. umap/static/umap/locale/br.json +13 -4
  48. umap/static/umap/locale/ca.js +21 -12
  49. umap/static/umap/locale/ca.json +21 -12
  50. umap/static/umap/locale/cs_CZ.js +87 -78
  51. umap/static/umap/locale/cs_CZ.json +87 -78
  52. umap/static/umap/locale/de.js +17 -8
  53. umap/static/umap/locale/de.json +17 -8
  54. umap/static/umap/locale/en.js +9 -2
  55. umap/static/umap/locale/en.json +9 -2
  56. umap/static/umap/locale/eu.js +10 -3
  57. umap/static/umap/locale/eu.json +10 -3
  58. umap/static/umap/locale/fa_IR.js +11 -4
  59. umap/static/umap/locale/fa_IR.json +11 -4
  60. umap/static/umap/locale/fr.js +11 -4
  61. umap/static/umap/locale/fr.json +11 -4
  62. umap/static/umap/locale/hu.js +10 -3
  63. umap/static/umap/locale/hu.json +10 -3
  64. umap/static/umap/locale/pt.js +17 -8
  65. umap/static/umap/locale/pt.json +17 -8
  66. umap/static/umap/locale/pt_PT.js +13 -4
  67. umap/static/umap/locale/pt_PT.json +13 -4
  68. umap/static/umap/locale/zh_TW.js +13 -4
  69. umap/static/umap/locale/zh_TW.json +13 -4
  70. umap/static/umap/map.css +7 -22
  71. umap/static/umap/unittests/hlc.js +158 -0
  72. umap/static/umap/unittests/sync.js +321 -15
  73. umap/static/umap/unittests/utils.js +23 -0
  74. umap/static/umap/vendors/georsstogeojson/GeoRSSToGeoJSON.js +111 -80
  75. umap/templates/umap/dashboard_menu.html +4 -2
  76. umap/templates/umap/js.html +0 -4
  77. umap/tests/integration/test_anonymous_owned_map.py +1 -0
  78. umap/tests/integration/test_basics.py +1 -1
  79. umap/tests/integration/test_circles_layer.py +12 -0
  80. umap/tests/integration/test_datalayer.py +5 -0
  81. umap/tests/integration/test_draw_polygon.py +17 -9
  82. umap/tests/integration/test_draw_polyline.py +12 -8
  83. umap/tests/integration/test_edit_datalayer.py +4 -6
  84. umap/tests/integration/test_edit_map.py +1 -1
  85. umap/tests/integration/test_import.py +5 -0
  86. umap/tests/integration/test_map.py +5 -0
  87. umap/tests/integration/test_owned_map.py +1 -1
  88. umap/tests/integration/test_view_polygon.py +12 -12
  89. umap/tests/integration/test_websocket_sync.py +65 -3
  90. umap/tests/test_clean_tilelayer.py +83 -0
  91. umap/tests/test_datalayer.py +24 -0
  92. umap/tests/test_map_views.py +1 -0
  93. umap/tests/test_purge_purgatory.py +25 -0
  94. umap/tests/test_websocket_server.py +22 -0
  95. umap/urls.py +5 -1
  96. umap/views.py +6 -3
  97. umap/websocket_server.py +130 -27
  98. {umap_project-2.6.3.dist-info → umap_project-2.7.0b0.dist-info}/METADATA +9 -9
  99. {umap_project-2.6.3.dist-info → umap_project-2.7.0b0.dist-info}/RECORD +102 -97
  100. umap/static/umap/vendors/contextmenu/leaflet.contextmenu.min.css +0 -1
  101. umap/static/umap/vendors/contextmenu/leaflet.contextmenu.min.js +0 -7
  102. {umap_project-2.6.3.dist-info → umap_project-2.7.0b0.dist-info}/WHEEL +0 -0
  103. {umap_project-2.6.3.dist-info → umap_project-2.7.0b0.dist-info}/entry_points.txt +0 -0
  104. {umap_project-2.6.3.dist-info → umap_project-2.7.0b0.dist-info}/licenses/LICENSE +0 -0
@@ -578,11 +578,15 @@ const ControlsMixin = {
578
578
  ],
579
579
 
580
580
  renderEditToolbar: function () {
581
- const container = L.DomUtil.create(
582
- 'div',
583
- 'umap-main-edit-toolbox with-transition dark',
584
- this._controlContainer
585
- )
581
+ const className = 'umap-main-edit-toolbox'
582
+ const container =
583
+ document.querySelector(`.${className}`) ||
584
+ L.DomUtil.create(
585
+ 'div',
586
+ `${className} with-transition dark`,
587
+ this._controlContainer
588
+ )
589
+ container.innerHTML = ''
586
590
  const leftContainer = L.DomUtil.create('div', 'umap-left-edit-toolbox', container)
587
591
  const rightContainer = L.DomUtil.create('div', 'umap-right-edit-toolbox', container)
588
592
  const logo = L.DomUtil.create('div', 'logo', leftContainer)
@@ -623,32 +627,44 @@ const ControlsMixin = {
623
627
  },
624
628
  this
625
629
  )
626
- const update = () => {
627
- const status = this.permissions.getShareStatusDisplay()
628
- nameButton.textContent = this.getDisplayName()
629
- // status is not set until map is saved once
630
- if (status) {
631
- shareStatusButton.textContent = L._('Visibility: {status}', {
632
- status: status,
633
- })
634
- }
635
- }
636
- update()
637
- this.once('saved', L.bind(update, this))
638
630
  if (this.options.editMode === 'advanced') {
639
631
  L.DomEvent.on(nameButton, 'click', this.editCaption, this)
640
632
  L.DomEvent.on(shareStatusButton, 'click', this.permissions.edit, this.permissions)
641
633
  }
642
- this.on('postsync', L.bind(update, this))
643
634
  if (this.options.user?.id) {
644
- L.DomUtil.createLink(
645
- 'umap-user',
646
- rightContainer,
647
- L._('My Dashboard ({username})', {
648
- username: this.options.user.name,
649
- }),
650
- this.options.user.url
651
- )
635
+ const button = U.Utils.loadTemplate(`
636
+ <button class="umap-user flat" type="button">
637
+ <i class="icon icon-16 icon-profile"></i>
638
+ <span>${this.options.user.name}</span>
639
+ </button>
640
+ `)
641
+ rightContainer.appendChild(button)
642
+ const menu = new U.ContextMenu({ className: 'dark', fixed: true })
643
+ const actions = [
644
+ {
645
+ label: L._('New map'),
646
+ action: this.urls.get('map_new'),
647
+ },
648
+ {
649
+ label: L._('My maps'),
650
+ action: this.urls.get('user_dashboard'),
651
+ },
652
+ {
653
+ label: L._('My teams'),
654
+ action: this.urls.get('user_teams'),
655
+ },
656
+ ]
657
+ if (this.urls.has('user_profile')) {
658
+ actions.push({
659
+ label: L._('My profile'),
660
+ action: this.urls.get('user_profile'),
661
+ })
662
+ }
663
+ button.addEventListener('click', () => {
664
+ const x = button.offsetLeft
665
+ const y = button.offsetTop + button.offsetHeight
666
+ menu.open([x, y], actions)
667
+ })
652
668
  }
653
669
  this.help.getStartedLink(rightContainer)
654
670
  const controlEditCancel = L.DomUtil.createButton(
@@ -808,7 +824,8 @@ U.TileLayerControl = L.Control.IconLayers.extend({
808
824
  )
809
825
  const button = L.DomUtil.element({
810
826
  tagName: 'button',
811
- className: 'leaflet-iconLayers-layerCell leaflet-iconLayers-layerCell-plus button',
827
+ className:
828
+ 'leaflet-iconLayers-layerCell leaflet-iconLayers-layerCell-plus button',
812
829
  textContent: '+',
813
830
  parent: lastRow,
814
831
  })
@@ -1140,23 +1157,6 @@ L.Control.Loading.include({
1140
1157
  },
1141
1158
  })
1142
1159
 
1143
- /*
1144
- * Make it dynamic
1145
- */
1146
- U.ContextMenu = L.Map.ContextMenu.extend({
1147
- _createItems: function (e) {
1148
- this._map.setContextMenuItems(e)
1149
- L.Map.ContextMenu.prototype._createItems.call(this)
1150
- },
1151
-
1152
- _showAtPoint: function (pt, e) {
1153
- this._items = []
1154
- this._container.innerHTML = ''
1155
- this._createItems(e)
1156
- L.Map.ContextMenu.prototype._showAtPoint.call(this, pt, e)
1157
- },
1158
- })
1159
-
1160
1160
  U.Editable = L.Editable.extend({
1161
1161
  initialize: function (map, options) {
1162
1162
  L.Editable.prototype.initialize.call(this, map, options)
@@ -1181,9 +1181,9 @@ U.Editable = L.Editable.extend({
1181
1181
  if (this.map.editedFeature !== event.layer) event.layer.feature.edit(event)
1182
1182
  })
1183
1183
  this.on('editable:editing', (event) => {
1184
- const layer = event.layer
1185
- layer.feature.isDirty = true
1186
- layer.feature.fromLatLngs(layer.getLatLngs())
1184
+ const feature = event.layer.feature
1185
+ feature.isDirty = true
1186
+ feature.pullGeometry(false)
1187
1187
  })
1188
1188
  this.on('editable:vertex:ctrlclick', (event) => {
1189
1189
  const index = event.vertex.getIndex()
@@ -141,10 +141,10 @@ L.DomUtil.createButtonIcon = (parent, className, title, callback, size = 16) =>
141
141
  return el
142
142
  }
143
143
 
144
- L.DomUtil.createTitle = (parent, text, className, tag = 'h3') => {
144
+ L.DomUtil.createTitle = (parent, text, iconClassName, className = '', tag = 'h3') => {
145
145
  const title = L.DomUtil.create(tag, '', parent)
146
- if (className) L.DomUtil.createIcon(title, className)
147
- L.DomUtil.add('span', '', title, text)
146
+ if (className) L.DomUtil.createIcon(title, iconClassName)
147
+ L.DomUtil.add('span', className, title, text)
148
148
  return title
149
149
  }
150
150
 
@@ -1029,6 +1029,7 @@ L.FormBuilder.ManageOwner = L.FormBuilder.Element.extend({
1029
1029
  const options = {
1030
1030
  className: 'edit-owner',
1031
1031
  on_select: L.bind(this.onSelect, this),
1032
+ placeholder: L._("Type new owner's username"),
1032
1033
  }
1033
1034
  this.autocomplete = new U.AjaxAutocomplete(this.parentNode, options)
1034
1035
  const owner = this.toHTML()
@@ -1058,6 +1059,7 @@ L.FormBuilder.ManageEditors = L.FormBuilder.Element.extend({
1058
1059
  className: 'edit-editors',
1059
1060
  on_select: L.bind(this.onSelect, this),
1060
1061
  on_unselect: L.bind(this.onUnselect, this),
1062
+ placeholder: L._("Type editor's username"),
1061
1063
  }
1062
1064
  this.autocomplete = new U.AjaxAutocompleteMultiple(this.parentNode, options)
1063
1065
  this._values = this.toHTML()
@@ -1131,7 +1133,7 @@ U.FormBuilder = L.FormBuilder.extend({
1131
1133
  else schema.handler = 'IntInput'
1132
1134
  } else if (schema.choices) {
1133
1135
  const text_length = schema.choices.reduce(
1134
- (acc, [value, label]) => acc + label.length,
1136
+ (acc, [_, label]) => acc + label.length,
1135
1137
  0
1136
1138
  )
1137
1139
  // Try to be smart and use MultiChoice only
@@ -58,6 +58,7 @@ U.Map = L.Map.extend({
58
58
  this.panel = new U.Panel(this)
59
59
  this.dialog = new U.Dialog({ className: 'dark' })
60
60
  this.tooltip = new U.Tooltip(this._controlContainer)
61
+ this.contextmenu = new U.ContextMenu()
61
62
  if (this.hasEditMode()) {
62
63
  this.editPanel = new U.EditPanel(this)
63
64
  this.fullPanel = new U.FullPanel(this)
@@ -109,10 +110,9 @@ U.Map = L.Map.extend({
109
110
  delete this.options.advancedFilterKey
110
111
  }
111
112
 
112
- // Global storage for retrieving datalayers and features
113
- this.datalayers = {}
114
- this.datalayers_index = []
115
- this.dirty_datalayers = []
113
+ // Global storage for retrieving datalayers and features.
114
+ this.datalayers = {} // All datalayers, including deleted.
115
+ this.datalayers_index = [] // Datalayers actually on the map and ordered.
116
116
  this.features_index = {}
117
117
 
118
118
  // Needed for actions labels
@@ -197,8 +197,9 @@ U.Map = L.Map.extend({
197
197
 
198
198
  window.onbeforeunload = () => (this.editEnabled && this.isDirty) || null
199
199
  this.backup()
200
- this.initContextMenu()
201
200
  this.on('click', this.closeInplaceToolbar)
201
+ this.on('contextmenu', this.onContextMenu)
202
+ this.propagate()
202
203
  },
203
204
 
204
205
  initSyncEngine: async function () {
@@ -230,6 +231,7 @@ U.Map = L.Map.extend({
230
231
  this.renderEditToolbar()
231
232
  this.renderControls()
232
233
  this.browser.redraw()
234
+ this.propagate()
233
235
  break
234
236
  case 'data':
235
237
  this.redrawVisibleDataLayers()
@@ -486,7 +488,7 @@ U.Map = L.Map.extend({
486
488
  loadDataLayers: async function () {
487
489
  this.datalayersLoaded = true
488
490
  this.fire('datalayersloaded')
489
- for (const datalayer of Object.values(this.datalayers)) {
491
+ for (const datalayer of this.datalayers_index) {
490
492
  if (datalayer.showAtLoad()) await datalayer.show()
491
493
  }
492
494
  this.dataloaded = true
@@ -724,6 +726,10 @@ U.Map = L.Map.extend({
724
726
  }
725
727
  },
726
728
 
729
+ hasLayers: function () {
730
+ return Boolean(this.datalayers_index.length)
731
+ },
732
+
727
733
  fitDataBounds: function () {
728
734
  const bounds = this.getLayersBounds()
729
735
  if (!this.hasData() || !bounds.isValid()) return false
@@ -928,6 +934,7 @@ U.Map = L.Map.extend({
928
934
  if (mustReindex) datalayer.reindex()
929
935
  datalayer.redraw()
930
936
  })
937
+ this.propagate()
931
938
  this.fire('postsync')
932
939
  this.isDirty = true
933
940
  },
@@ -991,12 +998,12 @@ U.Map = L.Map.extend({
991
998
  if (this.editTools) this.editTools.stopDrawing()
992
999
  this.resetOptions()
993
1000
  this.datalayers_index = [].concat(this._datalayers_index_bk)
994
- this.dirty_datalayers.slice().forEach((datalayer) => {
1001
+ // Iter over all datalayers, including deleted if any.
1002
+ for (const datalayer of Object.values(this.datalayers)) {
995
1003
  if (datalayer.isDeleted) datalayer.connectToMap()
996
- datalayer.reset()
997
- })
1004
+ if (datalayer.isDirty) datalayer.reset()
1005
+ }
998
1006
  this.ensurePanesOrder()
999
- this.dirty_datalayers = []
1000
1007
  this.initTileLayers()
1001
1008
  this.isDirty = false
1002
1009
  this.onDataLayersChanged()
@@ -1006,25 +1013,6 @@ U.Map = L.Map.extend({
1006
1013
  this._container.classList.toggle('umap-is-dirty', this.isDirty)
1007
1014
  },
1008
1015
 
1009
- addDirtyDatalayer: function (datalayer) {
1010
- if (this.dirty_datalayers.indexOf(datalayer) === -1) {
1011
- this.dirty_datalayers.push(datalayer)
1012
- this.isDirty = true
1013
- }
1014
- },
1015
-
1016
- removeDirtyDatalayer: function (datalayer) {
1017
- if (this.dirty_datalayers.indexOf(datalayer) !== -1) {
1018
- this.dirty_datalayers.splice(this.dirty_datalayers.indexOf(datalayer), 1)
1019
- this.checkDirty()
1020
- }
1021
- },
1022
-
1023
- continueSaving: function () {
1024
- if (this.dirty_datalayers.length) this.dirty_datalayers[0].save()
1025
- else this.fire('saved')
1026
- },
1027
-
1028
1016
  exportOptions: function () {
1029
1017
  const properties = {}
1030
1018
  for (const option of Object.keys(U.SCHEMA)) {
@@ -1054,19 +1042,17 @@ U.Map = L.Map.extend({
1054
1042
  return
1055
1043
  }
1056
1044
  if (data.login_required) {
1057
- window.onLogin = () => this.saveSelf()
1045
+ window.onLogin = () => this.save()
1058
1046
  window.open(data.login_required)
1059
1047
  return
1060
1048
  }
1061
- if (data.user?.id) {
1062
- this.options.user = data.user
1063
- this.renderEditToolbar()
1064
- }
1049
+ this.options.user = data.user
1050
+ this.renderEditToolbar()
1065
1051
  if (!this.options.umap_id) {
1066
1052
  this.options.umap_id = data.id
1067
1053
  this.permissions.setOptions(data.permissions)
1068
1054
  this.permissions.commit()
1069
- if (data?.permissions?.anonymous_edit_url) {
1055
+ if (data.permissions?.anonymous_edit_url) {
1070
1056
  this.once('saved', () => {
1071
1057
  U.AlertCreation.info(
1072
1058
  L._('Your map has been created with an anonymous account!'),
@@ -1099,21 +1085,42 @@ U.Map = L.Map.extend({
1099
1085
  } else {
1100
1086
  window.location = data.url
1101
1087
  }
1102
- this.permissions.save()
1088
+ this.propagate()
1089
+ return true
1103
1090
  },
1104
1091
 
1105
- save: function () {
1092
+ save: async function () {
1106
1093
  if (!this.isDirty) return
1107
1094
  if (this._default_extent) this._setCenterAndZoom()
1108
1095
  this.backup()
1109
- this.once('saved', () => {
1110
- this.isDirty = false
1111
- })
1112
1096
  if (this.options.editMode === 'advanced') {
1113
1097
  // Only save the map if the user has the rights to do so.
1114
- this.saveSelf()
1115
- } else {
1116
- this.permissions.save()
1098
+ const ok = await this.saveSelf()
1099
+ if (!ok) return
1100
+ }
1101
+ await this.permissions.save()
1102
+ // Iter over all datalayers, including deleted.
1103
+ for (const datalayer of Object.values(this.datalayers)) {
1104
+ if (datalayer.isDirty) await datalayer.save()
1105
+ }
1106
+ this.isDirty = false
1107
+ this.renderEditToolbar()
1108
+ this.fire('saved')
1109
+ },
1110
+
1111
+ propagate: function () {
1112
+ let els = document.querySelectorAll('.map-name')
1113
+ for (const el of els) {
1114
+ el.textContent = this.getDisplayName()
1115
+ }
1116
+ const status = this.permissions.getShareStatusDisplay()
1117
+ els = document.querySelectorAll('.share-status')
1118
+ for (const el of els) {
1119
+ if (status) {
1120
+ el.textContent = L._('Visibility: {status}', {
1121
+ status: status,
1122
+ })
1123
+ }
1117
1124
  }
1118
1125
  },
1119
1126
 
@@ -1653,13 +1660,13 @@ U.Map = L.Map.extend({
1653
1660
  },
1654
1661
 
1655
1662
  clone: async function () {
1656
- if (
1657
- confirm(L._('Are you sure you want to clone this map and all its datalayers?'))
1658
- ) {
1659
- const url = this.urls.get('map_clone', { map_id: this.options.umap_id })
1660
- const [data, response, error] = await this.server.post(url)
1661
- if (data.redirect) window.location = data.redirect
1662
- }
1663
+ this.dialog
1664
+ .confirm(L._('Are you sure you want to clone this map and all its datalayers?'))
1665
+ .then(async () => {
1666
+ const url = this.urls.get('map_clone', { map_id: this.options.umap_id })
1667
+ const [data, response, error] = await this.server.post(url)
1668
+ if (data.redirect) window.location = data.redirect
1669
+ })
1663
1670
  },
1664
1671
 
1665
1672
  removeDataLayers: function () {
@@ -1679,118 +1686,94 @@ U.Map = L.Map.extend({
1679
1686
  this.loader.onAdd(this)
1680
1687
  },
1681
1688
 
1682
- initContextMenu: function () {
1683
- this.contextmenu = new U.ContextMenu(this)
1684
- this.contextmenu.enable()
1685
- },
1686
-
1687
- setContextMenuItems: function (e) {
1688
- let items = []
1689
- if (this._zoom !== this.getMaxZoom()) {
1690
- items.push({
1691
- text: L._('Zoom in'),
1692
- callback: function () {
1693
- this.zoomIn()
1694
- },
1695
- })
1696
- }
1697
- if (this._zoom !== this.getMinZoom()) {
1698
- items.push({
1699
- text: L._('Zoom out'),
1700
- callback: function () {
1701
- this.zoomOut()
1702
- },
1703
- })
1704
- }
1705
- if (e?.relatedTarget) {
1706
- if (e.relatedTarget.getContextMenuItems) {
1707
- items = items.concat(e.relatedTarget.getContextMenuItems(e))
1708
- }
1709
- }
1689
+ getContextMenuItems: function (event) {
1690
+ const items = []
1710
1691
  if (this.hasEditMode()) {
1711
- items.push('-')
1712
1692
  if (this.editEnabled) {
1713
1693
  if (!this.isDirty) {
1714
1694
  items.push({
1715
- text: this.help.displayLabel('STOP_EDIT'),
1716
- callback: this.disableEdit,
1695
+ label: this.help.displayLabel('STOP_EDIT'),
1696
+ action: () => this.disableEdit(),
1717
1697
  })
1718
1698
  }
1719
1699
  if (this.options.enableMarkerDraw) {
1720
1700
  items.push({
1721
- text: this.help.displayLabel('DRAW_MARKER'),
1722
- callback: this.startMarker,
1723
- context: this,
1701
+ label: this.help.displayLabel('DRAW_MARKER'),
1702
+ action: () => this.startMarker(event),
1724
1703
  })
1725
1704
  }
1726
1705
  if (this.options.enablePolylineDraw) {
1727
1706
  items.push({
1728
- text: this.help.displayLabel('DRAW_POLYGON'),
1729
- callback: this.startPolygon,
1730
- context: this,
1707
+ label: this.help.displayLabel('DRAW_POLYGON'),
1708
+ action: () => this.startPolygon(event),
1731
1709
  })
1732
1710
  }
1733
1711
  if (this.options.enablePolygonDraw) {
1734
1712
  items.push({
1735
- text: this.help.displayLabel('DRAW_LINE'),
1736
- callback: this.startPolyline,
1737
- context: this,
1713
+ label: this.help.displayLabel('DRAW_LINE'),
1714
+ action: () => this.startPolyline(event),
1738
1715
  })
1739
1716
  }
1740
1717
  items.push('-')
1741
1718
  items.push({
1742
- text: L._('Help'),
1743
- callback: function () {
1744
- this.help.show('edit')
1745
- },
1719
+ label: L._('Help'),
1720
+ action: () => this.help.show('edit'),
1746
1721
  })
1747
1722
  } else {
1748
1723
  items.push({
1749
- text: this.help.displayLabel('TOGGLE_EDIT'),
1750
- callback: this.enableEdit,
1724
+ label: this.help.displayLabel('TOGGLE_EDIT'),
1725
+ action: () => this.enableEdit(),
1751
1726
  })
1752
1727
  }
1753
1728
  }
1754
1729
  items.push(
1755
1730
  '-',
1756
1731
  {
1757
- text: L._('Open browser'),
1758
- callback: () => this.openBrowser('layers'),
1732
+ label: L._('Open browser'),
1733
+ action: () => this.openBrowser('layers'),
1759
1734
  },
1760
1735
  {
1761
- text: L._('Browse data'),
1762
- callback: () => this.openBrowser('data'),
1736
+ label: L._('Browse data'),
1737
+ action: () => this.openBrowser('data'),
1763
1738
  }
1764
1739
  )
1765
1740
  if (this.options.facetKey) {
1766
1741
  items.push({
1767
- text: L._('Filter data'),
1768
- callback: () => this.openBrowser('filters'),
1742
+ label: L._('Filter data'),
1743
+ action: () => this.openBrowser('filters'),
1769
1744
  })
1770
1745
  }
1771
1746
  items.push(
1772
1747
  {
1773
- text: L._('Open caption'),
1774
- callback: this.openCaption,
1748
+ label: L._('Open caption'),
1749
+ action: () => this.openCaption(),
1775
1750
  },
1776
1751
  {
1777
- text: this.help.displayLabel('SEARCH'),
1778
- callback: this.search,
1752
+ label: this.help.displayLabel('SEARCH'),
1753
+ action: () => this.search(event),
1779
1754
  }
1780
1755
  )
1781
1756
  if (this.options.urls.routing) {
1782
1757
  items.push('-', {
1783
- text: L._('Directions from here'),
1784
- callback: this.openExternalRouting,
1758
+ label: L._('Directions from here'),
1759
+ action: () => this.openExternalRouting(event),
1785
1760
  })
1786
1761
  }
1787
1762
  if (this.options.urls.edit_in_osm) {
1788
1763
  items.push('-', {
1789
- text: L._('Edit in OpenStreetMap'),
1790
- callback: this.editInOSM,
1764
+ label: L._('Edit in OpenStreetMap'),
1765
+ action: () => this.editInOSM(event),
1791
1766
  })
1792
1767
  }
1793
- this.options.contextmenuItems = items
1768
+ return items
1769
+ },
1770
+
1771
+ onContextMenu: function (event) {
1772
+ const items = this.getContextMenuItems(event)
1773
+ this.contextmenu.open(
1774
+ [event.originalEvent.clientX, event.originalEvent.clientY],
1775
+ items
1776
+ )
1794
1777
  },
1795
1778
 
1796
1779
  editInOSM: function (e) {
@@ -1850,7 +1833,7 @@ U.Map = L.Map.extend({
1850
1833
 
1851
1834
  getFeatureById: function (id) {
1852
1835
  let feature
1853
- for (const datalayer of Object.values(this.datalayers)) {
1836
+ for (const datalayer of this.datalayers_index) {
1854
1837
  feature = datalayer.getFeatureById(id)
1855
1838
  if (feature) return feature
1856
1839
  }
@@ -1,6 +1,6 @@
1
1
  const locale = {
2
- "(area: {measure})": "(gorread: {measure})",
3
- "(length: {measure})": "(pellder: {measure})",
2
+ "(area: {measure})": "(area: {measure})",
3
+ "(length: {measure})": "(length: {measure})",
4
4
  "# one hash for main heading": "# un diezenn evit an titl pennañ",
5
5
  "## two hashes for second heading": "## div ziezenn evit an eil titl",
6
6
  "### three hashes for third heading": "### teir diezenn evit an trede titl",
@@ -477,7 +477,6 @@ const locale = {
477
477
  "Search area": "Search area",
478
478
  "Type area name, or let empty to load data in current map view": "Type area name, or let empty to load data in current map view",
479
479
  "Data successfully imported!": "Roadennoù enporzhiet gant berzh!",
480
- "My Dashboard ({username})": "Ma Zaolenn-vourzh ({username})",
481
480
  "Clear data": "Skarzhañ ar roadennoù",
482
481
  "Remove layers": "Lemel ar gwiskadoù",
483
482
  "Categorized": "Dre rummad",
@@ -504,7 +503,17 @@ const locale = {
504
503
  "Property name to compute circles": "Property name to compute circles",
505
504
  "Min circle radius": "Min circle radius",
506
505
  "Max circle radius": "Max circle radius",
507
- "Display the open browser control": "Display the open browser control"
506
+ "Display the open browser control": "Display the open browser control",
507
+ "Copy as GeoJSON": "Copy as GeoJSON",
508
+ "Please zoom in to edit the geometry": "Please zoom in to edit the geometry",
509
+ "New map": "New map",
510
+ "My maps": "My maps",
511
+ "My teams": "My teams",
512
+ "My profile": "My profile",
513
+ "Type new owner's username": "Type new owner's username",
514
+ "Type editor's username": "Type editor's username",
515
+ "Map": "Map",
516
+ "Manage collaborators": "Manage collaborators"
508
517
  }
509
518
  L.registerLocale("br", locale)
510
519
  L.setLocale("br")
@@ -1,6 +1,6 @@
1
1
  {
2
- "(area: {measure})": "(gorread: {measure})",
3
- "(length: {measure})": "(pellder: {measure})",
2
+ "(area: {measure})": "(area: {measure})",
3
+ "(length: {measure})": "(length: {measure})",
4
4
  "# one hash for main heading": "# un diezenn evit an titl pennañ",
5
5
  "## two hashes for second heading": "## div ziezenn evit an eil titl",
6
6
  "### three hashes for third heading": "### teir diezenn evit an trede titl",
@@ -477,7 +477,6 @@
477
477
  "Search area": "Search area",
478
478
  "Type area name, or let empty to load data in current map view": "Type area name, or let empty to load data in current map view",
479
479
  "Data successfully imported!": "Roadennoù enporzhiet gant berzh!",
480
- "My Dashboard ({username})": "Ma Zaolenn-vourzh ({username})",
481
480
  "Clear data": "Skarzhañ ar roadennoù",
482
481
  "Remove layers": "Lemel ar gwiskadoù",
483
482
  "Categorized": "Dre rummad",
@@ -504,5 +503,15 @@
504
503
  "Property name to compute circles": "Property name to compute circles",
505
504
  "Min circle radius": "Min circle radius",
506
505
  "Max circle radius": "Max circle radius",
507
- "Display the open browser control": "Display the open browser control"
506
+ "Display the open browser control": "Display the open browser control",
507
+ "Copy as GeoJSON": "Copy as GeoJSON",
508
+ "Please zoom in to edit the geometry": "Please zoom in to edit the geometry",
509
+ "New map": "New map",
510
+ "My maps": "My maps",
511
+ "My teams": "My teams",
512
+ "My profile": "My profile",
513
+ "Type new owner's username": "Type new owner's username",
514
+ "Type editor's username": "Type editor's username",
515
+ "Map": "Map",
516
+ "Manage collaborators": "Manage collaborators"
508
517
  }