umap-project 2.1.2__py3-none-any.whl → 2.2.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of umap-project might be problematic. Click here for more details.

Files changed (211) hide show
  1. umap/__init__.py +1 -1
  2. umap/context_processors.py +1 -0
  3. umap/locale/br/LC_MESSAGES/django.mo +0 -0
  4. umap/locale/en/LC_MESSAGES/django.po +32 -32
  5. umap/locale/hu/LC_MESSAGES/django.mo +0 -0
  6. umap/locale/it/LC_MESSAGES/django.mo +0 -0
  7. umap/locale/ms/LC_MESSAGES/django.mo +0 -0
  8. umap/migrations/0020_alter_tilelayer_url_template.py +19 -0
  9. umap/migrations/0021_remove_map_description.py +16 -0
  10. umap/models.py +10 -6
  11. umap/settings/base.py +1 -0
  12. umap/static/umap/base.css +43 -156
  13. umap/static/umap/content.css +7 -25
  14. umap/static/umap/css/icon.css +112 -0
  15. umap/static/umap/css/panel.css +140 -0
  16. umap/static/umap/img/16-white.svg +5 -1
  17. umap/static/umap/img/16.svg +7 -4
  18. umap/static/umap/img/24-white.svg +3 -1
  19. umap/static/umap/img/24.svg +3 -4
  20. umap/static/umap/img/source/16-white.svg +176 -940
  21. umap/static/umap/img/source/16.svg +8 -5
  22. umap/static/umap/img/source/24-white.svg +5 -3
  23. umap/static/umap/img/source/24.svg +6 -7
  24. umap/static/umap/js/modules/browser.js +97 -73
  25. umap/static/umap/js/modules/dompurify.js +12 -0
  26. umap/static/umap/js/modules/facets.js +149 -0
  27. umap/static/umap/js/modules/global.js +9 -1
  28. umap/static/umap/js/modules/i18n.js +7 -0
  29. umap/static/umap/js/modules/orderable.js +84 -0
  30. umap/static/umap/js/modules/panel.js +76 -0
  31. umap/static/umap/js/modules/request.js +0 -1
  32. umap/static/umap/js/modules/schema.js +324 -223
  33. umap/static/umap/js/modules/urls.js +1 -16
  34. umap/static/umap/js/modules/utils.js +340 -0
  35. umap/static/umap/js/umap.autocomplete.js +40 -25
  36. umap/static/umap/js/umap.controls.js +248 -361
  37. umap/static/umap/js/umap.core.js +77 -366
  38. umap/static/umap/js/umap.datalayer.permissions.js +1 -1
  39. umap/static/umap/js/umap.features.js +65 -43
  40. umap/static/umap/js/umap.forms.js +128 -36
  41. umap/static/umap/js/umap.icon.js +11 -4
  42. umap/static/umap/js/umap.importer.js +78 -58
  43. umap/static/umap/js/umap.js +206 -192
  44. umap/static/umap/js/umap.layer.js +86 -46
  45. umap/static/umap/js/umap.permissions.js +13 -9
  46. umap/static/umap/js/umap.popup.js +26 -30
  47. umap/static/umap/js/umap.share.js +12 -9
  48. umap/static/umap/js/umap.tableeditor.js +4 -6
  49. umap/static/umap/js/umap.ui.js +10 -60
  50. umap/static/umap/locale/am_ET.js +243 -227
  51. umap/static/umap/locale/am_ET.json +21 -9
  52. umap/static/umap/locale/ar.js +243 -227
  53. umap/static/umap/locale/ar.json +21 -9
  54. umap/static/umap/locale/ast.js +243 -227
  55. umap/static/umap/locale/ast.json +21 -9
  56. umap/static/umap/locale/bg.js +243 -227
  57. umap/static/umap/locale/bg.json +21 -9
  58. umap/static/umap/locale/br.js +253 -237
  59. umap/static/umap/locale/br.json +25 -13
  60. umap/static/umap/locale/ca.js +243 -227
  61. umap/static/umap/locale/ca.json +21 -9
  62. umap/static/umap/locale/cs_CZ.js +243 -227
  63. umap/static/umap/locale/cs_CZ.json +21 -9
  64. umap/static/umap/locale/da.js +243 -227
  65. umap/static/umap/locale/da.json +21 -9
  66. umap/static/umap/locale/de.js +243 -227
  67. umap/static/umap/locale/de.json +21 -9
  68. umap/static/umap/locale/el.js +243 -227
  69. umap/static/umap/locale/el.json +21 -9
  70. umap/static/umap/locale/en.js +243 -234
  71. umap/static/umap/locale/en.json +22 -10
  72. umap/static/umap/locale/en_US.json +21 -9
  73. umap/static/umap/locale/es.js +243 -227
  74. umap/static/umap/locale/es.json +21 -9
  75. umap/static/umap/locale/et.js +243 -227
  76. umap/static/umap/locale/et.json +21 -9
  77. umap/static/umap/locale/eu.js +227 -199
  78. umap/static/umap/locale/eu.json +1 -1
  79. umap/static/umap/locale/fa_IR.js +243 -227
  80. umap/static/umap/locale/fa_IR.json +21 -9
  81. umap/static/umap/locale/fi.js +243 -227
  82. umap/static/umap/locale/fi.json +21 -9
  83. umap/static/umap/locale/fr.js +243 -234
  84. umap/static/umap/locale/fr.json +21 -9
  85. umap/static/umap/locale/gl.js +243 -227
  86. umap/static/umap/locale/gl.json +21 -9
  87. umap/static/umap/locale/he.js +243 -227
  88. umap/static/umap/locale/he.json +21 -9
  89. umap/static/umap/locale/hr.js +243 -227
  90. umap/static/umap/locale/hr.json +21 -9
  91. umap/static/umap/locale/hu.js +243 -234
  92. umap/static/umap/locale/hu.json +21 -9
  93. umap/static/umap/locale/id.js +243 -227
  94. umap/static/umap/locale/id.json +21 -9
  95. umap/static/umap/locale/is.js +243 -227
  96. umap/static/umap/locale/is.json +21 -9
  97. umap/static/umap/locale/it.js +243 -234
  98. umap/static/umap/locale/it.json +21 -9
  99. umap/static/umap/locale/ja.js +243 -227
  100. umap/static/umap/locale/ja.json +21 -9
  101. umap/static/umap/locale/ko.js +243 -227
  102. umap/static/umap/locale/ko.json +21 -9
  103. umap/static/umap/locale/lt.js +243 -227
  104. umap/static/umap/locale/lt.json +21 -9
  105. umap/static/umap/locale/ms.js +243 -234
  106. umap/static/umap/locale/ms.json +22 -10
  107. umap/static/umap/locale/nl.js +246 -230
  108. umap/static/umap/locale/nl.json +21 -9
  109. umap/static/umap/locale/no.js +243 -227
  110. umap/static/umap/locale/no.json +21 -9
  111. umap/static/umap/locale/pl.js +243 -227
  112. umap/static/umap/locale/pl.json +21 -9
  113. umap/static/umap/locale/pl_PL.json +21 -9
  114. umap/static/umap/locale/pt.js +243 -227
  115. umap/static/umap/locale/pt.json +21 -9
  116. umap/static/umap/locale/pt_BR.js +243 -227
  117. umap/static/umap/locale/pt_BR.json +21 -9
  118. umap/static/umap/locale/pt_PT.js +243 -227
  119. umap/static/umap/locale/pt_PT.json +21 -9
  120. umap/static/umap/locale/ro.js +243 -227
  121. umap/static/umap/locale/ro.json +21 -9
  122. umap/static/umap/locale/ru.js +243 -227
  123. umap/static/umap/locale/ru.json +21 -9
  124. umap/static/umap/locale/si.js +1 -1
  125. umap/static/umap/locale/si.json +1 -1
  126. umap/static/umap/locale/sk_SK.js +243 -227
  127. umap/static/umap/locale/sk_SK.json +21 -9
  128. umap/static/umap/locale/sl.js +243 -227
  129. umap/static/umap/locale/sl.json +21 -9
  130. umap/static/umap/locale/sr.js +243 -227
  131. umap/static/umap/locale/sr.json +21 -9
  132. umap/static/umap/locale/sv.js +243 -227
  133. umap/static/umap/locale/sv.json +21 -9
  134. umap/static/umap/locale/th_TH.js +243 -227
  135. umap/static/umap/locale/th_TH.json +21 -9
  136. umap/static/umap/locale/tr.js +243 -227
  137. umap/static/umap/locale/tr.json +21 -9
  138. umap/static/umap/locale/uk_UA.js +243 -227
  139. umap/static/umap/locale/uk_UA.json +21 -9
  140. umap/static/umap/locale/vi.js +243 -227
  141. umap/static/umap/locale/vi.json +21 -9
  142. umap/static/umap/locale/vi_VN.json +21 -9
  143. umap/static/umap/locale/zh.js +243 -227
  144. umap/static/umap/locale/zh.json +21 -9
  145. umap/static/umap/locale/zh_CN.json +21 -9
  146. umap/static/umap/locale/zh_TW.Big5.json +21 -9
  147. umap/static/umap/locale/zh_TW.js +243 -234
  148. umap/static/umap/locale/zh_TW.json +21 -9
  149. umap/static/umap/map.css +124 -264
  150. umap/static/umap/test/DataLayer.js +1 -1
  151. umap/static/umap/test/Feature.js +0 -226
  152. umap/static/umap/test/Map.js +0 -304
  153. umap/static/umap/test/Polygon.js +0 -256
  154. umap/static/umap/test/Polyline.js +0 -116
  155. umap/static/umap/test/TableEditor.js +10 -10
  156. umap/static/umap/test/Util.js +0 -521
  157. umap/static/umap/test/index.html +1 -5
  158. umap/static/umap/unittests/URLs.js +1 -1
  159. umap/static/umap/unittests/utils.js +610 -0
  160. umap/static/umap/vars.css +9 -0
  161. umap/static/umap/vendors/dompurify/purify.es.mjs +1525 -0
  162. umap/static/umap/vendors/formbuilder/Leaflet.FormBuilder.js +1 -0
  163. umap/static/umap/vendors/iconlayers/iconLayers.js +1 -1
  164. umap/templates/umap/css.html +2 -0
  165. umap/templates/umap/js.html +0 -1
  166. umap/templates/umap/map_detail.html +4 -0
  167. umap/templates/umap/map_table.html +12 -10
  168. umap/templatetags/umap_tags.py +5 -0
  169. umap/tests/conftest.py +9 -0
  170. umap/tests/fixtures/test_upload_data.csv +2 -1
  171. umap/tests/fixtures/test_upload_data.umap +171 -0
  172. umap/tests/fixtures/test_upload_data_osm.json +33 -0
  173. umap/tests/integration/conftest.py +16 -0
  174. umap/tests/integration/test_anonymous_owned_map.py +30 -5
  175. umap/tests/integration/test_basics.py +21 -0
  176. umap/tests/integration/test_browser.py +16 -36
  177. umap/tests/integration/test_choropleth.py +89 -0
  178. umap/tests/integration/test_collaborative_editing.py +30 -1
  179. umap/tests/integration/test_dashboard.py +10 -0
  180. umap/tests/integration/test_datalayer.py +132 -0
  181. umap/tests/integration/test_draw_polygon.py +363 -0
  182. umap/tests/integration/test_draw_polyline.py +325 -0
  183. umap/tests/integration/test_edit_datalayer.py +145 -6
  184. umap/tests/integration/test_edit_map.py +202 -0
  185. umap/tests/integration/test_edit_marker.py +120 -0
  186. umap/tests/integration/test_edit_polygon.py +122 -0
  187. umap/tests/integration/test_facets_browser.py +132 -11
  188. umap/tests/integration/test_import.py +407 -10
  189. umap/tests/integration/test_map.py +36 -54
  190. umap/tests/integration/test_map_list.py +28 -0
  191. umap/tests/integration/test_owned_map.py +24 -6
  192. umap/tests/integration/test_picto.py +25 -38
  193. umap/tests/integration/test_querystring.py +9 -15
  194. umap/tests/integration/test_slideshow.py +0 -5
  195. umap/tests/integration/test_statics.py +3 -2
  196. umap/tests/integration/test_tableeditor.py +23 -0
  197. umap/tests/integration/test_tilelayer.py +10 -0
  198. umap/tests/integration/test_view_marker.py +64 -0
  199. umap/tests/integration/test_view_polygon.py +59 -0
  200. umap/tests/integration/test_view_polyline.py +51 -0
  201. umap/tests/test_map_views.py +13 -0
  202. {umap_project-2.1.2.dist-info → umap_project-2.2.0.dist-info}/METADATA +12 -12
  203. {umap_project-2.1.2.dist-info → umap_project-2.2.0.dist-info}/RECORD +206 -187
  204. {umap_project-2.1.2.dist-info → umap_project-2.2.0.dist-info}/WHEEL +1 -1
  205. umap/static/umap/test/Choropleth.js +0 -245
  206. umap/static/umap/test/Permissions.js +0 -74
  207. umap/static/umap/vendors/dompurify/purify.min.js +0 -3
  208. umap/static/umap/vendors/dompurify/purify.min.js.map +0 -1
  209. umap/tests/integration/test_drawing.py +0 -243
  210. {umap_project-2.1.2.dist-info → umap_project-2.2.0.dist-info}/entry_points.txt +0 -0
  211. {umap_project-2.1.2.dist-info → umap_project-2.2.0.dist-info}/licenses/LICENSE +0 -0
@@ -1,7 +1,31 @@
1
- /*
2
- * Utils
3
- */
4
- L.Util.queryString = (name, fallback) => {
1
+ L.Util.copyToClipboard = function (textToCopy) {
2
+ // https://stackoverflow.com/a/65996386
3
+ // Navigator clipboard api needs a secure context (https)
4
+ if (navigator.clipboard && window.isSecureContext) {
5
+ navigator.clipboard.writeText(textToCopy)
6
+ } else {
7
+ // Use the 'out of viewport hidden text area' trick
8
+ const textArea = document.createElement('textarea')
9
+ textArea.value = textToCopy
10
+
11
+ // Move textarea out of the viewport so it's not visible
12
+ textArea.style.position = 'absolute'
13
+ textArea.style.left = '-999999px'
14
+
15
+ document.body.prepend(textArea)
16
+ textArea.select()
17
+
18
+ try {
19
+ document.execCommand('copy')
20
+ } catch (error) {
21
+ console.error(error)
22
+ } finally {
23
+ textArea.remove()
24
+ }
25
+ }
26
+ }
27
+
28
+ L.Util.queryString = function (name, fallback) {
5
29
  const decode = (s) => decodeURIComponent(s.replace(/\+/g, ' '))
6
30
  const qs = window.location.search.slice(1).split('&'),
7
31
  qa = {}
@@ -13,25 +37,27 @@ L.Util.queryString = (name, fallback) => {
13
37
  return qa[name] || fallback
14
38
  }
15
39
 
16
- L.Util.booleanFromQueryString = (name) => {
40
+ L.Util.booleanFromQueryString = function (name) {
17
41
  const value = L.Util.queryString(name)
18
42
  return value === '1' || value === 'true'
19
43
  }
20
44
 
21
- L.Util.setFromQueryString = (options, name) => {
45
+ L.Util.setFromQueryString = function (options, name) {
22
46
  const value = L.Util.queryString(name)
23
47
  if (typeof value !== 'undefined') options[name] = value
24
48
  }
25
49
 
26
- L.Util.setBooleanFromQueryString = (options, name) => {
50
+ L.Util.setBooleanFromQueryString = function (options, name) {
27
51
  const value = L.Util.queryString(name)
28
52
  if (typeof value !== 'undefined') options[name] = value == '1' || value == 'true'
29
53
  }
30
- L.Util.setNumberFromQueryString = (options, name) => {
54
+
55
+ L.Util.setNumberFromQueryString = function (options, name) {
31
56
  const value = +L.Util.queryString(name)
32
57
  if (!isNaN(value)) options[name] = value
33
58
  }
34
- L.Util.setNullableBooleanFromQueryString = (options, name) => {
59
+
60
+ L.Util.setNullableBooleanFromQueryString = function (options, name) {
35
61
  let value = L.Util.queryString(name)
36
62
  if (typeof value !== 'undefined') {
37
63
  if (value === 'null') value = null
@@ -40,271 +66,6 @@ L.Util.setNullableBooleanFromQueryString = (options, name) => {
40
66
  options[name] = value
41
67
  }
42
68
  }
43
- L.Util.escapeHTML = (s) => {
44
- s = s ? s.toString() : ''
45
- s = DOMPurify.sanitize(s, {
46
- USE_PROFILES: { html: true },
47
- ADD_TAGS: ['iframe'],
48
- ALLOWED_TAGS: [
49
- 'h3',
50
- 'h4',
51
- 'h5',
52
- 'hr',
53
- 'strong',
54
- 'em',
55
- 'ul',
56
- 'li',
57
- 'a',
58
- 'div',
59
- 'iframe',
60
- 'img',
61
- 'br',
62
- ],
63
- ADD_ATTR: ['target', 'allow', 'allowfullscreen', 'frameborder', 'scrolling'],
64
- ALLOWED_ATTR: ['href', 'src', 'width', 'height'],
65
- // Added: `geo:` URL scheme as defined in RFC5870:
66
- // https://www.rfc-editor.org/rfc/rfc5870.html
67
- // The base RegExp comes from:
68
- // https://github.com/cure53/DOMPurify/blob/main/src/regexp.js#L10
69
- ALLOWED_URI_REGEXP:
70
- /^(?:(?:(?:f|ht)tps?|mailto|tel|callto|sms|cid|xmpp|geo):|[^a-z]|[a-z+.\-]+(?:[^a-z+.\-:]|$))/i,
71
- })
72
- return s
73
- }
74
- L.Util.toHTML = (r, options) => {
75
- if (!r) return ''
76
- const target = (options && options.target) || 'blank'
77
- let ii
78
-
79
- // detect newline format
80
- const newline = r.indexOf('\r\n') != -1 ? '\r\n' : r.indexOf('\n') != -1 ? '\n' : ''
81
-
82
- // headings and hr
83
- r = r.replace(/^### (.*)/gm, '<h5>$1</h5>')
84
- r = r.replace(/^## (.*)/gm, '<h4>$1</h4>')
85
- r = r.replace(/^# (.*)/gm, '<h3>$1</h3>')
86
- r = r.replace(/^---/gm, '<hr>')
87
-
88
- // bold, italics
89
- r = r.replace(/\*\*(.*?)\*\*/g, '<strong>$1</strong>')
90
- r = r.replace(/\*(.*?)\*/g, '<em>$1</em>')
91
-
92
- // unordered lists
93
- r = r.replace(/^\*\* (.*)/gm, '<ul><ul><li>$1</li></ul></ul>')
94
- r = r.replace(/^\* (.*)/gm, '<ul><li>$1</li></ul>')
95
- for (ii = 0; ii < 3; ii++)
96
- r = r.replace(new RegExp(`</ul>${newline}<ul>`, 'g'), newline)
97
-
98
- // links
99
- r = r.replace(/(\[\[http)/g, '[[h_t_t_p') // Escape for avoiding clash between [[http://xxx]] and http://xxx
100
- r = r.replace(/({{http)/g, '{{h_t_t_p')
101
- r = r.replace(/(=http)/g, '=h_t_t_p') // http://xxx as query string, see https://github.com/umap-project/umap/issues/607
102
- r = r.replace(/(https?:[^ \<)\n]*)/g, `<a target="_${target}" href="$1">$1</a>`)
103
- r = r.replace(
104
- /\[\[(h_t_t_ps?:[^\]|]*?)\]\]/g,
105
- `<a target="_${target}" href="$1">$1</a>`
106
- )
107
- r = r.replace(
108
- /\[\[(h_t_t_ps?:[^|]*?)\|(.*?)\]\]/g,
109
- `<a target="_${target}" href="$1">$2</a>`
110
- )
111
- r = r.replace(/\[\[([^\]|]*?)\]\]/g, `<a target="_${target}" href="$1">$1</a>`)
112
- r = r.replace(/\[\[([^|]*?)\|(.*?)\]\]/g, `<a target="_${target}" href="$1">$2</a>`)
113
-
114
- // iframe
115
- r = r.replace(
116
- /{{{(h_t_t_ps?[^ |{]*)}}}/g,
117
- '<div><iframe frameborder="0" src="$1" width="100%" height="300px"></iframe></div>'
118
- )
119
- r = r.replace(
120
- /{{{(h_t_t_ps?[^ |{]*)\|(\d*)(px)?}}}/g,
121
- '<div><iframe frameborder="0" src="$1" width="100%" height="$2px"></iframe></div>'
122
- )
123
- r = r.replace(
124
- /{{{(h_t_t_ps?[^ |{]*)\|(\d*)(px)?\*(\d*)(px)?}}}/g,
125
- '<div><iframe frameborder="0" src="$1" width="$4px" height="$2px"></iframe></div>'
126
- )
127
-
128
- // images
129
- r = r.replace(/{{([^\]|]*?)}}/g, '<img src="$1">')
130
- r = r.replace(
131
- /{{([^|]*?)\|(\d*?)(px)?}}/g,
132
- '<img src="$1" style="width:$2px;min-width:$2px;">'
133
- )
134
-
135
- //Unescape http
136
- r = r.replace(/(h_t_t_p)/g, 'http')
137
-
138
- // Preserver line breaks
139
- if (newline) r = r.replace(new RegExp(`${newline}(?=[^]+)`, 'g'), `<br>${newline}`)
140
-
141
- r = L.Util.escapeHTML(r)
142
-
143
- return r
144
- }
145
- L.Util.isObject = (what) => typeof what === 'object' && what !== null
146
- L.Util.CopyJSON = (geojson) => JSON.parse(JSON.stringify(geojson))
147
- L.Util.detectFileType = (f) => {
148
- const filename = f.name ? escape(f.name.toLowerCase()) : ''
149
- function ext(_) {
150
- return filename.indexOf(_) !== -1
151
- }
152
- if (f.type === 'application/vnd.google-earth.kml+xml' || ext('.kml')) {
153
- return 'kml'
154
- }
155
- if (ext('.gpx')) return 'gpx'
156
- if (ext('.geojson') || ext('.json')) return 'geojson'
157
- if (f.type === 'text/csv' || ext('.csv') || ext('.tsv') || ext('.dsv')) {
158
- return 'csv'
159
- }
160
- if (ext('.xml') || ext('.osm')) return 'osm'
161
- if (ext('.umap')) return 'umap'
162
- }
163
-
164
- L.Util.usableOption = (options, option) =>
165
- options[option] !== undefined && options[option] !== ''
166
-
167
- L.Util.greedyTemplate = (str, data, ignore) => {
168
- function getValue(data, path) {
169
- let value = data
170
- for (let i = 0; i < path.length; i++) {
171
- value = value[path[i]]
172
- if (value === undefined) break
173
- }
174
- return value
175
- }
176
-
177
- if (typeof str !== 'string') return ''
178
-
179
- return str.replace(
180
- /\{ *([^\{\}/\-]+)(?:\|("[^"]*"))? *\}/g,
181
- (str, key, staticFallback) => {
182
- const vars = key.split('|')
183
- let value
184
- let path
185
- if (staticFallback !== undefined) {
186
- vars.push(staticFallback)
187
- }
188
- for (let i = 0; i < vars.length; i++) {
189
- path = vars[i]
190
- if (path.startsWith('"') && path.endsWith('"'))
191
- value = path.substring(1, path.length - 1) // static default value.
192
- else value = getValue(data, path.split('.'))
193
- if (value !== undefined) break
194
- }
195
- if (value === undefined) {
196
- if (ignore) value = str
197
- else value = ''
198
- }
199
- return value
200
- }
201
- )
202
- }
203
-
204
- L.Util.naturalSort = (a, b) => {
205
- return a
206
- .toString()
207
- .toLowerCase()
208
- .localeCompare(b.toString().toLowerCase(), L.lang || 'en', {
209
- sensitivity: 'base',
210
- numeric: true,
211
- })
212
- }
213
-
214
- L.Util.sortFeatures = (features, sortKey) => {
215
- const sortKeys = (sortKey || 'name').split(',')
216
-
217
- const sort = (a, b, i) => {
218
- let sortKey = sortKeys[i],
219
- reverse = 1
220
- if (sortKey[0] === '-') {
221
- reverse = -1
222
- sortKey = sortKey.substring(1)
223
- }
224
- let score
225
- const valA = a.properties[sortKey] || ''
226
- const valB = b.properties[sortKey] || ''
227
- if (!valA) score = -1
228
- else if (!valB) score = 1
229
- else score = L.Util.naturalSort(valA, valB)
230
- if (score === 0 && sortKeys[i + 1]) return sort(a, b, i + 1)
231
- return score * reverse
232
- }
233
-
234
- features.sort((a, b) => {
235
- if (!a.properties || !b.properties) {
236
- return 0
237
- }
238
- return sort(a, b, 0)
239
- })
240
-
241
- return features
242
- }
243
-
244
- L.Util.flattenCoordinates = (coords) => {
245
- while (coords[0] && typeof coords[0][0] !== 'number') coords = coords[0]
246
- return coords
247
- }
248
-
249
- L.Util.buildQueryString = (params) => {
250
- const query_string = []
251
- for (const key in params) {
252
- query_string.push(`${encodeURIComponent(key)}=${encodeURIComponent(params[key])}`)
253
- }
254
- return query_string.join('&')
255
- }
256
-
257
- L.Util.getBaseUrl = () => `//${window.location.host}${window.location.pathname}`
258
-
259
- L.Util.hasVar = (value) => {
260
- return typeof value === 'string' && value.indexOf('{') != -1
261
- }
262
-
263
- L.Util.isPath = function (value) {
264
- return value && value.length && value.startsWith('/')
265
- }
266
-
267
- L.Util.isRemoteUrl = function (value) {
268
- return value && value.length && value.startsWith('http')
269
- }
270
-
271
- L.Util.isDataImage = function (value) {
272
- return value && value.length && value.startsWith('data:image')
273
- }
274
-
275
- L.Util.copyToClipboard = function (textToCopy) {
276
- // https://stackoverflow.com/a/65996386
277
- // Navigator clipboard api needs a secure context (https)
278
- if (navigator.clipboard && window.isSecureContext) {
279
- navigator.clipboard.writeText(textToCopy)
280
- } else {
281
- // Use the 'out of viewport hidden text area' trick
282
- const textArea = document.createElement('textarea')
283
- textArea.value = textToCopy
284
-
285
- // Move textarea out of the viewport so it's not visible
286
- textArea.style.position = 'absolute'
287
- textArea.style.left = '-999999px'
288
-
289
- document.body.prepend(textArea)
290
- textArea.select()
291
-
292
- try {
293
- document.execCommand('copy')
294
- } catch (error) {
295
- console.error(error)
296
- } finally {
297
- textArea.remove()
298
- }
299
- }
300
- }
301
-
302
- L.Util.normalize = function (s) {
303
- return (s || '')
304
- .toLowerCase()
305
- .normalize('NFD')
306
- .replace(/[\u0300-\u036f]/g, '')
307
- }
308
69
 
309
70
  L.DomUtil.add = (tagName, className, container, content) => {
310
71
  const el = L.DomUtil.create(tagName, className, container)
@@ -312,7 +73,7 @@ L.DomUtil.add = (tagName, className, container, content) => {
312
73
  if (content.nodeType && content.nodeType === 1) {
313
74
  el.appendChild(content)
314
75
  } else {
315
- el.innerHTML = content
76
+ el.textContent = content
316
77
  }
317
78
  }
318
79
  return el
@@ -356,6 +117,31 @@ L.DomUtil.createLink = (className, container, content, url, target, title) => {
356
117
  return el
357
118
  }
358
119
 
120
+ L.DomUtil.createIcon = (parent, className, title, size = 16) => {
121
+ return L.DomUtil.element({
122
+ tagName: 'i',
123
+ parent: parent,
124
+ className: `icon icon-${size} ${className}`,
125
+ title: title || '',
126
+ })
127
+ }
128
+
129
+ L.DomUtil.createButtonIcon = (parent, className, title, size = 16) => {
130
+ return L.DomUtil.element({
131
+ tagName: 'button',
132
+ parent: parent,
133
+ className: `icon icon-${size} ${className}`,
134
+ title: title || '',
135
+ })
136
+ }
137
+
138
+ L.DomUtil.createTitle = (parent, text, className, tag = 'h3') => {
139
+ const title = L.DomUtil.create(tag, '', parent)
140
+ L.DomUtil.createIcon(title, className)
141
+ L.DomUtil.add('span', '', title, text)
142
+ return title
143
+ }
144
+
359
145
  L.DomUtil.createCopiableInput = (parent, label, value) => {
360
146
  const wrapper = L.DomUtil.add('div', 'copiable-input', parent)
361
147
  const labelEl = L.DomUtil.add('label', '', wrapper, label)
@@ -379,8 +165,13 @@ L.DomUtil.classIf = (el, className, bool) => {
379
165
  else L.DomUtil.removeClass(el, className)
380
166
  }
381
167
 
382
- L.DomUtil.element = (what, attrs, parent) => {
383
- const el = document.createElement(what)
168
+ L.DomUtil.element = ({ tagName, parent, ...attrs }) => {
169
+ const el = document.createElement(tagName)
170
+ if (attrs.innerHTML) {
171
+ attrs.innerHTML = U.Utils.escapeHTML(attrs.innerHTML)
172
+ } else if (attrs.safeHTML) {
173
+ attrs.innerHTML = attrs.safeHTML
174
+ }
384
175
  for (const attr in attrs) {
385
176
  el[attr] = attrs[attr]
386
177
  }
@@ -530,6 +321,14 @@ U.Help = L.Class.extend({
530
321
  shortcut: 'Modifier+S',
531
322
  label: L._('Save current edits'),
532
323
  },
324
+ EDIT_FEATURE_LAYER: {
325
+ shortcut: 'Modifier+⇧+Click',
326
+ label: L._("Edit feature's layer"),
327
+ },
328
+ CONTINUE_LINE: {
329
+ shortcut: 'Modifier+Click',
330
+ label: L._('Continue line'),
331
+ },
533
332
  },
534
333
 
535
334
  displayLabel: function (action, withKbdTag = true) {
@@ -757,7 +556,7 @@ U.Help = L.Class.extend({
757
556
  slugKey: L._('The name of the property to use as feature unique identifier.'),
758
557
  filterKey: L._('Comma separated list of properties to use when filtering features'),
759
558
  facetKey: L._(
760
- 'Comma separated list of properties to use for facet search (eg.: mykey,otherkey). To control label, add it after a | (eg.: mykey|My Key,otherkey|Other Key)'
559
+ 'Comma separated list of properties to use for facet search (eg.: mykey,otherkey). To control label, add it after a | (eg.: mykey|My Key,otherkey|Other Key). To control input field type, add it after another | (eg.: mykey|My Key|checkbox,otherkey|Other Key|datetime). Allowed values for the input field type are checkbox (default), radio, number, date and datetime.'
761
560
  ),
762
561
  interactive: L._(
763
562
  'If false, the polygon or line will act as a part of the underlying map.'
@@ -770,94 +569,6 @@ U.Help = L.Class.extend({
770
569
  ),
771
570
  })
772
571
 
773
- U.Orderable = L.Evented.extend({
774
- options: {
775
- selector: 'li',
776
- color: '#374E75',
777
- },
778
-
779
- initialize: function (parent, options) {
780
- L.Util.setOptions(this, options)
781
- this.parent = parent
782
- this.src = null
783
- this.dst = null
784
- this.els = this.parent.querySelectorAll(this.options.selector)
785
- for (let i = 0; i < this.els.length; i++) this.makeDraggable(this.els[i])
786
- },
787
-
788
- makeDraggable: function (node) {
789
- node.draggable = true
790
- L.DomEvent.on(node, 'dragstart', this.onDragStart, this)
791
- L.DomEvent.on(node, 'dragenter', this.onDragEnter, this)
792
- L.DomEvent.on(node, 'dragover', this.onDragOver, this)
793
- L.DomEvent.on(node, 'dragleave', this.onDragLeave, this)
794
- L.DomEvent.on(node, 'drop', this.onDrop, this)
795
- L.DomEvent.on(node, 'dragend', this.onDragEnd, this)
796
- },
797
-
798
- nodeIndex: function (node) {
799
- return Array.prototype.indexOf.call(this.parent.children, node)
800
- },
801
-
802
- findTarget: function (node) {
803
- while (node) {
804
- if (this.nodeIndex(node) !== -1) return node
805
- node = node.parentNode
806
- }
807
- },
808
-
809
- onDragStart: function (e) {
810
- // e.target is the source node.
811
- this.src = e.target
812
- this.initialIndex = this.nodeIndex(this.src)
813
- this.srcBackgroundColor = this.src.style.backgroundColor
814
- this.src.style.backgroundColor = this.options.color
815
- e.dataTransfer.effectAllowed = 'move'
816
- e.dataTransfer.setData('text/html', this.src.innerHTML)
817
- },
818
-
819
- onDragOver: function (e) {
820
- L.DomEvent.stop(e)
821
- if (e.preventDefault) e.preventDefault() // Necessary. Allows us to drop.
822
- e.dataTransfer.dropEffect = 'move'
823
- return false
824
- },
825
-
826
- onDragEnter: function (e) {
827
- L.DomEvent.stop(e)
828
- // e.target is the current hover target.
829
- const dst = this.findTarget(e.target)
830
- if (!dst || dst === this.src) return
831
- this.dst = dst
832
- const targetIndex = this.nodeIndex(this.dst),
833
- srcIndex = this.nodeIndex(this.src)
834
- if (targetIndex > srcIndex) this.parent.insertBefore(this.dst, this.src)
835
- else this.parent.insertBefore(this.src, this.dst)
836
- },
837
-
838
- onDragLeave: function (e) {
839
- // e.target is previous target element.
840
- },
841
-
842
- onDrop: function (e) {
843
- // e.target is current target element.
844
- if (e.stopPropagation) e.stopPropagation() // Stops the browser from redirecting.
845
- if (!this.dst) return
846
- this.fire('drop', {
847
- src: this.src,
848
- initialIndex: this.initialIndex,
849
- finalIndex: this.nodeIndex(this.src),
850
- dst: this.dst,
851
- })
852
- return false
853
- },
854
-
855
- onDragEnd: function (e) {
856
- // e.target is the source node.
857
- this.src.style.backgroundColor = this.srcBackgroundColor
858
- },
859
- })
860
-
861
572
  L.LatLng.prototype.isValid = function () {
862
573
  return (
863
574
  isFinite(this.lat) &&
@@ -46,7 +46,7 @@ U.DataLayerPermissions = L.Class.extend({
46
46
  },
47
47
 
48
48
  getUrl: function () {
49
- return L.Util.template(this.datalayer.map.options.urls.datalayer_permissions, {
49
+ return U.Utils.template(this.datalayer.map.options.urls.datalayer_permissions, {
50
50
  map_id: this.datalayer.map.options.umap_id,
51
51
  pk: this.datalayer.umap_id,
52
52
  })