mapshaper 0.7.10 → 0.7.12
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.
- package/mapshaper.js +131 -39
- package/package.json +6 -6
- package/www/geoparquet.js +569 -3153
- package/www/mapshaper-gui.js +132 -892
- package/www/mapshaper.js +131 -39
- package/www/page.css +35 -24
- package/www/zstd.wasm +0 -0
package/www/mapshaper-gui.js
CHANGED
|
@@ -5472,7 +5472,7 @@
|
|
|
5472
5472
|
|
|
5473
5473
|
function getUndoStorageLimit(name, defaultValue) {
|
|
5474
5474
|
var opt = gui.options && gui.options[name],
|
|
5475
|
-
query = getQueryValue$
|
|
5475
|
+
query = getQueryValue$2(name);
|
|
5476
5476
|
if (query !== null && +query >= 0) return +query;
|
|
5477
5477
|
return opt >= 0 ? opt : defaultValue;
|
|
5478
5478
|
}
|
|
@@ -5540,10 +5540,10 @@
|
|
|
5540
5540
|
|
|
5541
5541
|
function getUndoStorageLimitLabel(store) {
|
|
5542
5542
|
var stats = store && store.getStats ? store.getStats() : null;
|
|
5543
|
-
return formatBytes$
|
|
5543
|
+
return formatBytes$1(stats && stats.maxBytes || 1024 * 1024 * 1024);
|
|
5544
5544
|
}
|
|
5545
5545
|
|
|
5546
|
-
function formatBytes$
|
|
5546
|
+
function formatBytes$1(bytes) {
|
|
5547
5547
|
var units = ['B', 'KB', 'MB', 'GB'];
|
|
5548
5548
|
var value = bytes;
|
|
5549
5549
|
var i = 0;
|
|
@@ -5554,7 +5554,97 @@
|
|
|
5554
5554
|
return (i === 0 ? String(value) : value.toFixed(value < 10 ? 1 : 0)) + ' ' + units[i];
|
|
5555
5555
|
}
|
|
5556
5556
|
|
|
5557
|
-
function getQueryValue$
|
|
5557
|
+
function getQueryValue$2(key) {
|
|
5558
|
+
var rxp, match;
|
|
5559
|
+
if (typeof window == 'undefined' || !window.location) return null;
|
|
5560
|
+
rxp = new RegExp('[?&]' + key + '=([^&]+)');
|
|
5561
|
+
match = rxp.exec(window.location.search);
|
|
5562
|
+
return match ? decodeURIComponent(match[1]) : null;
|
|
5563
|
+
}
|
|
5564
|
+
|
|
5565
|
+
// Shared helpers for GUI controls that need to create app-level undo entries
|
|
5566
|
+
// (the storage-backed undo flow used by console commands and a few GUI actions).
|
|
5567
|
+
//
|
|
5568
|
+
// The five GUI controls that grew their own copies of these helpers in the
|
|
5569
|
+
// initial undo/redo commit (gui-add-layer-popup, gui-import-control,
|
|
5570
|
+
// gui-layer-control, gui-simplify-control, gui-undo) should import from here
|
|
5571
|
+
// instead.
|
|
5572
|
+
|
|
5573
|
+
|
|
5574
|
+
var DEFAULT_HISTORY_LIMIT = 10;
|
|
5575
|
+
|
|
5576
|
+
// Returns true if the manifest, URL query, gui-installed checker, or
|
|
5577
|
+
// localStorage indicates that app-level undo should be active.
|
|
5578
|
+
function appUndoIsEnabled(gui) {
|
|
5579
|
+
var opt = gui && gui.options && (gui.options.undoCommands || gui.options.appUndo);
|
|
5580
|
+
var query = getUndoQueryValue();
|
|
5581
|
+
if (opt === true || query == 'on' || query == 'commands') return true;
|
|
5582
|
+
if (gui && gui.appUndoIsEnabled) return gui.appUndoIsEnabled();
|
|
5583
|
+
return appUndoSettingIsOn();
|
|
5584
|
+
}
|
|
5585
|
+
|
|
5586
|
+
// True when the undo URL flag forces app undo on regardless of UI settings.
|
|
5587
|
+
// Mirrors the toggle-disable behavior in HistoryMenu.
|
|
5588
|
+
function appUndoForcedByUrl() {
|
|
5589
|
+
var query = getUndoQueryValue();
|
|
5590
|
+
return query == 'on' || query == 'commands';
|
|
5591
|
+
}
|
|
5592
|
+
|
|
5593
|
+
// Read the persisted localStorage opt-in. Returns false in non-browser
|
|
5594
|
+
// environments or when storage is blocked.
|
|
5595
|
+
function appUndoSettingIsOn() {
|
|
5596
|
+
try {
|
|
5597
|
+
return !!(typeof window != 'undefined' && window.localStorage &&
|
|
5598
|
+
window.localStorage.getItem('mapshaper.undo') == 'on');
|
|
5599
|
+
} catch(e) {
|
|
5600
|
+
return false;
|
|
5601
|
+
}
|
|
5602
|
+
}
|
|
5603
|
+
|
|
5604
|
+
// Returns the per-gui-instance stored undo history, creating it on first use.
|
|
5605
|
+
// Memoizing on `gui` avoids constructing parallel histories from different
|
|
5606
|
+
// callers (the original code created multiple instances depending on which
|
|
5607
|
+
// control ran first).
|
|
5608
|
+
function getStoredUndoHistory(gui) {
|
|
5609
|
+
if (!gui.storedUndoHistory) {
|
|
5610
|
+
gui.storedUndoHistory = createStoredUndoHistory(gui);
|
|
5611
|
+
}
|
|
5612
|
+
return gui.storedUndoHistory;
|
|
5613
|
+
}
|
|
5614
|
+
|
|
5615
|
+
function getUndoHistoryLimit(gui) {
|
|
5616
|
+
var opt = gui && gui.options && gui.options.undoHistoryLimit;
|
|
5617
|
+
return opt > 0 ? opt : DEFAULT_HISTORY_LIMIT;
|
|
5618
|
+
}
|
|
5619
|
+
|
|
5620
|
+
// Construct an UndoTransaction if and only if app undo is enabled and the
|
|
5621
|
+
// transaction can plausibly be added to history. Returns null when undo is
|
|
5622
|
+
// off, when the gui's undo manager is missing, or when the UndoTransaction
|
|
5623
|
+
// constructor cannot be located on the internal namespace.
|
|
5624
|
+
function createUndoTransaction(gui, label) {
|
|
5625
|
+
var Transaction;
|
|
5626
|
+
if (!appUndoIsEnabled(gui)) return null;
|
|
5627
|
+
if (!gui || !gui.undo || typeof gui.undo.addHistoryState != 'function') return null;
|
|
5628
|
+
Transaction = internal.UndoTransaction &&
|
|
5629
|
+
(internal.UndoTransaction.UndoTransaction || internal.UndoTransaction);
|
|
5630
|
+
return Transaction ? new Transaction(label || '') : null;
|
|
5631
|
+
}
|
|
5632
|
+
|
|
5633
|
+
// Add a captured transaction to gui-level history. Pass options through to
|
|
5634
|
+
// stored-undo-history.addTransaction(); fills in maxStates when not provided.
|
|
5635
|
+
function addUndoTransactionToHistory(gui, tx, opts) {
|
|
5636
|
+
if (!tx) return Promise.resolve(null);
|
|
5637
|
+
var fullOpts = Object.assign({maxStates: getUndoHistoryLimit(gui)}, opts || {});
|
|
5638
|
+
return getStoredUndoHistory(gui).addTransaction(tx, fullOpts).catch(function(e) {
|
|
5639
|
+
console.error(e);
|
|
5640
|
+
});
|
|
5641
|
+
}
|
|
5642
|
+
|
|
5643
|
+
function getUndoQueryValue() {
|
|
5644
|
+
return getQueryValue$1('undo');
|
|
5645
|
+
}
|
|
5646
|
+
|
|
5647
|
+
function getQueryValue$1(key) {
|
|
5558
5648
|
var rxp, match;
|
|
5559
5649
|
if (typeof window == 'undefined' || !window.location) return null;
|
|
5560
5650
|
rxp = new RegExp('[?&]' + key + '=([^&]+)');
|
|
@@ -5588,7 +5678,7 @@
|
|
|
5588
5678
|
function addEmptyLayer(gui, name, type) {
|
|
5589
5679
|
var targ = gui.model.getActiveLayer();
|
|
5590
5680
|
var crsInfo = targ && internal.getDatasetCrsInfo(targ.dataset);
|
|
5591
|
-
var undoTransaction =
|
|
5681
|
+
var undoTransaction = createUndoTransaction(gui, 'add empty layer');
|
|
5592
5682
|
var dataset = {
|
|
5593
5683
|
layers: [{
|
|
5594
5684
|
name: name || undefined,
|
|
@@ -5608,60 +5698,12 @@
|
|
|
5608
5698
|
}
|
|
5609
5699
|
gui.model.addDataset(dataset);
|
|
5610
5700
|
gui.model.updated({select: true});
|
|
5611
|
-
|
|
5612
|
-
}
|
|
5613
|
-
|
|
5614
|
-
function createAddLayerUndoTransaction(gui) {
|
|
5615
|
-
var Transaction;
|
|
5616
|
-
if (!appUndoIsEnabled(gui)) return null;
|
|
5617
|
-
if (!gui.undo || typeof gui.undo.addHistoryState != 'function') return null;
|
|
5618
|
-
Transaction = internal.UndoTransaction && (internal.UndoTransaction.UndoTransaction || internal.UndoTransaction);
|
|
5619
|
-
return Transaction ? new Transaction('add empty layer') : null;
|
|
5620
|
-
}
|
|
5621
|
-
|
|
5622
|
-
function addEmptyLayerUndoHistory(gui, tx) {
|
|
5623
|
-
if (!tx) return;
|
|
5624
|
-
getStoredUndoHistory(gui).addTransaction(tx, {
|
|
5701
|
+
addUndoTransactionToHistory(gui, undoTransaction, {
|
|
5625
5702
|
flags: {select: true},
|
|
5626
|
-
entryPrefix: 'add-layer'
|
|
5627
|
-
maxStates: getUndoHistoryLimit(gui)
|
|
5628
|
-
}).catch(function(e) {
|
|
5629
|
-
console.error(e);
|
|
5703
|
+
entryPrefix: 'add-layer'
|
|
5630
5704
|
});
|
|
5631
5705
|
}
|
|
5632
5706
|
|
|
5633
|
-
function getStoredUndoHistory(gui) {
|
|
5634
|
-
if (!gui.storedUndoHistory) {
|
|
5635
|
-
gui.storedUndoHistory = createStoredUndoHistory(gui);
|
|
5636
|
-
}
|
|
5637
|
-
return gui.storedUndoHistory;
|
|
5638
|
-
}
|
|
5639
|
-
|
|
5640
|
-
function getUndoHistoryLimit(gui) {
|
|
5641
|
-
var opt = gui.options && gui.options.undoHistoryLimit;
|
|
5642
|
-
return opt > 0 ? opt : 10;
|
|
5643
|
-
}
|
|
5644
|
-
|
|
5645
|
-
function appUndoIsEnabled(gui) {
|
|
5646
|
-
var opt = gui.options && (gui.options.undoCommands || gui.options.appUndo);
|
|
5647
|
-
var query = getQueryValue$3('undo');
|
|
5648
|
-
if (opt === true || query == 'on' || query == 'commands') return true;
|
|
5649
|
-
if (gui.appUndoIsEnabled) return gui.appUndoIsEnabled();
|
|
5650
|
-
try {
|
|
5651
|
-
return window.localStorage && window.localStorage.getItem('mapshaper.undo') == 'on';
|
|
5652
|
-
} catch(e) {
|
|
5653
|
-
return false;
|
|
5654
|
-
}
|
|
5655
|
-
}
|
|
5656
|
-
|
|
5657
|
-
function getQueryValue$3(key) {
|
|
5658
|
-
var rxp, match;
|
|
5659
|
-
if (typeof window == 'undefined' || !window.location) return null;
|
|
5660
|
-
rxp = new RegExp('[?&]' + key + '=([^&]+)');
|
|
5661
|
-
match = rxp.exec(window.location.search);
|
|
5662
|
-
return match ? decodeURIComponent(match[1]) : null;
|
|
5663
|
-
}
|
|
5664
|
-
|
|
5665
5707
|
async function considerReprojecting(gui, dataset, opts) {
|
|
5666
5708
|
var mapCRS = gui.map.getActiveLayerCRS();
|
|
5667
5709
|
var dataCRS = internal.getDatasetCRS(dataset);
|
|
@@ -5709,7 +5751,7 @@
|
|
|
5709
5751
|
}
|
|
5710
5752
|
|
|
5711
5753
|
async function loadGeoParquetLib() {
|
|
5712
|
-
if (!window.modules || !window.modules.hyparquet || !window.modules['hyparquet-compressors'] || !window.modules['hyparquet-writer'] || !window.modules['zstd-
|
|
5754
|
+
if (!window.modules || !window.modules.hyparquet || !window.modules['hyparquet-compressors'] || !window.modules['hyparquet-writer'] || !window.modules['@bokuweb/zstd-wasm']) {
|
|
5713
5755
|
if (!geoParquetPromise) {
|
|
5714
5756
|
geoParquetPromise = loadScript('geoparquet.js');
|
|
5715
5757
|
}
|
|
@@ -5921,7 +5963,7 @@
|
|
|
5921
5963
|
// gui.container.removeClass('queued-files');
|
|
5922
5964
|
hideImportMenu();
|
|
5923
5965
|
var files = queuedFiles;
|
|
5924
|
-
var undoTransaction =
|
|
5966
|
+
var undoTransaction = model.isEmpty() ? null : createUndoTransaction(gui, 'import files');
|
|
5925
5967
|
var imported = false;
|
|
5926
5968
|
try {
|
|
5927
5969
|
if (files.length > 0) {
|
|
@@ -5941,7 +5983,10 @@
|
|
|
5941
5983
|
gui.clearMode();
|
|
5942
5984
|
}
|
|
5943
5985
|
if (undoTransaction && imported) {
|
|
5944
|
-
|
|
5986
|
+
addUndoTransactionToHistory(gui, undoTransaction, {
|
|
5987
|
+
flags: {select: true, arc_count: true},
|
|
5988
|
+
entryPrefix: 'import'
|
|
5989
|
+
});
|
|
5945
5990
|
}
|
|
5946
5991
|
}
|
|
5947
5992
|
|
|
@@ -6174,56 +6219,6 @@
|
|
|
6174
6219
|
return imported;
|
|
6175
6220
|
}
|
|
6176
6221
|
|
|
6177
|
-
function createImportUndoTransaction() {
|
|
6178
|
-
var Transaction;
|
|
6179
|
-
if (model.isEmpty() || !appUndoIsEnabled()) return null;
|
|
6180
|
-
if (!gui.undo || typeof gui.undo.addHistoryState != 'function') return null;
|
|
6181
|
-
Transaction = internal.UndoTransaction && (internal.UndoTransaction.UndoTransaction || internal.UndoTransaction);
|
|
6182
|
-
return Transaction ? new Transaction('import files') : null;
|
|
6183
|
-
}
|
|
6184
|
-
|
|
6185
|
-
function addImportUndoHistory(tx) {
|
|
6186
|
-
getStoredUndoHistory().addTransaction(tx, {
|
|
6187
|
-
flags: {select: true, arc_count: true},
|
|
6188
|
-
entryPrefix: 'import',
|
|
6189
|
-
maxStates: getUndoHistoryLimit()
|
|
6190
|
-
}).catch(function(e) {
|
|
6191
|
-
console.error(e);
|
|
6192
|
-
});
|
|
6193
|
-
}
|
|
6194
|
-
|
|
6195
|
-
function getStoredUndoHistory() {
|
|
6196
|
-
if (!gui.storedUndoHistory) {
|
|
6197
|
-
gui.storedUndoHistory = createStoredUndoHistory(gui);
|
|
6198
|
-
}
|
|
6199
|
-
return gui.storedUndoHistory;
|
|
6200
|
-
}
|
|
6201
|
-
|
|
6202
|
-
function getUndoHistoryLimit() {
|
|
6203
|
-
var opt = gui.options && gui.options.undoHistoryLimit;
|
|
6204
|
-
return opt > 0 ? opt : 10;
|
|
6205
|
-
}
|
|
6206
|
-
|
|
6207
|
-
function appUndoIsEnabled() {
|
|
6208
|
-
var opt = gui.options && (gui.options.undoCommands || gui.options.appUndo);
|
|
6209
|
-
var query = getQueryValue('undo');
|
|
6210
|
-
if (opt === true || query == 'on' || query == 'commands') return true;
|
|
6211
|
-
if (gui.appUndoIsEnabled) return gui.appUndoIsEnabled();
|
|
6212
|
-
try {
|
|
6213
|
-
return window.localStorage && window.localStorage.getItem('mapshaper.undo') == 'on';
|
|
6214
|
-
} catch(e) {
|
|
6215
|
-
return false;
|
|
6216
|
-
}
|
|
6217
|
-
}
|
|
6218
|
-
|
|
6219
|
-
function getQueryValue(key) {
|
|
6220
|
-
var rxp, match;
|
|
6221
|
-
if (typeof window == 'undefined' || !window.location) return null;
|
|
6222
|
-
rxp = new RegExp('[?&]' + key + '=([^&]+)');
|
|
6223
|
-
match = rxp.exec(window.location.search);
|
|
6224
|
-
return match ? decodeURIComponent(match[1]) : null;
|
|
6225
|
-
}
|
|
6226
|
-
|
|
6227
6222
|
|
|
6228
6223
|
|
|
6229
6224
|
function filesMayContainPaths(files) {
|
|
@@ -6731,13 +6726,11 @@
|
|
|
6731
6726
|
|
|
6732
6727
|
function message() {
|
|
6733
6728
|
var msg = GUI.formatMessageArgs(arguments);
|
|
6734
|
-
if (gui.notify) {
|
|
6735
|
-
gui.notify({severity: 'info', body: msg});
|
|
6736
|
-
} else {
|
|
6729
|
+
if (!gui.notify) {
|
|
6737
6730
|
// Fallback for early messages before MessageControl is constructed
|
|
6738
6731
|
gui.message(msg);
|
|
6739
|
-
internal.logArgs(arguments);
|
|
6740
6732
|
}
|
|
6733
|
+
internal.logArgs(arguments);
|
|
6741
6734
|
}
|
|
6742
6735
|
|
|
6743
6736
|
// CLI warnings used to surface as modal alerts, which interrupt the user
|
|
@@ -7789,8 +7782,8 @@
|
|
|
7789
7782
|
|
|
7790
7783
|
function startSimplifyUndoSession(dataset) {
|
|
7791
7784
|
var tx;
|
|
7792
|
-
if (!dataset || !dataset.arcs || !appUndoIsEnabled()) return;
|
|
7793
|
-
tx =
|
|
7785
|
+
if (!dataset || !dataset.arcs || !appUndoIsEnabled(gui)) return;
|
|
7786
|
+
tx = createUndoTransaction(gui, 'simplify');
|
|
7794
7787
|
if (!tx) return;
|
|
7795
7788
|
tx.captureArcsSimplificationBefore(dataset.arcs, {operation: 'simplifyTool'});
|
|
7796
7789
|
tx.captureDatasetInfoBefore(dataset, {operation: 'simplifyTool'});
|
|
@@ -7808,54 +7801,12 @@
|
|
|
7808
7801
|
var session = undoSession;
|
|
7809
7802
|
undoSession = null;
|
|
7810
7803
|
if (!session || !session.changed) return;
|
|
7811
|
-
|
|
7804
|
+
addUndoTransactionToHistory(gui, session.tx, {
|
|
7812
7805
|
flags: {simplify: true, info: true},
|
|
7813
|
-
entryPrefix: 'simplify-tool'
|
|
7814
|
-
maxStates: getUndoHistoryLimit()
|
|
7815
|
-
}).catch(function(e) {
|
|
7816
|
-
console.error(e);
|
|
7806
|
+
entryPrefix: 'simplify-tool'
|
|
7817
7807
|
});
|
|
7818
7808
|
}
|
|
7819
7809
|
|
|
7820
|
-
function createSimplifyUndoTransaction() {
|
|
7821
|
-
var Transaction;
|
|
7822
|
-
if (!gui.undo || typeof gui.undo.addHistoryState != 'function') return null;
|
|
7823
|
-
Transaction = internal.UndoTransaction && (internal.UndoTransaction.UndoTransaction || internal.UndoTransaction);
|
|
7824
|
-
return Transaction ? new Transaction('simplify') : null;
|
|
7825
|
-
}
|
|
7826
|
-
|
|
7827
|
-
function getStoredUndoHistory() {
|
|
7828
|
-
if (!gui.storedUndoHistory) {
|
|
7829
|
-
gui.storedUndoHistory = createStoredUndoHistory(gui);
|
|
7830
|
-
}
|
|
7831
|
-
return gui.storedUndoHistory;
|
|
7832
|
-
}
|
|
7833
|
-
|
|
7834
|
-
function getUndoHistoryLimit() {
|
|
7835
|
-
var opt = gui.options && gui.options.undoHistoryLimit;
|
|
7836
|
-
return opt > 0 ? opt : 10;
|
|
7837
|
-
}
|
|
7838
|
-
|
|
7839
|
-
function appUndoIsEnabled() {
|
|
7840
|
-
var opt = gui.options && (gui.options.undoCommands || gui.options.appUndo);
|
|
7841
|
-
var query = getQueryValue('undo');
|
|
7842
|
-
if (opt === true || query == 'on' || query == 'commands') return true;
|
|
7843
|
-
if (gui.appUndoIsEnabled) return gui.appUndoIsEnabled();
|
|
7844
|
-
try {
|
|
7845
|
-
return window.localStorage && window.localStorage.getItem('mapshaper.undo') == 'on';
|
|
7846
|
-
} catch(e) {
|
|
7847
|
-
return false;
|
|
7848
|
-
}
|
|
7849
|
-
}
|
|
7850
|
-
|
|
7851
|
-
function getQueryValue(key) {
|
|
7852
|
-
var rxp, match;
|
|
7853
|
-
if (typeof window == 'undefined' || !window.location) return null;
|
|
7854
|
-
rxp = new RegExp('[?&]' + key + '=([^&]+)');
|
|
7855
|
-
match = rxp.exec(window.location.search);
|
|
7856
|
-
return match ? decodeURIComponent(match[1]) : null;
|
|
7857
|
-
}
|
|
7858
|
-
|
|
7859
7810
|
function updateSliderDisplay() {
|
|
7860
7811
|
// TODO: display resolution and vertex count
|
|
7861
7812
|
// var dataset = model.getActiveLayer().dataset;
|
|
@@ -7973,599 +7924,6 @@
|
|
|
7973
7924
|
return internal.formatOptionValue(internal.getLayerTargetId(gui.model, lyr));
|
|
7974
7925
|
}
|
|
7975
7926
|
|
|
7976
|
-
// Diagnostic-only scaffolding for app-wide undo/redo R&D.
|
|
7977
|
-
// It is intentionally dormant unless explicitly enabled.
|
|
7978
|
-
|
|
7979
|
-
var DEFAULT_POLICY = {
|
|
7980
|
-
maxStates: 20,
|
|
7981
|
-
maxBytes: 256 * 1024 * 1024,
|
|
7982
|
-
largeChangeBytes: 64 * 1024 * 1024,
|
|
7983
|
-
captureMode: 'diagnostic',
|
|
7984
|
-
sessionHistory: 'audit-log'
|
|
7985
|
-
};
|
|
7986
|
-
|
|
7987
|
-
var EDITOR_UNDO_INVENTORY = [
|
|
7988
|
-
{
|
|
7989
|
-
name: 'attributes',
|
|
7990
|
-
events: ['data_preupdate', 'data_postupdate'],
|
|
7991
|
-
capture: 'record copies',
|
|
7992
|
-
reusable: true
|
|
7993
|
-
},
|
|
7994
|
-
{
|
|
7995
|
-
name: 'labels',
|
|
7996
|
-
events: ['label_dragstart', 'label_dragend'],
|
|
7997
|
-
capture: 'record copies',
|
|
7998
|
-
reusable: true
|
|
7999
|
-
},
|
|
8000
|
-
{
|
|
8001
|
-
name: 'points and symbols',
|
|
8002
|
-
events: ['symbol_dragend', 'point_add', 'feature_delete'],
|
|
8003
|
-
capture: 'coordinate and feature inserts/deletes',
|
|
8004
|
-
reusable: true
|
|
8005
|
-
},
|
|
8006
|
-
{
|
|
8007
|
-
name: 'vertices and rectangles',
|
|
8008
|
-
events: ['vertex_dragend', 'vertex_delete', 'rectangle_dragend'],
|
|
8009
|
-
capture: 'coordinate and vertex inserts/deletes',
|
|
8010
|
-
reusable: true
|
|
8011
|
-
},
|
|
8012
|
-
{
|
|
8013
|
-
name: 'path drawing',
|
|
8014
|
-
events: ['path_add', 'path_extend'],
|
|
8015
|
-
capture: 'editor events replayed through drawing mode',
|
|
8016
|
-
reusable: false
|
|
8017
|
-
}
|
|
8018
|
-
];
|
|
8019
|
-
|
|
8020
|
-
function createUndoFeasibilityMonitor(gui, opts) {
|
|
8021
|
-
opts = Object.assign({}, DEFAULT_POLICY, opts || {});
|
|
8022
|
-
var tracker = createRuntimeIdTracker();
|
|
8023
|
-
var enabled = isUndoFeasibilityEnabled(gui);
|
|
8024
|
-
var lastReport = null;
|
|
8025
|
-
|
|
8026
|
-
return {
|
|
8027
|
-
beforeCommand,
|
|
8028
|
-
afterCommand,
|
|
8029
|
-
isEnabled: function() { return enabled; },
|
|
8030
|
-
setEnabled: function(val) { enabled = !!val; },
|
|
8031
|
-
getPolicy: function() { return Object.assign({}, opts); },
|
|
8032
|
-
getLastReport: function() { return lastReport; },
|
|
8033
|
-
getEditorUndoInventory: function() { return EDITOR_UNDO_INVENTORY.slice(); }
|
|
8034
|
-
};
|
|
8035
|
-
|
|
8036
|
-
function beforeCommand(commands, commandString) {
|
|
8037
|
-
var start, before;
|
|
8038
|
-
if (!enabled || !gui.model || gui.model.isEmpty()) return null;
|
|
8039
|
-
start = Date.now();
|
|
8040
|
-
before = captureModelState(gui.model, tracker);
|
|
8041
|
-
return {
|
|
8042
|
-
commandString: commandString || '',
|
|
8043
|
-
commandNames: commands.map(function(cmd) { return cmd.name; }),
|
|
8044
|
-
before: before,
|
|
8045
|
-
beforeCaptureMillis: Date.now() - start,
|
|
8046
|
-
startedAt: start
|
|
8047
|
-
};
|
|
8048
|
-
}
|
|
8049
|
-
|
|
8050
|
-
function afterCommand(token, o) {
|
|
8051
|
-
var start, after, report;
|
|
8052
|
-
if (!token) return null;
|
|
8053
|
-
start = Date.now();
|
|
8054
|
-
after = captureModelState(gui.model, tracker);
|
|
8055
|
-
report = diffModelStates(token.before, after, {
|
|
8056
|
-
commandString: token.commandString,
|
|
8057
|
-
commandNames: token.commandNames,
|
|
8058
|
-
error: o && o.error,
|
|
8059
|
-
flags: o && o.flags,
|
|
8060
|
-
policy: opts,
|
|
8061
|
-
timings: {
|
|
8062
|
-
beforeCaptureMillis: token.beforeCaptureMillis,
|
|
8063
|
-
afterCaptureMillis: Date.now() - start,
|
|
8064
|
-
elapsedMillis: Date.now() - token.startedAt
|
|
8065
|
-
}
|
|
8066
|
-
});
|
|
8067
|
-
lastReport = report;
|
|
8068
|
-
gui.dispatchEvent('undo_feasibility_change', report);
|
|
8069
|
-
logUndoFeasibilityReport(report);
|
|
8070
|
-
return report;
|
|
8071
|
-
}
|
|
8072
|
-
}
|
|
8073
|
-
|
|
8074
|
-
function captureModelState(model, tracker) {
|
|
8075
|
-
tracker = tracker || createRuntimeIdTracker();
|
|
8076
|
-
var datasets = model.getDatasets();
|
|
8077
|
-
var active = model.getActiveLayer && model.getActiveLayer();
|
|
8078
|
-
var targets = model.getDefaultTargets ? model.getDefaultTargets() : [];
|
|
8079
|
-
var state = {
|
|
8080
|
-
datasets: [],
|
|
8081
|
-
datasetsById: {},
|
|
8082
|
-
layersById: {},
|
|
8083
|
-
activeLayerId: active && active.layer ? tracker.layerId(active.layer) : null,
|
|
8084
|
-
activeDatasetId: active && active.dataset ? tracker.datasetId(active.dataset) : null,
|
|
8085
|
-
targetHash: hashStableValue(targets.map(function(target) {
|
|
8086
|
-
return {
|
|
8087
|
-
dataset: tracker.datasetId(target.dataset),
|
|
8088
|
-
layers: target.layers.map(function(lyr) {
|
|
8089
|
-
return tracker.layerId(lyr);
|
|
8090
|
-
})
|
|
8091
|
-
};
|
|
8092
|
-
})),
|
|
8093
|
-
editorUndo: EDITOR_UNDO_INVENTORY.slice(),
|
|
8094
|
-
policy: Object.assign({}, DEFAULT_POLICY)
|
|
8095
|
-
};
|
|
8096
|
-
|
|
8097
|
-
datasets.forEach(function(dataset, i) {
|
|
8098
|
-
var datasetState = captureDatasetState(dataset, i, tracker);
|
|
8099
|
-
state.datasets.push(datasetState);
|
|
8100
|
-
state.datasetsById[datasetState.id] = datasetState;
|
|
8101
|
-
datasetState.layers.forEach(function(layerState) {
|
|
8102
|
-
state.layersById[layerState.id] = layerState;
|
|
8103
|
-
});
|
|
8104
|
-
});
|
|
8105
|
-
state.catalogHash = hashStableValue({
|
|
8106
|
-
datasets: state.datasets.map(function(dataset) {
|
|
8107
|
-
return {
|
|
8108
|
-
id: dataset.id,
|
|
8109
|
-
index: dataset.index,
|
|
8110
|
-
layers: dataset.layerIds
|
|
8111
|
-
};
|
|
8112
|
-
}),
|
|
8113
|
-
activeLayerId: state.activeLayerId,
|
|
8114
|
-
activeDatasetId: state.activeDatasetId,
|
|
8115
|
-
targetHash: state.targetHash
|
|
8116
|
-
});
|
|
8117
|
-
state.bytes = estimateStateBytes(state);
|
|
8118
|
-
return state;
|
|
8119
|
-
}
|
|
8120
|
-
|
|
8121
|
-
function diffModelStates(before, after, opts) {
|
|
8122
|
-
opts = opts || {};
|
|
8123
|
-
var changes = {
|
|
8124
|
-
catalog: before.catalogHash != after.catalogHash,
|
|
8125
|
-
selection: before.activeLayerId != after.activeLayerId ||
|
|
8126
|
-
before.activeDatasetId != after.activeDatasetId ||
|
|
8127
|
-
before.targetHash != after.targetHash,
|
|
8128
|
-
datasets: [],
|
|
8129
|
-
layers: []
|
|
8130
|
-
};
|
|
8131
|
-
var ids = mergeKeys(before.datasetsById, after.datasetsById);
|
|
8132
|
-
var layerIds = mergeKeys(before.layersById, after.layersById);
|
|
8133
|
-
|
|
8134
|
-
ids.forEach(function(id) {
|
|
8135
|
-
var a = before.datasetsById[id];
|
|
8136
|
-
var b = after.datasetsById[id];
|
|
8137
|
-
var change = diffDatasetState(a, b);
|
|
8138
|
-
if (change) changes.datasets.push(change);
|
|
8139
|
-
});
|
|
8140
|
-
|
|
8141
|
-
layerIds.forEach(function(id) {
|
|
8142
|
-
var a = before.layersById[id];
|
|
8143
|
-
var b = after.layersById[id];
|
|
8144
|
-
var change = diffLayerState(a, b);
|
|
8145
|
-
if (change) changes.layers.push(change);
|
|
8146
|
-
});
|
|
8147
|
-
|
|
8148
|
-
return {
|
|
8149
|
-
type: 'undo-feasibility',
|
|
8150
|
-
command: opts.commandString || '',
|
|
8151
|
-
commandNames: opts.commandNames || [],
|
|
8152
|
-
failed: !!(opts.error),
|
|
8153
|
-
error: opts.error ? String(opts.error.message || opts.error) : null,
|
|
8154
|
-
changes: changes,
|
|
8155
|
-
storage: estimateUndoStorage(before, after, changes, opts.policy || DEFAULT_POLICY),
|
|
8156
|
-
timings: opts.timings || null,
|
|
8157
|
-
restore: getRestoreContract(changes),
|
|
8158
|
-
policy: getUndoPolicy(opts.policy || DEFAULT_POLICY)
|
|
8159
|
-
};
|
|
8160
|
-
}
|
|
8161
|
-
|
|
8162
|
-
function getUndoPolicy(policy) {
|
|
8163
|
-
policy = Object.assign({}, DEFAULT_POLICY, policy || {});
|
|
8164
|
-
return {
|
|
8165
|
-
optIn: true,
|
|
8166
|
-
enableWith: ['gui option undoFeasibility', 'URL parameter undo=diagnostic', 'localStorage mapshaper.undo=diagnostic'],
|
|
8167
|
-
stackLimits: {
|
|
8168
|
-
maxStates: policy.maxStates,
|
|
8169
|
-
maxBytes: policy.maxBytes,
|
|
8170
|
-
largeChangeBytes: policy.largeChangeBytes
|
|
8171
|
-
},
|
|
8172
|
-
coexistence: 'Existing editor undo remains event-based; app-wide command undo should use the same UI state events after it graduates from diagnostics.',
|
|
8173
|
-
sessionHistory: 'Session history remains an audit log and is not rewound by undo/redo.'
|
|
8174
|
-
};
|
|
8175
|
-
}
|
|
8176
|
-
|
|
8177
|
-
function getRestoreContract(changes) {
|
|
8178
|
-
var flags = {
|
|
8179
|
-
select: changes.selection || changes.catalog,
|
|
8180
|
-
arc_count: false
|
|
8181
|
-
};
|
|
8182
|
-
var levels = [];
|
|
8183
|
-
|
|
8184
|
-
changes.datasets.forEach(function(change) {
|
|
8185
|
-
if (change.status != 'changed') {
|
|
8186
|
-
levels.push('catalog');
|
|
8187
|
-
} else {
|
|
8188
|
-
if (change.arcs) {
|
|
8189
|
-
levels.push('dataset');
|
|
8190
|
-
flags.arc_count = true;
|
|
8191
|
-
}
|
|
8192
|
-
if (change.info) levels.push('dataset-info');
|
|
8193
|
-
if (change.layerOrder) levels.push('layer-order');
|
|
8194
|
-
}
|
|
8195
|
-
});
|
|
8196
|
-
changes.layers.forEach(function(change) {
|
|
8197
|
-
if (change.status != 'changed') {
|
|
8198
|
-
levels.push('catalog');
|
|
8199
|
-
} else {
|
|
8200
|
-
if (change.shapes) levels.push('layer-shapes');
|
|
8201
|
-
if (change.data) levels.push('layer-data');
|
|
8202
|
-
if (change.meta) levels.push('layer-meta');
|
|
8203
|
-
}
|
|
8204
|
-
});
|
|
8205
|
-
|
|
8206
|
-
return {
|
|
8207
|
-
levels: unique(levels),
|
|
8208
|
-
updateFlags: flags,
|
|
8209
|
-
redoInvalidation: 'Discard redo entries when a new command report contains data, catalog, or selection changes.',
|
|
8210
|
-
failureHandling: 'Capture starts before command execution, so partial success after an error can still be rolled back.'
|
|
8211
|
-
};
|
|
8212
|
-
}
|
|
8213
|
-
|
|
8214
|
-
function createRuntimeIdTracker() {
|
|
8215
|
-
var datasetIds = new WeakMap();
|
|
8216
|
-
var layerIds = new WeakMap();
|
|
8217
|
-
var datasetId = 0;
|
|
8218
|
-
var layerId = 0;
|
|
8219
|
-
return {
|
|
8220
|
-
datasetId: function(dataset) {
|
|
8221
|
-
if (!datasetIds.has(dataset)) datasetIds.set(dataset, 'd' + (++datasetId));
|
|
8222
|
-
return datasetIds.get(dataset);
|
|
8223
|
-
},
|
|
8224
|
-
layerId: function(layer) {
|
|
8225
|
-
if (!layerIds.has(layer)) layerIds.set(layer, 'l' + (++layerId));
|
|
8226
|
-
return layerIds.get(layer);
|
|
8227
|
-
}
|
|
8228
|
-
};
|
|
8229
|
-
}
|
|
8230
|
-
|
|
8231
|
-
function captureDatasetState(dataset, i, tracker) {
|
|
8232
|
-
var layers = dataset.layers.map(function(lyr, j) {
|
|
8233
|
-
return captureLayerState(lyr, j, tracker, dataset);
|
|
8234
|
-
});
|
|
8235
|
-
var arcs = captureArcsState(dataset.arcs);
|
|
8236
|
-
var info = captureValueState(dataset.info || null);
|
|
8237
|
-
var state = {
|
|
8238
|
-
id: tracker.datasetId(dataset),
|
|
8239
|
-
index: i,
|
|
8240
|
-
layerIds: layers.map(function(lyr) { return lyr.id; }),
|
|
8241
|
-
layerOrderHash: hashStableValue(layers.map(function(lyr) { return lyr.id; })),
|
|
8242
|
-
arcs: arcs,
|
|
8243
|
-
info: info,
|
|
8244
|
-
layers: layers
|
|
8245
|
-
};
|
|
8246
|
-
state.bytes = arcs.bytes + info.bytes + layers.reduce(function(memo, lyr) {
|
|
8247
|
-
return memo + lyr.bytes;
|
|
8248
|
-
}, 0);
|
|
8249
|
-
return state;
|
|
8250
|
-
}
|
|
8251
|
-
|
|
8252
|
-
function captureLayerState(lyr, i, tracker, dataset) {
|
|
8253
|
-
var data = captureDataState(lyr.data);
|
|
8254
|
-
var shapes = captureValueState(lyr.shapes || null);
|
|
8255
|
-
var meta = captureValueState(getLayerMeta(lyr));
|
|
8256
|
-
return {
|
|
8257
|
-
id: tracker.layerId(lyr),
|
|
8258
|
-
datasetId: tracker.datasetId(dataset),
|
|
8259
|
-
index: i,
|
|
8260
|
-
name: lyr.name || '',
|
|
8261
|
-
geometry_type: lyr.geometry_type || null,
|
|
8262
|
-
data: data,
|
|
8263
|
-
shapes: shapes,
|
|
8264
|
-
meta: meta,
|
|
8265
|
-
bytes: data.bytes + shapes.bytes + meta.bytes
|
|
8266
|
-
};
|
|
8267
|
-
}
|
|
8268
|
-
|
|
8269
|
-
function captureDataState(data) {
|
|
8270
|
-
var records = data ? data.getRecords() : null;
|
|
8271
|
-
var state = captureValueState(records);
|
|
8272
|
-
state.recordCount = data ? data.size() : 0;
|
|
8273
|
-
state.fields = data ? data.getFields() : [];
|
|
8274
|
-
return state;
|
|
8275
|
-
}
|
|
8276
|
-
|
|
8277
|
-
function captureArcsState(arcs) {
|
|
8278
|
-
var data, bytes;
|
|
8279
|
-
if (!arcs) {
|
|
8280
|
-
return {
|
|
8281
|
-
exists: false,
|
|
8282
|
-
arcCount: 0,
|
|
8283
|
-
pointCount: 0,
|
|
8284
|
-
retainedInterval: 0,
|
|
8285
|
-
hash: 'null',
|
|
8286
|
-
bytes: 0
|
|
8287
|
-
};
|
|
8288
|
-
}
|
|
8289
|
-
data = arcs.getVertexData();
|
|
8290
|
-
bytes = typedBytes(data.nn) + typedBytes(data.xx) + typedBytes(data.yy) + typedBytes(data.zz);
|
|
8291
|
-
return {
|
|
8292
|
-
exists: true,
|
|
8293
|
-
arcCount: arcs.size(),
|
|
8294
|
-
pointCount: arcs.getPointCount(),
|
|
8295
|
-
retainedInterval: arcs.getRetainedInterval(),
|
|
8296
|
-
hash: hashStableValue({
|
|
8297
|
-
nn: hashTypedArray(data.nn),
|
|
8298
|
-
xx: hashTypedArray(data.xx),
|
|
8299
|
-
yy: hashTypedArray(data.yy),
|
|
8300
|
-
zz: data.zz ? hashTypedArray(data.zz) : null,
|
|
8301
|
-
zlimit: arcs.getRetainedInterval()
|
|
8302
|
-
}),
|
|
8303
|
-
bytes: bytes
|
|
8304
|
-
};
|
|
8305
|
-
}
|
|
8306
|
-
|
|
8307
|
-
function captureValueState(obj) {
|
|
8308
|
-
var str = stableStringify$1(obj);
|
|
8309
|
-
return {
|
|
8310
|
-
hash: hashString$1(str),
|
|
8311
|
-
bytes: estimateStringBytes(str)
|
|
8312
|
-
};
|
|
8313
|
-
}
|
|
8314
|
-
|
|
8315
|
-
function diffDatasetState(a, b) {
|
|
8316
|
-
if (!a) return {id: b.id, status: 'added', bytes: b.bytes};
|
|
8317
|
-
if (!b) return {id: a.id, status: 'removed', bytes: a.bytes};
|
|
8318
|
-
var change = {
|
|
8319
|
-
id: a.id,
|
|
8320
|
-
status: 'changed',
|
|
8321
|
-
index: a.index != b.index,
|
|
8322
|
-
layerOrder: a.layerOrderHash != b.layerOrderHash,
|
|
8323
|
-
arcs: a.arcs.hash != b.arcs.hash,
|
|
8324
|
-
info: a.info.hash != b.info.hash,
|
|
8325
|
-
beforeBytes: a.bytes,
|
|
8326
|
-
afterBytes: b.bytes
|
|
8327
|
-
};
|
|
8328
|
-
return change.index || change.layerOrder || change.arcs || change.info ? change : null;
|
|
8329
|
-
}
|
|
8330
|
-
|
|
8331
|
-
function diffLayerState(a, b) {
|
|
8332
|
-
if (!a) return {id: b.id, datasetId: b.datasetId, status: 'added', bytes: b.bytes};
|
|
8333
|
-
if (!b) return {id: a.id, datasetId: a.datasetId, status: 'removed', bytes: a.bytes};
|
|
8334
|
-
var change = {
|
|
8335
|
-
id: a.id,
|
|
8336
|
-
datasetId: b.datasetId,
|
|
8337
|
-
status: 'changed',
|
|
8338
|
-
index: a.index != b.index,
|
|
8339
|
-
data: a.data.hash != b.data.hash,
|
|
8340
|
-
shapes: a.shapes.hash != b.shapes.hash,
|
|
8341
|
-
meta: a.meta.hash != b.meta.hash,
|
|
8342
|
-
beforeBytes: a.bytes,
|
|
8343
|
-
afterBytes: b.bytes,
|
|
8344
|
-
recordCount: b.data.recordCount,
|
|
8345
|
-
fields: b.data.fields
|
|
8346
|
-
};
|
|
8347
|
-
return change.index || change.data || change.shapes || change.meta ? change : null;
|
|
8348
|
-
}
|
|
8349
|
-
|
|
8350
|
-
function estimateUndoStorage(before, after, changes, policy) {
|
|
8351
|
-
var bytes = 0;
|
|
8352
|
-
var strategy = 'none';
|
|
8353
|
-
var changedDatasets = {};
|
|
8354
|
-
var changedLayers = {};
|
|
8355
|
-
var alternatives = estimateStorageAlternatives(before, after, changes);
|
|
8356
|
-
|
|
8357
|
-
changes.datasets.forEach(function(change) {
|
|
8358
|
-
var beforeState = before.datasetsById[change.id];
|
|
8359
|
-
var afterState = after.datasetsById[change.id];
|
|
8360
|
-
if (change.status != 'changed' || change.arcs || change.layerOrder) {
|
|
8361
|
-
bytes += (beforeState ? beforeState.bytes : 0) + (afterState ? afterState.bytes : 0);
|
|
8362
|
-
changedDatasets[change.id] = true;
|
|
8363
|
-
} else if (change.info) {
|
|
8364
|
-
bytes += (beforeState ? beforeState.info.bytes : 0) + (afterState ? afterState.info.bytes : 0);
|
|
8365
|
-
strategy = strategy == 'none' ? 'dataset-info' : strategy;
|
|
8366
|
-
}
|
|
8367
|
-
});
|
|
8368
|
-
changes.layers.forEach(function(change) {
|
|
8369
|
-
var beforeState = before.layersById[change.id];
|
|
8370
|
-
var afterState = after.layersById[change.id];
|
|
8371
|
-
if (changedDatasets[change.datasetId]) return;
|
|
8372
|
-
if (change.data && !change.shapes && !change.meta) {
|
|
8373
|
-
bytes += (beforeState ? beforeState.data.bytes : 0) + (afterState ? afterState.data.bytes : 0);
|
|
8374
|
-
strategy = strategy == 'none' ? 'table' : strategy;
|
|
8375
|
-
} else {
|
|
8376
|
-
bytes += (beforeState ? beforeState.bytes : 0) + (afterState ? afterState.bytes : 0);
|
|
8377
|
-
changedLayers[change.id] = true;
|
|
8378
|
-
}
|
|
8379
|
-
});
|
|
8380
|
-
|
|
8381
|
-
if (Object.keys(changedDatasets).length > 0) {
|
|
8382
|
-
strategy = 'dataset';
|
|
8383
|
-
} else if (Object.keys(changedLayers).length > 0) {
|
|
8384
|
-
strategy = strategy == 'none' ? 'layer' : 'mixed';
|
|
8385
|
-
}
|
|
8386
|
-
|
|
8387
|
-
return {
|
|
8388
|
-
strategy: strategy,
|
|
8389
|
-
estimatedBytes: bytes,
|
|
8390
|
-
displaySize: formatBytes$1(bytes),
|
|
8391
|
-
alternatives: alternatives,
|
|
8392
|
-
exceedsLargeChangeLimit: bytes > policy.largeChangeBytes,
|
|
8393
|
-
exceedsStackLimit: bytes > policy.maxBytes
|
|
8394
|
-
};
|
|
8395
|
-
}
|
|
8396
|
-
|
|
8397
|
-
function estimateStorageAlternatives(before, after, changes) {
|
|
8398
|
-
var alternatives = {
|
|
8399
|
-
fullSession: before.bytes + after.bytes,
|
|
8400
|
-
changedDatasets: 0,
|
|
8401
|
-
changedLayers: 0,
|
|
8402
|
-
changedTables: 0,
|
|
8403
|
-
changedArcs: 0
|
|
8404
|
-
};
|
|
8405
|
-
var datasetIds = {};
|
|
8406
|
-
|
|
8407
|
-
changes.datasets.forEach(function(change) {
|
|
8408
|
-
var beforeState = before.datasetsById[change.id];
|
|
8409
|
-
var afterState = after.datasetsById[change.id];
|
|
8410
|
-
var datasetBytes = (beforeState ? beforeState.bytes : 0) + (afterState ? afterState.bytes : 0);
|
|
8411
|
-
alternatives.changedDatasets += datasetBytes;
|
|
8412
|
-
datasetIds[change.id] = true;
|
|
8413
|
-
if (change.arcs) {
|
|
8414
|
-
alternatives.changedArcs += (beforeState ? beforeState.arcs.bytes : 0) +
|
|
8415
|
-
(afterState ? afterState.arcs.bytes : 0);
|
|
8416
|
-
}
|
|
8417
|
-
});
|
|
8418
|
-
changes.layers.forEach(function(change) {
|
|
8419
|
-
var beforeState = before.layersById[change.id];
|
|
8420
|
-
var afterState = after.layersById[change.id];
|
|
8421
|
-
if (datasetIds[change.datasetId]) return;
|
|
8422
|
-
alternatives.changedLayers += (beforeState ? beforeState.bytes : 0) +
|
|
8423
|
-
(afterState ? afterState.bytes : 0);
|
|
8424
|
-
if (change.data) {
|
|
8425
|
-
alternatives.changedTables += (beforeState ? beforeState.data.bytes : 0) +
|
|
8426
|
-
(afterState ? afterState.data.bytes : 0);
|
|
8427
|
-
}
|
|
8428
|
-
});
|
|
8429
|
-
Object.keys(alternatives).forEach(function(key) {
|
|
8430
|
-
alternatives[key] = {
|
|
8431
|
-
estimatedBytes: alternatives[key],
|
|
8432
|
-
displaySize: formatBytes$1(alternatives[key])
|
|
8433
|
-
};
|
|
8434
|
-
});
|
|
8435
|
-
return alternatives;
|
|
8436
|
-
}
|
|
8437
|
-
|
|
8438
|
-
function estimateStateBytes(state) {
|
|
8439
|
-
return state.datasets.reduce(function(memo, dataset) {
|
|
8440
|
-
return memo + dataset.bytes;
|
|
8441
|
-
}, 0);
|
|
8442
|
-
}
|
|
8443
|
-
|
|
8444
|
-
function getLayerMeta(lyr) {
|
|
8445
|
-
var meta = {};
|
|
8446
|
-
Object.keys(lyr).sort().forEach(function(key) {
|
|
8447
|
-
if (key == 'data' || key == 'shapes') return;
|
|
8448
|
-
meta[key] = lyr[key];
|
|
8449
|
-
});
|
|
8450
|
-
return meta;
|
|
8451
|
-
}
|
|
8452
|
-
|
|
8453
|
-
function isUndoFeasibilityEnabled(gui) {
|
|
8454
|
-
var opt = gui.options && gui.options.undoFeasibility;
|
|
8455
|
-
var query = getQueryValue$2('undo');
|
|
8456
|
-
if (opt === true || opt == 'diagnostic') return true;
|
|
8457
|
-
if (query == 'diagnostic' || query == 'debug') return true;
|
|
8458
|
-
try {
|
|
8459
|
-
return window.localStorage && window.localStorage.getItem('mapshaper.undo') == 'diagnostic';
|
|
8460
|
-
} catch(e) {
|
|
8461
|
-
return false;
|
|
8462
|
-
}
|
|
8463
|
-
}
|
|
8464
|
-
|
|
8465
|
-
function getQueryValue$2(key) {
|
|
8466
|
-
var rxp, match;
|
|
8467
|
-
if (typeof window == 'undefined' || !window.location) return null;
|
|
8468
|
-
rxp = new RegExp('[?&]' + key + '=([^&]+)');
|
|
8469
|
-
match = rxp.exec(window.location.search);
|
|
8470
|
-
return match ? decodeURIComponent(match[1]) : null;
|
|
8471
|
-
}
|
|
8472
|
-
|
|
8473
|
-
function logUndoFeasibilityReport(report) {
|
|
8474
|
-
if (typeof console == 'undefined' || !console.log) return;
|
|
8475
|
-
console.log('[mapshaper undo feasibility]', {
|
|
8476
|
-
command: report.command,
|
|
8477
|
-
failed: report.failed,
|
|
8478
|
-
changes: report.changes,
|
|
8479
|
-
storage: report.storage,
|
|
8480
|
-
restore: report.restore
|
|
8481
|
-
});
|
|
8482
|
-
}
|
|
8483
|
-
|
|
8484
|
-
function mergeKeys(a, b) {
|
|
8485
|
-
var index = {};
|
|
8486
|
-
Object.keys(a).forEach(function(key) { index[key] = true; });
|
|
8487
|
-
Object.keys(b).forEach(function(key) { index[key] = true; });
|
|
8488
|
-
return Object.keys(index);
|
|
8489
|
-
}
|
|
8490
|
-
|
|
8491
|
-
function unique(arr) {
|
|
8492
|
-
var index = {};
|
|
8493
|
-
arr.forEach(function(item) { index[item] = true; });
|
|
8494
|
-
return Object.keys(index);
|
|
8495
|
-
}
|
|
8496
|
-
|
|
8497
|
-
function hashStableValue(obj) {
|
|
8498
|
-
return hashString$1(stableStringify$1(obj));
|
|
8499
|
-
}
|
|
8500
|
-
|
|
8501
|
-
function stableStringify$1(obj) {
|
|
8502
|
-
var seen = [];
|
|
8503
|
-
return stringify(obj, 0);
|
|
8504
|
-
|
|
8505
|
-
function stringify(val, depth) {
|
|
8506
|
-
var keys;
|
|
8507
|
-
if (val === null) return 'null';
|
|
8508
|
-
if (val === undefined) return 'undefined';
|
|
8509
|
-
if (typeof val == 'number') return Number.isNaN(val) ? 'NaN' : String(val);
|
|
8510
|
-
if (typeof val == 'string') return JSON.stringify(val);
|
|
8511
|
-
if (typeof val == 'boolean') return String(val);
|
|
8512
|
-
if (typeof val == 'function') return '[Function]';
|
|
8513
|
-
if (ArrayBuffer.isView(val)) return hashTypedArray(val);
|
|
8514
|
-
if (seen.indexOf(val) > -1) return '[Circular]';
|
|
8515
|
-
if (depth > 8) return '[MaxDepth]';
|
|
8516
|
-
seen.push(val);
|
|
8517
|
-
if (Array.isArray(val)) {
|
|
8518
|
-
return '[' + val.map(function(item) {
|
|
8519
|
-
return stringify(item, depth + 1);
|
|
8520
|
-
}).join(',') + ']';
|
|
8521
|
-
}
|
|
8522
|
-
keys = Object.keys(val).sort();
|
|
8523
|
-
return '{' + keys.map(function(key) {
|
|
8524
|
-
return JSON.stringify(key) + ':' + stringify(val[key], depth + 1);
|
|
8525
|
-
}).join(',') + '}';
|
|
8526
|
-
}
|
|
8527
|
-
}
|
|
8528
|
-
|
|
8529
|
-
function hashTypedArray(arr) {
|
|
8530
|
-
var hash, view;
|
|
8531
|
-
if (!arr) return 'null';
|
|
8532
|
-
view = new Uint8Array(arr.buffer, arr.byteOffset, arr.byteLength);
|
|
8533
|
-
hash = hashBytes(view);
|
|
8534
|
-
return arr.constructor.name + ':' + arr.length + ':' + hash;
|
|
8535
|
-
}
|
|
8536
|
-
|
|
8537
|
-
function hashBytes(bytes) {
|
|
8538
|
-
var hash = 2166136261;
|
|
8539
|
-
for (var i=0, n=bytes.length; i<n; i++) {
|
|
8540
|
-
hash ^= bytes[i];
|
|
8541
|
-
hash += (hash << 1) + (hash << 4) + (hash << 7) + (hash << 8) + (hash << 24);
|
|
8542
|
-
}
|
|
8543
|
-
return (hash >>> 0).toString(16);
|
|
8544
|
-
}
|
|
8545
|
-
|
|
8546
|
-
function hashString$1(str) {
|
|
8547
|
-
var hash = 2166136261;
|
|
8548
|
-
for (var i=0, n=str.length; i<n; i++) {
|
|
8549
|
-
hash ^= str.charCodeAt(i);
|
|
8550
|
-
hash += (hash << 1) + (hash << 4) + (hash << 7) + (hash << 8) + (hash << 24);
|
|
8551
|
-
}
|
|
8552
|
-
return (hash >>> 0).toString(16);
|
|
8553
|
-
}
|
|
8554
|
-
|
|
8555
|
-
function typedBytes(arr) {
|
|
8556
|
-
return arr ? arr.byteLength : 0;
|
|
8557
|
-
}
|
|
8558
|
-
|
|
8559
|
-
function estimateStringBytes(str) {
|
|
8560
|
-
return str.length * 2;
|
|
8561
|
-
}
|
|
8562
|
-
|
|
8563
|
-
function formatBytes$1(bytes) {
|
|
8564
|
-
if (bytes > 1024 * 1024) return (bytes / (1024 * 1024)).toFixed(1) + 'MB';
|
|
8565
|
-
if (bytes > 1024) return Math.round(bytes / 1024) + 'KB';
|
|
8566
|
-
return bytes + 'B';
|
|
8567
|
-
}
|
|
8568
|
-
|
|
8569
7927
|
function Console(gui) {
|
|
8570
7928
|
var model = gui.model;
|
|
8571
7929
|
var CURSOR = '$ ';
|
|
@@ -8595,9 +7953,6 @@
|
|
|
8595
7953
|
});
|
|
8596
7954
|
var globals = {}; // share user-defined globals (job.defs) between runs
|
|
8597
7955
|
var sharedVars = {}; // share -vars / -defaults templating scope between runs
|
|
8598
|
-
var undoFeasibility = createUndoFeasibilityMonitor(gui);
|
|
8599
|
-
var storedUndoHistory = createStoredUndoHistory(gui);
|
|
8600
|
-
gui.undoFeasibility = undoFeasibility;
|
|
8601
7956
|
|
|
8602
7957
|
// expose this function, so other components can run commands (e.g. box tool)
|
|
8603
7958
|
this.runMapshaperCommands = runMapshaperCommands;
|
|
@@ -8999,7 +8354,6 @@
|
|
|
8999
8354
|
prevTable = active?.layer.data,
|
|
9000
8355
|
prevTableSize = prevTable ? prevTable.size() : 0,
|
|
9001
8356
|
prevArcCount = prevArcs ? prevArcs.size() : 0,
|
|
9002
|
-
undoCapture = undoFeasibility.beforeCommand(commands, commandString),
|
|
9003
8357
|
undoTransaction = createCommandUndoTransaction(commandString),
|
|
9004
8358
|
job = new internal.Job(model),
|
|
9005
8359
|
commandStart = Date.now();
|
|
@@ -9048,7 +8402,6 @@
|
|
|
9048
8402
|
// signal the map to update even if an error has occured, because the
|
|
9049
8403
|
// commands may have partially succeeded and changes may have occured to
|
|
9050
8404
|
// the data.
|
|
9051
|
-
undoFeasibility.afterCommand(undoCapture, {error: err, flags: flags});
|
|
9052
8405
|
if (!err) {
|
|
9053
8406
|
commandString = internal.standardizeConsoleCommands(commandString);
|
|
9054
8407
|
historyIds.push(gui.session.consoleCommands(commandString));
|
|
@@ -9074,7 +8427,7 @@
|
|
|
9074
8427
|
function createCommandUndoTransaction(commandString) {
|
|
9075
8428
|
var Transaction;
|
|
9076
8429
|
if (!isCommandUndoEnabled()) return null;
|
|
9077
|
-
|
|
8430
|
+
getStoredUndoHistory(gui).getPayloadStore();
|
|
9078
8431
|
Transaction = internal.UndoTransaction.UndoTransaction || internal.UndoTransaction;
|
|
9079
8432
|
return new Transaction(commandString);
|
|
9080
8433
|
}
|
|
@@ -9098,7 +8451,7 @@
|
|
|
9098
8451
|
}
|
|
9099
8452
|
|
|
9100
8453
|
async function addCommandUndoHistory(tx, err, flags, historyIds) {
|
|
9101
|
-
return
|
|
8454
|
+
return getStoredUndoHistory(gui).addTransaction(tx, {
|
|
9102
8455
|
error: err,
|
|
9103
8456
|
flags: flags,
|
|
9104
8457
|
entryPrefix: 'command',
|
|
@@ -9113,15 +8466,8 @@
|
|
|
9113
8466
|
}
|
|
9114
8467
|
|
|
9115
8468
|
function isCommandUndoEnabled() {
|
|
9116
|
-
var opt = gui.options && (gui.options.undoCommands || gui.options.appUndo);
|
|
9117
|
-
var query = getQueryValue('undo');
|
|
9118
8469
|
if (!gui.undo || typeof gui.undo.addHistoryState != 'function') return false;
|
|
9119
|
-
|
|
9120
|
-
try {
|
|
9121
|
-
return window.localStorage && window.localStorage.getItem('mapshaper.undo') == 'on';
|
|
9122
|
-
} catch(e) {
|
|
9123
|
-
return false;
|
|
9124
|
-
}
|
|
8470
|
+
return appUndoIsEnabled(gui);
|
|
9125
8471
|
}
|
|
9126
8472
|
|
|
9127
8473
|
function getCommandUndoHistoryLimit() {
|
|
@@ -9130,8 +8476,8 @@
|
|
|
9130
8476
|
}
|
|
9131
8477
|
|
|
9132
8478
|
function logCommandTiming(commandString, commands, err, totalMillis, undoTiming) {
|
|
9133
|
-
var enabled =
|
|
9134
|
-
|
|
8479
|
+
var enabled = getQueryFlag('command-timing') == 'on' ||
|
|
8480
|
+
getQueryFlag('undo-timing') == 'on';
|
|
9135
8481
|
var undoMillis = undoTiming && undoTiming.undoMillis || 0;
|
|
9136
8482
|
if (!enabled || typeof console == 'undefined' || !console.log) return;
|
|
9137
8483
|
console.log('[mapshaper command timing]', {
|
|
@@ -9147,7 +8493,7 @@
|
|
|
9147
8493
|
});
|
|
9148
8494
|
}
|
|
9149
8495
|
|
|
9150
|
-
function
|
|
8496
|
+
function getQueryFlag(key) {
|
|
9151
8497
|
var rxp, match;
|
|
9152
8498
|
if (typeof window == 'undefined' || !window.location) return null;
|
|
9153
8499
|
rxp = new RegExp('[?&]' + key + '=([^&]+)');
|
|
@@ -10264,8 +9610,9 @@
|
|
|
10264
9610
|
|
|
10265
9611
|
function deleteLayer() {
|
|
10266
9612
|
var target = findLayerById(id);
|
|
10267
|
-
var undoTransaction
|
|
9613
|
+
var undoTransaction;
|
|
10268
9614
|
if (!target) return;
|
|
9615
|
+
undoTransaction = createUndoTransaction(gui, 'delete layer');
|
|
10269
9616
|
if (map.isVisibleLayer(target.layer)) {
|
|
10270
9617
|
// TODO: check for double map refresh after model.deleteLayer() below
|
|
10271
9618
|
setLayerPinning(target.layer, false);
|
|
@@ -10274,7 +9621,10 @@
|
|
|
10274
9621
|
undoTransaction.run(function() {
|
|
10275
9622
|
model.deleteLayer(target.layer, target.dataset);
|
|
10276
9623
|
});
|
|
10277
|
-
|
|
9624
|
+
addUndoTransactionToHistory(gui, undoTransaction, {
|
|
9625
|
+
flags: {select: true, arc_count: true},
|
|
9626
|
+
entryPrefix: 'delete-layer'
|
|
9627
|
+
});
|
|
10278
9628
|
} else {
|
|
10279
9629
|
model.deleteLayer(target.layer, target.dataset);
|
|
10280
9630
|
}
|
|
@@ -10375,41 +9725,18 @@
|
|
|
10375
9725
|
return str;
|
|
10376
9726
|
}
|
|
10377
9727
|
|
|
10378
|
-
function createDeleteLayerUndoTransaction(target) {
|
|
10379
|
-
return createLayerMenuUndoTransaction(target, 'delete layer');
|
|
10380
|
-
}
|
|
10381
|
-
|
|
10382
|
-
function createLayerRenameUndoTransaction(target) {
|
|
10383
|
-
return createLayerMenuUndoTransaction(target, 'rename layer');
|
|
10384
|
-
}
|
|
10385
|
-
|
|
10386
|
-
function createLayerMenuUndoTransaction(target, label) {
|
|
10387
|
-
var Transaction;
|
|
10388
|
-
if (!target || !appUndoIsEnabled()) return null;
|
|
10389
|
-
if (!gui.undo || typeof gui.undo.addHistoryState != 'function') return null;
|
|
10390
|
-
Transaction = internal.UndoTransaction && (internal.UndoTransaction.UndoTransaction || internal.UndoTransaction);
|
|
10391
|
-
return Transaction ? new Transaction(label) : null;
|
|
10392
|
-
}
|
|
10393
|
-
|
|
10394
|
-
function addDeleteLayerUndoHistory(tx) {
|
|
10395
|
-
getStoredUndoHistory().addTransaction(tx, {
|
|
10396
|
-
flags: {select: true, arc_count: true},
|
|
10397
|
-
entryPrefix: 'delete-layer',
|
|
10398
|
-
maxStates: getUndoHistoryLimit()
|
|
10399
|
-
}).catch(function(e) {
|
|
10400
|
-
console.error(e);
|
|
10401
|
-
});
|
|
10402
|
-
}
|
|
10403
|
-
|
|
10404
9728
|
function renameLayer(target, name) {
|
|
10405
9729
|
var undoTransaction;
|
|
10406
9730
|
if (!target || target.layer.name == name) return;
|
|
10407
|
-
undoTransaction =
|
|
9731
|
+
undoTransaction = createUndoTransaction(gui, 'rename layer');
|
|
10408
9732
|
if (undoTransaction) {
|
|
10409
9733
|
undoTransaction.captureLayerMetadataBefore(target.layer, {operation: 'renameLayer', unit: 'name'});
|
|
10410
9734
|
target.layer.name = name;
|
|
10411
9735
|
markLayerChanged(target.layer, {operation: 'renameLayer', unit: 'name'});
|
|
10412
|
-
|
|
9736
|
+
addUndoTransactionToHistory(gui, undoTransaction, {
|
|
9737
|
+
flags: {select: true},
|
|
9738
|
+
entryPrefix: 'rename-layer'
|
|
9739
|
+
});
|
|
10413
9740
|
} else {
|
|
10414
9741
|
target.layer.name = name;
|
|
10415
9742
|
}
|
|
@@ -10417,54 +9744,12 @@
|
|
|
10417
9744
|
updateMenuBtn();
|
|
10418
9745
|
}
|
|
10419
9746
|
|
|
10420
|
-
function addLayerRenameUndoHistory(tx) {
|
|
10421
|
-
getStoredUndoHistory().addTransaction(tx, {
|
|
10422
|
-
flags: {select: true},
|
|
10423
|
-
entryPrefix: 'rename-layer',
|
|
10424
|
-
maxStates: getUndoHistoryLimit()
|
|
10425
|
-
}).catch(function(e) {
|
|
10426
|
-
console.error(e);
|
|
10427
|
-
});
|
|
10428
|
-
}
|
|
10429
|
-
|
|
10430
9747
|
function markLayerChanged(layer, detail) {
|
|
10431
9748
|
if (internal.UndoTracking && internal.UndoTracking.markLayerChanged) {
|
|
10432
9749
|
internal.UndoTracking.markLayerChanged(layer, detail);
|
|
10433
9750
|
}
|
|
10434
9751
|
}
|
|
10435
9752
|
|
|
10436
|
-
function getStoredUndoHistory() {
|
|
10437
|
-
if (!gui.storedUndoHistory) {
|
|
10438
|
-
gui.storedUndoHistory = createStoredUndoHistory(gui);
|
|
10439
|
-
}
|
|
10440
|
-
return gui.storedUndoHistory;
|
|
10441
|
-
}
|
|
10442
|
-
|
|
10443
|
-
function getUndoHistoryLimit() {
|
|
10444
|
-
var opt = gui.options && gui.options.undoHistoryLimit;
|
|
10445
|
-
return opt > 0 ? opt : 10;
|
|
10446
|
-
}
|
|
10447
|
-
|
|
10448
|
-
function appUndoIsEnabled() {
|
|
10449
|
-
var opt = gui.options && (gui.options.undoCommands || gui.options.appUndo);
|
|
10450
|
-
var query = getQueryValue('undo');
|
|
10451
|
-
if (opt === true || query == 'on' || query == 'commands') return true;
|
|
10452
|
-
if (gui.appUndoIsEnabled) return gui.appUndoIsEnabled();
|
|
10453
|
-
try {
|
|
10454
|
-
return window.localStorage && window.localStorage.getItem('mapshaper.undo') == 'on';
|
|
10455
|
-
} catch(e) {
|
|
10456
|
-
return false;
|
|
10457
|
-
}
|
|
10458
|
-
}
|
|
10459
|
-
|
|
10460
|
-
function getQueryValue(key) {
|
|
10461
|
-
var rxp, match;
|
|
10462
|
-
if (typeof window == 'undefined' || !window.location) return null;
|
|
10463
|
-
rxp = new RegExp('[?&]' + key + '=([^&]+)');
|
|
10464
|
-
match = rxp.exec(window.location.search);
|
|
10465
|
-
return match ? decodeURIComponent(match[1]) : null;
|
|
10466
|
-
}
|
|
10467
|
-
|
|
10468
9753
|
function getWarnings(lyr, dataset) {
|
|
10469
9754
|
var file = internal.getLayerSourceFile(lyr, dataset);
|
|
10470
9755
|
var missing = [];
|
|
@@ -10674,19 +9959,6 @@
|
|
|
10674
9959
|
el.attr('aria-disabled', enabled ? 'false' : 'true');
|
|
10675
9960
|
}
|
|
10676
9961
|
|
|
10677
|
-
function appUndoForcedByUrl() {
|
|
10678
|
-
var query = getQueryValue$1('undo');
|
|
10679
|
-
return query == 'on' || query == 'commands';
|
|
10680
|
-
}
|
|
10681
|
-
|
|
10682
|
-
function appUndoSettingIsOn() {
|
|
10683
|
-
try {
|
|
10684
|
-
return window.localStorage && window.localStorage.getItem(APP_UNDO_KEY) == 'on';
|
|
10685
|
-
} catch(e) {
|
|
10686
|
-
return false;
|
|
10687
|
-
}
|
|
10688
|
-
}
|
|
10689
|
-
|
|
10690
9962
|
function setAppUndoEnabled(enabled) {
|
|
10691
9963
|
try {
|
|
10692
9964
|
if (window.localStorage) {
|
|
@@ -10698,9 +9970,6 @@
|
|
|
10698
9970
|
function getRestoreDataNote(gui) {
|
|
10699
9971
|
var stats = getUndoPayloadStats(gui);
|
|
10700
9972
|
var bytes = stats ? stats.ownBytes || 0 : 0;
|
|
10701
|
-
if (bytes === 0) {
|
|
10702
|
-
return 'estimated on-disk restore data: none';
|
|
10703
|
-
}
|
|
10704
9973
|
return 'estimated on-disk restore data: ' + formatBytes(bytes);
|
|
10705
9974
|
}
|
|
10706
9975
|
|
|
@@ -10710,22 +9979,14 @@
|
|
|
10710
9979
|
}
|
|
10711
9980
|
|
|
10712
9981
|
function formatBytes(bytes) {
|
|
10713
|
-
var units = ['
|
|
10714
|
-
var value = bytes;
|
|
9982
|
+
var units = ['KB', 'MB', 'GB'];
|
|
9983
|
+
var value = bytes / 1000;
|
|
10715
9984
|
var i = 0;
|
|
10716
9985
|
while (value >= 1000 && i < units.length - 1) {
|
|
10717
9986
|
value /= 1000;
|
|
10718
9987
|
i++;
|
|
10719
9988
|
}
|
|
10720
|
-
return (
|
|
10721
|
-
}
|
|
10722
|
-
|
|
10723
|
-
function getQueryValue$1(key) {
|
|
10724
|
-
var rxp, match;
|
|
10725
|
-
if (typeof window == 'undefined' || !window.location) return null;
|
|
10726
|
-
rxp = new RegExp('[?&]' + key + '=([^&]+)');
|
|
10727
|
-
match = rxp.exec(window.location.search);
|
|
10728
|
-
return match ? decodeURIComponent(match[1]) : null;
|
|
9989
|
+
return value.toFixed(value < 10 && value >= 0.5 ? 1 : 0) + ' ' + units[i];
|
|
10729
9990
|
}
|
|
10730
9991
|
|
|
10731
9992
|
function SessionHistory(gui) {
|
|
@@ -10955,8 +10216,7 @@
|
|
|
10955
10216
|
}
|
|
10956
10217
|
|
|
10957
10218
|
function Undo(gui) {
|
|
10958
|
-
var history, offset, stashedUndo,
|
|
10959
|
-
storedUndoHistory = createStoredUndoHistory(gui);
|
|
10219
|
+
var history, offset, stashedUndo, editSession;
|
|
10960
10220
|
editSession = createEditSessionUndo();
|
|
10961
10221
|
reset();
|
|
10962
10222
|
|
|
@@ -11318,7 +10578,7 @@
|
|
|
11318
10578
|
function start(nextMode) {
|
|
11319
10579
|
var target, Transaction;
|
|
11320
10580
|
if (!isEditSessionMode(nextMode)) return;
|
|
11321
|
-
if (!appUndoIsEnabled()) return;
|
|
10581
|
+
if (!appUndoIsEnabled(gui)) return;
|
|
11322
10582
|
target = gui.model.getActiveLayer();
|
|
11323
10583
|
if (!target || !target.layer) return;
|
|
11324
10584
|
Transaction = getUndoTransactionConstructor();
|
|
@@ -11337,7 +10597,7 @@
|
|
|
11337
10597
|
}
|
|
11338
10598
|
resetSession();
|
|
11339
10599
|
if (!wasChanged) return;
|
|
11340
|
-
|
|
10600
|
+
getStoredUndoHistory(gui).addTransaction(finishedTx, {
|
|
11341
10601
|
flags: {select: true},
|
|
11342
10602
|
entryPrefix: 'edit-session',
|
|
11343
10603
|
maxStates: getEditSessionUndoHistoryLimit()
|
|
@@ -11392,26 +10652,6 @@
|
|
|
11392
10652
|
return opt > 0 ? opt : 10;
|
|
11393
10653
|
}
|
|
11394
10654
|
|
|
11395
|
-
function appUndoIsEnabled() {
|
|
11396
|
-
var opt = gui.options && (gui.options.undoCommands || gui.options.appUndo);
|
|
11397
|
-
var query = getQueryValue('undo');
|
|
11398
|
-
if (opt === true || query == 'on' || query == 'commands') return true;
|
|
11399
|
-
if (gui.appUndoIsEnabled) return gui.appUndoIsEnabled();
|
|
11400
|
-
try {
|
|
11401
|
-
return window.localStorage && window.localStorage.getItem('mapshaper.undo') == 'on';
|
|
11402
|
-
} catch(e) {
|
|
11403
|
-
return false;
|
|
11404
|
-
}
|
|
11405
|
-
}
|
|
11406
|
-
|
|
11407
|
-
function getQueryValue(key) {
|
|
11408
|
-
var rxp, match;
|
|
11409
|
-
if (typeof window == 'undefined' || !window.location) return null;
|
|
11410
|
-
rxp = new RegExp('[?&]' + key + '=([^&]+)');
|
|
11411
|
-
match = rxp.exec(window.location.search);
|
|
11412
|
-
return match ? decodeURIComponent(match[1]) : null;
|
|
11413
|
-
}
|
|
11414
|
-
|
|
11415
10655
|
}
|
|
11416
10656
|
|
|
11417
10657
|
function SidebarButtons(gui) {
|