umap-project 3.3.6__py3-none-any.whl → 3.4.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.
- umap/__init__.py +1 -1
- umap/context_processors.py +4 -1
- umap/locale/cs_CZ/LC_MESSAGES/django.mo +0 -0
- umap/locale/cs_CZ/LC_MESSAGES/django.po +43 -33
- umap/locale/da/LC_MESSAGES/django.mo +0 -0
- umap/locale/da/LC_MESSAGES/django.po +43 -33
- umap/locale/de/LC_MESSAGES/django.mo +0 -0
- umap/locale/de/LC_MESSAGES/django.po +35 -29
- umap/locale/el/LC_MESSAGES/django.mo +0 -0
- umap/locale/el/LC_MESSAGES/django.po +35 -29
- umap/locale/en/LC_MESSAGES/django.po +47 -41
- umap/locale/es/LC_MESSAGES/django.mo +0 -0
- umap/locale/es/LC_MESSAGES/django.po +43 -33
- umap/locale/et/LC_MESSAGES/django.mo +0 -0
- umap/locale/et/LC_MESSAGES/django.po +58 -54
- umap/locale/eu/LC_MESSAGES/django.mo +0 -0
- umap/locale/eu/LC_MESSAGES/django.po +43 -33
- umap/locale/fa_IR/LC_MESSAGES/django.mo +0 -0
- umap/locale/fa_IR/LC_MESSAGES/django.po +43 -33
- umap/locale/fr/LC_MESSAGES/django.mo +0 -0
- umap/locale/fr/LC_MESSAGES/django.po +36 -30
- umap/locale/gl/LC_MESSAGES/django.mo +0 -0
- umap/locale/gl/LC_MESSAGES/django.po +43 -33
- umap/locale/hu/LC_MESSAGES/django.mo +0 -0
- umap/locale/hu/LC_MESSAGES/django.po +35 -29
- umap/locale/is/LC_MESSAGES/django.mo +0 -0
- umap/locale/is/LC_MESSAGES/django.po +43 -33
- umap/locale/it/LC_MESSAGES/django.mo +0 -0
- umap/locale/it/LC_MESSAGES/django.po +43 -33
- umap/locale/nl/LC_MESSAGES/django.mo +0 -0
- umap/locale/nl/LC_MESSAGES/django.po +35 -29
- umap/locale/pl/LC_MESSAGES/django.mo +0 -0
- umap/locale/pl/LC_MESSAGES/django.po +114 -103
- umap/locale/pt/LC_MESSAGES/django.mo +0 -0
- umap/locale/pt/LC_MESSAGES/django.po +43 -33
- umap/locale/th_TH/LC_MESSAGES/django.mo +0 -0
- umap/locale/th_TH/LC_MESSAGES/django.po +310 -109
- umap/locale/zh_TW/LC_MESSAGES/django.mo +0 -0
- umap/locale/zh_TW/LC_MESSAGES/django.po +80 -70
- umap/management/commands/switch_user.py +2 -2
- umap/migrations/0018_datalayer_uuid.py +1 -1
- umap/models.py +7 -3
- umap/settings/local.py.sample +1 -1
- umap/static/umap/base.css +89 -32
- umap/static/umap/content.css +129 -33
- umap/static/umap/css/bar.css +82 -20
- umap/static/umap/css/browser.css +163 -0
- umap/static/umap/css/contextmenu.css +15 -0
- umap/static/umap/css/dialog.css +36 -16
- umap/static/umap/css/form.css +123 -33
- umap/static/umap/css/icon.css +46 -3
- umap/static/umap/css/panel.css +7 -3
- umap/static/umap/css/popup.css +34 -8
- umap/static/umap/css/tooltip.css +8 -4
- umap/static/umap/img/16-white.svg +26 -8
- umap/static/umap/img/16.svg +1 -1
- umap/static/umap/img/source/16-white.svg +36 -18
- umap/static/umap/img/source/16.svg +1 -1
- umap/static/umap/js/components/alerts/alert.css +69 -31
- umap/static/umap/js/components/alerts/alert.js +20 -2
- umap/static/umap/js/components/base.js +1 -1
- umap/static/umap/js/modules/browser.js +69 -61
- umap/static/umap/js/modules/caption.js +10 -7
- umap/static/umap/js/modules/data/features.js +85 -60
- umap/static/umap/js/modules/data/fields.js +446 -0
- umap/static/umap/js/modules/data/layer.js +78 -184
- umap/static/umap/js/modules/domutils.js +109 -0
- umap/static/umap/js/modules/filters.js +780 -0
- umap/static/umap/js/modules/form/builder.js +8 -5
- umap/static/umap/js/modules/form/fields.js +111 -221
- umap/static/umap/js/modules/formatter.js +24 -1
- umap/static/umap/js/modules/help.js +4 -3
- umap/static/umap/js/modules/i18n.js +1 -1
- umap/static/umap/js/modules/importer.js +1 -1
- umap/static/umap/js/modules/importers/opendata.js +15 -0
- umap/static/umap/js/modules/importers/openrouteservice.js +6 -1
- umap/static/umap/js/modules/managers.js +2 -2
- umap/static/umap/js/modules/permissions.js +39 -31
- umap/static/umap/js/modules/rendering/controls.js +11 -9
- umap/static/umap/js/modules/rendering/icon.js +3 -8
- umap/static/umap/js/modules/rendering/layers/base.js +1 -1
- umap/static/umap/js/modules/rendering/layers/classified.js +18 -11
- umap/static/umap/js/modules/rendering/layers/cluster.js +5 -3
- umap/static/umap/js/modules/rendering/layers/heat.js +27 -21
- umap/static/umap/js/modules/rendering/template.js +50 -23
- umap/static/umap/js/modules/rendering/ui.js +29 -23
- umap/static/umap/js/modules/rules.js +38 -44
- umap/static/umap/js/modules/schema.js +3 -6
- umap/static/umap/js/modules/share.js +5 -4
- umap/static/umap/js/modules/tableeditor.js +50 -38
- umap/static/umap/js/modules/templates.js +2 -3
- umap/static/umap/js/modules/ui/bar.js +55 -23
- umap/static/umap/js/modules/ui/dialog.js +38 -27
- umap/static/umap/js/modules/ui/panel.js +23 -8
- umap/static/umap/js/modules/ui/tooltip.js +6 -5
- umap/static/umap/js/modules/umap.js +151 -56
- umap/static/umap/js/modules/utils.js +24 -2
- umap/static/umap/js/umap.core.js +1 -110
- umap/static/umap/locale/am_ET.js +52 -17
- umap/static/umap/locale/am_ET.json +52 -17
- umap/static/umap/locale/ar.js +52 -17
- umap/static/umap/locale/ar.json +52 -17
- umap/static/umap/locale/ast.js +52 -17
- umap/static/umap/locale/ast.json +52 -17
- umap/static/umap/locale/bg.js +52 -17
- umap/static/umap/locale/bg.json +52 -17
- umap/static/umap/locale/br.js +48 -22
- umap/static/umap/locale/br.json +48 -22
- umap/static/umap/locale/ca.js +52 -17
- umap/static/umap/locale/ca.json +52 -17
- umap/static/umap/locale/cs_CZ.js +52 -17
- umap/static/umap/locale/cs_CZ.json +52 -17
- umap/static/umap/locale/da.js +54 -17
- umap/static/umap/locale/da.json +54 -17
- umap/static/umap/locale/de.js +51 -16
- umap/static/umap/locale/de.json +51 -16
- umap/static/umap/locale/el.js +52 -17
- umap/static/umap/locale/el.json +52 -17
- umap/static/umap/locale/en.js +53 -16
- umap/static/umap/locale/en.json +53 -16
- umap/static/umap/locale/en_US.json +52 -17
- umap/static/umap/locale/es.js +54 -17
- umap/static/umap/locale/es.json +54 -17
- umap/static/umap/locale/et.js +91 -56
- umap/static/umap/locale/et.json +91 -56
- umap/static/umap/locale/eu.js +84 -49
- umap/static/umap/locale/eu.json +84 -49
- umap/static/umap/locale/fa_IR.js +52 -17
- umap/static/umap/locale/fa_IR.json +52 -17
- umap/static/umap/locale/fi.js +52 -17
- umap/static/umap/locale/fi.json +52 -17
- umap/static/umap/locale/fr.js +53 -16
- umap/static/umap/locale/fr.json +53 -16
- umap/static/umap/locale/gl.js +52 -17
- umap/static/umap/locale/gl.json +52 -17
- umap/static/umap/locale/he.js +52 -17
- umap/static/umap/locale/he.json +52 -17
- umap/static/umap/locale/hr.js +52 -17
- umap/static/umap/locale/hr.json +52 -17
- umap/static/umap/locale/hu.js +59 -24
- umap/static/umap/locale/hu.json +59 -24
- umap/static/umap/locale/id.js +52 -17
- umap/static/umap/locale/id.json +52 -17
- umap/static/umap/locale/is.js +52 -17
- umap/static/umap/locale/is.json +52 -17
- umap/static/umap/locale/it.js +52 -17
- umap/static/umap/locale/it.json +52 -17
- umap/static/umap/locale/ja.js +52 -17
- umap/static/umap/locale/ja.json +52 -17
- umap/static/umap/locale/ko.js +52 -17
- umap/static/umap/locale/ko.json +52 -17
- umap/static/umap/locale/lt.js +52 -17
- umap/static/umap/locale/lt.json +52 -17
- umap/static/umap/locale/ms.js +52 -17
- umap/static/umap/locale/ms.json +52 -17
- umap/static/umap/locale/nl.js +52 -17
- umap/static/umap/locale/nl.json +52 -17
- umap/static/umap/locale/no.js +52 -17
- umap/static/umap/locale/no.json +52 -17
- umap/static/umap/locale/pl.js +53 -17
- umap/static/umap/locale/pl.json +53 -17
- umap/static/umap/locale/pl_PL.json +52 -17
- umap/static/umap/locale/pt.js +52 -17
- umap/static/umap/locale/pt.json +52 -17
- umap/static/umap/locale/pt_BR.js +52 -17
- umap/static/umap/locale/pt_BR.json +52 -17
- umap/static/umap/locale/pt_PT.js +52 -17
- umap/static/umap/locale/pt_PT.json +52 -17
- umap/static/umap/locale/ro.js +52 -17
- umap/static/umap/locale/ro.json +52 -17
- umap/static/umap/locale/ru.js +52 -17
- umap/static/umap/locale/ru.json +52 -17
- umap/static/umap/locale/si.js +1 -1
- umap/static/umap/locale/si.json +1 -1
- umap/static/umap/locale/sk_SK.js +52 -17
- umap/static/umap/locale/sk_SK.json +52 -17
- umap/static/umap/locale/sl.js +52 -17
- umap/static/umap/locale/sl.json +52 -17
- umap/static/umap/locale/sr.js +52 -17
- umap/static/umap/locale/sr.json +52 -17
- umap/static/umap/locale/sv.js +52 -17
- umap/static/umap/locale/sv.json +52 -17
- umap/static/umap/locale/th_TH.js +52 -17
- umap/static/umap/locale/th_TH.json +52 -17
- umap/static/umap/locale/tr.js +52 -17
- umap/static/umap/locale/tr.json +52 -17
- umap/static/umap/locale/uk_UA.js +52 -17
- umap/static/umap/locale/uk_UA.json +52 -17
- umap/static/umap/locale/vi.js +52 -17
- umap/static/umap/locale/vi.json +52 -17
- umap/static/umap/locale/vi_VN.json +52 -17
- umap/static/umap/locale/zh.js +52 -17
- umap/static/umap/locale/zh.json +52 -17
- umap/static/umap/locale/zh_CN.json +52 -17
- umap/static/umap/locale/zh_TW.Big5.json +52 -17
- umap/static/umap/locale/zh_TW.js +52 -16
- umap/static/umap/locale/zh_TW.json +52 -16
- umap/static/umap/map.css +63 -226
- umap/static/umap/unittests/utils.js +18 -0
- umap/static/umap/vars.css +23 -5
- umap/templates/umap/components/alerts/alert.html +32 -29
- umap/templates/umap/css.html +2 -1
- umap/templates/umap/login_popup_end.html +18 -9
- umap/templates/umap/user_map_table.html +7 -2
- umap/tests/integration/conftest.py +10 -6
- umap/tests/integration/test_anonymous_owned_map.py +90 -37
- umap/tests/integration/test_basics.py +25 -1
- umap/tests/integration/test_browser.py +37 -0
- umap/tests/integration/test_conditional_rules.py +107 -52
- umap/tests/integration/test_draw_polygon.py +6 -0
- umap/tests/integration/test_draw_polyline.py +11 -0
- umap/tests/integration/test_edit_marker.py +1 -1
- umap/tests/integration/test_export_map.py +19 -0
- umap/tests/integration/test_fields.py +541 -0
- umap/tests/integration/test_filters.py +616 -0
- umap/tests/integration/test_iframe.py +1 -1
- umap/tests/integration/test_import.py +38 -42
- umap/tests/integration/test_map_preview.py +1 -1
- umap/tests/integration/test_picto.py +1 -1
- umap/tests/integration/test_popup.py +31 -0
- umap/tests/integration/test_remote_data.py +60 -4
- umap/tests/integration/test_save.py +1 -1
- umap/tests/integration/test_share.py +4 -4
- umap/tests/integration/test_tableeditor.py +31 -7
- umap/tests/integration/test_websocket_sync.py +71 -20
- umap/tests/test_dashboard.py +11 -1
- umap/tests/test_statics.py +2 -2
- umap/tests/test_utils.py +19 -2
- umap/tests/test_views.py +1 -1
- umap/urls.py +1 -0
- umap/utils.py +8 -1
- umap/views.py +5 -0
- {umap_project-3.3.6.dist-info → umap_project-3.4.0.dist-info}/METADATA +15 -15
- {umap_project-3.3.6.dist-info → umap_project-3.4.0.dist-info}/RECORD +237 -233
- umap/static/umap/js/modules/facets.js +0 -164
- umap/tests/integration/test_facets_browser.py +0 -279
- {umap_project-3.3.6.dist-info → umap_project-3.4.0.dist-info}/WHEEL +0 -0
- {umap_project-3.3.6.dist-info → umap_project-3.4.0.dist-info}/entry_points.txt +0 -0
- {umap_project-3.3.6.dist-info → umap_project-3.4.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -6,6 +6,7 @@ import Orderable from './orderable.js'
|
|
|
6
6
|
import * as Utils from './utils.js'
|
|
7
7
|
import * as Icon from './rendering/icon.js'
|
|
8
8
|
import { SCHEMA } from './schema.js'
|
|
9
|
+
import { Registry as Fields } from './data/fields.js'
|
|
9
10
|
|
|
10
11
|
const EMPTY_VALUES = ['', undefined, null]
|
|
11
12
|
|
|
@@ -28,12 +29,12 @@ class Rule {
|
|
|
28
29
|
// cf https://caniuse.com/?search=public%20class%20field
|
|
29
30
|
this._condition = null
|
|
30
31
|
this.OPERATORS = [
|
|
31
|
-
['>',
|
|
32
|
-
['<',
|
|
32
|
+
['>', 'gt'],
|
|
33
|
+
['<', 'lt'],
|
|
33
34
|
// When sent by Django
|
|
34
|
-
['<',
|
|
35
|
-
['!=',
|
|
36
|
-
['=',
|
|
35
|
+
['<', 'lt'],
|
|
36
|
+
['!=', 'not_equal'],
|
|
37
|
+
['=', 'equal'],
|
|
37
38
|
]
|
|
38
39
|
this.parent = parent
|
|
39
40
|
this._umap = umap
|
|
@@ -47,58 +48,47 @@ class Rule {
|
|
|
47
48
|
this.parent.render(fields)
|
|
48
49
|
}
|
|
49
50
|
|
|
50
|
-
equal(other) {
|
|
51
|
-
return this.expected === other
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
not_equal(other) {
|
|
55
|
-
return this.expected !== other
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
gt(other) {
|
|
59
|
-
return other > this.expected
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
lt(other) {
|
|
63
|
-
return other < this.expected
|
|
64
|
-
}
|
|
65
|
-
|
|
66
51
|
parse() {
|
|
67
52
|
let vars = []
|
|
68
53
|
this.cast = (v) => v
|
|
69
54
|
this.operator = undefined
|
|
70
|
-
|
|
55
|
+
let operator = undefined
|
|
56
|
+
for (const [sign, funcName] of this.OPERATORS) {
|
|
71
57
|
if (this.condition.includes(sign)) {
|
|
72
|
-
|
|
58
|
+
operator = funcName
|
|
73
59
|
vars = this.condition.split(sign)
|
|
74
60
|
break
|
|
75
61
|
}
|
|
76
62
|
}
|
|
77
63
|
if (vars.length !== 2) return
|
|
78
|
-
this.
|
|
64
|
+
this.field = this.parent.fields.get(vars[0]) || new Fields.String(vars[0])
|
|
65
|
+
this.operator = this.field[operator]
|
|
79
66
|
this.expected = vars[1]
|
|
80
67
|
if (EMPTY_VALUES.includes(this.expected)) {
|
|
81
68
|
this.cast = (v) => EMPTY_VALUES.includes(v)
|
|
82
69
|
}
|
|
83
|
-
//
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
70
|
+
// TODO: deal with legacy rules on non typed fields
|
|
71
|
+
else {
|
|
72
|
+
this.cast = this.field.parse
|
|
73
|
+
if (
|
|
74
|
+
// Special cases where we want to be lousy when checking isNaN without
|
|
75
|
+
// coercing to a Number first because we handle multiple types.
|
|
76
|
+
// See https://developer.mozilla.org/en-US/docs/Web/JavaScript/
|
|
77
|
+
// Reference/Global_Objects/Number/isNaN
|
|
78
|
+
// biome-ignore lint/suspicious/noGlobalIsNan: expected might not be a number.
|
|
79
|
+
!isNaN(this.expected) &&
|
|
80
|
+
['gt', 'lt'].includes(operator) &&
|
|
81
|
+
this.field.TYPE !== 'Number'
|
|
82
|
+
) {
|
|
83
|
+
this.cast = Number.parseFloat
|
|
94
84
|
}
|
|
95
85
|
}
|
|
96
86
|
this.expected = this.cast(this.expected)
|
|
97
87
|
}
|
|
98
88
|
|
|
99
89
|
match(props) {
|
|
100
|
-
if (!this.operator || !this.active) return false
|
|
101
|
-
return this.operator(this.cast(props[this.key]))
|
|
90
|
+
if (!this.operator || !this.active || !this.field) return false
|
|
91
|
+
return this.operator(this.expected, this.cast(props[this.field.key]))
|
|
102
92
|
}
|
|
103
93
|
|
|
104
94
|
getOption(option) {
|
|
@@ -118,6 +108,7 @@ class Rule {
|
|
|
118
108
|
'name',
|
|
119
109
|
'properties.color',
|
|
120
110
|
'properties.iconClass',
|
|
111
|
+
'properties.iconSize',
|
|
121
112
|
'properties.iconUrl',
|
|
122
113
|
'properties.iconOpacity',
|
|
123
114
|
'properties.opacity',
|
|
@@ -132,7 +123,7 @@ class Rule {
|
|
|
132
123
|
const container = document.createElement('div')
|
|
133
124
|
container.appendChild(builder.build())
|
|
134
125
|
const autocomplete = new AutocompleteDatalist(builder.helpers.condition.input)
|
|
135
|
-
const properties = this.parent.
|
|
126
|
+
const properties = Array.from(this.parent.fields.keys())
|
|
136
127
|
autocomplete.suggestions = properties
|
|
137
128
|
autocomplete.input.addEventListener('input', (event) => {
|
|
138
129
|
const value = event.target.value
|
|
@@ -140,9 +131,11 @@ class Rule {
|
|
|
140
131
|
autocomplete.suggestions = [`${value}=`, `${value}!=`, `${value}>`, `${value}<`]
|
|
141
132
|
} else if (value.endsWith('=')) {
|
|
142
133
|
const key = value.split('!')[0].split('=')[0]
|
|
143
|
-
|
|
144
|
-
.
|
|
145
|
-
|
|
134
|
+
if (key) {
|
|
135
|
+
autocomplete.suggestions = this.parent
|
|
136
|
+
.sortedValues(key)
|
|
137
|
+
.map((str) => `${value}${str ?? ''}`)
|
|
138
|
+
}
|
|
146
139
|
}
|
|
147
140
|
})
|
|
148
141
|
const backButton = Utils.loadTemplate(`
|
|
@@ -207,7 +200,8 @@ class Rule {
|
|
|
207
200
|
const [li, { colorBox }] = Utils.loadTemplateWithRefs(
|
|
208
201
|
`<li><span class="color-box" data-ref=colorBox></span>${this.label}</li>`
|
|
209
202
|
)
|
|
210
|
-
const bgcolor =
|
|
203
|
+
const bgcolor =
|
|
204
|
+
this.properties.fillColor || this.properties.color || this.parent.getColor()
|
|
211
205
|
const symbol = this.properties.iconUrl
|
|
212
206
|
colorBox.style.backgroundColor = bgcolor
|
|
213
207
|
if (symbol && symbol !== SCHEMA.iconUrl.default) {
|
|
@@ -265,10 +259,10 @@ export default class Rules {
|
|
|
265
259
|
edit(container) {
|
|
266
260
|
const template = `
|
|
267
261
|
<details id="rules">
|
|
268
|
-
<summary>${translate('Conditional style rules')}</summary>
|
|
262
|
+
<summary><h4>${translate('Conditional style rules')}</h4></summary>
|
|
269
263
|
<fieldset>
|
|
270
264
|
<ul data-ref=ul></ul>
|
|
271
|
-
<button
|
|
265
|
+
<button type="button" data-ref=add>${translate('Add rule')}</button>
|
|
272
266
|
</fieldset>
|
|
273
267
|
</details>
|
|
274
268
|
`
|
|
@@ -140,15 +140,12 @@ export const SCHEMA = {
|
|
|
140
140
|
label: translate('Display the embed control'),
|
|
141
141
|
default: true,
|
|
142
142
|
},
|
|
143
|
-
|
|
144
|
-
type:
|
|
143
|
+
filters: {
|
|
144
|
+
type: Array,
|
|
145
145
|
impacts: ['ui'],
|
|
146
|
-
helpEntries: ['facetKey'],
|
|
147
|
-
placeholder: translate('Example: key1,key2|Label 2,key3|Label 3|checkbox'),
|
|
148
|
-
label: translate('Filters keys'),
|
|
149
146
|
},
|
|
150
147
|
fields: {
|
|
151
|
-
type:
|
|
148
|
+
type: Array,
|
|
152
149
|
},
|
|
153
150
|
fill: {
|
|
154
151
|
type: Boolean,
|
|
@@ -3,6 +3,7 @@ import { MutatingForm } from './form/builder.js'
|
|
|
3
3
|
import { EXPORT_FORMATS } from './formatter.js'
|
|
4
4
|
import { translate } from './i18n.js'
|
|
5
5
|
import * as Utils from './utils.js'
|
|
6
|
+
import * as DOMUtils from './domutils.js'
|
|
6
7
|
|
|
7
8
|
export default class Share {
|
|
8
9
|
constructor(umap) {
|
|
@@ -17,14 +18,14 @@ export default class Share {
|
|
|
17
18
|
'icon-share'
|
|
18
19
|
)
|
|
19
20
|
|
|
20
|
-
|
|
21
|
+
DOMUtils.copiableInput(
|
|
21
22
|
this.container,
|
|
22
23
|
translate('Link to view the map'),
|
|
23
24
|
window.location.protocol + Utils.getBaseUrl()
|
|
24
25
|
)
|
|
25
26
|
|
|
26
27
|
if (this._umap.properties.shortUrl) {
|
|
27
|
-
|
|
28
|
+
DOMUtils.copiableInput(
|
|
28
29
|
this.container,
|
|
29
30
|
translate('Short link'),
|
|
30
31
|
this._umap.properties.shortUrl
|
|
@@ -79,7 +80,7 @@ export default class Share {
|
|
|
79
80
|
const embedTitle = DomUtil.add('h4', '', this.container, translate('Embed the map'))
|
|
80
81
|
const iframe = DomUtil.create('textarea', 'umap-share-iframe', this.container)
|
|
81
82
|
const urlTitle = DomUtil.add('h4', '', this.container, translate('Direct link'))
|
|
82
|
-
const exportUrl =
|
|
83
|
+
const exportUrl = DOMUtils.copiableInput(
|
|
83
84
|
this.container,
|
|
84
85
|
translate('Share this link to open a customized map view'),
|
|
85
86
|
''
|
|
@@ -227,7 +228,7 @@ class IframeExporter {
|
|
|
227
228
|
|
|
228
229
|
build() {
|
|
229
230
|
const iframeUrl = this.buildUrl()
|
|
230
|
-
let code = `<iframe
|
|
231
|
+
let code = `<iframe style="width: ${this.dimensions.width}; height: ${this.dimensions.height}; border: 0;" allowfullscreen allow="geolocation" src="${iframeUrl}"></iframe>`
|
|
231
232
|
if (this.options.includeFullScreenLink) {
|
|
232
233
|
const fullUrl = this.buildUrl({ scrollWheelZoom: true })
|
|
233
234
|
code += `<p><a href="${fullUrl}">${translate('See full screen')}</a></p>`
|
|
@@ -31,53 +31,73 @@ export default class TableEditor extends WithTemplate {
|
|
|
31
31
|
this.elements.body.addEventListener('keydown', (event) => this.onKeyDown(event))
|
|
32
32
|
this.elements.header.addEventListener('click', (event) => {
|
|
33
33
|
const property = event.target.dataset.property
|
|
34
|
-
|
|
34
|
+
const parentType = event.target.dataset.fieldParent
|
|
35
|
+
if (property)
|
|
36
|
+
this.openHeaderMenu(
|
|
37
|
+
property,
|
|
38
|
+
parentType === 'map' ? this._umap : this.datalayer
|
|
39
|
+
)
|
|
35
40
|
})
|
|
36
41
|
}
|
|
37
42
|
|
|
38
|
-
openHeaderMenu(
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
43
|
+
openHeaderMenu(name, parent) {
|
|
44
|
+
let actionLabel
|
|
45
|
+
if (parent.filters.has(name)) {
|
|
46
|
+
actionLabel = translate('Edit filter for this field')
|
|
47
|
+
} else {
|
|
48
|
+
actionLabel = translate('Add filter for this field')
|
|
49
|
+
}
|
|
50
|
+
const actions = [
|
|
51
|
+
{
|
|
52
|
+
label: actionLabel,
|
|
44
53
|
action: () => {
|
|
45
|
-
|
|
54
|
+
parent.filters.createFilterForm(name)
|
|
46
55
|
this._umap.browser.open('filters')
|
|
47
56
|
},
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
label: translate('Add filter for this column'),
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
label: translate('Edit this field'),
|
|
52
60
|
action: () => {
|
|
53
|
-
|
|
54
|
-
this._umap.browser.open('filters')
|
|
61
|
+
parent.fields.editField(name).then(() => this.open())
|
|
55
62
|
},
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
if (!
|
|
63
|
+
},
|
|
64
|
+
]
|
|
65
|
+
// Only allow deleting fields for map and local datalayer.
|
|
66
|
+
if (!parent.isRemoteLayer?.()) {
|
|
60
67
|
actions.push({
|
|
61
|
-
label: translate('
|
|
62
|
-
action: () =>
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
label: translate('Delete this column'),
|
|
66
|
-
action: () => this.deleteProperty(property),
|
|
68
|
+
label: translate('Delete this field'),
|
|
69
|
+
action: () => {
|
|
70
|
+
parent.fields.confirmDelete(name).then(() => this.open())
|
|
71
|
+
},
|
|
67
72
|
})
|
|
68
73
|
}
|
|
69
74
|
this.contextmenu.open(event, actions)
|
|
70
75
|
}
|
|
71
76
|
|
|
77
|
+
get fields() {
|
|
78
|
+
return [
|
|
79
|
+
...this.datalayer.fields.all().map((field) => {
|
|
80
|
+
const copy = { ...field }
|
|
81
|
+
copy.parent = 'datalayer'
|
|
82
|
+
return copy
|
|
83
|
+
}),
|
|
84
|
+
...this._umap.fields.all().map((field) => {
|
|
85
|
+
const copy = { ...field }
|
|
86
|
+
copy.parent = 'map'
|
|
87
|
+
return copy
|
|
88
|
+
}),
|
|
89
|
+
]
|
|
90
|
+
}
|
|
91
|
+
|
|
72
92
|
renderHeaders() {
|
|
73
93
|
this.elements.header.innerHTML = ''
|
|
74
94
|
const th = loadTemplate('<th><input type="checkbox" /></th>')
|
|
75
95
|
const checkbox = th.firstChild
|
|
76
96
|
this.elements.header.appendChild(th)
|
|
77
|
-
for (const field of this.
|
|
97
|
+
for (const field of this.fields) {
|
|
78
98
|
this.elements.header.appendChild(
|
|
79
99
|
loadTemplate(
|
|
80
|
-
`<th>${field.key}<button data-property="${field.key}" class="flat" aria-label="${translate('Advanced actions')}">…</button></th>`
|
|
100
|
+
`<th>${field.key}<button data-property="${field.key}" data-field-parent="${field.parent}" class="flat" aria-label="${translate('Advanced actions')}">…</button></th>`
|
|
81
101
|
)
|
|
82
102
|
)
|
|
83
103
|
}
|
|
@@ -94,7 +114,7 @@ export default class TableEditor extends WithTemplate {
|
|
|
94
114
|
this.datalayer.features.forEach((feature) => {
|
|
95
115
|
if (feature.isFiltered()) return
|
|
96
116
|
if (inBbox && !feature.isOnScreen(bounds)) return
|
|
97
|
-
const tds = this.
|
|
117
|
+
const tds = this.fields.map(
|
|
98
118
|
(field) =>
|
|
99
119
|
`<td tabindex="0" data-property="${field.key}">${feature.properties[field.key] ?? ''}</td>`
|
|
100
120
|
)
|
|
@@ -103,16 +123,8 @@ export default class TableEditor extends WithTemplate {
|
|
|
103
123
|
this.elements.body.innerHTML = html
|
|
104
124
|
}
|
|
105
125
|
|
|
106
|
-
|
|
107
|
-
this.datalayer.
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
deleteProperty(property) {
|
|
111
|
-
this.datalayer.confirmDeleteProperty(property).then(() => this.open())
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
addProperty() {
|
|
115
|
-
this.datalayer.addProperty().then(() => this.open())
|
|
126
|
+
addField() {
|
|
127
|
+
this.datalayer.fields.editField().then(() => this.open())
|
|
116
128
|
}
|
|
117
129
|
|
|
118
130
|
open() {
|
|
@@ -127,7 +139,7 @@ export default class TableEditor extends WithTemplate {
|
|
|
127
139
|
<button class="flat" type="button" data-ref="add">
|
|
128
140
|
<i class="icon icon-16 icon-add"></i>${translate('Add a new field')}
|
|
129
141
|
</button>`)
|
|
130
|
-
addButton.addEventListener('click', () => this.
|
|
142
|
+
addButton.addEventListener('click', () => this.addField())
|
|
131
143
|
actions.push(addButton)
|
|
132
144
|
|
|
133
145
|
const deleteButton = loadTemplate(`
|
|
@@ -32,9 +32,8 @@ export default class TemplateImporter {
|
|
|
32
32
|
const [root, { tabs, form, body, mine, confirm, confirmData }] =
|
|
33
33
|
Utils.loadTemplateWithRefs(TEMPLATE)
|
|
34
34
|
const uri = this.umap.urls.get('template_list')
|
|
35
|
-
const
|
|
36
|
-
|
|
37
|
-
mine.hidden = !userIsAuth
|
|
35
|
+
const defaultTab = this.umap.permissions.userIsAuth() ? 'mine' : 'staff'
|
|
36
|
+
mine.hidden = !this.umap.permissions.userIsAuth()
|
|
38
37
|
|
|
39
38
|
const loadTemplates = async (source) => {
|
|
40
39
|
const [data, response, error] = await this.umap.server.get(
|
|
@@ -11,11 +11,16 @@ const TOP_BAR_TEMPLATE = `
|
|
|
11
11
|
<div class="umap-left-edit-toolbox" data-ref="left">
|
|
12
12
|
<div class="logo"><a class="" href="/" title="${translate('Go to the homepage')}">uMap</a></div>
|
|
13
13
|
<button class="map-name flat truncate" type="button" data-ref="name"></button>
|
|
14
|
-
<button class="
|
|
15
|
-
|
|
14
|
+
<button class="flat truncate" type="button" data-ref="share">
|
|
15
|
+
<i class="icon icon-16 icon-draft show-on-draft"></i><span class="share-status"></span>
|
|
16
|
+
</button>
|
|
17
|
+
<button class="anonymous truncate soft-round" type="button" data-ref="shareAnonymous" hidden>
|
|
18
|
+
<i class="icon icon-16 icon-anonymous"></i><span class="share-status"></span>
|
|
19
|
+
</button>
|
|
20
|
+
<button class="edit-undo flat" type="button" data-ref="undo" disabled>
|
|
16
21
|
<i class="icon icon-16 icon-undo"></i>
|
|
17
22
|
</button>
|
|
18
|
-
<button class="edit-redo
|
|
23
|
+
<button class="edit-redo flat" type="button" data-ref="redo" disabled>
|
|
19
24
|
<i class="icon icon-16 icon-redo"></i>
|
|
20
25
|
</button>
|
|
21
26
|
</div>
|
|
@@ -26,7 +31,7 @@ const TOP_BAR_TEMPLATE = `
|
|
|
26
31
|
</button>
|
|
27
32
|
<button class="umap-user flat" type="button" data-ref="user">
|
|
28
33
|
<i class="icon icon-16 icon-profile"></i>
|
|
29
|
-
<span class="username truncate" data-ref="username"
|
|
34
|
+
<span class="username truncate" data-ref="username">${translate('Anonymous')}</span>
|
|
30
35
|
</button>
|
|
31
36
|
<button class="umap-help-link flat" type="button" title="${translate('Help')}" data-ref="help">${translate('Help')}</button>
|
|
32
37
|
<button class="edit-disable round disabled-on-dirty" type="button" data-ref="view">
|
|
@@ -72,17 +77,31 @@ export class TopBar extends WithTemplate {
|
|
|
72
77
|
duration: 5000,
|
|
73
78
|
})
|
|
74
79
|
})
|
|
80
|
+
this.elements.shareAnonymous.addEventListener('mouseover', () => {
|
|
81
|
+
this._umap.tooltip.open({
|
|
82
|
+
content: translate('Anonymous map: update who can see and edit it'),
|
|
83
|
+
anchor: this.elements.shareAnonymous,
|
|
84
|
+
position: 'bottom',
|
|
85
|
+
delay: 500,
|
|
86
|
+
duration: 5000,
|
|
87
|
+
})
|
|
88
|
+
})
|
|
75
89
|
if (this._umap.properties.editMode === 'advanced') {
|
|
76
90
|
this.elements.name.addEventListener('click', () => this._umap.editCaption())
|
|
77
91
|
this.elements.share.addEventListener('click', () => this._umap.permissions.edit())
|
|
92
|
+
this.elements.shareAnonymous.addEventListener('click', () =>
|
|
93
|
+
this._umap.permissions.edit()
|
|
94
|
+
)
|
|
78
95
|
}
|
|
79
96
|
this.elements.user.addEventListener('click', () => {
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
97
|
+
const actions = [
|
|
98
|
+
{
|
|
99
|
+
label: translate('New map'),
|
|
100
|
+
action: this._umap.urls.get('map_new'),
|
|
101
|
+
},
|
|
102
|
+
]
|
|
103
|
+
if (this._umap.permissions.userIsAuth()) {
|
|
104
|
+
actions.push(
|
|
86
105
|
{
|
|
87
106
|
label: translate('My maps'),
|
|
88
107
|
action: this._umap.urls.get('user_dashboard'),
|
|
@@ -90,18 +109,22 @@ export class TopBar extends WithTemplate {
|
|
|
90
109
|
{
|
|
91
110
|
label: translate('My teams'),
|
|
92
111
|
action: this._umap.urls.get('user_teams'),
|
|
93
|
-
}
|
|
94
|
-
|
|
112
|
+
}
|
|
113
|
+
)
|
|
95
114
|
if (this._umap.urls.has('user_profile')) {
|
|
96
115
|
actions.push({
|
|
97
116
|
label: translate('My profile'),
|
|
98
117
|
action: this._umap.urls.get('user_profile'),
|
|
99
118
|
})
|
|
100
119
|
}
|
|
101
|
-
|
|
120
|
+
} else {
|
|
121
|
+
actions.push({
|
|
122
|
+
label: translate('Login'),
|
|
123
|
+
action: () => this._umap.askForLogin(),
|
|
124
|
+
})
|
|
102
125
|
}
|
|
126
|
+
this._menu.openBelow(this.elements.user, actions)
|
|
103
127
|
})
|
|
104
|
-
|
|
105
128
|
this.elements.peers.addEventListener('mouseover', () => {
|
|
106
129
|
const connectedPeers = this._umap.sync.getPeers()
|
|
107
130
|
if (!Object.keys(connectedPeers).length) return
|
|
@@ -162,10 +185,10 @@ export class TopBar extends WithTemplate {
|
|
|
162
185
|
duration: 5000,
|
|
163
186
|
})
|
|
164
187
|
})
|
|
165
|
-
this.redraw()
|
|
166
188
|
}
|
|
167
189
|
|
|
168
190
|
redraw() {
|
|
191
|
+
this.element.classList.toggle('draft', this._umap.permissions.isDraft())
|
|
169
192
|
const syncEnabled = this._umap.getProperty('syncEnabled')
|
|
170
193
|
this.elements.peers.hidden = !syncEnabled
|
|
171
194
|
this.elements.view.disabled = this._umap.sync._undoManager.isDirty()
|
|
@@ -175,6 +198,8 @@ export class TopBar extends WithTemplate {
|
|
|
175
198
|
this.elements.saveDraftLabel.hidden = !isDraft || isTemplate
|
|
176
199
|
this.elements.saveTemplateLabel.hidden = !isTemplate
|
|
177
200
|
this._umap.sync._undoManager.toggleState()
|
|
201
|
+
this.elements.share.hidden = this._umap.permissions.isAnonymousMap()
|
|
202
|
+
this.elements.shareAnonymous.hidden = !this._umap.permissions.isAnonymousMap()
|
|
178
203
|
}
|
|
179
204
|
}
|
|
180
205
|
|
|
@@ -211,14 +236,19 @@ export class BottomBar extends WithTemplate {
|
|
|
211
236
|
this.elements.layers.addEventListener('change', () => {
|
|
212
237
|
const select = this.elements.layers
|
|
213
238
|
const selected = select.options[select.selectedIndex].value
|
|
214
|
-
|
|
215
|
-
this._umap.datalayers.active().map((datalayer) => {
|
|
239
|
+
for (const datalayer of this._umap.datalayers.active()) {
|
|
216
240
|
if (datalayer.properties.inCaption !== false) {
|
|
217
|
-
|
|
241
|
+
if (!selected) {
|
|
242
|
+
datalayer.autoVisibility = true
|
|
243
|
+
if (datalayer.showAtZoom() && !datalayer.isVisible()) {
|
|
244
|
+
datalayer.show()
|
|
245
|
+
}
|
|
246
|
+
} else {
|
|
247
|
+
datalayer.toggle(datalayer.id === selected)
|
|
248
|
+
}
|
|
218
249
|
}
|
|
219
|
-
}
|
|
250
|
+
}
|
|
220
251
|
})
|
|
221
|
-
this.redraw()
|
|
222
252
|
}
|
|
223
253
|
|
|
224
254
|
redraw() {
|
|
@@ -229,7 +259,7 @@ export class BottomBar extends WithTemplate {
|
|
|
229
259
|
const showMenus = this._umap.getProperty('captionMenus')
|
|
230
260
|
this.elements.caption.hidden = !showMenus
|
|
231
261
|
this.elements.browse.hidden = !showMenus
|
|
232
|
-
this.elements.filter.hidden = !showMenus || !this._umap.
|
|
262
|
+
this.elements.filter.hidden = !showMenus || !this._umap.hasFilters()
|
|
233
263
|
this.buildDataLayerSwitcher()
|
|
234
264
|
}
|
|
235
265
|
|
|
@@ -239,7 +269,9 @@ export class BottomBar extends WithTemplate {
|
|
|
239
269
|
if (datalayers.length < 2) {
|
|
240
270
|
this.elements.layers.hidden = true
|
|
241
271
|
} else {
|
|
242
|
-
this.elements.layers.appendChild(
|
|
272
|
+
this.elements.layers.appendChild(
|
|
273
|
+
Utils.loadTemplate(`<option value="">${translate('All layers')}</option>`)
|
|
274
|
+
)
|
|
243
275
|
this.elements.layers.hidden = !this._umap.getProperty('layerSwitcher')
|
|
244
276
|
const visible = datalayers.filter((datalayer) => datalayer.isVisible())
|
|
245
277
|
for (const datalayer of datalayers) {
|
|
@@ -267,7 +299,7 @@ const EDIT_BAR_TEMPLATE = `
|
|
|
267
299
|
</li>
|
|
268
300
|
<li data-ref="route" hidden><button type="button" data-getstarted title="${translate('Draw along routes')}"><i class="icon icon-24 icon-route"></i></button></li>
|
|
269
301
|
<hr>
|
|
270
|
-
<li data-ref="caption" hidden><button data-getstarted type="button" title="${translate('Edit map name and caption')}"><i class="icon icon-24 icon-
|
|
302
|
+
<li data-ref="caption" hidden><button data-getstarted type="button" title="${translate('Edit map name and caption')}"><i class="icon icon-24 icon-info"></i></button></li>
|
|
271
303
|
<li data-ref="import" hidden><button type="button"><i class="icon icon-24 icon-upload"></i></button></li>
|
|
272
304
|
<li data-ref="templates" hidden><button type="button" title="${translate('Load template')}" data-getstarted><i class="icon icon-24 icon-template"></i></button></li>
|
|
273
305
|
<li data-ref="layers" hidden><button type="button" title="${translate('Manage layers')}"><i class="icon icon-24 icon-layers"></i></button></li>
|
|
@@ -78,8 +78,7 @@ export default class Dialog extends WithTemplate {
|
|
|
78
78
|
if (!this.dialogSupported) {
|
|
79
79
|
this.elements.form.addEventListener('submit', (event) => {
|
|
80
80
|
event.preventDefault()
|
|
81
|
-
this.
|
|
82
|
-
this.close()
|
|
81
|
+
this.accept()
|
|
83
82
|
})
|
|
84
83
|
}
|
|
85
84
|
this.dialog.addEventListener('keydown', (e) => {
|
|
@@ -118,7 +117,6 @@ export default class Dialog extends WithTemplate {
|
|
|
118
117
|
this.elements.cancel.hidden = !dialog.cancel
|
|
119
118
|
this.elements.message.textContent = dialog.message
|
|
120
119
|
this.elements.message.hidden = !dialog.message
|
|
121
|
-
this.elements.target = dialog.target || ''
|
|
122
120
|
this.elements.template.innerHTML = ''
|
|
123
121
|
if (dialog.template?.nodeType === 1) {
|
|
124
122
|
this.elements.template.appendChild(dialog.template)
|
|
@@ -137,12 +135,13 @@ export default class Dialog extends WithTemplate {
|
|
|
137
135
|
if (currentZIndex) {
|
|
138
136
|
this.dialog.style.zIndex = currentZIndex + 1
|
|
139
137
|
}
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
138
|
+
if (this.dialogSupported) {
|
|
139
|
+
this.dialog.show()
|
|
140
|
+
} else {
|
|
141
|
+
this.dialog.hidden = false
|
|
142
|
+
}
|
|
143
143
|
if (this.hasFormData) this.focusable[0].focus()
|
|
144
144
|
else this.elements.accept.focus()
|
|
145
|
-
|
|
146
145
|
return this.waitForUser()
|
|
147
146
|
}
|
|
148
147
|
|
|
@@ -151,37 +150,49 @@ export default class Dialog extends WithTemplate {
|
|
|
151
150
|
}
|
|
152
151
|
|
|
153
152
|
close() {
|
|
154
|
-
this.
|
|
155
|
-
this.dialog.returnValue = undefined
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
toggle(open = false) {
|
|
153
|
+
this._closing = true
|
|
159
154
|
if (this.dialogSupported) {
|
|
160
|
-
|
|
161
|
-
else this.dialog.close()
|
|
155
|
+
this.dialog.close()
|
|
162
156
|
} else {
|
|
163
|
-
this.dialog.hidden =
|
|
164
|
-
|
|
165
|
-
this.elements.target.focus()
|
|
166
|
-
}
|
|
167
|
-
if (!open) {
|
|
168
|
-
this.dialog.dispatchEvent(new CustomEvent('close'))
|
|
169
|
-
}
|
|
157
|
+
this.dialog.hidden = true
|
|
158
|
+
this.dialog.dispatchEvent(new CustomEvent('close'))
|
|
170
159
|
}
|
|
171
160
|
}
|
|
172
161
|
|
|
173
|
-
|
|
162
|
+
accept() {
|
|
163
|
+
this.dialog.returnValue = 'accept'
|
|
174
164
|
return new Promise((resolve) => {
|
|
175
165
|
this.dialog.addEventListener(
|
|
176
166
|
'close',
|
|
177
|
-
(
|
|
178
|
-
|
|
179
|
-
const value = this.hasFormData ? this.collectFormData() : true
|
|
180
|
-
resolve(value)
|
|
181
|
-
}
|
|
167
|
+
() => {
|
|
168
|
+
resolve()
|
|
182
169
|
},
|
|
183
170
|
{ once: true }
|
|
184
171
|
)
|
|
172
|
+
this.close()
|
|
173
|
+
})
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
waitForUser() {
|
|
177
|
+
return new Promise((resolve) => {
|
|
178
|
+
const onClose = () => {
|
|
179
|
+
this._closing = false
|
|
180
|
+
if (this.dialog.returnValue === 'accept') {
|
|
181
|
+
const value = this.hasFormData ? this.collectFormData() : true
|
|
182
|
+
resolve(value)
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
const waitForClose = () => {
|
|
186
|
+
this.dialog.returnValue = undefined
|
|
187
|
+
this.dialog.addEventListener('close', () => onClose(), { once: true })
|
|
188
|
+
}
|
|
189
|
+
if (this._closing) {
|
|
190
|
+
// We are opening a new dialog while another is not fully closed,
|
|
191
|
+
// so let's first wait for that one to be fully closed
|
|
192
|
+
this.dialog.addEventListener('close', () => waitForClose(), { once: true })
|
|
193
|
+
} else {
|
|
194
|
+
waitForClose()
|
|
195
|
+
}
|
|
185
196
|
})
|
|
186
197
|
}
|
|
187
198
|
|