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,19 +1,4 @@
1
- // Vendorized from leaflet.utils
2
- // https://github.com/Leaflet/Leaflet/blob/108c6717b70f57c63645498f9bd66b6677758786/src/core/Util.js#L132-L151
3
- var templateRe = /\{ *([\w_ -]+) *\}/g
4
-
5
- function template(str, data) {
6
- return str.replace(templateRe, function (str, key) {
7
- var value = data[key]
8
-
9
- if (value === undefined) {
10
- throw new Error('No value provided for variable ' + str)
11
- } else if (typeof value === 'function') {
12
- value = value(data)
13
- }
14
- return value
15
- })
16
- }
1
+ import { template } from "./utils.js"
17
2
 
18
3
  export default class URLs {
19
4
  constructor(serverUrls) {
@@ -1,3 +1,5 @@
1
+ import { default as DOMPurifyInitializer } from '../../vendors/dompurify/purify.es.mjs'
2
+
1
3
  /**
2
4
  * Generate a pseudo-unique identifier (5 chars long, mixed-case alphanumeric)
3
5
  *
@@ -22,3 +24,341 @@ export function checkId(string) {
22
24
  if (typeof string !== 'string') return false
23
25
  return /^[A-Za-z0-9]{5}$/.test(string)
24
26
  }
27
+
28
+ /**
29
+ * Compute the impacts for a given list of fields.
30
+ *
31
+ * Return an array of unique impacts.
32
+ *
33
+ * @param {fields} list[fields]
34
+ * @returns Array[string]
35
+ */
36
+ export function getImpactsFromSchema(fields, schema) {
37
+ schema = schema || U.SCHEMA
38
+ let impacted = fields
39
+ .map((field) => {
40
+ // remove the option prefix for fields
41
+ // And only keep the first part in case of a subfield
42
+ // (e.g "options.limitBounds.foobar" will just return "limitBounds")
43
+ return field.replace('options.', '').split('.')[0]
44
+ })
45
+ .reduce((acc, field) => {
46
+ // retrieve the "impacts" field from the schema
47
+ // and merge them together using sets
48
+ const impacts = schema[field]?.impacts || []
49
+ impacts.forEach((impact) => acc.add(impact))
50
+ return acc
51
+ }, new Set())
52
+
53
+ return Array.from(impacted)
54
+ }
55
+
56
+ /**
57
+ * Import DOM purify, and initialize it.
58
+ *
59
+ * If the context is a node server, uses jsdom to provide
60
+ * DOM APIs
61
+ */
62
+ export default function getPurify() {
63
+ if (typeof window === 'undefined') {
64
+ return DOMPurifyInitializer(new global.JSDOM('').window)
65
+ } else {
66
+ return DOMPurifyInitializer(window)
67
+ }
68
+ }
69
+
70
+ export function escapeHTML(s) {
71
+ s = s ? s.toString() : ''
72
+ s = getPurify().sanitize(s, {
73
+ ADD_TAGS: ['iframe'],
74
+ ALLOWED_TAGS: [
75
+ 'h3',
76
+ 'h4',
77
+ 'h5',
78
+ 'hr',
79
+ 'strong',
80
+ 'em',
81
+ 'ul',
82
+ 'li',
83
+ 'a',
84
+ 'div',
85
+ 'iframe',
86
+ 'img',
87
+ 'br',
88
+ 'span',
89
+ ],
90
+ ADD_ATTR: ['target', 'allow', 'allowfullscreen', 'frameborder', 'scrolling'],
91
+ ALLOWED_ATTR: ['href', 'src', 'width', 'height', 'style'],
92
+ // Added: `geo:` URL scheme as defined in RFC5870:
93
+ // https://www.rfc-editor.org/rfc/rfc5870.html
94
+ // The base RegExp comes from:
95
+ // https://github.com/cure53/DOMPurify/blob/main/src/regexp.js#L10
96
+ ALLOWED_URI_REGEXP:
97
+ /^(?:(?:(?:f|ht)tps?|mailto|tel|callto|sms|cid|xmpp|geo):|[^a-z]|[a-z+.\-]+(?:[^a-z+.\-:]|$))/i,
98
+ })
99
+ return s
100
+ }
101
+
102
+ export function toHTML(r, options) {
103
+ if (!r) return ''
104
+ const target = (options && options.target) || 'blank'
105
+ let ii
106
+
107
+ // detect newline format
108
+ const newline = r.indexOf('\r\n') != -1 ? '\r\n' : r.indexOf('\n') != -1 ? '\n' : ''
109
+
110
+ // headings and hr
111
+ r = r.replace(/^### (.*)/gm, '<h5>$1</h5>')
112
+ r = r.replace(/^## (.*)/gm, '<h4>$1</h4>')
113
+ r = r.replace(/^# (.*)/gm, '<h3>$1</h3>')
114
+ r = r.replace(/^---/gm, '<hr>')
115
+
116
+ // bold, italics
117
+ r = r.replace(/\*\*(.*?)\*\*/g, '<strong>$1</strong>')
118
+ r = r.replace(/\*(.*?)\*/g, '<em>$1</em>')
119
+
120
+ // unordered lists
121
+ r = r.replace(/^\*\* (.*)/gm, '<ul><ul><li>$1</li></ul></ul>')
122
+ r = r.replace(/^\* (.*)/gm, '<ul><li>$1</li></ul>')
123
+ for (ii = 0; ii < 3; ii++)
124
+ r = r.replace(new RegExp(`</ul>${newline}<ul>`, 'g'), newline)
125
+
126
+ // links
127
+ r = r.replace(/(\[\[http)/g, '[[h_t_t_p') // Escape for avoiding clash between [[http://xxx]] and http://xxx
128
+ r = r.replace(/({{http)/g, '{{h_t_t_p')
129
+ r = r.replace(/(=http)/g, '=h_t_t_p') // http://xxx as query string, see https://github.com/umap-project/umap/issues/607
130
+ r = r.replace(/(https?:[^ \<)\n]*)/g, `<a target="_${target}" href="$1">$1</a>`)
131
+ r = r.replace(
132
+ /\[\[(h_t_t_ps?:[^\]|]*?)\]\]/g,
133
+ `<a target="_${target}" href="$1">$1</a>`
134
+ )
135
+ r = r.replace(
136
+ /\[\[(h_t_t_ps?:[^|]*?)\|(.*?)\]\]/g,
137
+ `<a target="_${target}" href="$1">$2</a>`
138
+ )
139
+ r = r.replace(/\[\[([^\]|]*?)\]\]/g, `<a target="_${target}" href="$1">$1</a>`)
140
+ r = r.replace(/\[\[([^|]*?)\|(.*?)\]\]/g, `<a target="_${target}" href="$1">$2</a>`)
141
+
142
+ // iframe
143
+ r = r.replace(
144
+ /{{{(h_t_t_ps?[^ |{]*)}}}/g,
145
+ '<div><iframe frameborder="0" src="$1" width="100%" height="300px"></iframe></div>'
146
+ )
147
+ r = r.replace(
148
+ /{{{(h_t_t_ps?[^ |{]*)\|(\d*)(px)?}}}/g,
149
+ '<div><iframe frameborder="0" src="$1" width="100%" height="$2px"></iframe></div>'
150
+ )
151
+ r = r.replace(
152
+ /{{{(h_t_t_ps?[^ |{]*)\|(\d*)(px)?\*(\d*)(px)?}}}/g,
153
+ '<div><iframe frameborder="0" src="$1" width="$4px" height="$2px"></iframe></div>'
154
+ )
155
+
156
+ // images
157
+ r = r.replace(/{{([^\]|]*?)}}/g, '<img src="$1">')
158
+ r = r.replace(
159
+ /{{([^|]*?)\|(\d*?)(px)?}}/g,
160
+ '<img src="$1" style="width:$2px;min-width:$2px;">'
161
+ )
162
+
163
+ //Unescape http
164
+ r = r.replace(/(h_t_t_p)/g, 'http')
165
+
166
+ // Preserver line breaks
167
+ if (newline) r = r.replace(new RegExp(`${newline}(?=[^]+)`, 'g'), `<br>${newline}`)
168
+
169
+ r = escapeHTML(r)
170
+
171
+ return r
172
+ }
173
+
174
+ export function isObject(what) {
175
+ return typeof what === 'object' && what !== null
176
+ }
177
+
178
+ export function CopyJSON(geojson) {
179
+ return JSON.parse(JSON.stringify(geojson))
180
+ }
181
+
182
+ export function detectFileType(f) {
183
+ const filename = f.name ? escape(f.name.toLowerCase()) : ''
184
+ function ext(_) {
185
+ return filename.indexOf(_) !== -1
186
+ }
187
+ if (f.type === 'application/vnd.google-earth.kml+xml' || ext('.kml')) {
188
+ return 'kml'
189
+ }
190
+ if (ext('.gpx')) return 'gpx'
191
+ if (ext('.geojson') || ext('.json')) return 'geojson'
192
+ if (f.type === 'text/csv' || ext('.csv') || ext('.tsv') || ext('.dsv')) {
193
+ return 'csv'
194
+ }
195
+ if (ext('.xml') || ext('.osm')) return 'osm'
196
+ if (ext('.umap')) return 'umap'
197
+ }
198
+
199
+ export function usableOption(options, option) {
200
+ return options[option] !== undefined && options[option] !== ''
201
+ }
202
+
203
+ /**
204
+ * Processes a template string by replacing placeholders with corresponding
205
+ * data values.
206
+ *
207
+ * Supports dot notation for nested objects and optional static fallbacks.
208
+ * If a value is not found, the placeholder is replaced with an empty string
209
+ * or left unchanged when 'ignore' is true.
210
+ *
211
+ * @param {string} str - The template string with placeholders.
212
+ * @param {Object} data - The data object from which to pull replacement values.
213
+ * @param {boolean} [ignore=false] - If true, leaves placeholders unchanged when no value is found.
214
+ * @returns {string} The processed string with placeholders replaced.
215
+ */
216
+ export function greedyTemplate(str, data, ignore) {
217
+ function getValue(data, path) {
218
+ let value = data
219
+ for (let i = 0; i < path.length; i++) {
220
+ value = value[path[i]]
221
+ if (value === undefined) break
222
+ }
223
+ return value
224
+ }
225
+
226
+ if (typeof str !== 'string') return ''
227
+
228
+ return str.replace(
229
+ /\{ *([^\{\}/\-]+)(?:\|("[^"]*"))? *\}/g,
230
+ (str, key, staticFallback) => {
231
+ const vars = key.split('|')
232
+ let value
233
+ let path
234
+ if (staticFallback !== undefined) {
235
+ vars.push(staticFallback)
236
+ }
237
+ for (let i = 0; i < vars.length; i++) {
238
+ path = vars[i]
239
+ if (path.startsWith('"') && path.endsWith('"'))
240
+ value = path.substring(1, path.length - 1) // static default value.
241
+ else value = getValue(data, path.split('.'))
242
+ if (value !== undefined) break
243
+ }
244
+ if (value === undefined) {
245
+ if (ignore) value = str
246
+ else value = ''
247
+ }
248
+ return value
249
+ }
250
+ )
251
+ }
252
+
253
+ export function naturalSort(a, b, lang) {
254
+ return a
255
+ .toString()
256
+ .toLowerCase()
257
+ .localeCompare(b.toString().toLowerCase(), lang || 'en', {
258
+ sensitivity: 'base',
259
+ numeric: true,
260
+ })
261
+ }
262
+
263
+ export function sortFeatures(features, sortKey, lang) {
264
+ const sortKeys = (sortKey || 'name').split(',')
265
+
266
+ const sort = (a, b, i) => {
267
+ let sortKey = sortKeys[i],
268
+ reverse = 1
269
+ if (sortKey[0] === '-') {
270
+ reverse = -1
271
+ sortKey = sortKey.substring(1)
272
+ }
273
+ let score
274
+ const valA = a.properties[sortKey] || ''
275
+ const valB = b.properties[sortKey] || ''
276
+ if (!valA) score = -1
277
+ else if (!valB) score = 1
278
+ else score = naturalSort(valA, valB, lang)
279
+ if (score === 0 && sortKeys[i + 1]) return sort(a, b, i + 1)
280
+ return score * reverse
281
+ }
282
+
283
+ features.sort((a, b) => {
284
+ if (!a.properties || !b.properties) {
285
+ return 0
286
+ }
287
+ return sort(a, b, 0)
288
+ })
289
+
290
+ return features
291
+ }
292
+
293
+ export function flattenCoordinates(coords) {
294
+ while (coords[0] && typeof coords[0][0] !== 'number') coords = coords[0]
295
+ return coords
296
+ }
297
+
298
+ export function buildQueryString(params) {
299
+ const query_string = []
300
+ for (const key in params) {
301
+ query_string.push(`${encodeURIComponent(key)}=${encodeURIComponent(params[key])}`)
302
+ }
303
+ return query_string.join('&')
304
+ }
305
+
306
+ export function getBaseUrl() {
307
+ return `//${window.location.host}${window.location.pathname}`
308
+ }
309
+
310
+ export function hasVar(value) {
311
+ return typeof value === 'string' && value.indexOf('{') != -1
312
+ }
313
+
314
+ export function isPath(value) {
315
+ return value && value.length && value.startsWith('/')
316
+ }
317
+
318
+ export function isRemoteUrl(value) {
319
+ return value && value.length && value.startsWith('http')
320
+ }
321
+
322
+ export function isDataImage(value) {
323
+ return value && value.length && value.startsWith('data:image')
324
+ }
325
+
326
+ /**
327
+ * Normalizes the input string by converting to lowercase
328
+ * and removing diacritics.
329
+ *
330
+ * If the input is nullish, it defaults to an empty string.
331
+ *
332
+ * @param {string} s - The string to be normalized.
333
+ * @returns {string} - The normalized string with lowercase
334
+ * characters and no diacritics.
335
+ */
336
+ export function normalize(s) {
337
+ return (s || '')
338
+ .toLowerCase()
339
+ .normalize('NFD')
340
+ .replace(/[\u0300-\u036f]/g, '')
341
+ }
342
+
343
+ // Vendorized from leaflet.utils
344
+ // https://github.com/Leaflet/Leaflet/blob/108c6717b70f57c63645498f9bd66b6677758786/src/core/Util.js#L132-L151
345
+ var templateRe = /\{ *([\w_ -]+) *\}/g
346
+
347
+ export function template(str, data) {
348
+ return str.replace(templateRe, function (str, key) {
349
+ var value = data[key]
350
+
351
+ if (value === undefined) {
352
+ throw new Error('No value provided for variable ' + str)
353
+ } else if (typeof value === 'function') {
354
+ value = value(data)
355
+ }
356
+ return value
357
+ })
358
+ }
359
+
360
+ export function parseNaiveDate(value) {
361
+ const naive = new Date(value)
362
+ // Let's pretend naive date are UTC, and remove time…
363
+ return new Date(Date.UTC(naive.getFullYear(), naive.getMonth(), naive.getDate()))
364
+ }
@@ -35,27 +35,25 @@ U.AutoComplete = L.Class.extend({
35
35
  },
36
36
 
37
37
  createInput: function () {
38
- this.input = L.DomUtil.element(
39
- 'input',
40
- {
41
- type: 'text',
42
- placeholder: this.options.placeholder,
43
- autocomplete: 'off',
44
- className: this.options.className,
45
- },
46
- this.el
47
- )
38
+ this.input = L.DomUtil.element({
39
+ tagName: 'input',
40
+ type: 'text',
41
+ parent: this.el,
42
+ placeholder: this.options.placeholder,
43
+ autocomplete: 'off',
44
+ className: this.options.className,
45
+ })
48
46
  L.DomEvent.on(this.input, 'keydown', this.onKeyDown, this)
49
47
  L.DomEvent.on(this.input, 'keyup', this.onKeyUp, this)
50
48
  L.DomEvent.on(this.input, 'blur', this.onBlur, this)
51
49
  },
52
50
 
53
51
  createContainer: function () {
54
- this.container = L.DomUtil.element(
55
- 'ul',
56
- { className: 'umap-autocomplete' },
57
- document.body
58
- )
52
+ this.container = L.DomUtil.element({
53
+ tagName: 'ul',
54
+ parent: document.body,
55
+ className: 'umap-autocomplete',
56
+ })
59
57
  },
60
58
 
61
59
  resizeContainer: function () {
@@ -174,8 +172,11 @@ U.AutoComplete = L.Class.extend({
174
172
  },
175
173
 
176
174
  createResult: function (item) {
177
- const el = L.DomUtil.element('li', {}, this.container)
178
- el.textContent = item.label
175
+ const el = L.DomUtil.element({
176
+ tagName: 'li',
177
+ parent: this.container,
178
+ textContent: item.label,
179
+ })
179
180
  const result = {
180
181
  item: item,
181
182
  el: el,
@@ -276,15 +277,22 @@ U.AutoComplete.Ajax.SelectMultiple = U.AutoComplete.Ajax.extend({
276
277
  initSelectedContainer: function () {
277
278
  return L.DomUtil.after(
278
279
  this.input,
279
- L.DomUtil.element('ul', { className: 'umap-multiresult' })
280
+ L.DomUtil.element({ tagName: 'ul', className: 'umap-multiresult' })
280
281
  )
281
282
  },
282
283
 
283
284
  displaySelected: function (result) {
284
- const result_el = L.DomUtil.element('li', {}, this.selected_container)
285
+ const result_el = L.DomUtil.element({
286
+ tagName: 'li',
287
+ parent: this.selected_container,
288
+ })
285
289
  result_el.textContent = result.item.label
286
- const close = L.DomUtil.element('span', { className: 'close' }, result_el)
287
- close.textContent = '×'
290
+ const close = L.DomUtil.element({
291
+ tagName: 'span',
292
+ parent: result_el,
293
+ className: 'close',
294
+ textContent: '×',
295
+ })
288
296
  L.DomEvent.on(
289
297
  close,
290
298
  'click',
@@ -302,15 +310,22 @@ U.AutoComplete.Ajax.Select = U.AutoComplete.Ajax.extend({
302
310
  initSelectedContainer: function () {
303
311
  return L.DomUtil.after(
304
312
  this.input,
305
- L.DomUtil.element('div', { className: 'umap-singleresult' })
313
+ L.DomUtil.element({ tagName: 'div', className: 'umap-singleresult' })
306
314
  )
307
315
  },
308
316
 
309
317
  displaySelected: function (result) {
310
- const result_el = L.DomUtil.element('div', {}, this.selected_container)
318
+ const result_el = L.DomUtil.element({
319
+ tagName: 'div',
320
+ parent: this.selected_container,
321
+ })
311
322
  result_el.textContent = result.item.label
312
- const close = L.DomUtil.element('span', { className: 'close' }, result_el)
313
- close.textContent = '×'
323
+ const close = L.DomUtil.element({
324
+ tagName: 'span',
325
+ parent: result_el,
326
+ className: 'close',
327
+ textContent: '×',
328
+ })
314
329
  this.input.style.display = 'none'
315
330
  L.DomEvent.on(
316
331
  close,