umap-project 2.9.3__py3-none-any.whl → 3.0.1__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.
- umap/__init__.py +1 -1
- umap/context_processors.py +1 -0
- umap/forms.py +1 -2
- umap/locale/de/LC_MESSAGES/django.mo +0 -0
- umap/locale/de/LC_MESSAGES/django.po +218 -96
- umap/locale/en/LC_MESSAGES/django.po +128 -52
- umap/locale/fr/LC_MESSAGES/django.mo +0 -0
- umap/locale/fr/LC_MESSAGES/django.po +128 -52
- umap/locale/hu/LC_MESSAGES/django.mo +0 -0
- umap/locale/hu/LC_MESSAGES/django.po +209 -88
- umap/locale/is/LC_MESSAGES/django.mo +0 -0
- umap/locale/is/LC_MESSAGES/django.po +296 -175
- umap/migrations/0027_map_tags.py +23 -0
- umap/models.py +13 -2
- umap/settings/base.py +23 -5
- umap/static/umap/base.css +41 -8
- umap/static/umap/content.css +72 -37
- umap/static/umap/css/bar.css +43 -21
- umap/static/umap/css/dialog.css +4 -1
- umap/static/umap/css/form.css +40 -27
- umap/static/umap/css/icon.css +11 -1
- umap/static/umap/css/importers.css +7 -0
- umap/static/umap/img/16-white.svg +23 -2
- umap/static/umap/img/16.svg +1 -1
- umap/static/umap/img/24.svg +4 -4
- umap/static/umap/img/home.svg +7 -0
- umap/static/umap/img/importers/banfr.svg +1 -0
- umap/static/umap/img/marker.svg +2 -5
- umap/static/umap/img/source/16-white.svg +24 -3
- umap/static/umap/img/source/16.svg +1 -1
- umap/static/umap/img/source/24.svg +5 -5
- umap/static/umap/img/target.svg +1 -0
- umap/static/umap/js/components/alerts/alert.js +0 -1
- umap/static/umap/js/modules/browser.js +4 -4
- umap/static/umap/js/modules/caption.js +1 -1
- umap/static/umap/js/modules/data/features.js +25 -25
- umap/static/umap/js/modules/data/layer.js +91 -97
- umap/static/umap/js/modules/facets.js +9 -5
- umap/static/umap/js/modules/form/builder.js +19 -27
- umap/static/umap/js/modules/form/fields.js +40 -14
- umap/static/umap/js/modules/formatter.js +1 -1
- umap/static/umap/js/modules/global.js +9 -5
- umap/static/umap/js/modules/help.js +18 -5
- umap/static/umap/js/modules/importer.js +5 -2
- umap/static/umap/js/modules/importers/banfr.js +93 -0
- umap/static/umap/js/modules/importers/cadastrefr.js +2 -2
- umap/static/umap/js/modules/importers/communesfr.js +1 -1
- umap/static/umap/js/modules/permissions.js +20 -10
- umap/static/umap/js/modules/rendering/icon.js +15 -2
- umap/static/umap/js/modules/rendering/layers/classified.js +7 -7
- umap/static/umap/js/modules/rendering/layers/cluster.js +2 -2
- umap/static/umap/js/modules/rendering/layers/heat.js +4 -4
- umap/static/umap/js/modules/rendering/map.js +14 -6
- umap/static/umap/js/modules/rendering/popup.js +2 -2
- umap/static/umap/js/modules/rendering/template.js +3 -3
- umap/static/umap/js/modules/rendering/ui.js +17 -11
- umap/static/umap/js/modules/rules.js +13 -16
- umap/static/umap/js/modules/schema.js +23 -1
- umap/static/umap/js/modules/share.js +1 -1
- umap/static/umap/js/modules/slideshow.js +1 -0
- umap/static/umap/js/modules/sync/engine.js +141 -19
- umap/static/umap/js/modules/sync/undo.js +101 -0
- umap/static/umap/js/modules/sync/updaters.js +51 -28
- umap/static/umap/js/modules/tableeditor.js +1 -1
- umap/static/umap/js/modules/ui/bar.js +61 -21
- umap/static/umap/js/modules/ui/tooltip.js +1 -1
- umap/static/umap/js/modules/umap.js +190 -176
- umap/static/umap/js/modules/utils.js +30 -4
- umap/static/umap/js/umap.controls.js +82 -38
- umap/static/umap/locale/am_ET.js +11 -6
- umap/static/umap/locale/am_ET.json +11 -6
- umap/static/umap/locale/ar.js +11 -6
- umap/static/umap/locale/ar.json +11 -6
- umap/static/umap/locale/ast.js +11 -6
- umap/static/umap/locale/ast.json +11 -6
- umap/static/umap/locale/bg.js +11 -6
- umap/static/umap/locale/bg.json +11 -6
- umap/static/umap/locale/br.js +12 -7
- umap/static/umap/locale/br.json +12 -7
- umap/static/umap/locale/ca.js +11 -6
- umap/static/umap/locale/ca.json +11 -6
- umap/static/umap/locale/cs_CZ.js +11 -6
- umap/static/umap/locale/cs_CZ.json +11 -6
- umap/static/umap/locale/da.js +11 -6
- umap/static/umap/locale/da.json +11 -6
- umap/static/umap/locale/de.js +47 -42
- umap/static/umap/locale/de.json +47 -42
- umap/static/umap/locale/el.js +11 -6
- umap/static/umap/locale/el.json +11 -6
- umap/static/umap/locale/en.js +11 -6
- umap/static/umap/locale/en.json +11 -6
- umap/static/umap/locale/en_US.json +11 -6
- umap/static/umap/locale/es.js +11 -6
- umap/static/umap/locale/es.json +11 -6
- umap/static/umap/locale/et.js +11 -6
- umap/static/umap/locale/et.json +11 -6
- umap/static/umap/locale/eu.js +11 -6
- umap/static/umap/locale/eu.json +11 -6
- umap/static/umap/locale/fa_IR.js +11 -6
- umap/static/umap/locale/fa_IR.json +11 -6
- umap/static/umap/locale/fi.js +11 -6
- umap/static/umap/locale/fi.json +11 -6
- umap/static/umap/locale/fr.js +11 -6
- umap/static/umap/locale/fr.json +11 -6
- umap/static/umap/locale/gl.js +12 -7
- umap/static/umap/locale/gl.json +12 -7
- umap/static/umap/locale/he.js +11 -6
- umap/static/umap/locale/he.json +11 -6
- umap/static/umap/locale/hr.js +11 -6
- umap/static/umap/locale/hr.json +11 -6
- umap/static/umap/locale/hu.js +25 -20
- umap/static/umap/locale/hu.json +25 -20
- umap/static/umap/locale/id.js +11 -6
- umap/static/umap/locale/id.json +11 -6
- umap/static/umap/locale/is.js +151 -146
- umap/static/umap/locale/is.json +151 -146
- umap/static/umap/locale/it.js +11 -6
- umap/static/umap/locale/it.json +11 -6
- umap/static/umap/locale/ja.js +11 -6
- umap/static/umap/locale/ja.json +11 -6
- umap/static/umap/locale/ko.js +11 -6
- umap/static/umap/locale/ko.json +11 -6
- umap/static/umap/locale/lt.js +11 -6
- umap/static/umap/locale/lt.json +11 -6
- umap/static/umap/locale/ms.js +11 -6
- umap/static/umap/locale/ms.json +11 -6
- umap/static/umap/locale/nl.js +12 -7
- umap/static/umap/locale/nl.json +12 -7
- umap/static/umap/locale/no.js +11 -6
- umap/static/umap/locale/no.json +11 -6
- umap/static/umap/locale/pl.js +11 -6
- umap/static/umap/locale/pl.json +11 -6
- umap/static/umap/locale/pl_PL.json +11 -6
- umap/static/umap/locale/pt.js +11 -6
- umap/static/umap/locale/pt.json +11 -6
- umap/static/umap/locale/pt_BR.js +11 -6
- umap/static/umap/locale/pt_BR.json +11 -6
- umap/static/umap/locale/pt_PT.js +11 -6
- umap/static/umap/locale/pt_PT.json +11 -6
- umap/static/umap/locale/ro.js +11 -6
- umap/static/umap/locale/ro.json +11 -6
- umap/static/umap/locale/ru.js +11 -6
- umap/static/umap/locale/ru.json +11 -6
- umap/static/umap/locale/sk_SK.js +11 -6
- umap/static/umap/locale/sk_SK.json +11 -6
- umap/static/umap/locale/sl.js +11 -6
- umap/static/umap/locale/sl.json +11 -6
- umap/static/umap/locale/sr.js +11 -6
- umap/static/umap/locale/sr.json +11 -6
- umap/static/umap/locale/sv.js +11 -6
- umap/static/umap/locale/sv.json +11 -6
- umap/static/umap/locale/th_TH.js +11 -6
- umap/static/umap/locale/th_TH.json +11 -6
- umap/static/umap/locale/tr.js +11 -6
- umap/static/umap/locale/tr.json +11 -6
- umap/static/umap/locale/uk_UA.js +11 -6
- umap/static/umap/locale/uk_UA.json +11 -6
- umap/static/umap/locale/vi.js +11 -6
- umap/static/umap/locale/vi.json +11 -6
- umap/static/umap/locale/vi_VN.json +11 -6
- umap/static/umap/locale/zh.js +11 -6
- umap/static/umap/locale/zh.json +11 -6
- umap/static/umap/locale/zh_CN.json +11 -6
- umap/static/umap/locale/zh_TW.Big5.json +11 -6
- umap/static/umap/locale/zh_TW.js +19 -14
- umap/static/umap/locale/zh_TW.json +19 -14
- umap/static/umap/map.css +58 -28
- umap/static/umap/unittests/sync.js +0 -57
- umap/static/umap/unittests/utils.js +47 -0
- umap/static/umap/vars.css +5 -2
- umap/static/umap/vendors/photon/leaflet.photon.js +3 -0
- umap/sync/payloads.py +3 -2
- umap/templates/auth/user_detail.html +1 -1
- umap/templates/auth/user_stars.html +1 -1
- umap/templates/umap/content.html +17 -12
- umap/templates/umap/home.html +7 -5
- umap/templates/umap/map_fragment.html +1 -1
- umap/templates/umap/map_list.html +20 -13
- umap/templates/umap/search.html +7 -3
- umap/templates/umap/search_bar.html +13 -11
- umap/templates/umap/team_detail.html +1 -1
- umap/tests/base.py +2 -1
- umap/tests/fixtures/remote_data.umap +55 -0
- umap/tests/fixtures/test_upload_data_with_iconurl.umap +122 -0
- umap/tests/integration/test_browser.py +1 -3
- umap/tests/integration/test_conditional_rules.py +3 -0
- umap/tests/integration/test_edit_datalayer.py +2 -7
- umap/tests/integration/test_edit_map.py +15 -0
- umap/tests/integration/test_edit_polygon.py +1 -2
- umap/tests/integration/test_import.py +59 -2
- umap/tests/integration/test_optimistic_merge.py +4 -3
- umap/tests/integration/test_owned_map.py +0 -1
- umap/tests/integration/test_save.py +2 -4
- umap/tests/integration/test_undo_redo.py +267 -0
- umap/tests/integration/test_websocket_sync.py +78 -11
- umap/tests/settings.py +1 -3
- umap/tests/test_datalayer_s3.py +1 -0
- umap/tests/test_map_views.py +1 -0
- umap/tests/test_views.py +34 -0
- umap/utils.py +1 -1
- umap/views.py +23 -2
- {umap_project-2.9.3.dist-info → umap_project-3.0.1.dist-info}/METADATA +13 -12
- {umap_project-2.9.3.dist-info → umap_project-3.0.1.dist-info}/RECORD +206 -208
- umap/static/umap/js/modules/saving.js +0 -52
- umap/static/umap/test/.eslintrc +0 -21
- umap/static/umap/test/DataLayer.js +0 -463
- umap/static/umap/test/Feature.js +0 -131
- umap/static/umap/test/Map.js +0 -37
- umap/static/umap/test/Marker.js +0 -126
- umap/static/umap/test/Polygon.js +0 -111
- umap/static/umap/test/Polyline.js +0 -286
- umap/static/umap/test/Util.js +0 -28
- umap/static/umap/test/_pre.js +0 -455
- umap/static/umap/test/index.html +0 -139
- {umap_project-2.9.3.dist-info → umap_project-3.0.1.dist-info}/WHEEL +0 -0
- {umap_project-2.9.3.dist-info → umap_project-3.0.1.dist-info}/entry_points.txt +0 -0
- {umap_project-2.9.3.dist-info → umap_project-3.0.1.dist-info}/licenses/LICENSE +0 -0
|
@@ -44,6 +44,10 @@ export const SCHEMA = {
|
|
|
44
44
|
type: Object,
|
|
45
45
|
impacts: ['data'],
|
|
46
46
|
},
|
|
47
|
+
center: {
|
|
48
|
+
type: Object,
|
|
49
|
+
impacts: [], // default center, doesn't need any update of the map
|
|
50
|
+
},
|
|
47
51
|
color: {
|
|
48
52
|
type: String,
|
|
49
53
|
impacts: ['data'],
|
|
@@ -118,6 +122,9 @@ export const SCHEMA = {
|
|
|
118
122
|
default: false,
|
|
119
123
|
label: translate('Animated transitions'),
|
|
120
124
|
},
|
|
125
|
+
edit_status: {
|
|
126
|
+
type: Number,
|
|
127
|
+
},
|
|
121
128
|
editinosmControl: {
|
|
122
129
|
type: Boolean,
|
|
123
130
|
impacts: ['ui'],
|
|
@@ -125,6 +132,9 @@ export const SCHEMA = {
|
|
|
125
132
|
label: translate('Display the control to open OpenStreetMap editor'),
|
|
126
133
|
default: null,
|
|
127
134
|
},
|
|
135
|
+
editors: {
|
|
136
|
+
type: Array,
|
|
137
|
+
},
|
|
128
138
|
embedControl: {
|
|
129
139
|
type: Boolean,
|
|
130
140
|
impacts: ['ui'],
|
|
@@ -204,6 +214,7 @@ export const SCHEMA = {
|
|
|
204
214
|
['Circle', translate('Circle')],
|
|
205
215
|
['Drop', translate('Drop')],
|
|
206
216
|
['Ball', translate('Ball')],
|
|
217
|
+
['Raw', translate('None')],
|
|
207
218
|
],
|
|
208
219
|
default: 'Default',
|
|
209
220
|
},
|
|
@@ -362,6 +373,9 @@ export const SCHEMA = {
|
|
|
362
373
|
type: Object,
|
|
363
374
|
impacts: ['background'],
|
|
364
375
|
},
|
|
376
|
+
owner: {
|
|
377
|
+
type: Object,
|
|
378
|
+
},
|
|
365
379
|
permanentCredit: {
|
|
366
380
|
type: 'Text',
|
|
367
381
|
impacts: ['ui'],
|
|
@@ -436,6 +450,9 @@ export const SCHEMA = {
|
|
|
436
450
|
label: translate('Display the search control'),
|
|
437
451
|
default: true,
|
|
438
452
|
},
|
|
453
|
+
share_status: {
|
|
454
|
+
type: Number,
|
|
455
|
+
},
|
|
439
456
|
shortCredit: {
|
|
440
457
|
type: String,
|
|
441
458
|
impacts: ['ui'],
|
|
@@ -500,6 +517,12 @@ export const SCHEMA = {
|
|
|
500
517
|
helpEntries: ['sync'],
|
|
501
518
|
default: false,
|
|
502
519
|
},
|
|
520
|
+
tags: {
|
|
521
|
+
type: Array,
|
|
522
|
+
},
|
|
523
|
+
team: {
|
|
524
|
+
type: Object,
|
|
525
|
+
},
|
|
503
526
|
tilelayer: {
|
|
504
527
|
type: Object,
|
|
505
528
|
impacts: ['background'],
|
|
@@ -566,7 +589,6 @@ export const SCHEMA = {
|
|
|
566
589
|
type: Object,
|
|
567
590
|
impacts: ['data'],
|
|
568
591
|
},
|
|
569
|
-
|
|
570
592
|
_referenceVersion: {
|
|
571
593
|
type: Number,
|
|
572
594
|
impacts: ['data'],
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { DomUtil } from '../../vendors/leaflet/leaflet-src.esm.js'
|
|
2
|
+
import { MutatingForm } from './form/builder.js'
|
|
2
3
|
import { EXPORT_FORMATS } from './formatter.js'
|
|
3
4
|
import { translate } from './i18n.js'
|
|
4
5
|
import * as Utils from './utils.js'
|
|
5
|
-
import { MutatingForm } from './form/builder.js'
|
|
6
6
|
|
|
7
7
|
export default class Share {
|
|
8
8
|
constructor(umap) {
|
|
@@ -1,8 +1,14 @@
|
|
|
1
1
|
import * as Utils from '../utils.js'
|
|
2
2
|
import { HybridLogicalClock } from './hlc.js'
|
|
3
|
-
import {
|
|
3
|
+
import { UndoManager } from './undo.js'
|
|
4
|
+
import {
|
|
5
|
+
DataLayerUpdater,
|
|
6
|
+
FeatureUpdater,
|
|
7
|
+
MapUpdater,
|
|
8
|
+
MapPermissionsUpdater,
|
|
9
|
+
DataLayerPermissionsUpdater,
|
|
10
|
+
} from './updaters.js'
|
|
4
11
|
import { WebSocketTransport } from './websocket.js'
|
|
5
|
-
import * as SaveManager from '../saving.js'
|
|
6
12
|
|
|
7
13
|
// Start reconnecting after 2 seconds, then double the delay each time
|
|
8
14
|
// maxing out at 32 seconds.
|
|
@@ -55,6 +61,8 @@ export class SyncEngine {
|
|
|
55
61
|
map: new MapUpdater(umap),
|
|
56
62
|
feature: new FeatureUpdater(umap),
|
|
57
63
|
datalayer: new DataLayerUpdater(umap),
|
|
64
|
+
mappermissions: new MapPermissionsUpdater(umap),
|
|
65
|
+
datalayerpermissions: new DataLayerPermissionsUpdater(umap),
|
|
58
66
|
}
|
|
59
67
|
this.transport = undefined
|
|
60
68
|
this._operations = new Operations()
|
|
@@ -64,6 +72,7 @@ export class SyncEngine {
|
|
|
64
72
|
this.websocketConnected = false
|
|
65
73
|
this.closeRequested = false
|
|
66
74
|
this.peerId = Utils.generateId()
|
|
75
|
+
this._undoManager = new UndoManager(umap, this.updaters, this)
|
|
67
76
|
}
|
|
68
77
|
|
|
69
78
|
get isOpen() {
|
|
@@ -122,16 +131,107 @@ export class SyncEngine {
|
|
|
122
131
|
await this.authenticate()
|
|
123
132
|
}, this._reconnectDelay)
|
|
124
133
|
}
|
|
125
|
-
|
|
126
|
-
|
|
134
|
+
|
|
135
|
+
startBatch() {
|
|
136
|
+
this._batch = []
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
commitBatch(subject, metadata) {
|
|
140
|
+
if (!this._batch.length) {
|
|
141
|
+
this._batch = null
|
|
142
|
+
return
|
|
143
|
+
}
|
|
144
|
+
const operations = this._batch.map((stage) => stage.operation)
|
|
145
|
+
const operation = { verb: 'batch', operations, subject, metadata }
|
|
146
|
+
this._undoManager.add({ operation, stages: this._batch })
|
|
147
|
+
this._send(operation)
|
|
148
|
+
this._batch = null
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
upsert(subject, metadata, value, oldValue) {
|
|
152
|
+
const operation = {
|
|
153
|
+
verb: 'upsert',
|
|
154
|
+
subject,
|
|
155
|
+
metadata,
|
|
156
|
+
value,
|
|
157
|
+
}
|
|
158
|
+
const stage = {
|
|
159
|
+
operation,
|
|
160
|
+
newValue: value,
|
|
161
|
+
oldValue: oldValue,
|
|
162
|
+
}
|
|
163
|
+
if (this._batch) {
|
|
164
|
+
this._batch.push(stage)
|
|
165
|
+
return
|
|
166
|
+
}
|
|
167
|
+
this._undoManager.add(stage)
|
|
168
|
+
this._send(operation)
|
|
127
169
|
}
|
|
128
170
|
|
|
129
|
-
update(subject, metadata, key, value) {
|
|
130
|
-
|
|
171
|
+
update(subject, metadata, key, value, oldValue, { undo } = { undo: true }) {
|
|
172
|
+
const operation = {
|
|
173
|
+
verb: 'update',
|
|
174
|
+
subject,
|
|
175
|
+
metadata,
|
|
176
|
+
key,
|
|
177
|
+
value,
|
|
178
|
+
}
|
|
179
|
+
const stage = {
|
|
180
|
+
operation,
|
|
181
|
+
oldValue: oldValue,
|
|
182
|
+
newValue: value,
|
|
183
|
+
}
|
|
184
|
+
if (this._batch) {
|
|
185
|
+
this._batch.push(stage)
|
|
186
|
+
return
|
|
187
|
+
}
|
|
188
|
+
if (undo) this._undoManager.add(stage)
|
|
189
|
+
this._send(operation)
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
delete(subject, metadata, oldValue) {
|
|
193
|
+
const operation = {
|
|
194
|
+
verb: 'delete',
|
|
195
|
+
subject,
|
|
196
|
+
metadata,
|
|
197
|
+
}
|
|
198
|
+
const stage = {
|
|
199
|
+
operation,
|
|
200
|
+
oldValue: oldValue,
|
|
201
|
+
}
|
|
202
|
+
if (this._batch) {
|
|
203
|
+
this._batch.push(stage)
|
|
204
|
+
return
|
|
205
|
+
}
|
|
206
|
+
this._undoManager.add(stage)
|
|
207
|
+
this._send(operation)
|
|
131
208
|
}
|
|
132
209
|
|
|
133
|
-
|
|
134
|
-
|
|
210
|
+
async save() {
|
|
211
|
+
const needSave = new Map()
|
|
212
|
+
if (!this._umap.id) {
|
|
213
|
+
// There is no operation for first map save
|
|
214
|
+
needSave.set(this._umap, [])
|
|
215
|
+
}
|
|
216
|
+
for (const operation of this._operations.sorted()) {
|
|
217
|
+
if (operation.dirty) {
|
|
218
|
+
const updater = this._getUpdater(operation.subject)
|
|
219
|
+
const obj = updater.getStoredObject(operation.metadata)
|
|
220
|
+
if (!needSave.has(obj)) {
|
|
221
|
+
needSave.set(obj, [])
|
|
222
|
+
}
|
|
223
|
+
needSave.get(obj).push(operation)
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
for (const [obj, operations] of needSave.entries()) {
|
|
227
|
+
const ok = await obj.save()
|
|
228
|
+
if (!ok) break
|
|
229
|
+
for (const operation of operations) {
|
|
230
|
+
operation.dirty = false
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
this.saved()
|
|
234
|
+
this._undoManager.toggleState()
|
|
135
235
|
}
|
|
136
236
|
|
|
137
237
|
saved() {
|
|
@@ -144,8 +244,8 @@ export class SyncEngine {
|
|
|
144
244
|
}
|
|
145
245
|
}
|
|
146
246
|
|
|
147
|
-
_send(
|
|
148
|
-
const message = this._operations.addLocal(
|
|
247
|
+
_send(operation) {
|
|
248
|
+
const message = this._operations.addLocal(operation)
|
|
149
249
|
|
|
150
250
|
if (this.offline) return
|
|
151
251
|
if (this.transport) {
|
|
@@ -153,7 +253,11 @@ export class SyncEngine {
|
|
|
153
253
|
}
|
|
154
254
|
}
|
|
155
255
|
|
|
156
|
-
_getUpdater(subject, metadata) {
|
|
256
|
+
_getUpdater(subject, metadata, sync) {
|
|
257
|
+
// For now, prevent permissions to be synced, for security reasons
|
|
258
|
+
if (sync && (subject === 'mappermissions' || subject === 'datalayerpermissions')) {
|
|
259
|
+
return
|
|
260
|
+
}
|
|
157
261
|
if (Object.keys(this.updaters).includes(subject)) {
|
|
158
262
|
return this.updaters[subject]
|
|
159
263
|
}
|
|
@@ -161,7 +265,15 @@ export class SyncEngine {
|
|
|
161
265
|
}
|
|
162
266
|
|
|
163
267
|
_applyOperation(operation) {
|
|
268
|
+
if (operation.verb === 'batch') {
|
|
269
|
+
operation.operations.map((op) => this._applyOperation(op))
|
|
270
|
+
return
|
|
271
|
+
}
|
|
164
272
|
const updater = this._getUpdater(operation.subject, operation.metadata)
|
|
273
|
+
if (!updater) {
|
|
274
|
+
debug('No updater for', operation)
|
|
275
|
+
return
|
|
276
|
+
}
|
|
165
277
|
updater.applyMessage(operation)
|
|
166
278
|
}
|
|
167
279
|
|
|
@@ -304,9 +416,8 @@ export class SyncEngine {
|
|
|
304
416
|
|
|
305
417
|
onSavedMessage({ sender, lastKnownHLC }) {
|
|
306
418
|
debug(`received saved message from peer ${sender}`, lastKnownHLC)
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
}
|
|
419
|
+
this._operations.saved(lastKnownHLC)
|
|
420
|
+
this._undoManager.toggleState()
|
|
310
421
|
}
|
|
311
422
|
|
|
312
423
|
/**
|
|
@@ -356,7 +467,7 @@ export class SyncEngine {
|
|
|
356
467
|
const handler = {
|
|
357
468
|
get(target, prop) {
|
|
358
469
|
// Only proxy these methods
|
|
359
|
-
if (['upsert', 'update', 'delete'].includes(prop)) {
|
|
470
|
+
if (['upsert', 'update', 'delete', 'commitBatch'].includes(prop)) {
|
|
360
471
|
const { subject, metadata } = object.getSyncMetadata()
|
|
361
472
|
// Reflect.get is calling the original method.
|
|
362
473
|
// .bind is adding the parameters automatically
|
|
@@ -378,16 +489,22 @@ export class Operations {
|
|
|
378
489
|
this._operations = new Array()
|
|
379
490
|
}
|
|
380
491
|
|
|
492
|
+
saved(hlc) {
|
|
493
|
+
for (const operation of this.getOperationsBefore(hlc)) {
|
|
494
|
+
operation.dirty = false
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
|
|
381
498
|
/**
|
|
382
499
|
* Tick the clock and store the passed message in the operations list.
|
|
383
500
|
*
|
|
384
501
|
* @param {*} inputMessage
|
|
385
502
|
* @returns {*} clock-aware message
|
|
386
503
|
*/
|
|
387
|
-
addLocal(
|
|
388
|
-
|
|
389
|
-
this._operations.push(
|
|
390
|
-
return
|
|
504
|
+
addLocal(operation) {
|
|
505
|
+
operation.hlc = this._hlc.tick()
|
|
506
|
+
this._operations.push(operation)
|
|
507
|
+
return operation
|
|
391
508
|
}
|
|
392
509
|
|
|
393
510
|
/**
|
|
@@ -445,6 +562,11 @@ export class Operations {
|
|
|
445
562
|
return this._operations.filter((op) => op.hlc > hlc)
|
|
446
563
|
}
|
|
447
564
|
|
|
565
|
+
getOperationsBefore(hlc) {
|
|
566
|
+
if (!hlc) return this._operations
|
|
567
|
+
return this._operations.filter((op) => op.hlc <= hlc)
|
|
568
|
+
}
|
|
569
|
+
|
|
448
570
|
/**
|
|
449
571
|
* Returns the last known HLC value.
|
|
450
572
|
*/
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import * as Utils from '../utils.js'
|
|
2
|
+
import { DataLayerUpdater, FeatureUpdater, MapUpdater } from './updaters.js'
|
|
3
|
+
|
|
4
|
+
export class UndoManager {
|
|
5
|
+
constructor(umap, updaters, syncEngine) {
|
|
6
|
+
this._umap = umap
|
|
7
|
+
this._syncEngine = syncEngine
|
|
8
|
+
this.updaters = updaters
|
|
9
|
+
this._undoStack = []
|
|
10
|
+
this._redoStack = []
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
toggleState() {
|
|
14
|
+
// document is undefined during unittests
|
|
15
|
+
if (typeof document === 'undefined') return
|
|
16
|
+
const undoButton = document.querySelector('.edit-undo')
|
|
17
|
+
const redoButton = document.querySelector('.edit-redo')
|
|
18
|
+
if (undoButton) undoButton.disabled = !this._undoStack.length
|
|
19
|
+
if (redoButton) redoButton.disabled = !this._redoStack.length
|
|
20
|
+
const dirty = this.isDirty()
|
|
21
|
+
document.body.classList.toggle('umap-is-dirty', dirty)
|
|
22
|
+
for (const button of document.querySelectorAll('.disabled-on-dirty')) {
|
|
23
|
+
button.disabled = dirty
|
|
24
|
+
}
|
|
25
|
+
for (const button of document.querySelectorAll('.enabled-on-dirty')) {
|
|
26
|
+
button.disabled = !dirty
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
isDirty() {
|
|
31
|
+
if (!this._umap.id) return true
|
|
32
|
+
for (const stage of this._undoStack) {
|
|
33
|
+
if (stage.operation.dirty) return true
|
|
34
|
+
}
|
|
35
|
+
for (const stage of this._redoStack) {
|
|
36
|
+
if (stage.operation.dirty) return true
|
|
37
|
+
}
|
|
38
|
+
return false
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
add(stage) {
|
|
42
|
+
stage.operation.dirty = true
|
|
43
|
+
this._redoStack = []
|
|
44
|
+
this._undoStack.push(stage)
|
|
45
|
+
this.toggleState()
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
copyOperation(stage, redo) {
|
|
49
|
+
const operation = Utils.CopyJSON(stage.operation)
|
|
50
|
+
const value = redo ? stage.newValue : stage.oldValue
|
|
51
|
+
operation.value = value
|
|
52
|
+
if (['delete', 'upsert'].includes(operation.verb)) {
|
|
53
|
+
operation.verb = value === null || value === undefined ? 'delete' : 'upsert'
|
|
54
|
+
}
|
|
55
|
+
return operation
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
undo(redo = false) {
|
|
59
|
+
const fromStack = redo ? this._redoStack : this._undoStack
|
|
60
|
+
const toStack = redo ? this._undoStack : this._redoStack
|
|
61
|
+
const stage = fromStack.pop()
|
|
62
|
+
if (!stage) return
|
|
63
|
+
stage.operation.dirty = !stage.operation.dirty
|
|
64
|
+
if (stage.operation.verb === 'batch') {
|
|
65
|
+
for (const st of stage.stages) {
|
|
66
|
+
this.applyOperation(this.copyOperation(st, redo))
|
|
67
|
+
}
|
|
68
|
+
} else {
|
|
69
|
+
this.applyOperation(this.copyOperation(stage, redo))
|
|
70
|
+
}
|
|
71
|
+
toStack.push(stage)
|
|
72
|
+
this.toggleState()
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
redo() {
|
|
76
|
+
this.undo(true)
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
applyOperation(operation) {
|
|
80
|
+
const updater = this._getUpdater(operation.subject, operation.metadata)
|
|
81
|
+
switch (operation.verb) {
|
|
82
|
+
case 'update':
|
|
83
|
+
updater.update(operation)
|
|
84
|
+
break
|
|
85
|
+
case 'delete':
|
|
86
|
+
updater.delete(operation)
|
|
87
|
+
break
|
|
88
|
+
case 'upsert':
|
|
89
|
+
updater.upsert(operation)
|
|
90
|
+
break
|
|
91
|
+
}
|
|
92
|
+
this._syncEngine._send(operation)
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
_getUpdater(subject, metadata) {
|
|
96
|
+
if (Object.keys(this.updaters).includes(subject)) {
|
|
97
|
+
return this.updaters[subject]
|
|
98
|
+
}
|
|
99
|
+
throw new Error(`Unknown updater ${subject}, ${metadata}`)
|
|
100
|
+
}
|
|
101
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import * as Utils from '../utils.js'
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Updaters are classes able to convert messages
|
|
@@ -10,27 +10,6 @@ class BaseUpdater {
|
|
|
10
10
|
this._umap = umap
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
-
updateObjectValue(obj, key, value) {
|
|
14
|
-
const parts = key.split('.')
|
|
15
|
-
const lastKey = parts.pop()
|
|
16
|
-
|
|
17
|
-
// Reduce the current list of attributes,
|
|
18
|
-
// to find the object to set the property onto
|
|
19
|
-
const objectToSet = parts.reduce((currentObj, part) => {
|
|
20
|
-
if (currentObj !== undefined && part in currentObj) return currentObj[part]
|
|
21
|
-
}, obj)
|
|
22
|
-
|
|
23
|
-
// In case the given path doesn't exist, stop here
|
|
24
|
-
if (objectToSet === undefined) return
|
|
25
|
-
|
|
26
|
-
// Set the value (or delete it)
|
|
27
|
-
if (typeof value === 'undefined') {
|
|
28
|
-
delete objectToSet[lastKey]
|
|
29
|
-
} else {
|
|
30
|
-
objectToSet[lastKey] = value
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
|
|
34
13
|
getDataLayerFromID(layerId) {
|
|
35
14
|
return this._umap.getDataLayerByUmapId(layerId)
|
|
36
15
|
}
|
|
@@ -43,12 +22,17 @@ class BaseUpdater {
|
|
|
43
22
|
|
|
44
23
|
export class MapUpdater extends BaseUpdater {
|
|
45
24
|
update({ key, value }) {
|
|
46
|
-
if (fieldInSchema(key)) {
|
|
47
|
-
|
|
25
|
+
if (Utils.fieldInSchema(key)) {
|
|
26
|
+
Utils.setObjectValue(this._umap, key, value)
|
|
48
27
|
}
|
|
49
28
|
|
|
29
|
+
this._umap.onPropertiesUpdated([key])
|
|
50
30
|
this._umap.render([key])
|
|
51
31
|
}
|
|
32
|
+
|
|
33
|
+
getStoredObject() {
|
|
34
|
+
return this._umap
|
|
35
|
+
}
|
|
52
36
|
}
|
|
53
37
|
|
|
54
38
|
export class DataLayerUpdater extends BaseUpdater {
|
|
@@ -57,14 +41,21 @@ export class DataLayerUpdater extends BaseUpdater {
|
|
|
57
41
|
try {
|
|
58
42
|
this.getDataLayerFromID(value.id)
|
|
59
43
|
} catch {
|
|
60
|
-
this._umap.createDataLayer(value, false)
|
|
44
|
+
const datalayer = this._umap.createDataLayer(value._umap_options || value, false)
|
|
45
|
+
if (value.features) {
|
|
46
|
+
// FIXME: this will create new stages in the undoStack, thus this will empty
|
|
47
|
+
// the redoStack
|
|
48
|
+
datalayer.addData(value)
|
|
49
|
+
}
|
|
61
50
|
}
|
|
62
51
|
}
|
|
63
52
|
|
|
64
53
|
update({ key, metadata, value }) {
|
|
65
54
|
const datalayer = this.getDataLayerFromID(metadata.id)
|
|
66
|
-
if (
|
|
67
|
-
|
|
55
|
+
if (key === 'options') {
|
|
56
|
+
datalayer.setOptions(value)
|
|
57
|
+
} else if (Utils.fieldInSchema(key)) {
|
|
58
|
+
Utils.setObjectValue(datalayer, key, value)
|
|
68
59
|
} else {
|
|
69
60
|
console.debug(
|
|
70
61
|
'Not applying update for datalayer because key is not in the schema',
|
|
@@ -81,6 +72,10 @@ export class DataLayerUpdater extends BaseUpdater {
|
|
|
81
72
|
datalayer.commitDelete()
|
|
82
73
|
}
|
|
83
74
|
}
|
|
75
|
+
|
|
76
|
+
getStoredObject(metadata) {
|
|
77
|
+
return this.getDataLayerFromID(metadata.id)
|
|
78
|
+
}
|
|
84
79
|
}
|
|
85
80
|
|
|
86
81
|
export class FeatureUpdater extends BaseUpdater {
|
|
@@ -113,7 +108,7 @@ export class FeatureUpdater extends BaseUpdater {
|
|
|
113
108
|
const feature = this.getFeatureFromMetadata(metadata)
|
|
114
109
|
feature.geometry = value
|
|
115
110
|
} else {
|
|
116
|
-
|
|
111
|
+
Utils.setObjectValue(feature, key, value)
|
|
117
112
|
feature.datalayer.indexProperties(feature)
|
|
118
113
|
}
|
|
119
114
|
|
|
@@ -126,4 +121,32 @@ export class FeatureUpdater extends BaseUpdater {
|
|
|
126
121
|
const feature = this.getFeatureFromMetadata(metadata)
|
|
127
122
|
if (feature) feature.del(false)
|
|
128
123
|
}
|
|
124
|
+
|
|
125
|
+
getStoredObject(metadata) {
|
|
126
|
+
return this.getDataLayerFromID(metadata.layerId)
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
export class MapPermissionsUpdater extends BaseUpdater {
|
|
131
|
+
update({ key, value }) {
|
|
132
|
+
if (Utils.fieldInSchema(key)) {
|
|
133
|
+
Utils.setObjectValue(this._umap.permissions, key, value)
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
getStoredObject(metadata) {
|
|
138
|
+
return this._umap.permissions
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
export class DataLayerPermissionsUpdater extends BaseUpdater {
|
|
143
|
+
update({ key, value, metadata }) {
|
|
144
|
+
if (Utils.fieldInSchema(key)) {
|
|
145
|
+
Utils.setObjectValue(this.getDataLayerFromID(metadata.id), key, value)
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
getStoredObject(metadata) {
|
|
150
|
+
return this.getDataLayerFromID(metadata.id).permissions
|
|
151
|
+
}
|
|
129
152
|
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { DomEvent, DomUtil } from '../../vendors/leaflet/leaflet-src.esm.js'
|
|
2
|
+
import { MutatingForm } from './form/builder.js'
|
|
2
3
|
import { translate } from './i18n.js'
|
|
3
4
|
import ContextMenu from './ui/contextmenu.js'
|
|
4
5
|
import { WithTemplate, loadTemplate } from './utils.js'
|
|
5
|
-
import { MutatingForm } from './form/builder.js'
|
|
6
6
|
|
|
7
7
|
const TEMPLATE = `
|
|
8
8
|
<table>
|