umap-project 2.8.2__py3-none-any.whl → 2.9.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 (260) hide show
  1. umap/__init__.py +1 -1
  2. umap/admin.py +15 -2
  3. umap/asgi.py +12 -7
  4. umap/context_processors.py +1 -0
  5. umap/locale/br/LC_MESSAGES/django.mo +0 -0
  6. umap/locale/br/LC_MESSAGES/django.po +111 -67
  7. umap/locale/cs_CZ/LC_MESSAGES/django.mo +0 -0
  8. umap/locale/cs_CZ/LC_MESSAGES/django.po +110 -66
  9. umap/locale/el/LC_MESSAGES/django.mo +0 -0
  10. umap/locale/el/LC_MESSAGES/django.po +129 -85
  11. umap/locale/en/LC_MESSAGES/django.po +103 -60
  12. umap/locale/es/LC_MESSAGES/django.mo +0 -0
  13. umap/locale/es/LC_MESSAGES/django.po +114 -69
  14. umap/locale/fr/LC_MESSAGES/django.mo +0 -0
  15. umap/locale/fr/LC_MESSAGES/django.po +105 -61
  16. umap/locale/gl/LC_MESSAGES/django.mo +0 -0
  17. umap/locale/gl/LC_MESSAGES/django.po +216 -171
  18. umap/locale/it/LC_MESSAGES/django.mo +0 -0
  19. umap/locale/it/LC_MESSAGES/django.po +142 -98
  20. umap/locale/nl/LC_MESSAGES/django.mo +0 -0
  21. umap/locale/nl/LC_MESSAGES/django.po +196 -151
  22. umap/locale/pt/LC_MESSAGES/django.mo +0 -0
  23. umap/locale/pt/LC_MESSAGES/django.po +115 -71
  24. umap/locale/zh_TW/LC_MESSAGES/django.mo +0 -0
  25. umap/locale/zh_TW/LC_MESSAGES/django.po +109 -65
  26. umap/management/commands/empty_trash.py +12 -1
  27. umap/migrations/0026_datalayer_modified_at_datalayer_share_status.py +26 -0
  28. umap/models.py +43 -13
  29. umap/settings/base.py +5 -2
  30. umap/static/umap/base.css +5 -2
  31. umap/static/umap/content.css +2 -22
  32. umap/static/umap/css/bar.css +39 -10
  33. umap/static/umap/css/contextmenu.css +14 -2
  34. umap/static/umap/css/form.css +33 -39
  35. umap/static/umap/css/icon.css +47 -5
  36. umap/static/umap/css/panel.css +20 -2
  37. umap/static/umap/css/popup.css +0 -1
  38. umap/static/umap/css/tooltip.css +33 -31
  39. umap/static/umap/img/16-white.svg +5 -3
  40. umap/static/umap/img/16.svg +1 -1
  41. umap/static/umap/img/24-white.svg +17 -16
  42. umap/static/umap/img/24.svg +29 -18
  43. umap/static/umap/img/providers/bitbucket.png +0 -0
  44. umap/static/umap/img/providers/github.png +0 -0
  45. umap/static/umap/img/providers/keycloak.png +0 -0
  46. umap/static/umap/img/providers/openstreetmap-oauth2.png +0 -0
  47. umap/static/umap/img/providers/twitter-oauth2.png +0 -0
  48. umap/static/umap/img/source/16-white.svg +6 -4
  49. umap/static/umap/img/source/16.svg +1 -1
  50. umap/static/umap/img/source/24-white.svg +20 -18
  51. umap/static/umap/img/source/24.svg +30 -19
  52. umap/static/umap/js/components/alerts/alert.js +4 -1
  53. umap/static/umap/js/modules/browser.js +8 -8
  54. umap/static/umap/js/modules/caption.js +30 -7
  55. umap/static/umap/js/modules/data/features.js +101 -56
  56. umap/static/umap/js/modules/data/layer.js +108 -83
  57. umap/static/umap/js/modules/form/builder.js +242 -0
  58. umap/static/umap/js/modules/form/fields.js +1346 -0
  59. umap/static/umap/js/modules/formatter.js +9 -8
  60. umap/static/umap/js/modules/help.js +20 -24
  61. umap/static/umap/js/modules/importer.js +6 -3
  62. umap/static/umap/js/modules/permissions.js +11 -6
  63. umap/static/umap/js/modules/rendering/icon.js +5 -1
  64. umap/static/umap/js/modules/rendering/layers/classified.js +12 -8
  65. umap/static/umap/js/modules/rendering/layers/cluster.js +11 -1
  66. umap/static/umap/js/modules/rendering/map.js +1 -23
  67. umap/static/umap/js/modules/rendering/ui.js +20 -38
  68. umap/static/umap/js/modules/rules.js +3 -2
  69. umap/static/umap/js/modules/saving.js +5 -0
  70. umap/static/umap/js/modules/schema.js +8 -6
  71. umap/static/umap/js/modules/share.js +3 -3
  72. umap/static/umap/js/modules/sync/engine.js +56 -26
  73. umap/static/umap/js/modules/sync/updaters.js +15 -6
  74. umap/static/umap/js/modules/sync/websocket.js +50 -37
  75. umap/static/umap/js/modules/tableeditor.js +3 -2
  76. umap/static/umap/js/modules/ui/bar.js +101 -9
  77. umap/static/umap/js/modules/ui/base.js +7 -24
  78. umap/static/umap/js/modules/ui/contextmenu.js +9 -2
  79. umap/static/umap/js/modules/ui/panel.js +5 -1
  80. umap/static/umap/js/modules/ui/tooltip.js +19 -11
  81. umap/static/umap/js/modules/umap.js +121 -68
  82. umap/static/umap/js/modules/utils.js +196 -12
  83. umap/static/umap/js/umap.controls.js +11 -353
  84. umap/static/umap/locale/am_ET.js +17 -5
  85. umap/static/umap/locale/am_ET.json +17 -5
  86. umap/static/umap/locale/ar.js +17 -5
  87. umap/static/umap/locale/ar.json +17 -5
  88. umap/static/umap/locale/ast.js +17 -5
  89. umap/static/umap/locale/ast.json +17 -5
  90. umap/static/umap/locale/bg.js +17 -5
  91. umap/static/umap/locale/bg.json +17 -5
  92. umap/static/umap/locale/br.js +33 -20
  93. umap/static/umap/locale/br.json +33 -20
  94. umap/static/umap/locale/ca.js +17 -5
  95. umap/static/umap/locale/ca.json +17 -5
  96. umap/static/umap/locale/cs_CZ.js +15 -5
  97. umap/static/umap/locale/cs_CZ.json +15 -5
  98. umap/static/umap/locale/da.js +17 -5
  99. umap/static/umap/locale/da.json +17 -5
  100. umap/static/umap/locale/de.js +17 -5
  101. umap/static/umap/locale/de.json +17 -5
  102. umap/static/umap/locale/el.js +63 -51
  103. umap/static/umap/locale/el.json +63 -51
  104. umap/static/umap/locale/en.js +15 -5
  105. umap/static/umap/locale/en.json +15 -5
  106. umap/static/umap/locale/en_US.json +17 -5
  107. umap/static/umap/locale/es.js +25 -13
  108. umap/static/umap/locale/es.json +25 -13
  109. umap/static/umap/locale/et.js +17 -5
  110. umap/static/umap/locale/et.json +17 -5
  111. umap/static/umap/locale/eu.js +17 -5
  112. umap/static/umap/locale/eu.json +17 -5
  113. umap/static/umap/locale/fa_IR.js +17 -5
  114. umap/static/umap/locale/fa_IR.json +17 -5
  115. umap/static/umap/locale/fi.js +17 -5
  116. umap/static/umap/locale/fi.json +17 -5
  117. umap/static/umap/locale/fr.js +16 -6
  118. umap/static/umap/locale/fr.json +16 -6
  119. umap/static/umap/locale/gl.js +357 -345
  120. umap/static/umap/locale/gl.json +357 -345
  121. umap/static/umap/locale/he.js +17 -5
  122. umap/static/umap/locale/he.json +17 -5
  123. umap/static/umap/locale/hr.js +17 -5
  124. umap/static/umap/locale/hr.json +17 -5
  125. umap/static/umap/locale/hu.js +14 -5
  126. umap/static/umap/locale/hu.json +14 -5
  127. umap/static/umap/locale/id.js +17 -5
  128. umap/static/umap/locale/id.json +17 -5
  129. umap/static/umap/locale/is.js +17 -5
  130. umap/static/umap/locale/is.json +17 -5
  131. umap/static/umap/locale/it.js +125 -113
  132. umap/static/umap/locale/it.json +125 -113
  133. umap/static/umap/locale/ja.js +17 -5
  134. umap/static/umap/locale/ja.json +17 -5
  135. umap/static/umap/locale/ko.js +17 -5
  136. umap/static/umap/locale/ko.json +17 -5
  137. umap/static/umap/locale/lt.js +17 -5
  138. umap/static/umap/locale/lt.json +17 -5
  139. umap/static/umap/locale/ms.js +17 -5
  140. umap/static/umap/locale/ms.json +17 -5
  141. umap/static/umap/locale/nl.js +132 -119
  142. umap/static/umap/locale/nl.json +132 -119
  143. umap/static/umap/locale/no.js +17 -5
  144. umap/static/umap/locale/no.json +17 -5
  145. umap/static/umap/locale/pl.js +17 -5
  146. umap/static/umap/locale/pl.json +17 -5
  147. umap/static/umap/locale/pl_PL.json +17 -5
  148. umap/static/umap/locale/pt.js +38 -25
  149. umap/static/umap/locale/pt.json +38 -25
  150. umap/static/umap/locale/pt_BR.js +17 -5
  151. umap/static/umap/locale/pt_BR.json +17 -5
  152. umap/static/umap/locale/pt_PT.js +17 -5
  153. umap/static/umap/locale/pt_PT.json +17 -5
  154. umap/static/umap/locale/ro.js +17 -5
  155. umap/static/umap/locale/ro.json +17 -5
  156. umap/static/umap/locale/ru.js +17 -5
  157. umap/static/umap/locale/ru.json +17 -5
  158. umap/static/umap/locale/sk_SK.js +17 -5
  159. umap/static/umap/locale/sk_SK.json +17 -5
  160. umap/static/umap/locale/sl.js +17 -5
  161. umap/static/umap/locale/sl.json +17 -5
  162. umap/static/umap/locale/sr.js +17 -5
  163. umap/static/umap/locale/sr.json +17 -5
  164. umap/static/umap/locale/sv.js +17 -5
  165. umap/static/umap/locale/sv.json +17 -5
  166. umap/static/umap/locale/th_TH.js +17 -5
  167. umap/static/umap/locale/th_TH.json +17 -5
  168. umap/static/umap/locale/tr.js +17 -5
  169. umap/static/umap/locale/tr.json +17 -5
  170. umap/static/umap/locale/uk_UA.js +17 -5
  171. umap/static/umap/locale/uk_UA.json +17 -5
  172. umap/static/umap/locale/vi.js +17 -5
  173. umap/static/umap/locale/vi.json +17 -5
  174. umap/static/umap/locale/vi_VN.json +17 -5
  175. umap/static/umap/locale/zh.js +17 -5
  176. umap/static/umap/locale/zh.json +17 -5
  177. umap/static/umap/locale/zh_CN.json +17 -5
  178. umap/static/umap/locale/zh_TW.Big5.json +17 -5
  179. umap/static/umap/locale/zh_TW.js +15 -5
  180. umap/static/umap/locale/zh_TW.json +15 -5
  181. umap/static/umap/map.css +29 -76
  182. umap/static/umap/nav.css +6 -3
  183. umap/static/umap/unittests/utils.js +14 -0
  184. umap/static/umap/vars.css +3 -0
  185. umap/static/umap/vendors/dompurify/purify.es.js +138 -354
  186. umap/static/umap/vendors/dompurify/purify.es.mjs.map +1 -1
  187. umap/static/umap/vendors/editable/Leaflet.Editable.js +1 -0
  188. umap/sync/__init__.py +0 -0
  189. umap/sync/app.py +187 -0
  190. umap/sync/payloads.py +56 -0
  191. umap/templates/auth/user_detail.html +4 -0
  192. umap/templates/auth/user_form.html +9 -6
  193. umap/templates/auth/user_stars.html +4 -0
  194. umap/templates/base.html +1 -1
  195. umap/templates/registration/login.html +2 -5
  196. umap/templates/umap/about.html +5 -0
  197. umap/templates/umap/about_summary.html +2 -2
  198. umap/templates/umap/components/provider.html +8 -0
  199. umap/templates/umap/content_footer.html +1 -1
  200. umap/templates/umap/css.html +0 -2
  201. umap/templates/umap/js.html +0 -4
  202. umap/templates/umap/map_detail.html +1 -1
  203. umap/templates/umap/password_change.html +4 -0
  204. umap/templates/umap/password_change_done.html +4 -0
  205. umap/templates/umap/search.html +4 -0
  206. umap/templates/umap/search_bar.html +1 -0
  207. umap/templates/umap/team_confirm_delete.html +4 -0
  208. umap/templates/umap/team_detail.html +4 -0
  209. umap/templates/umap/team_form.html +4 -0
  210. umap/templates/umap/user_dashboard.html +1 -1
  211. umap/templates/umap/user_teams.html +4 -0
  212. umap/tests/base.py +3 -1
  213. umap/tests/integration/conftest.py +16 -23
  214. umap/tests/integration/test_anonymous_owned_map.py +2 -2
  215. umap/tests/integration/test_basics.py +4 -7
  216. umap/tests/integration/test_caption.py +1 -0
  217. umap/tests/integration/test_categorized_layer.py +4 -8
  218. umap/tests/integration/test_choropleth.py +1 -1
  219. umap/tests/integration/test_conditional_rules.py +3 -3
  220. umap/tests/integration/test_draw_polygon.py +14 -22
  221. umap/tests/integration/test_draw_polyline.py +6 -14
  222. umap/tests/integration/test_edit_datalayer.py +11 -11
  223. umap/tests/integration/test_edit_map.py +30 -4
  224. umap/tests/integration/test_edit_marker.py +5 -5
  225. umap/tests/integration/test_edit_polygon.py +6 -6
  226. umap/tests/integration/test_features_id_generation.py +2 -6
  227. umap/tests/integration/test_import.py +115 -29
  228. umap/tests/integration/test_optimistic_merge.py +1 -0
  229. umap/tests/integration/test_owned_map.py +1 -1
  230. umap/tests/integration/test_picto.py +8 -8
  231. umap/tests/integration/test_save.py +3 -2
  232. umap/tests/integration/test_star.py +13 -9
  233. umap/tests/integration/test_tableeditor.py +8 -7
  234. umap/tests/integration/test_view_marker.py +10 -0
  235. umap/tests/integration/test_websocket_sync.py +239 -64
  236. umap/tests/settings.py +2 -0
  237. umap/tests/test_datalayer.py +2 -3
  238. umap/tests/test_datalayer_views.py +20 -1
  239. umap/tests/test_empty_trash.py +10 -3
  240. umap/tests/test_map_views.py +11 -0
  241. umap/utils.py +27 -11
  242. umap/views.py +37 -6
  243. {umap_project-2.8.2.dist-info → umap_project-2.9.0.dist-info}/METADATA +22 -22
  244. {umap_project-2.8.2.dist-info → umap_project-2.9.0.dist-info}/RECORD +247 -248
  245. {umap_project-2.8.2.dist-info → umap_project-2.9.0.dist-info}/WHEEL +1 -1
  246. umap/management/commands/run_websocket_server.py +0 -23
  247. umap/settings/local_s3.py +0 -45
  248. umap/static/umap/bitbucket.png +0 -0
  249. umap/static/umap/github.png +0 -0
  250. umap/static/umap/js/umap.forms.js +0 -1242
  251. umap/static/umap/keycloak.png +0 -0
  252. umap/static/umap/openstreetmap.png +0 -0
  253. umap/static/umap/twitter.png +0 -0
  254. umap/static/umap/vendors/formbuilder/Leaflet.FormBuilder.js +0 -468
  255. umap/static/umap/vendors/toolbar/leaflet.toolbar.css +0 -1
  256. umap/static/umap/vendors/toolbar/leaflet.toolbar.js +0 -1
  257. umap/tests/test_websocket_server.py +0 -22
  258. umap/websocket_server.py +0 -202
  259. {umap_project-2.8.2.dist-info → umap_project-2.9.0.dist-info}/entry_points.txt +0 -0
  260. {umap_project-2.8.2.dist-info → umap_project-2.9.0.dist-info}/licenses/LICENSE +0 -0
@@ -1,1242 +0,0 @@
1
- U.COLORS = [
2
- 'Black',
3
- 'Navy',
4
- 'DarkBlue',
5
- 'MediumBlue',
6
- 'Blue',
7
- 'DarkGreen',
8
- 'Green',
9
- 'Teal',
10
- 'DarkCyan',
11
- 'DeepSkyBlue',
12
- 'DarkTurquoise',
13
- 'MediumSpringGreen',
14
- 'Lime',
15
- 'SpringGreen',
16
- 'Aqua',
17
- 'Cyan',
18
- 'MidnightBlue',
19
- 'DodgerBlue',
20
- 'LightSeaGreen',
21
- 'ForestGreen',
22
- 'SeaGreen',
23
- 'DarkSlateGray',
24
- 'DarkSlateGrey',
25
- 'LimeGreen',
26
- 'MediumSeaGreen',
27
- 'Turquoise',
28
- 'RoyalBlue',
29
- 'SteelBlue',
30
- 'DarkSlateBlue',
31
- 'MediumTurquoise',
32
- 'Indigo',
33
- 'DarkOliveGreen',
34
- 'CadetBlue',
35
- 'CornflowerBlue',
36
- 'MediumAquaMarine',
37
- 'DimGray',
38
- 'DimGrey',
39
- 'SlateBlue',
40
- 'OliveDrab',
41
- 'SlateGray',
42
- 'SlateGrey',
43
- 'LightSlateGray',
44
- 'LightSlateGrey',
45
- 'MediumSlateBlue',
46
- 'LawnGreen',
47
- 'Chartreuse',
48
- 'Aquamarine',
49
- 'Maroon',
50
- 'Purple',
51
- 'Olive',
52
- 'Gray',
53
- 'Grey',
54
- 'SkyBlue',
55
- 'LightSkyBlue',
56
- 'BlueViolet',
57
- 'DarkRed',
58
- 'DarkMagenta',
59
- 'SaddleBrown',
60
- 'DarkSeaGreen',
61
- 'LightGreen',
62
- 'MediumPurple',
63
- 'DarkViolet',
64
- 'PaleGreen',
65
- 'DarkOrchid',
66
- 'YellowGreen',
67
- 'Sienna',
68
- 'Brown',
69
- 'DarkGray',
70
- 'DarkGrey',
71
- 'LightBlue',
72
- 'GreenYellow',
73
- 'PaleTurquoise',
74
- 'LightSteelBlue',
75
- 'PowderBlue',
76
- 'FireBrick',
77
- 'DarkGoldenRod',
78
- 'MediumOrchid',
79
- 'RosyBrown',
80
- 'DarkKhaki',
81
- 'Silver',
82
- 'MediumVioletRed',
83
- 'IndianRed',
84
- 'Peru',
85
- 'Chocolate',
86
- 'Tan',
87
- 'LightGray',
88
- 'LightGrey',
89
- 'Thistle',
90
- 'Orchid',
91
- 'GoldenRod',
92
- 'PaleVioletRed',
93
- 'Crimson',
94
- 'Gainsboro',
95
- 'Plum',
96
- 'BurlyWood',
97
- 'LightCyan',
98
- 'Lavender',
99
- 'DarkSalmon',
100
- 'Violet',
101
- 'PaleGoldenRod',
102
- 'LightCoral',
103
- 'Khaki',
104
- 'AliceBlue',
105
- 'HoneyDew',
106
- 'Azure',
107
- 'SandyBrown',
108
- 'Wheat',
109
- 'Beige',
110
- 'WhiteSmoke',
111
- 'MintCream',
112
- 'GhostWhite',
113
- 'Salmon',
114
- 'AntiqueWhite',
115
- 'Linen',
116
- 'LightGoldenRodYellow',
117
- 'OldLace',
118
- 'Red',
119
- 'Fuchsia',
120
- 'Magenta',
121
- 'DeepPink',
122
- 'OrangeRed',
123
- 'Tomato',
124
- 'HotPink',
125
- 'Coral',
126
- 'DarkOrange',
127
- 'LightSalmon',
128
- 'Orange',
129
- 'LightPink',
130
- 'Pink',
131
- 'Gold',
132
- 'PeachPuff',
133
- 'NavajoWhite',
134
- 'Moccasin',
135
- 'Bisque',
136
- 'MistyRose',
137
- 'BlanchedAlmond',
138
- 'PapayaWhip',
139
- 'LavenderBlush',
140
- 'SeaShell',
141
- 'Cornsilk',
142
- 'LemonChiffon',
143
- 'FloralWhite',
144
- 'Snow',
145
- 'Yellow',
146
- 'LightYellow',
147
- 'Ivory',
148
- 'White',
149
- ]
150
-
151
- L.FormBuilder.Element.include({
152
- undefine: function () {
153
- L.DomUtil.addClass(this.wrapper, 'undefined')
154
- this.clear()
155
- this.sync()
156
- },
157
-
158
- getParentNode: function () {
159
- if (this.options.wrapper) {
160
- return L.DomUtil.create(
161
- this.options.wrapper,
162
- this.options.wrapperClass || '',
163
- this.form
164
- )
165
- }
166
- let className = 'formbox'
167
- if (this.options.inheritable) {
168
- className +=
169
- this.get(true) === undefined ? ' inheritable undefined' : ' inheritable '
170
- }
171
- className += ` umap-field-${this.name}`
172
- this.wrapper = L.DomUtil.create('div', className, this.form)
173
- this.header = L.DomUtil.create('div', 'header', this.wrapper)
174
- if (this.options.inheritable) {
175
- const undefine = L.DomUtil.add('a', 'button undefine', this.header, L._('clear'))
176
- const define = L.DomUtil.add('a', 'button define', this.header, L._('define'))
177
- L.DomEvent.on(
178
- define,
179
- 'click',
180
- function (e) {
181
- L.DomEvent.stop(e)
182
- this.fetch()
183
- this.fire('define')
184
- L.DomUtil.removeClass(this.wrapper, 'undefined')
185
- },
186
- this
187
- )
188
- L.DomEvent.on(undefine, 'click', L.DomEvent.stop).on(
189
- undefine,
190
- 'click',
191
- this.undefine,
192
- this
193
- )
194
- }
195
- this.quickContainer = L.DomUtil.create(
196
- 'span',
197
- 'quick-actions show-on-defined',
198
- this.header
199
- )
200
- this.extendedContainer = L.DomUtil.create('div', 'show-on-defined', this.wrapper)
201
- return this.extendedContainer
202
- },
203
-
204
- getLabelParent: function () {
205
- return this.header
206
- },
207
-
208
- clear: function () {
209
- this.input.value = ''
210
- },
211
-
212
- get: function (own) {
213
- if (!this.options.inheritable || own) return this.builder.getter(this.field)
214
- const path = this.field.split('.')
215
- const key = path[path.length - 1]
216
- return this.obj.getOption(key)
217
- },
218
-
219
- buildLabel: function () {
220
- if (this.options.label) {
221
- this.label = L.DomUtil.create('label', '', this.getLabelParent())
222
- this.label.textContent = this.label.title = this.options.label
223
- if (this.options.helpEntries) {
224
- this.builder._umap.help.button(this.label, this.options.helpEntries)
225
- } else if (this.options.helpTooltip) {
226
- const info = L.DomUtil.create('i', 'info', this.label)
227
- L.DomEvent.on(info, 'mouseover', () => {
228
- this.builder._umap.tooltip.open({
229
- anchor: info,
230
- content: this.options.helpTooltip,
231
- position: 'top',
232
- })
233
- })
234
- }
235
- }
236
- },
237
- })
238
-
239
- L.FormBuilder.Select.include({
240
- clear: function () {
241
- this.select.value = ''
242
- },
243
-
244
- getDefault: function () {
245
- if (this.options.inheritable) return undefined
246
- return this.getOptions()[0][0]
247
- },
248
- })
249
-
250
- L.FormBuilder.CheckBox.include({
251
- value: function () {
252
- return L.DomUtil.hasClass(this.wrapper, 'undefined')
253
- ? undefined
254
- : this.input.checked
255
- },
256
-
257
- clear: function () {
258
- this.fetch()
259
- },
260
- })
261
-
262
- L.FormBuilder.EditableText = L.FormBuilder.Element.extend({
263
- build: function () {
264
- this.input = L.DomUtil.create('span', this.options.className || '', this.parentNode)
265
- this.input.contentEditable = true
266
- this.fetch()
267
- L.DomEvent.on(this.input, 'input', this.sync, this)
268
- L.DomEvent.on(this.input, 'keypress', this.onKeyPress, this)
269
- },
270
-
271
- getParentNode: function () {
272
- return this.form
273
- },
274
-
275
- value: function () {
276
- return this.input.textContent
277
- },
278
-
279
- fetch: function () {
280
- this.input.textContent = this.toHTML()
281
- },
282
-
283
- onKeyPress: function (event) {
284
- if (event.keyCode === 13) {
285
- event.preventDefault()
286
- this.input.blur()
287
- }
288
- },
289
- })
290
-
291
- L.FormBuilder.ColorPicker = L.FormBuilder.Input.extend({
292
- colors: U.COLORS,
293
- getParentNode: function () {
294
- L.FormBuilder.CheckBox.prototype.getParentNode.call(this)
295
- return this.quickContainer
296
- },
297
-
298
- build: function () {
299
- L.FormBuilder.Input.prototype.build.call(this)
300
- this.input.placeholder = this.options.placeholder || L._('Inherit')
301
- this.container = L.DomUtil.create(
302
- 'div',
303
- 'umap-color-picker',
304
- this.extendedContainer
305
- )
306
- this.container.style.display = 'none'
307
- for (const idx in this.colors) {
308
- this.addColor(this.colors[idx])
309
- }
310
- this.spreadColor()
311
- this.input.autocomplete = 'off'
312
- L.DomEvent.on(this.input, 'focus', this.onFocus, this)
313
- L.DomEvent.on(this.input, 'blur', this.onBlur, this)
314
- L.DomEvent.on(this.input, 'change', this.sync, this)
315
- this.on('define', this.onFocus)
316
- },
317
-
318
- onFocus: function () {
319
- this.container.style.display = 'block'
320
- this.spreadColor()
321
- },
322
-
323
- onBlur: function () {
324
- const closePicker = () => {
325
- this.container.style.display = 'none'
326
- }
327
- // We must leave time for the click to be listened.
328
- window.setTimeout(closePicker, 100)
329
- },
330
-
331
- sync: function () {
332
- this.spreadColor()
333
- L.FormBuilder.Input.prototype.sync.call(this)
334
- },
335
-
336
- spreadColor: function () {
337
- if (this.input.value) this.input.style.backgroundColor = this.input.value
338
- else this.input.style.backgroundColor = 'inherit'
339
- },
340
-
341
- addColor: function (colorName) {
342
- const span = L.DomUtil.create('span', '', this.container)
343
- span.style.backgroundColor = span.title = colorName
344
- const updateColorInput = function () {
345
- this.input.value = colorName
346
- this.sync()
347
- this.container.style.display = 'none'
348
- }
349
- L.DomEvent.on(span, 'mousedown', updateColorInput, this)
350
- },
351
- })
352
-
353
- L.FormBuilder.TextColorPicker = L.FormBuilder.ColorPicker.extend({
354
- colors: [
355
- 'Black',
356
- 'DarkSlateGrey',
357
- 'DimGrey',
358
- 'SlateGrey',
359
- 'LightSlateGrey',
360
- 'Grey',
361
- 'DarkGrey',
362
- 'LightGrey',
363
- 'White',
364
- ],
365
- })
366
-
367
- L.FormBuilder.LayerTypeChooser = L.FormBuilder.Select.extend({
368
- getOptions: () => {
369
- return U.LAYER_TYPES.map((class_) => [class_.TYPE, class_.NAME])
370
- },
371
- })
372
-
373
- L.FormBuilder.SlideshowDelay = L.FormBuilder.IntSelect.extend({
374
- getOptions: () => {
375
- const options = []
376
- for (let i = 1; i < 30; i++) {
377
- options.push([i * 1000, L._('{delay} seconds', { delay: i })])
378
- }
379
- return options
380
- },
381
- })
382
-
383
- L.FormBuilder.DataLayerSwitcher = L.FormBuilder.Select.extend({
384
- getOptions: function () {
385
- const options = []
386
- this.builder._umap.eachDataLayerReverse((datalayer) => {
387
- if (
388
- datalayer.isLoaded() &&
389
- !datalayer.isDataReadOnly() &&
390
- datalayer.isBrowsable()
391
- ) {
392
- options.push([L.stamp(datalayer), datalayer.getName()])
393
- }
394
- })
395
- return options
396
- },
397
-
398
- toHTML: function () {
399
- return L.stamp(this.obj.datalayer)
400
- },
401
-
402
- toJS: function () {
403
- return this.builder._umap.datalayers[this.value()]
404
- },
405
-
406
- set: function () {
407
- this.builder._umap.lastUsedDataLayer = this.toJS()
408
- this.obj.changeDataLayer(this.toJS())
409
- },
410
- })
411
-
412
- L.FormBuilder.DataFormat = L.FormBuilder.Select.extend({
413
- selectOptions: [
414
- [undefined, L._('Choose the data format')],
415
- ['geojson', 'geojson'],
416
- ['osm', 'osm'],
417
- ['csv', 'csv'],
418
- ['gpx', 'gpx'],
419
- ['kml', 'kml'],
420
- ['georss', 'georss'],
421
- ],
422
- })
423
-
424
- L.FormBuilder.LicenceChooser = L.FormBuilder.Select.extend({
425
- getOptions: function () {
426
- const licences = []
427
- const licencesList = this.builder.obj.properties.licences
428
- let licence
429
- for (const i in licencesList) {
430
- licence = licencesList[i]
431
- licences.push([i, licence.name])
432
- }
433
- return licences
434
- },
435
-
436
- toHTML: function () {
437
- return this.get()?.name
438
- },
439
-
440
- toJS: function () {
441
- return this.builder.obj.properties.licences[this.value()]
442
- },
443
- })
444
-
445
- L.FormBuilder.NullableBoolean = L.FormBuilder.Select.extend({
446
- selectOptions: [
447
- [undefined, L._('inherit')],
448
- [true, L._('yes')],
449
- [false, L._('no')],
450
- ],
451
-
452
- toJS: function () {
453
- let value = this.value()
454
- switch (value) {
455
- case 'true':
456
- case true:
457
- value = true
458
- break
459
- case 'false':
460
- case false:
461
- value = false
462
- break
463
- default:
464
- value = undefined
465
- }
466
- return value
467
- },
468
- })
469
-
470
- L.FormBuilder.BlurInput.include({
471
- build: function () {
472
- this.options.className = 'blur'
473
- L.FormBuilder.Input.prototype.build.call(this)
474
- const button = L.DomUtil.create('span', 'button blur-button')
475
- L.DomUtil.after(this.input, button)
476
- L.DomEvent.on(this.input, 'focus', this.fetch, this)
477
- },
478
- })
479
-
480
- // Adds an autocomplete using all available user defined properties
481
- L.FormBuilder.PropertyInput = L.FormBuilder.BlurInput.extend({
482
- build: function () {
483
- L.FormBuilder.BlurInput.prototype.build.call(this)
484
- const autocomplete = new U.AutocompleteDatalist(this.input)
485
- // Will be used on Umap and DataLayer
486
- const properties = this.builder.obj.allProperties()
487
- autocomplete.suggestions = properties
488
- },
489
- })
490
-
491
- L.FormBuilder.IconUrl = L.FormBuilder.BlurInput.extend({
492
- type: () => 'hidden',
493
-
494
- build: function () {
495
- L.FormBuilder.BlurInput.prototype.build.call(this)
496
- this.buttons = L.DomUtil.create('div', '', this.parentNode)
497
- this.tabs = L.DomUtil.create('div', 'flat-tabs', this.parentNode)
498
- this.body = L.DomUtil.create('div', 'umap-pictogram-body', this.parentNode)
499
- this.footer = L.DomUtil.create('div', '', this.parentNode)
500
- this.updatePreview()
501
- this.on('define', this.onDefine)
502
- },
503
-
504
- onDefine: async function () {
505
- this.buttons.innerHTML = ''
506
- this.footer.innerHTML = ''
507
- const [{ pictogram_list }, response, error] = await this.builder._umap.server.get(
508
- this.builder._umap.properties.urls.pictogram_list_json
509
- )
510
- if (!error) this.pictogram_list = pictogram_list
511
- this.buildTabs()
512
- const value = this.value()
513
- if (U.Icon.RECENT.length) this.showRecentTab()
514
- else if (!value || U.Utils.isPath(value)) this.showSymbolsTab()
515
- else if (U.Utils.isRemoteUrl(value) || U.Utils.isDataImage(value)) this.showURLTab()
516
- else this.showCharsTab()
517
- const closeButton = L.DomUtil.createButton(
518
- 'button action-button',
519
- this.footer,
520
- L._('Close'),
521
- function (e) {
522
- this.body.innerHTML = ''
523
- this.tabs.innerHTML = ''
524
- this.footer.innerHTML = ''
525
- if (this.isDefault()) this.undefine(e)
526
- else this.updatePreview()
527
- },
528
- this
529
- )
530
- },
531
-
532
- buildTabs: function () {
533
- this.tabs.innerHTML = ''
534
- if (U.Icon.RECENT.length) {
535
- const recent = L.DomUtil.add(
536
- 'button',
537
- 'flat tab-recent',
538
- this.tabs,
539
- L._('Recent')
540
- )
541
- L.DomEvent.on(recent, 'click', L.DomEvent.stop).on(
542
- recent,
543
- 'click',
544
- this.showRecentTab,
545
- this
546
- )
547
- }
548
- const symbol = L.DomUtil.add('button', 'flat tab-symbols', this.tabs, L._('Symbol'))
549
- const char = L.DomUtil.add(
550
- 'button',
551
- 'flat tab-chars',
552
- this.tabs,
553
- L._('Emoji & Character')
554
- )
555
- url = L.DomUtil.add('button', 'flat tab-url', this.tabs, L._('URL'))
556
- L.DomEvent.on(symbol, 'click', L.DomEvent.stop).on(
557
- symbol,
558
- 'click',
559
- this.showSymbolsTab,
560
- this
561
- )
562
- L.DomEvent.on(char, 'click', L.DomEvent.stop).on(
563
- char,
564
- 'click',
565
- this.showCharsTab,
566
- this
567
- )
568
- L.DomEvent.on(url, 'click', L.DomEvent.stop).on(url, 'click', this.showURLTab, this)
569
- },
570
-
571
- openTab: function (name) {
572
- const els = this.tabs.querySelectorAll('button')
573
- for (const el of els) {
574
- L.DomUtil.removeClass(el, 'on')
575
- }
576
- const el = this.tabs.querySelector(`.tab-${name}`)
577
- L.DomUtil.addClass(el, 'on')
578
- this.body.innerHTML = ''
579
- },
580
-
581
- updatePreview: function () {
582
- this.buttons.innerHTML = ''
583
- if (this.isDefault()) return
584
- if (!U.Utils.hasVar(this.value())) {
585
- // Do not try to render URL with variables
586
- const box = L.DomUtil.create('div', 'umap-pictogram-choice', this.buttons)
587
- L.DomEvent.on(box, 'click', this.onDefine, this)
588
- const icon = U.Icon.makeElement(this.value(), box)
589
- }
590
- this.button = L.DomUtil.createButton(
591
- 'button action-button',
592
- this.buttons,
593
- this.value() ? L._('Change') : L._('Add'),
594
- this.onDefine,
595
- this
596
- )
597
- },
598
-
599
- addIconPreview: function (pictogram, parent) {
600
- const baseClass = 'umap-pictogram-choice'
601
- const value = pictogram.src
602
- const search = U.Utils.normalize(this.searchInput.value)
603
- const title = pictogram.attribution
604
- ? `${pictogram.name} — © ${pictogram.attribution}`
605
- : pictogram.name || pictogram.src
606
- if (search && U.Utils.normalize(title).indexOf(search) === -1) return
607
- const className = value === this.value() ? `${baseClass} selected` : baseClass
608
- const container = L.DomUtil.create('div', className, parent)
609
- U.Icon.makeElement(value, container)
610
- container.title = title
611
- L.DomEvent.on(
612
- container,
613
- 'click',
614
- function (e) {
615
- this.input.value = value
616
- this.sync()
617
- this.unselectAll(this.grid)
618
- L.DomUtil.addClass(container, 'selected')
619
- },
620
- this
621
- )
622
- return true // Icon has been added (not filtered)
623
- },
624
-
625
- clear: function () {
626
- this.input.value = ''
627
- this.unselectAll(this.body)
628
- this.sync()
629
- this.body.innerHTML = ''
630
- this.updatePreview()
631
- },
632
-
633
- addCategory: function (items, name) {
634
- const parent = L.DomUtil.create('div', 'umap-pictogram-category')
635
- if (name) L.DomUtil.add('h6', '', parent, name)
636
- const grid = L.DomUtil.create('div', 'umap-pictogram-grid', parent)
637
- let status = false
638
- for (const item of items) {
639
- status = this.addIconPreview(item, grid) || status
640
- }
641
- if (status) this.grid.appendChild(parent)
642
- },
643
-
644
- buildSymbolsList: function () {
645
- this.grid.innerHTML = ''
646
- const categories = {}
647
- let category
648
- for (const props of this.pictogram_list) {
649
- category = props.category || L._('Generic')
650
- categories[category] = categories[category] || []
651
- categories[category].push(props)
652
- }
653
- const sorted = Object.entries(categories).toSorted(([a], [b]) =>
654
- U.Utils.naturalSort(a, b, U.lang)
655
- )
656
- for (const [name, items] of sorted) {
657
- this.addCategory(items, name)
658
- }
659
- },
660
-
661
- buildRecentList: function () {
662
- this.grid.innerHTML = ''
663
- const items = U.Icon.RECENT.map((src) => ({
664
- src,
665
- }))
666
- this.addCategory(items)
667
- },
668
-
669
- isDefault: function () {
670
- return !this.value() || this.value() === U.SCHEMA.iconUrl.default
671
- },
672
-
673
- addGrid: function (onSearch) {
674
- this.searchInput = L.DomUtil.create('input', '', this.body)
675
- this.searchInput.type = 'search'
676
- this.searchInput.placeholder = L._('Search')
677
- this.grid = L.DomUtil.create('div', '', this.body)
678
- L.DomEvent.on(this.searchInput, 'input', onSearch, this)
679
- },
680
-
681
- showRecentTab: function () {
682
- if (!U.Icon.RECENT.length) return
683
- this.openTab('recent')
684
- this.addGrid(this.buildRecentList)
685
- this.buildRecentList()
686
- },
687
-
688
- showSymbolsTab: function () {
689
- this.openTab('symbols')
690
- this.addGrid(this.buildSymbolsList)
691
- this.buildSymbolsList()
692
- },
693
-
694
- showCharsTab: function () {
695
- this.openTab('chars')
696
- const value = !U.Icon.isImg(this.value()) ? this.value() : null
697
- const input = this.buildInput(this.body, value)
698
- input.placeholder = L._('Type char or paste emoji')
699
- input.type = 'text'
700
- },
701
-
702
- showURLTab: function () {
703
- this.openTab('url')
704
- const value =
705
- U.Utils.isRemoteUrl(this.value()) || U.Utils.isDataImage(this.value())
706
- ? this.value()
707
- : null
708
- const input = this.buildInput(this.body, value)
709
- input.placeholder = L._('Add image URL')
710
- input.type = 'url'
711
- },
712
-
713
- buildInput: function (parent, value) {
714
- const input = L.DomUtil.create('input', 'blur', parent)
715
- const button = L.DomUtil.create('span', 'button blur-button', parent)
716
- if (value) input.value = value
717
- L.DomEvent.on(input, 'blur', () => {
718
- // Do not clear this.input when focus-blur
719
- // empty input
720
- if (input.value === value) return
721
- this.input.value = input.value
722
- this.sync()
723
- })
724
- return input
725
- },
726
-
727
- unselectAll: (container) => {
728
- const els = container.querySelectorAll('div.selected')
729
- for (const el in els) {
730
- if (els.hasOwnProperty(el)) L.DomUtil.removeClass(els[el], 'selected')
731
- }
732
- },
733
- })
734
-
735
- L.FormBuilder.Url = L.FormBuilder.Input.extend({
736
- type: () => 'url',
737
- })
738
-
739
- L.FormBuilder.Switch = L.FormBuilder.CheckBox.extend({
740
- getParentNode: function () {
741
- L.FormBuilder.CheckBox.prototype.getParentNode.call(this)
742
- if (this.options.inheritable) return this.quickContainer
743
- return this.extendedContainer
744
- },
745
-
746
- build: function () {
747
- L.FormBuilder.CheckBox.prototype.build.apply(this)
748
- if (this.options.inheritable)
749
- this.label = L.DomUtil.create('label', '', this.input.parentNode)
750
- else this.input.parentNode.appendChild(this.label)
751
- L.DomUtil.addClass(this.input.parentNode, 'with-switch')
752
- const id = `${this.builder.options.id || Date.now()}.${this.name}`
753
- this.label.setAttribute('for', id)
754
- L.DomUtil.addClass(this.input, 'switch')
755
- this.input.id = id
756
- },
757
- })
758
-
759
- L.FormBuilder.FacetSearchBase = L.FormBuilder.Element.extend({
760
- buildLabel: function () {
761
- this.label = L.DomUtil.element({
762
- tagName: 'legend',
763
- textContent: this.options.label,
764
- })
765
- },
766
- })
767
- L.FormBuilder.FacetSearchChoices = L.FormBuilder.FacetSearchBase.extend({
768
- build: function () {
769
- this.container = L.DomUtil.create('fieldset', 'umap-facet', this.parentNode)
770
- this.container.appendChild(this.label)
771
- this.ul = L.DomUtil.create('ul', '', this.container)
772
- this.type = this.options.criteria.type
773
-
774
- const choices = this.options.criteria.choices
775
- choices.sort()
776
- choices.forEach((value) => this.buildLi(value))
777
- },
778
-
779
- buildLi: function (value) {
780
- const property_li = L.DomUtil.create('li', '', this.ul)
781
- const label = L.DomUtil.create('label', '', property_li)
782
- const input = L.DomUtil.create('input', '', label)
783
- L.DomUtil.add('span', '', label, value)
784
-
785
- input.type = this.type
786
- input.name = `${this.type}_${this.name}`
787
- input.checked = this.get().choices.includes(value)
788
- input.dataset.value = value
789
-
790
- L.DomEvent.on(input, 'change', (e) => this.sync())
791
- },
792
-
793
- toJS: function () {
794
- return {
795
- type: this.type,
796
- choices: [...this.ul.querySelectorAll('input:checked')].map(
797
- (i) => i.dataset.value
798
- ),
799
- }
800
- },
801
- })
802
-
803
- L.FormBuilder.MinMaxBase = L.FormBuilder.FacetSearchBase.extend({
804
- getInputType: (type) => type,
805
-
806
- getLabels: () => [L._('Min'), L._('Max')],
807
-
808
- prepareForHTML: (value) => value.valueOf(),
809
-
810
- build: function () {
811
- this.container = L.DomUtil.create('fieldset', 'umap-facet', this.parentNode)
812
- this.container.appendChild(this.label)
813
- const { min, max, type } = this.options.criteria
814
- const { min: modifiedMin, max: modifiedMax } = this.get()
815
-
816
- const currentMin = modifiedMin !== undefined ? modifiedMin : min
817
- const currentMax = modifiedMax !== undefined ? modifiedMax : max
818
- this.type = type
819
- this.inputType = this.getInputType(this.type)
820
-
821
- const [minLabel, maxLabel] = this.getLabels()
822
-
823
- this.minLabel = L.DomUtil.create('label', '', this.container)
824
- this.minLabel.textContent = minLabel
825
-
826
- this.minInput = L.DomUtil.create('input', '', this.minLabel)
827
- this.minInput.type = this.inputType
828
- this.minInput.step = 'any'
829
- this.minInput.min = this.prepareForHTML(min)
830
- this.minInput.max = this.prepareForHTML(max)
831
- if (min != null) {
832
- // The value stored using setAttribute is not modified by
833
- // user input, and will be used as initial value when calling
834
- // form.reset(), and can also be retrieve later on by using
835
- // getAttributing, to compare with current value and know
836
- // if this value has been modified by the user
837
- // https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormElement/reset
838
- this.minInput.setAttribute('value', this.prepareForHTML(min))
839
- this.minInput.value = this.prepareForHTML(currentMin)
840
- }
841
-
842
- this.maxLabel = L.DomUtil.create('label', '', this.container)
843
- this.maxLabel.textContent = maxLabel
844
-
845
- this.maxInput = L.DomUtil.create('input', '', this.maxLabel)
846
- this.maxInput.type = this.inputType
847
- this.maxInput.step = 'any'
848
- this.maxInput.min = this.prepareForHTML(min)
849
- this.maxInput.max = this.prepareForHTML(max)
850
- if (max != null) {
851
- // Cf comment above about setAttribute vs value
852
- this.maxInput.setAttribute('value', this.prepareForHTML(max))
853
- this.maxInput.value = this.prepareForHTML(currentMax)
854
- }
855
- this.toggleStatus()
856
-
857
- L.DomEvent.on(this.minInput, 'change', () => this.sync())
858
- L.DomEvent.on(this.maxInput, 'change', () => this.sync())
859
- },
860
-
861
- toggleStatus: function () {
862
- this.minInput.dataset.modified = this.isMinModified()
863
- this.maxInput.dataset.modified = this.isMaxModified()
864
- },
865
-
866
- sync: function () {
867
- L.FormBuilder.Element.prototype.sync.call(this)
868
- this.toggleStatus()
869
- },
870
-
871
- isMinModified: function () {
872
- const default_ = this.minInput.getAttribute('value')
873
- const current = this.minInput.value
874
- return current !== default_
875
- },
876
-
877
- isMaxModified: function () {
878
- const default_ = this.maxInput.getAttribute('value')
879
- const current = this.maxInput.value
880
- return current !== default_
881
- },
882
-
883
- toJS: function () {
884
- const opts = {
885
- type: this.type,
886
- }
887
- if (this.minInput.value !== '' && this.isMinModified()) {
888
- opts.min = this.prepareForJS(this.minInput.value)
889
- }
890
- if (this.maxInput.value !== '' && this.isMaxModified()) {
891
- opts.max = this.prepareForJS(this.maxInput.value)
892
- }
893
- return opts
894
- },
895
- })
896
-
897
- L.FormBuilder.FacetSearchNumber = L.FormBuilder.MinMaxBase.extend({
898
- prepareForJS: (value) => new Number(value),
899
- })
900
-
901
- L.FormBuilder.FacetSearchDate = L.FormBuilder.MinMaxBase.extend({
902
- prepareForJS: (value) => new Date(value),
903
-
904
- toLocaleDateTime: (dt) => new Date(dt.valueOf() - dt.getTimezoneOffset() * 60000),
905
-
906
- prepareForHTML: function (value) {
907
- // Value must be in local time
908
- if (Number.isNaN(value)) return
909
- return this.toLocaleDateTime(value).toISOString().substr(0, 10)
910
- },
911
-
912
- getLabels: () => [L._('From'), L._('Until')],
913
- })
914
-
915
- L.FormBuilder.FacetSearchDateTime = L.FormBuilder.FacetSearchDate.extend({
916
- getInputType: (type) => 'datetime-local',
917
-
918
- prepareForHTML: function (value) {
919
- // Value must be in local time
920
- if (Number.isNaN(value)) return
921
- return this.toLocaleDateTime(value).toISOString().slice(0, -1)
922
- },
923
- })
924
-
925
- L.FormBuilder.MultiChoice = L.FormBuilder.Element.extend({
926
- default: 'null',
927
- className: 'umap-multiplechoice',
928
-
929
- clear: function () {
930
- const checked = this.container.querySelector('input[type="radio"]:checked')
931
- if (checked) checked.checked = false
932
- },
933
-
934
- fetch: function () {
935
- this.initial = this.toHTML()
936
- let value = this.initial
937
- if (!this.container.querySelector(`input[type="radio"][value="${value}"]`)) {
938
- value = this.options.default !== undefined ? this.options.default : this.default
939
- }
940
- const choices = this.getChoices().map(([value, label]) => `${value}`)
941
- if (choices.includes(`${value}`)) {
942
- this.container.querySelector(`input[type="radio"][value="${value}"]`).checked =
943
- true
944
- }
945
- },
946
-
947
- value: function () {
948
- const checked = this.container.querySelector('input[type="radio"]:checked')
949
- if (checked) return checked.value
950
- },
951
-
952
- getChoices: function () {
953
- return this.options.choices || this.choices
954
- },
955
-
956
- build: function () {
957
- const choices = this.getChoices()
958
- this.container = L.DomUtil.create(
959
- 'div',
960
- `${this.className} by${choices.length}`,
961
- this.parentNode
962
- )
963
- for (const [i, [value, label]] of choices.entries()) {
964
- this.addChoice(value, label, i)
965
- }
966
- this.fetch()
967
- },
968
-
969
- addChoice: function (value, label, counter) {
970
- const input = L.DomUtil.create('input', '', this.container)
971
- label = L.DomUtil.add('label', '', this.container, label)
972
- input.type = 'radio'
973
- input.name = this.name
974
- input.value = value
975
- const id = `${Date.now()}.${this.name}.${counter}`
976
- label.setAttribute('for', id)
977
- input.id = id
978
- L.DomEvent.on(input, 'change', this.sync, this)
979
- },
980
- })
981
-
982
- L.FormBuilder.TernaryChoices = L.FormBuilder.MultiChoice.extend({
983
- default: 'null',
984
-
985
- toJS: function () {
986
- let value = this.value()
987
- switch (value) {
988
- case 'true':
989
- case true:
990
- value = true
991
- break
992
- case 'false':
993
- case false:
994
- value = false
995
- break
996
- case 'null':
997
- case null:
998
- value = null
999
- break
1000
- default:
1001
- value = undefined
1002
- }
1003
- return value
1004
- },
1005
- })
1006
-
1007
- L.FormBuilder.NullableChoices = L.FormBuilder.TernaryChoices.extend({
1008
- choices: [
1009
- [true, L._('always')],
1010
- [false, L._('never')],
1011
- ['null', L._('hidden')],
1012
- ],
1013
- })
1014
-
1015
- L.FormBuilder.DataLayersControl = L.FormBuilder.TernaryChoices.extend({
1016
- choices: [
1017
- [true, L._('collapsed')],
1018
- ['expanded', L._('expanded')],
1019
- [false, L._('never')],
1020
- ['null', L._('hidden')],
1021
- ],
1022
-
1023
- toJS: function () {
1024
- let value = this.value()
1025
- if (value !== 'expanded')
1026
- value = L.FormBuilder.TernaryChoices.prototype.toJS.call(this)
1027
- return value
1028
- },
1029
- })
1030
-
1031
- L.FormBuilder.Range = L.FormBuilder.FloatInput.extend({
1032
- type: () => 'range',
1033
-
1034
- value: function () {
1035
- return L.DomUtil.hasClass(this.wrapper, 'undefined')
1036
- ? undefined
1037
- : L.FormBuilder.FloatInput.prototype.value.call(this)
1038
- },
1039
-
1040
- buildHelpText: function () {
1041
- let options = ''
1042
- const step = this.options.step || 1
1043
- const digits = step < 1 ? 1 : 0
1044
- const id = `range-${this.options.label || this.name}`
1045
- for (let i = this.options.min; i <= this.options.max; i += this.options.step) {
1046
- options += `<option value="${i.toFixed(digits)}" label="${i.toFixed(
1047
- digits
1048
- )}"></option>`
1049
- }
1050
- const datalist = L.DomUtil.element({
1051
- tagName: 'datalist',
1052
- parent: this.getHelpTextParent(),
1053
- className: 'umap-field-datalist',
1054
- safeHTML: options,
1055
- id: id,
1056
- })
1057
- this.input.setAttribute('list', id)
1058
- L.FormBuilder.Input.prototype.buildHelpText.call(this)
1059
- },
1060
- })
1061
-
1062
- L.FormBuilder.ManageOwner = L.FormBuilder.Element.extend({
1063
- build: function () {
1064
- const options = {
1065
- className: 'edit-owner',
1066
- on_select: L.bind(this.onSelect, this),
1067
- placeholder: L._("Type new owner's username"),
1068
- }
1069
- this.autocomplete = new U.AjaxAutocomplete(this.parentNode, options)
1070
- const owner = this.toHTML()
1071
- if (owner)
1072
- this.autocomplete.displaySelected({
1073
- item: { value: owner.id, label: owner.name },
1074
- })
1075
- },
1076
-
1077
- value: function () {
1078
- return this._value
1079
- },
1080
-
1081
- onSelect: function (choice) {
1082
- this._value = {
1083
- id: choice.item.value,
1084
- name: choice.item.label,
1085
- url: choice.item.url,
1086
- }
1087
- this.set()
1088
- },
1089
- })
1090
-
1091
- L.FormBuilder.ManageEditors = L.FormBuilder.Element.extend({
1092
- build: function () {
1093
- const options = {
1094
- className: 'edit-editors',
1095
- on_select: L.bind(this.onSelect, this),
1096
- on_unselect: L.bind(this.onUnselect, this),
1097
- placeholder: L._("Type editor's username"),
1098
- }
1099
- this.autocomplete = new U.AjaxAutocompleteMultiple(this.parentNode, options)
1100
- this._values = this.toHTML()
1101
- if (this._values)
1102
- for (let i = 0; i < this._values.length; i++)
1103
- this.autocomplete.displaySelected({
1104
- item: { value: this._values[i].id, label: this._values[i].name },
1105
- })
1106
- },
1107
-
1108
- value: function () {
1109
- return this._values
1110
- },
1111
-
1112
- onSelect: function (choice) {
1113
- this._values.push({
1114
- id: choice.item.value,
1115
- name: choice.item.label,
1116
- url: choice.item.url,
1117
- })
1118
- this.set()
1119
- },
1120
-
1121
- onUnselect: function (choice) {
1122
- const index = this._values.findIndex((item) => item.id === choice.item.value)
1123
- if (index !== -1) {
1124
- this._values.splice(index, 1)
1125
- this.set()
1126
- }
1127
- },
1128
- })
1129
-
1130
- L.FormBuilder.ManageTeam = L.FormBuilder.IntSelect.extend({
1131
- getOptions: function () {
1132
- return [[null, L._('None')]].concat(
1133
- this.options.teams.map((team) => [team.id, team.name])
1134
- )
1135
- },
1136
- toHTML: function () {
1137
- return this.get()?.id
1138
- },
1139
- toJS: function () {
1140
- const value = this.value()
1141
- for (const team of this.options.teams) {
1142
- if (team.id === value) return team
1143
- }
1144
- },
1145
- })
1146
-
1147
- U.FormBuilder = L.FormBuilder.extend({
1148
- options: {
1149
- className: 'umap-form',
1150
- },
1151
-
1152
- customHandlers: {
1153
- sortKey: 'PropertyInput',
1154
- easing: 'Switch',
1155
- facetKey: 'PropertyInput',
1156
- slugKey: 'PropertyInput',
1157
- labelKey: 'PropertyInput',
1158
- },
1159
-
1160
- computeDefaultOptions: function () {
1161
- for (const [key, schema] of Object.entries(U.SCHEMA)) {
1162
- if (schema.type === Boolean) {
1163
- if (schema.nullable) schema.handler = 'NullableChoices'
1164
- else schema.handler = 'Switch'
1165
- } else if (schema.type === 'Text') {
1166
- schema.handler = 'Textarea'
1167
- } else if (schema.type === Number) {
1168
- if (schema.step) schema.handler = 'Range'
1169
- else schema.handler = 'IntInput'
1170
- } else if (schema.choices) {
1171
- const text_length = schema.choices.reduce(
1172
- (acc, [_, label]) => acc + label.length,
1173
- 0
1174
- )
1175
- // Try to be smart and use MultiChoice only
1176
- // for choices where labels are shorts…
1177
- if (text_length < 40) {
1178
- schema.handler = 'MultiChoice'
1179
- } else {
1180
- schema.handler = 'Select'
1181
- schema.selectOptions = schema.choices
1182
- }
1183
- } else {
1184
- switch (key) {
1185
- case 'color':
1186
- case 'fillColor':
1187
- schema.handler = 'ColorPicker'
1188
- break
1189
- case 'iconUrl':
1190
- schema.handler = 'IconUrl'
1191
- break
1192
- case 'licence':
1193
- schema.handler = 'LicenceChooser'
1194
- break
1195
- }
1196
- }
1197
- if (this.customHandlers[key]) {
1198
- schema.handler = this.customHandlers[key]
1199
- }
1200
- // FormBuilder use this key for the input type itself
1201
- delete schema.type
1202
- this.defaultOptions[key] = schema
1203
- }
1204
- },
1205
-
1206
- initialize: function (obj, fields, options = {}) {
1207
- this._umap = obj._umap || options.umap
1208
- this.computeDefaultOptions()
1209
- L.FormBuilder.prototype.initialize.call(this, obj, fields, options)
1210
- this.on('finish', this.finish)
1211
- },
1212
-
1213
- setter: function (field, value) {
1214
- L.FormBuilder.prototype.setter.call(this, field, value)
1215
- this.obj.isDirty = true
1216
- if ('render' in this.obj) {
1217
- this.obj.render([field], this)
1218
- }
1219
- if ('sync' in this.obj) {
1220
- this.obj.sync.update(field, value)
1221
- }
1222
- },
1223
-
1224
- getter: function (field) {
1225
- const path = field.split('.')
1226
- let value = this.obj
1227
- let sub
1228
- for (sub of path) {
1229
- try {
1230
- value = value[sub]
1231
- } catch {
1232
- console.log(field)
1233
- }
1234
- }
1235
- if (value === undefined) values = U.SCHEMA[sub]?.default
1236
- return value
1237
- },
1238
-
1239
- finish: (event) => {
1240
- event.helper?.input?.blur()
1241
- },
1242
- })