umap-project 1.14.0a1__py3-none-any.whl → 1.14.0a2__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 (182) hide show
  1. umap/__init__.py +1 -1
  2. umap/static/.gitignore +0 -0
  3. umap/static/umap/base.css +994 -0
  4. umap/static/umap/bitbucket.png +0 -0
  5. umap/static/umap/content.css +395 -0
  6. umap/static/umap/favicons/apple-touch-icon.png +0 -0
  7. umap/static/umap/favicons/favicon.ico +0 -0
  8. umap/static/umap/favicons/icon-192.png +0 -0
  9. umap/static/umap/favicons/icon-512.png +0 -0
  10. umap/static/umap/favicons/icon.svg +5 -0
  11. umap/static/umap/font/FiraSans-Light.woff +0 -0
  12. umap/static/umap/font/FiraSans-Light.woff2 +0 -0
  13. umap/static/umap/font/FiraSans-LightItalic.woff +0 -0
  14. umap/static/umap/font/FiraSans-LightItalic.woff2 +0 -0
  15. umap/static/umap/font/FiraSans-SemiBold.woff +0 -0
  16. umap/static/umap/font/FiraSans-SemiBold.woff2 +0 -0
  17. umap/static/umap/font.css +33 -0
  18. umap/static/umap/github.png +0 -0
  19. umap/static/umap/img/16-white.svg +190 -0
  20. umap/static/umap/img/16.svg +182 -0
  21. umap/static/umap/img/24-white.svg +62 -0
  22. umap/static/umap/img/24.svg +90 -0
  23. umap/static/umap/img/edit.svg +7 -0
  24. umap/static/umap/img/icon-bg.png +0 -0
  25. umap/static/umap/img/logo.svg +4 -0
  26. umap/static/umap/img/logo_filigree.png +0 -0
  27. umap/static/umap/img/logo_small.svg +14 -0
  28. umap/static/umap/img/marker.png +0 -0
  29. umap/static/umap/img/opensource.svg +7 -0
  30. umap/static/umap/img/osm.svg +7 -0
  31. umap/static/umap/img/search.gif +0 -0
  32. umap/static/umap/img/source/16-white.svg +980 -0
  33. umap/static/umap/img/source/16.svg +201 -0
  34. umap/static/umap/img/source/24-white.svg +83 -0
  35. umap/static/umap/img/source/24.svg +110 -0
  36. umap/static/umap/js/components/fragment.js +13 -0
  37. umap/static/umap/js/modules/global.js +8 -0
  38. umap/static/umap/js/modules/urls.js +29 -0
  39. umap/static/umap/js/umap.autocomplete.js +336 -0
  40. umap/static/umap/js/umap.browser.js +148 -0
  41. umap/static/umap/js/umap.controls.js +1542 -0
  42. umap/static/umap/js/umap.core.js +851 -0
  43. umap/static/umap/js/umap.datalayer.permissions.js +72 -0
  44. umap/static/umap/js/umap.features.js +1216 -0
  45. umap/static/umap/js/umap.forms.js +1267 -0
  46. umap/static/umap/js/umap.icon.js +234 -0
  47. umap/static/umap/js/umap.importer.js +166 -0
  48. umap/static/umap/js/umap.js +2010 -0
  49. umap/static/umap/js/umap.layer.js +1636 -0
  50. umap/static/umap/js/umap.permissions.js +212 -0
  51. umap/static/umap/js/umap.popup.js +340 -0
  52. umap/static/umap/js/umap.share.js +254 -0
  53. umap/static/umap/js/umap.slideshow.js +165 -0
  54. umap/static/umap/js/umap.tableeditor.js +120 -0
  55. umap/static/umap/js/umap.ui.js +240 -0
  56. umap/static/umap/js/umap.xhr.js +304 -0
  57. umap/static/umap/locale/am_ET.js +447 -0
  58. umap/static/umap/locale/am_ET.json +445 -0
  59. umap/static/umap/locale/ar.js +447 -0
  60. umap/static/umap/locale/ar.json +445 -0
  61. umap/static/umap/locale/ast.js +447 -0
  62. umap/static/umap/locale/ast.json +445 -0
  63. umap/static/umap/locale/bg.js +447 -0
  64. umap/static/umap/locale/bg.json +445 -0
  65. umap/static/umap/locale/br.js +447 -0
  66. umap/static/umap/locale/br.json +445 -0
  67. umap/static/umap/locale/ca.js +447 -0
  68. umap/static/umap/locale/ca.json +445 -0
  69. umap/static/umap/locale/cs_CZ.js +447 -0
  70. umap/static/umap/locale/cs_CZ.json +445 -0
  71. umap/static/umap/locale/da.js +447 -0
  72. umap/static/umap/locale/da.json +445 -0
  73. umap/static/umap/locale/de.js +447 -0
  74. umap/static/umap/locale/de.json +445 -0
  75. umap/static/umap/locale/el.js +447 -0
  76. umap/static/umap/locale/el.json +445 -0
  77. umap/static/umap/locale/en.js +447 -0
  78. umap/static/umap/locale/en.json +445 -0
  79. umap/static/umap/locale/en_US.json +445 -0
  80. umap/static/umap/locale/es.js +447 -0
  81. umap/static/umap/locale/es.json +445 -0
  82. umap/static/umap/locale/et.js +447 -0
  83. umap/static/umap/locale/et.json +445 -0
  84. umap/static/umap/locale/eu.js +413 -0
  85. umap/static/umap/locale/eu.json +411 -0
  86. umap/static/umap/locale/fa_IR.js +447 -0
  87. umap/static/umap/locale/fa_IR.json +445 -0
  88. umap/static/umap/locale/fi.js +447 -0
  89. umap/static/umap/locale/fi.json +445 -0
  90. umap/static/umap/locale/fr.js +447 -0
  91. umap/static/umap/locale/fr.json +445 -0
  92. umap/static/umap/locale/gl.js +447 -0
  93. umap/static/umap/locale/gl.json +445 -0
  94. umap/static/umap/locale/he.js +447 -0
  95. umap/static/umap/locale/he.json +445 -0
  96. umap/static/umap/locale/hr.js +447 -0
  97. umap/static/umap/locale/hr.json +445 -0
  98. umap/static/umap/locale/hu.js +447 -0
  99. umap/static/umap/locale/hu.json +445 -0
  100. umap/static/umap/locale/id.js +447 -0
  101. umap/static/umap/locale/id.json +445 -0
  102. umap/static/umap/locale/is.js +447 -0
  103. umap/static/umap/locale/is.json +445 -0
  104. umap/static/umap/locale/it.js +447 -0
  105. umap/static/umap/locale/it.json +445 -0
  106. umap/static/umap/locale/ja.js +447 -0
  107. umap/static/umap/locale/ja.json +445 -0
  108. umap/static/umap/locale/ko.js +447 -0
  109. umap/static/umap/locale/ko.json +445 -0
  110. umap/static/umap/locale/lt.js +447 -0
  111. umap/static/umap/locale/lt.json +445 -0
  112. umap/static/umap/locale/ms.js +447 -0
  113. umap/static/umap/locale/ms.json +445 -0
  114. umap/static/umap/locale/nl.js +447 -0
  115. umap/static/umap/locale/nl.json +445 -0
  116. umap/static/umap/locale/no.js +447 -0
  117. umap/static/umap/locale/no.json +445 -0
  118. umap/static/umap/locale/pl.js +447 -0
  119. umap/static/umap/locale/pl.json +445 -0
  120. umap/static/umap/locale/pl_PL.json +445 -0
  121. umap/static/umap/locale/pt.js +447 -0
  122. umap/static/umap/locale/pt.json +445 -0
  123. umap/static/umap/locale/pt_BR.js +447 -0
  124. umap/static/umap/locale/pt_BR.json +445 -0
  125. umap/static/umap/locale/pt_PT.js +447 -0
  126. umap/static/umap/locale/pt_PT.json +445 -0
  127. umap/static/umap/locale/ro.js +447 -0
  128. umap/static/umap/locale/ro.json +445 -0
  129. umap/static/umap/locale/ru.js +447 -0
  130. umap/static/umap/locale/ru.json +445 -0
  131. umap/static/umap/locale/si.js +439 -0
  132. umap/static/umap/locale/si.json +437 -0
  133. umap/static/umap/locale/sk_SK.js +447 -0
  134. umap/static/umap/locale/sk_SK.json +445 -0
  135. umap/static/umap/locale/sl.js +447 -0
  136. umap/static/umap/locale/sl.json +445 -0
  137. umap/static/umap/locale/sr.js +447 -0
  138. umap/static/umap/locale/sr.json +445 -0
  139. umap/static/umap/locale/sv.js +447 -0
  140. umap/static/umap/locale/sv.json +445 -0
  141. umap/static/umap/locale/th_TH.js +447 -0
  142. umap/static/umap/locale/th_TH.json +445 -0
  143. umap/static/umap/locale/tr.js +447 -0
  144. umap/static/umap/locale/tr.json +445 -0
  145. umap/static/umap/locale/uk_UA.js +447 -0
  146. umap/static/umap/locale/uk_UA.json +445 -0
  147. umap/static/umap/locale/vi.js +447 -0
  148. umap/static/umap/locale/vi.json +445 -0
  149. umap/static/umap/locale/vi_VN.json +445 -0
  150. umap/static/umap/locale/zh.js +447 -0
  151. umap/static/umap/locale/zh.json +445 -0
  152. umap/static/umap/locale/zh_CN.json +445 -0
  153. umap/static/umap/locale/zh_TW.Big5.json +445 -0
  154. umap/static/umap/locale/zh_TW.js +447 -0
  155. umap/static/umap/locale/zh_TW.json +445 -0
  156. umap/static/umap/map.css +1843 -0
  157. umap/static/umap/nav.css +81 -0
  158. umap/static/umap/openstreetmap.png +0 -0
  159. umap/static/umap/test/.eslintrc +22 -0
  160. umap/static/umap/test/Choropleth.js +243 -0
  161. umap/static/umap/test/Controls.js +100 -0
  162. umap/static/umap/test/DataLayer.js +495 -0
  163. umap/static/umap/test/Feature.js +382 -0
  164. umap/static/umap/test/Map.Export.js +106 -0
  165. umap/static/umap/test/Map.Init.js +46 -0
  166. umap/static/umap/test/Map.js +342 -0
  167. umap/static/umap/test/Marker.js +122 -0
  168. umap/static/umap/test/Permissions.js +74 -0
  169. umap/static/umap/test/Polygon.js +367 -0
  170. umap/static/umap/test/Polyline.js +402 -0
  171. umap/static/umap/test/TableEditor.js +100 -0
  172. umap/static/umap/test/URLs.js +54 -0
  173. umap/static/umap/test/Util.js +549 -0
  174. umap/static/umap/test/_pre.js +460 -0
  175. umap/static/umap/test/index.html +135 -0
  176. umap/static/umap/theme.css +1 -0
  177. umap/static/umap/twitter.png +0 -0
  178. {umap_project-1.14.0a1.dist-info → umap_project-1.14.0a2.dist-info}/METADATA +1 -1
  179. {umap_project-1.14.0a1.dist-info → umap_project-1.14.0a2.dist-info}/RECORD +182 -6
  180. {umap_project-1.14.0a1.dist-info → umap_project-1.14.0a2.dist-info}/WHEEL +0 -0
  181. {umap_project-1.14.0a1.dist-info → umap_project-1.14.0a2.dist-info}/entry_points.txt +0 -0
  182. {umap_project-1.14.0a1.dist-info → umap_project-1.14.0a2.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,1542 @@
1
+ L.U.BaseAction = L.ToolbarAction.extend({
2
+ initialize: function (map) {
3
+ this.map = map
4
+ if (this.options.label) {
5
+ this.options.tooltip = this.map.help.displayLabel(
6
+ this.options.label,
7
+ (withKbdTag = false)
8
+ )
9
+ }
10
+ this.options.toolbarIcon = {
11
+ className: this.options.className,
12
+ tooltip: this.options.tooltip,
13
+ }
14
+ L.ToolbarAction.prototype.initialize.call(this)
15
+ if (this.options.helpMenu && !this.map.helpMenuActions[this.options.className])
16
+ this.map.helpMenuActions[this.options.className] = this
17
+ },
18
+ })
19
+
20
+ L.U.ImportAction = L.U.BaseAction.extend({
21
+ options: {
22
+ helpMenu: true,
23
+ className: 'upload-data dark',
24
+ label: 'IMPORT_PANEL',
25
+ },
26
+
27
+ addHooks: function () {
28
+ this.map.importer.open()
29
+ },
30
+ })
31
+
32
+ L.U.EditPropertiesAction = L.U.BaseAction.extend({
33
+ options: {
34
+ helpMenu: true,
35
+ className: 'update-map-settings dark',
36
+ tooltip: L._('Edit map properties'),
37
+ },
38
+
39
+ addHooks: function () {
40
+ this.map.edit()
41
+ },
42
+ })
43
+
44
+ L.U.ChangeTileLayerAction = L.U.BaseAction.extend({
45
+ options: {
46
+ helpMenu: true,
47
+ className: 'dark update-map-tilelayers',
48
+ tooltip: L._('Change tilelayers'),
49
+ },
50
+
51
+ addHooks: function () {
52
+ this.map.updateTileLayers()
53
+ },
54
+ })
55
+
56
+ L.U.ManageDatalayersAction = L.U.BaseAction.extend({
57
+ options: {
58
+ className: 'dark manage-datalayers',
59
+ tooltip: L._('Manage layers'),
60
+ },
61
+
62
+ addHooks: function () {
63
+ this.map.manageDatalayers()
64
+ },
65
+ })
66
+
67
+ L.U.UpdateExtentAction = L.U.BaseAction.extend({
68
+ options: {
69
+ className: 'update-map-extent dark',
70
+ tooltip: L._('Save this center and zoom'),
71
+ },
72
+
73
+ addHooks: function () {
74
+ this.map.updateExtent()
75
+ },
76
+ })
77
+
78
+ L.U.UpdatePermsAction = L.U.BaseAction.extend({
79
+ options: {
80
+ className: 'update-map-permissions dark',
81
+ tooltip: L._('Update permissions and editors'),
82
+ },
83
+
84
+ addHooks: function () {
85
+ this.map.permissions.edit()
86
+ },
87
+ })
88
+
89
+ L.U.DrawMarkerAction = L.U.BaseAction.extend({
90
+ options: {
91
+ helpMenu: true,
92
+ className: 'umap-draw-marker dark',
93
+ label: 'DRAW_MARKER',
94
+ },
95
+
96
+ addHooks: function () {
97
+ this.map.startMarker()
98
+ },
99
+ })
100
+
101
+ L.U.DrawPolylineAction = L.U.BaseAction.extend({
102
+ options: {
103
+ helpMenu: true,
104
+ className: 'umap-draw-polyline dark',
105
+ label: 'DRAW_LINE',
106
+ },
107
+
108
+ addHooks: function () {
109
+ this.map.startPolyline()
110
+ },
111
+ })
112
+
113
+ L.U.DrawPolygonAction = L.U.BaseAction.extend({
114
+ options: {
115
+ helpMenu: true,
116
+ className: 'umap-draw-polygon dark',
117
+ label: 'DRAW_POLYGON',
118
+ },
119
+
120
+ addHooks: function () {
121
+ this.map.startPolygon()
122
+ },
123
+ })
124
+
125
+ L.U.AddPolylineShapeAction = L.U.BaseAction.extend({
126
+ options: {
127
+ className: 'umap-draw-polyline-multi dark',
128
+ tooltip: L._('Add a line to the current multi'),
129
+ },
130
+
131
+ addHooks: function () {
132
+ this.map.editedFeature.editor.newShape()
133
+ },
134
+ })
135
+
136
+ L.U.AddPolygonShapeAction = L.U.AddPolylineShapeAction.extend({
137
+ options: {
138
+ className: 'umap-draw-polygon-multi dark',
139
+ tooltip: L._('Add a polygon to the current multi'),
140
+ },
141
+ })
142
+
143
+ L.U.BaseFeatureAction = L.ToolbarAction.extend({
144
+ initialize: function (map, feature, latlng) {
145
+ this.map = map
146
+ this.feature = feature
147
+ this.latlng = latlng
148
+ L.ToolbarAction.prototype.initialize.call(this)
149
+ this.postInit()
150
+ },
151
+
152
+ postInit: function () {},
153
+
154
+ hideToolbar: function () {
155
+ this.map.removeLayer(this.toolbar)
156
+ },
157
+
158
+ addHooks: function () {
159
+ this.onClick({ latlng: this.latlng })
160
+ this.hideToolbar()
161
+ },
162
+ })
163
+
164
+ L.U.CreateHoleAction = L.U.BaseFeatureAction.extend({
165
+ options: {
166
+ toolbarIcon: {
167
+ className: 'umap-new-hole',
168
+ tooltip: L._('Start a hole here'),
169
+ },
170
+ },
171
+
172
+ onClick: function (e) {
173
+ this.feature.startHole(e)
174
+ },
175
+ })
176
+
177
+ L.U.ToggleEditAction = L.U.BaseFeatureAction.extend({
178
+ options: {
179
+ toolbarIcon: {
180
+ className: 'umap-toggle-edit',
181
+ tooltip: L._('Toggle edit mode (⇧+Click)'),
182
+ },
183
+ },
184
+
185
+ onClick: function (e) {
186
+ if (this.feature._toggleEditing) this.feature._toggleEditing(e) // Path
187
+ else this.feature.edit(e) // Marker
188
+ },
189
+ })
190
+
191
+ L.U.DeleteFeatureAction = L.U.BaseFeatureAction.extend({
192
+ options: {
193
+ toolbarIcon: {
194
+ className: 'umap-delete-all',
195
+ tooltip: L._('Delete this feature'),
196
+ },
197
+ },
198
+
199
+ postInit: function () {
200
+ if (!this.feature.isMulti())
201
+ this.options.toolbarIcon.className = 'umap-delete-one-of-one'
202
+ },
203
+
204
+ onClick: function (e) {
205
+ this.feature.confirmDelete(e)
206
+ },
207
+ })
208
+
209
+ L.U.DeleteShapeAction = L.U.BaseFeatureAction.extend({
210
+ options: {
211
+ toolbarIcon: {
212
+ className: 'umap-delete-one-of-multi',
213
+ tooltip: L._('Delete this shape'),
214
+ },
215
+ },
216
+
217
+ onClick: function (e) {
218
+ this.feature.enableEdit().deleteShapeAt(e.latlng)
219
+ },
220
+ })
221
+
222
+ L.U.ExtractShapeFromMultiAction = L.U.BaseFeatureAction.extend({
223
+ options: {
224
+ toolbarIcon: {
225
+ className: 'umap-extract-shape-from-multi',
226
+ tooltip: L._('Extract shape to separate feature'),
227
+ },
228
+ },
229
+
230
+ onClick: function (e) {
231
+ this.feature.isolateShape(e.latlng)
232
+ },
233
+ })
234
+
235
+ L.U.BaseVertexAction = L.U.BaseFeatureAction.extend({
236
+ initialize: function (map, feature, latlng, vertex) {
237
+ this.vertex = vertex
238
+ L.U.BaseFeatureAction.prototype.initialize.call(this, map, feature, latlng)
239
+ },
240
+ })
241
+
242
+ L.U.DeleteVertexAction = L.U.BaseVertexAction.extend({
243
+ options: {
244
+ toolbarIcon: {
245
+ className: 'umap-delete-vertex',
246
+ tooltip: L._('Delete this vertex (Alt+Click)'),
247
+ },
248
+ },
249
+
250
+ onClick: function () {
251
+ this.vertex.delete()
252
+ },
253
+ })
254
+
255
+ L.U.SplitLineAction = L.U.BaseVertexAction.extend({
256
+ options: {
257
+ toolbarIcon: {
258
+ className: 'umap-split-line',
259
+ tooltip: L._('Split line'),
260
+ },
261
+ },
262
+
263
+ onClick: function () {
264
+ this.vertex.split()
265
+ },
266
+ })
267
+
268
+ L.U.ContinueLineAction = L.U.BaseVertexAction.extend({
269
+ options: {
270
+ toolbarIcon: {
271
+ className: 'umap-continue-line',
272
+ tooltip: L._('Continue line'),
273
+ },
274
+ },
275
+
276
+ onClick: function () {
277
+ this.vertex.continue()
278
+ },
279
+ })
280
+
281
+ // Leaflet.Toolbar doesn't allow twice same toolbar class…
282
+ L.U.SettingsToolbar = L.Toolbar.Control.extend({
283
+ addTo: function (map) {
284
+ if (map.options.editMode !== 'advanced') return
285
+ L.Toolbar.Control.prototype.addTo.call(this, map)
286
+ },
287
+ })
288
+ L.U.DrawToolbar = L.Toolbar.Control.extend({
289
+ initialize: function (options) {
290
+ L.Toolbar.Control.prototype.initialize.call(this, options)
291
+ this.map = this.options.map
292
+ this.map.on('seteditedfeature', this.redraw, this)
293
+ },
294
+
295
+ appendToContainer: function (container) {
296
+ this.options.actions = []
297
+ if (this.map.options.enableMarkerDraw) {
298
+ this.options.actions.push(L.U.DrawMarkerAction)
299
+ }
300
+ if (this.map.options.enablePolylineDraw) {
301
+ this.options.actions.push(L.U.DrawPolylineAction)
302
+ if (this.map.editedFeature && this.map.editedFeature instanceof L.U.Polyline) {
303
+ this.options.actions.push(L.U.AddPolylineShapeAction)
304
+ }
305
+ }
306
+ if (this.map.options.enablePolygonDraw) {
307
+ this.options.actions.push(L.U.DrawPolygonAction)
308
+ if (this.map.editedFeature && this.map.editedFeature instanceof L.U.Polygon) {
309
+ this.options.actions.push(L.U.AddPolygonShapeAction)
310
+ }
311
+ }
312
+ L.Toolbar.Control.prototype.appendToContainer.call(this, container)
313
+ },
314
+
315
+ redraw: function () {
316
+ const container = this._control.getContainer()
317
+ container.innerHTML = ''
318
+ this.appendToContainer(container)
319
+ },
320
+ })
321
+
322
+ L.U.DropControl = L.Class.extend({
323
+ initialize: function (map) {
324
+ this.map = map
325
+ this.dropzone = map._container
326
+ },
327
+
328
+ enable: function () {
329
+ L.DomEvent.on(this.dropzone, 'dragenter', this.dragenter, this)
330
+ L.DomEvent.on(this.dropzone, 'dragover', this.dragover, this)
331
+ L.DomEvent.on(this.dropzone, 'drop', this.drop, this)
332
+ L.DomEvent.on(this.dropzone, 'dragleave', this.dragleave, this)
333
+ },
334
+
335
+ disable: function () {
336
+ L.DomEvent.off(this.dropzone, 'dragenter', this.dragenter, this)
337
+ L.DomEvent.off(this.dropzone, 'dragover', this.dragover, this)
338
+ L.DomEvent.off(this.dropzone, 'drop', this.drop, this)
339
+ L.DomEvent.off(this.dropzone, 'dragleave', this.dragleave, this)
340
+ },
341
+
342
+ dragenter: function (e) {
343
+ L.DomEvent.stop(e)
344
+ this.map.scrollWheelZoom.disable()
345
+ this.dropzone.classList.add('umap-dragover')
346
+ },
347
+
348
+ dragover: function (e) {
349
+ L.DomEvent.stop(e)
350
+ },
351
+
352
+ drop: function (e) {
353
+ this.map.scrollWheelZoom.enable()
354
+ this.dropzone.classList.remove('umap-dragover')
355
+ L.DomEvent.stop(e)
356
+ for (let i = 0, file; (file = e.dataTransfer.files[i]); i++) {
357
+ this.map.processFileToImport(file)
358
+ }
359
+ this.map.onceDataLoaded(this.map.fitDataBounds)
360
+ },
361
+
362
+ dragleave: function () {
363
+ this.map.scrollWheelZoom.enable()
364
+ this.dropzone.classList.remove('umap-dragover')
365
+ },
366
+ })
367
+
368
+ L.U.EditControl = L.Control.extend({
369
+ options: {
370
+ position: 'topright',
371
+ },
372
+
373
+ onAdd: function (map) {
374
+ const container = L.DomUtil.create('div', 'leaflet-control-edit-enable')
375
+ const enableEditing = L.DomUtil.createButton(
376
+ '',
377
+ container,
378
+ L._('Edit'),
379
+ map.enableEdit,
380
+ map
381
+ )
382
+ L.DomEvent.on(
383
+ enableEditing,
384
+ 'mouseover',
385
+ function () {
386
+ map.ui.tooltip({
387
+ content: `${L._('Switch to edit mode')} (<kbd>Ctrl+E</kbd>)`,
388
+ anchor: enableEditing,
389
+ position: 'bottom',
390
+ delay: 750,
391
+ duration: 5000,
392
+ })
393
+ },
394
+ this
395
+ )
396
+
397
+ return container
398
+ },
399
+ })
400
+
401
+ /* Share control */
402
+ L.Control.Embed = L.Control.extend({
403
+ options: {
404
+ position: 'topleft',
405
+ },
406
+
407
+ onAdd: function (map) {
408
+ const container = L.DomUtil.create('div', 'leaflet-control-embed umap-control')
409
+ const shareButton = L.DomUtil.createButton(
410
+ '',
411
+ container,
412
+ L._('Share and download'),
413
+ map.share.open,
414
+ map.share
415
+ )
416
+ L.DomEvent.on(shareButton, 'dblclick', L.DomEvent.stopPropagation)
417
+ return container
418
+ },
419
+ })
420
+
421
+ L.U.MoreControls = L.Control.extend({
422
+ options: {
423
+ position: 'topleft',
424
+ },
425
+
426
+ onAdd: function () {
427
+ const container = L.DomUtil.create('div', 'umap-control-text')
428
+ const moreButton = L.DomUtil.createButton(
429
+ 'umap-control-more',
430
+ container,
431
+ L._('More controls'),
432
+ this.toggle,
433
+ this
434
+ )
435
+ const lessButton = L.DomUtil.createButton(
436
+ 'umap-control-less',
437
+ container,
438
+ L._('Hide controls'),
439
+ this.toggle,
440
+ this
441
+ )
442
+ return container
443
+ },
444
+
445
+ toggle: function () {
446
+ const pos = this.getPosition(),
447
+ corner = this._map._controlCorners[pos],
448
+ className = 'umap-more-controls'
449
+ if (L.DomUtil.hasClass(corner, className)) L.DomUtil.removeClass(corner, className)
450
+ else L.DomUtil.addClass(corner, className)
451
+ },
452
+ })
453
+
454
+ L.U.PermanentCreditsControl = L.Control.extend({
455
+ options: {
456
+ position: 'bottomleft',
457
+ },
458
+
459
+ initialize: function (map, options) {
460
+ this.map = map
461
+ L.Control.prototype.initialize.call(this, options)
462
+ },
463
+
464
+ onAdd: function () {
465
+ const paragraphContainer = L.DomUtil.create(
466
+ 'div',
467
+ 'umap-permanent-credits-container'
468
+ ),
469
+ creditsParagraph = L.DomUtil.create('p', '', paragraphContainer)
470
+
471
+ this.paragraphContainer = paragraphContainer
472
+ this.setCredits()
473
+ this.setBackground()
474
+
475
+ return paragraphContainer
476
+ },
477
+
478
+ setCredits: function () {
479
+ this.paragraphContainer.innerHTML = L.Util.toHTML(this.map.options.permanentCredit)
480
+ },
481
+
482
+ setBackground: function () {
483
+ if (this.map.options.permanentCreditBackground) {
484
+ this.paragraphContainer.style.backgroundColor = '#FFFFFFB0'
485
+ } else {
486
+ this.paragraphContainer.style.backgroundColor = ''
487
+ }
488
+ },
489
+ })
490
+
491
+ L.U.DataLayersControl = L.Control.extend({
492
+ options: {
493
+ position: 'topleft',
494
+ },
495
+
496
+ labels: {
497
+ zoomToLayer: L._('Zoom to layer extent'),
498
+ toggleLayer: L._('Show/hide layer'),
499
+ editLayer: L._('Edit'),
500
+ },
501
+
502
+ initialize: function (map, options) {
503
+ this.map = map
504
+ L.Control.prototype.initialize.call(this, options)
505
+ },
506
+
507
+ _initLayout: function (map) {
508
+ const container = (this._container = L.DomUtil.create(
509
+ 'div',
510
+ 'leaflet-control-browse umap-control'
511
+ )),
512
+ actions = L.DomUtil.create('div', 'umap-browse-actions', container)
513
+ this._datalayers_container = L.DomUtil.create(
514
+ 'ul',
515
+ 'umap-browse-datalayers',
516
+ actions
517
+ )
518
+
519
+ L.DomUtil.createButton(
520
+ 'umap-browse-link',
521
+ actions,
522
+ L._('Browse data'),
523
+ map.openBrowser,
524
+ map
525
+ )
526
+
527
+ const toggleButton = L.DomUtil.createButton(
528
+ 'umap-browse-toggle',
529
+ container,
530
+ L._('See data layers')
531
+ )
532
+ L.DomEvent.on(toggleButton, 'click', L.DomEvent.stop)
533
+
534
+ map.whenReady(function () {
535
+ this.update()
536
+ }, this)
537
+
538
+ if (L.Browser.pointer) {
539
+ L.DomEvent.disableClickPropagation(container)
540
+ L.DomEvent.on(container, 'wheel', L.DomEvent.stopPropagation)
541
+ L.DomEvent.on(container, 'MozMousePixelScroll', L.DomEvent.stopPropagation)
542
+ }
543
+ if (!L.Browser.touch) {
544
+ L.DomEvent.on(
545
+ container,
546
+ {
547
+ mouseenter: this.expand,
548
+ mouseleave: this.collapse,
549
+ },
550
+ this
551
+ )
552
+ } else {
553
+ L.DomEvent.on(container, 'click', L.DomEvent.stopPropagation)
554
+ L.DomEvent.on(toggleButton, 'click', L.DomEvent.stop).on(
555
+ toggleButton,
556
+ 'click',
557
+ this.expand,
558
+ this
559
+ )
560
+ map.on('click', this.collapse, this)
561
+ }
562
+
563
+ return container
564
+ },
565
+
566
+ onAdd: function (map) {
567
+ if (!this._container) this._initLayout(map)
568
+ if (map.options.datalayersControl === 'expanded') this.expand()
569
+ return this._container
570
+ },
571
+
572
+ onRemove: function (map) {
573
+ this.collapse()
574
+ },
575
+
576
+ update: function () {
577
+ if (this._datalayers_container && this._map) {
578
+ this._datalayers_container.innerHTML = ''
579
+ this.map.eachDataLayerReverse(function (datalayer) {
580
+ this.addDataLayer(this._datalayers_container, datalayer)
581
+ }, this)
582
+ }
583
+ },
584
+
585
+ expand: function () {
586
+ L.DomUtil.addClass(this._container, 'expanded')
587
+ },
588
+
589
+ collapse: function () {
590
+ if (this.map.options.datalayersControl === 'expanded') return
591
+ L.DomUtil.removeClass(this._container, 'expanded')
592
+ },
593
+
594
+ addDataLayer: function (container, datalayer, draggable) {
595
+ const datalayerLi = L.DomUtil.create('li', '', container)
596
+ if (draggable)
597
+ L.DomUtil.element(
598
+ 'i',
599
+ { className: 'drag-handle', title: L._('Drag to reorder') },
600
+ datalayerLi
601
+ )
602
+ datalayer.renderToolbox(datalayerLi)
603
+ const title = L.DomUtil.add(
604
+ 'span',
605
+ 'layer-title',
606
+ datalayerLi,
607
+ datalayer.options.name
608
+ )
609
+
610
+ datalayerLi.id = `browse_data_toggle_${L.stamp(datalayer)}`
611
+ L.DomUtil.classIf(datalayerLi, 'off', !datalayer.isVisible())
612
+
613
+ title.textContent = datalayer.options.name
614
+ },
615
+
616
+ newDataLayer: function () {
617
+ const datalayer = this.map.createDataLayer({})
618
+ datalayer.edit()
619
+ },
620
+
621
+ openPanel: function () {
622
+ if (!this.map.editEnabled) return
623
+ const container = L.DomUtil.create('ul', 'umap-browse-datalayers')
624
+ const title = L.DomUtil.create('h3', '', container)
625
+ title.textContent = L._('Manage layers')
626
+ this.map.eachDataLayerReverse(function (datalayer) {
627
+ this.addDataLayer(container, datalayer, true)
628
+ }, this)
629
+ const orderable = new L.U.Orderable(container)
630
+ orderable.on(
631
+ 'drop',
632
+ function (e) {
633
+ const layer = this.map.datalayers[e.src.dataset.id],
634
+ other = this.map.datalayers[e.dst.dataset.id],
635
+ minIndex = Math.min(layer.getRank(), other.getRank()),
636
+ maxIndex = Math.max(layer.getRank(), other.getRank())
637
+ if (e.finalIndex === 0) layer.bringToTop()
638
+ else if (e.finalIndex > e.initialIndex) layer.insertBefore(other)
639
+ else layer.insertAfter(other)
640
+ this.map.eachDataLayerReverse((datalayer) => {
641
+ if (datalayer.getRank() >= minIndex && datalayer.getRank() <= maxIndex)
642
+ datalayer.isDirty = true
643
+ })
644
+ this.map.indexDatalayers()
645
+ },
646
+ this
647
+ )
648
+
649
+ const bar = L.DomUtil.create('div', 'button-bar', container)
650
+ L.DomUtil.createButton(
651
+ 'show-on-edit block add-datalayer button',
652
+ bar,
653
+ L._('Add a layer'),
654
+ this.newDataLayer,
655
+ this
656
+ )
657
+
658
+ this.map.ui.openPanel({ data: { html: container }, className: 'dark' })
659
+ },
660
+ })
661
+
662
+ L.U.DataLayer.include({
663
+ renderLegend: function (container) {
664
+ if (this.layer.renderLegend) return this.layer.renderLegend(container)
665
+ const color = L.DomUtil.create('span', 'datalayer-color', container)
666
+ color.style.backgroundColor = this.getColor()
667
+ },
668
+
669
+ renderToolbox: function (container) {
670
+ const toggle = L.DomUtil.create('i', 'layer-toggle', container),
671
+ zoomTo = L.DomUtil.create('i', 'layer-zoom_to', container),
672
+ edit = L.DomUtil.create('i', 'layer-edit show-on-edit', container),
673
+ table = L.DomUtil.create('i', 'layer-table-edit show-on-edit', container),
674
+ remove = L.DomUtil.create('i', 'layer-delete show-on-edit', container)
675
+ zoomTo.title = L._('Zoom to layer extent')
676
+ toggle.title = L._('Show/hide layer')
677
+ edit.title = L._('Edit')
678
+ table.title = L._('Edit properties in a table')
679
+ remove.title = L._('Delete layer')
680
+ if (this.isReadOnly()) {
681
+ L.DomUtil.addClass(container, 'readonly')
682
+ } else {
683
+ L.DomEvent.on(edit, 'click', this.edit, this)
684
+ L.DomEvent.on(table, 'click', this.tableEdit, this)
685
+ L.DomEvent.on(
686
+ remove,
687
+ 'click',
688
+ function () {
689
+ if (!this.isVisible()) return
690
+ if (!confirm(L._('Are you sure you want to delete this layer?'))) return
691
+ this._delete()
692
+ this.map.ui.closePanel()
693
+ },
694
+ this
695
+ )
696
+ }
697
+ L.DomEvent.on(toggle, 'click', this.toggle, this)
698
+ L.DomEvent.on(zoomTo, 'click', this.zoomTo, this)
699
+ L.DomUtil.addClass(container, this.getHidableClass())
700
+ L.DomUtil.classIf(container, 'off', !this.isVisible())
701
+ container.dataset.id = L.stamp(this)
702
+ },
703
+
704
+ getHidableElements: function () {
705
+ return document.querySelectorAll(`.${this.getHidableClass()}`)
706
+ },
707
+
708
+ getHidableClass: function () {
709
+ return `show_with_datalayer_${L.stamp(this)}`
710
+ },
711
+
712
+ propagateRemote: function () {
713
+ const els = this.getHidableElements()
714
+ for (let i = 0; i < els.length; i++) {
715
+ L.DomUtil.classIf(els[i], 'remotelayer', this.isRemoteLayer())
716
+ }
717
+ },
718
+
719
+ propagateHide: function () {
720
+ const els = this.getHidableElements()
721
+ for (let i = 0; i < els.length; i++) {
722
+ L.DomUtil.addClass(els[i], 'off')
723
+ }
724
+ },
725
+
726
+ propagateShow: function () {
727
+ this.onceLoaded(function () {
728
+ const els = this.getHidableElements()
729
+ for (let i = 0; i < els.length; i++) {
730
+ L.DomUtil.removeClass(els[i], 'off')
731
+ }
732
+ }, this)
733
+ },
734
+ })
735
+
736
+ L.U.DataLayer.addInitHook(function () {
737
+ this.on('hide', this.propagateHide)
738
+ this.on('show', this.propagateShow)
739
+ if (this.isVisible()) this.propagateShow()
740
+ })
741
+
742
+ L.U.Map.include({
743
+ _openFacet: function () {
744
+ const container = L.DomUtil.create('div', 'umap-facet-search'),
745
+ title = L.DomUtil.add('h3', 'umap-filter-title', container, L._('Facet search')),
746
+ keys = Object.keys(this.getFacetKeys())
747
+
748
+ const knownValues = {}
749
+
750
+ keys.forEach((key) => {
751
+ knownValues[key] = []
752
+ if (!this.facets[key]) this.facets[key] = []
753
+ })
754
+
755
+ this.eachBrowsableDataLayer((datalayer) => {
756
+ datalayer.eachFeature((feature) => {
757
+ keys.forEach((key) => {
758
+ let value = feature.properties[key]
759
+ if (typeof value !== 'undefined' && !knownValues[key].includes(value)) {
760
+ knownValues[key].push(value)
761
+ }
762
+ })
763
+ })
764
+ })
765
+
766
+ const filterFeatures = function () {
767
+ let found = false
768
+ this.eachBrowsableDataLayer((datalayer) => {
769
+ datalayer.resetLayer(true)
770
+ if (datalayer.hasDataVisible()) found = true
771
+ })
772
+ // TODO: display a results counter in the panel instead.
773
+ if (!found)
774
+ this.ui.alert({ content: L._('No results for these facets'), level: 'info' })
775
+ }
776
+
777
+ const fields = keys.map((current) => [
778
+ `facets.${current}`,
779
+ {
780
+ handler: 'FacetSearch',
781
+ choices: knownValues[current],
782
+ label: this.getFacetKeys()[current],
783
+ },
784
+ ])
785
+ const builder = new L.U.FormBuilder(this, fields, {
786
+ makeDirty: false,
787
+ callback: filterFeatures,
788
+ callbackContext: this,
789
+ })
790
+ container.appendChild(builder.build())
791
+
792
+ this.ui.openPanel({ data: { html: container }, actions: [this._aboutLink()] })
793
+ },
794
+
795
+ _aboutLink: function () {
796
+ const link = L.DomUtil.create('li', '')
797
+ L.DomUtil.create('i', 'umap-icon-16 umap-caption', link)
798
+ const label = L.DomUtil.create('span', '', link)
799
+ label.textContent = label.title = L._('About')
800
+ L.DomEvent.on(link, 'click', this.displayCaption, this)
801
+ return link
802
+ },
803
+
804
+ displayCaption: function () {
805
+ const container = L.DomUtil.create('div', 'umap-caption')
806
+ let title = L.DomUtil.create('h3', '', container)
807
+ title.textContent = this.options.name
808
+ this.permissions.addOwnerLink('h5', container)
809
+ if (this.options.description) {
810
+ const description = L.DomUtil.create('div', 'umap-map-description', container)
811
+ description.innerHTML = L.Util.toHTML(this.options.description)
812
+ }
813
+ const datalayerContainer = L.DomUtil.create('div', 'datalayer-container', container)
814
+ this.eachVisibleDataLayer((datalayer) => {
815
+ if (!datalayer.options.inCaption) return
816
+ const p = L.DomUtil.create('p', 'datalayer-legend', datalayerContainer),
817
+ legend = L.DomUtil.create('span', '', p),
818
+ headline = L.DomUtil.create('strong', '', p),
819
+ description = L.DomUtil.create('span', '', p)
820
+ datalayer.onceLoaded(function () {
821
+ datalayer.renderLegend(legend)
822
+ if (datalayer.options.description) {
823
+ description.innerHTML = L.Util.toHTML(datalayer.options.description)
824
+ }
825
+ })
826
+ datalayer.renderToolbox(headline)
827
+ L.DomUtil.add('span', '', headline, `${datalayer.options.name} `)
828
+ })
829
+ const creditsContainer = L.DomUtil.create('div', 'credits-container', container),
830
+ credits = L.DomUtil.createFieldset(creditsContainer, L._('Credits'))
831
+ title = L.DomUtil.add('h5', '', credits, L._('User content credits'))
832
+ if (this.options.shortCredit || this.options.longCredit) {
833
+ L.DomUtil.add(
834
+ 'p',
835
+ '',
836
+ credits,
837
+ L.Util.toHTML(this.options.longCredit || this.options.shortCredit)
838
+ )
839
+ }
840
+ if (this.options.licence) {
841
+ const licence = L.DomUtil.add(
842
+ 'p',
843
+ '',
844
+ credits,
845
+ `${L._('Map user content has been published under licence')} `
846
+ )
847
+ L.DomUtil.createLink(
848
+ '',
849
+ licence,
850
+ this.options.licence.name,
851
+ this.options.licence.url
852
+ )
853
+ } else {
854
+ L.DomUtil.add('p', '', credits, L._('No licence has been set'))
855
+ }
856
+ L.DomUtil.create('hr', '', credits)
857
+ title = L.DomUtil.create('h5', '', credits)
858
+ title.textContent = L._('Map background credits')
859
+ const tilelayerCredit = L.DomUtil.create('p', '', credits),
860
+ name = L.DomUtil.create('strong', '', tilelayerCredit),
861
+ attribution = L.DomUtil.create('span', '', tilelayerCredit)
862
+ name.textContent = `${this.selected_tilelayer.options.name} `
863
+ attribution.innerHTML = this.selected_tilelayer.getAttribution()
864
+ L.DomUtil.create('hr', '', credits)
865
+ const umapCredit = L.DomUtil.create('p', '', credits),
866
+ urls = {
867
+ leaflet: 'http://leafletjs.com',
868
+ django: 'https://www.djangoproject.com',
869
+ umap: 'http://wiki.openstreetmap.org/wiki/UMap',
870
+ changelog: 'https://umap-project.readthedocs.io/en/master/changelog/',
871
+ version: this.options.umap_version,
872
+ }
873
+ umapCredit.innerHTML = L._(
874
+ `
875
+ Powered by <a href="{leaflet}">Leaflet</a> and
876
+ <a href="{django}">Django</a>,
877
+ glued by <a href="{umap}">uMap project</a>
878
+ (version <a href="{changelog}">{version}</a>).
879
+ `,
880
+ urls
881
+ )
882
+ const browser = L.DomUtil.create('li', '')
883
+ L.DomUtil.create('i', 'umap-icon-16 umap-list', browser)
884
+ const labelBrowser = L.DomUtil.create('span', '', browser)
885
+ labelBrowser.textContent = labelBrowser.title = L._('Browse data')
886
+ L.DomEvent.on(browser, 'click', this.openBrowser, this)
887
+ const actions = [browser]
888
+ if (this.options.facetKey) {
889
+ const filter = L.DomUtil.create('li', '')
890
+ L.DomUtil.create('i', 'umap-icon-16 umap-add', filter)
891
+ const labelFilter = L.DomUtil.create('span', '', filter)
892
+ labelFilter.textContent = labelFilter.title = L._('Facet search')
893
+ L.DomEvent.on(filter, 'click', this.openFacet, this)
894
+ actions.push(filter)
895
+ }
896
+ this.ui.openPanel({ data: { html: container }, actions: actions })
897
+ },
898
+
899
+ renderEditToolbar: function () {
900
+ const container = L.DomUtil.create(
901
+ 'div',
902
+ 'umap-main-edit-toolbox with-transition dark',
903
+ this._controlContainer
904
+ )
905
+ const leftContainer = L.DomUtil.create('div', 'umap-left-edit-toolbox', container)
906
+ const rightContainer = L.DomUtil.create('div', 'umap-right-edit-toolbox', container)
907
+ const logo = L.DomUtil.create('div', 'logo', leftContainer)
908
+ L.DomUtil.createLink('', logo, 'uMap', '/', null, L._('Go to the homepage'))
909
+ const nameButton = L.DomUtil.createButton(
910
+ 'map-name',
911
+ leftContainer,
912
+ '',
913
+ this.edit,
914
+ this
915
+ )
916
+ L.DomEvent.on(
917
+ nameButton,
918
+ 'mouseover',
919
+ function () {
920
+ this.ui.tooltip({
921
+ content: L._('Edit the title of the map'),
922
+ anchor: nameButton,
923
+ position: 'bottom',
924
+ delay: 500,
925
+ duration: 5000,
926
+ })
927
+ },
928
+ this
929
+ )
930
+ const shareStatusButton = L.DomUtil.createButton(
931
+ 'share-status',
932
+ leftContainer,
933
+ '',
934
+ this.permissions.edit,
935
+ this.permissions
936
+ )
937
+ L.DomEvent.on(
938
+ shareStatusButton,
939
+ 'mouseover',
940
+ function () {
941
+ this.ui.tooltip({
942
+ content: L._('Update who can see and edit the map'),
943
+ anchor: shareStatusButton,
944
+ position: 'bottom',
945
+ delay: 500,
946
+ duration: 5000,
947
+ })
948
+ },
949
+ this
950
+ )
951
+ const update = () => {
952
+ const status = this.permissions.getShareStatusDisplay()
953
+ nameButton.textContent = this.getDisplayName()
954
+ // status is not set until map is saved once
955
+ if (status) {
956
+ shareStatusButton.textContent = L._('Visibility: {status}', {
957
+ status: status,
958
+ })
959
+ }
960
+ }
961
+ update()
962
+ this.once('saved', L.bind(update, this))
963
+ if (this.options.editMode === 'advanced') {
964
+ L.DomEvent.on(nameButton, 'click', this.edit, this)
965
+ L.DomEvent.on(shareStatusButton, 'click', this.permissions.edit, this.permissions)
966
+ }
967
+ this.on('postsync', L.bind(update, this))
968
+ if (this.options.user) {
969
+ L.DomUtil.createLink(
970
+ 'umap-user',
971
+ rightContainer,
972
+ L._(`My Dashboard <span>({username})</span>`, {
973
+ username: this.options.user.name,
974
+ }),
975
+ this.options.user.url
976
+ )
977
+ }
978
+ this.help.link(rightContainer, 'edit')
979
+ const controlEditCancel = L.DomUtil.createButton(
980
+ 'leaflet-control-edit-cancel',
981
+ rightContainer,
982
+ L.DomUtil.add('span', '', null, L._('Cancel edits')),
983
+ this.askForReset,
984
+ this
985
+ )
986
+ L.DomEvent.on(
987
+ controlEditCancel,
988
+ 'mouseover',
989
+ function () {
990
+ this.ui.tooltip({
991
+ content: this.help.displayLabel('CANCEL'),
992
+ anchor: controlEditCancel,
993
+ position: 'bottom',
994
+ delay: 500,
995
+ duration: 5000,
996
+ })
997
+ },
998
+ this
999
+ )
1000
+ const controlEditDisable = L.DomUtil.createButton(
1001
+ 'leaflet-control-edit-disable',
1002
+ rightContainer,
1003
+ L.DomUtil.add('span', '', null, L._('View')),
1004
+ function (e) {
1005
+ this.disableEdit(e)
1006
+ this.ui.closePanel()
1007
+ },
1008
+ this
1009
+ )
1010
+ L.DomEvent.on(
1011
+ controlEditDisable,
1012
+ 'mouseover',
1013
+ function () {
1014
+ this.ui.tooltip({
1015
+ content: this.help.displayLabel('PREVIEW'),
1016
+ anchor: controlEditDisable,
1017
+ position: 'bottom',
1018
+ delay: 500,
1019
+ duration: 5000,
1020
+ })
1021
+ },
1022
+ this
1023
+ )
1024
+ const controlEditSave = L.DomUtil.createButton(
1025
+ 'leaflet-control-edit-save button',
1026
+ rightContainer,
1027
+ L.DomUtil.add('span', '', null, L._('Save')),
1028
+ this.save,
1029
+ this
1030
+ )
1031
+ L.DomEvent.on(
1032
+ controlEditSave,
1033
+ 'mouseover',
1034
+ function () {
1035
+ this.ui.tooltip({
1036
+ content: this.help.displayLabel('SAVE'),
1037
+ anchor: controlEditSave,
1038
+ position: 'bottom',
1039
+ delay: 500,
1040
+ duration: 5000,
1041
+ })
1042
+ },
1043
+ this
1044
+ )
1045
+ },
1046
+ })
1047
+
1048
+ /* Used in view mode to define the current tilelayer */
1049
+ L.U.TileLayerControl = L.Control.IconLayers.extend({
1050
+ initialize: function (map, options) {
1051
+ this.map = map
1052
+ L.Control.IconLayers.prototype.initialize.call(this, {
1053
+ position: 'topleft',
1054
+ manageLayers: false,
1055
+ })
1056
+ this.on('activelayerchange', (e) => map.selectTileLayer(e.layer))
1057
+ },
1058
+
1059
+ setLayers: function (layers) {
1060
+ if (!layers) {
1061
+ layers = []
1062
+ this.map.eachTileLayer((layer) => {
1063
+ try {
1064
+ // We'd like to use layer.getTileUrl, but this method will only work
1065
+ // when the tilelayer is actually added to the map (needs this._tileZoom
1066
+ // to be defined)
1067
+ // Fixme when https://github.com/Leaflet/Leaflet/pull/9201 is released
1068
+ const icon = L.Util.template(layer.options.url_template, this.map.demoTileInfos)
1069
+ layers.push({
1070
+ title: layer.options.name,
1071
+ layer: layer,
1072
+ icon: icon,
1073
+ })
1074
+ } catch (e) {
1075
+ // Skip this tilelayer
1076
+ console.error(e)
1077
+ }
1078
+ })
1079
+ }
1080
+ const maxShown = 10
1081
+ L.Control.IconLayers.prototype.setLayers.call(this, layers.slice(0, maxShown))
1082
+ if (this.map.selected_tilelayer) this.setActiveLayer(this.map.selected_tilelayer)
1083
+ },
1084
+ })
1085
+
1086
+ /* Used in edit mode to define the default tilelayer */
1087
+ L.U.TileLayerChooser = L.Control.extend({
1088
+ options: {
1089
+ position: 'topleft',
1090
+ },
1091
+
1092
+ initialize: function (map, options) {
1093
+ this.map = map
1094
+ L.Control.prototype.initialize.call(this, options)
1095
+ },
1096
+
1097
+ onAdd: function () {
1098
+ const container = L.DomUtil.create('div', 'leaflet-control-tilelayers umap-control')
1099
+ const changeMapBackgroundButton = L.DomUtil.createButton(
1100
+ '',
1101
+ container,
1102
+ L._('Change map background'),
1103
+ this.openSwitcher,
1104
+ this
1105
+ )
1106
+ L.DomEvent.on(changeMapBackgroundButton, 'dblclick', L.DomEvent.stopPropagation)
1107
+ return container
1108
+ },
1109
+
1110
+ openSwitcher: function (options) {
1111
+ const container = L.DomUtil.create('div', 'umap-tilelayer-switcher-container')
1112
+ const title = L.DomUtil.create('h3', '', container)
1113
+ title.textContent = L._('Change tilelayers')
1114
+ this._tilelayers_container = L.DomUtil.create('ul', '', container)
1115
+ this.buildList(options)
1116
+ this.map.ui.openPanel({
1117
+ data: { html: container },
1118
+ className: options.className,
1119
+ })
1120
+ },
1121
+
1122
+ buildList: function (options) {
1123
+ this.map.eachTileLayer(function (tilelayer) {
1124
+ if (
1125
+ window.location.protocol === 'https:' &&
1126
+ tilelayer.options.url_template.indexOf('http:') === 0
1127
+ )
1128
+ return
1129
+ this.addTileLayerElement(tilelayer, options)
1130
+ }, this)
1131
+ },
1132
+
1133
+ addTileLayerElement: function (tilelayer, options) {
1134
+ const selectedClass = this.map.hasLayer(tilelayer) ? 'selected' : '',
1135
+ el = L.DomUtil.create('li', selectedClass, this._tilelayers_container),
1136
+ img = L.DomUtil.create('img', '', el),
1137
+ name = L.DomUtil.create('div', '', el)
1138
+ img.src = L.Util.template(tilelayer.options.url_template, this.map.demoTileInfos)
1139
+ img.loading = 'lazy'
1140
+ name.textContent = tilelayer.options.name
1141
+ L.DomEvent.on(
1142
+ el,
1143
+ 'click',
1144
+ function () {
1145
+ this.map.selectTileLayer(tilelayer)
1146
+ this.map._controls.tilelayers.setLayers()
1147
+ if (options && options.callback) options.callback(tilelayer)
1148
+ },
1149
+ this
1150
+ )
1151
+ },
1152
+ })
1153
+
1154
+ L.U.AttributionControl = L.Control.Attribution.extend({
1155
+ options: {
1156
+ prefix: '',
1157
+ },
1158
+
1159
+ _update: function () {
1160
+ // Layer is no more on the map
1161
+ if (!this._map) return
1162
+ L.Control.Attribution.prototype._update.call(this)
1163
+ // Use our own container, so we can hide/show on small screens
1164
+ const credits = this._container.innerHTML
1165
+ this._container.innerHTML = ''
1166
+ const container = L.DomUtil.add(
1167
+ 'div',
1168
+ 'attribution-container',
1169
+ this._container,
1170
+ credits
1171
+ )
1172
+ if (this._map.options.shortCredit) {
1173
+ L.DomUtil.add(
1174
+ 'span',
1175
+ '',
1176
+ container,
1177
+ ` — ${L.Util.toHTML(this._map.options.shortCredit)}`
1178
+ )
1179
+ }
1180
+ if (this._map.options.captionMenus) {
1181
+ const link = L.DomUtil.add('a', '', container, ` — ${L._('About')}`)
1182
+ L.DomEvent.on(link, 'click', L.DomEvent.stop)
1183
+ .on(link, 'click', this._map.displayCaption, this._map)
1184
+ .on(link, 'dblclick', L.DomEvent.stop)
1185
+ }
1186
+ if (window.top === window.self && this._map.options.captionMenus) {
1187
+ // We are not in iframe mode
1188
+ L.DomUtil.createLink('', container, ` — ${L._('Home')}`, '/')
1189
+ }
1190
+ if (this._map.options.captionMenus) {
1191
+ L.DomUtil.createLink(
1192
+ '',
1193
+ container,
1194
+ ` — ${L._('Powered by uMap')}`,
1195
+ 'https://github.com/umap-project/umap/'
1196
+ )
1197
+ }
1198
+ L.DomUtil.createLink('attribution-toggle', this._container, '')
1199
+ },
1200
+ })
1201
+
1202
+ L.U.StarControl = L.Control.extend({
1203
+ options: {
1204
+ position: 'topleft',
1205
+ },
1206
+
1207
+ onAdd: function (map) {
1208
+ const status = map.options.starred ? ' starred' : ''
1209
+ const container = L.DomUtil.create(
1210
+ 'div',
1211
+ `leaflet-control-star umap-control${status}`
1212
+ )
1213
+ const starMapButton = L.DomUtil.createButton(
1214
+ '',
1215
+ container,
1216
+ L._('Star this map'),
1217
+ map.star,
1218
+ map
1219
+ )
1220
+ L.DomEvent.on(starMapButton, 'dblclick', L.DomEvent.stopPropagation)
1221
+ return container
1222
+ },
1223
+ })
1224
+
1225
+ L.U.Search = L.PhotonSearch.extend({
1226
+ initialize: function (map, input, options) {
1227
+ this.options.placeholder = L._('Type a place name or coordinates')
1228
+ L.PhotonSearch.prototype.initialize.call(this, map, input, options)
1229
+ this.options.url = map.options.urls.search
1230
+ if (map.options.maxBounds) this.options.bbox = map.options.maxBounds.toBBoxString()
1231
+ this.reverse = new L.PhotonReverse({
1232
+ handleResults: (geojson) => {
1233
+ this.handleResultsWithReverse(geojson)
1234
+ },
1235
+ })
1236
+ },
1237
+
1238
+ handleResultsWithReverse: function (geojson) {
1239
+ const latlng = this.reverse.latlng
1240
+ geojson.features.unshift({
1241
+ type: 'Feature',
1242
+ geometry: { type: 'Point', coordinates: [latlng.lng, latlng.lat] },
1243
+ properties: {
1244
+ name: L._('Go to "{coords}"', { coords: `${latlng.lat} ${latlng.lng}` }),
1245
+ },
1246
+ })
1247
+
1248
+ this.handleResults(geojson)
1249
+ },
1250
+
1251
+ search: function () {
1252
+ const pattern = /^(?<lat>[-+]?\d{1,2}[.,]\d+)\s*[ ,]\s*(?<lng>[-+]?\d{1,3}[.,]\d+)$/
1253
+ if (pattern.test(this.input.value)) {
1254
+ this.hide()
1255
+ const { lat, lng } = pattern.exec(this.input.value).groups
1256
+ const latlng = L.latLng(lat, lng)
1257
+ if (latlng.isValid()) {
1258
+ this.reverse.doReverse(latlng)
1259
+ } else {
1260
+ this.map.ui.alert({ content: 'Invalid latitude or longitude', mode: 'error' })
1261
+ }
1262
+ return
1263
+ }
1264
+ // Only numbers, abort.
1265
+ if (/^[\d .,]*$/.test(this.input.value)) return
1266
+ // Do normal search
1267
+ L.PhotonSearch.prototype.search.call(this)
1268
+ },
1269
+
1270
+ onBlur: function (e) {
1271
+ // Overrided because we don't want to hide the results on blur.
1272
+ this.fire('blur')
1273
+ },
1274
+
1275
+ formatResult: function (feature, el) {
1276
+ const self = this
1277
+ const tools = L.DomUtil.create('span', 'search-result-tools', el),
1278
+ zoom = L.DomUtil.create('i', 'feature-zoom_to', tools),
1279
+ edit = L.DomUtil.create('i', 'feature-edit show-on-edit', tools)
1280
+ zoom.title = L._('Zoom to this place')
1281
+ edit.title = L._('Save this location as new feature')
1282
+ // We need to use "mousedown" because Leaflet.Photon listen to mousedown
1283
+ // on el.
1284
+ L.DomEvent.on(zoom, 'mousedown', (e) => {
1285
+ L.DomEvent.stop(e)
1286
+ self.zoomToFeature(feature)
1287
+ })
1288
+ L.DomEvent.on(edit, 'mousedown', (e) => {
1289
+ L.DomEvent.stop(e)
1290
+ const datalayer = self.map.defaultEditDataLayer()
1291
+ const layer = datalayer.geojsonToFeatures(feature)
1292
+ layer.isDirty = true
1293
+ layer.edit()
1294
+ })
1295
+ this._formatResult(feature, el)
1296
+ },
1297
+
1298
+ zoomToFeature: function (feature) {
1299
+ const zoom = Math.max(this.map.getZoom(), 16) // Never unzoom.
1300
+ this.map.setView(
1301
+ [feature.geometry.coordinates[1], feature.geometry.coordinates[0]],
1302
+ zoom
1303
+ )
1304
+ },
1305
+
1306
+ onSelected: function (feature) {
1307
+ this.zoomToFeature(feature)
1308
+ this.map.ui.closePanel()
1309
+ },
1310
+ })
1311
+
1312
+ L.U.SearchControl = L.Control.extend({
1313
+ options: {
1314
+ position: 'topleft',
1315
+ },
1316
+
1317
+ onAdd: function (map) {
1318
+ const container = L.DomUtil.create('div', 'leaflet-control-search umap-control')
1319
+ L.DomEvent.disableClickPropagation(container)
1320
+ L.DomUtil.createButton(
1321
+ '',
1322
+ container,
1323
+ L._('Search location'),
1324
+ (e) => {
1325
+ L.DomEvent.stop(e)
1326
+ this.openPanel(map)
1327
+ },
1328
+ this
1329
+ )
1330
+ return container
1331
+ },
1332
+
1333
+ openPanel: function (map) {
1334
+ const options = {
1335
+ limit: 10,
1336
+ noResultLabel: L._('No results'),
1337
+ }
1338
+ if (map.options.photonUrl) options.url = map.options.photonUrl
1339
+ const container = L.DomUtil.create('div', 'umap-search')
1340
+
1341
+ const title = L.DomUtil.create('h3', '', container)
1342
+ title.textContent = L._('Search location')
1343
+ const input = L.DomUtil.create('input', 'photon-input', container)
1344
+ const resultsContainer = L.DomUtil.create('div', 'photon-autocomplete', container)
1345
+ this.search = new L.U.Search(map, input, options)
1346
+ const id = Math.random()
1347
+ this.search.on('ajax:send', () => {
1348
+ map.fire('dataloading', { id: id })
1349
+ })
1350
+ this.search.on('ajax:return', () => {
1351
+ map.fire('dataload', { id: id })
1352
+ })
1353
+ this.search.resultsContainer = resultsContainer
1354
+ map.ui.once('panel:ready', () => {
1355
+ input.focus()
1356
+ })
1357
+ map.ui.openPanel({ data: { html: container } })
1358
+ },
1359
+ })
1360
+
1361
+ L.Control.MiniMap.include({
1362
+ initialize: function (layer, options) {
1363
+ L.Util.setOptions(this, options)
1364
+ this._layer = this._cloneLayer(layer)
1365
+ },
1366
+
1367
+ onMainMapBaseLayerChange: function (e) {
1368
+ const layer = this._cloneLayer(e.layer)
1369
+ if (this._miniMap.hasLayer(this._layer)) {
1370
+ this._miniMap.removeLayer(this._layer)
1371
+ }
1372
+ this._layer = layer
1373
+ this._miniMap.addLayer(this._layer)
1374
+ },
1375
+
1376
+ _cloneLayer: function (layer) {
1377
+ return new L.TileLayer(layer._url, L.Util.extend({}, layer.options))
1378
+ },
1379
+ })
1380
+
1381
+ L.Control.Loading.include({
1382
+ onAdd: function (map) {
1383
+ this._container = L.DomUtil.create('div', 'umap-loader', map._controlContainer)
1384
+ map.on('baselayerchange', this._layerAdd, this)
1385
+ this._addMapListeners(map)
1386
+ this._map = map
1387
+ },
1388
+
1389
+ _showIndicator: function () {
1390
+ L.DomUtil.addClass(this._map._container, 'umap-loading')
1391
+ },
1392
+
1393
+ _hideIndicator: function () {
1394
+ L.DomUtil.removeClass(this._map._container, 'umap-loading')
1395
+ },
1396
+ })
1397
+
1398
+ /*
1399
+ * Make it dynamic
1400
+ */
1401
+ L.U.ContextMenu = L.Map.ContextMenu.extend({
1402
+ _createItems: function (e) {
1403
+ this._map.setContextMenuItems(e)
1404
+ L.Map.ContextMenu.prototype._createItems.call(this)
1405
+ },
1406
+
1407
+ _showAtPoint: function (pt, e) {
1408
+ this._items = []
1409
+ this._container.innerHTML = ''
1410
+ this._createItems(e)
1411
+ L.Map.ContextMenu.prototype._showAtPoint.call(this, pt, e)
1412
+ },
1413
+ })
1414
+
1415
+ L.U.Editable = L.Editable.extend({
1416
+ initialize: function (map, options) {
1417
+ L.Editable.prototype.initialize.call(this, map, options)
1418
+ this.on(
1419
+ 'editable:drawing:start editable:drawing:click editable:drawing:move',
1420
+ this.drawingTooltip
1421
+ )
1422
+ this.on('editable:drawing:end', (e) => {
1423
+ this.closeTooltip()
1424
+ // Leaflet.Editable will delete the drawn shape if invalid
1425
+ // (eg. line has only one drawn point)
1426
+ // So let's check if the layer has no more shape
1427
+ if (!e.layer.hasGeom()) e.layer.del()
1428
+ })
1429
+ // Layer for items added by users
1430
+ this.on('editable:drawing:cancel', (e) => {
1431
+ if (e.layer instanceof L.U.Marker) e.layer.del()
1432
+ })
1433
+ this.on('editable:drawing:commit', function (e) {
1434
+ e.layer.isDirty = true
1435
+ if (this.map.editedFeature !== e.layer) e.layer.edit(e)
1436
+ })
1437
+ this.on('editable:editing', (e) => {
1438
+ const layer = e.layer
1439
+ layer.isDirty = true
1440
+ if (layer._tooltip && layer.isTooltipOpen()) {
1441
+ layer._tooltip.setLatLng(layer.getCenter())
1442
+ layer._tooltip.update()
1443
+ }
1444
+ })
1445
+ this.on('editable:vertex:ctrlclick', (e) => {
1446
+ const index = e.vertex.getIndex()
1447
+ if (index === 0 || (index === e.vertex.getLastIndex() && e.vertex.continue))
1448
+ e.vertex.continue()
1449
+ })
1450
+ this.on('editable:vertex:altclick', (e) => {
1451
+ if (e.vertex.editor.vertexCanBeDeleted(e.vertex)) e.vertex.delete()
1452
+ })
1453
+ this.on('editable:vertex:rawclick', this.onVertexRawClick)
1454
+ },
1455
+
1456
+ createPolyline: function (latlngs) {
1457
+ return new L.U.Polyline(this.map, latlngs, this._getDefaultProperties())
1458
+ },
1459
+
1460
+ createPolygon: function (latlngs) {
1461
+ return new L.U.Polygon(this.map, latlngs, this._getDefaultProperties())
1462
+ },
1463
+
1464
+ createMarker: function (latlng) {
1465
+ return new L.U.Marker(this.map, latlng, this._getDefaultProperties())
1466
+ },
1467
+
1468
+ _getDefaultProperties: function () {
1469
+ const result = {}
1470
+ if (this.map.options.featuresHaveOwner && this.map.options.hasOwnProperty('user')) {
1471
+ result.geojson = { properties: { owner: this.map.options.user.id } }
1472
+ }
1473
+ return result
1474
+ },
1475
+
1476
+ connectCreatedToMap: function (layer) {
1477
+ // Overrided from Leaflet.Editable
1478
+ const datalayer = this.map.defaultEditDataLayer()
1479
+ datalayer.addLayer(layer)
1480
+ layer.isDirty = true
1481
+ return layer
1482
+ },
1483
+
1484
+ drawingTooltip: function (e) {
1485
+ if (e.layer instanceof L.Marker && e.type == 'editable:drawing:start') {
1486
+ this.map.ui.tooltip({ content: L._('Click to add a marker') })
1487
+ }
1488
+ if (!(e.layer instanceof L.Polyline)) {
1489
+ // only continue with Polylines and Polygons
1490
+ return
1491
+ }
1492
+
1493
+ let content = L._('Drawing')
1494
+ let measure
1495
+ if (e.layer.editor._drawnLatLngs) {
1496
+ // when drawing (a Polyline or Polygon)
1497
+ if (!e.layer.editor._drawnLatLngs.length) {
1498
+ // when drawing first point
1499
+ if (e.layer instanceof L.Polygon) {
1500
+ content = L._('Click to start drawing a polygon')
1501
+ } else if (e.layer instanceof L.Polyline) {
1502
+ content = L._('Click to start drawing a line')
1503
+ }
1504
+ } else {
1505
+ const tmpLatLngs = e.layer.editor._drawnLatLngs.slice()
1506
+ tmpLatLngs.push(e.latlng)
1507
+ measure = e.layer.getMeasure(tmpLatLngs)
1508
+
1509
+ if (e.layer.editor._drawnLatLngs.length < e.layer.editor.MIN_VERTEX) {
1510
+ // when drawing second point
1511
+ content = L._('Click to continue drawing')
1512
+ } else {
1513
+ // when drawing third point (or more)
1514
+ content = L._('Click last point to finish shape')
1515
+ }
1516
+ }
1517
+ } else {
1518
+ // when moving an existing point
1519
+ measure = e.layer.getMeasure()
1520
+ }
1521
+ if (measure) {
1522
+ if (e.layer instanceof L.Polygon) {
1523
+ content += L._(' (area: {measure})', { measure: measure })
1524
+ } else if (e.layer instanceof L.Polyline) {
1525
+ content += L._(' (length: {measure})', { measure: measure })
1526
+ }
1527
+ }
1528
+ if (content) {
1529
+ this.map.ui.tooltip({ content: content })
1530
+ }
1531
+ },
1532
+
1533
+ closeTooltip: function () {
1534
+ this.map.ui.closeTooltip()
1535
+ },
1536
+
1537
+ onVertexRawClick: function (e) {
1538
+ e.layer.onVertexRawClick(e)
1539
+ L.DomEvent.stop(e)
1540
+ e.cancel()
1541
+ },
1542
+ })