mapshaper 0.5.86 → 0.5.91
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/bin/mapshaper-gui +4 -1
- package/mapshaper.js +347 -87
- package/package.json +1 -1
- package/www/mapshaper-gui.js +568 -2300
- package/www/mapshaper.js +347 -87
- package/www/nacis/Makefile +22 -0
- package/www/nacis/Makefile.txt +22 -0
- package/www/nacis/images/close.png +0 -0
- package/www/nacis/images/eye.png +0 -0
- package/www/nacis/images/eye2.png +0 -0
- package/www/nacis/index.html +262 -0
- package/www/nacis/manifest.js +27 -0
- package/www/nacis/map.svg +12308 -0
- package/CHANGELOG.md +0 -1291
package/www/mapshaper-gui.js
CHANGED
|
@@ -1,114 +1,16 @@
|
|
|
1
1
|
(function () {
|
|
2
2
|
|
|
3
|
-
var utils = /*#__PURE__*/Object.freeze({
|
|
4
|
-
__proto__: null,
|
|
5
|
-
get default () { return utils; },
|
|
6
|
-
get getUniqueName () { return getUniqueName; },
|
|
7
|
-
get isFunction () { return isFunction; },
|
|
8
|
-
get isObject () { return isObject; },
|
|
9
|
-
get clamp () { return clamp; },
|
|
10
|
-
get isArray () { return isArray; },
|
|
11
|
-
get isNumber () { return isNumber; },
|
|
12
|
-
get isValidNumber () { return isValidNumber; },
|
|
13
|
-
get isFiniteNumber () { return isFiniteNumber; },
|
|
14
|
-
get isNonNegNumber () { return isNonNegNumber; },
|
|
15
|
-
get isInteger () { return isInteger; },
|
|
16
|
-
get isEven () { return isEven; },
|
|
17
|
-
get isOdd () { return isOdd; },
|
|
18
|
-
get isString () { return isString; },
|
|
19
|
-
get isDate () { return isDate; },
|
|
20
|
-
get isBoolean () { return isBoolean; },
|
|
21
|
-
get formatDateISO () { return formatDateISO; },
|
|
22
|
-
get toArray () { return toArray; },
|
|
23
|
-
get isArrayLike () { return isArrayLike; },
|
|
24
|
-
get addslashes () { return addslashes; },
|
|
25
|
-
get regexEscape () { return regexEscape; },
|
|
26
|
-
get htmlEscape () { return htmlEscape; },
|
|
27
|
-
get defaults () { return defaults; },
|
|
28
|
-
get extend () { return extend; },
|
|
29
|
-
get inherit () { return inherit; },
|
|
30
|
-
get reduceAsync () { return reduceAsync; },
|
|
31
|
-
get merge () { return merge; },
|
|
32
|
-
get difference () { return difference; },
|
|
33
|
-
get intersection () { return intersection; },
|
|
34
|
-
get indexOf () { return indexOf; },
|
|
35
|
-
get contains () { return contains; },
|
|
36
|
-
get some () { return some; },
|
|
37
|
-
get every () { return every; },
|
|
38
|
-
get find () { return find; },
|
|
39
|
-
get range () { return range; },
|
|
40
|
-
get repeat () { return repeat; },
|
|
41
|
-
get sum () { return sum; },
|
|
42
|
-
get getArrayBounds () { return getArrayBounds; },
|
|
43
|
-
get uniq () { return uniq; },
|
|
44
|
-
get pluck () { return pluck; },
|
|
45
|
-
get countValues () { return countValues; },
|
|
46
|
-
get indexOn () { return indexOn; },
|
|
47
|
-
get groupBy () { return groupBy; },
|
|
48
|
-
get arrayToIndex () { return arrayToIndex; },
|
|
49
|
-
get forEach () { return forEach; },
|
|
50
|
-
get forEachProperty () { return forEachProperty; },
|
|
51
|
-
get initializeArray () { return initializeArray; },
|
|
52
|
-
get replaceArray () { return replaceArray; },
|
|
53
|
-
get repeatString () { return repeatString; },
|
|
54
|
-
get splitLines () { return splitLines; },
|
|
55
|
-
get pluralSuffix () { return pluralSuffix; },
|
|
56
|
-
get endsWith () { return endsWith; },
|
|
57
|
-
get lpad () { return lpad; },
|
|
58
|
-
get rpad () { return rpad; },
|
|
59
|
-
get trim () { return trim; },
|
|
60
|
-
get ltrim () { return ltrim; },
|
|
61
|
-
get rtrim () { return rtrim; },
|
|
62
|
-
get addThousandsSep () { return addThousandsSep; },
|
|
63
|
-
get numToStr () { return numToStr; },
|
|
64
|
-
get formatNumber () { return formatNumber; },
|
|
65
|
-
get formatIntlNumber () { return formatIntlNumber; },
|
|
66
|
-
get formatNumberForDisplay () { return formatNumberForDisplay; },
|
|
67
|
-
get shuffle () { return shuffle; },
|
|
68
|
-
get sortOn () { return sortOn; },
|
|
69
|
-
get genericSort () { return genericSort; },
|
|
70
|
-
get getSortedIds () { return getSortedIds; },
|
|
71
|
-
get sortArrayIndex () { return sortArrayIndex; },
|
|
72
|
-
get reorderArray () { return reorderArray; },
|
|
73
|
-
get getKeyComparator () { return getKeyComparator; },
|
|
74
|
-
get getGenericComparator () { return getGenericComparator; },
|
|
75
|
-
get quicksort () { return quicksort; },
|
|
76
|
-
get quicksortPartition () { return quicksortPartition; },
|
|
77
|
-
get findRankByValue () { return findRankByValue; },
|
|
78
|
-
get findValueByPct () { return findValueByPct; },
|
|
79
|
-
get findValueByRank () { return findValueByRank; },
|
|
80
|
-
get findMedian () { return findMedian; },
|
|
81
|
-
get mean () { return mean; },
|
|
82
|
-
get format () { return format; },
|
|
83
|
-
get formatter () { return formatter; },
|
|
84
|
-
get wildcardToRegExp () { return wildcardToRegExp; },
|
|
85
|
-
get createBuffer () { return createBuffer; },
|
|
86
|
-
get expandoBuffer () { return expandoBuffer; },
|
|
87
|
-
get copyElements () { return copyElements; },
|
|
88
|
-
get extendBuffer () { return extendBuffer; },
|
|
89
|
-
get mergeNames () { return mergeNames; },
|
|
90
|
-
get findStringPrefix () { return findStringPrefix; },
|
|
91
|
-
get parsePercent () { return parsePercent; },
|
|
92
|
-
get formatVersionedName () { return formatVersionedName; },
|
|
93
|
-
get uniqifyNames () { return uniqifyNames; },
|
|
94
|
-
get parseString () { return parseString; },
|
|
95
|
-
get parseNumber () { return parseNumber; },
|
|
96
|
-
get parseIntlNumber () { return parseIntlNumber; },
|
|
97
|
-
get cleanNumericString () { return cleanNumericString; },
|
|
98
|
-
get trimQuotes () { return trimQuotes; }
|
|
99
|
-
});
|
|
100
|
-
|
|
101
3
|
var api = window.mapshaper; // assuming mapshaper is in global scope
|
|
102
4
|
var mapshaper = api,
|
|
103
|
-
utils
|
|
5
|
+
utils = api.utils,
|
|
104
6
|
cli = api.cli,
|
|
105
7
|
geom = api.geom,
|
|
106
8
|
internal = api.internal,
|
|
107
9
|
Bounds = internal.Bounds,
|
|
108
|
-
UserError
|
|
109
|
-
message
|
|
110
|
-
stop
|
|
111
|
-
error
|
|
10
|
+
UserError = internal.UserError,
|
|
11
|
+
message = internal.message, // stop, error and message are overridden in gui-proxy.js
|
|
12
|
+
stop = internal.stop,
|
|
13
|
+
error = internal.error;
|
|
112
14
|
|
|
113
15
|
api.enableLogging();
|
|
114
16
|
|
|
@@ -144,7 +46,7 @@
|
|
|
144
46
|
|
|
145
47
|
html = '<table>';
|
|
146
48
|
if (catalog.title) {
|
|
147
|
-
html += utils
|
|
49
|
+
html += utils.format('<tr><th colspan="%d"><h4>%s</h4></th></tr>', cols, catalog.title);
|
|
148
50
|
}
|
|
149
51
|
while (row < rows) {
|
|
150
52
|
html += renderRow(items.slice(row * cols, row * cols + cols));
|
|
@@ -209,7 +111,7 @@
|
|
|
209
111
|
|
|
210
112
|
function renderCell(item, i) {
|
|
211
113
|
var template = '<td data-id="%d"><h4 class="title">%s</h4><div class="subtitle">%s</div></td>';
|
|
212
|
-
return utils
|
|
114
|
+
return utils.format(template, i, item.title, item.subtitle || '');
|
|
213
115
|
}
|
|
214
116
|
|
|
215
117
|
}
|
|
@@ -280,6 +182,10 @@
|
|
|
280
182
|
return (el && (el.tagName == 'INPUT' || el.contentEditable == 'true')) ? el : null;
|
|
281
183
|
};
|
|
282
184
|
|
|
185
|
+
GUI.textIsSelected = function() {
|
|
186
|
+
return !!GUI.getInputElement();
|
|
187
|
+
};
|
|
188
|
+
|
|
283
189
|
GUI.selectElement = function(el) {
|
|
284
190
|
var range = document.createRange(),
|
|
285
191
|
sel = window.getSelection();
|
|
@@ -322,11 +228,35 @@
|
|
|
322
228
|
}
|
|
323
229
|
parsed = internal.parseCommands(str);
|
|
324
230
|
if (!parsed.length || parsed[0].name != cmd) {
|
|
325
|
-
stop
|
|
231
|
+
stop("Unable to parse command line options");
|
|
326
232
|
}
|
|
327
233
|
return parsed[0].options;
|
|
328
234
|
};
|
|
329
235
|
|
|
236
|
+
// Convert an options object to a command line options string
|
|
237
|
+
// (used by gui-import-control.js)
|
|
238
|
+
// TODO: handle options with irregular string <-> object conversion
|
|
239
|
+
GUI.formatCommandOptions = function(o) {
|
|
240
|
+
var arr = [];
|
|
241
|
+
Object.keys(o).forEach(function(key) {
|
|
242
|
+
var name = key.replace(/_/g, '-');
|
|
243
|
+
var val = o[key];
|
|
244
|
+
var str;
|
|
245
|
+
// TODO: quote values that contain spaces
|
|
246
|
+
if (Array.isArray(val)) {
|
|
247
|
+
str = name + '=' + val.join(',');
|
|
248
|
+
} else if (val === true) {
|
|
249
|
+
str = name;
|
|
250
|
+
} else if (val === false) {
|
|
251
|
+
return;
|
|
252
|
+
} else {
|
|
253
|
+
str = name + '=' + val;
|
|
254
|
+
}
|
|
255
|
+
arr.push(str);
|
|
256
|
+
});
|
|
257
|
+
return arr.join(' ');
|
|
258
|
+
};
|
|
259
|
+
|
|
330
260
|
// @file: Zip file
|
|
331
261
|
// @cb: function(err, <files>)
|
|
332
262
|
//
|
|
@@ -391,7 +321,7 @@
|
|
|
391
321
|
evt = new EventData(this.type);
|
|
392
322
|
evt.target = this.target;
|
|
393
323
|
} else if (evt.target != this.target || evt.type != this.type) {
|
|
394
|
-
error
|
|
324
|
+
error("[Handler] event target/type have changed.");
|
|
395
325
|
}
|
|
396
326
|
this.callback.call(this.listener, evt);
|
|
397
327
|
};
|
|
@@ -400,7 +330,7 @@
|
|
|
400
330
|
this.type = type;
|
|
401
331
|
this.target = target;
|
|
402
332
|
if (data) {
|
|
403
|
-
utils
|
|
333
|
+
utils.defaults(this, data);
|
|
404
334
|
this.data = data;
|
|
405
335
|
}
|
|
406
336
|
}
|
|
@@ -630,12 +560,12 @@
|
|
|
630
560
|
try {
|
|
631
561
|
els = root.querySelectorAll(selector);
|
|
632
562
|
} catch (e) {
|
|
633
|
-
error
|
|
563
|
+
error("Invalid selector:", selector);
|
|
634
564
|
}
|
|
635
565
|
} else {
|
|
636
|
-
error
|
|
566
|
+
error("This browser doesn't support CSS query selectors");
|
|
637
567
|
}
|
|
638
|
-
return utils
|
|
568
|
+
return utils.toArray(els);
|
|
639
569
|
};
|
|
640
570
|
|
|
641
571
|
// Converts dash-separated names (e.g. background-color) to camelCase (e.g. backgroundColor)
|
|
@@ -672,7 +602,7 @@
|
|
|
672
602
|
};
|
|
673
603
|
|
|
674
604
|
function El(ref) {
|
|
675
|
-
if (!ref) error
|
|
605
|
+
if (!ref) error("Element() needs a reference");
|
|
676
606
|
if (ref instanceof El) {
|
|
677
607
|
return ref;
|
|
678
608
|
}
|
|
@@ -681,7 +611,7 @@
|
|
|
681
611
|
}
|
|
682
612
|
|
|
683
613
|
var node;
|
|
684
|
-
if (utils
|
|
614
|
+
if (utils.isString(ref)) {
|
|
685
615
|
if (ref[0] == '<') {
|
|
686
616
|
var parent = El('div').html(ref).node();
|
|
687
617
|
node = parent.childNodes.length == 1 ? parent.childNodes[0] : parent;
|
|
@@ -693,13 +623,13 @@
|
|
|
693
623
|
} else if (ref.tagName) {
|
|
694
624
|
node = ref;
|
|
695
625
|
}
|
|
696
|
-
if (!node) error
|
|
626
|
+
if (!node) error("Unmatched element selector:", ref);
|
|
697
627
|
this.el = node;
|
|
698
628
|
}
|
|
699
629
|
|
|
700
|
-
utils
|
|
630
|
+
utils.inherit(El, EventDispatcher);
|
|
701
631
|
|
|
702
|
-
utils
|
|
632
|
+
utils.extend(El.prototype, {
|
|
703
633
|
|
|
704
634
|
clone: function() {
|
|
705
635
|
var el = this.el.cloneNode(true);
|
|
@@ -708,7 +638,7 @@
|
|
|
708
638
|
// can ...
|
|
709
639
|
el = El('div').addClass(el.className).html(el.innerHTML).node();
|
|
710
640
|
}
|
|
711
|
-
el.id = utils
|
|
641
|
+
el.id = utils.getUniqueName();
|
|
712
642
|
this.el = el;
|
|
713
643
|
return this;
|
|
714
644
|
},
|
|
@@ -738,11 +668,11 @@
|
|
|
738
668
|
if (val != null) {
|
|
739
669
|
El.setStyle(this.el, css, val);
|
|
740
670
|
}
|
|
741
|
-
else if (utils
|
|
671
|
+
else if (utils.isString(css)) {
|
|
742
672
|
addCSS(this.el, css);
|
|
743
673
|
}
|
|
744
|
-
else if (utils
|
|
745
|
-
utils
|
|
674
|
+
else if (utils.isObject(css)) {
|
|
675
|
+
utils.forEachProperty(css, function(val, key) {
|
|
746
676
|
El.setStyle(this.el, key, val);
|
|
747
677
|
}, this);
|
|
748
678
|
}
|
|
@@ -750,7 +680,7 @@
|
|
|
750
680
|
},
|
|
751
681
|
|
|
752
682
|
attr: function(obj, value) {
|
|
753
|
-
if (utils
|
|
683
|
+
if (utils.isString(obj)) {
|
|
754
684
|
if (arguments.length == 1) {
|
|
755
685
|
return this.el.getAttribute(obj);
|
|
756
686
|
}
|
|
@@ -834,7 +764,7 @@
|
|
|
834
764
|
},
|
|
835
765
|
|
|
836
766
|
text: function(str) {
|
|
837
|
-
this.html(utils
|
|
767
|
+
this.html(utils.htmlEscape(str));
|
|
838
768
|
return this;
|
|
839
769
|
},
|
|
840
770
|
|
|
@@ -924,7 +854,7 @@
|
|
|
924
854
|
El.prototype.__on = El.prototype.on;
|
|
925
855
|
El.prototype.on = function(type, func, ctx) {
|
|
926
856
|
if (ctx) {
|
|
927
|
-
error
|
|
857
|
+
error("[El#on()] Third argument no longer supported.");
|
|
928
858
|
}
|
|
929
859
|
if (this.constructor == El) {
|
|
930
860
|
this.el.addEventListener(type, func);
|
|
@@ -980,7 +910,7 @@
|
|
|
980
910
|
}
|
|
981
911
|
|
|
982
912
|
this.value = function(str) {
|
|
983
|
-
if (utils
|
|
913
|
+
if (utils.isString(str)) {
|
|
984
914
|
el.node().textContent = str;
|
|
985
915
|
} else {
|
|
986
916
|
return el.node().textContent;
|
|
@@ -988,7 +918,7 @@
|
|
|
988
918
|
};
|
|
989
919
|
}
|
|
990
920
|
|
|
991
|
-
utils
|
|
921
|
+
utils.inherit(ClickText2, EventDispatcher);
|
|
992
922
|
|
|
993
923
|
// @ref reference to a text input element
|
|
994
924
|
function ClickText(ref) {
|
|
@@ -1059,9 +989,9 @@
|
|
|
1059
989
|
// return _parser ? _parser(valStr) : parseFloat(valStr);
|
|
1060
990
|
return _value;
|
|
1061
991
|
}
|
|
1062
|
-
var val = utils
|
|
992
|
+
var val = utils.clamp(arg, _min, _max);
|
|
1063
993
|
if (!_validator(val)) {
|
|
1064
|
-
error
|
|
994
|
+
error("ClickText#value() invalid value:", arg);
|
|
1065
995
|
} else {
|
|
1066
996
|
_value = val;
|
|
1067
997
|
}
|
|
@@ -1070,14 +1000,14 @@
|
|
|
1070
1000
|
};
|
|
1071
1001
|
}
|
|
1072
1002
|
|
|
1073
|
-
utils
|
|
1003
|
+
utils.inherit(ClickText, EventDispatcher);
|
|
1074
1004
|
|
|
1075
1005
|
|
|
1076
1006
|
function Checkbox(ref) {
|
|
1077
1007
|
var _el = El(ref);
|
|
1078
1008
|
}
|
|
1079
1009
|
|
|
1080
|
-
utils
|
|
1010
|
+
utils.inherit(Checkbox, EventDispatcher);
|
|
1081
1011
|
|
|
1082
1012
|
function SimpleButton(ref) {
|
|
1083
1013
|
var _el = El(ref),
|
|
@@ -1106,7 +1036,7 @@
|
|
|
1106
1036
|
}
|
|
1107
1037
|
}
|
|
1108
1038
|
|
|
1109
|
-
utils
|
|
1039
|
+
utils.inherit(SimpleButton, EventDispatcher);
|
|
1110
1040
|
|
|
1111
1041
|
// @cb function(<FileList>)
|
|
1112
1042
|
function DropControl(gui, el, cb) {
|
|
@@ -1125,6 +1055,10 @@
|
|
|
1125
1055
|
var types = Array.from(e.clipboardData.types || []).join(',');
|
|
1126
1056
|
var items = Array.from(e.clipboardData.items || []);
|
|
1127
1057
|
var files;
|
|
1058
|
+
if (GUI.textIsSelected()) {
|
|
1059
|
+
// user is probably pasting text into an editable text field
|
|
1060
|
+
return;
|
|
1061
|
+
}
|
|
1128
1062
|
block(e);
|
|
1129
1063
|
// Browser compatibility (tested on MacOS only):
|
|
1130
1064
|
// Chrome and Safari: full support
|
|
@@ -1197,20 +1131,19 @@
|
|
|
1197
1131
|
|
|
1198
1132
|
function ImportControl(gui, opts) {
|
|
1199
1133
|
var model = gui.model;
|
|
1134
|
+
var initialImport = true;
|
|
1200
1135
|
var importCount = 0;
|
|
1201
1136
|
var importTotal = 0;
|
|
1202
1137
|
var overQuickView = false;
|
|
1203
|
-
var useQuickView = opts.quick_view; // may be set by mapshaper-gui
|
|
1204
1138
|
var queuedFiles = [];
|
|
1205
1139
|
var manifestFiles = opts.files || [];
|
|
1206
|
-
var cachedFiles = {};
|
|
1207
1140
|
var catalog;
|
|
1208
1141
|
|
|
1209
1142
|
if (opts.catalog) {
|
|
1210
1143
|
catalog = new CatalogControl(gui, opts.catalog, downloadFiles);
|
|
1211
1144
|
}
|
|
1212
1145
|
|
|
1213
|
-
new SimpleButton('#import-buttons .submit-btn').on('click',
|
|
1146
|
+
new SimpleButton('#import-buttons .submit-btn').on('click', importQueuedFiles);
|
|
1214
1147
|
new SimpleButton('#import-buttons .cancel-btn').on('click', gui.clearMode);
|
|
1215
1148
|
new DropControl(gui, 'body', receiveFiles);
|
|
1216
1149
|
new FileChooser('#file-selection-btn', receiveFiles);
|
|
@@ -1218,7 +1151,7 @@
|
|
|
1218
1151
|
new FileChooser('#add-file-btn', receiveFiles);
|
|
1219
1152
|
initDropArea('#import-quick-drop', true);
|
|
1220
1153
|
initDropArea('#import-drop');
|
|
1221
|
-
gui.keyboard.onMenuSubmit(El('#import-options'),
|
|
1154
|
+
gui.keyboard.onMenuSubmit(El('#import-options'), importQueuedFiles);
|
|
1222
1155
|
|
|
1223
1156
|
gui.addMode('import', turnOn, turnOff);
|
|
1224
1157
|
gui.enterMode('import');
|
|
@@ -1230,6 +1163,10 @@
|
|
|
1230
1163
|
}
|
|
1231
1164
|
});
|
|
1232
1165
|
|
|
1166
|
+
function useQuickView() {
|
|
1167
|
+
return initialImport && (opts.quick_view || overQuickView);
|
|
1168
|
+
}
|
|
1169
|
+
|
|
1233
1170
|
function initDropArea(el, isQuick) {
|
|
1234
1171
|
var area = El(el)
|
|
1235
1172
|
.on('dragleave', onout)
|
|
@@ -1247,15 +1184,19 @@
|
|
|
1247
1184
|
}
|
|
1248
1185
|
}
|
|
1249
1186
|
|
|
1250
|
-
function
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1187
|
+
async function importQueuedFiles() {
|
|
1188
|
+
gui.container.removeClass('queued-files');
|
|
1189
|
+
gui.container.removeClass('splash-screen');
|
|
1190
|
+
var files = queuedFiles;
|
|
1191
|
+
try {
|
|
1192
|
+
if (files.length > 0) {
|
|
1193
|
+
queuedFiles = [];
|
|
1194
|
+
await importFiles(files);
|
|
1195
|
+
}
|
|
1196
|
+
} catch(e) {
|
|
1197
|
+
console.error(e);
|
|
1198
|
+
}
|
|
1199
|
+
gui.clearMode();
|
|
1259
1200
|
}
|
|
1260
1201
|
|
|
1261
1202
|
function turnOn() {
|
|
@@ -1276,13 +1217,8 @@
|
|
|
1276
1217
|
importCount = 0;
|
|
1277
1218
|
}
|
|
1278
1219
|
gui.clearProgressMessage();
|
|
1279
|
-
|
|
1280
|
-
close();
|
|
1281
|
-
}
|
|
1282
|
-
|
|
1283
|
-
function close() {
|
|
1220
|
+
initialImport = false; // unset 'quick view' mode, if on
|
|
1284
1221
|
clearQueuedFiles();
|
|
1285
|
-
cachedFiles = {};
|
|
1286
1222
|
}
|
|
1287
1223
|
|
|
1288
1224
|
function onImportComplete() {
|
|
@@ -1316,34 +1252,6 @@
|
|
|
1316
1252
|
}, []);
|
|
1317
1253
|
}
|
|
1318
1254
|
|
|
1319
|
-
// When a Shapefile component is at the head of the queue, move the entire
|
|
1320
|
-
// Shapefile to the front of the queue, sorted in reverse alphabetical order,
|
|
1321
|
-
// (a kludge), so .shp is read before .dbf and .prj
|
|
1322
|
-
// (If a .dbf file is imported before a .shp, it becomes a separate dataset)
|
|
1323
|
-
// TODO: import Shapefile parts without relying on this kludge
|
|
1324
|
-
function sortQueue(queue) {
|
|
1325
|
-
var nextFile = queue[0];
|
|
1326
|
-
var basename, parts;
|
|
1327
|
-
if (!isShapefilePart(nextFile.name)) {
|
|
1328
|
-
return queue;
|
|
1329
|
-
}
|
|
1330
|
-
basename = internal.getFileBase(nextFile.name).toLowerCase();
|
|
1331
|
-
parts = [];
|
|
1332
|
-
queue = queue.filter(function(file) {
|
|
1333
|
-
if (internal.getFileBase(file.name).toLowerCase() == basename) {
|
|
1334
|
-
parts.push(file);
|
|
1335
|
-
return false;
|
|
1336
|
-
}
|
|
1337
|
-
return true;
|
|
1338
|
-
});
|
|
1339
|
-
parts.sort(function(a, b) {
|
|
1340
|
-
// Sorting on LC filename so Shapefiles with mixed-case
|
|
1341
|
-
// extensions are sorted correctly
|
|
1342
|
-
return a.name.toLowerCase() < b.name.toLowerCase() ? 1 : -1;
|
|
1343
|
-
});
|
|
1344
|
-
return parts.concat(queue);
|
|
1345
|
-
}
|
|
1346
|
-
|
|
1347
1255
|
function showQueuedFiles() {
|
|
1348
1256
|
var list = gui.container.findChild('.dropped-file-list').empty();
|
|
1349
1257
|
queuedFiles.forEach(function(f) {
|
|
@@ -1351,16 +1259,14 @@
|
|
|
1351
1259
|
});
|
|
1352
1260
|
}
|
|
1353
1261
|
|
|
1354
|
-
function receiveFiles(files) {
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
addFilesToQueue(files);
|
|
1262
|
+
async function receiveFiles(files) {
|
|
1263
|
+
// TODO: show importing message here?
|
|
1264
|
+
var expanded = await expandFiles(files);
|
|
1265
|
+
addFilesToQueue(expanded);
|
|
1359
1266
|
if (queuedFiles.length === 0) return;
|
|
1360
1267
|
gui.enterMode('import');
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
onSubmit();
|
|
1268
|
+
if (useQuickView()) {
|
|
1269
|
+
importQueuedFiles();
|
|
1364
1270
|
} else {
|
|
1365
1271
|
gui.container.addClass('queued-files');
|
|
1366
1272
|
El('#path-import-options').classed('hidden', !filesMayContainPaths(queuedFiles));
|
|
@@ -1368,25 +1274,51 @@
|
|
|
1368
1274
|
}
|
|
1369
1275
|
}
|
|
1370
1276
|
|
|
1371
|
-
function
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1277
|
+
async function expandFiles(files) {
|
|
1278
|
+
var files2 = [], expanded;
|
|
1279
|
+
for (var f of files) {
|
|
1280
|
+
if (internal.isZipFile(f.name)) {
|
|
1281
|
+
expanded = await readZipFile(f);
|
|
1282
|
+
files2 = files2.concat(expanded);
|
|
1283
|
+
} else {
|
|
1284
|
+
files2.push(f);
|
|
1285
|
+
}
|
|
1286
|
+
}
|
|
1287
|
+
return files2;
|
|
1376
1288
|
}
|
|
1377
1289
|
|
|
1378
|
-
function
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1290
|
+
async function importFiles(files) {
|
|
1291
|
+
var fileData = await readFiles(files);
|
|
1292
|
+
var importOpts = readImportOpts();
|
|
1293
|
+
var groups = groupFilesForImport(fileData, importOpts);
|
|
1294
|
+
for (var group of groups) {
|
|
1295
|
+
if (group.size > 4e7) {
|
|
1296
|
+
gui.showProgressMessage('Importing');
|
|
1297
|
+
await wait(35);
|
|
1298
|
+
}
|
|
1299
|
+
importDataset(group, importOpts);
|
|
1300
|
+
}
|
|
1382
1301
|
}
|
|
1383
1302
|
|
|
1384
|
-
function
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1303
|
+
function importDataset(group, importOpts) {
|
|
1304
|
+
var optStr = GUI.formatCommandOptions(importOpts);
|
|
1305
|
+
var dataset = internal.importContent(group, importOpts);
|
|
1306
|
+
if (datasetIsEmpty(dataset)) return;
|
|
1307
|
+
if (group.layername) {
|
|
1308
|
+
dataset.layers.forEach(lyr => lyr.name = group.layername);
|
|
1388
1309
|
}
|
|
1389
|
-
|
|
1310
|
+
// save import options for use by repair control, etc.
|
|
1311
|
+
dataset.info.import_options = importOpts;
|
|
1312
|
+
model.addDataset(dataset);
|
|
1313
|
+
importCount++;
|
|
1314
|
+
gui.session.fileImported(group.filename, optStr);
|
|
1315
|
+
}
|
|
1316
|
+
|
|
1317
|
+
function filesMayContainPaths(files) {
|
|
1318
|
+
return utils.some(files, function(f) {
|
|
1319
|
+
var type = internal.guessInputFileType(f.name);
|
|
1320
|
+
return type == 'shp' || type == 'json' || internal.isZipFile(f.name);
|
|
1321
|
+
});
|
|
1390
1322
|
}
|
|
1391
1323
|
|
|
1392
1324
|
function datasetIsEmpty(dataset) {
|
|
@@ -1395,41 +1327,27 @@
|
|
|
1395
1327
|
});
|
|
1396
1328
|
}
|
|
1397
1329
|
|
|
1398
|
-
function procNextQueuedFile() {
|
|
1399
|
-
if (queuedFiles.length === 0) {
|
|
1400
|
-
gui.clearMode();
|
|
1401
|
-
} else {
|
|
1402
|
-
queuedFiles = sortQueue(queuedFiles);
|
|
1403
|
-
readFile(queuedFiles.shift());
|
|
1404
|
-
}
|
|
1405
|
-
}
|
|
1406
1330
|
|
|
1407
1331
|
// TODO: support .cpg
|
|
1408
1332
|
function isShapefilePart(name) {
|
|
1409
1333
|
return /\.(shp|shx|dbf|prj)$/i.test(name);
|
|
1410
1334
|
}
|
|
1411
1335
|
|
|
1412
|
-
|
|
1413
1336
|
function readImportOpts() {
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
if (useQuickView) return '';
|
|
1425
|
-
var freeform = El('#import-options .advanced-options').node().value;
|
|
1426
|
-
var opts = readImportOpts();
|
|
1427
|
-
if (opts.snap) freeform = 'snap ' + freeform;
|
|
1428
|
-
return freeform.trim();
|
|
1337
|
+
var importOpts;
|
|
1338
|
+
if (useQuickView()) {
|
|
1339
|
+
importOpts = {}; // default opts using quickview
|
|
1340
|
+
} else {
|
|
1341
|
+
var freeform = El('#import-options .advanced-options').node().value;
|
|
1342
|
+
importOpts = GUI.parseFreeformOptions(freeform, 'i');
|
|
1343
|
+
importOpts.no_repair = !El("#repair-intersections-opt").node().checked;
|
|
1344
|
+
importOpts.snap = !!El("#snap-points-opt").node().checked;
|
|
1345
|
+
}
|
|
1346
|
+
return importOpts;
|
|
1429
1347
|
}
|
|
1430
1348
|
|
|
1431
1349
|
// @file a File object
|
|
1432
|
-
function
|
|
1350
|
+
async function readContentFileAsync(file, cb) {
|
|
1433
1351
|
var name = file.name,
|
|
1434
1352
|
reader = new FileReader(),
|
|
1435
1353
|
useBinary = internal.isSupportedBinaryInputType(name) ||
|
|
@@ -1439,9 +1357,9 @@
|
|
|
1439
1357
|
|
|
1440
1358
|
reader.addEventListener('loadend', function(e) {
|
|
1441
1359
|
if (!reader.result) {
|
|
1442
|
-
|
|
1360
|
+
cb(new Error());
|
|
1443
1361
|
} else {
|
|
1444
|
-
|
|
1362
|
+
cb(null, reader.result);
|
|
1445
1363
|
}
|
|
1446
1364
|
});
|
|
1447
1365
|
if (useBinary) {
|
|
@@ -1452,93 +1370,8 @@
|
|
|
1452
1370
|
}
|
|
1453
1371
|
}
|
|
1454
1372
|
|
|
1455
|
-
function importFileContent(fileName, content) {
|
|
1456
|
-
var fileType = internal.guessInputType(fileName, content),
|
|
1457
|
-
importOpts = readImportOpts(),
|
|
1458
|
-
matches = findMatchingShp(fileName),
|
|
1459
|
-
dataset, lyr;
|
|
1460
|
-
|
|
1461
|
-
// Add dbf data to a previously imported .shp file with a matching name
|
|
1462
|
-
// (.shp should have been queued before .dbf)
|
|
1463
|
-
if (fileType == 'dbf' && matches.length > 0) {
|
|
1464
|
-
// find an imported .shp layer that is missing attribute data
|
|
1465
|
-
// (if multiple matches, try to use the most recently imported one)
|
|
1466
|
-
dataset = matches.reduce(function(memo, d) {
|
|
1467
|
-
if (!d.layers[0].data) {
|
|
1468
|
-
memo = d;
|
|
1469
|
-
}
|
|
1470
|
-
return memo;
|
|
1471
|
-
}, null);
|
|
1472
|
-
if (dataset) {
|
|
1473
|
-
lyr = dataset.layers[0];
|
|
1474
|
-
lyr.data = new internal.ShapefileTable(content, importOpts.encoding);
|
|
1475
|
-
if (lyr.shapes && lyr.data.size() != lyr.shapes.length) {
|
|
1476
|
-
stop$1("Different number of records in .shp and .dbf files");
|
|
1477
|
-
}
|
|
1478
|
-
if (!lyr.geometry_type) {
|
|
1479
|
-
// kludge: trigger display of table cells if .shp has null geometry
|
|
1480
|
-
// TODO: test case if lyr is not the current active layer
|
|
1481
|
-
model.updated({});
|
|
1482
|
-
}
|
|
1483
|
-
procNextQueuedFile();
|
|
1484
|
-
return;
|
|
1485
|
-
}
|
|
1486
|
-
}
|
|
1487
|
-
|
|
1488
|
-
if (fileType == 'shx') {
|
|
1489
|
-
// save .shx for use when importing .shp
|
|
1490
|
-
// (queue should be sorted so that .shx is processed before .shp)
|
|
1491
|
-
cachedFiles[fileName.toLowerCase()] = {filename: fileName, content: content};
|
|
1492
|
-
procNextQueuedFile();
|
|
1493
|
-
return;
|
|
1494
|
-
}
|
|
1495
|
-
|
|
1496
|
-
// Add .prj file to previously imported .shp file
|
|
1497
|
-
if (fileType == 'prj') {
|
|
1498
|
-
matches.forEach(function(d) {
|
|
1499
|
-
if (!d.info.prj) {
|
|
1500
|
-
d.info.prj = content;
|
|
1501
|
-
}
|
|
1502
|
-
});
|
|
1503
|
-
procNextQueuedFile();
|
|
1504
|
-
return;
|
|
1505
|
-
}
|
|
1506
|
-
|
|
1507
|
-
importNewDataset(fileType, fileName, content, importOpts);
|
|
1508
|
-
}
|
|
1509
|
-
|
|
1510
|
-
function importNewDataset(fileType, fileName, content, importOpts) {
|
|
1511
|
-
var size = content.byteLength || content.length, // ArrayBuffer or string
|
|
1512
|
-
delay = 0;
|
|
1513
|
-
|
|
1514
|
-
// show importing message if file is large
|
|
1515
|
-
if (size > 4e7) {
|
|
1516
|
-
gui.showProgressMessage('Importing');
|
|
1517
|
-
delay = 35;
|
|
1518
|
-
}
|
|
1519
|
-
setTimeout(function() {
|
|
1520
|
-
var dataset;
|
|
1521
|
-
var input = {};
|
|
1522
|
-
try {
|
|
1523
|
-
input[fileType] = {filename: fileName, content: content};
|
|
1524
|
-
if (fileType == 'shp') {
|
|
1525
|
-
// shx file should already be cached, if it was added together with the shp
|
|
1526
|
-
input.shx = cachedFiles[fileName.replace(/shp$/i, 'shx').toLowerCase()] || null;
|
|
1527
|
-
}
|
|
1528
|
-
dataset = internal.importContent(input, importOpts);
|
|
1529
|
-
// save import options for use by repair control, etc.
|
|
1530
|
-
dataset.info.import_options = importOpts;
|
|
1531
|
-
gui.session.fileImported(fileName, readImportOptsAsString());
|
|
1532
|
-
addDataset(dataset);
|
|
1533
|
-
|
|
1534
|
-
} catch(e) {
|
|
1535
|
-
handleImportError(e, fileName);
|
|
1536
|
-
}
|
|
1537
|
-
}, delay);
|
|
1538
|
-
}
|
|
1539
|
-
|
|
1540
1373
|
function handleImportError(e, fileName) {
|
|
1541
|
-
var msg = utils
|
|
1374
|
+
var msg = utils.isString(e) ? e : e.message;
|
|
1542
1375
|
if (fileName) {
|
|
1543
1376
|
msg = "Error importing <i>" + fileName + "</i><br>" + msg;
|
|
1544
1377
|
}
|
|
@@ -1547,34 +1380,6 @@
|
|
|
1547
1380
|
console.error(e);
|
|
1548
1381
|
}
|
|
1549
1382
|
|
|
1550
|
-
function handleZipFiles(files) {
|
|
1551
|
-
return files.filter(function(file) {
|
|
1552
|
-
var isZip = internal.isZipFile(file.name);
|
|
1553
|
-
if (isZip) {
|
|
1554
|
-
importZipFile(file);
|
|
1555
|
-
}
|
|
1556
|
-
return !isZip;
|
|
1557
|
-
});
|
|
1558
|
-
}
|
|
1559
|
-
|
|
1560
|
-
function importZipFile(file) {
|
|
1561
|
-
// gui.showProgressMessage('Importing');
|
|
1562
|
-
setTimeout(function() {
|
|
1563
|
-
GUI.readZipFile(file, function(err, files) {
|
|
1564
|
-
if (err) {
|
|
1565
|
-
handleImportError(err, file.name);
|
|
1566
|
-
} else {
|
|
1567
|
-
// don't try to import .txt files from zip files
|
|
1568
|
-
// (these would be parsed as dsv and throw errows)
|
|
1569
|
-
files = files.filter(function(f) {
|
|
1570
|
-
return !/\.txt$/i.test(f.name);
|
|
1571
|
-
});
|
|
1572
|
-
receiveFiles(files);
|
|
1573
|
-
}
|
|
1574
|
-
});
|
|
1575
|
-
}, 35);
|
|
1576
|
-
}
|
|
1577
|
-
|
|
1578
1383
|
function prepFilesForDownload(names) {
|
|
1579
1384
|
var items = names.map(function(name) {
|
|
1580
1385
|
var isUrl = /:\/\//.test(name);
|
|
@@ -1596,7 +1401,7 @@
|
|
|
1596
1401
|
|
|
1597
1402
|
function downloadFiles(paths) {
|
|
1598
1403
|
var items = prepFilesForDownload(paths);
|
|
1599
|
-
utils
|
|
1404
|
+
utils.reduceAsync(items, [], downloadNextFile, function(err, files) {
|
|
1600
1405
|
if (err) {
|
|
1601
1406
|
gui.alert(err);
|
|
1602
1407
|
} else if (!files.length) {
|
|
@@ -1620,37 +1425,107 @@
|
|
|
1620
1425
|
});
|
|
1621
1426
|
}
|
|
1622
1427
|
|
|
1623
|
-
function
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
req.addEventListener('progress', function(e) {
|
|
1633
|
-
if (!e.lengthComputable) return;
|
|
1634
|
-
var pct = e.loaded / e.total;
|
|
1635
|
-
if (catalog) catalog.progress(pct);
|
|
1428
|
+
function wait(ms) {
|
|
1429
|
+
return new Promise(resolve => setTimeout(resolve, ms));
|
|
1430
|
+
}
|
|
1431
|
+
|
|
1432
|
+
function runAsync(fn, arg) {
|
|
1433
|
+
return new Promise((resolve, reject) => {
|
|
1434
|
+
fn(arg, function(err, data) {
|
|
1435
|
+
return err ? reject(err) : resolve(data);
|
|
1436
|
+
});
|
|
1636
1437
|
});
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1438
|
+
}
|
|
1439
|
+
|
|
1440
|
+
async function readZipFile(file) {
|
|
1441
|
+
var files;
|
|
1442
|
+
await wait(35); // pause a beat so status message can display
|
|
1443
|
+
try {
|
|
1444
|
+
files = await runAsync(GUI.readZipFile, file);
|
|
1445
|
+
// don't try to import .txt files from zip files
|
|
1446
|
+
// (these would be parsed as dsv and throw errows)
|
|
1447
|
+
files = files.filter(function(f) {
|
|
1448
|
+
return !/\.txt$/i.test(f.name);
|
|
1449
|
+
});
|
|
1450
|
+
} catch(e) {
|
|
1451
|
+
handleImportError(e, file.name);
|
|
1452
|
+
files = [];
|
|
1453
|
+
}
|
|
1454
|
+
return files;
|
|
1455
|
+
}
|
|
1456
|
+
|
|
1457
|
+
async function readFileData(file) {
|
|
1458
|
+
try {
|
|
1459
|
+
var content = await runAsync(readContentFileAsync, file);
|
|
1460
|
+
return {
|
|
1461
|
+
content: content,
|
|
1462
|
+
size: content.byteLength || content.length, // ArrayBuffer or string
|
|
1463
|
+
name: file.name,
|
|
1464
|
+
basename: internal.getFileBase(file.name).toLowerCase(),
|
|
1465
|
+
type: internal.guessInputType(file.name, content)
|
|
1466
|
+
};
|
|
1467
|
+
} catch (e) {
|
|
1468
|
+
handleImportError("Web browser was unable to load the file.", file.name);
|
|
1469
|
+
}
|
|
1470
|
+
return null;
|
|
1471
|
+
}
|
|
1472
|
+
|
|
1473
|
+
async function readFiles(files) {
|
|
1474
|
+
var data = [], d;
|
|
1475
|
+
for (var file of files) {
|
|
1476
|
+
d = await readFileData(file);
|
|
1477
|
+
if (d) data.push(d);
|
|
1478
|
+
}
|
|
1479
|
+
return data;
|
|
1480
|
+
}
|
|
1481
|
+
|
|
1482
|
+
function groupFilesForImport(data, importOpts) {
|
|
1483
|
+
var names = importOpts.name ? [importOpts.name] : null;
|
|
1484
|
+
if (initialImport && opts.name) { // name from mapshaper-gui --name option
|
|
1485
|
+
names = opts.name.split(',');
|
|
1486
|
+
}
|
|
1487
|
+
|
|
1488
|
+
function key(basename, type) {
|
|
1489
|
+
return basename + '.' + type;
|
|
1490
|
+
}
|
|
1491
|
+
function hasShp(basename) {
|
|
1492
|
+
var shpKey = key(basename, 'shp');
|
|
1493
|
+
return data.some(d => key(d.basename, d.type) == shpKey);
|
|
1494
|
+
}
|
|
1495
|
+
data.forEach(d => {
|
|
1496
|
+
if (d.type == 'shp' || !isShapefilePart(d.name)) {
|
|
1497
|
+
d.group = key(d.basename, d.type);
|
|
1498
|
+
d.filename = d.name;
|
|
1499
|
+
} else if (hasShp(d.basename)) {
|
|
1500
|
+
d.group = key(d.basename, 'shp');
|
|
1501
|
+
} else if (d.type == 'dbf') {
|
|
1502
|
+
d.filename = d.name;
|
|
1503
|
+
d.group = key(d.basename, 'dbf');
|
|
1646
1504
|
} else {
|
|
1647
|
-
|
|
1648
|
-
|
|
1505
|
+
// shapefile part without a .shp file
|
|
1506
|
+
d.group = null;
|
|
1649
1507
|
}
|
|
1650
|
-
next(err, memo);
|
|
1651
1508
|
});
|
|
1652
|
-
|
|
1653
|
-
|
|
1509
|
+
var index = {};
|
|
1510
|
+
var groups = [];
|
|
1511
|
+
data.forEach(d => {
|
|
1512
|
+
if (!d.group) return;
|
|
1513
|
+
var g = index[d.group];
|
|
1514
|
+
if (!g) {
|
|
1515
|
+
g = {};
|
|
1516
|
+
g.layername = names ? names[groups.length] || names[names.length - 1] : null;
|
|
1517
|
+
groups.push(g);
|
|
1518
|
+
index[d.group] = g;
|
|
1519
|
+
}
|
|
1520
|
+
g.size = (g.size || 0) + d.size; // accumulate size
|
|
1521
|
+
g[d.type] = {
|
|
1522
|
+
filename: d.name,
|
|
1523
|
+
content: d.content
|
|
1524
|
+
};
|
|
1525
|
+
// kludge: stash import name for session history
|
|
1526
|
+
if (d.filename) g.filename = d.filename;
|
|
1527
|
+
});
|
|
1528
|
+
return groups;
|
|
1654
1529
|
}
|
|
1655
1530
|
}
|
|
1656
1531
|
|
|
@@ -1692,7 +1567,7 @@
|
|
|
1692
1567
|
var defaults = {
|
|
1693
1568
|
space: 7
|
|
1694
1569
|
};
|
|
1695
|
-
opts = utils
|
|
1570
|
+
opts = utils.extend(defaults, opts);
|
|
1696
1571
|
|
|
1697
1572
|
var _pct = 0;
|
|
1698
1573
|
var _track,
|
|
@@ -1745,7 +1620,7 @@
|
|
|
1745
1620
|
};
|
|
1746
1621
|
|
|
1747
1622
|
function setHandlePos(x, fire) {
|
|
1748
|
-
x = utils
|
|
1623
|
+
x = utils.clamp(x, 0, size());
|
|
1749
1624
|
var pct = x / size();
|
|
1750
1625
|
if (pct != _pct) {
|
|
1751
1626
|
_pct = pct;
|
|
@@ -1760,7 +1635,7 @@
|
|
|
1760
1635
|
}
|
|
1761
1636
|
}
|
|
1762
1637
|
|
|
1763
|
-
utils
|
|
1638
|
+
utils.inherit(Slider, EventDispatcher);
|
|
1764
1639
|
|
|
1765
1640
|
/*
|
|
1766
1641
|
How changes in the simplify control should affect other components
|
|
@@ -1828,7 +1703,7 @@
|
|
|
1828
1703
|
slider.on('change', function(e) {
|
|
1829
1704
|
var pct = fromSliderPct(e.pct);
|
|
1830
1705
|
text.value(pct);
|
|
1831
|
-
pct = utils
|
|
1706
|
+
pct = utils.parsePercent(text.text()); // use rounded value (for consistency w/ cli)
|
|
1832
1707
|
onChange(pct);
|
|
1833
1708
|
});
|
|
1834
1709
|
slider.on('start', function(e) {
|
|
@@ -1849,7 +1724,7 @@
|
|
|
1849
1724
|
else if (pct < 0.01) decimals = 3;
|
|
1850
1725
|
else if (pct < 1) decimals = 2;
|
|
1851
1726
|
else if (pct < 100) decimals = 1;
|
|
1852
|
-
return utils
|
|
1727
|
+
return utils.formatNumberForDisplay(pct, decimals) + "%";
|
|
1853
1728
|
});
|
|
1854
1729
|
|
|
1855
1730
|
text.parser(function(s) {
|
|
@@ -2101,7 +1976,7 @@
|
|
|
2101
1976
|
|
|
2102
1977
|
// Replace error function in mapshaper lib
|
|
2103
1978
|
var error = function() {
|
|
2104
|
-
stop.apply(null, utils
|
|
1979
|
+
stop.apply(null, utils.toArray(arguments));
|
|
2105
1980
|
};
|
|
2106
1981
|
|
|
2107
1982
|
var message = function() {
|
|
@@ -2115,11 +1990,11 @@
|
|
|
2115
1990
|
// replace CLI version of writeFiles()
|
|
2116
1991
|
internal.replaceWriteFiles(function(files, opts, done) {
|
|
2117
1992
|
var filename;
|
|
2118
|
-
if (!utils
|
|
1993
|
+
if (!utils.isArray(files) || files.length === 0) {
|
|
2119
1994
|
done("Nothing to export");
|
|
2120
1995
|
} else if (GUI.canSaveToServer() && !opts.save_to_download_folder) {
|
|
2121
|
-
var paths = internal.getOutputPaths(utils
|
|
2122
|
-
var data = utils
|
|
1996
|
+
var paths = internal.getOutputPaths(utils.pluck(files, 'filename'), opts);
|
|
1997
|
+
var data = utils.pluck(files, 'content');
|
|
2123
1998
|
saveFilesToServer(paths, data, function(err) {
|
|
2124
1999
|
var msg;
|
|
2125
2000
|
if (err) {
|
|
@@ -2138,7 +2013,7 @@
|
|
|
2138
2013
|
} else if (files.length == 1) {
|
|
2139
2014
|
saveBlobToDownloadFolder(files[0].filename, new Blob([files[0].content]), done);
|
|
2140
2015
|
} else {
|
|
2141
|
-
filename = internal.getCommonFileBase(utils
|
|
2016
|
+
filename = internal.getCommonFileBase(utils.pluck(files, 'filename')) || "output";
|
|
2142
2017
|
saveZipFile(filename + ".zip", files, done);
|
|
2143
2018
|
}
|
|
2144
2019
|
});
|
|
@@ -2160,10 +2035,10 @@
|
|
|
2160
2035
|
// try to match import filename of this dataset
|
|
2161
2036
|
if (d.info.input_files[0] == src) return d;
|
|
2162
2037
|
// try to match name of a layer in this dataset
|
|
2163
|
-
lyr = utils
|
|
2038
|
+
lyr = utils.find(d.layers, function(lyr) {return lyr.name == src;});
|
|
2164
2039
|
return lyr ? internal.isolateLayer(lyr, d) : null;
|
|
2165
2040
|
}, null);
|
|
2166
|
-
if (!retn) stop
|
|
2041
|
+
if (!retn) stop("Missing data layer [" + src + "]");
|
|
2167
2042
|
return retn;
|
|
2168
2043
|
}
|
|
2169
2044
|
|
|
@@ -2172,7 +2047,7 @@
|
|
|
2172
2047
|
// Return a copy with layers duplicated, so changes won't affect original layers
|
|
2173
2048
|
// This makes an (unsafe) assumption that the dataset arcs won't be changed...
|
|
2174
2049
|
// need to rethink this.
|
|
2175
|
-
return utils
|
|
2050
|
+
return utils.defaults({
|
|
2176
2051
|
layers: dataset.layers.map(internal.copyLayer)
|
|
2177
2052
|
}, dataset);
|
|
2178
2053
|
});
|
|
@@ -2244,7 +2119,7 @@
|
|
|
2244
2119
|
}
|
|
2245
2120
|
|
|
2246
2121
|
function projectPointsForDisplay(lyr, src, dest) {
|
|
2247
|
-
var copy = utils
|
|
2122
|
+
var copy = utils.extend({}, lyr);
|
|
2248
2123
|
var proj = internal.getProjTransform2(src, dest);
|
|
2249
2124
|
copy.shapes = internal.cloneShapes(lyr.shapes);
|
|
2250
2125
|
internal.projectPointLayer(copy, proj);
|
|
@@ -2642,7 +2517,7 @@
|
|
|
2642
2517
|
} else if (cmd == 'history') {
|
|
2643
2518
|
toLog(gui.session.toCommandLineString());
|
|
2644
2519
|
} else if (cmd == 'layers') {
|
|
2645
|
-
message
|
|
2520
|
+
message("Available layers:",
|
|
2646
2521
|
internal.getFormattedLayerList(model));
|
|
2647
2522
|
} else if (cmd == 'close' || cmd == 'exit' || cmd == 'quit') {
|
|
2648
2523
|
turnOff();
|
|
@@ -2731,7 +2606,7 @@
|
|
|
2731
2606
|
}
|
|
2732
2607
|
|
|
2733
2608
|
function onError(err) {
|
|
2734
|
-
if (utils
|
|
2609
|
+
if (utils.isString(err)) {
|
|
2735
2610
|
consoleStop(err);
|
|
2736
2611
|
} else if (err.name == 'UserError') {
|
|
2737
2612
|
// stop() has already been called, don't need to log
|
|
@@ -2746,7 +2621,7 @@
|
|
|
2746
2621
|
function consoleStop() {
|
|
2747
2622
|
var msg = GUI.formatMessageArgs(arguments);
|
|
2748
2623
|
consoleWarning(msg);
|
|
2749
|
-
throw new UserError
|
|
2624
|
+
throw new UserError(msg);
|
|
2750
2625
|
}
|
|
2751
2626
|
|
|
2752
2627
|
function consoleWarning() {
|
|
@@ -2910,7 +2785,7 @@
|
|
|
2910
2785
|
// console.log("first intersection:", internal.getIntersectionDebugData(XX[0], arcs));
|
|
2911
2786
|
pointLyr = {geometry_type: 'point', shapes: [internal.getIntersectionPoints(XX)]};
|
|
2912
2787
|
map.setIntersectionLayer(pointLyr, {layers:[pointLyr]});
|
|
2913
|
-
readout.html(utils
|
|
2788
|
+
readout.html(utils.format('<span class="icon"></span>%s line intersection%s <img class="close-btn" src="images/close.png">', n, utils.pluralSuffix(n)));
|
|
2914
2789
|
readout.findChild('.close-btn').on('click', dismiss);
|
|
2915
2790
|
} else {
|
|
2916
2791
|
map.setIntersectionLayer(null);
|
|
@@ -2919,7 +2794,7 @@
|
|
|
2919
2794
|
}
|
|
2920
2795
|
}
|
|
2921
2796
|
|
|
2922
|
-
utils
|
|
2797
|
+
utils.inherit(RepairControl, EventDispatcher);
|
|
2923
2798
|
|
|
2924
2799
|
function filterLayerByIds(lyr, ids) {
|
|
2925
2800
|
var shapes;
|
|
@@ -2927,7 +2802,7 @@
|
|
|
2927
2802
|
shapes = ids.map(function(id) {
|
|
2928
2803
|
return lyr.shapes[id];
|
|
2929
2804
|
});
|
|
2930
|
-
return utils
|
|
2805
|
+
return utils.defaults({shapes: shapes, data: null}, lyr);
|
|
2931
2806
|
}
|
|
2932
2807
|
return lyr;
|
|
2933
2808
|
}
|
|
@@ -2980,7 +2855,7 @@
|
|
|
2980
2855
|
});
|
|
2981
2856
|
|
|
2982
2857
|
internal.writeFiles = function() {
|
|
2983
|
-
error
|
|
2858
|
+
error(unsupportedMsg);
|
|
2984
2859
|
};
|
|
2985
2860
|
} else {
|
|
2986
2861
|
new SimpleButton(menu.findChild('.save-btn').addClass('default-btn')).on('click', onExportClick);
|
|
@@ -3016,7 +2891,7 @@
|
|
|
3016
2891
|
setTimeout(function() {
|
|
3017
2892
|
exportMenuSelection(layers, function(err) {
|
|
3018
2893
|
if (err) {
|
|
3019
|
-
if (utils
|
|
2894
|
+
if (utils.isString(err)) {
|
|
3020
2895
|
gui.alert(err);
|
|
3021
2896
|
} else {
|
|
3022
2897
|
// stack seems to change if Error is logged directly
|
|
@@ -3063,7 +2938,7 @@
|
|
|
3063
2938
|
// without changing its name elsewhere
|
|
3064
2939
|
layer: Object.assign({}, o.layer)
|
|
3065
2940
|
};
|
|
3066
|
-
var html = utils
|
|
2941
|
+
var html = utils.format(template, i + 1, target.layer.name || '[unnamed layer]');
|
|
3067
2942
|
// return {layer: o.layer, html: html};
|
|
3068
2943
|
var el = El('div').html(html).addClass('layer-item');
|
|
3069
2944
|
var box = el.findChild('input').node();
|
|
@@ -3173,9 +3048,9 @@
|
|
|
3173
3048
|
|
|
3174
3049
|
function initFormatMenu() {
|
|
3175
3050
|
var defaults = ['shapefile', 'geojson', 'topojson', 'json', 'dsv', 'svg'];
|
|
3176
|
-
var formats = utils
|
|
3051
|
+
var formats = utils.uniq(defaults.concat(getInputFormats()));
|
|
3177
3052
|
var items = formats.map(function(fmt) {
|
|
3178
|
-
return utils
|
|
3053
|
+
return utils.format('<div><label><input type="radio" name="format" value="%s"' +
|
|
3179
3054
|
' class="radio">%s</label></div>', fmt, internal.getFormatName(fmt));
|
|
3180
3055
|
});
|
|
3181
3056
|
menu.findChild('.export-formats').html(items.join('\n'));
|
|
@@ -3338,7 +3213,7 @@
|
|
|
3338
3213
|
// can be used as unique identifiers for caching rendered HTML, and as
|
|
3339
3214
|
// an id for layer menu event handlers
|
|
3340
3215
|
if (!lyr.menu_id || uniqIds[lyr.menu_id]) {
|
|
3341
|
-
lyr.menu_id = utils
|
|
3216
|
+
lyr.menu_id = utils.getUniqueName();
|
|
3342
3217
|
}
|
|
3343
3218
|
uniqIds[lyr.menu_id] = true;
|
|
3344
3219
|
if (isPinnable(lyr)) pinnableCount++;
|
|
@@ -3493,7 +3368,7 @@
|
|
|
3493
3368
|
GUI.onClick(entry, function() {
|
|
3494
3369
|
var target = findLayerById(id);
|
|
3495
3370
|
// don't select if user is typing or dragging
|
|
3496
|
-
if (!GUI.
|
|
3371
|
+
if (!GUI.textIsSelected() && !dragging) {
|
|
3497
3372
|
gui.clearMode();
|
|
3498
3373
|
if (!map.isActiveLayer(target.layer)) {
|
|
3499
3374
|
model.selectLayer(target.layer, target.dataset);
|
|
@@ -3511,1795 +3386,172 @@
|
|
|
3511
3386
|
type = lyr.geometry_type + ' feature';
|
|
3512
3387
|
}
|
|
3513
3388
|
if (type) {
|
|
3514
|
-
str = utils
|
|
3389
|
+
str = utils.format('%,d %s%s', n, type, utils.pluralSuffix(n));
|
|
3515
3390
|
} else {
|
|
3516
3391
|
str = "[empty]";
|
|
3517
3392
|
}
|
|
3518
3393
|
return str;
|
|
3519
3394
|
}
|
|
3520
3395
|
|
|
3521
|
-
function getWarnings(lyr, dataset) {
|
|
3522
|
-
var file = internal.getLayerSourceFile(lyr, dataset);
|
|
3523
|
-
var missing = [];
|
|
3524
|
-
var msg;
|
|
3525
|
-
if (utils
|
|
3526
|
-
if (!lyr.data) {
|
|
3527
|
-
missing.push('.dbf');
|
|
3528
|
-
}
|
|
3529
|
-
if (!dataset.info.prj && !dataset.info.crs) {
|
|
3530
|
-
missing.push('.prj');
|
|
3531
|
-
}
|
|
3532
|
-
}
|
|
3533
|
-
if (missing.length) {
|
|
3534
|
-
msg = 'missing ' + missing.join(' and ') + ' data';
|
|
3535
|
-
}
|
|
3536
|
-
return msg;
|
|
3537
|
-
}
|
|
3538
|
-
|
|
3539
|
-
function describeSrc(lyr, dataset) {
|
|
3540
|
-
return internal.getLayerSourceFile(lyr, dataset);
|
|
3541
|
-
}
|
|
3542
|
-
|
|
3543
|
-
|
|
3544
|
-
function isPinnable(lyr) {
|
|
3545
|
-
return internal.layerHasGeometry(lyr) || internal.layerHasFurniture(lyr);
|
|
3546
|
-
}
|
|
3547
|
-
|
|
3548
|
-
|
|
3549
|
-
|
|
3550
|
-
function rowHTML(c1, c2, cname) {
|
|
3551
|
-
return utils$1.format('<div class="row%s"><div class="col1">%s</div>' +
|
|
3552
|
-
'<div class="col2">%s</div></div>', cname ? ' ' + cname : '', c1, c2);
|
|
3553
|
-
}
|
|
3554
|
-
}
|
|
3555
|
-
|
|
3556
|
-
function SessionHistory(gui) {
|
|
3557
|
-
var commands = [];
|
|
3558
|
-
// commands that can be ignored when checking for unsaved changes
|
|
3559
|
-
var nonEditingCommands = 'i,target,info,version,verbose,projections,inspect,help,h,encodings,calc'.split(',');
|
|
3560
|
-
|
|
3561
|
-
this.unsavedChanges = function() {
|
|
3562
|
-
var cmd, cmdName;
|
|
3563
|
-
for (var i=commands.length - 1; i >= 0; i--) {
|
|
3564
|
-
cmdName = getCommandName(commands[i]);
|
|
3565
|
-
if (cmdName == 'o') break;
|
|
3566
|
-
if (nonEditingCommands.includes(cmdName)) continue;
|
|
3567
|
-
return true;
|
|
3568
|
-
}
|
|
3569
|
-
return false;
|
|
3570
|
-
};
|
|
3571
|
-
|
|
3572
|
-
this.fileImported = function(file, optStr) {
|
|
3573
|
-
var cmd = '-i ' + file;
|
|
3574
|
-
if (optStr) {
|
|
3575
|
-
cmd += ' ' + optStr;
|
|
3576
|
-
}
|
|
3577
|
-
commands.push(cmd);
|
|
3578
|
-
};
|
|
3579
|
-
|
|
3580
|
-
this.layerRenamed = function(lyr, name) {
|
|
3581
|
-
var currTarget = getCurrentTarget();
|
|
3582
|
-
var layerTarget = getTargetFromLayer(lyr);
|
|
3583
|
-
if (currTarget == layerTarget) {
|
|
3584
|
-
commands.push('-rename-layers ' + name);
|
|
3585
|
-
} else {
|
|
3586
|
-
commands.push('-rename-layers ' + name + ' target=' + layerTarget);
|
|
3587
|
-
commands.push('-target ' + currTarget);
|
|
3588
|
-
}
|
|
3589
|
-
};
|
|
3590
|
-
|
|
3591
|
-
this.consoleCommands = function(str) {
|
|
3592
|
-
commands.push(str); // todo: split commands?
|
|
3593
|
-
};
|
|
3594
|
-
|
|
3595
|
-
this.simplificationApplied = function(optStr) {
|
|
3596
|
-
commands.push('-simplify ' + optStr);
|
|
3597
|
-
};
|
|
3598
|
-
|
|
3599
|
-
this.simplificationRepair = function() {
|
|
3600
|
-
// TODO: improve this... repair does not necessarily apply to most recent
|
|
3601
|
-
// simplification command
|
|
3602
|
-
// consider adding a (hidden) repair command to handle this event
|
|
3603
|
-
var i = indexOfLastCommand('-simplify');
|
|
3604
|
-
if (i > -1) {
|
|
3605
|
-
commands[i] = commands[i].replace(' no-repair', '');
|
|
3606
|
-
}
|
|
3607
|
-
};
|
|
3608
|
-
|
|
3609
|
-
this.updateSimplificationPct = function(pct) {
|
|
3610
|
-
var i = indexOfLastCommand('-simplify');
|
|
3611
|
-
if (i > -1) {
|
|
3612
|
-
commands[i] = commands[i].replace(/percentage=[^ ]+/, 'percentage=' + pct);
|
|
3613
|
-
}
|
|
3614
|
-
};
|
|
3615
|
-
|
|
3616
|
-
this.dataValueUpdated = function(id, field, value) {
|
|
3617
|
-
var cmd = `-each 'd[${JSON.stringify(field)}] = ${JSON.stringify(value)}' where='this.id == ${id}'`;
|
|
3618
|
-
commands.push(cmd);
|
|
3619
|
-
};
|
|
3620
|
-
|
|
3621
|
-
this.layersExported = function(ids, optStr) {
|
|
3622
|
-
var layers = gui.model.getLayers();
|
|
3623
|
-
var cmd = '-o';
|
|
3624
|
-
if (layers.length > 1) {
|
|
3625
|
-
cmd += ' target=' + ids.map(getTargetFromId).join(',');
|
|
3626
|
-
}
|
|
3627
|
-
if (optStr) {
|
|
3628
|
-
cmd += ' ' + optStr;
|
|
3629
|
-
}
|
|
3630
|
-
commands.push(cmd);
|
|
3631
|
-
};
|
|
3632
|
-
|
|
3633
|
-
this.setTargetLayer = function(lyr) {
|
|
3634
|
-
var layers = gui.model.getLayers();
|
|
3635
|
-
if (layers.length > 1) {
|
|
3636
|
-
if (indexOfLastCommand('-target') == commands.length - 1) {
|
|
3637
|
-
commands.pop(); // if last commands was -target, remove it
|
|
3638
|
-
}
|
|
3639
|
-
commands.push('-target ' + getTargetFromLayer(lyr));
|
|
3640
|
-
}
|
|
3641
|
-
};
|
|
3642
|
-
|
|
3643
|
-
this.toCommandLineString = function() {
|
|
3644
|
-
var str = commands.join(' \\\n ');
|
|
3645
|
-
return 'mapshaper ' + str;
|
|
3646
|
-
};
|
|
3647
|
-
|
|
3648
|
-
function getCommandName(cmd) {
|
|
3649
|
-
var rxp = /^-([a-z0-9-]+)/;
|
|
3650
|
-
var match = rxp.exec(cmd);
|
|
3651
|
-
return match ? match[1] : null;
|
|
3652
|
-
}
|
|
3653
|
-
|
|
3654
|
-
function getCurrentTarget() {
|
|
3655
|
-
return getTargetFromLayer(gui.model.getActiveLayer().layer);
|
|
3656
|
-
}
|
|
3657
|
-
|
|
3658
|
-
function indexOfLastCommand(cmd) {
|
|
3659
|
-
return commands.reduce(function(memo, str, i) {
|
|
3660
|
-
return str.indexOf(cmd) === 0 ? i : memo;
|
|
3661
|
-
}, -1);
|
|
3662
|
-
}
|
|
3663
|
-
|
|
3664
|
-
function getTargetFromId(id) {
|
|
3665
|
-
var layers = gui.model.getLayers();
|
|
3666
|
-
return getTargetFromLayer(layers[id - 1].layer);
|
|
3667
|
-
}
|
|
3668
|
-
|
|
3669
|
-
function getTargetFromLayer(lyr) {
|
|
3670
|
-
var id = internal.getLayerTargetId(gui.model, lyr);
|
|
3671
|
-
return internal.formatOptionValue(id);
|
|
3672
|
-
}
|
|
3673
|
-
}
|
|
3674
|
-
|
|
3675
|
-
var Buffer = require('buffer').Buffer; // works with browserify
|
|
3676
|
-
|
|
3677
|
-
var uniqCount = 0;
|
|
3678
|
-
function getUniqueName(prefix) {
|
|
3679
|
-
return (prefix || "__id_") + (++uniqCount);
|
|
3680
|
-
}
|
|
3681
|
-
|
|
3682
|
-
function isFunction(obj) {
|
|
3683
|
-
return typeof obj == 'function';
|
|
3684
|
-
}
|
|
3685
|
-
|
|
3686
|
-
function isObject(obj) {
|
|
3687
|
-
return obj === Object(obj); // via underscore
|
|
3688
|
-
}
|
|
3689
|
-
|
|
3690
|
-
function clamp(val, min, max) {
|
|
3691
|
-
return val < min ? min : (val > max ? max : val);
|
|
3692
|
-
}
|
|
3693
|
-
|
|
3694
|
-
function isArray(obj) {
|
|
3695
|
-
return Array.isArray(obj);
|
|
3696
|
-
}
|
|
3697
|
-
|
|
3698
|
-
// Is obj a valid number or NaN? (test if obj is type number)
|
|
3699
|
-
function isNumber(obj) {
|
|
3700
|
-
return obj != null && obj.constructor == Number;
|
|
3701
|
-
}
|
|
3702
|
-
|
|
3703
|
-
function isValidNumber(val) {
|
|
3704
|
-
return isNumber(val) && !isNaN(val);
|
|
3705
|
-
}
|
|
3706
|
-
|
|
3707
|
-
// Similar to isFinite() but does not coerce strings or other types
|
|
3708
|
-
function isFiniteNumber(val) {
|
|
3709
|
-
return isValidNumber(val) && val !== Infinity && val !== -Infinity;
|
|
3710
|
-
}
|
|
3711
|
-
|
|
3712
|
-
// This uses type conversion
|
|
3713
|
-
// export function isFiniteNumber(val) {
|
|
3714
|
-
// return val > -Infinity && val < Infinity;
|
|
3715
|
-
// }
|
|
3716
|
-
|
|
3717
|
-
function isNonNegNumber(val) {
|
|
3718
|
-
return isNumber(val) && val >= 0;
|
|
3719
|
-
}
|
|
3720
|
-
|
|
3721
|
-
function isInteger(obj) {
|
|
3722
|
-
return isNumber(obj) && ((obj | 0) === obj);
|
|
3723
|
-
}
|
|
3724
|
-
|
|
3725
|
-
function isEven(obj) {
|
|
3726
|
-
return (obj % 2) === 0;
|
|
3727
|
-
}
|
|
3728
|
-
|
|
3729
|
-
function isOdd(obj) {
|
|
3730
|
-
return (obj % 2) === 1;
|
|
3731
|
-
}
|
|
3732
|
-
|
|
3733
|
-
function isString(obj) {
|
|
3734
|
-
return obj != null && obj.toString === String.prototype.toString;
|
|
3735
|
-
// TODO: replace w/ something better.
|
|
3736
|
-
}
|
|
3737
|
-
|
|
3738
|
-
function isDate(obj) {
|
|
3739
|
-
return !!obj && obj.getTime === Date.prototype.getTime;
|
|
3740
|
-
}
|
|
3741
|
-
|
|
3742
|
-
function isBoolean(obj) {
|
|
3743
|
-
return obj === true || obj === false;
|
|
3744
|
-
}
|
|
3745
|
-
|
|
3746
|
-
function formatDateISO(d) {
|
|
3747
|
-
if (!isDate(d)) return '';
|
|
3748
|
-
return d.toISOString().replace(':00.000Z', 'Z');
|
|
3749
|
-
}
|
|
3750
|
-
|
|
3751
|
-
// Convert an array-like object to an Array, or make a copy if @obj is an Array
|
|
3752
|
-
function toArray(obj) {
|
|
3753
|
-
var arr;
|
|
3754
|
-
if (!isArrayLike(obj)) error("toArray() requires an array-like object");
|
|
3755
|
-
try {
|
|
3756
|
-
arr = Array.prototype.slice.call(obj, 0); // breaks in ie8
|
|
3757
|
-
} catch(e) {
|
|
3758
|
-
// support ie8
|
|
3759
|
-
arr = [];
|
|
3760
|
-
for (var i=0, n=obj.length; i<n; i++) {
|
|
3761
|
-
arr[i] = obj[i];
|
|
3762
|
-
}
|
|
3763
|
-
}
|
|
3764
|
-
return arr;
|
|
3765
|
-
}
|
|
3766
|
-
|
|
3767
|
-
// Array like: has length property, is numerically indexed and mutable.
|
|
3768
|
-
// TODO: try to detect objects with length property but no indexed data elements
|
|
3769
|
-
function isArrayLike(obj) {
|
|
3770
|
-
if (!obj) return false;
|
|
3771
|
-
if (isArray(obj)) return true;
|
|
3772
|
-
if (isString(obj)) return false;
|
|
3773
|
-
if (obj.length === 0) return true;
|
|
3774
|
-
if (obj.length > 0) return true;
|
|
3775
|
-
return false;
|
|
3776
|
-
}
|
|
3777
|
-
|
|
3778
|
-
// See https://raw.github.com/kvz/phpjs/master/functions/strings/addslashes.js
|
|
3779
|
-
function addslashes(str) {
|
|
3780
|
-
return (str + '').replace(/[\\"']/g, '\\$&').replace(/\u0000/g, '\\0');
|
|
3781
|
-
}
|
|
3782
|
-
|
|
3783
|
-
// Escape a literal string to use in a regexp.
|
|
3784
|
-
// Ref.: http://simonwillison.net/2006/Jan/20/escape/
|
|
3785
|
-
function regexEscape(str) {
|
|
3786
|
-
return str.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&');
|
|
3787
|
-
}
|
|
3788
|
-
|
|
3789
|
-
|
|
3790
|
-
// See https://github.com/janl/mustache.js/blob/master/mustache.js
|
|
3791
|
-
var entityMap = {
|
|
3792
|
-
'&': '&',
|
|
3793
|
-
'<': '<',
|
|
3794
|
-
'>': '>',
|
|
3795
|
-
'"': '"',
|
|
3796
|
-
"'": ''',
|
|
3797
|
-
'/': '/'
|
|
3798
|
-
};
|
|
3799
|
-
function htmlEscape(s) {
|
|
3800
|
-
return String(s).replace(/[&<>"'\/]/g, function(s) {
|
|
3801
|
-
return entityMap[s];
|
|
3802
|
-
});
|
|
3803
|
-
}
|
|
3804
|
-
|
|
3805
|
-
function defaults(dest) {
|
|
3806
|
-
for (var i=1, n=arguments.length; i<n; i++) {
|
|
3807
|
-
var src = arguments[i] || {};
|
|
3808
|
-
for (var key in src) {
|
|
3809
|
-
if (key in dest === false && src.hasOwnProperty(key)) {
|
|
3810
|
-
dest[key] = src[key];
|
|
3811
|
-
}
|
|
3812
|
-
}
|
|
3813
|
-
}
|
|
3814
|
-
return dest;
|
|
3815
|
-
}
|
|
3816
|
-
|
|
3817
|
-
function extend(o) {
|
|
3818
|
-
var dest = o || {},
|
|
3819
|
-
n = arguments.length,
|
|
3820
|
-
key, i, src;
|
|
3821
|
-
for (i=1; i<n; i++) {
|
|
3822
|
-
src = arguments[i] || {};
|
|
3823
|
-
for (key in src) {
|
|
3824
|
-
if (src.hasOwnProperty(key)) {
|
|
3825
|
-
dest[key] = src[key];
|
|
3826
|
-
}
|
|
3827
|
-
}
|
|
3828
|
-
}
|
|
3829
|
-
return dest;
|
|
3830
|
-
}
|
|
3831
|
-
|
|
3832
|
-
// Pseudoclassical inheritance
|
|
3833
|
-
//
|
|
3834
|
-
// Inherit from a Parent function:
|
|
3835
|
-
// inherit(Child, Parent);
|
|
3836
|
-
// Call parent's constructor (inside child constructor):
|
|
3837
|
-
// this.__super__([args...]);
|
|
3838
|
-
function inherit(targ, src) {
|
|
3839
|
-
var f = function() {
|
|
3840
|
-
if (this.__super__ == f) {
|
|
3841
|
-
// add __super__ of parent to front of lookup chain
|
|
3842
|
-
// so parent class constructor can call its parent using this.__super__
|
|
3843
|
-
this.__super__ = src.prototype.__super__;
|
|
3844
|
-
// call parent constructor function. this.__super__ now points to parent-of-parent
|
|
3845
|
-
src.apply(this, arguments);
|
|
3846
|
-
// remove temp __super__, expose targ.prototype.__super__ again
|
|
3847
|
-
delete this.__super__;
|
|
3848
|
-
}
|
|
3849
|
-
};
|
|
3850
|
-
|
|
3851
|
-
f.prototype = src.prototype || src; // added || src to allow inheriting from objects as well as functions
|
|
3852
|
-
// Extend targ prototype instead of wiping it out --
|
|
3853
|
-
// in case inherit() is called after targ.prototype = {stuff}; statement
|
|
3854
|
-
targ.prototype = extend(new f(), targ.prototype); //
|
|
3855
|
-
targ.prototype.constructor = targ;
|
|
3856
|
-
targ.prototype.__super__ = f;
|
|
3857
|
-
}
|
|
3858
|
-
|
|
3859
|
-
|
|
3860
|
-
// Call @iter on each member of an array (similar to Array#reduce(iter))
|
|
3861
|
-
// iter: function(memo, item, callback)
|
|
3862
|
-
// Call @done when all members have been processed or if an error occurs
|
|
3863
|
-
// done: function(err, memo)
|
|
3864
|
-
// @memo: Initial value
|
|
3865
|
-
//
|
|
3866
|
-
function reduceAsync(arr, memo, iter, done) {
|
|
3867
|
-
var call = typeof setImmediate == 'undefined' ? setTimeout : setImmediate;
|
|
3868
|
-
var i=0;
|
|
3869
|
-
next(null, memo);
|
|
3870
|
-
|
|
3871
|
-
function next(err, memo) {
|
|
3872
|
-
// Detach next operation from call stack to prevent overflow
|
|
3873
|
-
// Don't use setTimeout(, 0) if setImmediate is available
|
|
3874
|
-
// (setTimeout() can introduce a long delay if previous operation was slow,
|
|
3875
|
-
// as of Node 0.10.32 -- a bug?)
|
|
3876
|
-
if (err) {
|
|
3877
|
-
return done(err, null);
|
|
3878
|
-
}
|
|
3879
|
-
call(function() {
|
|
3880
|
-
if (i < arr.length === false) {
|
|
3881
|
-
done(null, memo);
|
|
3882
|
-
} else {
|
|
3883
|
-
iter(memo, arr[i++], next);
|
|
3884
|
-
}
|
|
3885
|
-
}, 0);
|
|
3886
|
-
}
|
|
3887
|
-
}
|
|
3888
|
-
|
|
3889
|
-
|
|
3890
|
-
// Append elements of @src array to @dest array
|
|
3891
|
-
function merge(dest, src) {
|
|
3892
|
-
if (!isArray(dest) || !isArray(src)) {
|
|
3893
|
-
error("Usage: merge(destArray, srcArray);");
|
|
3894
|
-
}
|
|
3895
|
-
for (var i=0, n=src.length; i<n; i++) {
|
|
3896
|
-
dest.push(src[i]);
|
|
3897
|
-
}
|
|
3898
|
-
return dest;
|
|
3899
|
-
}
|
|
3900
|
-
|
|
3901
|
-
// Returns elements in arr and not in other
|
|
3902
|
-
// (similar to underscore diff)
|
|
3903
|
-
function difference(arr, other) {
|
|
3904
|
-
var index = arrayToIndex(other);
|
|
3905
|
-
return arr.filter(function(el) {
|
|
3906
|
-
return !Object.prototype.hasOwnProperty.call(index, el);
|
|
3907
|
-
});
|
|
3908
|
-
}
|
|
3909
|
-
|
|
3910
|
-
// Return the intersection of two arrays
|
|
3911
|
-
function intersection(a, b) {
|
|
3912
|
-
return a.filter(function(el) {
|
|
3913
|
-
return b.includes(el);
|
|
3914
|
-
});
|
|
3915
|
-
}
|
|
3916
|
-
|
|
3917
|
-
function indexOf(arr, item) {
|
|
3918
|
-
var nan = item !== item;
|
|
3919
|
-
for (var i = 0, len = arr.length || 0; i < len; i++) {
|
|
3920
|
-
if (arr[i] === item) return i;
|
|
3921
|
-
if (nan && arr[i] !== arr[i]) return i;
|
|
3922
|
-
}
|
|
3923
|
-
return -1;
|
|
3924
|
-
}
|
|
3925
|
-
|
|
3926
|
-
// Test a string or array-like object for existence of substring or element
|
|
3927
|
-
function contains(container, item) {
|
|
3928
|
-
if (isString(container)) {
|
|
3929
|
-
return container.indexOf(item) != -1;
|
|
3930
|
-
}
|
|
3931
|
-
else if (isArrayLike(container)) {
|
|
3932
|
-
return indexOf(container, item) != -1;
|
|
3933
|
-
}
|
|
3934
|
-
error("Expected Array or String argument");
|
|
3935
|
-
}
|
|
3936
|
-
|
|
3937
|
-
function some(arr, test) {
|
|
3938
|
-
return arr.reduce(function(val, item) {
|
|
3939
|
-
return val || test(item); // TODO: short-circuit?
|
|
3940
|
-
}, false);
|
|
3941
|
-
}
|
|
3942
|
-
|
|
3943
|
-
function every(arr, test) {
|
|
3944
|
-
return arr.reduce(function(val, item) {
|
|
3945
|
-
return val && test(item);
|
|
3946
|
-
}, true);
|
|
3947
|
-
}
|
|
3948
|
-
|
|
3949
|
-
function find(arr, test, ctx) {
|
|
3950
|
-
var matches = arr.filter(test, ctx);
|
|
3951
|
-
return matches.length === 0 ? null : matches[0];
|
|
3952
|
-
}
|
|
3953
|
-
|
|
3954
|
-
function range(len, start, inc) {
|
|
3955
|
-
var arr = [],
|
|
3956
|
-
v = start === void 0 ? 0 : start,
|
|
3957
|
-
i = inc === void 0 ? 1 : inc;
|
|
3958
|
-
while(len--) {
|
|
3959
|
-
arr.push(v);
|
|
3960
|
-
v += i;
|
|
3961
|
-
}
|
|
3962
|
-
return arr;
|
|
3963
|
-
}
|
|
3964
|
-
|
|
3965
|
-
function repeat(times, func) {
|
|
3966
|
-
var values = [],
|
|
3967
|
-
val;
|
|
3968
|
-
for (var i=0; i<times; i++) {
|
|
3969
|
-
val = func(i);
|
|
3970
|
-
if (val !== void 0) {
|
|
3971
|
-
values[i] = val;
|
|
3972
|
-
}
|
|
3973
|
-
}
|
|
3974
|
-
return values.length > 0 ? values : void 0;
|
|
3975
|
-
}
|
|
3976
|
-
|
|
3977
|
-
// Calc sum, skip falsy and NaN values
|
|
3978
|
-
// Assumes: no other non-numeric objects in array
|
|
3979
|
-
//
|
|
3980
|
-
function sum(arr, info) {
|
|
3981
|
-
if (!isArrayLike(arr)) error ("sum() expects an array, received:", arr);
|
|
3982
|
-
var tot = 0,
|
|
3983
|
-
nan = 0,
|
|
3984
|
-
val;
|
|
3985
|
-
for (var i=0, n=arr.length; i<n; i++) {
|
|
3986
|
-
val = arr[i];
|
|
3987
|
-
if (val) {
|
|
3988
|
-
tot += val;
|
|
3989
|
-
} else if (isNaN(val)) {
|
|
3990
|
-
nan++;
|
|
3991
|
-
}
|
|
3992
|
-
}
|
|
3993
|
-
if (info) {
|
|
3994
|
-
info.nan = nan;
|
|
3995
|
-
}
|
|
3996
|
-
return tot;
|
|
3997
|
-
}
|
|
3998
|
-
|
|
3999
|
-
// Calculate min and max values of an array, ignoring NaN values
|
|
4000
|
-
function getArrayBounds(arr) {
|
|
4001
|
-
var min = Infinity,
|
|
4002
|
-
max = -Infinity,
|
|
4003
|
-
nan = 0, val;
|
|
4004
|
-
for (var i=0, len=arr.length; i<len; i++) {
|
|
4005
|
-
val = arr[i];
|
|
4006
|
-
if (val !== val) nan++;
|
|
4007
|
-
if (val < min) min = val;
|
|
4008
|
-
if (val > max) max = val;
|
|
4009
|
-
}
|
|
4010
|
-
return {
|
|
4011
|
-
min: min,
|
|
4012
|
-
max: max,
|
|
4013
|
-
nan: nan
|
|
4014
|
-
};
|
|
4015
|
-
}
|
|
4016
|
-
|
|
4017
|
-
// export function uniq(src) {
|
|
4018
|
-
// var index = {};
|
|
4019
|
-
// return src.reduce(function(memo, el) {
|
|
4020
|
-
// if (el in index === false) {
|
|
4021
|
-
// index[el] = true;
|
|
4022
|
-
// memo.push(el);
|
|
4023
|
-
// }
|
|
4024
|
-
// return memo;
|
|
4025
|
-
// }, []);
|
|
4026
|
-
// }
|
|
4027
|
-
|
|
4028
|
-
function uniq(src) {
|
|
4029
|
-
var index = new Set();
|
|
4030
|
-
var arr = [];
|
|
4031
|
-
var item;
|
|
4032
|
-
for (var i=0, n=src.length; i<n; i++) {
|
|
4033
|
-
item = src[i];
|
|
4034
|
-
if (!index.has(item)) {
|
|
4035
|
-
arr.push(item);
|
|
4036
|
-
index.add(item);
|
|
4037
|
-
}
|
|
4038
|
-
}
|
|
4039
|
-
return arr;
|
|
4040
|
-
}
|
|
4041
|
-
|
|
4042
|
-
function pluck(arr, key) {
|
|
4043
|
-
return arr.map(function(obj) {
|
|
4044
|
-
return obj[key];
|
|
4045
|
-
});
|
|
4046
|
-
}
|
|
4047
|
-
|
|
4048
|
-
function countValues(arr) {
|
|
4049
|
-
return arr.reduce(function(memo, val) {
|
|
4050
|
-
memo[val] = (val in memo) ? memo[val] + 1 : 1;
|
|
4051
|
-
return memo;
|
|
4052
|
-
}, {});
|
|
4053
|
-
}
|
|
4054
|
-
|
|
4055
|
-
function indexOn(arr, k) {
|
|
4056
|
-
return arr.reduce(function(index, o) {
|
|
4057
|
-
index[o[k]] = o;
|
|
4058
|
-
return index;
|
|
4059
|
-
}, {});
|
|
4060
|
-
}
|
|
4061
|
-
|
|
4062
|
-
function groupBy(arr, k) {
|
|
4063
|
-
return arr.reduce(function(index, o) {
|
|
4064
|
-
var keyval = o[k];
|
|
4065
|
-
if (keyval in index) {
|
|
4066
|
-
index[keyval].push(o);
|
|
4067
|
-
} else {
|
|
4068
|
-
index[keyval] = [o];
|
|
4069
|
-
}
|
|
4070
|
-
return index;
|
|
4071
|
-
}, {});
|
|
4072
|
-
}
|
|
4073
|
-
|
|
4074
|
-
function arrayToIndex(arr, val) {
|
|
4075
|
-
var init = arguments.length > 1;
|
|
4076
|
-
return arr.reduce(function(index, key) {
|
|
4077
|
-
index[key] = init ? val : true;
|
|
4078
|
-
return index;
|
|
4079
|
-
}, {});
|
|
4080
|
-
}
|
|
4081
|
-
|
|
4082
|
-
// Support for iterating over array-like objects, like typed arrays
|
|
4083
|
-
function forEach(arr, func, ctx) {
|
|
4084
|
-
if (!isArrayLike(arr)) {
|
|
4085
|
-
throw new Error("#forEach() takes an array-like argument. " + arr);
|
|
4086
|
-
}
|
|
4087
|
-
for (var i=0, n=arr.length; i < n; i++) {
|
|
4088
|
-
func.call(ctx, arr[i], i);
|
|
4089
|
-
}
|
|
4090
|
-
}
|
|
4091
|
-
|
|
4092
|
-
function forEachProperty(o, func, ctx) {
|
|
4093
|
-
Object.keys(o).forEach(function(key) {
|
|
4094
|
-
func.call(ctx, o[key], key);
|
|
4095
|
-
});
|
|
4096
|
-
}
|
|
4097
|
-
|
|
4098
|
-
function initializeArray(arr, init) {
|
|
4099
|
-
for (var i=0, len=arr.length; i<len; i++) {
|
|
4100
|
-
arr[i] = init;
|
|
4101
|
-
}
|
|
4102
|
-
return arr;
|
|
4103
|
-
}
|
|
4104
|
-
|
|
4105
|
-
function replaceArray(arr, arr2) {
|
|
4106
|
-
arr.splice(0, arr.length);
|
|
4107
|
-
for (var i=0, n=arr2.length; i<n; i++) {
|
|
4108
|
-
arr.push(arr2[i]);
|
|
4109
|
-
}
|
|
4110
|
-
}
|
|
4111
|
-
|
|
4112
|
-
function repeatString(src, n) {
|
|
4113
|
-
var str = "";
|
|
4114
|
-
for (var i=0; i<n; i++)
|
|
4115
|
-
str += src;
|
|
4116
|
-
return str;
|
|
4117
|
-
}
|
|
4118
|
-
|
|
4119
|
-
function splitLines(str) {
|
|
4120
|
-
return str.split(/\r?\n/);
|
|
4121
|
-
}
|
|
4122
|
-
|
|
4123
|
-
function pluralSuffix(count) {
|
|
4124
|
-
return count != 1 ? 's' : '';
|
|
4125
|
-
}
|
|
4126
|
-
|
|
4127
|
-
function endsWith(str, ending) {
|
|
4128
|
-
return str.indexOf(ending, str.length - ending.length) !== -1;
|
|
4129
|
-
}
|
|
4130
|
-
|
|
4131
|
-
function lpad(str, size, pad) {
|
|
4132
|
-
pad = pad || ' ';
|
|
4133
|
-
str = String(str);
|
|
4134
|
-
return repeatString(pad, size - str.length) + str;
|
|
4135
|
-
}
|
|
4136
|
-
|
|
4137
|
-
function rpad(str, size, pad) {
|
|
4138
|
-
pad = pad || ' ';
|
|
4139
|
-
str = String(str);
|
|
4140
|
-
return str + repeatString(pad, size - str.length);
|
|
4141
|
-
}
|
|
4142
|
-
|
|
4143
|
-
function trim(str) {
|
|
4144
|
-
return ltrim(rtrim(str));
|
|
4145
|
-
}
|
|
4146
|
-
|
|
4147
|
-
var ltrimRxp = /^\s+/;
|
|
4148
|
-
function ltrim(str) {
|
|
4149
|
-
return str.replace(ltrimRxp, '');
|
|
4150
|
-
}
|
|
4151
|
-
|
|
4152
|
-
var rtrimRxp = /\s+$/;
|
|
4153
|
-
function rtrim(str) {
|
|
4154
|
-
return str.replace(rtrimRxp, '');
|
|
4155
|
-
}
|
|
4156
|
-
|
|
4157
|
-
function addThousandsSep(str) {
|
|
4158
|
-
var fmt = '',
|
|
4159
|
-
start = str[0] == '-' ? 1 : 0,
|
|
4160
|
-
dec = str.indexOf('.'),
|
|
4161
|
-
end = str.length,
|
|
4162
|
-
ins = (dec == -1 ? end : dec) - 3;
|
|
4163
|
-
while (ins > start) {
|
|
4164
|
-
fmt = ',' + str.substring(ins, end) + fmt;
|
|
4165
|
-
end = ins;
|
|
4166
|
-
ins -= 3;
|
|
4167
|
-
}
|
|
4168
|
-
return str.substring(0, end) + fmt;
|
|
4169
|
-
}
|
|
4170
|
-
|
|
4171
|
-
function numToStr(num, decimals) {
|
|
4172
|
-
return decimals >= 0 ? num.toFixed(decimals) : String(num);
|
|
4173
|
-
}
|
|
4174
|
-
|
|
4175
|
-
function formatNumber(val) {
|
|
4176
|
-
return val + '';
|
|
4177
|
-
}
|
|
4178
|
-
|
|
4179
|
-
function formatIntlNumber(val) {
|
|
4180
|
-
var str = formatNumber(val);
|
|
4181
|
-
return '"' + str.replace('.', ',') + '"'; // need to quote if comma-delimited
|
|
4182
|
-
}
|
|
4183
|
-
|
|
4184
|
-
function formatNumberForDisplay(num, decimals, nullStr, showPos) {
|
|
4185
|
-
var fmt;
|
|
4186
|
-
if (isNaN(num)) {
|
|
4187
|
-
fmt = nullStr || '-';
|
|
4188
|
-
} else {
|
|
4189
|
-
fmt = numToStr(num, decimals);
|
|
4190
|
-
fmt = addThousandsSep(fmt);
|
|
4191
|
-
if (showPos && parseFloat(fmt) > 0) {
|
|
4192
|
-
fmt = "+" + fmt;
|
|
4193
|
-
}
|
|
4194
|
-
}
|
|
4195
|
-
return fmt;
|
|
4196
|
-
}
|
|
4197
|
-
|
|
4198
|
-
function shuffle(arr) {
|
|
4199
|
-
var tmp, i, j;
|
|
4200
|
-
for (i = arr.length - 1; i > 0; i--) {
|
|
4201
|
-
j = Math.floor(Math.random() * (i + 1));
|
|
4202
|
-
tmp = arr[i];
|
|
4203
|
-
arr[i] = arr[j];
|
|
4204
|
-
arr[j] = tmp;
|
|
4205
|
-
}
|
|
4206
|
-
}
|
|
4207
|
-
|
|
4208
|
-
// Sort an array of objects based on one or more properties.
|
|
4209
|
-
// Usage: sortOn(array, key1, asc?[, key2, asc? ...])
|
|
4210
|
-
//
|
|
4211
|
-
function sortOn(arr) {
|
|
4212
|
-
var comparators = [];
|
|
4213
|
-
for (var i=1; i<arguments.length; i+=2) {
|
|
4214
|
-
comparators.push(getKeyComparator(arguments[i], arguments[i+1]));
|
|
4215
|
-
}
|
|
4216
|
-
arr.sort(function(a, b) {
|
|
4217
|
-
var cmp = 0,
|
|
4218
|
-
i = 0,
|
|
4219
|
-
n = comparators.length;
|
|
4220
|
-
while (i < n && cmp === 0) {
|
|
4221
|
-
cmp = comparators[i](a, b);
|
|
4222
|
-
i++;
|
|
4223
|
-
}
|
|
4224
|
-
return cmp;
|
|
4225
|
-
});
|
|
4226
|
-
return arr;
|
|
4227
|
-
}
|
|
4228
|
-
|
|
4229
|
-
// Sort array of values that can be compared with < > operators (strings, numbers)
|
|
4230
|
-
// null, undefined and NaN are sorted to the end of the array
|
|
4231
|
-
// default order is ascending
|
|
4232
|
-
//
|
|
4233
|
-
function genericSort(arr, ascending) {
|
|
4234
|
-
var compare = getGenericComparator(ascending);
|
|
4235
|
-
Array.prototype.sort.call(arr, compare);
|
|
4236
|
-
return arr;
|
|
4237
|
-
}
|
|
4238
|
-
|
|
4239
|
-
function getSortedIds(arr, asc) {
|
|
4240
|
-
var ids = range(arr.length);
|
|
4241
|
-
sortArrayIndex(ids, arr, asc);
|
|
4242
|
-
return ids;
|
|
4243
|
-
}
|
|
4244
|
-
|
|
4245
|
-
function sortArrayIndex(ids, arr, asc) {
|
|
4246
|
-
var compare = getGenericComparator(asc);
|
|
4247
|
-
ids.sort(function(i, j) {
|
|
4248
|
-
// added i, j comparison to guarantee that sort is stable
|
|
4249
|
-
var cmp = compare(arr[i], arr[j]);
|
|
4250
|
-
return cmp > 0 || cmp === 0 && i > j ? 1 : -1;
|
|
4251
|
-
});
|
|
4252
|
-
}
|
|
4253
|
-
|
|
4254
|
-
function reorderArray(arr, idxs) {
|
|
4255
|
-
var len = idxs.length;
|
|
4256
|
-
var arr2 = [];
|
|
4257
|
-
for (var i=0; i<len; i++) {
|
|
4258
|
-
var idx = idxs[i];
|
|
4259
|
-
if (idx < 0 || idx >= len) error("Out-of-bounds array idx");
|
|
4260
|
-
arr2[i] = arr[idx];
|
|
4261
|
-
}
|
|
4262
|
-
replaceArray(arr, arr2);
|
|
4263
|
-
}
|
|
4264
|
-
|
|
4265
|
-
function getKeyComparator(key, asc) {
|
|
4266
|
-
var compare = getGenericComparator(asc);
|
|
4267
|
-
return function(a, b) {
|
|
4268
|
-
return compare(a[key], b[key]);
|
|
4269
|
-
};
|
|
4270
|
-
}
|
|
4271
|
-
|
|
4272
|
-
function getGenericComparator(asc) {
|
|
4273
|
-
asc = asc !== false;
|
|
4274
|
-
return function(a, b) {
|
|
4275
|
-
var retn = 0;
|
|
4276
|
-
if (b == null) {
|
|
4277
|
-
retn = a == null ? 0 : -1;
|
|
4278
|
-
} else if (a == null) {
|
|
4279
|
-
retn = 1;
|
|
4280
|
-
} else if (a < b) {
|
|
4281
|
-
retn = asc ? -1 : 1;
|
|
4282
|
-
} else if (a > b) {
|
|
4283
|
-
retn = asc ? 1 : -1;
|
|
4284
|
-
} else if (a !== a) {
|
|
4285
|
-
retn = 1;
|
|
4286
|
-
} else if (b !== b) {
|
|
4287
|
-
retn = -1;
|
|
4288
|
-
}
|
|
4289
|
-
return retn;
|
|
4290
|
-
};
|
|
4291
|
-
}
|
|
4292
|
-
|
|
4293
|
-
|
|
4294
|
-
// Generic in-place sort (null, NaN, undefined not handled)
|
|
4295
|
-
function quicksort(arr, asc) {
|
|
4296
|
-
quicksortPartition(arr, 0, arr.length-1);
|
|
4297
|
-
if (asc === false) Array.prototype.reverse.call(arr); // Works with typed arrays
|
|
4298
|
-
return arr;
|
|
4299
|
-
}
|
|
4300
|
-
|
|
4301
|
-
// Moved out of quicksort() (saw >100% speedup in Chrome with deep recursion)
|
|
4302
|
-
function quicksortPartition (a, lo, hi) {
|
|
4303
|
-
var i = lo,
|
|
4304
|
-
j = hi,
|
|
4305
|
-
pivot, tmp;
|
|
4306
|
-
while (i < hi) {
|
|
4307
|
-
pivot = a[lo + hi >> 1]; // avoid n^2 performance on sorted arrays
|
|
4308
|
-
while (i <= j) {
|
|
4309
|
-
while (a[i] < pivot) i++;
|
|
4310
|
-
while (a[j] > pivot) j--;
|
|
4311
|
-
if (i <= j) {
|
|
4312
|
-
tmp = a[i];
|
|
4313
|
-
a[i] = a[j];
|
|
4314
|
-
a[j] = tmp;
|
|
4315
|
-
i++;
|
|
4316
|
-
j--;
|
|
4317
|
-
}
|
|
4318
|
-
}
|
|
4319
|
-
if (lo < j) quicksortPartition(a, lo, j);
|
|
4320
|
-
lo = i;
|
|
4321
|
-
j = hi;
|
|
4322
|
-
}
|
|
4323
|
-
}
|
|
4324
|
-
|
|
4325
|
-
|
|
4326
|
-
function findRankByValue(arr, value) {
|
|
4327
|
-
if (isNaN(value)) return arr.length;
|
|
4328
|
-
var rank = 1;
|
|
4329
|
-
for (var i=0, n=arr.length; i<n; i++) {
|
|
4330
|
-
if (value > arr[i]) rank++;
|
|
4331
|
-
}
|
|
4332
|
-
return rank;
|
|
4333
|
-
}
|
|
4334
|
-
|
|
4335
|
-
function findValueByPct(arr, pct) {
|
|
4336
|
-
var rank = Math.ceil((1-pct) * (arr.length));
|
|
4337
|
-
return findValueByRank(arr, rank);
|
|
4338
|
-
}
|
|
4339
|
-
|
|
4340
|
-
// See http://ndevilla.free.fr/median/median/src/wirth.c
|
|
4341
|
-
// Elements of @arr are reordered
|
|
4342
|
-
//
|
|
4343
|
-
function findValueByRank(arr, rank) {
|
|
4344
|
-
if (!arr.length || rank < 1 || rank > arr.length) error("[findValueByRank()] invalid input");
|
|
4345
|
-
|
|
4346
|
-
rank = clamp(rank | 0, 1, arr.length);
|
|
4347
|
-
var k = rank - 1, // conv. rank to array index
|
|
4348
|
-
n = arr.length,
|
|
4349
|
-
l = 0,
|
|
4350
|
-
m = n - 1,
|
|
4351
|
-
i, j, val, tmp;
|
|
4352
|
-
|
|
4353
|
-
while (l < m) {
|
|
4354
|
-
val = arr[k];
|
|
4355
|
-
i = l;
|
|
4356
|
-
j = m;
|
|
4357
|
-
do {
|
|
4358
|
-
while (arr[i] < val) {i++;}
|
|
4359
|
-
while (val < arr[j]) {j--;}
|
|
4360
|
-
if (i <= j) {
|
|
4361
|
-
tmp = arr[i];
|
|
4362
|
-
arr[i] = arr[j];
|
|
4363
|
-
arr[j] = tmp;
|
|
4364
|
-
i++;
|
|
4365
|
-
j--;
|
|
4366
|
-
}
|
|
4367
|
-
} while (i <= j);
|
|
4368
|
-
if (j < k) l = i;
|
|
4369
|
-
if (k < i) m = j;
|
|
4370
|
-
}
|
|
4371
|
-
return arr[k];
|
|
4372
|
-
}
|
|
4373
|
-
|
|
4374
|
-
//
|
|
4375
|
-
//
|
|
4376
|
-
function findMedian(arr) {
|
|
4377
|
-
var n = arr.length,
|
|
4378
|
-
rank = Math.floor(n / 2) + 1,
|
|
4379
|
-
median = findValueByRank(arr, rank);
|
|
4380
|
-
if ((n & 1) == 0) {
|
|
4381
|
-
median = (median + findValueByRank(arr, rank - 1)) / 2;
|
|
4382
|
-
}
|
|
4383
|
-
return median;
|
|
4384
|
-
}
|
|
4385
|
-
|
|
4386
|
-
|
|
4387
|
-
function mean(arr) {
|
|
4388
|
-
var count = 0,
|
|
4389
|
-
avg = NaN,
|
|
4390
|
-
val;
|
|
4391
|
-
for (var i=0, n=arr.length; i<n; i++) {
|
|
4392
|
-
val = arr[i];
|
|
4393
|
-
if (isNaN(val)) continue;
|
|
4394
|
-
avg = ++count == 1 ? val : val / count + (count - 1) / count * avg;
|
|
4395
|
-
}
|
|
4396
|
-
return avg;
|
|
4397
|
-
}
|
|
4398
|
-
|
|
4399
|
-
|
|
4400
|
-
/*
|
|
4401
|
-
A simplified version of printf formatting
|
|
4402
|
-
Format codes: %[flags][width][.precision]type
|
|
4403
|
-
|
|
4404
|
-
supported flags:
|
|
4405
|
-
+ add '+' before positive numbers
|
|
4406
|
-
0 left-pad with '0'
|
|
4407
|
-
' Add thousands separator
|
|
4408
|
-
width: 1 to many
|
|
4409
|
-
precision: .(1 to many)
|
|
4410
|
-
type:
|
|
4411
|
-
s string
|
|
4412
|
-
di integers
|
|
4413
|
-
f decimal numbers
|
|
4414
|
-
xX hexidecimal (unsigned)
|
|
4415
|
-
% literal '%'
|
|
4416
|
-
|
|
4417
|
-
Examples:
|
|
4418
|
-
code val formatted
|
|
4419
|
-
%+d 1 '+1'
|
|
4420
|
-
%4i 32 ' 32'
|
|
4421
|
-
%04i 32 '0032'
|
|
4422
|
-
%x 255 'ff'
|
|
4423
|
-
%.2f 0.125 '0.13'
|
|
4424
|
-
%'f 1000 '1,000'
|
|
4425
|
-
*/
|
|
4426
|
-
|
|
4427
|
-
// Usage: format(formatString, [values])
|
|
4428
|
-
// Tip: When reusing the same format many times, use formatter() for 5x - 10x better performance
|
|
4429
|
-
//
|
|
4430
|
-
function format(fmt) {
|
|
4431
|
-
var fn = formatter(fmt);
|
|
4432
|
-
var str = fn.apply(null, Array.prototype.slice.call(arguments, 1));
|
|
4433
|
-
return str;
|
|
4434
|
-
}
|
|
4435
|
-
|
|
4436
|
-
function formatValue(val, matches) {
|
|
4437
|
-
var flags = matches[1];
|
|
4438
|
-
var padding = matches[2];
|
|
4439
|
-
var decimals = matches[3] ? parseInt(matches[3].substr(1)) : void 0;
|
|
4440
|
-
var type = matches[4];
|
|
4441
|
-
var isString = type == 's',
|
|
4442
|
-
isHex = type == 'x' || type == 'X',
|
|
4443
|
-
isInt = type == 'd' || type == 'i',
|
|
4444
|
-
isFloat = type == 'f',
|
|
4445
|
-
isNumber = !isString;
|
|
4446
|
-
|
|
4447
|
-
var sign = "",
|
|
4448
|
-
padDigits = 0,
|
|
4449
|
-
isZero = false,
|
|
4450
|
-
isNeg = false;
|
|
4451
|
-
|
|
4452
|
-
var str, padChar, padStr;
|
|
4453
|
-
if (isString) {
|
|
4454
|
-
str = String(val);
|
|
4455
|
-
}
|
|
4456
|
-
else if (isHex) {
|
|
4457
|
-
str = val.toString(16);
|
|
4458
|
-
if (type == 'X')
|
|
4459
|
-
str = str.toUpperCase();
|
|
4460
|
-
}
|
|
4461
|
-
else if (isNumber) {
|
|
4462
|
-
// str = formatNumberForDisplay(val, isInt ? 0 : decimals);
|
|
4463
|
-
str = numToStr(val, decimals);
|
|
4464
|
-
if (str[0] == '-') {
|
|
4465
|
-
isNeg = true;
|
|
4466
|
-
str = str.substr(1);
|
|
4467
|
-
}
|
|
4468
|
-
isZero = parseFloat(str) == 0;
|
|
4469
|
-
if (flags.indexOf("'") != -1 || flags.indexOf(',') != -1) {
|
|
4470
|
-
str = addThousandsSep(str);
|
|
4471
|
-
}
|
|
4472
|
-
if (!isZero) { // BUG: sign is added when num rounds to 0
|
|
4473
|
-
if (isNeg) {
|
|
4474
|
-
sign = "\u2212"; // U+2212
|
|
4475
|
-
} else if (flags.indexOf('+') != -1) {
|
|
4476
|
-
sign = '+';
|
|
4477
|
-
}
|
|
4478
|
-
}
|
|
4479
|
-
}
|
|
4480
|
-
|
|
4481
|
-
if (padding) {
|
|
4482
|
-
var strLen = str.length + sign.length;
|
|
4483
|
-
var minWidth = parseInt(padding, 10);
|
|
4484
|
-
if (strLen < minWidth) {
|
|
4485
|
-
padDigits = minWidth - strLen;
|
|
4486
|
-
padChar = flags.indexOf('0') == -1 ? ' ' : '0';
|
|
4487
|
-
padStr = repeatString(padChar, padDigits);
|
|
4488
|
-
}
|
|
4489
|
-
}
|
|
4490
|
-
|
|
4491
|
-
if (padDigits == 0) {
|
|
4492
|
-
str = sign + str;
|
|
4493
|
-
} else if (padChar == '0') {
|
|
4494
|
-
str = sign + padStr + str;
|
|
4495
|
-
} else {
|
|
4496
|
-
str = padStr + sign + str;
|
|
4497
|
-
}
|
|
4498
|
-
return str;
|
|
4499
|
-
}
|
|
4500
|
-
|
|
4501
|
-
// Get a function for interpolating formatted values into a string.
|
|
4502
|
-
function formatter(fmt) {
|
|
4503
|
-
var codeRxp = /%([\',+0]*)([1-9]?)((?:\.[1-9])?)([sdifxX%])/g;
|
|
4504
|
-
var literals = [],
|
|
4505
|
-
formatCodes = [],
|
|
4506
|
-
startIdx = 0,
|
|
4507
|
-
prefix = "",
|
|
4508
|
-
matches = codeRxp.exec(fmt),
|
|
4509
|
-
literal;
|
|
4510
|
-
|
|
4511
|
-
while (matches) {
|
|
4512
|
-
literal = fmt.substring(startIdx, codeRxp.lastIndex - matches[0].length);
|
|
4513
|
-
if (matches[0] == '%%') {
|
|
4514
|
-
prefix += literal + '%';
|
|
4515
|
-
} else {
|
|
4516
|
-
literals.push(prefix + literal);
|
|
4517
|
-
prefix = '';
|
|
4518
|
-
formatCodes.push(matches);
|
|
4519
|
-
}
|
|
4520
|
-
startIdx = codeRxp.lastIndex;
|
|
4521
|
-
matches = codeRxp.exec(fmt);
|
|
4522
|
-
}
|
|
4523
|
-
literals.push(prefix + fmt.substr(startIdx));
|
|
4524
|
-
|
|
4525
|
-
return function() {
|
|
4526
|
-
var str = literals[0],
|
|
4527
|
-
n = arguments.length;
|
|
4528
|
-
if (n != formatCodes.length) {
|
|
4529
|
-
error("[format()] Data does not match format string; format:", fmt, "data:", arguments);
|
|
4530
|
-
}
|
|
4531
|
-
for (var i=0; i<n; i++) {
|
|
4532
|
-
str += formatValue(arguments[i], formatCodes[i]) + literals[i+1];
|
|
4533
|
-
}
|
|
4534
|
-
return str;
|
|
4535
|
-
};
|
|
4536
|
-
}
|
|
4537
|
-
|
|
4538
|
-
function wildcardToRegExp(name) {
|
|
4539
|
-
var rxp = name.split('*').map(function(str) {
|
|
4540
|
-
return regexEscape(str);
|
|
4541
|
-
}).join('.*');
|
|
4542
|
-
return new RegExp('^' + rxp + '$');
|
|
4543
|
-
}
|
|
4544
|
-
|
|
4545
|
-
function createBuffer(arg, arg2) {
|
|
4546
|
-
if (isInteger(arg)) {
|
|
4547
|
-
return Buffer.allocUnsafe ? Buffer.allocUnsafe(arg) : new Buffer(arg);
|
|
4548
|
-
} else {
|
|
4549
|
-
// check allocUnsafe to make sure Buffer.from() will accept strings (it didn't before Node v5.10)
|
|
4550
|
-
return Buffer.from && Buffer.allocUnsafe ? Buffer.from(arg, arg2) : new Buffer(arg, arg2);
|
|
4551
|
-
}
|
|
4552
|
-
}
|
|
4553
|
-
|
|
4554
|
-
function expandoBuffer(constructor, rate) {
|
|
4555
|
-
var capacity = 0,
|
|
4556
|
-
k = rate >= 1 ? rate : 1.2,
|
|
4557
|
-
buf;
|
|
4558
|
-
return function(size) {
|
|
4559
|
-
if (size > capacity) {
|
|
4560
|
-
capacity = Math.ceil(size * k);
|
|
4561
|
-
buf = constructor ? new constructor(capacity) : createBuffer(capacity);
|
|
4562
|
-
}
|
|
4563
|
-
return buf;
|
|
4564
|
-
};
|
|
4565
|
-
}
|
|
4566
|
-
|
|
4567
|
-
function copyElements(src, i, dest, j, n, rev) {
|
|
4568
|
-
if (src === dest && j > i) error ("copy error");
|
|
4569
|
-
var inc = 1,
|
|
4570
|
-
offs = 0;
|
|
4571
|
-
if (rev) {
|
|
4572
|
-
inc = -1;
|
|
4573
|
-
offs = n - 1;
|
|
4574
|
-
}
|
|
4575
|
-
for (var k=0; k<n; k++, offs += inc) {
|
|
4576
|
-
dest[k + j] = src[i + offs];
|
|
4577
|
-
}
|
|
4578
|
-
}
|
|
4579
|
-
|
|
4580
|
-
function extendBuffer(src, newLen, copyLen) {
|
|
4581
|
-
var len = Math.max(src.length, newLen);
|
|
4582
|
-
var n = copyLen || src.length;
|
|
4583
|
-
var dest = new src.constructor(len);
|
|
4584
|
-
copyElements(src, 0, dest, 0, n);
|
|
4585
|
-
return dest;
|
|
4586
|
-
}
|
|
4587
|
-
|
|
4588
|
-
function mergeNames(name1, name2) {
|
|
4589
|
-
var merged;
|
|
4590
|
-
if (name1 && name2) {
|
|
4591
|
-
merged = findStringPrefix(name1, name2).replace(/[-_]$/, '');
|
|
4592
|
-
}
|
|
4593
|
-
return merged || '';
|
|
4594
|
-
}
|
|
4595
|
-
|
|
4596
|
-
function findStringPrefix(a, b) {
|
|
4597
|
-
var i = 0;
|
|
4598
|
-
for (var n=a.length; i<n; i++) {
|
|
4599
|
-
if (a[i] !== b[i]) break;
|
|
4600
|
-
}
|
|
4601
|
-
return a.substr(0, i);
|
|
4602
|
-
}
|
|
4603
|
-
|
|
4604
|
-
function parsePercent(o) {
|
|
4605
|
-
var str = String(o);
|
|
4606
|
-
var isPct = str.indexOf('%') > 0;
|
|
4607
|
-
var pct;
|
|
4608
|
-
if (isPct) {
|
|
4609
|
-
pct = Number(str.replace('%', '')) / 100;
|
|
4610
|
-
} else {
|
|
4611
|
-
pct = Number(str);
|
|
4612
|
-
}
|
|
4613
|
-
if (!(pct >= 0 && pct <= 1)) {
|
|
4614
|
-
stop(format("Invalid percentage: %s", str));
|
|
4615
|
-
}
|
|
4616
|
-
return pct;
|
|
4617
|
-
}
|
|
4618
|
-
|
|
4619
|
-
function formatVersionedName(name, i) {
|
|
4620
|
-
var suffix = String(i);
|
|
4621
|
-
if (/[0-9]$/.test(name)) {
|
|
4622
|
-
suffix = '-' + suffix;
|
|
4623
|
-
}
|
|
4624
|
-
return name + suffix;
|
|
4625
|
-
}
|
|
4626
|
-
|
|
4627
|
-
function uniqifyNames(names, formatter) {
|
|
4628
|
-
var counts = countValues(names),
|
|
4629
|
-
format = formatter || formatVersionedName,
|
|
4630
|
-
names2 = [];
|
|
4631
|
-
|
|
4632
|
-
names.forEach(function(name) {
|
|
4633
|
-
var i = 0,
|
|
4634
|
-
candidate = name,
|
|
4635
|
-
versionedName;
|
|
4636
|
-
while (
|
|
4637
|
-
names2.indexOf(candidate) > -1 || // candidate name has already been used
|
|
4638
|
-
candidate == name && counts[candidate] > 1 || // duplicate unversioned names
|
|
4639
|
-
candidate != name && counts[candidate] > 0) { // versioned name is a preexisting name
|
|
4640
|
-
i++;
|
|
4641
|
-
versionedName = format(name, i);
|
|
4642
|
-
if (!versionedName || versionedName == candidate) {
|
|
4643
|
-
throw new Error("Naming error"); // catch buggy versioning function
|
|
4644
|
-
}
|
|
4645
|
-
candidate = versionedName;
|
|
4646
|
-
}
|
|
4647
|
-
names2.push(candidate);
|
|
4648
|
-
});
|
|
4649
|
-
return names2;
|
|
4650
|
-
}
|
|
4651
|
-
|
|
4652
|
-
|
|
4653
|
-
// Assume: @raw is string, undefined or null
|
|
4654
|
-
function parseString(raw) {
|
|
4655
|
-
return raw ? raw : "";
|
|
4656
|
-
}
|
|
4657
|
-
|
|
4658
|
-
// Assume: @raw is string, undefined or null
|
|
4659
|
-
// Use null instead of NaN for unparsable values
|
|
4660
|
-
// (in part because if NaN is used, empty strings get converted to "NaN"
|
|
4661
|
-
// when re-exported).
|
|
4662
|
-
function parseNumber(raw) {
|
|
4663
|
-
return parseToNum(raw, cleanNumericString);
|
|
4664
|
-
}
|
|
4665
|
-
|
|
4666
|
-
function parseIntlNumber(raw) {
|
|
4667
|
-
return parseToNum(raw, convertIntlNumString);
|
|
4668
|
-
}
|
|
4669
|
-
|
|
4670
|
-
function parseToNum(raw, clean) {
|
|
4671
|
-
var str = String(raw).trim();
|
|
4672
|
-
var parsed = str ? Number(clean(str)) : NaN;
|
|
4673
|
-
return isNaN(parsed) ? null : parsed;
|
|
4674
|
-
}
|
|
4675
|
-
|
|
4676
|
-
// Remove comma separators from strings
|
|
4677
|
-
function cleanNumericString(str) {
|
|
4678
|
-
return (str.indexOf(',') > 0) ? str.replace(/,([0-9]{3})/g, '$1') : str;
|
|
4679
|
-
}
|
|
4680
|
-
|
|
4681
|
-
function convertIntlNumString(str) {
|
|
4682
|
-
str = str.replace(/[ .]([0-9]{3})/g, '$1');
|
|
4683
|
-
return str.replace(',', '.');
|
|
4684
|
-
}
|
|
4685
|
-
|
|
4686
|
-
function trimQuotes(raw) {
|
|
4687
|
-
var len = raw.length, first, last;
|
|
4688
|
-
if (len >= 2) {
|
|
4689
|
-
first = raw.charAt(0);
|
|
4690
|
-
last = raw.charAt(len-1);
|
|
4691
|
-
if (first == '"' && last == '"' && !raw.includes('","') ||
|
|
4692
|
-
first == "'" && last == "'" && !raw.includes("','")) {
|
|
4693
|
-
return raw.substr(1, len-2);
|
|
4694
|
-
}
|
|
4695
|
-
}
|
|
4696
|
-
return raw;
|
|
4697
|
-
}
|
|
4698
|
-
|
|
4699
|
-
var context = createContext(); // command context (persist for the current command cycle)
|
|
4700
|
-
|
|
4701
|
-
function runningInBrowser() {
|
|
4702
|
-
return typeof window !== 'undefined' && typeof window.document !== 'undefined';
|
|
4703
|
-
}
|
|
4704
|
-
|
|
4705
|
-
function getStateVar(key) {
|
|
4706
|
-
return context[key];
|
|
4707
|
-
}
|
|
4708
|
-
|
|
4709
|
-
function setStateVar(key, val) {
|
|
4710
|
-
context[key] = val;
|
|
4711
|
-
}
|
|
4712
|
-
|
|
4713
|
-
function createContext() {
|
|
4714
|
-
return {
|
|
4715
|
-
DEBUG: false,
|
|
4716
|
-
QUIET: false,
|
|
4717
|
-
VERBOSE: false,
|
|
4718
|
-
defs: {},
|
|
4719
|
-
input_files: []
|
|
4720
|
-
};
|
|
4721
|
-
}
|
|
4722
|
-
|
|
4723
|
-
// Install a new set of context variables, clear them when an async callback is called.
|
|
4724
|
-
// @cb callback function to wrap
|
|
4725
|
-
// returns wrapped callback function
|
|
4726
|
-
function createAsyncContext(cb) {
|
|
4727
|
-
context = createContext();
|
|
4728
|
-
return function() {
|
|
4729
|
-
cb.apply(null, utils.toArray(arguments));
|
|
4730
|
-
// clear context after cb(), so output/errors can be handled in current context
|
|
4731
|
-
context = createContext();
|
|
4732
|
-
};
|
|
4733
|
-
}
|
|
4734
|
-
|
|
4735
|
-
// Save the current context, restore it when an async callback is called
|
|
4736
|
-
// @cb callback function to wrap
|
|
4737
|
-
// returns wrapped callback function
|
|
4738
|
-
function preserveContext(cb) {
|
|
4739
|
-
var ctx = context;
|
|
4740
|
-
return function() {
|
|
4741
|
-
context = ctx;
|
|
4742
|
-
cb.apply(null, utils.toArray(arguments));
|
|
4743
|
-
};
|
|
4744
|
-
}
|
|
4745
|
-
|
|
4746
|
-
var LOGGING = false;
|
|
4747
|
-
var STDOUT = false; // use stdout for status messages
|
|
4748
|
-
|
|
4749
|
-
// These three functions can be reset by GUI using setLoggingFunctions();
|
|
4750
|
-
var _error = function() {
|
|
4751
|
-
var msg = utils.toArray(arguments).join(' ');
|
|
4752
|
-
throw new Error(msg);
|
|
4753
|
-
};
|
|
4754
|
-
|
|
4755
|
-
var _stop = function() {
|
|
4756
|
-
throw new UserError(formatLogArgs(arguments));
|
|
4757
|
-
};
|
|
4758
|
-
|
|
4759
|
-
var _interrupt = function() {
|
|
4760
|
-
throw new NonFatalError(formatLogArgs(arguments));
|
|
4761
|
-
};
|
|
4762
|
-
|
|
4763
|
-
var _message = function() {
|
|
4764
|
-
logArgs(arguments);
|
|
4765
|
-
};
|
|
4766
|
-
|
|
4767
|
-
function enableLogging() {
|
|
4768
|
-
LOGGING = true;
|
|
4769
|
-
}
|
|
4770
|
-
|
|
4771
|
-
function loggingEnabled() {
|
|
4772
|
-
return !!LOGGING;
|
|
4773
|
-
}
|
|
4774
|
-
|
|
4775
|
-
// Handle an unexpected condition (internal error)
|
|
4776
|
-
function error() {
|
|
4777
|
-
_error.apply(null, utils.toArray(arguments));
|
|
4778
|
-
}
|
|
4779
|
-
|
|
4780
|
-
// Handle an error caused by invalid input or misuse of API
|
|
4781
|
-
function stop() {
|
|
4782
|
-
_stop.apply(null, utils.toArray(arguments));
|
|
4783
|
-
}
|
|
4784
|
-
|
|
4785
|
-
function interrupt() {
|
|
4786
|
-
_interrupt.apply(null, utils.toArray(arguments));
|
|
4787
|
-
}
|
|
4788
|
-
|
|
4789
|
-
// Print a status message
|
|
4790
|
-
function message() {
|
|
4791
|
-
_message.apply(null, messageArgs(arguments));
|
|
4792
|
-
}
|
|
4793
|
-
|
|
4794
|
-
// A way for the GUI to replace the CLI logging functions
|
|
4795
|
-
function setLoggingFunctions(message, error, stop) {
|
|
4796
|
-
_message = message;
|
|
4797
|
-
_error = error;
|
|
4798
|
-
_stop = stop;
|
|
4799
|
-
}
|
|
4800
|
-
|
|
4801
|
-
|
|
4802
|
-
// print a message to stdout
|
|
4803
|
-
function print() {
|
|
4804
|
-
STDOUT = true; // tell logArgs() to print to stdout, not stderr
|
|
4805
|
-
message.apply(null, arguments);
|
|
4806
|
-
STDOUT = false;
|
|
4807
|
-
}
|
|
4808
|
-
|
|
4809
|
-
function verbose() {
|
|
4810
|
-
// verbose can be set globally with the -verbose command or separately for each command
|
|
4811
|
-
if (getStateVar('VERBOSE') || getStateVar('verbose')) {
|
|
4812
|
-
message.apply(null, arguments);
|
|
4813
|
-
}
|
|
4814
|
-
}
|
|
4815
|
-
|
|
4816
|
-
function debug() {
|
|
4817
|
-
if (getStateVar('DEBUG') || getStateVar('debug')) {
|
|
4818
|
-
logArgs(arguments);
|
|
4819
|
-
}
|
|
4820
|
-
}
|
|
4821
|
-
|
|
4822
|
-
function printError(err) {
|
|
4823
|
-
var msg;
|
|
4824
|
-
if (!LOGGING) return;
|
|
4825
|
-
if (utils.isString(err)) {
|
|
4826
|
-
err = new UserError(err);
|
|
4827
|
-
}
|
|
4828
|
-
if (err.name == 'NonFatalError') {
|
|
4829
|
-
console.error(messageArgs([err.message]).join(' '));
|
|
4830
|
-
} else if (err.name == 'UserError') {
|
|
4831
|
-
msg = err.message;
|
|
4832
|
-
if (!/Error/.test(msg)) {
|
|
4833
|
-
msg = "Error: " + msg;
|
|
4834
|
-
}
|
|
4835
|
-
console.error(messageArgs([msg]).join(' '));
|
|
4836
|
-
console.error("Run mapshaper -h to view help");
|
|
4837
|
-
} else {
|
|
4838
|
-
// not a user error (i.e. a bug in mapshaper)
|
|
4839
|
-
console.error(err);
|
|
4840
|
-
// throw err;
|
|
4841
|
-
}
|
|
4842
|
-
}
|
|
4843
|
-
|
|
4844
|
-
function UserError(msg) {
|
|
4845
|
-
var err = new Error(msg);
|
|
4846
|
-
err.name = 'UserError';
|
|
4847
|
-
return err;
|
|
4848
|
-
}
|
|
4849
|
-
|
|
4850
|
-
function NonFatalError(msg) {
|
|
4851
|
-
var err = new Error(msg);
|
|
4852
|
-
err.name = 'NonFatalError';
|
|
4853
|
-
return err;
|
|
4854
|
-
}
|
|
4855
|
-
|
|
4856
|
-
function formatColumns(arr, alignments) {
|
|
4857
|
-
var widths = arr.reduce(function(memo, line) {
|
|
4858
|
-
return line.map(function(str, i) {
|
|
4859
|
-
return memo ? Math.max(memo[i], str.length) : str.length;
|
|
4860
|
-
});
|
|
4861
|
-
}, null);
|
|
4862
|
-
return arr.map(function(line) {
|
|
4863
|
-
line = line.map(function(str, i) {
|
|
4864
|
-
var rt = alignments && alignments[i] == 'right';
|
|
4865
|
-
var pad = (rt ? str.padStart : str.padEnd).bind(str);
|
|
4866
|
-
return pad(widths[i], ' ');
|
|
4867
|
-
});
|
|
4868
|
-
return ' ' + line.join(' ');
|
|
4869
|
-
}).join('\n');
|
|
4870
|
-
}
|
|
4871
|
-
|
|
4872
|
-
// Format an array of (preferably short) strings in columns for console logging.
|
|
4873
|
-
function formatStringsAsGrid(arr) {
|
|
4874
|
-
// TODO: variable column width
|
|
4875
|
-
var longest = arr.reduce(function(len, str) {
|
|
4876
|
-
return Math.max(len, str.length);
|
|
4877
|
-
}, 0),
|
|
4878
|
-
colWidth = longest + 2,
|
|
4879
|
-
perLine = Math.floor(80 / colWidth) || 1;
|
|
4880
|
-
return arr.reduce(function(memo, name, i) {
|
|
4881
|
-
var col = i % perLine;
|
|
4882
|
-
if (i > 0 && col === 0) memo += '\n';
|
|
4883
|
-
if (col < perLine - 1) { // right-pad all but rightmost column
|
|
4884
|
-
name = utils.rpad(name, colWidth - 2, ' ');
|
|
4885
|
-
}
|
|
4886
|
-
return memo + ' ' + name;
|
|
4887
|
-
}, '');
|
|
4888
|
-
}
|
|
4889
|
-
|
|
4890
|
-
// expose so GUI can use it
|
|
4891
|
-
function formatLogArgs(args) {
|
|
4892
|
-
return utils.toArray(args).join(' ');
|
|
4893
|
-
}
|
|
4894
|
-
|
|
4895
|
-
function messageArgs(args) {
|
|
4896
|
-
var arr = utils.toArray(args);
|
|
4897
|
-
var cmd = getStateVar('current_command');
|
|
4898
|
-
if (cmd && cmd != 'help') {
|
|
4899
|
-
arr.unshift('[' + cmd + ']');
|
|
4900
|
-
}
|
|
4901
|
-
return arr;
|
|
4902
|
-
}
|
|
4903
|
-
|
|
4904
|
-
function logArgs(args) {
|
|
4905
|
-
if (!LOGGING || getStateVar('QUIET') || !utils.isArrayLike(args)) return;
|
|
4906
|
-
var msg = formatLogArgs(args);
|
|
4907
|
-
if (STDOUT) console.log(msg);
|
|
4908
|
-
else console.error(msg);
|
|
4909
|
-
}
|
|
4910
|
-
|
|
4911
|
-
// Utility functions for both paths and points
|
|
4912
|
-
|
|
4913
|
-
// @shp An element of the layer.shapes array
|
|
4914
|
-
// (may be null, or, depending on layer type, an array of points or an array of arrays of arc ids)
|
|
4915
|
-
function cloneShape(shp) {
|
|
4916
|
-
if (!shp) return null;
|
|
4917
|
-
return shp.map(function(part) {
|
|
4918
|
-
return part.concat();
|
|
4919
|
-
});
|
|
4920
|
-
}
|
|
4921
|
-
|
|
4922
|
-
function cloneShapes(arr) {
|
|
4923
|
-
return utils.isArray(arr) ? arr.map(cloneShape) : null;
|
|
4924
|
-
}
|
|
4925
|
-
|
|
4926
|
-
function forEachShapePart(paths, cb) {
|
|
4927
|
-
editShapeParts(paths, cb);
|
|
4928
|
-
}
|
|
4929
|
-
|
|
4930
|
-
// Updates shapes array in-place.
|
|
4931
|
-
// editPart: callback function
|
|
4932
|
-
function editShapes(shapes, editPart) {
|
|
4933
|
-
for (var i=0, n=shapes.length; i<n; i++) {
|
|
4934
|
-
shapes[i] = editShapeParts(shapes[i], editPart);
|
|
4935
|
-
}
|
|
4936
|
-
}
|
|
4937
|
-
|
|
4938
|
-
// @parts: geometry of a feature (array of paths, array of points or null)
|
|
4939
|
-
// @cb: function(part, i, parts)
|
|
4940
|
-
// If @cb returns an array, it replaces the existing value
|
|
4941
|
-
// If @cb returns null, the path is removed from the feature
|
|
4942
|
-
//
|
|
4943
|
-
function editShapeParts(parts, cb) {
|
|
4944
|
-
if (!parts) return null; // null geometry not edited
|
|
4945
|
-
if (!utils.isArray(parts)) error("Expected an array, received:", parts);
|
|
4946
|
-
var nulls = 0,
|
|
4947
|
-
n = parts.length,
|
|
4948
|
-
retn;
|
|
4949
|
-
|
|
4950
|
-
for (var i=0; i<n; i++) {
|
|
4951
|
-
retn = cb(parts[i], i, parts);
|
|
4952
|
-
if (retn === null) {
|
|
4953
|
-
nulls++;
|
|
4954
|
-
parts[i] = null;
|
|
4955
|
-
} else if (utils.isArray(retn)) {
|
|
4956
|
-
parts[i] = retn;
|
|
4957
|
-
}
|
|
4958
|
-
}
|
|
4959
|
-
if (nulls == n) {
|
|
4960
|
-
return null;
|
|
4961
|
-
} else if (nulls > 0) {
|
|
4962
|
-
return parts.filter(function(part) {return !!part;});
|
|
4963
|
-
} else {
|
|
4964
|
-
return parts;
|
|
4965
|
-
}
|
|
4966
|
-
}
|
|
4967
|
-
|
|
4968
|
-
// Get max number of parts in a single shape from an array of shapes.
|
|
4969
|
-
// Caveat: polygon holes are counted as separate parts.
|
|
4970
|
-
function findMaxPartCount(shapes) {
|
|
4971
|
-
var maxCount = 0, shp;
|
|
4972
|
-
for (var i=0, n=shapes.length; i<n; i++) {
|
|
4973
|
-
shp = shapes[i];
|
|
4974
|
-
if (shp && shp.length > maxCount) {
|
|
4975
|
-
maxCount = shp.length;
|
|
3396
|
+
function getWarnings(lyr, dataset) {
|
|
3397
|
+
var file = internal.getLayerSourceFile(lyr, dataset);
|
|
3398
|
+
var missing = [];
|
|
3399
|
+
var msg;
|
|
3400
|
+
if (utils.endsWith(file, '.shp') && lyr == dataset.layers[0]) {
|
|
3401
|
+
if (!lyr.data) {
|
|
3402
|
+
missing.push('.dbf');
|
|
3403
|
+
}
|
|
3404
|
+
if (!dataset.info.prj && !dataset.info.crs) {
|
|
3405
|
+
missing.push('.prj');
|
|
3406
|
+
}
|
|
4976
3407
|
}
|
|
3408
|
+
if (missing.length) {
|
|
3409
|
+
msg = 'missing ' + missing.join(' and ') + ' data';
|
|
3410
|
+
}
|
|
3411
|
+
return msg;
|
|
4977
3412
|
}
|
|
4978
|
-
return maxCount;
|
|
4979
|
-
}
|
|
4980
|
-
|
|
4981
|
-
// List of encodings supported by iconv-lite:
|
|
4982
|
-
// https://github.com/ashtuchkin/iconv-lite/wiki/Supported-Encodings
|
|
4983
|
-
|
|
4984
|
-
var iconv = require('iconv-lite');
|
|
4985
|
-
var toUtf8 = getNativeEncoder('utf8');
|
|
4986
|
-
var fromUtf8 = getNativeDecoder('utf8');
|
|
4987
|
-
|
|
4988
|
-
// Return list of supported encodings
|
|
4989
|
-
function getEncodings() {
|
|
4990
|
-
iconv.encodingExists('ascii'); // make iconv load its encodings
|
|
4991
|
-
return Object.keys(iconv.encodings);
|
|
4992
|
-
}
|
|
4993
3413
|
|
|
4994
|
-
|
|
4995
|
-
|
|
4996
|
-
stop("Unknown encoding:", enc, "\nRun the -encodings command see a list of supported encodings");
|
|
3414
|
+
function describeSrc(lyr, dataset) {
|
|
3415
|
+
return internal.getLayerSourceFile(lyr, dataset);
|
|
4997
3416
|
}
|
|
4998
|
-
return enc;
|
|
4999
|
-
}
|
|
5000
3417
|
|
|
5001
|
-
function stringsAreAscii(arr) {
|
|
5002
|
-
return stringIsAscii(arr.join(''));
|
|
5003
|
-
}
|
|
5004
3418
|
|
|
5005
|
-
|
|
5006
|
-
|
|
5007
|
-
for (var i=0, n=str.length; i<n; i++) {
|
|
5008
|
-
c = str.charCodeAt(i);
|
|
5009
|
-
if (c >= 128) return false;
|
|
3419
|
+
function isPinnable(lyr) {
|
|
3420
|
+
return internal.layerHasGeometry(lyr) || internal.layerHasFurniture(lyr);
|
|
5010
3421
|
}
|
|
5011
|
-
return true;
|
|
5012
|
-
}
|
|
5013
|
-
|
|
5014
|
-
function encodingIsUtf8(enc) {
|
|
5015
|
-
// treating utf-8 as default
|
|
5016
|
-
return !enc || /^utf-?8$/i.test(String(enc));
|
|
5017
|
-
}
|
|
5018
|
-
|
|
5019
|
-
// Identify the most common encodings that are supersets of ascii at the
|
|
5020
|
-
// single-byte level (meaning that bytes in 0 - 0x7f range must be ascii)
|
|
5021
|
-
// (this allows identifying line breaks and other ascii patterns in buffers)
|
|
5022
|
-
function encodingIsAsciiCompat(enc) {
|
|
5023
|
-
enc = standardizeEncodingName(enc);
|
|
5024
|
-
// gb.* selects the Guo Biao encodings
|
|
5025
|
-
// big5 in not compatible -- second byte starts at 0x40
|
|
5026
|
-
return !enc || /^(win|latin|utf8|ascii|iso88|gb)/.test(enc);
|
|
5027
|
-
}
|
|
5028
3422
|
|
|
5029
|
-
// Ex. convert UTF-8 to utf8
|
|
5030
|
-
function standardizeEncodingName(enc) {
|
|
5031
|
-
return (enc || '').toLowerCase().replace(/[_-]/g, '');
|
|
5032
|
-
}
|
|
5033
3423
|
|
|
5034
|
-
// Similar to Buffer#toString(); tries to speed up utf8 conversion in
|
|
5035
|
-
// web browser (when using browserify Buffer shim)
|
|
5036
|
-
function bufferToString(buf, enc, start, end) {
|
|
5037
|
-
if (start >= 0) {
|
|
5038
|
-
buf = buf.slice(start, end);
|
|
5039
|
-
}
|
|
5040
|
-
return decodeString(buf, enc);
|
|
5041
|
-
}
|
|
5042
3424
|
|
|
5043
|
-
|
|
5044
|
-
|
|
5045
|
-
|
|
5046
|
-
if (enc != 'utf8') {
|
|
5047
|
-
// TODO: support more encodings if TextEncoder is available
|
|
5048
|
-
return null;
|
|
5049
|
-
}
|
|
5050
|
-
if (typeof TextEncoder != 'undefined') {
|
|
5051
|
-
encoder = new TextEncoder(enc);
|
|
3425
|
+
function rowHTML(c1, c2, cname) {
|
|
3426
|
+
return utils.format('<div class="row%s"><div class="col1">%s</div>' +
|
|
3427
|
+
'<div class="col2">%s</div></div>', cname ? ' ' + cname : '', c1, c2);
|
|
5052
3428
|
}
|
|
5053
|
-
return function(str) {
|
|
5054
|
-
// Convert Uint8Array from encoder to Buffer (fix for issue #216)
|
|
5055
|
-
return encoder ? Buffer.from(encoder.encode(str).buffer) : utils.createBuffer(str, enc);
|
|
5056
|
-
};
|
|
5057
3429
|
}
|
|
5058
3430
|
|
|
5059
|
-
function
|
|
5060
|
-
|
|
5061
|
-
|
|
5062
|
-
|
|
5063
|
-
buf = toUtf8(str);
|
|
5064
|
-
} else {
|
|
5065
|
-
buf = iconv.encode(str, enc);
|
|
5066
|
-
}
|
|
5067
|
-
return buf;
|
|
5068
|
-
}
|
|
3431
|
+
function SessionHistory(gui) {
|
|
3432
|
+
var commands = [];
|
|
3433
|
+
// commands that can be ignored when checking for unsaved changes
|
|
3434
|
+
var nonEditingCommands = 'i,target,info,version,verbose,projections,inspect,help,h,encodings,calc'.split(',');
|
|
5069
3435
|
|
|
5070
|
-
|
|
5071
|
-
|
|
5072
|
-
|
|
5073
|
-
|
|
5074
|
-
|
|
5075
|
-
|
|
5076
|
-
|
|
5077
|
-
|
|
5078
|
-
|
|
5079
|
-
}
|
|
5080
|
-
return function(buf) {
|
|
5081
|
-
return decoder ? decoder.decode(buf) : buf.toString(enc);
|
|
3436
|
+
this.unsavedChanges = function() {
|
|
3437
|
+
var cmd, cmdName;
|
|
3438
|
+
for (var i=commands.length - 1; i >= 0; i--) {
|
|
3439
|
+
cmdName = getCommandName(commands[i]);
|
|
3440
|
+
if (cmdName == 'o') break;
|
|
3441
|
+
if (nonEditingCommands.includes(cmdName)) continue;
|
|
3442
|
+
return true;
|
|
3443
|
+
}
|
|
3444
|
+
return false;
|
|
5082
3445
|
};
|
|
5083
|
-
}
|
|
5084
3446
|
|
|
5085
|
-
|
|
5086
|
-
|
|
5087
|
-
|
|
5088
|
-
|
|
5089
|
-
|
|
5090
|
-
|
|
5091
|
-
|
|
5092
|
-
}
|
|
5093
|
-
return str;
|
|
5094
|
-
}
|
|
3447
|
+
this.fileImported = function(file, optStr) {
|
|
3448
|
+
var cmd = '-i ' + file;
|
|
3449
|
+
if (optStr) {
|
|
3450
|
+
cmd += ' ' + optStr;
|
|
3451
|
+
}
|
|
3452
|
+
commands.push(cmd);
|
|
3453
|
+
};
|
|
5095
3454
|
|
|
5096
|
-
|
|
5097
|
-
|
|
5098
|
-
|
|
5099
|
-
|
|
3455
|
+
this.layerRenamed = function(lyr, name) {
|
|
3456
|
+
var currTarget = getCurrentTarget();
|
|
3457
|
+
var layerTarget = getTargetFromLayer(lyr);
|
|
3458
|
+
if (currTarget == layerTarget) {
|
|
3459
|
+
commands.push('-rename-layers ' + name);
|
|
3460
|
+
} else {
|
|
3461
|
+
commands.push('-rename-layers ' + name + ' target=' + layerTarget);
|
|
3462
|
+
commands.push('-target ' + currTarget);
|
|
3463
|
+
}
|
|
3464
|
+
};
|
|
5100
3465
|
|
|
5101
|
-
|
|
5102
|
-
|
|
5103
|
-
|
|
5104
|
-
str = str.substr(1);
|
|
5105
|
-
}
|
|
5106
|
-
return str;
|
|
5107
|
-
}
|
|
3466
|
+
this.consoleCommands = function(str) {
|
|
3467
|
+
commands.push(str); // todo: split commands?
|
|
3468
|
+
};
|
|
5108
3469
|
|
|
5109
|
-
|
|
5110
|
-
|
|
5111
|
-
|
|
5112
|
-
return !/^(_|cs|internal|ibm|isoir|singlebyte|table|[0-9]|l[0-9]|windows)/.test(name);
|
|
5113
|
-
});
|
|
5114
|
-
encodings.sort();
|
|
5115
|
-
print("Supported encodings:\n" + formatStringsAsGrid(encodings));
|
|
5116
|
-
}
|
|
3470
|
+
this.simplificationApplied = function(optStr) {
|
|
3471
|
+
commands.push('-simplify ' + optStr);
|
|
3472
|
+
};
|
|
5117
3473
|
|
|
5118
|
-
|
|
5119
|
-
|
|
5120
|
-
|
|
5121
|
-
|
|
5122
|
-
|
|
5123
|
-
if (
|
|
5124
|
-
|
|
5125
|
-
if (val == o) {
|
|
5126
|
-
// avoid infinite recursion if val is a circular reference, by copying all properties except key
|
|
5127
|
-
val = utils.extend({}, val);
|
|
5128
|
-
delete val[key];
|
|
5129
|
-
}
|
|
5130
|
-
o2[key] = val && val.constructor === Object ? copyRecord(val) : val;
|
|
3474
|
+
this.simplificationRepair = function() {
|
|
3475
|
+
// TODO: improve this... repair does not necessarily apply to most recent
|
|
3476
|
+
// simplification command
|
|
3477
|
+
// consider adding a (hidden) repair command to handle this event
|
|
3478
|
+
var i = indexOfLastCommand('-simplify');
|
|
3479
|
+
if (i > -1) {
|
|
3480
|
+
commands[i] = commands[i].replace(' no-repair', '');
|
|
5131
3481
|
}
|
|
5132
|
-
}
|
|
5133
|
-
return o2;
|
|
5134
|
-
}
|
|
3482
|
+
};
|
|
5135
3483
|
|
|
5136
|
-
|
|
5137
|
-
|
|
5138
|
-
|
|
5139
|
-
|
|
5140
|
-
|
|
5141
|
-
|
|
5142
|
-
} else if (utils.isBoolean(val)) {
|
|
5143
|
-
type = 'boolean';
|
|
5144
|
-
} else if (utils.isDate(val)) {
|
|
5145
|
-
type = 'date';
|
|
5146
|
-
} else if (utils.isObject(val)) {
|
|
5147
|
-
type = 'object';
|
|
5148
|
-
}
|
|
5149
|
-
return type;
|
|
5150
|
-
}
|
|
3484
|
+
this.updateSimplificationPct = function(pct) {
|
|
3485
|
+
var i = indexOfLastCommand('-simplify');
|
|
3486
|
+
if (i > -1) {
|
|
3487
|
+
commands[i] = commands[i].replace(/percentage=[^ ]+/, 'percentage=' + pct);
|
|
3488
|
+
}
|
|
3489
|
+
};
|
|
5151
3490
|
|
|
5152
|
-
|
|
5153
|
-
|
|
5154
|
-
|
|
5155
|
-
|
|
5156
|
-
var fields = findIncompleteFields(records);
|
|
5157
|
-
patchMissingFields(records, fields);
|
|
5158
|
-
}
|
|
3491
|
+
this.dataValueUpdated = function(id, field, value) {
|
|
3492
|
+
var cmd = `-each 'd[${JSON.stringify(field)}] = ${JSON.stringify(value)}' where='this.id == ${id}'`;
|
|
3493
|
+
commands.push(cmd);
|
|
3494
|
+
};
|
|
5159
3495
|
|
|
5160
|
-
|
|
5161
|
-
|
|
5162
|
-
|
|
5163
|
-
|
|
5164
|
-
|
|
5165
|
-
for (j=0; j<keys.length; j++) {
|
|
5166
|
-
counts[keys[j]] = (counts[keys[j]] | 0) + 1;
|
|
3496
|
+
this.layersExported = function(ids, optStr) {
|
|
3497
|
+
var layers = gui.model.getLayers();
|
|
3498
|
+
var cmd = '-o';
|
|
3499
|
+
if (layers.length > 1) {
|
|
3500
|
+
cmd += ' target=' + ids.map(getTargetFromId).join(',');
|
|
5167
3501
|
}
|
|
5168
|
-
|
|
5169
|
-
|
|
5170
|
-
|
|
3502
|
+
if (optStr) {
|
|
3503
|
+
cmd += ' ' + optStr;
|
|
3504
|
+
}
|
|
3505
|
+
commands.push(cmd);
|
|
3506
|
+
};
|
|
5171
3507
|
|
|
5172
|
-
|
|
5173
|
-
|
|
5174
|
-
|
|
5175
|
-
|
|
5176
|
-
|
|
5177
|
-
f = fields[j];
|
|
5178
|
-
if (f in rec === false) {
|
|
5179
|
-
rec[f] = undefined;
|
|
3508
|
+
this.setTargetLayer = function(lyr) {
|
|
3509
|
+
var layers = gui.model.getLayers();
|
|
3510
|
+
if (layers.length > 1) {
|
|
3511
|
+
if (indexOfLastCommand('-target') == commands.length - 1) {
|
|
3512
|
+
commands.pop(); // if last commands was -target, remove it
|
|
5180
3513
|
}
|
|
3514
|
+
commands.push('-target ' + getTargetFromLayer(lyr));
|
|
5181
3515
|
}
|
|
5182
|
-
}
|
|
5183
|
-
}
|
|
3516
|
+
};
|
|
5184
3517
|
|
|
5185
|
-
|
|
5186
|
-
|
|
5187
|
-
|
|
3518
|
+
this.toCommandLineString = function() {
|
|
3519
|
+
var str = commands.join(' \\\n ');
|
|
3520
|
+
return 'mapshaper ' + str;
|
|
3521
|
+
};
|
|
5188
3522
|
|
|
5189
|
-
|
|
5190
|
-
|
|
5191
|
-
|
|
5192
|
-
|
|
5193
|
-
rec = records[i];
|
|
5194
|
-
type = rec ? getValueType(rec[key]) : null;
|
|
5195
|
-
if (type) break;
|
|
3523
|
+
function getCommandName(cmd) {
|
|
3524
|
+
var rxp = /^-([a-z0-9-]+)/;
|
|
3525
|
+
var match = rxp.exec(cmd);
|
|
3526
|
+
return match ? match[1] : null;
|
|
5196
3527
|
}
|
|
5197
|
-
return type;
|
|
5198
|
-
}
|
|
5199
|
-
|
|
5200
|
-
function deleteFields(table, test) {
|
|
5201
|
-
table.getFields().forEach(function(name) {
|
|
5202
|
-
if (test(name)) {
|
|
5203
|
-
table.deleteField(name);
|
|
5204
|
-
}
|
|
5205
|
-
});
|
|
5206
|
-
}
|
|
5207
|
-
|
|
5208
|
-
function isInvalidFieldName(f) {
|
|
5209
|
-
// Reject empty and all-whitespace strings. TODO: consider other criteria
|
|
5210
|
-
return /^\s*$/.test(f);
|
|
5211
|
-
}
|
|
5212
|
-
|
|
5213
|
-
// Resolve name conflicts in field names by appending numbers
|
|
5214
|
-
// @fields Array of field names
|
|
5215
|
-
// @maxLen (optional) Maximum chars in name
|
|
5216
|
-
//
|
|
5217
|
-
function getUniqFieldNames(fields, maxLen, encoding) {
|
|
5218
|
-
var used = {};
|
|
5219
|
-
return fields.map(function(name) {
|
|
5220
|
-
var i = 0,
|
|
5221
|
-
validName;
|
|
5222
|
-
do {
|
|
5223
|
-
validName = encoding && encoding != 'ascii' ?
|
|
5224
|
-
adjustEncodedFieldName(name, maxLen, i, encoding) :
|
|
5225
|
-
adjustFieldName(name, maxLen, i);
|
|
5226
|
-
i++;
|
|
5227
|
-
} while ((validName in used) ||
|
|
5228
|
-
// don't replace an existing valid field name with a truncated name
|
|
5229
|
-
name != validName && utils.contains(fields, validName));
|
|
5230
|
-
used[validName] = true;
|
|
5231
|
-
return validName;
|
|
5232
|
-
});
|
|
5233
|
-
}
|
|
5234
|
-
|
|
5235
|
-
function getFieldValues(records, field) {
|
|
5236
|
-
return records.map(function(rec) {
|
|
5237
|
-
return rec ? rec[field] : undefined;
|
|
5238
|
-
});
|
|
5239
|
-
}
|
|
5240
|
-
|
|
5241
|
-
function getUniqFieldValues(records, field) {
|
|
5242
|
-
var index = {};
|
|
5243
|
-
var values = [];
|
|
5244
|
-
records.forEach(function(rec) {
|
|
5245
|
-
var val = rec[field];
|
|
5246
|
-
if (val in index === false) {
|
|
5247
|
-
index[val] = true;
|
|
5248
|
-
values.push(val);
|
|
5249
|
-
}
|
|
5250
|
-
});
|
|
5251
|
-
return values;
|
|
5252
|
-
}
|
|
5253
3528
|
|
|
5254
|
-
|
|
5255
|
-
|
|
5256
|
-
var name2, suff;
|
|
5257
|
-
maxLen = maxLen || 256;
|
|
5258
|
-
if (!i) {
|
|
5259
|
-
name2 = name.substr(0, maxLen);
|
|
5260
|
-
} else {
|
|
5261
|
-
suff = String(i);
|
|
5262
|
-
if (suff.length == 1) {
|
|
5263
|
-
suff = '_' + suff;
|
|
5264
|
-
}
|
|
5265
|
-
name2 = name.substr(0, maxLen - suff.length) + suff;
|
|
3529
|
+
function getCurrentTarget() {
|
|
3530
|
+
return getTargetFromLayer(gui.model.getActiveLayer().layer);
|
|
5266
3531
|
}
|
|
5267
|
-
return name2;
|
|
5268
|
-
}
|
|
5269
3532
|
|
|
5270
|
-
|
|
5271
|
-
|
|
5272
|
-
|
|
5273
|
-
|
|
5274
|
-
var buf = encodeString(name2, encoding);
|
|
5275
|
-
if (buf.length > (maxLen || 256)) {
|
|
5276
|
-
name = name.substr(0, name.length - 1);
|
|
5277
|
-
return adjustEncodedFieldName(name, maxLen, i, encoding);
|
|
3533
|
+
function indexOfLastCommand(cmd) {
|
|
3534
|
+
return commands.reduce(function(memo, str, i) {
|
|
3535
|
+
return str.indexOf(cmd) === 0 ? i : memo;
|
|
3536
|
+
}, -1);
|
|
5278
3537
|
}
|
|
5279
|
-
return name2;
|
|
5280
|
-
}
|
|
5281
3538
|
|
|
5282
|
-
|
|
5283
|
-
|
|
5284
|
-
|
|
5285
|
-
return a.toLowerCase() < b.toLowerCase() ? -1 : 1;
|
|
5286
|
-
});
|
|
3539
|
+
function getTargetFromId(id) {
|
|
3540
|
+
var layers = gui.model.getLayers();
|
|
3541
|
+
return getTargetFromLayer(layers[id - 1].layer);
|
|
5287
3542
|
}
|
|
5288
|
-
return arr;
|
|
5289
|
-
}
|
|
5290
3543
|
|
|
5291
|
-
|
|
5292
|
-
|
|
5293
|
-
|
|
3544
|
+
function getTargetFromLayer(lyr) {
|
|
3545
|
+
var id = internal.getLayerTargetId(gui.model, lyr);
|
|
3546
|
+
return internal.formatOptionValue(id);
|
|
5294
3547
|
}
|
|
5295
|
-
return null;
|
|
5296
3548
|
}
|
|
5297
3549
|
|
|
5298
|
-
|
|
5299
|
-
|
|
5300
|
-
|
|
5301
|
-
|
|
5302
|
-
|
|
3550
|
+
// import { cloneShape } from '../paths/mapshaper-shape-utils';
|
|
3551
|
+
// import { copyRecord } from '../datatable/mapshaper-data-utils';
|
|
3552
|
+
var snapVerticesToPoint$1 = internal.snapVerticesToPoint;
|
|
3553
|
+
var cloneShape = internal.cloneShape;
|
|
3554
|
+
var copyRecord = internal.copyRecord;
|
|
5303
3555
|
|
|
5304
3556
|
function Undo(gui) {
|
|
5305
3557
|
var history, offset, stashedUndo;
|
|
@@ -5311,9 +3563,6 @@
|
|
|
5311
3563
|
offset = 0;
|
|
5312
3564
|
}
|
|
5313
3565
|
|
|
5314
|
-
function refreshMap() {
|
|
5315
|
-
gui.dispatchEvent('undo_redo');
|
|
5316
|
-
}
|
|
5317
3566
|
|
|
5318
3567
|
function isUndoEvt(e) {
|
|
5319
3568
|
return (e.ctrlKey || e.metaKey) && !e.shiftKey && e.key == 'z';
|
|
@@ -5361,6 +3610,28 @@
|
|
|
5361
3610
|
this.addHistoryState(stashedUndo, redo);
|
|
5362
3611
|
}, this);
|
|
5363
3612
|
|
|
3613
|
+
// undo/redo data editing
|
|
3614
|
+
// TODO: consider setting selected feature to the undo/redo target feature
|
|
3615
|
+
//
|
|
3616
|
+
gui.on('data_preupdate', function(e) {
|
|
3617
|
+
stashedUndo = this.makeDataSetter(e.FID);
|
|
3618
|
+
}, this);
|
|
3619
|
+
|
|
3620
|
+
gui.on('data_postupdate', function(e) {
|
|
3621
|
+
var redo = this.makeDataSetter(e.FID);
|
|
3622
|
+
this.addHistoryState(stashedUndo, redo);
|
|
3623
|
+
}, this);
|
|
3624
|
+
|
|
3625
|
+
// undo/redo vertex dragging
|
|
3626
|
+
gui.on('vertex_dragstart', function(e) {
|
|
3627
|
+
stashedUndo = this.makeVertexSetter(e.FID, e.vertex_ids);
|
|
3628
|
+
}, this);
|
|
3629
|
+
|
|
3630
|
+
gui.on('vertex_dragend', function(e) {
|
|
3631
|
+
var redo = this.makeVertexSetter(e.FID, e.vertex_ids);
|
|
3632
|
+
this.addHistoryState(stashedUndo, redo);
|
|
3633
|
+
}, this);
|
|
3634
|
+
|
|
5364
3635
|
this.clear = function() {
|
|
5365
3636
|
reset();
|
|
5366
3637
|
};
|
|
@@ -5373,7 +3644,6 @@
|
|
|
5373
3644
|
};
|
|
5374
3645
|
};
|
|
5375
3646
|
|
|
5376
|
-
|
|
5377
3647
|
this.makeDataSetter = function(id) {
|
|
5378
3648
|
var target = gui.model.getActiveLayer();
|
|
5379
3649
|
var rec = copyRecord(target.layer.data.getRecordAt(id));
|
|
@@ -5383,6 +3653,14 @@
|
|
|
5383
3653
|
};
|
|
5384
3654
|
};
|
|
5385
3655
|
|
|
3656
|
+
this.makeVertexSetter = function(fid, ids) {
|
|
3657
|
+
var target = gui.model.getActiveLayer();
|
|
3658
|
+
var arcs = target.dataset.arcs;
|
|
3659
|
+
var p = internal.getVertexCoords(ids[0], arcs);
|
|
3660
|
+
return function() {
|
|
3661
|
+
snapVerticesToPoint$1(ids, p, arcs, true);
|
|
3662
|
+
};
|
|
3663
|
+
};
|
|
5386
3664
|
|
|
5387
3665
|
this.addHistoryState = function(undo, redo) {
|
|
5388
3666
|
if (offset > 0) {
|
|
@@ -5397,7 +3675,7 @@
|
|
|
5397
3675
|
if (item) {
|
|
5398
3676
|
offset++;
|
|
5399
3677
|
item.undo();
|
|
5400
|
-
|
|
3678
|
+
gui.dispatchEvent('map-needs-refresh');
|
|
5401
3679
|
}
|
|
5402
3680
|
};
|
|
5403
3681
|
|
|
@@ -5406,7 +3684,7 @@
|
|
|
5406
3684
|
offset--;
|
|
5407
3685
|
var item = getHistoryItem();
|
|
5408
3686
|
item.redo();
|
|
5409
|
-
|
|
3687
|
+
gui.dispatchEvent('map-needs-refresh');
|
|
5410
3688
|
};
|
|
5411
3689
|
|
|
5412
3690
|
function getHistoryItem() {
|
|
@@ -5513,7 +3791,7 @@
|
|
|
5513
3791
|
};
|
|
5514
3792
|
}
|
|
5515
3793
|
|
|
5516
|
-
utils
|
|
3794
|
+
utils.inherit(ModeSwitcher, EventDispatcher);
|
|
5517
3795
|
|
|
5518
3796
|
function KeyboardEvents(gui) {
|
|
5519
3797
|
var self = this;
|
|
@@ -5541,12 +3819,13 @@
|
|
|
5541
3819
|
};
|
|
5542
3820
|
}
|
|
5543
3821
|
|
|
5544
|
-
utils
|
|
3822
|
+
utils.inherit(KeyboardEvents, EventDispatcher);
|
|
5545
3823
|
|
|
5546
3824
|
function InteractionMode(gui) {
|
|
5547
3825
|
|
|
5548
3826
|
var menus = {
|
|
5549
3827
|
standard: ['info', 'selection', 'data', 'box'],
|
|
3828
|
+
polygons: ['info', 'selection', 'data', 'box', 'vertices'],
|
|
5550
3829
|
lines: ['info', 'selection', 'data', 'box', 'vertices'],
|
|
5551
3830
|
table: ['info', 'selection', 'data'],
|
|
5552
3831
|
labels: ['info', 'selection', 'data', 'box', 'labels', 'location'],
|
|
@@ -5658,6 +3937,9 @@
|
|
|
5658
3937
|
if (internal.layerHasPaths(o.layer) && o.layer.geometry_type == 'polyline') {
|
|
5659
3938
|
return menus.lines;
|
|
5660
3939
|
}
|
|
3940
|
+
if (internal.layerHasPaths(o.layer) && o.layer.geometry_type == 'polygon') {
|
|
3941
|
+
return menus.polygons;
|
|
3942
|
+
}
|
|
5661
3943
|
return menus.standard;
|
|
5662
3944
|
}
|
|
5663
3945
|
|
|
@@ -5730,7 +4012,9 @@
|
|
|
5730
4012
|
}
|
|
5731
4013
|
|
|
5732
4014
|
function onModeChange() {
|
|
5733
|
-
|
|
4015
|
+
var mode = getInteractionMode();
|
|
4016
|
+
gui.state.interaction_mode = mode;
|
|
4017
|
+
gui.dispatchEvent('interaction_mode_change', {mode: mode});
|
|
5734
4018
|
}
|
|
5735
4019
|
|
|
5736
4020
|
// Update button highlight and selected menu item highlight (if any)
|
|
@@ -5757,7 +4041,7 @@
|
|
|
5757
4041
|
function Model(gui) {
|
|
5758
4042
|
var self = new internal.Catalog();
|
|
5759
4043
|
var deleteLayer = self.deleteLayer;
|
|
5760
|
-
utils
|
|
4044
|
+
utils.extend(self, EventDispatcher.prototype);
|
|
5761
4045
|
|
|
5762
4046
|
// override Catalog method (so -drop command will work in web console)
|
|
5763
4047
|
self.deleteLayer = function(lyr, dataset) {
|
|
@@ -5791,7 +4075,7 @@
|
|
|
5791
4075
|
if (flags.select) {
|
|
5792
4076
|
self.dispatchEvent('select', active);
|
|
5793
4077
|
}
|
|
5794
|
-
self.dispatchEvent('update', utils
|
|
4078
|
+
self.dispatchEvent('update', utils.extend({flags: flags}, active));
|
|
5795
4079
|
};
|
|
5796
4080
|
|
|
5797
4081
|
self.selectLayer = function(lyr, dataset) {
|
|
@@ -5826,7 +4110,7 @@
|
|
|
5826
4110
|
} else if (geoType == 'polygon') {
|
|
5827
4111
|
test = polygonTest;
|
|
5828
4112
|
} else {
|
|
5829
|
-
error
|
|
4113
|
+
error("Unexpected geometry type:", geoType);
|
|
5830
4114
|
}
|
|
5831
4115
|
return test;
|
|
5832
4116
|
|
|
@@ -5893,7 +4177,7 @@
|
|
|
5893
4177
|
for (var i=0; i<cands.length; i++) {
|
|
5894
4178
|
cands[i].dist = geom.getPointToShapeDistance(x, y, cands[i].shape, arcs);
|
|
5895
4179
|
}
|
|
5896
|
-
utils
|
|
4180
|
+
utils.sortOn(cands, 'dist');
|
|
5897
4181
|
}
|
|
5898
4182
|
|
|
5899
4183
|
function pointTest(x, y) {
|
|
@@ -5919,7 +4203,7 @@
|
|
|
5919
4203
|
}
|
|
5920
4204
|
});
|
|
5921
4205
|
// console.log(hitThreshold, bullseye);
|
|
5922
|
-
return utils
|
|
4206
|
+
return utils.uniq(hits); // multipoint features can register multiple hits
|
|
5923
4207
|
}
|
|
5924
4208
|
|
|
5925
4209
|
function getRadiusFunction(style) {
|
|
@@ -6126,9 +4410,9 @@
|
|
|
6126
4410
|
};
|
|
6127
4411
|
var svgData = svgTest(e); // null or a data object
|
|
6128
4412
|
if (svgData) { // mouse is over an SVG symbol
|
|
6129
|
-
utils
|
|
4413
|
+
utils.extend(data, svgData);
|
|
6130
4414
|
// placing symbol id in front of any other hits
|
|
6131
|
-
data.ids = utils
|
|
4415
|
+
data.ids = utils.uniq([svgData.targetId].concat(data.ids));
|
|
6132
4416
|
}
|
|
6133
4417
|
data.id = data.ids.length > 0 ? data.ids[0] : -1;
|
|
6134
4418
|
return data;
|
|
@@ -6161,7 +4445,7 @@
|
|
|
6161
4445
|
}
|
|
6162
4446
|
|
|
6163
4447
|
// ignore keypress if no feature is selected or user is editing text
|
|
6164
|
-
if (pinnedId() == -1 || GUI.
|
|
4448
|
+
if (pinnedId() == -1 || GUI.textIsSelected()) return;
|
|
6165
4449
|
|
|
6166
4450
|
if (e.keyCode == 37 || e.keyCode == 39) {
|
|
6167
4451
|
// L/R arrow keys
|
|
@@ -6229,8 +4513,8 @@
|
|
|
6229
4513
|
|
|
6230
4514
|
self.addSelectionIds = function(ids) {
|
|
6231
4515
|
turnOn('selection');
|
|
6232
|
-
selectionIds = utils
|
|
6233
|
-
ids = utils
|
|
4516
|
+
selectionIds = utils.uniq(selectionIds.concat(ids));
|
|
4517
|
+
ids = utils.uniq(storedData.ids.concat(ids));
|
|
6234
4518
|
updateSelectionState({ids: ids});
|
|
6235
4519
|
};
|
|
6236
4520
|
|
|
@@ -6319,6 +4603,7 @@
|
|
|
6319
4603
|
|
|
6320
4604
|
// Hits are re-detected on 'hover' (if hit detection is active)
|
|
6321
4605
|
mouse.on('hover', function(e) {
|
|
4606
|
+
handlePointerEvent(e);
|
|
6322
4607
|
if (storedData.pinned || !hitTest || !active) return;
|
|
6323
4608
|
if (e.hover && isOverMap(e)) {
|
|
6324
4609
|
// mouse is hovering directly over map area -- update hit detection
|
|
@@ -6388,7 +4673,7 @@
|
|
|
6388
4673
|
|
|
6389
4674
|
function toggleId(id, ids) {
|
|
6390
4675
|
if (ids.indexOf(id) > -1) {
|
|
6391
|
-
return utils
|
|
4676
|
+
return utils.difference(ids, [id]);
|
|
6392
4677
|
}
|
|
6393
4678
|
return [id].concat(ids);
|
|
6394
4679
|
}
|
|
@@ -6440,9 +4725,9 @@
|
|
|
6440
4725
|
// d: event data (may be a pointer event object, an ordinary object or null)
|
|
6441
4726
|
function triggerHitEvent(type, d) {
|
|
6442
4727
|
// Merge stored hit data into the event data
|
|
6443
|
-
var eventData = utils
|
|
4728
|
+
var eventData = utils.extend({mode: interactionMode}, d || {}, storedData);
|
|
6444
4729
|
if (transientIds.length) {
|
|
6445
|
-
eventData.ids = utils
|
|
4730
|
+
eventData.ids = utils.uniq(transientIds.concat(eventData.ids || []));
|
|
6446
4731
|
}
|
|
6447
4732
|
self.dispatchEvent(type, eventData);
|
|
6448
4733
|
}
|
|
@@ -6576,7 +4861,7 @@
|
|
|
6576
4861
|
}
|
|
6577
4862
|
}
|
|
6578
4863
|
|
|
6579
|
-
utils
|
|
4864
|
+
utils.inherit(Timer, EventDispatcher);
|
|
6580
4865
|
|
|
6581
4866
|
function Tween(ease) {
|
|
6582
4867
|
var self = this,
|
|
@@ -6598,7 +4883,7 @@
|
|
|
6598
4883
|
}
|
|
6599
4884
|
}
|
|
6600
4885
|
|
|
6601
|
-
utils
|
|
4886
|
+
utils.inherit(Tween, EventDispatcher);
|
|
6602
4887
|
|
|
6603
4888
|
Tween.sineInOut = function(n) {
|
|
6604
4889
|
return 0.5 - Math.cos(n * Math.PI) / 2;
|
|
@@ -6666,7 +4951,7 @@
|
|
|
6666
4951
|
update();
|
|
6667
4952
|
}
|
|
6668
4953
|
|
|
6669
|
-
utils
|
|
4954
|
+
utils.inherit(ElementPosition, EventDispatcher);
|
|
6670
4955
|
|
|
6671
4956
|
function MouseWheelDirection() {
|
|
6672
4957
|
var prevTime = 0;
|
|
@@ -6764,13 +5049,13 @@
|
|
|
6764
5049
|
// Decelerate towards the end of the sustain interval (for smoother zooming)
|
|
6765
5050
|
multiplier *= Tween.quadraticOut(1 - fadeFactor);
|
|
6766
5051
|
}
|
|
6767
|
-
obj = utils
|
|
5052
|
+
obj = utils.extend({direction: wheelDirection, multiplier: multiplier}, mouse.mouseData());
|
|
6768
5053
|
self.dispatchEvent('mousewheel', obj);
|
|
6769
5054
|
}
|
|
6770
5055
|
}
|
|
6771
5056
|
}
|
|
6772
5057
|
|
|
6773
|
-
utils
|
|
5058
|
+
utils.inherit(MouseWheel, EventDispatcher);
|
|
6774
5059
|
|
|
6775
5060
|
|
|
6776
5061
|
function MouseArea(element, pos) {
|
|
@@ -6824,7 +5109,7 @@
|
|
|
6824
5109
|
};
|
|
6825
5110
|
|
|
6826
5111
|
this.mouseData = function() {
|
|
6827
|
-
return utils
|
|
5112
|
+
return utils.extend({}, _prevEvt);
|
|
6828
5113
|
};
|
|
6829
5114
|
|
|
6830
5115
|
function onAreaDown(e) {
|
|
@@ -6883,7 +5168,7 @@
|
|
|
6883
5168
|
dragX: evt.pageX - _downEvt.pageX,
|
|
6884
5169
|
dragY: evt.pageY - _downEvt.pageY
|
|
6885
5170
|
};
|
|
6886
|
-
_self.dispatchEvent('drag', utils
|
|
5171
|
+
_self.dispatchEvent('drag', utils.extend(obj, evt));
|
|
6887
5172
|
} else {
|
|
6888
5173
|
_self.dispatchEvent('hover', evt);
|
|
6889
5174
|
}
|
|
@@ -6915,7 +5200,7 @@
|
|
|
6915
5200
|
}
|
|
6916
5201
|
}
|
|
6917
5202
|
|
|
6918
|
-
utils
|
|
5203
|
+
utils.inherit(MouseArea, EventDispatcher);
|
|
6919
5204
|
|
|
6920
5205
|
function initVariableClick(node, cb) {
|
|
6921
5206
|
var downEvent = null;
|
|
@@ -7062,7 +5347,7 @@
|
|
|
7062
5347
|
maxScale = 4,
|
|
7063
5348
|
minTime = 100,
|
|
7064
5349
|
maxTime = 800,
|
|
7065
|
-
time = utils
|
|
5350
|
+
time = utils.clamp(clickTime || 200, minTime, maxTime),
|
|
7066
5351
|
k = (time - minTime) / (maxTime - minTime),
|
|
7067
5352
|
scale = minScale + k * (maxScale - minScale);
|
|
7068
5353
|
return 1 + scale * zoomScaleMultiplier;
|
|
@@ -7289,7 +5574,7 @@
|
|
|
7289
5574
|
rows = 0;
|
|
7290
5575
|
// self.hide(); // clean up if panel is already open
|
|
7291
5576
|
el.empty(); // clean up if panel is already open
|
|
7292
|
-
utils
|
|
5577
|
+
utils.forEachProperty(rec, function(v, k) {
|
|
7293
5578
|
var type;
|
|
7294
5579
|
// missing GeoJSON fields are set to undefined on import; skip these
|
|
7295
5580
|
if (v !== undefined) {
|
|
@@ -7317,7 +5602,7 @@
|
|
|
7317
5602
|
// Some individual features can have undefined values for some or all of
|
|
7318
5603
|
// their data properties (properties are set to undefined when an input JSON file
|
|
7319
5604
|
// has inconsistent fields, or after force-merging layers with inconsistent fields).
|
|
7320
|
-
el.html(utils
|
|
5605
|
+
el.html(utils.format('<div class="note">This %s is missing attribute data.</div>',
|
|
7321
5606
|
table && table.getFields().length > 0 ? 'feature': 'layer'));
|
|
7322
5607
|
}
|
|
7323
5608
|
}
|
|
@@ -7328,7 +5613,7 @@
|
|
|
7328
5613
|
var str = formatInspectorValue(val, type);
|
|
7329
5614
|
var cell = El('tr')
|
|
7330
5615
|
.appendTo(table)
|
|
7331
|
-
.html(utils
|
|
5616
|
+
.html(utils.format(rowHtml, key, utils.htmlEscape(str)))
|
|
7332
5617
|
.findChild('.value');
|
|
7333
5618
|
setFieldClass(cell, val, type);
|
|
7334
5619
|
if (editable) {
|
|
@@ -7337,7 +5622,7 @@
|
|
|
7337
5622
|
}
|
|
7338
5623
|
|
|
7339
5624
|
function setFieldClass(el, val, type) {
|
|
7340
|
-
var isNum = type ? type == 'number' : utils
|
|
5625
|
+
var isNum = type ? type == 'number' : utils.isNumber(val);
|
|
7341
5626
|
var isNully = val === undefined || val === null || val !== val;
|
|
7342
5627
|
var isEmpty = val === '';
|
|
7343
5628
|
el.classed('num-field', isNum);
|
|
@@ -7363,7 +5648,9 @@
|
|
|
7363
5648
|
} else {
|
|
7364
5649
|
// field content has changed
|
|
7365
5650
|
strval = strval2;
|
|
5651
|
+
gui.dispatchEvent('data_preupdate', {FID: currId}); // for undo/redo
|
|
7366
5652
|
rec[key] = val2;
|
|
5653
|
+
gui.dispatchEvent('data_postupdate', {FID: currId});
|
|
7367
5654
|
input.value(strval);
|
|
7368
5655
|
setFieldClass(el, val2, type);
|
|
7369
5656
|
self.dispatchEvent('update', {field: key, value: val2, id: currId});
|
|
@@ -7375,7 +5662,7 @@
|
|
|
7375
5662
|
function formatInspectorValue(val, type) {
|
|
7376
5663
|
var str;
|
|
7377
5664
|
if (type == 'date') {
|
|
7378
|
-
str = utils
|
|
5665
|
+
str = utils.formatDateISO(val);
|
|
7379
5666
|
} else if (type == 'object') {
|
|
7380
5667
|
str = val ? JSON.stringify(val) : "";
|
|
7381
5668
|
} else {
|
|
@@ -7574,11 +5861,13 @@
|
|
|
7574
5861
|
// handle either numeric strings or numbers in fields
|
|
7575
5862
|
function applyDelta(rec, key, delta) {
|
|
7576
5863
|
var currVal = rec[key];
|
|
7577
|
-
var isString = utils
|
|
5864
|
+
var isString = utils.isString(currVal);
|
|
7578
5865
|
var newVal = (+currVal + delta) || 0;
|
|
7579
5866
|
rec[key] = isString ? String(newVal) : newVal;
|
|
7580
5867
|
}
|
|
7581
5868
|
|
|
5869
|
+
var snapVerticesToPoint = internal.snapVerticesToPoint;
|
|
5870
|
+
|
|
7582
5871
|
function getDisplayCoordsById(id, layer, ext) {
|
|
7583
5872
|
var coords = getPointCoordsById(id, layer);
|
|
7584
5873
|
return ext.translateCoords(coords[0], coords[1]);
|
|
@@ -7599,7 +5888,7 @@
|
|
|
7599
5888
|
}
|
|
7600
5889
|
|
|
7601
5890
|
|
|
7602
|
-
function
|
|
5891
|
+
function InteractiveEditor(gui, ext, hit) {
|
|
7603
5892
|
// var targetTextNode; // text node currently being dragged
|
|
7604
5893
|
var dragging = false;
|
|
7605
5894
|
var activeRecord;
|
|
@@ -7681,6 +5970,7 @@
|
|
|
7681
5970
|
triggerGlobalEvent('symbol_dragstart', e);
|
|
7682
5971
|
startDragging();
|
|
7683
5972
|
} else if (vertexEditingEnabled()) {
|
|
5973
|
+
onVertexDragStart(e);
|
|
7684
5974
|
triggerGlobalEvent('vertex_dragstart', e);
|
|
7685
5975
|
startDragging();
|
|
7686
5976
|
}
|
|
@@ -7704,7 +5994,8 @@
|
|
|
7704
5994
|
triggerGlobalEvent('label_dragend', e);
|
|
7705
5995
|
stopDragging();
|
|
7706
5996
|
} else if (vertexEditingEnabled()) {
|
|
7707
|
-
|
|
5997
|
+
// kludge to get dataset to recalculate internal bounding boxes
|
|
5998
|
+
hit.getHitTarget().arcs.transformPoints(function() {});
|
|
7708
5999
|
triggerGlobalEvent('vertex_dragend', e);
|
|
7709
6000
|
stopDragging();
|
|
7710
6001
|
}
|
|
@@ -7712,42 +6003,62 @@
|
|
|
7712
6003
|
|
|
7713
6004
|
hit.on('click', function(e) {
|
|
7714
6005
|
if (labelEditingEnabled()) {
|
|
6006
|
+
var target = hit.getHitTarget();
|
|
7715
6007
|
onLabelClick(e);
|
|
7716
6008
|
}
|
|
7717
6009
|
});
|
|
7718
6010
|
|
|
6011
|
+
// TODO: highlight hit vertex in path edit mode
|
|
6012
|
+
if (false) hit.on('hover', function(e) {
|
|
6013
|
+
if (vertexEditingEnabled() && !dragging) {
|
|
6014
|
+
onVertexHover(e);
|
|
6015
|
+
}
|
|
6016
|
+
}, null, 100);
|
|
6017
|
+
|
|
6018
|
+
function onVertexHover(e) {
|
|
6019
|
+
// hovering in vertex edit mode: find vertex insertion point
|
|
6020
|
+
var target = hit.getHitTarget();
|
|
6021
|
+
var shp = target.layer.shapes[e.id];
|
|
6022
|
+
var p = ext.translatePixelCoords(e.x, e.y);
|
|
6023
|
+
var o = internal.findInsertionPoint(p, shp, target.arcs, ext.getPixelSize());
|
|
6024
|
+
}
|
|
6025
|
+
|
|
6026
|
+
|
|
6027
|
+
function getVertexEventData(e) {
|
|
6028
|
+
return {
|
|
6029
|
+
FID: activeId,
|
|
6030
|
+
vertexIds: activeVertexIds
|
|
6031
|
+
};
|
|
6032
|
+
}
|
|
7719
6033
|
|
|
7720
6034
|
function onLocationDrag(e) {
|
|
7721
6035
|
var lyr = hit.getHitTarget().layer;
|
|
7722
|
-
var p = getPointCoordsById(e.id,
|
|
6036
|
+
var p = getPointCoordsById(e.id, lyr);
|
|
7723
6037
|
if (!p) return;
|
|
7724
6038
|
var diff = translateDeltaDisplayCoords(e.dx, e.dy, ext);
|
|
7725
6039
|
p[0] += diff[0];
|
|
7726
6040
|
p[1] += diff[1];
|
|
7727
|
-
|
|
6041
|
+
triggerRedraw();
|
|
7728
6042
|
triggerGlobalEvent('symbol_drag', e);
|
|
7729
6043
|
}
|
|
7730
6044
|
|
|
7731
|
-
function
|
|
6045
|
+
function onVertexDragStart(e) {
|
|
7732
6046
|
var target = hit.getHitTarget();
|
|
6047
|
+
var shp = target.layer.shapes[e.id];
|
|
7733
6048
|
var p = ext.translatePixelCoords(e.x, e.y);
|
|
7734
|
-
|
|
7735
|
-
|
|
7736
|
-
|
|
6049
|
+
activeVertexIds = internal.findNearestVertices(p, shp, target.arcs);
|
|
6050
|
+
activeId = e.id;
|
|
6051
|
+
}
|
|
6052
|
+
|
|
6053
|
+
function onVertexDrag(e) {
|
|
6054
|
+
var target = hit.getHitTarget();
|
|
7737
6055
|
if (!activeVertexIds) return; // ignore error condition
|
|
6056
|
+
var p = ext.translatePixelCoords(e.x, e.y);
|
|
7738
6057
|
if (gui.keyboard.shiftIsPressed()) {
|
|
7739
6058
|
internal.snapPointToArcEndpoint(p, activeVertexIds, target.arcs);
|
|
7740
6059
|
}
|
|
7741
|
-
activeVertexIds.
|
|
7742
|
-
|
|
7743
|
-
});
|
|
7744
|
-
self.dispatchEvent('location_change'); // signal map to redraw
|
|
7745
|
-
}
|
|
7746
|
-
|
|
7747
|
-
function onVertexDragEnd(e) {
|
|
7748
|
-
// kludge to get dataset to recalculate internal bounding boxes
|
|
7749
|
-
hit.getHitTarget().arcs.transformPoints(function() {});
|
|
7750
|
-
activeVertexIds = null;
|
|
6060
|
+
snapVerticesToPoint(activeVertexIds, p, target.arcs);
|
|
6061
|
+
triggerRedraw();
|
|
7751
6062
|
}
|
|
7752
6063
|
|
|
7753
6064
|
function onLabelClick(e) {
|
|
@@ -7760,11 +6071,19 @@
|
|
|
7760
6071
|
}
|
|
7761
6072
|
}
|
|
7762
6073
|
|
|
6074
|
+
function triggerRedraw() {
|
|
6075
|
+
gui.dispatchEvent('map-needs-refresh');
|
|
6076
|
+
}
|
|
6077
|
+
|
|
7763
6078
|
function triggerGlobalEvent(type, e) {
|
|
7764
|
-
if (e.id >= 0)
|
|
7765
|
-
|
|
7766
|
-
|
|
7767
|
-
|
|
6079
|
+
if (e.id >= 0 === false) return;
|
|
6080
|
+
var o = {
|
|
6081
|
+
FID: e.id,
|
|
6082
|
+
layer_name: hit.getHitTarget().layer.name,
|
|
6083
|
+
vertex_ids: activeVertexIds
|
|
6084
|
+
};
|
|
6085
|
+
// fire event to signal external editor that symbol coords have changed
|
|
6086
|
+
gui.dispatchEvent(type, o);
|
|
7768
6087
|
}
|
|
7769
6088
|
|
|
7770
6089
|
function getLabelRecordById(id) {
|
|
@@ -7798,7 +6117,7 @@
|
|
|
7798
6117
|
var textNode;
|
|
7799
6118
|
if (!dragging) return;
|
|
7800
6119
|
if (e.id != activeId) {
|
|
7801
|
-
error
|
|
6120
|
+
error("Mismatched hit ids:", e.id, activeId);
|
|
7802
6121
|
}
|
|
7803
6122
|
applyDelta(activeRecord, 'dx', e.dx / scale);
|
|
7804
6123
|
applyDelta(activeRecord, 'dy', e.dy / scale);
|
|
@@ -7839,80 +6158,6 @@
|
|
|
7839
6158
|
}
|
|
7840
6159
|
return el.tagName == 'text' ? el : null;
|
|
7841
6160
|
}
|
|
7842
|
-
|
|
7843
|
-
// svg.addEventListener('mousedown', function(e) {
|
|
7844
|
-
// var textTarget = getTextTarget(e);
|
|
7845
|
-
// downEvt = e;
|
|
7846
|
-
// if (!textTarget) {
|
|
7847
|
-
// stopEditing();
|
|
7848
|
-
// } else if (!editing) {
|
|
7849
|
-
// // nop
|
|
7850
|
-
// } else if (textTarget == targetTextNode) {
|
|
7851
|
-
// startDragging();
|
|
7852
|
-
// } else {
|
|
7853
|
-
// startDragging();
|
|
7854
|
-
// editTextNode(textTarget);
|
|
7855
|
-
// }
|
|
7856
|
-
// });
|
|
7857
|
-
|
|
7858
|
-
// up event on svg
|
|
7859
|
-
// a: currently dragging text
|
|
7860
|
-
// -> stop dragging
|
|
7861
|
-
// b: clicked on a text feature
|
|
7862
|
-
// -> start editing it
|
|
7863
|
-
|
|
7864
|
-
|
|
7865
|
-
// svg.addEventListener('mouseup', function(e) {
|
|
7866
|
-
// var textTarget = getTextTarget(e);
|
|
7867
|
-
// var isClick = isClickEvent(e, downEvt);
|
|
7868
|
-
// if (isClick && textTarget && textTarget == targetTextNode &&
|
|
7869
|
-
// activeRecord && isMultilineLabel(targetTextNode)) {
|
|
7870
|
-
// toggleTextAlign(targetTextNode, activeRecord);
|
|
7871
|
-
// updateSymbol();
|
|
7872
|
-
// }
|
|
7873
|
-
// if (dragging) {
|
|
7874
|
-
// stopDragging();
|
|
7875
|
-
// } else if (isClick && textTarget) {
|
|
7876
|
-
// editTextNode(textTarget);
|
|
7877
|
-
// }
|
|
7878
|
-
// });
|
|
7879
|
-
|
|
7880
|
-
// block dbl-click navigation when editing
|
|
7881
|
-
// mouse.on('dblclick', function(e) {
|
|
7882
|
-
// if (editing) e.stopPropagation();
|
|
7883
|
-
// }, null, eventPriority);
|
|
7884
|
-
|
|
7885
|
-
// mouse.on('dragstart', function(e) {
|
|
7886
|
-
// onLabelDrag(e);
|
|
7887
|
-
// }, null, eventPriority);
|
|
7888
|
-
|
|
7889
|
-
// mouse.on('drag', function(e) {
|
|
7890
|
-
// var scale = ext.getSymbolScale() || 1;
|
|
7891
|
-
// onLabelDrag(e);
|
|
7892
|
-
// if (!dragging || !activeRecord) return;
|
|
7893
|
-
// applyDelta(activeRecord, 'dx', e.dx / scale);
|
|
7894
|
-
// applyDelta(activeRecord, 'dy', e.dy / scale);
|
|
7895
|
-
// if (!isMultilineLabel(targetTextNode)) {
|
|
7896
|
-
// // update anchor position of single-line labels based on label position
|
|
7897
|
-
// // relative to anchor point, for better placement when eventual display font is
|
|
7898
|
-
// // different from mapshaper's font.
|
|
7899
|
-
// updateTextAnchor(targetTextNode, activeRecord);
|
|
7900
|
-
// }
|
|
7901
|
-
// // updateSymbol(targetTextNode, activeRecord);
|
|
7902
|
-
// targetTextNode = updateSymbol2(targetTextNode, activeRecord, activeId);
|
|
7903
|
-
// }, null, eventPriority);
|
|
7904
|
-
|
|
7905
|
-
// mouse.on('dragend', function(e) {
|
|
7906
|
-
// onLabelDrag(e);
|
|
7907
|
-
// stopDragging();
|
|
7908
|
-
// }, null, eventPriority);
|
|
7909
|
-
|
|
7910
|
-
|
|
7911
|
-
// function onLabelDrag(e) {
|
|
7912
|
-
// if (dragging) {
|
|
7913
|
-
// e.stopPropagation();
|
|
7914
|
-
// }
|
|
7915
|
-
// }
|
|
7916
6161
|
}
|
|
7917
6162
|
|
|
7918
6163
|
function startDragging() {
|
|
@@ -7923,8 +6168,7 @@
|
|
|
7923
6168
|
dragging = false;
|
|
7924
6169
|
activeId = -1;
|
|
7925
6170
|
activeRecord = null;
|
|
7926
|
-
|
|
7927
|
-
// svg.removeAttribute('class');
|
|
6171
|
+
activeVertexIds = null;
|
|
7928
6172
|
}
|
|
7929
6173
|
|
|
7930
6174
|
function isClickEvent(up, down) {
|
|
@@ -7978,7 +6222,7 @@
|
|
|
7978
6222
|
dotSize: 2.5
|
|
7979
6223
|
}, polyline: {
|
|
7980
6224
|
strokeColor: black,
|
|
7981
|
-
strokeWidth: 2.5
|
|
6225
|
+
strokeWidth: 2.5,
|
|
7982
6226
|
}
|
|
7983
6227
|
},
|
|
7984
6228
|
unselectedHoverStyles = {
|
|
@@ -8040,7 +6284,7 @@
|
|
|
8040
6284
|
}
|
|
8041
6285
|
|
|
8042
6286
|
function getDefaultStyle(lyr, baseStyle) {
|
|
8043
|
-
var style = utils
|
|
6287
|
+
var style = utils.extend({}, baseStyle);
|
|
8044
6288
|
// reduce the dot size of large point layers
|
|
8045
6289
|
if (lyr.geometry_type == 'point' && style.dotSize > 0) {
|
|
8046
6290
|
style.dotSize *= getDotScale$1(lyr);
|
|
@@ -8106,7 +6350,7 @@
|
|
|
8106
6350
|
var topId = o.id; // pinned id (if pinned) or hover id
|
|
8107
6351
|
var topIdx = -1;
|
|
8108
6352
|
var styler = function(o, i) {
|
|
8109
|
-
utils
|
|
6353
|
+
utils.extend(o, i === topIdx ? topStyle: baseStyle);
|
|
8110
6354
|
};
|
|
8111
6355
|
var baseStyle = getDefaultStyle(lyr, selectionStyles[geomType]);
|
|
8112
6356
|
var topStyle;
|
|
@@ -8219,7 +6463,7 @@
|
|
|
8219
6463
|
if (lyr.geometry_type == 'point') {
|
|
8220
6464
|
return fields.indexOf('r') > -1; // require 'r' field for point symbols
|
|
8221
6465
|
}
|
|
8222
|
-
return utils
|
|
6466
|
+
return utils.difference(fields, ['opacity', 'class']).length > 0;
|
|
8223
6467
|
}
|
|
8224
6468
|
|
|
8225
6469
|
|
|
@@ -8292,6 +6536,7 @@
|
|
|
8292
6536
|
|
|
8293
6537
|
this.maxScale = maxScale;
|
|
8294
6538
|
|
|
6539
|
+
// Display scale, e.g. meters per pixel or degrees per pixel
|
|
8295
6540
|
this.getPixelSize = function() {
|
|
8296
6541
|
return 1 / this.getTransform().mx;
|
|
8297
6542
|
};
|
|
@@ -8375,7 +6620,7 @@
|
|
|
8375
6620
|
}
|
|
8376
6621
|
|
|
8377
6622
|
function maxAbs() {
|
|
8378
|
-
return Math.max.apply(null, utils
|
|
6623
|
+
return Math.max.apply(null, utils.toArray(arguments).map(Math.abs));
|
|
8379
6624
|
}
|
|
8380
6625
|
|
|
8381
6626
|
function limitScale(scale) {
|
|
@@ -8429,7 +6674,7 @@
|
|
|
8429
6674
|
}
|
|
8430
6675
|
}
|
|
8431
6676
|
|
|
8432
|
-
utils
|
|
6677
|
+
utils.inherit(MapExtent, EventDispatcher);
|
|
8433
6678
|
|
|
8434
6679
|
// TODO: consider moving this upstream
|
|
8435
6680
|
function getArcsForRendering(obj, ext) {
|
|
@@ -8483,7 +6728,10 @@
|
|
|
8483
6728
|
} else {
|
|
8484
6729
|
arcs = getArcsForRendering(obj, ext);
|
|
8485
6730
|
filter = getShapeFilter(arcs, ext);
|
|
8486
|
-
canv.
|
|
6731
|
+
canv.drawStyledPaths(layer.shapes, arcs, style, filter);
|
|
6732
|
+
if (style.vertices) {
|
|
6733
|
+
canv.drawVertices(layer.shapes, arcs, style, filter);
|
|
6734
|
+
}
|
|
8487
6735
|
}
|
|
8488
6736
|
}
|
|
8489
6737
|
|
|
@@ -8557,7 +6805,7 @@
|
|
|
8557
6805
|
|
|
8558
6806
|
/*
|
|
8559
6807
|
// Original function, not optimized
|
|
8560
|
-
_self.
|
|
6808
|
+
_self.drawStyledPaths = function(shapes, arcs, style) {
|
|
8561
6809
|
var startPath = getPathStart(_ext),
|
|
8562
6810
|
drawPath = getShapePencil(arcs, _ext),
|
|
8563
6811
|
styler = style.styler || null;
|
|
@@ -8570,8 +6818,28 @@
|
|
|
8570
6818
|
};
|
|
8571
6819
|
*/
|
|
8572
6820
|
|
|
6821
|
+
_self.drawVertices = function(shapes, arcs, style, filter) {
|
|
6822
|
+
var iter = new internal.ShapeIter(arcs);
|
|
6823
|
+
var t = getScaledTransform(_ext);
|
|
6824
|
+
var radius = (style.strokeWidth * 0.9 || 2.2) * GUI.getPixelRatio() * getScaledLineScale(_ext);
|
|
6825
|
+
var color = style.strokeColor || 'black';
|
|
6826
|
+
var shp;
|
|
6827
|
+
_ctx.beginPath();
|
|
6828
|
+
_ctx.fillStyle = color;
|
|
6829
|
+
for (var i=0; i<shapes.length; i++) {
|
|
6830
|
+
shp = shapes[i];
|
|
6831
|
+
if (!shp || filter && !filter(shp)) continue;
|
|
6832
|
+
iter.init(shp);
|
|
6833
|
+
while (iter.hasNext()) {
|
|
6834
|
+
drawCircle(iter.x * t.mx + t.bx, iter.y * t.my + t.by, radius, _ctx);
|
|
6835
|
+
}
|
|
6836
|
+
}
|
|
6837
|
+
_ctx.fill();
|
|
6838
|
+
_ctx.closePath();
|
|
6839
|
+
};
|
|
6840
|
+
|
|
8573
6841
|
// Optimized to draw paths in same-style batches (faster Canvas drawing)
|
|
8574
|
-
_self.
|
|
6842
|
+
_self.drawStyledPaths = function(shapes, arcs, style, filter) {
|
|
8575
6843
|
var styleIndex = {};
|
|
8576
6844
|
var batchSize = 1500;
|
|
8577
6845
|
var startPath = getPathStart(_ext, getScaledLineScale(_ext));
|
|
@@ -8590,7 +6858,7 @@
|
|
|
8590
6858
|
key = getStyleKey(style);
|
|
8591
6859
|
if (key in styleIndex === false) {
|
|
8592
6860
|
styleIndex[key] = {
|
|
8593
|
-
style: utils
|
|
6861
|
+
style: utils.defaults({}, style),
|
|
8594
6862
|
shapes: []
|
|
8595
6863
|
};
|
|
8596
6864
|
}
|
|
@@ -8699,7 +6967,7 @@
|
|
|
8699
6967
|
}
|
|
8700
6968
|
}
|
|
8701
6969
|
|
|
8702
|
-
// TODO: consider using
|
|
6970
|
+
// TODO: consider using drawStyledPaths(), which draws paths in batches
|
|
8703
6971
|
// for faster Canvas rendering. Downside: changes stacking order, which
|
|
8704
6972
|
// is bad if circles are graduated.
|
|
8705
6973
|
_self.drawPoints = function(shapes, style) {
|
|
@@ -9064,7 +7332,7 @@
|
|
|
9064
7332
|
var frame = ext.getFrame(); // frame should be set if we're rendering a furniture layer
|
|
9065
7333
|
var obj = internal.getEmptyLayerForSVG(lyr, {});
|
|
9066
7334
|
if (!frame) {
|
|
9067
|
-
stop
|
|
7335
|
+
stop('Missing map frame data');
|
|
9068
7336
|
}
|
|
9069
7337
|
obj.properties.transform = getSvgFurnitureTransform(ext);
|
|
9070
7338
|
obj.properties.class = 'mapshaper-svg-furniture';
|
|
@@ -9092,7 +7360,7 @@
|
|
|
9092
7360
|
var html = '';
|
|
9093
7361
|
// generate a unique id so layer can be identified when symbols are repositioned
|
|
9094
7362
|
// use it as a class name to avoid id collisions
|
|
9095
|
-
var id = utils
|
|
7363
|
+
var id = utils.getUniqueName();
|
|
9096
7364
|
var classNames = [id, 'mapshaper-svg-layer', 'mapshaper-' + type + '-layer'];
|
|
9097
7365
|
g.setAttribute('class', classNames.join(' '));
|
|
9098
7366
|
target.svg_id = id;
|
|
@@ -9543,7 +7811,7 @@
|
|
|
9543
7811
|
}
|
|
9544
7812
|
|
|
9545
7813
|
if (obj.tabular) {
|
|
9546
|
-
utils
|
|
7814
|
+
utils.extend(obj, getDisplayLayerForTable(layer.data));
|
|
9547
7815
|
}
|
|
9548
7816
|
|
|
9549
7817
|
// dynamic reprojection (arcs were already reprojected above)
|
|
@@ -9601,7 +7869,7 @@
|
|
|
9601
7869
|
return ids;
|
|
9602
7870
|
}
|
|
9603
7871
|
|
|
9604
|
-
utils
|
|
7872
|
+
utils.inherit(MshpMap, EventDispatcher);
|
|
9605
7873
|
|
|
9606
7874
|
function MshpMap(gui) {
|
|
9607
7875
|
var opts = gui.options,
|
|
@@ -9639,7 +7907,7 @@
|
|
|
9639
7907
|
_mouse.disable();
|
|
9640
7908
|
});
|
|
9641
7909
|
|
|
9642
|
-
gui.on('
|
|
7910
|
+
gui.on('map-needs-refresh', function() {
|
|
9643
7911
|
drawLayers();
|
|
9644
7912
|
});
|
|
9645
7913
|
|
|
@@ -9695,7 +7963,7 @@
|
|
|
9695
7963
|
this.setDisplayCRS = function(crs) {
|
|
9696
7964
|
// TODO: update bounds of frame layer, if there is a frame layer
|
|
9697
7965
|
var oldCRS = this.getDisplayCRS();
|
|
9698
|
-
var newCRS = utils
|
|
7966
|
+
var newCRS = utils.isString(crs) ? internal.getCRS(crs) : crs;
|
|
9699
7967
|
// TODO: handle case that old and new CRS are the same
|
|
9700
7968
|
_dynamicCRS = newCRS;
|
|
9701
7969
|
if (!_activeLyr) return; // stop here if no layers have been selected
|
|
@@ -9801,13 +8069,7 @@
|
|
|
9801
8069
|
});
|
|
9802
8070
|
}
|
|
9803
8071
|
|
|
9804
|
-
|
|
9805
|
-
_editor = new SymbolDragging2(gui, _ext, _hit);
|
|
9806
|
-
_editor.on('location_change', function(e) {
|
|
9807
|
-
// TODO: look into optimizing, so only changed symbol is redrawn
|
|
9808
|
-
drawLayers();
|
|
9809
|
-
});
|
|
9810
|
-
}
|
|
8072
|
+
_editor = new InteractiveEditor(gui, _ext, _hit);
|
|
9811
8073
|
|
|
9812
8074
|
_ext.on('change', function(e) {
|
|
9813
8075
|
if (e.reset) return; // don't need to redraw map here if extent has been reset
|
|
@@ -9827,7 +8089,11 @@
|
|
|
9827
8089
|
function updateOverlayLayer(e) {
|
|
9828
8090
|
var style = getOverlayStyle(_activeLyr.layer, e);
|
|
9829
8091
|
if (style) {
|
|
9830
|
-
|
|
8092
|
+
// kludge to show vertices when editing path shapes
|
|
8093
|
+
if (gui.state.interaction_mode == 'vertices') {
|
|
8094
|
+
style.vertices = true;
|
|
8095
|
+
}
|
|
8096
|
+
_overlayLyr = utils.defaults({
|
|
9831
8097
|
layer: filterLayerByIds(_activeLyr.layer, style.ids),
|
|
9832
8098
|
style: style
|
|
9833
8099
|
}, _activeLyr);
|
|
@@ -9992,7 +8258,7 @@
|
|
|
9992
8258
|
// kludge to hide ghosted layers when reference layers are present
|
|
9993
8259
|
// TODO: consider never showing ghosted layers (which appear after
|
|
9994
8260
|
// commands like dissolve and filter).
|
|
9995
|
-
mapLayer.style = utils
|
|
8261
|
+
mapLayer.style = utils.defaults({
|
|
9996
8262
|
strokeColors: [null, mapLayer.style.strokeColors[1]]
|
|
9997
8263
|
}, mapLayer.style);
|
|
9998
8264
|
}
|
|
@@ -10048,7 +8314,7 @@
|
|
|
10048
8314
|
|
|
10049
8315
|
function GuiInstance(container, opts) {
|
|
10050
8316
|
var gui = new ModeSwitcher();
|
|
10051
|
-
opts = utils
|
|
8317
|
+
opts = utils.extend({
|
|
10052
8318
|
// defaults
|
|
10053
8319
|
homeControl: true,
|
|
10054
8320
|
zoomControl: true,
|
|
@@ -10067,6 +8333,7 @@
|
|
|
10067
8333
|
gui.interaction = new InteractionMode(gui);
|
|
10068
8334
|
gui.session = new SessionHistory(gui);
|
|
10069
8335
|
gui.undo = new Undo(gui);
|
|
8336
|
+
gui.state = {};
|
|
10070
8337
|
|
|
10071
8338
|
gui.showProgressMessage = function(msg) {
|
|
10072
8339
|
if (!gui.progressMessage) {
|
|
@@ -10150,6 +8417,7 @@
|
|
|
10150
8417
|
opts.display_all = vars['display-all'] || vars.a || !!manifest.display_all;
|
|
10151
8418
|
opts.quick_view = vars['quick-view'] || vars.q || !!manifest.quick_view;
|
|
10152
8419
|
opts.target = vars.target || manifest.target || null;
|
|
8420
|
+
opts.name = vars.name || manifest.name || null;
|
|
10153
8421
|
return opts;
|
|
10154
8422
|
}
|
|
10155
8423
|
|