mapshaper 0.7.10 → 0.7.11
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 +19 -24
- package/package.json +4 -4
- package/www/geoparquet.js +569 -3153
- package/www/mapshaper-gui.js +130 -888
- package/www/mapshaper.js +19 -24
- 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) {
|
|
@@ -7789,8 +7784,8 @@
|
|
|
7789
7784
|
|
|
7790
7785
|
function startSimplifyUndoSession(dataset) {
|
|
7791
7786
|
var tx;
|
|
7792
|
-
if (!dataset || !dataset.arcs || !appUndoIsEnabled()) return;
|
|
7793
|
-
tx =
|
|
7787
|
+
if (!dataset || !dataset.arcs || !appUndoIsEnabled(gui)) return;
|
|
7788
|
+
tx = createUndoTransaction(gui, 'simplify');
|
|
7794
7789
|
if (!tx) return;
|
|
7795
7790
|
tx.captureArcsSimplificationBefore(dataset.arcs, {operation: 'simplifyTool'});
|
|
7796
7791
|
tx.captureDatasetInfoBefore(dataset, {operation: 'simplifyTool'});
|
|
@@ -7808,54 +7803,12 @@
|
|
|
7808
7803
|
var session = undoSession;
|
|
7809
7804
|
undoSession = null;
|
|
7810
7805
|
if (!session || !session.changed) return;
|
|
7811
|
-
|
|
7806
|
+
addUndoTransactionToHistory(gui, session.tx, {
|
|
7812
7807
|
flags: {simplify: true, info: true},
|
|
7813
|
-
entryPrefix: 'simplify-tool'
|
|
7814
|
-
maxStates: getUndoHistoryLimit()
|
|
7815
|
-
}).catch(function(e) {
|
|
7816
|
-
console.error(e);
|
|
7808
|
+
entryPrefix: 'simplify-tool'
|
|
7817
7809
|
});
|
|
7818
7810
|
}
|
|
7819
7811
|
|
|
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
7812
|
function updateSliderDisplay() {
|
|
7860
7813
|
// TODO: display resolution and vertex count
|
|
7861
7814
|
// var dataset = model.getActiveLayer().dataset;
|
|
@@ -7973,599 +7926,6 @@
|
|
|
7973
7926
|
return internal.formatOptionValue(internal.getLayerTargetId(gui.model, lyr));
|
|
7974
7927
|
}
|
|
7975
7928
|
|
|
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
7929
|
function Console(gui) {
|
|
8570
7930
|
var model = gui.model;
|
|
8571
7931
|
var CURSOR = '$ ';
|
|
@@ -8595,9 +7955,6 @@
|
|
|
8595
7955
|
});
|
|
8596
7956
|
var globals = {}; // share user-defined globals (job.defs) between runs
|
|
8597
7957
|
var sharedVars = {}; // share -vars / -defaults templating scope between runs
|
|
8598
|
-
var undoFeasibility = createUndoFeasibilityMonitor(gui);
|
|
8599
|
-
var storedUndoHistory = createStoredUndoHistory(gui);
|
|
8600
|
-
gui.undoFeasibility = undoFeasibility;
|
|
8601
7958
|
|
|
8602
7959
|
// expose this function, so other components can run commands (e.g. box tool)
|
|
8603
7960
|
this.runMapshaperCommands = runMapshaperCommands;
|
|
@@ -8999,7 +8356,6 @@
|
|
|
8999
8356
|
prevTable = active?.layer.data,
|
|
9000
8357
|
prevTableSize = prevTable ? prevTable.size() : 0,
|
|
9001
8358
|
prevArcCount = prevArcs ? prevArcs.size() : 0,
|
|
9002
|
-
undoCapture = undoFeasibility.beforeCommand(commands, commandString),
|
|
9003
8359
|
undoTransaction = createCommandUndoTransaction(commandString),
|
|
9004
8360
|
job = new internal.Job(model),
|
|
9005
8361
|
commandStart = Date.now();
|
|
@@ -9048,7 +8404,6 @@
|
|
|
9048
8404
|
// signal the map to update even if an error has occured, because the
|
|
9049
8405
|
// commands may have partially succeeded and changes may have occured to
|
|
9050
8406
|
// the data.
|
|
9051
|
-
undoFeasibility.afterCommand(undoCapture, {error: err, flags: flags});
|
|
9052
8407
|
if (!err) {
|
|
9053
8408
|
commandString = internal.standardizeConsoleCommands(commandString);
|
|
9054
8409
|
historyIds.push(gui.session.consoleCommands(commandString));
|
|
@@ -9074,7 +8429,7 @@
|
|
|
9074
8429
|
function createCommandUndoTransaction(commandString) {
|
|
9075
8430
|
var Transaction;
|
|
9076
8431
|
if (!isCommandUndoEnabled()) return null;
|
|
9077
|
-
|
|
8432
|
+
getStoredUndoHistory(gui).getPayloadStore();
|
|
9078
8433
|
Transaction = internal.UndoTransaction.UndoTransaction || internal.UndoTransaction;
|
|
9079
8434
|
return new Transaction(commandString);
|
|
9080
8435
|
}
|
|
@@ -9098,7 +8453,7 @@
|
|
|
9098
8453
|
}
|
|
9099
8454
|
|
|
9100
8455
|
async function addCommandUndoHistory(tx, err, flags, historyIds) {
|
|
9101
|
-
return
|
|
8456
|
+
return getStoredUndoHistory(gui).addTransaction(tx, {
|
|
9102
8457
|
error: err,
|
|
9103
8458
|
flags: flags,
|
|
9104
8459
|
entryPrefix: 'command',
|
|
@@ -9113,15 +8468,8 @@
|
|
|
9113
8468
|
}
|
|
9114
8469
|
|
|
9115
8470
|
function isCommandUndoEnabled() {
|
|
9116
|
-
var opt = gui.options && (gui.options.undoCommands || gui.options.appUndo);
|
|
9117
|
-
var query = getQueryValue('undo');
|
|
9118
8471
|
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
|
-
}
|
|
8472
|
+
return appUndoIsEnabled(gui);
|
|
9125
8473
|
}
|
|
9126
8474
|
|
|
9127
8475
|
function getCommandUndoHistoryLimit() {
|
|
@@ -9130,8 +8478,8 @@
|
|
|
9130
8478
|
}
|
|
9131
8479
|
|
|
9132
8480
|
function logCommandTiming(commandString, commands, err, totalMillis, undoTiming) {
|
|
9133
|
-
var enabled =
|
|
9134
|
-
|
|
8481
|
+
var enabled = getQueryFlag('command-timing') == 'on' ||
|
|
8482
|
+
getQueryFlag('undo-timing') == 'on';
|
|
9135
8483
|
var undoMillis = undoTiming && undoTiming.undoMillis || 0;
|
|
9136
8484
|
if (!enabled || typeof console == 'undefined' || !console.log) return;
|
|
9137
8485
|
console.log('[mapshaper command timing]', {
|
|
@@ -9147,7 +8495,7 @@
|
|
|
9147
8495
|
});
|
|
9148
8496
|
}
|
|
9149
8497
|
|
|
9150
|
-
function
|
|
8498
|
+
function getQueryFlag(key) {
|
|
9151
8499
|
var rxp, match;
|
|
9152
8500
|
if (typeof window == 'undefined' || !window.location) return null;
|
|
9153
8501
|
rxp = new RegExp('[?&]' + key + '=([^&]+)');
|
|
@@ -10264,8 +9612,9 @@
|
|
|
10264
9612
|
|
|
10265
9613
|
function deleteLayer() {
|
|
10266
9614
|
var target = findLayerById(id);
|
|
10267
|
-
var undoTransaction
|
|
9615
|
+
var undoTransaction;
|
|
10268
9616
|
if (!target) return;
|
|
9617
|
+
undoTransaction = createUndoTransaction(gui, 'delete layer');
|
|
10269
9618
|
if (map.isVisibleLayer(target.layer)) {
|
|
10270
9619
|
// TODO: check for double map refresh after model.deleteLayer() below
|
|
10271
9620
|
setLayerPinning(target.layer, false);
|
|
@@ -10274,7 +9623,10 @@
|
|
|
10274
9623
|
undoTransaction.run(function() {
|
|
10275
9624
|
model.deleteLayer(target.layer, target.dataset);
|
|
10276
9625
|
});
|
|
10277
|
-
|
|
9626
|
+
addUndoTransactionToHistory(gui, undoTransaction, {
|
|
9627
|
+
flags: {select: true, arc_count: true},
|
|
9628
|
+
entryPrefix: 'delete-layer'
|
|
9629
|
+
});
|
|
10278
9630
|
} else {
|
|
10279
9631
|
model.deleteLayer(target.layer, target.dataset);
|
|
10280
9632
|
}
|
|
@@ -10375,41 +9727,18 @@
|
|
|
10375
9727
|
return str;
|
|
10376
9728
|
}
|
|
10377
9729
|
|
|
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
9730
|
function renameLayer(target, name) {
|
|
10405
9731
|
var undoTransaction;
|
|
10406
9732
|
if (!target || target.layer.name == name) return;
|
|
10407
|
-
undoTransaction =
|
|
9733
|
+
undoTransaction = createUndoTransaction(gui, 'rename layer');
|
|
10408
9734
|
if (undoTransaction) {
|
|
10409
9735
|
undoTransaction.captureLayerMetadataBefore(target.layer, {operation: 'renameLayer', unit: 'name'});
|
|
10410
9736
|
target.layer.name = name;
|
|
10411
9737
|
markLayerChanged(target.layer, {operation: 'renameLayer', unit: 'name'});
|
|
10412
|
-
|
|
9738
|
+
addUndoTransactionToHistory(gui, undoTransaction, {
|
|
9739
|
+
flags: {select: true},
|
|
9740
|
+
entryPrefix: 'rename-layer'
|
|
9741
|
+
});
|
|
10413
9742
|
} else {
|
|
10414
9743
|
target.layer.name = name;
|
|
10415
9744
|
}
|
|
@@ -10417,54 +9746,12 @@
|
|
|
10417
9746
|
updateMenuBtn();
|
|
10418
9747
|
}
|
|
10419
9748
|
|
|
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
9749
|
function markLayerChanged(layer, detail) {
|
|
10431
9750
|
if (internal.UndoTracking && internal.UndoTracking.markLayerChanged) {
|
|
10432
9751
|
internal.UndoTracking.markLayerChanged(layer, detail);
|
|
10433
9752
|
}
|
|
10434
9753
|
}
|
|
10435
9754
|
|
|
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
9755
|
function getWarnings(lyr, dataset) {
|
|
10469
9756
|
var file = internal.getLayerSourceFile(lyr, dataset);
|
|
10470
9757
|
var missing = [];
|
|
@@ -10674,19 +9961,6 @@
|
|
|
10674
9961
|
el.attr('aria-disabled', enabled ? 'false' : 'true');
|
|
10675
9962
|
}
|
|
10676
9963
|
|
|
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
9964
|
function setAppUndoEnabled(enabled) {
|
|
10691
9965
|
try {
|
|
10692
9966
|
if (window.localStorage) {
|
|
@@ -10698,9 +9972,6 @@
|
|
|
10698
9972
|
function getRestoreDataNote(gui) {
|
|
10699
9973
|
var stats = getUndoPayloadStats(gui);
|
|
10700
9974
|
var bytes = stats ? stats.ownBytes || 0 : 0;
|
|
10701
|
-
if (bytes === 0) {
|
|
10702
|
-
return 'estimated on-disk restore data: none';
|
|
10703
|
-
}
|
|
10704
9975
|
return 'estimated on-disk restore data: ' + formatBytes(bytes);
|
|
10705
9976
|
}
|
|
10706
9977
|
|
|
@@ -10710,22 +9981,14 @@
|
|
|
10710
9981
|
}
|
|
10711
9982
|
|
|
10712
9983
|
function formatBytes(bytes) {
|
|
10713
|
-
var units = ['
|
|
10714
|
-
var value = bytes;
|
|
9984
|
+
var units = ['KB', 'MB', 'GB'];
|
|
9985
|
+
var value = bytes / 1000;
|
|
10715
9986
|
var i = 0;
|
|
10716
9987
|
while (value >= 1000 && i < units.length - 1) {
|
|
10717
9988
|
value /= 1000;
|
|
10718
9989
|
i++;
|
|
10719
9990
|
}
|
|
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;
|
|
9991
|
+
return value.toFixed(value < 10 && value >= 0.5 ? 1 : 0) + ' ' + units[i];
|
|
10729
9992
|
}
|
|
10730
9993
|
|
|
10731
9994
|
function SessionHistory(gui) {
|
|
@@ -10955,8 +10218,7 @@
|
|
|
10955
10218
|
}
|
|
10956
10219
|
|
|
10957
10220
|
function Undo(gui) {
|
|
10958
|
-
var history, offset, stashedUndo,
|
|
10959
|
-
storedUndoHistory = createStoredUndoHistory(gui);
|
|
10221
|
+
var history, offset, stashedUndo, editSession;
|
|
10960
10222
|
editSession = createEditSessionUndo();
|
|
10961
10223
|
reset();
|
|
10962
10224
|
|
|
@@ -11318,7 +10580,7 @@
|
|
|
11318
10580
|
function start(nextMode) {
|
|
11319
10581
|
var target, Transaction;
|
|
11320
10582
|
if (!isEditSessionMode(nextMode)) return;
|
|
11321
|
-
if (!appUndoIsEnabled()) return;
|
|
10583
|
+
if (!appUndoIsEnabled(gui)) return;
|
|
11322
10584
|
target = gui.model.getActiveLayer();
|
|
11323
10585
|
if (!target || !target.layer) return;
|
|
11324
10586
|
Transaction = getUndoTransactionConstructor();
|
|
@@ -11337,7 +10599,7 @@
|
|
|
11337
10599
|
}
|
|
11338
10600
|
resetSession();
|
|
11339
10601
|
if (!wasChanged) return;
|
|
11340
|
-
|
|
10602
|
+
getStoredUndoHistory(gui).addTransaction(finishedTx, {
|
|
11341
10603
|
flags: {select: true},
|
|
11342
10604
|
entryPrefix: 'edit-session',
|
|
11343
10605
|
maxStates: getEditSessionUndoHistoryLimit()
|
|
@@ -11392,26 +10654,6 @@
|
|
|
11392
10654
|
return opt > 0 ? opt : 10;
|
|
11393
10655
|
}
|
|
11394
10656
|
|
|
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
10657
|
}
|
|
11416
10658
|
|
|
11417
10659
|
function SidebarButtons(gui) {
|