]> git.openstreetmap.org Git - rails.git/blob - vendor/assets/iD/iD.js
Merge remote-tracking branch 'upstream/pull/3297'
[rails.git] / vendor / assets / iD / iD.js
1 (function () {
2
3         var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};
4
5         function createCommonjsModule(fn) {
6           var module = { exports: {} };
7                 return fn(module, module.exports), module.exports;
8         }
9
10         var check = function (it) {
11           return it && it.Math == Math && it;
12         };
13
14         // https://github.com/zloirock/core-js/issues/86#issuecomment-115759028
15         var global$2 =
16           // eslint-disable-next-line es/no-global-this -- safe
17           check(typeof globalThis == 'object' && globalThis) ||
18           check(typeof window == 'object' && window) ||
19           // eslint-disable-next-line no-restricted-globals -- safe
20           check(typeof self == 'object' && self) ||
21           check(typeof commonjsGlobal == 'object' && commonjsGlobal) ||
22           // eslint-disable-next-line no-new-func -- fallback
23           (function () { return this; })() || Function('return this')();
24
25         var fails = function (exec) {
26           try {
27             return !!exec();
28           } catch (error) {
29             return true;
30           }
31         };
32
33         // Detect IE8's incomplete defineProperty implementation
34         var descriptors = !fails(function () {
35           // eslint-disable-next-line es/no-object-defineproperty -- required for testing
36           return Object.defineProperty({}, 1, { get: function () { return 7; } })[1] != 7;
37         });
38
39         var $propertyIsEnumerable$1 = {}.propertyIsEnumerable;
40         // eslint-disable-next-line es/no-object-getownpropertydescriptor -- safe
41         var getOwnPropertyDescriptor$5 = Object.getOwnPropertyDescriptor;
42
43         // Nashorn ~ JDK8 bug
44         var NASHORN_BUG = getOwnPropertyDescriptor$5 && !$propertyIsEnumerable$1.call({ 1: 2 }, 1);
45
46         // `Object.prototype.propertyIsEnumerable` method implementation
47         // https://tc39.es/ecma262/#sec-object.prototype.propertyisenumerable
48         var f$7 = NASHORN_BUG ? function propertyIsEnumerable(V) {
49           var descriptor = getOwnPropertyDescriptor$5(this, V);
50           return !!descriptor && descriptor.enumerable;
51         } : $propertyIsEnumerable$1;
52
53         var objectPropertyIsEnumerable = {
54                 f: f$7
55         };
56
57         var createPropertyDescriptor = function (bitmap, value) {
58           return {
59             enumerable: !(bitmap & 1),
60             configurable: !(bitmap & 2),
61             writable: !(bitmap & 4),
62             value: value
63           };
64         };
65
66         var toString$1 = {}.toString;
67
68         var classofRaw = function (it) {
69           return toString$1.call(it).slice(8, -1);
70         };
71
72         var split$1 = ''.split;
73
74         // fallback for non-array-like ES3 and non-enumerable old V8 strings
75         var indexedObject = fails(function () {
76           // throws an error in rhino, see https://github.com/mozilla/rhino/issues/346
77           // eslint-disable-next-line no-prototype-builtins -- safe
78           return !Object('z').propertyIsEnumerable(0);
79         }) ? function (it) {
80           return classofRaw(it) == 'String' ? split$1.call(it, '') : Object(it);
81         } : Object;
82
83         // `RequireObjectCoercible` abstract operation
84         // https://tc39.es/ecma262/#sec-requireobjectcoercible
85         var requireObjectCoercible = function (it) {
86           if (it == undefined) throw TypeError("Can't call method on " + it);
87           return it;
88         };
89
90         // toObject with fallback for non-array-like ES3 strings
91
92
93
94         var toIndexedObject = function (it) {
95           return indexedObject(requireObjectCoercible(it));
96         };
97
98         var isObject$4 = function (it) {
99           return typeof it === 'object' ? it !== null : typeof it === 'function';
100         };
101
102         // `ToPrimitive` abstract operation
103         // https://tc39.es/ecma262/#sec-toprimitive
104         // instead of the ES6 spec version, we didn't implement @@toPrimitive case
105         // and the second argument - flag - preferred type is a string
106         var toPrimitive = function (input, PREFERRED_STRING) {
107           if (!isObject$4(input)) return input;
108           var fn, val;
109           if (PREFERRED_STRING && typeof (fn = input.toString) == 'function' && !isObject$4(val = fn.call(input))) return val;
110           if (typeof (fn = input.valueOf) == 'function' && !isObject$4(val = fn.call(input))) return val;
111           if (!PREFERRED_STRING && typeof (fn = input.toString) == 'function' && !isObject$4(val = fn.call(input))) return val;
112           throw TypeError("Can't convert object to primitive value");
113         };
114
115         // `ToObject` abstract operation
116         // https://tc39.es/ecma262/#sec-toobject
117         var toObject = function (argument) {
118           return Object(requireObjectCoercible(argument));
119         };
120
121         var hasOwnProperty$3 = {}.hasOwnProperty;
122
123         var has$1 = Object.hasOwn || function hasOwn(it, key) {
124           return hasOwnProperty$3.call(toObject(it), key);
125         };
126
127         var document$3 = global$2.document;
128         // typeof document.createElement is 'object' in old IE
129         var EXISTS = isObject$4(document$3) && isObject$4(document$3.createElement);
130
131         var documentCreateElement = function (it) {
132           return EXISTS ? document$3.createElement(it) : {};
133         };
134
135         // Thank's IE8 for his funny defineProperty
136         var ie8DomDefine = !descriptors && !fails(function () {
137           // eslint-disable-next-line es/no-object-defineproperty -- requied for testing
138           return Object.defineProperty(documentCreateElement('div'), 'a', {
139             get: function () { return 7; }
140           }).a != 7;
141         });
142
143         // eslint-disable-next-line es/no-object-getownpropertydescriptor -- safe
144         var $getOwnPropertyDescriptor$1 = Object.getOwnPropertyDescriptor;
145
146         // `Object.getOwnPropertyDescriptor` method
147         // https://tc39.es/ecma262/#sec-object.getownpropertydescriptor
148         var f$6 = descriptors ? $getOwnPropertyDescriptor$1 : function getOwnPropertyDescriptor(O, P) {
149           O = toIndexedObject(O);
150           P = toPrimitive(P, true);
151           if (ie8DomDefine) try {
152             return $getOwnPropertyDescriptor$1(O, P);
153           } catch (error) { /* empty */ }
154           if (has$1(O, P)) return createPropertyDescriptor(!objectPropertyIsEnumerable.f.call(O, P), O[P]);
155         };
156
157         var objectGetOwnPropertyDescriptor = {
158                 f: f$6
159         };
160
161         var anObject = function (it) {
162           if (!isObject$4(it)) {
163             throw TypeError(String(it) + ' is not an object');
164           } return it;
165         };
166
167         // eslint-disable-next-line es/no-object-defineproperty -- safe
168         var $defineProperty$1 = Object.defineProperty;
169
170         // `Object.defineProperty` method
171         // https://tc39.es/ecma262/#sec-object.defineproperty
172         var f$5 = descriptors ? $defineProperty$1 : function defineProperty(O, P, Attributes) {
173           anObject(O);
174           P = toPrimitive(P, true);
175           anObject(Attributes);
176           if (ie8DomDefine) try {
177             return $defineProperty$1(O, P, Attributes);
178           } catch (error) { /* empty */ }
179           if ('get' in Attributes || 'set' in Attributes) throw TypeError('Accessors not supported');
180           if ('value' in Attributes) O[P] = Attributes.value;
181           return O;
182         };
183
184         var objectDefineProperty = {
185                 f: f$5
186         };
187
188         var createNonEnumerableProperty = descriptors ? function (object, key, value) {
189           return objectDefineProperty.f(object, key, createPropertyDescriptor(1, value));
190         } : function (object, key, value) {
191           object[key] = value;
192           return object;
193         };
194
195         var setGlobal = function (key, value) {
196           try {
197             createNonEnumerableProperty(global$2, key, value);
198           } catch (error) {
199             global$2[key] = value;
200           } return value;
201         };
202
203         var SHARED = '__core-js_shared__';
204         var store$1 = global$2[SHARED] || setGlobal(SHARED, {});
205
206         var sharedStore = store$1;
207
208         var functionToString = Function.toString;
209
210         // this helper broken in `core-js@3.4.1-3.4.4`, so we can't use `shared` helper
211         if (typeof sharedStore.inspectSource != 'function') {
212           sharedStore.inspectSource = function (it) {
213             return functionToString.call(it);
214           };
215         }
216
217         var inspectSource = sharedStore.inspectSource;
218
219         var WeakMap$1 = global$2.WeakMap;
220
221         var nativeWeakMap = typeof WeakMap$1 === 'function' && /native code/.test(inspectSource(WeakMap$1));
222
223         var isPure = false;
224
225         var shared = createCommonjsModule(function (module) {
226         (module.exports = function (key, value) {
227           return sharedStore[key] || (sharedStore[key] = value !== undefined ? value : {});
228         })('versions', []).push({
229           version: '3.15.0',
230           mode: 'global',
231           copyright: '© 2021 Denis Pushkarev (zloirock.ru)'
232         });
233         });
234
235         var id$1 = 0;
236         var postfix = Math.random();
237
238         var uid = function (key) {
239           return 'Symbol(' + String(key === undefined ? '' : key) + ')_' + (++id$1 + postfix).toString(36);
240         };
241
242         var keys$3 = shared('keys');
243
244         var sharedKey = function (key) {
245           return keys$3[key] || (keys$3[key] = uid(key));
246         };
247
248         var hiddenKeys$1 = {};
249
250         var OBJECT_ALREADY_INITIALIZED = 'Object already initialized';
251         var WeakMap = global$2.WeakMap;
252         var set$4, get$5, has;
253
254         var enforce = function (it) {
255           return has(it) ? get$5(it) : set$4(it, {});
256         };
257
258         var getterFor = function (TYPE) {
259           return function (it) {
260             var state;
261             if (!isObject$4(it) || (state = get$5(it)).type !== TYPE) {
262               throw TypeError('Incompatible receiver, ' + TYPE + ' required');
263             } return state;
264           };
265         };
266
267         if (nativeWeakMap || sharedStore.state) {
268           var store = sharedStore.state || (sharedStore.state = new WeakMap());
269           var wmget = store.get;
270           var wmhas = store.has;
271           var wmset = store.set;
272           set$4 = function (it, metadata) {
273             if (wmhas.call(store, it)) throw new TypeError(OBJECT_ALREADY_INITIALIZED);
274             metadata.facade = it;
275             wmset.call(store, it, metadata);
276             return metadata;
277           };
278           get$5 = function (it) {
279             return wmget.call(store, it) || {};
280           };
281           has = function (it) {
282             return wmhas.call(store, it);
283           };
284         } else {
285           var STATE = sharedKey('state');
286           hiddenKeys$1[STATE] = true;
287           set$4 = function (it, metadata) {
288             if (has$1(it, STATE)) throw new TypeError(OBJECT_ALREADY_INITIALIZED);
289             metadata.facade = it;
290             createNonEnumerableProperty(it, STATE, metadata);
291             return metadata;
292           };
293           get$5 = function (it) {
294             return has$1(it, STATE) ? it[STATE] : {};
295           };
296           has = function (it) {
297             return has$1(it, STATE);
298           };
299         }
300
301         var internalState = {
302           set: set$4,
303           get: get$5,
304           has: has,
305           enforce: enforce,
306           getterFor: getterFor
307         };
308
309         var redefine = createCommonjsModule(function (module) {
310         var getInternalState = internalState.get;
311         var enforceInternalState = internalState.enforce;
312         var TEMPLATE = String(String).split('String');
313
314         (module.exports = function (O, key, value, options) {
315           var unsafe = options ? !!options.unsafe : false;
316           var simple = options ? !!options.enumerable : false;
317           var noTargetGet = options ? !!options.noTargetGet : false;
318           var state;
319           if (typeof value == 'function') {
320             if (typeof key == 'string' && !has$1(value, 'name')) {
321               createNonEnumerableProperty(value, 'name', key);
322             }
323             state = enforceInternalState(value);
324             if (!state.source) {
325               state.source = TEMPLATE.join(typeof key == 'string' ? key : '');
326             }
327           }
328           if (O === global$2) {
329             if (simple) O[key] = value;
330             else setGlobal(key, value);
331             return;
332           } else if (!unsafe) {
333             delete O[key];
334           } else if (!noTargetGet && O[key]) {
335             simple = true;
336           }
337           if (simple) O[key] = value;
338           else createNonEnumerableProperty(O, key, value);
339         // add fake Function#toString for correct work wrapped methods / constructors with methods like LoDash isNative
340         })(Function.prototype, 'toString', function toString() {
341           return typeof this == 'function' && getInternalState(this).source || inspectSource(this);
342         });
343         });
344
345         var path = global$2;
346
347         var aFunction$1 = function (variable) {
348           return typeof variable == 'function' ? variable : undefined;
349         };
350
351         var getBuiltIn = function (namespace, method) {
352           return arguments.length < 2 ? aFunction$1(path[namespace]) || aFunction$1(global$2[namespace])
353             : path[namespace] && path[namespace][method] || global$2[namespace] && global$2[namespace][method];
354         };
355
356         var ceil$1 = Math.ceil;
357         var floor$7 = Math.floor;
358
359         // `ToInteger` abstract operation
360         // https://tc39.es/ecma262/#sec-tointeger
361         var toInteger = function (argument) {
362           return isNaN(argument = +argument) ? 0 : (argument > 0 ? floor$7 : ceil$1)(argument);
363         };
364
365         var min$9 = Math.min;
366
367         // `ToLength` abstract operation
368         // https://tc39.es/ecma262/#sec-tolength
369         var toLength = function (argument) {
370           return argument > 0 ? min$9(toInteger(argument), 0x1FFFFFFFFFFFFF) : 0; // 2 ** 53 - 1 == 9007199254740991
371         };
372
373         var max$4 = Math.max;
374         var min$8 = Math.min;
375
376         // Helper for a popular repeating case of the spec:
377         // Let integer be ? ToInteger(index).
378         // If integer < 0, let result be max((length + integer), 0); else let result be min(integer, length).
379         var toAbsoluteIndex = function (index, length) {
380           var integer = toInteger(index);
381           return integer < 0 ? max$4(integer + length, 0) : min$8(integer, length);
382         };
383
384         // `Array.prototype.{ indexOf, includes }` methods implementation
385         var createMethod$6 = function (IS_INCLUDES) {
386           return function ($this, el, fromIndex) {
387             var O = toIndexedObject($this);
388             var length = toLength(O.length);
389             var index = toAbsoluteIndex(fromIndex, length);
390             var value;
391             // Array#includes uses SameValueZero equality algorithm
392             // eslint-disable-next-line no-self-compare -- NaN check
393             if (IS_INCLUDES && el != el) while (length > index) {
394               value = O[index++];
395               // eslint-disable-next-line no-self-compare -- NaN check
396               if (value != value) return true;
397             // Array#indexOf ignores holes, Array#includes - not
398             } else for (;length > index; index++) {
399               if ((IS_INCLUDES || index in O) && O[index] === el) return IS_INCLUDES || index || 0;
400             } return !IS_INCLUDES && -1;
401           };
402         };
403
404         var arrayIncludes = {
405           // `Array.prototype.includes` method
406           // https://tc39.es/ecma262/#sec-array.prototype.includes
407           includes: createMethod$6(true),
408           // `Array.prototype.indexOf` method
409           // https://tc39.es/ecma262/#sec-array.prototype.indexof
410           indexOf: createMethod$6(false)
411         };
412
413         var indexOf = arrayIncludes.indexOf;
414
415
416         var objectKeysInternal = function (object, names) {
417           var O = toIndexedObject(object);
418           var i = 0;
419           var result = [];
420           var key;
421           for (key in O) !has$1(hiddenKeys$1, key) && has$1(O, key) && result.push(key);
422           // Don't enum bug & hidden keys
423           while (names.length > i) if (has$1(O, key = names[i++])) {
424             ~indexOf(result, key) || result.push(key);
425           }
426           return result;
427         };
428
429         // IE8- don't enum bug keys
430         var enumBugKeys = [
431           'constructor',
432           'hasOwnProperty',
433           'isPrototypeOf',
434           'propertyIsEnumerable',
435           'toLocaleString',
436           'toString',
437           'valueOf'
438         ];
439
440         var hiddenKeys = enumBugKeys.concat('length', 'prototype');
441
442         // `Object.getOwnPropertyNames` method
443         // https://tc39.es/ecma262/#sec-object.getownpropertynames
444         // eslint-disable-next-line es/no-object-getownpropertynames -- safe
445         var f$4 = Object.getOwnPropertyNames || function getOwnPropertyNames(O) {
446           return objectKeysInternal(O, hiddenKeys);
447         };
448
449         var objectGetOwnPropertyNames = {
450                 f: f$4
451         };
452
453         // eslint-disable-next-line es/no-object-getownpropertysymbols -- safe
454         var f$3 = Object.getOwnPropertySymbols;
455
456         var objectGetOwnPropertySymbols = {
457                 f: f$3
458         };
459
460         // all object keys, includes non-enumerable and symbols
461         var ownKeys = getBuiltIn('Reflect', 'ownKeys') || function ownKeys(it) {
462           var keys = objectGetOwnPropertyNames.f(anObject(it));
463           var getOwnPropertySymbols = objectGetOwnPropertySymbols.f;
464           return getOwnPropertySymbols ? keys.concat(getOwnPropertySymbols(it)) : keys;
465         };
466
467         var copyConstructorProperties = function (target, source) {
468           var keys = ownKeys(source);
469           var defineProperty = objectDefineProperty.f;
470           var getOwnPropertyDescriptor = objectGetOwnPropertyDescriptor.f;
471           for (var i = 0; i < keys.length; i++) {
472             var key = keys[i];
473             if (!has$1(target, key)) defineProperty(target, key, getOwnPropertyDescriptor(source, key));
474           }
475         };
476
477         var replacement = /#|\.prototype\./;
478
479         var isForced = function (feature, detection) {
480           var value = data[normalize$1(feature)];
481           return value == POLYFILL ? true
482             : value == NATIVE ? false
483             : typeof detection == 'function' ? fails(detection)
484             : !!detection;
485         };
486
487         var normalize$1 = isForced.normalize = function (string) {
488           return String(string).replace(replacement, '.').toLowerCase();
489         };
490
491         var data = isForced.data = {};
492         var NATIVE = isForced.NATIVE = 'N';
493         var POLYFILL = isForced.POLYFILL = 'P';
494
495         var isForced_1 = isForced;
496
497         var getOwnPropertyDescriptor$4 = objectGetOwnPropertyDescriptor.f;
498
499
500
501
502
503
504         /*
505           options.target      - name of the target object
506           options.global      - target is the global object
507           options.stat        - export as static methods of target
508           options.proto       - export as prototype methods of target
509           options.real        - real prototype method for the `pure` version
510           options.forced      - export even if the native feature is available
511           options.bind        - bind methods to the target, required for the `pure` version
512           options.wrap        - wrap constructors to preventing global pollution, required for the `pure` version
513           options.unsafe      - use the simple assignment of property instead of delete + defineProperty
514           options.sham        - add a flag to not completely full polyfills
515           options.enumerable  - export as enumerable property
516           options.noTargetGet - prevent calling a getter on target
517         */
518         var _export = function (options, source) {
519           var TARGET = options.target;
520           var GLOBAL = options.global;
521           var STATIC = options.stat;
522           var FORCED, target, key, targetProperty, sourceProperty, descriptor;
523           if (GLOBAL) {
524             target = global$2;
525           } else if (STATIC) {
526             target = global$2[TARGET] || setGlobal(TARGET, {});
527           } else {
528             target = (global$2[TARGET] || {}).prototype;
529           }
530           if (target) for (key in source) {
531             sourceProperty = source[key];
532             if (options.noTargetGet) {
533               descriptor = getOwnPropertyDescriptor$4(target, key);
534               targetProperty = descriptor && descriptor.value;
535             } else targetProperty = target[key];
536             FORCED = isForced_1(GLOBAL ? key : TARGET + (STATIC ? '.' : '#') + key, options.forced);
537             // contained in target
538             if (!FORCED && targetProperty !== undefined) {
539               if (typeof sourceProperty === typeof targetProperty) continue;
540               copyConstructorProperties(sourceProperty, targetProperty);
541             }
542             // add a flag to not completely full polyfills
543             if (options.sham || (targetProperty && targetProperty.sham)) {
544               createNonEnumerableProperty(sourceProperty, 'sham', true);
545             }
546             // extend global
547             redefine(target, key, sourceProperty, options);
548           }
549         };
550
551         // `Date.now` method
552         // https://tc39.es/ecma262/#sec-date.now
553         _export({ target: 'Date', stat: true }, {
554           now: function now() {
555             return new Date().getTime();
556           }
557         });
558
559         var DatePrototype$1 = Date.prototype;
560         var INVALID_DATE = 'Invalid Date';
561         var TO_STRING$1 = 'toString';
562         var nativeDateToString = DatePrototype$1[TO_STRING$1];
563         var getTime$1 = DatePrototype$1.getTime;
564
565         // `Date.prototype.toString` method
566         // https://tc39.es/ecma262/#sec-date.prototype.tostring
567         if (new Date(NaN) + '' != INVALID_DATE) {
568           redefine(DatePrototype$1, TO_STRING$1, function toString() {
569             var value = getTime$1.call(this);
570             // eslint-disable-next-line no-self-compare -- NaN check
571             return value === value ? nativeDateToString.call(this) : INVALID_DATE;
572           });
573         }
574
575         function _typeof(obj) {
576           "@babel/helpers - typeof";
577
578           if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") {
579             _typeof = function (obj) {
580               return typeof obj;
581             };
582           } else {
583             _typeof = function (obj) {
584               return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
585             };
586           }
587
588           return _typeof(obj);
589         }
590
591         function _classCallCheck$1(instance, Constructor) {
592           if (!(instance instanceof Constructor)) {
593             throw new TypeError("Cannot call a class as a function");
594           }
595         }
596
597         function _defineProperties$1(target, props) {
598           for (var i = 0; i < props.length; i++) {
599             var descriptor = props[i];
600             descriptor.enumerable = descriptor.enumerable || false;
601             descriptor.configurable = true;
602             if ("value" in descriptor) descriptor.writable = true;
603             Object.defineProperty(target, descriptor.key, descriptor);
604           }
605         }
606
607         function _createClass$1(Constructor, protoProps, staticProps) {
608           if (protoProps) _defineProperties$1(Constructor.prototype, protoProps);
609           if (staticProps) _defineProperties$1(Constructor, staticProps);
610           return Constructor;
611         }
612
613         function _defineProperty(obj, key, value) {
614           if (key in obj) {
615             Object.defineProperty(obj, key, {
616               value: value,
617               enumerable: true,
618               configurable: true,
619               writable: true
620             });
621           } else {
622             obj[key] = value;
623           }
624
625           return obj;
626         }
627
628         function _slicedToArray(arr, i) {
629           return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest();
630         }
631
632         function _toConsumableArray(arr) {
633           return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread();
634         }
635
636         function _arrayWithoutHoles(arr) {
637           if (Array.isArray(arr)) return _arrayLikeToArray(arr);
638         }
639
640         function _arrayWithHoles(arr) {
641           if (Array.isArray(arr)) return arr;
642         }
643
644         function _iterableToArray(iter) {
645           if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter);
646         }
647
648         function _iterableToArrayLimit(arr, i) {
649           var _i = arr == null ? null : typeof Symbol !== "undefined" && arr[Symbol.iterator] || arr["@@iterator"];
650
651           if (_i == null) return;
652           var _arr = [];
653           var _n = true;
654           var _d = false;
655
656           var _s, _e;
657
658           try {
659             for (_i = _i.call(arr); !(_n = (_s = _i.next()).done); _n = true) {
660               _arr.push(_s.value);
661
662               if (i && _arr.length === i) break;
663             }
664           } catch (err) {
665             _d = true;
666             _e = err;
667           } finally {
668             try {
669               if (!_n && _i["return"] != null) _i["return"]();
670             } finally {
671               if (_d) throw _e;
672             }
673           }
674
675           return _arr;
676         }
677
678         function _unsupportedIterableToArray(o, minLen) {
679           if (!o) return;
680           if (typeof o === "string") return _arrayLikeToArray(o, minLen);
681           var n = Object.prototype.toString.call(o).slice(8, -1);
682           if (n === "Object" && o.constructor) n = o.constructor.name;
683           if (n === "Map" || n === "Set") return Array.from(o);
684           if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen);
685         }
686
687         function _arrayLikeToArray(arr, len) {
688           if (len == null || len > arr.length) len = arr.length;
689
690           for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i];
691
692           return arr2;
693         }
694
695         function _nonIterableSpread() {
696           throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
697         }
698
699         function _nonIterableRest() {
700           throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
701         }
702
703         function _createForOfIteratorHelper(o, allowArrayLike) {
704           var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"];
705
706           if (!it) {
707             if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") {
708               if (it) o = it;
709               var i = 0;
710
711               var F = function () {};
712
713               return {
714                 s: F,
715                 n: function () {
716                   if (i >= o.length) return {
717                     done: true
718                   };
719                   return {
720                     done: false,
721                     value: o[i++]
722                   };
723                 },
724                 e: function (e) {
725                   throw e;
726                 },
727                 f: F
728               };
729             }
730
731             throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
732           }
733
734           var normalCompletion = true,
735               didErr = false,
736               err;
737           return {
738             s: function () {
739               it = it.call(o);
740             },
741             n: function () {
742               var step = it.next();
743               normalCompletion = step.done;
744               return step;
745             },
746             e: function (e) {
747               didErr = true;
748               err = e;
749             },
750             f: function () {
751               try {
752                 if (!normalCompletion && it.return != null) it.return();
753               } finally {
754                 if (didErr) throw err;
755               }
756             }
757           };
758         }
759
760         var engineUserAgent = getBuiltIn('navigator', 'userAgent') || '';
761
762         var process$4 = global$2.process;
763         var versions = process$4 && process$4.versions;
764         var v8 = versions && versions.v8;
765         var match, version$1;
766
767         if (v8) {
768           match = v8.split('.');
769           version$1 = match[0] < 4 ? 1 : match[0] + match[1];
770         } else if (engineUserAgent) {
771           match = engineUserAgent.match(/Edge\/(\d+)/);
772           if (!match || match[1] >= 74) {
773             match = engineUserAgent.match(/Chrome\/(\d+)/);
774             if (match) version$1 = match[1];
775           }
776         }
777
778         var engineV8Version = version$1 && +version$1;
779
780         /* eslint-disable es/no-symbol -- required for testing */
781
782         // eslint-disable-next-line es/no-object-getownpropertysymbols -- required for testing
783         var nativeSymbol = !!Object.getOwnPropertySymbols && !fails(function () {
784           var symbol = Symbol();
785           // Chrome 38 Symbol has incorrect toString conversion
786           // `get-own-property-symbols` polyfill symbols converted to object are not Symbol instances
787           return !String(symbol) || !(Object(symbol) instanceof Symbol) ||
788             // Chrome 38-40 symbols are not inherited from DOM collections prototypes to instances
789             !Symbol.sham && engineV8Version && engineV8Version < 41;
790         });
791
792         /* eslint-disable es/no-symbol -- required for testing */
793
794         var useSymbolAsUid = nativeSymbol
795           && !Symbol.sham
796           && typeof Symbol.iterator == 'symbol';
797
798         var WellKnownSymbolsStore$1 = shared('wks');
799         var Symbol$1 = global$2.Symbol;
800         var createWellKnownSymbol = useSymbolAsUid ? Symbol$1 : Symbol$1 && Symbol$1.withoutSetter || uid;
801
802         var wellKnownSymbol = function (name) {
803           if (!has$1(WellKnownSymbolsStore$1, name) || !(nativeSymbol || typeof WellKnownSymbolsStore$1[name] == 'string')) {
804             if (nativeSymbol && has$1(Symbol$1, name)) {
805               WellKnownSymbolsStore$1[name] = Symbol$1[name];
806             } else {
807               WellKnownSymbolsStore$1[name] = createWellKnownSymbol('Symbol.' + name);
808             }
809           } return WellKnownSymbolsStore$1[name];
810         };
811
812         var f$2 = wellKnownSymbol;
813
814         var wellKnownSymbolWrapped = {
815                 f: f$2
816         };
817
818         var defineProperty$9 = objectDefineProperty.f;
819
820         var defineWellKnownSymbol = function (NAME) {
821           var Symbol = path.Symbol || (path.Symbol = {});
822           if (!has$1(Symbol, NAME)) defineProperty$9(Symbol, NAME, {
823             value: wellKnownSymbolWrapped.f(NAME)
824           });
825         };
826
827         // `Symbol.iterator` well-known symbol
828         // https://tc39.es/ecma262/#sec-symbol.iterator
829         defineWellKnownSymbol('iterator');
830
831         // `Object.keys` method
832         // https://tc39.es/ecma262/#sec-object.keys
833         // eslint-disable-next-line es/no-object-keys -- safe
834         var objectKeys = Object.keys || function keys(O) {
835           return objectKeysInternal(O, enumBugKeys);
836         };
837
838         // `Object.defineProperties` method
839         // https://tc39.es/ecma262/#sec-object.defineproperties
840         // eslint-disable-next-line es/no-object-defineproperties -- safe
841         var objectDefineProperties = descriptors ? Object.defineProperties : function defineProperties(O, Properties) {
842           anObject(O);
843           var keys = objectKeys(Properties);
844           var length = keys.length;
845           var index = 0;
846           var key;
847           while (length > index) objectDefineProperty.f(O, key = keys[index++], Properties[key]);
848           return O;
849         };
850
851         var html = getBuiltIn('document', 'documentElement');
852
853         var GT = '>';
854         var LT = '<';
855         var PROTOTYPE$2 = 'prototype';
856         var SCRIPT = 'script';
857         var IE_PROTO$1 = sharedKey('IE_PROTO');
858
859         var EmptyConstructor = function () { /* empty */ };
860
861         var scriptTag = function (content) {
862           return LT + SCRIPT + GT + content + LT + '/' + SCRIPT + GT;
863         };
864
865         // Create object with fake `null` prototype: use ActiveX Object with cleared prototype
866         var NullProtoObjectViaActiveX = function (activeXDocument) {
867           activeXDocument.write(scriptTag(''));
868           activeXDocument.close();
869           var temp = activeXDocument.parentWindow.Object;
870           activeXDocument = null; // avoid memory leak
871           return temp;
872         };
873
874         // Create object with fake `null` prototype: use iframe Object with cleared prototype
875         var NullProtoObjectViaIFrame = function () {
876           // Thrash, waste and sodomy: IE GC bug
877           var iframe = documentCreateElement('iframe');
878           var JS = 'java' + SCRIPT + ':';
879           var iframeDocument;
880           iframe.style.display = 'none';
881           html.appendChild(iframe);
882           // https://github.com/zloirock/core-js/issues/475
883           iframe.src = String(JS);
884           iframeDocument = iframe.contentWindow.document;
885           iframeDocument.open();
886           iframeDocument.write(scriptTag('document.F=Object'));
887           iframeDocument.close();
888           return iframeDocument.F;
889         };
890
891         // Check for document.domain and active x support
892         // No need to use active x approach when document.domain is not set
893         // see https://github.com/es-shims/es5-shim/issues/150
894         // variation of https://github.com/kitcambridge/es5-shim/commit/4f738ac066346
895         // avoid IE GC bug
896         var activeXDocument;
897         var NullProtoObject = function () {
898           try {
899             /* global ActiveXObject -- old IE */
900             activeXDocument = document.domain && new ActiveXObject('htmlfile');
901           } catch (error) { /* ignore */ }
902           NullProtoObject = activeXDocument ? NullProtoObjectViaActiveX(activeXDocument) : NullProtoObjectViaIFrame();
903           var length = enumBugKeys.length;
904           while (length--) delete NullProtoObject[PROTOTYPE$2][enumBugKeys[length]];
905           return NullProtoObject();
906         };
907
908         hiddenKeys$1[IE_PROTO$1] = true;
909
910         // `Object.create` method
911         // https://tc39.es/ecma262/#sec-object.create
912         var objectCreate = Object.create || function create(O, Properties) {
913           var result;
914           if (O !== null) {
915             EmptyConstructor[PROTOTYPE$2] = anObject(O);
916             result = new EmptyConstructor();
917             EmptyConstructor[PROTOTYPE$2] = null;
918             // add "__proto__" for Object.getPrototypeOf polyfill
919             result[IE_PROTO$1] = O;
920           } else result = NullProtoObject();
921           return Properties === undefined ? result : objectDefineProperties(result, Properties);
922         };
923
924         var UNSCOPABLES = wellKnownSymbol('unscopables');
925         var ArrayPrototype$1 = Array.prototype;
926
927         // Array.prototype[@@unscopables]
928         // https://tc39.es/ecma262/#sec-array.prototype-@@unscopables
929         if (ArrayPrototype$1[UNSCOPABLES] == undefined) {
930           objectDefineProperty.f(ArrayPrototype$1, UNSCOPABLES, {
931             configurable: true,
932             value: objectCreate(null)
933           });
934         }
935
936         // add a key to Array.prototype[@@unscopables]
937         var addToUnscopables = function (key) {
938           ArrayPrototype$1[UNSCOPABLES][key] = true;
939         };
940
941         var iterators = {};
942
943         var correctPrototypeGetter = !fails(function () {
944           function F() { /* empty */ }
945           F.prototype.constructor = null;
946           // eslint-disable-next-line es/no-object-getprototypeof -- required for testing
947           return Object.getPrototypeOf(new F()) !== F.prototype;
948         });
949
950         var IE_PROTO = sharedKey('IE_PROTO');
951         var ObjectPrototype$3 = Object.prototype;
952
953         // `Object.getPrototypeOf` method
954         // https://tc39.es/ecma262/#sec-object.getprototypeof
955         // eslint-disable-next-line es/no-object-getprototypeof -- safe
956         var objectGetPrototypeOf = correctPrototypeGetter ? Object.getPrototypeOf : function (O) {
957           O = toObject(O);
958           if (has$1(O, IE_PROTO)) return O[IE_PROTO];
959           if (typeof O.constructor == 'function' && O instanceof O.constructor) {
960             return O.constructor.prototype;
961           } return O instanceof Object ? ObjectPrototype$3 : null;
962         };
963
964         var ITERATOR$8 = wellKnownSymbol('iterator');
965         var BUGGY_SAFARI_ITERATORS$1 = false;
966
967         var returnThis$2 = function () { return this; };
968
969         // `%IteratorPrototype%` object
970         // https://tc39.es/ecma262/#sec-%iteratorprototype%-object
971         var IteratorPrototype$2, PrototypeOfArrayIteratorPrototype, arrayIterator;
972
973         /* eslint-disable es/no-array-prototype-keys -- safe */
974         if ([].keys) {
975           arrayIterator = [].keys();
976           // Safari 8 has buggy iterators w/o `next`
977           if (!('next' in arrayIterator)) BUGGY_SAFARI_ITERATORS$1 = true;
978           else {
979             PrototypeOfArrayIteratorPrototype = objectGetPrototypeOf(objectGetPrototypeOf(arrayIterator));
980             if (PrototypeOfArrayIteratorPrototype !== Object.prototype) IteratorPrototype$2 = PrototypeOfArrayIteratorPrototype;
981           }
982         }
983
984         var NEW_ITERATOR_PROTOTYPE = IteratorPrototype$2 == undefined || fails(function () {
985           var test = {};
986           // FF44- legacy iterators case
987           return IteratorPrototype$2[ITERATOR$8].call(test) !== test;
988         });
989
990         if (NEW_ITERATOR_PROTOTYPE) IteratorPrototype$2 = {};
991
992         // `%IteratorPrototype%[@@iterator]()` method
993         // https://tc39.es/ecma262/#sec-%iteratorprototype%-@@iterator
994         if (!has$1(IteratorPrototype$2, ITERATOR$8)) {
995           createNonEnumerableProperty(IteratorPrototype$2, ITERATOR$8, returnThis$2);
996         }
997
998         var iteratorsCore = {
999           IteratorPrototype: IteratorPrototype$2,
1000           BUGGY_SAFARI_ITERATORS: BUGGY_SAFARI_ITERATORS$1
1001         };
1002
1003         var defineProperty$8 = objectDefineProperty.f;
1004
1005
1006
1007         var TO_STRING_TAG$4 = wellKnownSymbol('toStringTag');
1008
1009         var setToStringTag = function (it, TAG, STATIC) {
1010           if (it && !has$1(it = STATIC ? it : it.prototype, TO_STRING_TAG$4)) {
1011             defineProperty$8(it, TO_STRING_TAG$4, { configurable: true, value: TAG });
1012           }
1013         };
1014
1015         var IteratorPrototype$1 = iteratorsCore.IteratorPrototype;
1016
1017
1018
1019
1020
1021         var returnThis$1 = function () { return this; };
1022
1023         var createIteratorConstructor = function (IteratorConstructor, NAME, next) {
1024           var TO_STRING_TAG = NAME + ' Iterator';
1025           IteratorConstructor.prototype = objectCreate(IteratorPrototype$1, { next: createPropertyDescriptor(1, next) });
1026           setToStringTag(IteratorConstructor, TO_STRING_TAG, false);
1027           iterators[TO_STRING_TAG] = returnThis$1;
1028           return IteratorConstructor;
1029         };
1030
1031         var aPossiblePrototype = function (it) {
1032           if (!isObject$4(it) && it !== null) {
1033             throw TypeError("Can't set " + String(it) + ' as a prototype');
1034           } return it;
1035         };
1036
1037         /* eslint-disable no-proto -- safe */
1038
1039         // `Object.setPrototypeOf` method
1040         // https://tc39.es/ecma262/#sec-object.setprototypeof
1041         // Works with __proto__ only. Old v8 can't work with null proto objects.
1042         // eslint-disable-next-line es/no-object-setprototypeof -- safe
1043         var objectSetPrototypeOf = Object.setPrototypeOf || ('__proto__' in {} ? function () {
1044           var CORRECT_SETTER = false;
1045           var test = {};
1046           var setter;
1047           try {
1048             // eslint-disable-next-line es/no-object-getownpropertydescriptor -- safe
1049             setter = Object.getOwnPropertyDescriptor(Object.prototype, '__proto__').set;
1050             setter.call(test, []);
1051             CORRECT_SETTER = test instanceof Array;
1052           } catch (error) { /* empty */ }
1053           return function setPrototypeOf(O, proto) {
1054             anObject(O);
1055             aPossiblePrototype(proto);
1056             if (CORRECT_SETTER) setter.call(O, proto);
1057             else O.__proto__ = proto;
1058             return O;
1059           };
1060         }() : undefined);
1061
1062         var IteratorPrototype = iteratorsCore.IteratorPrototype;
1063         var BUGGY_SAFARI_ITERATORS = iteratorsCore.BUGGY_SAFARI_ITERATORS;
1064         var ITERATOR$7 = wellKnownSymbol('iterator');
1065         var KEYS = 'keys';
1066         var VALUES = 'values';
1067         var ENTRIES = 'entries';
1068
1069         var returnThis = function () { return this; };
1070
1071         var defineIterator = function (Iterable, NAME, IteratorConstructor, next, DEFAULT, IS_SET, FORCED) {
1072           createIteratorConstructor(IteratorConstructor, NAME, next);
1073
1074           var getIterationMethod = function (KIND) {
1075             if (KIND === DEFAULT && defaultIterator) return defaultIterator;
1076             if (!BUGGY_SAFARI_ITERATORS && KIND in IterablePrototype) return IterablePrototype[KIND];
1077             switch (KIND) {
1078               case KEYS: return function keys() { return new IteratorConstructor(this, KIND); };
1079               case VALUES: return function values() { return new IteratorConstructor(this, KIND); };
1080               case ENTRIES: return function entries() { return new IteratorConstructor(this, KIND); };
1081             } return function () { return new IteratorConstructor(this); };
1082           };
1083
1084           var TO_STRING_TAG = NAME + ' Iterator';
1085           var INCORRECT_VALUES_NAME = false;
1086           var IterablePrototype = Iterable.prototype;
1087           var nativeIterator = IterablePrototype[ITERATOR$7]
1088             || IterablePrototype['@@iterator']
1089             || DEFAULT && IterablePrototype[DEFAULT];
1090           var defaultIterator = !BUGGY_SAFARI_ITERATORS && nativeIterator || getIterationMethod(DEFAULT);
1091           var anyNativeIterator = NAME == 'Array' ? IterablePrototype.entries || nativeIterator : nativeIterator;
1092           var CurrentIteratorPrototype, methods, KEY;
1093
1094           // fix native
1095           if (anyNativeIterator) {
1096             CurrentIteratorPrototype = objectGetPrototypeOf(anyNativeIterator.call(new Iterable()));
1097             if (IteratorPrototype !== Object.prototype && CurrentIteratorPrototype.next) {
1098               if (objectGetPrototypeOf(CurrentIteratorPrototype) !== IteratorPrototype) {
1099                 if (objectSetPrototypeOf) {
1100                   objectSetPrototypeOf(CurrentIteratorPrototype, IteratorPrototype);
1101                 } else if (typeof CurrentIteratorPrototype[ITERATOR$7] != 'function') {
1102                   createNonEnumerableProperty(CurrentIteratorPrototype, ITERATOR$7, returnThis);
1103                 }
1104               }
1105               // Set @@toStringTag to native iterators
1106               setToStringTag(CurrentIteratorPrototype, TO_STRING_TAG, true);
1107             }
1108           }
1109
1110           // fix Array.prototype.{ values, @@iterator }.name in V8 / FF
1111           if (DEFAULT == VALUES && nativeIterator && nativeIterator.name !== VALUES) {
1112             INCORRECT_VALUES_NAME = true;
1113             defaultIterator = function values() { return nativeIterator.call(this); };
1114           }
1115
1116           // define iterator
1117           if (IterablePrototype[ITERATOR$7] !== defaultIterator) {
1118             createNonEnumerableProperty(IterablePrototype, ITERATOR$7, defaultIterator);
1119           }
1120           iterators[NAME] = defaultIterator;
1121
1122           // export additional methods
1123           if (DEFAULT) {
1124             methods = {
1125               values: getIterationMethod(VALUES),
1126               keys: IS_SET ? defaultIterator : getIterationMethod(KEYS),
1127               entries: getIterationMethod(ENTRIES)
1128             };
1129             if (FORCED) for (KEY in methods) {
1130               if (BUGGY_SAFARI_ITERATORS || INCORRECT_VALUES_NAME || !(KEY in IterablePrototype)) {
1131                 redefine(IterablePrototype, KEY, methods[KEY]);
1132               }
1133             } else _export({ target: NAME, proto: true, forced: BUGGY_SAFARI_ITERATORS || INCORRECT_VALUES_NAME }, methods);
1134           }
1135
1136           return methods;
1137         };
1138
1139         var ARRAY_ITERATOR = 'Array Iterator';
1140         var setInternalState$7 = internalState.set;
1141         var getInternalState$5 = internalState.getterFor(ARRAY_ITERATOR);
1142
1143         // `Array.prototype.entries` method
1144         // https://tc39.es/ecma262/#sec-array.prototype.entries
1145         // `Array.prototype.keys` method
1146         // https://tc39.es/ecma262/#sec-array.prototype.keys
1147         // `Array.prototype.values` method
1148         // https://tc39.es/ecma262/#sec-array.prototype.values
1149         // `Array.prototype[@@iterator]` method
1150         // https://tc39.es/ecma262/#sec-array.prototype-@@iterator
1151         // `CreateArrayIterator` internal method
1152         // https://tc39.es/ecma262/#sec-createarrayiterator
1153         var es_array_iterator = defineIterator(Array, 'Array', function (iterated, kind) {
1154           setInternalState$7(this, {
1155             type: ARRAY_ITERATOR,
1156             target: toIndexedObject(iterated), // target
1157             index: 0,                          // next index
1158             kind: kind                         // kind
1159           });
1160         // `%ArrayIteratorPrototype%.next` method
1161         // https://tc39.es/ecma262/#sec-%arrayiteratorprototype%.next
1162         }, function () {
1163           var state = getInternalState$5(this);
1164           var target = state.target;
1165           var kind = state.kind;
1166           var index = state.index++;
1167           if (!target || index >= target.length) {
1168             state.target = undefined;
1169             return { value: undefined, done: true };
1170           }
1171           if (kind == 'keys') return { value: index, done: false };
1172           if (kind == 'values') return { value: target[index], done: false };
1173           return { value: [index, target[index]], done: false };
1174         }, 'values');
1175
1176         // argumentsList[@@iterator] is %ArrayProto_values%
1177         // https://tc39.es/ecma262/#sec-createunmappedargumentsobject
1178         // https://tc39.es/ecma262/#sec-createmappedargumentsobject
1179         iterators.Arguments = iterators.Array;
1180
1181         // https://tc39.es/ecma262/#sec-array.prototype-@@unscopables
1182         addToUnscopables('keys');
1183         addToUnscopables('values');
1184         addToUnscopables('entries');
1185
1186         var TO_STRING_TAG$3 = wellKnownSymbol('toStringTag');
1187         var test$2 = {};
1188
1189         test$2[TO_STRING_TAG$3] = 'z';
1190
1191         var toStringTagSupport = String(test$2) === '[object z]';
1192
1193         var TO_STRING_TAG$2 = wellKnownSymbol('toStringTag');
1194         // ES3 wrong here
1195         var CORRECT_ARGUMENTS = classofRaw(function () { return arguments; }()) == 'Arguments';
1196
1197         // fallback for IE11 Script Access Denied error
1198         var tryGet = function (it, key) {
1199           try {
1200             return it[key];
1201           } catch (error) { /* empty */ }
1202         };
1203
1204         // getting tag from ES6+ `Object.prototype.toString`
1205         var classof = toStringTagSupport ? classofRaw : function (it) {
1206           var O, tag, result;
1207           return it === undefined ? 'Undefined' : it === null ? 'Null'
1208             // @@toStringTag case
1209             : typeof (tag = tryGet(O = Object(it), TO_STRING_TAG$2)) == 'string' ? tag
1210             // builtinTag case
1211             : CORRECT_ARGUMENTS ? classofRaw(O)
1212             // ES3 arguments fallback
1213             : (result = classofRaw(O)) == 'Object' && typeof O.callee == 'function' ? 'Arguments' : result;
1214         };
1215
1216         // `Object.prototype.toString` method implementation
1217         // https://tc39.es/ecma262/#sec-object.prototype.tostring
1218         var objectToString$1 = toStringTagSupport ? {}.toString : function toString() {
1219           return '[object ' + classof(this) + ']';
1220         };
1221
1222         // `Object.prototype.toString` method
1223         // https://tc39.es/ecma262/#sec-object.prototype.tostring
1224         if (!toStringTagSupport) {
1225           redefine(Object.prototype, 'toString', objectToString$1, { unsafe: true });
1226         }
1227
1228         // `String.prototype.{ codePointAt, at }` methods implementation
1229         var createMethod$5 = function (CONVERT_TO_STRING) {
1230           return function ($this, pos) {
1231             var S = String(requireObjectCoercible($this));
1232             var position = toInteger(pos);
1233             var size = S.length;
1234             var first, second;
1235             if (position < 0 || position >= size) return CONVERT_TO_STRING ? '' : undefined;
1236             first = S.charCodeAt(position);
1237             return first < 0xD800 || first > 0xDBFF || position + 1 === size
1238               || (second = S.charCodeAt(position + 1)) < 0xDC00 || second > 0xDFFF
1239                 ? CONVERT_TO_STRING ? S.charAt(position) : first
1240                 : CONVERT_TO_STRING ? S.slice(position, position + 2) : (first - 0xD800 << 10) + (second - 0xDC00) + 0x10000;
1241           };
1242         };
1243
1244         var stringMultibyte = {
1245           // `String.prototype.codePointAt` method
1246           // https://tc39.es/ecma262/#sec-string.prototype.codepointat
1247           codeAt: createMethod$5(false),
1248           // `String.prototype.at` method
1249           // https://github.com/mathiasbynens/String.prototype.at
1250           charAt: createMethod$5(true)
1251         };
1252
1253         var charAt$1 = stringMultibyte.charAt;
1254
1255
1256
1257         var STRING_ITERATOR = 'String Iterator';
1258         var setInternalState$6 = internalState.set;
1259         var getInternalState$4 = internalState.getterFor(STRING_ITERATOR);
1260
1261         // `String.prototype[@@iterator]` method
1262         // https://tc39.es/ecma262/#sec-string.prototype-@@iterator
1263         defineIterator(String, 'String', function (iterated) {
1264           setInternalState$6(this, {
1265             type: STRING_ITERATOR,
1266             string: String(iterated),
1267             index: 0
1268           });
1269         // `%StringIteratorPrototype%.next` method
1270         // https://tc39.es/ecma262/#sec-%stringiteratorprototype%.next
1271         }, function next() {
1272           var state = getInternalState$4(this);
1273           var string = state.string;
1274           var index = state.index;
1275           var point;
1276           if (index >= string.length) return { value: undefined, done: true };
1277           point = charAt$1(string, index);
1278           state.index += point.length;
1279           return { value: point, done: false };
1280         });
1281
1282         // iterable DOM collections
1283         // flag - `iterable` interface - 'entries', 'keys', 'values', 'forEach' methods
1284         var domIterables = {
1285           CSSRuleList: 0,
1286           CSSStyleDeclaration: 0,
1287           CSSValueList: 0,
1288           ClientRectList: 0,
1289           DOMRectList: 0,
1290           DOMStringList: 0,
1291           DOMTokenList: 1,
1292           DataTransferItemList: 0,
1293           FileList: 0,
1294           HTMLAllCollection: 0,
1295           HTMLCollection: 0,
1296           HTMLFormElement: 0,
1297           HTMLSelectElement: 0,
1298           MediaList: 0,
1299           MimeTypeArray: 0,
1300           NamedNodeMap: 0,
1301           NodeList: 1,
1302           PaintRequestList: 0,
1303           Plugin: 0,
1304           PluginArray: 0,
1305           SVGLengthList: 0,
1306           SVGNumberList: 0,
1307           SVGPathSegList: 0,
1308           SVGPointList: 0,
1309           SVGStringList: 0,
1310           SVGTransformList: 0,
1311           SourceBufferList: 0,
1312           StyleSheetList: 0,
1313           TextTrackCueList: 0,
1314           TextTrackList: 0,
1315           TouchList: 0
1316         };
1317
1318         var ITERATOR$6 = wellKnownSymbol('iterator');
1319         var TO_STRING_TAG$1 = wellKnownSymbol('toStringTag');
1320         var ArrayValues = es_array_iterator.values;
1321
1322         for (var COLLECTION_NAME$1 in domIterables) {
1323           var Collection$1 = global$2[COLLECTION_NAME$1];
1324           var CollectionPrototype$1 = Collection$1 && Collection$1.prototype;
1325           if (CollectionPrototype$1) {
1326             // some Chrome versions have non-configurable methods on DOMTokenList
1327             if (CollectionPrototype$1[ITERATOR$6] !== ArrayValues) try {
1328               createNonEnumerableProperty(CollectionPrototype$1, ITERATOR$6, ArrayValues);
1329             } catch (error) {
1330               CollectionPrototype$1[ITERATOR$6] = ArrayValues;
1331             }
1332             if (!CollectionPrototype$1[TO_STRING_TAG$1]) {
1333               createNonEnumerableProperty(CollectionPrototype$1, TO_STRING_TAG$1, COLLECTION_NAME$1);
1334             }
1335             if (domIterables[COLLECTION_NAME$1]) for (var METHOD_NAME in es_array_iterator) {
1336               // some Chrome versions have non-configurable methods on DOMTokenList
1337               if (CollectionPrototype$1[METHOD_NAME] !== es_array_iterator[METHOD_NAME]) try {
1338                 createNonEnumerableProperty(CollectionPrototype$1, METHOD_NAME, es_array_iterator[METHOD_NAME]);
1339               } catch (error) {
1340                 CollectionPrototype$1[METHOD_NAME] = es_array_iterator[METHOD_NAME];
1341               }
1342             }
1343           }
1344         }
1345
1346         // `IsArray` abstract operation
1347         // https://tc39.es/ecma262/#sec-isarray
1348         // eslint-disable-next-line es/no-array-isarray -- safe
1349         var isArray = Array.isArray || function isArray(arg) {
1350           return classofRaw(arg) == 'Array';
1351         };
1352
1353         /* eslint-disable es/no-object-getownpropertynames -- safe */
1354
1355         var $getOwnPropertyNames$1 = objectGetOwnPropertyNames.f;
1356
1357         var toString = {}.toString;
1358
1359         var windowNames = typeof window == 'object' && window && Object.getOwnPropertyNames
1360           ? Object.getOwnPropertyNames(window) : [];
1361
1362         var getWindowNames = function (it) {
1363           try {
1364             return $getOwnPropertyNames$1(it);
1365           } catch (error) {
1366             return windowNames.slice();
1367           }
1368         };
1369
1370         // fallback for IE11 buggy Object.getOwnPropertyNames with iframe and window
1371         var f$1 = function getOwnPropertyNames(it) {
1372           return windowNames && toString.call(it) == '[object Window]'
1373             ? getWindowNames(it)
1374             : $getOwnPropertyNames$1(toIndexedObject(it));
1375         };
1376
1377         var objectGetOwnPropertyNamesExternal = {
1378                 f: f$1
1379         };
1380
1381         var aFunction = function (it) {
1382           if (typeof it != 'function') {
1383             throw TypeError(String(it) + ' is not a function');
1384           } return it;
1385         };
1386
1387         // optional / simple context binding
1388         var functionBindContext = function (fn, that, length) {
1389           aFunction(fn);
1390           if (that === undefined) return fn;
1391           switch (length) {
1392             case 0: return function () {
1393               return fn.call(that);
1394             };
1395             case 1: return function (a) {
1396               return fn.call(that, a);
1397             };
1398             case 2: return function (a, b) {
1399               return fn.call(that, a, b);
1400             };
1401             case 3: return function (a, b, c) {
1402               return fn.call(that, a, b, c);
1403             };
1404           }
1405           return function (/* ...args */) {
1406             return fn.apply(that, arguments);
1407           };
1408         };
1409
1410         var SPECIES$6 = wellKnownSymbol('species');
1411
1412         // `ArraySpeciesCreate` abstract operation
1413         // https://tc39.es/ecma262/#sec-arrayspeciescreate
1414         var arraySpeciesCreate = function (originalArray, length) {
1415           var C;
1416           if (isArray(originalArray)) {
1417             C = originalArray.constructor;
1418             // cross-realm fallback
1419             if (typeof C == 'function' && (C === Array || isArray(C.prototype))) C = undefined;
1420             else if (isObject$4(C)) {
1421               C = C[SPECIES$6];
1422               if (C === null) C = undefined;
1423             }
1424           } return new (C === undefined ? Array : C)(length === 0 ? 0 : length);
1425         };
1426
1427         var push = [].push;
1428
1429         // `Array.prototype.{ forEach, map, filter, some, every, find, findIndex, filterOut }` methods implementation
1430         var createMethod$4 = function (TYPE) {
1431           var IS_MAP = TYPE == 1;
1432           var IS_FILTER = TYPE == 2;
1433           var IS_SOME = TYPE == 3;
1434           var IS_EVERY = TYPE == 4;
1435           var IS_FIND_INDEX = TYPE == 6;
1436           var IS_FILTER_OUT = TYPE == 7;
1437           var NO_HOLES = TYPE == 5 || IS_FIND_INDEX;
1438           return function ($this, callbackfn, that, specificCreate) {
1439             var O = toObject($this);
1440             var self = indexedObject(O);
1441             var boundFunction = functionBindContext(callbackfn, that, 3);
1442             var length = toLength(self.length);
1443             var index = 0;
1444             var create = specificCreate || arraySpeciesCreate;
1445             var target = IS_MAP ? create($this, length) : IS_FILTER || IS_FILTER_OUT ? create($this, 0) : undefined;
1446             var value, result;
1447             for (;length > index; index++) if (NO_HOLES || index in self) {
1448               value = self[index];
1449               result = boundFunction(value, index, O);
1450               if (TYPE) {
1451                 if (IS_MAP) target[index] = result; // map
1452                 else if (result) switch (TYPE) {
1453                   case 3: return true;              // some
1454                   case 5: return value;             // find
1455                   case 6: return index;             // findIndex
1456                   case 2: push.call(target, value); // filter
1457                 } else switch (TYPE) {
1458                   case 4: return false;             // every
1459                   case 7: push.call(target, value); // filterOut
1460                 }
1461               }
1462             }
1463             return IS_FIND_INDEX ? -1 : IS_SOME || IS_EVERY ? IS_EVERY : target;
1464           };
1465         };
1466
1467         var arrayIteration = {
1468           // `Array.prototype.forEach` method
1469           // https://tc39.es/ecma262/#sec-array.prototype.foreach
1470           forEach: createMethod$4(0),
1471           // `Array.prototype.map` method
1472           // https://tc39.es/ecma262/#sec-array.prototype.map
1473           map: createMethod$4(1),
1474           // `Array.prototype.filter` method
1475           // https://tc39.es/ecma262/#sec-array.prototype.filter
1476           filter: createMethod$4(2),
1477           // `Array.prototype.some` method
1478           // https://tc39.es/ecma262/#sec-array.prototype.some
1479           some: createMethod$4(3),
1480           // `Array.prototype.every` method
1481           // https://tc39.es/ecma262/#sec-array.prototype.every
1482           every: createMethod$4(4),
1483           // `Array.prototype.find` method
1484           // https://tc39.es/ecma262/#sec-array.prototype.find
1485           find: createMethod$4(5),
1486           // `Array.prototype.findIndex` method
1487           // https://tc39.es/ecma262/#sec-array.prototype.findIndex
1488           findIndex: createMethod$4(6),
1489           // `Array.prototype.filterOut` method
1490           // https://github.com/tc39/proposal-array-filtering
1491           filterOut: createMethod$4(7)
1492         };
1493
1494         var $forEach$2 = arrayIteration.forEach;
1495
1496         var HIDDEN = sharedKey('hidden');
1497         var SYMBOL = 'Symbol';
1498         var PROTOTYPE$1 = 'prototype';
1499         var TO_PRIMITIVE = wellKnownSymbol('toPrimitive');
1500         var setInternalState$5 = internalState.set;
1501         var getInternalState$3 = internalState.getterFor(SYMBOL);
1502         var ObjectPrototype$2 = Object[PROTOTYPE$1];
1503         var $Symbol = global$2.Symbol;
1504         var $stringify = getBuiltIn('JSON', 'stringify');
1505         var nativeGetOwnPropertyDescriptor$1 = objectGetOwnPropertyDescriptor.f;
1506         var nativeDefineProperty = objectDefineProperty.f;
1507         var nativeGetOwnPropertyNames = objectGetOwnPropertyNamesExternal.f;
1508         var nativePropertyIsEnumerable = objectPropertyIsEnumerable.f;
1509         var AllSymbols = shared('symbols');
1510         var ObjectPrototypeSymbols = shared('op-symbols');
1511         var StringToSymbolRegistry = shared('string-to-symbol-registry');
1512         var SymbolToStringRegistry = shared('symbol-to-string-registry');
1513         var WellKnownSymbolsStore = shared('wks');
1514         var QObject = global$2.QObject;
1515         // Don't use setters in Qt Script, https://github.com/zloirock/core-js/issues/173
1516         var USE_SETTER = !QObject || !QObject[PROTOTYPE$1] || !QObject[PROTOTYPE$1].findChild;
1517
1518         // fallback for old Android, https://code.google.com/p/v8/issues/detail?id=687
1519         var setSymbolDescriptor = descriptors && fails(function () {
1520           return objectCreate(nativeDefineProperty({}, 'a', {
1521             get: function () { return nativeDefineProperty(this, 'a', { value: 7 }).a; }
1522           })).a != 7;
1523         }) ? function (O, P, Attributes) {
1524           var ObjectPrototypeDescriptor = nativeGetOwnPropertyDescriptor$1(ObjectPrototype$2, P);
1525           if (ObjectPrototypeDescriptor) delete ObjectPrototype$2[P];
1526           nativeDefineProperty(O, P, Attributes);
1527           if (ObjectPrototypeDescriptor && O !== ObjectPrototype$2) {
1528             nativeDefineProperty(ObjectPrototype$2, P, ObjectPrototypeDescriptor);
1529           }
1530         } : nativeDefineProperty;
1531
1532         var wrap$2 = function (tag, description) {
1533           var symbol = AllSymbols[tag] = objectCreate($Symbol[PROTOTYPE$1]);
1534           setInternalState$5(symbol, {
1535             type: SYMBOL,
1536             tag: tag,
1537             description: description
1538           });
1539           if (!descriptors) symbol.description = description;
1540           return symbol;
1541         };
1542
1543         var isSymbol$1 = useSymbolAsUid ? function (it) {
1544           return typeof it == 'symbol';
1545         } : function (it) {
1546           return Object(it) instanceof $Symbol;
1547         };
1548
1549         var $defineProperty = function defineProperty(O, P, Attributes) {
1550           if (O === ObjectPrototype$2) $defineProperty(ObjectPrototypeSymbols, P, Attributes);
1551           anObject(O);
1552           var key = toPrimitive(P, true);
1553           anObject(Attributes);
1554           if (has$1(AllSymbols, key)) {
1555             if (!Attributes.enumerable) {
1556               if (!has$1(O, HIDDEN)) nativeDefineProperty(O, HIDDEN, createPropertyDescriptor(1, {}));
1557               O[HIDDEN][key] = true;
1558             } else {
1559               if (has$1(O, HIDDEN) && O[HIDDEN][key]) O[HIDDEN][key] = false;
1560               Attributes = objectCreate(Attributes, { enumerable: createPropertyDescriptor(0, false) });
1561             } return setSymbolDescriptor(O, key, Attributes);
1562           } return nativeDefineProperty(O, key, Attributes);
1563         };
1564
1565         var $defineProperties = function defineProperties(O, Properties) {
1566           anObject(O);
1567           var properties = toIndexedObject(Properties);
1568           var keys = objectKeys(properties).concat($getOwnPropertySymbols(properties));
1569           $forEach$2(keys, function (key) {
1570             if (!descriptors || $propertyIsEnumerable.call(properties, key)) $defineProperty(O, key, properties[key]);
1571           });
1572           return O;
1573         };
1574
1575         var $create = function create(O, Properties) {
1576           return Properties === undefined ? objectCreate(O) : $defineProperties(objectCreate(O), Properties);
1577         };
1578
1579         var $propertyIsEnumerable = function propertyIsEnumerable(V) {
1580           var P = toPrimitive(V, true);
1581           var enumerable = nativePropertyIsEnumerable.call(this, P);
1582           if (this === ObjectPrototype$2 && has$1(AllSymbols, P) && !has$1(ObjectPrototypeSymbols, P)) return false;
1583           return enumerable || !has$1(this, P) || !has$1(AllSymbols, P) || has$1(this, HIDDEN) && this[HIDDEN][P] ? enumerable : true;
1584         };
1585
1586         var $getOwnPropertyDescriptor = function getOwnPropertyDescriptor(O, P) {
1587           var it = toIndexedObject(O);
1588           var key = toPrimitive(P, true);
1589           if (it === ObjectPrototype$2 && has$1(AllSymbols, key) && !has$1(ObjectPrototypeSymbols, key)) return;
1590           var descriptor = nativeGetOwnPropertyDescriptor$1(it, key);
1591           if (descriptor && has$1(AllSymbols, key) && !(has$1(it, HIDDEN) && it[HIDDEN][key])) {
1592             descriptor.enumerable = true;
1593           }
1594           return descriptor;
1595         };
1596
1597         var $getOwnPropertyNames = function getOwnPropertyNames(O) {
1598           var names = nativeGetOwnPropertyNames(toIndexedObject(O));
1599           var result = [];
1600           $forEach$2(names, function (key) {
1601             if (!has$1(AllSymbols, key) && !has$1(hiddenKeys$1, key)) result.push(key);
1602           });
1603           return result;
1604         };
1605
1606         var $getOwnPropertySymbols = function getOwnPropertySymbols(O) {
1607           var IS_OBJECT_PROTOTYPE = O === ObjectPrototype$2;
1608           var names = nativeGetOwnPropertyNames(IS_OBJECT_PROTOTYPE ? ObjectPrototypeSymbols : toIndexedObject(O));
1609           var result = [];
1610           $forEach$2(names, function (key) {
1611             if (has$1(AllSymbols, key) && (!IS_OBJECT_PROTOTYPE || has$1(ObjectPrototype$2, key))) {
1612               result.push(AllSymbols[key]);
1613             }
1614           });
1615           return result;
1616         };
1617
1618         // `Symbol` constructor
1619         // https://tc39.es/ecma262/#sec-symbol-constructor
1620         if (!nativeSymbol) {
1621           $Symbol = function Symbol() {
1622             if (this instanceof $Symbol) throw TypeError('Symbol is not a constructor');
1623             var description = !arguments.length || arguments[0] === undefined ? undefined : String(arguments[0]);
1624             var tag = uid(description);
1625             var setter = function (value) {
1626               if (this === ObjectPrototype$2) setter.call(ObjectPrototypeSymbols, value);
1627               if (has$1(this, HIDDEN) && has$1(this[HIDDEN], tag)) this[HIDDEN][tag] = false;
1628               setSymbolDescriptor(this, tag, createPropertyDescriptor(1, value));
1629             };
1630             if (descriptors && USE_SETTER) setSymbolDescriptor(ObjectPrototype$2, tag, { configurable: true, set: setter });
1631             return wrap$2(tag, description);
1632           };
1633
1634           redefine($Symbol[PROTOTYPE$1], 'toString', function toString() {
1635             return getInternalState$3(this).tag;
1636           });
1637
1638           redefine($Symbol, 'withoutSetter', function (description) {
1639             return wrap$2(uid(description), description);
1640           });
1641
1642           objectPropertyIsEnumerable.f = $propertyIsEnumerable;
1643           objectDefineProperty.f = $defineProperty;
1644           objectGetOwnPropertyDescriptor.f = $getOwnPropertyDescriptor;
1645           objectGetOwnPropertyNames.f = objectGetOwnPropertyNamesExternal.f = $getOwnPropertyNames;
1646           objectGetOwnPropertySymbols.f = $getOwnPropertySymbols;
1647
1648           wellKnownSymbolWrapped.f = function (name) {
1649             return wrap$2(wellKnownSymbol(name), name);
1650           };
1651
1652           if (descriptors) {
1653             // https://github.com/tc39/proposal-Symbol-description
1654             nativeDefineProperty($Symbol[PROTOTYPE$1], 'description', {
1655               configurable: true,
1656               get: function description() {
1657                 return getInternalState$3(this).description;
1658               }
1659             });
1660             {
1661               redefine(ObjectPrototype$2, 'propertyIsEnumerable', $propertyIsEnumerable, { unsafe: true });
1662             }
1663           }
1664         }
1665
1666         _export({ global: true, wrap: true, forced: !nativeSymbol, sham: !nativeSymbol }, {
1667           Symbol: $Symbol
1668         });
1669
1670         $forEach$2(objectKeys(WellKnownSymbolsStore), function (name) {
1671           defineWellKnownSymbol(name);
1672         });
1673
1674         _export({ target: SYMBOL, stat: true, forced: !nativeSymbol }, {
1675           // `Symbol.for` method
1676           // https://tc39.es/ecma262/#sec-symbol.for
1677           'for': function (key) {
1678             var string = String(key);
1679             if (has$1(StringToSymbolRegistry, string)) return StringToSymbolRegistry[string];
1680             var symbol = $Symbol(string);
1681             StringToSymbolRegistry[string] = symbol;
1682             SymbolToStringRegistry[symbol] = string;
1683             return symbol;
1684           },
1685           // `Symbol.keyFor` method
1686           // https://tc39.es/ecma262/#sec-symbol.keyfor
1687           keyFor: function keyFor(sym) {
1688             if (!isSymbol$1(sym)) throw TypeError(sym + ' is not a symbol');
1689             if (has$1(SymbolToStringRegistry, sym)) return SymbolToStringRegistry[sym];
1690           },
1691           useSetter: function () { USE_SETTER = true; },
1692           useSimple: function () { USE_SETTER = false; }
1693         });
1694
1695         _export({ target: 'Object', stat: true, forced: !nativeSymbol, sham: !descriptors }, {
1696           // `Object.create` method
1697           // https://tc39.es/ecma262/#sec-object.create
1698           create: $create,
1699           // `Object.defineProperty` method
1700           // https://tc39.es/ecma262/#sec-object.defineproperty
1701           defineProperty: $defineProperty,
1702           // `Object.defineProperties` method
1703           // https://tc39.es/ecma262/#sec-object.defineproperties
1704           defineProperties: $defineProperties,
1705           // `Object.getOwnPropertyDescriptor` method
1706           // https://tc39.es/ecma262/#sec-object.getownpropertydescriptors
1707           getOwnPropertyDescriptor: $getOwnPropertyDescriptor
1708         });
1709
1710         _export({ target: 'Object', stat: true, forced: !nativeSymbol }, {
1711           // `Object.getOwnPropertyNames` method
1712           // https://tc39.es/ecma262/#sec-object.getownpropertynames
1713           getOwnPropertyNames: $getOwnPropertyNames,
1714           // `Object.getOwnPropertySymbols` method
1715           // https://tc39.es/ecma262/#sec-object.getownpropertysymbols
1716           getOwnPropertySymbols: $getOwnPropertySymbols
1717         });
1718
1719         // Chrome 38 and 39 `Object.getOwnPropertySymbols` fails on primitives
1720         // https://bugs.chromium.org/p/v8/issues/detail?id=3443
1721         _export({ target: 'Object', stat: true, forced: fails(function () { objectGetOwnPropertySymbols.f(1); }) }, {
1722           getOwnPropertySymbols: function getOwnPropertySymbols(it) {
1723             return objectGetOwnPropertySymbols.f(toObject(it));
1724           }
1725         });
1726
1727         // `JSON.stringify` method behavior with symbols
1728         // https://tc39.es/ecma262/#sec-json.stringify
1729         if ($stringify) {
1730           var FORCED_JSON_STRINGIFY = !nativeSymbol || fails(function () {
1731             var symbol = $Symbol();
1732             // MS Edge converts symbol values to JSON as {}
1733             return $stringify([symbol]) != '[null]'
1734               // WebKit converts symbol values to JSON as null
1735               || $stringify({ a: symbol }) != '{}'
1736               // V8 throws on boxed symbols
1737               || $stringify(Object(symbol)) != '{}';
1738           });
1739
1740           _export({ target: 'JSON', stat: true, forced: FORCED_JSON_STRINGIFY }, {
1741             // eslint-disable-next-line no-unused-vars -- required for `.length`
1742             stringify: function stringify(it, replacer, space) {
1743               var args = [it];
1744               var index = 1;
1745               var $replacer;
1746               while (arguments.length > index) args.push(arguments[index++]);
1747               $replacer = replacer;
1748               if (!isObject$4(replacer) && it === undefined || isSymbol$1(it)) return; // IE8 returns string on undefined
1749               if (!isArray(replacer)) replacer = function (key, value) {
1750                 if (typeof $replacer == 'function') value = $replacer.call(this, key, value);
1751                 if (!isSymbol$1(value)) return value;
1752               };
1753               args[1] = replacer;
1754               return $stringify.apply(null, args);
1755             }
1756           });
1757         }
1758
1759         // `Symbol.prototype[@@toPrimitive]` method
1760         // https://tc39.es/ecma262/#sec-symbol.prototype-@@toprimitive
1761         if (!$Symbol[PROTOTYPE$1][TO_PRIMITIVE]) {
1762           createNonEnumerableProperty($Symbol[PROTOTYPE$1], TO_PRIMITIVE, $Symbol[PROTOTYPE$1].valueOf);
1763         }
1764         // `Symbol.prototype[@@toStringTag]` property
1765         // https://tc39.es/ecma262/#sec-symbol.prototype-@@tostringtag
1766         setToStringTag($Symbol, SYMBOL);
1767
1768         hiddenKeys$1[HIDDEN] = true;
1769
1770         var defineProperty$7 = objectDefineProperty.f;
1771
1772
1773         var NativeSymbol = global$2.Symbol;
1774
1775         if (descriptors && typeof NativeSymbol == 'function' && (!('description' in NativeSymbol.prototype) ||
1776           // Safari 12 bug
1777           NativeSymbol().description !== undefined
1778         )) {
1779           var EmptyStringDescriptionStore = {};
1780           // wrap Symbol constructor for correct work with undefined description
1781           var SymbolWrapper = function Symbol() {
1782             var description = arguments.length < 1 || arguments[0] === undefined ? undefined : String(arguments[0]);
1783             var result = this instanceof SymbolWrapper
1784               ? new NativeSymbol(description)
1785               // in Edge 13, String(Symbol(undefined)) === 'Symbol(undefined)'
1786               : description === undefined ? NativeSymbol() : NativeSymbol(description);
1787             if (description === '') EmptyStringDescriptionStore[result] = true;
1788             return result;
1789           };
1790           copyConstructorProperties(SymbolWrapper, NativeSymbol);
1791           var symbolPrototype = SymbolWrapper.prototype = NativeSymbol.prototype;
1792           symbolPrototype.constructor = SymbolWrapper;
1793
1794           var symbolToString = symbolPrototype.toString;
1795           var native = String(NativeSymbol('test')) == 'Symbol(test)';
1796           var regexp = /^Symbol\((.*)\)[^)]+$/;
1797           defineProperty$7(symbolPrototype, 'description', {
1798             configurable: true,
1799             get: function description() {
1800               var symbol = isObject$4(this) ? this.valueOf() : this;
1801               var string = symbolToString.call(symbol);
1802               if (has$1(EmptyStringDescriptionStore, symbol)) return '';
1803               var desc = native ? string.slice(7, -1) : string.replace(regexp, '$1');
1804               return desc === '' ? undefined : desc;
1805             }
1806           });
1807
1808           _export({ global: true, forced: true }, {
1809             Symbol: SymbolWrapper
1810           });
1811         }
1812
1813         // eslint-disable-next-line es/no-typed-arrays -- safe
1814         var arrayBufferNative = typeof ArrayBuffer !== 'undefined' && typeof DataView !== 'undefined';
1815
1816         var redefineAll = function (target, src, options) {
1817           for (var key in src) redefine(target, key, src[key], options);
1818           return target;
1819         };
1820
1821         var anInstance = function (it, Constructor, name) {
1822           if (!(it instanceof Constructor)) {
1823             throw TypeError('Incorrect ' + (name ? name + ' ' : '') + 'invocation');
1824           } return it;
1825         };
1826
1827         // `ToIndex` abstract operation
1828         // https://tc39.es/ecma262/#sec-toindex
1829         var toIndex = function (it) {
1830           if (it === undefined) return 0;
1831           var number = toInteger(it);
1832           var length = toLength(number);
1833           if (number !== length) throw RangeError('Wrong length or index');
1834           return length;
1835         };
1836
1837         // IEEE754 conversions based on https://github.com/feross/ieee754
1838         var abs$4 = Math.abs;
1839         var pow$2 = Math.pow;
1840         var floor$6 = Math.floor;
1841         var log$2 = Math.log;
1842         var LN2 = Math.LN2;
1843
1844         var pack = function (number, mantissaLength, bytes) {
1845           var buffer = new Array(bytes);
1846           var exponentLength = bytes * 8 - mantissaLength - 1;
1847           var eMax = (1 << exponentLength) - 1;
1848           var eBias = eMax >> 1;
1849           var rt = mantissaLength === 23 ? pow$2(2, -24) - pow$2(2, -77) : 0;
1850           var sign = number < 0 || number === 0 && 1 / number < 0 ? 1 : 0;
1851           var index = 0;
1852           var exponent, mantissa, c;
1853           number = abs$4(number);
1854           // eslint-disable-next-line no-self-compare -- NaN check
1855           if (number != number || number === Infinity) {
1856             // eslint-disable-next-line no-self-compare -- NaN check
1857             mantissa = number != number ? 1 : 0;
1858             exponent = eMax;
1859           } else {
1860             exponent = floor$6(log$2(number) / LN2);
1861             if (number * (c = pow$2(2, -exponent)) < 1) {
1862               exponent--;
1863               c *= 2;
1864             }
1865             if (exponent + eBias >= 1) {
1866               number += rt / c;
1867             } else {
1868               number += rt * pow$2(2, 1 - eBias);
1869             }
1870             if (number * c >= 2) {
1871               exponent++;
1872               c /= 2;
1873             }
1874             if (exponent + eBias >= eMax) {
1875               mantissa = 0;
1876               exponent = eMax;
1877             } else if (exponent + eBias >= 1) {
1878               mantissa = (number * c - 1) * pow$2(2, mantissaLength);
1879               exponent = exponent + eBias;
1880             } else {
1881               mantissa = number * pow$2(2, eBias - 1) * pow$2(2, mantissaLength);
1882               exponent = 0;
1883             }
1884           }
1885           for (; mantissaLength >= 8; buffer[index++] = mantissa & 255, mantissa /= 256, mantissaLength -= 8);
1886           exponent = exponent << mantissaLength | mantissa;
1887           exponentLength += mantissaLength;
1888           for (; exponentLength > 0; buffer[index++] = exponent & 255, exponent /= 256, exponentLength -= 8);
1889           buffer[--index] |= sign * 128;
1890           return buffer;
1891         };
1892
1893         var unpack = function (buffer, mantissaLength) {
1894           var bytes = buffer.length;
1895           var exponentLength = bytes * 8 - mantissaLength - 1;
1896           var eMax = (1 << exponentLength) - 1;
1897           var eBias = eMax >> 1;
1898           var nBits = exponentLength - 7;
1899           var index = bytes - 1;
1900           var sign = buffer[index--];
1901           var exponent = sign & 127;
1902           var mantissa;
1903           sign >>= 7;
1904           for (; nBits > 0; exponent = exponent * 256 + buffer[index], index--, nBits -= 8);
1905           mantissa = exponent & (1 << -nBits) - 1;
1906           exponent >>= -nBits;
1907           nBits += mantissaLength;
1908           for (; nBits > 0; mantissa = mantissa * 256 + buffer[index], index--, nBits -= 8);
1909           if (exponent === 0) {
1910             exponent = 1 - eBias;
1911           } else if (exponent === eMax) {
1912             return mantissa ? NaN : sign ? -Infinity : Infinity;
1913           } else {
1914             mantissa = mantissa + pow$2(2, mantissaLength);
1915             exponent = exponent - eBias;
1916           } return (sign ? -1 : 1) * mantissa * pow$2(2, exponent - mantissaLength);
1917         };
1918
1919         var ieee754$1 = {
1920           pack: pack,
1921           unpack: unpack
1922         };
1923
1924         // `Array.prototype.fill` method implementation
1925         // https://tc39.es/ecma262/#sec-array.prototype.fill
1926         var arrayFill = function fill(value /* , start = 0, end = @length */) {
1927           var O = toObject(this);
1928           var length = toLength(O.length);
1929           var argumentsLength = arguments.length;
1930           var index = toAbsoluteIndex(argumentsLength > 1 ? arguments[1] : undefined, length);
1931           var end = argumentsLength > 2 ? arguments[2] : undefined;
1932           var endPos = end === undefined ? length : toAbsoluteIndex(end, length);
1933           while (endPos > index) O[index++] = value;
1934           return O;
1935         };
1936
1937         var getOwnPropertyNames$3 = objectGetOwnPropertyNames.f;
1938         var defineProperty$6 = objectDefineProperty.f;
1939
1940
1941
1942
1943         var getInternalState$2 = internalState.get;
1944         var setInternalState$4 = internalState.set;
1945         var ARRAY_BUFFER$1 = 'ArrayBuffer';
1946         var DATA_VIEW = 'DataView';
1947         var PROTOTYPE = 'prototype';
1948         var WRONG_LENGTH = 'Wrong length';
1949         var WRONG_INDEX = 'Wrong index';
1950         var NativeArrayBuffer$1 = global$2[ARRAY_BUFFER$1];
1951         var $ArrayBuffer = NativeArrayBuffer$1;
1952         var $DataView = global$2[DATA_VIEW];
1953         var $DataViewPrototype = $DataView && $DataView[PROTOTYPE];
1954         var ObjectPrototype$1 = Object.prototype;
1955         var RangeError$1 = global$2.RangeError;
1956
1957         var packIEEE754 = ieee754$1.pack;
1958         var unpackIEEE754 = ieee754$1.unpack;
1959
1960         var packInt8 = function (number) {
1961           return [number & 0xFF];
1962         };
1963
1964         var packInt16 = function (number) {
1965           return [number & 0xFF, number >> 8 & 0xFF];
1966         };
1967
1968         var packInt32 = function (number) {
1969           return [number & 0xFF, number >> 8 & 0xFF, number >> 16 & 0xFF, number >> 24 & 0xFF];
1970         };
1971
1972         var unpackInt32 = function (buffer) {
1973           return buffer[3] << 24 | buffer[2] << 16 | buffer[1] << 8 | buffer[0];
1974         };
1975
1976         var packFloat32 = function (number) {
1977           return packIEEE754(number, 23, 4);
1978         };
1979
1980         var packFloat64 = function (number) {
1981           return packIEEE754(number, 52, 8);
1982         };
1983
1984         var addGetter = function (Constructor, key) {
1985           defineProperty$6(Constructor[PROTOTYPE], key, { get: function () { return getInternalState$2(this)[key]; } });
1986         };
1987
1988         var get$4 = function (view, count, index, isLittleEndian) {
1989           var intIndex = toIndex(index);
1990           var store = getInternalState$2(view);
1991           if (intIndex + count > store.byteLength) throw RangeError$1(WRONG_INDEX);
1992           var bytes = getInternalState$2(store.buffer).bytes;
1993           var start = intIndex + store.byteOffset;
1994           var pack = bytes.slice(start, start + count);
1995           return isLittleEndian ? pack : pack.reverse();
1996         };
1997
1998         var set$3 = function (view, count, index, conversion, value, isLittleEndian) {
1999           var intIndex = toIndex(index);
2000           var store = getInternalState$2(view);
2001           if (intIndex + count > store.byteLength) throw RangeError$1(WRONG_INDEX);
2002           var bytes = getInternalState$2(store.buffer).bytes;
2003           var start = intIndex + store.byteOffset;
2004           var pack = conversion(+value);
2005           for (var i = 0; i < count; i++) bytes[start + i] = pack[isLittleEndian ? i : count - i - 1];
2006         };
2007
2008         if (!arrayBufferNative) {
2009           $ArrayBuffer = function ArrayBuffer(length) {
2010             anInstance(this, $ArrayBuffer, ARRAY_BUFFER$1);
2011             var byteLength = toIndex(length);
2012             setInternalState$4(this, {
2013               bytes: arrayFill.call(new Array(byteLength), 0),
2014               byteLength: byteLength
2015             });
2016             if (!descriptors) this.byteLength = byteLength;
2017           };
2018
2019           $DataView = function DataView(buffer, byteOffset, byteLength) {
2020             anInstance(this, $DataView, DATA_VIEW);
2021             anInstance(buffer, $ArrayBuffer, DATA_VIEW);
2022             var bufferLength = getInternalState$2(buffer).byteLength;
2023             var offset = toInteger(byteOffset);
2024             if (offset < 0 || offset > bufferLength) throw RangeError$1('Wrong offset');
2025             byteLength = byteLength === undefined ? bufferLength - offset : toLength(byteLength);
2026             if (offset + byteLength > bufferLength) throw RangeError$1(WRONG_LENGTH);
2027             setInternalState$4(this, {
2028               buffer: buffer,
2029               byteLength: byteLength,
2030               byteOffset: offset
2031             });
2032             if (!descriptors) {
2033               this.buffer = buffer;
2034               this.byteLength = byteLength;
2035               this.byteOffset = offset;
2036             }
2037           };
2038
2039           if (descriptors) {
2040             addGetter($ArrayBuffer, 'byteLength');
2041             addGetter($DataView, 'buffer');
2042             addGetter($DataView, 'byteLength');
2043             addGetter($DataView, 'byteOffset');
2044           }
2045
2046           redefineAll($DataView[PROTOTYPE], {
2047             getInt8: function getInt8(byteOffset) {
2048               return get$4(this, 1, byteOffset)[0] << 24 >> 24;
2049             },
2050             getUint8: function getUint8(byteOffset) {
2051               return get$4(this, 1, byteOffset)[0];
2052             },
2053             getInt16: function getInt16(byteOffset /* , littleEndian */) {
2054               var bytes = get$4(this, 2, byteOffset, arguments.length > 1 ? arguments[1] : undefined);
2055               return (bytes[1] << 8 | bytes[0]) << 16 >> 16;
2056             },
2057             getUint16: function getUint16(byteOffset /* , littleEndian */) {
2058               var bytes = get$4(this, 2, byteOffset, arguments.length > 1 ? arguments[1] : undefined);
2059               return bytes[1] << 8 | bytes[0];
2060             },
2061             getInt32: function getInt32(byteOffset /* , littleEndian */) {
2062               return unpackInt32(get$4(this, 4, byteOffset, arguments.length > 1 ? arguments[1] : undefined));
2063             },
2064             getUint32: function getUint32(byteOffset /* , littleEndian */) {
2065               return unpackInt32(get$4(this, 4, byteOffset, arguments.length > 1 ? arguments[1] : undefined)) >>> 0;
2066             },
2067             getFloat32: function getFloat32(byteOffset /* , littleEndian */) {
2068               return unpackIEEE754(get$4(this, 4, byteOffset, arguments.length > 1 ? arguments[1] : undefined), 23);
2069             },
2070             getFloat64: function getFloat64(byteOffset /* , littleEndian */) {
2071               return unpackIEEE754(get$4(this, 8, byteOffset, arguments.length > 1 ? arguments[1] : undefined), 52);
2072             },
2073             setInt8: function setInt8(byteOffset, value) {
2074               set$3(this, 1, byteOffset, packInt8, value);
2075             },
2076             setUint8: function setUint8(byteOffset, value) {
2077               set$3(this, 1, byteOffset, packInt8, value);
2078             },
2079             setInt16: function setInt16(byteOffset, value /* , littleEndian */) {
2080               set$3(this, 2, byteOffset, packInt16, value, arguments.length > 2 ? arguments[2] : undefined);
2081             },
2082             setUint16: function setUint16(byteOffset, value /* , littleEndian */) {
2083               set$3(this, 2, byteOffset, packInt16, value, arguments.length > 2 ? arguments[2] : undefined);
2084             },
2085             setInt32: function setInt32(byteOffset, value /* , littleEndian */) {
2086               set$3(this, 4, byteOffset, packInt32, value, arguments.length > 2 ? arguments[2] : undefined);
2087             },
2088             setUint32: function setUint32(byteOffset, value /* , littleEndian */) {
2089               set$3(this, 4, byteOffset, packInt32, value, arguments.length > 2 ? arguments[2] : undefined);
2090             },
2091             setFloat32: function setFloat32(byteOffset, value /* , littleEndian */) {
2092               set$3(this, 4, byteOffset, packFloat32, value, arguments.length > 2 ? arguments[2] : undefined);
2093             },
2094             setFloat64: function setFloat64(byteOffset, value /* , littleEndian */) {
2095               set$3(this, 8, byteOffset, packFloat64, value, arguments.length > 2 ? arguments[2] : undefined);
2096             }
2097           });
2098         } else {
2099           /* eslint-disable no-new -- required for testing */
2100           if (!fails(function () {
2101             NativeArrayBuffer$1(1);
2102           }) || !fails(function () {
2103             new NativeArrayBuffer$1(-1);
2104           }) || fails(function () {
2105             new NativeArrayBuffer$1();
2106             new NativeArrayBuffer$1(1.5);
2107             new NativeArrayBuffer$1(NaN);
2108             return NativeArrayBuffer$1.name != ARRAY_BUFFER$1;
2109           })) {
2110           /* eslint-enable no-new -- required for testing */
2111             $ArrayBuffer = function ArrayBuffer(length) {
2112               anInstance(this, $ArrayBuffer);
2113               return new NativeArrayBuffer$1(toIndex(length));
2114             };
2115             var ArrayBufferPrototype = $ArrayBuffer[PROTOTYPE] = NativeArrayBuffer$1[PROTOTYPE];
2116             for (var keys$2 = getOwnPropertyNames$3(NativeArrayBuffer$1), j$2 = 0, key$1; keys$2.length > j$2;) {
2117               if (!((key$1 = keys$2[j$2++]) in $ArrayBuffer)) {
2118                 createNonEnumerableProperty($ArrayBuffer, key$1, NativeArrayBuffer$1[key$1]);
2119               }
2120             }
2121             ArrayBufferPrototype.constructor = $ArrayBuffer;
2122           }
2123
2124           // WebKit bug - the same parent prototype for typed arrays and data view
2125           if (objectSetPrototypeOf && objectGetPrototypeOf($DataViewPrototype) !== ObjectPrototype$1) {
2126             objectSetPrototypeOf($DataViewPrototype, ObjectPrototype$1);
2127           }
2128
2129           // iOS Safari 7.x bug
2130           var testView = new $DataView(new $ArrayBuffer(2));
2131           var $setInt8 = $DataViewPrototype.setInt8;
2132           testView.setInt8(0, 2147483648);
2133           testView.setInt8(1, 2147483649);
2134           if (testView.getInt8(0) || !testView.getInt8(1)) redefineAll($DataViewPrototype, {
2135             setInt8: function setInt8(byteOffset, value) {
2136               $setInt8.call(this, byteOffset, value << 24 >> 24);
2137             },
2138             setUint8: function setUint8(byteOffset, value) {
2139               $setInt8.call(this, byteOffset, value << 24 >> 24);
2140             }
2141           }, { unsafe: true });
2142         }
2143
2144         setToStringTag($ArrayBuffer, ARRAY_BUFFER$1);
2145         setToStringTag($DataView, DATA_VIEW);
2146
2147         var arrayBuffer = {
2148           ArrayBuffer: $ArrayBuffer,
2149           DataView: $DataView
2150         };
2151
2152         var SPECIES$5 = wellKnownSymbol('species');
2153
2154         // `SpeciesConstructor` abstract operation
2155         // https://tc39.es/ecma262/#sec-speciesconstructor
2156         var speciesConstructor = function (O, defaultConstructor) {
2157           var C = anObject(O).constructor;
2158           var S;
2159           return C === undefined || (S = anObject(C)[SPECIES$5]) == undefined ? defaultConstructor : aFunction(S);
2160         };
2161
2162         var ArrayBuffer$3 = arrayBuffer.ArrayBuffer;
2163         var DataView$1 = arrayBuffer.DataView;
2164         var nativeArrayBufferSlice = ArrayBuffer$3.prototype.slice;
2165
2166         var INCORRECT_SLICE = fails(function () {
2167           return !new ArrayBuffer$3(2).slice(1, undefined).byteLength;
2168         });
2169
2170         // `ArrayBuffer.prototype.slice` method
2171         // https://tc39.es/ecma262/#sec-arraybuffer.prototype.slice
2172         _export({ target: 'ArrayBuffer', proto: true, unsafe: true, forced: INCORRECT_SLICE }, {
2173           slice: function slice(start, end) {
2174             if (nativeArrayBufferSlice !== undefined && end === undefined) {
2175               return nativeArrayBufferSlice.call(anObject(this), start); // FF fix
2176             }
2177             var length = anObject(this).byteLength;
2178             var first = toAbsoluteIndex(start, length);
2179             var fin = toAbsoluteIndex(end === undefined ? length : end, length);
2180             var result = new (speciesConstructor(this, ArrayBuffer$3))(toLength(fin - first));
2181             var viewSource = new DataView$1(this);
2182             var viewTarget = new DataView$1(result);
2183             var index = 0;
2184             while (first < fin) {
2185               viewTarget.setUint8(index++, viewSource.getUint8(first++));
2186             } return result;
2187           }
2188         });
2189
2190         // `DataView` constructor
2191         // https://tc39.es/ecma262/#sec-dataview-constructor
2192         _export({ global: true, forced: !arrayBufferNative }, {
2193           DataView: arrayBuffer.DataView
2194         });
2195
2196         var defineProperty$5 = objectDefineProperty.f;
2197
2198
2199
2200
2201
2202         var Int8Array$3 = global$2.Int8Array;
2203         var Int8ArrayPrototype = Int8Array$3 && Int8Array$3.prototype;
2204         var Uint8ClampedArray = global$2.Uint8ClampedArray;
2205         var Uint8ClampedArrayPrototype = Uint8ClampedArray && Uint8ClampedArray.prototype;
2206         var TypedArray = Int8Array$3 && objectGetPrototypeOf(Int8Array$3);
2207         var TypedArrayPrototype = Int8ArrayPrototype && objectGetPrototypeOf(Int8ArrayPrototype);
2208         var ObjectPrototype = Object.prototype;
2209         var isPrototypeOf = ObjectPrototype.isPrototypeOf;
2210
2211         var TO_STRING_TAG = wellKnownSymbol('toStringTag');
2212         var TYPED_ARRAY_TAG = uid('TYPED_ARRAY_TAG');
2213         // Fixing native typed arrays in Opera Presto crashes the browser, see #595
2214         var NATIVE_ARRAY_BUFFER_VIEWS$2 = arrayBufferNative && !!objectSetPrototypeOf && classof(global$2.opera) !== 'Opera';
2215         var TYPED_ARRAY_TAG_REQIRED = false;
2216         var NAME$1;
2217
2218         var TypedArrayConstructorsList = {
2219           Int8Array: 1,
2220           Uint8Array: 1,
2221           Uint8ClampedArray: 1,
2222           Int16Array: 2,
2223           Uint16Array: 2,
2224           Int32Array: 4,
2225           Uint32Array: 4,
2226           Float32Array: 4,
2227           Float64Array: 8
2228         };
2229
2230         var BigIntArrayConstructorsList = {
2231           BigInt64Array: 8,
2232           BigUint64Array: 8
2233         };
2234
2235         var isView = function isView(it) {
2236           if (!isObject$4(it)) return false;
2237           var klass = classof(it);
2238           return klass === 'DataView'
2239             || has$1(TypedArrayConstructorsList, klass)
2240             || has$1(BigIntArrayConstructorsList, klass);
2241         };
2242
2243         var isTypedArray = function (it) {
2244           if (!isObject$4(it)) return false;
2245           var klass = classof(it);
2246           return has$1(TypedArrayConstructorsList, klass)
2247             || has$1(BigIntArrayConstructorsList, klass);
2248         };
2249
2250         var aTypedArray$m = function (it) {
2251           if (isTypedArray(it)) return it;
2252           throw TypeError('Target is not a typed array');
2253         };
2254
2255         var aTypedArrayConstructor$4 = function (C) {
2256           if (objectSetPrototypeOf) {
2257             if (isPrototypeOf.call(TypedArray, C)) return C;
2258           } else for (var ARRAY in TypedArrayConstructorsList) if (has$1(TypedArrayConstructorsList, NAME$1)) {
2259             var TypedArrayConstructor = global$2[ARRAY];
2260             if (TypedArrayConstructor && (C === TypedArrayConstructor || isPrototypeOf.call(TypedArrayConstructor, C))) {
2261               return C;
2262             }
2263           } throw TypeError('Target is not a typed array constructor');
2264         };
2265
2266         var exportTypedArrayMethod$n = function (KEY, property, forced) {
2267           if (!descriptors) return;
2268           if (forced) for (var ARRAY in TypedArrayConstructorsList) {
2269             var TypedArrayConstructor = global$2[ARRAY];
2270             if (TypedArrayConstructor && has$1(TypedArrayConstructor.prototype, KEY)) try {
2271               delete TypedArrayConstructor.prototype[KEY];
2272             } catch (error) { /* empty */ }
2273           }
2274           if (!TypedArrayPrototype[KEY] || forced) {
2275             redefine(TypedArrayPrototype, KEY, forced ? property
2276               : NATIVE_ARRAY_BUFFER_VIEWS$2 && Int8ArrayPrototype[KEY] || property);
2277           }
2278         };
2279
2280         var exportTypedArrayStaticMethod$1 = function (KEY, property, forced) {
2281           var ARRAY, TypedArrayConstructor;
2282           if (!descriptors) return;
2283           if (objectSetPrototypeOf) {
2284             if (forced) for (ARRAY in TypedArrayConstructorsList) {
2285               TypedArrayConstructor = global$2[ARRAY];
2286               if (TypedArrayConstructor && has$1(TypedArrayConstructor, KEY)) try {
2287                 delete TypedArrayConstructor[KEY];
2288               } catch (error) { /* empty */ }
2289             }
2290             if (!TypedArray[KEY] || forced) {
2291               // V8 ~ Chrome 49-50 `%TypedArray%` methods are non-writable non-configurable
2292               try {
2293                 return redefine(TypedArray, KEY, forced ? property : NATIVE_ARRAY_BUFFER_VIEWS$2 && TypedArray[KEY] || property);
2294               } catch (error) { /* empty */ }
2295             } else return;
2296           }
2297           for (ARRAY in TypedArrayConstructorsList) {
2298             TypedArrayConstructor = global$2[ARRAY];
2299             if (TypedArrayConstructor && (!TypedArrayConstructor[KEY] || forced)) {
2300               redefine(TypedArrayConstructor, KEY, property);
2301             }
2302           }
2303         };
2304
2305         for (NAME$1 in TypedArrayConstructorsList) {
2306           if (!global$2[NAME$1]) NATIVE_ARRAY_BUFFER_VIEWS$2 = false;
2307         }
2308
2309         // WebKit bug - typed arrays constructors prototype is Object.prototype
2310         if (!NATIVE_ARRAY_BUFFER_VIEWS$2 || typeof TypedArray != 'function' || TypedArray === Function.prototype) {
2311           // eslint-disable-next-line no-shadow -- safe
2312           TypedArray = function TypedArray() {
2313             throw TypeError('Incorrect invocation');
2314           };
2315           if (NATIVE_ARRAY_BUFFER_VIEWS$2) for (NAME$1 in TypedArrayConstructorsList) {
2316             if (global$2[NAME$1]) objectSetPrototypeOf(global$2[NAME$1], TypedArray);
2317           }
2318         }
2319
2320         if (!NATIVE_ARRAY_BUFFER_VIEWS$2 || !TypedArrayPrototype || TypedArrayPrototype === ObjectPrototype) {
2321           TypedArrayPrototype = TypedArray.prototype;
2322           if (NATIVE_ARRAY_BUFFER_VIEWS$2) for (NAME$1 in TypedArrayConstructorsList) {
2323             if (global$2[NAME$1]) objectSetPrototypeOf(global$2[NAME$1].prototype, TypedArrayPrototype);
2324           }
2325         }
2326
2327         // WebKit bug - one more object in Uint8ClampedArray prototype chain
2328         if (NATIVE_ARRAY_BUFFER_VIEWS$2 && objectGetPrototypeOf(Uint8ClampedArrayPrototype) !== TypedArrayPrototype) {
2329           objectSetPrototypeOf(Uint8ClampedArrayPrototype, TypedArrayPrototype);
2330         }
2331
2332         if (descriptors && !has$1(TypedArrayPrototype, TO_STRING_TAG)) {
2333           TYPED_ARRAY_TAG_REQIRED = true;
2334           defineProperty$5(TypedArrayPrototype, TO_STRING_TAG, { get: function () {
2335             return isObject$4(this) ? this[TYPED_ARRAY_TAG] : undefined;
2336           } });
2337           for (NAME$1 in TypedArrayConstructorsList) if (global$2[NAME$1]) {
2338             createNonEnumerableProperty(global$2[NAME$1], TYPED_ARRAY_TAG, NAME$1);
2339           }
2340         }
2341
2342         var arrayBufferViewCore = {
2343           NATIVE_ARRAY_BUFFER_VIEWS: NATIVE_ARRAY_BUFFER_VIEWS$2,
2344           TYPED_ARRAY_TAG: TYPED_ARRAY_TAG_REQIRED && TYPED_ARRAY_TAG,
2345           aTypedArray: aTypedArray$m,
2346           aTypedArrayConstructor: aTypedArrayConstructor$4,
2347           exportTypedArrayMethod: exportTypedArrayMethod$n,
2348           exportTypedArrayStaticMethod: exportTypedArrayStaticMethod$1,
2349           isView: isView,
2350           isTypedArray: isTypedArray,
2351           TypedArray: TypedArray,
2352           TypedArrayPrototype: TypedArrayPrototype
2353         };
2354
2355         var NATIVE_ARRAY_BUFFER_VIEWS$1 = arrayBufferViewCore.NATIVE_ARRAY_BUFFER_VIEWS;
2356
2357         // `ArrayBuffer.isView` method
2358         // https://tc39.es/ecma262/#sec-arraybuffer.isview
2359         _export({ target: 'ArrayBuffer', stat: true, forced: !NATIVE_ARRAY_BUFFER_VIEWS$1 }, {
2360           isView: arrayBufferViewCore.isView
2361         });
2362
2363         var SPECIES$4 = wellKnownSymbol('species');
2364
2365         var setSpecies = function (CONSTRUCTOR_NAME) {
2366           var Constructor = getBuiltIn(CONSTRUCTOR_NAME);
2367           var defineProperty = objectDefineProperty.f;
2368
2369           if (descriptors && Constructor && !Constructor[SPECIES$4]) {
2370             defineProperty(Constructor, SPECIES$4, {
2371               configurable: true,
2372               get: function () { return this; }
2373             });
2374           }
2375         };
2376
2377         var ARRAY_BUFFER = 'ArrayBuffer';
2378         var ArrayBuffer$2 = arrayBuffer[ARRAY_BUFFER];
2379         var NativeArrayBuffer = global$2[ARRAY_BUFFER];
2380
2381         // `ArrayBuffer` constructor
2382         // https://tc39.es/ecma262/#sec-arraybuffer-constructor
2383         _export({ global: true, forced: NativeArrayBuffer !== ArrayBuffer$2 }, {
2384           ArrayBuffer: ArrayBuffer$2
2385         });
2386
2387         setSpecies(ARRAY_BUFFER);
2388
2389         var arrayMethodIsStrict = function (METHOD_NAME, argument) {
2390           var method = [][METHOD_NAME];
2391           return !!method && fails(function () {
2392             // eslint-disable-next-line no-useless-call,no-throw-literal -- required for testing
2393             method.call(null, argument || function () { throw 1; }, 1);
2394           });
2395         };
2396
2397         /* eslint-disable es/no-array-prototype-indexof -- required for testing */
2398
2399         var $indexOf$1 = arrayIncludes.indexOf;
2400
2401
2402         var nativeIndexOf = [].indexOf;
2403
2404         var NEGATIVE_ZERO$1 = !!nativeIndexOf && 1 / [1].indexOf(1, -0) < 0;
2405         var STRICT_METHOD$7 = arrayMethodIsStrict('indexOf');
2406
2407         // `Array.prototype.indexOf` method
2408         // https://tc39.es/ecma262/#sec-array.prototype.indexof
2409         _export({ target: 'Array', proto: true, forced: NEGATIVE_ZERO$1 || !STRICT_METHOD$7 }, {
2410           indexOf: function indexOf(searchElement /* , fromIndex = 0 */) {
2411             return NEGATIVE_ZERO$1
2412               // convert -0 to +0
2413               ? nativeIndexOf.apply(this, arguments) || 0
2414               : $indexOf$1(this, searchElement, arguments.length > 1 ? arguments[1] : undefined);
2415           }
2416         });
2417
2418         var SPECIES$3 = wellKnownSymbol('species');
2419
2420         var arrayMethodHasSpeciesSupport = function (METHOD_NAME) {
2421           // We can't use this feature detection in V8 since it causes
2422           // deoptimization and serious performance degradation
2423           // https://github.com/zloirock/core-js/issues/677
2424           return engineV8Version >= 51 || !fails(function () {
2425             var array = [];
2426             var constructor = array.constructor = {};
2427             constructor[SPECIES$3] = function () {
2428               return { foo: 1 };
2429             };
2430             return array[METHOD_NAME](Boolean).foo !== 1;
2431           });
2432         };
2433
2434         var $map$1 = arrayIteration.map;
2435
2436
2437         var HAS_SPECIES_SUPPORT$3 = arrayMethodHasSpeciesSupport('map');
2438
2439         // `Array.prototype.map` method
2440         // https://tc39.es/ecma262/#sec-array.prototype.map
2441         // with adding support of @@species
2442         _export({ target: 'Array', proto: true, forced: !HAS_SPECIES_SUPPORT$3 }, {
2443           map: function map(callbackfn /* , thisArg */) {
2444             return $map$1(this, callbackfn, arguments.length > 1 ? arguments[1] : undefined);
2445           }
2446         });
2447
2448         var $forEach$1 = arrayIteration.forEach;
2449
2450
2451         var STRICT_METHOD$6 = arrayMethodIsStrict('forEach');
2452
2453         // `Array.prototype.forEach` method implementation
2454         // https://tc39.es/ecma262/#sec-array.prototype.foreach
2455         var arrayForEach = !STRICT_METHOD$6 ? function forEach(callbackfn /* , thisArg */) {
2456           return $forEach$1(this, callbackfn, arguments.length > 1 ? arguments[1] : undefined);
2457         // eslint-disable-next-line es/no-array-prototype-foreach -- safe
2458         } : [].forEach;
2459
2460         // `Array.prototype.forEach` method
2461         // https://tc39.es/ecma262/#sec-array.prototype.foreach
2462         // eslint-disable-next-line es/no-array-prototype-foreach -- safe
2463         _export({ target: 'Array', proto: true, forced: [].forEach != arrayForEach }, {
2464           forEach: arrayForEach
2465         });
2466
2467         for (var COLLECTION_NAME in domIterables) {
2468           var Collection = global$2[COLLECTION_NAME];
2469           var CollectionPrototype = Collection && Collection.prototype;
2470           // some Chrome versions have non-configurable methods on DOMTokenList
2471           if (CollectionPrototype && CollectionPrototype.forEach !== arrayForEach) try {
2472             createNonEnumerableProperty(CollectionPrototype, 'forEach', arrayForEach);
2473           } catch (error) {
2474             CollectionPrototype.forEach = arrayForEach;
2475           }
2476         }
2477
2478         // `Array.isArray` method
2479         // https://tc39.es/ecma262/#sec-array.isarray
2480         _export({ target: 'Array', stat: true }, {
2481           isArray: isArray
2482         });
2483
2484         var getOwnPropertyNames$2 = objectGetOwnPropertyNamesExternal.f;
2485
2486         // eslint-disable-next-line es/no-object-getownpropertynames -- required for testing
2487         var FAILS_ON_PRIMITIVES$4 = fails(function () { return !Object.getOwnPropertyNames(1); });
2488
2489         // `Object.getOwnPropertyNames` method
2490         // https://tc39.es/ecma262/#sec-object.getownpropertynames
2491         _export({ target: 'Object', stat: true, forced: FAILS_ON_PRIMITIVES$4 }, {
2492           getOwnPropertyNames: getOwnPropertyNames$2
2493         });
2494
2495         var nativePromiseConstructor = global$2.Promise;
2496
2497         var ITERATOR$5 = wellKnownSymbol('iterator');
2498         var ArrayPrototype = Array.prototype;
2499
2500         // check on default Array iterator
2501         var isArrayIteratorMethod = function (it) {
2502           return it !== undefined && (iterators.Array === it || ArrayPrototype[ITERATOR$5] === it);
2503         };
2504
2505         var ITERATOR$4 = wellKnownSymbol('iterator');
2506
2507         var getIteratorMethod = function (it) {
2508           if (it != undefined) return it[ITERATOR$4]
2509             || it['@@iterator']
2510             || iterators[classof(it)];
2511         };
2512
2513         var iteratorClose = function (iterator) {
2514           var returnMethod = iterator['return'];
2515           if (returnMethod !== undefined) {
2516             return anObject(returnMethod.call(iterator)).value;
2517           }
2518         };
2519
2520         var Result = function (stopped, result) {
2521           this.stopped = stopped;
2522           this.result = result;
2523         };
2524
2525         var iterate = function (iterable, unboundFunction, options) {
2526           var that = options && options.that;
2527           var AS_ENTRIES = !!(options && options.AS_ENTRIES);
2528           var IS_ITERATOR = !!(options && options.IS_ITERATOR);
2529           var INTERRUPTED = !!(options && options.INTERRUPTED);
2530           var fn = functionBindContext(unboundFunction, that, 1 + AS_ENTRIES + INTERRUPTED);
2531           var iterator, iterFn, index, length, result, next, step;
2532
2533           var stop = function (condition) {
2534             if (iterator) iteratorClose(iterator);
2535             return new Result(true, condition);
2536           };
2537
2538           var callFn = function (value) {
2539             if (AS_ENTRIES) {
2540               anObject(value);
2541               return INTERRUPTED ? fn(value[0], value[1], stop) : fn(value[0], value[1]);
2542             } return INTERRUPTED ? fn(value, stop) : fn(value);
2543           };
2544
2545           if (IS_ITERATOR) {
2546             iterator = iterable;
2547           } else {
2548             iterFn = getIteratorMethod(iterable);
2549             if (typeof iterFn != 'function') throw TypeError('Target is not iterable');
2550             // optimisation for array iterators
2551             if (isArrayIteratorMethod(iterFn)) {
2552               for (index = 0, length = toLength(iterable.length); length > index; index++) {
2553                 result = callFn(iterable[index]);
2554                 if (result && result instanceof Result) return result;
2555               } return new Result(false);
2556             }
2557             iterator = iterFn.call(iterable);
2558           }
2559
2560           next = iterator.next;
2561           while (!(step = next.call(iterator)).done) {
2562             try {
2563               result = callFn(step.value);
2564             } catch (error) {
2565               iteratorClose(iterator);
2566               throw error;
2567             }
2568             if (typeof result == 'object' && result && result instanceof Result) return result;
2569           } return new Result(false);
2570         };
2571
2572         var ITERATOR$3 = wellKnownSymbol('iterator');
2573         var SAFE_CLOSING = false;
2574
2575         try {
2576           var called = 0;
2577           var iteratorWithReturn = {
2578             next: function () {
2579               return { done: !!called++ };
2580             },
2581             'return': function () {
2582               SAFE_CLOSING = true;
2583             }
2584           };
2585           iteratorWithReturn[ITERATOR$3] = function () {
2586             return this;
2587           };
2588           // eslint-disable-next-line es/no-array-from, no-throw-literal -- required for testing
2589           Array.from(iteratorWithReturn, function () { throw 2; });
2590         } catch (error) { /* empty */ }
2591
2592         var checkCorrectnessOfIteration = function (exec, SKIP_CLOSING) {
2593           if (!SKIP_CLOSING && !SAFE_CLOSING) return false;
2594           var ITERATION_SUPPORT = false;
2595           try {
2596             var object = {};
2597             object[ITERATOR$3] = function () {
2598               return {
2599                 next: function () {
2600                   return { done: ITERATION_SUPPORT = true };
2601                 }
2602               };
2603             };
2604             exec(object);
2605           } catch (error) { /* empty */ }
2606           return ITERATION_SUPPORT;
2607         };
2608
2609         var engineIsIos = /(?:iphone|ipod|ipad).*applewebkit/i.test(engineUserAgent);
2610
2611         var engineIsNode = classofRaw(global$2.process) == 'process';
2612
2613         var location$1 = global$2.location;
2614         var set$2 = global$2.setImmediate;
2615         var clear = global$2.clearImmediate;
2616         var process$3 = global$2.process;
2617         var MessageChannel = global$2.MessageChannel;
2618         var Dispatch$1 = global$2.Dispatch;
2619         var counter = 0;
2620         var queue = {};
2621         var ONREADYSTATECHANGE = 'onreadystatechange';
2622         var defer, channel, port;
2623
2624         var run = function (id) {
2625           // eslint-disable-next-line no-prototype-builtins -- safe
2626           if (queue.hasOwnProperty(id)) {
2627             var fn = queue[id];
2628             delete queue[id];
2629             fn();
2630           }
2631         };
2632
2633         var runner = function (id) {
2634           return function () {
2635             run(id);
2636           };
2637         };
2638
2639         var listener = function (event) {
2640           run(event.data);
2641         };
2642
2643         var post = function (id) {
2644           // old engines have not location.origin
2645           global$2.postMessage(id + '', location$1.protocol + '//' + location$1.host);
2646         };
2647
2648         // Node.js 0.9+ & IE10+ has setImmediate, otherwise:
2649         if (!set$2 || !clear) {
2650           set$2 = function setImmediate(fn) {
2651             var args = [];
2652             var i = 1;
2653             while (arguments.length > i) args.push(arguments[i++]);
2654             queue[++counter] = function () {
2655               // eslint-disable-next-line no-new-func -- spec requirement
2656               (typeof fn == 'function' ? fn : Function(fn)).apply(undefined, args);
2657             };
2658             defer(counter);
2659             return counter;
2660           };
2661           clear = function clearImmediate(id) {
2662             delete queue[id];
2663           };
2664           // Node.js 0.8-
2665           if (engineIsNode) {
2666             defer = function (id) {
2667               process$3.nextTick(runner(id));
2668             };
2669           // Sphere (JS game engine) Dispatch API
2670           } else if (Dispatch$1 && Dispatch$1.now) {
2671             defer = function (id) {
2672               Dispatch$1.now(runner(id));
2673             };
2674           // Browsers with MessageChannel, includes WebWorkers
2675           // except iOS - https://github.com/zloirock/core-js/issues/624
2676           } else if (MessageChannel && !engineIsIos) {
2677             channel = new MessageChannel();
2678             port = channel.port2;
2679             channel.port1.onmessage = listener;
2680             defer = functionBindContext(port.postMessage, port, 1);
2681           // Browsers with postMessage, skip WebWorkers
2682           // IE8 has postMessage, but it's sync & typeof its postMessage is 'object'
2683           } else if (
2684             global$2.addEventListener &&
2685             typeof postMessage == 'function' &&
2686             !global$2.importScripts &&
2687             location$1 && location$1.protocol !== 'file:' &&
2688             !fails(post)
2689           ) {
2690             defer = post;
2691             global$2.addEventListener('message', listener, false);
2692           // IE8-
2693           } else if (ONREADYSTATECHANGE in documentCreateElement('script')) {
2694             defer = function (id) {
2695               html.appendChild(documentCreateElement('script'))[ONREADYSTATECHANGE] = function () {
2696                 html.removeChild(this);
2697                 run(id);
2698               };
2699             };
2700           // Rest old browsers
2701           } else {
2702             defer = function (id) {
2703               setTimeout(runner(id), 0);
2704             };
2705           }
2706         }
2707
2708         var task$1 = {
2709           set: set$2,
2710           clear: clear
2711         };
2712
2713         var engineIsWebosWebkit = /web0s(?!.*chrome)/i.test(engineUserAgent);
2714
2715         var getOwnPropertyDescriptor$3 = objectGetOwnPropertyDescriptor.f;
2716         var macrotask = task$1.set;
2717
2718
2719
2720
2721         var MutationObserver = global$2.MutationObserver || global$2.WebKitMutationObserver;
2722         var document$2 = global$2.document;
2723         var process$2 = global$2.process;
2724         var Promise$1 = global$2.Promise;
2725         // Node.js 11 shows ExperimentalWarning on getting `queueMicrotask`
2726         var queueMicrotaskDescriptor = getOwnPropertyDescriptor$3(global$2, 'queueMicrotask');
2727         var queueMicrotask = queueMicrotaskDescriptor && queueMicrotaskDescriptor.value;
2728
2729         var flush, head, last, notify$1, toggle, node, promise, then;
2730
2731         // modern engines have queueMicrotask method
2732         if (!queueMicrotask) {
2733           flush = function () {
2734             var parent, fn;
2735             if (engineIsNode && (parent = process$2.domain)) parent.exit();
2736             while (head) {
2737               fn = head.fn;
2738               head = head.next;
2739               try {
2740                 fn();
2741               } catch (error) {
2742                 if (head) notify$1();
2743                 else last = undefined;
2744                 throw error;
2745               }
2746             } last = undefined;
2747             if (parent) parent.enter();
2748           };
2749
2750           // browsers with MutationObserver, except iOS - https://github.com/zloirock/core-js/issues/339
2751           // also except WebOS Webkit https://github.com/zloirock/core-js/issues/898
2752           if (!engineIsIos && !engineIsNode && !engineIsWebosWebkit && MutationObserver && document$2) {
2753             toggle = true;
2754             node = document$2.createTextNode('');
2755             new MutationObserver(flush).observe(node, { characterData: true });
2756             notify$1 = function () {
2757               node.data = toggle = !toggle;
2758             };
2759           // environments with maybe non-completely correct, but existent Promise
2760           } else if (Promise$1 && Promise$1.resolve) {
2761             // Promise.resolve without an argument throws an error in LG WebOS 2
2762             promise = Promise$1.resolve(undefined);
2763             // workaround of WebKit ~ iOS Safari 10.1 bug
2764             promise.constructor = Promise$1;
2765             then = promise.then;
2766             notify$1 = function () {
2767               then.call(promise, flush);
2768             };
2769           // Node.js without promises
2770           } else if (engineIsNode) {
2771             notify$1 = function () {
2772               process$2.nextTick(flush);
2773             };
2774           // for other environments - macrotask based on:
2775           // - setImmediate
2776           // - MessageChannel
2777           // - window.postMessag
2778           // - onreadystatechange
2779           // - setTimeout
2780           } else {
2781             notify$1 = function () {
2782               // strange IE + webpack dev server bug - use .call(global)
2783               macrotask.call(global$2, flush);
2784             };
2785           }
2786         }
2787
2788         var microtask = queueMicrotask || function (fn) {
2789           var task = { fn: fn, next: undefined };
2790           if (last) last.next = task;
2791           if (!head) {
2792             head = task;
2793             notify$1();
2794           } last = task;
2795         };
2796
2797         var PromiseCapability = function (C) {
2798           var resolve, reject;
2799           this.promise = new C(function ($$resolve, $$reject) {
2800             if (resolve !== undefined || reject !== undefined) throw TypeError('Bad Promise constructor');
2801             resolve = $$resolve;
2802             reject = $$reject;
2803           });
2804           this.resolve = aFunction(resolve);
2805           this.reject = aFunction(reject);
2806         };
2807
2808         // `NewPromiseCapability` abstract operation
2809         // https://tc39.es/ecma262/#sec-newpromisecapability
2810         var f = function (C) {
2811           return new PromiseCapability(C);
2812         };
2813
2814         var newPromiseCapability$1 = {
2815                 f: f
2816         };
2817
2818         var promiseResolve = function (C, x) {
2819           anObject(C);
2820           if (isObject$4(x) && x.constructor === C) return x;
2821           var promiseCapability = newPromiseCapability$1.f(C);
2822           var resolve = promiseCapability.resolve;
2823           resolve(x);
2824           return promiseCapability.promise;
2825         };
2826
2827         var hostReportErrors = function (a, b) {
2828           var console = global$2.console;
2829           if (console && console.error) {
2830             arguments.length === 1 ? console.error(a) : console.error(a, b);
2831           }
2832         };
2833
2834         var perform = function (exec) {
2835           try {
2836             return { error: false, value: exec() };
2837           } catch (error) {
2838             return { error: true, value: error };
2839           }
2840         };
2841
2842         var engineIsBrowser = typeof window == 'object';
2843
2844         var task = task$1.set;
2845
2846
2847
2848
2849
2850
2851
2852
2853
2854
2855
2856
2857         var SPECIES$2 = wellKnownSymbol('species');
2858         var PROMISE = 'Promise';
2859         var getInternalState$1 = internalState.get;
2860         var setInternalState$3 = internalState.set;
2861         var getInternalPromiseState = internalState.getterFor(PROMISE);
2862         var NativePromisePrototype = nativePromiseConstructor && nativePromiseConstructor.prototype;
2863         var PromiseConstructor = nativePromiseConstructor;
2864         var PromiseConstructorPrototype = NativePromisePrototype;
2865         var TypeError$1 = global$2.TypeError;
2866         var document$1 = global$2.document;
2867         var process$1 = global$2.process;
2868         var newPromiseCapability = newPromiseCapability$1.f;
2869         var newGenericPromiseCapability = newPromiseCapability;
2870         var DISPATCH_EVENT = !!(document$1 && document$1.createEvent && global$2.dispatchEvent);
2871         var NATIVE_REJECTION_EVENT = typeof PromiseRejectionEvent == 'function';
2872         var UNHANDLED_REJECTION = 'unhandledrejection';
2873         var REJECTION_HANDLED = 'rejectionhandled';
2874         var PENDING = 0;
2875         var FULFILLED = 1;
2876         var REJECTED = 2;
2877         var HANDLED = 1;
2878         var UNHANDLED = 2;
2879         var SUBCLASSING = false;
2880         var Internal, OwnPromiseCapability, PromiseWrapper, nativeThen;
2881
2882         var FORCED$f = isForced_1(PROMISE, function () {
2883           var GLOBAL_CORE_JS_PROMISE = inspectSource(PromiseConstructor) !== String(PromiseConstructor);
2884           // V8 6.6 (Node 10 and Chrome 66) have a bug with resolving custom thenables
2885           // https://bugs.chromium.org/p/chromium/issues/detail?id=830565
2886           // We can't detect it synchronously, so just check versions
2887           if (!GLOBAL_CORE_JS_PROMISE && engineV8Version === 66) return true;
2888           // We can't use @@species feature detection in V8 since it causes
2889           // deoptimization and performance degradation
2890           // https://github.com/zloirock/core-js/issues/679
2891           if (engineV8Version >= 51 && /native code/.test(PromiseConstructor)) return false;
2892           // Detect correctness of subclassing with @@species support
2893           var promise = new PromiseConstructor(function (resolve) { resolve(1); });
2894           var FakePromise = function (exec) {
2895             exec(function () { /* empty */ }, function () { /* empty */ });
2896           };
2897           var constructor = promise.constructor = {};
2898           constructor[SPECIES$2] = FakePromise;
2899           SUBCLASSING = promise.then(function () { /* empty */ }) instanceof FakePromise;
2900           if (!SUBCLASSING) return true;
2901           // Unhandled rejections tracking support, NodeJS Promise without it fails @@species test
2902           return !GLOBAL_CORE_JS_PROMISE && engineIsBrowser && !NATIVE_REJECTION_EVENT;
2903         });
2904
2905         var INCORRECT_ITERATION$1 = FORCED$f || !checkCorrectnessOfIteration(function (iterable) {
2906           PromiseConstructor.all(iterable)['catch'](function () { /* empty */ });
2907         });
2908
2909         // helpers
2910         var isThenable = function (it) {
2911           var then;
2912           return isObject$4(it) && typeof (then = it.then) == 'function' ? then : false;
2913         };
2914
2915         var notify = function (state, isReject) {
2916           if (state.notified) return;
2917           state.notified = true;
2918           var chain = state.reactions;
2919           microtask(function () {
2920             var value = state.value;
2921             var ok = state.state == FULFILLED;
2922             var index = 0;
2923             // variable length - can't use forEach
2924             while (chain.length > index) {
2925               var reaction = chain[index++];
2926               var handler = ok ? reaction.ok : reaction.fail;
2927               var resolve = reaction.resolve;
2928               var reject = reaction.reject;
2929               var domain = reaction.domain;
2930               var result, then, exited;
2931               try {
2932                 if (handler) {
2933                   if (!ok) {
2934                     if (state.rejection === UNHANDLED) onHandleUnhandled(state);
2935                     state.rejection = HANDLED;
2936                   }
2937                   if (handler === true) result = value;
2938                   else {
2939                     if (domain) domain.enter();
2940                     result = handler(value); // can throw
2941                     if (domain) {
2942                       domain.exit();
2943                       exited = true;
2944                     }
2945                   }
2946                   if (result === reaction.promise) {
2947                     reject(TypeError$1('Promise-chain cycle'));
2948                   } else if (then = isThenable(result)) {
2949                     then.call(result, resolve, reject);
2950                   } else resolve(result);
2951                 } else reject(value);
2952               } catch (error) {
2953                 if (domain && !exited) domain.exit();
2954                 reject(error);
2955               }
2956             }
2957             state.reactions = [];
2958             state.notified = false;
2959             if (isReject && !state.rejection) onUnhandled(state);
2960           });
2961         };
2962
2963         var dispatchEvent$1 = function (name, promise, reason) {
2964           var event, handler;
2965           if (DISPATCH_EVENT) {
2966             event = document$1.createEvent('Event');
2967             event.promise = promise;
2968             event.reason = reason;
2969             event.initEvent(name, false, true);
2970             global$2.dispatchEvent(event);
2971           } else event = { promise: promise, reason: reason };
2972           if (!NATIVE_REJECTION_EVENT && (handler = global$2['on' + name])) handler(event);
2973           else if (name === UNHANDLED_REJECTION) hostReportErrors('Unhandled promise rejection', reason);
2974         };
2975
2976         var onUnhandled = function (state) {
2977           task.call(global$2, function () {
2978             var promise = state.facade;
2979             var value = state.value;
2980             var IS_UNHANDLED = isUnhandled(state);
2981             var result;
2982             if (IS_UNHANDLED) {
2983               result = perform(function () {
2984                 if (engineIsNode) {
2985                   process$1.emit('unhandledRejection', value, promise);
2986                 } else dispatchEvent$1(UNHANDLED_REJECTION, promise, value);
2987               });
2988               // Browsers should not trigger `rejectionHandled` event if it was handled here, NodeJS - should
2989               state.rejection = engineIsNode || isUnhandled(state) ? UNHANDLED : HANDLED;
2990               if (result.error) throw result.value;
2991             }
2992           });
2993         };
2994
2995         var isUnhandled = function (state) {
2996           return state.rejection !== HANDLED && !state.parent;
2997         };
2998
2999         var onHandleUnhandled = function (state) {
3000           task.call(global$2, function () {
3001             var promise = state.facade;
3002             if (engineIsNode) {
3003               process$1.emit('rejectionHandled', promise);
3004             } else dispatchEvent$1(REJECTION_HANDLED, promise, state.value);
3005           });
3006         };
3007
3008         var bind$2 = function (fn, state, unwrap) {
3009           return function (value) {
3010             fn(state, value, unwrap);
3011           };
3012         };
3013
3014         var internalReject = function (state, value, unwrap) {
3015           if (state.done) return;
3016           state.done = true;
3017           if (unwrap) state = unwrap;
3018           state.value = value;
3019           state.state = REJECTED;
3020           notify(state, true);
3021         };
3022
3023         var internalResolve = function (state, value, unwrap) {
3024           if (state.done) return;
3025           state.done = true;
3026           if (unwrap) state = unwrap;
3027           try {
3028             if (state.facade === value) throw TypeError$1("Promise can't be resolved itself");
3029             var then = isThenable(value);
3030             if (then) {
3031               microtask(function () {
3032                 var wrapper = { done: false };
3033                 try {
3034                   then.call(value,
3035                     bind$2(internalResolve, wrapper, state),
3036                     bind$2(internalReject, wrapper, state)
3037                   );
3038                 } catch (error) {
3039                   internalReject(wrapper, error, state);
3040                 }
3041               });
3042             } else {
3043               state.value = value;
3044               state.state = FULFILLED;
3045               notify(state, false);
3046             }
3047           } catch (error) {
3048             internalReject({ done: false }, error, state);
3049           }
3050         };
3051
3052         // constructor polyfill
3053         if (FORCED$f) {
3054           // 25.4.3.1 Promise(executor)
3055           PromiseConstructor = function Promise(executor) {
3056             anInstance(this, PromiseConstructor, PROMISE);
3057             aFunction(executor);
3058             Internal.call(this);
3059             var state = getInternalState$1(this);
3060             try {
3061               executor(bind$2(internalResolve, state), bind$2(internalReject, state));
3062             } catch (error) {
3063               internalReject(state, error);
3064             }
3065           };
3066           PromiseConstructorPrototype = PromiseConstructor.prototype;
3067           // eslint-disable-next-line no-unused-vars -- required for `.length`
3068           Internal = function Promise(executor) {
3069             setInternalState$3(this, {
3070               type: PROMISE,
3071               done: false,
3072               notified: false,
3073               parent: false,
3074               reactions: [],
3075               rejection: false,
3076               state: PENDING,
3077               value: undefined
3078             });
3079           };
3080           Internal.prototype = redefineAll(PromiseConstructorPrototype, {
3081             // `Promise.prototype.then` method
3082             // https://tc39.es/ecma262/#sec-promise.prototype.then
3083             then: function then(onFulfilled, onRejected) {
3084               var state = getInternalPromiseState(this);
3085               var reaction = newPromiseCapability(speciesConstructor(this, PromiseConstructor));
3086               reaction.ok = typeof onFulfilled == 'function' ? onFulfilled : true;
3087               reaction.fail = typeof onRejected == 'function' && onRejected;
3088               reaction.domain = engineIsNode ? process$1.domain : undefined;
3089               state.parent = true;
3090               state.reactions.push(reaction);
3091               if (state.state != PENDING) notify(state, false);
3092               return reaction.promise;
3093             },
3094             // `Promise.prototype.catch` method
3095             // https://tc39.es/ecma262/#sec-promise.prototype.catch
3096             'catch': function (onRejected) {
3097               return this.then(undefined, onRejected);
3098             }
3099           });
3100           OwnPromiseCapability = function () {
3101             var promise = new Internal();
3102             var state = getInternalState$1(promise);
3103             this.promise = promise;
3104             this.resolve = bind$2(internalResolve, state);
3105             this.reject = bind$2(internalReject, state);
3106           };
3107           newPromiseCapability$1.f = newPromiseCapability = function (C) {
3108             return C === PromiseConstructor || C === PromiseWrapper
3109               ? new OwnPromiseCapability(C)
3110               : newGenericPromiseCapability(C);
3111           };
3112
3113           if (typeof nativePromiseConstructor == 'function' && NativePromisePrototype !== Object.prototype) {
3114             nativeThen = NativePromisePrototype.then;
3115
3116             if (!SUBCLASSING) {
3117               // make `Promise#then` return a polyfilled `Promise` for native promise-based APIs
3118               redefine(NativePromisePrototype, 'then', function then(onFulfilled, onRejected) {
3119                 var that = this;
3120                 return new PromiseConstructor(function (resolve, reject) {
3121                   nativeThen.call(that, resolve, reject);
3122                 }).then(onFulfilled, onRejected);
3123               // https://github.com/zloirock/core-js/issues/640
3124               }, { unsafe: true });
3125
3126               // makes sure that native promise-based APIs `Promise#catch` properly works with patched `Promise#then`
3127               redefine(NativePromisePrototype, 'catch', PromiseConstructorPrototype['catch'], { unsafe: true });
3128             }
3129
3130             // make `.constructor === Promise` work for native promise-based APIs
3131             try {
3132               delete NativePromisePrototype.constructor;
3133             } catch (error) { /* empty */ }
3134
3135             // make `instanceof Promise` work for native promise-based APIs
3136             if (objectSetPrototypeOf) {
3137               objectSetPrototypeOf(NativePromisePrototype, PromiseConstructorPrototype);
3138             }
3139           }
3140         }
3141
3142         _export({ global: true, wrap: true, forced: FORCED$f }, {
3143           Promise: PromiseConstructor
3144         });
3145
3146         setToStringTag(PromiseConstructor, PROMISE, false);
3147         setSpecies(PROMISE);
3148
3149         PromiseWrapper = getBuiltIn(PROMISE);
3150
3151         // statics
3152         _export({ target: PROMISE, stat: true, forced: FORCED$f }, {
3153           // `Promise.reject` method
3154           // https://tc39.es/ecma262/#sec-promise.reject
3155           reject: function reject(r) {
3156             var capability = newPromiseCapability(this);
3157             capability.reject.call(undefined, r);
3158             return capability.promise;
3159           }
3160         });
3161
3162         _export({ target: PROMISE, stat: true, forced: FORCED$f }, {
3163           // `Promise.resolve` method
3164           // https://tc39.es/ecma262/#sec-promise.resolve
3165           resolve: function resolve(x) {
3166             return promiseResolve(this, x);
3167           }
3168         });
3169
3170         _export({ target: PROMISE, stat: true, forced: INCORRECT_ITERATION$1 }, {
3171           // `Promise.all` method
3172           // https://tc39.es/ecma262/#sec-promise.all
3173           all: function all(iterable) {
3174             var C = this;
3175             var capability = newPromiseCapability(C);
3176             var resolve = capability.resolve;
3177             var reject = capability.reject;
3178             var result = perform(function () {
3179               var $promiseResolve = aFunction(C.resolve);
3180               var values = [];
3181               var counter = 0;
3182               var remaining = 1;
3183               iterate(iterable, function (promise) {
3184                 var index = counter++;
3185                 var alreadyCalled = false;
3186                 values.push(undefined);
3187                 remaining++;
3188                 $promiseResolve.call(C, promise).then(function (value) {
3189                   if (alreadyCalled) return;
3190                   alreadyCalled = true;
3191                   values[index] = value;
3192                   --remaining || resolve(values);
3193                 }, reject);
3194               });
3195               --remaining || resolve(values);
3196             });
3197             if (result.error) reject(result.value);
3198             return capability.promise;
3199           },
3200           // `Promise.race` method
3201           // https://tc39.es/ecma262/#sec-promise.race
3202           race: function race(iterable) {
3203             var C = this;
3204             var capability = newPromiseCapability(C);
3205             var reject = capability.reject;
3206             var result = perform(function () {
3207               var $promiseResolve = aFunction(C.resolve);
3208               iterate(iterable, function (promise) {
3209                 $promiseResolve.call(C, promise).then(capability.resolve, reject);
3210               });
3211             });
3212             if (result.error) reject(result.value);
3213             return capability.promise;
3214           }
3215         });
3216
3217         /* eslint-disable no-new -- required for testing */
3218
3219         var NATIVE_ARRAY_BUFFER_VIEWS = arrayBufferViewCore.NATIVE_ARRAY_BUFFER_VIEWS;
3220
3221         var ArrayBuffer$1 = global$2.ArrayBuffer;
3222         var Int8Array$2 = global$2.Int8Array;
3223
3224         var typedArrayConstructorsRequireWrappers = !NATIVE_ARRAY_BUFFER_VIEWS || !fails(function () {
3225           Int8Array$2(1);
3226         }) || !fails(function () {
3227           new Int8Array$2(-1);
3228         }) || !checkCorrectnessOfIteration(function (iterable) {
3229           new Int8Array$2();
3230           new Int8Array$2(null);
3231           new Int8Array$2(1.5);
3232           new Int8Array$2(iterable);
3233         }, true) || fails(function () {
3234           // Safari (11+) bug - a reason why even Safari 13 should load a typed array polyfill
3235           return new Int8Array$2(new ArrayBuffer$1(2), 1, undefined).length !== 1;
3236         });
3237
3238         var toPositiveInteger = function (it) {
3239           var result = toInteger(it);
3240           if (result < 0) throw RangeError("The argument can't be less than 0");
3241           return result;
3242         };
3243
3244         var toOffset = function (it, BYTES) {
3245           var offset = toPositiveInteger(it);
3246           if (offset % BYTES) throw RangeError('Wrong offset');
3247           return offset;
3248         };
3249
3250         var aTypedArrayConstructor$3 = arrayBufferViewCore.aTypedArrayConstructor;
3251
3252         var typedArrayFrom = function from(source /* , mapfn, thisArg */) {
3253           var O = toObject(source);
3254           var argumentsLength = arguments.length;
3255           var mapfn = argumentsLength > 1 ? arguments[1] : undefined;
3256           var mapping = mapfn !== undefined;
3257           var iteratorMethod = getIteratorMethod(O);
3258           var i, length, result, step, iterator, next;
3259           if (iteratorMethod != undefined && !isArrayIteratorMethod(iteratorMethod)) {
3260             iterator = iteratorMethod.call(O);
3261             next = iterator.next;
3262             O = [];
3263             while (!(step = next.call(iterator)).done) {
3264               O.push(step.value);
3265             }
3266           }
3267           if (mapping && argumentsLength > 2) {
3268             mapfn = functionBindContext(mapfn, arguments[2], 2);
3269           }
3270           length = toLength(O.length);
3271           result = new (aTypedArrayConstructor$3(this))(length);
3272           for (i = 0; length > i; i++) {
3273             result[i] = mapping ? mapfn(O[i], i) : O[i];
3274           }
3275           return result;
3276         };
3277
3278         // makes subclassing work correct for wrapped built-ins
3279         var inheritIfRequired = function ($this, dummy, Wrapper) {
3280           var NewTarget, NewTargetPrototype;
3281           if (
3282             // it can work only with native `setPrototypeOf`
3283             objectSetPrototypeOf &&
3284             // we haven't completely correct pre-ES6 way for getting `new.target`, so use this
3285             typeof (NewTarget = dummy.constructor) == 'function' &&
3286             NewTarget !== Wrapper &&
3287             isObject$4(NewTargetPrototype = NewTarget.prototype) &&
3288             NewTargetPrototype !== Wrapper.prototype
3289           ) objectSetPrototypeOf($this, NewTargetPrototype);
3290           return $this;
3291         };
3292
3293         var typedArrayConstructor = createCommonjsModule(function (module) {
3294
3295
3296
3297
3298
3299
3300
3301
3302
3303
3304
3305
3306
3307
3308
3309
3310
3311
3312         var getOwnPropertyNames = objectGetOwnPropertyNames.f;
3313
3314         var forEach = arrayIteration.forEach;
3315
3316
3317
3318
3319
3320
3321         var getInternalState = internalState.get;
3322         var setInternalState = internalState.set;
3323         var nativeDefineProperty = objectDefineProperty.f;
3324         var nativeGetOwnPropertyDescriptor = objectGetOwnPropertyDescriptor.f;
3325         var round = Math.round;
3326         var RangeError = global$2.RangeError;
3327         var ArrayBuffer = arrayBuffer.ArrayBuffer;
3328         var DataView = arrayBuffer.DataView;
3329         var NATIVE_ARRAY_BUFFER_VIEWS = arrayBufferViewCore.NATIVE_ARRAY_BUFFER_VIEWS;
3330         var TYPED_ARRAY_TAG = arrayBufferViewCore.TYPED_ARRAY_TAG;
3331         var TypedArray = arrayBufferViewCore.TypedArray;
3332         var TypedArrayPrototype = arrayBufferViewCore.TypedArrayPrototype;
3333         var aTypedArrayConstructor = arrayBufferViewCore.aTypedArrayConstructor;
3334         var isTypedArray = arrayBufferViewCore.isTypedArray;
3335         var BYTES_PER_ELEMENT = 'BYTES_PER_ELEMENT';
3336         var WRONG_LENGTH = 'Wrong length';
3337
3338         var fromList = function (C, list) {
3339           var index = 0;
3340           var length = list.length;
3341           var result = new (aTypedArrayConstructor(C))(length);
3342           while (length > index) result[index] = list[index++];
3343           return result;
3344         };
3345
3346         var addGetter = function (it, key) {
3347           nativeDefineProperty(it, key, { get: function () {
3348             return getInternalState(this)[key];
3349           } });
3350         };
3351
3352         var isArrayBuffer = function (it) {
3353           var klass;
3354           return it instanceof ArrayBuffer || (klass = classof(it)) == 'ArrayBuffer' || klass == 'SharedArrayBuffer';
3355         };
3356
3357         var isTypedArrayIndex = function (target, key) {
3358           return isTypedArray(target)
3359             && typeof key != 'symbol'
3360             && key in target
3361             && String(+key) == String(key);
3362         };
3363
3364         var wrappedGetOwnPropertyDescriptor = function getOwnPropertyDescriptor(target, key) {
3365           return isTypedArrayIndex(target, key = toPrimitive(key, true))
3366             ? createPropertyDescriptor(2, target[key])
3367             : nativeGetOwnPropertyDescriptor(target, key);
3368         };
3369
3370         var wrappedDefineProperty = function defineProperty(target, key, descriptor) {
3371           if (isTypedArrayIndex(target, key = toPrimitive(key, true))
3372             && isObject$4(descriptor)
3373             && has$1(descriptor, 'value')
3374             && !has$1(descriptor, 'get')
3375             && !has$1(descriptor, 'set')
3376             // TODO: add validation descriptor w/o calling accessors
3377             && !descriptor.configurable
3378             && (!has$1(descriptor, 'writable') || descriptor.writable)
3379             && (!has$1(descriptor, 'enumerable') || descriptor.enumerable)
3380           ) {
3381             target[key] = descriptor.value;
3382             return target;
3383           } return nativeDefineProperty(target, key, descriptor);
3384         };
3385
3386         if (descriptors) {
3387           if (!NATIVE_ARRAY_BUFFER_VIEWS) {
3388             objectGetOwnPropertyDescriptor.f = wrappedGetOwnPropertyDescriptor;
3389             objectDefineProperty.f = wrappedDefineProperty;
3390             addGetter(TypedArrayPrototype, 'buffer');
3391             addGetter(TypedArrayPrototype, 'byteOffset');
3392             addGetter(TypedArrayPrototype, 'byteLength');
3393             addGetter(TypedArrayPrototype, 'length');
3394           }
3395
3396           _export({ target: 'Object', stat: true, forced: !NATIVE_ARRAY_BUFFER_VIEWS }, {
3397             getOwnPropertyDescriptor: wrappedGetOwnPropertyDescriptor,
3398             defineProperty: wrappedDefineProperty
3399           });
3400
3401           module.exports = function (TYPE, wrapper, CLAMPED) {
3402             var BYTES = TYPE.match(/\d+$/)[0] / 8;
3403             var CONSTRUCTOR_NAME = TYPE + (CLAMPED ? 'Clamped' : '') + 'Array';
3404             var GETTER = 'get' + TYPE;
3405             var SETTER = 'set' + TYPE;
3406             var NativeTypedArrayConstructor = global$2[CONSTRUCTOR_NAME];
3407             var TypedArrayConstructor = NativeTypedArrayConstructor;
3408             var TypedArrayConstructorPrototype = TypedArrayConstructor && TypedArrayConstructor.prototype;
3409             var exported = {};
3410
3411             var getter = function (that, index) {
3412               var data = getInternalState(that);
3413               return data.view[GETTER](index * BYTES + data.byteOffset, true);
3414             };
3415
3416             var setter = function (that, index, value) {
3417               var data = getInternalState(that);
3418               if (CLAMPED) value = (value = round(value)) < 0 ? 0 : value > 0xFF ? 0xFF : value & 0xFF;
3419               data.view[SETTER](index * BYTES + data.byteOffset, value, true);
3420             };
3421
3422             var addElement = function (that, index) {
3423               nativeDefineProperty(that, index, {
3424                 get: function () {
3425                   return getter(this, index);
3426                 },
3427                 set: function (value) {
3428                   return setter(this, index, value);
3429                 },
3430                 enumerable: true
3431               });
3432             };
3433
3434             if (!NATIVE_ARRAY_BUFFER_VIEWS) {
3435               TypedArrayConstructor = wrapper(function (that, data, offset, $length) {
3436                 anInstance(that, TypedArrayConstructor, CONSTRUCTOR_NAME);
3437                 var index = 0;
3438                 var byteOffset = 0;
3439                 var buffer, byteLength, length;
3440                 if (!isObject$4(data)) {
3441                   length = toIndex(data);
3442                   byteLength = length * BYTES;
3443                   buffer = new ArrayBuffer(byteLength);
3444                 } else if (isArrayBuffer(data)) {
3445                   buffer = data;
3446                   byteOffset = toOffset(offset, BYTES);
3447                   var $len = data.byteLength;
3448                   if ($length === undefined) {
3449                     if ($len % BYTES) throw RangeError(WRONG_LENGTH);
3450                     byteLength = $len - byteOffset;
3451                     if (byteLength < 0) throw RangeError(WRONG_LENGTH);
3452                   } else {
3453                     byteLength = toLength($length) * BYTES;
3454                     if (byteLength + byteOffset > $len) throw RangeError(WRONG_LENGTH);
3455                   }
3456                   length = byteLength / BYTES;
3457                 } else if (isTypedArray(data)) {
3458                   return fromList(TypedArrayConstructor, data);
3459                 } else {
3460                   return typedArrayFrom.call(TypedArrayConstructor, data);
3461                 }
3462                 setInternalState(that, {
3463                   buffer: buffer,
3464                   byteOffset: byteOffset,
3465                   byteLength: byteLength,
3466                   length: length,
3467                   view: new DataView(buffer)
3468                 });
3469                 while (index < length) addElement(that, index++);
3470               });
3471
3472               if (objectSetPrototypeOf) objectSetPrototypeOf(TypedArrayConstructor, TypedArray);
3473               TypedArrayConstructorPrototype = TypedArrayConstructor.prototype = objectCreate(TypedArrayPrototype);
3474             } else if (typedArrayConstructorsRequireWrappers) {
3475               TypedArrayConstructor = wrapper(function (dummy, data, typedArrayOffset, $length) {
3476                 anInstance(dummy, TypedArrayConstructor, CONSTRUCTOR_NAME);
3477                 return inheritIfRequired(function () {
3478                   if (!isObject$4(data)) return new NativeTypedArrayConstructor(toIndex(data));
3479                   if (isArrayBuffer(data)) return $length !== undefined
3480                     ? new NativeTypedArrayConstructor(data, toOffset(typedArrayOffset, BYTES), $length)
3481                     : typedArrayOffset !== undefined
3482                       ? new NativeTypedArrayConstructor(data, toOffset(typedArrayOffset, BYTES))
3483                       : new NativeTypedArrayConstructor(data);
3484                   if (isTypedArray(data)) return fromList(TypedArrayConstructor, data);
3485                   return typedArrayFrom.call(TypedArrayConstructor, data);
3486                 }(), dummy, TypedArrayConstructor);
3487               });
3488
3489               if (objectSetPrototypeOf) objectSetPrototypeOf(TypedArrayConstructor, TypedArray);
3490               forEach(getOwnPropertyNames(NativeTypedArrayConstructor), function (key) {
3491                 if (!(key in TypedArrayConstructor)) {
3492                   createNonEnumerableProperty(TypedArrayConstructor, key, NativeTypedArrayConstructor[key]);
3493                 }
3494               });
3495               TypedArrayConstructor.prototype = TypedArrayConstructorPrototype;
3496             }
3497
3498             if (TypedArrayConstructorPrototype.constructor !== TypedArrayConstructor) {
3499               createNonEnumerableProperty(TypedArrayConstructorPrototype, 'constructor', TypedArrayConstructor);
3500             }
3501
3502             if (TYPED_ARRAY_TAG) {
3503               createNonEnumerableProperty(TypedArrayConstructorPrototype, TYPED_ARRAY_TAG, CONSTRUCTOR_NAME);
3504             }
3505
3506             exported[CONSTRUCTOR_NAME] = TypedArrayConstructor;
3507
3508             _export({
3509               global: true, forced: TypedArrayConstructor != NativeTypedArrayConstructor, sham: !NATIVE_ARRAY_BUFFER_VIEWS
3510             }, exported);
3511
3512             if (!(BYTES_PER_ELEMENT in TypedArrayConstructor)) {
3513               createNonEnumerableProperty(TypedArrayConstructor, BYTES_PER_ELEMENT, BYTES);
3514             }
3515
3516             if (!(BYTES_PER_ELEMENT in TypedArrayConstructorPrototype)) {
3517               createNonEnumerableProperty(TypedArrayConstructorPrototype, BYTES_PER_ELEMENT, BYTES);
3518             }
3519
3520             setSpecies(CONSTRUCTOR_NAME);
3521           };
3522         } else module.exports = function () { /* empty */ };
3523         });
3524
3525         // `Uint8Array` constructor
3526         // https://tc39.es/ecma262/#sec-typedarray-objects
3527         typedArrayConstructor('Uint8', function (init) {
3528           return function Uint8Array(data, byteOffset, length) {
3529             return init(this, data, byteOffset, length);
3530           };
3531         });
3532
3533         var min$7 = Math.min;
3534
3535         // `Array.prototype.copyWithin` method implementation
3536         // https://tc39.es/ecma262/#sec-array.prototype.copywithin
3537         // eslint-disable-next-line es/no-array-prototype-copywithin -- safe
3538         var arrayCopyWithin = [].copyWithin || function copyWithin(target /* = 0 */, start /* = 0, end = @length */) {
3539           var O = toObject(this);
3540           var len = toLength(O.length);
3541           var to = toAbsoluteIndex(target, len);
3542           var from = toAbsoluteIndex(start, len);
3543           var end = arguments.length > 2 ? arguments[2] : undefined;
3544           var count = min$7((end === undefined ? len : toAbsoluteIndex(end, len)) - from, len - to);
3545           var inc = 1;
3546           if (from < to && to < from + count) {
3547             inc = -1;
3548             from += count - 1;
3549             to += count - 1;
3550           }
3551           while (count-- > 0) {
3552             if (from in O) O[to] = O[from];
3553             else delete O[to];
3554             to += inc;
3555             from += inc;
3556           } return O;
3557         };
3558
3559         var aTypedArray$l = arrayBufferViewCore.aTypedArray;
3560         var exportTypedArrayMethod$m = arrayBufferViewCore.exportTypedArrayMethod;
3561
3562         // `%TypedArray%.prototype.copyWithin` method
3563         // https://tc39.es/ecma262/#sec-%typedarray%.prototype.copywithin
3564         exportTypedArrayMethod$m('copyWithin', function copyWithin(target, start /* , end */) {
3565           return arrayCopyWithin.call(aTypedArray$l(this), target, start, arguments.length > 2 ? arguments[2] : undefined);
3566         });
3567
3568         var $every$1 = arrayIteration.every;
3569
3570         var aTypedArray$k = arrayBufferViewCore.aTypedArray;
3571         var exportTypedArrayMethod$l = arrayBufferViewCore.exportTypedArrayMethod;
3572
3573         // `%TypedArray%.prototype.every` method
3574         // https://tc39.es/ecma262/#sec-%typedarray%.prototype.every
3575         exportTypedArrayMethod$l('every', function every(callbackfn /* , thisArg */) {
3576           return $every$1(aTypedArray$k(this), callbackfn, arguments.length > 1 ? arguments[1] : undefined);
3577         });
3578
3579         var aTypedArray$j = arrayBufferViewCore.aTypedArray;
3580         var exportTypedArrayMethod$k = arrayBufferViewCore.exportTypedArrayMethod;
3581
3582         // `%TypedArray%.prototype.fill` method
3583         // https://tc39.es/ecma262/#sec-%typedarray%.prototype.fill
3584         // eslint-disable-next-line no-unused-vars -- required for `.length`
3585         exportTypedArrayMethod$k('fill', function fill(value /* , start, end */) {
3586           return arrayFill.apply(aTypedArray$j(this), arguments);
3587         });
3588
3589         var aTypedArrayConstructor$2 = arrayBufferViewCore.aTypedArrayConstructor;
3590
3591
3592         var typedArrayFromSpeciesAndList = function (instance, list) {
3593           var C = speciesConstructor(instance, instance.constructor);
3594           var index = 0;
3595           var length = list.length;
3596           var result = new (aTypedArrayConstructor$2(C))(length);
3597           while (length > index) result[index] = list[index++];
3598           return result;
3599         };
3600
3601         var $filter$1 = arrayIteration.filter;
3602
3603
3604         var aTypedArray$i = arrayBufferViewCore.aTypedArray;
3605         var exportTypedArrayMethod$j = arrayBufferViewCore.exportTypedArrayMethod;
3606
3607         // `%TypedArray%.prototype.filter` method
3608         // https://tc39.es/ecma262/#sec-%typedarray%.prototype.filter
3609         exportTypedArrayMethod$j('filter', function filter(callbackfn /* , thisArg */) {
3610           var list = $filter$1(aTypedArray$i(this), callbackfn, arguments.length > 1 ? arguments[1] : undefined);
3611           return typedArrayFromSpeciesAndList(this, list);
3612         });
3613
3614         var $find$1 = arrayIteration.find;
3615
3616         var aTypedArray$h = arrayBufferViewCore.aTypedArray;
3617         var exportTypedArrayMethod$i = arrayBufferViewCore.exportTypedArrayMethod;
3618
3619         // `%TypedArray%.prototype.find` method
3620         // https://tc39.es/ecma262/#sec-%typedarray%.prototype.find
3621         exportTypedArrayMethod$i('find', function find(predicate /* , thisArg */) {
3622           return $find$1(aTypedArray$h(this), predicate, arguments.length > 1 ? arguments[1] : undefined);
3623         });
3624
3625         var $findIndex$1 = arrayIteration.findIndex;
3626
3627         var aTypedArray$g = arrayBufferViewCore.aTypedArray;
3628         var exportTypedArrayMethod$h = arrayBufferViewCore.exportTypedArrayMethod;
3629
3630         // `%TypedArray%.prototype.findIndex` method
3631         // https://tc39.es/ecma262/#sec-%typedarray%.prototype.findindex
3632         exportTypedArrayMethod$h('findIndex', function findIndex(predicate /* , thisArg */) {
3633           return $findIndex$1(aTypedArray$g(this), predicate, arguments.length > 1 ? arguments[1] : undefined);
3634         });
3635
3636         var $forEach = arrayIteration.forEach;
3637
3638         var aTypedArray$f = arrayBufferViewCore.aTypedArray;
3639         var exportTypedArrayMethod$g = arrayBufferViewCore.exportTypedArrayMethod;
3640
3641         // `%TypedArray%.prototype.forEach` method
3642         // https://tc39.es/ecma262/#sec-%typedarray%.prototype.foreach
3643         exportTypedArrayMethod$g('forEach', function forEach(callbackfn /* , thisArg */) {
3644           $forEach(aTypedArray$f(this), callbackfn, arguments.length > 1 ? arguments[1] : undefined);
3645         });
3646
3647         var $includes$1 = arrayIncludes.includes;
3648
3649         var aTypedArray$e = arrayBufferViewCore.aTypedArray;
3650         var exportTypedArrayMethod$f = arrayBufferViewCore.exportTypedArrayMethod;
3651
3652         // `%TypedArray%.prototype.includes` method
3653         // https://tc39.es/ecma262/#sec-%typedarray%.prototype.includes
3654         exportTypedArrayMethod$f('includes', function includes(searchElement /* , fromIndex */) {
3655           return $includes$1(aTypedArray$e(this), searchElement, arguments.length > 1 ? arguments[1] : undefined);
3656         });
3657
3658         var $indexOf = arrayIncludes.indexOf;
3659
3660         var aTypedArray$d = arrayBufferViewCore.aTypedArray;
3661         var exportTypedArrayMethod$e = arrayBufferViewCore.exportTypedArrayMethod;
3662
3663         // `%TypedArray%.prototype.indexOf` method
3664         // https://tc39.es/ecma262/#sec-%typedarray%.prototype.indexof
3665         exportTypedArrayMethod$e('indexOf', function indexOf(searchElement /* , fromIndex */) {
3666           return $indexOf(aTypedArray$d(this), searchElement, arguments.length > 1 ? arguments[1] : undefined);
3667         });
3668
3669         var ITERATOR$2 = wellKnownSymbol('iterator');
3670         var Uint8Array$2 = global$2.Uint8Array;
3671         var arrayValues = es_array_iterator.values;
3672         var arrayKeys = es_array_iterator.keys;
3673         var arrayEntries = es_array_iterator.entries;
3674         var aTypedArray$c = arrayBufferViewCore.aTypedArray;
3675         var exportTypedArrayMethod$d = arrayBufferViewCore.exportTypedArrayMethod;
3676         var nativeTypedArrayIterator = Uint8Array$2 && Uint8Array$2.prototype[ITERATOR$2];
3677
3678         var CORRECT_ITER_NAME = !!nativeTypedArrayIterator
3679           && (nativeTypedArrayIterator.name == 'values' || nativeTypedArrayIterator.name == undefined);
3680
3681         var typedArrayValues = function values() {
3682           return arrayValues.call(aTypedArray$c(this));
3683         };
3684
3685         // `%TypedArray%.prototype.entries` method
3686         // https://tc39.es/ecma262/#sec-%typedarray%.prototype.entries
3687         exportTypedArrayMethod$d('entries', function entries() {
3688           return arrayEntries.call(aTypedArray$c(this));
3689         });
3690         // `%TypedArray%.prototype.keys` method
3691         // https://tc39.es/ecma262/#sec-%typedarray%.prototype.keys
3692         exportTypedArrayMethod$d('keys', function keys() {
3693           return arrayKeys.call(aTypedArray$c(this));
3694         });
3695         // `%TypedArray%.prototype.values` method
3696         // https://tc39.es/ecma262/#sec-%typedarray%.prototype.values
3697         exportTypedArrayMethod$d('values', typedArrayValues, !CORRECT_ITER_NAME);
3698         // `%TypedArray%.prototype[@@iterator]` method
3699         // https://tc39.es/ecma262/#sec-%typedarray%.prototype-@@iterator
3700         exportTypedArrayMethod$d(ITERATOR$2, typedArrayValues, !CORRECT_ITER_NAME);
3701
3702         var aTypedArray$b = arrayBufferViewCore.aTypedArray;
3703         var exportTypedArrayMethod$c = arrayBufferViewCore.exportTypedArrayMethod;
3704         var $join = [].join;
3705
3706         // `%TypedArray%.prototype.join` method
3707         // https://tc39.es/ecma262/#sec-%typedarray%.prototype.join
3708         // eslint-disable-next-line no-unused-vars -- required for `.length`
3709         exportTypedArrayMethod$c('join', function join(separator) {
3710           return $join.apply(aTypedArray$b(this), arguments);
3711         });
3712
3713         /* eslint-disable es/no-array-prototype-lastindexof -- safe */
3714
3715
3716
3717
3718
3719         var min$6 = Math.min;
3720         var $lastIndexOf = [].lastIndexOf;
3721         var NEGATIVE_ZERO = !!$lastIndexOf && 1 / [1].lastIndexOf(1, -0) < 0;
3722         var STRICT_METHOD$5 = arrayMethodIsStrict('lastIndexOf');
3723         var FORCED$e = NEGATIVE_ZERO || !STRICT_METHOD$5;
3724
3725         // `Array.prototype.lastIndexOf` method implementation
3726         // https://tc39.es/ecma262/#sec-array.prototype.lastindexof
3727         var arrayLastIndexOf = FORCED$e ? function lastIndexOf(searchElement /* , fromIndex = @[*-1] */) {
3728           // convert -0 to +0
3729           if (NEGATIVE_ZERO) return $lastIndexOf.apply(this, arguments) || 0;
3730           var O = toIndexedObject(this);
3731           var length = toLength(O.length);
3732           var index = length - 1;
3733           if (arguments.length > 1) index = min$6(index, toInteger(arguments[1]));
3734           if (index < 0) index = length + index;
3735           for (;index >= 0; index--) if (index in O && O[index] === searchElement) return index || 0;
3736           return -1;
3737         } : $lastIndexOf;
3738
3739         var aTypedArray$a = arrayBufferViewCore.aTypedArray;
3740         var exportTypedArrayMethod$b = arrayBufferViewCore.exportTypedArrayMethod;
3741
3742         // `%TypedArray%.prototype.lastIndexOf` method
3743         // https://tc39.es/ecma262/#sec-%typedarray%.prototype.lastindexof
3744         // eslint-disable-next-line no-unused-vars -- required for `.length`
3745         exportTypedArrayMethod$b('lastIndexOf', function lastIndexOf(searchElement /* , fromIndex */) {
3746           return arrayLastIndexOf.apply(aTypedArray$a(this), arguments);
3747         });
3748
3749         var $map = arrayIteration.map;
3750
3751
3752         var aTypedArray$9 = arrayBufferViewCore.aTypedArray;
3753         var aTypedArrayConstructor$1 = arrayBufferViewCore.aTypedArrayConstructor;
3754         var exportTypedArrayMethod$a = arrayBufferViewCore.exportTypedArrayMethod;
3755
3756         // `%TypedArray%.prototype.map` method
3757         // https://tc39.es/ecma262/#sec-%typedarray%.prototype.map
3758         exportTypedArrayMethod$a('map', function map(mapfn /* , thisArg */) {
3759           return $map(aTypedArray$9(this), mapfn, arguments.length > 1 ? arguments[1] : undefined, function (O, length) {
3760             return new (aTypedArrayConstructor$1(speciesConstructor(O, O.constructor)))(length);
3761           });
3762         });
3763
3764         // `Array.prototype.{ reduce, reduceRight }` methods implementation
3765         var createMethod$3 = function (IS_RIGHT) {
3766           return function (that, callbackfn, argumentsLength, memo) {
3767             aFunction(callbackfn);
3768             var O = toObject(that);
3769             var self = indexedObject(O);
3770             var length = toLength(O.length);
3771             var index = IS_RIGHT ? length - 1 : 0;
3772             var i = IS_RIGHT ? -1 : 1;
3773             if (argumentsLength < 2) while (true) {
3774               if (index in self) {
3775                 memo = self[index];
3776                 index += i;
3777                 break;
3778               }
3779               index += i;
3780               if (IS_RIGHT ? index < 0 : length <= index) {
3781                 throw TypeError('Reduce of empty array with no initial value');
3782               }
3783             }
3784             for (;IS_RIGHT ? index >= 0 : length > index; index += i) if (index in self) {
3785               memo = callbackfn(memo, self[index], index, O);
3786             }
3787             return memo;
3788           };
3789         };
3790
3791         var arrayReduce = {
3792           // `Array.prototype.reduce` method
3793           // https://tc39.es/ecma262/#sec-array.prototype.reduce
3794           left: createMethod$3(false),
3795           // `Array.prototype.reduceRight` method
3796           // https://tc39.es/ecma262/#sec-array.prototype.reduceright
3797           right: createMethod$3(true)
3798         };
3799
3800         var $reduce$1 = arrayReduce.left;
3801
3802         var aTypedArray$8 = arrayBufferViewCore.aTypedArray;
3803         var exportTypedArrayMethod$9 = arrayBufferViewCore.exportTypedArrayMethod;
3804
3805         // `%TypedArray%.prototype.reduce` method
3806         // https://tc39.es/ecma262/#sec-%typedarray%.prototype.reduce
3807         exportTypedArrayMethod$9('reduce', function reduce(callbackfn /* , initialValue */) {
3808           return $reduce$1(aTypedArray$8(this), callbackfn, arguments.length, arguments.length > 1 ? arguments[1] : undefined);
3809         });
3810
3811         var $reduceRight = arrayReduce.right;
3812
3813         var aTypedArray$7 = arrayBufferViewCore.aTypedArray;
3814         var exportTypedArrayMethod$8 = arrayBufferViewCore.exportTypedArrayMethod;
3815
3816         // `%TypedArray%.prototype.reduceRicht` method
3817         // https://tc39.es/ecma262/#sec-%typedarray%.prototype.reduceright
3818         exportTypedArrayMethod$8('reduceRight', function reduceRight(callbackfn /* , initialValue */) {
3819           return $reduceRight(aTypedArray$7(this), callbackfn, arguments.length, arguments.length > 1 ? arguments[1] : undefined);
3820         });
3821
3822         var aTypedArray$6 = arrayBufferViewCore.aTypedArray;
3823         var exportTypedArrayMethod$7 = arrayBufferViewCore.exportTypedArrayMethod;
3824         var floor$5 = Math.floor;
3825
3826         // `%TypedArray%.prototype.reverse` method
3827         // https://tc39.es/ecma262/#sec-%typedarray%.prototype.reverse
3828         exportTypedArrayMethod$7('reverse', function reverse() {
3829           var that = this;
3830           var length = aTypedArray$6(that).length;
3831           var middle = floor$5(length / 2);
3832           var index = 0;
3833           var value;
3834           while (index < middle) {
3835             value = that[index];
3836             that[index++] = that[--length];
3837             that[length] = value;
3838           } return that;
3839         });
3840
3841         var aTypedArray$5 = arrayBufferViewCore.aTypedArray;
3842         var exportTypedArrayMethod$6 = arrayBufferViewCore.exportTypedArrayMethod;
3843
3844         var FORCED$d = fails(function () {
3845           // eslint-disable-next-line es/no-typed-arrays -- required for testing
3846           new Int8Array(1).set({});
3847         });
3848
3849         // `%TypedArray%.prototype.set` method
3850         // https://tc39.es/ecma262/#sec-%typedarray%.prototype.set
3851         exportTypedArrayMethod$6('set', function set(arrayLike /* , offset */) {
3852           aTypedArray$5(this);
3853           var offset = toOffset(arguments.length > 1 ? arguments[1] : undefined, 1);
3854           var length = this.length;
3855           var src = toObject(arrayLike);
3856           var len = toLength(src.length);
3857           var index = 0;
3858           if (len + offset > length) throw RangeError('Wrong length');
3859           while (index < len) this[offset + index] = src[index++];
3860         }, FORCED$d);
3861
3862         var aTypedArray$4 = arrayBufferViewCore.aTypedArray;
3863         var aTypedArrayConstructor = arrayBufferViewCore.aTypedArrayConstructor;
3864         var exportTypedArrayMethod$5 = arrayBufferViewCore.exportTypedArrayMethod;
3865         var $slice$1 = [].slice;
3866
3867         var FORCED$c = fails(function () {
3868           // eslint-disable-next-line es/no-typed-arrays -- required for testing
3869           new Int8Array(1).slice();
3870         });
3871
3872         // `%TypedArray%.prototype.slice` method
3873         // https://tc39.es/ecma262/#sec-%typedarray%.prototype.slice
3874         exportTypedArrayMethod$5('slice', function slice(start, end) {
3875           var list = $slice$1.call(aTypedArray$4(this), start, end);
3876           var C = speciesConstructor(this, this.constructor);
3877           var index = 0;
3878           var length = list.length;
3879           var result = new (aTypedArrayConstructor(C))(length);
3880           while (length > index) result[index] = list[index++];
3881           return result;
3882         }, FORCED$c);
3883
3884         var $some$1 = arrayIteration.some;
3885
3886         var aTypedArray$3 = arrayBufferViewCore.aTypedArray;
3887         var exportTypedArrayMethod$4 = arrayBufferViewCore.exportTypedArrayMethod;
3888
3889         // `%TypedArray%.prototype.some` method
3890         // https://tc39.es/ecma262/#sec-%typedarray%.prototype.some
3891         exportTypedArrayMethod$4('some', function some(callbackfn /* , thisArg */) {
3892           return $some$1(aTypedArray$3(this), callbackfn, arguments.length > 1 ? arguments[1] : undefined);
3893         });
3894
3895         // TODO: use something more complex like timsort?
3896         var floor$4 = Math.floor;
3897
3898         var mergeSort = function (array, comparefn) {
3899           var length = array.length;
3900           var middle = floor$4(length / 2);
3901           return length < 8 ? insertionSort(array, comparefn) : merge$5(
3902             mergeSort(array.slice(0, middle), comparefn),
3903             mergeSort(array.slice(middle), comparefn),
3904             comparefn
3905           );
3906         };
3907
3908         var insertionSort = function (array, comparefn) {
3909           var length = array.length;
3910           var i = 1;
3911           var element, j;
3912
3913           while (i < length) {
3914             j = i;
3915             element = array[i];
3916             while (j && comparefn(array[j - 1], element) > 0) {
3917               array[j] = array[--j];
3918             }
3919             if (j !== i++) array[j] = element;
3920           } return array;
3921         };
3922
3923         var merge$5 = function (left, right, comparefn) {
3924           var llength = left.length;
3925           var rlength = right.length;
3926           var lindex = 0;
3927           var rindex = 0;
3928           var result = [];
3929
3930           while (lindex < llength || rindex < rlength) {
3931             if (lindex < llength && rindex < rlength) {
3932               result.push(comparefn(left[lindex], right[rindex]) <= 0 ? left[lindex++] : right[rindex++]);
3933             } else {
3934               result.push(lindex < llength ? left[lindex++] : right[rindex++]);
3935             }
3936           } return result;
3937         };
3938
3939         var arraySort = mergeSort;
3940
3941         var firefox = engineUserAgent.match(/firefox\/(\d+)/i);
3942
3943         var engineFfVersion = !!firefox && +firefox[1];
3944
3945         var engineIsIeOrEdge = /MSIE|Trident/.test(engineUserAgent);
3946
3947         var webkit = engineUserAgent.match(/AppleWebKit\/(\d+)\./);
3948
3949         var engineWebkitVersion = !!webkit && +webkit[1];
3950
3951         var aTypedArray$2 = arrayBufferViewCore.aTypedArray;
3952         var exportTypedArrayMethod$3 = arrayBufferViewCore.exportTypedArrayMethod;
3953         var Uint16Array = global$2.Uint16Array;
3954         var nativeSort$1 = Uint16Array && Uint16Array.prototype.sort;
3955
3956         // WebKit
3957         var ACCEPT_INCORRECT_ARGUMENTS = !!nativeSort$1 && !fails(function () {
3958           var array = new Uint16Array(2);
3959           array.sort(null);
3960           array.sort({});
3961         });
3962
3963         var STABLE_SORT$1 = !!nativeSort$1 && !fails(function () {
3964           // feature detection can be too slow, so check engines versions
3965           if (engineV8Version) return engineV8Version < 74;
3966           if (engineFfVersion) return engineFfVersion < 67;
3967           if (engineIsIeOrEdge) return true;
3968           if (engineWebkitVersion) return engineWebkitVersion < 602;
3969
3970           var array = new Uint16Array(516);
3971           var expected = Array(516);
3972           var index, mod;
3973
3974           for (index = 0; index < 516; index++) {
3975             mod = index % 4;
3976             array[index] = 515 - index;
3977             expected[index] = index - 2 * mod + 3;
3978           }
3979
3980           array.sort(function (a, b) {
3981             return (a / 4 | 0) - (b / 4 | 0);
3982           });
3983
3984           for (index = 0; index < 516; index++) {
3985             if (array[index] !== expected[index]) return true;
3986           }
3987         });
3988
3989         var getSortCompare$1 = function (comparefn) {
3990           return function (x, y) {
3991             if (comparefn !== undefined) return +comparefn(x, y) || 0;
3992             // eslint-disable-next-line no-self-compare -- NaN check
3993             if (y !== y) return -1;
3994             // eslint-disable-next-line no-self-compare -- NaN check
3995             if (x !== x) return 1;
3996             if (x === 0 && y === 0) return 1 / x > 0 && 1 / y < 0 ? 1 : -1;
3997             return x > y;
3998           };
3999         };
4000
4001         // `%TypedArray%.prototype.sort` method
4002         // https://tc39.es/ecma262/#sec-%typedarray%.prototype.sort
4003         exportTypedArrayMethod$3('sort', function sort(comparefn) {
4004           var array = this;
4005           if (comparefn !== undefined) aFunction(comparefn);
4006           if (STABLE_SORT$1) return nativeSort$1.call(array, comparefn);
4007
4008           aTypedArray$2(array);
4009           var arrayLength = toLength(array.length);
4010           var items = Array(arrayLength);
4011           var index;
4012
4013           for (index = 0; index < arrayLength; index++) {
4014             items[index] = array[index];
4015           }
4016
4017           items = arraySort(array, getSortCompare$1(comparefn));
4018
4019           for (index = 0; index < arrayLength; index++) {
4020             array[index] = items[index];
4021           }
4022
4023           return array;
4024         }, !STABLE_SORT$1 || ACCEPT_INCORRECT_ARGUMENTS);
4025
4026         var aTypedArray$1 = arrayBufferViewCore.aTypedArray;
4027         var exportTypedArrayMethod$2 = arrayBufferViewCore.exportTypedArrayMethod;
4028
4029         // `%TypedArray%.prototype.subarray` method
4030         // https://tc39.es/ecma262/#sec-%typedarray%.prototype.subarray
4031         exportTypedArrayMethod$2('subarray', function subarray(begin, end) {
4032           var O = aTypedArray$1(this);
4033           var length = O.length;
4034           var beginIndex = toAbsoluteIndex(begin, length);
4035           return new (speciesConstructor(O, O.constructor))(
4036             O.buffer,
4037             O.byteOffset + beginIndex * O.BYTES_PER_ELEMENT,
4038             toLength((end === undefined ? length : toAbsoluteIndex(end, length)) - beginIndex)
4039           );
4040         });
4041
4042         var Int8Array$1 = global$2.Int8Array;
4043         var aTypedArray = arrayBufferViewCore.aTypedArray;
4044         var exportTypedArrayMethod$1 = arrayBufferViewCore.exportTypedArrayMethod;
4045         var $toLocaleString = [].toLocaleString;
4046         var $slice = [].slice;
4047
4048         // iOS Safari 6.x fails here
4049         var TO_LOCALE_STRING_BUG = !!Int8Array$1 && fails(function () {
4050           $toLocaleString.call(new Int8Array$1(1));
4051         });
4052
4053         var FORCED$b = fails(function () {
4054           return [1, 2].toLocaleString() != new Int8Array$1([1, 2]).toLocaleString();
4055         }) || !fails(function () {
4056           Int8Array$1.prototype.toLocaleString.call([1, 2]);
4057         });
4058
4059         // `%TypedArray%.prototype.toLocaleString` method
4060         // https://tc39.es/ecma262/#sec-%typedarray%.prototype.tolocalestring
4061         exportTypedArrayMethod$1('toLocaleString', function toLocaleString() {
4062           return $toLocaleString.apply(TO_LOCALE_STRING_BUG ? $slice.call(aTypedArray(this)) : aTypedArray(this), arguments);
4063         }, FORCED$b);
4064
4065         var exportTypedArrayMethod = arrayBufferViewCore.exportTypedArrayMethod;
4066
4067
4068
4069         var Uint8Array$1 = global$2.Uint8Array;
4070         var Uint8ArrayPrototype = Uint8Array$1 && Uint8Array$1.prototype || {};
4071         var arrayToString = [].toString;
4072         var arrayJoin = [].join;
4073
4074         if (fails(function () { arrayToString.call({}); })) {
4075           arrayToString = function toString() {
4076             return arrayJoin.call(this);
4077           };
4078         }
4079
4080         var IS_NOT_ARRAY_METHOD = Uint8ArrayPrototype.toString != arrayToString;
4081
4082         // `%TypedArray%.prototype.toString` method
4083         // https://tc39.es/ecma262/#sec-%typedarray%.prototype.tostring
4084         exportTypedArrayMethod('toString', arrayToString, IS_NOT_ARRAY_METHOD);
4085
4086         var nativeJoin = [].join;
4087
4088         var ES3_STRINGS = indexedObject != Object;
4089         var STRICT_METHOD$4 = arrayMethodIsStrict('join', ',');
4090
4091         // `Array.prototype.join` method
4092         // https://tc39.es/ecma262/#sec-array.prototype.join
4093         _export({ target: 'Array', proto: true, forced: ES3_STRINGS || !STRICT_METHOD$4 }, {
4094           join: function join(separator) {
4095             return nativeJoin.call(toIndexedObject(this), separator === undefined ? ',' : separator);
4096           }
4097         });
4098
4099         var createProperty = function (object, key, value) {
4100           var propertyKey = toPrimitive(key);
4101           if (propertyKey in object) objectDefineProperty.f(object, propertyKey, createPropertyDescriptor(0, value));
4102           else object[propertyKey] = value;
4103         };
4104
4105         var HAS_SPECIES_SUPPORT$2 = arrayMethodHasSpeciesSupport('slice');
4106
4107         var SPECIES$1 = wellKnownSymbol('species');
4108         var nativeSlice = [].slice;
4109         var max$3 = Math.max;
4110
4111         // `Array.prototype.slice` method
4112         // https://tc39.es/ecma262/#sec-array.prototype.slice
4113         // fallback for not array-like ES3 strings and DOM objects
4114         _export({ target: 'Array', proto: true, forced: !HAS_SPECIES_SUPPORT$2 }, {
4115           slice: function slice(start, end) {
4116             var O = toIndexedObject(this);
4117             var length = toLength(O.length);
4118             var k = toAbsoluteIndex(start, length);
4119             var fin = toAbsoluteIndex(end === undefined ? length : end, length);
4120             // inline `ArraySpeciesCreate` for usage native `Array#slice` where it's possible
4121             var Constructor, result, n;
4122             if (isArray(O)) {
4123               Constructor = O.constructor;
4124               // cross-realm fallback
4125               if (typeof Constructor == 'function' && (Constructor === Array || isArray(Constructor.prototype))) {
4126                 Constructor = undefined;
4127               } else if (isObject$4(Constructor)) {
4128                 Constructor = Constructor[SPECIES$1];
4129                 if (Constructor === null) Constructor = undefined;
4130               }
4131               if (Constructor === Array || Constructor === undefined) {
4132                 return nativeSlice.call(O, k, fin);
4133               }
4134             }
4135             result = new (Constructor === undefined ? Array : Constructor)(max$3(fin - k, 0));
4136             for (n = 0; k < fin; k++, n++) if (k in O) createProperty(result, n, O[k]);
4137             result.length = n;
4138             return result;
4139           }
4140         });
4141
4142         var ITERATOR$1 = wellKnownSymbol('iterator');
4143
4144         var nativeUrl = !fails(function () {
4145           var url = new URL('b?a=1&b=2&c=3', 'http://a');
4146           var searchParams = url.searchParams;
4147           var result = '';
4148           url.pathname = 'c%20d';
4149           searchParams.forEach(function (value, key) {
4150             searchParams['delete']('b');
4151             result += key + value;
4152           });
4153           return (isPure && !url.toJSON)
4154             || !searchParams.sort
4155             || url.href !== 'http://a/c%20d?a=1&c=3'
4156             || searchParams.get('c') !== '3'
4157             || String(new URLSearchParams('?a=1')) !== 'a=1'
4158             || !searchParams[ITERATOR$1]
4159             // throws in Edge
4160             || new URL('https://a@b').username !== 'a'
4161             || new URLSearchParams(new URLSearchParams('a=b')).get('a') !== 'b'
4162             // not punycoded in Edge
4163             || new URL('http://тест').host !== 'xn--e1aybc'
4164             // not escaped in Chrome 62-
4165             || new URL('http://a#б').hash !== '#%D0%B1'
4166             // fails in Chrome 66-
4167             || result !== 'a1c3'
4168             // throws in Safari
4169             || new URL('http://x', undefined).host !== 'x';
4170         });
4171
4172         // eslint-disable-next-line es/no-object-assign -- safe
4173         var $assign = Object.assign;
4174         // eslint-disable-next-line es/no-object-defineproperty -- required for testing
4175         var defineProperty$4 = Object.defineProperty;
4176
4177         // `Object.assign` method
4178         // https://tc39.es/ecma262/#sec-object.assign
4179         var objectAssign = !$assign || fails(function () {
4180           // should have correct order of operations (Edge bug)
4181           if (descriptors && $assign({ b: 1 }, $assign(defineProperty$4({}, 'a', {
4182             enumerable: true,
4183             get: function () {
4184               defineProperty$4(this, 'b', {
4185                 value: 3,
4186                 enumerable: false
4187               });
4188             }
4189           }), { b: 2 })).b !== 1) return true;
4190           // should work with symbols and should have deterministic property order (V8 bug)
4191           var A = {};
4192           var B = {};
4193           // eslint-disable-next-line es/no-symbol -- safe
4194           var symbol = Symbol();
4195           var alphabet = 'abcdefghijklmnopqrst';
4196           A[symbol] = 7;
4197           alphabet.split('').forEach(function (chr) { B[chr] = chr; });
4198           return $assign({}, A)[symbol] != 7 || objectKeys($assign({}, B)).join('') != alphabet;
4199         }) ? function assign(target, source) { // eslint-disable-line no-unused-vars -- required for `.length`
4200           var T = toObject(target);
4201           var argumentsLength = arguments.length;
4202           var index = 1;
4203           var getOwnPropertySymbols = objectGetOwnPropertySymbols.f;
4204           var propertyIsEnumerable = objectPropertyIsEnumerable.f;
4205           while (argumentsLength > index) {
4206             var S = indexedObject(arguments[index++]);
4207             var keys = getOwnPropertySymbols ? objectKeys(S).concat(getOwnPropertySymbols(S)) : objectKeys(S);
4208             var length = keys.length;
4209             var j = 0;
4210             var key;
4211             while (length > j) {
4212               key = keys[j++];
4213               if (!descriptors || propertyIsEnumerable.call(S, key)) T[key] = S[key];
4214             }
4215           } return T;
4216         } : $assign;
4217
4218         // call something on iterator step with safe closing on error
4219         var callWithSafeIterationClosing = function (iterator, fn, value, ENTRIES) {
4220           try {
4221             return ENTRIES ? fn(anObject(value)[0], value[1]) : fn(value);
4222           } catch (error) {
4223             iteratorClose(iterator);
4224             throw error;
4225           }
4226         };
4227
4228         // `Array.from` method implementation
4229         // https://tc39.es/ecma262/#sec-array.from
4230         var arrayFrom = function from(arrayLike /* , mapfn = undefined, thisArg = undefined */) {
4231           var O = toObject(arrayLike);
4232           var C = typeof this == 'function' ? this : Array;
4233           var argumentsLength = arguments.length;
4234           var mapfn = argumentsLength > 1 ? arguments[1] : undefined;
4235           var mapping = mapfn !== undefined;
4236           var iteratorMethod = getIteratorMethod(O);
4237           var index = 0;
4238           var length, result, step, iterator, next, value;
4239           if (mapping) mapfn = functionBindContext(mapfn, argumentsLength > 2 ? arguments[2] : undefined, 2);
4240           // if the target is not iterable or it's an array with the default iterator - use a simple case
4241           if (iteratorMethod != undefined && !(C == Array && isArrayIteratorMethod(iteratorMethod))) {
4242             iterator = iteratorMethod.call(O);
4243             next = iterator.next;
4244             result = new C();
4245             for (;!(step = next.call(iterator)).done; index++) {
4246               value = mapping ? callWithSafeIterationClosing(iterator, mapfn, [step.value, index], true) : step.value;
4247               createProperty(result, index, value);
4248             }
4249           } else {
4250             length = toLength(O.length);
4251             result = new C(length);
4252             for (;length > index; index++) {
4253               value = mapping ? mapfn(O[index], index) : O[index];
4254               createProperty(result, index, value);
4255             }
4256           }
4257           result.length = index;
4258           return result;
4259         };
4260
4261         // based on https://github.com/bestiejs/punycode.js/blob/master/punycode.js
4262         var maxInt = 2147483647; // aka. 0x7FFFFFFF or 2^31-1
4263         var base = 36;
4264         var tMin = 1;
4265         var tMax = 26;
4266         var skew = 38;
4267         var damp = 700;
4268         var initialBias = 72;
4269         var initialN = 128; // 0x80
4270         var delimiter = '-'; // '\x2D'
4271         var regexNonASCII = /[^\0-\u007E]/; // non-ASCII chars
4272         var regexSeparators = /[.\u3002\uFF0E\uFF61]/g; // RFC 3490 separators
4273         var OVERFLOW_ERROR = 'Overflow: input needs wider integers to process';
4274         var baseMinusTMin = base - tMin;
4275         var floor$3 = Math.floor;
4276         var stringFromCharCode = String.fromCharCode;
4277
4278         /**
4279          * Creates an array containing the numeric code points of each Unicode
4280          * character in the string. While JavaScript uses UCS-2 internally,
4281          * this function will convert a pair of surrogate halves (each of which
4282          * UCS-2 exposes as separate characters) into a single code point,
4283          * matching UTF-16.
4284          */
4285         var ucs2decode = function (string) {
4286           var output = [];
4287           var counter = 0;
4288           var length = string.length;
4289           while (counter < length) {
4290             var value = string.charCodeAt(counter++);
4291             if (value >= 0xD800 && value <= 0xDBFF && counter < length) {
4292               // It's a high surrogate, and there is a next character.
4293               var extra = string.charCodeAt(counter++);
4294               if ((extra & 0xFC00) == 0xDC00) { // Low surrogate.
4295                 output.push(((value & 0x3FF) << 10) + (extra & 0x3FF) + 0x10000);
4296               } else {
4297                 // It's an unmatched surrogate; only append this code unit, in case the
4298                 // next code unit is the high surrogate of a surrogate pair.
4299                 output.push(value);
4300                 counter--;
4301               }
4302             } else {
4303               output.push(value);
4304             }
4305           }
4306           return output;
4307         };
4308
4309         /**
4310          * Converts a digit/integer into a basic code point.
4311          */
4312         var digitToBasic = function (digit) {
4313           //  0..25 map to ASCII a..z or A..Z
4314           // 26..35 map to ASCII 0..9
4315           return digit + 22 + 75 * (digit < 26);
4316         };
4317
4318         /**
4319          * Bias adaptation function as per section 3.4 of RFC 3492.
4320          * https://tools.ietf.org/html/rfc3492#section-3.4
4321          */
4322         var adapt = function (delta, numPoints, firstTime) {
4323           var k = 0;
4324           delta = firstTime ? floor$3(delta / damp) : delta >> 1;
4325           delta += floor$3(delta / numPoints);
4326           for (; delta > baseMinusTMin * tMax >> 1; k += base) {
4327             delta = floor$3(delta / baseMinusTMin);
4328           }
4329           return floor$3(k + (baseMinusTMin + 1) * delta / (delta + skew));
4330         };
4331
4332         /**
4333          * Converts a string of Unicode symbols (e.g. a domain name label) to a
4334          * Punycode string of ASCII-only symbols.
4335          */
4336         // eslint-disable-next-line max-statements -- TODO
4337         var encode = function (input) {
4338           var output = [];
4339
4340           // Convert the input in UCS-2 to an array of Unicode code points.
4341           input = ucs2decode(input);
4342
4343           // Cache the length.
4344           var inputLength = input.length;
4345
4346           // Initialize the state.
4347           var n = initialN;
4348           var delta = 0;
4349           var bias = initialBias;
4350           var i, currentValue;
4351
4352           // Handle the basic code points.
4353           for (i = 0; i < input.length; i++) {
4354             currentValue = input[i];
4355             if (currentValue < 0x80) {
4356               output.push(stringFromCharCode(currentValue));
4357             }
4358           }
4359
4360           var basicLength = output.length; // number of basic code points.
4361           var handledCPCount = basicLength; // number of code points that have been handled;
4362
4363           // Finish the basic string with a delimiter unless it's empty.
4364           if (basicLength) {
4365             output.push(delimiter);
4366           }
4367
4368           // Main encoding loop:
4369           while (handledCPCount < inputLength) {
4370             // All non-basic code points < n have been handled already. Find the next larger one:
4371             var m = maxInt;
4372             for (i = 0; i < input.length; i++) {
4373               currentValue = input[i];
4374               if (currentValue >= n && currentValue < m) {
4375                 m = currentValue;
4376               }
4377             }
4378
4379             // Increase `delta` enough to advance the decoder's <n,i> state to <m,0>, but guard against overflow.
4380             var handledCPCountPlusOne = handledCPCount + 1;
4381             if (m - n > floor$3((maxInt - delta) / handledCPCountPlusOne)) {
4382               throw RangeError(OVERFLOW_ERROR);
4383             }
4384
4385             delta += (m - n) * handledCPCountPlusOne;
4386             n = m;
4387
4388             for (i = 0; i < input.length; i++) {
4389               currentValue = input[i];
4390               if (currentValue < n && ++delta > maxInt) {
4391                 throw RangeError(OVERFLOW_ERROR);
4392               }
4393               if (currentValue == n) {
4394                 // Represent delta as a generalized variable-length integer.
4395                 var q = delta;
4396                 for (var k = base; /* no condition */; k += base) {
4397                   var t = k <= bias ? tMin : (k >= bias + tMax ? tMax : k - bias);
4398                   if (q < t) break;
4399                   var qMinusT = q - t;
4400                   var baseMinusT = base - t;
4401                   output.push(stringFromCharCode(digitToBasic(t + qMinusT % baseMinusT)));
4402                   q = floor$3(qMinusT / baseMinusT);
4403                 }
4404
4405                 output.push(stringFromCharCode(digitToBasic(q)));
4406                 bias = adapt(delta, handledCPCountPlusOne, handledCPCount == basicLength);
4407                 delta = 0;
4408                 ++handledCPCount;
4409               }
4410             }
4411
4412             ++delta;
4413             ++n;
4414           }
4415           return output.join('');
4416         };
4417
4418         var stringPunycodeToAscii = function (input) {
4419           var encoded = [];
4420           var labels = input.toLowerCase().replace(regexSeparators, '\u002E').split('.');
4421           var i, label;
4422           for (i = 0; i < labels.length; i++) {
4423             label = labels[i];
4424             encoded.push(regexNonASCII.test(label) ? 'xn--' + encode(label) : label);
4425           }
4426           return encoded.join('.');
4427         };
4428
4429         var getIterator = function (it) {
4430           var iteratorMethod = getIteratorMethod(it);
4431           if (typeof iteratorMethod != 'function') {
4432             throw TypeError(String(it) + ' is not iterable');
4433           } return anObject(iteratorMethod.call(it));
4434         };
4435
4436         // TODO: in core-js@4, move /modules/ dependencies to public entries for better optimization by tools like `preset-env`
4437
4438
4439
4440
4441
4442
4443
4444
4445
4446
4447
4448
4449
4450
4451
4452
4453
4454
4455
4456
4457
4458         var $fetch = getBuiltIn('fetch');
4459         var Headers$1 = getBuiltIn('Headers');
4460         var ITERATOR = wellKnownSymbol('iterator');
4461         var URL_SEARCH_PARAMS = 'URLSearchParams';
4462         var URL_SEARCH_PARAMS_ITERATOR = URL_SEARCH_PARAMS + 'Iterator';
4463         var setInternalState$2 = internalState.set;
4464         var getInternalParamsState = internalState.getterFor(URL_SEARCH_PARAMS);
4465         var getInternalIteratorState = internalState.getterFor(URL_SEARCH_PARAMS_ITERATOR);
4466
4467         var plus = /\+/g;
4468         var sequences = Array(4);
4469
4470         var percentSequence = function (bytes) {
4471           return sequences[bytes - 1] || (sequences[bytes - 1] = RegExp('((?:%[\\da-f]{2}){' + bytes + '})', 'gi'));
4472         };
4473
4474         var percentDecode = function (sequence) {
4475           try {
4476             return decodeURIComponent(sequence);
4477           } catch (error) {
4478             return sequence;
4479           }
4480         };
4481
4482         var deserialize = function (it) {
4483           var result = it.replace(plus, ' ');
4484           var bytes = 4;
4485           try {
4486             return decodeURIComponent(result);
4487           } catch (error) {
4488             while (bytes) {
4489               result = result.replace(percentSequence(bytes--), percentDecode);
4490             }
4491             return result;
4492           }
4493         };
4494
4495         var find$1 = /[!'()~]|%20/g;
4496
4497         var replace$1 = {
4498           '!': '%21',
4499           "'": '%27',
4500           '(': '%28',
4501           ')': '%29',
4502           '~': '%7E',
4503           '%20': '+'
4504         };
4505
4506         var replacer = function (match) {
4507           return replace$1[match];
4508         };
4509
4510         var serialize = function (it) {
4511           return encodeURIComponent(it).replace(find$1, replacer);
4512         };
4513
4514         var parseSearchParams = function (result, query) {
4515           if (query) {
4516             var attributes = query.split('&');
4517             var index = 0;
4518             var attribute, entry;
4519             while (index < attributes.length) {
4520               attribute = attributes[index++];
4521               if (attribute.length) {
4522                 entry = attribute.split('=');
4523                 result.push({
4524                   key: deserialize(entry.shift()),
4525                   value: deserialize(entry.join('='))
4526                 });
4527               }
4528             }
4529           }
4530         };
4531
4532         var updateSearchParams = function (query) {
4533           this.entries.length = 0;
4534           parseSearchParams(this.entries, query);
4535         };
4536
4537         var validateArgumentsLength = function (passed, required) {
4538           if (passed < required) throw TypeError('Not enough arguments');
4539         };
4540
4541         var URLSearchParamsIterator = createIteratorConstructor(function Iterator(params, kind) {
4542           setInternalState$2(this, {
4543             type: URL_SEARCH_PARAMS_ITERATOR,
4544             iterator: getIterator(getInternalParamsState(params).entries),
4545             kind: kind
4546           });
4547         }, 'Iterator', function next() {
4548           var state = getInternalIteratorState(this);
4549           var kind = state.kind;
4550           var step = state.iterator.next();
4551           var entry = step.value;
4552           if (!step.done) {
4553             step.value = kind === 'keys' ? entry.key : kind === 'values' ? entry.value : [entry.key, entry.value];
4554           } return step;
4555         });
4556
4557         // `URLSearchParams` constructor
4558         // https://url.spec.whatwg.org/#interface-urlsearchparams
4559         var URLSearchParamsConstructor = function URLSearchParams(/* init */) {
4560           anInstance(this, URLSearchParamsConstructor, URL_SEARCH_PARAMS);
4561           var init = arguments.length > 0 ? arguments[0] : undefined;
4562           var that = this;
4563           var entries = [];
4564           var iteratorMethod, iterator, next, step, entryIterator, entryNext, first, second, key;
4565
4566           setInternalState$2(that, {
4567             type: URL_SEARCH_PARAMS,
4568             entries: entries,
4569             updateURL: function () { /* empty */ },
4570             updateSearchParams: updateSearchParams
4571           });
4572
4573           if (init !== undefined) {
4574             if (isObject$4(init)) {
4575               iteratorMethod = getIteratorMethod(init);
4576               if (typeof iteratorMethod === 'function') {
4577                 iterator = iteratorMethod.call(init);
4578                 next = iterator.next;
4579                 while (!(step = next.call(iterator)).done) {
4580                   entryIterator = getIterator(anObject(step.value));
4581                   entryNext = entryIterator.next;
4582                   if (
4583                     (first = entryNext.call(entryIterator)).done ||
4584                     (second = entryNext.call(entryIterator)).done ||
4585                     !entryNext.call(entryIterator).done
4586                   ) throw TypeError('Expected sequence with length 2');
4587                   entries.push({ key: first.value + '', value: second.value + '' });
4588                 }
4589               } else for (key in init) if (has$1(init, key)) entries.push({ key: key, value: init[key] + '' });
4590             } else {
4591               parseSearchParams(entries, typeof init === 'string' ? init.charAt(0) === '?' ? init.slice(1) : init : init + '');
4592             }
4593           }
4594         };
4595
4596         var URLSearchParamsPrototype = URLSearchParamsConstructor.prototype;
4597
4598         redefineAll(URLSearchParamsPrototype, {
4599           // `URLSearchParams.prototype.append` method
4600           // https://url.spec.whatwg.org/#dom-urlsearchparams-append
4601           append: function append(name, value) {
4602             validateArgumentsLength(arguments.length, 2);
4603             var state = getInternalParamsState(this);
4604             state.entries.push({ key: name + '', value: value + '' });
4605             state.updateURL();
4606           },
4607           // `URLSearchParams.prototype.delete` method
4608           // https://url.spec.whatwg.org/#dom-urlsearchparams-delete
4609           'delete': function (name) {
4610             validateArgumentsLength(arguments.length, 1);
4611             var state = getInternalParamsState(this);
4612             var entries = state.entries;
4613             var key = name + '';
4614             var index = 0;
4615             while (index < entries.length) {
4616               if (entries[index].key === key) entries.splice(index, 1);
4617               else index++;
4618             }
4619             state.updateURL();
4620           },
4621           // `URLSearchParams.prototype.get` method
4622           // https://url.spec.whatwg.org/#dom-urlsearchparams-get
4623           get: function get(name) {
4624             validateArgumentsLength(arguments.length, 1);
4625             var entries = getInternalParamsState(this).entries;
4626             var key = name + '';
4627             var index = 0;
4628             for (; index < entries.length; index++) {
4629               if (entries[index].key === key) return entries[index].value;
4630             }
4631             return null;
4632           },
4633           // `URLSearchParams.prototype.getAll` method
4634           // https://url.spec.whatwg.org/#dom-urlsearchparams-getall
4635           getAll: function getAll(name) {
4636             validateArgumentsLength(arguments.length, 1);
4637             var entries = getInternalParamsState(this).entries;
4638             var key = name + '';
4639             var result = [];
4640             var index = 0;
4641             for (; index < entries.length; index++) {
4642               if (entries[index].key === key) result.push(entries[index].value);
4643             }
4644             return result;
4645           },
4646           // `URLSearchParams.prototype.has` method
4647           // https://url.spec.whatwg.org/#dom-urlsearchparams-has
4648           has: function has(name) {
4649             validateArgumentsLength(arguments.length, 1);
4650             var entries = getInternalParamsState(this).entries;
4651             var key = name + '';
4652             var index = 0;
4653             while (index < entries.length) {
4654               if (entries[index++].key === key) return true;
4655             }
4656             return false;
4657           },
4658           // `URLSearchParams.prototype.set` method
4659           // https://url.spec.whatwg.org/#dom-urlsearchparams-set
4660           set: function set(name, value) {
4661             validateArgumentsLength(arguments.length, 1);
4662             var state = getInternalParamsState(this);
4663             var entries = state.entries;
4664             var found = false;
4665             var key = name + '';
4666             var val = value + '';
4667             var index = 0;
4668             var entry;
4669             for (; index < entries.length; index++) {
4670               entry = entries[index];
4671               if (entry.key === key) {
4672                 if (found) entries.splice(index--, 1);
4673                 else {
4674                   found = true;
4675                   entry.value = val;
4676                 }
4677               }
4678             }
4679             if (!found) entries.push({ key: key, value: val });
4680             state.updateURL();
4681           },
4682           // `URLSearchParams.prototype.sort` method
4683           // https://url.spec.whatwg.org/#dom-urlsearchparams-sort
4684           sort: function sort() {
4685             var state = getInternalParamsState(this);
4686             var entries = state.entries;
4687             // Array#sort is not stable in some engines
4688             var slice = entries.slice();
4689             var entry, entriesIndex, sliceIndex;
4690             entries.length = 0;
4691             for (sliceIndex = 0; sliceIndex < slice.length; sliceIndex++) {
4692               entry = slice[sliceIndex];
4693               for (entriesIndex = 0; entriesIndex < sliceIndex; entriesIndex++) {
4694                 if (entries[entriesIndex].key > entry.key) {
4695                   entries.splice(entriesIndex, 0, entry);
4696                   break;
4697                 }
4698               }
4699               if (entriesIndex === sliceIndex) entries.push(entry);
4700             }
4701             state.updateURL();
4702           },
4703           // `URLSearchParams.prototype.forEach` method
4704           forEach: function forEach(callback /* , thisArg */) {
4705             var entries = getInternalParamsState(this).entries;
4706             var boundFunction = functionBindContext(callback, arguments.length > 1 ? arguments[1] : undefined, 3);
4707             var index = 0;
4708             var entry;
4709             while (index < entries.length) {
4710               entry = entries[index++];
4711               boundFunction(entry.value, entry.key, this);
4712             }
4713           },
4714           // `URLSearchParams.prototype.keys` method
4715           keys: function keys() {
4716             return new URLSearchParamsIterator(this, 'keys');
4717           },
4718           // `URLSearchParams.prototype.values` method
4719           values: function values() {
4720             return new URLSearchParamsIterator(this, 'values');
4721           },
4722           // `URLSearchParams.prototype.entries` method
4723           entries: function entries() {
4724             return new URLSearchParamsIterator(this, 'entries');
4725           }
4726         }, { enumerable: true });
4727
4728         // `URLSearchParams.prototype[@@iterator]` method
4729         redefine(URLSearchParamsPrototype, ITERATOR, URLSearchParamsPrototype.entries);
4730
4731         // `URLSearchParams.prototype.toString` method
4732         // https://url.spec.whatwg.org/#urlsearchparams-stringification-behavior
4733         redefine(URLSearchParamsPrototype, 'toString', function toString() {
4734           var entries = getInternalParamsState(this).entries;
4735           var result = [];
4736           var index = 0;
4737           var entry;
4738           while (index < entries.length) {
4739             entry = entries[index++];
4740             result.push(serialize(entry.key) + '=' + serialize(entry.value));
4741           } return result.join('&');
4742         }, { enumerable: true });
4743
4744         setToStringTag(URLSearchParamsConstructor, URL_SEARCH_PARAMS);
4745
4746         _export({ global: true, forced: !nativeUrl }, {
4747           URLSearchParams: URLSearchParamsConstructor
4748         });
4749
4750         // Wrap `fetch` for correct work with polyfilled `URLSearchParams`
4751         // https://github.com/zloirock/core-js/issues/674
4752         if (!nativeUrl && typeof $fetch == 'function' && typeof Headers$1 == 'function') {
4753           _export({ global: true, enumerable: true, forced: true }, {
4754             fetch: function fetch(input /* , init */) {
4755               var args = [input];
4756               var init, body, headers;
4757               if (arguments.length > 1) {
4758                 init = arguments[1];
4759                 if (isObject$4(init)) {
4760                   body = init.body;
4761                   if (classof(body) === URL_SEARCH_PARAMS) {
4762                     headers = init.headers ? new Headers$1(init.headers) : new Headers$1();
4763                     if (!headers.has('content-type')) {
4764                       headers.set('content-type', 'application/x-www-form-urlencoded;charset=UTF-8');
4765                     }
4766                     init = objectCreate(init, {
4767                       body: createPropertyDescriptor(0, String(body)),
4768                       headers: createPropertyDescriptor(0, headers)
4769                     });
4770                   }
4771                 }
4772                 args.push(init);
4773               } return $fetch.apply(this, args);
4774             }
4775           });
4776         }
4777
4778         var web_urlSearchParams = {
4779           URLSearchParams: URLSearchParamsConstructor,
4780           getState: getInternalParamsState
4781         };
4782
4783         // TODO: in core-js@4, move /modules/ dependencies to public entries for better optimization by tools like `preset-env`
4784
4785
4786
4787
4788
4789
4790
4791
4792
4793
4794
4795         var codeAt = stringMultibyte.codeAt;
4796
4797
4798
4799
4800
4801         var NativeURL = global$2.URL;
4802         var URLSearchParams$1 = web_urlSearchParams.URLSearchParams;
4803         var getInternalSearchParamsState = web_urlSearchParams.getState;
4804         var setInternalState$1 = internalState.set;
4805         var getInternalURLState = internalState.getterFor('URL');
4806         var floor$2 = Math.floor;
4807         var pow$1 = Math.pow;
4808
4809         var INVALID_AUTHORITY = 'Invalid authority';
4810         var INVALID_SCHEME = 'Invalid scheme';
4811         var INVALID_HOST = 'Invalid host';
4812         var INVALID_PORT = 'Invalid port';
4813
4814         var ALPHA = /[A-Za-z]/;
4815         // eslint-disable-next-line regexp/no-obscure-range -- safe
4816         var ALPHANUMERIC = /[\d+-.A-Za-z]/;
4817         var DIGIT = /\d/;
4818         var HEX_START = /^0x/i;
4819         var OCT = /^[0-7]+$/;
4820         var DEC = /^\d+$/;
4821         var HEX = /^[\dA-Fa-f]+$/;
4822         /* eslint-disable no-control-regex -- safe */
4823         var FORBIDDEN_HOST_CODE_POINT = /[\0\t\n\r #%/:<>?@[\\\]^|]/;
4824         var FORBIDDEN_HOST_CODE_POINT_EXCLUDING_PERCENT = /[\0\t\n\r #/:<>?@[\\\]^|]/;
4825         var LEADING_AND_TRAILING_C0_CONTROL_OR_SPACE = /^[\u0000-\u001F ]+|[\u0000-\u001F ]+$/g;
4826         var TAB_AND_NEW_LINE = /[\t\n\r]/g;
4827         /* eslint-enable no-control-regex -- safe */
4828         var EOF;
4829
4830         var parseHost = function (url, input) {
4831           var result, codePoints, index;
4832           if (input.charAt(0) == '[') {
4833             if (input.charAt(input.length - 1) != ']') return INVALID_HOST;
4834             result = parseIPv6(input.slice(1, -1));
4835             if (!result) return INVALID_HOST;
4836             url.host = result;
4837           // opaque host
4838           } else if (!isSpecial(url)) {
4839             if (FORBIDDEN_HOST_CODE_POINT_EXCLUDING_PERCENT.test(input)) return INVALID_HOST;
4840             result = '';
4841             codePoints = arrayFrom(input);
4842             for (index = 0; index < codePoints.length; index++) {
4843               result += percentEncode(codePoints[index], C0ControlPercentEncodeSet);
4844             }
4845             url.host = result;
4846           } else {
4847             input = stringPunycodeToAscii(input);
4848             if (FORBIDDEN_HOST_CODE_POINT.test(input)) return INVALID_HOST;
4849             result = parseIPv4(input);
4850             if (result === null) return INVALID_HOST;
4851             url.host = result;
4852           }
4853         };
4854
4855         var parseIPv4 = function (input) {
4856           var parts = input.split('.');
4857           var partsLength, numbers, index, part, radix, number, ipv4;
4858           if (parts.length && parts[parts.length - 1] == '') {
4859             parts.pop();
4860           }
4861           partsLength = parts.length;
4862           if (partsLength > 4) return input;
4863           numbers = [];
4864           for (index = 0; index < partsLength; index++) {
4865             part = parts[index];
4866             if (part == '') return input;
4867             radix = 10;
4868             if (part.length > 1 && part.charAt(0) == '0') {
4869               radix = HEX_START.test(part) ? 16 : 8;
4870               part = part.slice(radix == 8 ? 1 : 2);
4871             }
4872             if (part === '') {
4873               number = 0;
4874             } else {
4875               if (!(radix == 10 ? DEC : radix == 8 ? OCT : HEX).test(part)) return input;
4876               number = parseInt(part, radix);
4877             }
4878             numbers.push(number);
4879           }
4880           for (index = 0; index < partsLength; index++) {
4881             number = numbers[index];
4882             if (index == partsLength - 1) {
4883               if (number >= pow$1(256, 5 - partsLength)) return null;
4884             } else if (number > 255) return null;
4885           }
4886           ipv4 = numbers.pop();
4887           for (index = 0; index < numbers.length; index++) {
4888             ipv4 += numbers[index] * pow$1(256, 3 - index);
4889           }
4890           return ipv4;
4891         };
4892
4893         // eslint-disable-next-line max-statements -- TODO
4894         var parseIPv6 = function (input) {
4895           var address = [0, 0, 0, 0, 0, 0, 0, 0];
4896           var pieceIndex = 0;
4897           var compress = null;
4898           var pointer = 0;
4899           var value, length, numbersSeen, ipv4Piece, number, swaps, swap;
4900
4901           var char = function () {
4902             return input.charAt(pointer);
4903           };
4904
4905           if (char() == ':') {
4906             if (input.charAt(1) != ':') return;
4907             pointer += 2;
4908             pieceIndex++;
4909             compress = pieceIndex;
4910           }
4911           while (char()) {
4912             if (pieceIndex == 8) return;
4913             if (char() == ':') {
4914               if (compress !== null) return;
4915               pointer++;
4916               pieceIndex++;
4917               compress = pieceIndex;
4918               continue;
4919             }
4920             value = length = 0;
4921             while (length < 4 && HEX.test(char())) {
4922               value = value * 16 + parseInt(char(), 16);
4923               pointer++;
4924               length++;
4925             }
4926             if (char() == '.') {
4927               if (length == 0) return;
4928               pointer -= length;
4929               if (pieceIndex > 6) return;
4930               numbersSeen = 0;
4931               while (char()) {
4932                 ipv4Piece = null;
4933                 if (numbersSeen > 0) {
4934                   if (char() == '.' && numbersSeen < 4) pointer++;
4935                   else return;
4936                 }
4937                 if (!DIGIT.test(char())) return;
4938                 while (DIGIT.test(char())) {
4939                   number = parseInt(char(), 10);
4940                   if (ipv4Piece === null) ipv4Piece = number;
4941                   else if (ipv4Piece == 0) return;
4942                   else ipv4Piece = ipv4Piece * 10 + number;
4943                   if (ipv4Piece > 255) return;
4944                   pointer++;
4945                 }
4946                 address[pieceIndex] = address[pieceIndex] * 256 + ipv4Piece;
4947                 numbersSeen++;
4948                 if (numbersSeen == 2 || numbersSeen == 4) pieceIndex++;
4949               }
4950               if (numbersSeen != 4) return;
4951               break;
4952             } else if (char() == ':') {
4953               pointer++;
4954               if (!char()) return;
4955             } else if (char()) return;
4956             address[pieceIndex++] = value;
4957           }
4958           if (compress !== null) {
4959             swaps = pieceIndex - compress;
4960             pieceIndex = 7;
4961             while (pieceIndex != 0 && swaps > 0) {
4962               swap = address[pieceIndex];
4963               address[pieceIndex--] = address[compress + swaps - 1];
4964               address[compress + --swaps] = swap;
4965             }
4966           } else if (pieceIndex != 8) return;
4967           return address;
4968         };
4969
4970         var findLongestZeroSequence = function (ipv6) {
4971           var maxIndex = null;
4972           var maxLength = 1;
4973           var currStart = null;
4974           var currLength = 0;
4975           var index = 0;
4976           for (; index < 8; index++) {
4977             if (ipv6[index] !== 0) {
4978               if (currLength > maxLength) {
4979                 maxIndex = currStart;
4980                 maxLength = currLength;
4981               }
4982               currStart = null;
4983               currLength = 0;
4984             } else {
4985               if (currStart === null) currStart = index;
4986               ++currLength;
4987             }
4988           }
4989           if (currLength > maxLength) {
4990             maxIndex = currStart;
4991             maxLength = currLength;
4992           }
4993           return maxIndex;
4994         };
4995
4996         var serializeHost = function (host) {
4997           var result, index, compress, ignore0;
4998           // ipv4
4999           if (typeof host == 'number') {
5000             result = [];
5001             for (index = 0; index < 4; index++) {
5002               result.unshift(host % 256);
5003               host = floor$2(host / 256);
5004             } return result.join('.');
5005           // ipv6
5006           } else if (typeof host == 'object') {
5007             result = '';
5008             compress = findLongestZeroSequence(host);
5009             for (index = 0; index < 8; index++) {
5010               if (ignore0 && host[index] === 0) continue;
5011               if (ignore0) ignore0 = false;
5012               if (compress === index) {
5013                 result += index ? ':' : '::';
5014                 ignore0 = true;
5015               } else {
5016                 result += host[index].toString(16);
5017                 if (index < 7) result += ':';
5018               }
5019             }
5020             return '[' + result + ']';
5021           } return host;
5022         };
5023
5024         var C0ControlPercentEncodeSet = {};
5025         var fragmentPercentEncodeSet = objectAssign({}, C0ControlPercentEncodeSet, {
5026           ' ': 1, '"': 1, '<': 1, '>': 1, '`': 1
5027         });
5028         var pathPercentEncodeSet = objectAssign({}, fragmentPercentEncodeSet, {
5029           '#': 1, '?': 1, '{': 1, '}': 1
5030         });
5031         var userinfoPercentEncodeSet = objectAssign({}, pathPercentEncodeSet, {
5032           '/': 1, ':': 1, ';': 1, '=': 1, '@': 1, '[': 1, '\\': 1, ']': 1, '^': 1, '|': 1
5033         });
5034
5035         var percentEncode = function (char, set) {
5036           var code = codeAt(char, 0);
5037           return code > 0x20 && code < 0x7F && !has$1(set, char) ? char : encodeURIComponent(char);
5038         };
5039
5040         var specialSchemes = {
5041           ftp: 21,
5042           file: null,
5043           http: 80,
5044           https: 443,
5045           ws: 80,
5046           wss: 443
5047         };
5048
5049         var isSpecial = function (url) {
5050           return has$1(specialSchemes, url.scheme);
5051         };
5052
5053         var includesCredentials = function (url) {
5054           return url.username != '' || url.password != '';
5055         };
5056
5057         var cannotHaveUsernamePasswordPort = function (url) {
5058           return !url.host || url.cannotBeABaseURL || url.scheme == 'file';
5059         };
5060
5061         var isWindowsDriveLetter = function (string, normalized) {
5062           var second;
5063           return string.length == 2 && ALPHA.test(string.charAt(0))
5064             && ((second = string.charAt(1)) == ':' || (!normalized && second == '|'));
5065         };
5066
5067         var startsWithWindowsDriveLetter = function (string) {
5068           var third;
5069           return string.length > 1 && isWindowsDriveLetter(string.slice(0, 2)) && (
5070             string.length == 2 ||
5071             ((third = string.charAt(2)) === '/' || third === '\\' || third === '?' || third === '#')
5072           );
5073         };
5074
5075         var shortenURLsPath = function (url) {
5076           var path = url.path;
5077           var pathSize = path.length;
5078           if (pathSize && (url.scheme != 'file' || pathSize != 1 || !isWindowsDriveLetter(path[0], true))) {
5079             path.pop();
5080           }
5081         };
5082
5083         var isSingleDot = function (segment) {
5084           return segment === '.' || segment.toLowerCase() === '%2e';
5085         };
5086
5087         var isDoubleDot = function (segment) {
5088           segment = segment.toLowerCase();
5089           return segment === '..' || segment === '%2e.' || segment === '.%2e' || segment === '%2e%2e';
5090         };
5091
5092         // States:
5093         var SCHEME_START = {};
5094         var SCHEME = {};
5095         var NO_SCHEME = {};
5096         var SPECIAL_RELATIVE_OR_AUTHORITY = {};
5097         var PATH_OR_AUTHORITY = {};
5098         var RELATIVE = {};
5099         var RELATIVE_SLASH = {};
5100         var SPECIAL_AUTHORITY_SLASHES = {};
5101         var SPECIAL_AUTHORITY_IGNORE_SLASHES = {};
5102         var AUTHORITY = {};
5103         var HOST = {};
5104         var HOSTNAME = {};
5105         var PORT = {};
5106         var FILE = {};
5107         var FILE_SLASH = {};
5108         var FILE_HOST = {};
5109         var PATH_START = {};
5110         var PATH = {};
5111         var CANNOT_BE_A_BASE_URL_PATH = {};
5112         var QUERY = {};
5113         var FRAGMENT = {};
5114
5115         // eslint-disable-next-line max-statements -- TODO
5116         var parseURL = function (url, input, stateOverride, base) {
5117           var state = stateOverride || SCHEME_START;
5118           var pointer = 0;
5119           var buffer = '';
5120           var seenAt = false;
5121           var seenBracket = false;
5122           var seenPasswordToken = false;
5123           var codePoints, char, bufferCodePoints, failure;
5124
5125           if (!stateOverride) {
5126             url.scheme = '';
5127             url.username = '';
5128             url.password = '';
5129             url.host = null;
5130             url.port = null;
5131             url.path = [];
5132             url.query = null;
5133             url.fragment = null;
5134             url.cannotBeABaseURL = false;
5135             input = input.replace(LEADING_AND_TRAILING_C0_CONTROL_OR_SPACE, '');
5136           }
5137
5138           input = input.replace(TAB_AND_NEW_LINE, '');
5139
5140           codePoints = arrayFrom(input);
5141
5142           while (pointer <= codePoints.length) {
5143             char = codePoints[pointer];
5144             switch (state) {
5145               case SCHEME_START:
5146                 if (char && ALPHA.test(char)) {
5147                   buffer += char.toLowerCase();
5148                   state = SCHEME;
5149                 } else if (!stateOverride) {
5150                   state = NO_SCHEME;
5151                   continue;
5152                 } else return INVALID_SCHEME;
5153                 break;
5154
5155               case SCHEME:
5156                 if (char && (ALPHANUMERIC.test(char) || char == '+' || char == '-' || char == '.')) {
5157                   buffer += char.toLowerCase();
5158                 } else if (char == ':') {
5159                   if (stateOverride && (
5160                     (isSpecial(url) != has$1(specialSchemes, buffer)) ||
5161                     (buffer == 'file' && (includesCredentials(url) || url.port !== null)) ||
5162                     (url.scheme == 'file' && !url.host)
5163                   )) return;
5164                   url.scheme = buffer;
5165                   if (stateOverride) {
5166                     if (isSpecial(url) && specialSchemes[url.scheme] == url.port) url.port = null;
5167                     return;
5168                   }
5169                   buffer = '';
5170                   if (url.scheme == 'file') {
5171                     state = FILE;
5172                   } else if (isSpecial(url) && base && base.scheme == url.scheme) {
5173                     state = SPECIAL_RELATIVE_OR_AUTHORITY;
5174                   } else if (isSpecial(url)) {
5175                     state = SPECIAL_AUTHORITY_SLASHES;
5176                   } else if (codePoints[pointer + 1] == '/') {
5177                     state = PATH_OR_AUTHORITY;
5178                     pointer++;
5179                   } else {
5180                     url.cannotBeABaseURL = true;
5181                     url.path.push('');
5182                     state = CANNOT_BE_A_BASE_URL_PATH;
5183                   }
5184                 } else if (!stateOverride) {
5185                   buffer = '';
5186                   state = NO_SCHEME;
5187                   pointer = 0;
5188                   continue;
5189                 } else return INVALID_SCHEME;
5190                 break;
5191
5192               case NO_SCHEME:
5193                 if (!base || (base.cannotBeABaseURL && char != '#')) return INVALID_SCHEME;
5194                 if (base.cannotBeABaseURL && char == '#') {
5195                   url.scheme = base.scheme;
5196                   url.path = base.path.slice();
5197                   url.query = base.query;
5198                   url.fragment = '';
5199                   url.cannotBeABaseURL = true;
5200                   state = FRAGMENT;
5201                   break;
5202                 }
5203                 state = base.scheme == 'file' ? FILE : RELATIVE;
5204                 continue;
5205
5206               case SPECIAL_RELATIVE_OR_AUTHORITY:
5207                 if (char == '/' && codePoints[pointer + 1] == '/') {
5208                   state = SPECIAL_AUTHORITY_IGNORE_SLASHES;
5209                   pointer++;
5210                 } else {
5211                   state = RELATIVE;
5212                   continue;
5213                 } break;
5214
5215               case PATH_OR_AUTHORITY:
5216                 if (char == '/') {
5217                   state = AUTHORITY;
5218                   break;
5219                 } else {
5220                   state = PATH;
5221                   continue;
5222                 }
5223
5224               case RELATIVE:
5225                 url.scheme = base.scheme;
5226                 if (char == EOF) {
5227                   url.username = base.username;
5228                   url.password = base.password;
5229                   url.host = base.host;
5230                   url.port = base.port;
5231                   url.path = base.path.slice();
5232                   url.query = base.query;
5233                 } else if (char == '/' || (char == '\\' && isSpecial(url))) {
5234                   state = RELATIVE_SLASH;
5235                 } else if (char == '?') {
5236                   url.username = base.username;
5237                   url.password = base.password;
5238                   url.host = base.host;
5239                   url.port = base.port;
5240                   url.path = base.path.slice();
5241                   url.query = '';
5242                   state = QUERY;
5243                 } else if (char == '#') {
5244                   url.username = base.username;
5245                   url.password = base.password;
5246                   url.host = base.host;
5247                   url.port = base.port;
5248                   url.path = base.path.slice();
5249                   url.query = base.query;
5250                   url.fragment = '';
5251                   state = FRAGMENT;
5252                 } else {
5253                   url.username = base.username;
5254                   url.password = base.password;
5255                   url.host = base.host;
5256                   url.port = base.port;
5257                   url.path = base.path.slice();
5258                   url.path.pop();
5259                   state = PATH;
5260                   continue;
5261                 } break;
5262
5263               case RELATIVE_SLASH:
5264                 if (isSpecial(url) && (char == '/' || char == '\\')) {
5265                   state = SPECIAL_AUTHORITY_IGNORE_SLASHES;
5266                 } else if (char == '/') {
5267                   state = AUTHORITY;
5268                 } else {
5269                   url.username = base.username;
5270                   url.password = base.password;
5271                   url.host = base.host;
5272                   url.port = base.port;
5273                   state = PATH;
5274                   continue;
5275                 } break;
5276
5277               case SPECIAL_AUTHORITY_SLASHES:
5278                 state = SPECIAL_AUTHORITY_IGNORE_SLASHES;
5279                 if (char != '/' || buffer.charAt(pointer + 1) != '/') continue;
5280                 pointer++;
5281                 break;
5282
5283               case SPECIAL_AUTHORITY_IGNORE_SLASHES:
5284                 if (char != '/' && char != '\\') {
5285                   state = AUTHORITY;
5286                   continue;
5287                 } break;
5288
5289               case AUTHORITY:
5290                 if (char == '@') {
5291                   if (seenAt) buffer = '%40' + buffer;
5292                   seenAt = true;
5293                   bufferCodePoints = arrayFrom(buffer);
5294                   for (var i = 0; i < bufferCodePoints.length; i++) {
5295                     var codePoint = bufferCodePoints[i];
5296                     if (codePoint == ':' && !seenPasswordToken) {
5297                       seenPasswordToken = true;
5298                       continue;
5299                     }
5300                     var encodedCodePoints = percentEncode(codePoint, userinfoPercentEncodeSet);
5301                     if (seenPasswordToken) url.password += encodedCodePoints;
5302                     else url.username += encodedCodePoints;
5303                   }
5304                   buffer = '';
5305                 } else if (
5306                   char == EOF || char == '/' || char == '?' || char == '#' ||
5307                   (char == '\\' && isSpecial(url))
5308                 ) {
5309                   if (seenAt && buffer == '') return INVALID_AUTHORITY;
5310                   pointer -= arrayFrom(buffer).length + 1;
5311                   buffer = '';
5312                   state = HOST;
5313                 } else buffer += char;
5314                 break;
5315
5316               case HOST:
5317               case HOSTNAME:
5318                 if (stateOverride && url.scheme == 'file') {
5319                   state = FILE_HOST;
5320                   continue;
5321                 } else if (char == ':' && !seenBracket) {
5322                   if (buffer == '') return INVALID_HOST;
5323                   failure = parseHost(url, buffer);
5324                   if (failure) return failure;
5325                   buffer = '';
5326                   state = PORT;
5327                   if (stateOverride == HOSTNAME) return;
5328                 } else if (
5329                   char == EOF || char == '/' || char == '?' || char == '#' ||
5330                   (char == '\\' && isSpecial(url))
5331                 ) {
5332                   if (isSpecial(url) && buffer == '') return INVALID_HOST;
5333                   if (stateOverride && buffer == '' && (includesCredentials(url) || url.port !== null)) return;
5334                   failure = parseHost(url, buffer);
5335                   if (failure) return failure;
5336                   buffer = '';
5337                   state = PATH_START;
5338                   if (stateOverride) return;
5339                   continue;
5340                 } else {
5341                   if (char == '[') seenBracket = true;
5342                   else if (char == ']') seenBracket = false;
5343                   buffer += char;
5344                 } break;
5345
5346               case PORT:
5347                 if (DIGIT.test(char)) {
5348                   buffer += char;
5349                 } else if (
5350                   char == EOF || char == '/' || char == '?' || char == '#' ||
5351                   (char == '\\' && isSpecial(url)) ||
5352                   stateOverride
5353                 ) {
5354                   if (buffer != '') {
5355                     var port = parseInt(buffer, 10);
5356                     if (port > 0xFFFF) return INVALID_PORT;
5357                     url.port = (isSpecial(url) && port === specialSchemes[url.scheme]) ? null : port;
5358                     buffer = '';
5359                   }
5360                   if (stateOverride) return;
5361                   state = PATH_START;
5362                   continue;
5363                 } else return INVALID_PORT;
5364                 break;
5365
5366               case FILE:
5367                 url.scheme = 'file';
5368                 if (char == '/' || char == '\\') state = FILE_SLASH;
5369                 else if (base && base.scheme == 'file') {
5370                   if (char == EOF) {
5371                     url.host = base.host;
5372                     url.path = base.path.slice();
5373                     url.query = base.query;
5374                   } else if (char == '?') {
5375                     url.host = base.host;
5376                     url.path = base.path.slice();
5377                     url.query = '';
5378                     state = QUERY;
5379                   } else if (char == '#') {
5380                     url.host = base.host;
5381                     url.path = base.path.slice();
5382                     url.query = base.query;
5383                     url.fragment = '';
5384                     state = FRAGMENT;
5385                   } else {
5386                     if (!startsWithWindowsDriveLetter(codePoints.slice(pointer).join(''))) {
5387                       url.host = base.host;
5388                       url.path = base.path.slice();
5389                       shortenURLsPath(url);
5390                     }
5391                     state = PATH;
5392                     continue;
5393                   }
5394                 } else {
5395                   state = PATH;
5396                   continue;
5397                 } break;
5398
5399               case FILE_SLASH:
5400                 if (char == '/' || char == '\\') {
5401                   state = FILE_HOST;
5402                   break;
5403                 }
5404                 if (base && base.scheme == 'file' && !startsWithWindowsDriveLetter(codePoints.slice(pointer).join(''))) {
5405                   if (isWindowsDriveLetter(base.path[0], true)) url.path.push(base.path[0]);
5406                   else url.host = base.host;
5407                 }
5408                 state = PATH;
5409                 continue;
5410
5411               case FILE_HOST:
5412                 if (char == EOF || char == '/' || char == '\\' || char == '?' || char == '#') {
5413                   if (!stateOverride && isWindowsDriveLetter(buffer)) {
5414                     state = PATH;
5415                   } else if (buffer == '') {
5416                     url.host = '';
5417                     if (stateOverride) return;
5418                     state = PATH_START;
5419                   } else {
5420                     failure = parseHost(url, buffer);
5421                     if (failure) return failure;
5422                     if (url.host == 'localhost') url.host = '';
5423                     if (stateOverride) return;
5424                     buffer = '';
5425                     state = PATH_START;
5426                   } continue;
5427                 } else buffer += char;
5428                 break;
5429
5430               case PATH_START:
5431                 if (isSpecial(url)) {
5432                   state = PATH;
5433                   if (char != '/' && char != '\\') continue;
5434                 } else if (!stateOverride && char == '?') {
5435                   url.query = '';
5436                   state = QUERY;
5437                 } else if (!stateOverride && char == '#') {
5438                   url.fragment = '';
5439                   state = FRAGMENT;
5440                 } else if (char != EOF) {
5441                   state = PATH;
5442                   if (char != '/') continue;
5443                 } break;
5444
5445               case PATH:
5446                 if (
5447                   char == EOF || char == '/' ||
5448                   (char == '\\' && isSpecial(url)) ||
5449                   (!stateOverride && (char == '?' || char == '#'))
5450                 ) {
5451                   if (isDoubleDot(buffer)) {
5452                     shortenURLsPath(url);
5453                     if (char != '/' && !(char == '\\' && isSpecial(url))) {
5454                       url.path.push('');
5455                     }
5456                   } else if (isSingleDot(buffer)) {
5457                     if (char != '/' && !(char == '\\' && isSpecial(url))) {
5458                       url.path.push('');
5459                     }
5460                   } else {
5461                     if (url.scheme == 'file' && !url.path.length && isWindowsDriveLetter(buffer)) {
5462                       if (url.host) url.host = '';
5463                       buffer = buffer.charAt(0) + ':'; // normalize windows drive letter
5464                     }
5465                     url.path.push(buffer);
5466                   }
5467                   buffer = '';
5468                   if (url.scheme == 'file' && (char == EOF || char == '?' || char == '#')) {
5469                     while (url.path.length > 1 && url.path[0] === '') {
5470                       url.path.shift();
5471                     }
5472                   }
5473                   if (char == '?') {
5474                     url.query = '';
5475                     state = QUERY;
5476                   } else if (char == '#') {
5477                     url.fragment = '';
5478                     state = FRAGMENT;
5479                   }
5480                 } else {
5481                   buffer += percentEncode(char, pathPercentEncodeSet);
5482                 } break;
5483
5484               case CANNOT_BE_A_BASE_URL_PATH:
5485                 if (char == '?') {
5486                   url.query = '';
5487                   state = QUERY;
5488                 } else if (char == '#') {
5489                   url.fragment = '';
5490                   state = FRAGMENT;
5491                 } else if (char != EOF) {
5492                   url.path[0] += percentEncode(char, C0ControlPercentEncodeSet);
5493                 } break;
5494
5495               case QUERY:
5496                 if (!stateOverride && char == '#') {
5497                   url.fragment = '';
5498                   state = FRAGMENT;
5499                 } else if (char != EOF) {
5500                   if (char == "'" && isSpecial(url)) url.query += '%27';
5501                   else if (char == '#') url.query += '%23';
5502                   else url.query += percentEncode(char, C0ControlPercentEncodeSet);
5503                 } break;
5504
5505               case FRAGMENT:
5506                 if (char != EOF) url.fragment += percentEncode(char, fragmentPercentEncodeSet);
5507                 break;
5508             }
5509
5510             pointer++;
5511           }
5512         };
5513
5514         // `URL` constructor
5515         // https://url.spec.whatwg.org/#url-class
5516         var URLConstructor = function URL(url /* , base */) {
5517           var that = anInstance(this, URLConstructor, 'URL');
5518           var base = arguments.length > 1 ? arguments[1] : undefined;
5519           var urlString = String(url);
5520           var state = setInternalState$1(that, { type: 'URL' });
5521           var baseState, failure;
5522           if (base !== undefined) {
5523             if (base instanceof URLConstructor) baseState = getInternalURLState(base);
5524             else {
5525               failure = parseURL(baseState = {}, String(base));
5526               if (failure) throw TypeError(failure);
5527             }
5528           }
5529           failure = parseURL(state, urlString, null, baseState);
5530           if (failure) throw TypeError(failure);
5531           var searchParams = state.searchParams = new URLSearchParams$1();
5532           var searchParamsState = getInternalSearchParamsState(searchParams);
5533           searchParamsState.updateSearchParams(state.query);
5534           searchParamsState.updateURL = function () {
5535             state.query = String(searchParams) || null;
5536           };
5537           if (!descriptors) {
5538             that.href = serializeURL.call(that);
5539             that.origin = getOrigin.call(that);
5540             that.protocol = getProtocol.call(that);
5541             that.username = getUsername.call(that);
5542             that.password = getPassword.call(that);
5543             that.host = getHost.call(that);
5544             that.hostname = getHostname.call(that);
5545             that.port = getPort.call(that);
5546             that.pathname = getPathname.call(that);
5547             that.search = getSearch.call(that);
5548             that.searchParams = getSearchParams.call(that);
5549             that.hash = getHash.call(that);
5550           }
5551         };
5552
5553         var URLPrototype = URLConstructor.prototype;
5554
5555         var serializeURL = function () {
5556           var url = getInternalURLState(this);
5557           var scheme = url.scheme;
5558           var username = url.username;
5559           var password = url.password;
5560           var host = url.host;
5561           var port = url.port;
5562           var path = url.path;
5563           var query = url.query;
5564           var fragment = url.fragment;
5565           var output = scheme + ':';
5566           if (host !== null) {
5567             output += '//';
5568             if (includesCredentials(url)) {
5569               output += username + (password ? ':' + password : '') + '@';
5570             }
5571             output += serializeHost(host);
5572             if (port !== null) output += ':' + port;
5573           } else if (scheme == 'file') output += '//';
5574           output += url.cannotBeABaseURL ? path[0] : path.length ? '/' + path.join('/') : '';
5575           if (query !== null) output += '?' + query;
5576           if (fragment !== null) output += '#' + fragment;
5577           return output;
5578         };
5579
5580         var getOrigin = function () {
5581           var url = getInternalURLState(this);
5582           var scheme = url.scheme;
5583           var port = url.port;
5584           if (scheme == 'blob') try {
5585             return new URLConstructor(scheme.path[0]).origin;
5586           } catch (error) {
5587             return 'null';
5588           }
5589           if (scheme == 'file' || !isSpecial(url)) return 'null';
5590           return scheme + '://' + serializeHost(url.host) + (port !== null ? ':' + port : '');
5591         };
5592
5593         var getProtocol = function () {
5594           return getInternalURLState(this).scheme + ':';
5595         };
5596
5597         var getUsername = function () {
5598           return getInternalURLState(this).username;
5599         };
5600
5601         var getPassword = function () {
5602           return getInternalURLState(this).password;
5603         };
5604
5605         var getHost = function () {
5606           var url = getInternalURLState(this);
5607           var host = url.host;
5608           var port = url.port;
5609           return host === null ? ''
5610             : port === null ? serializeHost(host)
5611             : serializeHost(host) + ':' + port;
5612         };
5613
5614         var getHostname = function () {
5615           var host = getInternalURLState(this).host;
5616           return host === null ? '' : serializeHost(host);
5617         };
5618
5619         var getPort = function () {
5620           var port = getInternalURLState(this).port;
5621           return port === null ? '' : String(port);
5622         };
5623
5624         var getPathname = function () {
5625           var url = getInternalURLState(this);
5626           var path = url.path;
5627           return url.cannotBeABaseURL ? path[0] : path.length ? '/' + path.join('/') : '';
5628         };
5629
5630         var getSearch = function () {
5631           var query = getInternalURLState(this).query;
5632           return query ? '?' + query : '';
5633         };
5634
5635         var getSearchParams = function () {
5636           return getInternalURLState(this).searchParams;
5637         };
5638
5639         var getHash = function () {
5640           var fragment = getInternalURLState(this).fragment;
5641           return fragment ? '#' + fragment : '';
5642         };
5643
5644         var accessorDescriptor = function (getter, setter) {
5645           return { get: getter, set: setter, configurable: true, enumerable: true };
5646         };
5647
5648         if (descriptors) {
5649           objectDefineProperties(URLPrototype, {
5650             // `URL.prototype.href` accessors pair
5651             // https://url.spec.whatwg.org/#dom-url-href
5652             href: accessorDescriptor(serializeURL, function (href) {
5653               var url = getInternalURLState(this);
5654               var urlString = String(href);
5655               var failure = parseURL(url, urlString);
5656               if (failure) throw TypeError(failure);
5657               getInternalSearchParamsState(url.searchParams).updateSearchParams(url.query);
5658             }),
5659             // `URL.prototype.origin` getter
5660             // https://url.spec.whatwg.org/#dom-url-origin
5661             origin: accessorDescriptor(getOrigin),
5662             // `URL.prototype.protocol` accessors pair
5663             // https://url.spec.whatwg.org/#dom-url-protocol
5664             protocol: accessorDescriptor(getProtocol, function (protocol) {
5665               var url = getInternalURLState(this);
5666               parseURL(url, String(protocol) + ':', SCHEME_START);
5667             }),
5668             // `URL.prototype.username` accessors pair
5669             // https://url.spec.whatwg.org/#dom-url-username
5670             username: accessorDescriptor(getUsername, function (username) {
5671               var url = getInternalURLState(this);
5672               var codePoints = arrayFrom(String(username));
5673               if (cannotHaveUsernamePasswordPort(url)) return;
5674               url.username = '';
5675               for (var i = 0; i < codePoints.length; i++) {
5676                 url.username += percentEncode(codePoints[i], userinfoPercentEncodeSet);
5677               }
5678             }),
5679             // `URL.prototype.password` accessors pair
5680             // https://url.spec.whatwg.org/#dom-url-password
5681             password: accessorDescriptor(getPassword, function (password) {
5682               var url = getInternalURLState(this);
5683               var codePoints = arrayFrom(String(password));
5684               if (cannotHaveUsernamePasswordPort(url)) return;
5685               url.password = '';
5686               for (var i = 0; i < codePoints.length; i++) {
5687                 url.password += percentEncode(codePoints[i], userinfoPercentEncodeSet);
5688               }
5689             }),
5690             // `URL.prototype.host` accessors pair
5691             // https://url.spec.whatwg.org/#dom-url-host
5692             host: accessorDescriptor(getHost, function (host) {
5693               var url = getInternalURLState(this);
5694               if (url.cannotBeABaseURL) return;
5695               parseURL(url, String(host), HOST);
5696             }),
5697             // `URL.prototype.hostname` accessors pair
5698             // https://url.spec.whatwg.org/#dom-url-hostname
5699             hostname: accessorDescriptor(getHostname, function (hostname) {
5700               var url = getInternalURLState(this);
5701               if (url.cannotBeABaseURL) return;
5702               parseURL(url, String(hostname), HOSTNAME);
5703             }),
5704             // `URL.prototype.port` accessors pair
5705             // https://url.spec.whatwg.org/#dom-url-port
5706             port: accessorDescriptor(getPort, function (port) {
5707               var url = getInternalURLState(this);
5708               if (cannotHaveUsernamePasswordPort(url)) return;
5709               port = String(port);
5710               if (port == '') url.port = null;
5711               else parseURL(url, port, PORT);
5712             }),
5713             // `URL.prototype.pathname` accessors pair
5714             // https://url.spec.whatwg.org/#dom-url-pathname
5715             pathname: accessorDescriptor(getPathname, function (pathname) {
5716               var url = getInternalURLState(this);
5717               if (url.cannotBeABaseURL) return;
5718               url.path = [];
5719               parseURL(url, pathname + '', PATH_START);
5720             }),
5721             // `URL.prototype.search` accessors pair
5722             // https://url.spec.whatwg.org/#dom-url-search
5723             search: accessorDescriptor(getSearch, function (search) {
5724               var url = getInternalURLState(this);
5725               search = String(search);
5726               if (search == '') {
5727                 url.query = null;
5728               } else {
5729                 if ('?' == search.charAt(0)) search = search.slice(1);
5730                 url.query = '';
5731                 parseURL(url, search, QUERY);
5732               }
5733               getInternalSearchParamsState(url.searchParams).updateSearchParams(url.query);
5734             }),
5735             // `URL.prototype.searchParams` getter
5736             // https://url.spec.whatwg.org/#dom-url-searchparams
5737             searchParams: accessorDescriptor(getSearchParams),
5738             // `URL.prototype.hash` accessors pair
5739             // https://url.spec.whatwg.org/#dom-url-hash
5740             hash: accessorDescriptor(getHash, function (hash) {
5741               var url = getInternalURLState(this);
5742               hash = String(hash);
5743               if (hash == '') {
5744                 url.fragment = null;
5745                 return;
5746               }
5747               if ('#' == hash.charAt(0)) hash = hash.slice(1);
5748               url.fragment = '';
5749               parseURL(url, hash, FRAGMENT);
5750             })
5751           });
5752         }
5753
5754         // `URL.prototype.toJSON` method
5755         // https://url.spec.whatwg.org/#dom-url-tojson
5756         redefine(URLPrototype, 'toJSON', function toJSON() {
5757           return serializeURL.call(this);
5758         }, { enumerable: true });
5759
5760         // `URL.prototype.toString` method
5761         // https://url.spec.whatwg.org/#URL-stringification-behavior
5762         redefine(URLPrototype, 'toString', function toString() {
5763           return serializeURL.call(this);
5764         }, { enumerable: true });
5765
5766         if (NativeURL) {
5767           var nativeCreateObjectURL = NativeURL.createObjectURL;
5768           var nativeRevokeObjectURL = NativeURL.revokeObjectURL;
5769           // `URL.createObjectURL` method
5770           // https://developer.mozilla.org/en-US/docs/Web/API/URL/createObjectURL
5771           // eslint-disable-next-line no-unused-vars -- required for `.length`
5772           if (nativeCreateObjectURL) redefine(URLConstructor, 'createObjectURL', function createObjectURL(blob) {
5773             return nativeCreateObjectURL.apply(NativeURL, arguments);
5774           });
5775           // `URL.revokeObjectURL` method
5776           // https://developer.mozilla.org/en-US/docs/Web/API/URL/revokeObjectURL
5777           // eslint-disable-next-line no-unused-vars -- required for `.length`
5778           if (nativeRevokeObjectURL) redefine(URLConstructor, 'revokeObjectURL', function revokeObjectURL(url) {
5779             return nativeRevokeObjectURL.apply(NativeURL, arguments);
5780           });
5781         }
5782
5783         setToStringTag(URLConstructor, 'URL');
5784
5785         _export({ global: true, forced: !nativeUrl, sham: !descriptors }, {
5786           URL: URLConstructor
5787         });
5788
5789         // `RegExp.prototype.flags` getter implementation
5790         // https://tc39.es/ecma262/#sec-get-regexp.prototype.flags
5791         var regexpFlags = function () {
5792           var that = anObject(this);
5793           var result = '';
5794           if (that.global) result += 'g';
5795           if (that.ignoreCase) result += 'i';
5796           if (that.multiline) result += 'm';
5797           if (that.dotAll) result += 's';
5798           if (that.unicode) result += 'u';
5799           if (that.sticky) result += 'y';
5800           return result;
5801         };
5802
5803         var TO_STRING = 'toString';
5804         var RegExpPrototype$2 = RegExp.prototype;
5805         var nativeToString = RegExpPrototype$2[TO_STRING];
5806
5807         var NOT_GENERIC = fails(function () { return nativeToString.call({ source: 'a', flags: 'b' }) != '/a/b'; });
5808         // FF44- RegExp#toString has a wrong name
5809         var INCORRECT_NAME = nativeToString.name != TO_STRING;
5810
5811         // `RegExp.prototype.toString` method
5812         // https://tc39.es/ecma262/#sec-regexp.prototype.tostring
5813         if (NOT_GENERIC || INCORRECT_NAME) {
5814           redefine(RegExp.prototype, TO_STRING, function toString() {
5815             var R = anObject(this);
5816             var p = String(R.source);
5817             var rf = R.flags;
5818             var f = String(rf === undefined && R instanceof RegExp && !('flags' in RegExpPrototype$2) ? regexpFlags.call(R) : rf);
5819             return '/' + p + '/' + f;
5820           }, { unsafe: true });
5821         }
5822
5823         // babel-minify transpiles RegExp('a', 'y') -> /a/y and it causes SyntaxError,
5824         var RE = function (s, f) {
5825           return RegExp(s, f);
5826         };
5827
5828         var UNSUPPORTED_Y$3 = fails(function () {
5829           var re = RE('a', 'y');
5830           re.lastIndex = 2;
5831           return re.exec('abcd') != null;
5832         });
5833
5834         var BROKEN_CARET = fails(function () {
5835           // https://bugzilla.mozilla.org/show_bug.cgi?id=773687
5836           var re = RE('^r', 'gy');
5837           re.lastIndex = 2;
5838           return re.exec('str') != null;
5839         });
5840
5841         var regexpStickyHelpers = {
5842                 UNSUPPORTED_Y: UNSUPPORTED_Y$3,
5843                 BROKEN_CARET: BROKEN_CARET
5844         };
5845
5846         var regexpUnsupportedDotAll = fails(function () {
5847           // babel-minify transpiles RegExp('.', 's') -> /./s and it causes SyntaxError
5848           var re = RegExp('.', (typeof '').charAt(0));
5849           return !(re.dotAll && re.exec('\n') && re.flags === 's');
5850         });
5851
5852         var regexpUnsupportedNcg = fails(function () {
5853           // babel-minify transpiles RegExp('.', 'g') -> /./g and it causes SyntaxError
5854           var re = RegExp('(?<a>b)', (typeof '').charAt(5));
5855           return re.exec('b').groups.a !== 'b' ||
5856             'b'.replace(re, '$<a>c') !== 'bc';
5857         });
5858
5859         /* eslint-disable regexp/no-assertion-capturing-group, regexp/no-empty-group, regexp/no-lazy-ends -- testing */
5860         /* eslint-disable regexp/no-useless-quantifier -- testing */
5861
5862
5863
5864
5865         var getInternalState = internalState.get;
5866
5867
5868
5869         var nativeExec = RegExp.prototype.exec;
5870         var nativeReplace = shared('native-string-replace', String.prototype.replace);
5871
5872         var patchedExec = nativeExec;
5873
5874         var UPDATES_LAST_INDEX_WRONG = (function () {
5875           var re1 = /a/;
5876           var re2 = /b*/g;
5877           nativeExec.call(re1, 'a');
5878           nativeExec.call(re2, 'a');
5879           return re1.lastIndex !== 0 || re2.lastIndex !== 0;
5880         })();
5881
5882         var UNSUPPORTED_Y$2 = regexpStickyHelpers.UNSUPPORTED_Y || regexpStickyHelpers.BROKEN_CARET;
5883
5884         // nonparticipating capturing group, copied from es5-shim's String#split patch.
5885         var NPCG_INCLUDED = /()??/.exec('')[1] !== undefined;
5886
5887         var PATCH = UPDATES_LAST_INDEX_WRONG || NPCG_INCLUDED || UNSUPPORTED_Y$2 || regexpUnsupportedDotAll || regexpUnsupportedNcg;
5888
5889         if (PATCH) {
5890           // eslint-disable-next-line max-statements -- TODO
5891           patchedExec = function exec(str) {
5892             var re = this;
5893             var state = getInternalState(re);
5894             var raw = state.raw;
5895             var result, reCopy, lastIndex, match, i, object, group;
5896
5897             if (raw) {
5898               raw.lastIndex = re.lastIndex;
5899               result = patchedExec.call(raw, str);
5900               re.lastIndex = raw.lastIndex;
5901               return result;
5902             }
5903
5904             var groups = state.groups;
5905             var sticky = UNSUPPORTED_Y$2 && re.sticky;
5906             var flags = regexpFlags.call(re);
5907             var source = re.source;
5908             var charsAdded = 0;
5909             var strCopy = str;
5910
5911             if (sticky) {
5912               flags = flags.replace('y', '');
5913               if (flags.indexOf('g') === -1) {
5914                 flags += 'g';
5915               }
5916
5917               strCopy = String(str).slice(re.lastIndex);
5918               // Support anchored sticky behavior.
5919               if (re.lastIndex > 0 && (!re.multiline || re.multiline && str[re.lastIndex - 1] !== '\n')) {
5920                 source = '(?: ' + source + ')';
5921                 strCopy = ' ' + strCopy;
5922                 charsAdded++;
5923               }
5924               // ^(? + rx + ) is needed, in combination with some str slicing, to
5925               // simulate the 'y' flag.
5926               reCopy = new RegExp('^(?:' + source + ')', flags);
5927             }
5928
5929             if (NPCG_INCLUDED) {
5930               reCopy = new RegExp('^' + source + '$(?!\\s)', flags);
5931             }
5932             if (UPDATES_LAST_INDEX_WRONG) lastIndex = re.lastIndex;
5933
5934             match = nativeExec.call(sticky ? reCopy : re, strCopy);
5935
5936             if (sticky) {
5937               if (match) {
5938                 match.input = match.input.slice(charsAdded);
5939                 match[0] = match[0].slice(charsAdded);
5940                 match.index = re.lastIndex;
5941                 re.lastIndex += match[0].length;
5942               } else re.lastIndex = 0;
5943             } else if (UPDATES_LAST_INDEX_WRONG && match) {
5944               re.lastIndex = re.global ? match.index + match[0].length : lastIndex;
5945             }
5946             if (NPCG_INCLUDED && match && match.length > 1) {
5947               // Fix browsers whose `exec` methods don't consistently return `undefined`
5948               // for NPCG, like IE8. NOTE: This doesn' work for /(.?)?/
5949               nativeReplace.call(match[0], reCopy, function () {
5950                 for (i = 1; i < arguments.length - 2; i++) {
5951                   if (arguments[i] === undefined) match[i] = undefined;
5952                 }
5953               });
5954             }
5955
5956             if (match && groups) {
5957               match.groups = object = objectCreate(null);
5958               for (i = 0; i < groups.length; i++) {
5959                 group = groups[i];
5960                 object[group[0]] = match[group[1]];
5961               }
5962             }
5963
5964             return match;
5965           };
5966         }
5967
5968         var regexpExec = patchedExec;
5969
5970         // `RegExp.prototype.exec` method
5971         // https://tc39.es/ecma262/#sec-regexp.prototype.exec
5972         _export({ target: 'RegExp', proto: true, forced: /./.exec !== regexpExec }, {
5973           exec: regexpExec
5974         });
5975
5976         // TODO: Remove from `core-js@4` since it's moved to entry points
5977
5978
5979
5980
5981
5982
5983
5984         var SPECIES = wellKnownSymbol('species');
5985         var RegExpPrototype$1 = RegExp.prototype;
5986
5987         var fixRegexpWellKnownSymbolLogic = function (KEY, exec, FORCED, SHAM) {
5988           var SYMBOL = wellKnownSymbol(KEY);
5989
5990           var DELEGATES_TO_SYMBOL = !fails(function () {
5991             // String methods call symbol-named RegEp methods
5992             var O = {};
5993             O[SYMBOL] = function () { return 7; };
5994             return ''[KEY](O) != 7;
5995           });
5996
5997           var DELEGATES_TO_EXEC = DELEGATES_TO_SYMBOL && !fails(function () {
5998             // Symbol-named RegExp methods call .exec
5999             var execCalled = false;
6000             var re = /a/;
6001
6002             if (KEY === 'split') {
6003               // We can't use real regex here since it causes deoptimization
6004               // and serious performance degradation in V8
6005               // https://github.com/zloirock/core-js/issues/306
6006               re = {};
6007               // RegExp[@@split] doesn't call the regex's exec method, but first creates
6008               // a new one. We need to return the patched regex when creating the new one.
6009               re.constructor = {};
6010               re.constructor[SPECIES] = function () { return re; };
6011               re.flags = '';
6012               re[SYMBOL] = /./[SYMBOL];
6013             }
6014
6015             re.exec = function () { execCalled = true; return null; };
6016
6017             re[SYMBOL]('');
6018             return !execCalled;
6019           });
6020
6021           if (
6022             !DELEGATES_TO_SYMBOL ||
6023             !DELEGATES_TO_EXEC ||
6024             FORCED
6025           ) {
6026             var nativeRegExpMethod = /./[SYMBOL];
6027             var methods = exec(SYMBOL, ''[KEY], function (nativeMethod, regexp, str, arg2, forceStringMethod) {
6028               var $exec = regexp.exec;
6029               if ($exec === regexpExec || $exec === RegExpPrototype$1.exec) {
6030                 if (DELEGATES_TO_SYMBOL && !forceStringMethod) {
6031                   // The native String method already delegates to @@method (this
6032                   // polyfilled function), leasing to infinite recursion.
6033                   // We avoid it by directly calling the native @@method method.
6034                   return { done: true, value: nativeRegExpMethod.call(regexp, str, arg2) };
6035                 }
6036                 return { done: true, value: nativeMethod.call(str, regexp, arg2) };
6037               }
6038               return { done: false };
6039             });
6040
6041             redefine(String.prototype, KEY, methods[0]);
6042             redefine(RegExpPrototype$1, SYMBOL, methods[1]);
6043           }
6044
6045           if (SHAM) createNonEnumerableProperty(RegExpPrototype$1[SYMBOL], 'sham', true);
6046         };
6047
6048         var charAt = stringMultibyte.charAt;
6049
6050         // `AdvanceStringIndex` abstract operation
6051         // https://tc39.es/ecma262/#sec-advancestringindex
6052         var advanceStringIndex = function (S, index, unicode) {
6053           return index + (unicode ? charAt(S, index).length : 1);
6054         };
6055
6056         var floor$1 = Math.floor;
6057         var replace = ''.replace;
6058         var SUBSTITUTION_SYMBOLS = /\$([$&'`]|\d{1,2}|<[^>]*>)/g;
6059         var SUBSTITUTION_SYMBOLS_NO_NAMED = /\$([$&'`]|\d{1,2})/g;
6060
6061         // `GetSubstitution` abstract operation
6062         // https://tc39.es/ecma262/#sec-getsubstitution
6063         var getSubstitution = function (matched, str, position, captures, namedCaptures, replacement) {
6064           var tailPos = position + matched.length;
6065           var m = captures.length;
6066           var symbols = SUBSTITUTION_SYMBOLS_NO_NAMED;
6067           if (namedCaptures !== undefined) {
6068             namedCaptures = toObject(namedCaptures);
6069             symbols = SUBSTITUTION_SYMBOLS;
6070           }
6071           return replace.call(replacement, symbols, function (match, ch) {
6072             var capture;
6073             switch (ch.charAt(0)) {
6074               case '$': return '$';
6075               case '&': return matched;
6076               case '`': return str.slice(0, position);
6077               case "'": return str.slice(tailPos);
6078               case '<':
6079                 capture = namedCaptures[ch.slice(1, -1)];
6080                 break;
6081               default: // \d\d?
6082                 var n = +ch;
6083                 if (n === 0) return match;
6084                 if (n > m) {
6085                   var f = floor$1(n / 10);
6086                   if (f === 0) return match;
6087                   if (f <= m) return captures[f - 1] === undefined ? ch.charAt(1) : captures[f - 1] + ch.charAt(1);
6088                   return match;
6089                 }
6090                 capture = captures[n - 1];
6091             }
6092             return capture === undefined ? '' : capture;
6093           });
6094         };
6095
6096         // `RegExpExec` abstract operation
6097         // https://tc39.es/ecma262/#sec-regexpexec
6098         var regexpExecAbstract = function (R, S) {
6099           var exec = R.exec;
6100           if (typeof exec === 'function') {
6101             var result = exec.call(R, S);
6102             if (typeof result !== 'object') {
6103               throw TypeError('RegExp exec method returned something other than an Object or null');
6104             }
6105             return result;
6106           }
6107
6108           if (classofRaw(R) !== 'RegExp') {
6109             throw TypeError('RegExp#exec called on incompatible receiver');
6110           }
6111
6112           return regexpExec.call(R, S);
6113         };
6114
6115         var REPLACE = wellKnownSymbol('replace');
6116         var max$2 = Math.max;
6117         var min$5 = Math.min;
6118
6119         var maybeToString = function (it) {
6120           return it === undefined ? it : String(it);
6121         };
6122
6123         // IE <= 11 replaces $0 with the whole match, as if it was $&
6124         // https://stackoverflow.com/questions/6024666/getting-ie-to-replace-a-regex-with-the-literal-string-0
6125         var REPLACE_KEEPS_$0 = (function () {
6126           // eslint-disable-next-line regexp/prefer-escape-replacement-dollar-char -- required for testing
6127           return 'a'.replace(/./, '$0') === '$0';
6128         })();
6129
6130         // Safari <= 13.0.3(?) substitutes nth capture where n>m with an empty string
6131         var REGEXP_REPLACE_SUBSTITUTES_UNDEFINED_CAPTURE = (function () {
6132           if (/./[REPLACE]) {
6133             return /./[REPLACE]('a', '$0') === '';
6134           }
6135           return false;
6136         })();
6137
6138         var REPLACE_SUPPORTS_NAMED_GROUPS = !fails(function () {
6139           var re = /./;
6140           re.exec = function () {
6141             var result = [];
6142             result.groups = { a: '7' };
6143             return result;
6144           };
6145           return ''.replace(re, '$<a>') !== '7';
6146         });
6147
6148         // @@replace logic
6149         fixRegexpWellKnownSymbolLogic('replace', function (_, nativeReplace, maybeCallNative) {
6150           var UNSAFE_SUBSTITUTE = REGEXP_REPLACE_SUBSTITUTES_UNDEFINED_CAPTURE ? '$' : '$0';
6151
6152           return [
6153             // `String.prototype.replace` method
6154             // https://tc39.es/ecma262/#sec-string.prototype.replace
6155             function replace(searchValue, replaceValue) {
6156               var O = requireObjectCoercible(this);
6157               var replacer = searchValue == undefined ? undefined : searchValue[REPLACE];
6158               return replacer !== undefined
6159                 ? replacer.call(searchValue, O, replaceValue)
6160                 : nativeReplace.call(String(O), searchValue, replaceValue);
6161             },
6162             // `RegExp.prototype[@@replace]` method
6163             // https://tc39.es/ecma262/#sec-regexp.prototype-@@replace
6164             function (string, replaceValue) {
6165               if (
6166                 typeof replaceValue === 'string' &&
6167                 replaceValue.indexOf(UNSAFE_SUBSTITUTE) === -1 &&
6168                 replaceValue.indexOf('$<') === -1
6169               ) {
6170                 var res = maybeCallNative(nativeReplace, this, string, replaceValue);
6171                 if (res.done) return res.value;
6172               }
6173
6174               var rx = anObject(this);
6175               var S = String(string);
6176
6177               var functionalReplace = typeof replaceValue === 'function';
6178               if (!functionalReplace) replaceValue = String(replaceValue);
6179
6180               var global = rx.global;
6181               if (global) {
6182                 var fullUnicode = rx.unicode;
6183                 rx.lastIndex = 0;
6184               }
6185               var results = [];
6186               while (true) {
6187                 var result = regexpExecAbstract(rx, S);
6188                 if (result === null) break;
6189
6190                 results.push(result);
6191                 if (!global) break;
6192
6193                 var matchStr = String(result[0]);
6194                 if (matchStr === '') rx.lastIndex = advanceStringIndex(S, toLength(rx.lastIndex), fullUnicode);
6195               }
6196
6197               var accumulatedResult = '';
6198               var nextSourcePosition = 0;
6199               for (var i = 0; i < results.length; i++) {
6200                 result = results[i];
6201
6202                 var matched = String(result[0]);
6203                 var position = max$2(min$5(toInteger(result.index), S.length), 0);
6204                 var captures = [];
6205                 // NOTE: This is equivalent to
6206                 //   captures = result.slice(1).map(maybeToString)
6207                 // but for some reason `nativeSlice.call(result, 1, result.length)` (called in
6208                 // the slice polyfill when slicing native arrays) "doesn't work" in safari 9 and
6209                 // causes a crash (https://pastebin.com/N21QzeQA) when trying to debug it.
6210                 for (var j = 1; j < result.length; j++) captures.push(maybeToString(result[j]));
6211                 var namedCaptures = result.groups;
6212                 if (functionalReplace) {
6213                   var replacerArgs = [matched].concat(captures, position, S);
6214                   if (namedCaptures !== undefined) replacerArgs.push(namedCaptures);
6215                   var replacement = String(replaceValue.apply(undefined, replacerArgs));
6216                 } else {
6217                   replacement = getSubstitution(matched, S, position, captures, namedCaptures, replaceValue);
6218                 }
6219                 if (position >= nextSourcePosition) {
6220                   accumulatedResult += S.slice(nextSourcePosition, position) + replacement;
6221                   nextSourcePosition = position + matched.length;
6222                 }
6223               }
6224               return accumulatedResult + S.slice(nextSourcePosition);
6225             }
6226           ];
6227         }, !REPLACE_SUPPORTS_NAMED_GROUPS || !REPLACE_KEEPS_$0 || REGEXP_REPLACE_SUBSTITUTES_UNDEFINED_CAPTURE);
6228
6229         var MATCH$2 = wellKnownSymbol('match');
6230
6231         // `IsRegExp` abstract operation
6232         // https://tc39.es/ecma262/#sec-isregexp
6233         var isRegexp = function (it) {
6234           var isRegExp;
6235           return isObject$4(it) && ((isRegExp = it[MATCH$2]) !== undefined ? !!isRegExp : classofRaw(it) == 'RegExp');
6236         };
6237
6238         var UNSUPPORTED_Y$1 = regexpStickyHelpers.UNSUPPORTED_Y;
6239         var arrayPush = [].push;
6240         var min$4 = Math.min;
6241         var MAX_UINT32 = 0xFFFFFFFF;
6242
6243         // Chrome 51 has a buggy "split" implementation when RegExp#exec !== nativeExec
6244         // Weex JS has frozen built-in prototypes, so use try / catch wrapper
6245         var SPLIT_WORKS_WITH_OVERWRITTEN_EXEC = !fails(function () {
6246           // eslint-disable-next-line regexp/no-empty-group -- required for testing
6247           var re = /(?:)/;
6248           var originalExec = re.exec;
6249           re.exec = function () { return originalExec.apply(this, arguments); };
6250           var result = 'ab'.split(re);
6251           return result.length !== 2 || result[0] !== 'a' || result[1] !== 'b';
6252         });
6253
6254         // @@split logic
6255         fixRegexpWellKnownSymbolLogic('split', function (SPLIT, nativeSplit, maybeCallNative) {
6256           var internalSplit;
6257           if (
6258             'abbc'.split(/(b)*/)[1] == 'c' ||
6259             // eslint-disable-next-line regexp/no-empty-group -- required for testing
6260             'test'.split(/(?:)/, -1).length != 4 ||
6261             'ab'.split(/(?:ab)*/).length != 2 ||
6262             '.'.split(/(.?)(.?)/).length != 4 ||
6263             // eslint-disable-next-line regexp/no-assertion-capturing-group, regexp/no-empty-group -- required for testing
6264             '.'.split(/()()/).length > 1 ||
6265             ''.split(/.?/).length
6266           ) {
6267             // based on es5-shim implementation, need to rework it
6268             internalSplit = function (separator, limit) {
6269               var string = String(requireObjectCoercible(this));
6270               var lim = limit === undefined ? MAX_UINT32 : limit >>> 0;
6271               if (lim === 0) return [];
6272               if (separator === undefined) return [string];
6273               // If `separator` is not a regex, use native split
6274               if (!isRegexp(separator)) {
6275                 return nativeSplit.call(string, separator, lim);
6276               }
6277               var output = [];
6278               var flags = (separator.ignoreCase ? 'i' : '') +
6279                           (separator.multiline ? 'm' : '') +
6280                           (separator.unicode ? 'u' : '') +
6281                           (separator.sticky ? 'y' : '');
6282               var lastLastIndex = 0;
6283               // Make `global` and avoid `lastIndex` issues by working with a copy
6284               var separatorCopy = new RegExp(separator.source, flags + 'g');
6285               var match, lastIndex, lastLength;
6286               while (match = regexpExec.call(separatorCopy, string)) {
6287                 lastIndex = separatorCopy.lastIndex;
6288                 if (lastIndex > lastLastIndex) {
6289                   output.push(string.slice(lastLastIndex, match.index));
6290                   if (match.length > 1 && match.index < string.length) arrayPush.apply(output, match.slice(1));
6291                   lastLength = match[0].length;
6292                   lastLastIndex = lastIndex;
6293                   if (output.length >= lim) break;
6294                 }
6295                 if (separatorCopy.lastIndex === match.index) separatorCopy.lastIndex++; // Avoid an infinite loop
6296               }
6297               if (lastLastIndex === string.length) {
6298                 if (lastLength || !separatorCopy.test('')) output.push('');
6299               } else output.push(string.slice(lastLastIndex));
6300               return output.length > lim ? output.slice(0, lim) : output;
6301             };
6302           // Chakra, V8
6303           } else if ('0'.split(undefined, 0).length) {
6304             internalSplit = function (separator, limit) {
6305               return separator === undefined && limit === 0 ? [] : nativeSplit.call(this, separator, limit);
6306             };
6307           } else internalSplit = nativeSplit;
6308
6309           return [
6310             // `String.prototype.split` method
6311             // https://tc39.es/ecma262/#sec-string.prototype.split
6312             function split(separator, limit) {
6313               var O = requireObjectCoercible(this);
6314               var splitter = separator == undefined ? undefined : separator[SPLIT];
6315               return splitter !== undefined
6316                 ? splitter.call(separator, O, limit)
6317                 : internalSplit.call(String(O), separator, limit);
6318             },
6319             // `RegExp.prototype[@@split]` method
6320             // https://tc39.es/ecma262/#sec-regexp.prototype-@@split
6321             //
6322             // NOTE: This cannot be properly polyfilled in engines that don't support
6323             // the 'y' flag.
6324             function (string, limit) {
6325               var res = maybeCallNative(internalSplit, this, string, limit, internalSplit !== nativeSplit);
6326               if (res.done) return res.value;
6327
6328               var rx = anObject(this);
6329               var S = String(string);
6330               var C = speciesConstructor(rx, RegExp);
6331
6332               var unicodeMatching = rx.unicode;
6333               var flags = (rx.ignoreCase ? 'i' : '') +
6334                           (rx.multiline ? 'm' : '') +
6335                           (rx.unicode ? 'u' : '') +
6336                           (UNSUPPORTED_Y$1 ? 'g' : 'y');
6337
6338               // ^(? + rx + ) is needed, in combination with some S slicing, to
6339               // simulate the 'y' flag.
6340               var splitter = new C(UNSUPPORTED_Y$1 ? '^(?:' + rx.source + ')' : rx, flags);
6341               var lim = limit === undefined ? MAX_UINT32 : limit >>> 0;
6342               if (lim === 0) return [];
6343               if (S.length === 0) return regexpExecAbstract(splitter, S) === null ? [S] : [];
6344               var p = 0;
6345               var q = 0;
6346               var A = [];
6347               while (q < S.length) {
6348                 splitter.lastIndex = UNSUPPORTED_Y$1 ? 0 : q;
6349                 var z = regexpExecAbstract(splitter, UNSUPPORTED_Y$1 ? S.slice(q) : S);
6350                 var e;
6351                 if (
6352                   z === null ||
6353                   (e = min$4(toLength(splitter.lastIndex + (UNSUPPORTED_Y$1 ? q : 0)), S.length)) === p
6354                 ) {
6355                   q = advanceStringIndex(S, q, unicodeMatching);
6356                 } else {
6357                   A.push(S.slice(p, q));
6358                   if (A.length === lim) return A;
6359                   for (var i = 1; i <= z.length - 1; i++) {
6360                     A.push(z[i]);
6361                     if (A.length === lim) return A;
6362                   }
6363                   q = p = e;
6364                 }
6365               }
6366               A.push(S.slice(p));
6367               return A;
6368             }
6369           ];
6370         }, !SPLIT_WORKS_WITH_OVERWRITTEN_EXEC, UNSUPPORTED_Y$1);
6371
6372         // a string of all valid unicode whitespaces
6373         var whitespaces = '\u0009\u000A\u000B\u000C\u000D\u0020\u00A0\u1680\u2000\u2001\u2002' +
6374           '\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u202F\u205F\u3000\u2028\u2029\uFEFF';
6375
6376         var whitespace = '[' + whitespaces + ']';
6377         var ltrim = RegExp('^' + whitespace + whitespace + '*');
6378         var rtrim$2 = RegExp(whitespace + whitespace + '*$');
6379
6380         // `String.prototype.{ trim, trimStart, trimEnd, trimLeft, trimRight }` methods implementation
6381         var createMethod$2 = function (TYPE) {
6382           return function ($this) {
6383             var string = String(requireObjectCoercible($this));
6384             if (TYPE & 1) string = string.replace(ltrim, '');
6385             if (TYPE & 2) string = string.replace(rtrim$2, '');
6386             return string;
6387           };
6388         };
6389
6390         var stringTrim = {
6391           // `String.prototype.{ trimLeft, trimStart }` methods
6392           // https://tc39.es/ecma262/#sec-string.prototype.trimstart
6393           start: createMethod$2(1),
6394           // `String.prototype.{ trimRight, trimEnd }` methods
6395           // https://tc39.es/ecma262/#sec-string.prototype.trimend
6396           end: createMethod$2(2),
6397           // `String.prototype.trim` method
6398           // https://tc39.es/ecma262/#sec-string.prototype.trim
6399           trim: createMethod$2(3)
6400         };
6401
6402         var non = '\u200B\u0085\u180E';
6403
6404         // check that a method works with the correct list
6405         // of whitespaces and has a correct name
6406         var stringTrimForced = function (METHOD_NAME) {
6407           return fails(function () {
6408             return !!whitespaces[METHOD_NAME]() || non[METHOD_NAME]() != non || whitespaces[METHOD_NAME].name !== METHOD_NAME;
6409           });
6410         };
6411
6412         var $trim = stringTrim.trim;
6413
6414
6415         // `String.prototype.trim` method
6416         // https://tc39.es/ecma262/#sec-string.prototype.trim
6417         _export({ target: 'String', proto: true, forced: stringTrimForced('trim') }, {
6418           trim: function trim() {
6419             return $trim(this);
6420           }
6421         });
6422
6423         var defineProperty$3 = objectDefineProperty.f;
6424
6425         var FunctionPrototype = Function.prototype;
6426         var FunctionPrototypeToString = FunctionPrototype.toString;
6427         var nameRE = /^\s*function ([^ (]*)/;
6428         var NAME = 'name';
6429
6430         // Function instances `.name` property
6431         // https://tc39.es/ecma262/#sec-function-instances-name
6432         if (descriptors && !(NAME in FunctionPrototype)) {
6433           defineProperty$3(FunctionPrototype, NAME, {
6434             configurable: true,
6435             get: function () {
6436               try {
6437                 return FunctionPrototypeToString.call(this).match(nameRE)[1];
6438               } catch (error) {
6439                 return '';
6440               }
6441             }
6442           });
6443         }
6444
6445         // `Object.create` method
6446         // https://tc39.es/ecma262/#sec-object.create
6447         _export({ target: 'Object', stat: true, sham: !descriptors }, {
6448           create: objectCreate
6449         });
6450
6451         var slice$3 = [].slice;
6452         var MSIE = /MSIE .\./.test(engineUserAgent); // <- dirty ie9- check
6453
6454         var wrap$1 = function (scheduler) {
6455           return function (handler, timeout /* , ...arguments */) {
6456             var boundArgs = arguments.length > 2;
6457             var args = boundArgs ? slice$3.call(arguments, 2) : undefined;
6458             return scheduler(boundArgs ? function () {
6459               // eslint-disable-next-line no-new-func -- spec requirement
6460               (typeof handler == 'function' ? handler : Function(handler)).apply(this, args);
6461             } : handler, timeout);
6462           };
6463         };
6464
6465         // ie9- setTimeout & setInterval additional parameters fix
6466         // https://html.spec.whatwg.org/multipage/timers-and-user-prompts.html#timers
6467         _export({ global: true, bind: true, forced: MSIE }, {
6468           // `setTimeout` method
6469           // https://html.spec.whatwg.org/multipage/timers-and-user-prompts.html#dom-settimeout
6470           setTimeout: wrap$1(global$2.setTimeout),
6471           // `setInterval` method
6472           // https://html.spec.whatwg.org/multipage/timers-and-user-prompts.html#dom-setinterval
6473           setInterval: wrap$1(global$2.setInterval)
6474         });
6475
6476         var global$1 = typeof globalThis !== 'undefined' && globalThis || typeof self !== 'undefined' && self || typeof global$1 !== 'undefined' && global$1;
6477         var support = {
6478           searchParams: 'URLSearchParams' in global$1,
6479           iterable: 'Symbol' in global$1 && 'iterator' in Symbol,
6480           blob: 'FileReader' in global$1 && 'Blob' in global$1 && function () {
6481             try {
6482               new Blob();
6483               return true;
6484             } catch (e) {
6485               return false;
6486             }
6487           }(),
6488           formData: 'FormData' in global$1,
6489           arrayBuffer: 'ArrayBuffer' in global$1
6490         };
6491
6492         function isDataView(obj) {
6493           return obj && DataView.prototype.isPrototypeOf(obj);
6494         }
6495
6496         if (support.arrayBuffer) {
6497           var viewClasses = ['[object Int8Array]', '[object Uint8Array]', '[object Uint8ClampedArray]', '[object Int16Array]', '[object Uint16Array]', '[object Int32Array]', '[object Uint32Array]', '[object Float32Array]', '[object Float64Array]'];
6498
6499           var isArrayBufferView = ArrayBuffer.isView || function (obj) {
6500             return obj && viewClasses.indexOf(Object.prototype.toString.call(obj)) > -1;
6501           };
6502         }
6503
6504         function normalizeName(name) {
6505           if (typeof name !== 'string') {
6506             name = String(name);
6507           }
6508
6509           if (/[^a-z0-9\-#$%&'*+.^_`|~!]/i.test(name) || name === '') {
6510             throw new TypeError('Invalid character in header field name: "' + name + '"');
6511           }
6512
6513           return name.toLowerCase();
6514         }
6515
6516         function normalizeValue(value) {
6517           if (typeof value !== 'string') {
6518             value = String(value);
6519           }
6520
6521           return value;
6522         } // Build a destructive iterator for the value list
6523
6524
6525         function iteratorFor(items) {
6526           var iterator = {
6527             next: function next() {
6528               var value = items.shift();
6529               return {
6530                 done: value === undefined,
6531                 value: value
6532               };
6533             }
6534           };
6535
6536           if (support.iterable) {
6537             iterator[Symbol.iterator] = function () {
6538               return iterator;
6539             };
6540           }
6541
6542           return iterator;
6543         }
6544
6545         function Headers(headers) {
6546           this.map = {};
6547
6548           if (headers instanceof Headers) {
6549             headers.forEach(function (value, name) {
6550               this.append(name, value);
6551             }, this);
6552           } else if (Array.isArray(headers)) {
6553             headers.forEach(function (header) {
6554               this.append(header[0], header[1]);
6555             }, this);
6556           } else if (headers) {
6557             Object.getOwnPropertyNames(headers).forEach(function (name) {
6558               this.append(name, headers[name]);
6559             }, this);
6560           }
6561         }
6562
6563         Headers.prototype.append = function (name, value) {
6564           name = normalizeName(name);
6565           value = normalizeValue(value);
6566           var oldValue = this.map[name];
6567           this.map[name] = oldValue ? oldValue + ', ' + value : value;
6568         };
6569
6570         Headers.prototype['delete'] = function (name) {
6571           delete this.map[normalizeName(name)];
6572         };
6573
6574         Headers.prototype.get = function (name) {
6575           name = normalizeName(name);
6576           return this.has(name) ? this.map[name] : null;
6577         };
6578
6579         Headers.prototype.has = function (name) {
6580           return this.map.hasOwnProperty(normalizeName(name));
6581         };
6582
6583         Headers.prototype.set = function (name, value) {
6584           this.map[normalizeName(name)] = normalizeValue(value);
6585         };
6586
6587         Headers.prototype.forEach = function (callback, thisArg) {
6588           for (var name in this.map) {
6589             if (this.map.hasOwnProperty(name)) {
6590               callback.call(thisArg, this.map[name], name, this);
6591             }
6592           }
6593         };
6594
6595         Headers.prototype.keys = function () {
6596           var items = [];
6597           this.forEach(function (value, name) {
6598             items.push(name);
6599           });
6600           return iteratorFor(items);
6601         };
6602
6603         Headers.prototype.values = function () {
6604           var items = [];
6605           this.forEach(function (value) {
6606             items.push(value);
6607           });
6608           return iteratorFor(items);
6609         };
6610
6611         Headers.prototype.entries = function () {
6612           var items = [];
6613           this.forEach(function (value, name) {
6614             items.push([name, value]);
6615           });
6616           return iteratorFor(items);
6617         };
6618
6619         if (support.iterable) {
6620           Headers.prototype[Symbol.iterator] = Headers.prototype.entries;
6621         }
6622
6623         function consumed(body) {
6624           if (body.bodyUsed) {
6625             return Promise.reject(new TypeError('Already read'));
6626           }
6627
6628           body.bodyUsed = true;
6629         }
6630
6631         function fileReaderReady(reader) {
6632           return new Promise(function (resolve, reject) {
6633             reader.onload = function () {
6634               resolve(reader.result);
6635             };
6636
6637             reader.onerror = function () {
6638               reject(reader.error);
6639             };
6640           });
6641         }
6642
6643         function readBlobAsArrayBuffer(blob) {
6644           var reader = new FileReader();
6645           var promise = fileReaderReady(reader);
6646           reader.readAsArrayBuffer(blob);
6647           return promise;
6648         }
6649
6650         function readBlobAsText(blob) {
6651           var reader = new FileReader();
6652           var promise = fileReaderReady(reader);
6653           reader.readAsText(blob);
6654           return promise;
6655         }
6656
6657         function readArrayBufferAsText(buf) {
6658           var view = new Uint8Array(buf);
6659           var chars = new Array(view.length);
6660
6661           for (var i = 0; i < view.length; i++) {
6662             chars[i] = String.fromCharCode(view[i]);
6663           }
6664
6665           return chars.join('');
6666         }
6667
6668         function bufferClone(buf) {
6669           if (buf.slice) {
6670             return buf.slice(0);
6671           } else {
6672             var view = new Uint8Array(buf.byteLength);
6673             view.set(new Uint8Array(buf));
6674             return view.buffer;
6675           }
6676         }
6677
6678         function Body() {
6679           this.bodyUsed = false;
6680
6681           this._initBody = function (body) {
6682             /*
6683               fetch-mock wraps the Response object in an ES6 Proxy to
6684               provide useful test harness features such as flush. However, on
6685               ES5 browsers without fetch or Proxy support pollyfills must be used;
6686               the proxy-pollyfill is unable to proxy an attribute unless it exists
6687               on the object before the Proxy is created. This change ensures
6688               Response.bodyUsed exists on the instance, while maintaining the
6689               semantic of setting Request.bodyUsed in the constructor before
6690               _initBody is called.
6691             */
6692             this.bodyUsed = this.bodyUsed;
6693             this._bodyInit = body;
6694
6695             if (!body) {
6696               this._bodyText = '';
6697             } else if (typeof body === 'string') {
6698               this._bodyText = body;
6699             } else if (support.blob && Blob.prototype.isPrototypeOf(body)) {
6700               this._bodyBlob = body;
6701             } else if (support.formData && FormData.prototype.isPrototypeOf(body)) {
6702               this._bodyFormData = body;
6703             } else if (support.searchParams && URLSearchParams.prototype.isPrototypeOf(body)) {
6704               this._bodyText = body.toString();
6705             } else if (support.arrayBuffer && support.blob && isDataView(body)) {
6706               this._bodyArrayBuffer = bufferClone(body.buffer); // IE 10-11 can't handle a DataView body.
6707
6708               this._bodyInit = new Blob([this._bodyArrayBuffer]);
6709             } else if (support.arrayBuffer && (ArrayBuffer.prototype.isPrototypeOf(body) || isArrayBufferView(body))) {
6710               this._bodyArrayBuffer = bufferClone(body);
6711             } else {
6712               this._bodyText = body = Object.prototype.toString.call(body);
6713             }
6714
6715             if (!this.headers.get('content-type')) {
6716               if (typeof body === 'string') {
6717                 this.headers.set('content-type', 'text/plain;charset=UTF-8');
6718               } else if (this._bodyBlob && this._bodyBlob.type) {
6719                 this.headers.set('content-type', this._bodyBlob.type);
6720               } else if (support.searchParams && URLSearchParams.prototype.isPrototypeOf(body)) {
6721                 this.headers.set('content-type', 'application/x-www-form-urlencoded;charset=UTF-8');
6722               }
6723             }
6724           };
6725
6726           if (support.blob) {
6727             this.blob = function () {
6728               var rejected = consumed(this);
6729
6730               if (rejected) {
6731                 return rejected;
6732               }
6733
6734               if (this._bodyBlob) {
6735                 return Promise.resolve(this._bodyBlob);
6736               } else if (this._bodyArrayBuffer) {
6737                 return Promise.resolve(new Blob([this._bodyArrayBuffer]));
6738               } else if (this._bodyFormData) {
6739                 throw new Error('could not read FormData body as blob');
6740               } else {
6741                 return Promise.resolve(new Blob([this._bodyText]));
6742               }
6743             };
6744
6745             this.arrayBuffer = function () {
6746               if (this._bodyArrayBuffer) {
6747                 var isConsumed = consumed(this);
6748
6749                 if (isConsumed) {
6750                   return isConsumed;
6751                 }
6752
6753                 if (ArrayBuffer.isView(this._bodyArrayBuffer)) {
6754                   return Promise.resolve(this._bodyArrayBuffer.buffer.slice(this._bodyArrayBuffer.byteOffset, this._bodyArrayBuffer.byteOffset + this._bodyArrayBuffer.byteLength));
6755                 } else {
6756                   return Promise.resolve(this._bodyArrayBuffer);
6757                 }
6758               } else {
6759                 return this.blob().then(readBlobAsArrayBuffer);
6760               }
6761             };
6762           }
6763
6764           this.text = function () {
6765             var rejected = consumed(this);
6766
6767             if (rejected) {
6768               return rejected;
6769             }
6770
6771             if (this._bodyBlob) {
6772               return readBlobAsText(this._bodyBlob);
6773             } else if (this._bodyArrayBuffer) {
6774               return Promise.resolve(readArrayBufferAsText(this._bodyArrayBuffer));
6775             } else if (this._bodyFormData) {
6776               throw new Error('could not read FormData body as text');
6777             } else {
6778               return Promise.resolve(this._bodyText);
6779             }
6780           };
6781
6782           if (support.formData) {
6783             this.formData = function () {
6784               return this.text().then(decode);
6785             };
6786           }
6787
6788           this.json = function () {
6789             return this.text().then(JSON.parse);
6790           };
6791
6792           return this;
6793         } // HTTP methods whose capitalization should be normalized
6794
6795
6796         var methods = ['DELETE', 'GET', 'HEAD', 'OPTIONS', 'POST', 'PUT'];
6797
6798         function normalizeMethod(method) {
6799           var upcased = method.toUpperCase();
6800           return methods.indexOf(upcased) > -1 ? upcased : method;
6801         }
6802
6803         function Request(input, options) {
6804           if (!(this instanceof Request)) {
6805             throw new TypeError('Please use the "new" operator, this DOM object constructor cannot be called as a function.');
6806           }
6807
6808           options = options || {};
6809           var body = options.body;
6810
6811           if (input instanceof Request) {
6812             if (input.bodyUsed) {
6813               throw new TypeError('Already read');
6814             }
6815
6816             this.url = input.url;
6817             this.credentials = input.credentials;
6818
6819             if (!options.headers) {
6820               this.headers = new Headers(input.headers);
6821             }
6822
6823             this.method = input.method;
6824             this.mode = input.mode;
6825             this.signal = input.signal;
6826
6827             if (!body && input._bodyInit != null) {
6828               body = input._bodyInit;
6829               input.bodyUsed = true;
6830             }
6831           } else {
6832             this.url = String(input);
6833           }
6834
6835           this.credentials = options.credentials || this.credentials || 'same-origin';
6836
6837           if (options.headers || !this.headers) {
6838             this.headers = new Headers(options.headers);
6839           }
6840
6841           this.method = normalizeMethod(options.method || this.method || 'GET');
6842           this.mode = options.mode || this.mode || null;
6843           this.signal = options.signal || this.signal;
6844           this.referrer = null;
6845
6846           if ((this.method === 'GET' || this.method === 'HEAD') && body) {
6847             throw new TypeError('Body not allowed for GET or HEAD requests');
6848           }
6849
6850           this._initBody(body);
6851
6852           if (this.method === 'GET' || this.method === 'HEAD') {
6853             if (options.cache === 'no-store' || options.cache === 'no-cache') {
6854               // Search for a '_' parameter in the query string
6855               var reParamSearch = /([?&])_=[^&]*/;
6856
6857               if (reParamSearch.test(this.url)) {
6858                 // If it already exists then set the value with the current time
6859                 this.url = this.url.replace(reParamSearch, '$1_=' + new Date().getTime());
6860               } else {
6861                 // Otherwise add a new '_' parameter to the end with the current time
6862                 var reQueryString = /\?/;
6863                 this.url += (reQueryString.test(this.url) ? '&' : '?') + '_=' + new Date().getTime();
6864               }
6865             }
6866           }
6867         }
6868
6869         Request.prototype.clone = function () {
6870           return new Request(this, {
6871             body: this._bodyInit
6872           });
6873         };
6874
6875         function decode(body) {
6876           var form = new FormData();
6877           body.trim().split('&').forEach(function (bytes) {
6878             if (bytes) {
6879               var split = bytes.split('=');
6880               var name = split.shift().replace(/\+/g, ' ');
6881               var value = split.join('=').replace(/\+/g, ' ');
6882               form.append(decodeURIComponent(name), decodeURIComponent(value));
6883             }
6884           });
6885           return form;
6886         }
6887
6888         function parseHeaders(rawHeaders) {
6889           var headers = new Headers(); // Replace instances of \r\n and \n followed by at least one space or horizontal tab with a space
6890           // https://tools.ietf.org/html/rfc7230#section-3.2
6891
6892           var preProcessedHeaders = rawHeaders.replace(/\r?\n[\t ]+/g, ' '); // Avoiding split via regex to work around a common IE11 bug with the core-js 3.6.0 regex polyfill
6893           // https://github.com/github/fetch/issues/748
6894           // https://github.com/zloirock/core-js/issues/751
6895
6896           preProcessedHeaders.split('\r').map(function (header) {
6897             return header.indexOf('\n') === 0 ? header.substr(1, header.length) : header;
6898           }).forEach(function (line) {
6899             var parts = line.split(':');
6900             var key = parts.shift().trim();
6901
6902             if (key) {
6903               var value = parts.join(':').trim();
6904               headers.append(key, value);
6905             }
6906           });
6907           return headers;
6908         }
6909
6910         Body.call(Request.prototype);
6911         function Response(bodyInit, options) {
6912           if (!(this instanceof Response)) {
6913             throw new TypeError('Please use the "new" operator, this DOM object constructor cannot be called as a function.');
6914           }
6915
6916           if (!options) {
6917             options = {};
6918           }
6919
6920           this.type = 'default';
6921           this.status = options.status === undefined ? 200 : options.status;
6922           this.ok = this.status >= 200 && this.status < 300;
6923           this.statusText = options.statusText === undefined ? '' : '' + options.statusText;
6924           this.headers = new Headers(options.headers);
6925           this.url = options.url || '';
6926
6927           this._initBody(bodyInit);
6928         }
6929         Body.call(Response.prototype);
6930
6931         Response.prototype.clone = function () {
6932           return new Response(this._bodyInit, {
6933             status: this.status,
6934             statusText: this.statusText,
6935             headers: new Headers(this.headers),
6936             url: this.url
6937           });
6938         };
6939
6940         Response.error = function () {
6941           var response = new Response(null, {
6942             status: 0,
6943             statusText: ''
6944           });
6945           response.type = 'error';
6946           return response;
6947         };
6948
6949         var redirectStatuses = [301, 302, 303, 307, 308];
6950
6951         Response.redirect = function (url, status) {
6952           if (redirectStatuses.indexOf(status) === -1) {
6953             throw new RangeError('Invalid status code');
6954           }
6955
6956           return new Response(null, {
6957             status: status,
6958             headers: {
6959               location: url
6960             }
6961           });
6962         };
6963
6964         var DOMException$1 = global$1.DOMException;
6965
6966         try {
6967           new DOMException$1();
6968         } catch (err) {
6969           DOMException$1 = function DOMException(message, name) {
6970             this.message = message;
6971             this.name = name;
6972             var error = Error(message);
6973             this.stack = error.stack;
6974           };
6975
6976           DOMException$1.prototype = Object.create(Error.prototype);
6977           DOMException$1.prototype.constructor = DOMException$1;
6978         }
6979
6980         function fetch$1(input, init) {
6981           return new Promise(function (resolve, reject) {
6982             var request = new Request(input, init);
6983
6984             if (request.signal && request.signal.aborted) {
6985               return reject(new DOMException$1('Aborted', 'AbortError'));
6986             }
6987
6988             var xhr = new XMLHttpRequest();
6989
6990             function abortXhr() {
6991               xhr.abort();
6992             }
6993
6994             xhr.onload = function () {
6995               var options = {
6996                 status: xhr.status,
6997                 statusText: xhr.statusText,
6998                 headers: parseHeaders(xhr.getAllResponseHeaders() || '')
6999               };
7000               options.url = 'responseURL' in xhr ? xhr.responseURL : options.headers.get('X-Request-URL');
7001               var body = 'response' in xhr ? xhr.response : xhr.responseText;
7002               setTimeout(function () {
7003                 resolve(new Response(body, options));
7004               }, 0);
7005             };
7006
7007             xhr.onerror = function () {
7008               setTimeout(function () {
7009                 reject(new TypeError('Network request failed'));
7010               }, 0);
7011             };
7012
7013             xhr.ontimeout = function () {
7014               setTimeout(function () {
7015                 reject(new TypeError('Network request failed'));
7016               }, 0);
7017             };
7018
7019             xhr.onabort = function () {
7020               setTimeout(function () {
7021                 reject(new DOMException$1('Aborted', 'AbortError'));
7022               }, 0);
7023             };
7024
7025             function fixUrl(url) {
7026               try {
7027                 return url === '' && global$1.location.href ? global$1.location.href : url;
7028               } catch (e) {
7029                 return url;
7030               }
7031             }
7032
7033             xhr.open(request.method, fixUrl(request.url), true);
7034
7035             if (request.credentials === 'include') {
7036               xhr.withCredentials = true;
7037             } else if (request.credentials === 'omit') {
7038               xhr.withCredentials = false;
7039             }
7040
7041             if ('responseType' in xhr) {
7042               if (support.blob) {
7043                 xhr.responseType = 'blob';
7044               } else if (support.arrayBuffer && request.headers.get('Content-Type') && request.headers.get('Content-Type').indexOf('application/octet-stream') !== -1) {
7045                 xhr.responseType = 'arraybuffer';
7046               }
7047             }
7048
7049             if (init && _typeof(init.headers) === 'object' && !(init.headers instanceof Headers)) {
7050               Object.getOwnPropertyNames(init.headers).forEach(function (name) {
7051                 xhr.setRequestHeader(name, normalizeValue(init.headers[name]));
7052               });
7053             } else {
7054               request.headers.forEach(function (value, name) {
7055                 xhr.setRequestHeader(name, value);
7056               });
7057             }
7058
7059             if (request.signal) {
7060               request.signal.addEventListener('abort', abortXhr);
7061
7062               xhr.onreadystatechange = function () {
7063                 // DONE (success or failure)
7064                 if (xhr.readyState === 4) {
7065                   request.signal.removeEventListener('abort', abortXhr);
7066                 }
7067               };
7068             }
7069
7070             xhr.send(typeof request._bodyInit === 'undefined' ? null : request._bodyInit);
7071           });
7072         }
7073         fetch$1.polyfill = true;
7074
7075         if (!global$1.fetch) {
7076           global$1.fetch = fetch$1;
7077           global$1.Headers = Headers;
7078           global$1.Request = Request;
7079           global$1.Response = Response;
7080         }
7081
7082         // `Object.defineProperty` method
7083         // https://tc39.es/ecma262/#sec-object.defineproperty
7084         _export({ target: 'Object', stat: true, forced: !descriptors, sham: !descriptors }, {
7085           defineProperty: objectDefineProperty.f
7086         });
7087
7088         // `Object.setPrototypeOf` method
7089         // https://tc39.es/ecma262/#sec-object.setprototypeof
7090         _export({ target: 'Object', stat: true }, {
7091           setPrototypeOf: objectSetPrototypeOf
7092         });
7093
7094         var FAILS_ON_PRIMITIVES$3 = fails(function () { objectGetPrototypeOf(1); });
7095
7096         // `Object.getPrototypeOf` method
7097         // https://tc39.es/ecma262/#sec-object.getprototypeof
7098         _export({ target: 'Object', stat: true, forced: FAILS_ON_PRIMITIVES$3, sham: !correctPrototypeGetter }, {
7099           getPrototypeOf: function getPrototypeOf(it) {
7100             return objectGetPrototypeOf(toObject(it));
7101           }
7102         });
7103
7104         var slice$2 = [].slice;
7105         var factories = {};
7106
7107         var construct = function (C, argsLength, args) {
7108           if (!(argsLength in factories)) {
7109             for (var list = [], i = 0; i < argsLength; i++) list[i] = 'a[' + i + ']';
7110             // eslint-disable-next-line no-new-func -- we have no proper alternatives, IE8- only
7111             factories[argsLength] = Function('C,a', 'return new C(' + list.join(',') + ')');
7112           } return factories[argsLength](C, args);
7113         };
7114
7115         // `Function.prototype.bind` method implementation
7116         // https://tc39.es/ecma262/#sec-function.prototype.bind
7117         var functionBind = Function.bind || function bind(that /* , ...args */) {
7118           var fn = aFunction(this);
7119           var partArgs = slice$2.call(arguments, 1);
7120           var boundFunction = function bound(/* args... */) {
7121             var args = partArgs.concat(slice$2.call(arguments));
7122             return this instanceof boundFunction ? construct(fn, args.length, args) : fn.apply(that, args);
7123           };
7124           if (isObject$4(fn.prototype)) boundFunction.prototype = fn.prototype;
7125           return boundFunction;
7126         };
7127
7128         var nativeConstruct = getBuiltIn('Reflect', 'construct');
7129
7130         // `Reflect.construct` method
7131         // https://tc39.es/ecma262/#sec-reflect.construct
7132         // MS Edge supports only 2 arguments and argumentsList argument is optional
7133         // FF Nightly sets third argument as `new.target`, but does not create `this` from it
7134         var NEW_TARGET_BUG = fails(function () {
7135           function F() { /* empty */ }
7136           return !(nativeConstruct(function () { /* empty */ }, [], F) instanceof F);
7137         });
7138         var ARGS_BUG = !fails(function () {
7139           nativeConstruct(function () { /* empty */ });
7140         });
7141         var FORCED$a = NEW_TARGET_BUG || ARGS_BUG;
7142
7143         _export({ target: 'Reflect', stat: true, forced: FORCED$a, sham: FORCED$a }, {
7144           construct: function construct(Target, args /* , newTarget */) {
7145             aFunction(Target);
7146             anObject(args);
7147             var newTarget = arguments.length < 3 ? Target : aFunction(arguments[2]);
7148             if (ARGS_BUG && !NEW_TARGET_BUG) return nativeConstruct(Target, args, newTarget);
7149             if (Target == newTarget) {
7150               // w/o altered newTarget, optimization for 0-4 arguments
7151               switch (args.length) {
7152                 case 0: return new Target();
7153                 case 1: return new Target(args[0]);
7154                 case 2: return new Target(args[0], args[1]);
7155                 case 3: return new Target(args[0], args[1], args[2]);
7156                 case 4: return new Target(args[0], args[1], args[2], args[3]);
7157               }
7158               // w/o altered newTarget, lot of arguments case
7159               var $args = [null];
7160               $args.push.apply($args, args);
7161               return new (functionBind.apply(Target, $args))();
7162             }
7163             // with altered newTarget, not support built-in constructors
7164             var proto = newTarget.prototype;
7165             var instance = objectCreate(isObject$4(proto) ? proto : Object.prototype);
7166             var result = Function.apply.call(Target, instance, args);
7167             return isObject$4(result) ? result : instance;
7168           }
7169         });
7170
7171         // `Reflect.get` method
7172         // https://tc39.es/ecma262/#sec-reflect.get
7173         function get$3(target, propertyKey /* , receiver */) {
7174           var receiver = arguments.length < 3 ? target : arguments[2];
7175           var descriptor, prototype;
7176           if (anObject(target) === receiver) return target[propertyKey];
7177           if (descriptor = objectGetOwnPropertyDescriptor.f(target, propertyKey)) return has$1(descriptor, 'value')
7178             ? descriptor.value
7179             : descriptor.get === undefined
7180               ? undefined
7181               : descriptor.get.call(receiver);
7182           if (isObject$4(prototype = objectGetPrototypeOf(target))) return get$3(prototype, propertyKey, receiver);
7183         }
7184
7185         _export({ target: 'Reflect', stat: true }, {
7186           get: get$3
7187         });
7188
7189         var nativeGetOwnPropertyDescriptor = objectGetOwnPropertyDescriptor.f;
7190
7191
7192         var FAILS_ON_PRIMITIVES$2 = fails(function () { nativeGetOwnPropertyDescriptor(1); });
7193         var FORCED$9 = !descriptors || FAILS_ON_PRIMITIVES$2;
7194
7195         // `Object.getOwnPropertyDescriptor` method
7196         // https://tc39.es/ecma262/#sec-object.getownpropertydescriptor
7197         _export({ target: 'Object', stat: true, forced: FORCED$9, sham: !descriptors }, {
7198           getOwnPropertyDescriptor: function getOwnPropertyDescriptor(it, key) {
7199             return nativeGetOwnPropertyDescriptor(toIndexedObject(it), key);
7200           }
7201         });
7202
7203         var HAS_SPECIES_SUPPORT$1 = arrayMethodHasSpeciesSupport('splice');
7204
7205         var max$1 = Math.max;
7206         var min$3 = Math.min;
7207         var MAX_SAFE_INTEGER$1 = 0x1FFFFFFFFFFFFF;
7208         var MAXIMUM_ALLOWED_LENGTH_EXCEEDED = 'Maximum allowed length exceeded';
7209
7210         // `Array.prototype.splice` method
7211         // https://tc39.es/ecma262/#sec-array.prototype.splice
7212         // with adding support of @@species
7213         _export({ target: 'Array', proto: true, forced: !HAS_SPECIES_SUPPORT$1 }, {
7214           splice: function splice(start, deleteCount /* , ...items */) {
7215             var O = toObject(this);
7216             var len = toLength(O.length);
7217             var actualStart = toAbsoluteIndex(start, len);
7218             var argumentsLength = arguments.length;
7219             var insertCount, actualDeleteCount, A, k, from, to;
7220             if (argumentsLength === 0) {
7221               insertCount = actualDeleteCount = 0;
7222             } else if (argumentsLength === 1) {
7223               insertCount = 0;
7224               actualDeleteCount = len - actualStart;
7225             } else {
7226               insertCount = argumentsLength - 2;
7227               actualDeleteCount = min$3(max$1(toInteger(deleteCount), 0), len - actualStart);
7228             }
7229             if (len + insertCount - actualDeleteCount > MAX_SAFE_INTEGER$1) {
7230               throw TypeError(MAXIMUM_ALLOWED_LENGTH_EXCEEDED);
7231             }
7232             A = arraySpeciesCreate(O, actualDeleteCount);
7233             for (k = 0; k < actualDeleteCount; k++) {
7234               from = actualStart + k;
7235               if (from in O) createProperty(A, k, O[from]);
7236             }
7237             A.length = actualDeleteCount;
7238             if (insertCount < actualDeleteCount) {
7239               for (k = actualStart; k < len - actualDeleteCount; k++) {
7240                 from = k + actualDeleteCount;
7241                 to = k + insertCount;
7242                 if (from in O) O[to] = O[from];
7243                 else delete O[to];
7244               }
7245               for (k = len; k > len - actualDeleteCount + insertCount; k--) delete O[k - 1];
7246             } else if (insertCount > actualDeleteCount) {
7247               for (k = len - actualDeleteCount; k > actualStart; k--) {
7248                 from = k + actualDeleteCount - 1;
7249                 to = k + insertCount - 1;
7250                 if (from in O) O[to] = O[from];
7251                 else delete O[to];
7252               }
7253             }
7254             for (k = 0; k < insertCount; k++) {
7255               O[k + actualStart] = arguments[k + 2];
7256             }
7257             O.length = len - actualDeleteCount + insertCount;
7258             return A;
7259           }
7260         });
7261
7262         // `Symbol.toStringTag` well-known symbol
7263         // https://tc39.es/ecma262/#sec-symbol.tostringtag
7264         defineWellKnownSymbol('toStringTag');
7265
7266         // JSON[@@toStringTag] property
7267         // https://tc39.es/ecma262/#sec-json-@@tostringtag
7268         setToStringTag(global$2.JSON, 'JSON', true);
7269
7270         // Math[@@toStringTag] property
7271         // https://tc39.es/ecma262/#sec-math-@@tostringtag
7272         setToStringTag(Math, 'Math', true);
7273
7274         (function (factory) {
7275           factory();
7276         })(function () {
7277
7278           function _classCallCheck(instance, Constructor) {
7279             if (!(instance instanceof Constructor)) {
7280               throw new TypeError("Cannot call a class as a function");
7281             }
7282           }
7283
7284           function _defineProperties(target, props) {
7285             for (var i = 0; i < props.length; i++) {
7286               var descriptor = props[i];
7287               descriptor.enumerable = descriptor.enumerable || false;
7288               descriptor.configurable = true;
7289               if ("value" in descriptor) descriptor.writable = true;
7290               Object.defineProperty(target, descriptor.key, descriptor);
7291             }
7292           }
7293
7294           function _createClass(Constructor, protoProps, staticProps) {
7295             if (protoProps) _defineProperties(Constructor.prototype, protoProps);
7296             if (staticProps) _defineProperties(Constructor, staticProps);
7297             return Constructor;
7298           }
7299
7300           function _inherits(subClass, superClass) {
7301             if (typeof superClass !== "function" && superClass !== null) {
7302               throw new TypeError("Super expression must either be null or a function");
7303             }
7304
7305             subClass.prototype = Object.create(superClass && superClass.prototype, {
7306               constructor: {
7307                 value: subClass,
7308                 writable: true,
7309                 configurable: true
7310               }
7311             });
7312             if (superClass) _setPrototypeOf(subClass, superClass);
7313           }
7314
7315           function _getPrototypeOf(o) {
7316             _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) {
7317               return o.__proto__ || Object.getPrototypeOf(o);
7318             };
7319             return _getPrototypeOf(o);
7320           }
7321
7322           function _setPrototypeOf(o, p) {
7323             _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) {
7324               o.__proto__ = p;
7325               return o;
7326             };
7327
7328             return _setPrototypeOf(o, p);
7329           }
7330
7331           function _isNativeReflectConstruct() {
7332             if (typeof Reflect === "undefined" || !Reflect.construct) return false;
7333             if (Reflect.construct.sham) return false;
7334             if (typeof Proxy === "function") return true;
7335
7336             try {
7337               Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {}));
7338               return true;
7339             } catch (e) {
7340               return false;
7341             }
7342           }
7343
7344           function _assertThisInitialized(self) {
7345             if (self === void 0) {
7346               throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
7347             }
7348
7349             return self;
7350           }
7351
7352           function _possibleConstructorReturn(self, call) {
7353             if (call && (_typeof(call) === "object" || typeof call === "function")) {
7354               return call;
7355             }
7356
7357             return _assertThisInitialized(self);
7358           }
7359
7360           function _createSuper(Derived) {
7361             var hasNativeReflectConstruct = _isNativeReflectConstruct();
7362
7363             return function _createSuperInternal() {
7364               var Super = _getPrototypeOf(Derived),
7365                   result;
7366
7367               if (hasNativeReflectConstruct) {
7368                 var NewTarget = _getPrototypeOf(this).constructor;
7369
7370                 result = Reflect.construct(Super, arguments, NewTarget);
7371               } else {
7372                 result = Super.apply(this, arguments);
7373               }
7374
7375               return _possibleConstructorReturn(this, result);
7376             };
7377           }
7378
7379           function _superPropBase(object, property) {
7380             while (!Object.prototype.hasOwnProperty.call(object, property)) {
7381               object = _getPrototypeOf(object);
7382               if (object === null) break;
7383             }
7384
7385             return object;
7386           }
7387
7388           function _get(target, property, receiver) {
7389             if (typeof Reflect !== "undefined" && Reflect.get) {
7390               _get = Reflect.get;
7391             } else {
7392               _get = function _get(target, property, receiver) {
7393                 var base = _superPropBase(target, property);
7394
7395                 if (!base) return;
7396                 var desc = Object.getOwnPropertyDescriptor(base, property);
7397
7398                 if (desc.get) {
7399                   return desc.get.call(receiver);
7400                 }
7401
7402                 return desc.value;
7403               };
7404             }
7405
7406             return _get(target, property, receiver || target);
7407           }
7408
7409           var Emitter = /*#__PURE__*/function () {
7410             function Emitter() {
7411               _classCallCheck(this, Emitter);
7412
7413               Object.defineProperty(this, 'listeners', {
7414                 value: {},
7415                 writable: true,
7416                 configurable: true
7417               });
7418             }
7419
7420             _createClass(Emitter, [{
7421               key: "addEventListener",
7422               value: function addEventListener(type, callback, options) {
7423                 if (!(type in this.listeners)) {
7424                   this.listeners[type] = [];
7425                 }
7426
7427                 this.listeners[type].push({
7428                   callback: callback,
7429                   options: options
7430                 });
7431               }
7432             }, {
7433               key: "removeEventListener",
7434               value: function removeEventListener(type, callback) {
7435                 if (!(type in this.listeners)) {
7436                   return;
7437                 }
7438
7439                 var stack = this.listeners[type];
7440
7441                 for (var i = 0, l = stack.length; i < l; i++) {
7442                   if (stack[i].callback === callback) {
7443                     stack.splice(i, 1);
7444                     return;
7445                   }
7446                 }
7447               }
7448             }, {
7449               key: "dispatchEvent",
7450               value: function dispatchEvent(event) {
7451                 if (!(event.type in this.listeners)) {
7452                   return;
7453                 }
7454
7455                 var stack = this.listeners[event.type];
7456                 var stackToCall = stack.slice();
7457
7458                 for (var i = 0, l = stackToCall.length; i < l; i++) {
7459                   var listener = stackToCall[i];
7460
7461                   try {
7462                     listener.callback.call(this, event);
7463                   } catch (e) {
7464                     Promise.resolve().then(function () {
7465                       throw e;
7466                     });
7467                   }
7468
7469                   if (listener.options && listener.options.once) {
7470                     this.removeEventListener(event.type, listener.callback);
7471                   }
7472                 }
7473
7474                 return !event.defaultPrevented;
7475               }
7476             }]);
7477
7478             return Emitter;
7479           }();
7480
7481           var AbortSignal = /*#__PURE__*/function (_Emitter) {
7482             _inherits(AbortSignal, _Emitter);
7483
7484             var _super = _createSuper(AbortSignal);
7485
7486             function AbortSignal() {
7487               var _this;
7488
7489               _classCallCheck(this, AbortSignal);
7490
7491               _this = _super.call(this); // Some versions of babel does not transpile super() correctly for IE <= 10, if the parent
7492               // constructor has failed to run, then "this.listeners" will still be undefined and then we call
7493               // the parent constructor directly instead as a workaround. For general details, see babel bug:
7494               // https://github.com/babel/babel/issues/3041
7495               // This hack was added as a fix for the issue described here:
7496               // https://github.com/Financial-Times/polyfill-library/pull/59#issuecomment-477558042
7497
7498               if (!_this.listeners) {
7499                 Emitter.call(_assertThisInitialized(_this));
7500               } // Compared to assignment, Object.defineProperty makes properties non-enumerable by default and
7501               // we want Object.keys(new AbortController().signal) to be [] for compat with the native impl
7502
7503
7504               Object.defineProperty(_assertThisInitialized(_this), 'aborted', {
7505                 value: false,
7506                 writable: true,
7507                 configurable: true
7508               });
7509               Object.defineProperty(_assertThisInitialized(_this), 'onabort', {
7510                 value: null,
7511                 writable: true,
7512                 configurable: true
7513               });
7514               return _this;
7515             }
7516
7517             _createClass(AbortSignal, [{
7518               key: "toString",
7519               value: function toString() {
7520                 return '[object AbortSignal]';
7521               }
7522             }, {
7523               key: "dispatchEvent",
7524               value: function dispatchEvent(event) {
7525                 if (event.type === 'abort') {
7526                   this.aborted = true;
7527
7528                   if (typeof this.onabort === 'function') {
7529                     this.onabort.call(this, event);
7530                   }
7531                 }
7532
7533                 _get(_getPrototypeOf(AbortSignal.prototype), "dispatchEvent", this).call(this, event);
7534               }
7535             }]);
7536
7537             return AbortSignal;
7538           }(Emitter);
7539
7540           var AbortController = /*#__PURE__*/function () {
7541             function AbortController() {
7542               _classCallCheck(this, AbortController); // Compared to assignment, Object.defineProperty makes properties non-enumerable by default and
7543               // we want Object.keys(new AbortController()) to be [] for compat with the native impl
7544
7545
7546               Object.defineProperty(this, 'signal', {
7547                 value: new AbortSignal(),
7548                 writable: true,
7549                 configurable: true
7550               });
7551             }
7552
7553             _createClass(AbortController, [{
7554               key: "abort",
7555               value: function abort() {
7556                 var event;
7557
7558                 try {
7559                   event = new Event('abort');
7560                 } catch (e) {
7561                   if (typeof document !== 'undefined') {
7562                     if (!document.createEvent) {
7563                       // For Internet Explorer 8:
7564                       event = document.createEventObject();
7565                       event.type = 'abort';
7566                     } else {
7567                       // For Internet Explorer 11:
7568                       event = document.createEvent('Event');
7569                       event.initEvent('abort', false, false);
7570                     }
7571                   } else {
7572                     // Fallback where document isn't available:
7573                     event = {
7574                       type: 'abort',
7575                       bubbles: false,
7576                       cancelable: false
7577                     };
7578                   }
7579                 }
7580
7581                 this.signal.dispatchEvent(event);
7582               }
7583             }, {
7584               key: "toString",
7585               value: function toString() {
7586                 return '[object AbortController]';
7587               }
7588             }]);
7589
7590             return AbortController;
7591           }();
7592
7593           if (typeof Symbol !== 'undefined' && Symbol.toStringTag) {
7594             // These are necessary to make sure that we get correct output for:
7595             // Object.prototype.toString.call(new AbortController())
7596             AbortController.prototype[Symbol.toStringTag] = 'AbortController';
7597             AbortSignal.prototype[Symbol.toStringTag] = 'AbortSignal';
7598           }
7599
7600           function polyfillNeeded(self) {
7601             if (self.__FORCE_INSTALL_ABORTCONTROLLER_POLYFILL) {
7602               console.log('__FORCE_INSTALL_ABORTCONTROLLER_POLYFILL=true is set, will force install polyfill');
7603               return true;
7604             } // Note that the "unfetch" minimal fetch polyfill defines fetch() without
7605             // defining window.Request, and this polyfill need to work on top of unfetch
7606             // so the below feature detection needs the !self.AbortController part.
7607             // The Request.prototype check is also needed because Safari versions 11.1.2
7608             // up to and including 12.1.x has a window.AbortController present but still
7609             // does NOT correctly implement abortable fetch:
7610             // https://bugs.webkit.org/show_bug.cgi?id=174980#c2
7611
7612
7613             return typeof self.Request === 'function' && !self.Request.prototype.hasOwnProperty('signal') || !self.AbortController;
7614           }
7615           /**
7616            * Note: the "fetch.Request" default value is available for fetch imported from
7617            * the "node-fetch" package and not in browsers. This is OK since browsers
7618            * will be importing umd-polyfill.js from that path "self" is passed the
7619            * decorator so the default value will not be used (because browsers that define
7620            * fetch also has Request). One quirky setup where self.fetch exists but
7621            * self.Request does not is when the "unfetch" minimal fetch polyfill is used
7622            * on top of IE11; for this case the browser will try to use the fetch.Request
7623            * default value which in turn will be undefined but then then "if (Request)"
7624            * will ensure that you get a patched fetch but still no Request (as expected).
7625            * @param {fetch, Request = fetch.Request}
7626            * @returns {fetch: abortableFetch, Request: AbortableRequest}
7627            */
7628
7629
7630           function abortableFetchDecorator(patchTargets) {
7631             if ('function' === typeof patchTargets) {
7632               patchTargets = {
7633                 fetch: patchTargets
7634               };
7635             }
7636
7637             var _patchTargets = patchTargets,
7638                 fetch = _patchTargets.fetch,
7639                 _patchTargets$Request = _patchTargets.Request,
7640                 NativeRequest = _patchTargets$Request === void 0 ? fetch.Request : _patchTargets$Request,
7641                 NativeAbortController = _patchTargets.AbortController,
7642                 _patchTargets$__FORCE = _patchTargets.__FORCE_INSTALL_ABORTCONTROLLER_POLYFILL,
7643                 __FORCE_INSTALL_ABORTCONTROLLER_POLYFILL = _patchTargets$__FORCE === void 0 ? false : _patchTargets$__FORCE;
7644
7645             if (!polyfillNeeded({
7646               fetch: fetch,
7647               Request: NativeRequest,
7648               AbortController: NativeAbortController,
7649               __FORCE_INSTALL_ABORTCONTROLLER_POLYFILL: __FORCE_INSTALL_ABORTCONTROLLER_POLYFILL
7650             })) {
7651               return {
7652                 fetch: fetch,
7653                 Request: Request
7654               };
7655             }
7656
7657             var Request = NativeRequest; // Note that the "unfetch" minimal fetch polyfill defines fetch() without
7658             // defining window.Request, and this polyfill need to work on top of unfetch
7659             // hence we only patch it if it's available. Also we don't patch it if signal
7660             // is already available on the Request prototype because in this case support
7661             // is present and the patching below can cause a crash since it assigns to
7662             // request.signal which is technically a read-only property. This latter error
7663             // happens when you run the main5.js node-fetch example in the repo
7664             // "abortcontroller-polyfill-examples". The exact error is:
7665             //   request.signal = init.signal;
7666             //   ^
7667             // TypeError: Cannot set property signal of #<Request> which has only a getter
7668
7669             if (Request && !Request.prototype.hasOwnProperty('signal') || __FORCE_INSTALL_ABORTCONTROLLER_POLYFILL) {
7670               Request = function Request(input, init) {
7671                 var signal;
7672
7673                 if (init && init.signal) {
7674                   signal = init.signal; // Never pass init.signal to the native Request implementation when the polyfill has
7675                   // been installed because if we're running on top of a browser with a
7676                   // working native AbortController (i.e. the polyfill was installed due to
7677                   // __FORCE_INSTALL_ABORTCONTROLLER_POLYFILL being set), then passing our
7678                   // fake AbortSignal to the native fetch will trigger:
7679                   // TypeError: Failed to construct 'Request': member signal is not of type AbortSignal.
7680
7681                   delete init.signal;
7682                 }
7683
7684                 var request = new NativeRequest(input, init);
7685
7686                 if (signal) {
7687                   Object.defineProperty(request, 'signal', {
7688                     writable: false,
7689                     enumerable: false,
7690                     configurable: true,
7691                     value: signal
7692                   });
7693                 }
7694
7695                 return request;
7696               };
7697
7698               Request.prototype = NativeRequest.prototype;
7699             }
7700
7701             var realFetch = fetch;
7702
7703             var abortableFetch = function abortableFetch(input, init) {
7704               var signal = Request && Request.prototype.isPrototypeOf(input) ? input.signal : init ? init.signal : undefined;
7705
7706               if (signal) {
7707                 var abortError;
7708
7709                 try {
7710                   abortError = new DOMException('Aborted', 'AbortError');
7711                 } catch (err) {
7712                   // IE 11 does not support calling the DOMException constructor, use a
7713                   // regular error object on it instead.
7714                   abortError = new Error('Aborted');
7715                   abortError.name = 'AbortError';
7716                 } // Return early if already aborted, thus avoiding making an HTTP request
7717
7718
7719                 if (signal.aborted) {
7720                   return Promise.reject(abortError);
7721                 } // Turn an event into a promise, reject it once `abort` is dispatched
7722
7723
7724                 var cancellation = new Promise(function (_, reject) {
7725                   signal.addEventListener('abort', function () {
7726                     return reject(abortError);
7727                   }, {
7728                     once: true
7729                   });
7730                 });
7731
7732                 if (init && init.signal) {
7733                   // Never pass .signal to the native implementation when the polyfill has
7734                   // been installed because if we're running on top of a browser with a
7735                   // working native AbortController (i.e. the polyfill was installed due to
7736                   // __FORCE_INSTALL_ABORTCONTROLLER_POLYFILL being set), then passing our
7737                   // fake AbortSignal to the native fetch will trigger:
7738                   // TypeError: Failed to execute 'fetch' on 'Window': member signal is not of type AbortSignal.
7739                   delete init.signal;
7740                 } // Return the fastest promise (don't need to wait for request to finish)
7741
7742
7743                 return Promise.race([cancellation, realFetch(input, init)]);
7744               }
7745
7746               return realFetch(input, init);
7747             };
7748
7749             return {
7750               fetch: abortableFetch,
7751               Request: Request
7752             };
7753           }
7754
7755           (function (self) {
7756             if (!polyfillNeeded(self)) {
7757               return;
7758             }
7759
7760             if (!self.fetch) {
7761               console.warn('fetch() is not available, cannot install abortcontroller-polyfill');
7762               return;
7763             }
7764
7765             var _abortableFetch = abortableFetchDecorator(self),
7766                 fetch = _abortableFetch.fetch,
7767                 Request = _abortableFetch.Request;
7768
7769             self.fetch = fetch;
7770             self.Request = Request;
7771             Object.defineProperty(self, 'AbortController', {
7772               writable: true,
7773               enumerable: false,
7774               configurable: true,
7775               value: AbortController
7776             });
7777             Object.defineProperty(self, 'AbortSignal', {
7778               writable: true,
7779               enumerable: false,
7780               configurable: true,
7781               value: AbortSignal
7782             });
7783           })(typeof self !== 'undefined' ? self : commonjsGlobal);
7784         });
7785
7786         function actionAddEntity(way) {
7787           return function (graph) {
7788             return graph.replace(way);
7789           };
7790         }
7791
7792         var IS_CONCAT_SPREADABLE = wellKnownSymbol('isConcatSpreadable');
7793         var MAX_SAFE_INTEGER = 0x1FFFFFFFFFFFFF;
7794         var MAXIMUM_ALLOWED_INDEX_EXCEEDED = 'Maximum allowed index exceeded';
7795
7796         // We can't use this feature detection in V8 since it causes
7797         // deoptimization and serious performance degradation
7798         // https://github.com/zloirock/core-js/issues/679
7799         var IS_CONCAT_SPREADABLE_SUPPORT = engineV8Version >= 51 || !fails(function () {
7800           var array = [];
7801           array[IS_CONCAT_SPREADABLE] = false;
7802           return array.concat()[0] !== array;
7803         });
7804
7805         var SPECIES_SUPPORT = arrayMethodHasSpeciesSupport('concat');
7806
7807         var isConcatSpreadable = function (O) {
7808           if (!isObject$4(O)) return false;
7809           var spreadable = O[IS_CONCAT_SPREADABLE];
7810           return spreadable !== undefined ? !!spreadable : isArray(O);
7811         };
7812
7813         var FORCED$8 = !IS_CONCAT_SPREADABLE_SUPPORT || !SPECIES_SUPPORT;
7814
7815         // `Array.prototype.concat` method
7816         // https://tc39.es/ecma262/#sec-array.prototype.concat
7817         // with adding support of @@isConcatSpreadable and @@species
7818         _export({ target: 'Array', proto: true, forced: FORCED$8 }, {
7819           // eslint-disable-next-line no-unused-vars -- required for `.length`
7820           concat: function concat(arg) {
7821             var O = toObject(this);
7822             var A = arraySpeciesCreate(O, 0);
7823             var n = 0;
7824             var i, k, length, len, E;
7825             for (i = -1, length = arguments.length; i < length; i++) {
7826               E = i === -1 ? O : arguments[i];
7827               if (isConcatSpreadable(E)) {
7828                 len = toLength(E.length);
7829                 if (n + len > MAX_SAFE_INTEGER) throw TypeError(MAXIMUM_ALLOWED_INDEX_EXCEEDED);
7830                 for (k = 0; k < len; k++, n++) if (k in E) createProperty(A, n, E[k]);
7831               } else {
7832                 if (n >= MAX_SAFE_INTEGER) throw TypeError(MAXIMUM_ALLOWED_INDEX_EXCEEDED);
7833                 createProperty(A, n++, E);
7834               }
7835             }
7836             A.length = n;
7837             return A;
7838           }
7839         });
7840
7841         // `Object.assign` method
7842         // https://tc39.es/ecma262/#sec-object.assign
7843         // eslint-disable-next-line es/no-object-assign -- required for testing
7844         _export({ target: 'Object', stat: true, forced: Object.assign !== objectAssign }, {
7845           assign: objectAssign
7846         });
7847
7848         var $filter = arrayIteration.filter;
7849
7850
7851         var HAS_SPECIES_SUPPORT = arrayMethodHasSpeciesSupport('filter');
7852
7853         // `Array.prototype.filter` method
7854         // https://tc39.es/ecma262/#sec-array.prototype.filter
7855         // with adding support of @@species
7856         _export({ target: 'Array', proto: true, forced: !HAS_SPECIES_SUPPORT }, {
7857           filter: function filter(callbackfn /* , thisArg */) {
7858             return $filter(this, callbackfn, arguments.length > 1 ? arguments[1] : undefined);
7859           }
7860         });
7861
7862         var FAILS_ON_PRIMITIVES$1 = fails(function () { objectKeys(1); });
7863
7864         // `Object.keys` method
7865         // https://tc39.es/ecma262/#sec-object.keys
7866         _export({ target: 'Object', stat: true, forced: FAILS_ON_PRIMITIVES$1 }, {
7867           keys: function keys(it) {
7868             return objectKeys(toObject(it));
7869           }
7870         });
7871
7872         var nativeReverse = [].reverse;
7873         var test$1 = [1, 2];
7874
7875         // `Array.prototype.reverse` method
7876         // https://tc39.es/ecma262/#sec-array.prototype.reverse
7877         // fix for Safari 12.0 bug
7878         // https://bugs.webkit.org/show_bug.cgi?id=188794
7879         _export({ target: 'Array', proto: true, forced: String(test$1) === String(test$1.reverse()) }, {
7880           reverse: function reverse() {
7881             // eslint-disable-next-line no-self-assign -- dirty hack
7882             if (isArray(this)) this.length = this.length;
7883             return nativeReverse.call(this);
7884           }
7885         });
7886
7887         var trim$4 = stringTrim.trim;
7888
7889
7890         var $parseFloat = global$2.parseFloat;
7891         var FORCED$7 = 1 / $parseFloat(whitespaces + '-0') !== -Infinity;
7892
7893         // `parseFloat` method
7894         // https://tc39.es/ecma262/#sec-parsefloat-string
7895         var numberParseFloat = FORCED$7 ? function parseFloat(string) {
7896           var trimmedString = trim$4(String(string));
7897           var result = $parseFloat(trimmedString);
7898           return result === 0 && trimmedString.charAt(0) == '-' ? -0 : result;
7899         } : $parseFloat;
7900
7901         // `parseFloat` method
7902         // https://tc39.es/ecma262/#sec-parsefloat-string
7903         _export({ global: true, forced: parseFloat != numberParseFloat }, {
7904           parseFloat: numberParseFloat
7905         });
7906
7907         /*
7908         Order the nodes of a way in reverse order and reverse any direction dependent tags
7909         other than `oneway`. (We assume that correcting a backwards oneway is the primary
7910         reason for reversing a way.)
7911
7912         In addition, numeric-valued `incline` tags are negated.
7913
7914         The JOSM implementation was used as a guide, but transformations that were of unclear benefit
7915         or adjusted tags that don't seem to be used in practice were omitted.
7916
7917         References:
7918             http://wiki.openstreetmap.org/wiki/Forward_%26_backward,_left_%26_right
7919             http://wiki.openstreetmap.org/wiki/Key:direction#Steps
7920             http://wiki.openstreetmap.org/wiki/Key:incline
7921             http://wiki.openstreetmap.org/wiki/Route#Members
7922             http://josm.openstreetmap.de/browser/josm/trunk/src/org/openstreetmap/josm/corrector/ReverseWayTagCorrector.java
7923             http://wiki.openstreetmap.org/wiki/Tag:highway%3Dstop
7924             http://wiki.openstreetmap.org/wiki/Key:traffic_sign#On_a_way_or_area
7925         */
7926         function actionReverse(entityID, options) {
7927           var ignoreKey = /^.*(_|:)?(description|name|note|website|ref|source|comment|watch|attribution)(_|:)?/;
7928           var numeric = /^([+\-]?)(?=[\d.])/;
7929           var directionKey = /direction$/;
7930           var turn_lanes = /^turn:lanes:?/;
7931           var keyReplacements = [[/:right$/, ':left'], [/:left$/, ':right'], [/:forward$/, ':backward'], [/:backward$/, ':forward'], [/:right:/, ':left:'], [/:left:/, ':right:'], [/:forward:/, ':backward:'], [/:backward:/, ':forward:']];
7932           var valueReplacements = {
7933             left: 'right',
7934             right: 'left',
7935             up: 'down',
7936             down: 'up',
7937             forward: 'backward',
7938             backward: 'forward',
7939             forwards: 'backward',
7940             backwards: 'forward'
7941           };
7942           var roleReplacements = {
7943             forward: 'backward',
7944             backward: 'forward',
7945             forwards: 'backward',
7946             backwards: 'forward'
7947           };
7948           var onewayReplacements = {
7949             yes: '-1',
7950             '1': '-1',
7951             '-1': 'yes'
7952           };
7953           var compassReplacements = {
7954             N: 'S',
7955             NNE: 'SSW',
7956             NE: 'SW',
7957             ENE: 'WSW',
7958             E: 'W',
7959             ESE: 'WNW',
7960             SE: 'NW',
7961             SSE: 'NNW',
7962             S: 'N',
7963             SSW: 'NNE',
7964             SW: 'NE',
7965             WSW: 'ENE',
7966             W: 'E',
7967             WNW: 'ESE',
7968             NW: 'SE',
7969             NNW: 'SSE'
7970           };
7971
7972           function reverseKey(key) {
7973             for (var i = 0; i < keyReplacements.length; ++i) {
7974               var replacement = keyReplacements[i];
7975
7976               if (replacement[0].test(key)) {
7977                 return key.replace(replacement[0], replacement[1]);
7978               }
7979             }
7980
7981             return key;
7982           }
7983
7984           function reverseValue(key, value, includeAbsolute) {
7985             if (ignoreKey.test(key)) return value; // Turn lanes are left/right to key (not way) direction - #5674
7986
7987             if (turn_lanes.test(key)) {
7988               return value;
7989             } else if (key === 'incline' && numeric.test(value)) {
7990               return value.replace(numeric, function (_, sign) {
7991                 return sign === '-' ? '' : '-';
7992               });
7993             } else if (options && options.reverseOneway && key === 'oneway') {
7994               return onewayReplacements[value] || value;
7995             } else if (includeAbsolute && directionKey.test(key)) {
7996               if (compassReplacements[value]) return compassReplacements[value];
7997               var degrees = parseFloat(value);
7998
7999               if (typeof degrees === 'number' && !isNaN(degrees)) {
8000                 if (degrees < 180) {
8001                   degrees += 180;
8002                 } else {
8003                   degrees -= 180;
8004                 }
8005
8006                 return degrees.toString();
8007               }
8008             }
8009
8010             return valueReplacements[value] || value;
8011           } // Reverse the direction of tags attached to the nodes - #3076
8012
8013
8014           function reverseNodeTags(graph, nodeIDs) {
8015             for (var i = 0; i < nodeIDs.length; i++) {
8016               var node = graph.hasEntity(nodeIDs[i]);
8017               if (!node || !Object.keys(node.tags).length) continue;
8018               var tags = {};
8019
8020               for (var key in node.tags) {
8021                 tags[reverseKey(key)] = reverseValue(key, node.tags[key], node.id === entityID);
8022               }
8023
8024               graph = graph.replace(node.update({
8025                 tags: tags
8026               }));
8027             }
8028
8029             return graph;
8030           }
8031
8032           function reverseWay(graph, way) {
8033             var nodes = way.nodes.slice().reverse();
8034             var tags = {};
8035             var role;
8036
8037             for (var key in way.tags) {
8038               tags[reverseKey(key)] = reverseValue(key, way.tags[key]);
8039             }
8040
8041             graph.parentRelations(way).forEach(function (relation) {
8042               relation.members.forEach(function (member, index) {
8043                 if (member.id === way.id && (role = roleReplacements[member.role])) {
8044                   relation = relation.updateMember({
8045                     role: role
8046                   }, index);
8047                   graph = graph.replace(relation);
8048                 }
8049               });
8050             }); // Reverse any associated directions on nodes on the way and then replace
8051             // the way itself with the reversed node ids and updated way tags
8052
8053             return reverseNodeTags(graph, nodes).replace(way.update({
8054               nodes: nodes,
8055               tags: tags
8056             }));
8057           }
8058
8059           var action = function action(graph) {
8060             var entity = graph.entity(entityID);
8061
8062             if (entity.type === 'way') {
8063               return reverseWay(graph, entity);
8064             }
8065
8066             return reverseNodeTags(graph, [entityID]);
8067           };
8068
8069           action.disabled = function (graph) {
8070             var entity = graph.hasEntity(entityID);
8071             if (!entity || entity.type === 'way') return false;
8072
8073             for (var key in entity.tags) {
8074               var value = entity.tags[key];
8075
8076               if (reverseKey(key) !== key || reverseValue(key, value, true) !== value) {
8077                 return false;
8078               }
8079             }
8080
8081             return 'nondirectional_node';
8082           };
8083
8084           action.entityID = function () {
8085             return entityID;
8086           };
8087
8088           return action;
8089         }
8090
8091         function osmIsInterestingTag(key) {
8092           return key !== 'attribution' && key !== 'created_by' && key !== 'source' && key !== 'odbl' && key.indexOf('source:') !== 0 && key.indexOf('source_ref') !== 0 && // purposely exclude colon
8093           key.indexOf('tiger:') !== 0;
8094         }
8095         var osmAreaKeys = {};
8096         function osmSetAreaKeys(value) {
8097           osmAreaKeys = value;
8098         } // returns an object with the tag from `tags` that implies an area geometry, if any
8099
8100         function osmTagSuggestingArea(tags) {
8101           if (tags.area === 'yes') return {
8102             area: 'yes'
8103           };
8104           if (tags.area === 'no') return null; // `highway` and `railway` are typically linear features, but there
8105           // are a few exceptions that should be treated as areas, even in the
8106           // absence of a proper `area=yes` or `areaKeys` tag.. see #4194
8107
8108           var lineKeys = {
8109             highway: {
8110               rest_area: true,
8111               services: true
8112             },
8113             railway: {
8114               roundhouse: true,
8115               station: true,
8116               traverser: true,
8117               turntable: true,
8118               wash: true
8119             }
8120           };
8121           var returnTags = {};
8122
8123           for (var key in tags) {
8124             if (key in osmAreaKeys && !(tags[key] in osmAreaKeys[key])) {
8125               returnTags[key] = tags[key];
8126               return returnTags;
8127             }
8128
8129             if (key in lineKeys && tags[key] in lineKeys[key]) {
8130               returnTags[key] = tags[key];
8131               return returnTags;
8132             }
8133           }
8134
8135           return null;
8136         } // Tags that indicate a node can be a standalone point
8137         // e.g. { amenity: { bar: true, parking: true, ... } ... }
8138
8139         var osmPointTags = {};
8140         function osmSetPointTags(value) {
8141           osmPointTags = value;
8142         } // Tags that indicate a node can be part of a way
8143         // e.g. { amenity: { parking: true, ... }, highway: { stop: true ... } ... }
8144
8145         var osmVertexTags = {};
8146         function osmSetVertexTags(value) {
8147           osmVertexTags = value;
8148         }
8149         function osmNodeGeometriesForTags(nodeTags) {
8150           var geometries = {};
8151
8152           for (var key in nodeTags) {
8153             if (osmPointTags[key] && (osmPointTags[key]['*'] || osmPointTags[key][nodeTags[key]])) {
8154               geometries.point = true;
8155             }
8156
8157             if (osmVertexTags[key] && (osmVertexTags[key]['*'] || osmVertexTags[key][nodeTags[key]])) {
8158               geometries.vertex = true;
8159             } // break early if both are already supported
8160
8161
8162             if (geometries.point && geometries.vertex) break;
8163           }
8164
8165           return geometries;
8166         }
8167         var osmOneWayTags = {
8168           'aerialway': {
8169             'chair_lift': true,
8170             'drag_lift': true,
8171             'j-bar': true,
8172             'magic_carpet': true,
8173             'mixed_lift': true,
8174             'platter': true,
8175             'rope_tow': true,
8176             't-bar': true,
8177             'zip_line': true
8178           },
8179           'highway': {
8180             'motorway': true
8181           },
8182           'junction': {
8183             'circular': true,
8184             'roundabout': true
8185           },
8186           'man_made': {
8187             'goods_conveyor': true,
8188             'piste:halfpipe': true
8189           },
8190           'piste:type': {
8191             'downhill': true,
8192             'sled': true,
8193             'yes': true
8194           },
8195           'waterway': {
8196             'canal': true,
8197             'ditch': true,
8198             'drain': true,
8199             'fish_pass': true,
8200             'river': true,
8201             'stream': true,
8202             'tidal_channel': true
8203           }
8204         }; // solid and smooth surfaces akin to the assumed default road surface in OSM
8205
8206         var osmPavedTags = {
8207           'surface': {
8208             'paved': true,
8209             'asphalt': true,
8210             'concrete': true,
8211             'concrete:lanes': true,
8212             'concrete:plates': true
8213           },
8214           'tracktype': {
8215             'grade1': true
8216           }
8217         }; // solid, if somewhat uncommon surfaces with a high range of smoothness
8218
8219         var osmSemipavedTags = {
8220           'surface': {
8221             'cobblestone': true,
8222             'cobblestone:flattened': true,
8223             'unhewn_cobblestone': true,
8224             'sett': true,
8225             'paving_stones': true,
8226             'metal': true,
8227             'wood': true
8228           }
8229         };
8230         var osmRightSideIsInsideTags = {
8231           'natural': {
8232             'cliff': true,
8233             'coastline': 'coastline'
8234           },
8235           'barrier': {
8236             'retaining_wall': true,
8237             'kerb': true,
8238             'guard_rail': true,
8239             'city_wall': true
8240           },
8241           'man_made': {
8242             'embankment': true
8243           },
8244           'waterway': {
8245             'weir': true
8246           }
8247         }; // "highway" tag values for pedestrian or vehicle right-of-ways that make up the routable network
8248         // (does not include `raceway`)
8249
8250         var osmRoutableHighwayTagValues = {
8251           motorway: true,
8252           trunk: true,
8253           primary: true,
8254           secondary: true,
8255           tertiary: true,
8256           residential: true,
8257           motorway_link: true,
8258           trunk_link: true,
8259           primary_link: true,
8260           secondary_link: true,
8261           tertiary_link: true,
8262           unclassified: true,
8263           road: true,
8264           service: true,
8265           track: true,
8266           living_street: true,
8267           bus_guideway: true,
8268           path: true,
8269           footway: true,
8270           cycleway: true,
8271           bridleway: true,
8272           pedestrian: true,
8273           corridor: true,
8274           steps: true
8275         }; // "highway" tag values that generally do not allow motor vehicles
8276
8277         var osmPathHighwayTagValues = {
8278           path: true,
8279           footway: true,
8280           cycleway: true,
8281           bridleway: true,
8282           pedestrian: true,
8283           corridor: true,
8284           steps: true
8285         }; // "railway" tag values representing existing railroad tracks (purposely does not include 'abandoned')
8286
8287         var osmRailwayTrackTagValues = {
8288           rail: true,
8289           light_rail: true,
8290           tram: true,
8291           subway: true,
8292           monorail: true,
8293           funicular: true,
8294           miniature: true,
8295           narrow_gauge: true,
8296           disused: true,
8297           preserved: true
8298         }; // "waterway" tag values for line features representing water flow
8299
8300         var osmFlowingWaterwayTagValues = {
8301           canal: true,
8302           ditch: true,
8303           drain: true,
8304           fish_pass: true,
8305           river: true,
8306           stream: true,
8307           tidal_channel: true
8308         };
8309
8310         var trim$3 = stringTrim.trim;
8311
8312
8313         var $parseInt = global$2.parseInt;
8314         var hex$2 = /^[+-]?0[Xx]/;
8315         var FORCED$6 = $parseInt(whitespaces + '08') !== 8 || $parseInt(whitespaces + '0x16') !== 22;
8316
8317         // `parseInt` method
8318         // https://tc39.es/ecma262/#sec-parseint-string-radix
8319         var numberParseInt = FORCED$6 ? function parseInt(string, radix) {
8320           var S = trim$3(String(string));
8321           return $parseInt(S, (radix >>> 0) || (hex$2.test(S) ? 16 : 10));
8322         } : $parseInt;
8323
8324         // `parseInt` method
8325         // https://tc39.es/ecma262/#sec-parseint-string-radix
8326         _export({ global: true, forced: parseInt != numberParseInt }, {
8327           parseInt: numberParseInt
8328         });
8329
8330         var freezing = !fails(function () {
8331           // eslint-disable-next-line es/no-object-isextensible, es/no-object-preventextensions -- required for testing
8332           return Object.isExtensible(Object.preventExtensions({}));
8333         });
8334
8335         var internalMetadata = createCommonjsModule(function (module) {
8336         var defineProperty = objectDefineProperty.f;
8337
8338
8339
8340         var METADATA = uid('meta');
8341         var id = 0;
8342
8343         // eslint-disable-next-line es/no-object-isextensible -- safe
8344         var isExtensible = Object.isExtensible || function () {
8345           return true;
8346         };
8347
8348         var setMetadata = function (it) {
8349           defineProperty(it, METADATA, { value: {
8350             objectID: 'O' + ++id, // object ID
8351             weakData: {}          // weak collections IDs
8352           } });
8353         };
8354
8355         var fastKey = function (it, create) {
8356           // return a primitive with prefix
8357           if (!isObject$4(it)) return typeof it == 'symbol' ? it : (typeof it == 'string' ? 'S' : 'P') + it;
8358           if (!has$1(it, METADATA)) {
8359             // can't set metadata to uncaught frozen object
8360             if (!isExtensible(it)) return 'F';
8361             // not necessary to add metadata
8362             if (!create) return 'E';
8363             // add missing metadata
8364             setMetadata(it);
8365           // return object ID
8366           } return it[METADATA].objectID;
8367         };
8368
8369         var getWeakData = function (it, create) {
8370           if (!has$1(it, METADATA)) {
8371             // can't set metadata to uncaught frozen object
8372             if (!isExtensible(it)) return true;
8373             // not necessary to add metadata
8374             if (!create) return false;
8375             // add missing metadata
8376             setMetadata(it);
8377           // return the store of weak collections IDs
8378           } return it[METADATA].weakData;
8379         };
8380
8381         // add metadata on freeze-family methods calling
8382         var onFreeze = function (it) {
8383           if (freezing && meta.REQUIRED && isExtensible(it) && !has$1(it, METADATA)) setMetadata(it);
8384           return it;
8385         };
8386
8387         var meta = module.exports = {
8388           REQUIRED: false,
8389           fastKey: fastKey,
8390           getWeakData: getWeakData,
8391           onFreeze: onFreeze
8392         };
8393
8394         hiddenKeys$1[METADATA] = true;
8395         });
8396
8397         var collection = function (CONSTRUCTOR_NAME, wrapper, common) {
8398           var IS_MAP = CONSTRUCTOR_NAME.indexOf('Map') !== -1;
8399           var IS_WEAK = CONSTRUCTOR_NAME.indexOf('Weak') !== -1;
8400           var ADDER = IS_MAP ? 'set' : 'add';
8401           var NativeConstructor = global$2[CONSTRUCTOR_NAME];
8402           var NativePrototype = NativeConstructor && NativeConstructor.prototype;
8403           var Constructor = NativeConstructor;
8404           var exported = {};
8405
8406           var fixMethod = function (KEY) {
8407             var nativeMethod = NativePrototype[KEY];
8408             redefine(NativePrototype, KEY,
8409               KEY == 'add' ? function add(value) {
8410                 nativeMethod.call(this, value === 0 ? 0 : value);
8411                 return this;
8412               } : KEY == 'delete' ? function (key) {
8413                 return IS_WEAK && !isObject$4(key) ? false : nativeMethod.call(this, key === 0 ? 0 : key);
8414               } : KEY == 'get' ? function get(key) {
8415                 return IS_WEAK && !isObject$4(key) ? undefined : nativeMethod.call(this, key === 0 ? 0 : key);
8416               } : KEY == 'has' ? function has(key) {
8417                 return IS_WEAK && !isObject$4(key) ? false : nativeMethod.call(this, key === 0 ? 0 : key);
8418               } : function set(key, value) {
8419                 nativeMethod.call(this, key === 0 ? 0 : key, value);
8420                 return this;
8421               }
8422             );
8423           };
8424
8425           var REPLACE = isForced_1(
8426             CONSTRUCTOR_NAME,
8427             typeof NativeConstructor != 'function' || !(IS_WEAK || NativePrototype.forEach && !fails(function () {
8428               new NativeConstructor().entries().next();
8429             }))
8430           );
8431
8432           if (REPLACE) {
8433             // create collection constructor
8434             Constructor = common.getConstructor(wrapper, CONSTRUCTOR_NAME, IS_MAP, ADDER);
8435             internalMetadata.REQUIRED = true;
8436           } else if (isForced_1(CONSTRUCTOR_NAME, true)) {
8437             var instance = new Constructor();
8438             // early implementations not supports chaining
8439             var HASNT_CHAINING = instance[ADDER](IS_WEAK ? {} : -0, 1) != instance;
8440             // V8 ~ Chromium 40- weak-collections throws on primitives, but should return false
8441             var THROWS_ON_PRIMITIVES = fails(function () { instance.has(1); });
8442             // most early implementations doesn't supports iterables, most modern - not close it correctly
8443             // eslint-disable-next-line no-new -- required for testing
8444             var ACCEPT_ITERABLES = checkCorrectnessOfIteration(function (iterable) { new NativeConstructor(iterable); });
8445             // for early implementations -0 and +0 not the same
8446             var BUGGY_ZERO = !IS_WEAK && fails(function () {
8447               // V8 ~ Chromium 42- fails only with 5+ elements
8448               var $instance = new NativeConstructor();
8449               var index = 5;
8450               while (index--) $instance[ADDER](index, index);
8451               return !$instance.has(-0);
8452             });
8453
8454             if (!ACCEPT_ITERABLES) {
8455               Constructor = wrapper(function (dummy, iterable) {
8456                 anInstance(dummy, Constructor, CONSTRUCTOR_NAME);
8457                 var that = inheritIfRequired(new NativeConstructor(), dummy, Constructor);
8458                 if (iterable != undefined) iterate(iterable, that[ADDER], { that: that, AS_ENTRIES: IS_MAP });
8459                 return that;
8460               });
8461               Constructor.prototype = NativePrototype;
8462               NativePrototype.constructor = Constructor;
8463             }
8464
8465             if (THROWS_ON_PRIMITIVES || BUGGY_ZERO) {
8466               fixMethod('delete');
8467               fixMethod('has');
8468               IS_MAP && fixMethod('get');
8469             }
8470
8471             if (BUGGY_ZERO || HASNT_CHAINING) fixMethod(ADDER);
8472
8473             // weak collections should not contains .clear method
8474             if (IS_WEAK && NativePrototype.clear) delete NativePrototype.clear;
8475           }
8476
8477           exported[CONSTRUCTOR_NAME] = Constructor;
8478           _export({ global: true, forced: Constructor != NativeConstructor }, exported);
8479
8480           setToStringTag(Constructor, CONSTRUCTOR_NAME);
8481
8482           if (!IS_WEAK) common.setStrong(Constructor, CONSTRUCTOR_NAME, IS_MAP);
8483
8484           return Constructor;
8485         };
8486
8487         var defineProperty$2 = objectDefineProperty.f;
8488
8489
8490
8491
8492
8493
8494
8495
8496         var fastKey = internalMetadata.fastKey;
8497
8498
8499         var setInternalState = internalState.set;
8500         var internalStateGetterFor = internalState.getterFor;
8501
8502         var collectionStrong = {
8503           getConstructor: function (wrapper, CONSTRUCTOR_NAME, IS_MAP, ADDER) {
8504             var C = wrapper(function (that, iterable) {
8505               anInstance(that, C, CONSTRUCTOR_NAME);
8506               setInternalState(that, {
8507                 type: CONSTRUCTOR_NAME,
8508                 index: objectCreate(null),
8509                 first: undefined,
8510                 last: undefined,
8511                 size: 0
8512               });
8513               if (!descriptors) that.size = 0;
8514               if (iterable != undefined) iterate(iterable, that[ADDER], { that: that, AS_ENTRIES: IS_MAP });
8515             });
8516
8517             var getInternalState = internalStateGetterFor(CONSTRUCTOR_NAME);
8518
8519             var define = function (that, key, value) {
8520               var state = getInternalState(that);
8521               var entry = getEntry(that, key);
8522               var previous, index;
8523               // change existing entry
8524               if (entry) {
8525                 entry.value = value;
8526               // create new entry
8527               } else {
8528                 state.last = entry = {
8529                   index: index = fastKey(key, true),
8530                   key: key,
8531                   value: value,
8532                   previous: previous = state.last,
8533                   next: undefined,
8534                   removed: false
8535                 };
8536                 if (!state.first) state.first = entry;
8537                 if (previous) previous.next = entry;
8538                 if (descriptors) state.size++;
8539                 else that.size++;
8540                 // add to index
8541                 if (index !== 'F') state.index[index] = entry;
8542               } return that;
8543             };
8544
8545             var getEntry = function (that, key) {
8546               var state = getInternalState(that);
8547               // fast case
8548               var index = fastKey(key);
8549               var entry;
8550               if (index !== 'F') return state.index[index];
8551               // frozen object case
8552               for (entry = state.first; entry; entry = entry.next) {
8553                 if (entry.key == key) return entry;
8554               }
8555             };
8556
8557             redefineAll(C.prototype, {
8558               // `{ Map, Set }.prototype.clear()` methods
8559               // https://tc39.es/ecma262/#sec-map.prototype.clear
8560               // https://tc39.es/ecma262/#sec-set.prototype.clear
8561               clear: function clear() {
8562                 var that = this;
8563                 var state = getInternalState(that);
8564                 var data = state.index;
8565                 var entry = state.first;
8566                 while (entry) {
8567                   entry.removed = true;
8568                   if (entry.previous) entry.previous = entry.previous.next = undefined;
8569                   delete data[entry.index];
8570                   entry = entry.next;
8571                 }
8572                 state.first = state.last = undefined;
8573                 if (descriptors) state.size = 0;
8574                 else that.size = 0;
8575               },
8576               // `{ Map, Set }.prototype.delete(key)` methods
8577               // https://tc39.es/ecma262/#sec-map.prototype.delete
8578               // https://tc39.es/ecma262/#sec-set.prototype.delete
8579               'delete': function (key) {
8580                 var that = this;
8581                 var state = getInternalState(that);
8582                 var entry = getEntry(that, key);
8583                 if (entry) {
8584                   var next = entry.next;
8585                   var prev = entry.previous;
8586                   delete state.index[entry.index];
8587                   entry.removed = true;
8588                   if (prev) prev.next = next;
8589                   if (next) next.previous = prev;
8590                   if (state.first == entry) state.first = next;
8591                   if (state.last == entry) state.last = prev;
8592                   if (descriptors) state.size--;
8593                   else that.size--;
8594                 } return !!entry;
8595               },
8596               // `{ Map, Set }.prototype.forEach(callbackfn, thisArg = undefined)` methods
8597               // https://tc39.es/ecma262/#sec-map.prototype.foreach
8598               // https://tc39.es/ecma262/#sec-set.prototype.foreach
8599               forEach: function forEach(callbackfn /* , that = undefined */) {
8600                 var state = getInternalState(this);
8601                 var boundFunction = functionBindContext(callbackfn, arguments.length > 1 ? arguments[1] : undefined, 3);
8602                 var entry;
8603                 while (entry = entry ? entry.next : state.first) {
8604                   boundFunction(entry.value, entry.key, this);
8605                   // revert to the last existing entry
8606                   while (entry && entry.removed) entry = entry.previous;
8607                 }
8608               },
8609               // `{ Map, Set}.prototype.has(key)` methods
8610               // https://tc39.es/ecma262/#sec-map.prototype.has
8611               // https://tc39.es/ecma262/#sec-set.prototype.has
8612               has: function has(key) {
8613                 return !!getEntry(this, key);
8614               }
8615             });
8616
8617             redefineAll(C.prototype, IS_MAP ? {
8618               // `Map.prototype.get(key)` method
8619               // https://tc39.es/ecma262/#sec-map.prototype.get
8620               get: function get(key) {
8621                 var entry = getEntry(this, key);
8622                 return entry && entry.value;
8623               },
8624               // `Map.prototype.set(key, value)` method
8625               // https://tc39.es/ecma262/#sec-map.prototype.set
8626               set: function set(key, value) {
8627                 return define(this, key === 0 ? 0 : key, value);
8628               }
8629             } : {
8630               // `Set.prototype.add(value)` method
8631               // https://tc39.es/ecma262/#sec-set.prototype.add
8632               add: function add(value) {
8633                 return define(this, value = value === 0 ? 0 : value, value);
8634               }
8635             });
8636             if (descriptors) defineProperty$2(C.prototype, 'size', {
8637               get: function () {
8638                 return getInternalState(this).size;
8639               }
8640             });
8641             return C;
8642           },
8643           setStrong: function (C, CONSTRUCTOR_NAME, IS_MAP) {
8644             var ITERATOR_NAME = CONSTRUCTOR_NAME + ' Iterator';
8645             var getInternalCollectionState = internalStateGetterFor(CONSTRUCTOR_NAME);
8646             var getInternalIteratorState = internalStateGetterFor(ITERATOR_NAME);
8647             // `{ Map, Set }.prototype.{ keys, values, entries, @@iterator }()` methods
8648             // https://tc39.es/ecma262/#sec-map.prototype.entries
8649             // https://tc39.es/ecma262/#sec-map.prototype.keys
8650             // https://tc39.es/ecma262/#sec-map.prototype.values
8651             // https://tc39.es/ecma262/#sec-map.prototype-@@iterator
8652             // https://tc39.es/ecma262/#sec-set.prototype.entries
8653             // https://tc39.es/ecma262/#sec-set.prototype.keys
8654             // https://tc39.es/ecma262/#sec-set.prototype.values
8655             // https://tc39.es/ecma262/#sec-set.prototype-@@iterator
8656             defineIterator(C, CONSTRUCTOR_NAME, function (iterated, kind) {
8657               setInternalState(this, {
8658                 type: ITERATOR_NAME,
8659                 target: iterated,
8660                 state: getInternalCollectionState(iterated),
8661                 kind: kind,
8662                 last: undefined
8663               });
8664             }, function () {
8665               var state = getInternalIteratorState(this);
8666               var kind = state.kind;
8667               var entry = state.last;
8668               // revert to the last existing entry
8669               while (entry && entry.removed) entry = entry.previous;
8670               // get next entry
8671               if (!state.target || !(state.last = entry = entry ? entry.next : state.state.first)) {
8672                 // or finish the iteration
8673                 state.target = undefined;
8674                 return { value: undefined, done: true };
8675               }
8676               // return step by kind
8677               if (kind == 'keys') return { value: entry.key, done: false };
8678               if (kind == 'values') return { value: entry.value, done: false };
8679               return { value: [entry.key, entry.value], done: false };
8680             }, IS_MAP ? 'entries' : 'values', !IS_MAP, true);
8681
8682             // `{ Map, Set }.prototype[@@species]` accessors
8683             // https://tc39.es/ecma262/#sec-get-map-@@species
8684             // https://tc39.es/ecma262/#sec-get-set-@@species
8685             setSpecies(CONSTRUCTOR_NAME);
8686           }
8687         };
8688
8689         // `Set` constructor
8690         // https://tc39.es/ecma262/#sec-set-objects
8691         collection('Set', function (init) {
8692           return function Set() { return init(this, arguments.length ? arguments[0] : undefined); };
8693         }, collectionStrong);
8694
8695         function d3_ascending (a, b) {
8696           return a < b ? -1 : a > b ? 1 : a >= b ? 0 : NaN;
8697         }
8698
8699         function d3_bisector (f) {
8700           var delta = f;
8701           var compare = f;
8702
8703           if (f.length === 1) {
8704             delta = function delta(d, x) {
8705               return f(d) - x;
8706             };
8707
8708             compare = ascendingComparator(f);
8709           }
8710
8711           function left(a, x, lo, hi) {
8712             if (lo == null) lo = 0;
8713             if (hi == null) hi = a.length;
8714
8715             while (lo < hi) {
8716               var mid = lo + hi >>> 1;
8717               if (compare(a[mid], x) < 0) lo = mid + 1;else hi = mid;
8718             }
8719
8720             return lo;
8721           }
8722
8723           function right(a, x, lo, hi) {
8724             if (lo == null) lo = 0;
8725             if (hi == null) hi = a.length;
8726
8727             while (lo < hi) {
8728               var mid = lo + hi >>> 1;
8729               if (compare(a[mid], x) > 0) hi = mid;else lo = mid + 1;
8730             }
8731
8732             return lo;
8733           }
8734
8735           function center(a, x, lo, hi) {
8736             if (lo == null) lo = 0;
8737             if (hi == null) hi = a.length;
8738             var i = left(a, x, lo, hi - 1);
8739             return i > lo && delta(a[i - 1], x) > -delta(a[i], x) ? i - 1 : i;
8740           }
8741
8742           return {
8743             left: left,
8744             center: center,
8745             right: right
8746           };
8747         }
8748
8749         function ascendingComparator(f) {
8750           return function (d, x) {
8751             return d3_ascending(f(d), x);
8752           };
8753         }
8754
8755         // `Symbol.asyncIterator` well-known symbol
8756         // https://tc39.es/ecma262/#sec-symbol.asynciterator
8757         defineWellKnownSymbol('asyncIterator');
8758
8759         createCommonjsModule(function (module) {
8760           var runtime = function (exports) {
8761
8762             var Op = Object.prototype;
8763             var hasOwn = Op.hasOwnProperty;
8764             var undefined$1; // More compressible than void 0.
8765
8766             var $Symbol = typeof Symbol === "function" ? Symbol : {};
8767             var iteratorSymbol = $Symbol.iterator || "@@iterator";
8768             var asyncIteratorSymbol = $Symbol.asyncIterator || "@@asyncIterator";
8769             var toStringTagSymbol = $Symbol.toStringTag || "@@toStringTag";
8770
8771             function define(obj, key, value) {
8772               Object.defineProperty(obj, key, {
8773                 value: value,
8774                 enumerable: true,
8775                 configurable: true,
8776                 writable: true
8777               });
8778               return obj[key];
8779             }
8780
8781             try {
8782               // IE 8 has a broken Object.defineProperty that only works on DOM objects.
8783               define({}, "");
8784             } catch (err) {
8785               define = function define(obj, key, value) {
8786                 return obj[key] = value;
8787               };
8788             }
8789
8790             function wrap(innerFn, outerFn, self, tryLocsList) {
8791               // If outerFn provided and outerFn.prototype is a Generator, then outerFn.prototype instanceof Generator.
8792               var protoGenerator = outerFn && outerFn.prototype instanceof Generator ? outerFn : Generator;
8793               var generator = Object.create(protoGenerator.prototype);
8794               var context = new Context(tryLocsList || []); // The ._invoke method unifies the implementations of the .next,
8795               // .throw, and .return methods.
8796
8797               generator._invoke = makeInvokeMethod(innerFn, self, context);
8798               return generator;
8799             }
8800
8801             exports.wrap = wrap; // Try/catch helper to minimize deoptimizations. Returns a completion
8802             // record like context.tryEntries[i].completion. This interface could
8803             // have been (and was previously) designed to take a closure to be
8804             // invoked without arguments, but in all the cases we care about we
8805             // already have an existing method we want to call, so there's no need
8806             // to create a new function object. We can even get away with assuming
8807             // the method takes exactly one argument, since that happens to be true
8808             // in every case, so we don't have to touch the arguments object. The
8809             // only additional allocation required is the completion record, which
8810             // has a stable shape and so hopefully should be cheap to allocate.
8811
8812             function tryCatch(fn, obj, arg) {
8813               try {
8814                 return {
8815                   type: "normal",
8816                   arg: fn.call(obj, arg)
8817                 };
8818               } catch (err) {
8819                 return {
8820                   type: "throw",
8821                   arg: err
8822                 };
8823               }
8824             }
8825
8826             var GenStateSuspendedStart = "suspendedStart";
8827             var GenStateSuspendedYield = "suspendedYield";
8828             var GenStateExecuting = "executing";
8829             var GenStateCompleted = "completed"; // Returning this object from the innerFn has the same effect as
8830             // breaking out of the dispatch switch statement.
8831
8832             var ContinueSentinel = {}; // Dummy constructor functions that we use as the .constructor and
8833             // .constructor.prototype properties for functions that return Generator
8834             // objects. For full spec compliance, you may wish to configure your
8835             // minifier not to mangle the names of these two functions.
8836
8837             function Generator() {}
8838
8839             function GeneratorFunction() {}
8840
8841             function GeneratorFunctionPrototype() {} // This is a polyfill for %IteratorPrototype% for environments that
8842             // don't natively support it.
8843
8844
8845             var IteratorPrototype = {};
8846
8847             IteratorPrototype[iteratorSymbol] = function () {
8848               return this;
8849             };
8850
8851             var getProto = Object.getPrototypeOf;
8852             var NativeIteratorPrototype = getProto && getProto(getProto(values([])));
8853
8854             if (NativeIteratorPrototype && NativeIteratorPrototype !== Op && hasOwn.call(NativeIteratorPrototype, iteratorSymbol)) {
8855               // This environment has a native %IteratorPrototype%; use it instead
8856               // of the polyfill.
8857               IteratorPrototype = NativeIteratorPrototype;
8858             }
8859
8860             var Gp = GeneratorFunctionPrototype.prototype = Generator.prototype = Object.create(IteratorPrototype);
8861             GeneratorFunction.prototype = Gp.constructor = GeneratorFunctionPrototype;
8862             GeneratorFunctionPrototype.constructor = GeneratorFunction;
8863             GeneratorFunction.displayName = define(GeneratorFunctionPrototype, toStringTagSymbol, "GeneratorFunction"); // Helper for defining the .next, .throw, and .return methods of the
8864             // Iterator interface in terms of a single ._invoke method.
8865
8866             function defineIteratorMethods(prototype) {
8867               ["next", "throw", "return"].forEach(function (method) {
8868                 define(prototype, method, function (arg) {
8869                   return this._invoke(method, arg);
8870                 });
8871               });
8872             }
8873
8874             exports.isGeneratorFunction = function (genFun) {
8875               var ctor = typeof genFun === "function" && genFun.constructor;
8876               return ctor ? ctor === GeneratorFunction || // For the native GeneratorFunction constructor, the best we can
8877               // do is to check its .name property.
8878               (ctor.displayName || ctor.name) === "GeneratorFunction" : false;
8879             };
8880
8881             exports.mark = function (genFun) {
8882               if (Object.setPrototypeOf) {
8883                 Object.setPrototypeOf(genFun, GeneratorFunctionPrototype);
8884               } else {
8885                 genFun.__proto__ = GeneratorFunctionPrototype;
8886                 define(genFun, toStringTagSymbol, "GeneratorFunction");
8887               }
8888
8889               genFun.prototype = Object.create(Gp);
8890               return genFun;
8891             }; // Within the body of any async function, `await x` is transformed to
8892             // `yield regeneratorRuntime.awrap(x)`, so that the runtime can test
8893             // `hasOwn.call(value, "__await")` to determine if the yielded value is
8894             // meant to be awaited.
8895
8896
8897             exports.awrap = function (arg) {
8898               return {
8899                 __await: arg
8900               };
8901             };
8902
8903             function AsyncIterator(generator, PromiseImpl) {
8904               function invoke(method, arg, resolve, reject) {
8905                 var record = tryCatch(generator[method], generator, arg);
8906
8907                 if (record.type === "throw") {
8908                   reject(record.arg);
8909                 } else {
8910                   var result = record.arg;
8911                   var value = result.value;
8912
8913                   if (value && _typeof(value) === "object" && hasOwn.call(value, "__await")) {
8914                     return PromiseImpl.resolve(value.__await).then(function (value) {
8915                       invoke("next", value, resolve, reject);
8916                     }, function (err) {
8917                       invoke("throw", err, resolve, reject);
8918                     });
8919                   }
8920
8921                   return PromiseImpl.resolve(value).then(function (unwrapped) {
8922                     // When a yielded Promise is resolved, its final value becomes
8923                     // the .value of the Promise<{value,done}> result for the
8924                     // current iteration.
8925                     result.value = unwrapped;
8926                     resolve(result);
8927                   }, function (error) {
8928                     // If a rejected Promise was yielded, throw the rejection back
8929                     // into the async generator function so it can be handled there.
8930                     return invoke("throw", error, resolve, reject);
8931                   });
8932                 }
8933               }
8934
8935               var previousPromise;
8936
8937               function enqueue(method, arg) {
8938                 function callInvokeWithMethodAndArg() {
8939                   return new PromiseImpl(function (resolve, reject) {
8940                     invoke(method, arg, resolve, reject);
8941                   });
8942                 }
8943
8944                 return previousPromise = // If enqueue has been called before, then we want to wait until
8945                 // all previous Promises have been resolved before calling invoke,
8946                 // so that results are always delivered in the correct order. If
8947                 // enqueue has not been called before, then it is important to
8948                 // call invoke immediately, without waiting on a callback to fire,
8949                 // so that the async generator function has the opportunity to do
8950                 // any necessary setup in a predictable way. This predictability
8951                 // is why the Promise constructor synchronously invokes its
8952                 // executor callback, and why async functions synchronously
8953                 // execute code before the first await. Since we implement simple
8954                 // async functions in terms of async generators, it is especially
8955                 // important to get this right, even though it requires care.
8956                 previousPromise ? previousPromise.then(callInvokeWithMethodAndArg, // Avoid propagating failures to Promises returned by later
8957                 // invocations of the iterator.
8958                 callInvokeWithMethodAndArg) : callInvokeWithMethodAndArg();
8959               } // Define the unified helper method that is used to implement .next,
8960               // .throw, and .return (see defineIteratorMethods).
8961
8962
8963               this._invoke = enqueue;
8964             }
8965
8966             defineIteratorMethods(AsyncIterator.prototype);
8967
8968             AsyncIterator.prototype[asyncIteratorSymbol] = function () {
8969               return this;
8970             };
8971
8972             exports.AsyncIterator = AsyncIterator; // Note that simple async functions are implemented on top of
8973             // AsyncIterator objects; they just return a Promise for the value of
8974             // the final result produced by the iterator.
8975
8976             exports.async = function (innerFn, outerFn, self, tryLocsList, PromiseImpl) {
8977               if (PromiseImpl === void 0) PromiseImpl = Promise;
8978               var iter = new AsyncIterator(wrap(innerFn, outerFn, self, tryLocsList), PromiseImpl);
8979               return exports.isGeneratorFunction(outerFn) ? iter // If outerFn is a generator, return the full iterator.
8980               : iter.next().then(function (result) {
8981                 return result.done ? result.value : iter.next();
8982               });
8983             };
8984
8985             function makeInvokeMethod(innerFn, self, context) {
8986               var state = GenStateSuspendedStart;
8987               return function invoke(method, arg) {
8988                 if (state === GenStateExecuting) {
8989                   throw new Error("Generator is already running");
8990                 }
8991
8992                 if (state === GenStateCompleted) {
8993                   if (method === "throw") {
8994                     throw arg;
8995                   } // Be forgiving, per 25.3.3.3.3 of the spec:
8996                   // https://people.mozilla.org/~jorendorff/es6-draft.html#sec-generatorresume
8997
8998
8999                   return doneResult();
9000                 }
9001
9002                 context.method = method;
9003                 context.arg = arg;
9004
9005                 while (true) {
9006                   var delegate = context.delegate;
9007
9008                   if (delegate) {
9009                     var delegateResult = maybeInvokeDelegate(delegate, context);
9010
9011                     if (delegateResult) {
9012                       if (delegateResult === ContinueSentinel) continue;
9013                       return delegateResult;
9014                     }
9015                   }
9016
9017                   if (context.method === "next") {
9018                     // Setting context._sent for legacy support of Babel's
9019                     // function.sent implementation.
9020                     context.sent = context._sent = context.arg;
9021                   } else if (context.method === "throw") {
9022                     if (state === GenStateSuspendedStart) {
9023                       state = GenStateCompleted;
9024                       throw context.arg;
9025                     }
9026
9027                     context.dispatchException(context.arg);
9028                   } else if (context.method === "return") {
9029                     context.abrupt("return", context.arg);
9030                   }
9031
9032                   state = GenStateExecuting;
9033                   var record = tryCatch(innerFn, self, context);
9034
9035                   if (record.type === "normal") {
9036                     // If an exception is thrown from innerFn, we leave state ===
9037                     // GenStateExecuting and loop back for another invocation.
9038                     state = context.done ? GenStateCompleted : GenStateSuspendedYield;
9039
9040                     if (record.arg === ContinueSentinel) {
9041                       continue;
9042                     }
9043
9044                     return {
9045                       value: record.arg,
9046                       done: context.done
9047                     };
9048                   } else if (record.type === "throw") {
9049                     state = GenStateCompleted; // Dispatch the exception by looping back around to the
9050                     // context.dispatchException(context.arg) call above.
9051
9052                     context.method = "throw";
9053                     context.arg = record.arg;
9054                   }
9055                 }
9056               };
9057             } // Call delegate.iterator[context.method](context.arg) and handle the
9058             // result, either by returning a { value, done } result from the
9059             // delegate iterator, or by modifying context.method and context.arg,
9060             // setting context.delegate to null, and returning the ContinueSentinel.
9061
9062
9063             function maybeInvokeDelegate(delegate, context) {
9064               var method = delegate.iterator[context.method];
9065
9066               if (method === undefined$1) {
9067                 // A .throw or .return when the delegate iterator has no .throw
9068                 // method always terminates the yield* loop.
9069                 context.delegate = null;
9070
9071                 if (context.method === "throw") {
9072                   // Note: ["return"] must be used for ES3 parsing compatibility.
9073                   if (delegate.iterator["return"]) {
9074                     // If the delegate iterator has a return method, give it a
9075                     // chance to clean up.
9076                     context.method = "return";
9077                     context.arg = undefined$1;
9078                     maybeInvokeDelegate(delegate, context);
9079
9080                     if (context.method === "throw") {
9081                       // If maybeInvokeDelegate(context) changed context.method from
9082                       // "return" to "throw", let that override the TypeError below.
9083                       return ContinueSentinel;
9084                     }
9085                   }
9086
9087                   context.method = "throw";
9088                   context.arg = new TypeError("The iterator does not provide a 'throw' method");
9089                 }
9090
9091                 return ContinueSentinel;
9092               }
9093
9094               var record = tryCatch(method, delegate.iterator, context.arg);
9095
9096               if (record.type === "throw") {
9097                 context.method = "throw";
9098                 context.arg = record.arg;
9099                 context.delegate = null;
9100                 return ContinueSentinel;
9101               }
9102
9103               var info = record.arg;
9104
9105               if (!info) {
9106                 context.method = "throw";
9107                 context.arg = new TypeError("iterator result is not an object");
9108                 context.delegate = null;
9109                 return ContinueSentinel;
9110               }
9111
9112               if (info.done) {
9113                 // Assign the result of the finished delegate to the temporary
9114                 // variable specified by delegate.resultName (see delegateYield).
9115                 context[delegate.resultName] = info.value; // Resume execution at the desired location (see delegateYield).
9116
9117                 context.next = delegate.nextLoc; // If context.method was "throw" but the delegate handled the
9118                 // exception, let the outer generator proceed normally. If
9119                 // context.method was "next", forget context.arg since it has been
9120                 // "consumed" by the delegate iterator. If context.method was
9121                 // "return", allow the original .return call to continue in the
9122                 // outer generator.
9123
9124                 if (context.method !== "return") {
9125                   context.method = "next";
9126                   context.arg = undefined$1;
9127                 }
9128               } else {
9129                 // Re-yield the result returned by the delegate method.
9130                 return info;
9131               } // The delegate iterator is finished, so forget it and continue with
9132               // the outer generator.
9133
9134
9135               context.delegate = null;
9136               return ContinueSentinel;
9137             } // Define Generator.prototype.{next,throw,return} in terms of the
9138             // unified ._invoke helper method.
9139
9140
9141             defineIteratorMethods(Gp);
9142             define(Gp, toStringTagSymbol, "Generator"); // A Generator should always return itself as the iterator object when the
9143             // @@iterator function is called on it. Some browsers' implementations of the
9144             // iterator prototype chain incorrectly implement this, causing the Generator
9145             // object to not be returned from this call. This ensures that doesn't happen.
9146             // See https://github.com/facebook/regenerator/issues/274 for more details.
9147
9148             Gp[iteratorSymbol] = function () {
9149               return this;
9150             };
9151
9152             Gp.toString = function () {
9153               return "[object Generator]";
9154             };
9155
9156             function pushTryEntry(locs) {
9157               var entry = {
9158                 tryLoc: locs[0]
9159               };
9160
9161               if (1 in locs) {
9162                 entry.catchLoc = locs[1];
9163               }
9164
9165               if (2 in locs) {
9166                 entry.finallyLoc = locs[2];
9167                 entry.afterLoc = locs[3];
9168               }
9169
9170               this.tryEntries.push(entry);
9171             }
9172
9173             function resetTryEntry(entry) {
9174               var record = entry.completion || {};
9175               record.type = "normal";
9176               delete record.arg;
9177               entry.completion = record;
9178             }
9179
9180             function Context(tryLocsList) {
9181               // The root entry object (effectively a try statement without a catch
9182               // or a finally block) gives us a place to store values thrown from
9183               // locations where there is no enclosing try statement.
9184               this.tryEntries = [{
9185                 tryLoc: "root"
9186               }];
9187               tryLocsList.forEach(pushTryEntry, this);
9188               this.reset(true);
9189             }
9190
9191             exports.keys = function (object) {
9192               var keys = [];
9193
9194               for (var key in object) {
9195                 keys.push(key);
9196               }
9197
9198               keys.reverse(); // Rather than returning an object with a next method, we keep
9199               // things simple and return the next function itself.
9200
9201               return function next() {
9202                 while (keys.length) {
9203                   var key = keys.pop();
9204
9205                   if (key in object) {
9206                     next.value = key;
9207                     next.done = false;
9208                     return next;
9209                   }
9210                 } // To avoid creating an additional object, we just hang the .value
9211                 // and .done properties off the next function object itself. This
9212                 // also ensures that the minifier will not anonymize the function.
9213
9214
9215                 next.done = true;
9216                 return next;
9217               };
9218             };
9219
9220             function values(iterable) {
9221               if (iterable) {
9222                 var iteratorMethod = iterable[iteratorSymbol];
9223
9224                 if (iteratorMethod) {
9225                   return iteratorMethod.call(iterable);
9226                 }
9227
9228                 if (typeof iterable.next === "function") {
9229                   return iterable;
9230                 }
9231
9232                 if (!isNaN(iterable.length)) {
9233                   var i = -1,
9234                       next = function next() {
9235                     while (++i < iterable.length) {
9236                       if (hasOwn.call(iterable, i)) {
9237                         next.value = iterable[i];
9238                         next.done = false;
9239                         return next;
9240                       }
9241                     }
9242
9243                     next.value = undefined$1;
9244                     next.done = true;
9245                     return next;
9246                   };
9247
9248                   return next.next = next;
9249                 }
9250               } // Return an iterator with no values.
9251
9252
9253               return {
9254                 next: doneResult
9255               };
9256             }
9257
9258             exports.values = values;
9259
9260             function doneResult() {
9261               return {
9262                 value: undefined$1,
9263                 done: true
9264               };
9265             }
9266
9267             Context.prototype = {
9268               constructor: Context,
9269               reset: function reset(skipTempReset) {
9270                 this.prev = 0;
9271                 this.next = 0; // Resetting context._sent for legacy support of Babel's
9272                 // function.sent implementation.
9273
9274                 this.sent = this._sent = undefined$1;
9275                 this.done = false;
9276                 this.delegate = null;
9277                 this.method = "next";
9278                 this.arg = undefined$1;
9279                 this.tryEntries.forEach(resetTryEntry);
9280
9281                 if (!skipTempReset) {
9282                   for (var name in this) {
9283                     // Not sure about the optimal order of these conditions:
9284                     if (name.charAt(0) === "t" && hasOwn.call(this, name) && !isNaN(+name.slice(1))) {
9285                       this[name] = undefined$1;
9286                     }
9287                   }
9288                 }
9289               },
9290               stop: function stop() {
9291                 this.done = true;
9292                 var rootEntry = this.tryEntries[0];
9293                 var rootRecord = rootEntry.completion;
9294
9295                 if (rootRecord.type === "throw") {
9296                   throw rootRecord.arg;
9297                 }
9298
9299                 return this.rval;
9300               },
9301               dispatchException: function dispatchException(exception) {
9302                 if (this.done) {
9303                   throw exception;
9304                 }
9305
9306                 var context = this;
9307
9308                 function handle(loc, caught) {
9309                   record.type = "throw";
9310                   record.arg = exception;
9311                   context.next = loc;
9312
9313                   if (caught) {
9314                     // If the dispatched exception was caught by a catch block,
9315                     // then let that catch block handle the exception normally.
9316                     context.method = "next";
9317                     context.arg = undefined$1;
9318                   }
9319
9320                   return !!caught;
9321                 }
9322
9323                 for (var i = this.tryEntries.length - 1; i >= 0; --i) {
9324                   var entry = this.tryEntries[i];
9325                   var record = entry.completion;
9326
9327                   if (entry.tryLoc === "root") {
9328                     // Exception thrown outside of any try block that could handle
9329                     // it, so set the completion value of the entire function to
9330                     // throw the exception.
9331                     return handle("end");
9332                   }
9333
9334                   if (entry.tryLoc <= this.prev) {
9335                     var hasCatch = hasOwn.call(entry, "catchLoc");
9336                     var hasFinally = hasOwn.call(entry, "finallyLoc");
9337
9338                     if (hasCatch && hasFinally) {
9339                       if (this.prev < entry.catchLoc) {
9340                         return handle(entry.catchLoc, true);
9341                       } else if (this.prev < entry.finallyLoc) {
9342                         return handle(entry.finallyLoc);
9343                       }
9344                     } else if (hasCatch) {
9345                       if (this.prev < entry.catchLoc) {
9346                         return handle(entry.catchLoc, true);
9347                       }
9348                     } else if (hasFinally) {
9349                       if (this.prev < entry.finallyLoc) {
9350                         return handle(entry.finallyLoc);
9351                       }
9352                     } else {
9353                       throw new Error("try statement without catch or finally");
9354                     }
9355                   }
9356                 }
9357               },
9358               abrupt: function abrupt(type, arg) {
9359                 for (var i = this.tryEntries.length - 1; i >= 0; --i) {
9360                   var entry = this.tryEntries[i];
9361
9362                   if (entry.tryLoc <= this.prev && hasOwn.call(entry, "finallyLoc") && this.prev < entry.finallyLoc) {
9363                     var finallyEntry = entry;
9364                     break;
9365                   }
9366                 }
9367
9368                 if (finallyEntry && (type === "break" || type === "continue") && finallyEntry.tryLoc <= arg && arg <= finallyEntry.finallyLoc) {
9369                   // Ignore the finally entry if control is not jumping to a
9370                   // location outside the try/catch block.
9371                   finallyEntry = null;
9372                 }
9373
9374                 var record = finallyEntry ? finallyEntry.completion : {};
9375                 record.type = type;
9376                 record.arg = arg;
9377
9378                 if (finallyEntry) {
9379                   this.method = "next";
9380                   this.next = finallyEntry.finallyLoc;
9381                   return ContinueSentinel;
9382                 }
9383
9384                 return this.complete(record);
9385               },
9386               complete: function complete(record, afterLoc) {
9387                 if (record.type === "throw") {
9388                   throw record.arg;
9389                 }
9390
9391                 if (record.type === "break" || record.type === "continue") {
9392                   this.next = record.arg;
9393                 } else if (record.type === "return") {
9394                   this.rval = this.arg = record.arg;
9395                   this.method = "return";
9396                   this.next = "end";
9397                 } else if (record.type === "normal" && afterLoc) {
9398                   this.next = afterLoc;
9399                 }
9400
9401                 return ContinueSentinel;
9402               },
9403               finish: function finish(finallyLoc) {
9404                 for (var i = this.tryEntries.length - 1; i >= 0; --i) {
9405                   var entry = this.tryEntries[i];
9406
9407                   if (entry.finallyLoc === finallyLoc) {
9408                     this.complete(entry.completion, entry.afterLoc);
9409                     resetTryEntry(entry);
9410                     return ContinueSentinel;
9411                   }
9412                 }
9413               },
9414               "catch": function _catch(tryLoc) {
9415                 for (var i = this.tryEntries.length - 1; i >= 0; --i) {
9416                   var entry = this.tryEntries[i];
9417
9418                   if (entry.tryLoc === tryLoc) {
9419                     var record = entry.completion;
9420
9421                     if (record.type === "throw") {
9422                       var thrown = record.arg;
9423                       resetTryEntry(entry);
9424                     }
9425
9426                     return thrown;
9427                   }
9428                 } // The context.catch method must only be called with a location
9429                 // argument that corresponds to a known catch block.
9430
9431
9432                 throw new Error("illegal catch attempt");
9433               },
9434               delegateYield: function delegateYield(iterable, resultName, nextLoc) {
9435                 this.delegate = {
9436                   iterator: values(iterable),
9437                   resultName: resultName,
9438                   nextLoc: nextLoc
9439                 };
9440
9441                 if (this.method === "next") {
9442                   // Deliberately forget the last sent value so that we don't
9443                   // accidentally pass it on to the delegate.
9444                   this.arg = undefined$1;
9445                 }
9446
9447                 return ContinueSentinel;
9448               }
9449             }; // Regardless of whether this script is executing as a CommonJS module
9450             // or not, return the runtime object so that we can declare the variable
9451             // regeneratorRuntime in the outer scope, which allows this module to be
9452             // injected easily by `bin/regenerator --include-runtime script.js`.
9453
9454             return exports;
9455           }( // If this script is executing as a CommonJS module, use module.exports
9456           // as the regeneratorRuntime namespace. Otherwise create a new empty
9457           // object. Either way, the resulting object will be used to initialize
9458           // the regeneratorRuntime variable at the top of this file.
9459           module.exports );
9460
9461           try {
9462             regeneratorRuntime = runtime;
9463           } catch (accidentalStrictMode) {
9464             // This module should not be running in strict mode, so the above
9465             // assignment should always work unless something is misconfigured. Just
9466             // in case runtime.js accidentally runs in strict mode, we can escape
9467             // strict mode using a global Function call. This could conceivably fail
9468             // if a Content Security Policy forbids using Function, but in that case
9469             // the proper solution is to fix the accidental strict mode problem. If
9470             // you've misconfigured your bundler to force strict mode and applied a
9471             // CSP to forbid Function, and you're not willing to fix either of those
9472             // problems, please detail your unique predicament in a GitHub issue.
9473             Function("r", "regeneratorRuntime = r")(runtime);
9474           }
9475         });
9476
9477         var _marked$3 = /*#__PURE__*/regeneratorRuntime.mark(numbers);
9478
9479         function number$1 (x) {
9480           return x === null ? NaN : +x;
9481         }
9482         function numbers(values, valueof) {
9483           var _iterator, _step, value, index, _iterator2, _step2, _value;
9484
9485           return regeneratorRuntime.wrap(function numbers$(_context) {
9486             while (1) {
9487               switch (_context.prev = _context.next) {
9488                 case 0:
9489                   if (!(valueof === undefined)) {
9490                     _context.next = 21;
9491                     break;
9492                   }
9493
9494                   _iterator = _createForOfIteratorHelper(values);
9495                   _context.prev = 2;
9496
9497                   _iterator.s();
9498
9499                 case 4:
9500                   if ((_step = _iterator.n()).done) {
9501                     _context.next = 11;
9502                     break;
9503                   }
9504
9505                   value = _step.value;
9506
9507                   if (!(value != null && (value = +value) >= value)) {
9508                     _context.next = 9;
9509                     break;
9510                   }
9511
9512                   _context.next = 9;
9513                   return value;
9514
9515                 case 9:
9516                   _context.next = 4;
9517                   break;
9518
9519                 case 11:
9520                   _context.next = 16;
9521                   break;
9522
9523                 case 13:
9524                   _context.prev = 13;
9525                   _context.t0 = _context["catch"](2);
9526
9527                   _iterator.e(_context.t0);
9528
9529                 case 16:
9530                   _context.prev = 16;
9531
9532                   _iterator.f();
9533
9534                   return _context.finish(16);
9535
9536                 case 19:
9537                   _context.next = 40;
9538                   break;
9539
9540                 case 21:
9541                   index = -1;
9542                   _iterator2 = _createForOfIteratorHelper(values);
9543                   _context.prev = 23;
9544
9545                   _iterator2.s();
9546
9547                 case 25:
9548                   if ((_step2 = _iterator2.n()).done) {
9549                     _context.next = 32;
9550                     break;
9551                   }
9552
9553                   _value = _step2.value;
9554
9555                   if (!((_value = valueof(_value, ++index, values)) != null && (_value = +_value) >= _value)) {
9556                     _context.next = 30;
9557                     break;
9558                   }
9559
9560                   _context.next = 30;
9561                   return _value;
9562
9563                 case 30:
9564                   _context.next = 25;
9565                   break;
9566
9567                 case 32:
9568                   _context.next = 37;
9569                   break;
9570
9571                 case 34:
9572                   _context.prev = 34;
9573                   _context.t1 = _context["catch"](23);
9574
9575                   _iterator2.e(_context.t1);
9576
9577                 case 37:
9578                   _context.prev = 37;
9579
9580                   _iterator2.f();
9581
9582                   return _context.finish(37);
9583
9584                 case 40:
9585                 case "end":
9586                   return _context.stop();
9587               }
9588             }
9589           }, _marked$3, null, [[2, 13, 16, 19], [23, 34, 37, 40]]);
9590         }
9591
9592         var ascendingBisect = d3_bisector(d3_ascending);
9593         var bisectRight = ascendingBisect.right;
9594         d3_bisector(number$1).center;
9595
9596         var INCORRECT_ITERATION = !checkCorrectnessOfIteration(function (iterable) {
9597           // eslint-disable-next-line es/no-array-from -- required for testing
9598           Array.from(iterable);
9599         });
9600
9601         // `Array.from` method
9602         // https://tc39.es/ecma262/#sec-array.from
9603         _export({ target: 'Array', stat: true, forced: INCORRECT_ITERATION }, {
9604           from: arrayFrom
9605         });
9606
9607         // `Array.prototype.fill` method
9608         // https://tc39.es/ecma262/#sec-array.prototype.fill
9609         _export({ target: 'Array', proto: true }, {
9610           fill: arrayFill
9611         });
9612
9613         // https://tc39.es/ecma262/#sec-array.prototype-@@unscopables
9614         addToUnscopables('fill');
9615
9616         var $some = arrayIteration.some;
9617
9618
9619         var STRICT_METHOD$3 = arrayMethodIsStrict('some');
9620
9621         // `Array.prototype.some` method
9622         // https://tc39.es/ecma262/#sec-array.prototype.some
9623         _export({ target: 'Array', proto: true, forced: !STRICT_METHOD$3 }, {
9624           some: function some(callbackfn /* , thisArg */) {
9625             return $some(this, callbackfn, arguments.length > 1 ? arguments[1] : undefined);
9626           }
9627         });
9628
9629         var exportTypedArrayStaticMethod = arrayBufferViewCore.exportTypedArrayStaticMethod;
9630
9631
9632         // `%TypedArray%.from` method
9633         // https://tc39.es/ecma262/#sec-%typedarray%.from
9634         exportTypedArrayStaticMethod('from', typedArrayFrom, typedArrayConstructorsRequireWrappers);
9635
9636         // `Float64Array` constructor
9637         // https://tc39.es/ecma262/#sec-typedarray-objects
9638         typedArrayConstructor('Float64', function (init) {
9639           return function Float64Array(data, byteOffset, length) {
9640             return init(this, data, byteOffset, length);
9641           };
9642         });
9643
9644         function d3_descending (a, b) {
9645           return b < a ? -1 : b > a ? 1 : b >= a ? 0 : NaN;
9646         }
9647
9648         // https://github.com/python/cpython/blob/a74eea238f5baba15797e2e8b570d153bc8690a7/Modules/mathmodule.c#L1423
9649         var Adder = /*#__PURE__*/function () {
9650           function Adder() {
9651             _classCallCheck$1(this, Adder);
9652
9653             this._partials = new Float64Array(32);
9654             this._n = 0;
9655           }
9656
9657           _createClass$1(Adder, [{
9658             key: "add",
9659             value: function add(x) {
9660               var p = this._partials;
9661               var i = 0;
9662
9663               for (var j = 0; j < this._n && j < 32; j++) {
9664                 var y = p[j],
9665                     hi = x + y,
9666                     lo = Math.abs(x) < Math.abs(y) ? x - (hi - y) : y - (hi - x);
9667                 if (lo) p[i++] = lo;
9668                 x = hi;
9669               }
9670
9671               p[i] = x;
9672               this._n = i + 1;
9673               return this;
9674             }
9675           }, {
9676             key: "valueOf",
9677             value: function valueOf() {
9678               var p = this._partials;
9679               var n = this._n,
9680                   x,
9681                   y,
9682                   lo,
9683                   hi = 0;
9684
9685               if (n > 0) {
9686                 hi = p[--n];
9687
9688                 while (n > 0) {
9689                   x = hi;
9690                   y = p[--n];
9691                   hi = x + y;
9692                   lo = y - (hi - x);
9693                   if (lo) break;
9694                 }
9695
9696                 if (n > 0 && (lo < 0 && p[n - 1] < 0 || lo > 0 && p[n - 1] > 0)) {
9697                   y = lo * 2;
9698                   x = hi + y;
9699                   if (y == x - hi) hi = x;
9700                 }
9701               }
9702
9703               return hi;
9704             }
9705           }]);
9706
9707           return Adder;
9708         }();
9709
9710         // `Object.defineProperties` method
9711         // https://tc39.es/ecma262/#sec-object.defineproperties
9712         _export({ target: 'Object', stat: true, forced: !descriptors, sham: !descriptors }, {
9713           defineProperties: objectDefineProperties
9714         });
9715
9716         // `Map` constructor
9717         // https://tc39.es/ecma262/#sec-map-objects
9718         collection('Map', function (init) {
9719           return function Map() { return init(this, arguments.length ? arguments[0] : undefined); };
9720         }, collectionStrong);
9721
9722         var test = [];
9723         var nativeSort = test.sort;
9724
9725         // IE8-
9726         var FAILS_ON_UNDEFINED = fails(function () {
9727           test.sort(undefined);
9728         });
9729         // V8 bug
9730         var FAILS_ON_NULL = fails(function () {
9731           test.sort(null);
9732         });
9733         // Old WebKit
9734         var STRICT_METHOD$2 = arrayMethodIsStrict('sort');
9735
9736         var STABLE_SORT = !fails(function () {
9737           // feature detection can be too slow, so check engines versions
9738           if (engineV8Version) return engineV8Version < 70;
9739           if (engineFfVersion && engineFfVersion > 3) return;
9740           if (engineIsIeOrEdge) return true;
9741           if (engineWebkitVersion) return engineWebkitVersion < 603;
9742
9743           var result = '';
9744           var code, chr, value, index;
9745
9746           // generate an array with more 512 elements (Chakra and old V8 fails only in this case)
9747           for (code = 65; code < 76; code++) {
9748             chr = String.fromCharCode(code);
9749
9750             switch (code) {
9751               case 66: case 69: case 70: case 72: value = 3; break;
9752               case 68: case 71: value = 4; break;
9753               default: value = 2;
9754             }
9755
9756             for (index = 0; index < 47; index++) {
9757               test.push({ k: chr + index, v: value });
9758             }
9759           }
9760
9761           test.sort(function (a, b) { return b.v - a.v; });
9762
9763           for (index = 0; index < test.length; index++) {
9764             chr = test[index].k.charAt(0);
9765             if (result.charAt(result.length - 1) !== chr) result += chr;
9766           }
9767
9768           return result !== 'DGBEFHACIJK';
9769         });
9770
9771         var FORCED$5 = FAILS_ON_UNDEFINED || !FAILS_ON_NULL || !STRICT_METHOD$2 || !STABLE_SORT;
9772
9773         var getSortCompare = function (comparefn) {
9774           return function (x, y) {
9775             if (y === undefined) return -1;
9776             if (x === undefined) return 1;
9777             if (comparefn !== undefined) return +comparefn(x, y) || 0;
9778             return String(x) > String(y) ? 1 : -1;
9779           };
9780         };
9781
9782         // `Array.prototype.sort` method
9783         // https://tc39.es/ecma262/#sec-array.prototype.sort
9784         _export({ target: 'Array', proto: true, forced: FORCED$5 }, {
9785           sort: function sort(comparefn) {
9786             if (comparefn !== undefined) aFunction(comparefn);
9787
9788             var array = toObject(this);
9789
9790             if (STABLE_SORT) return comparefn === undefined ? nativeSort.call(array) : nativeSort.call(array, comparefn);
9791
9792             var items = [];
9793             var arrayLength = toLength(array.length);
9794             var itemsLength, index;
9795
9796             for (index = 0; index < arrayLength; index++) {
9797               if (index in array) items.push(array[index]);
9798             }
9799
9800             items = arraySort(items, getSortCompare(comparefn));
9801             itemsLength = items.length;
9802             index = 0;
9803
9804             while (index < itemsLength) array[index] = items[index++];
9805             while (index < arrayLength) delete array[index++];
9806
9807             return array;
9808           }
9809         });
9810
9811         var e10 = Math.sqrt(50),
9812             e5 = Math.sqrt(10),
9813             e2 = Math.sqrt(2);
9814         function ticks (start, stop, count) {
9815           var reverse,
9816               i = -1,
9817               n,
9818               ticks,
9819               step;
9820           stop = +stop, start = +start, count = +count;
9821           if (start === stop && count > 0) return [start];
9822           if (reverse = stop < start) n = start, start = stop, stop = n;
9823           if ((step = tickIncrement(start, stop, count)) === 0 || !isFinite(step)) return [];
9824
9825           if (step > 0) {
9826             var r0 = Math.round(start / step),
9827                 r1 = Math.round(stop / step);
9828             if (r0 * step < start) ++r0;
9829             if (r1 * step > stop) --r1;
9830             ticks = new Array(n = r1 - r0 + 1);
9831
9832             while (++i < n) {
9833               ticks[i] = (r0 + i) * step;
9834             }
9835           } else {
9836             step = -step;
9837
9838             var _r = Math.round(start * step),
9839                 _r2 = Math.round(stop * step);
9840
9841             if (_r / step < start) ++_r;
9842             if (_r2 / step > stop) --_r2;
9843             ticks = new Array(n = _r2 - _r + 1);
9844
9845             while (++i < n) {
9846               ticks[i] = (_r + i) / step;
9847             }
9848           }
9849
9850           if (reverse) ticks.reverse();
9851           return ticks;
9852         }
9853         function tickIncrement(start, stop, count) {
9854           var step = (stop - start) / Math.max(0, count),
9855               power = Math.floor(Math.log(step) / Math.LN10),
9856               error = step / Math.pow(10, power);
9857           return power >= 0 ? (error >= e10 ? 10 : error >= e5 ? 5 : error >= e2 ? 2 : 1) * Math.pow(10, power) : -Math.pow(10, -power) / (error >= e10 ? 10 : error >= e5 ? 5 : error >= e2 ? 2 : 1);
9858         }
9859         function tickStep(start, stop, count) {
9860           var step0 = Math.abs(stop - start) / Math.max(0, count),
9861               step1 = Math.pow(10, Math.floor(Math.log(step0) / Math.LN10)),
9862               error = step0 / step1;
9863           if (error >= e10) step1 *= 10;else if (error >= e5) step1 *= 5;else if (error >= e2) step1 *= 2;
9864           return stop < start ? -step1 : step1;
9865         }
9866
9867         function max(values, valueof) {
9868           var max;
9869
9870           if (valueof === undefined) {
9871             var _iterator = _createForOfIteratorHelper(values),
9872                 _step;
9873
9874             try {
9875               for (_iterator.s(); !(_step = _iterator.n()).done;) {
9876                 var value = _step.value;
9877
9878                 if (value != null && (max < value || max === undefined && value >= value)) {
9879                   max = value;
9880                 }
9881               }
9882             } catch (err) {
9883               _iterator.e(err);
9884             } finally {
9885               _iterator.f();
9886             }
9887           } else {
9888             var index = -1;
9889
9890             var _iterator2 = _createForOfIteratorHelper(values),
9891                 _step2;
9892
9893             try {
9894               for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
9895                 var _value = _step2.value;
9896
9897                 if ((_value = valueof(_value, ++index, values)) != null && (max < _value || max === undefined && _value >= _value)) {
9898                   max = _value;
9899                 }
9900               }
9901             } catch (err) {
9902               _iterator2.e(err);
9903             } finally {
9904               _iterator2.f();
9905             }
9906           }
9907
9908           return max;
9909         }
9910
9911         function min$2(values, valueof) {
9912           var min;
9913
9914           if (valueof === undefined) {
9915             var _iterator = _createForOfIteratorHelper(values),
9916                 _step;
9917
9918             try {
9919               for (_iterator.s(); !(_step = _iterator.n()).done;) {
9920                 var value = _step.value;
9921
9922                 if (value != null && (min > value || min === undefined && value >= value)) {
9923                   min = value;
9924                 }
9925               }
9926             } catch (err) {
9927               _iterator.e(err);
9928             } finally {
9929               _iterator.f();
9930             }
9931           } else {
9932             var index = -1;
9933
9934             var _iterator2 = _createForOfIteratorHelper(values),
9935                 _step2;
9936
9937             try {
9938               for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
9939                 var _value = _step2.value;
9940
9941                 if ((_value = valueof(_value, ++index, values)) != null && (min > _value || min === undefined && _value >= _value)) {
9942                   min = _value;
9943                 }
9944               }
9945             } catch (err) {
9946               _iterator2.e(err);
9947             } finally {
9948               _iterator2.f();
9949             }
9950           }
9951
9952           return min;
9953         }
9954
9955         // ISC license, Copyright 2018 Vladimir Agafonkin.
9956
9957         function quickselect$2(array, k) {
9958           var left = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0;
9959           var right = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : array.length - 1;
9960           var compare = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : d3_ascending;
9961
9962           while (right > left) {
9963             if (right - left > 600) {
9964               var n = right - left + 1;
9965               var m = k - left + 1;
9966               var z = Math.log(n);
9967               var s = 0.5 * Math.exp(2 * z / 3);
9968               var sd = 0.5 * Math.sqrt(z * s * (n - s) / n) * (m - n / 2 < 0 ? -1 : 1);
9969               var newLeft = Math.max(left, Math.floor(k - m * s / n + sd));
9970               var newRight = Math.min(right, Math.floor(k + (n - m) * s / n + sd));
9971               quickselect$2(array, k, newLeft, newRight, compare);
9972             }
9973
9974             var t = array[k];
9975             var i = left;
9976             var j = right;
9977             swap$1(array, left, k);
9978             if (compare(array[right], t) > 0) swap$1(array, left, right);
9979
9980             while (i < j) {
9981               swap$1(array, i, j), ++i, --j;
9982
9983               while (compare(array[i], t) < 0) {
9984                 ++i;
9985               }
9986
9987               while (compare(array[j], t) > 0) {
9988                 --j;
9989               }
9990             }
9991
9992             if (compare(array[left], t) === 0) swap$1(array, left, j);else ++j, swap$1(array, j, right);
9993             if (j <= k) left = j + 1;
9994             if (k <= j) right = j - 1;
9995           }
9996
9997           return array;
9998         }
9999
10000         function swap$1(array, i, j) {
10001           var t = array[i];
10002           array[i] = array[j];
10003           array[j] = t;
10004         }
10005
10006         function quantile(values, p, valueof) {
10007           values = Float64Array.from(numbers(values, valueof));
10008           if (!(n = values.length)) return;
10009           if ((p = +p) <= 0 || n < 2) return min$2(values);
10010           if (p >= 1) return max(values);
10011           var n,
10012               i = (n - 1) * p,
10013               i0 = Math.floor(i),
10014               value0 = max(quickselect$2(values, i0).subarray(0, i0 + 1)),
10015               value1 = min$2(values.subarray(i0 + 1));
10016           return value0 + (value1 - value0) * (i - i0);
10017         }
10018
10019         function d3_median (values, valueof) {
10020           return quantile(values, 0.5, valueof);
10021         }
10022
10023         var _marked$2 = /*#__PURE__*/regeneratorRuntime.mark(flatten);
10024
10025         function flatten(arrays) {
10026           var _iterator, _step, array;
10027
10028           return regeneratorRuntime.wrap(function flatten$(_context) {
10029             while (1) {
10030               switch (_context.prev = _context.next) {
10031                 case 0:
10032                   _iterator = _createForOfIteratorHelper(arrays);
10033                   _context.prev = 1;
10034
10035                   _iterator.s();
10036
10037                 case 3:
10038                   if ((_step = _iterator.n()).done) {
10039                     _context.next = 8;
10040                     break;
10041                   }
10042
10043                   array = _step.value;
10044                   return _context.delegateYield(array, "t0", 6);
10045
10046                 case 6:
10047                   _context.next = 3;
10048                   break;
10049
10050                 case 8:
10051                   _context.next = 13;
10052                   break;
10053
10054                 case 10:
10055                   _context.prev = 10;
10056                   _context.t1 = _context["catch"](1);
10057
10058                   _iterator.e(_context.t1);
10059
10060                 case 13:
10061                   _context.prev = 13;
10062
10063                   _iterator.f();
10064
10065                   return _context.finish(13);
10066
10067                 case 16:
10068                 case "end":
10069                   return _context.stop();
10070               }
10071             }
10072           }, _marked$2, null, [[1, 10, 13, 16]]);
10073         }
10074
10075         function merge$4(arrays) {
10076           return Array.from(flatten(arrays));
10077         }
10078
10079         function range$1 (start, stop, step) {
10080           start = +start, stop = +stop, step = (n = arguments.length) < 2 ? (stop = start, start = 0, 1) : n < 3 ? 1 : +step;
10081           var i = -1,
10082               n = Math.max(0, Math.ceil((stop - start) / step)) | 0,
10083               range = new Array(n);
10084
10085           while (++i < n) {
10086             range[i] = start + i * step;
10087           }
10088
10089           return range;
10090         }
10091
10092         // `SameValue` abstract operation
10093         // https://tc39.es/ecma262/#sec-samevalue
10094         // eslint-disable-next-line es/no-object-is -- safe
10095         var sameValue = Object.is || function is(x, y) {
10096           // eslint-disable-next-line no-self-compare -- NaN check
10097           return x === y ? x !== 0 || 1 / x === 1 / y : x != x && y != y;
10098         };
10099
10100         // eslint-disable-next-line es/no-math-hypot -- required for testing
10101         var $hypot = Math.hypot;
10102         var abs$3 = Math.abs;
10103         var sqrt$1 = Math.sqrt;
10104
10105         // Chrome 77 bug
10106         // https://bugs.chromium.org/p/v8/issues/detail?id=9546
10107         var BUGGY = !!$hypot && $hypot(Infinity, NaN) !== Infinity;
10108
10109         // `Math.hypot` method
10110         // https://tc39.es/ecma262/#sec-math.hypot
10111         _export({ target: 'Math', stat: true, forced: BUGGY }, {
10112           // eslint-disable-next-line no-unused-vars -- required for `.length`
10113           hypot: function hypot(value1, value2) {
10114             var sum = 0;
10115             var i = 0;
10116             var aLen = arguments.length;
10117             var larg = 0;
10118             var arg, div;
10119             while (i < aLen) {
10120               arg = abs$3(arguments[i++]);
10121               if (larg < arg) {
10122                 div = larg / arg;
10123                 sum = sum * div * div + 1;
10124                 larg = arg;
10125               } else if (arg > 0) {
10126                 div = arg / larg;
10127                 sum += div * div;
10128               } else sum += arg;
10129             }
10130             return larg === Infinity ? Infinity : larg * sqrt$1(sum);
10131           }
10132         });
10133
10134         // `Math.sign` method implementation
10135         // https://tc39.es/ecma262/#sec-math.sign
10136         // eslint-disable-next-line es/no-math-sign -- safe
10137         var mathSign = Math.sign || function sign(x) {
10138           // eslint-disable-next-line no-self-compare -- NaN check
10139           return (x = +x) == 0 || x != x ? x : x < 0 ? -1 : 1;
10140         };
10141
10142         // `Math.sign` method
10143         // https://tc39.es/ecma262/#sec-math.sign
10144         _export({ target: 'Math', stat: true }, {
10145           sign: mathSign
10146         });
10147
10148         var epsilon$1 = 1e-6;
10149         var epsilon2$1 = 1e-12;
10150         var pi = Math.PI;
10151         var halfPi = pi / 2;
10152         var quarterPi = pi / 4;
10153         var tau = pi * 2;
10154         var degrees$1 = 180 / pi;
10155         var radians = pi / 180;
10156         var abs$2 = Math.abs;
10157         var atan = Math.atan;
10158         var atan2 = Math.atan2;
10159         var cos = Math.cos;
10160         var exp$2 = Math.exp;
10161         var log$1 = Math.log;
10162         var sin = Math.sin;
10163         var sign = Math.sign || function (x) {
10164           return x > 0 ? 1 : x < 0 ? -1 : 0;
10165         };
10166         var sqrt = Math.sqrt;
10167         var tan = Math.tan;
10168         function acos(x) {
10169           return x > 1 ? 0 : x < -1 ? pi : Math.acos(x);
10170         }
10171         function asin(x) {
10172           return x > 1 ? halfPi : x < -1 ? -halfPi : Math.asin(x);
10173         }
10174
10175         function noop$1() {}
10176
10177         function streamGeometry(geometry, stream) {
10178           if (geometry && streamGeometryType.hasOwnProperty(geometry.type)) {
10179             streamGeometryType[geometry.type](geometry, stream);
10180           }
10181         }
10182
10183         var streamObjectType = {
10184           Feature: function Feature(object, stream) {
10185             streamGeometry(object.geometry, stream);
10186           },
10187           FeatureCollection: function FeatureCollection(object, stream) {
10188             var features = object.features,
10189                 i = -1,
10190                 n = features.length;
10191
10192             while (++i < n) {
10193               streamGeometry(features[i].geometry, stream);
10194             }
10195           }
10196         };
10197         var streamGeometryType = {
10198           Sphere: function Sphere(object, stream) {
10199             stream.sphere();
10200           },
10201           Point: function Point(object, stream) {
10202             object = object.coordinates;
10203             stream.point(object[0], object[1], object[2]);
10204           },
10205           MultiPoint: function MultiPoint(object, stream) {
10206             var coordinates = object.coordinates,
10207                 i = -1,
10208                 n = coordinates.length;
10209
10210             while (++i < n) {
10211               object = coordinates[i], stream.point(object[0], object[1], object[2]);
10212             }
10213           },
10214           LineString: function LineString(object, stream) {
10215             streamLine(object.coordinates, stream, 0);
10216           },
10217           MultiLineString: function MultiLineString(object, stream) {
10218             var coordinates = object.coordinates,
10219                 i = -1,
10220                 n = coordinates.length;
10221
10222             while (++i < n) {
10223               streamLine(coordinates[i], stream, 0);
10224             }
10225           },
10226           Polygon: function Polygon(object, stream) {
10227             streamPolygon(object.coordinates, stream);
10228           },
10229           MultiPolygon: function MultiPolygon(object, stream) {
10230             var coordinates = object.coordinates,
10231                 i = -1,
10232                 n = coordinates.length;
10233
10234             while (++i < n) {
10235               streamPolygon(coordinates[i], stream);
10236             }
10237           },
10238           GeometryCollection: function GeometryCollection(object, stream) {
10239             var geometries = object.geometries,
10240                 i = -1,
10241                 n = geometries.length;
10242
10243             while (++i < n) {
10244               streamGeometry(geometries[i], stream);
10245             }
10246           }
10247         };
10248
10249         function streamLine(coordinates, stream, closed) {
10250           var i = -1,
10251               n = coordinates.length - closed,
10252               coordinate;
10253           stream.lineStart();
10254
10255           while (++i < n) {
10256             coordinate = coordinates[i], stream.point(coordinate[0], coordinate[1], coordinate[2]);
10257           }
10258
10259           stream.lineEnd();
10260         }
10261
10262         function streamPolygon(coordinates, stream) {
10263           var i = -1,
10264               n = coordinates.length;
10265           stream.polygonStart();
10266
10267           while (++i < n) {
10268             streamLine(coordinates[i], stream, 1);
10269           }
10270
10271           stream.polygonEnd();
10272         }
10273
10274         function d3_geoStream (object, stream) {
10275           if (object && streamObjectType.hasOwnProperty(object.type)) {
10276             streamObjectType[object.type](object, stream);
10277           } else {
10278             streamGeometry(object, stream);
10279           }
10280         }
10281
10282         var areaRingSum$1 = new Adder(); // hello?
10283
10284         var areaSum$1 = new Adder(),
10285             lambda00$1,
10286             phi00$1,
10287             lambda0$2,
10288             cosPhi0$1,
10289             sinPhi0$1;
10290         var areaStream$1 = {
10291           point: noop$1,
10292           lineStart: noop$1,
10293           lineEnd: noop$1,
10294           polygonStart: function polygonStart() {
10295             areaRingSum$1 = new Adder();
10296             areaStream$1.lineStart = areaRingStart$1;
10297             areaStream$1.lineEnd = areaRingEnd$1;
10298           },
10299           polygonEnd: function polygonEnd() {
10300             var areaRing = +areaRingSum$1;
10301             areaSum$1.add(areaRing < 0 ? tau + areaRing : areaRing);
10302             this.lineStart = this.lineEnd = this.point = noop$1;
10303           },
10304           sphere: function sphere() {
10305             areaSum$1.add(tau);
10306           }
10307         };
10308
10309         function areaRingStart$1() {
10310           areaStream$1.point = areaPointFirst$1;
10311         }
10312
10313         function areaRingEnd$1() {
10314           areaPoint$1(lambda00$1, phi00$1);
10315         }
10316
10317         function areaPointFirst$1(lambda, phi) {
10318           areaStream$1.point = areaPoint$1;
10319           lambda00$1 = lambda, phi00$1 = phi;
10320           lambda *= radians, phi *= radians;
10321           lambda0$2 = lambda, cosPhi0$1 = cos(phi = phi / 2 + quarterPi), sinPhi0$1 = sin(phi);
10322         }
10323
10324         function areaPoint$1(lambda, phi) {
10325           lambda *= radians, phi *= radians;
10326           phi = phi / 2 + quarterPi; // half the angular distance from south pole
10327           // Spherical excess E for a spherical triangle with vertices: south pole,
10328           // previous point, current point.  Uses a formula derived from Cagnoli’s
10329           // theorem.  See Todhunter, Spherical Trig. (1871), Sec. 103, Eq. (2).
10330
10331           var dLambda = lambda - lambda0$2,
10332               sdLambda = dLambda >= 0 ? 1 : -1,
10333               adLambda = sdLambda * dLambda,
10334               cosPhi = cos(phi),
10335               sinPhi = sin(phi),
10336               k = sinPhi0$1 * sinPhi,
10337               u = cosPhi0$1 * cosPhi + k * cos(adLambda),
10338               v = k * sdLambda * sin(adLambda);
10339           areaRingSum$1.add(atan2(v, u)); // Advance the previous points.
10340
10341           lambda0$2 = lambda, cosPhi0$1 = cosPhi, sinPhi0$1 = sinPhi;
10342         }
10343
10344         function d3_geoArea (object) {
10345           areaSum$1 = new Adder();
10346           d3_geoStream(object, areaStream$1);
10347           return areaSum$1 * 2;
10348         }
10349
10350         function spherical(cartesian) {
10351           return [atan2(cartesian[1], cartesian[0]), asin(cartesian[2])];
10352         }
10353         function cartesian(spherical) {
10354           var lambda = spherical[0],
10355               phi = spherical[1],
10356               cosPhi = cos(phi);
10357           return [cosPhi * cos(lambda), cosPhi * sin(lambda), sin(phi)];
10358         }
10359         function cartesianDot(a, b) {
10360           return a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
10361         }
10362         function cartesianCross(a, b) {
10363           return [a[1] * b[2] - a[2] * b[1], a[2] * b[0] - a[0] * b[2], a[0] * b[1] - a[1] * b[0]];
10364         } // TODO return a
10365
10366         function cartesianAddInPlace(a, b) {
10367           a[0] += b[0], a[1] += b[1], a[2] += b[2];
10368         }
10369         function cartesianScale(vector, k) {
10370           return [vector[0] * k, vector[1] * k, vector[2] * k];
10371         } // TODO return d
10372
10373         function cartesianNormalizeInPlace(d) {
10374           var l = sqrt(d[0] * d[0] + d[1] * d[1] + d[2] * d[2]);
10375           d[0] /= l, d[1] /= l, d[2] /= l;
10376         }
10377
10378         var lambda0$1, phi0, lambda1, phi1, // bounds
10379         lambda2, // previous lambda-coordinate
10380         lambda00, phi00, // first point
10381         p0, // previous 3D point
10382         deltaSum, ranges, range;
10383         var boundsStream$1 = {
10384           point: boundsPoint$1,
10385           lineStart: boundsLineStart,
10386           lineEnd: boundsLineEnd,
10387           polygonStart: function polygonStart() {
10388             boundsStream$1.point = boundsRingPoint;
10389             boundsStream$1.lineStart = boundsRingStart;
10390             boundsStream$1.lineEnd = boundsRingEnd;
10391             deltaSum = new Adder();
10392             areaStream$1.polygonStart();
10393           },
10394           polygonEnd: function polygonEnd() {
10395             areaStream$1.polygonEnd();
10396             boundsStream$1.point = boundsPoint$1;
10397             boundsStream$1.lineStart = boundsLineStart;
10398             boundsStream$1.lineEnd = boundsLineEnd;
10399             if (areaRingSum$1 < 0) lambda0$1 = -(lambda1 = 180), phi0 = -(phi1 = 90);else if (deltaSum > epsilon$1) phi1 = 90;else if (deltaSum < -epsilon$1) phi0 = -90;
10400             range[0] = lambda0$1, range[1] = lambda1;
10401           },
10402           sphere: function sphere() {
10403             lambda0$1 = -(lambda1 = 180), phi0 = -(phi1 = 90);
10404           }
10405         };
10406
10407         function boundsPoint$1(lambda, phi) {
10408           ranges.push(range = [lambda0$1 = lambda, lambda1 = lambda]);
10409           if (phi < phi0) phi0 = phi;
10410           if (phi > phi1) phi1 = phi;
10411         }
10412
10413         function linePoint(lambda, phi) {
10414           var p = cartesian([lambda * radians, phi * radians]);
10415
10416           if (p0) {
10417             var normal = cartesianCross(p0, p),
10418                 equatorial = [normal[1], -normal[0], 0],
10419                 inflection = cartesianCross(equatorial, normal);
10420             cartesianNormalizeInPlace(inflection);
10421             inflection = spherical(inflection);
10422             var delta = lambda - lambda2,
10423                 sign = delta > 0 ? 1 : -1,
10424                 lambdai = inflection[0] * degrees$1 * sign,
10425                 phii,
10426                 antimeridian = abs$2(delta) > 180;
10427
10428             if (antimeridian ^ (sign * lambda2 < lambdai && lambdai < sign * lambda)) {
10429               phii = inflection[1] * degrees$1;
10430               if (phii > phi1) phi1 = phii;
10431             } else if (lambdai = (lambdai + 360) % 360 - 180, antimeridian ^ (sign * lambda2 < lambdai && lambdai < sign * lambda)) {
10432               phii = -inflection[1] * degrees$1;
10433               if (phii < phi0) phi0 = phii;
10434             } else {
10435               if (phi < phi0) phi0 = phi;
10436               if (phi > phi1) phi1 = phi;
10437             }
10438
10439             if (antimeridian) {
10440               if (lambda < lambda2) {
10441                 if (angle(lambda0$1, lambda) > angle(lambda0$1, lambda1)) lambda1 = lambda;
10442               } else {
10443                 if (angle(lambda, lambda1) > angle(lambda0$1, lambda1)) lambda0$1 = lambda;
10444               }
10445             } else {
10446               if (lambda1 >= lambda0$1) {
10447                 if (lambda < lambda0$1) lambda0$1 = lambda;
10448                 if (lambda > lambda1) lambda1 = lambda;
10449               } else {
10450                 if (lambda > lambda2) {
10451                   if (angle(lambda0$1, lambda) > angle(lambda0$1, lambda1)) lambda1 = lambda;
10452                 } else {
10453                   if (angle(lambda, lambda1) > angle(lambda0$1, lambda1)) lambda0$1 = lambda;
10454                 }
10455               }
10456             }
10457           } else {
10458             ranges.push(range = [lambda0$1 = lambda, lambda1 = lambda]);
10459           }
10460
10461           if (phi < phi0) phi0 = phi;
10462           if (phi > phi1) phi1 = phi;
10463           p0 = p, lambda2 = lambda;
10464         }
10465
10466         function boundsLineStart() {
10467           boundsStream$1.point = linePoint;
10468         }
10469
10470         function boundsLineEnd() {
10471           range[0] = lambda0$1, range[1] = lambda1;
10472           boundsStream$1.point = boundsPoint$1;
10473           p0 = null;
10474         }
10475
10476         function boundsRingPoint(lambda, phi) {
10477           if (p0) {
10478             var delta = lambda - lambda2;
10479             deltaSum.add(abs$2(delta) > 180 ? delta + (delta > 0 ? 360 : -360) : delta);
10480           } else {
10481             lambda00 = lambda, phi00 = phi;
10482           }
10483
10484           areaStream$1.point(lambda, phi);
10485           linePoint(lambda, phi);
10486         }
10487
10488         function boundsRingStart() {
10489           areaStream$1.lineStart();
10490         }
10491
10492         function boundsRingEnd() {
10493           boundsRingPoint(lambda00, phi00);
10494           areaStream$1.lineEnd();
10495           if (abs$2(deltaSum) > epsilon$1) lambda0$1 = -(lambda1 = 180);
10496           range[0] = lambda0$1, range[1] = lambda1;
10497           p0 = null;
10498         } // Finds the left-right distance between two longitudes.
10499         // This is almost the same as (lambda1 - lambda0 + 360°) % 360°, except that we want
10500         // the distance between ±180° to be 360°.
10501
10502
10503         function angle(lambda0, lambda1) {
10504           return (lambda1 -= lambda0) < 0 ? lambda1 + 360 : lambda1;
10505         }
10506
10507         function rangeCompare(a, b) {
10508           return a[0] - b[0];
10509         }
10510
10511         function rangeContains(range, x) {
10512           return range[0] <= range[1] ? range[0] <= x && x <= range[1] : x < range[0] || range[1] < x;
10513         }
10514
10515         function d3_geoBounds (feature) {
10516           var i, n, a, b, merged, deltaMax, delta;
10517           phi1 = lambda1 = -(lambda0$1 = phi0 = Infinity);
10518           ranges = [];
10519           d3_geoStream(feature, boundsStream$1); // First, sort ranges by their minimum longitudes.
10520
10521           if (n = ranges.length) {
10522             ranges.sort(rangeCompare); // Then, merge any ranges that overlap.
10523
10524             for (i = 1, a = ranges[0], merged = [a]; i < n; ++i) {
10525               b = ranges[i];
10526
10527               if (rangeContains(a, b[0]) || rangeContains(a, b[1])) {
10528                 if (angle(a[0], b[1]) > angle(a[0], a[1])) a[1] = b[1];
10529                 if (angle(b[0], a[1]) > angle(a[0], a[1])) a[0] = b[0];
10530               } else {
10531                 merged.push(a = b);
10532               }
10533             } // Finally, find the largest gap between the merged ranges.
10534             // The final bounding box will be the inverse of this gap.
10535
10536
10537             for (deltaMax = -Infinity, n = merged.length - 1, i = 0, a = merged[n]; i <= n; a = b, ++i) {
10538               b = merged[i];
10539               if ((delta = angle(a[1], b[0])) > deltaMax) deltaMax = delta, lambda0$1 = b[0], lambda1 = a[1];
10540             }
10541           }
10542
10543           ranges = range = null;
10544           return lambda0$1 === Infinity || phi0 === Infinity ? [[NaN, NaN], [NaN, NaN]] : [[lambda0$1, phi0], [lambda1, phi1]];
10545         }
10546
10547         function compose (a, b) {
10548           function compose(x, y) {
10549             return x = a(x, y), b(x[0], x[1]);
10550           }
10551
10552           if (a.invert && b.invert) compose.invert = function (x, y) {
10553             return x = b.invert(x, y), x && a.invert(x[0], x[1]);
10554           };
10555           return compose;
10556         }
10557
10558         function rotationIdentity(lambda, phi) {
10559           return [abs$2(lambda) > pi ? lambda + Math.round(-lambda / tau) * tau : lambda, phi];
10560         }
10561
10562         rotationIdentity.invert = rotationIdentity;
10563         function rotateRadians(deltaLambda, deltaPhi, deltaGamma) {
10564           return (deltaLambda %= tau) ? deltaPhi || deltaGamma ? compose(rotationLambda(deltaLambda), rotationPhiGamma(deltaPhi, deltaGamma)) : rotationLambda(deltaLambda) : deltaPhi || deltaGamma ? rotationPhiGamma(deltaPhi, deltaGamma) : rotationIdentity;
10565         }
10566
10567         function forwardRotationLambda(deltaLambda) {
10568           return function (lambda, phi) {
10569             return lambda += deltaLambda, [lambda > pi ? lambda - tau : lambda < -pi ? lambda + tau : lambda, phi];
10570           };
10571         }
10572
10573         function rotationLambda(deltaLambda) {
10574           var rotation = forwardRotationLambda(deltaLambda);
10575           rotation.invert = forwardRotationLambda(-deltaLambda);
10576           return rotation;
10577         }
10578
10579         function rotationPhiGamma(deltaPhi, deltaGamma) {
10580           var cosDeltaPhi = cos(deltaPhi),
10581               sinDeltaPhi = sin(deltaPhi),
10582               cosDeltaGamma = cos(deltaGamma),
10583               sinDeltaGamma = sin(deltaGamma);
10584
10585           function rotation(lambda, phi) {
10586             var cosPhi = cos(phi),
10587                 x = cos(lambda) * cosPhi,
10588                 y = sin(lambda) * cosPhi,
10589                 z = sin(phi),
10590                 k = z * cosDeltaPhi + x * sinDeltaPhi;
10591             return [atan2(y * cosDeltaGamma - k * sinDeltaGamma, x * cosDeltaPhi - z * sinDeltaPhi), asin(k * cosDeltaGamma + y * sinDeltaGamma)];
10592           }
10593
10594           rotation.invert = function (lambda, phi) {
10595             var cosPhi = cos(phi),
10596                 x = cos(lambda) * cosPhi,
10597                 y = sin(lambda) * cosPhi,
10598                 z = sin(phi),
10599                 k = z * cosDeltaGamma - y * sinDeltaGamma;
10600             return [atan2(y * cosDeltaGamma + z * sinDeltaGamma, x * cosDeltaPhi + k * sinDeltaPhi), asin(k * cosDeltaPhi - x * sinDeltaPhi)];
10601           };
10602
10603           return rotation;
10604         }
10605
10606         function rotation (rotate) {
10607           rotate = rotateRadians(rotate[0] * radians, rotate[1] * radians, rotate.length > 2 ? rotate[2] * radians : 0);
10608
10609           function forward(coordinates) {
10610             coordinates = rotate(coordinates[0] * radians, coordinates[1] * radians);
10611             return coordinates[0] *= degrees$1, coordinates[1] *= degrees$1, coordinates;
10612           }
10613
10614           forward.invert = function (coordinates) {
10615             coordinates = rotate.invert(coordinates[0] * radians, coordinates[1] * radians);
10616             return coordinates[0] *= degrees$1, coordinates[1] *= degrees$1, coordinates;
10617           };
10618
10619           return forward;
10620         }
10621
10622         function circleStream(stream, radius, delta, direction, t0, t1) {
10623           if (!delta) return;
10624           var cosRadius = cos(radius),
10625               sinRadius = sin(radius),
10626               step = direction * delta;
10627
10628           if (t0 == null) {
10629             t0 = radius + direction * tau;
10630             t1 = radius - step / 2;
10631           } else {
10632             t0 = circleRadius(cosRadius, t0);
10633             t1 = circleRadius(cosRadius, t1);
10634             if (direction > 0 ? t0 < t1 : t0 > t1) t0 += direction * tau;
10635           }
10636
10637           for (var point, t = t0; direction > 0 ? t > t1 : t < t1; t -= step) {
10638             point = spherical([cosRadius, -sinRadius * cos(t), -sinRadius * sin(t)]);
10639             stream.point(point[0], point[1]);
10640           }
10641         } // Returns the signed angle of a cartesian point relative to [cosRadius, 0, 0].
10642
10643         function circleRadius(cosRadius, point) {
10644           point = cartesian(point), point[0] -= cosRadius;
10645           cartesianNormalizeInPlace(point);
10646           var radius = acos(-point[1]);
10647           return ((-point[2] < 0 ? -radius : radius) + tau - epsilon$1) % tau;
10648         }
10649
10650         function clipBuffer () {
10651           var lines = [],
10652               line;
10653           return {
10654             point: function point(x, y, m) {
10655               line.push([x, y, m]);
10656             },
10657             lineStart: function lineStart() {
10658               lines.push(line = []);
10659             },
10660             lineEnd: noop$1,
10661             rejoin: function rejoin() {
10662               if (lines.length > 1) lines.push(lines.pop().concat(lines.shift()));
10663             },
10664             result: function result() {
10665               var result = lines;
10666               lines = [];
10667               line = null;
10668               return result;
10669             }
10670           };
10671         }
10672
10673         function pointEqual (a, b) {
10674           return abs$2(a[0] - b[0]) < epsilon$1 && abs$2(a[1] - b[1]) < epsilon$1;
10675         }
10676
10677         function Intersection(point, points, other, entry) {
10678           this.x = point;
10679           this.z = points;
10680           this.o = other; // another intersection
10681
10682           this.e = entry; // is an entry?
10683
10684           this.v = false; // visited
10685
10686           this.n = this.p = null; // next & previous
10687         } // A generalized polygon clipping algorithm: given a polygon that has been cut
10688         // into its visible line segments, and rejoins the segments by interpolating
10689         // along the clip edge.
10690
10691
10692         function clipRejoin (segments, compareIntersection, startInside, interpolate, stream) {
10693           var subject = [],
10694               clip = [],
10695               i,
10696               n;
10697           segments.forEach(function (segment) {
10698             if ((n = segment.length - 1) <= 0) return;
10699             var n,
10700                 p0 = segment[0],
10701                 p1 = segment[n],
10702                 x;
10703
10704             if (pointEqual(p0, p1)) {
10705               if (!p0[2] && !p1[2]) {
10706                 stream.lineStart();
10707
10708                 for (i = 0; i < n; ++i) {
10709                   stream.point((p0 = segment[i])[0], p0[1]);
10710                 }
10711
10712                 stream.lineEnd();
10713                 return;
10714               } // handle degenerate cases by moving the point
10715
10716
10717               p1[0] += 2 * epsilon$1;
10718             }
10719
10720             subject.push(x = new Intersection(p0, segment, null, true));
10721             clip.push(x.o = new Intersection(p0, null, x, false));
10722             subject.push(x = new Intersection(p1, segment, null, false));
10723             clip.push(x.o = new Intersection(p1, null, x, true));
10724           });
10725           if (!subject.length) return;
10726           clip.sort(compareIntersection);
10727           link(subject);
10728           link(clip);
10729
10730           for (i = 0, n = clip.length; i < n; ++i) {
10731             clip[i].e = startInside = !startInside;
10732           }
10733
10734           var start = subject[0],
10735               points,
10736               point;
10737
10738           while (1) {
10739             // Find first unvisited intersection.
10740             var current = start,
10741                 isSubject = true;
10742
10743             while (current.v) {
10744               if ((current = current.n) === start) return;
10745             }
10746
10747             points = current.z;
10748             stream.lineStart();
10749
10750             do {
10751               current.v = current.o.v = true;
10752
10753               if (current.e) {
10754                 if (isSubject) {
10755                   for (i = 0, n = points.length; i < n; ++i) {
10756                     stream.point((point = points[i])[0], point[1]);
10757                   }
10758                 } else {
10759                   interpolate(current.x, current.n.x, 1, stream);
10760                 }
10761
10762                 current = current.n;
10763               } else {
10764                 if (isSubject) {
10765                   points = current.p.z;
10766
10767                   for (i = points.length - 1; i >= 0; --i) {
10768                     stream.point((point = points[i])[0], point[1]);
10769                   }
10770                 } else {
10771                   interpolate(current.x, current.p.x, -1, stream);
10772                 }
10773
10774                 current = current.p;
10775               }
10776
10777               current = current.o;
10778               points = current.z;
10779               isSubject = !isSubject;
10780             } while (!current.v);
10781
10782             stream.lineEnd();
10783           }
10784         }
10785
10786         function link(array) {
10787           if (!(n = array.length)) return;
10788           var n,
10789               i = 0,
10790               a = array[0],
10791               b;
10792
10793           while (++i < n) {
10794             a.n = b = array[i];
10795             b.p = a;
10796             a = b;
10797           }
10798
10799           a.n = b = array[0];
10800           b.p = a;
10801         }
10802
10803         function longitude(point) {
10804           if (abs$2(point[0]) <= pi) return point[0];else return sign(point[0]) * ((abs$2(point[0]) + pi) % tau - pi);
10805         }
10806
10807         function polygonContains (polygon, point) {
10808           var lambda = longitude(point),
10809               phi = point[1],
10810               sinPhi = sin(phi),
10811               normal = [sin(lambda), -cos(lambda), 0],
10812               angle = 0,
10813               winding = 0;
10814           var sum = new Adder();
10815           if (sinPhi === 1) phi = halfPi + epsilon$1;else if (sinPhi === -1) phi = -halfPi - epsilon$1;
10816
10817           for (var i = 0, n = polygon.length; i < n; ++i) {
10818             if (!(m = (ring = polygon[i]).length)) continue;
10819             var ring,
10820                 m,
10821                 point0 = ring[m - 1],
10822                 lambda0 = longitude(point0),
10823                 phi0 = point0[1] / 2 + quarterPi,
10824                 sinPhi0 = sin(phi0),
10825                 cosPhi0 = cos(phi0);
10826
10827             for (var j = 0; j < m; ++j, lambda0 = lambda1, sinPhi0 = sinPhi1, cosPhi0 = cosPhi1, point0 = point1) {
10828               var point1 = ring[j],
10829                   lambda1 = longitude(point1),
10830                   phi1 = point1[1] / 2 + quarterPi,
10831                   sinPhi1 = sin(phi1),
10832                   cosPhi1 = cos(phi1),
10833                   delta = lambda1 - lambda0,
10834                   sign = delta >= 0 ? 1 : -1,
10835                   absDelta = sign * delta,
10836                   antimeridian = absDelta > pi,
10837                   k = sinPhi0 * sinPhi1;
10838               sum.add(atan2(k * sign * sin(absDelta), cosPhi0 * cosPhi1 + k * cos(absDelta)));
10839               angle += antimeridian ? delta + sign * tau : delta; // Are the longitudes either side of the point’s meridian (lambda),
10840               // and are the latitudes smaller than the parallel (phi)?
10841
10842               if (antimeridian ^ lambda0 >= lambda ^ lambda1 >= lambda) {
10843                 var arc = cartesianCross(cartesian(point0), cartesian(point1));
10844                 cartesianNormalizeInPlace(arc);
10845                 var intersection = cartesianCross(normal, arc);
10846                 cartesianNormalizeInPlace(intersection);
10847                 var phiArc = (antimeridian ^ delta >= 0 ? -1 : 1) * asin(intersection[2]);
10848
10849                 if (phi > phiArc || phi === phiArc && (arc[0] || arc[1])) {
10850                   winding += antimeridian ^ delta >= 0 ? 1 : -1;
10851                 }
10852               }
10853             }
10854           } // First, determine whether the South pole is inside or outside:
10855           //
10856           // It is inside if:
10857           // * the polygon winds around it in a clockwise direction.
10858           // * the polygon does not (cumulatively) wind around it, but has a negative
10859           //   (counter-clockwise) area.
10860           //
10861           // Second, count the (signed) number of times a segment crosses a lambda
10862           // from the point to the South pole.  If it is zero, then the point is the
10863           // same side as the South pole.
10864
10865
10866           return (angle < -epsilon$1 || angle < epsilon$1 && sum < -epsilon2$1) ^ winding & 1;
10867         }
10868
10869         function clip (pointVisible, clipLine, interpolate, start) {
10870           return function (sink) {
10871             var line = clipLine(sink),
10872                 ringBuffer = clipBuffer(),
10873                 ringSink = clipLine(ringBuffer),
10874                 polygonStarted = false,
10875                 polygon,
10876                 segments,
10877                 ring;
10878             var clip = {
10879               point: point,
10880               lineStart: lineStart,
10881               lineEnd: lineEnd,
10882               polygonStart: function polygonStart() {
10883                 clip.point = pointRing;
10884                 clip.lineStart = ringStart;
10885                 clip.lineEnd = ringEnd;
10886                 segments = [];
10887                 polygon = [];
10888               },
10889               polygonEnd: function polygonEnd() {
10890                 clip.point = point;
10891                 clip.lineStart = lineStart;
10892                 clip.lineEnd = lineEnd;
10893                 segments = merge$4(segments);
10894                 var startInside = polygonContains(polygon, start);
10895
10896                 if (segments.length) {
10897                   if (!polygonStarted) sink.polygonStart(), polygonStarted = true;
10898                   clipRejoin(segments, compareIntersection, startInside, interpolate, sink);
10899                 } else if (startInside) {
10900                   if (!polygonStarted) sink.polygonStart(), polygonStarted = true;
10901                   sink.lineStart();
10902                   interpolate(null, null, 1, sink);
10903                   sink.lineEnd();
10904                 }
10905
10906                 if (polygonStarted) sink.polygonEnd(), polygonStarted = false;
10907                 segments = polygon = null;
10908               },
10909               sphere: function sphere() {
10910                 sink.polygonStart();
10911                 sink.lineStart();
10912                 interpolate(null, null, 1, sink);
10913                 sink.lineEnd();
10914                 sink.polygonEnd();
10915               }
10916             };
10917
10918             function point(lambda, phi) {
10919               if (pointVisible(lambda, phi)) sink.point(lambda, phi);
10920             }
10921
10922             function pointLine(lambda, phi) {
10923               line.point(lambda, phi);
10924             }
10925
10926             function lineStart() {
10927               clip.point = pointLine;
10928               line.lineStart();
10929             }
10930
10931             function lineEnd() {
10932               clip.point = point;
10933               line.lineEnd();
10934             }
10935
10936             function pointRing(lambda, phi) {
10937               ring.push([lambda, phi]);
10938               ringSink.point(lambda, phi);
10939             }
10940
10941             function ringStart() {
10942               ringSink.lineStart();
10943               ring = [];
10944             }
10945
10946             function ringEnd() {
10947               pointRing(ring[0][0], ring[0][1]);
10948               ringSink.lineEnd();
10949               var clean = ringSink.clean(),
10950                   ringSegments = ringBuffer.result(),
10951                   i,
10952                   n = ringSegments.length,
10953                   m,
10954                   segment,
10955                   point;
10956               ring.pop();
10957               polygon.push(ring);
10958               ring = null;
10959               if (!n) return; // No intersections.
10960
10961               if (clean & 1) {
10962                 segment = ringSegments[0];
10963
10964                 if ((m = segment.length - 1) > 0) {
10965                   if (!polygonStarted) sink.polygonStart(), polygonStarted = true;
10966                   sink.lineStart();
10967
10968                   for (i = 0; i < m; ++i) {
10969                     sink.point((point = segment[i])[0], point[1]);
10970                   }
10971
10972                   sink.lineEnd();
10973                 }
10974
10975                 return;
10976               } // Rejoin connected segments.
10977               // TODO reuse ringBuffer.rejoin()?
10978
10979
10980               if (n > 1 && clean & 2) ringSegments.push(ringSegments.pop().concat(ringSegments.shift()));
10981               segments.push(ringSegments.filter(validSegment));
10982             }
10983
10984             return clip;
10985           };
10986         }
10987
10988         function validSegment(segment) {
10989           return segment.length > 1;
10990         } // Intersections are sorted along the clip edge. For both antimeridian cutting
10991         // and circle clipping, the same comparison is used.
10992
10993
10994         function compareIntersection(a, b) {
10995           return ((a = a.x)[0] < 0 ? a[1] - halfPi - epsilon$1 : halfPi - a[1]) - ((b = b.x)[0] < 0 ? b[1] - halfPi - epsilon$1 : halfPi - b[1]);
10996         }
10997
10998         var clipAntimeridian = clip(function () {
10999           return true;
11000         }, clipAntimeridianLine, clipAntimeridianInterpolate, [-pi, -halfPi]); // Takes a line and cuts into visible segments. Return values: 0 - there were
11001         // intersections or the line was empty; 1 - no intersections; 2 - there were
11002         // intersections, and the first and last segments should be rejoined.
11003
11004         function clipAntimeridianLine(stream) {
11005           var lambda0 = NaN,
11006               phi0 = NaN,
11007               sign0 = NaN,
11008               _clean; // no intersections
11009
11010
11011           return {
11012             lineStart: function lineStart() {
11013               stream.lineStart();
11014               _clean = 1;
11015             },
11016             point: function point(lambda1, phi1) {
11017               var sign1 = lambda1 > 0 ? pi : -pi,
11018                   delta = abs$2(lambda1 - lambda0);
11019
11020               if (abs$2(delta - pi) < epsilon$1) {
11021                 // line crosses a pole
11022                 stream.point(lambda0, phi0 = (phi0 + phi1) / 2 > 0 ? halfPi : -halfPi);
11023                 stream.point(sign0, phi0);
11024                 stream.lineEnd();
11025                 stream.lineStart();
11026                 stream.point(sign1, phi0);
11027                 stream.point(lambda1, phi0);
11028                 _clean = 0;
11029               } else if (sign0 !== sign1 && delta >= pi) {
11030                 // line crosses antimeridian
11031                 if (abs$2(lambda0 - sign0) < epsilon$1) lambda0 -= sign0 * epsilon$1; // handle degeneracies
11032
11033                 if (abs$2(lambda1 - sign1) < epsilon$1) lambda1 -= sign1 * epsilon$1;
11034                 phi0 = clipAntimeridianIntersect(lambda0, phi0, lambda1, phi1);
11035                 stream.point(sign0, phi0);
11036                 stream.lineEnd();
11037                 stream.lineStart();
11038                 stream.point(sign1, phi0);
11039                 _clean = 0;
11040               }
11041
11042               stream.point(lambda0 = lambda1, phi0 = phi1);
11043               sign0 = sign1;
11044             },
11045             lineEnd: function lineEnd() {
11046               stream.lineEnd();
11047               lambda0 = phi0 = NaN;
11048             },
11049             clean: function clean() {
11050               return 2 - _clean; // if intersections, rejoin first and last segments
11051             }
11052           };
11053         }
11054
11055         function clipAntimeridianIntersect(lambda0, phi0, lambda1, phi1) {
11056           var cosPhi0,
11057               cosPhi1,
11058               sinLambda0Lambda1 = sin(lambda0 - lambda1);
11059           return abs$2(sinLambda0Lambda1) > epsilon$1 ? atan((sin(phi0) * (cosPhi1 = cos(phi1)) * sin(lambda1) - sin(phi1) * (cosPhi0 = cos(phi0)) * sin(lambda0)) / (cosPhi0 * cosPhi1 * sinLambda0Lambda1)) : (phi0 + phi1) / 2;
11060         }
11061
11062         function clipAntimeridianInterpolate(from, to, direction, stream) {
11063           var phi;
11064
11065           if (from == null) {
11066             phi = direction * halfPi;
11067             stream.point(-pi, phi);
11068             stream.point(0, phi);
11069             stream.point(pi, phi);
11070             stream.point(pi, 0);
11071             stream.point(pi, -phi);
11072             stream.point(0, -phi);
11073             stream.point(-pi, -phi);
11074             stream.point(-pi, 0);
11075             stream.point(-pi, phi);
11076           } else if (abs$2(from[0] - to[0]) > epsilon$1) {
11077             var lambda = from[0] < to[0] ? pi : -pi;
11078             phi = direction * lambda / 2;
11079             stream.point(-lambda, phi);
11080             stream.point(0, phi);
11081             stream.point(lambda, phi);
11082           } else {
11083             stream.point(to[0], to[1]);
11084           }
11085         }
11086
11087         function clipCircle (radius) {
11088           var cr = cos(radius),
11089               delta = 6 * radians,
11090               smallRadius = cr > 0,
11091               notHemisphere = abs$2(cr) > epsilon$1; // TODO optimise for this common case
11092
11093           function interpolate(from, to, direction, stream) {
11094             circleStream(stream, radius, delta, direction, from, to);
11095           }
11096
11097           function visible(lambda, phi) {
11098             return cos(lambda) * cos(phi) > cr;
11099           } // Takes a line and cuts into visible segments. Return values used for polygon
11100           // clipping: 0 - there were intersections or the line was empty; 1 - no
11101           // intersections 2 - there were intersections, and the first and last segments
11102           // should be rejoined.
11103
11104
11105           function clipLine(stream) {
11106             var point0, // previous point
11107             c0, // code for previous point
11108             v0, // visibility of previous point
11109             v00, // visibility of first point
11110             _clean; // no intersections
11111
11112
11113             return {
11114               lineStart: function lineStart() {
11115                 v00 = v0 = false;
11116                 _clean = 1;
11117               },
11118               point: function point(lambda, phi) {
11119                 var point1 = [lambda, phi],
11120                     point2,
11121                     v = visible(lambda, phi),
11122                     c = smallRadius ? v ? 0 : code(lambda, phi) : v ? code(lambda + (lambda < 0 ? pi : -pi), phi) : 0;
11123                 if (!point0 && (v00 = v0 = v)) stream.lineStart();
11124
11125                 if (v !== v0) {
11126                   point2 = intersect(point0, point1);
11127                   if (!point2 || pointEqual(point0, point2) || pointEqual(point1, point2)) point1[2] = 1;
11128                 }
11129
11130                 if (v !== v0) {
11131                   _clean = 0;
11132
11133                   if (v) {
11134                     // outside going in
11135                     stream.lineStart();
11136                     point2 = intersect(point1, point0);
11137                     stream.point(point2[0], point2[1]);
11138                   } else {
11139                     // inside going out
11140                     point2 = intersect(point0, point1);
11141                     stream.point(point2[0], point2[1], 2);
11142                     stream.lineEnd();
11143                   }
11144
11145                   point0 = point2;
11146                 } else if (notHemisphere && point0 && smallRadius ^ v) {
11147                   var t; // If the codes for two points are different, or are both zero,
11148                   // and there this segment intersects with the small circle.
11149
11150                   if (!(c & c0) && (t = intersect(point1, point0, true))) {
11151                     _clean = 0;
11152
11153                     if (smallRadius) {
11154                       stream.lineStart();
11155                       stream.point(t[0][0], t[0][1]);
11156                       stream.point(t[1][0], t[1][1]);
11157                       stream.lineEnd();
11158                     } else {
11159                       stream.point(t[1][0], t[1][1]);
11160                       stream.lineEnd();
11161                       stream.lineStart();
11162                       stream.point(t[0][0], t[0][1], 3);
11163                     }
11164                   }
11165                 }
11166
11167                 if (v && (!point0 || !pointEqual(point0, point1))) {
11168                   stream.point(point1[0], point1[1]);
11169                 }
11170
11171                 point0 = point1, v0 = v, c0 = c;
11172               },
11173               lineEnd: function lineEnd() {
11174                 if (v0) stream.lineEnd();
11175                 point0 = null;
11176               },
11177               // Rejoin first and last segments if there were intersections and the first
11178               // and last points were visible.
11179               clean: function clean() {
11180                 return _clean | (v00 && v0) << 1;
11181               }
11182             };
11183           } // Intersects the great circle between a and b with the clip circle.
11184
11185
11186           function intersect(a, b, two) {
11187             var pa = cartesian(a),
11188                 pb = cartesian(b); // We have two planes, n1.p = d1 and n2.p = d2.
11189             // Find intersection line p(t) = c1 n1 + c2 n2 + t (n1 ⨯ n2).
11190
11191             var n1 = [1, 0, 0],
11192                 // normal
11193             n2 = cartesianCross(pa, pb),
11194                 n2n2 = cartesianDot(n2, n2),
11195                 n1n2 = n2[0],
11196                 // cartesianDot(n1, n2),
11197             determinant = n2n2 - n1n2 * n1n2; // Two polar points.
11198
11199             if (!determinant) return !two && a;
11200             var c1 = cr * n2n2 / determinant,
11201                 c2 = -cr * n1n2 / determinant,
11202                 n1xn2 = cartesianCross(n1, n2),
11203                 A = cartesianScale(n1, c1),
11204                 B = cartesianScale(n2, c2);
11205             cartesianAddInPlace(A, B); // Solve |p(t)|^2 = 1.
11206
11207             var u = n1xn2,
11208                 w = cartesianDot(A, u),
11209                 uu = cartesianDot(u, u),
11210                 t2 = w * w - uu * (cartesianDot(A, A) - 1);
11211             if (t2 < 0) return;
11212             var t = sqrt(t2),
11213                 q = cartesianScale(u, (-w - t) / uu);
11214             cartesianAddInPlace(q, A);
11215             q = spherical(q);
11216             if (!two) return q; // Two intersection points.
11217
11218             var lambda0 = a[0],
11219                 lambda1 = b[0],
11220                 phi0 = a[1],
11221                 phi1 = b[1],
11222                 z;
11223             if (lambda1 < lambda0) z = lambda0, lambda0 = lambda1, lambda1 = z;
11224             var delta = lambda1 - lambda0,
11225                 polar = abs$2(delta - pi) < epsilon$1,
11226                 meridian = polar || delta < epsilon$1;
11227             if (!polar && phi1 < phi0) z = phi0, phi0 = phi1, phi1 = z; // Check that the first point is between a and b.
11228
11229             if (meridian ? polar ? phi0 + phi1 > 0 ^ q[1] < (abs$2(q[0] - lambda0) < epsilon$1 ? phi0 : phi1) : phi0 <= q[1] && q[1] <= phi1 : delta > pi ^ (lambda0 <= q[0] && q[0] <= lambda1)) {
11230               var q1 = cartesianScale(u, (-w + t) / uu);
11231               cartesianAddInPlace(q1, A);
11232               return [q, spherical(q1)];
11233             }
11234           } // Generates a 4-bit vector representing the location of a point relative to
11235           // the small circle's bounding box.
11236
11237
11238           function code(lambda, phi) {
11239             var r = smallRadius ? radius : pi - radius,
11240                 code = 0;
11241             if (lambda < -r) code |= 1; // left
11242             else if (lambda > r) code |= 2; // right
11243
11244             if (phi < -r) code |= 4; // below
11245             else if (phi > r) code |= 8; // above
11246
11247             return code;
11248           }
11249
11250           return clip(visible, clipLine, interpolate, smallRadius ? [0, -radius] : [-pi, radius - pi]);
11251         }
11252
11253         function clipLine (a, b, x0, y0, x1, y1) {
11254           var ax = a[0],
11255               ay = a[1],
11256               bx = b[0],
11257               by = b[1],
11258               t0 = 0,
11259               t1 = 1,
11260               dx = bx - ax,
11261               dy = by - ay,
11262               r;
11263           r = x0 - ax;
11264           if (!dx && r > 0) return;
11265           r /= dx;
11266
11267           if (dx < 0) {
11268             if (r < t0) return;
11269             if (r < t1) t1 = r;
11270           } else if (dx > 0) {
11271             if (r > t1) return;
11272             if (r > t0) t0 = r;
11273           }
11274
11275           r = x1 - ax;
11276           if (!dx && r < 0) return;
11277           r /= dx;
11278
11279           if (dx < 0) {
11280             if (r > t1) return;
11281             if (r > t0) t0 = r;
11282           } else if (dx > 0) {
11283             if (r < t0) return;
11284             if (r < t1) t1 = r;
11285           }
11286
11287           r = y0 - ay;
11288           if (!dy && r > 0) return;
11289           r /= dy;
11290
11291           if (dy < 0) {
11292             if (r < t0) return;
11293             if (r < t1) t1 = r;
11294           } else if (dy > 0) {
11295             if (r > t1) return;
11296             if (r > t0) t0 = r;
11297           }
11298
11299           r = y1 - ay;
11300           if (!dy && r < 0) return;
11301           r /= dy;
11302
11303           if (dy < 0) {
11304             if (r > t1) return;
11305             if (r > t0) t0 = r;
11306           } else if (dy > 0) {
11307             if (r < t0) return;
11308             if (r < t1) t1 = r;
11309           }
11310
11311           if (t0 > 0) a[0] = ax + t0 * dx, a[1] = ay + t0 * dy;
11312           if (t1 < 1) b[0] = ax + t1 * dx, b[1] = ay + t1 * dy;
11313           return true;
11314         }
11315
11316         var clipMax = 1e9,
11317             clipMin = -clipMax; // TODO Use d3-polygon’s polygonContains here for the ring check?
11318         // TODO Eliminate duplicate buffering in clipBuffer and polygon.push?
11319
11320         function clipRectangle(x0, y0, x1, y1) {
11321           function visible(x, y) {
11322             return x0 <= x && x <= x1 && y0 <= y && y <= y1;
11323           }
11324
11325           function interpolate(from, to, direction, stream) {
11326             var a = 0,
11327                 a1 = 0;
11328
11329             if (from == null || (a = corner(from, direction)) !== (a1 = corner(to, direction)) || comparePoint(from, to) < 0 ^ direction > 0) {
11330               do {
11331                 stream.point(a === 0 || a === 3 ? x0 : x1, a > 1 ? y1 : y0);
11332               } while ((a = (a + direction + 4) % 4) !== a1);
11333             } else {
11334               stream.point(to[0], to[1]);
11335             }
11336           }
11337
11338           function corner(p, direction) {
11339             return abs$2(p[0] - x0) < epsilon$1 ? direction > 0 ? 0 : 3 : abs$2(p[0] - x1) < epsilon$1 ? direction > 0 ? 2 : 1 : abs$2(p[1] - y0) < epsilon$1 ? direction > 0 ? 1 : 0 : direction > 0 ? 3 : 2; // abs(p[1] - y1) < epsilon
11340           }
11341
11342           function compareIntersection(a, b) {
11343             return comparePoint(a.x, b.x);
11344           }
11345
11346           function comparePoint(a, b) {
11347             var ca = corner(a, 1),
11348                 cb = corner(b, 1);
11349             return ca !== cb ? ca - cb : ca === 0 ? b[1] - a[1] : ca === 1 ? a[0] - b[0] : ca === 2 ? a[1] - b[1] : b[0] - a[0];
11350           }
11351
11352           return function (stream) {
11353             var activeStream = stream,
11354                 bufferStream = clipBuffer(),
11355                 segments,
11356                 polygon,
11357                 ring,
11358                 x__,
11359                 y__,
11360                 v__,
11361                 // first point
11362             x_,
11363                 y_,
11364                 v_,
11365                 // previous point
11366             first,
11367                 clean;
11368             var clipStream = {
11369               point: point,
11370               lineStart: lineStart,
11371               lineEnd: lineEnd,
11372               polygonStart: polygonStart,
11373               polygonEnd: polygonEnd
11374             };
11375
11376             function point(x, y) {
11377               if (visible(x, y)) activeStream.point(x, y);
11378             }
11379
11380             function polygonInside() {
11381               var winding = 0;
11382
11383               for (var i = 0, n = polygon.length; i < n; ++i) {
11384                 for (var ring = polygon[i], j = 1, m = ring.length, point = ring[0], a0, a1, b0 = point[0], b1 = point[1]; j < m; ++j) {
11385                   a0 = b0, a1 = b1, point = ring[j], b0 = point[0], b1 = point[1];
11386
11387                   if (a1 <= y1) {
11388                     if (b1 > y1 && (b0 - a0) * (y1 - a1) > (b1 - a1) * (x0 - a0)) ++winding;
11389                   } else {
11390                     if (b1 <= y1 && (b0 - a0) * (y1 - a1) < (b1 - a1) * (x0 - a0)) --winding;
11391                   }
11392                 }
11393               }
11394
11395               return winding;
11396             } // Buffer geometry within a polygon and then clip it en masse.
11397
11398
11399             function polygonStart() {
11400               activeStream = bufferStream, segments = [], polygon = [], clean = true;
11401             }
11402
11403             function polygonEnd() {
11404               var startInside = polygonInside(),
11405                   cleanInside = clean && startInside,
11406                   visible = (segments = merge$4(segments)).length;
11407
11408               if (cleanInside || visible) {
11409                 stream.polygonStart();
11410
11411                 if (cleanInside) {
11412                   stream.lineStart();
11413                   interpolate(null, null, 1, stream);
11414                   stream.lineEnd();
11415                 }
11416
11417                 if (visible) {
11418                   clipRejoin(segments, compareIntersection, startInside, interpolate, stream);
11419                 }
11420
11421                 stream.polygonEnd();
11422               }
11423
11424               activeStream = stream, segments = polygon = ring = null;
11425             }
11426
11427             function lineStart() {
11428               clipStream.point = linePoint;
11429               if (polygon) polygon.push(ring = []);
11430               first = true;
11431               v_ = false;
11432               x_ = y_ = NaN;
11433             } // TODO rather than special-case polygons, simply handle them separately.
11434             // Ideally, coincident intersection points should be jittered to avoid
11435             // clipping issues.
11436
11437
11438             function lineEnd() {
11439               if (segments) {
11440                 linePoint(x__, y__);
11441                 if (v__ && v_) bufferStream.rejoin();
11442                 segments.push(bufferStream.result());
11443               }
11444
11445               clipStream.point = point;
11446               if (v_) activeStream.lineEnd();
11447             }
11448
11449             function linePoint(x, y) {
11450               var v = visible(x, y);
11451               if (polygon) ring.push([x, y]);
11452
11453               if (first) {
11454                 x__ = x, y__ = y, v__ = v;
11455                 first = false;
11456
11457                 if (v) {
11458                   activeStream.lineStart();
11459                   activeStream.point(x, y);
11460                 }
11461               } else {
11462                 if (v && v_) activeStream.point(x, y);else {
11463                   var a = [x_ = Math.max(clipMin, Math.min(clipMax, x_)), y_ = Math.max(clipMin, Math.min(clipMax, y_))],
11464                       b = [x = Math.max(clipMin, Math.min(clipMax, x)), y = Math.max(clipMin, Math.min(clipMax, y))];
11465
11466                   if (clipLine(a, b, x0, y0, x1, y1)) {
11467                     if (!v_) {
11468                       activeStream.lineStart();
11469                       activeStream.point(a[0], a[1]);
11470                     }
11471
11472                     activeStream.point(b[0], b[1]);
11473                     if (!v) activeStream.lineEnd();
11474                     clean = false;
11475                   } else if (v) {
11476                     activeStream.lineStart();
11477                     activeStream.point(x, y);
11478                     clean = false;
11479                   }
11480                 }
11481               }
11482
11483               x_ = x, y_ = y, v_ = v;
11484             }
11485
11486             return clipStream;
11487           };
11488         }
11489
11490         var lengthSum$1, lambda0, sinPhi0, cosPhi0;
11491         var lengthStream$1 = {
11492           sphere: noop$1,
11493           point: noop$1,
11494           lineStart: lengthLineStart,
11495           lineEnd: noop$1,
11496           polygonStart: noop$1,
11497           polygonEnd: noop$1
11498         };
11499
11500         function lengthLineStart() {
11501           lengthStream$1.point = lengthPointFirst$1;
11502           lengthStream$1.lineEnd = lengthLineEnd;
11503         }
11504
11505         function lengthLineEnd() {
11506           lengthStream$1.point = lengthStream$1.lineEnd = noop$1;
11507         }
11508
11509         function lengthPointFirst$1(lambda, phi) {
11510           lambda *= radians, phi *= radians;
11511           lambda0 = lambda, sinPhi0 = sin(phi), cosPhi0 = cos(phi);
11512           lengthStream$1.point = lengthPoint$1;
11513         }
11514
11515         function lengthPoint$1(lambda, phi) {
11516           lambda *= radians, phi *= radians;
11517           var sinPhi = sin(phi),
11518               cosPhi = cos(phi),
11519               delta = abs$2(lambda - lambda0),
11520               cosDelta = cos(delta),
11521               sinDelta = sin(delta),
11522               x = cosPhi * sinDelta,
11523               y = cosPhi0 * sinPhi - sinPhi0 * cosPhi * cosDelta,
11524               z = sinPhi0 * sinPhi + cosPhi0 * cosPhi * cosDelta;
11525           lengthSum$1.add(atan2(sqrt(x * x + y * y), z));
11526           lambda0 = lambda, sinPhi0 = sinPhi, cosPhi0 = cosPhi;
11527         }
11528
11529         function d3_geoLength (object) {
11530           lengthSum$1 = new Adder();
11531           d3_geoStream(object, lengthStream$1);
11532           return +lengthSum$1;
11533         }
11534
11535         var identity$4 = (function (x) {
11536           return x;
11537         });
11538
11539         var areaSum = new Adder(),
11540             areaRingSum = new Adder(),
11541             x00$2,
11542             y00$2,
11543             x0$3,
11544             y0$3;
11545         var areaStream = {
11546           point: noop$1,
11547           lineStart: noop$1,
11548           lineEnd: noop$1,
11549           polygonStart: function polygonStart() {
11550             areaStream.lineStart = areaRingStart;
11551             areaStream.lineEnd = areaRingEnd;
11552           },
11553           polygonEnd: function polygonEnd() {
11554             areaStream.lineStart = areaStream.lineEnd = areaStream.point = noop$1;
11555             areaSum.add(abs$2(areaRingSum));
11556             areaRingSum = new Adder();
11557           },
11558           result: function result() {
11559             var area = areaSum / 2;
11560             areaSum = new Adder();
11561             return area;
11562           }
11563         };
11564
11565         function areaRingStart() {
11566           areaStream.point = areaPointFirst;
11567         }
11568
11569         function areaPointFirst(x, y) {
11570           areaStream.point = areaPoint;
11571           x00$2 = x0$3 = x, y00$2 = y0$3 = y;
11572         }
11573
11574         function areaPoint(x, y) {
11575           areaRingSum.add(y0$3 * x - x0$3 * y);
11576           x0$3 = x, y0$3 = y;
11577         }
11578
11579         function areaRingEnd() {
11580           areaPoint(x00$2, y00$2);
11581         }
11582
11583         var x0$2 = Infinity,
11584             y0$2 = x0$2,
11585             x1 = -x0$2,
11586             y1 = x1;
11587         var boundsStream = {
11588           point: boundsPoint,
11589           lineStart: noop$1,
11590           lineEnd: noop$1,
11591           polygonStart: noop$1,
11592           polygonEnd: noop$1,
11593           result: function result() {
11594             var bounds = [[x0$2, y0$2], [x1, y1]];
11595             x1 = y1 = -(y0$2 = x0$2 = Infinity);
11596             return bounds;
11597           }
11598         };
11599
11600         function boundsPoint(x, y) {
11601           if (x < x0$2) x0$2 = x;
11602           if (x > x1) x1 = x;
11603           if (y < y0$2) y0$2 = y;
11604           if (y > y1) y1 = y;
11605         }
11606
11607         var X0 = 0,
11608             Y0 = 0,
11609             Z0 = 0,
11610             X1 = 0,
11611             Y1 = 0,
11612             Z1 = 0,
11613             X2 = 0,
11614             Y2 = 0,
11615             Z2 = 0,
11616             x00$1,
11617             y00$1,
11618             x0$1,
11619             y0$1;
11620         var centroidStream = {
11621           point: centroidPoint,
11622           lineStart: centroidLineStart,
11623           lineEnd: centroidLineEnd,
11624           polygonStart: function polygonStart() {
11625             centroidStream.lineStart = centroidRingStart;
11626             centroidStream.lineEnd = centroidRingEnd;
11627           },
11628           polygonEnd: function polygonEnd() {
11629             centroidStream.point = centroidPoint;
11630             centroidStream.lineStart = centroidLineStart;
11631             centroidStream.lineEnd = centroidLineEnd;
11632           },
11633           result: function result() {
11634             var centroid = Z2 ? [X2 / Z2, Y2 / Z2] : Z1 ? [X1 / Z1, Y1 / Z1] : Z0 ? [X0 / Z0, Y0 / Z0] : [NaN, NaN];
11635             X0 = Y0 = Z0 = X1 = Y1 = Z1 = X2 = Y2 = Z2 = 0;
11636             return centroid;
11637           }
11638         };
11639
11640         function centroidPoint(x, y) {
11641           X0 += x;
11642           Y0 += y;
11643           ++Z0;
11644         }
11645
11646         function centroidLineStart() {
11647           centroidStream.point = centroidPointFirstLine;
11648         }
11649
11650         function centroidPointFirstLine(x, y) {
11651           centroidStream.point = centroidPointLine;
11652           centroidPoint(x0$1 = x, y0$1 = y);
11653         }
11654
11655         function centroidPointLine(x, y) {
11656           var dx = x - x0$1,
11657               dy = y - y0$1,
11658               z = sqrt(dx * dx + dy * dy);
11659           X1 += z * (x0$1 + x) / 2;
11660           Y1 += z * (y0$1 + y) / 2;
11661           Z1 += z;
11662           centroidPoint(x0$1 = x, y0$1 = y);
11663         }
11664
11665         function centroidLineEnd() {
11666           centroidStream.point = centroidPoint;
11667         }
11668
11669         function centroidRingStart() {
11670           centroidStream.point = centroidPointFirstRing;
11671         }
11672
11673         function centroidRingEnd() {
11674           centroidPointRing(x00$1, y00$1);
11675         }
11676
11677         function centroidPointFirstRing(x, y) {
11678           centroidStream.point = centroidPointRing;
11679           centroidPoint(x00$1 = x0$1 = x, y00$1 = y0$1 = y);
11680         }
11681
11682         function centroidPointRing(x, y) {
11683           var dx = x - x0$1,
11684               dy = y - y0$1,
11685               z = sqrt(dx * dx + dy * dy);
11686           X1 += z * (x0$1 + x) / 2;
11687           Y1 += z * (y0$1 + y) / 2;
11688           Z1 += z;
11689           z = y0$1 * x - x0$1 * y;
11690           X2 += z * (x0$1 + x);
11691           Y2 += z * (y0$1 + y);
11692           Z2 += z * 3;
11693           centroidPoint(x0$1 = x, y0$1 = y);
11694         }
11695
11696         function PathContext(context) {
11697           this._context = context;
11698         }
11699         PathContext.prototype = {
11700           _radius: 4.5,
11701           pointRadius: function pointRadius(_) {
11702             return this._radius = _, this;
11703           },
11704           polygonStart: function polygonStart() {
11705             this._line = 0;
11706           },
11707           polygonEnd: function polygonEnd() {
11708             this._line = NaN;
11709           },
11710           lineStart: function lineStart() {
11711             this._point = 0;
11712           },
11713           lineEnd: function lineEnd() {
11714             if (this._line === 0) this._context.closePath();
11715             this._point = NaN;
11716           },
11717           point: function point(x, y) {
11718             switch (this._point) {
11719               case 0:
11720                 {
11721                   this._context.moveTo(x, y);
11722
11723                   this._point = 1;
11724                   break;
11725                 }
11726
11727               case 1:
11728                 {
11729                   this._context.lineTo(x, y);
11730
11731                   break;
11732                 }
11733
11734               default:
11735                 {
11736                   this._context.moveTo(x + this._radius, y);
11737
11738                   this._context.arc(x, y, this._radius, 0, tau);
11739
11740                   break;
11741                 }
11742             }
11743           },
11744           result: noop$1
11745         };
11746
11747         var lengthSum = new Adder(),
11748             lengthRing,
11749             x00,
11750             y00,
11751             x0,
11752             y0;
11753         var lengthStream = {
11754           point: noop$1,
11755           lineStart: function lineStart() {
11756             lengthStream.point = lengthPointFirst;
11757           },
11758           lineEnd: function lineEnd() {
11759             if (lengthRing) lengthPoint(x00, y00);
11760             lengthStream.point = noop$1;
11761           },
11762           polygonStart: function polygonStart() {
11763             lengthRing = true;
11764           },
11765           polygonEnd: function polygonEnd() {
11766             lengthRing = null;
11767           },
11768           result: function result() {
11769             var length = +lengthSum;
11770             lengthSum = new Adder();
11771             return length;
11772           }
11773         };
11774
11775         function lengthPointFirst(x, y) {
11776           lengthStream.point = lengthPoint;
11777           x00 = x0 = x, y00 = y0 = y;
11778         }
11779
11780         function lengthPoint(x, y) {
11781           x0 -= x, y0 -= y;
11782           lengthSum.add(sqrt(x0 * x0 + y0 * y0));
11783           x0 = x, y0 = y;
11784         }
11785
11786         function PathString() {
11787           this._string = [];
11788         }
11789         PathString.prototype = {
11790           _radius: 4.5,
11791           _circle: circle(4.5),
11792           pointRadius: function pointRadius(_) {
11793             if ((_ = +_) !== this._radius) this._radius = _, this._circle = null;
11794             return this;
11795           },
11796           polygonStart: function polygonStart() {
11797             this._line = 0;
11798           },
11799           polygonEnd: function polygonEnd() {
11800             this._line = NaN;
11801           },
11802           lineStart: function lineStart() {
11803             this._point = 0;
11804           },
11805           lineEnd: function lineEnd() {
11806             if (this._line === 0) this._string.push("Z");
11807             this._point = NaN;
11808           },
11809           point: function point(x, y) {
11810             switch (this._point) {
11811               case 0:
11812                 {
11813                   this._string.push("M", x, ",", y);
11814
11815                   this._point = 1;
11816                   break;
11817                 }
11818
11819               case 1:
11820                 {
11821                   this._string.push("L", x, ",", y);
11822
11823                   break;
11824                 }
11825
11826               default:
11827                 {
11828                   if (this._circle == null) this._circle = circle(this._radius);
11829
11830                   this._string.push("M", x, ",", y, this._circle);
11831
11832                   break;
11833                 }
11834             }
11835           },
11836           result: function result() {
11837             if (this._string.length) {
11838               var result = this._string.join("");
11839
11840               this._string = [];
11841               return result;
11842             } else {
11843               return null;
11844             }
11845           }
11846         };
11847
11848         function circle(radius) {
11849           return "m0," + radius + "a" + radius + "," + radius + " 0 1,1 0," + -2 * radius + "a" + radius + "," + radius + " 0 1,1 0," + 2 * radius + "z";
11850         }
11851
11852         function d3_geoPath (projection, context) {
11853           var pointRadius = 4.5,
11854               projectionStream,
11855               contextStream;
11856
11857           function path(object) {
11858             if (object) {
11859               if (typeof pointRadius === "function") contextStream.pointRadius(+pointRadius.apply(this, arguments));
11860               d3_geoStream(object, projectionStream(contextStream));
11861             }
11862
11863             return contextStream.result();
11864           }
11865
11866           path.area = function (object) {
11867             d3_geoStream(object, projectionStream(areaStream));
11868             return areaStream.result();
11869           };
11870
11871           path.measure = function (object) {
11872             d3_geoStream(object, projectionStream(lengthStream));
11873             return lengthStream.result();
11874           };
11875
11876           path.bounds = function (object) {
11877             d3_geoStream(object, projectionStream(boundsStream));
11878             return boundsStream.result();
11879           };
11880
11881           path.centroid = function (object) {
11882             d3_geoStream(object, projectionStream(centroidStream));
11883             return centroidStream.result();
11884           };
11885
11886           path.projection = function (_) {
11887             return arguments.length ? (projectionStream = _ == null ? (projection = null, identity$4) : (projection = _).stream, path) : projection;
11888           };
11889
11890           path.context = function (_) {
11891             if (!arguments.length) return context;
11892             contextStream = _ == null ? (context = null, new PathString()) : new PathContext(context = _);
11893             if (typeof pointRadius !== "function") contextStream.pointRadius(pointRadius);
11894             return path;
11895           };
11896
11897           path.pointRadius = function (_) {
11898             if (!arguments.length) return pointRadius;
11899             pointRadius = typeof _ === "function" ? _ : (contextStream.pointRadius(+_), +_);
11900             return path;
11901           };
11902
11903           return path.projection(projection).context(context);
11904         }
11905
11906         function d3_geoTransform (methods) {
11907           return {
11908             stream: transformer$1(methods)
11909           };
11910         }
11911         function transformer$1(methods) {
11912           return function (stream) {
11913             var s = new TransformStream();
11914
11915             for (var key in methods) {
11916               s[key] = methods[key];
11917             }
11918
11919             s.stream = stream;
11920             return s;
11921           };
11922         }
11923
11924         function TransformStream() {}
11925
11926         TransformStream.prototype = {
11927           constructor: TransformStream,
11928           point: function point(x, y) {
11929             this.stream.point(x, y);
11930           },
11931           sphere: function sphere() {
11932             this.stream.sphere();
11933           },
11934           lineStart: function lineStart() {
11935             this.stream.lineStart();
11936           },
11937           lineEnd: function lineEnd() {
11938             this.stream.lineEnd();
11939           },
11940           polygonStart: function polygonStart() {
11941             this.stream.polygonStart();
11942           },
11943           polygonEnd: function polygonEnd() {
11944             this.stream.polygonEnd();
11945           }
11946         };
11947
11948         function fit(projection, fitBounds, object) {
11949           var clip = projection.clipExtent && projection.clipExtent();
11950           projection.scale(150).translate([0, 0]);
11951           if (clip != null) projection.clipExtent(null);
11952           d3_geoStream(object, projection.stream(boundsStream));
11953           fitBounds(boundsStream.result());
11954           if (clip != null) projection.clipExtent(clip);
11955           return projection;
11956         }
11957
11958         function fitExtent(projection, extent, object) {
11959           return fit(projection, function (b) {
11960             var w = extent[1][0] - extent[0][0],
11961                 h = extent[1][1] - extent[0][1],
11962                 k = Math.min(w / (b[1][0] - b[0][0]), h / (b[1][1] - b[0][1])),
11963                 x = +extent[0][0] + (w - k * (b[1][0] + b[0][0])) / 2,
11964                 y = +extent[0][1] + (h - k * (b[1][1] + b[0][1])) / 2;
11965             projection.scale(150 * k).translate([x, y]);
11966           }, object);
11967         }
11968         function fitSize(projection, size, object) {
11969           return fitExtent(projection, [[0, 0], size], object);
11970         }
11971         function fitWidth(projection, width, object) {
11972           return fit(projection, function (b) {
11973             var w = +width,
11974                 k = w / (b[1][0] - b[0][0]),
11975                 x = (w - k * (b[1][0] + b[0][0])) / 2,
11976                 y = -k * b[0][1];
11977             projection.scale(150 * k).translate([x, y]);
11978           }, object);
11979         }
11980         function fitHeight(projection, height, object) {
11981           return fit(projection, function (b) {
11982             var h = +height,
11983                 k = h / (b[1][1] - b[0][1]),
11984                 x = -k * b[0][0],
11985                 y = (h - k * (b[1][1] + b[0][1])) / 2;
11986             projection.scale(150 * k).translate([x, y]);
11987           }, object);
11988         }
11989
11990         var maxDepth = 16,
11991             // maximum depth of subdivision
11992         cosMinDistance = cos(30 * radians); // cos(minimum angular distance)
11993
11994         function resample (project, delta2) {
11995           return +delta2 ? resample$1(project, delta2) : resampleNone(project);
11996         }
11997
11998         function resampleNone(project) {
11999           return transformer$1({
12000             point: function point(x, y) {
12001               x = project(x, y);
12002               this.stream.point(x[0], x[1]);
12003             }
12004           });
12005         }
12006
12007         function resample$1(project, delta2) {
12008           function resampleLineTo(x0, y0, lambda0, a0, b0, c0, x1, y1, lambda1, a1, b1, c1, depth, stream) {
12009             var dx = x1 - x0,
12010                 dy = y1 - y0,
12011                 d2 = dx * dx + dy * dy;
12012
12013             if (d2 > 4 * delta2 && depth--) {
12014               var a = a0 + a1,
12015                   b = b0 + b1,
12016                   c = c0 + c1,
12017                   m = sqrt(a * a + b * b + c * c),
12018                   phi2 = asin(c /= m),
12019                   lambda2 = abs$2(abs$2(c) - 1) < epsilon$1 || abs$2(lambda0 - lambda1) < epsilon$1 ? (lambda0 + lambda1) / 2 : atan2(b, a),
12020                   p = project(lambda2, phi2),
12021                   x2 = p[0],
12022                   y2 = p[1],
12023                   dx2 = x2 - x0,
12024                   dy2 = y2 - y0,
12025                   dz = dy * dx2 - dx * dy2;
12026
12027               if (dz * dz / d2 > delta2 // perpendicular projected distance
12028               || abs$2((dx * dx2 + dy * dy2) / d2 - 0.5) > 0.3 // midpoint close to an end
12029               || a0 * a1 + b0 * b1 + c0 * c1 < cosMinDistance) {
12030                 // angular distance
12031                 resampleLineTo(x0, y0, lambda0, a0, b0, c0, x2, y2, lambda2, a /= m, b /= m, c, depth, stream);
12032                 stream.point(x2, y2);
12033                 resampleLineTo(x2, y2, lambda2, a, b, c, x1, y1, lambda1, a1, b1, c1, depth, stream);
12034               }
12035             }
12036           }
12037
12038           return function (stream) {
12039             var lambda00, x00, y00, a00, b00, c00, // first point
12040             lambda0, x0, y0, a0, b0, c0; // previous point
12041
12042             var resampleStream = {
12043               point: point,
12044               lineStart: lineStart,
12045               lineEnd: lineEnd,
12046               polygonStart: function polygonStart() {
12047                 stream.polygonStart();
12048                 resampleStream.lineStart = ringStart;
12049               },
12050               polygonEnd: function polygonEnd() {
12051                 stream.polygonEnd();
12052                 resampleStream.lineStart = lineStart;
12053               }
12054             };
12055
12056             function point(x, y) {
12057               x = project(x, y);
12058               stream.point(x[0], x[1]);
12059             }
12060
12061             function lineStart() {
12062               x0 = NaN;
12063               resampleStream.point = linePoint;
12064               stream.lineStart();
12065             }
12066
12067             function linePoint(lambda, phi) {
12068               var c = cartesian([lambda, phi]),
12069                   p = project(lambda, phi);
12070               resampleLineTo(x0, y0, lambda0, a0, b0, c0, x0 = p[0], y0 = p[1], lambda0 = lambda, a0 = c[0], b0 = c[1], c0 = c[2], maxDepth, stream);
12071               stream.point(x0, y0);
12072             }
12073
12074             function lineEnd() {
12075               resampleStream.point = point;
12076               stream.lineEnd();
12077             }
12078
12079             function ringStart() {
12080               lineStart();
12081               resampleStream.point = ringPoint;
12082               resampleStream.lineEnd = ringEnd;
12083             }
12084
12085             function ringPoint(lambda, phi) {
12086               linePoint(lambda00 = lambda, phi), x00 = x0, y00 = y0, a00 = a0, b00 = b0, c00 = c0;
12087               resampleStream.point = linePoint;
12088             }
12089
12090             function ringEnd() {
12091               resampleLineTo(x0, y0, lambda0, a0, b0, c0, x00, y00, lambda00, a00, b00, c00, maxDepth, stream);
12092               resampleStream.lineEnd = lineEnd;
12093               lineEnd();
12094             }
12095
12096             return resampleStream;
12097           };
12098         }
12099
12100         var transformRadians = transformer$1({
12101           point: function point(x, y) {
12102             this.stream.point(x * radians, y * radians);
12103           }
12104         });
12105
12106         function transformRotate(rotate) {
12107           return transformer$1({
12108             point: function point(x, y) {
12109               var r = rotate(x, y);
12110               return this.stream.point(r[0], r[1]);
12111             }
12112           });
12113         }
12114
12115         function scaleTranslate(k, dx, dy, sx, sy) {
12116           function transform(x, y) {
12117             x *= sx;
12118             y *= sy;
12119             return [dx + k * x, dy - k * y];
12120           }
12121
12122           transform.invert = function (x, y) {
12123             return [(x - dx) / k * sx, (dy - y) / k * sy];
12124           };
12125
12126           return transform;
12127         }
12128
12129         function scaleTranslateRotate(k, dx, dy, sx, sy, alpha) {
12130           if (!alpha) return scaleTranslate(k, dx, dy, sx, sy);
12131           var cosAlpha = cos(alpha),
12132               sinAlpha = sin(alpha),
12133               a = cosAlpha * k,
12134               b = sinAlpha * k,
12135               ai = cosAlpha / k,
12136               bi = sinAlpha / k,
12137               ci = (sinAlpha * dy - cosAlpha * dx) / k,
12138               fi = (sinAlpha * dx + cosAlpha * dy) / k;
12139
12140           function transform(x, y) {
12141             x *= sx;
12142             y *= sy;
12143             return [a * x - b * y + dx, dy - b * x - a * y];
12144           }
12145
12146           transform.invert = function (x, y) {
12147             return [sx * (ai * x - bi * y + ci), sy * (fi - bi * x - ai * y)];
12148           };
12149
12150           return transform;
12151         }
12152
12153         function projection(project) {
12154           return projectionMutator(function () {
12155             return project;
12156           })();
12157         }
12158         function projectionMutator(projectAt) {
12159           var project,
12160               k = 150,
12161               // scale
12162           x = 480,
12163               y = 250,
12164               // translate
12165           lambda = 0,
12166               phi = 0,
12167               // center
12168           deltaLambda = 0,
12169               deltaPhi = 0,
12170               deltaGamma = 0,
12171               rotate,
12172               // pre-rotate
12173           alpha = 0,
12174               // post-rotate angle
12175           sx = 1,
12176               // reflectX
12177           sy = 1,
12178               // reflectX
12179           theta = null,
12180               preclip = clipAntimeridian,
12181               // pre-clip angle
12182           x0 = null,
12183               y0,
12184               x1,
12185               y1,
12186               postclip = identity$4,
12187               // post-clip extent
12188           delta2 = 0.5,
12189               // precision
12190           projectResample,
12191               projectTransform,
12192               projectRotateTransform,
12193               cache,
12194               cacheStream;
12195
12196           function projection(point) {
12197             return projectRotateTransform(point[0] * radians, point[1] * radians);
12198           }
12199
12200           function invert(point) {
12201             point = projectRotateTransform.invert(point[0], point[1]);
12202             return point && [point[0] * degrees$1, point[1] * degrees$1];
12203           }
12204
12205           projection.stream = function (stream) {
12206             return cache && cacheStream === stream ? cache : cache = transformRadians(transformRotate(rotate)(preclip(projectResample(postclip(cacheStream = stream)))));
12207           };
12208
12209           projection.preclip = function (_) {
12210             return arguments.length ? (preclip = _, theta = undefined, reset()) : preclip;
12211           };
12212
12213           projection.postclip = function (_) {
12214             return arguments.length ? (postclip = _, x0 = y0 = x1 = y1 = null, reset()) : postclip;
12215           };
12216
12217           projection.clipAngle = function (_) {
12218             return arguments.length ? (preclip = +_ ? clipCircle(theta = _ * radians) : (theta = null, clipAntimeridian), reset()) : theta * degrees$1;
12219           };
12220
12221           projection.clipExtent = function (_) {
12222             return arguments.length ? (postclip = _ == null ? (x0 = y0 = x1 = y1 = null, identity$4) : clipRectangle(x0 = +_[0][0], y0 = +_[0][1], x1 = +_[1][0], y1 = +_[1][1]), reset()) : x0 == null ? null : [[x0, y0], [x1, y1]];
12223           };
12224
12225           projection.scale = function (_) {
12226             return arguments.length ? (k = +_, recenter()) : k;
12227           };
12228
12229           projection.translate = function (_) {
12230             return arguments.length ? (x = +_[0], y = +_[1], recenter()) : [x, y];
12231           };
12232
12233           projection.center = function (_) {
12234             return arguments.length ? (lambda = _[0] % 360 * radians, phi = _[1] % 360 * radians, recenter()) : [lambda * degrees$1, phi * degrees$1];
12235           };
12236
12237           projection.rotate = function (_) {
12238             return arguments.length ? (deltaLambda = _[0] % 360 * radians, deltaPhi = _[1] % 360 * radians, deltaGamma = _.length > 2 ? _[2] % 360 * radians : 0, recenter()) : [deltaLambda * degrees$1, deltaPhi * degrees$1, deltaGamma * degrees$1];
12239           };
12240
12241           projection.angle = function (_) {
12242             return arguments.length ? (alpha = _ % 360 * radians, recenter()) : alpha * degrees$1;
12243           };
12244
12245           projection.reflectX = function (_) {
12246             return arguments.length ? (sx = _ ? -1 : 1, recenter()) : sx < 0;
12247           };
12248
12249           projection.reflectY = function (_) {
12250             return arguments.length ? (sy = _ ? -1 : 1, recenter()) : sy < 0;
12251           };
12252
12253           projection.precision = function (_) {
12254             return arguments.length ? (projectResample = resample(projectTransform, delta2 = _ * _), reset()) : sqrt(delta2);
12255           };
12256
12257           projection.fitExtent = function (extent, object) {
12258             return fitExtent(projection, extent, object);
12259           };
12260
12261           projection.fitSize = function (size, object) {
12262             return fitSize(projection, size, object);
12263           };
12264
12265           projection.fitWidth = function (width, object) {
12266             return fitWidth(projection, width, object);
12267           };
12268
12269           projection.fitHeight = function (height, object) {
12270             return fitHeight(projection, height, object);
12271           };
12272
12273           function recenter() {
12274             var center = scaleTranslateRotate(k, 0, 0, sx, sy, alpha).apply(null, project(lambda, phi)),
12275                 transform = scaleTranslateRotate(k, x - center[0], y - center[1], sx, sy, alpha);
12276             rotate = rotateRadians(deltaLambda, deltaPhi, deltaGamma);
12277             projectTransform = compose(project, transform);
12278             projectRotateTransform = compose(rotate, projectTransform);
12279             projectResample = resample(projectTransform, delta2);
12280             return reset();
12281           }
12282
12283           function reset() {
12284             cache = cacheStream = null;
12285             return projection;
12286           }
12287
12288           return function () {
12289             project = projectAt.apply(this, arguments);
12290             projection.invert = project.invert && invert;
12291             return recenter();
12292           };
12293         }
12294
12295         function mercatorRaw(lambda, phi) {
12296           return [lambda, log$1(tan((halfPi + phi) / 2))];
12297         }
12298
12299         mercatorRaw.invert = function (x, y) {
12300           return [x, 2 * atan(exp$2(y)) - halfPi];
12301         };
12302
12303         function mercator () {
12304           return mercatorProjection(mercatorRaw).scale(961 / tau);
12305         }
12306         function mercatorProjection(project) {
12307           var m = projection(project),
12308               center = m.center,
12309               scale = m.scale,
12310               translate = m.translate,
12311               clipExtent = m.clipExtent,
12312               x0 = null,
12313               y0,
12314               x1,
12315               y1; // clip extent
12316
12317           m.scale = function (_) {
12318             return arguments.length ? (scale(_), reclip()) : scale();
12319           };
12320
12321           m.translate = function (_) {
12322             return arguments.length ? (translate(_), reclip()) : translate();
12323           };
12324
12325           m.center = function (_) {
12326             return arguments.length ? (center(_), reclip()) : center();
12327           };
12328
12329           m.clipExtent = function (_) {
12330             return arguments.length ? (_ == null ? x0 = y0 = x1 = y1 = null : (x0 = +_[0][0], y0 = +_[0][1], x1 = +_[1][0], y1 = +_[1][1]), reclip()) : x0 == null ? null : [[x0, y0], [x1, y1]];
12331           };
12332
12333           function reclip() {
12334             var k = pi * scale(),
12335                 t = m(rotation(m.rotate()).invert([0, 0]));
12336             return clipExtent(x0 == null ? [[t[0] - k, t[1] - k], [t[0] + k, t[1] + k]] : project === mercatorRaw ? [[Math.max(t[0] - k, x0), y0], [Math.min(t[0] + k, x1), y1]] : [[x0, Math.max(t[1] - k, y0)], [x1, Math.min(t[1] + k, y1)]]);
12337           }
12338
12339           return reclip();
12340         }
12341
12342         function d3_geoIdentity () {
12343           var k = 1,
12344               tx = 0,
12345               ty = 0,
12346               sx = 1,
12347               sy = 1,
12348               // scale, translate and reflect
12349           alpha = 0,
12350               ca,
12351               sa,
12352               // angle
12353           x0 = null,
12354               y0,
12355               x1,
12356               y1,
12357               // clip extent
12358           kx = 1,
12359               ky = 1,
12360               transform = transformer$1({
12361             point: function point(x, y) {
12362               var p = projection([x, y]);
12363               this.stream.point(p[0], p[1]);
12364             }
12365           }),
12366               postclip = identity$4,
12367               cache,
12368               cacheStream;
12369
12370           function reset() {
12371             kx = k * sx;
12372             ky = k * sy;
12373             cache = cacheStream = null;
12374             return projection;
12375           }
12376
12377           function projection(p) {
12378             var x = p[0] * kx,
12379                 y = p[1] * ky;
12380
12381             if (alpha) {
12382               var t = y * ca - x * sa;
12383               x = x * ca + y * sa;
12384               y = t;
12385             }
12386
12387             return [x + tx, y + ty];
12388           }
12389
12390           projection.invert = function (p) {
12391             var x = p[0] - tx,
12392                 y = p[1] - ty;
12393
12394             if (alpha) {
12395               var t = y * ca + x * sa;
12396               x = x * ca - y * sa;
12397               y = t;
12398             }
12399
12400             return [x / kx, y / ky];
12401           };
12402
12403           projection.stream = function (stream) {
12404             return cache && cacheStream === stream ? cache : cache = transform(postclip(cacheStream = stream));
12405           };
12406
12407           projection.postclip = function (_) {
12408             return arguments.length ? (postclip = _, x0 = y0 = x1 = y1 = null, reset()) : postclip;
12409           };
12410
12411           projection.clipExtent = function (_) {
12412             return arguments.length ? (postclip = _ == null ? (x0 = y0 = x1 = y1 = null, identity$4) : clipRectangle(x0 = +_[0][0], y0 = +_[0][1], x1 = +_[1][0], y1 = +_[1][1]), reset()) : x0 == null ? null : [[x0, y0], [x1, y1]];
12413           };
12414
12415           projection.scale = function (_) {
12416             return arguments.length ? (k = +_, reset()) : k;
12417           };
12418
12419           projection.translate = function (_) {
12420             return arguments.length ? (tx = +_[0], ty = +_[1], reset()) : [tx, ty];
12421           };
12422
12423           projection.angle = function (_) {
12424             return arguments.length ? (alpha = _ % 360 * radians, sa = sin(alpha), ca = cos(alpha), reset()) : alpha * degrees$1;
12425           };
12426
12427           projection.reflectX = function (_) {
12428             return arguments.length ? (sx = _ ? -1 : 1, reset()) : sx < 0;
12429           };
12430
12431           projection.reflectY = function (_) {
12432             return arguments.length ? (sy = _ ? -1 : 1, reset()) : sy < 0;
12433           };
12434
12435           projection.fitExtent = function (extent, object) {
12436             return fitExtent(projection, extent, object);
12437           };
12438
12439           projection.fitSize = function (size, object) {
12440             return fitSize(projection, size, object);
12441           };
12442
12443           projection.fitWidth = function (width, object) {
12444             return fitWidth(projection, width, object);
12445           };
12446
12447           projection.fitHeight = function (height, object) {
12448             return fitHeight(projection, height, object);
12449           };
12450
12451           return projection;
12452         }
12453
12454         // constants
12455         var TAU = 2 * Math.PI;
12456         var EQUATORIAL_RADIUS = 6356752.314245179;
12457         var POLAR_RADIUS$1 = 6378137.0;
12458         function geoLatToMeters(dLat) {
12459           return dLat * (TAU * POLAR_RADIUS$1 / 360);
12460         }
12461         function geoLonToMeters(dLon, atLat) {
12462           return Math.abs(atLat) >= 90 ? 0 : dLon * (TAU * EQUATORIAL_RADIUS / 360) * Math.abs(Math.cos(atLat * (Math.PI / 180)));
12463         }
12464         function geoMetersToLat(m) {
12465           return m / (TAU * POLAR_RADIUS$1 / 360);
12466         }
12467         function geoMetersToLon(m, atLat) {
12468           return Math.abs(atLat) >= 90 ? 0 : m / (TAU * EQUATORIAL_RADIUS / 360) / Math.abs(Math.cos(atLat * (Math.PI / 180)));
12469         }
12470         function geoMetersToOffset(meters, tileSize) {
12471           tileSize = tileSize || 256;
12472           return [meters[0] * tileSize / (TAU * EQUATORIAL_RADIUS), -meters[1] * tileSize / (TAU * POLAR_RADIUS$1)];
12473         }
12474         function geoOffsetToMeters(offset, tileSize) {
12475           tileSize = tileSize || 256;
12476           return [offset[0] * TAU * EQUATORIAL_RADIUS / tileSize, -offset[1] * TAU * POLAR_RADIUS$1 / tileSize];
12477         } // Equirectangular approximation of spherical distances on Earth
12478
12479         function geoSphericalDistance(a, b) {
12480           var x = geoLonToMeters(a[0] - b[0], (a[1] + b[1]) / 2);
12481           var y = geoLatToMeters(a[1] - b[1]);
12482           return Math.sqrt(x * x + y * y);
12483         } // scale to zoom
12484
12485         function geoScaleToZoom(k, tileSize) {
12486           tileSize = tileSize || 256;
12487           var log2ts = Math.log(tileSize) * Math.LOG2E;
12488           return Math.log(k * TAU) / Math.LN2 - log2ts;
12489         } // zoom to scale
12490
12491         function geoZoomToScale(z, tileSize) {
12492           tileSize = tileSize || 256;
12493           return tileSize * Math.pow(2, z) / TAU;
12494         } // returns info about the node from `nodes` closest to the given `point`
12495
12496         function geoSphericalClosestNode(nodes, point) {
12497           var minDistance = Infinity,
12498               distance;
12499           var indexOfMin;
12500
12501           for (var i in nodes) {
12502             distance = geoSphericalDistance(nodes[i].loc, point);
12503
12504             if (distance < minDistance) {
12505               minDistance = distance;
12506               indexOfMin = i;
12507             }
12508           }
12509
12510           if (indexOfMin !== undefined) {
12511             return {
12512               index: indexOfMin,
12513               distance: minDistance,
12514               node: nodes[indexOfMin]
12515             };
12516           } else {
12517             return null;
12518           }
12519         }
12520
12521         function geoExtent(min, max) {
12522           if (!(this instanceof geoExtent)) {
12523             return new geoExtent(min, max);
12524           } else if (min instanceof geoExtent) {
12525             return min;
12526           } else if (min && min.length === 2 && min[0].length === 2 && min[1].length === 2) {
12527             this[0] = min[0];
12528             this[1] = min[1];
12529           } else {
12530             this[0] = min || [Infinity, Infinity];
12531             this[1] = max || min || [-Infinity, -Infinity];
12532           }
12533         }
12534         geoExtent.prototype = new Array(2);
12535         Object.assign(geoExtent.prototype, {
12536           equals: function equals(obj) {
12537             return this[0][0] === obj[0][0] && this[0][1] === obj[0][1] && this[1][0] === obj[1][0] && this[1][1] === obj[1][1];
12538           },
12539           extend: function extend(obj) {
12540             if (!(obj instanceof geoExtent)) obj = new geoExtent(obj);
12541             return geoExtent([Math.min(obj[0][0], this[0][0]), Math.min(obj[0][1], this[0][1])], [Math.max(obj[1][0], this[1][0]), Math.max(obj[1][1], this[1][1])]);
12542           },
12543           _extend: function _extend(extent) {
12544             this[0][0] = Math.min(extent[0][0], this[0][0]);
12545             this[0][1] = Math.min(extent[0][1], this[0][1]);
12546             this[1][0] = Math.max(extent[1][0], this[1][0]);
12547             this[1][1] = Math.max(extent[1][1], this[1][1]);
12548           },
12549           area: function area() {
12550             return Math.abs((this[1][0] - this[0][0]) * (this[1][1] - this[0][1]));
12551           },
12552           center: function center() {
12553             return [(this[0][0] + this[1][0]) / 2, (this[0][1] + this[1][1]) / 2];
12554           },
12555           rectangle: function rectangle() {
12556             return [this[0][0], this[0][1], this[1][0], this[1][1]];
12557           },
12558           bbox: function bbox() {
12559             return {
12560               minX: this[0][0],
12561               minY: this[0][1],
12562               maxX: this[1][0],
12563               maxY: this[1][1]
12564             };
12565           },
12566           polygon: function polygon() {
12567             return [[this[0][0], this[0][1]], [this[0][0], this[1][1]], [this[1][0], this[1][1]], [this[1][0], this[0][1]], [this[0][0], this[0][1]]];
12568           },
12569           contains: function contains(obj) {
12570             if (!(obj instanceof geoExtent)) obj = new geoExtent(obj);
12571             return obj[0][0] >= this[0][0] && obj[0][1] >= this[0][1] && obj[1][0] <= this[1][0] && obj[1][1] <= this[1][1];
12572           },
12573           intersects: function intersects(obj) {
12574             if (!(obj instanceof geoExtent)) obj = new geoExtent(obj);
12575             return obj[0][0] <= this[1][0] && obj[0][1] <= this[1][1] && obj[1][0] >= this[0][0] && obj[1][1] >= this[0][1];
12576           },
12577           intersection: function intersection(obj) {
12578             if (!this.intersects(obj)) return new geoExtent();
12579             return new geoExtent([Math.max(obj[0][0], this[0][0]), Math.max(obj[0][1], this[0][1])], [Math.min(obj[1][0], this[1][0]), Math.min(obj[1][1], this[1][1])]);
12580           },
12581           percentContainedIn: function percentContainedIn(obj) {
12582             if (!(obj instanceof geoExtent)) obj = new geoExtent(obj);
12583             var a1 = this.intersection(obj).area();
12584             var a2 = this.area();
12585
12586             if (a1 === Infinity || a2 === Infinity) {
12587               return 0;
12588             } else if (a1 === 0 || a2 === 0) {
12589               if (obj.contains(this)) {
12590                 return 1;
12591               }
12592
12593               return 0;
12594             } else {
12595               return a1 / a2;
12596             }
12597           },
12598           padByMeters: function padByMeters(meters) {
12599             var dLat = geoMetersToLat(meters);
12600             var dLon = geoMetersToLon(meters, this.center()[1]);
12601             return geoExtent([this[0][0] - dLon, this[0][1] - dLat], [this[1][0] + dLon, this[1][1] + dLat]);
12602           },
12603           toParam: function toParam() {
12604             return this.rectangle().join(',');
12605           }
12606         });
12607
12608         var $every = arrayIteration.every;
12609
12610
12611         var STRICT_METHOD$1 = arrayMethodIsStrict('every');
12612
12613         // `Array.prototype.every` method
12614         // https://tc39.es/ecma262/#sec-array.prototype.every
12615         _export({ target: 'Array', proto: true, forced: !STRICT_METHOD$1 }, {
12616           every: function every(callbackfn /* , thisArg */) {
12617             return $every(this, callbackfn, arguments.length > 1 ? arguments[1] : undefined);
12618           }
12619         });
12620
12621         var $reduce = arrayReduce.left;
12622
12623
12624
12625
12626         var STRICT_METHOD = arrayMethodIsStrict('reduce');
12627         // Chrome 80-82 has a critical bug
12628         // https://bugs.chromium.org/p/chromium/issues/detail?id=1049982
12629         var CHROME_BUG = !engineIsNode && engineV8Version > 79 && engineV8Version < 83;
12630
12631         // `Array.prototype.reduce` method
12632         // https://tc39.es/ecma262/#sec-array.prototype.reduce
12633         _export({ target: 'Array', proto: true, forced: !STRICT_METHOD || CHROME_BUG }, {
12634           reduce: function reduce(callbackfn /* , initialValue */) {
12635             return $reduce(this, callbackfn, arguments.length, arguments.length > 1 ? arguments[1] : undefined);
12636           }
12637         });
12638
12639         function d3_polygonArea (polygon) {
12640           var i = -1,
12641               n = polygon.length,
12642               a,
12643               b = polygon[n - 1],
12644               area = 0;
12645
12646           while (++i < n) {
12647             a = b;
12648             b = polygon[i];
12649             area += a[1] * b[0] - a[0] * b[1];
12650           }
12651
12652           return area / 2;
12653         }
12654
12655         function d3_polygonCentroid (polygon) {
12656           var i = -1,
12657               n = polygon.length,
12658               x = 0,
12659               y = 0,
12660               a,
12661               b = polygon[n - 1],
12662               c,
12663               k = 0;
12664
12665           while (++i < n) {
12666             a = b;
12667             b = polygon[i];
12668             k += c = a[0] * b[1] - b[0] * a[1];
12669             x += (a[0] + b[0]) * c;
12670             y += (a[1] + b[1]) * c;
12671           }
12672
12673           return k *= 3, [x / k, y / k];
12674         }
12675
12676         // Returns the 2D cross product of AB and AC vectors, i.e., the z-component of
12677         // the 3D cross product in a quadrant I Cartesian coordinate system (+x is
12678         // right, +y is up). Returns a positive value if ABC is counter-clockwise,
12679         // negative if clockwise, and zero if the points are collinear.
12680         function cross (a, b, c) {
12681           return (b[0] - a[0]) * (c[1] - a[1]) - (b[1] - a[1]) * (c[0] - a[0]);
12682         }
12683
12684         function lexicographicOrder(a, b) {
12685           return a[0] - b[0] || a[1] - b[1];
12686         } // Computes the upper convex hull per the monotone chain algorithm.
12687         // Assumes points.length >= 3, is sorted by x, unique in y.
12688         // Returns an array of indices into points in left-to-right order.
12689
12690
12691         function computeUpperHullIndexes(points) {
12692           var n = points.length,
12693               indexes = [0, 1];
12694           var size = 2,
12695               i;
12696
12697           for (i = 2; i < n; ++i) {
12698             while (size > 1 && cross(points[indexes[size - 2]], points[indexes[size - 1]], points[i]) <= 0) {
12699               --size;
12700             }
12701
12702             indexes[size++] = i;
12703           }
12704
12705           return indexes.slice(0, size); // remove popped points
12706         }
12707
12708         function d3_polygonHull (points) {
12709           if ((n = points.length) < 3) return null;
12710           var i,
12711               n,
12712               sortedPoints = new Array(n),
12713               flippedPoints = new Array(n);
12714
12715           for (i = 0; i < n; ++i) {
12716             sortedPoints[i] = [+points[i][0], +points[i][1], i];
12717           }
12718
12719           sortedPoints.sort(lexicographicOrder);
12720
12721           for (i = 0; i < n; ++i) {
12722             flippedPoints[i] = [sortedPoints[i][0], -sortedPoints[i][1]];
12723           }
12724
12725           var upperIndexes = computeUpperHullIndexes(sortedPoints),
12726               lowerIndexes = computeUpperHullIndexes(flippedPoints); // Construct the hull polygon, removing possible duplicate endpoints.
12727
12728           var skipLeft = lowerIndexes[0] === upperIndexes[0],
12729               skipRight = lowerIndexes[lowerIndexes.length - 1] === upperIndexes[upperIndexes.length - 1],
12730               hull = []; // Add upper hull in right-to-l order.
12731           // Then add lower hull in left-to-right order.
12732
12733           for (i = upperIndexes.length - 1; i >= 0; --i) {
12734             hull.push(points[sortedPoints[upperIndexes[i]][2]]);
12735           }
12736
12737           for (i = +skipLeft; i < lowerIndexes.length - skipRight; ++i) {
12738             hull.push(points[sortedPoints[lowerIndexes[i]][2]]);
12739           }
12740
12741           return hull;
12742         }
12743
12744         // vector equals
12745         function geoVecEqual(a, b, epsilon) {
12746           if (epsilon) {
12747             return Math.abs(a[0] - b[0]) <= epsilon && Math.abs(a[1] - b[1]) <= epsilon;
12748           } else {
12749             return a[0] === b[0] && a[1] === b[1];
12750           }
12751         } // vector addition
12752
12753         function geoVecAdd(a, b) {
12754           return [a[0] + b[0], a[1] + b[1]];
12755         } // vector subtraction
12756
12757         function geoVecSubtract(a, b) {
12758           return [a[0] - b[0], a[1] - b[1]];
12759         } // vector scaling
12760
12761         function geoVecScale(a, mag) {
12762           return [a[0] * mag, a[1] * mag];
12763         } // vector rounding (was: geoRoundCoordinates)
12764
12765         function geoVecFloor(a) {
12766           return [Math.floor(a[0]), Math.floor(a[1])];
12767         } // linear interpolation
12768
12769         function geoVecInterp(a, b, t) {
12770           return [a[0] + (b[0] - a[0]) * t, a[1] + (b[1] - a[1]) * t];
12771         } // http://jsperf.com/id-dist-optimization
12772
12773         function geoVecLength(a, b) {
12774           return Math.sqrt(geoVecLengthSquare(a, b));
12775         } // length of vector raised to the power two
12776
12777         function geoVecLengthSquare(a, b) {
12778           b = b || [0, 0];
12779           var x = a[0] - b[0];
12780           var y = a[1] - b[1];
12781           return x * x + y * y;
12782         } // get a unit vector
12783
12784         function geoVecNormalize(a) {
12785           var length = Math.sqrt(a[0] * a[0] + a[1] * a[1]);
12786
12787           if (length !== 0) {
12788             return geoVecScale(a, 1 / length);
12789           }
12790
12791           return [0, 0];
12792         } // Return the counterclockwise angle in the range (-pi, pi)
12793         // between the positive X axis and the line intersecting a and b.
12794
12795         function geoVecAngle(a, b) {
12796           return Math.atan2(b[1] - a[1], b[0] - a[0]);
12797         } // dot product
12798
12799         function geoVecDot(a, b, origin) {
12800           origin = origin || [0, 0];
12801           var p = geoVecSubtract(a, origin);
12802           var q = geoVecSubtract(b, origin);
12803           return p[0] * q[0] + p[1] * q[1];
12804         } // normalized dot product
12805
12806         function geoVecNormalizedDot(a, b, origin) {
12807           origin = origin || [0, 0];
12808           var p = geoVecNormalize(geoVecSubtract(a, origin));
12809           var q = geoVecNormalize(geoVecSubtract(b, origin));
12810           return geoVecDot(p, q);
12811         } // 2D cross product of OA and OB vectors, returns magnitude of Z vector
12812         // Returns a positive value, if OAB makes a counter-clockwise turn,
12813         // negative for clockwise turn, and zero if the points are collinear.
12814
12815         function geoVecCross(a, b, origin) {
12816           origin = origin || [0, 0];
12817           var p = geoVecSubtract(a, origin);
12818           var q = geoVecSubtract(b, origin);
12819           return p[0] * q[1] - p[1] * q[0];
12820         } // find closest orthogonal projection of point onto points array
12821
12822         function geoVecProject(a, points) {
12823           var min = Infinity;
12824           var idx;
12825           var target;
12826
12827           for (var i = 0; i < points.length - 1; i++) {
12828             var o = points[i];
12829             var s = geoVecSubtract(points[i + 1], o);
12830             var v = geoVecSubtract(a, o);
12831             var proj = geoVecDot(v, s) / geoVecDot(s, s);
12832             var p;
12833
12834             if (proj < 0) {
12835               p = o;
12836             } else if (proj > 1) {
12837               p = points[i + 1];
12838             } else {
12839               p = [o[0] + proj * s[0], o[1] + proj * s[1]];
12840             }
12841
12842             var dist = geoVecLength(p, a);
12843
12844             if (dist < min) {
12845               min = dist;
12846               idx = i + 1;
12847               target = p;
12848             }
12849           }
12850
12851           if (idx !== undefined) {
12852             return {
12853               index: idx,
12854               distance: min,
12855               target: target
12856             };
12857           } else {
12858             return null;
12859           }
12860         }
12861
12862         // between the positive X axis and the line intersecting a and b.
12863
12864         function geoAngle(a, b, projection) {
12865           return geoVecAngle(projection(a.loc), projection(b.loc));
12866         }
12867         function geoEdgeEqual(a, b) {
12868           return a[0] === b[0] && a[1] === b[1] || a[0] === b[1] && a[1] === b[0];
12869         } // Rotate all points counterclockwise around a pivot point by given angle
12870
12871         function geoRotate(points, angle, around) {
12872           return points.map(function (point) {
12873             var radial = geoVecSubtract(point, around);
12874             return [radial[0] * Math.cos(angle) - radial[1] * Math.sin(angle) + around[0], radial[0] * Math.sin(angle) + radial[1] * Math.cos(angle) + around[1]];
12875           });
12876         } // Choose the edge with the minimal distance from `point` to its orthogonal
12877         // projection onto that edge, if such a projection exists, or the distance to
12878         // the closest vertex on that edge. Returns an object with the `index` of the
12879         // chosen edge, the chosen `loc` on that edge, and the `distance` to to it.
12880
12881         function geoChooseEdge(nodes, point, projection, activeID) {
12882           var dist = geoVecLength;
12883           var points = nodes.map(function (n) {
12884             return projection(n.loc);
12885           });
12886           var ids = nodes.map(function (n) {
12887             return n.id;
12888           });
12889           var min = Infinity;
12890           var idx;
12891           var loc;
12892
12893           for (var i = 0; i < points.length - 1; i++) {
12894             if (ids[i] === activeID || ids[i + 1] === activeID) continue;
12895             var o = points[i];
12896             var s = geoVecSubtract(points[i + 1], o);
12897             var v = geoVecSubtract(point, o);
12898             var proj = geoVecDot(v, s) / geoVecDot(s, s);
12899             var p;
12900
12901             if (proj < 0) {
12902               p = o;
12903             } else if (proj > 1) {
12904               p = points[i + 1];
12905             } else {
12906               p = [o[0] + proj * s[0], o[1] + proj * s[1]];
12907             }
12908
12909             var d = dist(p, point);
12910
12911             if (d < min) {
12912               min = d;
12913               idx = i + 1;
12914               loc = projection.invert(p);
12915             }
12916           }
12917
12918           if (idx !== undefined) {
12919             return {
12920               index: idx,
12921               distance: min,
12922               loc: loc
12923             };
12924           } else {
12925             return null;
12926           }
12927         } // Test active (dragged or drawing) segments against inactive segments
12928         // This is used to test e.g. multipolygon rings that cross
12929         // `activeNodes` is the ring containing the activeID being dragged.
12930         // `inactiveNodes` is the other ring to test against
12931
12932         function geoHasLineIntersections(activeNodes, inactiveNodes, activeID) {
12933           var actives = [];
12934           var inactives = [];
12935           var j, k, n1, n2, segment; // gather active segments (only segments in activeNodes that contain the activeID)
12936
12937           for (j = 0; j < activeNodes.length - 1; j++) {
12938             n1 = activeNodes[j];
12939             n2 = activeNodes[j + 1];
12940             segment = [n1.loc, n2.loc];
12941
12942             if (n1.id === activeID || n2.id === activeID) {
12943               actives.push(segment);
12944             }
12945           } // gather inactive segments
12946
12947
12948           for (j = 0; j < inactiveNodes.length - 1; j++) {
12949             n1 = inactiveNodes[j];
12950             n2 = inactiveNodes[j + 1];
12951             segment = [n1.loc, n2.loc];
12952             inactives.push(segment);
12953           } // test
12954
12955
12956           for (j = 0; j < actives.length; j++) {
12957             for (k = 0; k < inactives.length; k++) {
12958               var p = actives[j];
12959               var q = inactives[k];
12960               var hit = geoLineIntersection(p, q);
12961
12962               if (hit) {
12963                 return true;
12964               }
12965             }
12966           }
12967
12968           return false;
12969         } // Test active (dragged or drawing) segments against inactive segments
12970         // This is used to test whether a way intersects with itself.
12971
12972         function geoHasSelfIntersections(nodes, activeID) {
12973           var actives = [];
12974           var inactives = [];
12975           var j, k; // group active and passive segments along the nodes
12976
12977           for (j = 0; j < nodes.length - 1; j++) {
12978             var n1 = nodes[j];
12979             var n2 = nodes[j + 1];
12980             var segment = [n1.loc, n2.loc];
12981
12982             if (n1.id === activeID || n2.id === activeID) {
12983               actives.push(segment);
12984             } else {
12985               inactives.push(segment);
12986             }
12987           } // test
12988
12989
12990           for (j = 0; j < actives.length; j++) {
12991             for (k = 0; k < inactives.length; k++) {
12992               var p = actives[j];
12993               var q = inactives[k]; // skip if segments share an endpoint
12994
12995               if (geoVecEqual(p[1], q[0]) || geoVecEqual(p[0], q[1]) || geoVecEqual(p[0], q[0]) || geoVecEqual(p[1], q[1])) {
12996                 continue;
12997               }
12998
12999               var hit = geoLineIntersection(p, q);
13000
13001               if (hit) {
13002                 var epsilon = 1e-8; // skip if the hit is at the segment's endpoint
13003
13004                 if (geoVecEqual(p[1], hit, epsilon) || geoVecEqual(p[0], hit, epsilon) || geoVecEqual(q[1], hit, epsilon) || geoVecEqual(q[0], hit, epsilon)) {
13005                   continue;
13006                 } else {
13007                   return true;
13008                 }
13009               }
13010             }
13011           }
13012
13013           return false;
13014         } // Return the intersection point of 2 line segments.
13015         // From https://github.com/pgkelley4/line-segments-intersect
13016         // This uses the vector cross product approach described below:
13017         //  http://stackoverflow.com/a/565282/786339
13018
13019         function geoLineIntersection(a, b) {
13020           var p = [a[0][0], a[0][1]];
13021           var p2 = [a[1][0], a[1][1]];
13022           var q = [b[0][0], b[0][1]];
13023           var q2 = [b[1][0], b[1][1]];
13024           var r = geoVecSubtract(p2, p);
13025           var s = geoVecSubtract(q2, q);
13026           var uNumerator = geoVecCross(geoVecSubtract(q, p), r);
13027           var denominator = geoVecCross(r, s);
13028
13029           if (uNumerator && denominator) {
13030             var u = uNumerator / denominator;
13031             var t = geoVecCross(geoVecSubtract(q, p), s) / denominator;
13032
13033             if (t >= 0 && t <= 1 && u >= 0 && u <= 1) {
13034               return geoVecInterp(p, p2, t);
13035             }
13036           }
13037
13038           return null;
13039         }
13040         function geoPathIntersections(path1, path2) {
13041           var intersections = [];
13042
13043           for (var i = 0; i < path1.length - 1; i++) {
13044             for (var j = 0; j < path2.length - 1; j++) {
13045               var a = [path1[i], path1[i + 1]];
13046               var b = [path2[j], path2[j + 1]];
13047               var hit = geoLineIntersection(a, b);
13048
13049               if (hit) {
13050                 intersections.push(hit);
13051               }
13052             }
13053           }
13054
13055           return intersections;
13056         }
13057         function geoPathHasIntersections(path1, path2) {
13058           for (var i = 0; i < path1.length - 1; i++) {
13059             for (var j = 0; j < path2.length - 1; j++) {
13060               var a = [path1[i], path1[i + 1]];
13061               var b = [path2[j], path2[j + 1]];
13062               var hit = geoLineIntersection(a, b);
13063
13064               if (hit) {
13065                 return true;
13066               }
13067             }
13068           }
13069
13070           return false;
13071         } // Return whether point is contained in polygon.
13072         //
13073         // `point` should be a 2-item array of coordinates.
13074         // `polygon` should be an array of 2-item arrays of coordinates.
13075         //
13076         // From https://github.com/substack/point-in-polygon.
13077         // ray-casting algorithm based on
13078         // http://www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html
13079         //
13080
13081         function geoPointInPolygon(point, polygon) {
13082           var x = point[0];
13083           var y = point[1];
13084           var inside = false;
13085
13086           for (var i = 0, j = polygon.length - 1; i < polygon.length; j = i++) {
13087             var xi = polygon[i][0];
13088             var yi = polygon[i][1];
13089             var xj = polygon[j][0];
13090             var yj = polygon[j][1];
13091             var intersect = yi > y !== yj > y && x < (xj - xi) * (y - yi) / (yj - yi) + xi;
13092             if (intersect) inside = !inside;
13093           }
13094
13095           return inside;
13096         }
13097         function geoPolygonContainsPolygon(outer, inner) {
13098           return inner.every(function (point) {
13099             return geoPointInPolygon(point, outer);
13100           });
13101         }
13102         function geoPolygonIntersectsPolygon(outer, inner, checkSegments) {
13103           function testPoints(outer, inner) {
13104             return inner.some(function (point) {
13105               return geoPointInPolygon(point, outer);
13106             });
13107           }
13108
13109           return testPoints(outer, inner) || !!checkSegments && geoPathHasIntersections(outer, inner);
13110         } // http://gis.stackexchange.com/questions/22895/finding-minimum-area-rectangle-for-given-points
13111         // http://gis.stackexchange.com/questions/3739/generalisation-strategies-for-building-outlines/3756#3756
13112
13113         function geoGetSmallestSurroundingRectangle(points) {
13114           var hull = d3_polygonHull(points);
13115           var centroid = d3_polygonCentroid(hull);
13116           var minArea = Infinity;
13117           var ssrExtent = [];
13118           var ssrAngle = 0;
13119           var c1 = hull[0];
13120
13121           for (var i = 0; i <= hull.length - 1; i++) {
13122             var c2 = i === hull.length - 1 ? hull[0] : hull[i + 1];
13123             var angle = Math.atan2(c2[1] - c1[1], c2[0] - c1[0]);
13124             var poly = geoRotate(hull, -angle, centroid);
13125             var extent = poly.reduce(function (extent, point) {
13126               return extent.extend(geoExtent(point));
13127             }, geoExtent());
13128             var area = extent.area();
13129
13130             if (area < minArea) {
13131               minArea = area;
13132               ssrExtent = extent;
13133               ssrAngle = angle;
13134             }
13135
13136             c1 = c2;
13137           }
13138
13139           return {
13140             poly: geoRotate(ssrExtent.polygon(), ssrAngle, centroid),
13141             angle: ssrAngle
13142           };
13143         }
13144         function geoPathLength(path) {
13145           var length = 0;
13146
13147           for (var i = 0; i < path.length - 1; i++) {
13148             length += geoVecLength(path[i], path[i + 1]);
13149           }
13150
13151           return length;
13152         } // If the given point is at the edge of the padded viewport,
13153         // return a vector that will nudge the viewport in that direction
13154
13155         function geoViewportEdge(point, dimensions) {
13156           var pad = [80, 20, 50, 20]; // top, right, bottom, left
13157
13158           var x = 0;
13159           var y = 0;
13160
13161           if (point[0] > dimensions[0] - pad[1]) {
13162             x = -10;
13163           }
13164
13165           if (point[0] < pad[3]) {
13166             x = 10;
13167           }
13168
13169           if (point[1] > dimensions[1] - pad[2]) {
13170             y = -10;
13171           }
13172
13173           if (point[1] < pad[0]) {
13174             y = 10;
13175           }
13176
13177           if (x || y) {
13178             return [x, y];
13179           } else {
13180             return null;
13181           }
13182         }
13183
13184         var noop = {
13185           value: function value() {}
13186         };
13187
13188         function dispatch$8() {
13189           for (var i = 0, n = arguments.length, _ = {}, t; i < n; ++i) {
13190             if (!(t = arguments[i] + "") || t in _ || /[\s.]/.test(t)) throw new Error("illegal type: " + t);
13191             _[t] = [];
13192           }
13193
13194           return new Dispatch(_);
13195         }
13196
13197         function Dispatch(_) {
13198           this._ = _;
13199         }
13200
13201         function parseTypenames$1(typenames, types) {
13202           return typenames.trim().split(/^|\s+/).map(function (t) {
13203             var name = "",
13204                 i = t.indexOf(".");
13205             if (i >= 0) name = t.slice(i + 1), t = t.slice(0, i);
13206             if (t && !types.hasOwnProperty(t)) throw new Error("unknown type: " + t);
13207             return {
13208               type: t,
13209               name: name
13210             };
13211           });
13212         }
13213
13214         Dispatch.prototype = dispatch$8.prototype = {
13215           constructor: Dispatch,
13216           on: function on(typename, callback) {
13217             var _ = this._,
13218                 T = parseTypenames$1(typename + "", _),
13219                 t,
13220                 i = -1,
13221                 n = T.length; // If no callback was specified, return the callback of the given type and name.
13222
13223             if (arguments.length < 2) {
13224               while (++i < n) {
13225                 if ((t = (typename = T[i]).type) && (t = get$2(_[t], typename.name))) return t;
13226               }
13227
13228               return;
13229             } // If a type was specified, set the callback for the given type and name.
13230             // Otherwise, if a null callback was specified, remove callbacks of the given name.
13231
13232
13233             if (callback != null && typeof callback !== "function") throw new Error("invalid callback: " + callback);
13234
13235             while (++i < n) {
13236               if (t = (typename = T[i]).type) _[t] = set$1(_[t], typename.name, callback);else if (callback == null) for (t in _) {
13237                 _[t] = set$1(_[t], typename.name, null);
13238               }
13239             }
13240
13241             return this;
13242           },
13243           copy: function copy() {
13244             var copy = {},
13245                 _ = this._;
13246
13247             for (var t in _) {
13248               copy[t] = _[t].slice();
13249             }
13250
13251             return new Dispatch(copy);
13252           },
13253           call: function call(type, that) {
13254             if ((n = arguments.length - 2) > 0) for (var args = new Array(n), i = 0, n, t; i < n; ++i) {
13255               args[i] = arguments[i + 2];
13256             }
13257             if (!this._.hasOwnProperty(type)) throw new Error("unknown type: " + type);
13258
13259             for (t = this._[type], i = 0, n = t.length; i < n; ++i) {
13260               t[i].value.apply(that, args);
13261             }
13262           },
13263           apply: function apply(type, that, args) {
13264             if (!this._.hasOwnProperty(type)) throw new Error("unknown type: " + type);
13265
13266             for (var t = this._[type], i = 0, n = t.length; i < n; ++i) {
13267               t[i].value.apply(that, args);
13268             }
13269           }
13270         };
13271
13272         function get$2(type, name) {
13273           for (var i = 0, n = type.length, c; i < n; ++i) {
13274             if ((c = type[i]).name === name) {
13275               return c.value;
13276             }
13277           }
13278         }
13279
13280         function set$1(type, name, callback) {
13281           for (var i = 0, n = type.length; i < n; ++i) {
13282             if (type[i].name === name) {
13283               type[i] = noop, type = type.slice(0, i).concat(type.slice(i + 1));
13284               break;
13285             }
13286           }
13287
13288           if (callback != null) type.push({
13289             name: name,
13290             value: callback
13291           });
13292           return type;
13293         }
13294
13295         var xhtml = "http://www.w3.org/1999/xhtml";
13296         var namespaces = {
13297           svg: "http://www.w3.org/2000/svg",
13298           xhtml: xhtml,
13299           xlink: "http://www.w3.org/1999/xlink",
13300           xml: "http://www.w3.org/XML/1998/namespace",
13301           xmlns: "http://www.w3.org/2000/xmlns/"
13302         };
13303
13304         function namespace (name) {
13305           var prefix = name += "",
13306               i = prefix.indexOf(":");
13307           if (i >= 0 && (prefix = name.slice(0, i)) !== "xmlns") name = name.slice(i + 1);
13308           return namespaces.hasOwnProperty(prefix) ? {
13309             space: namespaces[prefix],
13310             local: name
13311           } : name; // eslint-disable-line no-prototype-builtins
13312         }
13313
13314         function creatorInherit(name) {
13315           return function () {
13316             var document = this.ownerDocument,
13317                 uri = this.namespaceURI;
13318             return uri === xhtml && document.documentElement.namespaceURI === xhtml ? document.createElement(name) : document.createElementNS(uri, name);
13319           };
13320         }
13321
13322         function creatorFixed(fullname) {
13323           return function () {
13324             return this.ownerDocument.createElementNS(fullname.space, fullname.local);
13325           };
13326         }
13327
13328         function creator (name) {
13329           var fullname = namespace(name);
13330           return (fullname.local ? creatorFixed : creatorInherit)(fullname);
13331         }
13332
13333         function none() {}
13334
13335         function selector (selector) {
13336           return selector == null ? none : function () {
13337             return this.querySelector(selector);
13338           };
13339         }
13340
13341         function selection_select (select) {
13342           if (typeof select !== "function") select = selector(select);
13343
13344           for (var groups = this._groups, m = groups.length, subgroups = new Array(m), j = 0; j < m; ++j) {
13345             for (var group = groups[j], n = group.length, subgroup = subgroups[j] = new Array(n), node, subnode, i = 0; i < n; ++i) {
13346               if ((node = group[i]) && (subnode = select.call(node, node.__data__, i, group))) {
13347                 if ("__data__" in node) subnode.__data__ = node.__data__;
13348                 subgroup[i] = subnode;
13349               }
13350             }
13351           }
13352
13353           return new Selection$1(subgroups, this._parents);
13354         }
13355
13356         function array (x) {
13357           return _typeof(x) === "object" && "length" in x ? x // Array, TypedArray, NodeList, array-like
13358           : Array.from(x); // Map, Set, iterable, string, or anything else
13359         }
13360
13361         function empty() {
13362           return [];
13363         }
13364
13365         function selectorAll (selector) {
13366           return selector == null ? empty : function () {
13367             return this.querySelectorAll(selector);
13368           };
13369         }
13370
13371         function arrayAll(select) {
13372           return function () {
13373             var group = select.apply(this, arguments);
13374             return group == null ? [] : array(group);
13375           };
13376         }
13377
13378         function selection_selectAll (select) {
13379           if (typeof select === "function") select = arrayAll(select);else select = selectorAll(select);
13380
13381           for (var groups = this._groups, m = groups.length, subgroups = [], parents = [], j = 0; j < m; ++j) {
13382             for (var group = groups[j], n = group.length, node, i = 0; i < n; ++i) {
13383               if (node = group[i]) {
13384                 subgroups.push(select.call(node, node.__data__, i, group));
13385                 parents.push(node);
13386               }
13387             }
13388           }
13389
13390           return new Selection$1(subgroups, parents);
13391         }
13392
13393         var $find = arrayIteration.find;
13394
13395
13396         var FIND = 'find';
13397         var SKIPS_HOLES$1 = true;
13398
13399         // Shouldn't skip holes
13400         if (FIND in []) Array(1)[FIND](function () { SKIPS_HOLES$1 = false; });
13401
13402         // `Array.prototype.find` method
13403         // https://tc39.es/ecma262/#sec-array.prototype.find
13404         _export({ target: 'Array', proto: true, forced: SKIPS_HOLES$1 }, {
13405           find: function find(callbackfn /* , that = undefined */) {
13406             return $find(this, callbackfn, arguments.length > 1 ? arguments[1] : undefined);
13407           }
13408         });
13409
13410         // https://tc39.es/ecma262/#sec-array.prototype-@@unscopables
13411         addToUnscopables(FIND);
13412
13413         function matcher (selector) {
13414           return function () {
13415             return this.matches(selector);
13416           };
13417         }
13418         function childMatcher(selector) {
13419           return function (node) {
13420             return node.matches(selector);
13421           };
13422         }
13423
13424         var find = Array.prototype.find;
13425
13426         function childFind(match) {
13427           return function () {
13428             return find.call(this.children, match);
13429           };
13430         }
13431
13432         function childFirst() {
13433           return this.firstElementChild;
13434         }
13435
13436         function selection_selectChild (match) {
13437           return this.select(match == null ? childFirst : childFind(typeof match === "function" ? match : childMatcher(match)));
13438         }
13439
13440         var filter = Array.prototype.filter;
13441
13442         function children() {
13443           return this.children;
13444         }
13445
13446         function childrenFilter(match) {
13447           return function () {
13448             return filter.call(this.children, match);
13449           };
13450         }
13451
13452         function selection_selectChildren (match) {
13453           return this.selectAll(match == null ? children : childrenFilter(typeof match === "function" ? match : childMatcher(match)));
13454         }
13455
13456         function selection_filter (match) {
13457           if (typeof match !== "function") match = matcher(match);
13458
13459           for (var groups = this._groups, m = groups.length, subgroups = new Array(m), j = 0; j < m; ++j) {
13460             for (var group = groups[j], n = group.length, subgroup = subgroups[j] = [], node, i = 0; i < n; ++i) {
13461               if ((node = group[i]) && match.call(node, node.__data__, i, group)) {
13462                 subgroup.push(node);
13463               }
13464             }
13465           }
13466
13467           return new Selection$1(subgroups, this._parents);
13468         }
13469
13470         function sparse (update) {
13471           return new Array(update.length);
13472         }
13473
13474         function selection_enter () {
13475           return new Selection$1(this._enter || this._groups.map(sparse), this._parents);
13476         }
13477         function EnterNode(parent, datum) {
13478           this.ownerDocument = parent.ownerDocument;
13479           this.namespaceURI = parent.namespaceURI;
13480           this._next = null;
13481           this._parent = parent;
13482           this.__data__ = datum;
13483         }
13484         EnterNode.prototype = {
13485           constructor: EnterNode,
13486           appendChild: function appendChild(child) {
13487             return this._parent.insertBefore(child, this._next);
13488           },
13489           insertBefore: function insertBefore(child, next) {
13490             return this._parent.insertBefore(child, next);
13491           },
13492           querySelector: function querySelector(selector) {
13493             return this._parent.querySelector(selector);
13494           },
13495           querySelectorAll: function querySelectorAll(selector) {
13496             return this._parent.querySelectorAll(selector);
13497           }
13498         };
13499
13500         function constant$3 (x) {
13501           return function () {
13502             return x;
13503           };
13504         }
13505
13506         function bindIndex(parent, group, enter, update, exit, data) {
13507           var i = 0,
13508               node,
13509               groupLength = group.length,
13510               dataLength = data.length; // Put any non-null nodes that fit into update.
13511           // Put any null nodes into enter.
13512           // Put any remaining data into enter.
13513
13514           for (; i < dataLength; ++i) {
13515             if (node = group[i]) {
13516               node.__data__ = data[i];
13517               update[i] = node;
13518             } else {
13519               enter[i] = new EnterNode(parent, data[i]);
13520             }
13521           } // Put any non-null nodes that don’t fit into exit.
13522
13523
13524           for (; i < groupLength; ++i) {
13525             if (node = group[i]) {
13526               exit[i] = node;
13527             }
13528           }
13529         }
13530
13531         function bindKey(parent, group, enter, update, exit, data, key) {
13532           var i,
13533               node,
13534               nodeByKeyValue = new Map(),
13535               groupLength = group.length,
13536               dataLength = data.length,
13537               keyValues = new Array(groupLength),
13538               keyValue; // Compute the key for each node.
13539           // If multiple nodes have the same key, the duplicates are added to exit.
13540
13541           for (i = 0; i < groupLength; ++i) {
13542             if (node = group[i]) {
13543               keyValues[i] = keyValue = key.call(node, node.__data__, i, group) + "";
13544
13545               if (nodeByKeyValue.has(keyValue)) {
13546                 exit[i] = node;
13547               } else {
13548                 nodeByKeyValue.set(keyValue, node);
13549               }
13550             }
13551           } // Compute the key for each datum.
13552           // If there a node associated with this key, join and add it to update.
13553           // If there is not (or the key is a duplicate), add it to enter.
13554
13555
13556           for (i = 0; i < dataLength; ++i) {
13557             keyValue = key.call(parent, data[i], i, data) + "";
13558
13559             if (node = nodeByKeyValue.get(keyValue)) {
13560               update[i] = node;
13561               node.__data__ = data[i];
13562               nodeByKeyValue["delete"](keyValue);
13563             } else {
13564               enter[i] = new EnterNode(parent, data[i]);
13565             }
13566           } // Add any remaining nodes that were not bound to data to exit.
13567
13568
13569           for (i = 0; i < groupLength; ++i) {
13570             if ((node = group[i]) && nodeByKeyValue.get(keyValues[i]) === node) {
13571               exit[i] = node;
13572             }
13573           }
13574         }
13575
13576         function datum(node) {
13577           return node.__data__;
13578         }
13579
13580         function selection_data (value, key) {
13581           if (!arguments.length) return Array.from(this, datum);
13582           var bind = key ? bindKey : bindIndex,
13583               parents = this._parents,
13584               groups = this._groups;
13585           if (typeof value !== "function") value = constant$3(value);
13586
13587           for (var m = groups.length, update = new Array(m), enter = new Array(m), exit = new Array(m), j = 0; j < m; ++j) {
13588             var parent = parents[j],
13589                 group = groups[j],
13590                 groupLength = group.length,
13591                 data = array(value.call(parent, parent && parent.__data__, j, parents)),
13592                 dataLength = data.length,
13593                 enterGroup = enter[j] = new Array(dataLength),
13594                 updateGroup = update[j] = new Array(dataLength),
13595                 exitGroup = exit[j] = new Array(groupLength);
13596             bind(parent, group, enterGroup, updateGroup, exitGroup, data, key); // Now connect the enter nodes to their following update node, such that
13597             // appendChild can insert the materialized enter node before this node,
13598             // rather than at the end of the parent node.
13599
13600             for (var i0 = 0, i1 = 0, previous, next; i0 < dataLength; ++i0) {
13601               if (previous = enterGroup[i0]) {
13602                 if (i0 >= i1) i1 = i0 + 1;
13603
13604                 while (!(next = updateGroup[i1]) && ++i1 < dataLength) {
13605                 }
13606
13607                 previous._next = next || null;
13608               }
13609             }
13610           }
13611
13612           update = new Selection$1(update, parents);
13613           update._enter = enter;
13614           update._exit = exit;
13615           return update;
13616         }
13617
13618         function selection_exit () {
13619           return new Selection$1(this._exit || this._groups.map(sparse), this._parents);
13620         }
13621
13622         function selection_join (onenter, onupdate, onexit) {
13623           var enter = this.enter(),
13624               update = this,
13625               exit = this.exit();
13626           enter = typeof onenter === "function" ? onenter(enter) : enter.append(onenter + "");
13627           if (onupdate != null) update = onupdate(update);
13628           if (onexit == null) exit.remove();else onexit(exit);
13629           return enter && update ? enter.merge(update).order() : update;
13630         }
13631
13632         function selection_merge (selection) {
13633           if (!(selection instanceof Selection$1)) throw new Error("invalid merge");
13634
13635           for (var groups0 = this._groups, groups1 = selection._groups, m0 = groups0.length, m1 = groups1.length, m = Math.min(m0, m1), merges = new Array(m0), j = 0; j < m; ++j) {
13636             for (var group0 = groups0[j], group1 = groups1[j], n = group0.length, merge = merges[j] = new Array(n), node, i = 0; i < n; ++i) {
13637               if (node = group0[i] || group1[i]) {
13638                 merge[i] = node;
13639               }
13640             }
13641           }
13642
13643           for (; j < m0; ++j) {
13644             merges[j] = groups0[j];
13645           }
13646
13647           return new Selection$1(merges, this._parents);
13648         }
13649
13650         function selection_order () {
13651           for (var groups = this._groups, j = -1, m = groups.length; ++j < m;) {
13652             for (var group = groups[j], i = group.length - 1, next = group[i], node; --i >= 0;) {
13653               if (node = group[i]) {
13654                 if (next && node.compareDocumentPosition(next) ^ 4) next.parentNode.insertBefore(node, next);
13655                 next = node;
13656               }
13657             }
13658           }
13659
13660           return this;
13661         }
13662
13663         function selection_sort (compare) {
13664           if (!compare) compare = ascending;
13665
13666           function compareNode(a, b) {
13667             return a && b ? compare(a.__data__, b.__data__) : !a - !b;
13668           }
13669
13670           for (var groups = this._groups, m = groups.length, sortgroups = new Array(m), j = 0; j < m; ++j) {
13671             for (var group = groups[j], n = group.length, sortgroup = sortgroups[j] = new Array(n), node, i = 0; i < n; ++i) {
13672               if (node = group[i]) {
13673                 sortgroup[i] = node;
13674               }
13675             }
13676
13677             sortgroup.sort(compareNode);
13678           }
13679
13680           return new Selection$1(sortgroups, this._parents).order();
13681         }
13682
13683         function ascending(a, b) {
13684           return a < b ? -1 : a > b ? 1 : a >= b ? 0 : NaN;
13685         }
13686
13687         function selection_call () {
13688           var callback = arguments[0];
13689           arguments[0] = this;
13690           callback.apply(null, arguments);
13691           return this;
13692         }
13693
13694         function selection_nodes () {
13695           return Array.from(this);
13696         }
13697
13698         function selection_node () {
13699           for (var groups = this._groups, j = 0, m = groups.length; j < m; ++j) {
13700             for (var group = groups[j], i = 0, n = group.length; i < n; ++i) {
13701               var node = group[i];
13702               if (node) return node;
13703             }
13704           }
13705
13706           return null;
13707         }
13708
13709         function selection_size () {
13710           var size = 0;
13711
13712           var _iterator = _createForOfIteratorHelper(this),
13713               _step;
13714
13715           try {
13716             for (_iterator.s(); !(_step = _iterator.n()).done;) {
13717               var node = _step.value;
13718               ++size;
13719             } // eslint-disable-line no-unused-vars
13720
13721           } catch (err) {
13722             _iterator.e(err);
13723           } finally {
13724             _iterator.f();
13725           }
13726
13727           return size;
13728         }
13729
13730         function selection_empty () {
13731           return !this.node();
13732         }
13733
13734         function selection_each (callback) {
13735           for (var groups = this._groups, j = 0, m = groups.length; j < m; ++j) {
13736             for (var group = groups[j], i = 0, n = group.length, node; i < n; ++i) {
13737               if (node = group[i]) callback.call(node, node.__data__, i, group);
13738             }
13739           }
13740
13741           return this;
13742         }
13743
13744         function attrRemove$1(name) {
13745           return function () {
13746             this.removeAttribute(name);
13747           };
13748         }
13749
13750         function attrRemoveNS$1(fullname) {
13751           return function () {
13752             this.removeAttributeNS(fullname.space, fullname.local);
13753           };
13754         }
13755
13756         function attrConstant$1(name, value) {
13757           return function () {
13758             this.setAttribute(name, value);
13759           };
13760         }
13761
13762         function attrConstantNS$1(fullname, value) {
13763           return function () {
13764             this.setAttributeNS(fullname.space, fullname.local, value);
13765           };
13766         }
13767
13768         function attrFunction$1(name, value) {
13769           return function () {
13770             var v = value.apply(this, arguments);
13771             if (v == null) this.removeAttribute(name);else this.setAttribute(name, v);
13772           };
13773         }
13774
13775         function attrFunctionNS$1(fullname, value) {
13776           return function () {
13777             var v = value.apply(this, arguments);
13778             if (v == null) this.removeAttributeNS(fullname.space, fullname.local);else this.setAttributeNS(fullname.space, fullname.local, v);
13779           };
13780         }
13781
13782         function selection_attr (name, value) {
13783           var fullname = namespace(name);
13784
13785           if (arguments.length < 2) {
13786             var node = this.node();
13787             return fullname.local ? node.getAttributeNS(fullname.space, fullname.local) : node.getAttribute(fullname);
13788           }
13789
13790           return this.each((value == null ? fullname.local ? attrRemoveNS$1 : attrRemove$1 : typeof value === "function" ? fullname.local ? attrFunctionNS$1 : attrFunction$1 : fullname.local ? attrConstantNS$1 : attrConstant$1)(fullname, value));
13791         }
13792
13793         function defaultView (node) {
13794           return node.ownerDocument && node.ownerDocument.defaultView || // node is a Node
13795           node.document && node // node is a Window
13796           || node.defaultView; // node is a Document
13797         }
13798
13799         function styleRemove$1(name) {
13800           return function () {
13801             this.style.removeProperty(name);
13802           };
13803         }
13804
13805         function styleConstant$1(name, value, priority) {
13806           return function () {
13807             this.style.setProperty(name, value, priority);
13808           };
13809         }
13810
13811         function styleFunction$1(name, value, priority) {
13812           return function () {
13813             var v = value.apply(this, arguments);
13814             if (v == null) this.style.removeProperty(name);else this.style.setProperty(name, v, priority);
13815           };
13816         }
13817
13818         function selection_style (name, value, priority) {
13819           return arguments.length > 1 ? this.each((value == null ? styleRemove$1 : typeof value === "function" ? styleFunction$1 : styleConstant$1)(name, value, priority == null ? "" : priority)) : styleValue(this.node(), name);
13820         }
13821         function styleValue(node, name) {
13822           return node.style.getPropertyValue(name) || defaultView(node).getComputedStyle(node, null).getPropertyValue(name);
13823         }
13824
13825         function propertyRemove(name) {
13826           return function () {
13827             delete this[name];
13828           };
13829         }
13830
13831         function propertyConstant(name, value) {
13832           return function () {
13833             this[name] = value;
13834           };
13835         }
13836
13837         function propertyFunction(name, value) {
13838           return function () {
13839             var v = value.apply(this, arguments);
13840             if (v == null) delete this[name];else this[name] = v;
13841           };
13842         }
13843
13844         function selection_property (name, value) {
13845           return arguments.length > 1 ? this.each((value == null ? propertyRemove : typeof value === "function" ? propertyFunction : propertyConstant)(name, value)) : this.node()[name];
13846         }
13847
13848         function classArray(string) {
13849           return string.trim().split(/^|\s+/);
13850         }
13851
13852         function classList(node) {
13853           return node.classList || new ClassList(node);
13854         }
13855
13856         function ClassList(node) {
13857           this._node = node;
13858           this._names = classArray(node.getAttribute("class") || "");
13859         }
13860
13861         ClassList.prototype = {
13862           add: function add(name) {
13863             var i = this._names.indexOf(name);
13864
13865             if (i < 0) {
13866               this._names.push(name);
13867
13868               this._node.setAttribute("class", this._names.join(" "));
13869             }
13870           },
13871           remove: function remove(name) {
13872             var i = this._names.indexOf(name);
13873
13874             if (i >= 0) {
13875               this._names.splice(i, 1);
13876
13877               this._node.setAttribute("class", this._names.join(" "));
13878             }
13879           },
13880           contains: function contains(name) {
13881             return this._names.indexOf(name) >= 0;
13882           }
13883         };
13884
13885         function classedAdd(node, names) {
13886           var list = classList(node),
13887               i = -1,
13888               n = names.length;
13889
13890           while (++i < n) {
13891             list.add(names[i]);
13892           }
13893         }
13894
13895         function classedRemove(node, names) {
13896           var list = classList(node),
13897               i = -1,
13898               n = names.length;
13899
13900           while (++i < n) {
13901             list.remove(names[i]);
13902           }
13903         }
13904
13905         function classedTrue(names) {
13906           return function () {
13907             classedAdd(this, names);
13908           };
13909         }
13910
13911         function classedFalse(names) {
13912           return function () {
13913             classedRemove(this, names);
13914           };
13915         }
13916
13917         function classedFunction(names, value) {
13918           return function () {
13919             (value.apply(this, arguments) ? classedAdd : classedRemove)(this, names);
13920           };
13921         }
13922
13923         function selection_classed (name, value) {
13924           var names = classArray(name + "");
13925
13926           if (arguments.length < 2) {
13927             var list = classList(this.node()),
13928                 i = -1,
13929                 n = names.length;
13930
13931             while (++i < n) {
13932               if (!list.contains(names[i])) return false;
13933             }
13934
13935             return true;
13936           }
13937
13938           return this.each((typeof value === "function" ? classedFunction : value ? classedTrue : classedFalse)(names, value));
13939         }
13940
13941         function textRemove() {
13942           this.textContent = "";
13943         }
13944
13945         function textConstant$1(value) {
13946           return function () {
13947             this.textContent = value;
13948           };
13949         }
13950
13951         function textFunction$1(value) {
13952           return function () {
13953             var v = value.apply(this, arguments);
13954             this.textContent = v == null ? "" : v;
13955           };
13956         }
13957
13958         function selection_text (value) {
13959           return arguments.length ? this.each(value == null ? textRemove : (typeof value === "function" ? textFunction$1 : textConstant$1)(value)) : this.node().textContent;
13960         }
13961
13962         function htmlRemove() {
13963           this.innerHTML = "";
13964         }
13965
13966         function htmlConstant(value) {
13967           return function () {
13968             this.innerHTML = value;
13969           };
13970         }
13971
13972         function htmlFunction(value) {
13973           return function () {
13974             var v = value.apply(this, arguments);
13975             this.innerHTML = v == null ? "" : v;
13976           };
13977         }
13978
13979         function selection_html (value) {
13980           return arguments.length ? this.each(value == null ? htmlRemove : (typeof value === "function" ? htmlFunction : htmlConstant)(value)) : this.node().innerHTML;
13981         }
13982
13983         function raise() {
13984           if (this.nextSibling) this.parentNode.appendChild(this);
13985         }
13986
13987         function selection_raise () {
13988           return this.each(raise);
13989         }
13990
13991         function lower() {
13992           if (this.previousSibling) this.parentNode.insertBefore(this, this.parentNode.firstChild);
13993         }
13994
13995         function selection_lower () {
13996           return this.each(lower);
13997         }
13998
13999         function selection_append (name) {
14000           var create = typeof name === "function" ? name : creator(name);
14001           return this.select(function () {
14002             return this.appendChild(create.apply(this, arguments));
14003           });
14004         }
14005
14006         function constantNull() {
14007           return null;
14008         }
14009
14010         function selection_insert (name, before) {
14011           var create = typeof name === "function" ? name : creator(name),
14012               select = before == null ? constantNull : typeof before === "function" ? before : selector(before);
14013           return this.select(function () {
14014             return this.insertBefore(create.apply(this, arguments), select.apply(this, arguments) || null);
14015           });
14016         }
14017
14018         function remove$7() {
14019           var parent = this.parentNode;
14020           if (parent) parent.removeChild(this);
14021         }
14022
14023         function selection_remove () {
14024           return this.each(remove$7);
14025         }
14026
14027         function selection_cloneShallow() {
14028           var clone = this.cloneNode(false),
14029               parent = this.parentNode;
14030           return parent ? parent.insertBefore(clone, this.nextSibling) : clone;
14031         }
14032
14033         function selection_cloneDeep() {
14034           var clone = this.cloneNode(true),
14035               parent = this.parentNode;
14036           return parent ? parent.insertBefore(clone, this.nextSibling) : clone;
14037         }
14038
14039         function selection_clone (deep) {
14040           return this.select(deep ? selection_cloneDeep : selection_cloneShallow);
14041         }
14042
14043         function selection_datum (value) {
14044           return arguments.length ? this.property("__data__", value) : this.node().__data__;
14045         }
14046
14047         function contextListener(listener) {
14048           return function (event) {
14049             listener.call(this, event, this.__data__);
14050           };
14051         }
14052
14053         function parseTypenames(typenames) {
14054           return typenames.trim().split(/^|\s+/).map(function (t) {
14055             var name = "",
14056                 i = t.indexOf(".");
14057             if (i >= 0) name = t.slice(i + 1), t = t.slice(0, i);
14058             return {
14059               type: t,
14060               name: name
14061             };
14062           });
14063         }
14064
14065         function onRemove(typename) {
14066           return function () {
14067             var on = this.__on;
14068             if (!on) return;
14069
14070             for (var j = 0, i = -1, m = on.length, o; j < m; ++j) {
14071               if (o = on[j], (!typename.type || o.type === typename.type) && o.name === typename.name) {
14072                 this.removeEventListener(o.type, o.listener, o.options);
14073               } else {
14074                 on[++i] = o;
14075               }
14076             }
14077
14078             if (++i) on.length = i;else delete this.__on;
14079           };
14080         }
14081
14082         function onAdd(typename, value, options) {
14083           return function () {
14084             var on = this.__on,
14085                 o,
14086                 listener = contextListener(value);
14087             if (on) for (var j = 0, m = on.length; j < m; ++j) {
14088               if ((o = on[j]).type === typename.type && o.name === typename.name) {
14089                 this.removeEventListener(o.type, o.listener, o.options);
14090                 this.addEventListener(o.type, o.listener = listener, o.options = options);
14091                 o.value = value;
14092                 return;
14093               }
14094             }
14095             this.addEventListener(typename.type, listener, options);
14096             o = {
14097               type: typename.type,
14098               name: typename.name,
14099               value: value,
14100               listener: listener,
14101               options: options
14102             };
14103             if (!on) this.__on = [o];else on.push(o);
14104           };
14105         }
14106
14107         function selection_on (typename, value, options) {
14108           var typenames = parseTypenames(typename + ""),
14109               i,
14110               n = typenames.length,
14111               t;
14112
14113           if (arguments.length < 2) {
14114             var on = this.node().__on;
14115
14116             if (on) for (var j = 0, m = on.length, o; j < m; ++j) {
14117               for (i = 0, o = on[j]; i < n; ++i) {
14118                 if ((t = typenames[i]).type === o.type && t.name === o.name) {
14119                   return o.value;
14120                 }
14121               }
14122             }
14123             return;
14124           }
14125
14126           on = value ? onAdd : onRemove;
14127
14128           for (i = 0; i < n; ++i) {
14129             this.each(on(typenames[i], value, options));
14130           }
14131
14132           return this;
14133         }
14134
14135         function dispatchEvent(node, type, params) {
14136           var window = defaultView(node),
14137               event = window.CustomEvent;
14138
14139           if (typeof event === "function") {
14140             event = new event(type, params);
14141           } else {
14142             event = window.document.createEvent("Event");
14143             if (params) event.initEvent(type, params.bubbles, params.cancelable), event.detail = params.detail;else event.initEvent(type, false, false);
14144           }
14145
14146           node.dispatchEvent(event);
14147         }
14148
14149         function dispatchConstant(type, params) {
14150           return function () {
14151             return dispatchEvent(this, type, params);
14152           };
14153         }
14154
14155         function dispatchFunction(type, params) {
14156           return function () {
14157             return dispatchEvent(this, type, params.apply(this, arguments));
14158           };
14159         }
14160
14161         function selection_dispatch (type, params) {
14162           return this.each((typeof params === "function" ? dispatchFunction : dispatchConstant)(type, params));
14163         }
14164
14165         var _marked$1 = /*#__PURE__*/regeneratorRuntime.mark(_callee);
14166
14167         function _callee() {
14168           var groups, j, m, group, i, n, node;
14169           return regeneratorRuntime.wrap(function _callee$(_context) {
14170             while (1) {
14171               switch (_context.prev = _context.next) {
14172                 case 0:
14173                   groups = this._groups, j = 0, m = groups.length;
14174
14175                 case 1:
14176                   if (!(j < m)) {
14177                     _context.next = 13;
14178                     break;
14179                   }
14180
14181                   group = groups[j], i = 0, n = group.length;
14182
14183                 case 3:
14184                   if (!(i < n)) {
14185                     _context.next = 10;
14186                     break;
14187                   }
14188
14189                   if (!(node = group[i])) {
14190                     _context.next = 7;
14191                     break;
14192                   }
14193
14194                   _context.next = 7;
14195                   return node;
14196
14197                 case 7:
14198                   ++i;
14199                   _context.next = 3;
14200                   break;
14201
14202                 case 10:
14203                   ++j;
14204                   _context.next = 1;
14205                   break;
14206
14207                 case 13:
14208                 case "end":
14209                   return _context.stop();
14210               }
14211             }
14212           }, _marked$1, this);
14213         }
14214
14215         var root$1 = [null];
14216         function Selection$1(groups, parents) {
14217           this._groups = groups;
14218           this._parents = parents;
14219         }
14220
14221         function selection() {
14222           return new Selection$1([[document.documentElement]], root$1);
14223         }
14224
14225         function selection_selection() {
14226           return this;
14227         }
14228
14229         Selection$1.prototype = selection.prototype = _defineProperty({
14230           constructor: Selection$1,
14231           select: selection_select,
14232           selectAll: selection_selectAll,
14233           selectChild: selection_selectChild,
14234           selectChildren: selection_selectChildren,
14235           filter: selection_filter,
14236           data: selection_data,
14237           enter: selection_enter,
14238           exit: selection_exit,
14239           join: selection_join,
14240           merge: selection_merge,
14241           selection: selection_selection,
14242           order: selection_order,
14243           sort: selection_sort,
14244           call: selection_call,
14245           nodes: selection_nodes,
14246           node: selection_node,
14247           size: selection_size,
14248           empty: selection_empty,
14249           each: selection_each,
14250           attr: selection_attr,
14251           style: selection_style,
14252           property: selection_property,
14253           classed: selection_classed,
14254           text: selection_text,
14255           html: selection_html,
14256           raise: selection_raise,
14257           lower: selection_lower,
14258           append: selection_append,
14259           insert: selection_insert,
14260           remove: selection_remove,
14261           clone: selection_clone,
14262           datum: selection_datum,
14263           on: selection_on,
14264           dispatch: selection_dispatch
14265         }, Symbol.iterator, _callee);
14266
14267         function select (selector) {
14268           return typeof selector === "string" ? new Selection$1([[document.querySelector(selector)]], [document.documentElement]) : new Selection$1([[selector]], root$1);
14269         }
14270
14271         function sourceEvent (event) {
14272           var sourceEvent;
14273
14274           while (sourceEvent = event.sourceEvent) {
14275             event = sourceEvent;
14276           }
14277
14278           return event;
14279         }
14280
14281         function pointer (event, node) {
14282           event = sourceEvent(event);
14283           if (node === undefined) node = event.currentTarget;
14284
14285           if (node) {
14286             var svg = node.ownerSVGElement || node;
14287
14288             if (svg.createSVGPoint) {
14289               var point = svg.createSVGPoint();
14290               point.x = event.clientX, point.y = event.clientY;
14291               point = point.matrixTransform(node.getScreenCTM().inverse());
14292               return [point.x, point.y];
14293             }
14294
14295             if (node.getBoundingClientRect) {
14296               var rect = node.getBoundingClientRect();
14297               return [event.clientX - rect.left - node.clientLeft, event.clientY - rect.top - node.clientTop];
14298             }
14299           }
14300
14301           return [event.pageX, event.pageY];
14302         }
14303
14304         function selectAll (selector) {
14305           return typeof selector === "string" ? new Selection$1([document.querySelectorAll(selector)], [document.documentElement]) : new Selection$1([selector == null ? [] : array(selector)], root$1);
14306         }
14307
14308         function nopropagation$1(event) {
14309           event.stopImmediatePropagation();
14310         }
14311         function noevent$1 (event) {
14312           event.preventDefault();
14313           event.stopImmediatePropagation();
14314         }
14315
14316         function dragDisable (view) {
14317           var root = view.document.documentElement,
14318               selection = select(view).on("dragstart.drag", noevent$1, true);
14319
14320           if ("onselectstart" in root) {
14321             selection.on("selectstart.drag", noevent$1, true);
14322           } else {
14323             root.__noselect = root.style.MozUserSelect;
14324             root.style.MozUserSelect = "none";
14325           }
14326         }
14327         function yesdrag(view, noclick) {
14328           var root = view.document.documentElement,
14329               selection = select(view).on("dragstart.drag", null);
14330
14331           if (noclick) {
14332             selection.on("click.drag", noevent$1, true);
14333             setTimeout(function () {
14334               selection.on("click.drag", null);
14335             }, 0);
14336           }
14337
14338           if ("onselectstart" in root) {
14339             selection.on("selectstart.drag", null);
14340           } else {
14341             root.style.MozUserSelect = root.__noselect;
14342             delete root.__noselect;
14343           }
14344         }
14345
14346         var constant$2 = (function (x) {
14347           return function () {
14348             return x;
14349           };
14350         });
14351
14352         function DragEvent(type, _ref) {
14353           var sourceEvent = _ref.sourceEvent,
14354               subject = _ref.subject,
14355               target = _ref.target,
14356               identifier = _ref.identifier,
14357               active = _ref.active,
14358               x = _ref.x,
14359               y = _ref.y,
14360               dx = _ref.dx,
14361               dy = _ref.dy,
14362               dispatch = _ref.dispatch;
14363           Object.defineProperties(this, {
14364             type: {
14365               value: type,
14366               enumerable: true,
14367               configurable: true
14368             },
14369             sourceEvent: {
14370               value: sourceEvent,
14371               enumerable: true,
14372               configurable: true
14373             },
14374             subject: {
14375               value: subject,
14376               enumerable: true,
14377               configurable: true
14378             },
14379             target: {
14380               value: target,
14381               enumerable: true,
14382               configurable: true
14383             },
14384             identifier: {
14385               value: identifier,
14386               enumerable: true,
14387               configurable: true
14388             },
14389             active: {
14390               value: active,
14391               enumerable: true,
14392               configurable: true
14393             },
14394             x: {
14395               value: x,
14396               enumerable: true,
14397               configurable: true
14398             },
14399             y: {
14400               value: y,
14401               enumerable: true,
14402               configurable: true
14403             },
14404             dx: {
14405               value: dx,
14406               enumerable: true,
14407               configurable: true
14408             },
14409             dy: {
14410               value: dy,
14411               enumerable: true,
14412               configurable: true
14413             },
14414             _: {
14415               value: dispatch
14416             }
14417           });
14418         }
14419
14420         DragEvent.prototype.on = function () {
14421           var value = this._.on.apply(this._, arguments);
14422
14423           return value === this._ ? this : value;
14424         };
14425
14426         function defaultFilter$2(event) {
14427           return !event.ctrlKey && !event.button;
14428         }
14429
14430         function defaultContainer() {
14431           return this.parentNode;
14432         }
14433
14434         function defaultSubject(event, d) {
14435           return d == null ? {
14436             x: event.x,
14437             y: event.y
14438           } : d;
14439         }
14440
14441         function defaultTouchable$1() {
14442           return navigator.maxTouchPoints || "ontouchstart" in this;
14443         }
14444
14445         function d3_drag () {
14446           var filter = defaultFilter$2,
14447               container = defaultContainer,
14448               subject = defaultSubject,
14449               touchable = defaultTouchable$1,
14450               gestures = {},
14451               listeners = dispatch$8("start", "drag", "end"),
14452               active = 0,
14453               mousedownx,
14454               mousedowny,
14455               mousemoving,
14456               touchending,
14457               clickDistance2 = 0;
14458
14459           function drag(selection) {
14460             selection.on("mousedown.drag", mousedowned).filter(touchable).on("touchstart.drag", touchstarted).on("touchmove.drag", touchmoved).on("touchend.drag touchcancel.drag", touchended).style("touch-action", "none").style("-webkit-tap-highlight-color", "rgba(0,0,0,0)");
14461           }
14462
14463           function mousedowned(event, d) {
14464             if (touchending || !filter.call(this, event, d)) return;
14465             var gesture = beforestart(this, container.call(this, event, d), event, d, "mouse");
14466             if (!gesture) return;
14467             select(event.view).on("mousemove.drag", mousemoved, true).on("mouseup.drag", mouseupped, true);
14468             dragDisable(event.view);
14469             nopropagation$1(event);
14470             mousemoving = false;
14471             mousedownx = event.clientX;
14472             mousedowny = event.clientY;
14473             gesture("start", event);
14474           }
14475
14476           function mousemoved(event) {
14477             noevent$1(event);
14478
14479             if (!mousemoving) {
14480               var dx = event.clientX - mousedownx,
14481                   dy = event.clientY - mousedowny;
14482               mousemoving = dx * dx + dy * dy > clickDistance2;
14483             }
14484
14485             gestures.mouse("drag", event);
14486           }
14487
14488           function mouseupped(event) {
14489             select(event.view).on("mousemove.drag mouseup.drag", null);
14490             yesdrag(event.view, mousemoving);
14491             noevent$1(event);
14492             gestures.mouse("end", event);
14493           }
14494
14495           function touchstarted(event, d) {
14496             if (!filter.call(this, event, d)) return;
14497             var touches = event.changedTouches,
14498                 c = container.call(this, event, d),
14499                 n = touches.length,
14500                 i,
14501                 gesture;
14502
14503             for (i = 0; i < n; ++i) {
14504               if (gesture = beforestart(this, c, event, d, touches[i].identifier, touches[i])) {
14505                 nopropagation$1(event);
14506                 gesture("start", event, touches[i]);
14507               }
14508             }
14509           }
14510
14511           function touchmoved(event) {
14512             var touches = event.changedTouches,
14513                 n = touches.length,
14514                 i,
14515                 gesture;
14516
14517             for (i = 0; i < n; ++i) {
14518               if (gesture = gestures[touches[i].identifier]) {
14519                 noevent$1(event);
14520                 gesture("drag", event, touches[i]);
14521               }
14522             }
14523           }
14524
14525           function touchended(event) {
14526             var touches = event.changedTouches,
14527                 n = touches.length,
14528                 i,
14529                 gesture;
14530             if (touchending) clearTimeout(touchending);
14531             touchending = setTimeout(function () {
14532               touchending = null;
14533             }, 500); // Ghost clicks are delayed!
14534
14535             for (i = 0; i < n; ++i) {
14536               if (gesture = gestures[touches[i].identifier]) {
14537                 nopropagation$1(event);
14538                 gesture("end", event, touches[i]);
14539               }
14540             }
14541           }
14542
14543           function beforestart(that, container, event, d, identifier, touch) {
14544             var dispatch = listeners.copy(),
14545                 p = pointer(touch || event, container),
14546                 dx,
14547                 dy,
14548                 s;
14549             if ((s = subject.call(that, new DragEvent("beforestart", {
14550               sourceEvent: event,
14551               target: drag,
14552               identifier: identifier,
14553               active: active,
14554               x: p[0],
14555               y: p[1],
14556               dx: 0,
14557               dy: 0,
14558               dispatch: dispatch
14559             }), d)) == null) return;
14560             dx = s.x - p[0] || 0;
14561             dy = s.y - p[1] || 0;
14562             return function gesture(type, event, touch) {
14563               var p0 = p,
14564                   n;
14565
14566               switch (type) {
14567                 case "start":
14568                   gestures[identifier] = gesture, n = active++;
14569                   break;
14570
14571                 case "end":
14572                   delete gestures[identifier], --active;
14573                 // nobreak
14574
14575                 case "drag":
14576                   p = pointer(touch || event, container), n = active;
14577                   break;
14578               }
14579
14580               dispatch.call(type, that, new DragEvent(type, {
14581                 sourceEvent: event,
14582                 subject: s,
14583                 target: drag,
14584                 identifier: identifier,
14585                 active: n,
14586                 x: p[0] + dx,
14587                 y: p[1] + dy,
14588                 dx: p[0] - p0[0],
14589                 dy: p[1] - p0[1],
14590                 dispatch: dispatch
14591               }), d);
14592             };
14593           }
14594
14595           drag.filter = function (_) {
14596             return arguments.length ? (filter = typeof _ === "function" ? _ : constant$2(!!_), drag) : filter;
14597           };
14598
14599           drag.container = function (_) {
14600             return arguments.length ? (container = typeof _ === "function" ? _ : constant$2(_), drag) : container;
14601           };
14602
14603           drag.subject = function (_) {
14604             return arguments.length ? (subject = typeof _ === "function" ? _ : constant$2(_), drag) : subject;
14605           };
14606
14607           drag.touchable = function (_) {
14608             return arguments.length ? (touchable = typeof _ === "function" ? _ : constant$2(!!_), drag) : touchable;
14609           };
14610
14611           drag.on = function () {
14612             var value = listeners.on.apply(listeners, arguments);
14613             return value === listeners ? drag : value;
14614           };
14615
14616           drag.clickDistance = function (_) {
14617             return arguments.length ? (clickDistance2 = (_ = +_) * _, drag) : Math.sqrt(clickDistance2);
14618           };
14619
14620           return drag;
14621         }
14622
14623         var defineProperty$1 = objectDefineProperty.f;
14624         var getOwnPropertyNames$1 = objectGetOwnPropertyNames.f;
14625
14626
14627
14628
14629
14630
14631         var enforceInternalState = internalState.enforce;
14632
14633
14634
14635
14636
14637         var MATCH$1 = wellKnownSymbol('match');
14638         var NativeRegExp = global$2.RegExp;
14639         var RegExpPrototype = NativeRegExp.prototype;
14640         // TODO: Use only propper RegExpIdentifierName
14641         var IS_NCG = /^\?<[^\s\d!#%&*+<=>@^][^\s!#%&*+<=>@^]*>/;
14642         var re1 = /a/g;
14643         var re2 = /a/g;
14644
14645         // "new" should create a new object, old webkit bug
14646         var CORRECT_NEW = new NativeRegExp(re1) !== re1;
14647
14648         var UNSUPPORTED_Y = regexpStickyHelpers.UNSUPPORTED_Y;
14649
14650         var BASE_FORCED = descriptors &&
14651           (!CORRECT_NEW || UNSUPPORTED_Y || regexpUnsupportedDotAll || regexpUnsupportedNcg || fails(function () {
14652             re2[MATCH$1] = false;
14653             // RegExp constructor can alter flags and IsRegExp works correct with @@match
14654             return NativeRegExp(re1) != re1 || NativeRegExp(re2) == re2 || NativeRegExp(re1, 'i') != '/a/i';
14655           }));
14656
14657         var handleDotAll = function (string) {
14658           var length = string.length;
14659           var index = 0;
14660           var result = '';
14661           var brackets = false;
14662           var chr;
14663           for (; index <= length; index++) {
14664             chr = string.charAt(index);
14665             if (chr === '\\') {
14666               result += chr + string.charAt(++index);
14667               continue;
14668             }
14669             if (!brackets && chr === '.') {
14670               result += '[\\s\\S]';
14671             } else {
14672               if (chr === '[') {
14673                 brackets = true;
14674               } else if (chr === ']') {
14675                 brackets = false;
14676               } result += chr;
14677             }
14678           } return result;
14679         };
14680
14681         var handleNCG = function (string) {
14682           var length = string.length;
14683           var index = 0;
14684           var result = '';
14685           var named = [];
14686           var names = {};
14687           var brackets = false;
14688           var ncg = false;
14689           var groupid = 0;
14690           var groupname = '';
14691           var chr;
14692           for (; index <= length; index++) {
14693             chr = string.charAt(index);
14694             if (chr === '\\') {
14695               chr = chr + string.charAt(++index);
14696             } else if (chr === ']') {
14697               brackets = false;
14698             } else if (!brackets) switch (true) {
14699               case chr === '[':
14700                 brackets = true;
14701                 break;
14702               case chr === '(':
14703                 if (IS_NCG.test(string.slice(index + 1))) {
14704                   index += 2;
14705                   ncg = true;
14706                 }
14707                 result += chr;
14708                 groupid++;
14709                 continue;
14710               case chr === '>' && ncg:
14711                 if (groupname === '' || has$1(names, groupname)) {
14712                   throw new SyntaxError('Invalid capture group name');
14713                 }
14714                 names[groupname] = true;
14715                 named.push([groupname, groupid]);
14716                 ncg = false;
14717                 groupname = '';
14718                 continue;
14719             }
14720             if (ncg) groupname += chr;
14721             else result += chr;
14722           } return [result, named];
14723         };
14724
14725         // `RegExp` constructor
14726         // https://tc39.es/ecma262/#sec-regexp-constructor
14727         if (isForced_1('RegExp', BASE_FORCED)) {
14728           var RegExpWrapper = function RegExp(pattern, flags) {
14729             var thisIsRegExp = this instanceof RegExpWrapper;
14730             var patternIsRegExp = isRegexp(pattern);
14731             var flagsAreUndefined = flags === undefined;
14732             var groups = [];
14733             var rawPattern, rawFlags, dotAll, sticky, handled, result, state;
14734
14735             if (!thisIsRegExp && patternIsRegExp && pattern.constructor === RegExpWrapper && flagsAreUndefined) {
14736               return pattern;
14737             }
14738
14739             if (CORRECT_NEW) {
14740               if (patternIsRegExp && !flagsAreUndefined) pattern = pattern.source;
14741             } else if (pattern instanceof RegExpWrapper) {
14742               if (flagsAreUndefined) flags = regexpFlags.call(pattern);
14743               pattern = pattern.source;
14744             }
14745
14746             pattern = pattern === undefined ? '' : String(pattern);
14747             flags = flags === undefined ? '' : String(flags);
14748             rawPattern = pattern;
14749
14750             if (regexpUnsupportedDotAll && 'dotAll' in re1) {
14751               dotAll = !!flags && flags.indexOf('s') > -1;
14752               if (dotAll) flags = flags.replace(/s/g, '');
14753             }
14754
14755             rawFlags = flags;
14756
14757             if (UNSUPPORTED_Y && 'sticky' in re1) {
14758               sticky = !!flags && flags.indexOf('y') > -1;
14759               if (sticky) flags = flags.replace(/y/g, '');
14760             }
14761
14762             if (regexpUnsupportedNcg) {
14763               handled = handleNCG(pattern);
14764               pattern = handled[0];
14765               groups = handled[1];
14766             }
14767
14768             result = inheritIfRequired(
14769               CORRECT_NEW ? new NativeRegExp(pattern, flags) : NativeRegExp(pattern, flags),
14770               thisIsRegExp ? this : RegExpPrototype,
14771               RegExpWrapper
14772             );
14773
14774             if (dotAll || sticky || groups.length) {
14775               state = enforceInternalState(result);
14776               if (dotAll) {
14777                 state.dotAll = true;
14778                 state.raw = RegExpWrapper(handleDotAll(pattern), rawFlags);
14779               }
14780               if (sticky) state.sticky = true;
14781               if (groups.length) state.groups = groups;
14782             }
14783
14784             if (pattern !== rawPattern) try {
14785               // fails in old engines, but we have no alternatives for unsupported regex syntax
14786               createNonEnumerableProperty(result, 'source', rawPattern === '' ? '(?:)' : rawPattern);
14787             } catch (error) { /* empty */ }
14788
14789             return result;
14790           };
14791
14792           var proxy = function (key) {
14793             key in RegExpWrapper || defineProperty$1(RegExpWrapper, key, {
14794               configurable: true,
14795               get: function () { return NativeRegExp[key]; },
14796               set: function (it) { NativeRegExp[key] = it; }
14797             });
14798           };
14799
14800           for (var keys$1 = getOwnPropertyNames$1(NativeRegExp), index$1 = 0; keys$1.length > index$1;) {
14801             proxy(keys$1[index$1++]);
14802           }
14803
14804           RegExpPrototype.constructor = RegExpWrapper;
14805           RegExpWrapper.prototype = RegExpPrototype;
14806           redefine(global$2, 'RegExp', RegExpWrapper);
14807         }
14808
14809         // https://tc39.es/ecma262/#sec-get-regexp-@@species
14810         setSpecies('RegExp');
14811
14812         function define (constructor, factory, prototype) {
14813           constructor.prototype = factory.prototype = prototype;
14814           prototype.constructor = constructor;
14815         }
14816         function extend$3(parent, definition) {
14817           var prototype = Object.create(parent.prototype);
14818
14819           for (var key in definition) {
14820             prototype[key] = definition[key];
14821           }
14822
14823           return prototype;
14824         }
14825
14826         function Color() {}
14827         var _darker = 0.7;
14828
14829         var _brighter = 1 / _darker;
14830         var reI = "\\s*([+-]?\\d+)\\s*",
14831             reN = "\\s*([+-]?\\d*\\.?\\d+(?:[eE][+-]?\\d+)?)\\s*",
14832             reP = "\\s*([+-]?\\d*\\.?\\d+(?:[eE][+-]?\\d+)?)%\\s*",
14833             reHex = /^#([0-9a-f]{3,8})$/,
14834             reRgbInteger = new RegExp("^rgb\\(" + [reI, reI, reI] + "\\)$"),
14835             reRgbPercent = new RegExp("^rgb\\(" + [reP, reP, reP] + "\\)$"),
14836             reRgbaInteger = new RegExp("^rgba\\(" + [reI, reI, reI, reN] + "\\)$"),
14837             reRgbaPercent = new RegExp("^rgba\\(" + [reP, reP, reP, reN] + "\\)$"),
14838             reHslPercent = new RegExp("^hsl\\(" + [reN, reP, reP] + "\\)$"),
14839             reHslaPercent = new RegExp("^hsla\\(" + [reN, reP, reP, reN] + "\\)$");
14840         var named = {
14841           aliceblue: 0xf0f8ff,
14842           antiquewhite: 0xfaebd7,
14843           aqua: 0x00ffff,
14844           aquamarine: 0x7fffd4,
14845           azure: 0xf0ffff,
14846           beige: 0xf5f5dc,
14847           bisque: 0xffe4c4,
14848           black: 0x000000,
14849           blanchedalmond: 0xffebcd,
14850           blue: 0x0000ff,
14851           blueviolet: 0x8a2be2,
14852           brown: 0xa52a2a,
14853           burlywood: 0xdeb887,
14854           cadetblue: 0x5f9ea0,
14855           chartreuse: 0x7fff00,
14856           chocolate: 0xd2691e,
14857           coral: 0xff7f50,
14858           cornflowerblue: 0x6495ed,
14859           cornsilk: 0xfff8dc,
14860           crimson: 0xdc143c,
14861           cyan: 0x00ffff,
14862           darkblue: 0x00008b,
14863           darkcyan: 0x008b8b,
14864           darkgoldenrod: 0xb8860b,
14865           darkgray: 0xa9a9a9,
14866           darkgreen: 0x006400,
14867           darkgrey: 0xa9a9a9,
14868           darkkhaki: 0xbdb76b,
14869           darkmagenta: 0x8b008b,
14870           darkolivegreen: 0x556b2f,
14871           darkorange: 0xff8c00,
14872           darkorchid: 0x9932cc,
14873           darkred: 0x8b0000,
14874           darksalmon: 0xe9967a,
14875           darkseagreen: 0x8fbc8f,
14876           darkslateblue: 0x483d8b,
14877           darkslategray: 0x2f4f4f,
14878           darkslategrey: 0x2f4f4f,
14879           darkturquoise: 0x00ced1,
14880           darkviolet: 0x9400d3,
14881           deeppink: 0xff1493,
14882           deepskyblue: 0x00bfff,
14883           dimgray: 0x696969,
14884           dimgrey: 0x696969,
14885           dodgerblue: 0x1e90ff,
14886           firebrick: 0xb22222,
14887           floralwhite: 0xfffaf0,
14888           forestgreen: 0x228b22,
14889           fuchsia: 0xff00ff,
14890           gainsboro: 0xdcdcdc,
14891           ghostwhite: 0xf8f8ff,
14892           gold: 0xffd700,
14893           goldenrod: 0xdaa520,
14894           gray: 0x808080,
14895           green: 0x008000,
14896           greenyellow: 0xadff2f,
14897           grey: 0x808080,
14898           honeydew: 0xf0fff0,
14899           hotpink: 0xff69b4,
14900           indianred: 0xcd5c5c,
14901           indigo: 0x4b0082,
14902           ivory: 0xfffff0,
14903           khaki: 0xf0e68c,
14904           lavender: 0xe6e6fa,
14905           lavenderblush: 0xfff0f5,
14906           lawngreen: 0x7cfc00,
14907           lemonchiffon: 0xfffacd,
14908           lightblue: 0xadd8e6,
14909           lightcoral: 0xf08080,
14910           lightcyan: 0xe0ffff,
14911           lightgoldenrodyellow: 0xfafad2,
14912           lightgray: 0xd3d3d3,
14913           lightgreen: 0x90ee90,
14914           lightgrey: 0xd3d3d3,
14915           lightpink: 0xffb6c1,
14916           lightsalmon: 0xffa07a,
14917           lightseagreen: 0x20b2aa,
14918           lightskyblue: 0x87cefa,
14919           lightslategray: 0x778899,
14920           lightslategrey: 0x778899,
14921           lightsteelblue: 0xb0c4de,
14922           lightyellow: 0xffffe0,
14923           lime: 0x00ff00,
14924           limegreen: 0x32cd32,
14925           linen: 0xfaf0e6,
14926           magenta: 0xff00ff,
14927           maroon: 0x800000,
14928           mediumaquamarine: 0x66cdaa,
14929           mediumblue: 0x0000cd,
14930           mediumorchid: 0xba55d3,
14931           mediumpurple: 0x9370db,
14932           mediumseagreen: 0x3cb371,
14933           mediumslateblue: 0x7b68ee,
14934           mediumspringgreen: 0x00fa9a,
14935           mediumturquoise: 0x48d1cc,
14936           mediumvioletred: 0xc71585,
14937           midnightblue: 0x191970,
14938           mintcream: 0xf5fffa,
14939           mistyrose: 0xffe4e1,
14940           moccasin: 0xffe4b5,
14941           navajowhite: 0xffdead,
14942           navy: 0x000080,
14943           oldlace: 0xfdf5e6,
14944           olive: 0x808000,
14945           olivedrab: 0x6b8e23,
14946           orange: 0xffa500,
14947           orangered: 0xff4500,
14948           orchid: 0xda70d6,
14949           palegoldenrod: 0xeee8aa,
14950           palegreen: 0x98fb98,
14951           paleturquoise: 0xafeeee,
14952           palevioletred: 0xdb7093,
14953           papayawhip: 0xffefd5,
14954           peachpuff: 0xffdab9,
14955           peru: 0xcd853f,
14956           pink: 0xffc0cb,
14957           plum: 0xdda0dd,
14958           powderblue: 0xb0e0e6,
14959           purple: 0x800080,
14960           rebeccapurple: 0x663399,
14961           red: 0xff0000,
14962           rosybrown: 0xbc8f8f,
14963           royalblue: 0x4169e1,
14964           saddlebrown: 0x8b4513,
14965           salmon: 0xfa8072,
14966           sandybrown: 0xf4a460,
14967           seagreen: 0x2e8b57,
14968           seashell: 0xfff5ee,
14969           sienna: 0xa0522d,
14970           silver: 0xc0c0c0,
14971           skyblue: 0x87ceeb,
14972           slateblue: 0x6a5acd,
14973           slategray: 0x708090,
14974           slategrey: 0x708090,
14975           snow: 0xfffafa,
14976           springgreen: 0x00ff7f,
14977           steelblue: 0x4682b4,
14978           tan: 0xd2b48c,
14979           teal: 0x008080,
14980           thistle: 0xd8bfd8,
14981           tomato: 0xff6347,
14982           turquoise: 0x40e0d0,
14983           violet: 0xee82ee,
14984           wheat: 0xf5deb3,
14985           white: 0xffffff,
14986           whitesmoke: 0xf5f5f5,
14987           yellow: 0xffff00,
14988           yellowgreen: 0x9acd32
14989         };
14990         define(Color, color, {
14991           copy: function copy(channels) {
14992             return Object.assign(new this.constructor(), this, channels);
14993           },
14994           displayable: function displayable() {
14995             return this.rgb().displayable();
14996           },
14997           hex: color_formatHex,
14998           // Deprecated! Use color.formatHex.
14999           formatHex: color_formatHex,
15000           formatHsl: color_formatHsl,
15001           formatRgb: color_formatRgb,
15002           toString: color_formatRgb
15003         });
15004
15005         function color_formatHex() {
15006           return this.rgb().formatHex();
15007         }
15008
15009         function color_formatHsl() {
15010           return hslConvert(this).formatHsl();
15011         }
15012
15013         function color_formatRgb() {
15014           return this.rgb().formatRgb();
15015         }
15016
15017         function color(format) {
15018           var m, l;
15019           format = (format + "").trim().toLowerCase();
15020           return (m = reHex.exec(format)) ? (l = m[1].length, m = parseInt(m[1], 16), l === 6 ? rgbn(m) // #ff0000
15021           : l === 3 ? new Rgb(m >> 8 & 0xf | m >> 4 & 0xf0, m >> 4 & 0xf | m & 0xf0, (m & 0xf) << 4 | m & 0xf, 1) // #f00
15022           : l === 8 ? rgba(m >> 24 & 0xff, m >> 16 & 0xff, m >> 8 & 0xff, (m & 0xff) / 0xff) // #ff000000
15023           : l === 4 ? rgba(m >> 12 & 0xf | m >> 8 & 0xf0, m >> 8 & 0xf | m >> 4 & 0xf0, m >> 4 & 0xf | m & 0xf0, ((m & 0xf) << 4 | m & 0xf) / 0xff) // #f000
15024           : null // invalid hex
15025           ) : (m = reRgbInteger.exec(format)) ? new Rgb(m[1], m[2], m[3], 1) // rgb(255, 0, 0)
15026           : (m = reRgbPercent.exec(format)) ? new Rgb(m[1] * 255 / 100, m[2] * 255 / 100, m[3] * 255 / 100, 1) // rgb(100%, 0%, 0%)
15027           : (m = reRgbaInteger.exec(format)) ? rgba(m[1], m[2], m[3], m[4]) // rgba(255, 0, 0, 1)
15028           : (m = reRgbaPercent.exec(format)) ? rgba(m[1] * 255 / 100, m[2] * 255 / 100, m[3] * 255 / 100, m[4]) // rgb(100%, 0%, 0%, 1)
15029           : (m = reHslPercent.exec(format)) ? hsla(m[1], m[2] / 100, m[3] / 100, 1) // hsl(120, 50%, 50%)
15030           : (m = reHslaPercent.exec(format)) ? hsla(m[1], m[2] / 100, m[3] / 100, m[4]) // hsla(120, 50%, 50%, 1)
15031           : named.hasOwnProperty(format) ? rgbn(named[format]) // eslint-disable-line no-prototype-builtins
15032           : format === "transparent" ? new Rgb(NaN, NaN, NaN, 0) : null;
15033         }
15034
15035         function rgbn(n) {
15036           return new Rgb(n >> 16 & 0xff, n >> 8 & 0xff, n & 0xff, 1);
15037         }
15038
15039         function rgba(r, g, b, a) {
15040           if (a <= 0) r = g = b = NaN;
15041           return new Rgb(r, g, b, a);
15042         }
15043
15044         function rgbConvert(o) {
15045           if (!(o instanceof Color)) o = color(o);
15046           if (!o) return new Rgb();
15047           o = o.rgb();
15048           return new Rgb(o.r, o.g, o.b, o.opacity);
15049         }
15050         function rgb(r, g, b, opacity) {
15051           return arguments.length === 1 ? rgbConvert(r) : new Rgb(r, g, b, opacity == null ? 1 : opacity);
15052         }
15053         function Rgb(r, g, b, opacity) {
15054           this.r = +r;
15055           this.g = +g;
15056           this.b = +b;
15057           this.opacity = +opacity;
15058         }
15059         define(Rgb, rgb, extend$3(Color, {
15060           brighter: function brighter(k) {
15061             k = k == null ? _brighter : Math.pow(_brighter, k);
15062             return new Rgb(this.r * k, this.g * k, this.b * k, this.opacity);
15063           },
15064           darker: function darker(k) {
15065             k = k == null ? _darker : Math.pow(_darker, k);
15066             return new Rgb(this.r * k, this.g * k, this.b * k, this.opacity);
15067           },
15068           rgb: function rgb() {
15069             return this;
15070           },
15071           displayable: function displayable() {
15072             return -0.5 <= this.r && this.r < 255.5 && -0.5 <= this.g && this.g < 255.5 && -0.5 <= this.b && this.b < 255.5 && 0 <= this.opacity && this.opacity <= 1;
15073           },
15074           hex: rgb_formatHex,
15075           // Deprecated! Use color.formatHex.
15076           formatHex: rgb_formatHex,
15077           formatRgb: rgb_formatRgb,
15078           toString: rgb_formatRgb
15079         }));
15080
15081         function rgb_formatHex() {
15082           return "#" + hex$1(this.r) + hex$1(this.g) + hex$1(this.b);
15083         }
15084
15085         function rgb_formatRgb() {
15086           var a = this.opacity;
15087           a = isNaN(a) ? 1 : Math.max(0, Math.min(1, a));
15088           return (a === 1 ? "rgb(" : "rgba(") + Math.max(0, Math.min(255, Math.round(this.r) || 0)) + ", " + Math.max(0, Math.min(255, Math.round(this.g) || 0)) + ", " + Math.max(0, Math.min(255, Math.round(this.b) || 0)) + (a === 1 ? ")" : ", " + a + ")");
15089         }
15090
15091         function hex$1(value) {
15092           value = Math.max(0, Math.min(255, Math.round(value) || 0));
15093           return (value < 16 ? "0" : "") + value.toString(16);
15094         }
15095
15096         function hsla(h, s, l, a) {
15097           if (a <= 0) h = s = l = NaN;else if (l <= 0 || l >= 1) h = s = NaN;else if (s <= 0) h = NaN;
15098           return new Hsl(h, s, l, a);
15099         }
15100
15101         function hslConvert(o) {
15102           if (o instanceof Hsl) return new Hsl(o.h, o.s, o.l, o.opacity);
15103           if (!(o instanceof Color)) o = color(o);
15104           if (!o) return new Hsl();
15105           if (o instanceof Hsl) return o;
15106           o = o.rgb();
15107           var r = o.r / 255,
15108               g = o.g / 255,
15109               b = o.b / 255,
15110               min = Math.min(r, g, b),
15111               max = Math.max(r, g, b),
15112               h = NaN,
15113               s = max - min,
15114               l = (max + min) / 2;
15115
15116           if (s) {
15117             if (r === max) h = (g - b) / s + (g < b) * 6;else if (g === max) h = (b - r) / s + 2;else h = (r - g) / s + 4;
15118             s /= l < 0.5 ? max + min : 2 - max - min;
15119             h *= 60;
15120           } else {
15121             s = l > 0 && l < 1 ? 0 : h;
15122           }
15123
15124           return new Hsl(h, s, l, o.opacity);
15125         }
15126         function hsl(h, s, l, opacity) {
15127           return arguments.length === 1 ? hslConvert(h) : new Hsl(h, s, l, opacity == null ? 1 : opacity);
15128         }
15129
15130         function Hsl(h, s, l, opacity) {
15131           this.h = +h;
15132           this.s = +s;
15133           this.l = +l;
15134           this.opacity = +opacity;
15135         }
15136
15137         define(Hsl, hsl, extend$3(Color, {
15138           brighter: function brighter(k) {
15139             k = k == null ? _brighter : Math.pow(_brighter, k);
15140             return new Hsl(this.h, this.s, this.l * k, this.opacity);
15141           },
15142           darker: function darker(k) {
15143             k = k == null ? _darker : Math.pow(_darker, k);
15144             return new Hsl(this.h, this.s, this.l * k, this.opacity);
15145           },
15146           rgb: function rgb() {
15147             var h = this.h % 360 + (this.h < 0) * 360,
15148                 s = isNaN(h) || isNaN(this.s) ? 0 : this.s,
15149                 l = this.l,
15150                 m2 = l + (l < 0.5 ? l : 1 - l) * s,
15151                 m1 = 2 * l - m2;
15152             return new Rgb(hsl2rgb(h >= 240 ? h - 240 : h + 120, m1, m2), hsl2rgb(h, m1, m2), hsl2rgb(h < 120 ? h + 240 : h - 120, m1, m2), this.opacity);
15153           },
15154           displayable: function displayable() {
15155             return (0 <= this.s && this.s <= 1 || isNaN(this.s)) && 0 <= this.l && this.l <= 1 && 0 <= this.opacity && this.opacity <= 1;
15156           },
15157           formatHsl: function formatHsl() {
15158             var a = this.opacity;
15159             a = isNaN(a) ? 1 : Math.max(0, Math.min(1, a));
15160             return (a === 1 ? "hsl(" : "hsla(") + (this.h || 0) + ", " + (this.s || 0) * 100 + "%, " + (this.l || 0) * 100 + "%" + (a === 1 ? ")" : ", " + a + ")");
15161           }
15162         }));
15163         /* From FvD 13.37, CSS Color Module Level 3 */
15164
15165         function hsl2rgb(h, m1, m2) {
15166           return (h < 60 ? m1 + (m2 - m1) * h / 60 : h < 180 ? m2 : h < 240 ? m1 + (m2 - m1) * (240 - h) / 60 : m1) * 255;
15167         }
15168
15169         var constant$1 = (function (x) {
15170           return function () {
15171             return x;
15172           };
15173         });
15174
15175         function linear$2(a, d) {
15176           return function (t) {
15177             return a + t * d;
15178           };
15179         }
15180
15181         function exponential(a, b, y) {
15182           return a = Math.pow(a, y), b = Math.pow(b, y) - a, y = 1 / y, function (t) {
15183             return Math.pow(a + t * b, y);
15184           };
15185         }
15186         function gamma(y) {
15187           return (y = +y) === 1 ? nogamma : function (a, b) {
15188             return b - a ? exponential(a, b, y) : constant$1(isNaN(a) ? b : a);
15189           };
15190         }
15191         function nogamma(a, b) {
15192           var d = b - a;
15193           return d ? linear$2(a, d) : constant$1(isNaN(a) ? b : a);
15194         }
15195
15196         var d3_interpolateRgb = (function rgbGamma(y) {
15197           var color = gamma(y);
15198
15199           function rgb$1(start, end) {
15200             var r = color((start = rgb(start)).r, (end = rgb(end)).r),
15201                 g = color(start.g, end.g),
15202                 b = color(start.b, end.b),
15203                 opacity = nogamma(start.opacity, end.opacity);
15204             return function (t) {
15205               start.r = r(t);
15206               start.g = g(t);
15207               start.b = b(t);
15208               start.opacity = opacity(t);
15209               return start + "";
15210             };
15211           }
15212
15213           rgb$1.gamma = rgbGamma;
15214           return rgb$1;
15215         })(1);
15216
15217         function numberArray (a, b) {
15218           if (!b) b = [];
15219           var n = a ? Math.min(b.length, a.length) : 0,
15220               c = b.slice(),
15221               i;
15222           return function (t) {
15223             for (i = 0; i < n; ++i) {
15224               c[i] = a[i] * (1 - t) + b[i] * t;
15225             }
15226
15227             return c;
15228           };
15229         }
15230         function isNumberArray(x) {
15231           return ArrayBuffer.isView(x) && !(x instanceof DataView);
15232         }
15233
15234         function genericArray(a, b) {
15235           var nb = b ? b.length : 0,
15236               na = a ? Math.min(nb, a.length) : 0,
15237               x = new Array(na),
15238               c = new Array(nb),
15239               i;
15240
15241           for (i = 0; i < na; ++i) {
15242             x[i] = interpolate$1(a[i], b[i]);
15243           }
15244
15245           for (; i < nb; ++i) {
15246             c[i] = b[i];
15247           }
15248
15249           return function (t) {
15250             for (i = 0; i < na; ++i) {
15251               c[i] = x[i](t);
15252             }
15253
15254             return c;
15255           };
15256         }
15257
15258         function date (a, b) {
15259           var d = new Date();
15260           return a = +a, b = +b, function (t) {
15261             return d.setTime(a * (1 - t) + b * t), d;
15262           };
15263         }
15264
15265         function d3_interpolateNumber (a, b) {
15266           return a = +a, b = +b, function (t) {
15267             return a * (1 - t) + b * t;
15268           };
15269         }
15270
15271         function object (a, b) {
15272           var i = {},
15273               c = {},
15274               k;
15275           if (a === null || _typeof(a) !== "object") a = {};
15276           if (b === null || _typeof(b) !== "object") b = {};
15277
15278           for (k in b) {
15279             if (k in a) {
15280               i[k] = interpolate$1(a[k], b[k]);
15281             } else {
15282               c[k] = b[k];
15283             }
15284           }
15285
15286           return function (t) {
15287             for (k in i) {
15288               c[k] = i[k](t);
15289             }
15290
15291             return c;
15292           };
15293         }
15294
15295         var reA = /[-+]?(?:\d+\.?\d*|\.?\d+)(?:[eE][-+]?\d+)?/g,
15296             reB = new RegExp(reA.source, "g");
15297
15298         function zero(b) {
15299           return function () {
15300             return b;
15301           };
15302         }
15303
15304         function one(b) {
15305           return function (t) {
15306             return b(t) + "";
15307           };
15308         }
15309
15310         function interpolateString (a, b) {
15311           var bi = reA.lastIndex = reB.lastIndex = 0,
15312               // scan index for next number in b
15313           am,
15314               // current match in a
15315           bm,
15316               // current match in b
15317           bs,
15318               // string preceding current number in b, if any
15319           i = -1,
15320               // index in s
15321           s = [],
15322               // string constants and placeholders
15323           q = []; // number interpolators
15324           // Coerce inputs to strings.
15325
15326           a = a + "", b = b + ""; // Interpolate pairs of numbers in a & b.
15327
15328           while ((am = reA.exec(a)) && (bm = reB.exec(b))) {
15329             if ((bs = bm.index) > bi) {
15330               // a string precedes the next number in b
15331               bs = b.slice(bi, bs);
15332               if (s[i]) s[i] += bs; // coalesce with previous string
15333               else s[++i] = bs;
15334             }
15335
15336             if ((am = am[0]) === (bm = bm[0])) {
15337               // numbers in a & b match
15338               if (s[i]) s[i] += bm; // coalesce with previous string
15339               else s[++i] = bm;
15340             } else {
15341               // interpolate non-matching numbers
15342               s[++i] = null;
15343               q.push({
15344                 i: i,
15345                 x: d3_interpolateNumber(am, bm)
15346               });
15347             }
15348
15349             bi = reB.lastIndex;
15350           } // Add remains of b.
15351
15352
15353           if (bi < b.length) {
15354             bs = b.slice(bi);
15355             if (s[i]) s[i] += bs; // coalesce with previous string
15356             else s[++i] = bs;
15357           } // Special optimization for only a single match.
15358           // Otherwise, interpolate each of the numbers and rejoin the string.
15359
15360
15361           return s.length < 2 ? q[0] ? one(q[0].x) : zero(b) : (b = q.length, function (t) {
15362             for (var i = 0, o; i < b; ++i) {
15363               s[(o = q[i]).i] = o.x(t);
15364             }
15365
15366             return s.join("");
15367           });
15368         }
15369
15370         function interpolate$1 (a, b) {
15371           var t = _typeof(b),
15372               c;
15373
15374           return b == null || t === "boolean" ? constant$1(b) : (t === "number" ? d3_interpolateNumber : t === "string" ? (c = color(b)) ? (b = c, d3_interpolateRgb) : interpolateString : b instanceof color ? d3_interpolateRgb : b instanceof Date ? date : isNumberArray(b) ? numberArray : Array.isArray(b) ? genericArray : typeof b.valueOf !== "function" && typeof b.toString !== "function" || isNaN(b) ? object : d3_interpolateNumber)(a, b);
15375         }
15376
15377         function interpolateRound (a, b) {
15378           return a = +a, b = +b, function (t) {
15379             return Math.round(a * (1 - t) + b * t);
15380           };
15381         }
15382
15383         var degrees = 180 / Math.PI;
15384         var identity$3 = {
15385           translateX: 0,
15386           translateY: 0,
15387           rotate: 0,
15388           skewX: 0,
15389           scaleX: 1,
15390           scaleY: 1
15391         };
15392         function decompose (a, b, c, d, e, f) {
15393           var scaleX, scaleY, skewX;
15394           if (scaleX = Math.sqrt(a * a + b * b)) a /= scaleX, b /= scaleX;
15395           if (skewX = a * c + b * d) c -= a * skewX, d -= b * skewX;
15396           if (scaleY = Math.sqrt(c * c + d * d)) c /= scaleY, d /= scaleY, skewX /= scaleY;
15397           if (a * d < b * c) a = -a, b = -b, skewX = -skewX, scaleX = -scaleX;
15398           return {
15399             translateX: e,
15400             translateY: f,
15401             rotate: Math.atan2(b, a) * degrees,
15402             skewX: Math.atan(skewX) * degrees,
15403             scaleX: scaleX,
15404             scaleY: scaleY
15405           };
15406         }
15407
15408         var svgNode;
15409         /* eslint-disable no-undef */
15410
15411         function parseCss(value) {
15412           var m = new (typeof DOMMatrix === "function" ? DOMMatrix : WebKitCSSMatrix)(value + "");
15413           return m.isIdentity ? identity$3 : decompose(m.a, m.b, m.c, m.d, m.e, m.f);
15414         }
15415         function parseSvg(value) {
15416           if (value == null) return identity$3;
15417           if (!svgNode) svgNode = document.createElementNS("http://www.w3.org/2000/svg", "g");
15418           svgNode.setAttribute("transform", value);
15419           if (!(value = svgNode.transform.baseVal.consolidate())) return identity$3;
15420           value = value.matrix;
15421           return decompose(value.a, value.b, value.c, value.d, value.e, value.f);
15422         }
15423
15424         function interpolateTransform(parse, pxComma, pxParen, degParen) {
15425           function pop(s) {
15426             return s.length ? s.pop() + " " : "";
15427           }
15428
15429           function translate(xa, ya, xb, yb, s, q) {
15430             if (xa !== xb || ya !== yb) {
15431               var i = s.push("translate(", null, pxComma, null, pxParen);
15432               q.push({
15433                 i: i - 4,
15434                 x: d3_interpolateNumber(xa, xb)
15435               }, {
15436                 i: i - 2,
15437                 x: d3_interpolateNumber(ya, yb)
15438               });
15439             } else if (xb || yb) {
15440               s.push("translate(" + xb + pxComma + yb + pxParen);
15441             }
15442           }
15443
15444           function rotate(a, b, s, q) {
15445             if (a !== b) {
15446               if (a - b > 180) b += 360;else if (b - a > 180) a += 360; // shortest path
15447
15448               q.push({
15449                 i: s.push(pop(s) + "rotate(", null, degParen) - 2,
15450                 x: d3_interpolateNumber(a, b)
15451               });
15452             } else if (b) {
15453               s.push(pop(s) + "rotate(" + b + degParen);
15454             }
15455           }
15456
15457           function skewX(a, b, s, q) {
15458             if (a !== b) {
15459               q.push({
15460                 i: s.push(pop(s) + "skewX(", null, degParen) - 2,
15461                 x: d3_interpolateNumber(a, b)
15462               });
15463             } else if (b) {
15464               s.push(pop(s) + "skewX(" + b + degParen);
15465             }
15466           }
15467
15468           function scale(xa, ya, xb, yb, s, q) {
15469             if (xa !== xb || ya !== yb) {
15470               var i = s.push(pop(s) + "scale(", null, ",", null, ")");
15471               q.push({
15472                 i: i - 4,
15473                 x: d3_interpolateNumber(xa, xb)
15474               }, {
15475                 i: i - 2,
15476                 x: d3_interpolateNumber(ya, yb)
15477               });
15478             } else if (xb !== 1 || yb !== 1) {
15479               s.push(pop(s) + "scale(" + xb + "," + yb + ")");
15480             }
15481           }
15482
15483           return function (a, b) {
15484             var s = [],
15485                 // string constants and placeholders
15486             q = []; // number interpolators
15487
15488             a = parse(a), b = parse(b);
15489             translate(a.translateX, a.translateY, b.translateX, b.translateY, s, q);
15490             rotate(a.rotate, b.rotate, s, q);
15491             skewX(a.skewX, b.skewX, s, q);
15492             scale(a.scaleX, a.scaleY, b.scaleX, b.scaleY, s, q);
15493             a = b = null; // gc
15494
15495             return function (t) {
15496               var i = -1,
15497                   n = q.length,
15498                   o;
15499
15500               while (++i < n) {
15501                 s[(o = q[i]).i] = o.x(t);
15502               }
15503
15504               return s.join("");
15505             };
15506           };
15507         }
15508
15509         var interpolateTransformCss = interpolateTransform(parseCss, "px, ", "px)", "deg)");
15510         var interpolateTransformSvg = interpolateTransform(parseSvg, ", ", ")", ")");
15511
15512         var epsilon2 = 1e-12;
15513
15514         function cosh(x) {
15515           return ((x = Math.exp(x)) + 1 / x) / 2;
15516         }
15517
15518         function sinh(x) {
15519           return ((x = Math.exp(x)) - 1 / x) / 2;
15520         }
15521
15522         function tanh(x) {
15523           return ((x = Math.exp(2 * x)) - 1) / (x + 1);
15524         }
15525
15526         var interpolateZoom = (function zoomRho(rho, rho2, rho4) {
15527           // p0 = [ux0, uy0, w0]
15528           // p1 = [ux1, uy1, w1]
15529           function zoom(p0, p1) {
15530             var ux0 = p0[0],
15531                 uy0 = p0[1],
15532                 w0 = p0[2],
15533                 ux1 = p1[0],
15534                 uy1 = p1[1],
15535                 w1 = p1[2],
15536                 dx = ux1 - ux0,
15537                 dy = uy1 - uy0,
15538                 d2 = dx * dx + dy * dy,
15539                 i,
15540                 S; // Special case for u0 ≅ u1.
15541
15542             if (d2 < epsilon2) {
15543               S = Math.log(w1 / w0) / rho;
15544
15545               i = function i(t) {
15546                 return [ux0 + t * dx, uy0 + t * dy, w0 * Math.exp(rho * t * S)];
15547               };
15548             } // General case.
15549             else {
15550                 var d1 = Math.sqrt(d2),
15551                     b0 = (w1 * w1 - w0 * w0 + rho4 * d2) / (2 * w0 * rho2 * d1),
15552                     b1 = (w1 * w1 - w0 * w0 - rho4 * d2) / (2 * w1 * rho2 * d1),
15553                     r0 = Math.log(Math.sqrt(b0 * b0 + 1) - b0),
15554                     r1 = Math.log(Math.sqrt(b1 * b1 + 1) - b1);
15555                 S = (r1 - r0) / rho;
15556
15557                 i = function i(t) {
15558                   var s = t * S,
15559                       coshr0 = cosh(r0),
15560                       u = w0 / (rho2 * d1) * (coshr0 * tanh(rho * s + r0) - sinh(r0));
15561                   return [ux0 + u * dx, uy0 + u * dy, w0 * coshr0 / cosh(rho * s + r0)];
15562                 };
15563               }
15564
15565             i.duration = S * 1000 * rho / Math.SQRT2;
15566             return i;
15567           }
15568
15569           zoom.rho = function (_) {
15570             var _1 = Math.max(1e-3, +_),
15571                 _2 = _1 * _1,
15572                 _4 = _2 * _2;
15573
15574             return zoomRho(_1, _2, _4);
15575           };
15576
15577           return zoom;
15578         })(Math.SQRT2, 2, 4);
15579
15580         function d3_quantize (interpolator, n) {
15581           var samples = new Array(n);
15582
15583           for (var i = 0; i < n; ++i) {
15584             samples[i] = interpolator(i / (n - 1));
15585           }
15586
15587           return samples;
15588         }
15589
15590         // `Function.prototype.bind` method
15591         // https://tc39.es/ecma262/#sec-function.prototype.bind
15592         _export({ target: 'Function', proto: true }, {
15593           bind: functionBind
15594         });
15595
15596         var frame = 0,
15597             // is an animation frame pending?
15598         timeout = 0,
15599             // is a timeout pending?
15600         interval = 0,
15601             // are any timers active?
15602         pokeDelay = 1000,
15603             // how frequently we check for clock skew
15604         taskHead,
15605             taskTail,
15606             clockLast = 0,
15607             clockNow = 0,
15608             clockSkew = 0,
15609             clock = (typeof performance === "undefined" ? "undefined" : _typeof(performance)) === "object" && performance.now ? performance : Date,
15610             setFrame = (typeof window === "undefined" ? "undefined" : _typeof(window)) === "object" && window.requestAnimationFrame ? window.requestAnimationFrame.bind(window) : function (f) {
15611           setTimeout(f, 17);
15612         };
15613         function now$1() {
15614           return clockNow || (setFrame(clearNow), clockNow = clock.now() + clockSkew);
15615         }
15616
15617         function clearNow() {
15618           clockNow = 0;
15619         }
15620
15621         function Timer() {
15622           this._call = this._time = this._next = null;
15623         }
15624         Timer.prototype = timer.prototype = {
15625           constructor: Timer,
15626           restart: function restart(callback, delay, time) {
15627             if (typeof callback !== "function") throw new TypeError("callback is not a function");
15628             time = (time == null ? now$1() : +time) + (delay == null ? 0 : +delay);
15629
15630             if (!this._next && taskTail !== this) {
15631               if (taskTail) taskTail._next = this;else taskHead = this;
15632               taskTail = this;
15633             }
15634
15635             this._call = callback;
15636             this._time = time;
15637             sleep();
15638           },
15639           stop: function stop() {
15640             if (this._call) {
15641               this._call = null;
15642               this._time = Infinity;
15643               sleep();
15644             }
15645           }
15646         };
15647         function timer(callback, delay, time) {
15648           var t = new Timer();
15649           t.restart(callback, delay, time);
15650           return t;
15651         }
15652         function timerFlush() {
15653           now$1(); // Get the current time, if not already set.
15654
15655           ++frame; // Pretend we’ve set an alarm, if we haven’t already.
15656
15657           var t = taskHead,
15658               e;
15659
15660           while (t) {
15661             if ((e = clockNow - t._time) >= 0) t._call.call(null, e);
15662             t = t._next;
15663           }
15664
15665           --frame;
15666         }
15667
15668         function wake() {
15669           clockNow = (clockLast = clock.now()) + clockSkew;
15670           frame = timeout = 0;
15671
15672           try {
15673             timerFlush();
15674           } finally {
15675             frame = 0;
15676             nap();
15677             clockNow = 0;
15678           }
15679         }
15680
15681         function poke() {
15682           var now = clock.now(),
15683               delay = now - clockLast;
15684           if (delay > pokeDelay) clockSkew -= delay, clockLast = now;
15685         }
15686
15687         function nap() {
15688           var t0,
15689               t1 = taskHead,
15690               t2,
15691               time = Infinity;
15692
15693           while (t1) {
15694             if (t1._call) {
15695               if (time > t1._time) time = t1._time;
15696               t0 = t1, t1 = t1._next;
15697             } else {
15698               t2 = t1._next, t1._next = null;
15699               t1 = t0 ? t0._next = t2 : taskHead = t2;
15700             }
15701           }
15702
15703           taskTail = t0;
15704           sleep(time);
15705         }
15706
15707         function sleep(time) {
15708           if (frame) return; // Soonest alarm already set, or will be.
15709
15710           if (timeout) timeout = clearTimeout(timeout);
15711           var delay = time - clockNow; // Strictly less than if we recomputed clockNow.
15712
15713           if (delay > 24) {
15714             if (time < Infinity) timeout = setTimeout(wake, time - clock.now() - clockSkew);
15715             if (interval) interval = clearInterval(interval);
15716           } else {
15717             if (!interval) clockLast = clock.now(), interval = setInterval(poke, pokeDelay);
15718             frame = 1, setFrame(wake);
15719           }
15720         }
15721
15722         function d3_timeout (callback, delay, time) {
15723           var t = new Timer();
15724           delay = delay == null ? 0 : +delay;
15725           t.restart(function (elapsed) {
15726             t.stop();
15727             callback(elapsed + delay);
15728           }, delay, time);
15729           return t;
15730         }
15731
15732         var emptyOn = dispatch$8("start", "end", "cancel", "interrupt");
15733         var emptyTween = [];
15734         var CREATED = 0;
15735         var SCHEDULED = 1;
15736         var STARTING = 2;
15737         var STARTED = 3;
15738         var RUNNING = 4;
15739         var ENDING = 5;
15740         var ENDED = 6;
15741         function schedule (node, name, id, index, group, timing) {
15742           var schedules = node.__transition;
15743           if (!schedules) node.__transition = {};else if (id in schedules) return;
15744           create$2(node, id, {
15745             name: name,
15746             index: index,
15747             // For context during callback.
15748             group: group,
15749             // For context during callback.
15750             on: emptyOn,
15751             tween: emptyTween,
15752             time: timing.time,
15753             delay: timing.delay,
15754             duration: timing.duration,
15755             ease: timing.ease,
15756             timer: null,
15757             state: CREATED
15758           });
15759         }
15760         function init(node, id) {
15761           var schedule = get$1(node, id);
15762           if (schedule.state > CREATED) throw new Error("too late; already scheduled");
15763           return schedule;
15764         }
15765         function set(node, id) {
15766           var schedule = get$1(node, id);
15767           if (schedule.state > STARTED) throw new Error("too late; already running");
15768           return schedule;
15769         }
15770         function get$1(node, id) {
15771           var schedule = node.__transition;
15772           if (!schedule || !(schedule = schedule[id])) throw new Error("transition not found");
15773           return schedule;
15774         }
15775
15776         function create$2(node, id, self) {
15777           var schedules = node.__transition,
15778               tween; // Initialize the self timer when the transition is created.
15779           // Note the actual delay is not known until the first callback!
15780
15781           schedules[id] = self;
15782           self.timer = timer(schedule, 0, self.time);
15783
15784           function schedule(elapsed) {
15785             self.state = SCHEDULED;
15786             self.timer.restart(start, self.delay, self.time); // If the elapsed delay is less than our first sleep, start immediately.
15787
15788             if (self.delay <= elapsed) start(elapsed - self.delay);
15789           }
15790
15791           function start(elapsed) {
15792             var i, j, n, o; // If the state is not SCHEDULED, then we previously errored on start.
15793
15794             if (self.state !== SCHEDULED) return stop();
15795
15796             for (i in schedules) {
15797               o = schedules[i];
15798               if (o.name !== self.name) continue; // While this element already has a starting transition during this frame,
15799               // defer starting an interrupting transition until that transition has a
15800               // chance to tick (and possibly end); see d3/d3-transition#54!
15801
15802               if (o.state === STARTED) return d3_timeout(start); // Interrupt the active transition, if any.
15803
15804               if (o.state === RUNNING) {
15805                 o.state = ENDED;
15806                 o.timer.stop();
15807                 o.on.call("interrupt", node, node.__data__, o.index, o.group);
15808                 delete schedules[i];
15809               } // Cancel any pre-empted transitions.
15810               else if (+i < id) {
15811                   o.state = ENDED;
15812                   o.timer.stop();
15813                   o.on.call("cancel", node, node.__data__, o.index, o.group);
15814                   delete schedules[i];
15815                 }
15816             } // Defer the first tick to end of the current frame; see d3/d3#1576.
15817             // Note the transition may be canceled after start and before the first tick!
15818             // Note this must be scheduled before the start event; see d3/d3-transition#16!
15819             // Assuming this is successful, subsequent callbacks go straight to tick.
15820
15821
15822             d3_timeout(function () {
15823               if (self.state === STARTED) {
15824                 self.state = RUNNING;
15825                 self.timer.restart(tick, self.delay, self.time);
15826                 tick(elapsed);
15827               }
15828             }); // Dispatch the start event.
15829             // Note this must be done before the tween are initialized.
15830
15831             self.state = STARTING;
15832             self.on.call("start", node, node.__data__, self.index, self.group);
15833             if (self.state !== STARTING) return; // interrupted
15834
15835             self.state = STARTED; // Initialize the tween, deleting null tween.
15836
15837             tween = new Array(n = self.tween.length);
15838
15839             for (i = 0, j = -1; i < n; ++i) {
15840               if (o = self.tween[i].value.call(node, node.__data__, self.index, self.group)) {
15841                 tween[++j] = o;
15842               }
15843             }
15844
15845             tween.length = j + 1;
15846           }
15847
15848           function tick(elapsed) {
15849             var t = elapsed < self.duration ? self.ease.call(null, elapsed / self.duration) : (self.timer.restart(stop), self.state = ENDING, 1),
15850                 i = -1,
15851                 n = tween.length;
15852
15853             while (++i < n) {
15854               tween[i].call(node, t);
15855             } // Dispatch the end event.
15856
15857
15858             if (self.state === ENDING) {
15859               self.on.call("end", node, node.__data__, self.index, self.group);
15860               stop();
15861             }
15862           }
15863
15864           function stop() {
15865             self.state = ENDED;
15866             self.timer.stop();
15867             delete schedules[id];
15868
15869             for (var i in schedules) {
15870               return;
15871             } // eslint-disable-line no-unused-vars
15872
15873
15874             delete node.__transition;
15875           }
15876         }
15877
15878         function interrupt (node, name) {
15879           var schedules = node.__transition,
15880               schedule,
15881               active,
15882               empty = true,
15883               i;
15884           if (!schedules) return;
15885           name = name == null ? null : name + "";
15886
15887           for (i in schedules) {
15888             if ((schedule = schedules[i]).name !== name) {
15889               empty = false;
15890               continue;
15891             }
15892
15893             active = schedule.state > STARTING && schedule.state < ENDING;
15894             schedule.state = ENDED;
15895             schedule.timer.stop();
15896             schedule.on.call(active ? "interrupt" : "cancel", node, node.__data__, schedule.index, schedule.group);
15897             delete schedules[i];
15898           }
15899
15900           if (empty) delete node.__transition;
15901         }
15902
15903         function selection_interrupt (name) {
15904           return this.each(function () {
15905             interrupt(this, name);
15906           });
15907         }
15908
15909         function tweenRemove(id, name) {
15910           var tween0, tween1;
15911           return function () {
15912             var schedule = set(this, id),
15913                 tween = schedule.tween; // If this node shared tween with the previous node,
15914             // just assign the updated shared tween and we’re done!
15915             // Otherwise, copy-on-write.
15916
15917             if (tween !== tween0) {
15918               tween1 = tween0 = tween;
15919
15920               for (var i = 0, n = tween1.length; i < n; ++i) {
15921                 if (tween1[i].name === name) {
15922                   tween1 = tween1.slice();
15923                   tween1.splice(i, 1);
15924                   break;
15925                 }
15926               }
15927             }
15928
15929             schedule.tween = tween1;
15930           };
15931         }
15932
15933         function tweenFunction(id, name, value) {
15934           var tween0, tween1;
15935           if (typeof value !== "function") throw new Error();
15936           return function () {
15937             var schedule = set(this, id),
15938                 tween = schedule.tween; // If this node shared tween with the previous node,
15939             // just assign the updated shared tween and we’re done!
15940             // Otherwise, copy-on-write.
15941
15942             if (tween !== tween0) {
15943               tween1 = (tween0 = tween).slice();
15944
15945               for (var t = {
15946                 name: name,
15947                 value: value
15948               }, i = 0, n = tween1.length; i < n; ++i) {
15949                 if (tween1[i].name === name) {
15950                   tween1[i] = t;
15951                   break;
15952                 }
15953               }
15954
15955               if (i === n) tween1.push(t);
15956             }
15957
15958             schedule.tween = tween1;
15959           };
15960         }
15961
15962         function transition_tween (name, value) {
15963           var id = this._id;
15964           name += "";
15965
15966           if (arguments.length < 2) {
15967             var tween = get$1(this.node(), id).tween;
15968
15969             for (var i = 0, n = tween.length, t; i < n; ++i) {
15970               if ((t = tween[i]).name === name) {
15971                 return t.value;
15972               }
15973             }
15974
15975             return null;
15976           }
15977
15978           return this.each((value == null ? tweenRemove : tweenFunction)(id, name, value));
15979         }
15980         function tweenValue(transition, name, value) {
15981           var id = transition._id;
15982           transition.each(function () {
15983             var schedule = set(this, id);
15984             (schedule.value || (schedule.value = {}))[name] = value.apply(this, arguments);
15985           });
15986           return function (node) {
15987             return get$1(node, id).value[name];
15988           };
15989         }
15990
15991         function interpolate (a, b) {
15992           var c;
15993           return (typeof b === "number" ? d3_interpolateNumber : b instanceof color ? d3_interpolateRgb : (c = color(b)) ? (b = c, d3_interpolateRgb) : interpolateString)(a, b);
15994         }
15995
15996         function attrRemove(name) {
15997           return function () {
15998             this.removeAttribute(name);
15999           };
16000         }
16001
16002         function attrRemoveNS(fullname) {
16003           return function () {
16004             this.removeAttributeNS(fullname.space, fullname.local);
16005           };
16006         }
16007
16008         function attrConstant(name, interpolate, value1) {
16009           var string00,
16010               string1 = value1 + "",
16011               interpolate0;
16012           return function () {
16013             var string0 = this.getAttribute(name);
16014             return string0 === string1 ? null : string0 === string00 ? interpolate0 : interpolate0 = interpolate(string00 = string0, value1);
16015           };
16016         }
16017
16018         function attrConstantNS(fullname, interpolate, value1) {
16019           var string00,
16020               string1 = value1 + "",
16021               interpolate0;
16022           return function () {
16023             var string0 = this.getAttributeNS(fullname.space, fullname.local);
16024             return string0 === string1 ? null : string0 === string00 ? interpolate0 : interpolate0 = interpolate(string00 = string0, value1);
16025           };
16026         }
16027
16028         function attrFunction(name, interpolate, value) {
16029           var string00, string10, interpolate0;
16030           return function () {
16031             var string0,
16032                 value1 = value(this),
16033                 string1;
16034             if (value1 == null) return void this.removeAttribute(name);
16035             string0 = this.getAttribute(name);
16036             string1 = value1 + "";
16037             return string0 === string1 ? null : string0 === string00 && string1 === string10 ? interpolate0 : (string10 = string1, interpolate0 = interpolate(string00 = string0, value1));
16038           };
16039         }
16040
16041         function attrFunctionNS(fullname, interpolate, value) {
16042           var string00, string10, interpolate0;
16043           return function () {
16044             var string0,
16045                 value1 = value(this),
16046                 string1;
16047             if (value1 == null) return void this.removeAttributeNS(fullname.space, fullname.local);
16048             string0 = this.getAttributeNS(fullname.space, fullname.local);
16049             string1 = value1 + "";
16050             return string0 === string1 ? null : string0 === string00 && string1 === string10 ? interpolate0 : (string10 = string1, interpolate0 = interpolate(string00 = string0, value1));
16051           };
16052         }
16053
16054         function transition_attr (name, value) {
16055           var fullname = namespace(name),
16056               i = fullname === "transform" ? interpolateTransformSvg : interpolate;
16057           return this.attrTween(name, typeof value === "function" ? (fullname.local ? attrFunctionNS : attrFunction)(fullname, i, tweenValue(this, "attr." + name, value)) : value == null ? (fullname.local ? attrRemoveNS : attrRemove)(fullname) : (fullname.local ? attrConstantNS : attrConstant)(fullname, i, value));
16058         }
16059
16060         function attrInterpolate(name, i) {
16061           return function (t) {
16062             this.setAttribute(name, i.call(this, t));
16063           };
16064         }
16065
16066         function attrInterpolateNS(fullname, i) {
16067           return function (t) {
16068             this.setAttributeNS(fullname.space, fullname.local, i.call(this, t));
16069           };
16070         }
16071
16072         function attrTweenNS(fullname, value) {
16073           var t0, i0;
16074
16075           function tween() {
16076             var i = value.apply(this, arguments);
16077             if (i !== i0) t0 = (i0 = i) && attrInterpolateNS(fullname, i);
16078             return t0;
16079           }
16080
16081           tween._value = value;
16082           return tween;
16083         }
16084
16085         function attrTween(name, value) {
16086           var t0, i0;
16087
16088           function tween() {
16089             var i = value.apply(this, arguments);
16090             if (i !== i0) t0 = (i0 = i) && attrInterpolate(name, i);
16091             return t0;
16092           }
16093
16094           tween._value = value;
16095           return tween;
16096         }
16097
16098         function transition_attrTween (name, value) {
16099           var key = "attr." + name;
16100           if (arguments.length < 2) return (key = this.tween(key)) && key._value;
16101           if (value == null) return this.tween(key, null);
16102           if (typeof value !== "function") throw new Error();
16103           var fullname = namespace(name);
16104           return this.tween(key, (fullname.local ? attrTweenNS : attrTween)(fullname, value));
16105         }
16106
16107         function delayFunction(id, value) {
16108           return function () {
16109             init(this, id).delay = +value.apply(this, arguments);
16110           };
16111         }
16112
16113         function delayConstant(id, value) {
16114           return value = +value, function () {
16115             init(this, id).delay = value;
16116           };
16117         }
16118
16119         function transition_delay (value) {
16120           var id = this._id;
16121           return arguments.length ? this.each((typeof value === "function" ? delayFunction : delayConstant)(id, value)) : get$1(this.node(), id).delay;
16122         }
16123
16124         function durationFunction(id, value) {
16125           return function () {
16126             set(this, id).duration = +value.apply(this, arguments);
16127           };
16128         }
16129
16130         function durationConstant(id, value) {
16131           return value = +value, function () {
16132             set(this, id).duration = value;
16133           };
16134         }
16135
16136         function transition_duration (value) {
16137           var id = this._id;
16138           return arguments.length ? this.each((typeof value === "function" ? durationFunction : durationConstant)(id, value)) : get$1(this.node(), id).duration;
16139         }
16140
16141         function easeConstant(id, value) {
16142           if (typeof value !== "function") throw new Error();
16143           return function () {
16144             set(this, id).ease = value;
16145           };
16146         }
16147
16148         function transition_ease (value) {
16149           var id = this._id;
16150           return arguments.length ? this.each(easeConstant(id, value)) : get$1(this.node(), id).ease;
16151         }
16152
16153         function easeVarying(id, value) {
16154           return function () {
16155             var v = value.apply(this, arguments);
16156             if (typeof v !== "function") throw new Error();
16157             set(this, id).ease = v;
16158           };
16159         }
16160
16161         function transition_easeVarying (value) {
16162           if (typeof value !== "function") throw new Error();
16163           return this.each(easeVarying(this._id, value));
16164         }
16165
16166         function transition_filter (match) {
16167           if (typeof match !== "function") match = matcher(match);
16168
16169           for (var groups = this._groups, m = groups.length, subgroups = new Array(m), j = 0; j < m; ++j) {
16170             for (var group = groups[j], n = group.length, subgroup = subgroups[j] = [], node, i = 0; i < n; ++i) {
16171               if ((node = group[i]) && match.call(node, node.__data__, i, group)) {
16172                 subgroup.push(node);
16173               }
16174             }
16175           }
16176
16177           return new Transition(subgroups, this._parents, this._name, this._id);
16178         }
16179
16180         function transition_merge (transition) {
16181           if (transition._id !== this._id) throw new Error();
16182
16183           for (var groups0 = this._groups, groups1 = transition._groups, m0 = groups0.length, m1 = groups1.length, m = Math.min(m0, m1), merges = new Array(m0), j = 0; j < m; ++j) {
16184             for (var group0 = groups0[j], group1 = groups1[j], n = group0.length, merge = merges[j] = new Array(n), node, i = 0; i < n; ++i) {
16185               if (node = group0[i] || group1[i]) {
16186                 merge[i] = node;
16187               }
16188             }
16189           }
16190
16191           for (; j < m0; ++j) {
16192             merges[j] = groups0[j];
16193           }
16194
16195           return new Transition(merges, this._parents, this._name, this._id);
16196         }
16197
16198         function start(name) {
16199           return (name + "").trim().split(/^|\s+/).every(function (t) {
16200             var i = t.indexOf(".");
16201             if (i >= 0) t = t.slice(0, i);
16202             return !t || t === "start";
16203           });
16204         }
16205
16206         function onFunction(id, name, listener) {
16207           var on0,
16208               on1,
16209               sit = start(name) ? init : set;
16210           return function () {
16211             var schedule = sit(this, id),
16212                 on = schedule.on; // If this node shared a dispatch with the previous node,
16213             // just assign the updated shared dispatch and we’re done!
16214             // Otherwise, copy-on-write.
16215
16216             if (on !== on0) (on1 = (on0 = on).copy()).on(name, listener);
16217             schedule.on = on1;
16218           };
16219         }
16220
16221         function transition_on (name, listener) {
16222           var id = this._id;
16223           return arguments.length < 2 ? get$1(this.node(), id).on.on(name) : this.each(onFunction(id, name, listener));
16224         }
16225
16226         function removeFunction(id) {
16227           return function () {
16228             var parent = this.parentNode;
16229
16230             for (var i in this.__transition) {
16231               if (+i !== id) return;
16232             }
16233
16234             if (parent) parent.removeChild(this);
16235           };
16236         }
16237
16238         function transition_remove () {
16239           return this.on("end.remove", removeFunction(this._id));
16240         }
16241
16242         function transition_select (select) {
16243           var name = this._name,
16244               id = this._id;
16245           if (typeof select !== "function") select = selector(select);
16246
16247           for (var groups = this._groups, m = groups.length, subgroups = new Array(m), j = 0; j < m; ++j) {
16248             for (var group = groups[j], n = group.length, subgroup = subgroups[j] = new Array(n), node, subnode, i = 0; i < n; ++i) {
16249               if ((node = group[i]) && (subnode = select.call(node, node.__data__, i, group))) {
16250                 if ("__data__" in node) subnode.__data__ = node.__data__;
16251                 subgroup[i] = subnode;
16252                 schedule(subgroup[i], name, id, i, subgroup, get$1(node, id));
16253               }
16254             }
16255           }
16256
16257           return new Transition(subgroups, this._parents, name, id);
16258         }
16259
16260         function transition_selectAll (select) {
16261           var name = this._name,
16262               id = this._id;
16263           if (typeof select !== "function") select = selectorAll(select);
16264
16265           for (var groups = this._groups, m = groups.length, subgroups = [], parents = [], j = 0; j < m; ++j) {
16266             for (var group = groups[j], n = group.length, node, i = 0; i < n; ++i) {
16267               if (node = group[i]) {
16268                 for (var children = select.call(node, node.__data__, i, group), child, inherit = get$1(node, id), k = 0, l = children.length; k < l; ++k) {
16269                   if (child = children[k]) {
16270                     schedule(child, name, id, k, children, inherit);
16271                   }
16272                 }
16273
16274                 subgroups.push(children);
16275                 parents.push(node);
16276               }
16277             }
16278           }
16279
16280           return new Transition(subgroups, parents, name, id);
16281         }
16282
16283         var Selection = selection.prototype.constructor;
16284         function transition_selection () {
16285           return new Selection(this._groups, this._parents);
16286         }
16287
16288         function styleNull(name, interpolate) {
16289           var string00, string10, interpolate0;
16290           return function () {
16291             var string0 = styleValue(this, name),
16292                 string1 = (this.style.removeProperty(name), styleValue(this, name));
16293             return string0 === string1 ? null : string0 === string00 && string1 === string10 ? interpolate0 : interpolate0 = interpolate(string00 = string0, string10 = string1);
16294           };
16295         }
16296
16297         function styleRemove(name) {
16298           return function () {
16299             this.style.removeProperty(name);
16300           };
16301         }
16302
16303         function styleConstant(name, interpolate, value1) {
16304           var string00,
16305               string1 = value1 + "",
16306               interpolate0;
16307           return function () {
16308             var string0 = styleValue(this, name);
16309             return string0 === string1 ? null : string0 === string00 ? interpolate0 : interpolate0 = interpolate(string00 = string0, value1);
16310           };
16311         }
16312
16313         function styleFunction(name, interpolate, value) {
16314           var string00, string10, interpolate0;
16315           return function () {
16316             var string0 = styleValue(this, name),
16317                 value1 = value(this),
16318                 string1 = value1 + "";
16319             if (value1 == null) string1 = value1 = (this.style.removeProperty(name), styleValue(this, name));
16320             return string0 === string1 ? null : string0 === string00 && string1 === string10 ? interpolate0 : (string10 = string1, interpolate0 = interpolate(string00 = string0, value1));
16321           };
16322         }
16323
16324         function styleMaybeRemove(id, name) {
16325           var on0,
16326               on1,
16327               listener0,
16328               key = "style." + name,
16329               event = "end." + key,
16330               remove;
16331           return function () {
16332             var schedule = set(this, id),
16333                 on = schedule.on,
16334                 listener = schedule.value[key] == null ? remove || (remove = styleRemove(name)) : undefined; // If this node shared a dispatch with the previous node,
16335             // just assign the updated shared dispatch and we’re done!
16336             // Otherwise, copy-on-write.
16337
16338             if (on !== on0 || listener0 !== listener) (on1 = (on0 = on).copy()).on(event, listener0 = listener);
16339             schedule.on = on1;
16340           };
16341         }
16342
16343         function transition_style (name, value, priority) {
16344           var i = (name += "") === "transform" ? interpolateTransformCss : interpolate;
16345           return value == null ? this.styleTween(name, styleNull(name, i)).on("end.style." + name, styleRemove(name)) : typeof value === "function" ? this.styleTween(name, styleFunction(name, i, tweenValue(this, "style." + name, value))).each(styleMaybeRemove(this._id, name)) : this.styleTween(name, styleConstant(name, i, value), priority).on("end.style." + name, null);
16346         }
16347
16348         function styleInterpolate(name, i, priority) {
16349           return function (t) {
16350             this.style.setProperty(name, i.call(this, t), priority);
16351           };
16352         }
16353
16354         function styleTween(name, value, priority) {
16355           var t, i0;
16356
16357           function tween() {
16358             var i = value.apply(this, arguments);
16359             if (i !== i0) t = (i0 = i) && styleInterpolate(name, i, priority);
16360             return t;
16361           }
16362
16363           tween._value = value;
16364           return tween;
16365         }
16366
16367         function transition_styleTween (name, value, priority) {
16368           var key = "style." + (name += "");
16369           if (arguments.length < 2) return (key = this.tween(key)) && key._value;
16370           if (value == null) return this.tween(key, null);
16371           if (typeof value !== "function") throw new Error();
16372           return this.tween(key, styleTween(name, value, priority == null ? "" : priority));
16373         }
16374
16375         function textConstant(value) {
16376           return function () {
16377             this.textContent = value;
16378           };
16379         }
16380
16381         function textFunction(value) {
16382           return function () {
16383             var value1 = value(this);
16384             this.textContent = value1 == null ? "" : value1;
16385           };
16386         }
16387
16388         function transition_text (value) {
16389           return this.tween("text", typeof value === "function" ? textFunction(tweenValue(this, "text", value)) : textConstant(value == null ? "" : value + ""));
16390         }
16391
16392         function textInterpolate(i) {
16393           return function (t) {
16394             this.textContent = i.call(this, t);
16395           };
16396         }
16397
16398         function textTween(value) {
16399           var t0, i0;
16400
16401           function tween() {
16402             var i = value.apply(this, arguments);
16403             if (i !== i0) t0 = (i0 = i) && textInterpolate(i);
16404             return t0;
16405           }
16406
16407           tween._value = value;
16408           return tween;
16409         }
16410
16411         function transition_textTween (value) {
16412           var key = "text";
16413           if (arguments.length < 1) return (key = this.tween(key)) && key._value;
16414           if (value == null) return this.tween(key, null);
16415           if (typeof value !== "function") throw new Error();
16416           return this.tween(key, textTween(value));
16417         }
16418
16419         function transition_transition () {
16420           var name = this._name,
16421               id0 = this._id,
16422               id1 = newId();
16423
16424           for (var groups = this._groups, m = groups.length, j = 0; j < m; ++j) {
16425             for (var group = groups[j], n = group.length, node, i = 0; i < n; ++i) {
16426               if (node = group[i]) {
16427                 var inherit = get$1(node, id0);
16428                 schedule(node, name, id1, i, group, {
16429                   time: inherit.time + inherit.delay + inherit.duration,
16430                   delay: 0,
16431                   duration: inherit.duration,
16432                   ease: inherit.ease
16433                 });
16434               }
16435             }
16436           }
16437
16438           return new Transition(groups, this._parents, name, id1);
16439         }
16440
16441         function transition_end () {
16442           var on0,
16443               on1,
16444               that = this,
16445               id = that._id,
16446               size = that.size();
16447           return new Promise(function (resolve, reject) {
16448             var cancel = {
16449               value: reject
16450             },
16451                 end = {
16452               value: function value() {
16453                 if (--size === 0) resolve();
16454               }
16455             };
16456             that.each(function () {
16457               var schedule = set(this, id),
16458                   on = schedule.on; // If this node shared a dispatch with the previous node,
16459               // just assign the updated shared dispatch and we’re done!
16460               // Otherwise, copy-on-write.
16461
16462               if (on !== on0) {
16463                 on1 = (on0 = on).copy();
16464
16465                 on1._.cancel.push(cancel);
16466
16467                 on1._.interrupt.push(cancel);
16468
16469                 on1._.end.push(end);
16470               }
16471
16472               schedule.on = on1;
16473             }); // The selection was empty, resolve end immediately
16474
16475             if (size === 0) resolve();
16476           });
16477         }
16478
16479         var id = 0;
16480         function Transition(groups, parents, name, id) {
16481           this._groups = groups;
16482           this._parents = parents;
16483           this._name = name;
16484           this._id = id;
16485         }
16486         function newId() {
16487           return ++id;
16488         }
16489         var selection_prototype = selection.prototype;
16490         Transition.prototype = _defineProperty({
16491           constructor: Transition,
16492           select: transition_select,
16493           selectAll: transition_selectAll,
16494           filter: transition_filter,
16495           merge: transition_merge,
16496           selection: transition_selection,
16497           transition: transition_transition,
16498           call: selection_prototype.call,
16499           nodes: selection_prototype.nodes,
16500           node: selection_prototype.node,
16501           size: selection_prototype.size,
16502           empty: selection_prototype.empty,
16503           each: selection_prototype.each,
16504           on: transition_on,
16505           attr: transition_attr,
16506           attrTween: transition_attrTween,
16507           style: transition_style,
16508           styleTween: transition_styleTween,
16509           text: transition_text,
16510           textTween: transition_textTween,
16511           remove: transition_remove,
16512           tween: transition_tween,
16513           delay: transition_delay,
16514           duration: transition_duration,
16515           ease: transition_ease,
16516           easeVarying: transition_easeVarying,
16517           end: transition_end
16518         }, Symbol.iterator, selection_prototype[Symbol.iterator]);
16519
16520         var linear$1 = function linear(t) {
16521           return +t;
16522         };
16523
16524         function cubicInOut(t) {
16525           return ((t *= 2) <= 1 ? t * t * t : (t -= 2) * t * t + 2) / 2;
16526         }
16527
16528         var defaultTiming = {
16529           time: null,
16530           // Set on use.
16531           delay: 0,
16532           duration: 250,
16533           ease: cubicInOut
16534         };
16535
16536         function inherit(node, id) {
16537           var timing;
16538
16539           while (!(timing = node.__transition) || !(timing = timing[id])) {
16540             if (!(node = node.parentNode)) {
16541               throw new Error("transition ".concat(id, " not found"));
16542             }
16543           }
16544
16545           return timing;
16546         }
16547
16548         function selection_transition (name) {
16549           var id, timing;
16550
16551           if (name instanceof Transition) {
16552             id = name._id, name = name._name;
16553           } else {
16554             id = newId(), (timing = defaultTiming).time = now$1(), name = name == null ? null : name + "";
16555           }
16556
16557           for (var groups = this._groups, m = groups.length, j = 0; j < m; ++j) {
16558             for (var group = groups[j], n = group.length, node, i = 0; i < n; ++i) {
16559               if (node = group[i]) {
16560                 schedule(node, name, id, i, group, timing || inherit(node, id));
16561               }
16562             }
16563           }
16564
16565           return new Transition(groups, this._parents, name, id);
16566         }
16567
16568         selection.prototype.interrupt = selection_interrupt;
16569         selection.prototype.transition = selection_transition;
16570
16571         var constant = (function (x) {
16572           return function () {
16573             return x;
16574           };
16575         });
16576
16577         function ZoomEvent(type, _ref) {
16578           var sourceEvent = _ref.sourceEvent,
16579               target = _ref.target,
16580               transform = _ref.transform,
16581               dispatch = _ref.dispatch;
16582           Object.defineProperties(this, {
16583             type: {
16584               value: type,
16585               enumerable: true,
16586               configurable: true
16587             },
16588             sourceEvent: {
16589               value: sourceEvent,
16590               enumerable: true,
16591               configurable: true
16592             },
16593             target: {
16594               value: target,
16595               enumerable: true,
16596               configurable: true
16597             },
16598             transform: {
16599               value: transform,
16600               enumerable: true,
16601               configurable: true
16602             },
16603             _: {
16604               value: dispatch
16605             }
16606           });
16607         }
16608
16609         function Transform(k, x, y) {
16610           this.k = k;
16611           this.x = x;
16612           this.y = y;
16613         }
16614         Transform.prototype = {
16615           constructor: Transform,
16616           scale: function scale(k) {
16617             return k === 1 ? this : new Transform(this.k * k, this.x, this.y);
16618           },
16619           translate: function translate(x, y) {
16620             return x === 0 & y === 0 ? this : new Transform(this.k, this.x + this.k * x, this.y + this.k * y);
16621           },
16622           apply: function apply(point) {
16623             return [point[0] * this.k + this.x, point[1] * this.k + this.y];
16624           },
16625           applyX: function applyX(x) {
16626             return x * this.k + this.x;
16627           },
16628           applyY: function applyY(y) {
16629             return y * this.k + this.y;
16630           },
16631           invert: function invert(location) {
16632             return [(location[0] - this.x) / this.k, (location[1] - this.y) / this.k];
16633           },
16634           invertX: function invertX(x) {
16635             return (x - this.x) / this.k;
16636           },
16637           invertY: function invertY(y) {
16638             return (y - this.y) / this.k;
16639           },
16640           rescaleX: function rescaleX(x) {
16641             return x.copy().domain(x.range().map(this.invertX, this).map(x.invert, x));
16642           },
16643           rescaleY: function rescaleY(y) {
16644             return y.copy().domain(y.range().map(this.invertY, this).map(y.invert, y));
16645           },
16646           toString: function toString() {
16647             return "translate(" + this.x + "," + this.y + ") scale(" + this.k + ")";
16648           }
16649         };
16650         var identity$2 = new Transform(1, 0, 0);
16651
16652         function nopropagation(event) {
16653           event.stopImmediatePropagation();
16654         }
16655         function noevent (event) {
16656           event.preventDefault();
16657           event.stopImmediatePropagation();
16658         }
16659
16660         // except for pinch-to-zoom, which is sent as a wheel+ctrlKey event
16661
16662         function defaultFilter$1(event) {
16663           return (!event.ctrlKey || event.type === 'wheel') && !event.button;
16664         }
16665
16666         function defaultExtent$1() {
16667           var e = this;
16668
16669           if (e instanceof SVGElement) {
16670             e = e.ownerSVGElement || e;
16671
16672             if (e.hasAttribute("viewBox")) {
16673               e = e.viewBox.baseVal;
16674               return [[e.x, e.y], [e.x + e.width, e.y + e.height]];
16675             }
16676
16677             return [[0, 0], [e.width.baseVal.value, e.height.baseVal.value]];
16678           }
16679
16680           return [[0, 0], [e.clientWidth, e.clientHeight]];
16681         }
16682
16683         function defaultTransform() {
16684           return this.__zoom || identity$2;
16685         }
16686
16687         function defaultWheelDelta$1(event) {
16688           return -event.deltaY * (event.deltaMode === 1 ? 0.05 : event.deltaMode ? 1 : 0.002) * (event.ctrlKey ? 10 : 1);
16689         }
16690
16691         function defaultTouchable() {
16692           return navigator.maxTouchPoints || "ontouchstart" in this;
16693         }
16694
16695         function defaultConstrain$1(transform, extent, translateExtent) {
16696           var dx0 = transform.invertX(extent[0][0]) - translateExtent[0][0],
16697               dx1 = transform.invertX(extent[1][0]) - translateExtent[1][0],
16698               dy0 = transform.invertY(extent[0][1]) - translateExtent[0][1],
16699               dy1 = transform.invertY(extent[1][1]) - translateExtent[1][1];
16700           return transform.translate(dx1 > dx0 ? (dx0 + dx1) / 2 : Math.min(0, dx0) || Math.max(0, dx1), dy1 > dy0 ? (dy0 + dy1) / 2 : Math.min(0, dy0) || Math.max(0, dy1));
16701         }
16702
16703         function d3_zoom () {
16704           var filter = defaultFilter$1,
16705               extent = defaultExtent$1,
16706               constrain = defaultConstrain$1,
16707               wheelDelta = defaultWheelDelta$1,
16708               touchable = defaultTouchable,
16709               scaleExtent = [0, Infinity],
16710               translateExtent = [[-Infinity, -Infinity], [Infinity, Infinity]],
16711               duration = 250,
16712               interpolate = interpolateZoom,
16713               listeners = dispatch$8("start", "zoom", "end"),
16714               touchstarting,
16715               touchfirst,
16716               touchending,
16717               touchDelay = 500,
16718               wheelDelay = 150,
16719               clickDistance2 = 0,
16720               tapDistance = 10;
16721
16722           function zoom(selection) {
16723             selection.property("__zoom", defaultTransform).on("wheel.zoom", wheeled).on("mousedown.zoom", mousedowned).on("dblclick.zoom", dblclicked).filter(touchable).on("touchstart.zoom", touchstarted).on("touchmove.zoom", touchmoved).on("touchend.zoom touchcancel.zoom", touchended).style("-webkit-tap-highlight-color", "rgba(0,0,0,0)");
16724           }
16725
16726           zoom.transform = function (collection, transform, point, event) {
16727             var selection = collection.selection ? collection.selection() : collection;
16728             selection.property("__zoom", defaultTransform);
16729
16730             if (collection !== selection) {
16731               schedule(collection, transform, point, event);
16732             } else {
16733               selection.interrupt().each(function () {
16734                 gesture(this, arguments).event(event).start().zoom(null, typeof transform === "function" ? transform.apply(this, arguments) : transform).end();
16735               });
16736             }
16737           };
16738
16739           zoom.scaleBy = function (selection, k, p, event) {
16740             zoom.scaleTo(selection, function () {
16741               var k0 = this.__zoom.k,
16742                   k1 = typeof k === "function" ? k.apply(this, arguments) : k;
16743               return k0 * k1;
16744             }, p, event);
16745           };
16746
16747           zoom.scaleTo = function (selection, k, p, event) {
16748             zoom.transform(selection, function () {
16749               var e = extent.apply(this, arguments),
16750                   t0 = this.__zoom,
16751                   p0 = p == null ? centroid(e) : typeof p === "function" ? p.apply(this, arguments) : p,
16752                   p1 = t0.invert(p0),
16753                   k1 = typeof k === "function" ? k.apply(this, arguments) : k;
16754               return constrain(translate(scale(t0, k1), p0, p1), e, translateExtent);
16755             }, p, event);
16756           };
16757
16758           zoom.translateBy = function (selection, x, y, event) {
16759             zoom.transform(selection, function () {
16760               return constrain(this.__zoom.translate(typeof x === "function" ? x.apply(this, arguments) : x, typeof y === "function" ? y.apply(this, arguments) : y), extent.apply(this, arguments), translateExtent);
16761             }, null, event);
16762           };
16763
16764           zoom.translateTo = function (selection, x, y, p, event) {
16765             zoom.transform(selection, function () {
16766               var e = extent.apply(this, arguments),
16767                   t = this.__zoom,
16768                   p0 = p == null ? centroid(e) : typeof p === "function" ? p.apply(this, arguments) : p;
16769               return constrain(identity$2.translate(p0[0], p0[1]).scale(t.k).translate(typeof x === "function" ? -x.apply(this, arguments) : -x, typeof y === "function" ? -y.apply(this, arguments) : -y), e, translateExtent);
16770             }, p, event);
16771           };
16772
16773           function scale(transform, k) {
16774             k = Math.max(scaleExtent[0], Math.min(scaleExtent[1], k));
16775             return k === transform.k ? transform : new Transform(k, transform.x, transform.y);
16776           }
16777
16778           function translate(transform, p0, p1) {
16779             var x = p0[0] - p1[0] * transform.k,
16780                 y = p0[1] - p1[1] * transform.k;
16781             return x === transform.x && y === transform.y ? transform : new Transform(transform.k, x, y);
16782           }
16783
16784           function centroid(extent) {
16785             return [(+extent[0][0] + +extent[1][0]) / 2, (+extent[0][1] + +extent[1][1]) / 2];
16786           }
16787
16788           function schedule(transition, transform, point, event) {
16789             transition.on("start.zoom", function () {
16790               gesture(this, arguments).event(event).start();
16791             }).on("interrupt.zoom end.zoom", function () {
16792               gesture(this, arguments).event(event).end();
16793             }).tween("zoom", function () {
16794               var that = this,
16795                   args = arguments,
16796                   g = gesture(that, args).event(event),
16797                   e = extent.apply(that, args),
16798                   p = point == null ? centroid(e) : typeof point === "function" ? point.apply(that, args) : point,
16799                   w = Math.max(e[1][0] - e[0][0], e[1][1] - e[0][1]),
16800                   a = that.__zoom,
16801                   b = typeof transform === "function" ? transform.apply(that, args) : transform,
16802                   i = interpolate(a.invert(p).concat(w / a.k), b.invert(p).concat(w / b.k));
16803               return function (t) {
16804                 if (t === 1) t = b; // Avoid rounding error on end.
16805                 else {
16806                     var l = i(t),
16807                         k = w / l[2];
16808                     t = new Transform(k, p[0] - l[0] * k, p[1] - l[1] * k);
16809                   }
16810                 g.zoom(null, t);
16811               };
16812             });
16813           }
16814
16815           function gesture(that, args, clean) {
16816             return !clean && that.__zooming || new Gesture(that, args);
16817           }
16818
16819           function Gesture(that, args) {
16820             this.that = that;
16821             this.args = args;
16822             this.active = 0;
16823             this.sourceEvent = null;
16824             this.extent = extent.apply(that, args);
16825             this.taps = 0;
16826           }
16827
16828           Gesture.prototype = {
16829             event: function event(_event) {
16830               if (_event) this.sourceEvent = _event;
16831               return this;
16832             },
16833             start: function start() {
16834               if (++this.active === 1) {
16835                 this.that.__zooming = this;
16836                 this.emit("start");
16837               }
16838
16839               return this;
16840             },
16841             zoom: function zoom(key, transform) {
16842               if (this.mouse && key !== "mouse") this.mouse[1] = transform.invert(this.mouse[0]);
16843               if (this.touch0 && key !== "touch") this.touch0[1] = transform.invert(this.touch0[0]);
16844               if (this.touch1 && key !== "touch") this.touch1[1] = transform.invert(this.touch1[0]);
16845               this.that.__zoom = transform;
16846               this.emit("zoom");
16847               return this;
16848             },
16849             end: function end() {
16850               if (--this.active === 0) {
16851                 delete this.that.__zooming;
16852                 this.emit("end");
16853               }
16854
16855               return this;
16856             },
16857             emit: function emit(type) {
16858               var d = select(this.that).datum();
16859               listeners.call(type, this.that, new ZoomEvent(type, {
16860                 sourceEvent: this.sourceEvent,
16861                 target: zoom,
16862                 type: type,
16863                 transform: this.that.__zoom,
16864                 dispatch: listeners
16865               }), d);
16866             }
16867           };
16868
16869           function wheeled(event) {
16870             for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
16871               args[_key - 1] = arguments[_key];
16872             }
16873
16874             if (!filter.apply(this, arguments)) return;
16875             var g = gesture(this, args).event(event),
16876                 t = this.__zoom,
16877                 k = Math.max(scaleExtent[0], Math.min(scaleExtent[1], t.k * Math.pow(2, wheelDelta.apply(this, arguments)))),
16878                 p = pointer(event); // If the mouse is in the same location as before, reuse it.
16879             // If there were recent wheel events, reset the wheel idle timeout.
16880
16881             if (g.wheel) {
16882               if (g.mouse[0][0] !== p[0] || g.mouse[0][1] !== p[1]) {
16883                 g.mouse[1] = t.invert(g.mouse[0] = p);
16884               }
16885
16886               clearTimeout(g.wheel);
16887             } // If this wheel event won’t trigger a transform change, ignore it.
16888             else if (t.k === k) return; // Otherwise, capture the mouse point and location at the start.
16889               else {
16890                   g.mouse = [p, t.invert(p)];
16891                   interrupt(this);
16892                   g.start();
16893                 }
16894
16895             noevent(event);
16896             g.wheel = setTimeout(wheelidled, wheelDelay);
16897             g.zoom("mouse", constrain(translate(scale(t, k), g.mouse[0], g.mouse[1]), g.extent, translateExtent));
16898
16899             function wheelidled() {
16900               g.wheel = null;
16901               g.end();
16902             }
16903           }
16904
16905           function mousedowned(event) {
16906             for (var _len2 = arguments.length, args = new Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) {
16907               args[_key2 - 1] = arguments[_key2];
16908             }
16909
16910             if (touchending || !filter.apply(this, arguments)) return;
16911             var g = gesture(this, args, true).event(event),
16912                 v = select(event.view).on("mousemove.zoom", mousemoved, true).on("mouseup.zoom", mouseupped, true),
16913                 p = pointer(event, currentTarget),
16914                 currentTarget = event.currentTarget,
16915                 x0 = event.clientX,
16916                 y0 = event.clientY;
16917             dragDisable(event.view);
16918             nopropagation(event);
16919             g.mouse = [p, this.__zoom.invert(p)];
16920             interrupt(this);
16921             g.start();
16922
16923             function mousemoved(event) {
16924               noevent(event);
16925
16926               if (!g.moved) {
16927                 var dx = event.clientX - x0,
16928                     dy = event.clientY - y0;
16929                 g.moved = dx * dx + dy * dy > clickDistance2;
16930               }
16931
16932               g.event(event).zoom("mouse", constrain(translate(g.that.__zoom, g.mouse[0] = pointer(event, currentTarget), g.mouse[1]), g.extent, translateExtent));
16933             }
16934
16935             function mouseupped(event) {
16936               v.on("mousemove.zoom mouseup.zoom", null);
16937               yesdrag(event.view, g.moved);
16938               noevent(event);
16939               g.event(event).end();
16940             }
16941           }
16942
16943           function dblclicked(event) {
16944             for (var _len3 = arguments.length, args = new Array(_len3 > 1 ? _len3 - 1 : 0), _key3 = 1; _key3 < _len3; _key3++) {
16945               args[_key3 - 1] = arguments[_key3];
16946             }
16947
16948             if (!filter.apply(this, arguments)) return;
16949             var t0 = this.__zoom,
16950                 p0 = pointer(event.changedTouches ? event.changedTouches[0] : event, this),
16951                 p1 = t0.invert(p0),
16952                 k1 = t0.k * (event.shiftKey ? 0.5 : 2),
16953                 t1 = constrain(translate(scale(t0, k1), p0, p1), extent.apply(this, args), translateExtent);
16954             noevent(event);
16955             if (duration > 0) select(this).transition().duration(duration).call(schedule, t1, p0, event);else select(this).call(zoom.transform, t1, p0, event);
16956           }
16957
16958           function touchstarted(event) {
16959             for (var _len4 = arguments.length, args = new Array(_len4 > 1 ? _len4 - 1 : 0), _key4 = 1; _key4 < _len4; _key4++) {
16960               args[_key4 - 1] = arguments[_key4];
16961             }
16962
16963             if (!filter.apply(this, arguments)) return;
16964             var touches = event.touches,
16965                 n = touches.length,
16966                 g = gesture(this, args, event.changedTouches.length === n).event(event),
16967                 started,
16968                 i,
16969                 t,
16970                 p;
16971             nopropagation(event);
16972
16973             for (i = 0; i < n; ++i) {
16974               t = touches[i], p = pointer(t, this);
16975               p = [p, this.__zoom.invert(p), t.identifier];
16976               if (!g.touch0) g.touch0 = p, started = true, g.taps = 1 + !!touchstarting;else if (!g.touch1 && g.touch0[2] !== p[2]) g.touch1 = p, g.taps = 0;
16977             }
16978
16979             if (touchstarting) touchstarting = clearTimeout(touchstarting);
16980
16981             if (started) {
16982               if (g.taps < 2) touchfirst = p[0], touchstarting = setTimeout(function () {
16983                 touchstarting = null;
16984               }, touchDelay);
16985               interrupt(this);
16986               g.start();
16987             }
16988           }
16989
16990           function touchmoved(event) {
16991             if (!this.__zooming) return;
16992
16993             for (var _len5 = arguments.length, args = new Array(_len5 > 1 ? _len5 - 1 : 0), _key5 = 1; _key5 < _len5; _key5++) {
16994               args[_key5 - 1] = arguments[_key5];
16995             }
16996
16997             var g = gesture(this, args).event(event),
16998                 touches = event.changedTouches,
16999                 n = touches.length,
17000                 i,
17001                 t,
17002                 p,
17003                 l;
17004             noevent(event);
17005
17006             for (i = 0; i < n; ++i) {
17007               t = touches[i], p = pointer(t, this);
17008               if (g.touch0 && g.touch0[2] === t.identifier) g.touch0[0] = p;else if (g.touch1 && g.touch1[2] === t.identifier) g.touch1[0] = p;
17009             }
17010
17011             t = g.that.__zoom;
17012
17013             if (g.touch1) {
17014               var p0 = g.touch0[0],
17015                   l0 = g.touch0[1],
17016                   p1 = g.touch1[0],
17017                   l1 = g.touch1[1],
17018                   dp = (dp = p1[0] - p0[0]) * dp + (dp = p1[1] - p0[1]) * dp,
17019                   dl = (dl = l1[0] - l0[0]) * dl + (dl = l1[1] - l0[1]) * dl;
17020               t = scale(t, Math.sqrt(dp / dl));
17021               p = [(p0[0] + p1[0]) / 2, (p0[1] + p1[1]) / 2];
17022               l = [(l0[0] + l1[0]) / 2, (l0[1] + l1[1]) / 2];
17023             } else if (g.touch0) p = g.touch0[0], l = g.touch0[1];else return;
17024
17025             g.zoom("touch", constrain(translate(t, p, l), g.extent, translateExtent));
17026           }
17027
17028           function touchended(event) {
17029             for (var _len6 = arguments.length, args = new Array(_len6 > 1 ? _len6 - 1 : 0), _key6 = 1; _key6 < _len6; _key6++) {
17030               args[_key6 - 1] = arguments[_key6];
17031             }
17032
17033             if (!this.__zooming) return;
17034             var g = gesture(this, args).event(event),
17035                 touches = event.changedTouches,
17036                 n = touches.length,
17037                 i,
17038                 t;
17039             nopropagation(event);
17040             if (touchending) clearTimeout(touchending);
17041             touchending = setTimeout(function () {
17042               touchending = null;
17043             }, touchDelay);
17044
17045             for (i = 0; i < n; ++i) {
17046               t = touches[i];
17047               if (g.touch0 && g.touch0[2] === t.identifier) delete g.touch0;else if (g.touch1 && g.touch1[2] === t.identifier) delete g.touch1;
17048             }
17049
17050             if (g.touch1 && !g.touch0) g.touch0 = g.touch1, delete g.touch1;
17051             if (g.touch0) g.touch0[1] = this.__zoom.invert(g.touch0[0]);else {
17052               g.end(); // If this was a dbltap, reroute to the (optional) dblclick.zoom handler.
17053
17054               if (g.taps === 2) {
17055                 t = pointer(t, this);
17056
17057                 if (Math.hypot(touchfirst[0] - t[0], touchfirst[1] - t[1]) < tapDistance) {
17058                   var p = select(this).on("dblclick.zoom");
17059                   if (p) p.apply(this, arguments);
17060                 }
17061               }
17062             }
17063           }
17064
17065           zoom.wheelDelta = function (_) {
17066             return arguments.length ? (wheelDelta = typeof _ === "function" ? _ : constant(+_), zoom) : wheelDelta;
17067           };
17068
17069           zoom.filter = function (_) {
17070             return arguments.length ? (filter = typeof _ === "function" ? _ : constant(!!_), zoom) : filter;
17071           };
17072
17073           zoom.touchable = function (_) {
17074             return arguments.length ? (touchable = typeof _ === "function" ? _ : constant(!!_), zoom) : touchable;
17075           };
17076
17077           zoom.extent = function (_) {
17078             return arguments.length ? (extent = typeof _ === "function" ? _ : constant([[+_[0][0], +_[0][1]], [+_[1][0], +_[1][1]]]), zoom) : extent;
17079           };
17080
17081           zoom.scaleExtent = function (_) {
17082             return arguments.length ? (scaleExtent[0] = +_[0], scaleExtent[1] = +_[1], zoom) : [scaleExtent[0], scaleExtent[1]];
17083           };
17084
17085           zoom.translateExtent = function (_) {
17086             return arguments.length ? (translateExtent[0][0] = +_[0][0], translateExtent[1][0] = +_[1][0], translateExtent[0][1] = +_[0][1], translateExtent[1][1] = +_[1][1], zoom) : [[translateExtent[0][0], translateExtent[0][1]], [translateExtent[1][0], translateExtent[1][1]]];
17087           };
17088
17089           zoom.constrain = function (_) {
17090             return arguments.length ? (constrain = _, zoom) : constrain;
17091           };
17092
17093           zoom.duration = function (_) {
17094             return arguments.length ? (duration = +_, zoom) : duration;
17095           };
17096
17097           zoom.interpolate = function (_) {
17098             return arguments.length ? (interpolate = _, zoom) : interpolate;
17099           };
17100
17101           zoom.on = function () {
17102             var value = listeners.on.apply(listeners, arguments);
17103             return value === listeners ? zoom : value;
17104           };
17105
17106           zoom.clickDistance = function (_) {
17107             return arguments.length ? (clickDistance2 = (_ = +_) * _, zoom) : Math.sqrt(clickDistance2);
17108           };
17109
17110           zoom.tapDistance = function (_) {
17111             return arguments.length ? (tapDistance = +_, zoom) : tapDistance;
17112           };
17113
17114           return zoom;
17115         }
17116
17117         /*
17118             Bypasses features of D3's default projection stream pipeline that are unnecessary:
17119             * Antimeridian clipping
17120             * Spherical rotation
17121             * Resampling
17122         */
17123
17124         function geoRawMercator() {
17125           var project = mercatorRaw;
17126           var k = 512 / Math.PI; // scale
17127
17128           var x = 0;
17129           var y = 0; // translate
17130
17131           var clipExtent = [[0, 0], [0, 0]];
17132
17133           function projection(point) {
17134             point = project(point[0] * Math.PI / 180, point[1] * Math.PI / 180);
17135             return [point[0] * k + x, y - point[1] * k];
17136           }
17137
17138           projection.invert = function (point) {
17139             point = project.invert((point[0] - x) / k, (y - point[1]) / k);
17140             return point && [point[0] * 180 / Math.PI, point[1] * 180 / Math.PI];
17141           };
17142
17143           projection.scale = function (_) {
17144             if (!arguments.length) return k;
17145             k = +_;
17146             return projection;
17147           };
17148
17149           projection.translate = function (_) {
17150             if (!arguments.length) return [x, y];
17151             x = +_[0];
17152             y = +_[1];
17153             return projection;
17154           };
17155
17156           projection.clipExtent = function (_) {
17157             if (!arguments.length) return clipExtent;
17158             clipExtent = _;
17159             return projection;
17160           };
17161
17162           projection.transform = function (obj) {
17163             if (!arguments.length) return identity$2.translate(x, y).scale(k);
17164             x = +obj.x;
17165             y = +obj.y;
17166             k = +obj.k;
17167             return projection;
17168           };
17169
17170           projection.stream = d3_geoTransform({
17171             point: function point(x, y) {
17172               var vec = projection([x, y]);
17173               this.stream.point(vec[0], vec[1]);
17174             }
17175           }).stream;
17176           return projection;
17177         }
17178
17179         function geoOrthoNormalizedDotProduct(a, b, origin) {
17180           if (geoVecEqual(origin, a) || geoVecEqual(origin, b)) {
17181             return 1; // coincident points, treat as straight and try to remove
17182           }
17183
17184           return geoVecNormalizedDot(a, b, origin);
17185         }
17186
17187         function geoOrthoFilterDotProduct(dotp, epsilon, lowerThreshold, upperThreshold, allowStraightAngles) {
17188           var val = Math.abs(dotp);
17189
17190           if (val < epsilon) {
17191             return 0; // already orthogonal
17192           } else if (allowStraightAngles && Math.abs(val - 1) < epsilon) {
17193             return 0; // straight angle, which is okay in this case
17194           } else if (val < lowerThreshold || val > upperThreshold) {
17195             return dotp; // can be adjusted
17196           } else {
17197             return null; // ignore vertex
17198           }
17199         }
17200
17201         function geoOrthoCalcScore(points, isClosed, epsilon, threshold) {
17202           var score = 0;
17203           var first = isClosed ? 0 : 1;
17204           var last = isClosed ? points.length : points.length - 1;
17205           var coords = points.map(function (p) {
17206             return p.coord;
17207           });
17208           var lowerThreshold = Math.cos((90 - threshold) * Math.PI / 180);
17209           var upperThreshold = Math.cos(threshold * Math.PI / 180);
17210
17211           for (var i = first; i < last; i++) {
17212             var a = coords[(i - 1 + coords.length) % coords.length];
17213             var origin = coords[i];
17214             var b = coords[(i + 1) % coords.length];
17215             var dotp = geoOrthoFilterDotProduct(geoOrthoNormalizedDotProduct(a, b, origin), epsilon, lowerThreshold, upperThreshold);
17216             if (dotp === null) continue; // ignore vertex
17217
17218             score = score + 2.0 * Math.min(Math.abs(dotp - 1.0), Math.min(Math.abs(dotp), Math.abs(dotp + 1)));
17219           }
17220
17221           return score;
17222         } // returns the maximum angle less than `lessThan` between the actual corner and a 0° or 90° corner
17223
17224         function geoOrthoMaxOffsetAngle(coords, isClosed, lessThan) {
17225           var max = -Infinity;
17226           var first = isClosed ? 0 : 1;
17227           var last = isClosed ? coords.length : coords.length - 1;
17228
17229           for (var i = first; i < last; i++) {
17230             var a = coords[(i - 1 + coords.length) % coords.length];
17231             var origin = coords[i];
17232             var b = coords[(i + 1) % coords.length];
17233             var normalizedDotP = geoOrthoNormalizedDotProduct(a, b, origin);
17234             var angle = Math.acos(Math.abs(normalizedDotP)) * 180 / Math.PI;
17235             if (angle > 45) angle = 90 - angle;
17236             if (angle >= lessThan) continue;
17237             if (angle > max) max = angle;
17238           }
17239
17240           if (max === -Infinity) return null;
17241           return max;
17242         } // similar to geoOrthoCalcScore, but returns quickly if there is something to do
17243
17244         function geoOrthoCanOrthogonalize(coords, isClosed, epsilon, threshold, allowStraightAngles) {
17245           var score = null;
17246           var first = isClosed ? 0 : 1;
17247           var last = isClosed ? coords.length : coords.length - 1;
17248           var lowerThreshold = Math.cos((90 - threshold) * Math.PI / 180);
17249           var upperThreshold = Math.cos(threshold * Math.PI / 180);
17250
17251           for (var i = first; i < last; i++) {
17252             var a = coords[(i - 1 + coords.length) % coords.length];
17253             var origin = coords[i];
17254             var b = coords[(i + 1) % coords.length];
17255             var dotp = geoOrthoFilterDotProduct(geoOrthoNormalizedDotProduct(a, b, origin), epsilon, lowerThreshold, upperThreshold, allowStraightAngles);
17256             if (dotp === null) continue; // ignore vertex
17257
17258             if (Math.abs(dotp) > 0) return 1; // something to do
17259
17260             score = 0; // already square
17261           }
17262
17263           return score;
17264         }
17265
17266         var onFreeze = internalMetadata.onFreeze;
17267
17268         // eslint-disable-next-line es/no-object-freeze -- safe
17269         var $freeze = Object.freeze;
17270         var FAILS_ON_PRIMITIVES = fails(function () { $freeze(1); });
17271
17272         // `Object.freeze` method
17273         // https://tc39.es/ecma262/#sec-object.freeze
17274         _export({ target: 'Object', stat: true, forced: FAILS_ON_PRIMITIVES, sham: !freezing }, {
17275           freeze: function freeze(it) {
17276             return $freeze && isObject$4(it) ? $freeze(onFreeze(it)) : it;
17277           }
17278         });
17279
17280         // Returns true if a and b have the same elements at the same indices.
17281         function utilArrayIdentical(a, b) {
17282           // an array is always identical to itself
17283           if (a === b) return true;
17284           var i = a.length;
17285           if (i !== b.length) return false;
17286
17287           while (i--) {
17288             if (a[i] !== b[i]) return false;
17289           }
17290
17291           return true;
17292         } // http://2ality.com/2015/01/es6-set-operations.html
17293         // Difference (a \ b): create a set that contains those elements of set a that are not in set b.
17294         // This operation is also sometimes called minus (-).
17295         // var a = [1,2,3];
17296         // var b = [4,3,2];
17297         // utilArrayDifference(a, b)
17298         //   [1]
17299         // utilArrayDifference(b, a)
17300         //   [4]
17301
17302         function utilArrayDifference(a, b) {
17303           var other = new Set(b);
17304           return Array.from(new Set(a)).filter(function (v) {
17305             return !other.has(v);
17306           });
17307         } // Intersection (a ∩ b): create a set that contains those elements of set a that are also in set b.
17308         // var a = [1,2,3];
17309         // var b = [4,3,2];
17310         // utilArrayIntersection(a, b)
17311         //   [2,3]
17312
17313         function utilArrayIntersection(a, b) {
17314           var other = new Set(b);
17315           return Array.from(new Set(a)).filter(function (v) {
17316             return other.has(v);
17317           });
17318         } // Union (a ∪ b): create a set that contains the elements of both set a and set b.
17319         // var a = [1,2,3];
17320         // var b = [4,3,2];
17321         // utilArrayUnion(a, b)
17322         //   [1,2,3,4]
17323
17324         function utilArrayUnion(a, b) {
17325           var result = new Set(a);
17326           b.forEach(function (v) {
17327             result.add(v);
17328           });
17329           return Array.from(result);
17330         } // Returns an Array with all the duplicates removed
17331         // var a = [1,1,2,3,3];
17332         // utilArrayUniq(a)
17333         //   [1,2,3]
17334
17335         function utilArrayUniq(a) {
17336           return Array.from(new Set(a));
17337         } // Splits array into chunks of given chunk size
17338         // var a = [1,2,3,4,5,6,7];
17339         // utilArrayChunk(a, 3);
17340         //   [[1,2,3],[4,5,6],[7]];
17341
17342         function utilArrayChunk(a, chunkSize) {
17343           if (!chunkSize || chunkSize < 0) return [a.slice()];
17344           var result = new Array(Math.ceil(a.length / chunkSize));
17345           return Array.from(result, function (item, i) {
17346             return a.slice(i * chunkSize, i * chunkSize + chunkSize);
17347           });
17348         } // Flattens two level array into a single level
17349         // var a = [[1,2,3],[4,5,6],[7]];
17350         // utilArrayFlatten(a);
17351         //   [1,2,3,4,5,6,7];
17352
17353         function utilArrayFlatten(a) {
17354           return a.reduce(function (acc, val) {
17355             return acc.concat(val);
17356           }, []);
17357         } // Groups the items of the Array according to the given key
17358         // `key` can be passed as a property or as a key function
17359         //
17360         // var pets = [
17361         //     { type: 'Dog', name: 'Spot' },
17362         //     { type: 'Cat', name: 'Tiger' },
17363         //     { type: 'Dog', name: 'Rover' },
17364         //     { type: 'Cat', name: 'Leo' }
17365         // ];
17366         //
17367         // utilArrayGroupBy(pets, 'type')
17368         //   {
17369         //     'Dog': [{type: 'Dog', name: 'Spot'}, {type: 'Dog', name: 'Rover'}],
17370         //     'Cat': [{type: 'Cat', name: 'Tiger'}, {type: 'Cat', name: 'Leo'}]
17371         //   }
17372         //
17373         // utilArrayGroupBy(pets, function(item) { return item.name.length; })
17374         //   {
17375         //     3: [{type: 'Cat', name: 'Leo'}],
17376         //     4: [{type: 'Dog', name: 'Spot'}],
17377         //     5: [{type: 'Cat', name: 'Tiger'}, {type: 'Dog', name: 'Rover'}]
17378         //   }
17379
17380         function utilArrayGroupBy(a, key) {
17381           return a.reduce(function (acc, item) {
17382             var group = typeof key === 'function' ? key(item) : item[key];
17383             (acc[group] = acc[group] || []).push(item);
17384             return acc;
17385           }, {});
17386         } // Returns an Array with all the duplicates removed
17387         // where uniqueness determined by the given key
17388         // `key` can be passed as a property or as a key function
17389         //
17390         // var pets = [
17391         //     { type: 'Dog', name: 'Spot' },
17392         //     { type: 'Cat', name: 'Tiger' },
17393         //     { type: 'Dog', name: 'Rover' },
17394         //     { type: 'Cat', name: 'Leo' }
17395         // ];
17396         //
17397         // utilArrayUniqBy(pets, 'type')
17398         //   [
17399         //     { type: 'Dog', name: 'Spot' },
17400         //     { type: 'Cat', name: 'Tiger' }
17401         //   ]
17402         //
17403         // utilArrayUniqBy(pets, function(item) { return item.name.length; })
17404         //   [
17405         //     { type: 'Dog', name: 'Spot' },
17406         //     { type: 'Cat', name: 'Tiger' },
17407         //     { type: 'Cat', name: 'Leo' }
17408         //   }
17409
17410         function utilArrayUniqBy(a, key) {
17411           var seen = new Set();
17412           return a.reduce(function (acc, item) {
17413             var val = typeof key === 'function' ? key(item) : item[key];
17414
17415             if (val && !seen.has(val)) {
17416               seen.add(val);
17417               acc.push(item);
17418             }
17419
17420             return acc;
17421           }, []);
17422         }
17423
17424         // @@match logic
17425         fixRegexpWellKnownSymbolLogic('match', function (MATCH, nativeMatch, maybeCallNative) {
17426           return [
17427             // `String.prototype.match` method
17428             // https://tc39.es/ecma262/#sec-string.prototype.match
17429             function match(regexp) {
17430               var O = requireObjectCoercible(this);
17431               var matcher = regexp == undefined ? undefined : regexp[MATCH];
17432               return matcher !== undefined ? matcher.call(regexp, O) : new RegExp(regexp)[MATCH](String(O));
17433             },
17434             // `RegExp.prototype[@@match]` method
17435             // https://tc39.es/ecma262/#sec-regexp.prototype-@@match
17436             function (string) {
17437               var res = maybeCallNative(nativeMatch, this, string);
17438               if (res.done) return res.value;
17439
17440               var rx = anObject(this);
17441               var S = String(string);
17442
17443               if (!rx.global) return regexpExecAbstract(rx, S);
17444
17445               var fullUnicode = rx.unicode;
17446               rx.lastIndex = 0;
17447               var A = [];
17448               var n = 0;
17449               var result;
17450               while ((result = regexpExecAbstract(rx, S)) !== null) {
17451                 var matchStr = String(result[0]);
17452                 A[n] = matchStr;
17453                 if (matchStr === '') rx.lastIndex = advanceStringIndex(S, toLength(rx.lastIndex), fullUnicode);
17454                 n++;
17455               }
17456               return n === 0 ? null : A;
17457             }
17458           ];
17459         });
17460
17461         var remove$6 = removeDiacritics;
17462         var replacementList = [{
17463           base: ' ',
17464           chars: "\xA0"
17465         }, {
17466           base: '0',
17467           chars: "\u07C0"
17468         }, {
17469           base: 'A',
17470           chars: "\u24B6\uFF21\xC0\xC1\xC2\u1EA6\u1EA4\u1EAA\u1EA8\xC3\u0100\u0102\u1EB0\u1EAE\u1EB4\u1EB2\u0226\u01E0\xC4\u01DE\u1EA2\xC5\u01FA\u01CD\u0200\u0202\u1EA0\u1EAC\u1EB6\u1E00\u0104\u023A\u2C6F"
17471         }, {
17472           base: 'AA',
17473           chars: "\uA732"
17474         }, {
17475           base: 'AE',
17476           chars: "\xC6\u01FC\u01E2"
17477         }, {
17478           base: 'AO',
17479           chars: "\uA734"
17480         }, {
17481           base: 'AU',
17482           chars: "\uA736"
17483         }, {
17484           base: 'AV',
17485           chars: "\uA738\uA73A"
17486         }, {
17487           base: 'AY',
17488           chars: "\uA73C"
17489         }, {
17490           base: 'B',
17491           chars: "\u24B7\uFF22\u1E02\u1E04\u1E06\u0243\u0181"
17492         }, {
17493           base: 'C',
17494           chars: "\u24B8\uFF23\uA73E\u1E08\u0106C\u0108\u010A\u010C\xC7\u0187\u023B"
17495         }, {
17496           base: 'D',
17497           chars: "\u24B9\uFF24\u1E0A\u010E\u1E0C\u1E10\u1E12\u1E0E\u0110\u018A\u0189\u1D05\uA779"
17498         }, {
17499           base: 'Dh',
17500           chars: "\xD0"
17501         }, {
17502           base: 'DZ',
17503           chars: "\u01F1\u01C4"
17504         }, {
17505           base: 'Dz',
17506           chars: "\u01F2\u01C5"
17507         }, {
17508           base: 'E',
17509           chars: "\u025B\u24BA\uFF25\xC8\xC9\xCA\u1EC0\u1EBE\u1EC4\u1EC2\u1EBC\u0112\u1E14\u1E16\u0114\u0116\xCB\u1EBA\u011A\u0204\u0206\u1EB8\u1EC6\u0228\u1E1C\u0118\u1E18\u1E1A\u0190\u018E\u1D07"
17510         }, {
17511           base: 'F',
17512           chars: "\uA77C\u24BB\uFF26\u1E1E\u0191\uA77B"
17513         }, {
17514           base: 'G',
17515           chars: "\u24BC\uFF27\u01F4\u011C\u1E20\u011E\u0120\u01E6\u0122\u01E4\u0193\uA7A0\uA77D\uA77E\u0262"
17516         }, {
17517           base: 'H',
17518           chars: "\u24BD\uFF28\u0124\u1E22\u1E26\u021E\u1E24\u1E28\u1E2A\u0126\u2C67\u2C75\uA78D"
17519         }, {
17520           base: 'I',
17521           chars: "\u24BE\uFF29\xCC\xCD\xCE\u0128\u012A\u012C\u0130\xCF\u1E2E\u1EC8\u01CF\u0208\u020A\u1ECA\u012E\u1E2C\u0197"
17522         }, {
17523           base: 'J',
17524           chars: "\u24BF\uFF2A\u0134\u0248\u0237"
17525         }, {
17526           base: 'K',
17527           chars: "\u24C0\uFF2B\u1E30\u01E8\u1E32\u0136\u1E34\u0198\u2C69\uA740\uA742\uA744\uA7A2"
17528         }, {
17529           base: 'L',
17530           chars: "\u24C1\uFF2C\u013F\u0139\u013D\u1E36\u1E38\u013B\u1E3C\u1E3A\u0141\u023D\u2C62\u2C60\uA748\uA746\uA780"
17531         }, {
17532           base: 'LJ',
17533           chars: "\u01C7"
17534         }, {
17535           base: 'Lj',
17536           chars: "\u01C8"
17537         }, {
17538           base: 'M',
17539           chars: "\u24C2\uFF2D\u1E3E\u1E40\u1E42\u2C6E\u019C\u03FB"
17540         }, {
17541           base: 'N',
17542           chars: "\uA7A4\u0220\u24C3\uFF2E\u01F8\u0143\xD1\u1E44\u0147\u1E46\u0145\u1E4A\u1E48\u019D\uA790\u1D0E"
17543         }, {
17544           base: 'NJ',
17545           chars: "\u01CA"
17546         }, {
17547           base: 'Nj',
17548           chars: "\u01CB"
17549         }, {
17550           base: 'O',
17551           chars: "\u24C4\uFF2F\xD2\xD3\xD4\u1ED2\u1ED0\u1ED6\u1ED4\xD5\u1E4C\u022C\u1E4E\u014C\u1E50\u1E52\u014E\u022E\u0230\xD6\u022A\u1ECE\u0150\u01D1\u020C\u020E\u01A0\u1EDC\u1EDA\u1EE0\u1EDE\u1EE2\u1ECC\u1ED8\u01EA\u01EC\xD8\u01FE\u0186\u019F\uA74A\uA74C"
17552         }, {
17553           base: 'OE',
17554           chars: "\u0152"
17555         }, {
17556           base: 'OI',
17557           chars: "\u01A2"
17558         }, {
17559           base: 'OO',
17560           chars: "\uA74E"
17561         }, {
17562           base: 'OU',
17563           chars: "\u0222"
17564         }, {
17565           base: 'P',
17566           chars: "\u24C5\uFF30\u1E54\u1E56\u01A4\u2C63\uA750\uA752\uA754"
17567         }, {
17568           base: 'Q',
17569           chars: "\u24C6\uFF31\uA756\uA758\u024A"
17570         }, {
17571           base: 'R',
17572           chars: "\u24C7\uFF32\u0154\u1E58\u0158\u0210\u0212\u1E5A\u1E5C\u0156\u1E5E\u024C\u2C64\uA75A\uA7A6\uA782"
17573         }, {
17574           base: 'S',
17575           chars: "\u24C8\uFF33\u1E9E\u015A\u1E64\u015C\u1E60\u0160\u1E66\u1E62\u1E68\u0218\u015E\u2C7E\uA7A8\uA784"
17576         }, {
17577           base: 'T',
17578           chars: "\u24C9\uFF34\u1E6A\u0164\u1E6C\u021A\u0162\u1E70\u1E6E\u0166\u01AC\u01AE\u023E\uA786"
17579         }, {
17580           base: 'Th',
17581           chars: "\xDE"
17582         }, {
17583           base: 'TZ',
17584           chars: "\uA728"
17585         }, {
17586           base: 'U',
17587           chars: "\u24CA\uFF35\xD9\xDA\xDB\u0168\u1E78\u016A\u1E7A\u016C\xDC\u01DB\u01D7\u01D5\u01D9\u1EE6\u016E\u0170\u01D3\u0214\u0216\u01AF\u1EEA\u1EE8\u1EEE\u1EEC\u1EF0\u1EE4\u1E72\u0172\u1E76\u1E74\u0244"
17588         }, {
17589           base: 'V',
17590           chars: "\u24CB\uFF36\u1E7C\u1E7E\u01B2\uA75E\u0245"
17591         }, {
17592           base: 'VY',
17593           chars: "\uA760"
17594         }, {
17595           base: 'W',
17596           chars: "\u24CC\uFF37\u1E80\u1E82\u0174\u1E86\u1E84\u1E88\u2C72"
17597         }, {
17598           base: 'X',
17599           chars: "\u24CD\uFF38\u1E8A\u1E8C"
17600         }, {
17601           base: 'Y',
17602           chars: "\u24CE\uFF39\u1EF2\xDD\u0176\u1EF8\u0232\u1E8E\u0178\u1EF6\u1EF4\u01B3\u024E\u1EFE"
17603         }, {
17604           base: 'Z',
17605           chars: "\u24CF\uFF3A\u0179\u1E90\u017B\u017D\u1E92\u1E94\u01B5\u0224\u2C7F\u2C6B\uA762"
17606         }, {
17607           base: 'a',
17608           chars: "\u24D0\uFF41\u1E9A\xE0\xE1\xE2\u1EA7\u1EA5\u1EAB\u1EA9\xE3\u0101\u0103\u1EB1\u1EAF\u1EB5\u1EB3\u0227\u01E1\xE4\u01DF\u1EA3\xE5\u01FB\u01CE\u0201\u0203\u1EA1\u1EAD\u1EB7\u1E01\u0105\u2C65\u0250\u0251"
17609         }, {
17610           base: 'aa',
17611           chars: "\uA733"
17612         }, {
17613           base: 'ae',
17614           chars: "\xE6\u01FD\u01E3"
17615         }, {
17616           base: 'ao',
17617           chars: "\uA735"
17618         }, {
17619           base: 'au',
17620           chars: "\uA737"
17621         }, {
17622           base: 'av',
17623           chars: "\uA739\uA73B"
17624         }, {
17625           base: 'ay',
17626           chars: "\uA73D"
17627         }, {
17628           base: 'b',
17629           chars: "\u24D1\uFF42\u1E03\u1E05\u1E07\u0180\u0183\u0253\u0182"
17630         }, {
17631           base: 'c',
17632           chars: "\uFF43\u24D2\u0107\u0109\u010B\u010D\xE7\u1E09\u0188\u023C\uA73F\u2184"
17633         }, {
17634           base: 'd',
17635           chars: "\u24D3\uFF44\u1E0B\u010F\u1E0D\u1E11\u1E13\u1E0F\u0111\u018C\u0256\u0257\u018B\u13E7\u0501\uA7AA"
17636         }, {
17637           base: 'dh',
17638           chars: "\xF0"
17639         }, {
17640           base: 'dz',
17641           chars: "\u01F3\u01C6"
17642         }, {
17643           base: 'e',
17644           chars: "\u24D4\uFF45\xE8\xE9\xEA\u1EC1\u1EBF\u1EC5\u1EC3\u1EBD\u0113\u1E15\u1E17\u0115\u0117\xEB\u1EBB\u011B\u0205\u0207\u1EB9\u1EC7\u0229\u1E1D\u0119\u1E19\u1E1B\u0247\u01DD"
17645         }, {
17646           base: 'f',
17647           chars: "\u24D5\uFF46\u1E1F\u0192"
17648         }, {
17649           base: 'ff',
17650           chars: "\uFB00"
17651         }, {
17652           base: 'fi',
17653           chars: "\uFB01"
17654         }, {
17655           base: 'fl',
17656           chars: "\uFB02"
17657         }, {
17658           base: 'ffi',
17659           chars: "\uFB03"
17660         }, {
17661           base: 'ffl',
17662           chars: "\uFB04"
17663         }, {
17664           base: 'g',
17665           chars: "\u24D6\uFF47\u01F5\u011D\u1E21\u011F\u0121\u01E7\u0123\u01E5\u0260\uA7A1\uA77F\u1D79"
17666         }, {
17667           base: 'h',
17668           chars: "\u24D7\uFF48\u0125\u1E23\u1E27\u021F\u1E25\u1E29\u1E2B\u1E96\u0127\u2C68\u2C76\u0265"
17669         }, {
17670           base: 'hv',
17671           chars: "\u0195"
17672         }, {
17673           base: 'i',
17674           chars: "\u24D8\uFF49\xEC\xED\xEE\u0129\u012B\u012D\xEF\u1E2F\u1EC9\u01D0\u0209\u020B\u1ECB\u012F\u1E2D\u0268\u0131"
17675         }, {
17676           base: 'j',
17677           chars: "\u24D9\uFF4A\u0135\u01F0\u0249"
17678         }, {
17679           base: 'k',
17680           chars: "\u24DA\uFF4B\u1E31\u01E9\u1E33\u0137\u1E35\u0199\u2C6A\uA741\uA743\uA745\uA7A3"
17681         }, {
17682           base: 'l',
17683           chars: "\u24DB\uFF4C\u0140\u013A\u013E\u1E37\u1E39\u013C\u1E3D\u1E3B\u017F\u0142\u019A\u026B\u2C61\uA749\uA781\uA747\u026D"
17684         }, {
17685           base: 'lj',
17686           chars: "\u01C9"
17687         }, {
17688           base: 'm',
17689           chars: "\u24DC\uFF4D\u1E3F\u1E41\u1E43\u0271\u026F"
17690         }, {
17691           base: 'n',
17692           chars: "\u24DD\uFF4E\u01F9\u0144\xF1\u1E45\u0148\u1E47\u0146\u1E4B\u1E49\u019E\u0272\u0149\uA791\uA7A5\u043B\u0509"
17693         }, {
17694           base: 'nj',
17695           chars: "\u01CC"
17696         }, {
17697           base: 'o',
17698           chars: "\u24DE\uFF4F\xF2\xF3\xF4\u1ED3\u1ED1\u1ED7\u1ED5\xF5\u1E4D\u022D\u1E4F\u014D\u1E51\u1E53\u014F\u022F\u0231\xF6\u022B\u1ECF\u0151\u01D2\u020D\u020F\u01A1\u1EDD\u1EDB\u1EE1\u1EDF\u1EE3\u1ECD\u1ED9\u01EB\u01ED\xF8\u01FF\uA74B\uA74D\u0275\u0254\u1D11"
17699         }, {
17700           base: 'oe',
17701           chars: "\u0153"
17702         }, {
17703           base: 'oi',
17704           chars: "\u01A3"
17705         }, {
17706           base: 'oo',
17707           chars: "\uA74F"
17708         }, {
17709           base: 'ou',
17710           chars: "\u0223"
17711         }, {
17712           base: 'p',
17713           chars: "\u24DF\uFF50\u1E55\u1E57\u01A5\u1D7D\uA751\uA753\uA755\u03C1"
17714         }, {
17715           base: 'q',
17716           chars: "\u24E0\uFF51\u024B\uA757\uA759"
17717         }, {
17718           base: 'r',
17719           chars: "\u24E1\uFF52\u0155\u1E59\u0159\u0211\u0213\u1E5B\u1E5D\u0157\u1E5F\u024D\u027D\uA75B\uA7A7\uA783"
17720         }, {
17721           base: 's',
17722           chars: "\u24E2\uFF53\u015B\u1E65\u015D\u1E61\u0161\u1E67\u1E63\u1E69\u0219\u015F\u023F\uA7A9\uA785\u1E9B\u0282"
17723         }, {
17724           base: 'ss',
17725           chars: "\xDF"
17726         }, {
17727           base: 't',
17728           chars: "\u24E3\uFF54\u1E6B\u1E97\u0165\u1E6D\u021B\u0163\u1E71\u1E6F\u0167\u01AD\u0288\u2C66\uA787"
17729         }, {
17730           base: 'th',
17731           chars: "\xFE"
17732         }, {
17733           base: 'tz',
17734           chars: "\uA729"
17735         }, {
17736           base: 'u',
17737           chars: "\u24E4\uFF55\xF9\xFA\xFB\u0169\u1E79\u016B\u1E7B\u016D\xFC\u01DC\u01D8\u01D6\u01DA\u1EE7\u016F\u0171\u01D4\u0215\u0217\u01B0\u1EEB\u1EE9\u1EEF\u1EED\u1EF1\u1EE5\u1E73\u0173\u1E77\u1E75\u0289"
17738         }, {
17739           base: 'v',
17740           chars: "\u24E5\uFF56\u1E7D\u1E7F\u028B\uA75F\u028C"
17741         }, {
17742           base: 'vy',
17743           chars: "\uA761"
17744         }, {
17745           base: 'w',
17746           chars: "\u24E6\uFF57\u1E81\u1E83\u0175\u1E87\u1E85\u1E98\u1E89\u2C73"
17747         }, {
17748           base: 'x',
17749           chars: "\u24E7\uFF58\u1E8B\u1E8D"
17750         }, {
17751           base: 'y',
17752           chars: "\u24E8\uFF59\u1EF3\xFD\u0177\u1EF9\u0233\u1E8F\xFF\u1EF7\u1E99\u1EF5\u01B4\u024F\u1EFF"
17753         }, {
17754           base: 'z',
17755           chars: "\u24E9\uFF5A\u017A\u1E91\u017C\u017E\u1E93\u1E95\u01B6\u0225\u0240\u2C6C\uA763"
17756         }];
17757         var diacriticsMap = {};
17758
17759         for (var i$1 = 0; i$1 < replacementList.length; i$1 += 1) {
17760           var chars = replacementList[i$1].chars;
17761
17762           for (var j$1 = 0; j$1 < chars.length; j$1 += 1) {
17763             diacriticsMap[chars[j$1]] = replacementList[i$1].base;
17764           }
17765         }
17766
17767         function removeDiacritics(str) {
17768           return str.replace(/[^\u0000-\u007e]/g, function (c) {
17769             return diacriticsMap[c] || c;
17770           });
17771         }
17772
17773         var replacementList_1 = replacementList;
17774         var diacriticsMap_1 = diacriticsMap;
17775         var diacritics = {
17776           remove: remove$6,
17777           replacementList: replacementList_1,
17778           diacriticsMap: diacriticsMap_1
17779         };
17780
17781         var arabicBlocks = [[0x0600, 0x06FF], [0x0750, 0x077F], [0x08A0, 0x08FF], [0xFB50, 0xFDFF], [0xFE70, 0xFEFF], [0x10E60, 0x10E7F], [0x1EC70, 0x1ECBF], [0x1EE00, 0x1EEFF] // Mathematical Alphabetic symbols https://www.unicode.org/charts/PDF/U1EE00.pdf
17782         ];
17783
17784         function isArabic(_char) {
17785           if (_char.length > 1) {
17786             // allow the newer chars?
17787             throw new Error('isArabic works on only one-character strings');
17788           }
17789
17790           var code = _char.charCodeAt(0);
17791
17792           for (var i = 0; i < arabicBlocks.length; i++) {
17793             var block = arabicBlocks[i];
17794
17795             if (code >= block[0] && code <= block[1]) {
17796               return true;
17797             }
17798           }
17799
17800           return false;
17801         }
17802
17803         var isArabic_2 = isArabic;
17804
17805         function isMath(_char2) {
17806           if (_char2.length > 2) {
17807             // allow the newer chars?
17808             throw new Error('isMath works on only one-character strings');
17809           }
17810
17811           var code = _char2.charCodeAt(0);
17812
17813           return code >= 0x660 && code <= 0x66C || code >= 0x6F0 && code <= 0x6F9;
17814         }
17815
17816         var isMath_1 = isMath;
17817         var isArabic_1 = /*#__PURE__*/Object.defineProperty({
17818           isArabic: isArabic_2,
17819           isMath: isMath_1
17820         }, '__esModule', {
17821           value: true
17822         });
17823
17824         var arabicReference = {
17825           "alef": {
17826             "normal": ["\u0627"],
17827             "madda_above": {
17828               "normal": ["\u0627\u0653", "\u0622"],
17829               "isolated": "\uFE81",
17830               "final": "\uFE82"
17831             },
17832             "hamza_above": {
17833               "normal": ["\u0627\u0654", "\u0623"],
17834               "isolated": "\uFE83",
17835               "final": "\uFE84"
17836             },
17837             "hamza_below": {
17838               "normal": ["\u0627\u0655", "\u0625"],
17839               "isolated": "\uFE87",
17840               "final": "\uFE88"
17841             },
17842             "wasla": {
17843               "normal": "\u0671",
17844               "isolated": "\uFB50",
17845               "final": "\uFB51"
17846             },
17847             "wavy_hamza_above": ["\u0672"],
17848             "wavy_hamza_below": ["\u0627\u065F", "\u0673"],
17849             "high_hamza": ["\u0675", "\u0627\u0674"],
17850             "indic_two_above": ["\u0773"],
17851             "indic_three_above": ["\u0774"],
17852             "fathatan": {
17853               "normal": ["\u0627\u064B"],
17854               "final": "\uFD3C",
17855               "isolated": "\uFD3D"
17856             },
17857             "isolated": "\uFE8D",
17858             "final": "\uFE8E"
17859           },
17860           "beh": {
17861             "normal": ["\u0628"],
17862             "dotless": ["\u066E"],
17863             "three_dots_horizontally_below": ["\u0750"],
17864             "dot_below_three_dots_above": ["\u0751"],
17865             "three_dots_pointing_upwards_below": ["\u0752"],
17866             "three_dots_pointing_upwards_below_two_dots_above": ["\u0753"],
17867             "two_dots_below_dot_above": ["\u0754"],
17868             "inverted_small_v_below": ["\u0755"],
17869             "small_v": ["\u0756"],
17870             "small_v_below": ["\u08A0"],
17871             "hamza_above": ["\u08A1"],
17872             "small_meem_above": ["\u08B6"],
17873             "isolated": "\uFE8F",
17874             "final": "\uFE90",
17875             "initial": "\uFE91",
17876             "medial": "\uFE92"
17877           },
17878           "teh marbuta": {
17879             "normal": ["\u0629"],
17880             "isolated": "\uFE93",
17881             "final": "\uFE94"
17882           },
17883           "teh": {
17884             "normal": ["\u062A"],
17885             "ring": ["\u067C"],
17886             "three_dots_above_downwards": ["\u067D"],
17887             "small_teh_above": ["\u08B8"],
17888             "isolated": "\uFE95",
17889             "final": "\uFE96",
17890             "initial": "\uFE97",
17891             "medial": "\uFE98"
17892           },
17893           "theh": {
17894             "normal": ["\u062B"],
17895             "isolated": "\uFE99",
17896             "final": "\uFE9A",
17897             "initial": "\uFE9B",
17898             "medial": "\uFE9C"
17899           },
17900           "jeem": {
17901             "normal": ["\u062C"],
17902             "two_dots_above": ["\u08A2"],
17903             "isolated": "\uFE9D",
17904             "final": "\uFE9E",
17905             "initial": "\uFE9F",
17906             "medial": "\uFEA0"
17907           },
17908           "hah": {
17909             "normal": ["\u062D"],
17910             "hamza_above": ["\u0681"],
17911             "two_dots_vertical_above": ["\u0682"],
17912             "three_dots_above": ["\u0685"],
17913             "two_dots_above": ["\u0757"],
17914             "three_dots_pointing_upwards_below": ["\u0758"],
17915             "small_tah_below": ["\u076E"],
17916             "small_tah_two_dots": ["\u076F"],
17917             "small_tah_above": ["\u0772"],
17918             "indic_four_below": ["\u077C"],
17919             "isolated": "\uFEA1",
17920             "final": "\uFEA2",
17921             "initial": "\uFEA3",
17922             "medial": "\uFEA4"
17923           },
17924           "khah": {
17925             "normal": ["\u062E"],
17926             "isolated": "\uFEA5",
17927             "final": "\uFEA6",
17928             "initial": "\uFEA7",
17929             "medial": "\uFEA8"
17930           },
17931           "dal": {
17932             "normal": ["\u062F"],
17933             "ring": ["\u0689"],
17934             "dot_below": ["\u068A"],
17935             "dot_below_small_tah": ["\u068B"],
17936             "three_dots_above_downwards": ["\u068F"],
17937             "four_dots_above": ["\u0690"],
17938             "inverted_v": ["\u06EE"],
17939             "two_dots_vertically_below_small_tah": ["\u0759"],
17940             "inverted_small_v_below": ["\u075A"],
17941             "three_dots_below": ["\u08AE"],
17942             "isolated": "\uFEA9",
17943             "final": "\uFEAA"
17944           },
17945           "thal": {
17946             "normal": ["\u0630"],
17947             "isolated": "\uFEAB",
17948             "final": "\uFEAC"
17949           },
17950           "reh": {
17951             "normal": ["\u0631"],
17952             "small_v": ["\u0692"],
17953             "ring": ["\u0693"],
17954             "dot_below": ["\u0694"],
17955             "small_v_below": ["\u0695"],
17956             "dot_below_dot_above": ["\u0696"],
17957             "two_dots_above": ["\u0697"],
17958             "four_dots_above": ["\u0699"],
17959             "inverted_v": ["\u06EF"],
17960             "stroke": ["\u075B"],
17961             "two_dots_vertically_above": ["\u076B"],
17962             "hamza_above": ["\u076C"],
17963             "small_tah_two_dots": ["\u0771"],
17964             "loop": ["\u08AA"],
17965             "small_noon_above": ["\u08B9"],
17966             "isolated": "\uFEAD",
17967             "final": "\uFEAE"
17968           },
17969           "zain": {
17970             "normal": ["\u0632"],
17971             "inverted_v_above": ["\u08B2"],
17972             "isolated": "\uFEAF",
17973             "final": "\uFEB0"
17974           },
17975           "seen": {
17976             "normal": ["\u0633"],
17977             "dot_below_dot_above": ["\u069A"],
17978             "three_dots_below": ["\u069B"],
17979             "three_dots_below_three_dots_above": ["\u069C"],
17980             "four_dots_above": ["\u075C"],
17981             "two_dots_vertically_above": ["\u076D"],
17982             "small_tah_two_dots": ["\u0770"],
17983             "indic_four_above": ["\u077D"],
17984             "inverted_v": ["\u077E"],
17985             "isolated": "\uFEB1",
17986             "final": "\uFEB2",
17987             "initial": "\uFEB3",
17988             "medial": "\uFEB4"
17989           },
17990           "sheen": {
17991             "normal": ["\u0634"],
17992             "dot_below": ["\u06FA"],
17993             "isolated": "\uFEB5",
17994             "final": "\uFEB6",
17995             "initial": "\uFEB7",
17996             "medial": "\uFEB8"
17997           },
17998           "sad": {
17999             "normal": ["\u0635"],
18000             "two_dots_below": ["\u069D"],
18001             "three_dots_above": ["\u069E"],
18002             "three_dots_below": ["\u08AF"],
18003             "isolated": "\uFEB9",
18004             "final": "\uFEBA",
18005             "initial": "\uFEBB",
18006             "medial": "\uFEBC"
18007           },
18008           "dad": {
18009             "normal": ["\u0636"],
18010             "dot_below": ["\u06FB"],
18011             "isolated": "\uFEBD",
18012             "final": "\uFEBE",
18013             "initial": "\uFEBF",
18014             "medial": "\uFEC0"
18015           },
18016           "tah": {
18017             "normal": ["\u0637"],
18018             "three_dots_above": ["\u069F"],
18019             "two_dots_above": ["\u08A3"],
18020             "isolated": "\uFEC1",
18021             "final": "\uFEC2",
18022             "initial": "\uFEC3",
18023             "medial": "\uFEC4"
18024           },
18025           "zah": {
18026             "normal": ["\u0638"],
18027             "isolated": "\uFEC5",
18028             "final": "\uFEC6",
18029             "initial": "\uFEC7",
18030             "medial": "\uFEC8"
18031           },
18032           "ain": {
18033             "normal": ["\u0639"],
18034             "three_dots_above": ["\u06A0"],
18035             "two_dots_above": ["\u075D"],
18036             "three_dots_pointing_downwards_above": ["\u075E"],
18037             "two_dots_vertically_above": ["\u075F"],
18038             "three_dots_below": ["\u08B3"],
18039             "isolated": "\uFEC9",
18040             "final": "\uFECA",
18041             "initial": "\uFECB",
18042             "medial": "\uFECC"
18043           },
18044           "ghain": {
18045             "normal": ["\u063A"],
18046             "dot_below": ["\u06FC"],
18047             "isolated": "\uFECD",
18048             "final": "\uFECE",
18049             "initial": "\uFECF",
18050             "medial": "\uFED0"
18051           },
18052           "feh": {
18053             "normal": ["\u0641"],
18054             "dotless": ["\u06A1"],
18055             "dot_moved_below": ["\u06A2"],
18056             "dot_below": ["\u06A3"],
18057             "three_dots_below": ["\u06A5"],
18058             "two_dots_below": ["\u0760"],
18059             "three_dots_pointing_upwards_below": ["\u0761"],
18060             "dot_below_three_dots_above": ["\u08A4"],
18061             "isolated": "\uFED1",
18062             "final": "\uFED2",
18063             "initial": "\uFED3",
18064             "medial": "\uFED4"
18065           },
18066           "qaf": {
18067             "normal": ["\u0642"],
18068             "dotless": ["\u066F"],
18069             "dot_above": ["\u06A7"],
18070             "three_dots_above": ["\u06A8"],
18071             "dot_below": ["\u08A5"],
18072             "isolated": "\uFED5",
18073             "final": "\uFED6",
18074             "initial": "\uFED7",
18075             "medial": "\uFED8"
18076           },
18077           "kaf": {
18078             "normal": ["\u0643"],
18079             "swash": ["\u06AA"],
18080             "ring": ["\u06AB"],
18081             "dot_above": ["\u06AC"],
18082             "three_dots_below": ["\u06AE"],
18083             "two_dots_above": ["\u077F"],
18084             "dot_below": ["\u08B4"],
18085             "isolated": "\uFED9",
18086             "final": "\uFEDA",
18087             "initial": "\uFEDB",
18088             "medial": "\uFEDC"
18089           },
18090           "lam": {
18091             "normal": ["\u0644"],
18092             "small_v": ["\u06B5"],
18093             "dot_above": ["\u06B6"],
18094             "three_dots_above": ["\u06B7"],
18095             "three_dots_below": ["\u06B8"],
18096             "bar": ["\u076A"],
18097             "double_bar": ["\u08A6"],
18098             "isolated": "\uFEDD",
18099             "final": "\uFEDE",
18100             "initial": "\uFEDF",
18101             "medial": "\uFEE0"
18102           },
18103           "meem": {
18104             "normal": ["\u0645"],
18105             "dot_above": ["\u0765"],
18106             "dot_below": ["\u0766"],
18107             "three_dots_above": ["\u08A7"],
18108             "isolated": "\uFEE1",
18109             "final": "\uFEE2",
18110             "initial": "\uFEE3",
18111             "medial": "\uFEE4"
18112           },
18113           "noon": {
18114             "normal": ["\u0646"],
18115             "dot_below": ["\u06B9"],
18116             "ring": ["\u06BC"],
18117             "three_dots_above": ["\u06BD"],
18118             "two_dots_below": ["\u0767"],
18119             "small_tah": ["\u0768"],
18120             "small_v": ["\u0769"],
18121             "isolated": "\uFEE5",
18122             "final": "\uFEE6",
18123             "initial": "\uFEE7",
18124             "medial": "\uFEE8"
18125           },
18126           "heh": {
18127             "normal": ["\u0647"],
18128             "isolated": "\uFEE9",
18129             "final": "\uFEEA",
18130             "initial": "\uFEEB",
18131             "medial": "\uFEEC"
18132           },
18133           "waw": {
18134             "normal": ["\u0648"],
18135             "hamza_above": {
18136               "normal": ["\u0624", "\u0648\u0654"],
18137               "isolated": "\uFE85",
18138               "final": "\uFE86"
18139             },
18140             "high_hamza": ["\u0676", "\u0648\u0674"],
18141             "ring": ["\u06C4"],
18142             "two_dots_above": ["\u06CA"],
18143             "dot_above": ["\u06CF"],
18144             "indic_two_above": ["\u0778"],
18145             "indic_three_above": ["\u0779"],
18146             "dot_within": ["\u08AB"],
18147             "isolated": "\uFEED",
18148             "final": "\uFEEE"
18149           },
18150           "alef_maksura": {
18151             "normal": ["\u0649"],
18152             "hamza_above": ["\u0626", "\u064A\u0654"],
18153             "initial": "\uFBE8",
18154             "medial": "\uFBE9",
18155             "isolated": "\uFEEF",
18156             "final": "\uFEF0"
18157           },
18158           "yeh": {
18159             "normal": ["\u064A"],
18160             "hamza_above": {
18161               "normal": ["\u0626", "\u0649\u0654"],
18162               "isolated": "\uFE89",
18163               "final": "\uFE8A",
18164               "initial": "\uFE8B",
18165               "medial": "\uFE8C"
18166             },
18167             "two_dots_below_hamza_above": ["\u08A8"],
18168             "high_hamza": ["\u0678", "\u064A\u0674"],
18169             "tail": ["\u06CD"],
18170             "small_v": ["\u06CE"],
18171             "three_dots_below": ["\u06D1"],
18172             "two_dots_below_dot_above": ["\u08A9"],
18173             "two_dots_below_small_noon_above": ["\u08BA"],
18174             "isolated": "\uFEF1",
18175             "final": "\uFEF2",
18176             "initial": "\uFEF3",
18177             "medial": "\uFEF4"
18178           },
18179           "tteh": {
18180             "normal": ["\u0679"],
18181             "isolated": "\uFB66",
18182             "final": "\uFB67",
18183             "initial": "\uFB68",
18184             "medial": "\uFB69"
18185           },
18186           "tteheh": {
18187             "normal": ["\u067A"],
18188             "isolated": "\uFB5E",
18189             "final": "\uFB5F",
18190             "initial": "\uFB60",
18191             "medial": "\uFB61"
18192           },
18193           "beeh": {
18194             "normal": ["\u067B"],
18195             "isolated": "\uFB52",
18196             "final": "\uFB53",
18197             "initial": "\uFB54",
18198             "medial": "\uFB55"
18199           },
18200           "peh": {
18201             "normal": ["\u067E"],
18202             "small_meem_above": ["\u08B7"],
18203             "isolated": "\uFB56",
18204             "final": "\uFB57",
18205             "initial": "\uFB58",
18206             "medial": "\uFB59"
18207           },
18208           "teheh": {
18209             "normal": ["\u067F"],
18210             "isolated": "\uFB62",
18211             "final": "\uFB63",
18212             "initial": "\uFB64",
18213             "medial": "\uFB65"
18214           },
18215           "beheh": {
18216             "normal": ["\u0680"],
18217             "isolated": "\uFB5A",
18218             "final": "\uFB5B",
18219             "initial": "\uFB5C",
18220             "medial": "\uFB5D"
18221           },
18222           "nyeh": {
18223             "normal": ["\u0683"],
18224             "isolated": "\uFB76",
18225             "final": "\uFB77",
18226             "initial": "\uFB78",
18227             "medial": "\uFB79"
18228           },
18229           "dyeh": {
18230             "normal": ["\u0684"],
18231             "isolated": "\uFB72",
18232             "final": "\uFB73",
18233             "initial": "\uFB74",
18234             "medial": "\uFB75"
18235           },
18236           "tcheh": {
18237             "normal": ["\u0686"],
18238             "dot_above": ["\u06BF"],
18239             "isolated": "\uFB7A",
18240             "final": "\uFB7B",
18241             "initial": "\uFB7C",
18242             "medial": "\uFB7D"
18243           },
18244           "tcheheh": {
18245             "normal": ["\u0687"],
18246             "isolated": "\uFB7E",
18247             "final": "\uFB7F",
18248             "initial": "\uFB80",
18249             "medial": "\uFB81"
18250           },
18251           "ddal": {
18252             "normal": ["\u0688"],
18253             "isolated": "\uFB88",
18254             "final": "\uFB89"
18255           },
18256           "dahal": {
18257             "normal": ["\u068C"],
18258             "isolated": "\uFB84",
18259             "final": "\uFB85"
18260           },
18261           "ddahal": {
18262             "normal": ["\u068D"],
18263             "isolated": "\uFB82",
18264             "final": "\uFB83"
18265           },
18266           "dul": {
18267             "normal": ["\u068F", "\u068E"],
18268             "isolated": "\uFB86",
18269             "final": "\uFB87"
18270           },
18271           "rreh": {
18272             "normal": ["\u0691"],
18273             "isolated": "\uFB8C",
18274             "final": "\uFB8D"
18275           },
18276           "jeh": {
18277             "normal": ["\u0698"],
18278             "isolated": "\uFB8A",
18279             "final": "\uFB8B"
18280           },
18281           "veh": {
18282             "normal": ["\u06A4"],
18283             "isolated": "\uFB6A",
18284             "final": "\uFB6B",
18285             "initial": "\uFB6C",
18286             "medial": "\uFB6D"
18287           },
18288           "peheh": {
18289             "normal": ["\u06A6"],
18290             "isolated": "\uFB6E",
18291             "final": "\uFB6F",
18292             "initial": "\uFB70",
18293             "medial": "\uFB71"
18294           },
18295           "keheh": {
18296             "normal": ["\u06A9"],
18297             "dot_above": ["\u0762"],
18298             "three_dots_above": ["\u0763"],
18299             "three_dots_pointing_upwards_below": ["\u0764"],
18300             "isolated": "\uFB8E",
18301             "final": "\uFB8F",
18302             "initial": "\uFB90",
18303             "medial": "\uFB91"
18304           },
18305           "ng": {
18306             "normal": ["\u06AD"],
18307             "isolated": "\uFBD3",
18308             "final": "\uFBD4",
18309             "initial": "\uFBD5",
18310             "medial": "\uFBD6"
18311           },
18312           "gaf": {
18313             "normal": ["\u06AF"],
18314             "ring": ["\u06B0"],
18315             "two_dots_below": ["\u06B2"],
18316             "three_dots_above": ["\u06B4"],
18317             "inverted_stroke": ["\u08B0"],
18318             "isolated": "\uFB92",
18319             "final": "\uFB93",
18320             "initial": "\uFB94",
18321             "medial": "\uFB95"
18322           },
18323           "ngoeh": {
18324             "normal": ["\u06B1"],
18325             "isolated": "\uFB9A",
18326             "final": "\uFB9B",
18327             "initial": "\uFB9C",
18328             "medial": "\uFB9D"
18329           },
18330           "gueh": {
18331             "normal": ["\u06B3"],
18332             "isolated": "\uFB96",
18333             "final": "\uFB97",
18334             "initial": "\uFB98",
18335             "medial": "\uFB99"
18336           },
18337           "noon ghunna": {
18338             "normal": ["\u06BA"],
18339             "isolated": "\uFB9E",
18340             "final": "\uFB9F"
18341           },
18342           "rnoon": {
18343             "normal": ["\u06BB"],
18344             "isolated": "\uFBA0",
18345             "final": "\uFBA1",
18346             "initial": "\uFBA2",
18347             "medial": "\uFBA3"
18348           },
18349           "heh doachashmee": {
18350             "normal": ["\u06BE"],
18351             "isolated": "\uFBAA",
18352             "final": "\uFBAB",
18353             "initial": "\uFBAC",
18354             "medial": "\uFBAD"
18355           },
18356           "heh goal": {
18357             "normal": ["\u06C1"],
18358             "hamza_above": ["\u06C1\u0654", "\u06C2"],
18359             "isolated": "\uFBA6",
18360             "final": "\uFBA7",
18361             "initial": "\uFBA8",
18362             "medial": "\uFBA9"
18363           },
18364           "teh marbuta goal": {
18365             "normal": ["\u06C3"]
18366           },
18367           "kirghiz oe": {
18368             "normal": ["\u06C5"],
18369             "isolated": "\uFBE0",
18370             "final": "\uFBE1"
18371           },
18372           "oe": {
18373             "normal": ["\u06C6"],
18374             "isolated": "\uFBD9",
18375             "final": "\uFBDA"
18376           },
18377           "u": {
18378             "normal": ["\u06C7"],
18379             "hamza_above": {
18380               "normal": ["\u0677", "\u06C7\u0674"],
18381               "isolated": "\uFBDD"
18382             },
18383             "isolated": "\uFBD7",
18384             "final": "\uFBD8"
18385           },
18386           "yu": {
18387             "normal": ["\u06C8"],
18388             "isolated": "\uFBDB",
18389             "final": "\uFBDC"
18390           },
18391           "kirghiz yu": {
18392             "normal": ["\u06C9"],
18393             "isolated": "\uFBE2",
18394             "final": "\uFBE3"
18395           },
18396           "ve": {
18397             "normal": ["\u06CB"],
18398             "isolated": "\uFBDE",
18399             "final": "\uFBDF"
18400           },
18401           "farsi yeh": {
18402             "normal": ["\u06CC"],
18403             "indic_two_above": ["\u0775"],
18404             "indic_three_above": ["\u0776"],
18405             "indic_four_above": ["\u0777"],
18406             "isolated": "\uFBFC",
18407             "final": "\uFBFD",
18408             "initial": "\uFBFE",
18409             "medial": "\uFBFF"
18410           },
18411           "e": {
18412             "normal": ["\u06D0"],
18413             "isolated": "\uFBE4",
18414             "final": "\uFBE5",
18415             "initial": "\uFBE6",
18416             "medial": "\uFBE7"
18417           },
18418           "yeh barree": {
18419             "normal": ["\u06D2"],
18420             "hamza_above": {
18421               "normal": ["\u06D2\u0654", "\u06D3"],
18422               "isolated": "\uFBB0",
18423               "final": "\uFBB1"
18424             },
18425             "indic_two_above": ["\u077A"],
18426             "indic_three_above": ["\u077B"],
18427             "isolated": "\uFBAE",
18428             "final": "\uFBAF"
18429           },
18430           "ae": {
18431             "normal": ["\u06D5"],
18432             "isolated": "\u06D5",
18433             "final": "\uFEEA",
18434             "yeh_above": {
18435               "normal": ["\u06C0", "\u06D5\u0654"],
18436               "isolated": "\uFBA4",
18437               "final": "\uFBA5"
18438             }
18439           },
18440           "rohingya yeh": {
18441             "normal": ["\u08AC"]
18442           },
18443           "low alef": {
18444             "normal": ["\u08AD"]
18445           },
18446           "straight waw": {
18447             "normal": ["\u08B1"]
18448           },
18449           "african feh": {
18450             "normal": ["\u08BB"]
18451           },
18452           "african qaf": {
18453             "normal": ["\u08BC"]
18454           },
18455           "african noon": {
18456             "normal": ["\u08BD"]
18457           }
18458         };
18459         var _default$3 = arabicReference;
18460         var unicodeArabic = /*#__PURE__*/Object.defineProperty({
18461           "default": _default$3
18462         }, '__esModule', {
18463           value: true
18464         });
18465
18466         var ligatureReference = {
18467           "\u0626\u0627": {
18468             "isolated": "\uFBEA",
18469             "final": "\uFBEB"
18470           },
18471           "\u0626\u06D5": {
18472             "isolated": "\uFBEC",
18473             "final": "\uFBED"
18474           },
18475           "\u0626\u0648": {
18476             "isolated": "\uFBEE",
18477             "final": "\uFBEF"
18478           },
18479           "\u0626\u06C7": {
18480             "isolated": "\uFBF0",
18481             "final": "\uFBF1"
18482           },
18483           "\u0626\u06C6": {
18484             "isolated": "\uFBF2",
18485             "final": "\uFBF3"
18486           },
18487           "\u0626\u06C8": {
18488             "isolated": "\uFBF4",
18489             "final": "\uFBF5"
18490           },
18491           "\u0626\u06D0": {
18492             "isolated": "\uFBF6",
18493             "final": "\uFBF7",
18494             "initial": "\uFBF8"
18495           },
18496           "\u0626\u0649": {
18497             "uighur_kirghiz": {
18498               "isolated": "\uFBF9",
18499               "final": "\uFBFA",
18500               "initial": "\uFBFB"
18501             },
18502             "isolated": "\uFC03",
18503             "final": "\uFC68"
18504           },
18505           "\u0626\u062C": {
18506             "isolated": "\uFC00",
18507             "initial": "\uFC97"
18508           },
18509           "\u0626\u062D": {
18510             "isolated": "\uFC01",
18511             "initial": "\uFC98"
18512           },
18513           "\u0626\u0645": {
18514             "isolated": "\uFC02",
18515             "final": "\uFC66",
18516             "initial": "\uFC9A",
18517             "medial": "\uFCDF"
18518           },
18519           "\u0626\u064A": {
18520             "isolated": "\uFC04",
18521             "final": "\uFC69"
18522           },
18523           "\u0628\u062C": {
18524             "isolated": "\uFC05",
18525             "initial": "\uFC9C"
18526           },
18527           "\u0628\u062D": {
18528             "isolated": "\uFC06",
18529             "initial": "\uFC9D"
18530           },
18531           "\u0628\u062E": {
18532             "isolated": "\uFC07",
18533             "initial": "\uFC9E"
18534           },
18535           "\u0628\u0645": {
18536             "isolated": "\uFC08",
18537             "final": "\uFC6C",
18538             "initial": "\uFC9F",
18539             "medial": "\uFCE1"
18540           },
18541           "\u0628\u0649": {
18542             "isolated": "\uFC09",
18543             "final": "\uFC6E"
18544           },
18545           "\u0628\u064A": {
18546             "isolated": "\uFC0A",
18547             "final": "\uFC6F"
18548           },
18549           "\u062A\u062C": {
18550             "isolated": "\uFC0B",
18551             "initial": "\uFCA1"
18552           },
18553           "\u062A\u062D": {
18554             "isolated": "\uFC0C",
18555             "initial": "\uFCA2"
18556           },
18557           "\u062A\u062E": {
18558             "isolated": "\uFC0D",
18559             "initial": "\uFCA3"
18560           },
18561           "\u062A\u0645": {
18562             "isolated": "\uFC0E",
18563             "final": "\uFC72",
18564             "initial": "\uFCA4",
18565             "medial": "\uFCE3"
18566           },
18567           "\u062A\u0649": {
18568             "isolated": "\uFC0F",
18569             "final": "\uFC74"
18570           },
18571           "\u062A\u064A": {
18572             "isolated": "\uFC10",
18573             "final": "\uFC75"
18574           },
18575           "\u062B\u062C": {
18576             "isolated": "\uFC11"
18577           },
18578           "\u062B\u0645": {
18579             "isolated": "\uFC12",
18580             "final": "\uFC78",
18581             "initial": "\uFCA6",
18582             "medial": "\uFCE5"
18583           },
18584           "\u062B\u0649": {
18585             "isolated": "\uFC13",
18586             "final": "\uFC7A"
18587           },
18588           "\u062B\u0648": {
18589             "isolated": "\uFC14"
18590           },
18591           "\u062C\u062D": {
18592             "isolated": "\uFC15",
18593             "initial": "\uFCA7"
18594           },
18595           "\u062C\u0645": {
18596             "isolated": "\uFC16",
18597             "initial": "\uFCA8"
18598           },
18599           "\u062D\u062C": {
18600             "isolated": "\uFC17",
18601             "initial": "\uFCA9"
18602           },
18603           "\u062D\u0645": {
18604             "isolated": "\uFC18",
18605             "initial": "\uFCAA"
18606           },
18607           "\u062E\u062C": {
18608             "isolated": "\uFC19",
18609             "initial": "\uFCAB"
18610           },
18611           "\u062E\u062D": {
18612             "isolated": "\uFC1A"
18613           },
18614           "\u062E\u0645": {
18615             "isolated": "\uFC1B",
18616             "initial": "\uFCAC"
18617           },
18618           "\u0633\u062C": {
18619             "isolated": "\uFC1C",
18620             "initial": "\uFCAD",
18621             "medial": "\uFD34"
18622           },
18623           "\u0633\u062D": {
18624             "isolated": "\uFC1D",
18625             "initial": "\uFCAE",
18626             "medial": "\uFD35"
18627           },
18628           "\u0633\u062E": {
18629             "isolated": "\uFC1E",
18630             "initial": "\uFCAF",
18631             "medial": "\uFD36"
18632           },
18633           "\u0633\u0645": {
18634             "isolated": "\uFC1F",
18635             "initial": "\uFCB0",
18636             "medial": "\uFCE7"
18637           },
18638           "\u0635\u062D": {
18639             "isolated": "\uFC20",
18640             "initial": "\uFCB1"
18641           },
18642           "\u0635\u0645": {
18643             "isolated": "\uFC21",
18644             "initial": "\uFCB3"
18645           },
18646           "\u0636\u062C": {
18647             "isolated": "\uFC22",
18648             "initial": "\uFCB4"
18649           },
18650           "\u0636\u062D": {
18651             "isolated": "\uFC23",
18652             "initial": "\uFCB5"
18653           },
18654           "\u0636\u062E": {
18655             "isolated": "\uFC24",
18656             "initial": "\uFCB6"
18657           },
18658           "\u0636\u0645": {
18659             "isolated": "\uFC25",
18660             "initial": "\uFCB7"
18661           },
18662           "\u0637\u062D": {
18663             "isolated": "\uFC26",
18664             "initial": "\uFCB8"
18665           },
18666           "\u0637\u0645": {
18667             "isolated": "\uFC27",
18668             "initial": "\uFD33",
18669             "medial": "\uFD3A"
18670           },
18671           "\u0638\u0645": {
18672             "isolated": "\uFC28",
18673             "initial": "\uFCB9",
18674             "medial": "\uFD3B"
18675           },
18676           "\u0639\u062C": {
18677             "isolated": "\uFC29",
18678             "initial": "\uFCBA"
18679           },
18680           "\u0639\u0645": {
18681             "isolated": "\uFC2A",
18682             "initial": "\uFCBB"
18683           },
18684           "\u063A\u062C": {
18685             "isolated": "\uFC2B",
18686             "initial": "\uFCBC"
18687           },
18688           "\u063A\u0645": {
18689             "isolated": "\uFC2C",
18690             "initial": "\uFCBD"
18691           },
18692           "\u0641\u062C": {
18693             "isolated": "\uFC2D",
18694             "initial": "\uFCBE"
18695           },
18696           "\u0641\u062D": {
18697             "isolated": "\uFC2E",
18698             "initial": "\uFCBF"
18699           },
18700           "\u0641\u062E": {
18701             "isolated": "\uFC2F",
18702             "initial": "\uFCC0"
18703           },
18704           "\u0641\u0645": {
18705             "isolated": "\uFC30",
18706             "initial": "\uFCC1"
18707           },
18708           "\u0641\u0649": {
18709             "isolated": "\uFC31",
18710             "final": "\uFC7C"
18711           },
18712           "\u0641\u064A": {
18713             "isolated": "\uFC32",
18714             "final": "\uFC7D"
18715           },
18716           "\u0642\u062D": {
18717             "isolated": "\uFC33",
18718             "initial": "\uFCC2"
18719           },
18720           "\u0642\u0645": {
18721             "isolated": "\uFC34",
18722             "initial": "\uFCC3"
18723           },
18724           "\u0642\u0649": {
18725             "isolated": "\uFC35",
18726             "final": "\uFC7E"
18727           },
18728           "\u0642\u064A": {
18729             "isolated": "\uFC36",
18730             "final": "\uFC7F"
18731           },
18732           "\u0643\u0627": {
18733             "isolated": "\uFC37",
18734             "final": "\uFC80"
18735           },
18736           "\u0643\u062C": {
18737             "isolated": "\uFC38",
18738             "initial": "\uFCC4"
18739           },
18740           "\u0643\u062D": {
18741             "isolated": "\uFC39",
18742             "initial": "\uFCC5"
18743           },
18744           "\u0643\u062E": {
18745             "isolated": "\uFC3A",
18746             "initial": "\uFCC6"
18747           },
18748           "\u0643\u0644": {
18749             "isolated": "\uFC3B",
18750             "final": "\uFC81",
18751             "initial": "\uFCC7",
18752             "medial": "\uFCEB"
18753           },
18754           "\u0643\u0645": {
18755             "isolated": "\uFC3C",
18756             "final": "\uFC82",
18757             "initial": "\uFCC8",
18758             "medial": "\uFCEC"
18759           },
18760           "\u0643\u0649": {
18761             "isolated": "\uFC3D",
18762             "final": "\uFC83"
18763           },
18764           "\u0643\u064A": {
18765             "isolated": "\uFC3E",
18766             "final": "\uFC84"
18767           },
18768           "\u0644\u062C": {
18769             "isolated": "\uFC3F",
18770             "initial": "\uFCC9"
18771           },
18772           "\u0644\u062D": {
18773             "isolated": "\uFC40",
18774             "initial": "\uFCCA"
18775           },
18776           "\u0644\u062E": {
18777             "isolated": "\uFC41",
18778             "initial": "\uFCCB"
18779           },
18780           "\u0644\u0645": {
18781             "isolated": "\uFC42",
18782             "final": "\uFC85",
18783             "initial": "\uFCCC",
18784             "medial": "\uFCED"
18785           },
18786           "\u0644\u0649": {
18787             "isolated": "\uFC43",
18788             "final": "\uFC86"
18789           },
18790           "\u0644\u064A": {
18791             "isolated": "\uFC44",
18792             "final": "\uFC87"
18793           },
18794           "\u0645\u062C": {
18795             "isolated": "\uFC45",
18796             "initial": "\uFCCE"
18797           },
18798           "\u0645\u062D": {
18799             "isolated": "\uFC46",
18800             "initial": "\uFCCF"
18801           },
18802           "\u0645\u062E": {
18803             "isolated": "\uFC47",
18804             "initial": "\uFCD0"
18805           },
18806           "\u0645\u0645": {
18807             "isolated": "\uFC48",
18808             "final": "\uFC89",
18809             "initial": "\uFCD1"
18810           },
18811           "\u0645\u0649": {
18812             "isolated": "\uFC49"
18813           },
18814           "\u0645\u064A": {
18815             "isolated": "\uFC4A"
18816           },
18817           "\u0646\u062C": {
18818             "isolated": "\uFC4B",
18819             "initial": "\uFCD2"
18820           },
18821           "\u0646\u062D": {
18822             "isolated": "\uFC4C",
18823             "initial": "\uFCD3"
18824           },
18825           "\u0646\u062E": {
18826             "isolated": "\uFC4D",
18827             "initial": "\uFCD4"
18828           },
18829           "\u0646\u0645": {
18830             "isolated": "\uFC4E",
18831             "final": "\uFC8C",
18832             "initial": "\uFCD5",
18833             "medial": "\uFCEE"
18834           },
18835           "\u0646\u0649": {
18836             "isolated": "\uFC4F",
18837             "final": "\uFC8E"
18838           },
18839           "\u0646\u064A": {
18840             "isolated": "\uFC50",
18841             "final": "\uFC8F"
18842           },
18843           "\u0647\u062C": {
18844             "isolated": "\uFC51",
18845             "initial": "\uFCD7"
18846           },
18847           "\u0647\u0645": {
18848             "isolated": "\uFC52",
18849             "initial": "\uFCD8"
18850           },
18851           "\u0647\u0649": {
18852             "isolated": "\uFC53"
18853           },
18854           "\u0647\u064A": {
18855             "isolated": "\uFC54"
18856           },
18857           "\u064A\u062C": {
18858             "isolated": "\uFC55",
18859             "initial": "\uFCDA"
18860           },
18861           "\u064A\u062D": {
18862             "isolated": "\uFC56",
18863             "initial": "\uFCDB"
18864           },
18865           "\u064A\u062E": {
18866             "isolated": "\uFC57",
18867             "initial": "\uFCDC"
18868           },
18869           "\u064A\u0645": {
18870             "isolated": "\uFC58",
18871             "final": "\uFC93",
18872             "initial": "\uFCDD",
18873             "medial": "\uFCF0"
18874           },
18875           "\u064A\u0649": {
18876             "isolated": "\uFC59",
18877             "final": "\uFC95"
18878           },
18879           "\u064A\u064A": {
18880             "isolated": "\uFC5A",
18881             "final": "\uFC96"
18882           },
18883           "\u0630\u0670": {
18884             "isolated": "\uFC5B"
18885           },
18886           "\u0631\u0670": {
18887             "isolated": "\uFC5C"
18888           },
18889           "\u0649\u0670": {
18890             "isolated": "\uFC5D",
18891             "final": "\uFC90"
18892           },
18893           "\u064C\u0651": {
18894             "isolated": "\uFC5E"
18895           },
18896           "\u064D\u0651": {
18897             "isolated": "\uFC5F"
18898           },
18899           "\u064E\u0651": {
18900             "isolated": "\uFC60"
18901           },
18902           "\u064F\u0651": {
18903             "isolated": "\uFC61"
18904           },
18905           "\u0650\u0651": {
18906             "isolated": "\uFC62"
18907           },
18908           "\u0651\u0670": {
18909             "isolated": "\uFC63"
18910           },
18911           "\u0626\u0631": {
18912             "final": "\uFC64"
18913           },
18914           "\u0626\u0632": {
18915             "final": "\uFC65"
18916           },
18917           "\u0626\u0646": {
18918             "final": "\uFC67"
18919           },
18920           "\u0628\u0631": {
18921             "final": "\uFC6A"
18922           },
18923           "\u0628\u0632": {
18924             "final": "\uFC6B"
18925           },
18926           "\u0628\u0646": {
18927             "final": "\uFC6D"
18928           },
18929           "\u062A\u0631": {
18930             "final": "\uFC70"
18931           },
18932           "\u062A\u0632": {
18933             "final": "\uFC71"
18934           },
18935           "\u062A\u0646": {
18936             "final": "\uFC73"
18937           },
18938           "\u062B\u0631": {
18939             "final": "\uFC76"
18940           },
18941           "\u062B\u0632": {
18942             "final": "\uFC77"
18943           },
18944           "\u062B\u0646": {
18945             "final": "\uFC79"
18946           },
18947           "\u062B\u064A": {
18948             "final": "\uFC7B"
18949           },
18950           "\u0645\u0627": {
18951             "final": "\uFC88"
18952           },
18953           "\u0646\u0631": {
18954             "final": "\uFC8A"
18955           },
18956           "\u0646\u0632": {
18957             "final": "\uFC8B"
18958           },
18959           "\u0646\u0646": {
18960             "final": "\uFC8D"
18961           },
18962           "\u064A\u0631": {
18963             "final": "\uFC91"
18964           },
18965           "\u064A\u0632": {
18966             "final": "\uFC92"
18967           },
18968           "\u064A\u0646": {
18969             "final": "\uFC94"
18970           },
18971           "\u0626\u062E": {
18972             "initial": "\uFC99"
18973           },
18974           "\u0626\u0647": {
18975             "initial": "\uFC9B",
18976             "medial": "\uFCE0"
18977           },
18978           "\u0628\u0647": {
18979             "initial": "\uFCA0",
18980             "medial": "\uFCE2"
18981           },
18982           "\u062A\u0647": {
18983             "initial": "\uFCA5",
18984             "medial": "\uFCE4"
18985           },
18986           "\u0635\u062E": {
18987             "initial": "\uFCB2"
18988           },
18989           "\u0644\u0647": {
18990             "initial": "\uFCCD"
18991           },
18992           "\u0646\u0647": {
18993             "initial": "\uFCD6",
18994             "medial": "\uFCEF"
18995           },
18996           "\u0647\u0670": {
18997             "initial": "\uFCD9"
18998           },
18999           "\u064A\u0647": {
19000             "initial": "\uFCDE",
19001             "medial": "\uFCF1"
19002           },
19003           "\u062B\u0647": {
19004             "medial": "\uFCE6"
19005           },
19006           "\u0633\u0647": {
19007             "medial": "\uFCE8",
19008             "initial": "\uFD31"
19009           },
19010           "\u0634\u0645": {
19011             "medial": "\uFCE9",
19012             "isolated": "\uFD0C",
19013             "final": "\uFD28",
19014             "initial": "\uFD30"
19015           },
19016           "\u0634\u0647": {
19017             "medial": "\uFCEA",
19018             "initial": "\uFD32"
19019           },
19020           "\u0640\u064E\u0651": {
19021             "medial": "\uFCF2"
19022           },
19023           "\u0640\u064F\u0651": {
19024             "medial": "\uFCF3"
19025           },
19026           "\u0640\u0650\u0651": {
19027             "medial": "\uFCF4"
19028           },
19029           "\u0637\u0649": {
19030             "isolated": "\uFCF5",
19031             "final": "\uFD11"
19032           },
19033           "\u0637\u064A": {
19034             "isolated": "\uFCF6",
19035             "final": "\uFD12"
19036           },
19037           "\u0639\u0649": {
19038             "isolated": "\uFCF7",
19039             "final": "\uFD13"
19040           },
19041           "\u0639\u064A": {
19042             "isolated": "\uFCF8",
19043             "final": "\uFD14"
19044           },
19045           "\u063A\u0649": {
19046             "isolated": "\uFCF9",
19047             "final": "\uFD15"
19048           },
19049           "\u063A\u064A": {
19050             "isolated": "\uFCFA",
19051             "final": "\uFD16"
19052           },
19053           "\u0633\u0649": {
19054             "isolated": "\uFCFB"
19055           },
19056           "\u0633\u064A": {
19057             "isolated": "\uFCFC",
19058             "final": "\uFD18"
19059           },
19060           "\u0634\u0649": {
19061             "isolated": "\uFCFD",
19062             "final": "\uFD19"
19063           },
19064           "\u0634\u064A": {
19065             "isolated": "\uFCFE",
19066             "final": "\uFD1A"
19067           },
19068           "\u062D\u0649": {
19069             "isolated": "\uFCFF",
19070             "final": "\uFD1B"
19071           },
19072           "\u062D\u064A": {
19073             "isolated": "\uFD00",
19074             "final": "\uFD1C"
19075           },
19076           "\u062C\u0649": {
19077             "isolated": "\uFD01",
19078             "final": "\uFD1D"
19079           },
19080           "\u062C\u064A": {
19081             "isolated": "\uFD02",
19082             "final": "\uFD1E"
19083           },
19084           "\u062E\u0649": {
19085             "isolated": "\uFD03",
19086             "final": "\uFD1F"
19087           },
19088           "\u062E\u064A": {
19089             "isolated": "\uFD04",
19090             "final": "\uFD20"
19091           },
19092           "\u0635\u0649": {
19093             "isolated": "\uFD05",
19094             "final": "\uFD21"
19095           },
19096           "\u0635\u064A": {
19097             "isolated": "\uFD06",
19098             "final": "\uFD22"
19099           },
19100           "\u0636\u0649": {
19101             "isolated": "\uFD07",
19102             "final": "\uFD23"
19103           },
19104           "\u0636\u064A": {
19105             "isolated": "\uFD08",
19106             "final": "\uFD24"
19107           },
19108           "\u0634\u062C": {
19109             "isolated": "\uFD09",
19110             "final": "\uFD25",
19111             "initial": "\uFD2D",
19112             "medial": "\uFD37"
19113           },
19114           "\u0634\u062D": {
19115             "isolated": "\uFD0A",
19116             "final": "\uFD26",
19117             "initial": "\uFD2E",
19118             "medial": "\uFD38"
19119           },
19120           "\u0634\u062E": {
19121             "isolated": "\uFD0B",
19122             "final": "\uFD27",
19123             "initial": "\uFD2F",
19124             "medial": "\uFD39"
19125           },
19126           "\u0634\u0631": {
19127             "isolated": "\uFD0D",
19128             "final": "\uFD29"
19129           },
19130           "\u0633\u0631": {
19131             "isolated": "\uFD0E",
19132             "final": "\uFD2A"
19133           },
19134           "\u0635\u0631": {
19135             "isolated": "\uFD0F",
19136             "final": "\uFD2B"
19137           },
19138           "\u0636\u0631": {
19139             "isolated": "\uFD10",
19140             "final": "\uFD2C"
19141           },
19142           "\u0633\u0639": {
19143             "final": "\uFD17"
19144           },
19145           "\u062A\u062C\u0645": {
19146             "initial": "\uFD50"
19147           },
19148           "\u062A\u062D\u062C": {
19149             "final": "\uFD51",
19150             "initial": "\uFD52"
19151           },
19152           "\u062A\u062D\u0645": {
19153             "initial": "\uFD53"
19154           },
19155           "\u062A\u062E\u0645": {
19156             "initial": "\uFD54"
19157           },
19158           "\u062A\u0645\u062C": {
19159             "initial": "\uFD55"
19160           },
19161           "\u062A\u0645\u062D": {
19162             "initial": "\uFD56"
19163           },
19164           "\u062A\u0645\u062E": {
19165             "initial": "\uFD57"
19166           },
19167           "\u062C\u0645\u062D": {
19168             "final": "\uFD58",
19169             "initial": "\uFD59"
19170           },
19171           "\u062D\u0645\u064A": {
19172             "final": "\uFD5A"
19173           },
19174           "\u062D\u0645\u0649": {
19175             "final": "\uFD5B"
19176           },
19177           "\u0633\u062D\u062C": {
19178             "initial": "\uFD5C"
19179           },
19180           "\u0633\u062C\u062D": {
19181             "initial": "\uFD5D"
19182           },
19183           "\u0633\u062C\u0649": {
19184             "final": "\uFD5E"
19185           },
19186           "\u0633\u0645\u062D": {
19187             "final": "\uFD5F",
19188             "initial": "\uFD60"
19189           },
19190           "\u0633\u0645\u062C": {
19191             "initial": "\uFD61"
19192           },
19193           "\u0633\u0645\u0645": {
19194             "final": "\uFD62",
19195             "initial": "\uFD63"
19196           },
19197           "\u0635\u062D\u062D": {
19198             "final": "\uFD64",
19199             "initial": "\uFD65"
19200           },
19201           "\u0635\u0645\u0645": {
19202             "final": "\uFD66",
19203             "initial": "\uFDC5"
19204           },
19205           "\u0634\u062D\u0645": {
19206             "final": "\uFD67",
19207             "initial": "\uFD68"
19208           },
19209           "\u0634\u062C\u064A": {
19210             "final": "\uFD69"
19211           },
19212           "\u0634\u0645\u062E": {
19213             "final": "\uFD6A",
19214             "initial": "\uFD6B"
19215           },
19216           "\u0634\u0645\u0645": {
19217             "final": "\uFD6C",
19218             "initial": "\uFD6D"
19219           },
19220           "\u0636\u062D\u0649": {
19221             "final": "\uFD6E"
19222           },
19223           "\u0636\u062E\u0645": {
19224             "final": "\uFD6F",
19225             "initial": "\uFD70"
19226           },
19227           "\u0636\u0645\u062D": {
19228             "final": "\uFD71"
19229           },
19230           "\u0637\u0645\u062D": {
19231             "initial": "\uFD72"
19232           },
19233           "\u0637\u0645\u0645": {
19234             "initial": "\uFD73"
19235           },
19236           "\u0637\u0645\u064A": {
19237             "final": "\uFD74"
19238           },
19239           "\u0639\u062C\u0645": {
19240             "final": "\uFD75",
19241             "initial": "\uFDC4"
19242           },
19243           "\u0639\u0645\u0645": {
19244             "final": "\uFD76",
19245             "initial": "\uFD77"
19246           },
19247           "\u0639\u0645\u0649": {
19248             "final": "\uFD78"
19249           },
19250           "\u063A\u0645\u0645": {
19251             "final": "\uFD79"
19252           },
19253           "\u063A\u0645\u064A": {
19254             "final": "\uFD7A"
19255           },
19256           "\u063A\u0645\u0649": {
19257             "final": "\uFD7B"
19258           },
19259           "\u0641\u062E\u0645": {
19260             "final": "\uFD7C",
19261             "initial": "\uFD7D"
19262           },
19263           "\u0642\u0645\u062D": {
19264             "final": "\uFD7E",
19265             "initial": "\uFDB4"
19266           },
19267           "\u0642\u0645\u0645": {
19268             "final": "\uFD7F"
19269           },
19270           "\u0644\u062D\u0645": {
19271             "final": "\uFD80",
19272             "initial": "\uFDB5"
19273           },
19274           "\u0644\u062D\u064A": {
19275             "final": "\uFD81"
19276           },
19277           "\u0644\u062D\u0649": {
19278             "final": "\uFD82"
19279           },
19280           "\u0644\u062C\u062C": {
19281             "initial": "\uFD83",
19282             "final": "\uFD84"
19283           },
19284           "\u0644\u062E\u0645": {
19285             "final": "\uFD85",
19286             "initial": "\uFD86"
19287           },
19288           "\u0644\u0645\u062D": {
19289             "final": "\uFD87",
19290             "initial": "\uFD88"
19291           },
19292           "\u0645\u062D\u062C": {
19293             "initial": "\uFD89"
19294           },
19295           "\u0645\u062D\u0645": {
19296             "initial": "\uFD8A"
19297           },
19298           "\u0645\u062D\u064A": {
19299             "final": "\uFD8B"
19300           },
19301           "\u0645\u062C\u062D": {
19302             "initial": "\uFD8C"
19303           },
19304           "\u0645\u062C\u0645": {
19305             "initial": "\uFD8D"
19306           },
19307           "\u0645\u062E\u062C": {
19308             "initial": "\uFD8E"
19309           },
19310           "\u0645\u062E\u0645": {
19311             "initial": "\uFD8F"
19312           },
19313           "\u0645\u062C\u062E": {
19314             "initial": "\uFD92"
19315           },
19316           "\u0647\u0645\u062C": {
19317             "initial": "\uFD93"
19318           },
19319           "\u0647\u0645\u0645": {
19320             "initial": "\uFD94"
19321           },
19322           "\u0646\u062D\u0645": {
19323             "initial": "\uFD95"
19324           },
19325           "\u0646\u062D\u0649": {
19326             "final": "\uFD96"
19327           },
19328           "\u0646\u062C\u0645": {
19329             "final": "\uFD97",
19330             "initial": "\uFD98"
19331           },
19332           "\u0646\u062C\u0649": {
19333             "final": "\uFD99"
19334           },
19335           "\u0646\u0645\u064A": {
19336             "final": "\uFD9A"
19337           },
19338           "\u0646\u0645\u0649": {
19339             "final": "\uFD9B"
19340           },
19341           "\u064A\u0645\u0645": {
19342             "final": "\uFD9C",
19343             "initial": "\uFD9D"
19344           },
19345           "\u0628\u062E\u064A": {
19346             "final": "\uFD9E"
19347           },
19348           "\u062A\u062C\u064A": {
19349             "final": "\uFD9F"
19350           },
19351           "\u062A\u062C\u0649": {
19352             "final": "\uFDA0"
19353           },
19354           "\u062A\u062E\u064A": {
19355             "final": "\uFDA1"
19356           },
19357           "\u062A\u062E\u0649": {
19358             "final": "\uFDA2"
19359           },
19360           "\u062A\u0645\u064A": {
19361             "final": "\uFDA3"
19362           },
19363           "\u062A\u0645\u0649": {
19364             "final": "\uFDA4"
19365           },
19366           "\u062C\u0645\u064A": {
19367             "final": "\uFDA5"
19368           },
19369           "\u062C\u062D\u0649": {
19370             "final": "\uFDA6"
19371           },
19372           "\u062C\u0645\u0649": {
19373             "final": "\uFDA7"
19374           },
19375           "\u0633\u062E\u0649": {
19376             "final": "\uFDA8"
19377           },
19378           "\u0635\u062D\u064A": {
19379             "final": "\uFDA9"
19380           },
19381           "\u0634\u062D\u064A": {
19382             "final": "\uFDAA"
19383           },
19384           "\u0636\u062D\u064A": {
19385             "final": "\uFDAB"
19386           },
19387           "\u0644\u062C\u064A": {
19388             "final": "\uFDAC"
19389           },
19390           "\u0644\u0645\u064A": {
19391             "final": "\uFDAD"
19392           },
19393           "\u064A\u062D\u064A": {
19394             "final": "\uFDAE"
19395           },
19396           "\u064A\u062C\u064A": {
19397             "final": "\uFDAF"
19398           },
19399           "\u064A\u0645\u064A": {
19400             "final": "\uFDB0"
19401           },
19402           "\u0645\u0645\u064A": {
19403             "final": "\uFDB1"
19404           },
19405           "\u0642\u0645\u064A": {
19406             "final": "\uFDB2"
19407           },
19408           "\u0646\u062D\u064A": {
19409             "final": "\uFDB3"
19410           },
19411           "\u0639\u0645\u064A": {
19412             "final": "\uFDB6"
19413           },
19414           "\u0643\u0645\u064A": {
19415             "final": "\uFDB7"
19416           },
19417           "\u0646\u062C\u062D": {
19418             "initial": "\uFDB8",
19419             "final": "\uFDBD"
19420           },
19421           "\u0645\u062E\u064A": {
19422             "final": "\uFDB9"
19423           },
19424           "\u0644\u062C\u0645": {
19425             "initial": "\uFDBA",
19426             "final": "\uFDBC"
19427           },
19428           "\u0643\u0645\u0645": {
19429             "final": "\uFDBB",
19430             "initial": "\uFDC3"
19431           },
19432           "\u062C\u062D\u064A": {
19433             "final": "\uFDBE"
19434           },
19435           "\u062D\u062C\u064A": {
19436             "final": "\uFDBF"
19437           },
19438           "\u0645\u062C\u064A": {
19439             "final": "\uFDC0"
19440           },
19441           "\u0641\u0645\u064A": {
19442             "final": "\uFDC1"
19443           },
19444           "\u0628\u062D\u064A": {
19445             "final": "\uFDC2"
19446           },
19447           "\u0633\u062E\u064A": {
19448             "final": "\uFDC6"
19449           },
19450           "\u0646\u062C\u064A": {
19451             "final": "\uFDC7"
19452           },
19453           "\u0644\u0622": {
19454             "isolated": "\uFEF5",
19455             "final": "\uFEF6"
19456           },
19457           "\u0644\u0623": {
19458             "isolated": "\uFEF7",
19459             "final": "\uFEF8"
19460           },
19461           "\u0644\u0625": {
19462             "isolated": "\uFEF9",
19463             "final": "\uFEFA"
19464           },
19465           "\u0644\u0627": {
19466             "isolated": "\uFEFB",
19467             "final": "\uFEFC"
19468           },
19469           "words": {
19470             "\u0635\u0644\u06D2": "\uFDF0",
19471             "\u0642\u0644\u06D2": "\uFDF1",
19472             "\u0627\u0644\u0644\u0647": "\uFDF2",
19473             "\u0627\u0643\u0628\u0631": "\uFDF3",
19474             "\u0645\u062D\u0645\u062F": "\uFDF4",
19475             "\u0635\u0644\u0639\u0645": "\uFDF5",
19476             "\u0631\u0633\u0648\u0644": "\uFDF6",
19477             "\u0639\u0644\u064A\u0647": "\uFDF7",
19478             "\u0648\u0633\u0644\u0645": "\uFDF8",
19479             "\u0635\u0644\u0649": "\uFDF9",
19480             "\u0635\u0644\u0649\u0627\u0644\u0644\u0647\u0639\u0644\u064A\u0647\u0648\u0633\u0644\u0645": "\uFDFA",
19481             "\u062C\u0644\u062C\u0644\u0627\u0644\u0647": "\uFDFB",
19482             "\u0631\u06CC\u0627\u0644": "\uFDFC"
19483           }
19484         };
19485         var _default$2 = ligatureReference;
19486         var unicodeLigatures = /*#__PURE__*/Object.defineProperty({
19487           "default": _default$2
19488         }, '__esModule', {
19489           value: true
19490         });
19491
19492         var reference = createCommonjsModule(function (module, exports) {
19493
19494           Object.defineProperty(exports, "__esModule", {
19495             value: true
19496           });
19497           var letterList = Object.keys(unicodeArabic["default"]);
19498           exports.letterList = letterList;
19499           var ligatureList = Object.keys(unicodeLigatures["default"]);
19500           exports.ligatureList = ligatureList;
19501           var ligatureWordList = Object.keys(unicodeLigatures["default"].words);
19502           exports.ligatureWordList = ligatureWordList;
19503           var lams = "\u0644\u06B5\u06B6\u06B7\u06B8";
19504           exports.lams = lams;
19505           var alefs = "\u0627\u0622\u0623\u0625\u0671\u0672\u0673\u0675\u0773\u0774";
19506           exports.alefs = alefs; // for (var l = 1; l < lams.length; l++) {
19507           //   console.log('-');
19508           //   for (var a = 0; a < alefs.length; a++) {
19509           //     console.log(a + ': ' + lams[l] + alefs[a]);
19510           //   }
19511           // }
19512
19513           var tashkeel = "\u0605\u0640\u0670\u0674\u06DF\u06E7\u06E8";
19514           exports.tashkeel = tashkeel;
19515
19516           function addToTashkeel(start, finish) {
19517             for (var i = start; i <= finish; i++) {
19518               exports.tashkeel = tashkeel += String.fromCharCode(i);
19519             }
19520           }
19521
19522           addToTashkeel(0x0610, 0x061A);
19523           addToTashkeel(0x064B, 0x065F);
19524           addToTashkeel(0x06D6, 0x06DC);
19525           addToTashkeel(0x06E0, 0x06E4);
19526           addToTashkeel(0x06EA, 0x06ED);
19527           addToTashkeel(0x08D3, 0x08E1);
19528           addToTashkeel(0x08E3, 0x08FF);
19529           addToTashkeel(0xFE70, 0xFE7F);
19530           var lineBreakers = "\u0627\u0629\u0648\u06C0\u06CF\u06FD\u06FE\u076B\u076C\u0771\u0773\u0774\u0778\u0779\u08E2\u08B1\u08B2\u08B9";
19531           exports.lineBreakers = lineBreakers;
19532
19533           function addToLineBreakers(start, finish) {
19534             for (var i = start; i <= finish; i++) {
19535               exports.lineBreakers = lineBreakers += String.fromCharCode(i);
19536             }
19537           }
19538
19539           addToLineBreakers(0x0600, 0x061F); // it's OK to include tashkeel in this range as it is ignored
19540
19541           addToLineBreakers(0x0621, 0x0625);
19542           addToLineBreakers(0x062F, 0x0632);
19543           addToLineBreakers(0x0660, 0x066D); // numerals, math
19544
19545           addToLineBreakers(0x0671, 0x0677);
19546           addToLineBreakers(0x0688, 0x0699);
19547           addToLineBreakers(0x06C3, 0x06CB);
19548           addToLineBreakers(0x06D2, 0x06F9);
19549           addToLineBreakers(0x0759, 0x075B);
19550           addToLineBreakers(0x08AA, 0x08AE);
19551           addToLineBreakers(0xFB50, 0xFDFD); // presentation forms look like they could connect, but never do
19552           // Presentation Forms A includes diacritics but they are meant to stand alone
19553
19554           addToLineBreakers(0xFE80, 0xFEFC); // presentation forms look like they could connect, but never do
19555           // numerals, math
19556
19557           addToLineBreakers(0x10E60, 0x10E7F);
19558           addToLineBreakers(0x1EC70, 0x1ECBF);
19559           addToLineBreakers(0x1EE00, 0x1EEFF);
19560         });
19561
19562         function GlyphSplitter(word) {
19563           var letters = [];
19564           var lastLetter = '';
19565           word.split('').forEach(function (letter) {
19566             if (isArabic_1.isArabic(letter)) {
19567               if (reference.tashkeel.indexOf(letter) > -1) {
19568                 letters[letters.length - 1] += letter;
19569               } else if (lastLetter.length && (reference.lams.indexOf(lastLetter) === 0 && reference.alefs.indexOf(letter) > -1 || reference.lams.indexOf(lastLetter) > 0 && reference.alefs.indexOf(letter) === 0)) {
19570                 // valid LA forms
19571                 letters[letters.length - 1] += letter;
19572               } else {
19573                 letters.push(letter);
19574               }
19575             } else {
19576               letters.push(letter);
19577             }
19578
19579             if (reference.tashkeel.indexOf(letter) === -1) {
19580               lastLetter = letter;
19581             }
19582           });
19583           return letters;
19584         }
19585
19586         var GlyphSplitter_2 = GlyphSplitter;
19587         var GlyphSplitter_1 = /*#__PURE__*/Object.defineProperty({
19588           GlyphSplitter: GlyphSplitter_2
19589         }, '__esModule', {
19590           value: true
19591         });
19592
19593         function BaselineSplitter(word) {
19594           var letters = [];
19595           var lastLetter = '';
19596           word.split('').forEach(function (letter) {
19597             if (isArabic_1.isArabic(letter) && isArabic_1.isArabic(lastLetter)) {
19598               if (lastLetter.length && reference.tashkeel.indexOf(letter) > -1) {
19599                 letters[letters.length - 1] += letter;
19600               } else if (reference.lineBreakers.indexOf(lastLetter) > -1) {
19601                 letters.push(letter);
19602               } else {
19603                 letters[letters.length - 1] += letter;
19604               }
19605             } else {
19606               letters.push(letter);
19607             }
19608
19609             if (reference.tashkeel.indexOf(letter) === -1) {
19610               // don't allow tashkeel to hide line break
19611               lastLetter = letter;
19612             }
19613           });
19614           return letters;
19615         }
19616
19617         var BaselineSplitter_2 = BaselineSplitter;
19618         var BaselineSplitter_1 = /*#__PURE__*/Object.defineProperty({
19619           BaselineSplitter: BaselineSplitter_2
19620         }, '__esModule', {
19621           value: true
19622         });
19623
19624         function Normal(word, breakPresentationForm) {
19625           // default is to turn initial/isolated/medial/final presentation form to generic
19626           if (typeof breakPresentationForm === 'undefined') {
19627             breakPresentationForm = true;
19628           }
19629
19630           var returnable = '';
19631           word.split('').forEach(function (letter) {
19632             if (!isArabic_1.isArabic(letter)) {
19633               returnable += letter;
19634               return;
19635             }
19636
19637             for (var w = 0; w < reference.letterList.length; w++) {
19638               // ok so we are checking this potential lettertron
19639               var letterForms = unicodeArabic["default"][reference.letterList[w]];
19640               var versions = Object.keys(letterForms);
19641
19642               for (var v = 0; v < versions.length; v++) {
19643                 var localVersion = letterForms[versions[v]];
19644
19645                 if (_typeof(localVersion) === 'object' && typeof localVersion.indexOf === 'undefined') {
19646                   // look at this embedded object
19647                   var embeddedForms = Object.keys(localVersion);
19648
19649                   for (var ef = 0; ef < embeddedForms.length; ef++) {
19650                     var form = localVersion[embeddedForms[ef]];
19651
19652                     if (form === letter || _typeof(form) === 'object' && form.indexOf && form.indexOf(letter) > -1) {
19653                       // match
19654                       // console.log('embedded match');
19655                       if (form === letter) {
19656                         // match exact
19657                         if (breakPresentationForm && localVersion['normal'] && ['isolated', 'initial', 'medial', 'final'].indexOf(embeddedForms[ef]) > -1) {
19658                           // replace presentation form
19659                           // console.log('keeping normal form of the letter');
19660                           if (_typeof(localVersion['normal']) === 'object') {
19661                             returnable += localVersion['normal'][0];
19662                           } else {
19663                             returnable += localVersion['normal'];
19664                           }
19665
19666                           return;
19667                         } // console.log('keeping this letter');
19668
19669
19670                         returnable += letter;
19671                         return;
19672                       } else if (_typeof(form) === 'object' && form.indexOf && form.indexOf(letter) > -1) {
19673                         // match
19674                         returnable += form[0]; // console.log('added the first letter from the same array');
19675
19676                         return;
19677                       }
19678                     }
19679                   }
19680                 } else if (localVersion === letter) {
19681                   // match exact
19682                   if (breakPresentationForm && letterForms['normal'] && ['isolated', 'initial', 'medial', 'final'].indexOf(versions[v]) > -1) {
19683                     // replace presentation form
19684                     // console.log('keeping normal form of the letter');
19685                     if (_typeof(letterForms['normal']) === 'object') {
19686                       returnable += letterForms['normal'][0];
19687                     } else {
19688                       returnable += letterForms['normal'];
19689                     }
19690
19691                     return;
19692                   } // console.log('keeping this letter');
19693
19694
19695                   returnable += letter;
19696                   return;
19697                 } else if (_typeof(localVersion) === 'object' && localVersion.indexOf && localVersion.indexOf(letter) > -1) {
19698                   // match
19699                   returnable += localVersion[0]; // console.log('added the first letter from the same array');
19700
19701                   return;
19702                 }
19703               }
19704             } // try ligatures
19705
19706
19707             for (var v2 = 0; v2 < reference.ligatureList.length; v2++) {
19708               var normalForm = reference.ligatureList[v2];
19709
19710               if (normalForm !== 'words') {
19711                 var ligForms = Object.keys(unicodeLigatures["default"][normalForm]);
19712
19713                 for (var f = 0; f < ligForms.length; f++) {
19714                   if (unicodeLigatures["default"][normalForm][ligForms[f]] === letter) {
19715                     returnable += normalForm;
19716                     return;
19717                   }
19718                 }
19719               }
19720             } // try words ligatures
19721
19722
19723             for (var v3 = 0; v3 < reference.ligatureWordList.length; v3++) {
19724               var _normalForm = reference.ligatureWordList[v3];
19725
19726               if (unicodeLigatures["default"].words[_normalForm] === letter) {
19727                 returnable += _normalForm;
19728                 return;
19729               }
19730             }
19731
19732             returnable += letter; // console.log('kept the letter')
19733           });
19734           return returnable;
19735         }
19736
19737         var Normal_1 = Normal;
19738         var Normalization = /*#__PURE__*/Object.defineProperty({
19739           Normal: Normal_1
19740         }, '__esModule', {
19741           value: true
19742         });
19743
19744         function CharShaper(letter, form) {
19745           if (!isArabic_1.isArabic(letter)) {
19746             // fail not Arabic
19747             throw new Error('Not Arabic');
19748           }
19749
19750           if (letter === "\u0621") {
19751             // hamza alone
19752             return "\u0621";
19753           }
19754
19755           for (var w = 0; w < reference.letterList.length; w++) {
19756             // ok so we are checking this potential lettertron
19757             var letterForms = unicodeArabic["default"][reference.letterList[w]];
19758             var versions = Object.keys(letterForms);
19759
19760             for (var v = 0; v < versions.length; v++) {
19761               var localVersion = letterForms[versions[v]];
19762
19763               if (localVersion === letter || _typeof(localVersion) === 'object' && localVersion.indexOf && localVersion.indexOf(letter) > -1) {
19764                 if (versions.indexOf(form) > -1) {
19765                   return letterForms[form];
19766                 }
19767               } else if (_typeof(localVersion) === 'object' && typeof localVersion.indexOf === 'undefined') {
19768                 // check embedded
19769                 var embeddedVersions = Object.keys(localVersion);
19770
19771                 for (var ev = 0; ev < embeddedVersions.length; ev++) {
19772                   if (localVersion[embeddedVersions[ev]] === letter || _typeof(localVersion[embeddedVersions[ev]]) === 'object' && localVersion[embeddedVersions[ev]].indexOf && localVersion[embeddedVersions[ev]].indexOf(letter) > -1) {
19773                     if (embeddedVersions.indexOf(form) > -1) {
19774                       return localVersion[form];
19775                     }
19776                   }
19777                 }
19778               }
19779             }
19780           }
19781         }
19782
19783         var CharShaper_2 = CharShaper;
19784         var CharShaper_1 = /*#__PURE__*/Object.defineProperty({
19785           CharShaper: CharShaper_2
19786         }, '__esModule', {
19787           value: true
19788         });
19789
19790         function WordShaper$1(word) {
19791           var state = 'initial';
19792           var output = '';
19793
19794           for (var w = 0; w < word.length; w++) {
19795             var nextLetter = ' ';
19796
19797             for (var nxw = w + 1; nxw < word.length; nxw++) {
19798               if (!isArabic_1.isArabic(word[nxw])) {
19799                 break;
19800               }
19801
19802               if (reference.tashkeel.indexOf(word[nxw]) === -1) {
19803                 nextLetter = word[nxw];
19804                 break;
19805               }
19806             }
19807
19808             if (!isArabic_1.isArabic(word[w]) || isArabic_1.isMath(word[w])) {
19809               // space or other non-Arabic
19810               output += word[w];
19811               state = 'initial';
19812             } else if (reference.tashkeel.indexOf(word[w]) > -1) {
19813               // tashkeel - add without changing state
19814               output += word[w];
19815             } else if (nextLetter === ' ' || // last Arabic letter in this word
19816             reference.lineBreakers.indexOf(word[w]) > -1) {
19817               // the current letter is known to break lines
19818               output += CharShaper_1.CharShaper(word[w], state === 'initial' ? 'isolated' : 'final');
19819               state = 'initial';
19820             } else if (reference.lams.indexOf(word[w]) > -1 && reference.alefs.indexOf(nextLetter) > -1) {
19821               // LA letters - advance an additional letter after this
19822               output += unicodeLigatures["default"][word[w] + nextLetter][state === 'initial' ? 'isolated' : 'final'];
19823
19824               while (word[w] !== nextLetter) {
19825                 w++;
19826               }
19827
19828               state = 'initial';
19829             } else {
19830               output += CharShaper_1.CharShaper(word[w], state);
19831               state = 'medial';
19832             }
19833           }
19834
19835           return output;
19836         }
19837
19838         var WordShaper_2 = WordShaper$1;
19839         var WordShaper_1 = /*#__PURE__*/Object.defineProperty({
19840           WordShaper: WordShaper_2
19841         }, '__esModule', {
19842           value: true
19843         });
19844
19845         function ParentLetter(letter) {
19846           if (!isArabic_1.isArabic(letter)) {
19847             throw new Error('Not an Arabic letter');
19848           }
19849
19850           for (var w = 0; w < reference.letterList.length; w++) {
19851             // ok so we are checking this potential lettertron
19852             var letterForms = unicodeArabic["default"][reference.letterList[w]];
19853             var versions = Object.keys(letterForms);
19854
19855             for (var v = 0; v < versions.length; v++) {
19856               var localVersion = letterForms[versions[v]];
19857
19858               if (_typeof(localVersion) === 'object' && typeof localVersion.indexOf === 'undefined') {
19859                 // look at this embedded object
19860                 var embeddedForms = Object.keys(localVersion);
19861
19862                 for (var ef = 0; ef < embeddedForms.length; ef++) {
19863                   var form = localVersion[embeddedForms[ef]];
19864
19865                   if (form === letter || _typeof(form) === 'object' && form.indexOf && form.indexOf(letter) > -1) {
19866                     // match
19867                     return localVersion;
19868                   }
19869                 }
19870               } else if (localVersion === letter || _typeof(localVersion) === 'object' && localVersion.indexOf && localVersion.indexOf(letter) > -1) {
19871                 // match
19872                 return letterForms;
19873               }
19874             }
19875
19876             return null;
19877           }
19878         }
19879
19880         var ParentLetter_2 = ParentLetter;
19881
19882         function GrandparentLetter(letter) {
19883           if (!isArabic_1.isArabic(letter)) {
19884             throw new Error('Not an Arabic letter');
19885           }
19886
19887           for (var w = 0; w < reference.letterList.length; w++) {
19888             // ok so we are checking this potential lettertron
19889             var letterForms = unicodeArabic["default"][reference.letterList[w]];
19890             var versions = Object.keys(letterForms);
19891
19892             for (var v = 0; v < versions.length; v++) {
19893               var localVersion = letterForms[versions[v]];
19894
19895               if (_typeof(localVersion) === 'object' && typeof localVersion.indexOf === 'undefined') {
19896                 // look at this embedded object
19897                 var embeddedForms = Object.keys(localVersion);
19898
19899                 for (var ef = 0; ef < embeddedForms.length; ef++) {
19900                   var form = localVersion[embeddedForms[ef]];
19901
19902                   if (form === letter || _typeof(form) === 'object' && form.indexOf && form.indexOf(letter) > -1) {
19903                     // match
19904                     return letterForms;
19905                   }
19906                 }
19907               } else if (localVersion === letter || _typeof(localVersion) === 'object' && localVersion.indexOf && localVersion.indexOf(letter) > -1) {
19908                 // match
19909                 return letterForms;
19910               }
19911             }
19912
19913             return null;
19914           }
19915         }
19916
19917         var GrandparentLetter_1 = GrandparentLetter;
19918         var ParentLetter_1 = /*#__PURE__*/Object.defineProperty({
19919           ParentLetter: ParentLetter_2,
19920           GrandparentLetter: GrandparentLetter_1
19921         }, '__esModule', {
19922           value: true
19923         });
19924
19925         isArabic_1.isArabic;
19926         GlyphSplitter_1.GlyphSplitter;
19927         BaselineSplitter_1.BaselineSplitter;
19928         Normalization.Normal;
19929         CharShaper_1.CharShaper;
19930         var WordShaper = WordShaper_1.WordShaper;
19931         ParentLetter_1.ParentLetter;
19932         ParentLetter_1.GrandparentLetter;
19933
19934         var rtlRegex = /[\u0590-\u05FF\u0600-\u06FF\u0750-\u07BF\u08A0–\u08BF]/;
19935         function fixRTLTextForSvg(inputText) {
19936           var ret = '',
19937               rtlBuffer = [];
19938           var arabicRegex = /[\u0600-\u06FF]/g;
19939           var arabicDiacritics = /[\u0610-\u061A\u064B-\u065F\u0670\u06D6-\u06ED]/g;
19940           var arabicMath = /[\u0660-\u066C\u06F0-\u06F9]+/g;
19941           var thaanaVowel = /[\u07A6-\u07B0]/;
19942           var hebrewSign = /[\u0591-\u05bd\u05bf\u05c1-\u05c5\u05c7]/; // Arabic word shaping
19943
19944           if (arabicRegex.test(inputText)) {
19945             inputText = WordShaper(inputText);
19946           }
19947
19948           for (var n = 0; n < inputText.length; n++) {
19949             var c = inputText[n];
19950
19951             if (arabicMath.test(c)) {
19952               // Arabic numbers go LTR
19953               ret += rtlBuffer.reverse().join('');
19954               rtlBuffer = [c];
19955             } else {
19956               if (rtlBuffer.length && arabicMath.test(rtlBuffer[rtlBuffer.length - 1])) {
19957                 ret += rtlBuffer.reverse().join('');
19958                 rtlBuffer = [];
19959               }
19960
19961               if ((thaanaVowel.test(c) || hebrewSign.test(c) || arabicDiacritics.test(c)) && rtlBuffer.length) {
19962                 rtlBuffer[rtlBuffer.length - 1] += c;
19963               } else if (rtlRegex.test(c) // include Arabic presentation forms
19964               || c.charCodeAt(0) >= 64336 && c.charCodeAt(0) <= 65023 || c.charCodeAt(0) >= 65136 && c.charCodeAt(0) <= 65279) {
19965                 rtlBuffer.push(c);
19966               } else if (c === ' ' && rtlBuffer.length) {
19967                 // whitespace within RTL text
19968                 rtlBuffer = [rtlBuffer.reverse().join('') + ' '];
19969               } else {
19970                 // non-RTL character
19971                 ret += rtlBuffer.reverse().join('') + c;
19972                 rtlBuffer = [];
19973               }
19974             }
19975           }
19976
19977           ret += rtlBuffer.reverse().join('');
19978           return ret;
19979         }
19980
19981         var propertyIsEnumerable = objectPropertyIsEnumerable.f;
19982
19983         // `Object.{ entries, values }` methods implementation
19984         var createMethod$1 = function (TO_ENTRIES) {
19985           return function (it) {
19986             var O = toIndexedObject(it);
19987             var keys = objectKeys(O);
19988             var length = keys.length;
19989             var i = 0;
19990             var result = [];
19991             var key;
19992             while (length > i) {
19993               key = keys[i++];
19994               if (!descriptors || propertyIsEnumerable.call(O, key)) {
19995                 result.push(TO_ENTRIES ? [key, O[key]] : O[key]);
19996               }
19997             }
19998             return result;
19999           };
20000         };
20001
20002         var objectToArray = {
20003           // `Object.entries` method
20004           // https://tc39.es/ecma262/#sec-object.entries
20005           entries: createMethod$1(true),
20006           // `Object.values` method
20007           // https://tc39.es/ecma262/#sec-object.values
20008           values: createMethod$1(false)
20009         };
20010
20011         var $values = objectToArray.values;
20012
20013         // `Object.values` method
20014         // https://tc39.es/ecma262/#sec-object.values
20015         _export({ target: 'Object', stat: true }, {
20016           values: function values(O) {
20017             return $values(O);
20018           }
20019         });
20020
20021         // https://github.com/openstreetmap/iD/issues/772
20022         // http://mathiasbynens.be/notes/localstorage-pattern#comment-9
20023         var _storage;
20024
20025         try {
20026           _storage = localStorage;
20027         } catch (e) {} // eslint-disable-line no-empty
20028
20029
20030         _storage = _storage || function () {
20031           var s = {};
20032           return {
20033             getItem: function getItem(k) {
20034               return s[k];
20035             },
20036             setItem: function setItem(k, v) {
20037               return s[k] = v;
20038             },
20039             removeItem: function removeItem(k) {
20040               return delete s[k];
20041             }
20042           };
20043         }(); //
20044         // corePreferences is an interface for persisting basic key-value strings
20045         // within and between iD sessions on the same site.
20046         //
20047
20048
20049         function corePreferences(k, v) {
20050           try {
20051             if (arguments.length === 1) return _storage.getItem(k);else if (v === null) _storage.removeItem(k);else _storage.setItem(k, v);
20052           } catch (e) {
20053             /* eslint-disable no-console */
20054             if (typeof console !== 'undefined') {
20055               console.error('localStorage quota exceeded');
20056             }
20057             /* eslint-enable no-console */
20058
20059           }
20060         }
20061
20062         var vparse = createCommonjsModule(function (module) {
20063           (function (window) {
20064
20065             function parseVersion(v) {
20066               var m = v.replace(/[^0-9.]/g, '').match(/[0-9]*\.|[0-9]+/g) || [];
20067               v = {
20068                 major: +m[0] || 0,
20069                 minor: +m[1] || 0,
20070                 patch: +m[2] || 0,
20071                 build: +m[3] || 0
20072               };
20073               v.isEmpty = !v.major && !v.minor && !v.patch && !v.build;
20074               v.parsed = [v.major, v.minor, v.patch, v.build];
20075               v.text = v.parsed.join('.');
20076               v.compare = compare;
20077               return v;
20078             }
20079
20080             function compare(v) {
20081               if (typeof v === 'string') {
20082                 v = parseVersion(v);
20083               }
20084
20085               for (var i = 0; i < 4; i++) {
20086                 if (this.parsed[i] !== v.parsed[i]) {
20087                   return this.parsed[i] > v.parsed[i] ? 1 : -1;
20088                 }
20089               }
20090
20091               return 0;
20092             }
20093             /* istanbul ignore next */
20094
20095
20096             if (module && 'object' === 'object') {
20097               module.exports = parseVersion;
20098             } else {
20099               window.parseVersion = parseVersion;
20100             }
20101           })(commonjsGlobal);
20102         });
20103
20104         var name = "iD";
20105         var version = "2.20.1";
20106         var description = "A friendly editor for OpenStreetMap";
20107         var main = "dist/iD.min.js";
20108         var repository = "github:openstreetmap/iD";
20109         var homepage = "https://github.com/openstreetmap/iD";
20110         var bugs = "https://github.com/openstreetmap/iD/issues";
20111         var keywords = ["editor","openstreetmap"];
20112         var license = "ISC";
20113         var scripts = {all:"npm-run-all -s clean build build:legacy dist",build:"npm-run-all -s build:css build:data build:dev","build:css":"node scripts/build_css.js","build:data":"shx mkdir -p dist/data && node scripts/build_data.js","build:dev":"rollup --config config/rollup.config.dev.js","build:legacy":"rollup --config config/rollup.config.legacy.js","build:stats":"rollup --config config/rollup.config.stats.js",clean:"shx rm -f dist/*.js dist/*.map dist/*.css dist/img/*.svg",dist:"npm-run-all -p dist:**","dist:mapillary":"shx mkdir -p dist/mapillary-js && shx cp -R node_modules/mapillary-js/dist/* dist/mapillary-js/","dist:pannellum":"shx mkdir -p dist/pannellum-streetside && shx cp -R node_modules/pannellum/build/* dist/pannellum-streetside/","dist:min:iD":"uglifyjs dist/iD.legacy.js --compress --mangle --output dist/iD.min.js","dist:svg:iD":"svg-sprite --symbol --symbol-dest . --shape-id-generator \"iD-%s\" --symbol-sprite dist/img/iD-sprite.svg \"svg/iD-sprite/**/*.svg\"","dist:svg:community":"svg-sprite --symbol --symbol-dest . --shape-id-generator \"community-%s\" --symbol-sprite dist/img/community-sprite.svg node_modules/osm-community-index/dist/img/*.svg","dist:svg:fa":"svg-sprite --symbol --symbol-dest . --symbol-sprite dist/img/fa-sprite.svg svg/fontawesome/*.svg","dist:svg:maki":"svg-sprite --symbol --symbol-dest . --shape-id-generator \"maki-%s\" --symbol-sprite dist/img/maki-sprite.svg node_modules/@mapbox/maki/icons/*.svg","dist:svg:mapillary:signs":"svg-sprite --symbol --symbol-dest . --symbol-sprite dist/img/mapillary-sprite.svg node_modules/mapillary_sprite_source/package_signs/*.svg","dist:svg:mapillary:objects":"svg-sprite --symbol --symbol-dest . --symbol-sprite dist/img/mapillary-object-sprite.svg node_modules/mapillary_sprite_source/package_objects/*.svg","dist:svg:temaki":"svg-sprite --symbol --symbol-dest . --shape-id-generator \"temaki-%s\" --symbol-sprite dist/img/temaki-sprite.svg node_modules/@ideditor/temaki/icons/*.svg",imagery:"node scripts/update_imagery.js",lint:"eslint scripts test/spec modules","lint:fix":"eslint scripts test/spec modules --fix",start:"npm-run-all -s build start:server",quickstart:"npm-run-all -s build:dev start:server","start:server":"node scripts/server.js",test:"npm-run-all -s lint build:css build:data build:legacy test:spec","test:spec":"phantomjs --web-security=no node_modules/mocha-phantomjs-core/mocha-phantomjs-core.js test/index.html spec",translations:"node scripts/update_locales.js"};
20114         var dependencies = {"@ideditor/country-coder":"~5.0.3","@ideditor/location-conflation":"~1.0.2","@mapbox/geojson-area":"^0.2.2","@mapbox/sexagesimal":"1.2.0","@mapbox/vector-tile":"^1.3.1","@tmcw/togeojson":"^4.5.0","@turf/bbox-clip":"^6.0.0","abortcontroller-polyfill":"^1.4.0","aes-js":"^3.1.2","alif-toolkit":"^1.2.9","core-js":"^3.6.5",diacritics:"1.3.0","fast-deep-equal":"~3.1.1","fast-json-stable-stringify":"2.1.0","lodash-es":"~4.17.15",marked:"~2.0.0","node-diff3":"2.1.0","osm-auth":"1.1.0",pannellum:"2.5.6",pbf:"^3.2.1","polygon-clipping":"~0.15.1",rbush:"3.0.1","whatwg-fetch":"^3.4.1","which-polygon":"2.2.0"};
20115         var devDependencies = {"@babel/core":"^7.11.6","@babel/preset-env":"^7.11.5","@fortawesome/fontawesome-svg-core":"^1.2.32","@fortawesome/free-brands-svg-icons":"~5.15.1","@fortawesome/free-regular-svg-icons":"~5.15.1","@fortawesome/free-solid-svg-icons":"~5.15.1","@ideditor/temaki":"~4.4.0","@mapbox/maki":"^6.0.0","@rollup/plugin-babel":"^5.2.1","@rollup/plugin-commonjs":"^17.0.0","@rollup/plugin-json":"^4.0.1","@rollup/plugin-node-resolve":"~11.2.0",autoprefixer:"^10.0.1",btoa:"^1.2.1",chai:"^4.1.0","cldr-core":"37.0.0","cldr-localenames-full":"37.0.0",colors:"^1.1.2","concat-files":"^0.1.1",d3:"~6.6.0","editor-layer-index":"github:osmlab/editor-layer-index#gh-pages",eslint:"^7.1.0",gaze:"^1.1.3",glob:"^7.1.0",happen:"^0.3.1","js-yaml":"^4.0.0","json-stringify-pretty-compact":"^3.0.0",mapillary_sprite_source:"^1.8.0","mapillary-js":"4.0.0",minimist:"^1.2.3",mocha:"^7.0.1","mocha-phantomjs-core":"^2.1.0","name-suggestion-index":"~6.0","node-fetch":"^2.6.1","npm-run-all":"^4.0.0","object-inspect":"1.10.3","osm-community-index":"~5.1.0","phantomjs-prebuilt":"~2.1.16",postcss:"^8.1.1","postcss-selector-prepend":"^0.5.0",rollup:"~2.52.8","rollup-plugin-includepaths":"~0.2.3","rollup-plugin-progress":"^1.1.1","rollup-plugin-visualizer":"~4.2.0",shelljs:"^0.8.0",shx:"^0.3.0",sinon:"7.5.0","sinon-chai":"^3.3.0",smash:"0.0","static-server":"^2.2.1","svg-sprite":"1.5.1","uglify-js":"~3.13.0",vparse:"~1.1.0"};
20116         var engines = {node:">=10"};
20117         var browserslist = ["> 0.2%, last 6 major versions, Firefox ESR, IE 11, maintained node versions"];
20118         var packageJSON = {
20119         name: name,
20120         version: version,
20121         description: description,
20122         main: main,
20123         repository: repository,
20124         homepage: homepage,
20125         bugs: bugs,
20126         keywords: keywords,
20127         license: license,
20128         scripts: scripts,
20129         dependencies: dependencies,
20130         devDependencies: devDependencies,
20131         engines: engines,
20132         browserslist: browserslist
20133         };
20134
20135         var _mainFileFetcher = coreFileFetcher(); // singleton
20136         // coreFileFetcher asynchronously fetches data from JSON files
20137         //
20138
20139         function coreFileFetcher() {
20140           var ociVersion = packageJSON.devDependencies['osm-community-index'];
20141           var v = vparse(ociVersion);
20142           var vMinor = "".concat(v.major, ".").concat(v.minor);
20143           var _this = {};
20144           var _inflight = {};
20145           var _fileMap = {
20146             'address_formats': 'data/address_formats.min.json',
20147             'deprecated': 'https://cdn.jsdelivr.net/npm/@openstreetmap/id-tagging-schema@3/dist/deprecated.min.json',
20148             'discarded': 'https://cdn.jsdelivr.net/npm/@openstreetmap/id-tagging-schema@3/dist/discarded.min.json',
20149             'imagery': 'data/imagery.min.json',
20150             'intro_graph': 'data/intro_graph.min.json',
20151             'keepRight': 'data/keepRight.min.json',
20152             'languages': 'data/languages.min.json',
20153             'locales': 'locales/index.min.json',
20154             'oci_defaults': "https://cdn.jsdelivr.net/npm/osm-community-index@".concat(vMinor, "/dist/defaults.min.json"),
20155             'oci_features': "https://cdn.jsdelivr.net/npm/osm-community-index@".concat(vMinor, "/dist/featureCollection.min.json"),
20156             'oci_resources': "https://cdn.jsdelivr.net/npm/osm-community-index@".concat(vMinor, "/dist/resources.min.json"),
20157             'preset_categories': 'https://cdn.jsdelivr.net/npm/@openstreetmap/id-tagging-schema@3/dist/preset_categories.min.json',
20158             'preset_defaults': 'https://cdn.jsdelivr.net/npm/@openstreetmap/id-tagging-schema@3/dist/preset_defaults.min.json',
20159             'preset_fields': 'https://cdn.jsdelivr.net/npm/@openstreetmap/id-tagging-schema@3/dist/fields.min.json',
20160             'preset_presets': 'https://cdn.jsdelivr.net/npm/@openstreetmap/id-tagging-schema@3/dist/presets.min.json',
20161             'phone_formats': 'data/phone_formats.min.json',
20162             'qa_data': 'data/qa_data.min.json',
20163             'shortcuts': 'data/shortcuts.min.json',
20164             'territory_languages': 'data/territory_languages.min.json',
20165             'wmf_sitematrix': 'https://cdn.jsdelivr.net/npm/wmf-sitematrix@0.1/wikipedia.min.json'
20166           };
20167           var _cachedData = {}; // expose the cache; useful for tests
20168
20169           _this.cache = function () {
20170             return _cachedData;
20171           }; // Returns a Promise to fetch data
20172           // (resolved with the data if we have it already)
20173
20174
20175           _this.get = function (which) {
20176             if (_cachedData[which]) {
20177               return Promise.resolve(_cachedData[which]);
20178             }
20179
20180             var file = _fileMap[which];
20181
20182             var url = file && _this.asset(file);
20183
20184             if (!url) {
20185               return Promise.reject("Unknown data file for \"".concat(which, "\""));
20186             }
20187
20188             var prom = _inflight[url];
20189
20190             if (!prom) {
20191               _inflight[url] = prom = utilFetchJson(url).then(function (result) {
20192                 delete _inflight[url];
20193
20194                 if (!result) {
20195                   throw new Error("No data loaded for \"".concat(which, "\""));
20196                 }
20197
20198                 _cachedData[which] = result;
20199                 return result;
20200               })["catch"](function (err) {
20201                 delete _inflight[url];
20202                 throw err;
20203               });
20204             }
20205
20206             return prom;
20207           }; // Accessor for the file map
20208
20209
20210           _this.fileMap = function (val) {
20211             if (!arguments.length) return _fileMap;
20212             _fileMap = val;
20213             return _this;
20214           };
20215
20216           var _assetPath = '';
20217
20218           _this.assetPath = function (val) {
20219             if (!arguments.length) return _assetPath;
20220             _assetPath = val;
20221             return _this;
20222           };
20223
20224           var _assetMap = {};
20225
20226           _this.assetMap = function (val) {
20227             if (!arguments.length) return _assetMap;
20228             _assetMap = val;
20229             return _this;
20230           };
20231
20232           _this.asset = function (val) {
20233             if (/^http(s)?:\/\//i.test(val)) return val;
20234             var filename = _assetPath + val;
20235             return _assetMap[filename] || filename;
20236           };
20237
20238           return _this;
20239         }
20240
20241         var getOwnPropertyNames = objectGetOwnPropertyNames.f;
20242         var getOwnPropertyDescriptor$2 = objectGetOwnPropertyDescriptor.f;
20243         var defineProperty = objectDefineProperty.f;
20244         var trim$2 = stringTrim.trim;
20245
20246         var NUMBER = 'Number';
20247         var NativeNumber = global$2[NUMBER];
20248         var NumberPrototype = NativeNumber.prototype;
20249
20250         // Opera ~12 has broken Object#toString
20251         var BROKEN_CLASSOF = classofRaw(objectCreate(NumberPrototype)) == NUMBER;
20252
20253         // `ToNumber` abstract operation
20254         // https://tc39.es/ecma262/#sec-tonumber
20255         var toNumber$1 = function (argument) {
20256           var it = toPrimitive(argument, false);
20257           var first, third, radix, maxCode, digits, length, index, code;
20258           if (typeof it == 'string' && it.length > 2) {
20259             it = trim$2(it);
20260             first = it.charCodeAt(0);
20261             if (first === 43 || first === 45) {
20262               third = it.charCodeAt(2);
20263               if (third === 88 || third === 120) return NaN; // Number('+0x1') should be NaN, old V8 fix
20264             } else if (first === 48) {
20265               switch (it.charCodeAt(1)) {
20266                 case 66: case 98: radix = 2; maxCode = 49; break; // fast equal of /^0b[01]+$/i
20267                 case 79: case 111: radix = 8; maxCode = 55; break; // fast equal of /^0o[0-7]+$/i
20268                 default: return +it;
20269               }
20270               digits = it.slice(2);
20271               length = digits.length;
20272               for (index = 0; index < length; index++) {
20273                 code = digits.charCodeAt(index);
20274                 // parseInt parses a string to a first unavailable symbol
20275                 // but ToNumber should return NaN if a string contains unavailable symbols
20276                 if (code < 48 || code > maxCode) return NaN;
20277               } return parseInt(digits, radix);
20278             }
20279           } return +it;
20280         };
20281
20282         // `Number` constructor
20283         // https://tc39.es/ecma262/#sec-number-constructor
20284         if (isForced_1(NUMBER, !NativeNumber(' 0o1') || !NativeNumber('0b1') || NativeNumber('+0x1'))) {
20285           var NumberWrapper = function Number(value) {
20286             var it = arguments.length < 1 ? 0 : value;
20287             var dummy = this;
20288             return dummy instanceof NumberWrapper
20289               // check on 1..constructor(foo) case
20290               && (BROKEN_CLASSOF ? fails(function () { NumberPrototype.valueOf.call(dummy); }) : classofRaw(dummy) != NUMBER)
20291                 ? inheritIfRequired(new NativeNumber(toNumber$1(it)), dummy, NumberWrapper) : toNumber$1(it);
20292           };
20293           for (var keys = descriptors ? getOwnPropertyNames(NativeNumber) : (
20294             // ES3:
20295             'MAX_VALUE,MIN_VALUE,NaN,NEGATIVE_INFINITY,POSITIVE_INFINITY,' +
20296             // ES2015 (in case, if modules with ES2015 Number statics required before):
20297             'EPSILON,isFinite,isInteger,isNaN,isSafeInteger,MAX_SAFE_INTEGER,' +
20298             'MIN_SAFE_INTEGER,parseFloat,parseInt,isInteger,' +
20299             // ESNext
20300             'fromString,range'
20301           ).split(','), j = 0, key; keys.length > j; j++) {
20302             if (has$1(NativeNumber, key = keys[j]) && !has$1(NumberWrapper, key)) {
20303               defineProperty(NumberWrapper, key, getOwnPropertyDescriptor$2(NativeNumber, key));
20304             }
20305           }
20306           NumberWrapper.prototype = NumberPrototype;
20307           NumberPrototype.constructor = NumberWrapper;
20308           redefine(global$2, NUMBER, NumberWrapper);
20309         }
20310
20311         // `thisNumberValue` abstract operation
20312         // https://tc39.es/ecma262/#sec-thisnumbervalue
20313         var thisNumberValue = function (value) {
20314           if (typeof value != 'number' && classofRaw(value) != 'Number') {
20315             throw TypeError('Incorrect invocation');
20316           }
20317           return +value;
20318         };
20319
20320         // `String.prototype.repeat` method implementation
20321         // https://tc39.es/ecma262/#sec-string.prototype.repeat
20322         var stringRepeat = function repeat(count) {
20323           var str = String(requireObjectCoercible(this));
20324           var result = '';
20325           var n = toInteger(count);
20326           if (n < 0 || n == Infinity) throw RangeError('Wrong number of repetitions');
20327           for (;n > 0; (n >>>= 1) && (str += str)) if (n & 1) result += str;
20328           return result;
20329         };
20330
20331         var nativeToFixed = 1.0.toFixed;
20332         var floor = Math.floor;
20333
20334         var pow = function (x, n, acc) {
20335           return n === 0 ? acc : n % 2 === 1 ? pow(x, n - 1, acc * x) : pow(x * x, n / 2, acc);
20336         };
20337
20338         var log = function (x) {
20339           var n = 0;
20340           var x2 = x;
20341           while (x2 >= 4096) {
20342             n += 12;
20343             x2 /= 4096;
20344           }
20345           while (x2 >= 2) {
20346             n += 1;
20347             x2 /= 2;
20348           } return n;
20349         };
20350
20351         var multiply = function (data, n, c) {
20352           var index = -1;
20353           var c2 = c;
20354           while (++index < 6) {
20355             c2 += n * data[index];
20356             data[index] = c2 % 1e7;
20357             c2 = floor(c2 / 1e7);
20358           }
20359         };
20360
20361         var divide = function (data, n) {
20362           var index = 6;
20363           var c = 0;
20364           while (--index >= 0) {
20365             c += data[index];
20366             data[index] = floor(c / n);
20367             c = (c % n) * 1e7;
20368           }
20369         };
20370
20371         var dataToString = function (data) {
20372           var index = 6;
20373           var s = '';
20374           while (--index >= 0) {
20375             if (s !== '' || index === 0 || data[index] !== 0) {
20376               var t = String(data[index]);
20377               s = s === '' ? t : s + stringRepeat.call('0', 7 - t.length) + t;
20378             }
20379           } return s;
20380         };
20381
20382         var FORCED$4 = nativeToFixed && (
20383           0.00008.toFixed(3) !== '0.000' ||
20384           0.9.toFixed(0) !== '1' ||
20385           1.255.toFixed(2) !== '1.25' ||
20386           1000000000000000128.0.toFixed(0) !== '1000000000000000128'
20387         ) || !fails(function () {
20388           // V8 ~ Android 4.3-
20389           nativeToFixed.call({});
20390         });
20391
20392         // `Number.prototype.toFixed` method
20393         // https://tc39.es/ecma262/#sec-number.prototype.tofixed
20394         _export({ target: 'Number', proto: true, forced: FORCED$4 }, {
20395           toFixed: function toFixed(fractionDigits) {
20396             var number = thisNumberValue(this);
20397             var fractDigits = toInteger(fractionDigits);
20398             var data = [0, 0, 0, 0, 0, 0];
20399             var sign = '';
20400             var result = '0';
20401             var e, z, j, k;
20402
20403             if (fractDigits < 0 || fractDigits > 20) throw RangeError('Incorrect fraction digits');
20404             // eslint-disable-next-line no-self-compare -- NaN check
20405             if (number != number) return 'NaN';
20406             if (number <= -1e21 || number >= 1e21) return String(number);
20407             if (number < 0) {
20408               sign = '-';
20409               number = -number;
20410             }
20411             if (number > 1e-21) {
20412               e = log(number * pow(2, 69, 1)) - 69;
20413               z = e < 0 ? number * pow(2, -e, 1) : number / pow(2, e, 1);
20414               z *= 0x10000000000000;
20415               e = 52 - e;
20416               if (e > 0) {
20417                 multiply(data, 0, z);
20418                 j = fractDigits;
20419                 while (j >= 7) {
20420                   multiply(data, 1e7, 0);
20421                   j -= 7;
20422                 }
20423                 multiply(data, pow(10, j, 1), 0);
20424                 j = e - 1;
20425                 while (j >= 23) {
20426                   divide(data, 1 << 23);
20427                   j -= 23;
20428                 }
20429                 divide(data, 1 << j);
20430                 multiply(data, 1, 1);
20431                 divide(data, 2);
20432                 result = dataToString(data);
20433               } else {
20434                 multiply(data, 0, z);
20435                 multiply(data, 1 << -e, 0);
20436                 result = dataToString(data) + stringRepeat.call('0', fractDigits);
20437               }
20438             }
20439             if (fractDigits > 0) {
20440               k = result.length;
20441               result = sign + (k <= fractDigits
20442                 ? '0.' + stringRepeat.call('0', fractDigits - k) + result
20443                 : result.slice(0, k - fractDigits) + '.' + result.slice(k - fractDigits));
20444             } else {
20445               result = sign + result;
20446             } return result;
20447           }
20448         });
20449
20450         var globalIsFinite = global$2.isFinite;
20451
20452         // `Number.isFinite` method
20453         // https://tc39.es/ecma262/#sec-number.isfinite
20454         // eslint-disable-next-line es/no-number-isfinite -- safe
20455         var numberIsFinite = Number.isFinite || function isFinite(it) {
20456           return typeof it == 'number' && globalIsFinite(it);
20457         };
20458
20459         // `Number.isFinite` method
20460         // https://tc39.es/ecma262/#sec-number.isfinite
20461         _export({ target: 'Number', stat: true }, { isFinite: numberIsFinite });
20462
20463         var fromCharCode = String.fromCharCode;
20464         // eslint-disable-next-line es/no-string-fromcodepoint -- required for testing
20465         var $fromCodePoint = String.fromCodePoint;
20466
20467         // length should be 1, old FF problem
20468         var INCORRECT_LENGTH = !!$fromCodePoint && $fromCodePoint.length != 1;
20469
20470         // `String.fromCodePoint` method
20471         // https://tc39.es/ecma262/#sec-string.fromcodepoint
20472         _export({ target: 'String', stat: true, forced: INCORRECT_LENGTH }, {
20473           // eslint-disable-next-line no-unused-vars -- required for `.length`
20474           fromCodePoint: function fromCodePoint(x) {
20475             var elements = [];
20476             var length = arguments.length;
20477             var i = 0;
20478             var code;
20479             while (length > i) {
20480               code = +arguments[i++];
20481               if (toAbsoluteIndex(code, 0x10FFFF) !== code) throw RangeError(code + ' is not a valid code point');
20482               elements.push(code < 0x10000
20483                 ? fromCharCode(code)
20484                 : fromCharCode(((code -= 0x10000) >> 10) + 0xD800, code % 0x400 + 0xDC00)
20485               );
20486             } return elements.join('');
20487           }
20488         });
20489
20490         // @@search logic
20491         fixRegexpWellKnownSymbolLogic('search', function (SEARCH, nativeSearch, maybeCallNative) {
20492           return [
20493             // `String.prototype.search` method
20494             // https://tc39.es/ecma262/#sec-string.prototype.search
20495             function search(regexp) {
20496               var O = requireObjectCoercible(this);
20497               var searcher = regexp == undefined ? undefined : regexp[SEARCH];
20498               return searcher !== undefined ? searcher.call(regexp, O) : new RegExp(regexp)[SEARCH](String(O));
20499             },
20500             // `RegExp.prototype[@@search]` method
20501             // https://tc39.es/ecma262/#sec-regexp.prototype-@@search
20502             function (string) {
20503               var res = maybeCallNative(nativeSearch, this, string);
20504               if (res.done) return res.value;
20505
20506               var rx = anObject(this);
20507               var S = String(string);
20508
20509               var previousLastIndex = rx.lastIndex;
20510               if (!sameValue(previousLastIndex, 0)) rx.lastIndex = 0;
20511               var result = regexpExecAbstract(rx, S);
20512               if (!sameValue(rx.lastIndex, previousLastIndex)) rx.lastIndex = previousLastIndex;
20513               return result === null ? -1 : result.index;
20514             }
20515           ];
20516         });
20517
20518         var quickselect$1 = createCommonjsModule(function (module, exports) {
20519           (function (global, factory) {
20520             module.exports = factory() ;
20521           })(commonjsGlobal, function () {
20522
20523             function quickselect(arr, k, left, right, compare) {
20524               quickselectStep(arr, k, left || 0, right || arr.length - 1, compare || defaultCompare);
20525             }
20526
20527             function quickselectStep(arr, k, left, right, compare) {
20528               while (right > left) {
20529                 if (right - left > 600) {
20530                   var n = right - left + 1;
20531                   var m = k - left + 1;
20532                   var z = Math.log(n);
20533                   var s = 0.5 * Math.exp(2 * z / 3);
20534                   var sd = 0.5 * Math.sqrt(z * s * (n - s) / n) * (m - n / 2 < 0 ? -1 : 1);
20535                   var newLeft = Math.max(left, Math.floor(k - m * s / n + sd));
20536                   var newRight = Math.min(right, Math.floor(k + (n - m) * s / n + sd));
20537                   quickselectStep(arr, k, newLeft, newRight, compare);
20538                 }
20539
20540                 var t = arr[k];
20541                 var i = left;
20542                 var j = right;
20543                 swap(arr, left, k);
20544                 if (compare(arr[right], t) > 0) swap(arr, left, right);
20545
20546                 while (i < j) {
20547                   swap(arr, i, j);
20548                   i++;
20549                   j--;
20550
20551                   while (compare(arr[i], t) < 0) {
20552                     i++;
20553                   }
20554
20555                   while (compare(arr[j], t) > 0) {
20556                     j--;
20557                   }
20558                 }
20559
20560                 if (compare(arr[left], t) === 0) swap(arr, left, j);else {
20561                   j++;
20562                   swap(arr, j, right);
20563                 }
20564                 if (j <= k) left = j + 1;
20565                 if (k <= j) right = j - 1;
20566               }
20567             }
20568
20569             function swap(arr, i, j) {
20570               var tmp = arr[i];
20571               arr[i] = arr[j];
20572               arr[j] = tmp;
20573             }
20574
20575             function defaultCompare(a, b) {
20576               return a < b ? -1 : a > b ? 1 : 0;
20577             }
20578
20579             return quickselect;
20580           });
20581         });
20582
20583         var rbush_1 = rbush;
20584         var _default$1 = rbush;
20585
20586         function rbush(maxEntries, format) {
20587           if (!(this instanceof rbush)) return new rbush(maxEntries, format); // max entries in a node is 9 by default; min node fill is 40% for best performance
20588
20589           this._maxEntries = Math.max(4, maxEntries || 9);
20590           this._minEntries = Math.max(2, Math.ceil(this._maxEntries * 0.4));
20591
20592           if (format) {
20593             this._initFormat(format);
20594           }
20595
20596           this.clear();
20597         }
20598
20599         rbush.prototype = {
20600           all: function all() {
20601             return this._all(this.data, []);
20602           },
20603           search: function search(bbox) {
20604             var node = this.data,
20605                 result = [],
20606                 toBBox = this.toBBox;
20607             if (!intersects$1(bbox, node)) return result;
20608             var nodesToSearch = [],
20609                 i,
20610                 len,
20611                 child,
20612                 childBBox;
20613
20614             while (node) {
20615               for (i = 0, len = node.children.length; i < len; i++) {
20616                 child = node.children[i];
20617                 childBBox = node.leaf ? toBBox(child) : child;
20618
20619                 if (intersects$1(bbox, childBBox)) {
20620                   if (node.leaf) result.push(child);else if (contains$1(bbox, childBBox)) this._all(child, result);else nodesToSearch.push(child);
20621                 }
20622               }
20623
20624               node = nodesToSearch.pop();
20625             }
20626
20627             return result;
20628           },
20629           collides: function collides(bbox) {
20630             var node = this.data,
20631                 toBBox = this.toBBox;
20632             if (!intersects$1(bbox, node)) return false;
20633             var nodesToSearch = [],
20634                 i,
20635                 len,
20636                 child,
20637                 childBBox;
20638
20639             while (node) {
20640               for (i = 0, len = node.children.length; i < len; i++) {
20641                 child = node.children[i];
20642                 childBBox = node.leaf ? toBBox(child) : child;
20643
20644                 if (intersects$1(bbox, childBBox)) {
20645                   if (node.leaf || contains$1(bbox, childBBox)) return true;
20646                   nodesToSearch.push(child);
20647                 }
20648               }
20649
20650               node = nodesToSearch.pop();
20651             }
20652
20653             return false;
20654           },
20655           load: function load(data) {
20656             if (!(data && data.length)) return this;
20657
20658             if (data.length < this._minEntries) {
20659               for (var i = 0, len = data.length; i < len; i++) {
20660                 this.insert(data[i]);
20661               }
20662
20663               return this;
20664             } // recursively build the tree with the given data from scratch using OMT algorithm
20665
20666
20667             var node = this._build(data.slice(), 0, data.length - 1, 0);
20668
20669             if (!this.data.children.length) {
20670               // save as is if tree is empty
20671               this.data = node;
20672             } else if (this.data.height === node.height) {
20673               // split root if trees have the same height
20674               this._splitRoot(this.data, node);
20675             } else {
20676               if (this.data.height < node.height) {
20677                 // swap trees if inserted one is bigger
20678                 var tmpNode = this.data;
20679                 this.data = node;
20680                 node = tmpNode;
20681               } // insert the small tree into the large tree at appropriate level
20682
20683
20684               this._insert(node, this.data.height - node.height - 1, true);
20685             }
20686
20687             return this;
20688           },
20689           insert: function insert(item) {
20690             if (item) this._insert(item, this.data.height - 1);
20691             return this;
20692           },
20693           clear: function clear() {
20694             this.data = createNode$1([]);
20695             return this;
20696           },
20697           remove: function remove(item, equalsFn) {
20698             if (!item) return this;
20699             var node = this.data,
20700                 bbox = this.toBBox(item),
20701                 path = [],
20702                 indexes = [],
20703                 i,
20704                 parent,
20705                 index,
20706                 goingUp; // depth-first iterative tree traversal
20707
20708             while (node || path.length) {
20709               if (!node) {
20710                 // go up
20711                 node = path.pop();
20712                 parent = path[path.length - 1];
20713                 i = indexes.pop();
20714                 goingUp = true;
20715               }
20716
20717               if (node.leaf) {
20718                 // check current node
20719                 index = findItem$1(item, node.children, equalsFn);
20720
20721                 if (index !== -1) {
20722                   // item found, remove the item and condense tree upwards
20723                   node.children.splice(index, 1);
20724                   path.push(node);
20725
20726                   this._condense(path);
20727
20728                   return this;
20729                 }
20730               }
20731
20732               if (!goingUp && !node.leaf && contains$1(node, bbox)) {
20733                 // go down
20734                 path.push(node);
20735                 indexes.push(i);
20736                 i = 0;
20737                 parent = node;
20738                 node = node.children[0];
20739               } else if (parent) {
20740                 // go right
20741                 i++;
20742                 node = parent.children[i];
20743                 goingUp = false;
20744               } else node = null; // nothing found
20745
20746             }
20747
20748             return this;
20749           },
20750           toBBox: function toBBox(item) {
20751             return item;
20752           },
20753           compareMinX: compareNodeMinX$1,
20754           compareMinY: compareNodeMinY$1,
20755           toJSON: function toJSON() {
20756             return this.data;
20757           },
20758           fromJSON: function fromJSON(data) {
20759             this.data = data;
20760             return this;
20761           },
20762           _all: function _all(node, result) {
20763             var nodesToSearch = [];
20764
20765             while (node) {
20766               if (node.leaf) result.push.apply(result, node.children);else nodesToSearch.push.apply(nodesToSearch, node.children);
20767               node = nodesToSearch.pop();
20768             }
20769
20770             return result;
20771           },
20772           _build: function _build(items, left, right, height) {
20773             var N = right - left + 1,
20774                 M = this._maxEntries,
20775                 node;
20776
20777             if (N <= M) {
20778               // reached leaf level; return leaf
20779               node = createNode$1(items.slice(left, right + 1));
20780               calcBBox$1(node, this.toBBox);
20781               return node;
20782             }
20783
20784             if (!height) {
20785               // target height of the bulk-loaded tree
20786               height = Math.ceil(Math.log(N) / Math.log(M)); // target number of root entries to maximize storage utilization
20787
20788               M = Math.ceil(N / Math.pow(M, height - 1));
20789             }
20790
20791             node = createNode$1([]);
20792             node.leaf = false;
20793             node.height = height; // split the items into M mostly square tiles
20794
20795             var N2 = Math.ceil(N / M),
20796                 N1 = N2 * Math.ceil(Math.sqrt(M)),
20797                 i,
20798                 j,
20799                 right2,
20800                 right3;
20801             multiSelect$1(items, left, right, N1, this.compareMinX);
20802
20803             for (i = left; i <= right; i += N1) {
20804               right2 = Math.min(i + N1 - 1, right);
20805               multiSelect$1(items, i, right2, N2, this.compareMinY);
20806
20807               for (j = i; j <= right2; j += N2) {
20808                 right3 = Math.min(j + N2 - 1, right2); // pack each entry recursively
20809
20810                 node.children.push(this._build(items, j, right3, height - 1));
20811               }
20812             }
20813
20814             calcBBox$1(node, this.toBBox);
20815             return node;
20816           },
20817           _chooseSubtree: function _chooseSubtree(bbox, node, level, path) {
20818             var i, len, child, targetNode, area, enlargement, minArea, minEnlargement;
20819
20820             while (true) {
20821               path.push(node);
20822               if (node.leaf || path.length - 1 === level) break;
20823               minArea = minEnlargement = Infinity;
20824
20825               for (i = 0, len = node.children.length; i < len; i++) {
20826                 child = node.children[i];
20827                 area = bboxArea$1(child);
20828                 enlargement = enlargedArea$1(bbox, child) - area; // choose entry with the least area enlargement
20829
20830                 if (enlargement < minEnlargement) {
20831                   minEnlargement = enlargement;
20832                   minArea = area < minArea ? area : minArea;
20833                   targetNode = child;
20834                 } else if (enlargement === minEnlargement) {
20835                   // otherwise choose one with the smallest area
20836                   if (area < minArea) {
20837                     minArea = area;
20838                     targetNode = child;
20839                   }
20840                 }
20841               }
20842
20843               node = targetNode || node.children[0];
20844             }
20845
20846             return node;
20847           },
20848           _insert: function _insert(item, level, isNode) {
20849             var toBBox = this.toBBox,
20850                 bbox = isNode ? item : toBBox(item),
20851                 insertPath = []; // find the best node for accommodating the item, saving all nodes along the path too
20852
20853             var node = this._chooseSubtree(bbox, this.data, level, insertPath); // put the item into the node
20854
20855
20856             node.children.push(item);
20857             extend$2(node, bbox); // split on node overflow; propagate upwards if necessary
20858
20859             while (level >= 0) {
20860               if (insertPath[level].children.length > this._maxEntries) {
20861                 this._split(insertPath, level);
20862
20863                 level--;
20864               } else break;
20865             } // adjust bboxes along the insertion path
20866
20867
20868             this._adjustParentBBoxes(bbox, insertPath, level);
20869           },
20870           // split overflowed node into two
20871           _split: function _split(insertPath, level) {
20872             var node = insertPath[level],
20873                 M = node.children.length,
20874                 m = this._minEntries;
20875
20876             this._chooseSplitAxis(node, m, M);
20877
20878             var splitIndex = this._chooseSplitIndex(node, m, M);
20879
20880             var newNode = createNode$1(node.children.splice(splitIndex, node.children.length - splitIndex));
20881             newNode.height = node.height;
20882             newNode.leaf = node.leaf;
20883             calcBBox$1(node, this.toBBox);
20884             calcBBox$1(newNode, this.toBBox);
20885             if (level) insertPath[level - 1].children.push(newNode);else this._splitRoot(node, newNode);
20886           },
20887           _splitRoot: function _splitRoot(node, newNode) {
20888             // split root node
20889             this.data = createNode$1([node, newNode]);
20890             this.data.height = node.height + 1;
20891             this.data.leaf = false;
20892             calcBBox$1(this.data, this.toBBox);
20893           },
20894           _chooseSplitIndex: function _chooseSplitIndex(node, m, M) {
20895             var i, bbox1, bbox2, overlap, area, minOverlap, minArea, index;
20896             minOverlap = minArea = Infinity;
20897
20898             for (i = m; i <= M - m; i++) {
20899               bbox1 = distBBox$1(node, 0, i, this.toBBox);
20900               bbox2 = distBBox$1(node, i, M, this.toBBox);
20901               overlap = intersectionArea$1(bbox1, bbox2);
20902               area = bboxArea$1(bbox1) + bboxArea$1(bbox2); // choose distribution with minimum overlap
20903
20904               if (overlap < minOverlap) {
20905                 minOverlap = overlap;
20906                 index = i;
20907                 minArea = area < minArea ? area : minArea;
20908               } else if (overlap === minOverlap) {
20909                 // otherwise choose distribution with minimum area
20910                 if (area < minArea) {
20911                   minArea = area;
20912                   index = i;
20913                 }
20914               }
20915             }
20916
20917             return index;
20918           },
20919           // sorts node children by the best axis for split
20920           _chooseSplitAxis: function _chooseSplitAxis(node, m, M) {
20921             var compareMinX = node.leaf ? this.compareMinX : compareNodeMinX$1,
20922                 compareMinY = node.leaf ? this.compareMinY : compareNodeMinY$1,
20923                 xMargin = this._allDistMargin(node, m, M, compareMinX),
20924                 yMargin = this._allDistMargin(node, m, M, compareMinY); // if total distributions margin value is minimal for x, sort by minX,
20925             // otherwise it's already sorted by minY
20926
20927
20928             if (xMargin < yMargin) node.children.sort(compareMinX);
20929           },
20930           // total margin of all possible split distributions where each node is at least m full
20931           _allDistMargin: function _allDistMargin(node, m, M, compare) {
20932             node.children.sort(compare);
20933             var toBBox = this.toBBox,
20934                 leftBBox = distBBox$1(node, 0, m, toBBox),
20935                 rightBBox = distBBox$1(node, M - m, M, toBBox),
20936                 margin = bboxMargin$1(leftBBox) + bboxMargin$1(rightBBox),
20937                 i,
20938                 child;
20939
20940             for (i = m; i < M - m; i++) {
20941               child = node.children[i];
20942               extend$2(leftBBox, node.leaf ? toBBox(child) : child);
20943               margin += bboxMargin$1(leftBBox);
20944             }
20945
20946             for (i = M - m - 1; i >= m; i--) {
20947               child = node.children[i];
20948               extend$2(rightBBox, node.leaf ? toBBox(child) : child);
20949               margin += bboxMargin$1(rightBBox);
20950             }
20951
20952             return margin;
20953           },
20954           _adjustParentBBoxes: function _adjustParentBBoxes(bbox, path, level) {
20955             // adjust bboxes along the given tree path
20956             for (var i = level; i >= 0; i--) {
20957               extend$2(path[i], bbox);
20958             }
20959           },
20960           _condense: function _condense(path) {
20961             // go through the path, removing empty nodes and updating bboxes
20962             for (var i = path.length - 1, siblings; i >= 0; i--) {
20963               if (path[i].children.length === 0) {
20964                 if (i > 0) {
20965                   siblings = path[i - 1].children;
20966                   siblings.splice(siblings.indexOf(path[i]), 1);
20967                 } else this.clear();
20968               } else calcBBox$1(path[i], this.toBBox);
20969             }
20970           },
20971           _initFormat: function _initFormat(format) {
20972             // data format (minX, minY, maxX, maxY accessors)
20973             // uses eval-type function compilation instead of just accepting a toBBox function
20974             // because the algorithms are very sensitive to sorting functions performance,
20975             // so they should be dead simple and without inner calls
20976             var compareArr = ['return a', ' - b', ';'];
20977             this.compareMinX = new Function('a', 'b', compareArr.join(format[0]));
20978             this.compareMinY = new Function('a', 'b', compareArr.join(format[1]));
20979             this.toBBox = new Function('a', 'return {minX: a' + format[0] + ', minY: a' + format[1] + ', maxX: a' + format[2] + ', maxY: a' + format[3] + '};');
20980           }
20981         };
20982
20983         function findItem$1(item, items, equalsFn) {
20984           if (!equalsFn) return items.indexOf(item);
20985
20986           for (var i = 0; i < items.length; i++) {
20987             if (equalsFn(item, items[i])) return i;
20988           }
20989
20990           return -1;
20991         } // calculate node's bbox from bboxes of its children
20992
20993
20994         function calcBBox$1(node, toBBox) {
20995           distBBox$1(node, 0, node.children.length, toBBox, node);
20996         } // min bounding rectangle of node children from k to p-1
20997
20998
20999         function distBBox$1(node, k, p, toBBox, destNode) {
21000           if (!destNode) destNode = createNode$1(null);
21001           destNode.minX = Infinity;
21002           destNode.minY = Infinity;
21003           destNode.maxX = -Infinity;
21004           destNode.maxY = -Infinity;
21005
21006           for (var i = k, child; i < p; i++) {
21007             child = node.children[i];
21008             extend$2(destNode, node.leaf ? toBBox(child) : child);
21009           }
21010
21011           return destNode;
21012         }
21013
21014         function extend$2(a, b) {
21015           a.minX = Math.min(a.minX, b.minX);
21016           a.minY = Math.min(a.minY, b.minY);
21017           a.maxX = Math.max(a.maxX, b.maxX);
21018           a.maxY = Math.max(a.maxY, b.maxY);
21019           return a;
21020         }
21021
21022         function compareNodeMinX$1(a, b) {
21023           return a.minX - b.minX;
21024         }
21025
21026         function compareNodeMinY$1(a, b) {
21027           return a.minY - b.minY;
21028         }
21029
21030         function bboxArea$1(a) {
21031           return (a.maxX - a.minX) * (a.maxY - a.minY);
21032         }
21033
21034         function bboxMargin$1(a) {
21035           return a.maxX - a.minX + (a.maxY - a.minY);
21036         }
21037
21038         function enlargedArea$1(a, b) {
21039           return (Math.max(b.maxX, a.maxX) - Math.min(b.minX, a.minX)) * (Math.max(b.maxY, a.maxY) - Math.min(b.minY, a.minY));
21040         }
21041
21042         function intersectionArea$1(a, b) {
21043           var minX = Math.max(a.minX, b.minX),
21044               minY = Math.max(a.minY, b.minY),
21045               maxX = Math.min(a.maxX, b.maxX),
21046               maxY = Math.min(a.maxY, b.maxY);
21047           return Math.max(0, maxX - minX) * Math.max(0, maxY - minY);
21048         }
21049
21050         function contains$1(a, b) {
21051           return a.minX <= b.minX && a.minY <= b.minY && b.maxX <= a.maxX && b.maxY <= a.maxY;
21052         }
21053
21054         function intersects$1(a, b) {
21055           return b.minX <= a.maxX && b.minY <= a.maxY && b.maxX >= a.minX && b.maxY >= a.minY;
21056         }
21057
21058         function createNode$1(children) {
21059           return {
21060             children: children,
21061             height: 1,
21062             leaf: true,
21063             minX: Infinity,
21064             minY: Infinity,
21065             maxX: -Infinity,
21066             maxY: -Infinity
21067           };
21068         } // sort an array so that items come in groups of n unsorted items, with groups sorted between each other;
21069         // combines selection algorithm with binary divide & conquer approach
21070
21071
21072         function multiSelect$1(arr, left, right, n, compare) {
21073           var stack = [left, right],
21074               mid;
21075
21076           while (stack.length) {
21077             right = stack.pop();
21078             left = stack.pop();
21079             if (right - left <= n) continue;
21080             mid = left + Math.ceil((right - left) / n / 2) * n;
21081             quickselect$1(arr, mid, left, right, compare);
21082             stack.push(left, mid, mid, right);
21083           }
21084         }
21085         rbush_1["default"] = _default$1;
21086
21087         var lineclip_1 = lineclip$1;
21088         lineclip$1.polyline = lineclip$1;
21089         lineclip$1.polygon = polygonclip$1; // Cohen-Sutherland line clippign algorithm, adapted to efficiently
21090         // handle polylines rather than just segments
21091
21092         function lineclip$1(points, bbox, result) {
21093           var len = points.length,
21094               codeA = bitCode$1(points[0], bbox),
21095               part = [],
21096               i,
21097               a,
21098               b,
21099               codeB,
21100               lastCode;
21101           if (!result) result = [];
21102
21103           for (i = 1; i < len; i++) {
21104             a = points[i - 1];
21105             b = points[i];
21106             codeB = lastCode = bitCode$1(b, bbox);
21107
21108             while (true) {
21109               if (!(codeA | codeB)) {
21110                 // accept
21111                 part.push(a);
21112
21113                 if (codeB !== lastCode) {
21114                   // segment went outside
21115                   part.push(b);
21116
21117                   if (i < len - 1) {
21118                     // start a new line
21119                     result.push(part);
21120                     part = [];
21121                   }
21122                 } else if (i === len - 1) {
21123                   part.push(b);
21124                 }
21125
21126                 break;
21127               } else if (codeA & codeB) {
21128                 // trivial reject
21129                 break;
21130               } else if (codeA) {
21131                 // a outside, intersect with clip edge
21132                 a = intersect$1(a, b, codeA, bbox);
21133                 codeA = bitCode$1(a, bbox);
21134               } else {
21135                 // b outside
21136                 b = intersect$1(a, b, codeB, bbox);
21137                 codeB = bitCode$1(b, bbox);
21138               }
21139             }
21140
21141             codeA = lastCode;
21142           }
21143
21144           if (part.length) result.push(part);
21145           return result;
21146         } // Sutherland-Hodgeman polygon clipping algorithm
21147
21148
21149         function polygonclip$1(points, bbox) {
21150           var result, edge, prev, prevInside, i, p, inside; // clip against each side of the clip rectangle
21151
21152           for (edge = 1; edge <= 8; edge *= 2) {
21153             result = [];
21154             prev = points[points.length - 1];
21155             prevInside = !(bitCode$1(prev, bbox) & edge);
21156
21157             for (i = 0; i < points.length; i++) {
21158               p = points[i];
21159               inside = !(bitCode$1(p, bbox) & edge); // if segment goes through the clip window, add an intersection
21160
21161               if (inside !== prevInside) result.push(intersect$1(prev, p, edge, bbox));
21162               if (inside) result.push(p); // add a point if it's inside
21163
21164               prev = p;
21165               prevInside = inside;
21166             }
21167
21168             points = result;
21169             if (!points.length) break;
21170           }
21171
21172           return result;
21173         } // intersect a segment against one of the 4 lines that make up the bbox
21174
21175
21176         function intersect$1(a, b, edge, bbox) {
21177           return edge & 8 ? [a[0] + (b[0] - a[0]) * (bbox[3] - a[1]) / (b[1] - a[1]), bbox[3]] : // top
21178           edge & 4 ? [a[0] + (b[0] - a[0]) * (bbox[1] - a[1]) / (b[1] - a[1]), bbox[1]] : // bottom
21179           edge & 2 ? [bbox[2], a[1] + (b[1] - a[1]) * (bbox[2] - a[0]) / (b[0] - a[0])] : // right
21180           edge & 1 ? [bbox[0], a[1] + (b[1] - a[1]) * (bbox[0] - a[0]) / (b[0] - a[0])] : // left
21181           null;
21182         } // bit code reflects the point position relative to the bbox:
21183         //         left  mid  right
21184         //    top  1001  1000  1010
21185         //    mid  0001  0000  0010
21186         // bottom  0101  0100  0110
21187
21188
21189         function bitCode$1(p, bbox) {
21190           var code = 0;
21191           if (p[0] < bbox[0]) code |= 1; // left
21192           else if (p[0] > bbox[2]) code |= 2; // right
21193
21194           if (p[1] < bbox[1]) code |= 4; // bottom
21195           else if (p[1] > bbox[3]) code |= 8; // top
21196
21197           return code;
21198         }
21199
21200         var whichPolygon_1 = whichPolygon;
21201
21202         function whichPolygon(data) {
21203           var bboxes = [];
21204
21205           for (var i = 0; i < data.features.length; i++) {
21206             var feature = data.features[i];
21207             var coords = feature.geometry.coordinates;
21208
21209             if (feature.geometry.type === 'Polygon') {
21210               bboxes.push(treeItem(coords, feature.properties));
21211             } else if (feature.geometry.type === 'MultiPolygon') {
21212               for (var j = 0; j < coords.length; j++) {
21213                 bboxes.push(treeItem(coords[j], feature.properties));
21214               }
21215             }
21216           }
21217
21218           var tree = rbush_1().load(bboxes);
21219
21220           function query(p, multi) {
21221             var output = [],
21222                 result = tree.search({
21223               minX: p[0],
21224               minY: p[1],
21225               maxX: p[0],
21226               maxY: p[1]
21227             });
21228
21229             for (var i = 0; i < result.length; i++) {
21230               if (insidePolygon(result[i].coords, p)) {
21231                 if (multi) output.push(result[i].props);else return result[i].props;
21232               }
21233             }
21234
21235             return multi && output.length ? output : null;
21236           }
21237
21238           query.tree = tree;
21239
21240           query.bbox = function queryBBox(bbox) {
21241             var output = [];
21242             var result = tree.search({
21243               minX: bbox[0],
21244               minY: bbox[1],
21245               maxX: bbox[2],
21246               maxY: bbox[3]
21247             });
21248
21249             for (var i = 0; i < result.length; i++) {
21250               if (polygonIntersectsBBox(result[i].coords, bbox)) {
21251                 output.push(result[i].props);
21252               }
21253             }
21254
21255             return output;
21256           };
21257
21258           return query;
21259         }
21260
21261         function polygonIntersectsBBox(polygon, bbox) {
21262           var bboxCenter = [(bbox[0] + bbox[2]) / 2, (bbox[1] + bbox[3]) / 2];
21263           if (insidePolygon(polygon, bboxCenter)) return true;
21264
21265           for (var i = 0; i < polygon.length; i++) {
21266             if (lineclip_1(polygon[i], bbox).length > 0) return true;
21267           }
21268
21269           return false;
21270         } // ray casting algorithm for detecting if point is in polygon
21271
21272
21273         function insidePolygon(rings, p) {
21274           var inside = false;
21275
21276           for (var i = 0, len = rings.length; i < len; i++) {
21277             var ring = rings[i];
21278
21279             for (var j = 0, len2 = ring.length, k = len2 - 1; j < len2; k = j++) {
21280               if (rayIntersect(p, ring[j], ring[k])) inside = !inside;
21281             }
21282           }
21283
21284           return inside;
21285         }
21286
21287         function rayIntersect(p, p1, p2) {
21288           return p1[1] > p[1] !== p2[1] > p[1] && p[0] < (p2[0] - p1[0]) * (p[1] - p1[1]) / (p2[1] - p1[1]) + p1[0];
21289         }
21290
21291         function treeItem(coords, props) {
21292           var item = {
21293             minX: Infinity,
21294             minY: Infinity,
21295             maxX: -Infinity,
21296             maxY: -Infinity,
21297             coords: coords,
21298             props: props
21299           };
21300
21301           for (var i = 0; i < coords[0].length; i++) {
21302             var p = coords[0][i];
21303             item.minX = Math.min(item.minX, p[0]);
21304             item.minY = Math.min(item.minY, p[1]);
21305             item.maxX = Math.max(item.maxX, p[0]);
21306             item.maxY = Math.max(item.maxY, p[1]);
21307           }
21308
21309           return item;
21310         }
21311
21312         var type = "FeatureCollection";
21313         var features = [{
21314           type: "Feature",
21315           properties: {
21316             wikidata: "Q21",
21317             nameEn: "England",
21318             aliases: ["GB-ENG"],
21319             country: "GB",
21320             groups: ["Q23666", "Q3336843", "154", "150", "UN"],
21321             driveSide: "left",
21322             roadSpeedUnit: "mph",
21323             roadHeightUnit: "ft",
21324             callingCodes: ["44"]
21325           },
21326           geometry: {
21327             type: "MultiPolygon",
21328             coordinates: [[[[-6.03913, 51.13217], [-7.74976, 48.64773], [1.17405, 50.74239], [2.18458, 51.52087], [2.56575, 51.85301], [0.792, 57.56437], [-2.30613, 55.62698], [-2.17058, 55.45916], [-2.6095, 55.28488], [-2.63532, 55.19452], [-3.02906, 55.04606], [-3.09361, 54.94924], [-3.38407, 54.94278], [-4.1819, 54.57861], [-3.5082, 53.54318], [-3.08228, 53.25526], [-3.03675, 53.25092], [-2.92329, 53.19383], [-2.92022, 53.17685], [-2.98598, 53.15589], [-2.90649, 53.10964], [-2.87469, 53.12337], [-2.89131, 53.09374], [-2.83133, 52.99184], [-2.7251, 52.98389], [-2.72221, 52.92969], [-2.80549, 52.89428], [-2.85897, 52.94487], [-2.92401, 52.93836], [-2.97243, 52.9651], [-3.13576, 52.895], [-3.15744, 52.84947], [-3.16105, 52.79599], [-3.08734, 52.77504], [-3.01001, 52.76636], [-2.95581, 52.71794], [-3.01724, 52.72083], [-3.04398, 52.65435], [-3.13648, 52.58208], [-3.12926, 52.5286], [-3.09746, 52.53077], [-3.08662, 52.54811], [-3.00929, 52.57774], [-2.99701, 52.551], [-3.03603, 52.49969], [-3.13359, 52.49174], [-3.22971, 52.45344], [-3.22754, 52.42526], [-3.04687, 52.34504], [-2.95364, 52.3501], [-2.99701, 52.323], [-3.00785, 52.2753], [-3.09289, 52.20546], [-3.12638, 52.08114], [-2.97111, 51.90456], [-2.8818, 51.93196], [-2.78742, 51.88833], [-2.74277, 51.84367], [-2.66234, 51.83555], [-2.66336, 51.59504], [-3.20563, 51.31615], [-6.03913, 51.13217]]]]
21329           }
21330         }, {
21331           type: "Feature",
21332           properties: {
21333             wikidata: "Q22",
21334             nameEn: "Scotland",
21335             aliases: ["GB-SCT"],
21336             country: "GB",
21337             groups: ["Q23666", "Q3336843", "154", "150", "UN"],
21338             driveSide: "left",
21339             roadSpeedUnit: "mph",
21340             roadHeightUnit: "ft",
21341             callingCodes: ["44"]
21342           },
21343           geometry: {
21344             type: "MultiPolygon",
21345             coordinates: [[[[0.792, 57.56437], [-0.3751, 61.32236], [-14.78497, 57.60709], [-6.82333, 55.83103], [-4.69044, 54.3629], [-3.38407, 54.94278], [-3.09361, 54.94924], [-3.02906, 55.04606], [-2.63532, 55.19452], [-2.6095, 55.28488], [-2.17058, 55.45916], [-2.30613, 55.62698], [0.792, 57.56437]]]]
21346           }
21347         }, {
21348           type: "Feature",
21349           properties: {
21350             wikidata: "Q25",
21351             nameEn: "Wales",
21352             aliases: ["GB-WLS"],
21353             country: "GB",
21354             groups: ["Q23666", "Q3336843", "154", "150", "UN"],
21355             driveSide: "left",
21356             roadSpeedUnit: "mph",
21357             roadHeightUnit: "ft",
21358             callingCodes: ["44"]
21359           },
21360           geometry: {
21361             type: "MultiPolygon",
21362             coordinates: [[[[-3.5082, 53.54318], [-5.37267, 53.63269], [-6.03913, 51.13217], [-3.20563, 51.31615], [-2.66336, 51.59504], [-2.66234, 51.83555], [-2.74277, 51.84367], [-2.78742, 51.88833], [-2.8818, 51.93196], [-2.97111, 51.90456], [-3.12638, 52.08114], [-3.09289, 52.20546], [-3.00785, 52.2753], [-2.99701, 52.323], [-2.95364, 52.3501], [-3.04687, 52.34504], [-3.22754, 52.42526], [-3.22971, 52.45344], [-3.13359, 52.49174], [-3.03603, 52.49969], [-2.99701, 52.551], [-3.00929, 52.57774], [-3.08662, 52.54811], [-3.09746, 52.53077], [-3.12926, 52.5286], [-3.13648, 52.58208], [-3.04398, 52.65435], [-3.01724, 52.72083], [-2.95581, 52.71794], [-3.01001, 52.76636], [-3.08734, 52.77504], [-3.16105, 52.79599], [-3.15744, 52.84947], [-3.13576, 52.895], [-2.97243, 52.9651], [-2.92401, 52.93836], [-2.85897, 52.94487], [-2.80549, 52.89428], [-2.72221, 52.92969], [-2.7251, 52.98389], [-2.83133, 52.99184], [-2.89131, 53.09374], [-2.87469, 53.12337], [-2.90649, 53.10964], [-2.98598, 53.15589], [-2.92022, 53.17685], [-2.92329, 53.19383], [-3.03675, 53.25092], [-3.08228, 53.25526], [-3.5082, 53.54318]]]]
21363           }
21364         }, {
21365           type: "Feature",
21366           properties: {
21367             wikidata: "Q26",
21368             nameEn: "Northern Ireland",
21369             aliases: ["GB-NIR"],
21370             country: "GB",
21371             groups: ["Q22890", "Q3336843", "154", "150", "UN"],
21372             driveSide: "left",
21373             roadSpeedUnit: "mph",
21374             roadHeightUnit: "ft",
21375             callingCodes: ["44"]
21376           },
21377           geometry: {
21378             type: "MultiPolygon",
21379             coordinates: [[[[-6.34755, 55.49206], [-7.2471, 55.06933], [-7.34464, 55.04688], [-7.4033, 55.00391], [-7.40004, 54.94498], [-7.44404, 54.9403], [-7.4473, 54.87003], [-7.47626, 54.83084], [-7.54508, 54.79401], [-7.54671, 54.74606], [-7.64449, 54.75265], [-7.75041, 54.7103], [-7.83352, 54.73854], [-7.93293, 54.66603], [-7.70315, 54.62077], [-7.8596, 54.53671], [-7.99812, 54.54427], [-8.04538, 54.48941], [-8.179, 54.46763], [-8.04555, 54.36292], [-7.87101, 54.29299], [-7.8596, 54.21779], [-7.81397, 54.20159], [-7.69501, 54.20731], [-7.55812, 54.12239], [-7.4799, 54.12239], [-7.44567, 54.1539], [-7.32834, 54.11475], [-7.30553, 54.11869], [-7.34005, 54.14698], [-7.29157, 54.17191], [-7.28017, 54.16714], [-7.29687, 54.1354], [-7.29493, 54.12013], [-7.26316, 54.13863], [-7.25012, 54.20063], [-7.14908, 54.22732], [-7.19145, 54.31296], [-7.02034, 54.4212], [-6.87775, 54.34682], [-6.85179, 54.29176], [-6.81583, 54.22791], [-6.74575, 54.18788], [-6.70175, 54.20218], [-6.6382, 54.17071], [-6.66264, 54.0666], [-6.62842, 54.03503], [-6.47849, 54.06947], [-6.36605, 54.07234], [-6.36279, 54.11248], [-6.32694, 54.09337], [-6.29003, 54.11278], [-6.26218, 54.09785], [-5.83481, 53.87749], [-4.69044, 54.3629], [-6.34755, 55.49206]]]]
21380           }
21381         }, {
21382           type: "Feature",
21383           properties: {
21384             wikidata: "Q35",
21385             nameEn: "Denmark",
21386             country: "DK",
21387             groups: ["EU", "154", "150", "UN"],
21388             callingCodes: ["45"]
21389           },
21390           geometry: {
21391             type: "MultiPolygon",
21392             coordinates: [[[[12.16597, 56.60205], [10.40861, 58.38489], [7.28637, 57.35913], [8.02459, 55.09613], [8.45719, 55.06747], [8.55769, 54.91837], [8.63979, 54.91069], [8.76387, 54.8948], [8.81178, 54.90518], [8.92795, 54.90452], [9.04629, 54.87249], [9.14275, 54.87421], [9.20571, 54.85841], [9.24631, 54.84726], [9.23445, 54.83432], [9.2474, 54.8112], [9.32771, 54.80602], [9.33849, 54.80233], [9.36496, 54.81749], [9.38532, 54.83968], [9.41213, 54.84254], [9.43155, 54.82586], [9.4659, 54.83131], [9.58937, 54.88785], [9.62734, 54.88057], [9.61187, 54.85548], [9.73563, 54.8247], [9.89314, 54.84171], [10.16755, 54.73883], [10.31111, 54.65968], [11.00303, 54.63689], [11.90309, 54.38543], [12.85844, 54.82438], [13.93395, 54.84044], [15.36991, 54.73263], [15.79951, 55.54655], [14.89259, 55.5623], [14.28399, 55.1553], [12.84405, 55.13257], [12.60345, 55.42675], [12.88472, 55.63369], [12.6372, 55.91371], [12.65312, 56.04345], [12.07466, 56.29488], [12.16597, 56.60205]]]]
21393           }
21394         }, {
21395           type: "Feature",
21396           properties: {
21397             wikidata: "Q55",
21398             nameEn: "Netherlands",
21399             country: "NL",
21400             groups: ["EU", "155", "150", "UN"],
21401             callingCodes: ["31"]
21402           },
21403           geometry: {
21404             type: "MultiPolygon",
21405             coordinates: [[[[5.45168, 54.20039], [2.56575, 51.85301], [3.36263, 51.37112], [3.38696, 51.33436], [3.35847, 51.31572], [3.38289, 51.27331], [3.41704, 51.25933], [3.43488, 51.24135], [3.52698, 51.2458], [3.51502, 51.28697], [3.58939, 51.30064], [3.78999, 51.25766], [3.78783, 51.2151], [3.90125, 51.20371], [3.97889, 51.22537], [4.01957, 51.24504], [4.05165, 51.24171], [4.16721, 51.29348], [4.24024, 51.35371], [4.21923, 51.37443], [4.33265, 51.37687], [4.34086, 51.35738], [4.39292, 51.35547], [4.43777, 51.36989], [4.38064, 51.41965], [4.39747, 51.43316], [4.38122, 51.44905], [4.47736, 51.4778], [4.5388, 51.48184], [4.54675, 51.47265], [4.52846, 51.45002], [4.53521, 51.4243], [4.57489, 51.4324], [4.65442, 51.42352], [4.72935, 51.48424], [4.74578, 51.48937], [4.77321, 51.50529], [4.78803, 51.50284], [4.84139, 51.4799], [4.82409, 51.44736], [4.82946, 51.4213], [4.78314, 51.43319], [4.76577, 51.43046], [4.77229, 51.41337], [4.78941, 51.41102], [4.84988, 51.41502], [4.90016, 51.41404], [4.92152, 51.39487], [5.00393, 51.44406], [5.0106, 51.47167], [5.03281, 51.48679], [5.04774, 51.47022], [5.07891, 51.4715], [5.10456, 51.43163], [5.07102, 51.39469], [5.13105, 51.34791], [5.13377, 51.31592], [5.16222, 51.31035], [5.2002, 51.32243], [5.24244, 51.30495], [5.22542, 51.26888], [5.23814, 51.26064], [5.26461, 51.26693], [5.29716, 51.26104], [5.33886, 51.26314], [5.347, 51.27502], [5.41672, 51.26248], [5.4407, 51.28169], [5.46519, 51.2849], [5.48476, 51.30053], [5.515, 51.29462], [5.5569, 51.26544], [5.5603, 51.22249], [5.65145, 51.19788], [5.65528, 51.18736], [5.70344, 51.1829], [5.74617, 51.18928], [5.77735, 51.17845], [5.77697, 51.1522], [5.82564, 51.16753], [5.85508, 51.14445], [5.80798, 51.11661], [5.8109, 51.10861], [5.83226, 51.10585], [5.82921, 51.09328], [5.79903, 51.09371], [5.79835, 51.05834], [5.77258, 51.06196], [5.75961, 51.03113], [5.77688, 51.02483], [5.76242, 50.99703], [5.71864, 50.96092], [5.72875, 50.95428], [5.74752, 50.96202], [5.75927, 50.95601], [5.74644, 50.94723], [5.72545, 50.92312], [5.72644, 50.91167], [5.71626, 50.90796], [5.69858, 50.91046], [5.67886, 50.88142], [5.64504, 50.87107], [5.64009, 50.84742], [5.65259, 50.82309], [5.70118, 50.80764], [5.68995, 50.79641], [5.70107, 50.7827], [5.68091, 50.75804], [5.69469, 50.75529], [5.72216, 50.76398], [5.73904, 50.75674], [5.74356, 50.7691], [5.76533, 50.78159], [5.77513, 50.78308], [5.80673, 50.7558], [5.84548, 50.76542], [5.84888, 50.75448], [5.88734, 50.77092], [5.89129, 50.75125], [5.89132, 50.75124], [5.95942, 50.7622], [5.97545, 50.75441], [6.01976, 50.75398], [6.02624, 50.77453], [5.97497, 50.79992], [5.98404, 50.80988], [6.00462, 50.80065], [6.02328, 50.81694], [6.01921, 50.84435], [6.05623, 50.8572], [6.05702, 50.85179], [6.07431, 50.84674], [6.07693, 50.86025], [6.08805, 50.87223], [6.07486, 50.89307], [6.09297, 50.92066], [6.01615, 50.93367], [6.02697, 50.98303], [5.95282, 50.98728], [5.90296, 50.97356], [5.90493, 51.00198], [5.87849, 51.01969], [5.86735, 51.05182], [5.9134, 51.06736], [5.9541, 51.03496], [5.98292, 51.07469], [6.16706, 51.15677], [6.17384, 51.19589], [6.07889, 51.17038], [6.07889, 51.24432], [6.16977, 51.33169], [6.22674, 51.36135], [6.22641, 51.39948], [6.20654, 51.40049], [6.21724, 51.48568], [6.18017, 51.54096], [6.09055, 51.60564], [6.11759, 51.65609], [6.02767, 51.6742], [6.04091, 51.71821], [5.95003, 51.7493], [5.98665, 51.76944], [5.94568, 51.82786], [5.99848, 51.83195], [6.06705, 51.86136], [6.10337, 51.84829], [6.16902, 51.84094], [6.11551, 51.89769], [6.15349, 51.90439], [6.21443, 51.86801], [6.29872, 51.86801], [6.30593, 51.84998], [6.40704, 51.82771], [6.38815, 51.87257], [6.47179, 51.85395], [6.50231, 51.86313], [6.58556, 51.89386], [6.68386, 51.91861], [6.72319, 51.89518], [6.82357, 51.96711], [6.83035, 51.9905], [6.68128, 52.05052], [6.76117, 52.11895], [6.83984, 52.11728], [6.97189, 52.20329], [6.9897, 52.2271], [7.03729, 52.22695], [7.06365, 52.23789], [7.02703, 52.27941], [7.07044, 52.37805], [7.03417, 52.40237], [6.99041, 52.47235], [6.94293, 52.43597], [6.69507, 52.488], [6.71641, 52.62905], [6.77307, 52.65375], [7.04557, 52.63318], [7.07253, 52.81083], [7.21694, 53.00742], [7.17898, 53.13817], [7.22681, 53.18165], [7.21679, 53.20058], [7.19052, 53.31866], [7.00198, 53.32672], [6.91025, 53.44221], [5.45168, 54.20039]], [[4.93295, 51.44945], [4.95244, 51.45207], [4.9524, 51.45014], [4.93909, 51.44632], [4.93295, 51.44945]], [[4.91493, 51.4353], [4.91935, 51.43634], [4.92227, 51.44252], [4.91811, 51.44621], [4.92287, 51.44741], [4.92811, 51.4437], [4.92566, 51.44273], [4.92815, 51.43856], [4.92879, 51.44161], [4.93544, 51.44634], [4.94025, 51.44193], [4.93416, 51.44185], [4.93471, 51.43861], [4.94265, 51.44003], [4.93986, 51.43064], [4.92952, 51.42984], [4.92652, 51.43329], [4.91493, 51.4353]]]]
21406           }
21407         }, {
21408           type: "Feature",
21409           properties: {
21410             wikidata: "Q782",
21411             nameEn: "Hawaii",
21412             aliases: ["US-HI"],
21413             country: "US",
21414             groups: ["Q35657", "061", "009", "UN"],
21415             roadSpeedUnit: "mph",
21416             roadHeightUnit: "ft",
21417             callingCodes: ["1"]
21418           },
21419           geometry: {
21420             type: "MultiPolygon",
21421             coordinates: [[[[-177.8563, 29.18961], [-179.49839, 27.86265], [-151.6784, 9.55515], [-154.05867, 45.51124], [-177.5224, 27.7635], [-177.8563, 29.18961]]]]
21422           }
21423         }, {
21424           type: "Feature",
21425           properties: {
21426             wikidata: "Q797",
21427             nameEn: "Alaska",
21428             aliases: ["US-AK"],
21429             country: "US",
21430             groups: ["Q35657", "021", "003", "019", "UN"],
21431             roadSpeedUnit: "mph",
21432             roadHeightUnit: "ft",
21433             callingCodes: ["1"]
21434           },
21435           geometry: {
21436             type: "MultiPolygon",
21437             coordinates: [[[[169.34848, 52.47228], [180, 51.0171], [179.84401, 55.10087], [169.34848, 52.47228]]], [[[-168.95635, 65.98512], [-169.03888, 65.48473], [-172.76104, 63.77445], [-179.55295, 57.62081], [-179.55295, 50.81807], [-133.92876, 54.62289], [-130.61931, 54.70835], [-130.64499, 54.76912], [-130.44184, 54.85377], [-130.27203, 54.97174], [-130.18765, 55.07744], [-130.08035, 55.21556], [-129.97513, 55.28029], [-130.15373, 55.74895], [-130.00857, 55.91344], [-130.00093, 56.00325], [-130.10173, 56.12178], [-130.33965, 56.10849], [-130.77769, 56.36185], [-131.8271, 56.62247], [-133.38523, 58.42773], [-133.84645, 58.73543], [-134.27175, 58.8634], [-134.48059, 59.13231], [-134.55699, 59.1297], [-134.7047, 59.2458], [-135.00267, 59.28745], [-135.03069, 59.56208], [-135.48007, 59.79937], [-136.31566, 59.59083], [-136.22381, 59.55526], [-136.33727, 59.44466], [-136.47323, 59.46617], [-136.52365, 59.16752], [-136.82619, 59.16198], [-137.4925, 58.89415], [-137.60623, 59.24465], [-138.62145, 59.76431], [-138.71149, 59.90728], [-139.05365, 59.99655], [-139.20603, 60.08896], [-139.05831, 60.35205], [-139.68991, 60.33693], [-139.98024, 60.18027], [-140.45648, 60.30919], [-140.5227, 60.22077], [-141.00116, 60.30648], [-140.97446, 84.39275], [-168.25765, 71.99091], [-168.95635, 65.98512]]]]
21438           }
21439         }, {
21440           type: "Feature",
21441           properties: {
21442             wikidata: "Q3492",
21443             nameEn: "Sumatra",
21444             aliases: ["ID-SM"],
21445             country: "ID",
21446             groups: ["035", "142", "UN"],
21447             driveSide: "left",
21448             callingCodes: ["62"]
21449           },
21450           geometry: {
21451             type: "MultiPolygon",
21452             coordinates: [[[[109.82788, 2.86812], [110.90339, 7.52694], [105.01437, 3.24936], [104.56723, 1.44271], [104.34728, 1.33529], [104.12282, 1.27714], [104.03085, 1.26954], [103.74084, 1.12902], [103.66049, 1.18825], [103.56591, 1.19719], [103.03657, 1.30383], [96.11174, 6.69841], [74.28481, -3.17525], [102.92489, -8.17146], [106.32259, -5.50116], [106.38511, -5.16715], [109.17017, -4.07401], [109.3962, -2.07276], [108.50935, -2.01066], [107.94791, 1.06924], [109.82788, 2.86812]]]]
21453           }
21454         }, {
21455           type: "Feature",
21456           properties: {
21457             wikidata: "Q3757",
21458             nameEn: "Java",
21459             aliases: ["ID-JW"],
21460             country: "ID",
21461             groups: ["035", "142", "UN"],
21462             driveSide: "left",
21463             callingCodes: ["62"]
21464           },
21465           geometry: {
21466             type: "MultiPolygon",
21467             coordinates: [[[[109.17017, -4.07401], [106.38511, -5.16715], [106.32259, -5.50116], [102.92489, -8.17146], [116.22542, -10.49172], [114.39575, -8.2889], [114.42235, -8.09762], [114.92859, -7.49253], [116.33992, -7.56171], [116.58433, -5.30385], [109.17017, -4.07401]]]]
21468           }
21469         }, {
21470           type: "Feature",
21471           properties: {
21472             wikidata: "Q3795",
21473             nameEn: "Kalimantan",
21474             aliases: ["ID-KA"],
21475             country: "ID",
21476             groups: ["Q36117", "035", "142", "UN"],
21477             driveSide: "left",
21478             callingCodes: ["62"]
21479           },
21480           geometry: {
21481             type: "MultiPolygon",
21482             coordinates: [[[[120.02464, 2.83703], [118.06469, 4.16638], [117.67641, 4.16535], [117.47313, 4.18857], [117.25801, 4.35108], [115.90217, 4.37708], [115.58276, 3.93499], [115.53713, 3.14776], [115.11343, 2.82879], [115.1721, 2.49671], [114.80706, 2.21665], [114.80706, 1.92351], [114.57892, 1.5], [114.03788, 1.44787], [113.64677, 1.23933], [113.01448, 1.42832], [113.021, 1.57819], [112.48648, 1.56516], [112.2127, 1.44135], [112.15679, 1.17004], [111.94553, 1.12016], [111.82846, 0.99349], [111.55434, 0.97864], [111.22979, 1.08326], [110.62374, 0.873], [110.49182, 0.88088], [110.35354, 0.98869], [109.66397, 1.60425], [109.66397, 1.79972], [109.57923, 1.80624], [109.53794, 1.91771], [109.62558, 1.99182], [109.82788, 2.86812], [107.94791, 1.06924], [108.50935, -2.01066], [109.3962, -2.07276], [109.17017, -4.07401], [116.58433, -5.30385], [120.02464, 2.83703]]]]
21483           }
21484         }, {
21485           type: "Feature",
21486           properties: {
21487             wikidata: "Q3803",
21488             nameEn: "Lesser Sunda Islands",
21489             aliases: ["ID-NU"],
21490             country: "ID",
21491             groups: ["035", "142", "UN"],
21492             driveSide: "left",
21493             callingCodes: ["62"]
21494           },
21495           geometry: {
21496             type: "MultiPolygon",
21497             coordinates: [[[[116.96967, -8.01483], [114.92859, -7.49253], [114.42235, -8.09762], [114.39575, -8.2889], [116.22542, -10.49172], [122.14954, -11.52517], [125.68138, -9.85176], [125.09025, -9.46406], [124.97892, -9.19281], [125.04044, -9.17093], [125.09434, -9.19669], [125.18907, -9.16434], [125.18632, -9.03142], [125.11764, -8.96359], [124.97742, -9.08128], [124.94011, -8.85617], [124.46701, -9.13002], [124.45971, -9.30263], [124.38554, -9.3582], [124.35258, -9.43002], [124.3535, -9.48493], [124.28115, -9.50453], [124.28115, -9.42189], [124.21247, -9.36904], [124.14517, -9.42324], [124.10539, -9.41206], [124.04286, -9.34243], [124.04628, -9.22671], [124.33472, -9.11416], [124.92337, -8.75859], [125.87688, -7.49892], [116.96967, -8.01483]]]]
21498           }
21499         }, {
21500           type: "Feature",
21501           properties: {
21502             wikidata: "Q3812",
21503             nameEn: "Sulawesi",
21504             aliases: ["ID-SL"],
21505             country: "ID",
21506             groups: ["035", "142", "UN"],
21507             driveSide: "left",
21508             callingCodes: ["62"]
21509           },
21510           geometry: {
21511             type: "MultiPolygon",
21512             coordinates: [[[[128.34321, 3.90322], [126.69413, 6.02692], [119.56457, 0.90759], [116.58433, -5.30385], [116.33992, -7.56171], [116.96967, -8.01483], [125.87688, -7.49892], [123.78965, -0.86805], [128.34321, 3.90322]]]]
21513           }
21514         }, {
21515           type: "Feature",
21516           properties: {
21517             wikidata: "Q3827",
21518             nameEn: "Maluku Islands",
21519             aliases: ["ID-ML"],
21520             country: "ID",
21521             groups: ["035", "142", "UN"],
21522             driveSide: "left",
21523             callingCodes: ["62"]
21524           },
21525           geometry: {
21526             type: "MultiPolygon",
21527             coordinates: [[[[129.63187, 2.21409], [128.34321, 3.90322], [123.78965, -0.86805], [125.87688, -7.49892], [125.58506, -7.95311], [125.87691, -8.31789], [127.42116, -8.22471], [127.55165, -9.05052], [135.49042, -9.2276], [135.35517, -5.01442], [132.8312, -4.70282], [130.8468, -2.61103], [128.40647, -2.30349], [129.71519, -0.24692], [129.63187, 2.21409]]]]
21528           }
21529         }, {
21530           type: "Feature",
21531           properties: {
21532             wikidata: "Q3845",
21533             nameEn: "Western New Guinea",
21534             aliases: ["ID-PP"],
21535             country: "ID",
21536             groups: ["035", "142", "UN"],
21537             driveSide: "left",
21538             callingCodes: ["62"]
21539           },
21540           geometry: {
21541             type: "MultiPolygon",
21542             coordinates: [[[[135.49042, -9.2276], [141.01842, -9.35091], [141.01763, -6.90181], [140.90448, -6.85033], [140.85295, -6.72996], [140.99813, -6.3233], [141.02352, 0.08993], [129.63187, 2.21409], [129.71519, -0.24692], [128.40647, -2.30349], [130.8468, -2.61103], [132.8312, -4.70282], [135.35517, -5.01442], [135.49042, -9.2276]]]]
21543           }
21544         }, {
21545           type: "Feature",
21546           properties: {
21547             wikidata: "Q5765",
21548             nameEn: "Balearic Islands",
21549             aliases: ["ES-IB"],
21550             country: "ES",
21551             groups: ["EU", "039", "150", "UN"],
21552             callingCodes: ["34 971"]
21553           },
21554           geometry: {
21555             type: "MultiPolygon",
21556             coordinates: [[[[-2.27707, 35.35051], [5.10072, 39.89531], [3.75438, 42.33445], [-2.27707, 35.35051]]]]
21557           }
21558         }, {
21559           type: "Feature",
21560           properties: {
21561             wikidata: "Q5823",
21562             nameEn: "Ceuta",
21563             aliases: ["ES-CE"],
21564             country: "ES",
21565             groups: ["EA", "EU", "015", "002", "UN"],
21566             level: "subterritory",
21567             callingCodes: ["34"]
21568           },
21569           geometry: {
21570             type: "MultiPolygon",
21571             coordinates: [[[[-5.38491, 35.92591], [-5.37338, 35.88417], [-5.35844, 35.87375], [-5.34379, 35.8711], [-5.21179, 35.90091], [-5.38491, 35.92591]]]]
21572           }
21573         }, {
21574           type: "Feature",
21575           properties: {
21576             wikidata: "Q5831",
21577             nameEn: "Melilla",
21578             aliases: ["ES-ML"],
21579             country: "ES",
21580             groups: ["EA", "EU", "015", "002", "UN"],
21581             level: "subterritory",
21582             callingCodes: ["34"]
21583           },
21584           geometry: {
21585             type: "MultiPolygon",
21586             coordinates: [[[[-2.91909, 35.33927], [-2.96038, 35.31609], [-2.96648, 35.30475], [-2.96978, 35.29459], [-2.97035, 35.28852], [-2.96507, 35.28801], [-2.96826, 35.28296], [-2.96516, 35.27967], [-2.95431, 35.2728], [-2.95065, 35.26576], [-2.93893, 35.26737], [-2.92272, 35.27509], [-2.91909, 35.33927]]]]
21587           }
21588         }, {
21589           type: "Feature",
21590           properties: {
21591             wikidata: "Q7835",
21592             nameEn: "Crimea",
21593             country: "RU",
21594             groups: ["151", "150", "UN"],
21595             level: "subterritory",
21596             callingCodes: ["7"]
21597           },
21598           geometry: {
21599             type: "MultiPolygon",
21600             coordinates: [[[[33.5, 44], [36.4883, 45.0488], [36.475, 45.2411], [36.5049, 45.3136], [36.6545, 45.3417], [36.6645, 45.4514], [35.0498, 45.7683], [34.9601, 45.7563], [34.7991, 45.8101], [34.8015, 45.9005], [34.7548, 45.907], [34.6668, 45.9714], [34.6086, 45.9935], [34.5589, 45.9935], [34.5201, 45.951], [34.4873, 45.9427], [34.4415, 45.9599], [34.4122, 46.0025], [34.3391, 46.0611], [34.2511, 46.0532], [34.181, 46.068], [34.1293, 46.1049], [34.0731, 46.1177], [34.0527, 46.1084], [33.9155, 46.1594], [33.8523, 46.1986], [33.7972, 46.2048], [33.7405, 46.1855], [33.646, 46.2303], [33.6152, 46.2261], [33.6385, 46.1415], [33.6147, 46.1356], [33.5732, 46.1032], [33.5909, 46.0601], [33.5597, 46.0307], [31.5, 45.5], [33.5, 44]]]]
21601           }
21602         }, {
21603           type: "Feature",
21604           properties: {
21605             wikidata: "Q12837",
21606             nameEn: "Iberia",
21607             level: "sharedLandform"
21608           },
21609           geometry: null
21610         }, {
21611           type: "Feature",
21612           properties: {
21613             wikidata: "Q14056",
21614             nameEn: "Jan Mayen",
21615             aliases: ["NO-22"],
21616             country: "NO",
21617             groups: ["SJ", "154", "150", "UN"],
21618             level: "subterritory"
21619           },
21620           geometry: {
21621             type: "MultiPolygon",
21622             coordinates: [[[[-9.18243, 72.23144], [-10.71459, 70.09565], [-5.93364, 70.76368], [-9.18243, 72.23144]]]]
21623           }
21624         }, {
21625           type: "Feature",
21626           properties: {
21627             wikidata: "Q19188",
21628             nameEn: "Mainland China",
21629             country: "CN",
21630             groups: ["030", "142", "UN"],
21631             callingCodes: ["86"]
21632           },
21633           geometry: {
21634             type: "MultiPolygon",
21635             coordinates: [[[[125.6131, 53.07229], [125.17522, 53.20225], [124.46078, 53.21881], [123.86158, 53.49391], [123.26989, 53.54843], [122.85966, 53.47395], [122.35063, 53.49565], [121.39213, 53.31888], [120.85633, 53.28499], [120.0451, 52.7359], [120.04049, 52.58773], [120.46454, 52.63811], [120.71673, 52.54099], [120.61346, 52.32447], [120.77337, 52.20805], [120.65907, 51.93544], [120.10963, 51.671], [119.13553, 50.37412], [119.38598, 50.35162], [119.27996, 50.13348], [119.11003, 50.00276], [118.61623, 49.93809], [117.82343, 49.52696], [117.48208, 49.62324], [117.27597, 49.62544], [116.71193, 49.83813], [116.03781, 48.87014], [116.06565, 48.81716], [115.78876, 48.51781], [115.811, 48.25699], [115.52082, 48.15367], [115.57128, 47.91988], [115.94296, 47.67741], [116.21879, 47.88505], [116.4465, 47.83662], [116.67405, 47.89039], [116.9723, 47.87285], [117.37875, 47.63627], [117.50181, 47.77216], [117.80196, 48.01661], [118.03676, 48.00982], [118.11009, 48.04], [118.22677, 48.03853], [118.29654, 48.00246], [118.55766, 47.99277], [118.7564, 47.76947], [119.12343, 47.66458], [119.13995, 47.53997], [119.35892, 47.48104], [119.31964, 47.42617], [119.54918, 47.29505], [119.56019, 47.24874], [119.62403, 47.24575], [119.71209, 47.19192], [119.85518, 46.92196], [119.91242, 46.90091], [119.89261, 46.66423], [119.80455, 46.67631], [119.77373, 46.62947], [119.68127, 46.59015], [119.65265, 46.62342], [119.42827, 46.63783], [119.32827, 46.61433], [119.24978, 46.64761], [119.10448, 46.65516], [119.00541, 46.74273], [118.92616, 46.72765], [118.89974, 46.77139], [118.8337, 46.77742], [118.78747, 46.68689], [118.30534, 46.73519], [117.69554, 46.50991], [117.60748, 46.59771], [117.41782, 46.57862], [117.36609, 46.36335], [116.83166, 46.38637], [116.75551, 46.33083], [116.58612, 46.30211], [116.26678, 45.96479], [116.24012, 45.8778], [116.27366, 45.78637], [116.16989, 45.68603], [115.60329, 45.44717], [114.94546, 45.37377], [114.74612, 45.43585], [114.54801, 45.38337], [114.5166, 45.27189], [113.70918, 44.72891], [112.74662, 44.86297], [112.4164, 45.06858], [111.98695, 45.09074], [111.76275, 44.98032], [111.40498, 44.3461], [111.96289, 43.81596], [111.93776, 43.68709], [111.79758, 43.6637], [111.59087, 43.51207], [111.0149, 43.3289], [110.4327, 42.78293], [110.08401, 42.6411], [109.89402, 42.63111], [109.452, 42.44842], [109.00679, 42.45302], [108.84489, 42.40246], [107.57258, 42.40898], [107.49681, 42.46221], [107.29755, 42.41395], [107.24774, 42.36107], [106.76517, 42.28741], [105.0123, 41.63188], [104.51667, 41.66113], [104.52258, 41.8706], [103.92804, 41.78246], [102.72403, 42.14675], [102.07645, 42.22519], [101.80515, 42.50074], [100.84979, 42.67087], [100.33297, 42.68231], [99.50671, 42.56535], [97.1777, 42.7964], [96.37926, 42.72055], [96.35658, 42.90363], [95.89543, 43.2528], [95.52594, 43.99353], [95.32891, 44.02407], [95.39772, 44.2805], [95.01191, 44.25274], [94.71959, 44.35284], [94.10003, 44.71016], [93.51161, 44.95964], [91.64048, 45.07408], [90.89169, 45.19667], [90.65114, 45.49314], [90.70907, 45.73437], [91.03026, 46.04194], [90.99672, 46.14207], [90.89639, 46.30711], [91.07696, 46.57315], [91.0147, 46.58171], [91.03649, 46.72916], [90.84035, 46.99525], [90.76108, 46.99399], [90.48542, 47.30438], [90.48854, 47.41826], [90.33598, 47.68303], [90.10871, 47.7375], [90.06512, 47.88177], [89.76624, 47.82745], [89.55453, 48.0423], [89.0711, 47.98528], [88.93186, 48.10263], [88.8011, 48.11302], [88.58316, 48.21893], [88.58939, 48.34531], [87.96361, 48.58478], [88.0788, 48.71436], [87.73822, 48.89582], [87.88171, 48.95853], [87.81333, 49.17354], [87.48983, 49.13794], [87.478, 49.07403], [87.28386, 49.11626], [86.87238, 49.12432], [86.73568, 48.99918], [86.75343, 48.70331], [86.38069, 48.46064], [85.73581, 48.3939], [85.5169, 48.05493], [85.61067, 47.49753], [85.69696, 47.2898], [85.54294, 47.06171], [85.22443, 47.04816], [84.93995, 46.87399], [84.73077, 47.01394], [83.92184, 46.98912], [83.04622, 47.19053], [82.21792, 45.56619], [82.58474, 45.40027], [82.51374, 45.1755], [81.73278, 45.3504], [80.11169, 45.03352], [79.8987, 44.89957], [80.38384, 44.63073], [80.40229, 44.23319], [80.40031, 44.10986], [80.75156, 43.44948], [80.69718, 43.32589], [80.77771, 43.30065], [80.78817, 43.14235], [80.62913, 43.141], [80.3735, 43.01557], [80.58999, 42.9011], [80.38169, 42.83142], [80.26886, 42.8366], [80.16892, 42.61137], [80.26841, 42.23797], [80.17807, 42.21166], [80.17842, 42.03211], [79.92977, 42.04113], [78.3732, 41.39603], [78.15757, 41.38565], [78.12873, 41.23091], [77.81287, 41.14307], [77.76206, 41.01574], [77.52723, 41.00227], [77.3693, 41.0375], [77.28004, 41.0033], [76.99302, 41.0696], [76.75681, 40.95354], [76.5261, 40.46114], [76.33659, 40.3482], [75.96168, 40.38064], [75.91361, 40.2948], [75.69663, 40.28642], [75.5854, 40.66874], [75.22834, 40.45382], [75.08243, 40.43945], [74.82013, 40.52197], [74.78168, 40.44886], [74.85996, 40.32857], [74.69875, 40.34668], [74.35063, 40.09742], [74.25533, 40.13191], [73.97049, 40.04378], [73.83006, 39.76136], [73.9051, 39.75073], [73.92354, 39.69565], [73.94683, 39.60733], [73.87018, 39.47879], [73.59831, 39.46425], [73.59241, 39.40843], [73.5004, 39.38402], [73.55396, 39.3543], [73.54572, 39.27567], [73.60638, 39.24534], [73.75823, 39.023], [73.81728, 39.04007], [73.82964, 38.91517], [73.7445, 38.93867], [73.7033, 38.84782], [73.80656, 38.66449], [73.79806, 38.61106], [73.97933, 38.52945], [74.17022, 38.65504], [74.51217, 38.47034], [74.69619, 38.42947], [74.69894, 38.22155], [74.80331, 38.19889], [74.82665, 38.07359], [74.9063, 38.03033], [74.92416, 37.83428], [75.00935, 37.77486], [74.8912, 37.67576], [74.94338, 37.55501], [75.06011, 37.52779], [75.15899, 37.41443], [75.09719, 37.37297], [75.12328, 37.31839], [74.88887, 37.23275], [74.80605, 37.21565], [74.49981, 37.24518], [74.56453, 37.03023], [75.13839, 37.02622], [75.40481, 36.95382], [75.45562, 36.71971], [75.72737, 36.7529], [75.92391, 36.56986], [76.0324, 36.41198], [76.00906, 36.17511], [75.93028, 36.13136], [76.15325, 35.9264], [76.14913, 35.82848], [76.33453, 35.84296], [76.50961, 35.8908], [76.77323, 35.66062], [76.84539, 35.67356], [76.96624, 35.5932], [77.44277, 35.46132], [77.70232, 35.46244], [77.80532, 35.52058], [78.11664, 35.48022], [78.03466, 35.3785], [78.00033, 35.23954], [78.22692, 34.88771], [78.18435, 34.7998], [78.27781, 34.61484], [78.54964, 34.57283], [78.56475, 34.50835], [78.74465, 34.45174], [79.05364, 34.32482], [78.99802, 34.3027], [78.91769, 34.15452], [78.66225, 34.08858], [78.65657, 34.03195], [78.73367, 34.01121], [78.77349, 33.73871], [78.67599, 33.66445], [78.73636, 33.56521], [79.15252, 33.17156], [79.14016, 33.02545], [79.46562, 32.69668], [79.26768, 32.53277], [79.13174, 32.47766], [79.0979, 32.38051], [78.99322, 32.37948], [78.96713, 32.33655], [78.7831, 32.46873], [78.73916, 32.69438], [78.38897, 32.53938], [78.4645, 32.45367], [78.49609, 32.2762], [78.68754, 32.10256], [78.74404, 32.00384], [78.78036, 31.99478], [78.69933, 31.78723], [78.84516, 31.60631], [78.71032, 31.50197], [78.77898, 31.31209], [78.89344, 31.30481], [79.01931, 31.42817], [79.14016, 31.43403], [79.30694, 31.17357], [79.59884, 30.93943], [79.93255, 30.88288], [80.20721, 30.58541], [80.54504, 30.44936], [80.83343, 30.32023], [81.03953, 30.20059], [81.12842, 30.01395], [81.24362, 30.0126], [81.29032, 30.08806], [81.2623, 30.14596], [81.33355, 30.15303], [81.39928, 30.21862], [81.41018, 30.42153], [81.5459, 30.37688], [81.62033, 30.44703], [81.99082, 30.33423], [82.10135, 30.35439], [82.10757, 30.23745], [82.19475, 30.16884], [82.16984, 30.0692], [82.38622, 30.02608], [82.5341, 29.9735], [82.73024, 29.81695], [83.07116, 29.61957], [83.28131, 29.56813], [83.44787, 29.30513], [83.63156, 29.16249], [83.82303, 29.30513], [83.97559, 29.33091], [84.18107, 29.23451], [84.24801, 29.02783], [84.2231, 28.89571], [84.47528, 28.74023], [84.62317, 28.73887], [84.85511, 28.58041], [85.06059, 28.68562], [85.19135, 28.62825], [85.18668, 28.54076], [85.10729, 28.34092], [85.38127, 28.28336], [85.4233, 28.32996], [85.59765, 28.30529], [85.60854, 28.25045], [85.69105, 28.38475], [85.71907, 28.38064], [85.74864, 28.23126], [85.84672, 28.18187], [85.90743, 28.05144], [85.97813, 27.99023], [85.94946, 27.9401], [86.06309, 27.90021], [86.12069, 27.93047], [86.08333, 28.02121], [86.088, 28.09264], [86.18607, 28.17364], [86.22966, 27.9786], [86.42736, 27.91122], [86.51609, 27.96623], [86.56265, 28.09569], [86.74181, 28.10638], [86.75582, 28.04182], [87.03757, 27.94835], [87.11696, 27.84104], [87.56996, 27.84517], [87.72718, 27.80938], [87.82681, 27.95248], [88.13378, 27.88015], [88.1278, 27.95417], [88.25332, 27.9478], [88.54858, 28.06057], [88.63235, 28.12356], [88.83559, 28.01936], [88.88091, 27.85192], [88.77517, 27.45415], [88.82981, 27.38814], [88.91901, 27.32483], [88.93678, 27.33777], [88.96947, 27.30319], [89.00216, 27.32532], [88.95355, 27.4106], [88.97213, 27.51671], [89.0582, 27.60985], [89.12825, 27.62502], [89.59525, 28.16433], [89.79762, 28.23979], [90.13387, 28.19178], [90.58842, 28.02838], [90.69894, 28.07784], [91.20019, 27.98715], [91.25779, 28.07509], [91.46327, 28.0064], [91.48973, 27.93903], [91.5629, 27.84823], [91.6469, 27.76358], [91.84722, 27.76325], [91.87057, 27.7195], [92.27432, 27.89077], [92.32101, 27.79363], [92.42538, 27.80092], [92.7275, 27.98662], [92.73025, 28.05814], [92.65472, 28.07632], [92.67486, 28.15018], [92.93075, 28.25671], [93.14635, 28.37035], [93.18069, 28.50319], [93.44621, 28.67189], [93.72797, 28.68821], [94.35897, 29.01965], [94.2752, 29.11687], [94.69318, 29.31739], [94.81353, 29.17804], [95.0978, 29.14446], [95.11291, 29.09527], [95.2214, 29.10727], [95.26122, 29.07727], [95.3038, 29.13847], [95.41091, 29.13007], [95.50842, 29.13487], [95.72086, 29.20797], [95.75149, 29.32063], [95.84899, 29.31464], [96.05361, 29.38167], [96.31316, 29.18643], [96.18682, 29.11087], [96.20467, 29.02325], [96.3626, 29.10607], [96.61391, 28.72742], [96.40929, 28.51526], [96.48895, 28.42955], [96.6455, 28.61657], [96.85561, 28.4875], [96.88445, 28.39452], [96.98882, 28.32564], [97.1289, 28.3619], [97.34547, 28.21385], [97.41729, 28.29783], [97.47085, 28.2688], [97.50518, 28.49716], [97.56835, 28.55628], [97.70705, 28.5056], [97.79632, 28.33168], [97.90069, 28.3776], [98.15337, 28.12114], [98.13964, 27.9478], [98.32641, 27.51385], [98.42529, 27.55404], [98.43353, 27.67086], [98.69582, 27.56499], [98.7333, 26.85615], [98.77547, 26.61994], [98.72741, 26.36183], [98.67797, 26.24487], [98.7329, 26.17218], [98.66884, 26.09165], [98.63128, 26.15492], [98.57085, 26.11547], [98.60763, 26.01512], [98.70818, 25.86241], [98.63128, 25.79937], [98.54064, 25.85129], [98.40606, 25.61129], [98.31268, 25.55307], [98.25774, 25.6051], [98.16848, 25.62739], [98.18084, 25.56298], [98.12591, 25.50722], [98.14925, 25.41547], [97.92541, 25.20815], [97.83614, 25.2715], [97.77023, 25.11492], [97.72216, 25.08508], [97.72903, 24.91332], [97.79949, 24.85655], [97.76481, 24.8289], [97.73127, 24.83015], [97.70181, 24.84557], [97.64354, 24.79171], [97.56648, 24.76475], [97.56383, 24.75535], [97.5542, 24.74943], [97.54675, 24.74202], [97.56525, 24.72838], [97.56286, 24.54535], [97.52757, 24.43748], [97.60029, 24.4401], [97.66998, 24.45288], [97.7098, 24.35658], [97.65624, 24.33781], [97.66723, 24.30027], [97.71941, 24.29652], [97.76799, 24.26365], [97.72998, 24.2302], [97.72799, 24.18883], [97.75305, 24.16902], [97.72903, 24.12606], [97.62363, 24.00506], [97.5247, 23.94032], [97.64667, 23.84574], [97.72302, 23.89288], [97.79456, 23.94836], [97.79416, 23.95663], [97.84328, 23.97603], [97.86545, 23.97723], [97.88811, 23.97446], [97.8955, 23.97758], [97.89676, 23.97931], [97.89683, 23.98389], [97.88814, 23.98605], [97.88414, 23.99405], [97.88616, 24.00463], [97.90998, 24.02094], [97.93951, 24.01953], [97.98691, 24.03897], [97.99583, 24.04932], [98.04709, 24.07616], [98.05302, 24.07408], [98.05671, 24.07961], [98.0607, 24.07812], [98.06703, 24.08028], [98.07806, 24.07988], [98.20666, 24.11406], [98.54476, 24.13119], [98.59256, 24.08371], [98.85319, 24.13042], [98.87998, 24.15624], [98.89632, 24.10612], [98.67797, 23.9644], [98.68209, 23.80492], [98.79607, 23.77947], [98.82933, 23.72921], [98.81775, 23.694], [98.88396, 23.59555], [98.80294, 23.5345], [98.82877, 23.47908], [98.87683, 23.48995], [98.92104, 23.36946], [98.87573, 23.33038], [98.93958, 23.31414], [98.92515, 23.29535], [98.88597, 23.18656], [99.05975, 23.16382], [99.04601, 23.12215], [99.25741, 23.09025], [99.34127, 23.13099], [99.52214, 23.08218], [99.54218, 22.90014], [99.43537, 22.94086], [99.45654, 22.85726], [99.31243, 22.73893], [99.38247, 22.57544], [99.37972, 22.50188], [99.28771, 22.4105], [99.17318, 22.18025], [99.19176, 22.16983], [99.1552, 22.15874], [99.33166, 22.09656], [99.47585, 22.13345], [99.85351, 22.04183], [99.96612, 22.05965], [99.99084, 21.97053], [99.94003, 21.82782], [99.98654, 21.71064], [100.04956, 21.66843], [100.12679, 21.70539], [100.17486, 21.65306], [100.10757, 21.59945], [100.12542, 21.50365], [100.1625, 21.48704], [100.18447, 21.51898], [100.25863, 21.47043], [100.35201, 21.53176], [100.42892, 21.54325], [100.4811, 21.46148], [100.57861, 21.45637], [100.72143, 21.51898], [100.87265, 21.67396], [101.11744, 21.77659], [101.15156, 21.56129], [101.2124, 21.56422], [101.19349, 21.41959], [101.26912, 21.36482], [101.2229, 21.23271], [101.29326, 21.17254], [101.54563, 21.25668], [101.6068, 21.23329], [101.59491, 21.18621], [101.60886, 21.17947], [101.66977, 21.20004], [101.70548, 21.14911], [101.7622, 21.14813], [101.79266, 21.19025], [101.76745, 21.21571], [101.83887, 21.20983], [101.84412, 21.25291], [101.74014, 21.30967], [101.74224, 21.48276], [101.7727, 21.51794], [101.7475, 21.5873], [101.80001, 21.57461], [101.83257, 21.61562], [101.74555, 21.72852], [101.7791, 21.83019], [101.62566, 21.96574], [101.57525, 22.13026], [101.60675, 22.13513], [101.53638, 22.24794], [101.56789, 22.28876], [101.61306, 22.27515], [101.68973, 22.46843], [101.7685, 22.50337], [101.86828, 22.38397], [101.90714, 22.38688], [101.91344, 22.44417], [101.98487, 22.42766], [102.03633, 22.46164], [102.1245, 22.43372], [102.14099, 22.40092], [102.16621, 22.43336], [102.26428, 22.41321], [102.25339, 22.4607], [102.41061, 22.64184], [102.38415, 22.67919], [102.42618, 22.69212], [102.46665, 22.77108], [102.51802, 22.77969], [102.57095, 22.7036], [102.60675, 22.73376], [102.8636, 22.60735], [102.9321, 22.48659], [103.0722, 22.44775], [103.07843, 22.50097], [103.17961, 22.55705], [103.15782, 22.59873], [103.18895, 22.64471], [103.28079, 22.68063], [103.32282, 22.8127], [103.43179, 22.75816], [103.43646, 22.70648], [103.52675, 22.59155], [103.57812, 22.65764], [103.56255, 22.69499], [103.64506, 22.79979], [103.87904, 22.56683], [103.93286, 22.52703], [103.94513, 22.52553], [103.95191, 22.5134], [103.96352, 22.50584], [103.96783, 22.51173], [103.97384, 22.50634], [103.99247, 22.51958], [104.01088, 22.51823], [104.03734, 22.72945], [104.11384, 22.80363], [104.27084, 22.8457], [104.25683, 22.76534], [104.35593, 22.69353], [104.47225, 22.75813], [104.58122, 22.85571], [104.60457, 22.81841], [104.65283, 22.83419], [104.72755, 22.81984], [104.77114, 22.90017], [104.84942, 22.93631], [104.86765, 22.95178], [104.8334, 23.01484], [104.79478, 23.12934], [104.87382, 23.12854], [104.87992, 23.17141], [104.91435, 23.18666], [104.9486, 23.17235], [104.96532, 23.20463], [104.98712, 23.19176], [105.07002, 23.26248], [105.11672, 23.25247], [105.17276, 23.28679], [105.22569, 23.27249], [105.32376, 23.39684], [105.40782, 23.28107], [105.42805, 23.30824], [105.49966, 23.20669], [105.56037, 23.16806], [105.57594, 23.075], [105.72382, 23.06641], [105.8726, 22.92756], [105.90119, 22.94168], [105.99568, 22.94178], [106.00179, 22.99049], [106.19705, 22.98475], [106.27022, 22.87722], [106.34961, 22.86718], [106.49749, 22.91164], [106.51306, 22.94891], [106.55976, 22.92311], [106.60179, 22.92884], [106.6516, 22.86862], [106.6734, 22.89587], [106.71387, 22.88296], [106.71128, 22.85982], [106.78422, 22.81532], [106.81271, 22.8226], [106.83685, 22.8098], [106.82404, 22.7881], [106.76293, 22.73491], [106.72321, 22.63606], [106.71698, 22.58432], [106.65316, 22.5757], [106.61269, 22.60301], [106.58395, 22.474], [106.55665, 22.46498], [106.57221, 22.37], [106.55976, 22.34841], [106.6516, 22.33977], [106.69986, 22.22309], [106.67495, 22.1885], [106.6983, 22.15102], [106.70142, 22.02409], [106.68274, 21.99811], [106.69276, 21.96013], [106.72551, 21.97923], [106.74345, 22.00965], [106.81038, 21.97934], [106.9178, 21.97357], [106.92714, 21.93459], [106.97228, 21.92592], [106.99252, 21.95191], [107.05634, 21.92303], [107.06101, 21.88982], [107.00964, 21.85948], [107.02615, 21.81981], [107.10771, 21.79879], [107.20734, 21.71493], [107.24625, 21.7077], [107.29296, 21.74674], [107.35834, 21.6672], [107.35989, 21.60063], [107.38636, 21.59774], [107.41593, 21.64839], [107.47197, 21.6672], [107.49532, 21.62958], [107.49065, 21.59774], [107.54047, 21.5934], [107.56537, 21.61945], [107.66967, 21.60787], [107.80355, 21.66141], [107.86114, 21.65128], [107.90006, 21.5905], [107.92652, 21.58906], [107.95232, 21.5388], [107.96774, 21.53601], [107.97074, 21.54072], [107.97383, 21.53961], [107.97932, 21.54503], [108.02926, 21.54997], [108.0569, 21.53604], [108.10003, 21.47338], [108.00365, 17.98159], [111.60491, 13.57105], [118.41371, 24.06775], [118.11703, 24.39734], [118.28244, 24.51231], [118.35291, 24.51645], [118.42453, 24.54644], [118.56434, 24.49266], [120.49232, 25.22863], [121.03532, 26.8787], [123.5458, 31.01942], [122.29378, 31.76513], [122.80525, 33.30571], [123.85601, 37.49093], [123.90497, 38.79949], [124.17532, 39.8232], [124.23201, 39.9248], [124.35029, 39.95639], [124.37089, 40.03004], [124.3322, 40.05573], [124.38556, 40.11047], [124.40719, 40.13655], [124.86913, 40.45387], [125.71172, 40.85223], [125.76869, 40.87908], [126.00335, 40.92835], [126.242, 41.15454], [126.53189, 41.35206], [126.60631, 41.65565], [126.90729, 41.79955], [127.17841, 41.59714], [127.29712, 41.49473], [127.92943, 41.44291], [128.02633, 41.42103], [128.03311, 41.39232], [128.12967, 41.37931], [128.18546, 41.41279], [128.20061, 41.40895], [128.30716, 41.60322], [128.15119, 41.74568], [128.04487, 42.01769], [128.94007, 42.03537], [128.96068, 42.06657], [129.15178, 42.17224], [129.22285, 42.26491], [129.22423, 42.3553], [129.28541, 42.41574], [129.42882, 42.44702], [129.54701, 42.37254], [129.60482, 42.44461], [129.72541, 42.43739], [129.75294, 42.59409], [129.77183, 42.69435], [129.7835, 42.76521], [129.80719, 42.79218], [129.83277, 42.86746], [129.85261, 42.96494], [129.8865, 43.00395], [129.95082, 43.01051], [129.96409, 42.97306], [130.12957, 42.98361], [130.09764, 42.91425], [130.26095, 42.9027], [130.23068, 42.80125], [130.2385, 42.71127], [130.41826, 42.6011], [130.44361, 42.54849], [130.50123, 42.61636], [130.55143, 42.52158], [130.62107, 42.58413], [130.56576, 42.68925], [130.40213, 42.70788], [130.44361, 42.76205], [130.66524, 42.84753], [131.02438, 42.86518], [131.02668, 42.91246], [131.135, 42.94114], [131.10274, 43.04734], [131.20414, 43.13654], [131.19031, 43.21385], [131.30324, 43.39498], [131.29402, 43.46695], [131.19492, 43.53047], [131.21105, 43.82383], [131.26176, 43.94011], [131.23583, 43.96085], [131.25484, 44.03131], [131.30365, 44.04262], [131.1108, 44.70266], [130.95639, 44.85154], [131.48415, 44.99513], [131.68466, 45.12374], [131.66852, 45.2196], [131.76532, 45.22609], [131.86903, 45.33636], [131.99417, 45.2567], [132.83978, 45.05916], [132.96373, 45.0212], [133.12293, 45.1332], [133.09279, 45.25693], [133.19419, 45.51913], [133.41083, 45.57723], [133.48457, 45.86203], [133.60442, 45.90053], [133.67569, 45.9759], [133.72695, 46.05576], [133.68047, 46.14697], [133.88097, 46.25066], [133.91496, 46.4274], [133.84104, 46.46681], [134.03538, 46.75668], [134.20016, 47.33458], [134.50898, 47.4812], [134.7671, 47.72051], [134.55508, 47.98651], [134.67098, 48.1564], [134.75328, 48.36763], [134.49516, 48.42884], [132.66989, 47.96491], [132.57309, 47.71741], [131.90448, 47.68011], [131.2635, 47.73325], [131.09871, 47.6852], [130.95985, 47.6957], [130.90915, 47.90623], [130.65103, 48.10052], [130.84462, 48.30942], [130.52147, 48.61745], [130.66946, 48.88251], [130.43232, 48.90844], [130.2355, 48.86741], [129.85416, 49.11067], [129.67598, 49.29596], [129.50685, 49.42398], [129.40398, 49.44194], [129.35317, 49.3481], [129.23232, 49.40353], [129.11153, 49.36813], [128.72896, 49.58676], [127.83476, 49.5748], [127.53516, 49.84306], [127.49299, 50.01251], [127.60515, 50.23503], [127.37384, 50.28393], [127.36009, 50.43787], [127.28765, 50.46585], [127.36335, 50.58306], [127.28165, 50.72075], [127.14586, 50.91152], [126.93135, 51.0841], [126.90369, 51.3238], [126.68349, 51.70607], [126.44606, 51.98254], [126.558, 52.13738], [125.6131, 53.07229]], [[113.56865, 22.20973], [113.57123, 22.20416], [113.60504, 22.20464], [113.63011, 22.10782], [113.57191, 22.07696], [113.54839, 22.10909], [113.54942, 22.14519], [113.54093, 22.15497], [113.52659, 22.18271], [113.53552, 22.20607], [113.53301, 22.21235], [113.53591, 22.21369], [113.54093, 22.21314], [113.54333, 22.21688], [113.5508, 22.21672], [113.56865, 22.20973]], [[114.50148, 22.15017], [113.92195, 22.13873], [113.83338, 22.1826], [113.81621, 22.2163], [113.86771, 22.42972], [114.03113, 22.5065], [114.05438, 22.5026], [114.05729, 22.51104], [114.06272, 22.51617], [114.07267, 22.51855], [114.07817, 22.52997], [114.08606, 22.53276], [114.09048, 22.53716], [114.09692, 22.53435], [114.1034, 22.5352], [114.11181, 22.52878], [114.11656, 22.53415], [114.12665, 22.54003], [114.13823, 22.54319], [114.1482, 22.54091], [114.15123, 22.55163], [114.1597, 22.56041], [114.17247, 22.55944], [114.18338, 22.55444], [114.20655, 22.55706], [114.22185, 22.55343], [114.22888, 22.5436], [114.25154, 22.55977], [114.44998, 22.55977], [114.50148, 22.15017]]]]
21636           }
21637         }, {
21638           type: "Feature",
21639           properties: {
21640             wikidata: "Q22890",
21641             nameEn: "Ireland",
21642             level: "sharedLandform"
21643           },
21644           geometry: null
21645         }, {
21646           type: "Feature",
21647           properties: {
21648             wikidata: "Q23666",
21649             nameEn: "Great Britain",
21650             country: "GB",
21651             level: "sharedLandform"
21652           },
21653           geometry: null
21654         }, {
21655           type: "Feature",
21656           properties: {
21657             wikidata: "Q23681",
21658             nameEn: "Northern Cyprus",
21659             groups: ["Q644636", "145", "142"],
21660             driveSide: "left",
21661             callingCodes: ["90 392"]
21662           },
21663           geometry: {
21664             type: "MultiPolygon",
21665             coordinates: [[[[33.67678, 35.03866], [33.67742, 35.05963], [33.68474, 35.06602], [33.69095, 35.06237], [33.70861, 35.07644], [33.7161, 35.07279], [33.70209, 35.04882], [33.71482, 35.03722], [33.73824, 35.05321], [33.76106, 35.04253], [33.78581, 35.05104], [33.82067, 35.07826], [33.84168, 35.06823], [33.8541, 35.07201], [33.87479, 35.08881], [33.87097, 35.09389], [33.87622, 35.10457], [33.87224, 35.12293], [33.88561, 35.12449], [33.88943, 35.12007], [33.88737, 35.11408], [33.89853, 35.11377], [33.91789, 35.08688], [33.91299, 35.07579], [33.90247, 35.07686], [33.89485, 35.06873], [33.88367, 35.07877], [33.85261, 35.0574], [33.8355, 35.05777], [33.82051, 35.0667], [33.8012, 35.04786], [33.81524, 35.04192], [33.83055, 35.02865], [33.82875, 35.01685], [33.84045, 35.00616], [33.85216, 35.00579], [33.85891, 35.001], [33.85621, 34.98956], [33.83505, 34.98108], [33.84811, 34.97075], [33.86432, 34.97592], [33.90075, 34.96623], [33.98684, 34.76642], [35.48515, 34.70851], [35.51152, 36.10954], [32.82353, 35.70297], [32.46489, 35.48584], [32.60361, 35.16647], [32.64864, 35.19967], [32.70947, 35.18328], [32.70779, 35.14127], [32.85733, 35.07742], [32.86406, 35.1043], [32.94471, 35.09422], [33.01192, 35.15639], [33.08249, 35.17319], [33.11105, 35.15639], [33.15138, 35.19504], [33.27068, 35.16815], [33.3072, 35.16816], [33.31955, 35.18096], [33.35056, 35.18328], [33.34964, 35.17803], [33.35596, 35.17942], [33.35612, 35.17402], [33.36569, 35.17479], [33.3717, 35.1788], [33.37248, 35.18698], [33.38575, 35.2018], [33.4076, 35.20062], [33.41675, 35.16325], [33.46813, 35.10564], [33.48136, 35.0636], [33.47825, 35.04103], [33.45178, 35.02078], [33.45256, 35.00288], [33.47666, 35.00701], [33.48915, 35.06594], [33.53975, 35.08151], [33.57478, 35.06049], [33.567, 35.04803], [33.59658, 35.03635], [33.61215, 35.0527], [33.63765, 35.03869], [33.67678, 35.03866]]]]
21666           }
21667         }, {
21668           type: "Feature",
21669           properties: {
21670             wikidata: "Q25231",
21671             nameEn: "Svalbard",
21672             aliases: ["NO-21"],
21673             country: "NO",
21674             groups: ["SJ", "154", "150", "UN"],
21675             level: "subterritory",
21676             callingCodes: ["47 79"]
21677           },
21678           geometry: {
21679             type: "MultiPolygon",
21680             coordinates: [[[[-7.49892, 77.24208], [32.07813, 72.01005], [36.85549, 84.09565], [-7.49892, 77.24208]]]]
21681           }
21682         }, {
21683           type: "Feature",
21684           properties: {
21685             wikidata: "Q25263",
21686             nameEn: "Azores",
21687             aliases: ["PT-20"],
21688             country: "PT",
21689             groups: ["Q3320166", "Q2914565", "Q105472", "EU", "039", "150", "UN"],
21690             callingCodes: ["351"]
21691           },
21692           geometry: {
21693             type: "MultiPolygon",
21694             coordinates: [[[[-23.12984, 40.26428], [-36.43765, 41.39418], [-22.54767, 33.34416], [-23.12984, 40.26428]]]]
21695           }
21696         }, {
21697           type: "Feature",
21698           properties: {
21699             wikidata: "Q25359",
21700             nameEn: "Navassa Island",
21701             aliases: ["UM-76"],
21702             country: "US",
21703             groups: ["UM", "Q1352230", "029", "003", "419", "019", "UN"],
21704             level: "subterritory",
21705             roadSpeedUnit: "mph",
21706             roadHeightUnit: "ft"
21707           },
21708           geometry: {
21709             type: "MultiPolygon",
21710             coordinates: [[[[-74.7289, 18.71009], [-75.71816, 18.46438], [-74.76465, 18.06252], [-74.7289, 18.71009]]]]
21711           }
21712         }, {
21713           type: "Feature",
21714           properties: {
21715             wikidata: "Q25396",
21716             nameEn: "Bonaire",
21717             aliases: ["BQ-BO", "NL-BQ1"],
21718             country: "NL",
21719             groups: ["Q1451600", "BQ", "029", "003", "419", "019", "UN"],
21720             level: "subterritory",
21721             callingCodes: ["599 7"]
21722           },
21723           geometry: {
21724             type: "MultiPolygon",
21725             coordinates: [[[[-67.89186, 12.4116], [-68.90012, 12.62309], [-68.33524, 11.78151], [-68.01417, 11.77722], [-67.89186, 12.4116]]]]
21726           }
21727         }, {
21728           type: "Feature",
21729           properties: {
21730             wikidata: "Q25528",
21731             nameEn: "Saba",
21732             aliases: ["BQ-SA", "NL-BQ2"],
21733             country: "NL",
21734             groups: ["Q1451600", "BQ", "029", "003", "419", "019", "UN"],
21735             level: "subterritory",
21736             callingCodes: ["599 4"]
21737           },
21738           geometry: {
21739             type: "MultiPolygon",
21740             coordinates: [[[[-63.07669, 17.79659], [-63.81314, 17.95045], [-63.22932, 17.32592], [-63.07669, 17.79659]]]]
21741           }
21742         }, {
21743           type: "Feature",
21744           properties: {
21745             wikidata: "Q26180",
21746             nameEn: "Sint Eustatius",
21747             aliases: ["BQ-SE", "NL-BQ3"],
21748             country: "NL",
21749             groups: ["Q1451600", "BQ", "029", "003", "419", "019", "UN"],
21750             level: "subterritory",
21751             callingCodes: ["599 3"]
21752           },
21753           geometry: {
21754             type: "MultiPolygon",
21755             coordinates: [[[[-63.07669, 17.79659], [-63.34999, 16.94218], [-62.76692, 17.64353], [-63.07669, 17.79659]]]]
21756           }
21757         }, {
21758           type: "Feature",
21759           properties: {
21760             wikidata: "Q26253",
21761             nameEn: "Madeira",
21762             aliases: ["PT-30"],
21763             country: "PT",
21764             groups: ["Q3320166", "Q2914565", "Q105472", "EU", "039", "150", "UN"],
21765             callingCodes: ["351"]
21766           },
21767           geometry: {
21768             type: "MultiPolygon",
21769             coordinates: [[[[-19.30302, 33.65304], [-16.04789, 29.65076], [-11.68307, 33.12333], [-19.30302, 33.65304]]]]
21770           }
21771         }, {
21772           type: "Feature",
21773           properties: {
21774             wikidata: "Q26927",
21775             nameEn: "Lakshadweep",
21776             aliases: ["IN-LD"],
21777             country: "IN",
21778             groups: ["034", "142", "UN"],
21779             driveSide: "left",
21780             callingCodes: ["91"]
21781           },
21782           geometry: {
21783             type: "MultiPolygon",
21784             coordinates: [[[[67.64074, 11.57295], [76.59015, 5.591], [72.67494, 13.58102], [67.64074, 11.57295]]]]
21785           }
21786         }, {
21787           type: "Feature",
21788           properties: {
21789             wikidata: "Q27329",
21790             nameEn: "Asian Russia",
21791             country: "RU",
21792             groups: ["142", "UN"],
21793             callingCodes: ["7"]
21794           },
21795           geometry: {
21796             type: "MultiPolygon",
21797             coordinates: [[[[-179.99933, 64.74703], [-172.76104, 63.77445], [-169.03888, 65.48473], [-168.95635, 65.98512], [-168.25765, 71.99091], [-179.9843, 71.90735], [-179.99933, 64.74703]]], [[[59.99809, 51.98263], [60.19925, 51.99173], [60.48915, 52.15175], [60.72581, 52.15538], [60.78201, 52.22067], [61.05417, 52.35096], [60.98021, 52.50068], [60.84709, 52.52228], [60.84118, 52.63912], [60.71693, 52.66245], [60.71989, 52.75923], [61.05842, 52.92217], [61.23462, 53.03227], [62.0422, 52.96105], [62.12799, 52.99133], [62.14574, 53.09626], [61.19024, 53.30536], [61.14291, 53.41481], [61.29082, 53.50992], [61.37957, 53.45887], [61.57185, 53.50112], [61.55706, 53.57144], [60.90626, 53.62937], [61.22574, 53.80268], [61.14283, 53.90063], [60.99796, 53.93699], [61.26863, 53.92797], [61.3706, 54.08464], [61.47603, 54.08048], [61.56941, 53.95703], [61.65318, 54.02445], [62.03913, 53.94768], [62.00966, 54.04134], [62.38535, 54.03961], [62.45931, 53.90737], [62.56876, 53.94047], [62.58651, 54.05871], [63.80604, 54.27079], [63.91224, 54.20013], [64.02715, 54.22679], [63.97686, 54.29763], [64.97216, 54.4212], [65.11033, 54.33028], [65.24663, 54.35721], [65.20174, 54.55216], [68.21308, 54.98645], [68.26661, 55.09226], [68.19206, 55.18823], [68.90865, 55.38148], [69.34224, 55.36344], [69.74917, 55.35545], [70.19179, 55.1476], [70.76493, 55.3027], [70.96009, 55.10558], [71.08288, 54.71253], [71.24185, 54.64965], [71.08706, 54.33376], [71.10379, 54.13326], [71.96141, 54.17736], [72.17477, 54.36303], [72.43415, 53.92685], [72.71026, 54.1161], [73.37963, 53.96132], [73.74778, 54.07194], [73.68921, 53.86522], [73.25412, 53.61532], [73.39218, 53.44623], [75.07405, 53.80831], [75.43398, 53.98652], [75.3668, 54.07439], [76.91052, 54.4677], [76.82266, 54.1798], [76.44076, 54.16017], [76.54243, 53.99329], [77.90383, 53.29807], [79.11255, 52.01171], [80.08138, 50.77658], [80.4127, 50.95581], [80.44819, 51.20855], [80.80318, 51.28262], [81.16999, 51.15662], [81.06091, 50.94833], [81.41248, 50.97524], [81.46581, 50.77658], [81.94999, 50.79307], [82.55443, 50.75412], [83.14607, 51.00796], [83.8442, 50.87375], [84.29385, 50.27257], [84.99198, 50.06793], [85.24047, 49.60239], [86.18709, 49.50259], [86.63674, 49.80136], [86.79056, 49.74787], [86.61307, 49.60239], [86.82606, 49.51796], [87.03071, 49.25142], [87.31465, 49.23603], [87.28386, 49.11626], [87.478, 49.07403], [87.48983, 49.13794], [87.81333, 49.17354], [87.98977, 49.18147], [88.15543, 49.30314], [88.17223, 49.46934], [88.42449, 49.48821], [88.82499, 49.44808], [89.70687, 49.72535], [89.59711, 49.90851], [91.86048, 50.73734], [92.07173, 50.69585], [92.44714, 50.78762], [93.01109, 50.79001], [92.99595, 50.63183], [94.30823, 50.57498], [94.39258, 50.22193], [94.49477, 50.17832], [94.6121, 50.04239], [94.97166, 50.04725], [95.02465, 49.96941], [95.74757, 49.97915], [95.80056, 50.04239], [96.97388, 49.88413], [97.24639, 49.74737], [97.56811, 49.84265], [97.56432, 49.92801], [97.76871, 49.99861], [97.85197, 49.91339], [98.29481, 50.33561], [98.31373, 50.4996], [98.06393, 50.61262], [97.9693, 50.78044], [98.01472, 50.86652], [97.83305, 51.00248], [98.05257, 51.46696], [98.22053, 51.46579], [98.33222, 51.71832], [98.74142, 51.8637], [98.87768, 52.14563], [99.27888, 51.96876], [99.75578, 51.90108], [99.89203, 51.74903], [100.61116, 51.73028], [101.39085, 51.45753], [101.5044, 51.50467], [102.14032, 51.35566], [102.32194, 50.67982], [102.71178, 50.38873], [103.70343, 50.13952], [105.32528, 50.4648], [106.05562, 50.40582], [106.07865, 50.33474], [106.47156, 50.31909], [106.49628, 50.32436], [106.51122, 50.34408], [106.58373, 50.34044], [106.80326, 50.30177], [107.00007, 50.1977], [107.1174, 50.04239], [107.36407, 49.97612], [107.96116, 49.93191], [107.95387, 49.66659], [108.27937, 49.53167], [108.53969, 49.32325], [109.18017, 49.34709], [109.51325, 49.22859], [110.24373, 49.16676], [110.39891, 49.25083], [110.64493, 49.1816], [113.02647, 49.60772], [113.20216, 49.83356], [114.325, 50.28098], [114.9703, 50.19254], [115.26068, 49.97367], [115.73602, 49.87688], [116.22402, 50.04477], [116.62502, 49.92919], [116.71193, 49.83813], [117.27597, 49.62544], [117.48208, 49.62324], [117.82343, 49.52696], [118.61623, 49.93809], [119.11003, 50.00276], [119.27996, 50.13348], [119.38598, 50.35162], [119.13553, 50.37412], [120.10963, 51.671], [120.65907, 51.93544], [120.77337, 52.20805], [120.61346, 52.32447], [120.71673, 52.54099], [120.46454, 52.63811], [120.04049, 52.58773], [120.0451, 52.7359], [120.85633, 53.28499], [121.39213, 53.31888], [122.35063, 53.49565], [122.85966, 53.47395], [123.26989, 53.54843], [123.86158, 53.49391], [124.46078, 53.21881], [125.17522, 53.20225], [125.6131, 53.07229], [126.558, 52.13738], [126.44606, 51.98254], [126.68349, 51.70607], [126.90369, 51.3238], [126.93135, 51.0841], [127.14586, 50.91152], [127.28165, 50.72075], [127.36335, 50.58306], [127.28765, 50.46585], [127.36009, 50.43787], [127.37384, 50.28393], [127.60515, 50.23503], [127.49299, 50.01251], [127.53516, 49.84306], [127.83476, 49.5748], [128.72896, 49.58676], [129.11153, 49.36813], [129.23232, 49.40353], [129.35317, 49.3481], [129.40398, 49.44194], [129.50685, 49.42398], [129.67598, 49.29596], [129.85416, 49.11067], [130.2355, 48.86741], [130.43232, 48.90844], [130.66946, 48.88251], [130.52147, 48.61745], [130.84462, 48.30942], [130.65103, 48.10052], [130.90915, 47.90623], [130.95985, 47.6957], [131.09871, 47.6852], [131.2635, 47.73325], [131.90448, 47.68011], [132.57309, 47.71741], [132.66989, 47.96491], [134.49516, 48.42884], [134.75328, 48.36763], [134.67098, 48.1564], [134.55508, 47.98651], [134.7671, 47.72051], [134.50898, 47.4812], [134.20016, 47.33458], [134.03538, 46.75668], [133.84104, 46.46681], [133.91496, 46.4274], [133.88097, 46.25066], [133.68047, 46.14697], [133.72695, 46.05576], [133.67569, 45.9759], [133.60442, 45.90053], [133.48457, 45.86203], [133.41083, 45.57723], [133.19419, 45.51913], [133.09279, 45.25693], [133.12293, 45.1332], [132.96373, 45.0212], [132.83978, 45.05916], [131.99417, 45.2567], [131.86903, 45.33636], [131.76532, 45.22609], [131.66852, 45.2196], [131.68466, 45.12374], [131.48415, 44.99513], [130.95639, 44.85154], [131.1108, 44.70266], [131.30365, 44.04262], [131.25484, 44.03131], [131.23583, 43.96085], [131.26176, 43.94011], [131.21105, 43.82383], [131.19492, 43.53047], [131.29402, 43.46695], [131.30324, 43.39498], [131.19031, 43.21385], [131.20414, 43.13654], [131.10274, 43.04734], [131.135, 42.94114], [131.02668, 42.91246], [131.02438, 42.86518], [130.66524, 42.84753], [130.44361, 42.76205], [130.40213, 42.70788], [130.56576, 42.68925], [130.62107, 42.58413], [130.55143, 42.52158], [130.56835, 42.43281], [130.60805, 42.4317], [130.64181, 42.41422], [130.66367, 42.38024], [130.65022, 42.32281], [131.95041, 41.5445], [140.9182, 45.92937], [145.82343, 44.571], [145.23667, 43.76813], [153.94307, 38.42848], [180, 62.52334], [180, 71.53642], [155.31937, 81.93282], [76.13964, 83.37843], [64.18965, 69.94255], [66.1708, 67.61252], [61.98014, 65.72191], [60.74386, 64.95767], [59.63945, 64.78384], [59.80579, 64.13948], [59.24834, 63.01859], [59.61398, 62.44915], [59.36223, 61.3882], [59.50685, 60.91162], [58.3853, 59.487], [59.15636, 59.14682], [59.40376, 58.45822], [58.71104, 58.07475], [58.81412, 57.71602], [58.13789, 57.68097], [58.07604, 57.08308], [57.28024, 56.87898], [57.51527, 56.08729], [59.28419, 56.15739], [59.49035, 55.60486], [58.81825, 55.03378], [57.25137, 55.26262], [57.14829, 54.84204], [57.95234, 54.39672], [59.95217, 54.85853], [59.70487, 54.14846], [58.94336, 53.953], [58.79644, 52.43392], [59.22409, 52.28437], [59.25033, 52.46803], [60.17516, 52.39457], [60.17253, 52.25814], [59.91279, 52.06924], [59.99809, 51.98263]]]]
21798           }
21799         }, {
21800           type: "Feature",
21801           properties: {
21802             wikidata: "Q34366",
21803             nameEn: "Tasmania",
21804             aliases: ["AU-TAS"],
21805             country: "AU",
21806             groups: ["053", "009", "UN"],
21807             driveSide: "left",
21808             callingCodes: ["61"]
21809           },
21810           geometry: {
21811             type: "MultiPolygon",
21812             coordinates: [[[[123.64533, -39.13605], [159.69067, -56.28945], [159.74028, -39.1978], [123.64533, -39.13605]]]]
21813           }
21814         }, {
21815           type: "Feature",
21816           properties: {
21817             wikidata: "Q34497",
21818             nameEn: "Saint Helena",
21819             aliases: ["SH-HL"],
21820             country: "GB",
21821             groups: ["SH", "BOTS", "011", "202", "002", "UN"],
21822             level: "subterritory",
21823             driveSide: "left",
21824             roadSpeedUnit: "mph",
21825             roadHeightUnit: "ft",
21826             callingCodes: ["290"]
21827           },
21828           geometry: {
21829             type: "MultiPolygon",
21830             coordinates: [[[[-8.3824, -13.9131], [-6.17428, -19.07236], [-3.29308, -15.22647], [-8.3824, -13.9131]]]]
21831           }
21832         }, {
21833           type: "Feature",
21834           properties: {
21835             wikidata: "Q35657",
21836             nameEn: "US States",
21837             country: "US",
21838             level: "subcountryGroup"
21839           },
21840           geometry: null
21841         }, {
21842           type: "Feature",
21843           properties: {
21844             wikidata: "Q36117",
21845             nameEn: "Borneo",
21846             level: "sharedLandform"
21847           },
21848           geometry: null
21849         }, {
21850           type: "Feature",
21851           properties: {
21852             wikidata: "Q36678",
21853             nameEn: "West Bank",
21854             country: "PS",
21855             groups: ["145", "142"],
21856             callingCodes: ["970"]
21857           },
21858           geometry: {
21859             type: "MultiPolygon",
21860             coordinates: [[[[35.47672, 31.49578], [35.55941, 31.76535], [35.52758, 31.9131], [35.54375, 31.96587], [35.52012, 32.04076], [35.57111, 32.21877], [35.55807, 32.38674], [35.42078, 32.41562], [35.41048, 32.43706], [35.41598, 32.45593], [35.42034, 32.46009], [35.40224, 32.50136], [35.35212, 32.52047], [35.30685, 32.51024], [35.29306, 32.50947], [35.25049, 32.52453], [35.2244, 32.55289], [35.15937, 32.50466], [35.10882, 32.4757], [35.10024, 32.47856], [35.09236, 32.47614], [35.08564, 32.46948], [35.07059, 32.4585], [35.05423, 32.41754], [35.05311, 32.4024], [35.0421, 32.38242], [35.05142, 32.3667], [35.04243, 32.35008], [35.01772, 32.33863], [35.01119, 32.28684], [35.02939, 32.2671], [35.01841, 32.23981], [34.98885, 32.20758], [34.95703, 32.19522], [34.96009, 32.17503], [34.99039, 32.14626], [34.98507, 32.12606], [34.99437, 32.10962], [34.9863, 32.09551], [35.00261, 32.027], [34.98682, 31.96935], [35.00124, 31.93264], [35.03489, 31.92448], [35.03978, 31.89276], [35.03489, 31.85919], [34.99712, 31.85569], [34.9724, 31.83352], [35.01978, 31.82944], [35.05617, 31.85685], [35.07677, 31.85627], [35.14174, 31.81325], [35.18603, 31.80901], [35.18169, 31.82542], [35.19461, 31.82687], [35.21469, 31.81835], [35.216, 31.83894], [35.21128, 31.863], [35.20381, 31.86716], [35.20673, 31.88151], [35.20791, 31.8821], [35.20945, 31.8815], [35.21016, 31.88237], [35.21276, 31.88153], [35.2136, 31.88241], [35.22014, 31.88264], [35.22294, 31.87889], [35.22567, 31.86745], [35.22817, 31.8638], [35.2249, 31.85433], [35.2304, 31.84222], [35.24816, 31.8458], [35.25753, 31.8387], [35.251, 31.83085], [35.26404, 31.82567], [35.25573, 31.81362], [35.26058, 31.79064], [35.25225, 31.7678], [35.26319, 31.74846], [35.25182, 31.73945], [35.24981, 31.72543], [35.2438, 31.7201], [35.24315, 31.71244], [35.23972, 31.70896], [35.22392, 31.71899], [35.21937, 31.71578], [35.20538, 31.72388], [35.18023, 31.72067], [35.16478, 31.73242], [35.15474, 31.73352], [35.15119, 31.73634], [35.13931, 31.73012], [35.12933, 31.7325], [35.11895, 31.71454], [35.10782, 31.71594], [35.08226, 31.69107], [35.00879, 31.65426], [34.95249, 31.59813], [34.9415, 31.55601], [34.94356, 31.50743], [34.93258, 31.47816], [34.89756, 31.43891], [34.87833, 31.39321], [34.88932, 31.37093], [34.92571, 31.34337], [35.02459, 31.35979], [35.13033, 31.3551], [35.22921, 31.37445], [35.39675, 31.49572], [35.47672, 31.49578]]]]
21861           }
21862         }, {
21863           type: "Feature",
21864           properties: {
21865             wikidata: "Q37362",
21866             nameEn: "Akrotiri and Dhekelia",
21867             aliases: ["SBA"],
21868             country: "GB"
21869           },
21870           geometry: null
21871         }, {
21872           type: "Feature",
21873           properties: {
21874             wikidata: "Q38095",
21875             nameEn: "Gal\xE1pagos Islands",
21876             aliases: ["EC-W"],
21877             country: "EC",
21878             groups: ["005", "419", "019", "UN"],
21879             callingCodes: ["593"]
21880           },
21881           geometry: {
21882             type: "MultiPolygon",
21883             coordinates: [[[[-93.12365, 2.64343], [-92.46744, -2.52874], [-87.07749, -0.8849], [-93.12365, 2.64343]]]]
21884           }
21885         }, {
21886           type: "Feature",
21887           properties: {
21888             wikidata: "Q39760",
21889             nameEn: "Gaza Strip",
21890             country: "PS",
21891             groups: ["145", "142"],
21892             callingCodes: ["970"]
21893           },
21894           geometry: {
21895             type: "MultiPolygon",
21896             coordinates: [[[[34.052, 31.46619], [34.21853, 31.32363], [34.23572, 31.2966], [34.24012, 31.29591], [34.26742, 31.21998], [34.29417, 31.24194], [34.36523, 31.28963], [34.37381, 31.30598], [34.36505, 31.36404], [34.40077, 31.40926], [34.48892, 31.48365], [34.56797, 31.54197], [34.48681, 31.59711], [34.29262, 31.70393], [34.052, 31.46619]]]]
21897           }
21898         }, {
21899           type: "Feature",
21900           properties: {
21901             wikidata: "Q40888",
21902             nameEn: "Andaman and Nicobar Islands",
21903             aliases: ["IN-AN"],
21904             country: "IN",
21905             groups: ["034", "142", "UN"],
21906             driveSide: "left",
21907             callingCodes: ["91"]
21908           },
21909           geometry: {
21910             type: "MultiPolygon",
21911             coordinates: [[[[94.42132, 5.96581], [94.6371, 13.81803], [86.7822, 13.41052], [94.42132, 5.96581]]]]
21912           }
21913         }, {
21914           type: "Feature",
21915           properties: {
21916             wikidata: "Q41684",
21917             nameEn: "Stewart Island",
21918             country: "NZ",
21919             groups: ["053", "009", "UN"],
21920             driveSide: "left",
21921             callingCodes: ["64"]
21922           },
21923           geometry: {
21924             type: "MultiPolygon",
21925             coordinates: [[[[166.59185, -47.61313], [169.70504, -47.56021], [167.52103, -46.41337], [166.59185, -47.61313]]]]
21926           }
21927         }, {
21928           type: "Feature",
21929           properties: {
21930             wikidata: "Q43296",
21931             nameEn: "Wake Island",
21932             aliases: ["WK", "WAK", "WKUM", "872", "UM-79"],
21933             country: "US",
21934             groups: ["UM", "Q1352230", "057", "009", "UN"],
21935             level: "subterritory",
21936             roadSpeedUnit: "mph",
21937             roadHeightUnit: "ft",
21938             callingCodes: ["1"]
21939           },
21940           geometry: {
21941             type: "MultiPolygon",
21942             coordinates: [[[[167.34779, 18.97692], [166.67967, 20.14834], [165.82549, 18.97692], [167.34779, 18.97692]]]]
21943           }
21944         }, {
21945           type: "Feature",
21946           properties: {
21947             wikidata: "Q46275",
21948             nameEn: "New Zealand Subantarctic Islands",
21949             country: "NZ",
21950             groups: ["Q851132", "053", "009", "UN"],
21951             driveSide: "left"
21952           },
21953           geometry: {
21954             type: "MultiPolygon",
21955             coordinates: [[[[164.30551, -47.88072], [161.96603, -56.07661], [179.49541, -50.04657], [179.49541, -47.2902], [169.91032, -47.66283], [164.30551, -47.88072]]]]
21956           }
21957         }, {
21958           type: "Feature",
21959           properties: {
21960             wikidata: "Q46395",
21961             nameEn: "British Overseas Territories",
21962             aliases: ["BOTS", "UKOTS"],
21963             country: "GB",
21964             level: "subcountryGroup"
21965           },
21966           geometry: null
21967         }, {
21968           type: "Feature",
21969           properties: {
21970             wikidata: "Q46772",
21971             nameEn: "Kerguelen Islands",
21972             country: "FR",
21973             groups: ["TF", "Q1451600", "014", "202", "002", "UN"],
21974             level: "subterritory"
21975           },
21976           geometry: {
21977             type: "MultiPolygon",
21978             coordinates: [[[[61.9216, -49.39746], [70.67507, -51.14192], [74.25129, -45.45074], [61.9216, -49.39746]]]]
21979           }
21980         }, {
21981           type: "Feature",
21982           properties: {
21983             wikidata: "Q46879",
21984             nameEn: "Baker Island",
21985             aliases: ["UM-81"],
21986             country: "US",
21987             groups: ["UM", "Q1352230", "061", "009", "UN"],
21988             level: "subterritory",
21989             roadSpeedUnit: "mph",
21990             roadHeightUnit: "ft"
21991           },
21992           geometry: {
21993             type: "MultiPolygon",
21994             coordinates: [[[[-175.33482, -1.40631], [-175.31323, 0.5442], [-177.91421, 0.39582], [-175.33482, -1.40631]]]]
21995           }
21996         }, {
21997           type: "Feature",
21998           properties: {
21999             wikidata: "Q47863",
22000             nameEn: "Midway Atoll",
22001             aliases: ["MI", "MID", "MIUM", "488", "UM-71"],
22002             country: "US",
22003             groups: ["UM", "Q1352230", "061", "009", "UN"],
22004             level: "subterritory",
22005             roadSpeedUnit: "mph",
22006             roadHeightUnit: "ft",
22007             callingCodes: ["1"]
22008           },
22009           geometry: {
22010             type: "MultiPolygon",
22011             coordinates: [[[[-176.29741, 29.09786], [-177.77531, 29.29793], [-177.5224, 27.7635], [-176.29741, 29.09786]]]]
22012           }
22013         }, {
22014           type: "Feature",
22015           properties: {
22016             wikidata: "Q62218",
22017             nameEn: "Jarvis Island",
22018             aliases: ["UM-86"],
22019             country: "US",
22020             groups: ["UM", "Q1352230", "061", "009", "UN"],
22021             level: "subterritory",
22022             roadSpeedUnit: "mph",
22023             roadHeightUnit: "ft"
22024           },
22025           geometry: {
22026             type: "MultiPolygon",
22027             coordinates: [[[[-160.42921, -1.4364], [-159.12443, 0.19975], [-160.38779, 0.30331], [-160.42921, -1.4364]]]]
22028           }
22029         }, {
22030           type: "Feature",
22031           properties: {
22032             wikidata: "Q105472",
22033             nameEn: "Macaronesia",
22034             level: "sharedLandform"
22035           },
22036           geometry: null
22037         }, {
22038           type: "Feature",
22039           properties: {
22040             wikidata: "Q114935",
22041             nameEn: "Kermadec Islands",
22042             country: "NZ",
22043             groups: ["Q851132", "053", "009", "UN"],
22044             driveSide: "left",
22045             callingCodes: ["64"]
22046           },
22047           geometry: {
22048             type: "MultiPolygon",
22049             coordinates: [[[[-174.40891, -29.09438], [-180, -24.21376], [-179.96512, -35.00791], [-174.40891, -29.09438]]]]
22050           }
22051         }, {
22052           type: "Feature",
22053           properties: {
22054             wikidata: "Q115459",
22055             nameEn: "Chatham Islands",
22056             aliases: ["NZ-CIT"],
22057             country: "NZ",
22058             groups: ["Q851132", "053", "009", "UN"],
22059             driveSide: "left",
22060             callingCodes: ["64"]
22061           },
22062           geometry: {
22063             type: "MultiPolygon",
22064             coordinates: [[[[-179.93224, -45.18423], [-172.47015, -45.17912], [-176.30998, -41.38382], [-179.93224, -45.18423]]]]
22065           }
22066         }, {
22067           type: "Feature",
22068           properties: {
22069             wikidata: "Q118863",
22070             nameEn: "North Island",
22071             country: "NZ",
22072             groups: ["053", "009", "UN"],
22073             driveSide: "left",
22074             callingCodes: ["64"]
22075           },
22076           geometry: {
22077             type: "MultiPolygon",
22078             coordinates: [[[[179.49541, -47.2902], [179.49541, -36.79303], [174.17679, -32.62487], [170.27492, -36.38133], [174.58663, -40.80446], [174.46634, -41.55028], [179.49541, -47.2902]]]]
22079           }
22080         }, {
22081           type: "Feature",
22082           properties: {
22083             wikidata: "Q120755",
22084             nameEn: "South Island",
22085             country: "NZ",
22086             groups: ["053", "009", "UN"],
22087             driveSide: "left",
22088             callingCodes: ["64"]
22089           },
22090           geometry: {
22091             type: "MultiPolygon",
22092             coordinates: [[[[169.70504, -47.56021], [179.49541, -47.2902], [174.46634, -41.55028], [174.58663, -40.80446], [170.27492, -36.38133], [166.56976, -39.94841], [164.8365, -46.0205], [167.52103, -46.41337], [169.70504, -47.56021]]]]
22093           }
22094         }, {
22095           type: "Feature",
22096           properties: {
22097             wikidata: "Q123076",
22098             nameEn: "Palmyra Atoll",
22099             aliases: ["UM-95"],
22100             country: "US",
22101             groups: ["UM", "Q1352230", "061", "009", "UN"],
22102             level: "subterritory",
22103             roadSpeedUnit: "mph",
22104             roadHeightUnit: "ft",
22105             callingCodes: ["1"]
22106           },
22107           geometry: {
22108             type: "MultiPolygon",
22109             coordinates: [[[[-161.06795, 5.2462], [-161.0731, 7.1291], [-163.24478, 5.24198], [-161.06795, 5.2462]]]]
22110           }
22111         }, {
22112           type: "Feature",
22113           properties: {
22114             wikidata: "Q130574",
22115             nameEn: "Chafarinas Islands",
22116             country: "ES",
22117             groups: ["EU", "Q191011", "015", "002", "UN"],
22118             level: "subterritory"
22119           },
22120           geometry: {
22121             type: "MultiPolygon",
22122             coordinates: [[[[-2.40316, 35.16893], [-2.43262, 35.20652], [-2.45965, 35.16527], [-2.40316, 35.16893]]]]
22123           }
22124         }, {
22125           type: "Feature",
22126           properties: {
22127             wikidata: "Q130895",
22128             nameEn: "Kingman Reef",
22129             aliases: ["UM-89"],
22130             country: "US",
22131             groups: ["UM", "Q1352230", "061", "009", "UN"],
22132             level: "subterritory",
22133             roadSpeedUnit: "mph",
22134             roadHeightUnit: "ft"
22135           },
22136           geometry: {
22137             type: "MultiPolygon",
22138             coordinates: [[[[-161.0731, 7.1291], [-163.16627, 7.15036], [-163.24478, 5.24198], [-161.0731, 7.1291]]]]
22139           }
22140         }, {
22141           type: "Feature",
22142           properties: {
22143             wikidata: "Q131008",
22144             nameEn: "Johnston Atoll",
22145             aliases: ["JT", "JTN", "JTUM", "396", "UM-67"],
22146             country: "US",
22147             groups: ["UM", "Q1352230", "061", "009", "UN"],
22148             level: "subterritory",
22149             roadSpeedUnit: "mph",
22150             roadHeightUnit: "ft",
22151             callingCodes: ["1"]
22152           },
22153           geometry: {
22154             type: "MultiPolygon",
22155             coordinates: [[[[-170.65691, 16.57199], [-168.87689, 16.01159], [-169.2329, 17.4933], [-170.65691, 16.57199]]]]
22156           }
22157         }, {
22158           type: "Feature",
22159           properties: {
22160             wikidata: "Q131305",
22161             nameEn: "Howland Island",
22162             aliases: ["UM-84"],
22163             country: "US",
22164             groups: ["UM", "Q1352230", "061", "009", "UN"],
22165             level: "subterritory",
22166             roadSpeedUnit: "mph",
22167             roadHeightUnit: "ft"
22168           },
22169           geometry: {
22170             type: "MultiPolygon",
22171             coordinates: [[[[-177.91421, 0.39582], [-175.31323, 0.5442], [-176.74464, 2.28109], [-177.91421, 0.39582]]]]
22172           }
22173         }, {
22174           type: "Feature",
22175           properties: {
22176             wikidata: "Q133888",
22177             nameEn: "Ashmore and Cartier Islands",
22178             country: "AU",
22179             groups: ["053", "009", "UN"],
22180             driveSide: "left",
22181             callingCodes: ["61"]
22182           },
22183           geometry: {
22184             type: "MultiPolygon",
22185             coordinates: [[[[123.7463, -11.1783], [120.6877, -13.59408], [125.29076, -12.33139], [123.7463, -11.1783]]]]
22186           }
22187         }, {
22188           type: "Feature",
22189           properties: {
22190             wikidata: "Q153732",
22191             nameEn: "Mariana Islands",
22192             level: "sharedLandform"
22193           },
22194           geometry: null
22195         }, {
22196           type: "Feature",
22197           properties: {
22198             wikidata: "Q172216",
22199             nameEn: "Coral Sea Islands",
22200             country: "AU",
22201             groups: ["053", "009", "UN"],
22202             driveSide: "left",
22203             callingCodes: ["61"]
22204           },
22205           geometry: {
22206             type: "MultiPolygon",
22207             coordinates: [[[[159.77159, -28.41151], [156.73836, -14.50464], [145.2855, -9.62524], [147.69992, -17.5933], [152.93188, -20.92631], [154.02855, -24.43238], [159.77159, -28.41151]]]]
22208           }
22209         }, {
22210           type: "Feature",
22211           properties: {
22212             wikidata: "Q179313",
22213             nameEn: "Alderney",
22214             country: "GB",
22215             groups: ["GG", "830", "Q185086", "154", "150", "UN"],
22216             level: "subterritory",
22217             driveSide: "left",
22218             roadSpeedUnit: "mph",
22219             roadHeightUnit: "ft",
22220             callingCodes: ["44 01481"]
22221           },
22222           geometry: {
22223             type: "MultiPolygon",
22224             coordinates: [[[[-2.36485, 49.48223], [-2.09454, 49.46288], [-2.02963, 49.91866], [-2.49556, 49.79012], [-2.36485, 49.48223]]]]
22225           }
22226         }, {
22227           type: "Feature",
22228           properties: {
22229             wikidata: "Q185086",
22230             nameEn: "Crown Dependencies",
22231             country: "GB",
22232             level: "subcountryGroup"
22233           },
22234           geometry: null
22235         }, {
22236           type: "Feature",
22237           properties: {
22238             wikidata: "Q190571",
22239             nameEn: "Scattered Islands",
22240             country: "FR",
22241             groups: ["TF", "Q1451600", "014", "202", "002", "UN"],
22242             level: "subterritory"
22243           },
22244           geometry: {
22245             type: "MultiPolygon",
22246             coordinates: [[[[53.53458, -16.36909], [54.96649, -16.28353], [54.61476, -15.02273], [53.53458, -16.36909]]], [[[38.55969, -20.75596], [40.68027, -23.38889], [43.52893, -15.62903], [38.55969, -20.75596]]], [[[47.03092, -11.05648], [47.11593, -12.08552], [47.96702, -11.46447], [47.03092, -11.05648]]]]
22247           }
22248         }, {
22249           type: "Feature",
22250           properties: {
22251             wikidata: "Q191011",
22252             nameEn: "Plazas de soberan\xEDa",
22253             country: "ES"
22254           },
22255           geometry: null
22256         }, {
22257           type: "Feature",
22258           properties: {
22259             wikidata: "Q191146",
22260             nameEn: "Pe\xF1\xF3n de V\xE9lez de la Gomera",
22261             country: "ES",
22262             groups: ["EU", "Q191011", "015", "002", "UN"],
22263             level: "subterritory"
22264           },
22265           geometry: {
22266             type: "MultiPolygon",
22267             coordinates: [[[[-4.30191, 35.17419], [-4.30112, 35.17058], [-4.29436, 35.17149], [-4.30191, 35.17419]]]]
22268           }
22269         }, {
22270           type: "Feature",
22271           properties: {
22272             wikidata: "Q201698",
22273             nameEn: "Crozet Islands",
22274             country: "FR",
22275             groups: ["TF", "Q1451600", "014", "202", "002", "UN"],
22276             level: "subterritory"
22277           },
22278           geometry: {
22279             type: "MultiPolygon",
22280             coordinates: [[[[55.03425, -43.65017], [46.31615, -46.28749], [54.5587, -47.93013], [55.03425, -43.65017]]]]
22281           }
22282         }, {
22283           type: "Feature",
22284           properties: {
22285             wikidata: "Q578170",
22286             nameEn: "Contiguous United States",
22287             aliases: ["CONUS"],
22288             country: "US",
22289             groups: ["Q35657", "021", "003", "019", "UN"],
22290             roadSpeedUnit: "mph",
22291             roadHeightUnit: "ft",
22292             callingCodes: ["1"]
22293           },
22294           geometry: {
22295             type: "MultiPolygon",
22296             coordinates: [[[[-97.13927, 25.96583], [-96.92418, 25.97377], [-80.57035, 24.0565], [-78.91214, 27.76553], [-61.98255, 37.34815], [-67.16117, 44.20069], [-66.93432, 44.82597], [-66.96824, 44.83078], [-66.98249, 44.87071], [-66.96824, 44.90965], [-67.0216, 44.95333], [-67.11316, 45.11176], [-67.15965, 45.16179], [-67.19603, 45.16771], [-67.20349, 45.1722], [-67.22751, 45.16344], [-67.27039, 45.1934], [-67.29748, 45.18173], [-67.29754, 45.14865], [-67.34927, 45.122], [-67.48201, 45.27351], [-67.42394, 45.37969], [-67.50578, 45.48971], [-67.42144, 45.50584], [-67.43815, 45.59162], [-67.6049, 45.60725], [-67.80705, 45.69528], [-67.80653, 45.80022], [-67.75654, 45.82324], [-67.80961, 45.87531], [-67.75196, 45.91814], [-67.78111, 45.9392], [-67.78578, 47.06473], [-67.87993, 47.10377], [-67.94843, 47.1925], [-68.23244, 47.35712], [-68.37458, 47.35851], [-68.38332, 47.28723], [-68.57914, 47.28431], [-68.60575, 47.24659], [-68.70125, 47.24399], [-68.89222, 47.1807], [-69.05039, 47.2456], [-69.05073, 47.30076], [-69.05148, 47.42012], [-69.22119, 47.46461], [-69.99966, 46.69543], [-70.05812, 46.41768], [-70.18547, 46.35357], [-70.29078, 46.18832], [-70.23855, 46.1453], [-70.31025, 45.96424], [-70.24694, 45.95138], [-70.25976, 45.89675], [-70.41523, 45.79497], [-70.38934, 45.73215], [-70.54019, 45.67291], [-70.68516, 45.56964], [-70.72651, 45.49771], [-70.62518, 45.42286], [-70.65383, 45.37592], [-70.78372, 45.43269], [-70.82638, 45.39828], [-70.80236, 45.37444], [-70.84816, 45.22698], [-70.89864, 45.2398], [-70.91169, 45.29849], [-70.95193, 45.33895], [-71.0107, 45.34819], [-71.01866, 45.31573], [-71.08364, 45.30623], [-71.14568, 45.24128], [-71.19723, 45.25438], [-71.22338, 45.25184], [-71.29371, 45.29996], [-71.37133, 45.24624], [-71.44252, 45.2361], [-71.40364, 45.21382], [-71.42778, 45.12624], [-71.48735, 45.07784], [-71.50067, 45.01357], [-73.35025, 45.00942], [-74.32699, 44.99029], [-74.66689, 45.00646], [-74.8447, 45.00606], [-74.99101, 44.98051], [-75.01363, 44.95608], [-75.2193, 44.87821], [-75.41441, 44.76614], [-75.76813, 44.51537], [-75.8217, 44.43176], [-75.95947, 44.34463], [-76.00018, 44.34896], [-76.16285, 44.28262], [-76.1664, 44.23051], [-76.244, 44.19643], [-76.31222, 44.19894], [-76.35324, 44.13493], [-76.43859, 44.09393], [-76.79706, 43.63099], [-79.25796, 43.54052], [-79.06921, 43.26183], [-79.05512, 43.25375], [-79.05544, 43.21224], [-79.05002, 43.20133], [-79.05384, 43.17418], [-79.04652, 43.16396], [-79.0427, 43.13934], [-79.06881, 43.12029], [-79.05671, 43.10937], [-79.07486, 43.07845], [-79.01055, 43.06659], [-78.99941, 43.05612], [-79.02424, 43.01983], [-79.02074, 42.98444], [-78.98126, 42.97], [-78.96312, 42.95509], [-78.93224, 42.95229], [-78.90905, 42.93022], [-78.90712, 42.89733], [-78.93684, 42.82887], [-82.67862, 41.67615], [-83.11184, 41.95671], [-83.14962, 42.04089], [-83.12724, 42.2376], [-83.09837, 42.28877], [-83.07837, 42.30978], [-83.02253, 42.33045], [-82.82964, 42.37355], [-82.64242, 42.55594], [-82.58873, 42.54984], [-82.57583, 42.5718], [-82.51858, 42.611], [-82.51063, 42.66025], [-82.46613, 42.76615], [-82.4826, 42.8068], [-82.45331, 42.93139], [-82.4253, 42.95423], [-82.4146, 42.97626], [-82.42469, 42.992], [-82.48419, 45.30225], [-83.59589, 45.82131], [-83.43746, 45.99749], [-83.57017, 46.105], [-83.83329, 46.12169], [-83.90453, 46.05922], [-83.95399, 46.05634], [-84.1096, 46.23987], [-84.09756, 46.25512], [-84.11615, 46.2681], [-84.11254, 46.32329], [-84.13451, 46.39218], [-84.11196, 46.50248], [-84.12885, 46.53068], [-84.17723, 46.52753], [-84.1945, 46.54061], [-84.2264, 46.53337], [-84.26351, 46.49508], [-84.29893, 46.49127], [-84.34174, 46.50683], [-84.42101, 46.49853], [-84.4481, 46.48972], [-84.47607, 46.45225], [-84.55635, 46.45974], [-84.85871, 46.88881], [-88.37033, 48.30586], [-89.48837, 48.01412], [-89.57972, 48.00023], [-89.77248, 48.02607], [-89.89974, 47.98109], [-90.07418, 48.11043], [-90.56312, 48.09488], [-90.56444, 48.12184], [-90.75045, 48.09143], [-90.87588, 48.2484], [-91.08016, 48.18096], [-91.25025, 48.08522], [-91.43248, 48.04912], [-91.45829, 48.07454], [-91.58025, 48.04339], [-91.55649, 48.10611], [-91.70451, 48.11805], [-91.71231, 48.19875], [-91.86125, 48.21278], [-91.98929, 48.25409], [-92.05339, 48.35958], [-92.14732, 48.36578], [-92.202, 48.35252], [-92.26662, 48.35651], [-92.30939, 48.31251], [-92.27167, 48.25046], [-92.37185, 48.22259], [-92.48147, 48.36609], [-92.45588, 48.40624], [-92.50712, 48.44921], [-92.65606, 48.43471], [-92.71323, 48.46081], [-92.69927, 48.49573], [-92.62747, 48.50278], [-92.6342, 48.54133], [-92.7287, 48.54005], [-92.94973, 48.60866], [-93.25391, 48.64266], [-93.33946, 48.62787], [-93.3712, 48.60599], [-93.39758, 48.60364], [-93.40693, 48.60948], [-93.44472, 48.59147], [-93.47022, 48.54357], [-93.66382, 48.51845], [-93.79267, 48.51631], [-93.80939, 48.52439], [-93.80676, 48.58232], [-93.83288, 48.62745], [-93.85769, 48.63284], [-94.23215, 48.65202], [-94.25104, 48.65729], [-94.25172, 48.68404], [-94.27153, 48.70232], [-94.4174, 48.71049], [-94.44258, 48.69223], [-94.53826, 48.70216], [-94.54885, 48.71543], [-94.58903, 48.71803], [-94.69335, 48.77883], [-94.69669, 48.80918], [-94.70486, 48.82365], [-94.70087, 48.8339], [-94.687, 48.84077], [-94.75017, 49.09931], [-94.77355, 49.11998], [-94.82487, 49.29483], [-94.8159, 49.32299], [-94.85381, 49.32492], [-94.95681, 49.37035], [-94.99532, 49.36579], [-95.01419, 49.35647], [-95.05825, 49.35311], [-95.12903, 49.37056], [-95.15357, 49.384], [-95.15355, 48.9996], [-123.32163, 49.00419], [-123.0093, 48.83186], [-123.0093, 48.76586], [-123.26565, 48.6959], [-123.15614, 48.35395], [-123.50039, 48.21223], [-125.03842, 48.53282], [-133.98258, 38.06389], [-118.48109, 32.5991], [-117.1243, 32.53427], [-115.88053, 32.63624], [-114.71871, 32.71894], [-114.76736, 32.64094], [-114.80584, 32.62028], [-114.81141, 32.55543], [-114.79524, 32.55731], [-114.82011, 32.49609], [-111.07523, 31.33232], [-108.20979, 31.33316], [-108.20899, 31.78534], [-106.529, 31.784], [-106.52266, 31.77509], [-106.51251, 31.76922], [-106.50962, 31.76155], [-106.50111, 31.75714], [-106.48815, 31.74769], [-106.47298, 31.75054], [-106.46726, 31.75998], [-106.45244, 31.76523], [-106.43419, 31.75478], [-106.41773, 31.75196], [-106.38003, 31.73151], [-106.3718, 31.71165], [-106.34864, 31.69663], [-106.33419, 31.66303], [-106.30305, 31.62154], [-106.28084, 31.56173], [-106.24612, 31.54193], [-106.23711, 31.51262], [-106.20346, 31.46305], [-106.09025, 31.40569], [-106.00363, 31.39181], [-104.77674, 30.4236], [-104.5171, 29.64671], [-104.3969, 29.57105], [-104.39363, 29.55396], [-104.37752, 29.54255], [-103.15787, 28.93865], [-102.60596, 29.8192], [-101.47277, 29.7744], [-101.05686, 29.44738], [-101.01128, 29.36947], [-100.96725, 29.3477], [-100.94579, 29.34523], [-100.94056, 29.33371], [-100.87982, 29.296], [-100.79696, 29.24688], [-100.67294, 29.09744], [-100.63689, 28.90812], [-100.59809, 28.88197], [-100.52313, 28.75598], [-100.5075, 28.74066], [-100.51222, 28.70679], [-100.50029, 28.66117], [-99.55409, 27.61314], [-99.51478, 27.55836], [-99.52955, 27.49747], [-99.50208, 27.50021], [-99.48045, 27.49016], [-99.482, 27.47128], [-99.49744, 27.43746], [-99.53573, 27.30926], [-99.08477, 26.39849], [-99.03053, 26.41249], [-99.00546, 26.3925], [-98.35126, 26.15129], [-98.30491, 26.10475], [-98.27075, 26.09457], [-98.24603, 26.07191], [-97.97017, 26.05232], [-97.95155, 26.0625], [-97.66511, 26.01708], [-97.52025, 25.88518], [-97.49828, 25.89877], [-97.45669, 25.86874], [-97.42511, 25.83969], [-97.37332, 25.83854], [-97.35946, 25.92189], [-97.13927, 25.96583]]]]
22297           }
22298         }, {
22299           type: "Feature",
22300           properties: {
22301             wikidata: "Q620634",
22302             nameEn: "Bir Tawil",
22303             groups: ["015", "002"],
22304             level: "territory"
22305           },
22306           geometry: {
22307             type: "MultiPolygon",
22308             coordinates: [[[[33.17563, 22.00405], [33.57251, 21.72406], [33.99686, 21.76784], [34.0765, 22.00501], [33.17563, 22.00405]]]]
22309           }
22310         }, {
22311           type: "Feature",
22312           properties: {
22313             wikidata: "Q639185",
22314             nameEn: "Peros Banhos",
22315             country: "GB",
22316             groups: ["IO", "BOTS", "014", "202", "002", "UN"],
22317             level: "subterritory"
22318           },
22319           geometry: {
22320             type: "MultiPolygon",
22321             coordinates: [[[[72.12587, -4.02588], [70.1848, -6.37445], [72.09518, -5.61768], [72.12587, -4.02588]]]]
22322           }
22323         }, {
22324           type: "Feature",
22325           properties: {
22326             wikidata: "Q644636",
22327             nameEn: "Cyprus",
22328             level: "sharedLandform"
22329           },
22330           geometry: null
22331         }, {
22332           type: "Feature",
22333           properties: {
22334             wikidata: "Q851132",
22335             nameEn: "New Zealand Outlying Islands",
22336             country: "NZ",
22337             level: "subcountryGroup"
22338           },
22339           geometry: null
22340         }, {
22341           type: "Feature",
22342           properties: {
22343             wikidata: "Q875134",
22344             nameEn: "European Russia",
22345             country: "RU",
22346             groups: ["151", "150", "UN"],
22347             callingCodes: ["7"]
22348           },
22349           geometry: {
22350             type: "MultiPolygon",
22351             coordinates: [[[[18.57853, 55.25302], [19.64312, 54.45423], [19.8038, 54.44203], [20.63871, 54.3706], [21.41123, 54.32395], [22.79705, 54.36264], [22.7253, 54.41732], [22.70208, 54.45312], [22.67788, 54.532], [22.71293, 54.56454], [22.68021, 54.58486], [22.7522, 54.63525], [22.74225, 54.64339], [22.75467, 54.6483], [22.73397, 54.66604], [22.73631, 54.72952], [22.87317, 54.79492], [22.85083, 54.88711], [22.76422, 54.92521], [22.68723, 54.9811], [22.65451, 54.97037], [22.60075, 55.01863], [22.58907, 55.07085], [22.47688, 55.04408], [22.31562, 55.0655], [22.14267, 55.05345], [22.11697, 55.02131], [22.06087, 55.02935], [22.02582, 55.05078], [22.03984, 55.07888], [21.99543, 55.08691], [21.96505, 55.07353], [21.85521, 55.09493], [21.64954, 55.1791], [21.55605, 55.20311], [21.51095, 55.18507], [21.46766, 55.21115], [21.38446, 55.29348], [21.35465, 55.28427], [21.26425, 55.24456], [20.95181, 55.27994], [20.60454, 55.40986], [18.57853, 55.25302]]], [[[26.32936, 60.00121], [26.90044, 59.63819], [27.85643, 59.58538], [28.04187, 59.47017], [28.19061, 59.39962], [28.21137, 59.38058], [28.20537, 59.36491], [28.19284, 59.35791], [28.14215, 59.28934], [28.00689, 59.28351], [27.90911, 59.24353], [27.87978, 59.18097], [27.80482, 59.1116], [27.74429, 58.98351], [27.36366, 58.78381], [27.55489, 58.39525], [27.48541, 58.22615], [27.62393, 58.09462], [27.67282, 57.92627], [27.81841, 57.89244], [27.78526, 57.83963], [27.56689, 57.83356], [27.50171, 57.78842], [27.52615, 57.72843], [27.3746, 57.66834], [27.40393, 57.62125], [27.31919, 57.57672], [27.34698, 57.52242], [27.56832, 57.53728], [27.52453, 57.42826], [27.86101, 57.29402], [27.66511, 56.83921], [27.86101, 56.88204], [28.04768, 56.59004], [28.13526, 56.57989], [28.10069, 56.524], [28.19057, 56.44637], [28.16599, 56.37806], [28.23716, 56.27588], [28.15217, 56.16964], [28.30571, 56.06035], [28.36888, 56.05805], [28.37987, 56.11399], [28.43068, 56.09407], [28.5529, 56.11705], [28.68337, 56.10173], [28.63668, 56.07262], [28.73418, 55.97131], [29.08299, 56.03427], [29.21717, 55.98971], [29.44692, 55.95978], [29.3604, 55.75862], [29.51283, 55.70294], [29.61446, 55.77716], [29.80672, 55.79569], [29.97975, 55.87281], [30.12136, 55.8358], [30.27776, 55.86819], [30.30987, 55.83592], [30.48257, 55.81066], [30.51346, 55.78982], [30.51037, 55.76568], [30.63344, 55.73079], [30.67464, 55.64176], [30.72957, 55.66268], [30.7845, 55.58514], [30.86003, 55.63169], [30.93419, 55.6185], [30.95204, 55.50667], [30.90123, 55.46621], [30.93144, 55.3914], [30.8257, 55.3313], [30.81946, 55.27931], [30.87944, 55.28223], [30.97369, 55.17134], [31.02071, 55.06167], [31.00972, 55.02783], [30.94243, 55.03964], [30.9081, 55.02232], [30.95754, 54.98609], [30.93144, 54.9585], [30.81759, 54.94064], [30.8264, 54.90062], [30.75165, 54.80699], [30.95479, 54.74346], [30.97127, 54.71967], [31.0262, 54.70698], [30.98226, 54.68872], [30.99187, 54.67046], [31.19339, 54.66947], [31.21399, 54.63113], [31.08543, 54.50361], [31.22945, 54.46585], [31.3177, 54.34067], [31.30791, 54.25315], [31.57002, 54.14535], [31.89599, 54.0837], [31.88744, 54.03653], [31.85019, 53.91801], [31.77028, 53.80015], [31.89137, 53.78099], [32.12621, 53.81586], [32.36663, 53.7166], [32.45717, 53.74039], [32.50112, 53.68594], [32.40499, 53.6656], [32.47777, 53.5548], [32.74968, 53.45597], [32.73257, 53.33494], [32.51725, 53.28431], [32.40773, 53.18856], [32.15368, 53.07594], [31.82373, 53.10042], [31.787, 53.18033], [31.62496, 53.22886], [31.56316, 53.19432], [31.40523, 53.21406], [31.36403, 53.13504], [31.3915, 53.09712], [31.33519, 53.08805], [31.32283, 53.04101], [31.24147, 53.031], [31.35667, 52.97854], [31.592, 52.79011], [31.57277, 52.71613], [31.50406, 52.69707], [31.63869, 52.55361], [31.56316, 52.51518], [31.61397, 52.48843], [31.62084, 52.33849], [31.57971, 52.32146], [31.70735, 52.26711], [31.6895, 52.1973], [31.77877, 52.18636], [31.7822, 52.11406], [31.81722, 52.09955], [31.85018, 52.11305], [31.96141, 52.08015], [31.92159, 52.05144], [32.08813, 52.03319], [32.23331, 52.08085], [32.2777, 52.10266], [32.34044, 52.1434], [32.33083, 52.23685], [32.38988, 52.24946], [32.3528, 52.32842], [32.54781, 52.32423], [32.69475, 52.25535], [32.85405, 52.27888], [32.89937, 52.2461], [33.18913, 52.3754], [33.51323, 52.35779], [33.48027, 52.31499], [33.55718, 52.30324], [33.78789, 52.37204], [34.05239, 52.20132], [34.11199, 52.14087], [34.09413, 52.00835], [34.41136, 51.82793], [34.42922, 51.72852], [34.07765, 51.67065], [34.17599, 51.63253], [34.30562, 51.5205], [34.22048, 51.4187], [34.33446, 51.363], [34.23009, 51.26429], [34.31661, 51.23936], [34.38802, 51.2746], [34.6613, 51.25053], [34.6874, 51.18], [34.82472, 51.17483], [34.97304, 51.2342], [35.14058, 51.23162], [35.12685, 51.16191], [35.20375, 51.04723], [35.31774, 51.08434], [35.40837, 51.04119], [35.32598, 50.94524], [35.39307, 50.92145], [35.41367, 50.80227], [35.47704, 50.77274], [35.48116, 50.66405], [35.39464, 50.64751], [35.47463, 50.49247], [35.58003, 50.45117], [35.61711, 50.35707], [35.73659, 50.35489], [35.80388, 50.41356], [35.8926, 50.43829], [36.06893, 50.45205], [36.20763, 50.3943], [36.30101, 50.29088], [36.47817, 50.31457], [36.58371, 50.28563], [36.56655, 50.2413], [36.64571, 50.218], [36.69377, 50.26982], [36.91762, 50.34963], [37.08468, 50.34935], [37.48204, 50.46079], [37.47243, 50.36277], [37.62486, 50.29966], [37.62879, 50.24481], [37.61113, 50.21976], [37.75807, 50.07896], [37.79515, 50.08425], [37.90776, 50.04194], [38.02999, 49.94482], [38.02999, 49.90592], [38.21675, 49.98104], [38.18517, 50.08161], [38.32524, 50.08866], [38.35408, 50.00664], [38.65688, 49.97176], [38.68677, 50.00904], [38.73311, 49.90238], [38.90477, 49.86787], [38.9391, 49.79524], [39.1808, 49.88911], [39.27968, 49.75976], [39.44496, 49.76067], [39.59142, 49.73758], [39.65047, 49.61761], [39.84548, 49.56064], [40.13249, 49.61672], [40.16683, 49.56865], [40.03636, 49.52321], [40.03087, 49.45452], [40.1141, 49.38798], [40.14912, 49.37681], [40.18331, 49.34996], [40.22176, 49.25683], [40.01988, 49.1761], [39.93437, 49.05709], [39.6836, 49.05121], [39.6683, 48.99454], [39.71353, 48.98959], [39.72649, 48.9754], [39.74874, 48.98675], [39.78368, 48.91596], [39.98967, 48.86901], [40.03636, 48.91957], [40.08168, 48.87443], [39.97182, 48.79398], [39.79466, 48.83739], [39.73104, 48.7325], [39.71765, 48.68673], [39.67226, 48.59368], [39.79764, 48.58668], [39.84548, 48.57821], [39.86196, 48.46633], [39.88794, 48.44226], [39.94847, 48.35055], [39.84136, 48.33321], [39.84273, 48.30947], [39.90041, 48.3049], [39.91465, 48.26743], [39.95248, 48.29972], [39.9693, 48.29904], [39.97325, 48.31399], [39.99241, 48.31768], [40.00752, 48.22445], [39.94847, 48.22811], [39.83724, 48.06501], [39.88256, 48.04482], [39.77544, 48.04206], [39.82213, 47.96396], [39.73935, 47.82876], [38.87979, 47.87719], [38.79628, 47.81109], [38.76379, 47.69346], [38.35062, 47.61631], [38.28679, 47.53552], [38.28954, 47.39255], [38.22225, 47.30788], [38.33074, 47.30508], [38.32112, 47.2585], [38.23049, 47.2324], [38.22955, 47.12069], [38.3384, 46.98085], [38.12112, 46.86078], [37.62608, 46.82615], [35.23066, 45.79231], [35.04991, 45.76827], [36.6645, 45.4514], [36.6545, 45.3417], [36.5049, 45.3136], [36.475, 45.2411], [36.4883, 45.0488], [33.5943, 44.03313], [39.81147, 43.06294], [40.0078, 43.38551], [40.00853, 43.40578], [40.01552, 43.42025], [40.01007, 43.42411], [40.03312, 43.44262], [40.04445, 43.47776], [40.10657, 43.57344], [40.65957, 43.56212], [41.64935, 43.22331], [42.40563, 43.23226], [42.66667, 43.13917], [42.75889, 43.19651], [43.03322, 43.08883], [43.0419, 43.02413], [43.81453, 42.74297], [43.73119, 42.62043], [43.95517, 42.55396], [44.54202, 42.75699], [44.70002, 42.74679], [44.80941, 42.61277], [44.88754, 42.74934], [45.15318, 42.70598], [45.36501, 42.55268], [45.78692, 42.48358], [45.61676, 42.20768], [46.42738, 41.91323], [46.5332, 41.87389], [46.58924, 41.80547], [46.75269, 41.8623], [46.8134, 41.76252], [47.00955, 41.63583], [46.99554, 41.59743], [47.03757, 41.55434], [47.10762, 41.59044], [47.34579, 41.27884], [47.49004, 41.26366], [47.54504, 41.20275], [47.62288, 41.22969], [47.75831, 41.19455], [47.87973, 41.21798], [48.07587, 41.49957], [48.22064, 41.51472], [48.2878, 41.56221], [48.40277, 41.60441], [48.42301, 41.65444], [48.55078, 41.77917], [48.5867, 41.84306], [48.80971, 41.95365], [49.2134, 44.84989], [49.88945, 46.04554], [49.32259, 46.26944], [49.16518, 46.38542], [48.54988, 46.56267], [48.51142, 46.69268], [49.01136, 46.72716], [48.52326, 47.4102], [48.45173, 47.40818], [48.15348, 47.74545], [47.64973, 47.76559], [47.41689, 47.83687], [47.38731, 47.68176], [47.12107, 47.83687], [47.11516, 48.27188], [46.49011, 48.43019], [46.78392, 48.95352], [47.00857, 49.04921], [47.04658, 49.19834], [46.78398, 49.34026], [46.9078, 49.86707], [47.18319, 49.93721], [47.34589, 50.09308], [47.30448, 50.30894], [47.58551, 50.47867], [48.10044, 50.09242], [48.24519, 49.86099], [48.42564, 49.82283], [48.68352, 49.89546], [48.90782, 50.02281], [48.57946, 50.63278], [48.86936, 50.61589], [49.12673, 50.78639], [49.41959, 50.85927], [49.39001, 51.09396], [49.76866, 51.11067], [49.97277, 51.2405], [50.26859, 51.28677], [50.59695, 51.61859], [51.26254, 51.68466], [51.301, 51.48799], [51.77431, 51.49536], [51.8246, 51.67916], [52.36119, 51.74161], [52.54329, 51.48444], [53.46165, 51.49445], [53.69299, 51.23466], [54.12248, 51.11542], [54.46331, 50.85554], [54.41894, 50.61214], [54.55797, 50.52006], [54.71476, 50.61214], [54.56685, 51.01958], [54.72067, 51.03261], [55.67774, 50.54508], [56.11398, 50.7471], [56.17906, 50.93204], [57.17302, 51.11253], [57.44221, 50.88354], [57.74986, 50.93017], [57.75578, 51.13852], [58.3208, 51.15151], [58.87974, 50.70852], [59.48928, 50.64216], [59.51886, 50.49937], [59.81172, 50.54451], [60.01288, 50.8163], [60.17262, 50.83312], [60.31914, 50.67705], [60.81833, 50.6629], [61.4431, 50.80679], [61.56889, 51.23679], [61.6813, 51.25716], [61.55114, 51.32746], [61.50677, 51.40687], [60.95655, 51.48615], [60.92401, 51.61124], [60.5424, 51.61675], [60.36787, 51.66815], [60.50986, 51.7964], [60.09867, 51.87135], [59.99809, 51.98263], [59.91279, 52.06924], [60.17253, 52.25814], [60.17516, 52.39457], [59.25033, 52.46803], [59.22409, 52.28437], [58.79644, 52.43392], [58.94336, 53.953], [59.70487, 54.14846], [59.95217, 54.85853], [57.95234, 54.39672], [57.14829, 54.84204], [57.25137, 55.26262], [58.81825, 55.03378], [59.49035, 55.60486], [59.28419, 56.15739], [57.51527, 56.08729], [57.28024, 56.87898], [58.07604, 57.08308], [58.13789, 57.68097], [58.81412, 57.71602], [58.71104, 58.07475], [59.40376, 58.45822], [59.15636, 59.14682], [58.3853, 59.487], [59.50685, 60.91162], [59.36223, 61.3882], [59.61398, 62.44915], [59.24834, 63.01859], [59.80579, 64.13948], [59.63945, 64.78384], [60.74386, 64.95767], [61.98014, 65.72191], [66.1708, 67.61252], [64.18965, 69.94255], [76.13964, 83.37843], [36.85549, 84.09565], [32.07813, 72.01005], [31.59909, 70.16571], [30.84095, 69.80584], [30.95011, 69.54699], [30.52662, 69.54699], [30.16363, 69.65244], [29.97205, 69.41623], [29.27631, 69.2811], [29.26623, 69.13794], [29.0444, 69.0119], [28.91738, 69.04774], [28.45957, 68.91417], [28.78224, 68.86696], [28.43941, 68.53366], [28.62982, 68.19816], [29.34179, 68.06655], [29.66955, 67.79872], [30.02041, 67.67523], [29.91155, 67.51507], [28.9839, 66.94139], [29.91155, 66.13863], [30.16363, 65.66935], [29.97205, 65.70256], [29.74013, 65.64025], [29.84096, 65.56945], [29.68972, 65.31803], [29.61914, 65.23791], [29.8813, 65.22101], [29.84096, 65.1109], [29.61914, 65.05993], [29.68972, 64.80789], [30.05271, 64.79072], [30.12329, 64.64862], [30.01238, 64.57513], [30.06279, 64.35782], [30.4762, 64.25728], [30.55687, 64.09036], [30.25437, 63.83364], [29.98213, 63.75795], [30.49637, 63.46666], [31.23244, 63.22239], [31.29294, 63.09035], [31.58535, 62.91642], [31.38369, 62.66284], [31.10136, 62.43042], [29.01829, 61.17448], [28.82816, 61.1233], [28.47974, 60.93365], [27.77352, 60.52722], [27.71177, 60.3893], [27.44953, 60.22766], [26.32936, 60.00121]]]]
22352           }
22353         }, {
22354           type: "Feature",
22355           properties: {
22356             wikidata: "Q1083368",
22357             nameEn: "Mainland Finland",
22358             country: "FI",
22359             groups: ["EU", "154", "150", "UN"],
22360             callingCodes: ["358"]
22361           },
22362           geometry: {
22363             type: "MultiPolygon",
22364             coordinates: [[[[29.12697, 69.69193], [28.36883, 69.81658], [28.32849, 69.88605], [27.97558, 69.99671], [27.95542, 70.0965], [27.57226, 70.06215], [27.05802, 69.92069], [26.64461, 69.96565], [26.40261, 69.91377], [25.96904, 69.68397], [25.69679, 69.27039], [25.75729, 68.99383], [25.61613, 68.89602], [25.42455, 68.90328], [25.12206, 68.78684], [25.10189, 68.63307], [24.93048, 68.61102], [24.90023, 68.55579], [24.74898, 68.65143], [24.18432, 68.73936], [24.02299, 68.81601], [23.781, 68.84514], [23.68017, 68.70276], [23.13064, 68.64684], [22.53321, 68.74393], [22.38367, 68.71561], [22.27276, 68.89514], [21.63833, 69.27485], [21.27827, 69.31281], [21.00732, 69.22755], [20.98641, 69.18809], [21.11099, 69.10291], [21.05775, 69.0356], [20.72171, 69.11874], [20.55258, 69.06069], [20.78802, 69.03087], [20.91658, 68.96764], [20.85104, 68.93142], [20.90649, 68.89696], [21.03001, 68.88969], [22.00429, 68.50692], [22.73028, 68.40881], [23.10336, 68.26551], [23.15377, 68.14759], [23.26469, 68.15134], [23.40081, 68.05545], [23.65793, 67.9497], [23.45627, 67.85297], [23.54701, 67.59306], [23.39577, 67.46974], [23.75372, 67.43688], [23.75372, 67.29914], [23.54701, 67.25435], [23.58735, 67.20752], [23.56214, 67.17038], [23.98563, 66.84149], [23.98059, 66.79585], [23.89488, 66.772], [23.85959, 66.56434], [23.63776, 66.43568], [23.67591, 66.3862], [23.64982, 66.30603], [23.71339, 66.21299], [23.90497, 66.15802], [24.15791, 65.85385], [24.14798, 65.83466], [24.15107, 65.81427], [24.14112, 65.39731], [20.15877, 63.06556], [19.23413, 60.61414], [20.96741, 60.71528], [21.15143, 60.54555], [21.08159, 60.20167], [21.02509, 60.12142], [21.35468, 59.67511], [20.5104, 59.15546], [26.32936, 60.00121], [27.44953, 60.22766], [27.71177, 60.3893], [27.77352, 60.52722], [28.47974, 60.93365], [28.82816, 61.1233], [29.01829, 61.17448], [31.10136, 62.43042], [31.38369, 62.66284], [31.58535, 62.91642], [31.29294, 63.09035], [31.23244, 63.22239], [30.49637, 63.46666], [29.98213, 63.75795], [30.25437, 63.83364], [30.55687, 64.09036], [30.4762, 64.25728], [30.06279, 64.35782], [30.01238, 64.57513], [30.12329, 64.64862], [30.05271, 64.79072], [29.68972, 64.80789], [29.61914, 65.05993], [29.84096, 65.1109], [29.8813, 65.22101], [29.61914, 65.23791], [29.68972, 65.31803], [29.84096, 65.56945], [29.74013, 65.64025], [29.97205, 65.70256], [30.16363, 65.66935], [29.91155, 66.13863], [28.9839, 66.94139], [29.91155, 67.51507], [30.02041, 67.67523], [29.66955, 67.79872], [29.34179, 68.06655], [28.62982, 68.19816], [28.43941, 68.53366], [28.78224, 68.86696], [28.45957, 68.91417], [28.91738, 69.04774], [28.81248, 69.11997], [28.8629, 69.22395], [29.31664, 69.47994], [29.12697, 69.69193]]]]
22365           }
22366         }, {
22367           type: "Feature",
22368           properties: {
22369             wikidata: "Q1184963",
22370             nameEn: "Alhucemas Islands",
22371             country: "ES",
22372             groups: ["EU", "Q191011", "015", "002", "UN"],
22373             level: "subterritory"
22374           },
22375           geometry: {
22376             type: "MultiPolygon",
22377             coordinates: [[[[-3.90602, 35.21494], [-3.88372, 35.20767], [-3.89343, 35.22728], [-3.90602, 35.21494]]]]
22378           }
22379         }, {
22380           type: "Feature",
22381           properties: {
22382             wikidata: "Q1298289",
22383             nameEn: "Egmont Islands",
22384             country: "GB",
22385             groups: ["IO", "BOTS", "014", "202", "002", "UN"],
22386             level: "subterritory"
22387           },
22388           geometry: {
22389             type: "MultiPolygon",
22390             coordinates: [[[[70.1848, -6.37445], [70.67958, -8.2663], [72.17991, -6.68509], [70.1848, -6.37445]]]]
22391           }
22392         }, {
22393           type: "Feature",
22394           properties: {
22395             wikidata: "Q1352230",
22396             nameEn: "US Territories",
22397             country: "US",
22398             level: "subcountryGroup"
22399           },
22400           geometry: null
22401         }, {
22402           type: "Feature",
22403           properties: {
22404             wikidata: "Q1451600",
22405             nameEn: "Overseas Countries and Territories of the EU",
22406             aliases: ["OCT"],
22407             level: "subunion"
22408           },
22409           geometry: null
22410         }, {
22411           type: "Feature",
22412           properties: {
22413             wikidata: "Q1544253",
22414             nameEn: "Great Chagos Bank",
22415             country: "GB",
22416             groups: ["IO", "BOTS", "014", "202", "002", "UN"],
22417             level: "subterritory"
22418           },
22419           geometry: {
22420             type: "MultiPolygon",
22421             coordinates: [[[[70.1848, -6.37445], [72.17991, -6.68509], [73.20573, -5.20727], [70.1848, -6.37445]]]]
22422           }
22423         }, {
22424           type: "Feature",
22425           properties: {
22426             wikidata: "Q1585511",
22427             nameEn: "Salomon Atoll",
22428             country: "GB",
22429             groups: ["IO", "BOTS", "014", "202", "002", "UN"],
22430             level: "subterritory"
22431           },
22432           geometry: {
22433             type: "MultiPolygon",
22434             coordinates: [[[[72.09518, -5.61768], [73.20573, -5.20727], [72.12587, -4.02588], [72.09518, -5.61768]]]]
22435           }
22436         }, {
22437           type: "Feature",
22438           properties: {
22439             wikidata: "Q1681727",
22440             nameEn: "Saint-Paul and Amsterdam",
22441             country: "FR",
22442             groups: ["TF", "Q1451600", "014", "202", "002", "UN"],
22443             level: "subterritory"
22444           },
22445           geometry: {
22446             type: "MultiPolygon",
22447             coordinates: [[[[76.31747, -42.16264], [80.15867, -36.04977], [71.22311, -38.75287], [76.31747, -42.16264]]]]
22448           }
22449         }, {
22450           type: "Feature",
22451           properties: {
22452             wikidata: "Q1901211",
22453             nameEn: "East Malaysia",
22454             country: "MY",
22455             groups: ["Q36117", "035", "142", "UN"],
22456             driveSide: "left",
22457             callingCodes: ["60"]
22458           },
22459           geometry: {
22460             type: "MultiPolygon",
22461             coordinates: [[[[110.90339, 7.52694], [109.82788, 2.86812], [109.62558, 1.99182], [109.53794, 1.91771], [109.57923, 1.80624], [109.66397, 1.79972], [109.66397, 1.60425], [110.35354, 0.98869], [110.49182, 0.88088], [110.62374, 0.873], [111.22979, 1.08326], [111.55434, 0.97864], [111.82846, 0.99349], [111.94553, 1.12016], [112.15679, 1.17004], [112.2127, 1.44135], [112.48648, 1.56516], [113.021, 1.57819], [113.01448, 1.42832], [113.64677, 1.23933], [114.03788, 1.44787], [114.57892, 1.5], [114.80706, 1.92351], [114.80706, 2.21665], [115.1721, 2.49671], [115.11343, 2.82879], [115.53713, 3.14776], [115.58276, 3.93499], [115.90217, 4.37708], [117.25801, 4.35108], [117.47313, 4.18857], [117.67641, 4.16535], [118.06469, 4.16638], [118.93936, 4.09009], [119.52945, 5.35672], [117.98544, 6.27477], [117.93857, 6.89845], [117.17735, 7.52841], [116.79524, 7.43869], [115.02521, 5.35005], [115.16236, 5.01011], [115.15092, 4.87604], [115.20737, 4.8256], [115.27819, 4.63661], [115.2851, 4.42295], [115.36346, 4.33563], [115.31275, 4.30806], [115.09978, 4.39123], [115.07737, 4.53418], [115.04064, 4.63706], [115.02278, 4.74137], [115.02955, 4.82087], [115.05038, 4.90275], [114.99417, 4.88201], [114.96982, 4.81146], [114.88841, 4.81905], [114.8266, 4.75062], [114.77303, 4.72871], [114.83189, 4.42387], [114.88039, 4.4257], [114.78539, 4.12205], [114.64211, 4.00694], [114.49922, 4.13108], [114.4416, 4.27588], [114.32176, 4.2552], [114.32176, 4.34942], [114.26876, 4.49878], [114.15813, 4.57], [114.07448, 4.58441], [114.10166, 4.76112], [110.90339, 7.52694]]]]
22462           }
22463         }, {
22464           type: "Feature",
22465           properties: {
22466             wikidata: "Q1973345",
22467             nameEn: "Peninsular Malaysia",
22468             country: "MY",
22469             groups: ["035", "142", "UN"],
22470             driveSide: "left",
22471             callingCodes: ["60"]
22472           },
22473           geometry: {
22474             type: "MultiPolygon",
22475             coordinates: [[[[102.46318, 7.22462], [102.09086, 6.23546], [102.08127, 6.22679], [102.07732, 6.193], [102.09182, 6.14161], [102.01835, 6.05407], [101.99209, 6.04075], [101.97114, 6.01992], [101.9714, 6.00575], [101.94712, 5.98421], [101.92819, 5.85511], [101.91776, 5.84269], [101.89188, 5.8386], [101.80144, 5.74505], [101.75074, 5.79091], [101.69773, 5.75881], [101.58019, 5.93534], [101.25524, 5.78633], [101.25755, 5.71065], [101.14062, 5.61613], [100.98815, 5.79464], [101.02708, 5.91013], [101.087, 5.9193], [101.12388, 6.11411], [101.06165, 6.14161], [101.12618, 6.19431], [101.10313, 6.25617], [100.85884, 6.24929], [100.81045, 6.45086], [100.74822, 6.46231], [100.74361, 6.50811], [100.66986, 6.45086], [100.43027, 6.52389], [100.42351, 6.51762], [100.41791, 6.5189], [100.41152, 6.52299], [100.35413, 6.54932], [100.31929, 6.65413], [100.32607, 6.65933], [100.32671, 6.66526], [100.31884, 6.66423], [100.31618, 6.66781], [100.30828, 6.66462], [100.29651, 6.68439], [100.19511, 6.72559], [100.12, 6.42105], [100.0756, 6.4045], [99.91873, 6.50233], [99.50117, 6.44501], [99.31854, 5.99868], [99.75778, 3.86466], [103.03657, 1.30383], [103.56591, 1.19719], [103.62738, 1.35255], [103.67468, 1.43166], [103.7219, 1.46108], [103.74161, 1.4502], [103.76395, 1.45183], [103.81181, 1.47953], [103.86383, 1.46288], [103.89565, 1.42841], [103.93384, 1.42926], [104.00131, 1.42405], [104.02277, 1.4438], [104.04622, 1.44691], [104.07348, 1.43322], [104.08871, 1.42015], [104.09162, 1.39694], [104.08072, 1.35998], [104.12282, 1.27714], [104.34728, 1.33529], [104.56723, 1.44271], [105.01437, 3.24936], [102.46318, 7.22462]]]]
22476           }
22477         }, {
22478           type: "Feature",
22479           properties: {
22480             wikidata: "Q2093907",
22481             nameEn: "Three Kings Islands",
22482             country: "NZ",
22483             groups: ["Q851132", "053", "009", "UN"],
22484             driveSide: "left"
22485           },
22486           geometry: {
22487             type: "MultiPolygon",
22488             coordinates: [[[[174.17679, -32.62487], [170.93268, -32.97889], [171.97383, -34.64644], [174.17679, -32.62487]]]]
22489           }
22490         }, {
22491           type: "Feature",
22492           properties: {
22493             wikidata: "Q2298216",
22494             nameEn: "Solander Islands",
22495             country: "NZ",
22496             groups: ["Q851132", "053", "009", "UN"],
22497             driveSide: "left"
22498           },
22499           geometry: {
22500             type: "MultiPolygon",
22501             coordinates: [[[[167.39068, -46.49187], [166.5534, -46.39484], [166.84561, -46.84889], [167.39068, -46.49187]]]]
22502           }
22503         }, {
22504           type: "Feature",
22505           properties: {
22506             wikidata: "Q2872203",
22507             nameEn: "Mainland Australia",
22508             country: "AU",
22509             groups: ["053", "009", "UN"],
22510             level: "subcountryGroup",
22511             driveSide: "left",
22512             callingCodes: ["61"]
22513           },
22514           geometry: {
22515             type: "MultiPolygon",
22516             coordinates: [[[[88.16419, -23.49578], [123.64533, -39.13605], [159.74028, -39.1978], [159.76765, -29.76946], [154.02855, -24.43238], [152.93188, -20.92631], [147.69992, -17.5933], [145.2855, -9.62524], [143.87386, -9.02382], [143.29772, -9.33993], [142.48658, -9.36754], [142.19246, -9.15378], [141.88934, -9.36111], [141.01842, -9.35091], [135.49042, -9.2276], [127.55165, -9.05052], [125.29076, -12.33139], [88.16419, -23.49578]]]]
22517           }
22518         }, {
22519           type: "Feature",
22520           properties: {
22521             wikidata: "Q2914565",
22522             nameEn: "Autonomous Regions of Portugal",
22523             country: "PT",
22524             level: "subcountryGroup"
22525           },
22526           geometry: null
22527         }, {
22528           type: "Feature",
22529           properties: {
22530             wikidata: "Q2915956",
22531             nameEn: "Mainland Portugal",
22532             country: "PT",
22533             groups: ["Q12837", "EU", "039", "150", "UN"],
22534             level: "subcountryGroup",
22535             callingCodes: ["351"]
22536           },
22537           geometry: {
22538             type: "MultiPolygon",
22539             coordinates: [[[[-10.39881, 36.12218], [-7.37282, 36.96896], [-7.39769, 37.16868], [-7.41133, 37.20314], [-7.41854, 37.23813], [-7.43227, 37.25152], [-7.43974, 37.38913], [-7.46878, 37.47127], [-7.51759, 37.56119], [-7.41981, 37.75729], [-7.33441, 37.81193], [-7.27314, 37.90145], [-7.24544, 37.98884], [-7.12648, 38.00296], [-7.10366, 38.04404], [-7.05966, 38.01966], [-7.00375, 38.01914], [-6.93418, 38.21454], [-7.09389, 38.17227], [-7.15581, 38.27597], [-7.32529, 38.44336], [-7.265, 38.61674], [-7.26174, 38.72107], [-7.03848, 38.87221], [-7.051, 38.907], [-6.95211, 39.0243], [-6.97004, 39.07619], [-7.04011, 39.11919], [-7.10692, 39.10275], [-7.14929, 39.11287], [-7.12811, 39.17101], [-7.23566, 39.20132], [-7.23403, 39.27579], [-7.3149, 39.34857], [-7.2927, 39.45847], [-7.49477, 39.58794], [-7.54121, 39.66717], [-7.33507, 39.64569], [-7.24707, 39.66576], [-7.01613, 39.66877], [-6.97492, 39.81488], [-6.91463, 39.86618], [-6.86737, 40.01986], [-6.94233, 40.10716], [-7.00589, 40.12087], [-7.02544, 40.18564], [-7.00426, 40.23169], [-6.86085, 40.26776], [-6.86085, 40.2976], [-6.80218, 40.33239], [-6.78426, 40.36468], [-6.84618, 40.42177], [-6.84944, 40.46394], [-6.7973, 40.51723], [-6.80218, 40.55067], [-6.84292, 40.56801], [-6.79567, 40.65955], [-6.82826, 40.74603], [-6.82337, 40.84472], [-6.79892, 40.84842], [-6.80707, 40.88047], [-6.84292, 40.89771], [-6.8527, 40.93958], [-6.9357, 41.02888], [-6.913, 41.03922], [-6.88843, 41.03027], [-6.84781, 41.02692], [-6.80942, 41.03629], [-6.79241, 41.05397], [-6.75655, 41.10187], [-6.77319, 41.13049], [-6.69711, 41.1858], [-6.68286, 41.21641], [-6.65046, 41.24725], [-6.55937, 41.24417], [-6.38551, 41.35274], [-6.38553, 41.38655], [-6.3306, 41.37677], [-6.26777, 41.48796], [-6.19128, 41.57638], [-6.29863, 41.66432], [-6.44204, 41.68258], [-6.49907, 41.65823], [-6.54633, 41.68623], [-6.56426, 41.74219], [-6.51374, 41.8758], [-6.56752, 41.88429], [-6.5447, 41.94371], [-6.58544, 41.96674], [-6.61967, 41.94008], [-6.75004, 41.94129], [-6.76959, 41.98734], [-6.81196, 41.99097], [-6.82174, 41.94493], [-6.94396, 41.94403], [-6.95537, 41.96553], [-6.98144, 41.9728], [-7.01078, 41.94977], [-7.07596, 41.94977], [-7.08574, 41.97401], [-7.14115, 41.98855], [-7.18549, 41.97515], [-7.18677, 41.88793], [-7.32366, 41.8406], [-7.37092, 41.85031], [-7.42864, 41.80589], [-7.42854, 41.83262], [-7.44759, 41.84451], [-7.45566, 41.86488], [-7.49803, 41.87095], [-7.52737, 41.83939], [-7.62188, 41.83089], [-7.58603, 41.87944], [-7.65774, 41.88308], [-7.69848, 41.90977], [-7.84188, 41.88065], [-7.88055, 41.84571], [-7.88751, 41.92553], [-7.90707, 41.92432], [-7.92336, 41.8758], [-7.9804, 41.87337], [-8.01136, 41.83453], [-8.0961, 41.81024], [-8.16455, 41.81753], [-8.16944, 41.87944], [-8.19551, 41.87459], [-8.2185, 41.91237], [-8.16232, 41.9828], [-8.08796, 42.01398], [-8.08847, 42.05767], [-8.11729, 42.08537], [-8.18178, 42.06436], [-8.19406, 42.12141], [-8.18947, 42.13853], [-8.1986, 42.15402], [-8.22406, 42.1328], [-8.24681, 42.13993], [-8.2732, 42.12396], [-8.29809, 42.106], [-8.32161, 42.10218], [-8.33912, 42.08358], [-8.36353, 42.09065], [-8.38323, 42.07683], [-8.40143, 42.08052], [-8.42512, 42.07199], [-8.44123, 42.08218], [-8.48185, 42.0811], [-8.52837, 42.07658], [-8.5252, 42.06264], [-8.54563, 42.0537], [-8.58086, 42.05147], [-8.59493, 42.05708], [-8.63791, 42.04691], [-8.64626, 42.03668], [-8.65832, 42.02972], [-8.6681, 41.99703], [-8.69071, 41.98862], [-8.7478, 41.96282], [-8.74606, 41.9469], [-8.75712, 41.92833], [-8.81794, 41.90375], [-8.87157, 41.86488], [-11.19304, 41.83075], [-10.39881, 36.12218]]]]
22540           }
22541         }, {
22542           type: "Feature",
22543           properties: {
22544             wikidata: "Q3311985",
22545             nameEn: "Guernsey",
22546             country: "GB",
22547             groups: ["GG", "830", "Q185086", "154", "150", "UN"],
22548             level: "subterritory",
22549             driveSide: "left",
22550             roadSpeedUnit: "mph",
22551             roadHeightUnit: "ft",
22552             callingCodes: ["44 01481"]
22553           },
22554           geometry: {
22555             type: "MultiPolygon",
22556             coordinates: [[[[-2.49556, 49.79012], [-3.28154, 49.57329], [-2.65349, 49.15373], [-2.36485, 49.48223], [-2.49556, 49.79012]]]]
22557           }
22558         }, {
22559           type: "Feature",
22560           properties: {
22561             wikidata: "Q3320166",
22562             nameEn: "Outermost Regions of the EU",
22563             aliases: ["OMR"],
22564             level: "subunion"
22565           },
22566           geometry: null
22567         }, {
22568           type: "Feature",
22569           properties: {
22570             wikidata: "Q3336843",
22571             nameEn: "Countries of the United Kingdom",
22572             country: "GB",
22573             level: "subcountryGroup"
22574           },
22575           geometry: null
22576         }, {
22577           type: "Feature",
22578           properties: {
22579             wikidata: "Q6736667",
22580             nameEn: "Mainland India",
22581             country: "IN",
22582             groups: ["034", "142", "UN"],
22583             driveSide: "left",
22584             callingCodes: ["91"]
22585           },
22586           geometry: {
22587             type: "MultiPolygon",
22588             coordinates: [[[[89.08044, 21.41871], [89.07114, 22.15335], [88.9367, 22.58527], [88.94614, 22.66941], [88.9151, 22.75228], [88.96713, 22.83346], [88.87063, 22.95235], [88.88327, 23.03885], [88.86377, 23.08759], [88.99148, 23.21134], [88.71133, 23.2492], [88.79254, 23.46028], [88.79351, 23.50535], [88.74841, 23.47361], [88.56507, 23.64044], [88.58087, 23.87105], [88.66189, 23.87607], [88.73743, 23.91751], [88.6976, 24.14703], [88.74841, 24.1959], [88.68801, 24.31464], [88.50934, 24.32474], [88.12296, 24.51301], [88.08786, 24.63232], [88.00683, 24.66477], [88.15515, 24.85806], [88.14004, 24.93529], [88.21832, 24.96642], [88.27325, 24.88796], [88.33917, 24.86803], [88.46277, 25.07468], [88.44766, 25.20149], [88.94067, 25.18534], [89.00463, 25.26583], [89.01105, 25.30303], [88.85278, 25.34679], [88.81296, 25.51546], [88.677, 25.46959], [88.4559, 25.59227], [88.45103, 25.66245], [88.242, 25.80811], [88.13138, 25.78773], [88.08804, 25.91334], [88.16581, 26.0238], [88.1844, 26.14417], [88.34757, 26.22216], [88.35153, 26.29123], [88.51649, 26.35923], [88.48749, 26.45855], [88.36938, 26.48683], [88.35153, 26.45241], [88.33093, 26.48929], [88.41196, 26.63837], [88.4298, 26.54489], [88.62144, 26.46783], [88.69485, 26.38353], [88.67837, 26.26291], [88.78961, 26.31093], [88.85004, 26.23211], [89.05328, 26.2469], [88.91321, 26.37984], [88.92357, 26.40711], [88.95612, 26.4564], [89.08899, 26.38845], [89.15869, 26.13708], [89.35953, 26.0077], [89.53515, 26.00382], [89.57101, 25.9682], [89.63968, 26.22595], [89.70201, 26.15138], [89.73581, 26.15818], [89.77865, 26.08387], [89.77728, 26.04254], [89.86592, 25.93115], [89.80585, 25.82489], [89.84388, 25.70042], [89.86129, 25.61714], [89.81208, 25.37244], [89.84086, 25.31854], [89.83371, 25.29548], [89.87629, 25.28337], [89.90478, 25.31038], [90.1155, 25.22686], [90.40034, 25.1534], [90.65042, 25.17788], [90.87427, 25.15799], [91.25517, 25.20677], [91.63648, 25.12846], [92.0316, 25.1834], [92.33957, 25.07593], [92.39147, 25.01471], [92.49887, 24.88796], [92.38626, 24.86055], [92.25854, 24.9191], [92.15796, 24.54435], [92.11662, 24.38997], [91.96603, 24.3799], [91.89258, 24.14674], [91.82596, 24.22345], [91.76004, 24.23848], [91.73257, 24.14703], [91.65292, 24.22095], [91.63782, 24.1132], [91.55542, 24.08687], [91.37414, 24.10693], [91.35741, 23.99072], [91.29587, 24.0041], [91.22308, 23.89616], [91.25192, 23.83463], [91.15579, 23.6599], [91.28293, 23.37538], [91.36453, 23.06612], [91.40848, 23.07117], [91.4035, 23.27522], [91.46615, 23.2328], [91.54993, 23.01051], [91.61571, 22.93929], [91.7324, 23.00043], [91.81634, 23.08001], [91.76417, 23.26619], [91.84789, 23.42235], [91.95642, 23.47361], [91.95093, 23.73284], [92.04706, 23.64229], [92.15417, 23.73409], [92.26541, 23.70392], [92.38214, 23.28705], [92.37665, 22.9435], [92.5181, 22.71441], [92.60029, 22.1522], [92.56616, 22.13554], [92.60949, 21.97638], [92.67532, 22.03547], [92.70416, 22.16017], [92.86208, 22.05456], [92.89504, 21.95143], [92.93899, 22.02656], [92.99804, 21.98964], [92.99255, 22.05965], [93.04885, 22.20595], [93.15734, 22.18687], [93.14224, 22.24535], [93.19991, 22.25425], [93.18206, 22.43716], [93.13537, 22.45873], [93.11477, 22.54374], [93.134, 22.59573], [93.09417, 22.69459], [93.134, 22.92498], [93.12988, 23.05772], [93.2878, 23.00464], [93.38478, 23.13698], [93.36862, 23.35426], [93.38781, 23.36139], [93.39981, 23.38828], [93.38805, 23.4728], [93.43475, 23.68299], [93.3908, 23.7622], [93.3908, 23.92925], [93.36059, 23.93176], [93.32351, 24.04468], [93.34735, 24.10151], [93.41415, 24.07854], [93.46633, 23.97067], [93.50616, 23.94432], [93.62871, 24.00922], [93.75952, 24.0003], [93.80279, 23.92549], [93.92089, 23.95812], [94.14081, 23.83333], [94.30215, 24.23752], [94.32362, 24.27692], [94.45279, 24.56656], [94.50729, 24.59281], [94.5526, 24.70764], [94.60204, 24.70889], [94.73937, 25.00545], [94.74212, 25.13606], [94.57458, 25.20318], [94.68032, 25.47003], [94.80117, 25.49359], [95.18556, 26.07338], [95.11428, 26.1019], [95.12801, 26.38397], [95.05798, 26.45408], [95.23513, 26.68499], [95.30339, 26.65372], [95.437, 26.7083], [95.81603, 27.01335], [95.93002, 27.04149], [96.04949, 27.19428], [96.15591, 27.24572], [96.40779, 27.29818], [96.55761, 27.29928], [96.73888, 27.36638], [96.88445, 27.25046], [96.85287, 27.2065], [96.89132, 27.17474], [97.14675, 27.09041], [97.17422, 27.14052], [96.91431, 27.45752], [96.90112, 27.62149], [97.29919, 27.92233], [97.35824, 27.87256], [97.38845, 28.01329], [97.35412, 28.06663], [97.31292, 28.06784], [97.34547, 28.21385], [97.1289, 28.3619], [96.98882, 28.32564], [96.88445, 28.39452], [96.85561, 28.4875], [96.6455, 28.61657], [96.48895, 28.42955], [96.40929, 28.51526], [96.61391, 28.72742], [96.3626, 29.10607], [96.20467, 29.02325], [96.18682, 29.11087], [96.31316, 29.18643], [96.05361, 29.38167], [95.84899, 29.31464], [95.75149, 29.32063], [95.72086, 29.20797], [95.50842, 29.13487], [95.41091, 29.13007], [95.3038, 29.13847], [95.26122, 29.07727], [95.2214, 29.10727], [95.11291, 29.09527], [95.0978, 29.14446], [94.81353, 29.17804], [94.69318, 29.31739], [94.2752, 29.11687], [94.35897, 29.01965], [93.72797, 28.68821], [93.44621, 28.67189], [93.18069, 28.50319], [93.14635, 28.37035], [92.93075, 28.25671], [92.67486, 28.15018], [92.65472, 28.07632], [92.73025, 28.05814], [92.7275, 27.98662], [92.42538, 27.80092], [92.32101, 27.79363], [92.27432, 27.89077], [91.87057, 27.7195], [91.84722, 27.76325], [91.6469, 27.76358], [91.55819, 27.6144], [91.65007, 27.48287], [92.01132, 27.47352], [92.12019, 27.27829], [92.04702, 27.26861], [92.03457, 27.07334], [92.11863, 26.893], [92.05523, 26.8692], [91.83181, 26.87318], [91.50067, 26.79223], [90.67715, 26.77215], [90.48504, 26.8594], [90.39271, 26.90704], [90.30402, 26.85098], [90.04535, 26.72422], [89.86124, 26.73307], [89.63369, 26.74402], [89.42349, 26.83727], [89.3901, 26.84225], [89.38319, 26.85963], [89.37913, 26.86224], [89.1926, 26.81329], [89.12825, 26.81661], [89.09554, 26.89089], [88.95807, 26.92668], [88.92301, 26.99286], [88.8714, 26.97488], [88.86984, 27.10937], [88.74219, 27.144], [88.91901, 27.32483], [88.82981, 27.38814], [88.77517, 27.45415], [88.88091, 27.85192], [88.83559, 28.01936], [88.63235, 28.12356], [88.54858, 28.06057], [88.25332, 27.9478], [88.1278, 27.95417], [88.13378, 27.88015], [88.1973, 27.85067], [88.19107, 27.79285], [88.04008, 27.49223], [88.07277, 27.43007], [88.01646, 27.21612], [88.01587, 27.21388], [87.9887, 27.11045], [88.11719, 26.98758], [88.13422, 26.98705], [88.12302, 26.95324], [88.19107, 26.75516], [88.1659, 26.68177], [88.16452, 26.64111], [88.09963, 26.54195], [88.09414, 26.43732], [88.00895, 26.36029], [87.90115, 26.44923], [87.89085, 26.48565], [87.84193, 26.43663], [87.7918, 26.46737], [87.76004, 26.40711], [87.67893, 26.43501], [87.66803, 26.40294], [87.59175, 26.38342], [87.55274, 26.40596], [87.51571, 26.43106], [87.46566, 26.44058], [87.37314, 26.40815], [87.34568, 26.34787], [87.26568, 26.37294], [87.26587, 26.40592], [87.24682, 26.4143], [87.18863, 26.40558], [87.14751, 26.40542], [87.09147, 26.45039], [87.0707, 26.58571], [87.04691, 26.58685], [87.01559, 26.53228], [86.95912, 26.52076], [86.94543, 26.52076], [86.82898, 26.43919], [86.76797, 26.45892], [86.74025, 26.42386], [86.69124, 26.45169], [86.62686, 26.46891], [86.61313, 26.48658], [86.57073, 26.49825], [86.54258, 26.53819], [86.49726, 26.54218], [86.31564, 26.61925], [86.26235, 26.61886], [86.22513, 26.58863], [86.13596, 26.60651], [86.02729, 26.66756], [85.8492, 26.56667], [85.85126, 26.60866], [85.83126, 26.61134], [85.76907, 26.63076], [85.72315, 26.67471], [85.73483, 26.79613], [85.66239, 26.84822], [85.61621, 26.86721], [85.59461, 26.85161], [85.5757, 26.85955], [85.56471, 26.84133], [85.47752, 26.79292], [85.34302, 26.74954], [85.21159, 26.75933], [85.18046, 26.80519], [85.19291, 26.86909], [85.15883, 26.86966], [85.02635, 26.85381], [85.05592, 26.88991], [85.00536, 26.89523], [84.97186, 26.9149], [84.96687, 26.95599], [84.85754, 26.98984], [84.82913, 27.01989], [84.793, 26.9968], [84.64496, 27.04669], [84.69166, 27.21294], [84.62161, 27.33885], [84.29315, 27.39], [84.25735, 27.44941], [84.21376, 27.45218], [84.10791, 27.52399], [84.02229, 27.43836], [83.93306, 27.44939], [83.86182, 27.4241], [83.85595, 27.35797], [83.61288, 27.47013], [83.39495, 27.4798], [83.38872, 27.39276], [83.35136, 27.33885], [83.29999, 27.32778], [83.2673, 27.36235], [83.27197, 27.38309], [83.19413, 27.45632], [82.94938, 27.46036], [82.93261, 27.50328], [82.74119, 27.49838], [82.70378, 27.72122], [82.46405, 27.6716], [82.06554, 27.92222], [81.97214, 27.93322], [81.91223, 27.84995], [81.47867, 28.08303], [81.48179, 28.12148], [81.38683, 28.17638], [81.32923, 28.13521], [81.19847, 28.36284], [81.03471, 28.40054], [80.55142, 28.69182], [80.50575, 28.6706], [80.52443, 28.54897], [80.44504, 28.63098], [80.37188, 28.63371], [80.12125, 28.82346], [80.06957, 28.82763], [80.05743, 28.91479], [80.18085, 29.13649], [80.23178, 29.11626], [80.26602, 29.13938], [80.24112, 29.21414], [80.28626, 29.20327], [80.31428, 29.30784], [80.24322, 29.44299], [80.37939, 29.57098], [80.41858, 29.63581], [80.38428, 29.68513], [80.36803, 29.73865], [80.41554, 29.79451], [80.43458, 29.80466], [80.48997, 29.79566], [80.56247, 29.86661], [80.57179, 29.91422], [80.60226, 29.95732], [80.67076, 29.95732], [80.8778, 30.13384], [80.86673, 30.17321], [80.91143, 30.22173], [80.92547, 30.17193], [81.03953, 30.20059], [80.83343, 30.32023], [80.54504, 30.44936], [80.20721, 30.58541], [79.93255, 30.88288], [79.59884, 30.93943], [79.30694, 31.17357], [79.14016, 31.43403], [79.01931, 31.42817], [78.89344, 31.30481], [78.77898, 31.31209], [78.71032, 31.50197], [78.84516, 31.60631], [78.69933, 31.78723], [78.78036, 31.99478], [78.74404, 32.00384], [78.68754, 32.10256], [78.49609, 32.2762], [78.4645, 32.45367], [78.38897, 32.53938], [78.73916, 32.69438], [78.7831, 32.46873], [78.96713, 32.33655], [78.99322, 32.37948], [79.0979, 32.38051], [79.13174, 32.47766], [79.26768, 32.53277], [79.46562, 32.69668], [79.14016, 33.02545], [79.15252, 33.17156], [78.73636, 33.56521], [78.67599, 33.66445], [78.77349, 33.73871], [78.73367, 34.01121], [78.65657, 34.03195], [78.66225, 34.08858], [78.91769, 34.15452], [78.99802, 34.3027], [79.05364, 34.32482], [78.74465, 34.45174], [78.56475, 34.50835], [78.54964, 34.57283], [78.27781, 34.61484], [78.18435, 34.7998], [78.22692, 34.88771], [78.00033, 35.23954], [78.03466, 35.3785], [78.11664, 35.48022], [77.80532, 35.52058], [77.70232, 35.46244], [77.44277, 35.46132], [76.96624, 35.5932], [76.84539, 35.67356], [76.77323, 35.66062], [76.75475, 35.52617], [76.85088, 35.39754], [76.93465, 35.39866], [77.11796, 35.05419], [76.99251, 34.93349], [76.87193, 34.96906], [76.74514, 34.92488], [76.74377, 34.84039], [76.67648, 34.76371], [76.47186, 34.78965], [76.15463, 34.6429], [76.04614, 34.67566], [75.75438, 34.51827], [75.38009, 34.55021], [75.01479, 34.64629], [74.6663, 34.703], [74.58083, 34.77386], [74.31239, 34.79626], [74.12897, 34.70073], [73.96423, 34.68244], [73.93401, 34.63386], [73.93951, 34.57169], [73.89419, 34.54568], [73.88732, 34.48911], [73.74999, 34.3781], [73.74862, 34.34183], [73.8475, 34.32935], [73.90517, 34.35317], [73.98208, 34.2522], [73.90677, 34.10504], [73.88732, 34.05105], [73.91341, 34.01235], [74.21554, 34.03853], [74.25262, 34.01577], [74.26086, 33.92237], [74.14001, 33.83002], [74.05898, 33.82089], [74.00891, 33.75437], [73.96423, 33.73071], [73.98968, 33.66155], [73.97367, 33.64061], [74.03576, 33.56718], [74.10115, 33.56392], [74.18121, 33.4745], [74.17983, 33.3679], [74.08782, 33.26232], [74.01366, 33.25199], [74.02144, 33.18908], [74.15374, 33.13477], [74.17571, 33.07495], [74.31854, 33.02891], [74.34875, 32.97823], [74.31227, 32.92795], [74.41467, 32.90563], [74.45312, 32.77755], [74.6289, 32.75561], [74.64675, 32.82604], [74.7113, 32.84219], [74.65345, 32.71225], [74.69542, 32.66792], [74.64424, 32.60985], [74.65251, 32.56416], [74.67431, 32.56676], [74.68362, 32.49298], [74.84725, 32.49075], [74.97634, 32.45367], [75.03265, 32.49538], [75.28259, 32.36556], [75.38046, 32.26836], [75.25649, 32.10187], [75.00793, 32.03786], [74.9269, 32.0658], [74.86236, 32.04485], [74.79919, 31.95983], [74.58907, 31.87824], [74.47771, 31.72227], [74.57498, 31.60382], [74.61517, 31.55698], [74.59319, 31.50197], [74.64713, 31.45605], [74.59773, 31.4136], [74.53223, 31.30321], [74.51629, 31.13829], [74.56023, 31.08303], [74.60281, 31.10419], [74.60006, 31.13711], [74.6852, 31.12771], [74.67971, 31.05479], [74.5616, 31.04153], [73.88993, 30.36305], [73.95736, 30.28466], [73.97225, 30.19829], [73.80299, 30.06969], [73.58665, 30.01848], [73.3962, 29.94707], [73.28094, 29.56646], [73.05886, 29.1878], [73.01337, 29.16422], [72.94272, 29.02487], [72.40402, 28.78283], [72.29495, 28.66367], [72.20329, 28.3869], [71.9244, 28.11555], [71.89921, 27.96035], [70.79054, 27.68423], [70.60927, 28.02178], [70.37307, 28.01208], [70.12502, 27.8057], [70.03136, 27.56627], [69.58519, 27.18109], [69.50904, 26.74892], [69.88555, 26.56836], [70.05584, 26.60398], [70.17532, 26.55362], [70.17532, 26.24118], [70.08193, 26.08094], [70.0985, 25.93238], [70.2687, 25.71156], [70.37444, 25.67443], [70.53649, 25.68928], [70.60378, 25.71898], [70.67382, 25.68186], [70.66695, 25.39314], [70.89148, 25.15064], [70.94002, 24.92843], [71.09405, 24.69017], [70.97594, 24.60904], [71.00341, 24.46038], [71.12838, 24.42662], [71.04461, 24.34657], [70.94985, 24.3791], [70.85784, 24.30903], [70.88393, 24.27398], [70.71502, 24.23517], [70.57906, 24.27774], [70.5667, 24.43787], [70.11712, 24.30915], [70.03428, 24.172], [69.73335, 24.17007], [69.59579, 24.29777], [69.29778, 24.28712], [69.19341, 24.25646], [69.07806, 24.29777], [68.97781, 24.26021], [68.90914, 24.33156], [68.7416, 24.31904], [68.74643, 23.97027], [68.39339, 23.96838], [68.20763, 23.85849], [68.11329, 23.53945], [76.59015, 5.591], [79.50447, 8.91876], [79.42124, 9.80115], [80.48418, 10.20786], [89.08044, 21.41871]]]]
22589           }
22590         }, {
22591           type: "Feature",
22592           properties: {
22593             wikidata: "Q9143535",
22594             nameEn: "Akrotiri",
22595             country: "GB",
22596             groups: ["Q644636", "Q37362", "BOTS", "145", "142", "UN"],
22597             level: "subterritory",
22598             driveSide: "left",
22599             callingCodes: ["357"]
22600           },
22601           geometry: {
22602             type: "MultiPolygon",
22603             coordinates: [[[[32.86014, 34.70585], [32.82717, 34.70622], [32.79433, 34.67883], [32.76136, 34.68318], [32.75515, 34.64985], [32.74412, 34.43926], [33.26744, 34.49942], [33.0138, 34.64424], [32.96968, 34.64046], [32.96718, 34.63446], [32.95891, 34.62919], [32.95323, 34.64075], [32.95471, 34.64528], [32.94976, 34.65204], [32.94796, 34.6587], [32.95325, 34.66462], [32.97079, 34.66112], [32.97736, 34.65277], [32.99014, 34.65518], [32.98668, 34.67268], [32.99135, 34.68061], [32.95539, 34.68471], [32.94683, 34.67907], [32.94379, 34.67111], [32.93693, 34.67027], [32.93449, 34.66241], [32.92807, 34.66736], [32.93043, 34.67091], [32.91398, 34.67343], [32.9068, 34.66102], [32.86167, 34.68734], [32.86014, 34.70585]]]]
22604           }
22605         }, {
22606           type: "Feature",
22607           properties: {
22608             wikidata: "Q9206745",
22609             nameEn: "Dhekelia",
22610             country: "GB",
22611             groups: ["Q644636", "Q37362", "BOTS", "145", "142", "UN"],
22612             level: "subterritory",
22613             driveSide: "left",
22614             callingCodes: ["357"]
22615           },
22616           geometry: {
22617             type: "MultiPolygon",
22618             coordinates: [[[[33.70575, 34.97947], [33.83531, 34.73974], [33.98684, 34.76642], [33.90075, 34.96623], [33.86432, 34.97592], [33.84811, 34.97075], [33.83505, 34.98108], [33.85621, 34.98956], [33.85891, 35.001], [33.85216, 35.00579], [33.84045, 35.00616], [33.82875, 35.01685], [33.83055, 35.02865], [33.81524, 35.04192], [33.8012, 35.04786], [33.82051, 35.0667], [33.8355, 35.05777], [33.85261, 35.0574], [33.88367, 35.07877], [33.89485, 35.06873], [33.90247, 35.07686], [33.91299, 35.07579], [33.91789, 35.08688], [33.89853, 35.11377], [33.88737, 35.11408], [33.88943, 35.12007], [33.88561, 35.12449], [33.87224, 35.12293], [33.87622, 35.10457], [33.87097, 35.09389], [33.87479, 35.08881], [33.8541, 35.07201], [33.84168, 35.06823], [33.82067, 35.07826], [33.78581, 35.05104], [33.76106, 35.04253], [33.73824, 35.05321], [33.71482, 35.03722], [33.70209, 35.04882], [33.7161, 35.07279], [33.70861, 35.07644], [33.69095, 35.06237], [33.68474, 35.06602], [33.67742, 35.05963], [33.67678, 35.03866], [33.69938, 35.03123], [33.69731, 35.01754], [33.71514, 35.00294], [33.70639, 34.99303], [33.70575, 34.97947]], [[33.77312, 34.9976], [33.77553, 34.99518], [33.78516, 34.99582], [33.79191, 34.98914], [33.78917, 34.98854], [33.78571, 34.98951], [33.78318, 34.98699], [33.78149, 34.98854], [33.77843, 34.988], [33.7778, 34.98981], [33.76738, 34.99188], [33.76605, 34.99543], [33.75682, 34.99916], [33.75994, 35.00113], [33.77312, 34.9976]], [[33.74144, 35.01053], [33.7343, 35.01178], [33.73781, 35.02181], [33.74265, 35.02329], [33.74983, 35.02274], [33.7492, 35.01319], [33.74144, 35.01053]]]]
22619           }
22620         }, {
22621           type: "Feature",
22622           properties: {
22623             wikidata: "Q16390686",
22624             nameEn: "Peninsular Spain",
22625             country: "ES",
22626             groups: ["Q12837", "EU", "039", "150", "UN"],
22627             callingCodes: ["34"]
22628           },
22629           geometry: {
22630             type: "MultiPolygon",
22631             coordinates: [[[[3.75438, 42.33445], [3.17156, 42.43545], [3.11379, 42.43646], [3.10027, 42.42621], [3.08167, 42.42748], [3.03734, 42.47363], [2.96518, 42.46692], [2.94283, 42.48174], [2.92107, 42.4573], [2.88413, 42.45938], [2.86983, 42.46843], [2.85675, 42.45444], [2.84335, 42.45724], [2.77464, 42.41046], [2.75497, 42.42578], [2.72056, 42.42298], [2.65311, 42.38771], [2.6747, 42.33974], [2.57934, 42.35808], [2.55516, 42.35351], [2.54382, 42.33406], [2.48457, 42.33933], [2.43508, 42.37568], [2.43299, 42.39423], [2.38504, 42.39977], [2.25551, 42.43757], [2.20578, 42.41633], [2.16599, 42.42314], [2.12789, 42.41291], [2.11621, 42.38393], [2.06241, 42.35906], [2.00488, 42.35399], [1.96482, 42.37787], [1.9574, 42.42401], [1.94084, 42.43039], [1.94061, 42.43333], [1.94292, 42.44316], [1.93663, 42.45439], [1.88853, 42.4501], [1.83037, 42.48395], [1.76335, 42.48863], [1.72515, 42.50338], [1.70571, 42.48867], [1.66826, 42.50779], [1.65674, 42.47125], [1.58933, 42.46275], [1.57953, 42.44957], [1.55937, 42.45808], [1.55073, 42.43299], [1.5127, 42.42959], [1.44529, 42.43724], [1.43838, 42.47848], [1.41648, 42.48315], [1.46661, 42.50949], [1.44759, 42.54431], [1.41245, 42.53539], [1.4234, 42.55959], [1.44529, 42.56722], [1.42512, 42.58292], [1.44197, 42.60217], [1.35562, 42.71944], [1.15928, 42.71407], [1.0804, 42.78569], [0.98292, 42.78754], [0.96166, 42.80629], [0.93089, 42.79154], [0.711, 42.86372], [0.66121, 42.84021], [0.65421, 42.75872], [0.67873, 42.69458], [0.40214, 42.69779], [0.36251, 42.72282], [0.29407, 42.67431], [0.25336, 42.7174], [0.17569, 42.73424], [-0.02468, 42.68513], [-0.10519, 42.72761], [-0.16141, 42.79535], [-0.17939, 42.78974], [-0.3122, 42.84788], [-0.38833, 42.80132], [-0.41319, 42.80776], [-0.44334, 42.79939], [-0.50863, 42.82713], [-0.55497, 42.77846], [-0.67637, 42.88303], [-0.69837, 42.87945], [-0.72608, 42.89318], [-0.73422, 42.91228], [-0.72037, 42.92541], [-0.75478, 42.96916], [-0.81652, 42.95166], [-0.97133, 42.96239], [-1.00963, 42.99279], [-1.10333, 43.0059], [-1.22881, 43.05534], [-1.25244, 43.04164], [-1.30531, 43.06859], [-1.30052, 43.09581], [-1.27118, 43.11961], [-1.32209, 43.1127], [-1.34419, 43.09665], [-1.35272, 43.02658], [-1.44067, 43.047], [-1.47555, 43.08372], [-1.41562, 43.12815], [-1.3758, 43.24511], [-1.40942, 43.27272], [-1.45289, 43.27049], [-1.50992, 43.29481], [-1.55963, 43.28828], [-1.57674, 43.25269], [-1.61341, 43.25269], [-1.63052, 43.28591], [-1.62481, 43.30726], [-1.69407, 43.31378], [-1.73074, 43.29481], [-1.7397, 43.32979], [-1.75079, 43.3317], [-1.75334, 43.34107], [-1.77068, 43.34396], [-1.78714, 43.35476], [-1.78332, 43.36399], [-1.79319, 43.37497], [-1.77289, 43.38957], [-1.81005, 43.59738], [-10.14298, 44.17365], [-11.19304, 41.83075], [-8.87157, 41.86488], [-8.81794, 41.90375], [-8.75712, 41.92833], [-8.74606, 41.9469], [-8.7478, 41.96282], [-8.69071, 41.98862], [-8.6681, 41.99703], [-8.65832, 42.02972], [-8.64626, 42.03668], [-8.63791, 42.04691], [-8.59493, 42.05708], [-8.58086, 42.05147], [-8.54563, 42.0537], [-8.5252, 42.06264], [-8.52837, 42.07658], [-8.48185, 42.0811], [-8.44123, 42.08218], [-8.42512, 42.07199], [-8.40143, 42.08052], [-8.38323, 42.07683], [-8.36353, 42.09065], [-8.33912, 42.08358], [-8.32161, 42.10218], [-8.29809, 42.106], [-8.2732, 42.12396], [-8.24681, 42.13993], [-8.22406, 42.1328], [-8.1986, 42.15402], [-8.18947, 42.13853], [-8.19406, 42.12141], [-8.18178, 42.06436], [-8.11729, 42.08537], [-8.08847, 42.05767], [-8.08796, 42.01398], [-8.16232, 41.9828], [-8.2185, 41.91237], [-8.19551, 41.87459], [-8.16944, 41.87944], [-8.16455, 41.81753], [-8.0961, 41.81024], [-8.01136, 41.83453], [-7.9804, 41.87337], [-7.92336, 41.8758], [-7.90707, 41.92432], [-7.88751, 41.92553], [-7.88055, 41.84571], [-7.84188, 41.88065], [-7.69848, 41.90977], [-7.65774, 41.88308], [-7.58603, 41.87944], [-7.62188, 41.83089], [-7.52737, 41.83939], [-7.49803, 41.87095], [-7.45566, 41.86488], [-7.44759, 41.84451], [-7.42854, 41.83262], [-7.42864, 41.80589], [-7.37092, 41.85031], [-7.32366, 41.8406], [-7.18677, 41.88793], [-7.18549, 41.97515], [-7.14115, 41.98855], [-7.08574, 41.97401], [-7.07596, 41.94977], [-7.01078, 41.94977], [-6.98144, 41.9728], [-6.95537, 41.96553], [-6.94396, 41.94403], [-6.82174, 41.94493], [-6.81196, 41.99097], [-6.76959, 41.98734], [-6.75004, 41.94129], [-6.61967, 41.94008], [-6.58544, 41.96674], [-6.5447, 41.94371], [-6.56752, 41.88429], [-6.51374, 41.8758], [-6.56426, 41.74219], [-6.54633, 41.68623], [-6.49907, 41.65823], [-6.44204, 41.68258], [-6.29863, 41.66432], [-6.19128, 41.57638], [-6.26777, 41.48796], [-6.3306, 41.37677], [-6.38553, 41.38655], [-6.38551, 41.35274], [-6.55937, 41.24417], [-6.65046, 41.24725], [-6.68286, 41.21641], [-6.69711, 41.1858], [-6.77319, 41.13049], [-6.75655, 41.10187], [-6.79241, 41.05397], [-6.80942, 41.03629], [-6.84781, 41.02692], [-6.88843, 41.03027], [-6.913, 41.03922], [-6.9357, 41.02888], [-6.8527, 40.93958], [-6.84292, 40.89771], [-6.80707, 40.88047], [-6.79892, 40.84842], [-6.82337, 40.84472], [-6.82826, 40.74603], [-6.79567, 40.65955], [-6.84292, 40.56801], [-6.80218, 40.55067], [-6.7973, 40.51723], [-6.84944, 40.46394], [-6.84618, 40.42177], [-6.78426, 40.36468], [-6.80218, 40.33239], [-6.86085, 40.2976], [-6.86085, 40.26776], [-7.00426, 40.23169], [-7.02544, 40.18564], [-7.00589, 40.12087], [-6.94233, 40.10716], [-6.86737, 40.01986], [-6.91463, 39.86618], [-6.97492, 39.81488], [-7.01613, 39.66877], [-7.24707, 39.66576], [-7.33507, 39.64569], [-7.54121, 39.66717], [-7.49477, 39.58794], [-7.2927, 39.45847], [-7.3149, 39.34857], [-7.23403, 39.27579], [-7.23566, 39.20132], [-7.12811, 39.17101], [-7.14929, 39.11287], [-7.10692, 39.10275], [-7.04011, 39.11919], [-6.97004, 39.07619], [-6.95211, 39.0243], [-7.051, 38.907], [-7.03848, 38.87221], [-7.26174, 38.72107], [-7.265, 38.61674], [-7.32529, 38.44336], [-7.15581, 38.27597], [-7.09389, 38.17227], [-6.93418, 38.21454], [-7.00375, 38.01914], [-7.05966, 38.01966], [-7.10366, 38.04404], [-7.12648, 38.00296], [-7.24544, 37.98884], [-7.27314, 37.90145], [-7.33441, 37.81193], [-7.41981, 37.75729], [-7.51759, 37.56119], [-7.46878, 37.47127], [-7.43974, 37.38913], [-7.43227, 37.25152], [-7.41854, 37.23813], [-7.41133, 37.20314], [-7.39769, 37.16868], [-7.37282, 36.96896], [-7.2725, 35.73269], [-5.10878, 36.05227], [-2.27707, 35.35051], [3.75438, 42.33445]], [[-5.27801, 36.14942], [-5.34064, 36.03744], [-5.40526, 36.15488], [-5.34536, 36.15501], [-5.33822, 36.15272], [-5.27801, 36.14942]]], [[[1.99838, 42.44682], [2.01564, 42.45171], [1.99216, 42.46208], [1.98579, 42.47486], [1.99766, 42.4858], [1.98916, 42.49351], [1.98022, 42.49569], [1.97697, 42.48568], [1.97227, 42.48487], [1.97003, 42.48081], [1.96215, 42.47854], [1.95606, 42.45785], [1.96125, 42.45364], [1.98378, 42.44697], [1.99838, 42.44682]]]]
22632           }
22633         }, {
22634           type: "Feature",
22635           properties: {
22636             wikidata: "Q98059339",
22637             nameEn: "Mainland Norway",
22638             country: "NO",
22639             groups: ["154", "150", "UN"],
22640             callingCodes: ["47"]
22641           },
22642           geometry: {
22643             type: "MultiPolygon",
22644             coordinates: [[[[10.40861, 58.38489], [10.64958, 58.89391], [11.08911, 58.98745], [11.15367, 59.07862], [11.34459, 59.11672], [11.4601, 58.99022], [11.45199, 58.89604], [11.65732, 58.90177], [11.8213, 59.24985], [11.69297, 59.59442], [11.92112, 59.69531], [11.87121, 59.86039], [12.15641, 59.8926], [12.36317, 59.99259], [12.52003, 60.13846], [12.59133, 60.50559], [12.2277, 61.02442], [12.69115, 61.06584], [12.86939, 61.35427], [12.57707, 61.56547], [12.40595, 61.57226], [12.14746, 61.7147], [12.29187, 62.25699], [12.07085, 62.6297], [12.19919, 63.00104], [11.98529, 63.27487], [12.19919, 63.47935], [12.14928, 63.59373], [12.74105, 64.02171], [13.23411, 64.09087], [13.98222, 64.00953], [14.16051, 64.18725], [14.11117, 64.46674], [13.64276, 64.58402], [14.50926, 65.31786], [14.53778, 66.12399], [15.05113, 66.15572], [15.49318, 66.28509], [15.37197, 66.48217], [16.35589, 67.06419], [16.39154, 67.21653], [16.09922, 67.4364], [16.12774, 67.52106], [16.38441, 67.52923], [16.7409, 67.91037], [17.30416, 68.11591], [17.90787, 67.96537], [18.13836, 68.20874], [18.1241, 68.53721], [18.39503, 68.58672], [18.63032, 68.50849], [18.97255, 68.52416], [19.93508, 68.35911], [20.22027, 68.48759], [19.95647, 68.55546], [20.22027, 68.67246], [20.33435, 68.80174], [20.28444, 68.93283], [20.0695, 69.04469], [20.55258, 69.06069], [20.72171, 69.11874], [21.05775, 69.0356], [21.11099, 69.10291], [20.98641, 69.18809], [21.00732, 69.22755], [21.27827, 69.31281], [21.63833, 69.27485], [22.27276, 68.89514], [22.38367, 68.71561], [22.53321, 68.74393], [23.13064, 68.64684], [23.68017, 68.70276], [23.781, 68.84514], [24.02299, 68.81601], [24.18432, 68.73936], [24.74898, 68.65143], [24.90023, 68.55579], [24.93048, 68.61102], [25.10189, 68.63307], [25.12206, 68.78684], [25.42455, 68.90328], [25.61613, 68.89602], [25.75729, 68.99383], [25.69679, 69.27039], [25.96904, 69.68397], [26.40261, 69.91377], [26.64461, 69.96565], [27.05802, 69.92069], [27.57226, 70.06215], [27.95542, 70.0965], [27.97558, 69.99671], [28.32849, 69.88605], [28.36883, 69.81658], [29.12697, 69.69193], [29.31664, 69.47994], [28.8629, 69.22395], [28.81248, 69.11997], [28.91738, 69.04774], [29.0444, 69.0119], [29.26623, 69.13794], [29.27631, 69.2811], [29.97205, 69.41623], [30.16363, 69.65244], [30.52662, 69.54699], [30.95011, 69.54699], [30.84095, 69.80584], [31.59909, 70.16571], [32.07813, 72.01005], [-11.60274, 67.73467], [7.28637, 57.35913], [10.40861, 58.38489]]]]
22645           }
22646         }, {
22647           type: "Feature",
22648           properties: {
22649             wikidata: "Q98543636",
22650             nameEn: "Mainland Ecuador",
22651             country: "EC",
22652             groups: ["005", "419", "019", "UN"],
22653             callingCodes: ["593"]
22654           },
22655           geometry: {
22656             type: "MultiPolygon",
22657             coordinates: [[[[-84.52388, -3.36941], [-80.30602, -3.39149], [-80.20647, -3.431], [-80.24123, -3.46124], [-80.24586, -3.48677], [-80.23651, -3.48652], [-80.22629, -3.501], [-80.20535, -3.51667], [-80.21642, -3.5888], [-80.19848, -3.59249], [-80.18741, -3.63994], [-80.19926, -3.68894], [-80.13232, -3.90317], [-80.46386, -4.01342], [-80.4822, -4.05477], [-80.45023, -4.20938], [-80.32114, -4.21323], [-80.46386, -4.41516], [-80.39256, -4.48269], [-80.13945, -4.29786], [-79.79722, -4.47558], [-79.59402, -4.46848], [-79.26248, -4.95167], [-79.1162, -4.97774], [-79.01659, -5.01481], [-78.85149, -4.66795], [-78.68394, -4.60754], [-78.34362, -3.38633], [-78.24589, -3.39907], [-78.22642, -3.51113], [-78.14324, -3.47653], [-78.19369, -3.36431], [-77.94147, -3.05454], [-76.6324, -2.58397], [-76.05203, -2.12179], [-75.57429, -1.55961], [-75.3872, -0.9374], [-75.22862, -0.95588], [-75.22862, -0.60048], [-75.53615, -0.19213], [-75.60169, -0.18708], [-75.61997, -0.10012], [-75.40192, -0.17196], [-75.25764, -0.11943], [-75.82927, 0.09578], [-76.23441, 0.42294], [-76.41215, 0.38228], [-76.4094, 0.24015], [-76.89177, 0.24736], [-77.52001, 0.40782], [-77.49984, 0.64476], [-77.67815, 0.73863], [-77.66416, 0.81604], [-77.68613, 0.83029], [-77.7148, 0.85003], [-77.85677, 0.80197], [-78.42749, 1.15389], [-78.87137, 1.47457], [-82.12561, 4.00341], [-84.52388, -3.36941]]]]
22658           }
22659         }, {
22660           type: "Feature",
22661           properties: {
22662             m49: "001",
22663             wikidata: "Q2",
22664             nameEn: "World",
22665             aliases: ["Earth", "Planet"],
22666             level: "world"
22667           },
22668           geometry: null
22669         }, {
22670           type: "Feature",
22671           properties: {
22672             m49: "002",
22673             wikidata: "Q15",
22674             nameEn: "Africa",
22675             level: "region"
22676           },
22677           geometry: null
22678         }, {
22679           type: "Feature",
22680           properties: {
22681             m49: "003",
22682             wikidata: "Q49",
22683             nameEn: "North America",
22684             level: "subregion"
22685           },
22686           geometry: null
22687         }, {
22688           type: "Feature",
22689           properties: {
22690             m49: "005",
22691             wikidata: "Q18",
22692             nameEn: "South America",
22693             level: "intermediateRegion"
22694           },
22695           geometry: null
22696         }, {
22697           type: "Feature",
22698           properties: {
22699             m49: "009",
22700             wikidata: "Q538",
22701             nameEn: "Oceania",
22702             level: "region"
22703           },
22704           geometry: null
22705         }, {
22706           type: "Feature",
22707           properties: {
22708             m49: "011",
22709             wikidata: "Q4412",
22710             nameEn: "Western Africa",
22711             level: "intermediateRegion"
22712           },
22713           geometry: null
22714         }, {
22715           type: "Feature",
22716           properties: {
22717             m49: "013",
22718             wikidata: "Q27611",
22719             nameEn: "Central America",
22720             level: "intermediateRegion"
22721           },
22722           geometry: null
22723         }, {
22724           type: "Feature",
22725           properties: {
22726             m49: "014",
22727             wikidata: "Q27407",
22728             nameEn: "Eastern Africa",
22729             level: "intermediateRegion"
22730           },
22731           geometry: null
22732         }, {
22733           type: "Feature",
22734           properties: {
22735             m49: "015",
22736             wikidata: "Q27381",
22737             nameEn: "Northern Africa",
22738             level: "subregion"
22739           },
22740           geometry: null
22741         }, {
22742           type: "Feature",
22743           properties: {
22744             m49: "017",
22745             wikidata: "Q27433",
22746             nameEn: "Middle Africa",
22747             level: "intermediateRegion"
22748           },
22749           geometry: null
22750         }, {
22751           type: "Feature",
22752           properties: {
22753             m49: "018",
22754             wikidata: "Q27394",
22755             nameEn: "Southern Africa",
22756             level: "intermediateRegion"
22757           },
22758           geometry: null
22759         }, {
22760           type: "Feature",
22761           properties: {
22762             m49: "019",
22763             wikidata: "Q828",
22764             nameEn: "Americas",
22765             level: "region"
22766           },
22767           geometry: null
22768         }, {
22769           type: "Feature",
22770           properties: {
22771             m49: "021",
22772             wikidata: "Q2017699",
22773             nameEn: "Northern America",
22774             level: "subregion"
22775           },
22776           geometry: null
22777         }, {
22778           type: "Feature",
22779           properties: {
22780             m49: "029",
22781             wikidata: "Q664609",
22782             nameEn: "Caribbean",
22783             level: "intermediateRegion"
22784           },
22785           geometry: null
22786         }, {
22787           type: "Feature",
22788           properties: {
22789             m49: "030",
22790             wikidata: "Q27231",
22791             nameEn: "Eastern Asia",
22792             level: "subregion"
22793           },
22794           geometry: null
22795         }, {
22796           type: "Feature",
22797           properties: {
22798             m49: "034",
22799             wikidata: "Q771405",
22800             nameEn: "Southern Asia",
22801             level: "subregion"
22802           },
22803           geometry: null
22804         }, {
22805           type: "Feature",
22806           properties: {
22807             m49: "035",
22808             wikidata: "Q11708",
22809             nameEn: "South-eastern Asia",
22810             level: "subregion"
22811           },
22812           geometry: null
22813         }, {
22814           type: "Feature",
22815           properties: {
22816             m49: "039",
22817             wikidata: "Q27449",
22818             nameEn: "Southern Europe",
22819             level: "subregion"
22820           },
22821           geometry: null
22822         }, {
22823           type: "Feature",
22824           properties: {
22825             m49: "053",
22826             wikidata: "Q45256",
22827             nameEn: "Australia and New Zealand",
22828             aliases: ["Australasia"],
22829             level: "subregion"
22830           },
22831           geometry: null
22832         }, {
22833           type: "Feature",
22834           properties: {
22835             m49: "054",
22836             wikidata: "Q37394",
22837             nameEn: "Melanesia",
22838             level: "subregion"
22839           },
22840           geometry: null
22841         }, {
22842           type: "Feature",
22843           properties: {
22844             m49: "057",
22845             wikidata: "Q3359409",
22846             nameEn: "Micronesia",
22847             level: "subregion"
22848           },
22849           geometry: null
22850         }, {
22851           type: "Feature",
22852           properties: {
22853             m49: "061",
22854             wikidata: "Q35942",
22855             nameEn: "Polynesia",
22856             level: "subregion"
22857           },
22858           geometry: null
22859         }, {
22860           type: "Feature",
22861           properties: {
22862             m49: "142",
22863             wikidata: "Q48",
22864             nameEn: "Asia",
22865             level: "region"
22866           },
22867           geometry: null
22868         }, {
22869           type: "Feature",
22870           properties: {
22871             m49: "143",
22872             wikidata: "Q27275",
22873             nameEn: "Central Asia",
22874             level: "subregion"
22875           },
22876           geometry: null
22877         }, {
22878           type: "Feature",
22879           properties: {
22880             m49: "145",
22881             wikidata: "Q27293",
22882             nameEn: "Western Asia",
22883             level: "subregion"
22884           },
22885           geometry: null
22886         }, {
22887           type: "Feature",
22888           properties: {
22889             m49: "150",
22890             wikidata: "Q46",
22891             nameEn: "Europe",
22892             level: "region"
22893           },
22894           geometry: null
22895         }, {
22896           type: "Feature",
22897           properties: {
22898             m49: "151",
22899             wikidata: "Q27468",
22900             nameEn: "Eastern Europe",
22901             level: "subregion"
22902           },
22903           geometry: null
22904         }, {
22905           type: "Feature",
22906           properties: {
22907             m49: "154",
22908             wikidata: "Q27479",
22909             nameEn: "Northern Europe",
22910             level: "subregion"
22911           },
22912           geometry: null
22913         }, {
22914           type: "Feature",
22915           properties: {
22916             m49: "155",
22917             wikidata: "Q27496",
22918             nameEn: "Western Europe",
22919             level: "subregion"
22920           },
22921           geometry: null
22922         }, {
22923           type: "Feature",
22924           properties: {
22925             m49: "202",
22926             wikidata: "Q132959",
22927             nameEn: "Sub-Saharan Africa",
22928             level: "subregion"
22929           },
22930           geometry: null
22931         }, {
22932           type: "Feature",
22933           properties: {
22934             m49: "419",
22935             wikidata: "Q72829598",
22936             nameEn: "Latin America and the Caribbean",
22937             level: "subregion"
22938           },
22939           geometry: null
22940         }, {
22941           type: "Feature",
22942           properties: {
22943             m49: "680",
22944             wikidata: "Q3405693",
22945             nameEn: "Sark",
22946             country: "GB",
22947             groups: ["GG", "830", "Q185086", "154", "150", "UN"],
22948             level: "subterritory",
22949             driveSide: "left",
22950             roadSpeedUnit: "mph",
22951             roadHeightUnit: "ft",
22952             callingCodes: ["44 01481"]
22953           },
22954           geometry: {
22955             type: "MultiPolygon",
22956             coordinates: [[[[-2.36485, 49.48223], [-2.65349, 49.15373], [-2.09454, 49.46288], [-2.36485, 49.48223]]]]
22957           }
22958         }, {
22959           type: "Feature",
22960           properties: {
22961             m49: "830",
22962             wikidata: "Q42314",
22963             nameEn: "Channel Islands",
22964             level: "intermediateRegion"
22965           },
22966           geometry: null
22967         }, {
22968           type: "Feature",
22969           properties: {
22970             iso1A2: "AC",
22971             iso1A3: "ASC",
22972             wikidata: "Q46197",
22973             nameEn: "Ascension Island",
22974             aliases: ["SH-AC"],
22975             country: "GB",
22976             groups: ["SH", "BOTS", "011", "202", "002", "UN"],
22977             isoStatus: "excRes",
22978             driveSide: "left",
22979             roadSpeedUnit: "mph",
22980             roadHeightUnit: "ft",
22981             callingCodes: ["247"]
22982           },
22983           geometry: {
22984             type: "MultiPolygon",
22985             coordinates: [[[[-14.82771, -8.70814], [-13.33271, -8.07391], [-14.91926, -6.63386], [-14.82771, -8.70814]]]]
22986           }
22987         }, {
22988           type: "Feature",
22989           properties: {
22990             iso1A2: "AD",
22991             iso1A3: "AND",
22992             iso1N3: "020",
22993             wikidata: "Q228",
22994             nameEn: "Andorra",
22995             groups: ["Q12837", "039", "150", "UN"],
22996             callingCodes: ["376"]
22997           },
22998           geometry: {
22999             type: "MultiPolygon",
23000             coordinates: [[[[1.72515, 42.50338], [1.73683, 42.55492], [1.7858, 42.57698], [1.72588, 42.59098], [1.73452, 42.61515], [1.68267, 42.62533], [1.6625, 42.61982], [1.63485, 42.62957], [1.60085, 42.62703], [1.55418, 42.65669], [1.50867, 42.64483], [1.48043, 42.65203], [1.46718, 42.63296], [1.47986, 42.61346], [1.44197, 42.60217], [1.42512, 42.58292], [1.44529, 42.56722], [1.4234, 42.55959], [1.41245, 42.53539], [1.44759, 42.54431], [1.46661, 42.50949], [1.41648, 42.48315], [1.43838, 42.47848], [1.44529, 42.43724], [1.5127, 42.42959], [1.55073, 42.43299], [1.55937, 42.45808], [1.57953, 42.44957], [1.58933, 42.46275], [1.65674, 42.47125], [1.66826, 42.50779], [1.70571, 42.48867], [1.72515, 42.50338]]]]
23001           }
23002         }, {
23003           type: "Feature",
23004           properties: {
23005             iso1A2: "AE",
23006             iso1A3: "ARE",
23007             iso1N3: "784",
23008             wikidata: "Q878",
23009             nameEn: "United Arab Emirates",
23010             groups: ["145", "142", "UN"],
23011             callingCodes: ["971"]
23012           },
23013           geometry: {
23014             type: "MultiPolygon",
23015             coordinates: [[[[56.26534, 25.62825], [56.25341, 25.61443], [56.26636, 25.60643], [56.25365, 25.60211], [56.20473, 25.61119], [56.18363, 25.65508], [56.14826, 25.66351], [56.13579, 25.73524], [56.17416, 25.77239], [56.13963, 25.82765], [56.19334, 25.9795], [56.15498, 26.06828], [56.08666, 26.05038], [55.81777, 26.18798], [55.14145, 25.62624], [53.97892, 24.64436], [52.82259, 25.51697], [52.35509, 25.00368], [52.02277, 24.75635], [51.83108, 24.71675], [51.58834, 24.66608], [51.41644, 24.39615], [51.58871, 24.27256], [51.59617, 24.12041], [52.56622, 22.94341], [55.13599, 22.63334], [55.2137, 22.71065], [55.22634, 23.10378], [55.57358, 23.669], [55.48677, 23.94946], [55.73301, 24.05994], [55.8308, 24.01633], [56.01799, 24.07426], [55.95472, 24.2172], [55.83367, 24.20193], [55.77658, 24.23476], [55.76558, 24.23227], [55.75257, 24.23466], [55.75382, 24.2466], [55.75939, 24.26114], [55.76781, 24.26209], [55.79145, 24.27914], [55.80747, 24.31069], [55.83395, 24.32776], [55.83271, 24.41521], [55.76461, 24.5287], [55.83271, 24.68567], [55.83408, 24.77858], [55.81348, 24.80102], [55.81116, 24.9116], [55.85094, 24.96858], [55.90849, 24.96771], [55.96316, 25.00857], [56.05715, 24.95727], [56.05106, 24.87461], [55.97467, 24.89639], [55.97836, 24.87673], [56.03535, 24.81161], [56.06128, 24.74457], [56.13684, 24.73699], [56.20062, 24.78565], [56.20568, 24.85063], [56.30269, 24.88334], [56.34873, 24.93205], [56.3227, 24.97284], [56.86325, 25.03856], [56.82555, 25.7713], [56.26534, 25.62825]], [[56.26062, 25.33108], [56.3005, 25.31815], [56.3111, 25.30107], [56.35172, 25.30681], [56.34438, 25.26653], [56.27628, 25.23404], [56.24341, 25.22867], [56.20872, 25.24104], [56.20838, 25.25668], [56.24465, 25.27505], [56.25008, 25.28843], [56.23362, 25.31253], [56.26062, 25.33108]]], [[[56.28423, 25.26344], [56.29379, 25.2754], [56.28102, 25.28486], [56.2716, 25.27916], [56.27086, 25.26128], [56.28423, 25.26344]]]]
23016           }
23017         }, {
23018           type: "Feature",
23019           properties: {
23020             iso1A2: "AF",
23021             iso1A3: "AFG",
23022             iso1N3: "004",
23023             wikidata: "Q889",
23024             nameEn: "Afghanistan",
23025             groups: ["034", "142", "UN"],
23026             callingCodes: ["93"]
23027           },
23028           geometry: {
23029             type: "MultiPolygon",
23030             coordinates: [[[[70.61526, 38.34774], [70.60407, 38.28046], [70.54673, 38.24541], [70.4898, 38.12546], [70.17206, 37.93276], [70.1863, 37.84296], [70.27694, 37.81258], [70.28243, 37.66706], [70.15015, 37.52519], [69.95971, 37.5659], [69.93362, 37.61378], [69.84435, 37.60616], [69.80041, 37.5746], [69.51888, 37.5844], [69.44954, 37.4869], [69.36645, 37.40462], [69.45022, 37.23315], [69.39529, 37.16752], [69.25152, 37.09426], [69.03274, 37.25174], [68.96407, 37.32603], [68.88168, 37.33368], [68.91189, 37.26704], [68.80889, 37.32494], [68.81438, 37.23862], [68.6798, 37.27906], [68.61851, 37.19815], [68.41888, 37.13906], [68.41201, 37.10402], [68.29253, 37.10621], [68.27605, 37.00977], [68.18542, 37.02074], [68.02194, 36.91923], [67.87917, 37.0591], [67.7803, 37.08978], [67.78329, 37.1834], [67.51868, 37.26102], [67.2581, 37.17216], [67.2224, 37.24545], [67.13039, 37.27168], [67.08232, 37.35469], [66.95598, 37.40162], [66.64699, 37.32958], [66.55743, 37.35409], [66.30993, 37.32409], [65.72274, 37.55438], [65.64137, 37.45061], [65.64263, 37.34388], [65.51778, 37.23881], [64.97945, 37.21913], [64.61141, 36.6351], [64.62514, 36.44311], [64.57295, 36.34362], [64.43288, 36.24401], [64.05385, 36.10433], [63.98519, 36.03773], [63.56496, 35.95106], [63.53475, 35.90881], [63.29579, 35.85985], [63.12276, 35.86208], [63.10318, 35.81782], [63.23262, 35.67487], [63.10079, 35.63024], [63.12276, 35.53196], [63.0898, 35.43131], [62.90853, 35.37086], [62.74098, 35.25432], [62.62288, 35.22067], [62.48006, 35.28796], [62.29878, 35.13312], [62.29191, 35.25964], [62.15871, 35.33278], [62.05709, 35.43803], [61.97743, 35.4604], [61.77693, 35.41341], [61.58742, 35.43803], [61.27371, 35.61482], [61.18187, 35.30249], [61.0991, 35.27845], [61.12831, 35.09938], [61.06926, 34.82139], [61.00197, 34.70631], [60.99922, 34.63064], [60.72316, 34.52857], [60.91321, 34.30411], [60.66502, 34.31539], [60.50209, 34.13992], [60.5838, 33.80793], [60.5485, 33.73422], [60.57762, 33.59772], [60.69573, 33.56054], [60.91133, 33.55596], [60.88908, 33.50219], [60.56485, 33.12944], [60.86191, 32.22565], [60.84541, 31.49561], [61.70929, 31.37391], [61.80569, 31.16167], [61.80957, 31.12576], [61.83257, 31.0452], [61.8335, 30.97669], [61.78268, 30.92724], [61.80829, 30.84224], [60.87231, 29.86514], [62.47751, 29.40782], [63.5876, 29.50456], [64.12966, 29.39157], [64.19796, 29.50407], [64.62116, 29.58903], [65.04005, 29.53957], [66.24175, 29.85181], [66.36042, 29.9583], [66.23609, 30.06321], [66.34869, 30.404], [66.28413, 30.57001], [66.39194, 30.9408], [66.42645, 30.95309], [66.58175, 30.97532], [66.68166, 31.07597], [66.72561, 31.20526], [66.83273, 31.26867], [67.04147, 31.31561], [67.03323, 31.24519], [67.29964, 31.19586], [67.78854, 31.33203], [67.7748, 31.4188], [67.62374, 31.40473], [67.58323, 31.52772], [67.72056, 31.52304], [67.86887, 31.63536], [68.00071, 31.6564], [68.1655, 31.82691], [68.25614, 31.80357], [68.27605, 31.75863], [68.44222, 31.76446], [68.57475, 31.83158], [68.6956, 31.75687], [68.79997, 31.61665], [68.91078, 31.59687], [68.95995, 31.64822], [69.00939, 31.62249], [69.11514, 31.70782], [69.20577, 31.85957], [69.3225, 31.93186], [69.27032, 32.14141], [69.27932, 32.29119], [69.23599, 32.45946], [69.2868, 32.53938], [69.38155, 32.56601], [69.44747, 32.6678], [69.43649, 32.7302], [69.38018, 32.76601], [69.47082, 32.85834], [69.5436, 32.8768], [69.49854, 32.88843], [69.49004, 33.01509], [69.57656, 33.09911], [69.71526, 33.09911], [69.79766, 33.13247], [69.85259, 33.09451], [70.02563, 33.14282], [70.07369, 33.22557], [70.13686, 33.21064], [70.32775, 33.34496], [70.17062, 33.53535], [70.20141, 33.64387], [70.14785, 33.6553], [70.14236, 33.71701], [70.00503, 33.73528], [69.85671, 33.93719], [69.87307, 33.9689], [69.90203, 34.04194], [70.54336, 33.9463], [70.88119, 33.97933], [71.07345, 34.06242], [71.06933, 34.10564], [71.09307, 34.11961], [71.09453, 34.13524], [71.13078, 34.16503], [71.12815, 34.26619], [71.17662, 34.36769], [71.02401, 34.44835], [71.0089, 34.54568], [71.11602, 34.63047], [71.08718, 34.69034], [71.28356, 34.80882], [71.29472, 34.87728], [71.50329, 34.97328], [71.49917, 35.00478], [71.55273, 35.02615], [71.52938, 35.09023], [71.67495, 35.21262], [71.5541, 35.28776], [71.54294, 35.31037], [71.65435, 35.4479], [71.49917, 35.6267], [71.55273, 35.71483], [71.37969, 35.95865], [71.19505, 36.04134], [71.60491, 36.39429], [71.80267, 36.49924], [72.18135, 36.71838], [72.6323, 36.84601], [73.82685, 36.91421], [74.04856, 36.82648], [74.43389, 37.00977], [74.53739, 36.96224], [74.56453, 37.03023], [74.49981, 37.24518], [74.80605, 37.21565], [74.88887, 37.23275], [74.8294, 37.3435], [74.68383, 37.3948], [74.56161, 37.37734], [74.41055, 37.3948], [74.23339, 37.41116], [74.20308, 37.34208], [73.8564, 37.26158], [73.82552, 37.22659], [73.64974, 37.23643], [73.61129, 37.27469], [73.76647, 37.33913], [73.77197, 37.4417], [73.29633, 37.46495], [73.06884, 37.31729], [72.79693, 37.22222], [72.66381, 37.02014], [72.54095, 37.00007], [72.31676, 36.98115], [71.83229, 36.68084], [71.67083, 36.67346], [71.57195, 36.74943], [71.51502, 36.89128], [71.48481, 36.93218], [71.46923, 36.99925], [71.45578, 37.03094], [71.43097, 37.05855], [71.44127, 37.11856], [71.4494, 37.18137], [71.4555, 37.21418], [71.47386, 37.2269], [71.48339, 37.23937], [71.4824, 37.24921], [71.48536, 37.26017], [71.50674, 37.31502], [71.49821, 37.31975], [71.4862, 37.33405], [71.47685, 37.40281], [71.49612, 37.4279], [71.5256, 37.47971], [71.50616, 37.50733], [71.49693, 37.53527], [71.5065, 37.60912], [71.51972, 37.61945], [71.54186, 37.69691], [71.55234, 37.73209], [71.53053, 37.76534], [71.54324, 37.77104], [71.55752, 37.78677], [71.59255, 37.79956], [71.58843, 37.92425], [71.51565, 37.95349], [71.32871, 37.88564], [71.296, 37.93403], [71.2809, 37.91995], [71.24969, 37.93031], [71.27278, 37.96496], [71.27622, 37.99946], [71.28922, 38.01272], [71.29878, 38.04429], [71.36444, 38.15358], [71.37803, 38.25641], [71.33869, 38.27335], [71.33114, 38.30339], [71.21291, 38.32797], [71.1451, 38.40106], [71.10957, 38.40671], [71.10592, 38.42077], [71.09542, 38.42517], [71.0556, 38.40176], [71.03545, 38.44779], [70.98693, 38.48862], [70.92728, 38.43021], [70.88719, 38.46826], [70.84376, 38.44688], [70.82538, 38.45394], [70.81697, 38.44507], [70.80521, 38.44447], [70.79766, 38.44944], [70.78702, 38.45031], [70.78581, 38.45502], [70.77132, 38.45548], [70.75455, 38.4252], [70.72485, 38.4131], [70.69807, 38.41861], [70.67438, 38.40597], [70.6761, 38.39144], [70.69189, 38.37031], [70.64966, 38.34999], [70.61526, 38.34774]]]]
23031           }
23032         }, {
23033           type: "Feature",
23034           properties: {
23035             iso1A2: "AG",
23036             iso1A3: "ATG",
23037             iso1N3: "028",
23038             wikidata: "Q781",
23039             nameEn: "Antigua and Barbuda",
23040             groups: ["029", "003", "419", "019", "UN"],
23041             driveSide: "left",
23042             roadSpeedUnit: "mph",
23043             callingCodes: ["1 268"]
23044           },
23045           geometry: {
23046             type: "MultiPolygon",
23047             coordinates: [[[[-61.66959, 18.6782], [-62.58307, 16.68909], [-62.1023, 16.97277], [-61.23098, 16.62484], [-61.66959, 18.6782]]]]
23048           }
23049         }, {
23050           type: "Feature",
23051           properties: {
23052             iso1A2: "AI",
23053             iso1A3: "AIA",
23054             iso1N3: "660",
23055             wikidata: "Q25228",
23056             nameEn: "Anguilla",
23057             country: "GB",
23058             groups: ["BOTS", "029", "003", "419", "019", "UN"],
23059             driveSide: "left",
23060             roadSpeedUnit: "mph",
23061             callingCodes: ["1 264"]
23062           },
23063           geometry: {
23064             type: "MultiPolygon",
23065             coordinates: [[[[-63.79029, 19.11219], [-63.35989, 18.06012], [-62.62718, 18.26185], [-63.79029, 19.11219]]]]
23066           }
23067         }, {
23068           type: "Feature",
23069           properties: {
23070             iso1A2: "AL",
23071             iso1A3: "ALB",
23072             iso1N3: "008",
23073             wikidata: "Q222",
23074             nameEn: "Albania",
23075             groups: ["039", "150", "UN"],
23076             callingCodes: ["355"]
23077           },
23078           geometry: {
23079             type: "MultiPolygon",
23080             coordinates: [[[[20.07761, 42.55582], [20.01834, 42.54622], [20.00842, 42.5109], [19.9324, 42.51699], [19.82333, 42.46581], [19.76549, 42.50237], [19.74731, 42.57422], [19.77375, 42.58517], [19.73244, 42.66299], [19.65972, 42.62774], [19.4836, 42.40831], [19.42352, 42.36546], [19.42, 42.33019], [19.28623, 42.17745], [19.40687, 42.10024], [19.37548, 42.06835], [19.36867, 42.02564], [19.37691, 41.96977], [19.34601, 41.95675], [19.33812, 41.90669], [19.37451, 41.8842], [19.37597, 41.84849], [19.26406, 41.74971], [19.0384, 40.35325], [19.95905, 39.82857], [19.97622, 39.78684], [19.92466, 39.69533], [19.98042, 39.6504], [20.00957, 39.69227], [20.05189, 39.69112], [20.12956, 39.65805], [20.15988, 39.652], [20.22376, 39.64532], [20.22707, 39.67459], [20.27412, 39.69884], [20.31961, 39.72799], [20.29152, 39.80421], [20.30804, 39.81563], [20.38572, 39.78516], [20.41475, 39.81437], [20.41546, 39.82832], [20.31135, 39.99438], [20.37911, 39.99058], [20.42373, 40.06777], [20.48487, 40.06271], [20.51297, 40.08168], [20.55593, 40.06524], [20.61081, 40.07866], [20.62566, 40.0897], [20.67162, 40.09433], [20.71789, 40.27739], [20.78234, 40.35803], [20.7906, 40.42726], [20.83688, 40.47882], [20.94925, 40.46625], [20.96908, 40.51526], [21.03932, 40.56299], [21.05833, 40.66586], [20.98134, 40.76046], [20.95752, 40.76982], [20.98396, 40.79109], [20.97887, 40.85475], [20.97693, 40.90103], [20.94305, 40.92399], [20.83671, 40.92752], [20.81567, 40.89662], [20.73504, 40.9081], [20.71634, 40.91781], [20.65558, 41.08009], [20.63454, 41.0889], [20.59832, 41.09066], [20.58546, 41.11179], [20.59715, 41.13644], [20.51068, 41.2323], [20.49432, 41.33679], [20.52119, 41.34381], [20.55976, 41.4087], [20.51301, 41.442], [20.49039, 41.49277], [20.45331, 41.51436], [20.45809, 41.5549], [20.52103, 41.56473], [20.55508, 41.58113], [20.51769, 41.65975], [20.52937, 41.69292], [20.51301, 41.72433], [20.53405, 41.78099], [20.57144, 41.7897], [20.55976, 41.87068], [20.59524, 41.8818], [20.57946, 41.91593], [20.63069, 41.94913], [20.59434, 42.03879], [20.55633, 42.08173], [20.56955, 42.12097], [20.48857, 42.25444], [20.3819, 42.3029], [20.34479, 42.32656], [20.24399, 42.32168], [20.21797, 42.41237], [20.17127, 42.50469], [20.07761, 42.55582]]]]
23081           }
23082         }, {
23083           type: "Feature",
23084           properties: {
23085             iso1A2: "AM",
23086             iso1A3: "ARM",
23087             iso1N3: "051",
23088             wikidata: "Q399",
23089             nameEn: "Armenia",
23090             groups: ["145", "142", "UN"],
23091             callingCodes: ["374"]
23092           },
23093           geometry: {
23094             type: "MultiPolygon",
23095             coordinates: [[[[45.0133, 41.29747], [44.93493, 41.25685], [44.81437, 41.30371], [44.80053, 41.25949], [44.81749, 41.23488], [44.84358, 41.23088], [44.89911, 41.21366], [44.87887, 41.20195], [44.82084, 41.21513], [44.72814, 41.20338], [44.61462, 41.24018], [44.59322, 41.1933], [44.46791, 41.18204], [44.34417, 41.2382], [44.34337, 41.20312], [44.32139, 41.2079], [44.18148, 41.24644], [44.16591, 41.19141], [43.84835, 41.16329], [43.74717, 41.1117], [43.67712, 41.13398], [43.4717, 41.12611], [43.44984, 41.0988], [43.47319, 41.02251], [43.58683, 40.98961], [43.67712, 40.93084], [43.67712, 40.84846], [43.74872, 40.7365], [43.7425, 40.66805], [43.63664, 40.54159], [43.54791, 40.47413], [43.60862, 40.43267], [43.59928, 40.34019], [43.71136, 40.16673], [43.65221, 40.14889], [43.65688, 40.11199], [43.92307, 40.01787], [44.1057, 40.03555], [44.1778, 40.02845], [44.26973, 40.04866], [44.46635, 39.97733], [44.61845, 39.8281], [44.75779, 39.7148], [44.88354, 39.74432], [44.92869, 39.72157], [45.06604, 39.79277], [45.18554, 39.67846], [45.17464, 39.58614], [45.21784, 39.58074], [45.23535, 39.61373], [45.30385, 39.61373], [45.29606, 39.57654], [45.46992, 39.49888], [45.70547, 39.60174], [45.80804, 39.56716], [45.83, 39.46487], [45.79225, 39.3695], [45.99774, 39.28931], [46.02303, 39.09978], [46.06973, 39.0744], [46.14785, 38.84206], [46.20601, 38.85262], [46.34059, 38.92076], [46.53497, 38.86548], [46.51805, 38.94982], [46.54296, 39.07078], [46.44022, 39.19636], [46.52584, 39.18912], [46.54141, 39.15895], [46.58032, 39.21204], [46.63481, 39.23013], [46.56476, 39.24942], [46.50093, 39.33736], [46.43244, 39.35181], [46.37795, 39.42039], [46.4013, 39.45405], [46.53051, 39.47809], [46.51027, 39.52373], [46.57721, 39.54414], [46.57098, 39.56694], [46.52117, 39.58734], [46.42465, 39.57534], [46.40286, 39.63651], [46.18493, 39.60533], [45.96543, 39.78859], [45.82533, 39.82925], [45.7833, 39.9475], [45.60895, 39.97733], [45.59806, 40.0131], [45.78642, 40.03218], [45.83779, 39.98925], [45.97944, 40.181], [45.95609, 40.27846], [45.65098, 40.37696], [45.42994, 40.53804], [45.45484, 40.57707], [45.35366, 40.65979], [45.4206, 40.7424], [45.55914, 40.78366], [45.60584, 40.87436], [45.40814, 40.97904], [45.44083, 41.01663], [45.39725, 41.02603], [45.35677, 40.99784], [45.28859, 41.03757], [45.26162, 41.0228], [45.25897, 41.0027], [45.1994, 41.04518], [45.16493, 41.05068], [45.1634, 41.08082], [45.1313, 41.09369], [45.12923, 41.06059], [45.06784, 41.05379], [45.08028, 41.10917], [45.19942, 41.13299], [45.1969, 41.168], [45.11811, 41.19967], [45.05201, 41.19211], [45.02932, 41.2101], [45.05497, 41.2464], [45.0133, 41.29747]], [[45.21324, 40.9817], [45.21219, 40.99001], [45.20518, 40.99348], [45.19312, 40.98998], [45.18382, 41.0066], [45.20625, 41.01484], [45.23487, 41.00226], [45.23095, 40.97828], [45.21324, 40.9817]], [[45.00864, 41.03411], [44.9903, 41.05657], [44.96031, 41.06345], [44.95383, 41.07553], [44.97169, 41.09176], [45.00864, 41.09407], [45.03406, 41.07931], [45.04517, 41.06653], [45.03792, 41.03938], [45.00864, 41.03411]]], [[[45.50279, 40.58424], [45.56071, 40.64765], [45.51825, 40.67382], [45.47927, 40.65023], [45.50279, 40.58424]]]]
23096           }
23097         }, {
23098           type: "Feature",
23099           properties: {
23100             iso1A2: "AO",
23101             iso1A3: "AGO",
23102             iso1N3: "024",
23103             wikidata: "Q916",
23104             nameEn: "Angola",
23105             groups: ["017", "202", "002", "UN"],
23106             callingCodes: ["244"]
23107           },
23108           geometry: {
23109             type: "MultiPolygon",
23110             coordinates: [[[[16.55507, -5.85631], [13.04371, -5.87078], [12.42245, -6.07585], [11.95767, -5.94705], [12.20376, -5.76338], [12.26557, -5.74031], [12.52318, -5.74353], [12.52301, -5.17481], [12.53599, -5.1618], [12.53586, -5.14658], [12.51589, -5.1332], [12.49815, -5.14058], [12.46297, -5.09408], [12.60251, -5.01715], [12.63465, -4.94632], [12.70868, -4.95505], [12.8733, -4.74346], [13.11195, -4.67745], [13.09648, -4.63739], [12.91489, -4.47907], [12.87096, -4.40315], [12.76844, -4.38709], [12.64835, -4.55937], [12.40964, -4.60609], [12.32324, -4.78415], [12.25587, -4.79437], [12.20901, -4.75642], [12.16068, -4.90089], [12.00924, -5.02627], [11.50888, -5.33417], [10.5065, -17.25284], [11.75063, -17.25013], [12.07076, -17.15165], [12.52111, -17.24495], [12.97145, -16.98567], [13.36212, -16.98048], [13.95896, -17.43141], [14.28743, -17.38814], [18.39229, -17.38927], [18.84226, -17.80375], [21.14283, -17.94318], [21.42741, -18.02787], [23.47474, -17.62877], [23.20038, -17.47563], [22.17217, -16.50269], [22.00323, -16.18028], [21.97988, -13.00148], [24.03339, -12.99091], [23.90937, -12.844], [24.06672, -12.29058], [23.98804, -12.13149], [24.02603, -11.15368], [24.00027, -10.89356], [23.86868, -11.02856], [23.45631, -10.946], [23.16602, -11.10577], [22.54205, -11.05784], [22.25951, -11.24911], [22.17954, -10.85884], [22.32604, -10.76291], [22.19039, -9.94628], [21.84856, -9.59871], [21.79824, -7.29628], [20.56263, -7.28566], [20.61689, -6.90876], [20.31846, -6.91953], [20.30218, -6.98955], [19.5469, -7.00195], [19.33698, -7.99743], [18.33635, -8.00126], [17.5828, -8.13784], [16.96282, -7.21787], [16.55507, -5.85631]]]]
23111           }
23112         }, {
23113           type: "Feature",
23114           properties: {
23115             iso1A2: "AQ",
23116             iso1A3: "ATA",
23117             iso1N3: "010",
23118             wikidata: "Q51",
23119             nameEn: "Antarctica",
23120             level: "region",
23121             callingCodes: ["672"]
23122           },
23123           geometry: {
23124             type: "MultiPolygon",
23125             coordinates: [[[[180, -60], [-180, -60], [-180, -90], [180, -90], [180, -60]]]]
23126           }
23127         }, {
23128           type: "Feature",
23129           properties: {
23130             iso1A2: "AR",
23131             iso1A3: "ARG",
23132             iso1N3: "032",
23133             wikidata: "Q414",
23134             nameEn: "Argentina",
23135             aliases: ["RA"],
23136             groups: ["005", "419", "019", "UN"],
23137             callingCodes: ["54"]
23138           },
23139           geometry: {
23140             type: "MultiPolygon",
23141             coordinates: [[[[-72.31343, -50.58411], [-72.33873, -51.59954], [-71.99889, -51.98018], [-69.97824, -52.00845], [-68.41683, -52.33516], [-68.60702, -52.65781], [-68.60733, -54.9125], [-68.01394, -54.8753], [-67.46182, -54.92205], [-67.11046, -54.94199], [-66.07313, -55.19618], [-63.67376, -55.11859], [-54.78916, -36.21945], [-57.83001, -34.69099], [-58.34425, -34.15035], [-58.44442, -33.84033], [-58.40475, -33.11777], [-58.1224, -32.98842], [-58.22362, -32.52416], [-58.10036, -32.25338], [-58.20252, -31.86966], [-58.00076, -31.65016], [-58.0023, -31.53084], [-58.07569, -31.44916], [-57.98127, -31.3872], [-57.9908, -31.34924], [-57.86729, -31.06352], [-57.89476, -30.95994], [-57.8024, -30.77193], [-57.89115, -30.49572], [-57.64859, -30.35095], [-57.61478, -30.25165], [-57.65132, -30.19229], [-57.09386, -29.74211], [-56.81251, -29.48154], [-56.62789, -29.18073], [-56.57295, -29.11357], [-56.54171, -29.11447], [-56.05265, -28.62651], [-56.00458, -28.60421], [-56.01729, -28.51223], [-55.65418, -28.18304], [-55.6262, -28.17124], [-55.33303, -27.94661], [-55.16872, -27.86224], [-55.1349, -27.89759], [-54.90805, -27.73149], [-54.90159, -27.63132], [-54.67657, -27.57214], [-54.50416, -27.48232], [-54.41888, -27.40882], [-54.19268, -27.30751], [-54.19062, -27.27639], [-54.15978, -27.2889], [-53.80144, -27.09844], [-53.73372, -26.6131], [-53.68269, -26.33359], [-53.64505, -26.28089], [-53.64186, -26.25976], [-53.64632, -26.24798], [-53.63881, -26.25075], [-53.63739, -26.2496], [-53.65237, -26.23289], [-53.65018, -26.19501], [-53.73968, -26.10012], [-53.73391, -26.07006], [-53.7264, -26.0664], [-53.73086, -26.05842], [-53.73511, -26.04211], [-53.83691, -25.94849], [-53.90831, -25.55513], [-54.52926, -25.62846], [-54.5502, -25.58915], [-54.59398, -25.59224], [-54.62063, -25.91213], [-54.60664, -25.9691], [-54.67359, -25.98607], [-54.69333, -26.37705], [-54.70732, -26.45099], [-54.80868, -26.55669], [-55.00584, -26.78754], [-55.06351, -26.80195], [-55.16948, -26.96068], [-55.25243, -26.93808], [-55.39611, -26.97679], [-55.62322, -27.1941], [-55.59094, -27.32444], [-55.74475, -27.44485], [-55.89195, -27.3467], [-56.18313, -27.29851], [-56.85337, -27.5165], [-58.04205, -27.2387], [-58.59549, -27.29973], [-58.65321, -27.14028], [-58.3198, -26.83443], [-58.1188, -26.16704], [-57.87176, -25.93604], [-57.57431, -25.47269], [-57.80821, -25.13863], [-58.25492, -24.92528], [-58.33055, -24.97099], [-59.33886, -24.49935], [-59.45482, -24.34787], [-60.03367, -24.00701], [-60.28163, -24.04436], [-60.99754, -23.80934], [-61.0782, -23.62932], [-61.9756, -23.0507], [-62.22768, -22.55807], [-62.51761, -22.37684], [-62.64455, -22.25091], [-62.8078, -22.12534], [-62.81124, -21.9987], [-63.66482, -21.99918], [-63.68113, -22.0544], [-63.70963, -21.99934], [-63.93287, -21.99934], [-64.22918, -22.55807], [-64.31489, -22.88824], [-64.35108, -22.73282], [-64.4176, -22.67692], [-64.58888, -22.25035], [-64.67174, -22.18957], [-64.90014, -22.12136], [-64.99524, -22.08255], [-65.47435, -22.08908], [-65.57743, -22.07675], [-65.58694, -22.09794], [-65.61166, -22.09504], [-65.7467, -22.10105], [-65.9261, -21.93335], [-66.04832, -21.9187], [-66.03836, -21.84829], [-66.24077, -21.77837], [-66.29714, -22.08741], [-66.7298, -22.23644], [-67.18382, -22.81525], [-66.99632, -22.99839], [-67.33563, -24.04237], [-68.24825, -24.42596], [-68.56909, -24.69831], [-68.38372, -25.08636], [-68.57622, -25.32505], [-68.38372, -26.15353], [-68.56909, -26.28146], [-68.59048, -26.49861], [-68.27677, -26.90626], [-68.43363, -27.08414], [-68.77586, -27.16029], [-69.22504, -27.95042], [-69.66709, -28.44055], [-69.80969, -29.07185], [-69.99507, -29.28351], [-69.8596, -30.26131], [-70.14479, -30.36595], [-70.55832, -31.51559], [-69.88099, -33.34489], [-69.87386, -34.13344], [-70.49416, -35.24145], [-70.38008, -36.02375], [-70.95047, -36.4321], [-71.24279, -37.20264], [-70.89532, -38.6923], [-71.37826, -38.91474], [-71.92726, -40.72714], [-71.74901, -42.11711], [-72.15541, -42.15941], [-72.14828, -42.85321], [-71.64206, -43.64774], [-71.81318, -44.38097], [-71.16436, -44.46244], [-71.26418, -44.75684], [-72.06985, -44.81756], [-71.35687, -45.22075], [-71.75614, -45.61611], [-71.68577, -46.55385], [-71.94152, -47.13595], [-72.50478, -47.80586], [-72.27662, -48.28727], [-72.54042, -48.52392], [-72.56894, -48.81116], [-73.09655, -49.14342], [-73.45156, -49.79461], [-73.55259, -49.92488], [-73.15765, -50.78337], [-72.31343, -50.58411]]]]
23142           }
23143         }, {
23144           type: "Feature",
23145           properties: {
23146             iso1A2: "AS",
23147             iso1A3: "ASM",
23148             iso1N3: "016",
23149             wikidata: "Q16641",
23150             nameEn: "American Samoa",
23151             aliases: ["US-AS"],
23152             country: "US",
23153             groups: ["Q1352230", "061", "009", "UN"],
23154             roadSpeedUnit: "mph",
23155             roadHeightUnit: "ft",
23156             callingCodes: ["1 684"]
23157           },
23158           geometry: {
23159             type: "MultiPolygon",
23160             coordinates: [[[[-171.39864, -10.21587], [-170.99605, -15.1275], [-166.32598, -15.26169], [-171.39864, -10.21587]]]]
23161           }
23162         }, {
23163           type: "Feature",
23164           properties: {
23165             iso1A2: "AT",
23166             iso1A3: "AUT",
23167             iso1N3: "040",
23168             wikidata: "Q40",
23169             nameEn: "Austria",
23170             groups: ["EU", "155", "150", "UN"],
23171             callingCodes: ["43"]
23172           },
23173           geometry: {
23174             type: "MultiPolygon",
23175             coordinates: [[[[15.34823, 48.98444], [15.28305, 48.98831], [15.26177, 48.95766], [15.16358, 48.94278], [15.15534, 48.99056], [14.99878, 49.01444], [14.97612, 48.96983], [14.98917, 48.90082], [14.95072, 48.79101], [14.98032, 48.77959], [14.9782, 48.7766], [14.98112, 48.77524], [14.9758, 48.76857], [14.95641, 48.75915], [14.94773, 48.76268], [14.81545, 48.7874], [14.80821, 48.77711], [14.80584, 48.73489], [14.72756, 48.69502], [14.71794, 48.59794], [14.66762, 48.58215], [14.60808, 48.62881], [14.56139, 48.60429], [14.4587, 48.64695], [14.43076, 48.58855], [14.33909, 48.55852], [14.20691, 48.5898], [14.09104, 48.5943], [14.01482, 48.63788], [14.06151, 48.66873], [13.84023, 48.76988], [13.82266, 48.75544], [13.81863, 48.73257], [13.79337, 48.71375], [13.81791, 48.69832], [13.81283, 48.68426], [13.81901, 48.6761], [13.82609, 48.62345], [13.80038, 48.59487], [13.80519, 48.58026], [13.76921, 48.55324], [13.7513, 48.5624], [13.74816, 48.53058], [13.72802, 48.51208], [13.66113, 48.53558], [13.65186, 48.55092], [13.62508, 48.55501], [13.59705, 48.57013], [13.57535, 48.55912], [13.51291, 48.59023], [13.50131, 48.58091], [13.50663, 48.57506], [13.46967, 48.55157], [13.45214, 48.56472], [13.43695, 48.55776], [13.45727, 48.51092], [13.42527, 48.45711], [13.43929, 48.43386], [13.40709, 48.37292], [13.30897, 48.31575], [13.26039, 48.29422], [13.18093, 48.29577], [13.126, 48.27867], [13.0851, 48.27711], [13.02083, 48.25689], [12.95306, 48.20629], [12.87126, 48.20318], [12.84475, 48.16556], [12.836, 48.1647], [12.8362, 48.15876], [12.82673, 48.15245], [12.80676, 48.14979], [12.78595, 48.12445], [12.7617, 48.12796], [12.74973, 48.10885], [12.76141, 48.07373], [12.8549, 48.01122], [12.87476, 47.96195], [12.91683, 47.95647], [12.9211, 47.95135], [12.91985, 47.94069], [12.92668, 47.93879], [12.93419, 47.94063], [12.93642, 47.94436], [12.93886, 47.94046], [12.94163, 47.92927], [13.00588, 47.84374], [12.98543, 47.82896], [12.96311, 47.79957], [12.93202, 47.77302], [12.94371, 47.76281], [12.9353, 47.74788], [12.91711, 47.74026], [12.90274, 47.72513], [12.91333, 47.7178], [12.92969, 47.71094], [12.98578, 47.7078], [13.01382, 47.72116], [13.07692, 47.68814], [13.09562, 47.63304], [13.06407, 47.60075], [13.06641, 47.58577], [13.04537, 47.58183], [13.05355, 47.56291], [13.03252, 47.53373], [13.04537, 47.49426], [12.9998, 47.46267], [12.98344, 47.48716], [12.9624, 47.47452], [12.85256, 47.52741], [12.84672, 47.54556], [12.80699, 47.54477], [12.77427, 47.58025], [12.82101, 47.61493], [12.76492, 47.64485], [12.77777, 47.66689], [12.7357, 47.6787], [12.6071, 47.6741], [12.57438, 47.63238], [12.53816, 47.63553], [12.50076, 47.62293], [12.44117, 47.6741], [12.43883, 47.6977], [12.37222, 47.68433], [12.336, 47.69534], [12.27991, 47.68827], [12.26004, 47.67725], [12.24017, 47.69534], [12.26238, 47.73544], [12.2542, 47.7433], [12.22571, 47.71776], [12.18303, 47.70065], [12.16217, 47.70105], [12.16769, 47.68167], [12.18347, 47.66663], [12.18507, 47.65984], [12.19895, 47.64085], [12.20801, 47.61082], [12.20398, 47.60667], [12.18568, 47.6049], [12.17737, 47.60121], [12.18145, 47.61019], [12.17824, 47.61506], [12.13734, 47.60639], [12.05788, 47.61742], [12.02282, 47.61033], [12.0088, 47.62451], [11.85572, 47.60166], [11.84052, 47.58354], [11.63934, 47.59202], [11.60681, 47.57881], [11.58811, 47.55515], [11.58578, 47.52281], [11.52618, 47.50939], [11.4362, 47.51413], [11.38128, 47.47465], [11.4175, 47.44621], [11.33804, 47.44937], [11.29597, 47.42566], [11.27844, 47.39956], [11.22002, 47.3964], [11.25157, 47.43277], [11.20482, 47.43198], [11.12536, 47.41222], [11.11835, 47.39719], [10.97111, 47.39561], [10.97111, 47.41617], [10.98513, 47.42882], [10.92437, 47.46991], [10.93839, 47.48018], [10.90918, 47.48571], [10.87061, 47.4786], [10.86945, 47.5015], [10.91268, 47.51334], [10.88814, 47.53701], [10.77596, 47.51729], [10.7596, 47.53228], [10.6965, 47.54253], [10.68832, 47.55752], [10.63456, 47.5591], [10.60337, 47.56755], [10.56912, 47.53584], [10.48849, 47.54057], [10.47329, 47.58552], [10.43473, 47.58394], [10.44992, 47.5524], [10.4324, 47.50111], [10.44291, 47.48453], [10.46278, 47.47901], [10.47446, 47.43318], [10.4359, 47.41183], [10.4324, 47.38494], [10.39851, 47.37623], [10.33424, 47.30813], [10.23257, 47.27088], [10.17531, 47.27167], [10.17648, 47.29149], [10.2147, 47.31014], [10.19998, 47.32832], [10.23757, 47.37609], [10.22774, 47.38904], [10.2127, 47.38019], [10.17648, 47.38889], [10.16362, 47.36674], [10.11805, 47.37228], [10.09819, 47.35724], [10.06897, 47.40709], [10.1052, 47.4316], [10.09001, 47.46005], [10.07131, 47.45531], [10.03859, 47.48927], [10.00003, 47.48216], [9.96029, 47.53899], [9.92407, 47.53111], [9.87733, 47.54688], [9.87499, 47.52953], [9.8189, 47.54688], [9.82591, 47.58158], [9.80254, 47.59419], [9.76748, 47.5934], [9.72736, 47.53457], [9.55125, 47.53629], [9.56312, 47.49495], [9.58208, 47.48344], [9.59482, 47.46305], [9.60205, 47.46165], [9.60484, 47.46358], [9.60841, 47.47178], [9.62158, 47.45858], [9.62475, 47.45685], [9.6423, 47.45599], [9.65728, 47.45383], [9.65863, 47.44847], [9.64483, 47.43842], [9.6446, 47.43233], [9.65043, 47.41937], [9.65136, 47.40504], [9.6629, 47.39591], [9.67334, 47.39191], [9.67445, 47.38429], [9.6711, 47.37824], [9.66243, 47.37136], [9.65427, 47.36824], [9.62476, 47.36639], [9.59978, 47.34671], [9.58513, 47.31334], [9.55857, 47.29919], [9.54773, 47.2809], [9.53116, 47.27029], [9.56766, 47.24281], [9.55176, 47.22585], [9.56981, 47.21926], [9.58264, 47.20673], [9.56539, 47.17124], [9.62623, 47.14685], [9.63395, 47.08443], [9.61216, 47.07732], [9.60717, 47.06091], [9.87935, 47.01337], [9.88266, 46.93343], [9.98058, 46.91434], [10.10715, 46.84296], [10.22675, 46.86942], [10.24128, 46.93147], [10.30031, 46.92093], [10.36933, 47.00212], [10.48376, 46.93891], [10.47197, 46.85698], [10.54783, 46.84505], [10.66405, 46.87614], [10.75753, 46.82258], [10.72974, 46.78972], [11.00764, 46.76896], [11.10618, 46.92966], [11.33355, 46.99862], [11.50739, 47.00644], [11.74789, 46.98484], [12.19254, 47.09331], [12.21781, 47.03996], [12.11675, 47.01241], [12.2006, 46.88854], [12.27591, 46.88651], [12.38708, 46.71529], [12.59992, 46.6595], [12.94445, 46.60401], [13.27627, 46.56059], [13.64088, 46.53438], [13.7148, 46.5222], [13.89837, 46.52331], [14.00422, 46.48474], [14.04002, 46.49117], [14.12097, 46.47724], [14.15989, 46.43327], [14.28326, 46.44315], [14.314, 46.43327], [14.42608, 46.44614], [14.45877, 46.41717], [14.52176, 46.42617], [14.56463, 46.37208], [14.5942, 46.43434], [14.66892, 46.44936], [14.72185, 46.49974], [14.81836, 46.51046], [14.83549, 46.56614], [14.86419, 46.59411], [14.87129, 46.61], [14.92283, 46.60848], [14.96002, 46.63459], [14.98024, 46.6009], [15.01451, 46.641], [15.14215, 46.66131], [15.23711, 46.63994], [15.41235, 46.65556], [15.45514, 46.63697], [15.46906, 46.61321], [15.54431, 46.6312], [15.55333, 46.64988], [15.54533, 46.66985], [15.59826, 46.68908], [15.62317, 46.67947], [15.63255, 46.68069], [15.6365, 46.6894], [15.6543, 46.69228], [15.6543, 46.70616], [15.67411, 46.70735], [15.69523, 46.69823], [15.72279, 46.69548], [15.73823, 46.70011], [15.76771, 46.69863], [15.78518, 46.70712], [15.8162, 46.71897], [15.87691, 46.7211], [15.94864, 46.68769], [15.98512, 46.68463], [15.99988, 46.67947], [16.04036, 46.6549], [16.04347, 46.68694], [16.02808, 46.71094], [15.99769, 46.7266], [15.98432, 46.74991], [15.99126, 46.78199], [15.99054, 46.82772], [16.05786, 46.83927], [16.10983, 46.867], [16.19904, 46.94134], [16.22403, 46.939], [16.27594, 46.9643], [16.28202, 47.00159], [16.51369, 47.00084], [16.43936, 47.03548], [16.52176, 47.05747], [16.46134, 47.09395], [16.52863, 47.13974], [16.44932, 47.14418], [16.46442, 47.16845], [16.4523, 47.18812], [16.42801, 47.18422], [16.41739, 47.20649], [16.43663, 47.21127], [16.44142, 47.25079], [16.47782, 47.25918], [16.45104, 47.41181], [16.49908, 47.39416], [16.52414, 47.41007], [16.57152, 47.40868], [16.6718, 47.46139], [16.64821, 47.50155], [16.71059, 47.52692], [16.64193, 47.63114], [16.58699, 47.61772], [16.4222, 47.66537], [16.55129, 47.72268], [16.53514, 47.73837], [16.54779, 47.75074], [16.61183, 47.76171], [16.65679, 47.74197], [16.72089, 47.73469], [16.7511, 47.67878], [16.82938, 47.68432], [16.86509, 47.72268], [16.87538, 47.68895], [17.08893, 47.70928], [17.05048, 47.79377], [17.07039, 47.81129], [17.00997, 47.86245], [17.08275, 47.87719], [17.11022, 47.92461], [17.09786, 47.97336], [17.16001, 48.00636], [17.07039, 48.0317], [17.09168, 48.09366], [17.05735, 48.14179], [17.02919, 48.13996], [16.97701, 48.17385], [16.89461, 48.31332], [16.90903, 48.32519], [16.84243, 48.35258], [16.83317, 48.38138], [16.83588, 48.3844], [16.8497, 48.38321], [16.85204, 48.44968], [16.94611, 48.53614], [16.93955, 48.60371], [16.90354, 48.71541], [16.79779, 48.70998], [16.71883, 48.73806], [16.68518, 48.7281], [16.67008, 48.77699], [16.46134, 48.80865], [16.40915, 48.74576], [16.37345, 48.729], [16.06034, 48.75436], [15.84404, 48.86921], [15.78087, 48.87644], [15.75341, 48.8516], [15.6921, 48.85973], [15.61622, 48.89541], [15.51357, 48.91549], [15.48027, 48.94481], [15.34823, 48.98444]]]]
23176           }
23177         }, {
23178           type: "Feature",
23179           properties: {
23180             iso1A2: "AU",
23181             iso1A3: "AUS",
23182             iso1N3: "036",
23183             wikidata: "Q408",
23184             nameEn: "Australia"
23185           },
23186           geometry: null
23187         }, {
23188           type: "Feature",
23189           properties: {
23190             iso1A2: "AW",
23191             iso1A3: "ABW",
23192             iso1N3: "533",
23193             wikidata: "Q21203",
23194             nameEn: "Aruba",
23195             aliases: ["NL-AW"],
23196             country: "NL",
23197             groups: ["Q1451600", "029", "003", "419", "019", "UN"],
23198             callingCodes: ["297"]
23199           },
23200           geometry: {
23201             type: "MultiPolygon",
23202             coordinates: [[[[-70.00823, 12.98375], [-70.35625, 12.58277], [-69.60231, 12.17], [-70.00823, 12.98375]]]]
23203           }
23204         }, {
23205           type: "Feature",
23206           properties: {
23207             iso1A2: "AX",
23208             iso1A3: "ALA",
23209             iso1N3: "248",
23210             wikidata: "Q5689",
23211             nameEn: "\xC5land Islands",
23212             country: "FI",
23213             groups: ["EU", "154", "150", "UN"],
23214             callingCodes: ["358 18", "358 457"]
23215           },
23216           geometry: {
23217             type: "MultiPolygon",
23218             coordinates: [[[[19.08191, 60.19152], [20.5104, 59.15546], [21.35468, 59.67511], [21.02509, 60.12142], [21.08159, 60.20167], [21.15143, 60.54555], [20.96741, 60.71528], [19.23413, 60.61414], [19.08191, 60.19152]]]]
23219           }
23220         }, {
23221           type: "Feature",
23222           properties: {
23223             iso1A2: "AZ",
23224             iso1A3: "AZE",
23225             iso1N3: "031",
23226             wikidata: "Q227",
23227             nameEn: "Azerbaijan",
23228             groups: ["145", "142", "UN"],
23229             callingCodes: ["994"]
23230           },
23231           geometry: {
23232             type: "MultiPolygon",
23233             coordinates: [[[[46.42738, 41.91323], [46.3984, 41.84399], [46.30863, 41.79133], [46.23962, 41.75811], [46.20538, 41.77205], [46.17891, 41.72094], [46.19759, 41.62327], [46.24429, 41.59883], [46.26531, 41.63339], [46.28182, 41.60089], [46.3253, 41.60912], [46.34039, 41.5947], [46.34126, 41.57454], [46.29794, 41.5724], [46.33925, 41.4963], [46.40307, 41.48464], [46.4669, 41.43331], [46.63658, 41.37727], [46.72375, 41.28609], [46.66148, 41.20533], [46.63969, 41.09515], [46.55096, 41.1104], [46.48558, 41.0576], [46.456, 41.09984], [46.37661, 41.10805], [46.27698, 41.19011], [46.13221, 41.19479], [45.95786, 41.17956], [45.80842, 41.2229], [45.69946, 41.29545], [45.75705, 41.35157], [45.71035, 41.36208], [45.68389, 41.3539], [45.45973, 41.45898], [45.4006, 41.42402], [45.31352, 41.47168], [45.26285, 41.46433], [45.1797, 41.42231], [45.09867, 41.34065], [45.0133, 41.29747], [45.05497, 41.2464], [45.02932, 41.2101], [45.05201, 41.19211], [45.11811, 41.19967], [45.1969, 41.168], [45.19942, 41.13299], [45.08028, 41.10917], [45.06784, 41.05379], [45.12923, 41.06059], [45.1313, 41.09369], [45.1634, 41.08082], [45.16493, 41.05068], [45.1994, 41.04518], [45.25897, 41.0027], [45.26162, 41.0228], [45.28859, 41.03757], [45.35677, 40.99784], [45.39725, 41.02603], [45.44083, 41.01663], [45.40814, 40.97904], [45.60584, 40.87436], [45.55914, 40.78366], [45.4206, 40.7424], [45.35366, 40.65979], [45.45484, 40.57707], [45.42994, 40.53804], [45.65098, 40.37696], [45.95609, 40.27846], [45.97944, 40.181], [45.83779, 39.98925], [45.78642, 40.03218], [45.59806, 40.0131], [45.60895, 39.97733], [45.7833, 39.9475], [45.82533, 39.82925], [45.96543, 39.78859], [46.18493, 39.60533], [46.40286, 39.63651], [46.42465, 39.57534], [46.52117, 39.58734], [46.57098, 39.56694], [46.57721, 39.54414], [46.51027, 39.52373], [46.53051, 39.47809], [46.4013, 39.45405], [46.37795, 39.42039], [46.43244, 39.35181], [46.50093, 39.33736], [46.56476, 39.24942], [46.63481, 39.23013], [46.58032, 39.21204], [46.54141, 39.15895], [46.52584, 39.18912], [46.44022, 39.19636], [46.54296, 39.07078], [46.51805, 38.94982], [46.53497, 38.86548], [46.75752, 39.03231], [46.83822, 39.13143], [46.92539, 39.16644], [46.95341, 39.13505], [47.05771, 39.20143], [47.05927, 39.24846], [47.31301, 39.37492], [47.38978, 39.45999], [47.50099, 39.49615], [47.84774, 39.66285], [47.98977, 39.70999], [48.34264, 39.42935], [48.37385, 39.37584], [48.15984, 39.30028], [48.12404, 39.25208], [48.15361, 39.19419], [48.31239, 39.09278], [48.33884, 39.03022], [48.28437, 38.97186], [48.08627, 38.94434], [48.07734, 38.91616], [48.01409, 38.90333], [48.02581, 38.82705], [48.24773, 38.71883], [48.3146, 38.59958], [48.45084, 38.61013], [48.58793, 38.45076], [48.62217, 38.40198], [48.70001, 38.40564], [48.78979, 38.45026], [48.81072, 38.44853], [48.84969, 38.45015], [48.88288, 38.43975], [52.39847, 39.43556], [48.80971, 41.95365], [48.5867, 41.84306], [48.55078, 41.77917], [48.42301, 41.65444], [48.40277, 41.60441], [48.2878, 41.56221], [48.22064, 41.51472], [48.07587, 41.49957], [47.87973, 41.21798], [47.75831, 41.19455], [47.62288, 41.22969], [47.54504, 41.20275], [47.49004, 41.26366], [47.34579, 41.27884], [47.10762, 41.59044], [47.03757, 41.55434], [46.99554, 41.59743], [47.00955, 41.63583], [46.8134, 41.76252], [46.75269, 41.8623], [46.58924, 41.80547], [46.5332, 41.87389], [46.42738, 41.91323]], [[45.50279, 40.58424], [45.47927, 40.65023], [45.51825, 40.67382], [45.56071, 40.64765], [45.50279, 40.58424]]], [[[45.00864, 41.03411], [45.03792, 41.03938], [45.04517, 41.06653], [45.03406, 41.07931], [45.00864, 41.09407], [44.97169, 41.09176], [44.95383, 41.07553], [44.96031, 41.06345], [44.9903, 41.05657], [45.00864, 41.03411]]], [[[45.21324, 40.9817], [45.23095, 40.97828], [45.23487, 41.00226], [45.20625, 41.01484], [45.18382, 41.0066], [45.19312, 40.98998], [45.20518, 40.99348], [45.21219, 40.99001], [45.21324, 40.9817]]], [[[45.46992, 39.49888], [45.29606, 39.57654], [45.30385, 39.61373], [45.23535, 39.61373], [45.21784, 39.58074], [45.17464, 39.58614], [45.18554, 39.67846], [45.06604, 39.79277], [44.92869, 39.72157], [44.88354, 39.74432], [44.75779, 39.7148], [44.80977, 39.65768], [44.81043, 39.62677], [44.88916, 39.59653], [44.96746, 39.42998], [45.05932, 39.36435], [45.08751, 39.35052], [45.16168, 39.21952], [45.30489, 39.18333], [45.40148, 39.09007], [45.40452, 39.07224], [45.44811, 39.04927], [45.44966, 38.99243], [45.6131, 38.964], [45.6155, 38.94304], [45.65172, 38.95199], [45.83883, 38.90768], [45.90266, 38.87739], [45.94624, 38.89072], [46.00228, 38.87376], [46.06766, 38.87861], [46.14785, 38.84206], [46.06973, 39.0744], [46.02303, 39.09978], [45.99774, 39.28931], [45.79225, 39.3695], [45.83, 39.46487], [45.80804, 39.56716], [45.70547, 39.60174], [45.46992, 39.49888]]]]
23234           }
23235         }, {
23236           type: "Feature",
23237           properties: {
23238             iso1A2: "BA",
23239             iso1A3: "BIH",
23240             iso1N3: "070",
23241             wikidata: "Q225",
23242             nameEn: "Bosnia and Herzegovina",
23243             groups: ["039", "150", "UN"],
23244             callingCodes: ["387"]
23245           },
23246           geometry: {
23247             type: "MultiPolygon",
23248             coordinates: [[[[17.84826, 45.04489], [17.66571, 45.13408], [17.59104, 45.10816], [17.51469, 45.10791], [17.47589, 45.12656], [17.45615, 45.12523], [17.4498, 45.16119], [17.41229, 45.13335], [17.33573, 45.14521], [17.32092, 45.16246], [17.26815, 45.18444], [17.25131, 45.14957], [17.24325, 45.146], [17.18438, 45.14764], [17.0415, 45.20759], [16.9385, 45.22742], [16.92405, 45.27607], [16.83804, 45.18951], [16.81137, 45.18434], [16.78219, 45.19002], [16.74845, 45.20393], [16.64962, 45.20714], [16.60194, 45.23042], [16.56559, 45.22307], [16.5501, 45.2212], [16.52982, 45.22713], [16.49155, 45.21153], [16.4634, 45.14522], [16.40023, 45.1147], [16.38309, 45.05955], [16.38219, 45.05139], [16.3749, 45.05206], [16.35863, 45.03529], [16.35404, 45.00241], [16.29036, 44.99732], [16.12153, 45.09616], [15.98412, 45.23088], [15.83512, 45.22459], [15.76371, 45.16508], [15.78842, 45.11519], [15.74585, 45.0638], [15.78568, 44.97401], [15.74723, 44.96818], [15.76096, 44.87045], [15.79472, 44.8455], [15.72584, 44.82334], [15.8255, 44.71501], [15.89348, 44.74964], [16.05828, 44.61538], [16.00884, 44.58605], [16.03012, 44.55572], [16.10566, 44.52586], [16.16814, 44.40679], [16.12969, 44.38275], [16.21346, 44.35231], [16.18688, 44.27012], [16.36864, 44.08263], [16.43662, 44.07523], [16.43629, 44.02826], [16.50528, 44.0244], [16.55472, 43.95326], [16.70922, 43.84887], [16.75316, 43.77157], [16.80736, 43.76011], [17.00585, 43.58037], [17.15828, 43.49376], [17.24411, 43.49376], [17.29699, 43.44542], [17.25579, 43.40353], [17.286, 43.33065], [17.46986, 43.16559], [17.64268, 43.08595], [17.70879, 42.97223], [17.5392, 42.92787], [17.6444, 42.88641], [17.68151, 42.92725], [17.7948, 42.89556], [17.80854, 42.9182], [17.88201, 42.83668], [18.24318, 42.6112], [18.36197, 42.61423], [18.43735, 42.55921], [18.49778, 42.58409], [18.53751, 42.57376], [18.55504, 42.58409], [18.52232, 42.62279], [18.57373, 42.64429], [18.54841, 42.68328], [18.54603, 42.69171], [18.55221, 42.69045], [18.56789, 42.72074], [18.47324, 42.74992], [18.45921, 42.81682], [18.47633, 42.85829], [18.4935, 42.86433], [18.49661, 42.89306], [18.49076, 42.95553], [18.52232, 43.01451], [18.66254, 43.03928], [18.64735, 43.14766], [18.66605, 43.2056], [18.71747, 43.2286], [18.6976, 43.25243], [18.76538, 43.29838], [18.85342, 43.32426], [18.84794, 43.33735], [18.83912, 43.34795], [18.90911, 43.36383], [18.95819, 43.32899], [18.95001, 43.29327], [19.00844, 43.24988], [19.04233, 43.30008], [19.08206, 43.29668], [19.08673, 43.31453], [19.04071, 43.397], [19.01078, 43.43854], [18.96053, 43.45042], [18.95469, 43.49367], [18.91379, 43.50299], [19.01078, 43.55806], [19.04934, 43.50384], [19.13933, 43.5282], [19.15685, 43.53943], [19.22807, 43.5264], [19.24774, 43.53061], [19.2553, 43.5938], [19.33426, 43.58833], [19.36653, 43.60921], [19.41941, 43.54056], [19.42696, 43.57987], [19.50455, 43.58385], [19.5176, 43.71403], [19.3986, 43.79668], [19.23465, 43.98764], [19.24363, 44.01502], [19.38439, 43.96611], [19.52515, 43.95573], [19.56498, 43.99922], [19.61836, 44.01464], [19.61991, 44.05254], [19.57467, 44.04716], [19.55999, 44.06894], [19.51167, 44.08158], [19.47321, 44.1193], [19.48386, 44.14332], [19.47338, 44.15034], [19.43905, 44.13088], [19.40927, 44.16722], [19.3588, 44.18353], [19.34773, 44.23244], [19.32464, 44.27185], [19.26945, 44.26957], [19.23306, 44.26097], [19.20508, 44.2917], [19.18328, 44.28383], [19.16741, 44.28648], [19.13332, 44.31492], [19.13556, 44.338], [19.11547, 44.34218], [19.1083, 44.3558], [19.11865, 44.36712], [19.10298, 44.36924], [19.10365, 44.37795], [19.10704, 44.38249], [19.10749, 44.39421], [19.11785, 44.40313], [19.14681, 44.41463], [19.14837, 44.45253], [19.12278, 44.50132], [19.13369, 44.52521], [19.16699, 44.52197], [19.26388, 44.65412], [19.32543, 44.74058], [19.36722, 44.88164], [19.18183, 44.92055], [19.01994, 44.85493], [18.8704, 44.85097], [18.76347, 44.90669], [18.76369, 44.93707], [18.80661, 44.93561], [18.78357, 44.97741], [18.65723, 45.07544], [18.47939, 45.05871], [18.41896, 45.11083], [18.32077, 45.1021], [18.24387, 45.13699], [18.1624, 45.07654], [18.03121, 45.12632], [18.01594, 45.15163], [17.99479, 45.14958], [17.97834, 45.13831], [17.97336, 45.12245], [17.93706, 45.08016], [17.87148, 45.04645], [17.84826, 45.04489]]]]
23249           }
23250         }, {
23251           type: "Feature",
23252           properties: {
23253             iso1A2: "BB",
23254             iso1A3: "BRB",
23255             iso1N3: "052",
23256             wikidata: "Q244",
23257             nameEn: "Barbados",
23258             groups: ["029", "003", "419", "019", "UN"],
23259             driveSide: "left",
23260             callingCodes: ["1 246"]
23261           },
23262           geometry: {
23263             type: "MultiPolygon",
23264             coordinates: [[[[-58.56442, 13.24471], [-59.80731, 13.87556], [-59.82929, 12.70644], [-58.56442, 13.24471]]]]
23265           }
23266         }, {
23267           type: "Feature",
23268           properties: {
23269             iso1A2: "BD",
23270             iso1A3: "BGD",
23271             iso1N3: "050",
23272             wikidata: "Q902",
23273             nameEn: "Bangladesh",
23274             groups: ["034", "142", "UN"],
23275             driveSide: "left",
23276             callingCodes: ["880"]
23277           },
23278           geometry: {
23279             type: "MultiPolygon",
23280             coordinates: [[[[89.15869, 26.13708], [89.08899, 26.38845], [88.95612, 26.4564], [88.92357, 26.40711], [88.91321, 26.37984], [89.05328, 26.2469], [88.85004, 26.23211], [88.78961, 26.31093], [88.67837, 26.26291], [88.69485, 26.38353], [88.62144, 26.46783], [88.4298, 26.54489], [88.41196, 26.63837], [88.33093, 26.48929], [88.35153, 26.45241], [88.36938, 26.48683], [88.48749, 26.45855], [88.51649, 26.35923], [88.35153, 26.29123], [88.34757, 26.22216], [88.1844, 26.14417], [88.16581, 26.0238], [88.08804, 25.91334], [88.13138, 25.78773], [88.242, 25.80811], [88.45103, 25.66245], [88.4559, 25.59227], [88.677, 25.46959], [88.81296, 25.51546], [88.85278, 25.34679], [89.01105, 25.30303], [89.00463, 25.26583], [88.94067, 25.18534], [88.44766, 25.20149], [88.46277, 25.07468], [88.33917, 24.86803], [88.27325, 24.88796], [88.21832, 24.96642], [88.14004, 24.93529], [88.15515, 24.85806], [88.00683, 24.66477], [88.08786, 24.63232], [88.12296, 24.51301], [88.50934, 24.32474], [88.68801, 24.31464], [88.74841, 24.1959], [88.6976, 24.14703], [88.73743, 23.91751], [88.66189, 23.87607], [88.58087, 23.87105], [88.56507, 23.64044], [88.74841, 23.47361], [88.79351, 23.50535], [88.79254, 23.46028], [88.71133, 23.2492], [88.99148, 23.21134], [88.86377, 23.08759], [88.88327, 23.03885], [88.87063, 22.95235], [88.96713, 22.83346], [88.9151, 22.75228], [88.94614, 22.66941], [88.9367, 22.58527], [89.07114, 22.15335], [89.08044, 21.41871], [92.47409, 20.38654], [92.26071, 21.05697], [92.17752, 21.17445], [92.20087, 21.337], [92.37939, 21.47764], [92.43158, 21.37025], [92.55105, 21.3856], [92.60187, 21.24615], [92.68152, 21.28454], [92.59775, 21.6092], [92.62187, 21.87037], [92.60949, 21.97638], [92.56616, 22.13554], [92.60029, 22.1522], [92.5181, 22.71441], [92.37665, 22.9435], [92.38214, 23.28705], [92.26541, 23.70392], [92.15417, 23.73409], [92.04706, 23.64229], [91.95093, 23.73284], [91.95642, 23.47361], [91.84789, 23.42235], [91.76417, 23.26619], [91.81634, 23.08001], [91.7324, 23.00043], [91.61571, 22.93929], [91.54993, 23.01051], [91.46615, 23.2328], [91.4035, 23.27522], [91.40848, 23.07117], [91.36453, 23.06612], [91.28293, 23.37538], [91.15579, 23.6599], [91.25192, 23.83463], [91.22308, 23.89616], [91.29587, 24.0041], [91.35741, 23.99072], [91.37414, 24.10693], [91.55542, 24.08687], [91.63782, 24.1132], [91.65292, 24.22095], [91.73257, 24.14703], [91.76004, 24.23848], [91.82596, 24.22345], [91.89258, 24.14674], [91.96603, 24.3799], [92.11662, 24.38997], [92.15796, 24.54435], [92.25854, 24.9191], [92.38626, 24.86055], [92.49887, 24.88796], [92.39147, 25.01471], [92.33957, 25.07593], [92.0316, 25.1834], [91.63648, 25.12846], [91.25517, 25.20677], [90.87427, 25.15799], [90.65042, 25.17788], [90.40034, 25.1534], [90.1155, 25.22686], [89.90478, 25.31038], [89.87629, 25.28337], [89.83371, 25.29548], [89.84086, 25.31854], [89.81208, 25.37244], [89.86129, 25.61714], [89.84388, 25.70042], [89.80585, 25.82489], [89.86592, 25.93115], [89.77728, 26.04254], [89.77865, 26.08387], [89.73581, 26.15818], [89.70201, 26.15138], [89.63968, 26.22595], [89.57101, 25.9682], [89.53515, 26.00382], [89.35953, 26.0077], [89.15869, 26.13708]]]]
23281           }
23282         }, {
23283           type: "Feature",
23284           properties: {
23285             iso1A2: "BE",
23286             iso1A3: "BEL",
23287             iso1N3: "056",
23288             wikidata: "Q31",
23289             nameEn: "Belgium",
23290             groups: ["EU", "155", "150", "UN"],
23291             callingCodes: ["32"]
23292           },
23293           geometry: {
23294             type: "MultiPolygon",
23295             coordinates: [[[[4.93295, 51.44945], [4.93909, 51.44632], [4.9524, 51.45014], [4.95244, 51.45207], [4.93295, 51.44945]]], [[[4.91493, 51.4353], [4.92652, 51.43329], [4.92952, 51.42984], [4.93986, 51.43064], [4.94265, 51.44003], [4.93471, 51.43861], [4.93416, 51.44185], [4.94025, 51.44193], [4.93544, 51.44634], [4.92879, 51.44161], [4.92815, 51.43856], [4.92566, 51.44273], [4.92811, 51.4437], [4.92287, 51.44741], [4.91811, 51.44621], [4.92227, 51.44252], [4.91935, 51.43634], [4.91493, 51.4353]]], [[[4.82946, 51.4213], [4.82409, 51.44736], [4.84139, 51.4799], [4.78803, 51.50284], [4.77321, 51.50529], [4.74578, 51.48937], [4.72935, 51.48424], [4.65442, 51.42352], [4.57489, 51.4324], [4.53521, 51.4243], [4.52846, 51.45002], [4.54675, 51.47265], [4.5388, 51.48184], [4.47736, 51.4778], [4.38122, 51.44905], [4.39747, 51.43316], [4.38064, 51.41965], [4.43777, 51.36989], [4.39292, 51.35547], [4.34086, 51.35738], [4.33265, 51.37687], [4.21923, 51.37443], [4.24024, 51.35371], [4.16721, 51.29348], [4.05165, 51.24171], [4.01957, 51.24504], [3.97889, 51.22537], [3.90125, 51.20371], [3.78783, 51.2151], [3.78999, 51.25766], [3.58939, 51.30064], [3.51502, 51.28697], [3.52698, 51.2458], [3.43488, 51.24135], [3.41704, 51.25933], [3.38289, 51.27331], [3.35847, 51.31572], [3.38696, 51.33436], [3.36263, 51.37112], [2.56575, 51.85301], [2.18458, 51.52087], [2.55904, 51.07014], [2.57551, 51.00326], [2.63074, 50.94746], [2.59093, 50.91751], [2.63331, 50.81457], [2.71165, 50.81295], [2.81056, 50.71773], [2.8483, 50.72276], [2.86985, 50.7033], [2.87937, 50.70298], [2.88504, 50.70656], [2.90069, 50.69263], [2.91036, 50.6939], [2.90873, 50.702], [2.95019, 50.75138], [2.96778, 50.75242], [3.00537, 50.76588], [3.04314, 50.77674], [3.09163, 50.77717], [3.10614, 50.78303], [3.11206, 50.79416], [3.11987, 50.79188], [3.1257, 50.78603], [3.15017, 50.79031], [3.16476, 50.76843], [3.18339, 50.74981], [3.18811, 50.74025], [3.20064, 50.73547], [3.19017, 50.72569], [3.20845, 50.71662], [3.22042, 50.71019], [3.24593, 50.71389], [3.26063, 50.70086], [3.26141, 50.69151], [3.2536, 50.68977], [3.264, 50.67668], [3.23951, 50.6585], [3.2729, 50.60718], [3.28575, 50.52724], [3.37693, 50.49538], [3.44629, 50.51009], [3.47385, 50.53397], [3.51564, 50.5256], [3.49509, 50.48885], [3.5683, 50.50192], [3.58361, 50.49049], [3.61014, 50.49568], [3.64426, 50.46275], [3.66153, 50.45165], [3.67494, 50.40239], [3.67262, 50.38663], [3.65709, 50.36873], [3.66976, 50.34563], [3.71009, 50.30305], [3.70987, 50.3191], [3.73911, 50.34809], [3.84314, 50.35219], [3.90781, 50.32814], [3.96771, 50.34989], [4.0268, 50.35793], [4.0689, 50.3254], [4.10237, 50.31247], [4.10957, 50.30234], [4.11954, 50.30425], [4.13665, 50.25609], [4.16808, 50.25786], [4.15524, 50.2833], [4.17347, 50.28838], [4.17861, 50.27443], [4.20651, 50.27333], [4.21945, 50.25539], [4.15524, 50.21103], [4.16014, 50.19239], [4.13561, 50.13078], [4.20147, 50.13535], [4.23101, 50.06945], [4.16294, 50.04719], [4.13508, 50.01976], [4.14239, 49.98034], [4.20532, 49.95803], [4.31963, 49.97043], [4.35051, 49.95315], [4.43488, 49.94122], [4.51098, 49.94659], [4.5414, 49.96911], [4.68695, 49.99685], [4.70064, 50.09384], [4.75237, 50.11314], [4.82438, 50.16878], [4.83279, 50.15331], [4.88602, 50.15182], [4.8382, 50.06738], [4.78827, 49.95609], [4.88529, 49.9236], [4.85134, 49.86457], [4.86965, 49.82271], [4.85464, 49.78995], [4.96714, 49.79872], [5.09249, 49.76193], [5.14545, 49.70287], [5.26232, 49.69456], [5.31465, 49.66846], [5.33039, 49.6555], [5.30214, 49.63055], [5.3137, 49.61225], [5.33851, 49.61599], [5.34837, 49.62889], [5.3974, 49.61596], [5.43713, 49.5707], [5.46734, 49.52648], [5.46541, 49.49825], [5.55001, 49.52729], [5.60909, 49.51228], [5.64505, 49.55146], [5.75649, 49.54321], [5.7577, 49.55915], [5.77435, 49.56298], [5.79195, 49.55228], [5.81838, 49.54777], [5.84143, 49.5533], [5.84692, 49.55663], [5.8424, 49.56082], [5.87256, 49.57539], [5.86986, 49.58756], [5.84971, 49.58674], [5.84826, 49.5969], [5.8762, 49.60898], [5.87609, 49.62047], [5.88393, 49.62802], [5.88552, 49.63507], [5.90599, 49.63853], [5.90164, 49.6511], [5.9069, 49.66377], [5.86175, 49.67862], [5.86527, 49.69291], [5.88677, 49.70951], [5.86503, 49.72739], [5.84193, 49.72161], [5.82562, 49.72395], [5.83149, 49.74729], [5.82245, 49.75048], [5.78871, 49.7962], [5.75409, 49.79239], [5.74953, 49.81428], [5.74364, 49.82058], [5.74844, 49.82435], [5.7404, 49.83452], [5.74076, 49.83823], [5.74975, 49.83933], [5.74953, 49.84709], [5.75884, 49.84811], [5.74567, 49.85368], [5.75861, 49.85631], [5.75269, 49.8711], [5.78415, 49.87922], [5.73621, 49.89796], [5.77314, 49.93646], [5.77291, 49.96056], [5.80833, 49.96451], [5.81163, 49.97142], [5.83467, 49.97823], [5.83968, 49.9892], [5.82331, 49.99662], [5.81866, 50.01286], [5.8551, 50.02683], [5.86904, 50.04614], [5.85474, 50.06342], [5.8857, 50.07824], [5.89488, 50.11476], [5.95929, 50.13295], [5.96453, 50.17259], [6.02488, 50.18283], [6.03093, 50.16362], [6.06406, 50.15344], [6.08577, 50.17246], [6.12028, 50.16374], [6.1137, 50.13668], [6.1379, 50.12964], [6.15298, 50.14126], [6.14132, 50.14971], [6.14588, 50.17106], [6.18739, 50.1822], [6.18364, 50.20815], [6.16853, 50.2234], [6.208, 50.25179], [6.28797, 50.27458], [6.29949, 50.30887], [6.32488, 50.32333], [6.35701, 50.31139], [6.40641, 50.32425], [6.40785, 50.33557], [6.3688, 50.35898], [6.34406, 50.37994], [6.36852, 50.40776], [6.37219, 50.45397], [6.34005, 50.46083], [6.3465, 50.48833], [6.30809, 50.50058], [6.26637, 50.50272], [6.22335, 50.49578], [6.20599, 50.52089], [6.19193, 50.5212], [6.18716, 50.52653], [6.19579, 50.5313], [6.19735, 50.53576], [6.17802, 50.54179], [6.17739, 50.55875], [6.20281, 50.56952], [6.22581, 50.5907], [6.24005, 50.58732], [6.24888, 50.59869], [6.2476, 50.60392], [6.26957, 50.62444], [6.17852, 50.6245], [6.11707, 50.72231], [6.04428, 50.72861], [6.0406, 50.71848], [6.0326, 50.72647], [6.03889, 50.74618], [6.01976, 50.75398], [5.97545, 50.75441], [5.95942, 50.7622], [5.89132, 50.75124], [5.89129, 50.75125], [5.88734, 50.77092], [5.84888, 50.75448], [5.84548, 50.76542], [5.80673, 50.7558], [5.77513, 50.78308], [5.76533, 50.78159], [5.74356, 50.7691], [5.73904, 50.75674], [5.72216, 50.76398], [5.69469, 50.75529], [5.68091, 50.75804], [5.70107, 50.7827], [5.68995, 50.79641], [5.70118, 50.80764], [5.65259, 50.82309], [5.64009, 50.84742], [5.64504, 50.87107], [5.67886, 50.88142], [5.69858, 50.91046], [5.71626, 50.90796], [5.72644, 50.91167], [5.72545, 50.92312], [5.74644, 50.94723], [5.75927, 50.95601], [5.74752, 50.96202], [5.72875, 50.95428], [5.71864, 50.96092], [5.76242, 50.99703], [5.77688, 51.02483], [5.75961, 51.03113], [5.77258, 51.06196], [5.79835, 51.05834], [5.79903, 51.09371], [5.82921, 51.09328], [5.83226, 51.10585], [5.8109, 51.10861], [5.80798, 51.11661], [5.85508, 51.14445], [5.82564, 51.16753], [5.77697, 51.1522], [5.77735, 51.17845], [5.74617, 51.18928], [5.70344, 51.1829], [5.65528, 51.18736], [5.65145, 51.19788], [5.5603, 51.22249], [5.5569, 51.26544], [5.515, 51.29462], [5.48476, 51.30053], [5.46519, 51.2849], [5.4407, 51.28169], [5.41672, 51.26248], [5.347, 51.27502], [5.33886, 51.26314], [5.29716, 51.26104], [5.26461, 51.26693], [5.23814, 51.26064], [5.22542, 51.26888], [5.24244, 51.30495], [5.2002, 51.32243], [5.16222, 51.31035], [5.13377, 51.31592], [5.13105, 51.34791], [5.07102, 51.39469], [5.10456, 51.43163], [5.07891, 51.4715], [5.04774, 51.47022], [5.03281, 51.48679], [5.0106, 51.47167], [5.00393, 51.44406], [4.92152, 51.39487], [4.90016, 51.41404], [4.84988, 51.41502], [4.78941, 51.41102], [4.77229, 51.41337], [4.76577, 51.43046], [4.78314, 51.43319], [4.82946, 51.4213]]]]
23296           }
23297         }, {
23298           type: "Feature",
23299           properties: {
23300             iso1A2: "BF",
23301             iso1A3: "BFA",
23302             iso1N3: "854",
23303             wikidata: "Q965",
23304             nameEn: "Burkina Faso",
23305             groups: ["011", "202", "002", "UN"],
23306             callingCodes: ["226"]
23307           },
23308           geometry: {
23309             type: "MultiPolygon",
23310             coordinates: [[[[0.23859, 15.00135], [0.06588, 14.96961], [-0.24673, 15.07805], [-0.72004, 15.08655], [-1.05875, 14.7921], [-1.32166, 14.72774], [-1.68083, 14.50023], [-1.97945, 14.47709], [-1.9992, 14.19011], [-2.10223, 14.14878], [-2.47587, 14.29671], [-2.66175, 14.14713], [-2.84667, 14.05532], [-2.90831, 13.81174], [-2.88189, 13.64921], [-3.26407, 13.70699], [-3.28396, 13.5422], [-3.23599, 13.29035], [-3.43507, 13.27272], [-3.4313, 13.1588], [-3.54454, 13.1781], [-3.7911, 13.36665], [-3.96282, 13.38164], [-3.90558, 13.44375], [-3.96501, 13.49778], [-4.34477, 13.12927], [-4.21819, 12.95722], [-4.238, 12.71467], [-4.47356, 12.71252], [-4.41412, 12.31922], [-4.57703, 12.19875], [-4.54841, 12.1385], [-4.62546, 12.13204], [-4.62987, 12.06531], [-4.70692, 12.06746], [-4.72893, 12.01579], [-5.07897, 11.97918], [-5.26389, 11.84778], [-5.40258, 11.8327], [-5.26389, 11.75728], [-5.29251, 11.61715], [-5.22867, 11.60421], [-5.20665, 11.43811], [-5.25509, 11.36905], [-5.25949, 11.24816], [-5.32553, 11.21578], [-5.32994, 11.13371], [-5.49284, 11.07538], [-5.41579, 10.84628], [-5.47083, 10.75329], [-5.46643, 10.56074], [-5.51058, 10.43177], [-5.39602, 10.2929], [-5.12465, 10.29788], [-4.96453, 9.99923], [-4.96621, 9.89132], [-4.6426, 9.70696], [-4.31392, 9.60062], [-4.25999, 9.76012], [-3.69703, 9.94279], [-3.31779, 9.91125], [-3.27228, 9.84981], [-3.19306, 9.93781], [-3.16609, 9.85147], [-3.00765, 9.74019], [-2.93012, 9.57403], [-2.76494, 9.40778], [-2.68802, 9.49343], [-2.76534, 9.56589], [-2.74174, 9.83172], [-2.83108, 10.40252], [-2.94232, 10.64281], [-2.83373, 11.0067], [-0.67143, 10.99811], [-0.61937, 10.91305], [-0.44298, 11.04292], [-0.42391, 11.11661], [-0.38219, 11.12596], [-0.35955, 11.07801], [-0.28566, 11.12713], [-0.27374, 11.17157], [-0.13493, 11.14075], [0.50388, 11.01011], [0.48852, 10.98561], [0.50521, 10.98035], [0.4958, 10.93269], [0.66104, 10.99964], [0.91245, 10.99597], [0.9813, 11.08876], [1.03409, 11.04719], [1.42823, 11.46822], [2.00988, 11.42227], [2.29983, 11.68254], [2.39723, 11.89473], [2.05785, 12.35539], [2.26349, 12.41915], [0.99167, 13.10727], [0.99253, 13.37515], [1.18873, 13.31771], [1.21217, 13.37853], [1.24516, 13.33968], [1.28509, 13.35488], [1.24429, 13.39373], [1.20088, 13.38951], [1.02813, 13.46635], [0.99514, 13.5668], [0.77637, 13.64442], [0.77377, 13.6866], [0.61924, 13.68491], [0.38051, 14.05575], [0.16936, 14.51654], [0.23859, 15.00135]]]]
23311           }
23312         }, {
23313           type: "Feature",
23314           properties: {
23315             iso1A2: "BG",
23316             iso1A3: "BGR",
23317             iso1N3: "100",
23318             wikidata: "Q219",
23319             nameEn: "Bulgaria",
23320             groups: ["EU", "151", "150", "UN"],
23321             callingCodes: ["359"]
23322           },
23323           geometry: {
23324             type: "MultiPolygon",
23325             coordinates: [[[[23.05288, 43.79494], [22.85314, 43.84452], [22.83753, 43.88055], [22.87873, 43.9844], [23.01674, 44.01946], [23.04988, 44.07694], [22.67173, 44.21564], [22.61711, 44.16938], [22.61688, 44.06534], [22.41449, 44.00514], [22.35558, 43.81281], [22.41043, 43.69566], [22.47582, 43.6558], [22.53397, 43.47225], [22.82036, 43.33665], [22.89727, 43.22417], [23.00806, 43.19279], [22.98104, 43.11199], [22.89521, 43.03625], [22.78397, 42.98253], [22.74826, 42.88701], [22.54302, 42.87774], [22.43309, 42.82057], [22.4997, 42.74144], [22.43983, 42.56851], [22.55669, 42.50144], [22.51961, 42.3991], [22.47498, 42.3915], [22.45919, 42.33822], [22.34773, 42.31725], [22.38136, 42.30339], [22.47251, 42.20393], [22.50289, 42.19527], [22.51224, 42.15457], [22.67701, 42.06614], [22.86749, 42.02275], [22.90254, 41.87587], [22.96682, 41.77137], [23.01239, 41.76527], [23.03342, 41.71034], [22.95513, 41.63265], [22.96331, 41.35782], [22.93334, 41.34104], [23.1833, 41.31755], [23.21953, 41.33773], [23.22771, 41.37106], [23.31301, 41.40525], [23.33639, 41.36317], [23.40416, 41.39999], [23.52453, 41.40262], [23.63203, 41.37632], [23.67644, 41.41139], [23.76525, 41.40175], [23.80148, 41.43943], [23.89613, 41.45257], [23.91483, 41.47971], [23.96975, 41.44118], [24.06908, 41.46132], [24.06323, 41.53222], [24.10063, 41.54796], [24.18126, 41.51735], [24.27124, 41.57682], [24.30513, 41.51297], [24.52599, 41.56808], [24.61129, 41.42278], [24.71529, 41.41928], [24.8041, 41.34913], [24.82514, 41.4035], [24.86136, 41.39298], [24.90928, 41.40876], [24.942, 41.38685], [25.11611, 41.34212], [25.28322, 41.23411], [25.48187, 41.28506], [25.52394, 41.2798], [25.55082, 41.31667], [25.61042, 41.30614], [25.66183, 41.31316], [25.70507, 41.29209], [25.8266, 41.34563], [25.87919, 41.30526], [26.12926, 41.35878], [26.16548, 41.42278], [26.20288, 41.43943], [26.14796, 41.47533], [26.176, 41.50072], [26.17951, 41.55409], [26.14328, 41.55496], [26.15146, 41.60828], [26.07083, 41.64584], [26.06148, 41.70345], [26.16841, 41.74858], [26.21325, 41.73223], [26.22888, 41.74139], [26.2654, 41.71544], [26.30255, 41.70925], [26.35957, 41.71149], [26.32952, 41.73637], [26.33589, 41.76802], [26.36952, 41.82265], [26.53968, 41.82653], [26.57961, 41.90024], [26.56051, 41.92995], [26.62996, 41.97644], [26.79143, 41.97386], [26.95638, 42.00741], [27.03277, 42.0809], [27.08486, 42.08735], [27.19251, 42.06028], [27.22376, 42.10152], [27.27411, 42.10409], [27.45478, 41.96591], [27.52379, 41.93756], [27.55191, 41.90928], [27.69949, 41.97515], [27.81235, 41.94803], [27.83492, 41.99709], [27.91479, 41.97902], [28.02971, 41.98066], [28.32297, 41.98371], [29.24336, 43.70874], [28.23293, 43.76], [27.99558, 43.84193], [27.92008, 44.00761], [27.73468, 43.95326], [27.64542, 44.04958], [27.60834, 44.01206], [27.39757, 44.0141], [27.26845, 44.12602], [26.95141, 44.13555], [26.62712, 44.05698], [26.38764, 44.04356], [26.10115, 43.96908], [26.05584, 43.90925], [25.94911, 43.85745], [25.72792, 43.69263], [25.39528, 43.61866], [25.17144, 43.70261], [25.10718, 43.6831], [24.96682, 43.72693], [24.73542, 43.68523], [24.62281, 43.74082], [24.50264, 43.76314], [24.35364, 43.70211], [24.18149, 43.68218], [23.73978, 43.80627], [23.61687, 43.79289], [23.4507, 43.84936], [23.26772, 43.84843], [23.05288, 43.79494]]]]
23326           }
23327         }, {
23328           type: "Feature",
23329           properties: {
23330             iso1A2: "BH",
23331             iso1A3: "BHR",
23332             iso1N3: "048",
23333             wikidata: "Q398",
23334             nameEn: "Bahrain",
23335             groups: ["145", "142", "UN"],
23336             callingCodes: ["973"]
23337           },
23338           geometry: {
23339             type: "MultiPolygon",
23340             coordinates: [[[[50.93865, 26.30758], [50.71771, 26.73086], [50.38162, 26.53976], [50.26923, 26.08243], [50.302, 25.87592], [50.57069, 25.57887], [50.80824, 25.54641], [50.7801, 25.595], [50.86149, 25.6965], [50.81266, 25.88946], [50.93865, 26.30758]]]]
23341           }
23342         }, {
23343           type: "Feature",
23344           properties: {
23345             iso1A2: "BI",
23346             iso1A3: "BDI",
23347             iso1N3: "108",
23348             wikidata: "Q967",
23349             nameEn: "Burundi",
23350             groups: ["014", "202", "002", "UN"],
23351             callingCodes: ["257"]
23352           },
23353           geometry: {
23354             type: "MultiPolygon",
23355             coordinates: [[[[30.54501, -2.41404], [30.42933, -2.31064], [30.14034, -2.43626], [29.95911, -2.33348], [29.88237, -2.75105], [29.36805, -2.82933], [29.32234, -2.6483], [29.0562, -2.58632], [29.04081, -2.7416], [29.00167, -2.78523], [29.00404, -2.81978], [29.0505, -2.81774], [29.09119, -2.87871], [29.09797, -2.91935], [29.16037, -2.95457], [29.17258, -2.99385], [29.25633, -3.05471], [29.21463, -3.3514], [29.23708, -3.75856], [29.43673, -4.44845], [29.63827, -4.44681], [29.75109, -4.45836], [29.77289, -4.41733], [29.82885, -4.36153], [29.88172, -4.35743], [30.03323, -4.26631], [30.22042, -4.01738], [30.45915, -3.56532], [30.84165, -3.25152], [30.83823, -2.97837], [30.6675, -2.98987], [30.57926, -2.89791], [30.4987, -2.9573], [30.40662, -2.86151], [30.52747, -2.65841], [30.41789, -2.66266], [30.54501, -2.41404]]]]
23356           }
23357         }, {
23358           type: "Feature",
23359           properties: {
23360             iso1A2: "BJ",
23361             iso1A3: "BEN",
23362             iso1N3: "204",
23363             wikidata: "Q962",
23364             nameEn: "Benin",
23365             aliases: ["DY"],
23366             groups: ["011", "202", "002", "UN"],
23367             callingCodes: ["229"]
23368           },
23369           geometry: {
23370             type: "MultiPolygon",
23371             coordinates: [[[[3.59375, 11.70269], [3.48187, 11.86092], [3.31613, 11.88495], [3.25352, 12.01467], [2.83978, 12.40585], [2.6593, 12.30631], [2.37783, 12.24804], [2.39657, 12.10952], [2.45824, 11.98672], [2.39723, 11.89473], [2.29983, 11.68254], [2.00988, 11.42227], [1.42823, 11.46822], [1.03409, 11.04719], [0.9813, 11.08876], [0.91245, 10.99597], [0.8804, 10.803], [0.80358, 10.71459], [0.77666, 10.37665], [1.35507, 9.99525], [1.36624, 9.5951], [1.33675, 9.54765], [1.41746, 9.3226], [1.5649, 9.16941], [1.61838, 9.0527], [1.64249, 6.99562], [1.55877, 6.99737], [1.61812, 6.74843], [1.58105, 6.68619], [1.76906, 6.43189], [1.79826, 6.28221], [1.62913, 6.24075], [1.67336, 6.02702], [2.74181, 6.13349], [2.70566, 6.38038], [2.70464, 6.50831], [2.74334, 6.57291], [2.7325, 6.64057], [2.78204, 6.70514], [2.78823, 6.76356], [2.73405, 6.78508], [2.74024, 6.92802], [2.71702, 6.95722], [2.76965, 7.13543], [2.74489, 7.42565], [2.79442, 7.43486], [2.78668, 7.5116], [2.73405, 7.5423], [2.73095, 7.7755], [2.67523, 7.87825], [2.77907, 9.06924], [3.08017, 9.10006], [3.14147, 9.28375], [3.13928, 9.47167], [3.25093, 9.61632], [3.34726, 9.70696], [3.32099, 9.78032], [3.35383, 9.83641], [3.54429, 9.87739], [3.66908, 10.18136], [3.57275, 10.27185], [3.6844, 10.46351], [3.78292, 10.40538], [3.84243, 10.59316], [3.71505, 11.13015], [3.49175, 11.29765], [3.59375, 11.70269]]]]
23372           }
23373         }, {
23374           type: "Feature",
23375           properties: {
23376             iso1A2: "BL",
23377             iso1A3: "BLM",
23378             iso1N3: "652",
23379             wikidata: "Q25362",
23380             nameEn: "Saint-Barth\xE9lemy",
23381             country: "FR",
23382             groups: ["Q1451600", "029", "003", "419", "019", "UN"],
23383             callingCodes: ["590"]
23384           },
23385           geometry: {
23386             type: "MultiPolygon",
23387             coordinates: [[[[-62.62718, 18.26185], [-63.1055, 17.86651], [-62.34423, 17.49165], [-62.62718, 18.26185]]]]
23388           }
23389         }, {
23390           type: "Feature",
23391           properties: {
23392             iso1A2: "BM",
23393             iso1A3: "BMU",
23394             iso1N3: "060",
23395             wikidata: "Q23635",
23396             nameEn: "Bermuda",
23397             country: "GB",
23398             groups: ["BOTS", "021", "003", "019", "UN"],
23399             driveSide: "left",
23400             callingCodes: ["1 441"]
23401           },
23402           geometry: {
23403             type: "MultiPolygon",
23404             coordinates: [[[[-63.20987, 32.6953], [-65.31453, 32.68437], [-65.63955, 31.43417], [-63.20987, 32.6953]]]]
23405           }
23406         }, {
23407           type: "Feature",
23408           properties: {
23409             iso1A2: "BN",
23410             iso1A3: "BRN",
23411             iso1N3: "096",
23412             wikidata: "Q921",
23413             nameEn: "Brunei",
23414             groups: ["Q36117", "035", "142", "UN"],
23415             driveSide: "left",
23416             callingCodes: ["673"]
23417           },
23418           geometry: {
23419             type: "MultiPolygon",
23420             coordinates: [[[[115.16236, 5.01011], [115.02521, 5.35005], [114.10166, 4.76112], [114.07448, 4.58441], [114.15813, 4.57], [114.26876, 4.49878], [114.32176, 4.34942], [114.32176, 4.2552], [114.4416, 4.27588], [114.49922, 4.13108], [114.64211, 4.00694], [114.78539, 4.12205], [114.88039, 4.4257], [114.83189, 4.42387], [114.77303, 4.72871], [114.8266, 4.75062], [114.88841, 4.81905], [114.96982, 4.81146], [114.99417, 4.88201], [115.05038, 4.90275], [115.02955, 4.82087], [115.02278, 4.74137], [115.04064, 4.63706], [115.07737, 4.53418], [115.09978, 4.39123], [115.31275, 4.30806], [115.36346, 4.33563], [115.2851, 4.42295], [115.27819, 4.63661], [115.20737, 4.8256], [115.15092, 4.87604], [115.16236, 5.01011]]]]
23421           }
23422         }, {
23423           type: "Feature",
23424           properties: {
23425             iso1A2: "BO",
23426             iso1A3: "BOL",
23427             iso1N3: "068",
23428             wikidata: "Q750",
23429             nameEn: "Bolivia",
23430             groups: ["005", "419", "019", "UN"],
23431             callingCodes: ["591"]
23432           },
23433           geometry: {
23434             type: "MultiPolygon",
23435             coordinates: [[[[-63.90248, -12.52544], [-64.22539, -12.45267], [-64.30708, -12.46398], [-64.99778, -11.98604], [-65.30027, -11.48749], [-65.28141, -10.86289], [-65.35402, -10.78685], [-65.37923, -10.35141], [-65.29019, -9.86253], [-65.40615, -9.63894], [-65.56244, -9.84266], [-65.68343, -9.75323], [-67.17784, -10.34016], [-68.71533, -11.14749], [-68.7651, -11.0496], [-68.75179, -11.03688], [-68.75265, -11.02383], [-68.74802, -11.00891], [-69.42792, -10.93451], [-69.47839, -10.95254], [-69.57156, -10.94555], [-68.98115, -11.8979], [-68.65044, -12.50689], [-68.85615, -12.87769], [-68.8864, -13.40792], [-69.05265, -13.68546], [-68.88135, -14.18639], [-69.36254, -14.94634], [-69.14856, -15.23478], [-69.40336, -15.61358], [-69.20291, -16.16668], [-69.09986, -16.22693], [-68.96238, -16.194], [-68.79464, -16.33272], [-68.98358, -16.42165], [-69.04027, -16.57214], [-69.00853, -16.66769], [-69.16896, -16.72233], [-69.62883, -17.28142], [-69.46863, -17.37466], [-69.46897, -17.4988], [-69.46623, -17.60518], [-69.34126, -17.72753], [-69.28671, -17.94844], [-69.07496, -18.03715], [-69.14807, -18.16893], [-69.07432, -18.28259], [-68.94987, -18.93302], [-68.87082, -19.06003], [-68.80602, -19.08355], [-68.61989, -19.27584], [-68.41218, -19.40499], [-68.66761, -19.72118], [-68.54611, -19.84651], [-68.57132, -20.03134], [-68.74273, -20.08817], [-68.7276, -20.46178], [-68.44023, -20.62701], [-68.55383, -20.7355], [-68.53957, -20.91542], [-68.40403, -20.94562], [-68.18816, -21.28614], [-67.85114, -22.87076], [-67.54284, -22.89771], [-67.18382, -22.81525], [-66.7298, -22.23644], [-66.29714, -22.08741], [-66.24077, -21.77837], [-66.03836, -21.84829], [-66.04832, -21.9187], [-65.9261, -21.93335], [-65.7467, -22.10105], [-65.61166, -22.09504], [-65.58694, -22.09794], [-65.57743, -22.07675], [-65.47435, -22.08908], [-64.99524, -22.08255], [-64.90014, -22.12136], [-64.67174, -22.18957], [-64.58888, -22.25035], [-64.4176, -22.67692], [-64.35108, -22.73282], [-64.31489, -22.88824], [-64.22918, -22.55807], [-63.93287, -21.99934], [-63.70963, -21.99934], [-63.68113, -22.0544], [-63.66482, -21.99918], [-62.81124, -21.9987], [-62.8078, -22.12534], [-62.64455, -22.25091], [-62.2757, -21.06657], [-62.26883, -20.55311], [-61.93912, -20.10053], [-61.73723, -19.63958], [-60.00638, -19.2981], [-59.06965, -19.29148], [-58.23216, -19.80058], [-58.16225, -20.16193], [-57.8496, -19.98346], [-58.14215, -19.76276], [-57.78463, -19.03259], [-57.71113, -19.03161], [-57.69134, -19.00544], [-57.71995, -18.97546], [-57.71995, -18.89573], [-57.76764, -18.90087], [-57.56807, -18.25655], [-57.48237, -18.24219], [-57.69877, -17.8431], [-57.73949, -17.56095], [-57.90082, -17.44555], [-57.99661, -17.5273], [-58.32935, -17.28195], [-58.5058, -16.80958], [-58.30918, -16.3699], [-58.32431, -16.25861], [-58.41506, -16.32636], [-60.16069, -16.26479], [-60.23797, -15.50267], [-60.58224, -15.09887], [-60.23968, -15.09515], [-60.27887, -14.63021], [-60.46037, -14.22496], [-60.48053, -13.77981], [-61.05527, -13.50054], [-61.81151, -13.49564], [-63.76259, -12.42952], [-63.90248, -12.52544]]]]
23436           }
23437         }, {
23438           type: "Feature",
23439           properties: {
23440             iso1A2: "BQ",
23441             iso1A3: "BES",
23442             iso1N3: "535",
23443             wikidata: "Q27561",
23444             nameEn: "Caribbean Netherlands",
23445             country: "NL"
23446           },
23447           geometry: null
23448         }, {
23449           type: "Feature",
23450           properties: {
23451             iso1A2: "BR",
23452             iso1A3: "BRA",
23453             iso1N3: "076",
23454             wikidata: "Q155",
23455             nameEn: "Brazil",
23456             groups: ["005", "419", "019", "UN"],
23457             callingCodes: ["55"]
23458           },
23459           geometry: {
23460             type: "MultiPolygon",
23461             coordinates: [[[[-59.69361, 4.34069], [-59.78878, 4.45637], [-60.15953, 4.53456], [-60.04189, 4.69801], [-59.98129, 5.07097], [-60.20944, 5.28754], [-60.32352, 5.21299], [-60.73204, 5.20931], [-60.5802, 4.94312], [-60.86539, 4.70512], [-60.98303, 4.54167], [-61.15703, 4.49839], [-61.31457, 4.54167], [-61.29675, 4.44216], [-61.48569, 4.43149], [-61.54629, 4.2822], [-62.13094, 4.08309], [-62.44822, 4.18621], [-62.57656, 4.04754], [-62.74411, 4.03331], [-62.7655, 3.73099], [-62.98296, 3.59935], [-63.21111, 3.96219], [-63.4464, 3.9693], [-63.42233, 3.89995], [-63.50611, 3.83592], [-63.67099, 4.01731], [-63.70218, 3.91417], [-63.86082, 3.94796], [-63.99183, 3.90172], [-64.14512, 4.12932], [-64.57648, 4.12576], [-64.72977, 4.28931], [-64.84028, 4.24665], [-64.48379, 3.7879], [-64.02908, 2.79797], [-64.0257, 2.48156], [-63.39114, 2.4317], [-63.39827, 2.16098], [-64.06135, 1.94722], [-64.08274, 1.64792], [-64.34654, 1.35569], [-64.38932, 1.5125], [-65.11657, 1.12046], [-65.57288, 0.62856], [-65.50158, 0.92086], [-65.6727, 1.01353], [-66.28507, 0.74585], [-66.85795, 1.22998], [-67.08222, 1.17441], [-67.15784, 1.80439], [-67.299, 1.87494], [-67.40488, 2.22258], [-67.9292, 1.82455], [-68.18632, 2.00091], [-68.26699, 1.83463], [-68.18128, 1.72881], [-69.38621, 1.70865], [-69.53746, 1.76408], [-69.83491, 1.69353], [-69.82987, 1.07864], [-69.26017, 1.06856], [-69.14422, 0.84172], [-69.20976, 0.57958], [-69.47696, 0.71065], [-70.04162, 0.55437], [-70.03658, -0.19681], [-69.603, -0.51947], [-69.59796, -0.75136], [-69.4215, -1.01853], [-69.43395, -1.42219], [-69.94708, -4.2431], [-70.00888, -4.37833], [-70.11305, -4.27281], [-70.19582, -4.3607], [-70.33236, -4.15214], [-70.77601, -4.15717], [-70.96814, -4.36915], [-71.87003, -4.51661], [-72.64391, -5.0391], [-72.83973, -5.14765], [-73.24579, -6.05764], [-73.12983, -6.43852], [-73.73986, -6.87919], [-73.77011, -7.28944], [-73.96938, -7.58465], [-73.65485, -7.77897], [-73.76576, -7.89884], [-72.92886, -9.04074], [-73.21498, -9.40904], [-72.72216, -9.41397], [-72.31883, -9.5184], [-72.14742, -9.98049], [-71.23394, -9.9668], [-70.53373, -9.42628], [-70.58453, -9.58303], [-70.55429, -9.76692], [-70.62487, -9.80666], [-70.64134, -11.0108], [-70.51395, -10.92249], [-70.38791, -11.07096], [-69.90896, -10.92744], [-69.57835, -10.94051], [-69.57156, -10.94555], [-69.47839, -10.95254], [-69.42792, -10.93451], [-68.74802, -11.00891], [-68.75265, -11.02383], [-68.75179, -11.03688], [-68.7651, -11.0496], [-68.71533, -11.14749], [-67.17784, -10.34016], [-65.68343, -9.75323], [-65.56244, -9.84266], [-65.40615, -9.63894], [-65.29019, -9.86253], [-65.37923, -10.35141], [-65.35402, -10.78685], [-65.28141, -10.86289], [-65.30027, -11.48749], [-64.99778, -11.98604], [-64.30708, -12.46398], [-64.22539, -12.45267], [-63.90248, -12.52544], [-63.76259, -12.42952], [-61.81151, -13.49564], [-61.05527, -13.50054], [-60.48053, -13.77981], [-60.46037, -14.22496], [-60.27887, -14.63021], [-60.23968, -15.09515], [-60.58224, -15.09887], [-60.23797, -15.50267], [-60.16069, -16.26479], [-58.41506, -16.32636], [-58.32431, -16.25861], [-58.30918, -16.3699], [-58.5058, -16.80958], [-58.32935, -17.28195], [-57.99661, -17.5273], [-57.90082, -17.44555], [-57.73949, -17.56095], [-57.69877, -17.8431], [-57.48237, -18.24219], [-57.56807, -18.25655], [-57.76764, -18.90087], [-57.71995, -18.89573], [-57.71995, -18.97546], [-57.69134, -19.00544], [-57.71113, -19.03161], [-57.78463, -19.03259], [-58.14215, -19.76276], [-57.8496, -19.98346], [-58.16225, -20.16193], [-57.84536, -20.93155], [-57.93492, -21.65505], [-57.88239, -21.6868], [-57.94642, -21.73799], [-57.98625, -22.09157], [-56.6508, -22.28387], [-56.5212, -22.11556], [-56.45893, -22.08072], [-56.23206, -22.25347], [-55.8331, -22.29008], [-55.74941, -22.46436], [-55.741, -22.52018], [-55.72366, -22.5519], [-55.6986, -22.56268], [-55.68742, -22.58407], [-55.62493, -22.62765], [-55.63849, -22.95122], [-55.5446, -23.22811], [-55.52288, -23.2595], [-55.5555, -23.28237], [-55.43585, -23.87157], [-55.44117, -23.9185], [-55.41784, -23.9657], [-55.12292, -23.99669], [-55.0518, -23.98666], [-55.02691, -23.97317], [-54.6238, -23.83078], [-54.32807, -24.01865], [-54.28207, -24.07305], [-54.4423, -25.13381], [-54.62033, -25.46026], [-54.60196, -25.48397], [-54.59509, -25.53696], [-54.59398, -25.59224], [-54.5502, -25.58915], [-54.52926, -25.62846], [-53.90831, -25.55513], [-53.83691, -25.94849], [-53.73511, -26.04211], [-53.73086, -26.05842], [-53.7264, -26.0664], [-53.73391, -26.07006], [-53.73968, -26.10012], [-53.65018, -26.19501], [-53.65237, -26.23289], [-53.63739, -26.2496], [-53.63881, -26.25075], [-53.64632, -26.24798], [-53.64186, -26.25976], [-53.64505, -26.28089], [-53.68269, -26.33359], [-53.73372, -26.6131], [-53.80144, -27.09844], [-54.15978, -27.2889], [-54.19062, -27.27639], [-54.19268, -27.30751], [-54.41888, -27.40882], [-54.50416, -27.48232], [-54.67657, -27.57214], [-54.90159, -27.63132], [-54.90805, -27.73149], [-55.1349, -27.89759], [-55.16872, -27.86224], [-55.33303, -27.94661], [-55.6262, -28.17124], [-55.65418, -28.18304], [-56.01729, -28.51223], [-56.00458, -28.60421], [-56.05265, -28.62651], [-56.54171, -29.11447], [-56.57295, -29.11357], [-56.62789, -29.18073], [-56.81251, -29.48154], [-57.09386, -29.74211], [-57.65132, -30.19229], [-57.22502, -30.26121], [-56.90236, -30.02578], [-56.49267, -30.39471], [-56.4795, -30.3899], [-56.4619, -30.38457], [-55.87388, -31.05053], [-55.58866, -30.84117], [-55.5634, -30.8686], [-55.55373, -30.8732], [-55.55218, -30.88193], [-55.54572, -30.89051], [-55.53431, -30.89714], [-55.53276, -30.90218], [-55.52712, -30.89997], [-55.51862, -30.89828], [-55.50841, -30.9027], [-55.50821, -30.91349], [-54.17384, -31.86168], [-53.76024, -32.0751], [-53.39572, -32.58596], [-53.37671, -32.57005], [-53.1111, -32.71147], [-53.53459, -33.16843], [-53.52794, -33.68908], [-53.44031, -33.69344], [-53.39593, -33.75169], [-53.37138, -33.74313], [-52.83257, -34.01481], [-28.34015, -20.99094], [-28.99601, 1.86593], [-51.35485, 4.8383], [-51.63798, 4.51394], [-51.61983, 4.14596], [-51.79599, 3.89336], [-51.82312, 3.85825], [-51.85573, 3.83427], [-52.31787, 3.17896], [-52.6906, 2.37298], [-52.96539, 2.1881], [-53.78743, 2.34412], [-54.16286, 2.10779], [-54.6084, 2.32856], [-55.01919, 2.564], [-55.71493, 2.40342], [-55.96292, 2.53188], [-56.13054, 2.27723], [-55.92159, 2.05236], [-55.89863, 1.89861], [-55.99278, 1.83137], [-56.47045, 1.95135], [-56.7659, 1.89509], [-57.07092, 1.95304], [-57.09109, 2.01854], [-57.23981, 1.95808], [-57.35073, 1.98327], [-57.55743, 1.69605], [-57.77281, 1.73344], [-57.97336, 1.64566], [-58.01873, 1.51966], [-58.33887, 1.58014], [-58.4858, 1.48399], [-58.53571, 1.29154], [-58.84229, 1.17749], [-58.92072, 1.31293], [-59.25583, 1.40559], [-59.74066, 1.87596], [-59.7264, 2.27497], [-59.91177, 2.36759], [-59.99733, 2.92312], [-59.79769, 3.37162], [-59.86899, 3.57089], [-59.51963, 3.91951], [-59.73353, 4.20399], [-59.69361, 4.34069]]]]
23462           }
23463         }, {
23464           type: "Feature",
23465           properties: {
23466             iso1A2: "BS",
23467             iso1A3: "BHS",
23468             iso1N3: "044",
23469             wikidata: "Q778",
23470             nameEn: "The Bahamas",
23471             groups: ["029", "003", "419", "019", "UN"],
23472             driveSide: "left",
23473             roadSpeedUnit: "mph",
23474             callingCodes: ["1 242"]
23475           },
23476           geometry: {
23477             type: "MultiPolygon",
23478             coordinates: [[[[-72.98446, 20.4801], [-71.70065, 25.7637], [-78.91214, 27.76553], [-80.65727, 23.71953], [-72.98446, 20.4801]]]]
23479           }
23480         }, {
23481           type: "Feature",
23482           properties: {
23483             iso1A2: "BT",
23484             iso1A3: "BTN",
23485             iso1N3: "064",
23486             wikidata: "Q917",
23487             nameEn: "Bhutan",
23488             groups: ["034", "142", "UN"],
23489             driveSide: "left",
23490             callingCodes: ["975"]
23491           },
23492           geometry: {
23493             type: "MultiPolygon",
23494             coordinates: [[[[91.6469, 27.76358], [91.5629, 27.84823], [91.48973, 27.93903], [91.46327, 28.0064], [91.25779, 28.07509], [91.20019, 27.98715], [90.69894, 28.07784], [90.58842, 28.02838], [90.13387, 28.19178], [89.79762, 28.23979], [89.59525, 28.16433], [89.12825, 27.62502], [89.0582, 27.60985], [88.97213, 27.51671], [88.95355, 27.4106], [89.00216, 27.32532], [88.96947, 27.30319], [88.93678, 27.33777], [88.91901, 27.32483], [88.74219, 27.144], [88.86984, 27.10937], [88.8714, 26.97488], [88.92301, 26.99286], [88.95807, 26.92668], [89.09554, 26.89089], [89.12825, 26.81661], [89.1926, 26.81329], [89.37913, 26.86224], [89.38319, 26.85963], [89.3901, 26.84225], [89.42349, 26.83727], [89.63369, 26.74402], [89.86124, 26.73307], [90.04535, 26.72422], [90.30402, 26.85098], [90.39271, 26.90704], [90.48504, 26.8594], [90.67715, 26.77215], [91.50067, 26.79223], [91.83181, 26.87318], [92.05523, 26.8692], [92.11863, 26.893], [92.03457, 27.07334], [92.04702, 27.26861], [92.12019, 27.27829], [92.01132, 27.47352], [91.65007, 27.48287], [91.55819, 27.6144], [91.6469, 27.76358]]]]
23495           }
23496         }, {
23497           type: "Feature",
23498           properties: {
23499             iso1A2: "BV",
23500             iso1A3: "BVT",
23501             iso1N3: "074",
23502             wikidata: "Q23408",
23503             nameEn: "Bouvet Island",
23504             country: "NO",
23505             groups: ["005", "419", "019", "UN"]
23506           },
23507           geometry: {
23508             type: "MultiPolygon",
23509             coordinates: [[[[4.54042, -54.0949], [2.28941, -54.13089], [3.35353, -55.17558], [4.54042, -54.0949]]]]
23510           }
23511         }, {
23512           type: "Feature",
23513           properties: {
23514             iso1A2: "BW",
23515             iso1A3: "BWA",
23516             iso1N3: "072",
23517             wikidata: "Q963",
23518             nameEn: "Botswana",
23519             groups: ["018", "202", "002", "UN"],
23520             driveSide: "left",
23521             callingCodes: ["267"]
23522           },
23523           geometry: {
23524             type: "MultiPolygon",
23525             coordinates: [[[[25.26433, -17.79571], [25.16882, -17.78253], [25.05895, -17.84452], [24.95586, -17.79674], [24.73364, -17.89338], [24.71887, -17.9218], [24.6303, -17.9863], [24.57485, -18.07151], [24.40577, -17.95726], [24.19416, -18.01919], [23.61088, -18.4881], [23.29618, -17.99855], [23.0996, -18.00075], [21.45556, -18.31795], [20.99904, -18.31743], [20.99751, -22.00026], [19.99912, -21.99991], [19.99817, -24.76768], [20.02809, -24.78725], [20.03678, -24.81004], [20.29826, -24.94869], [20.64795, -25.47827], [20.86081, -26.14892], [20.61754, -26.4692], [20.63275, -26.78181], [20.68596, -26.9039], [20.87031, -26.80047], [21.13353, -26.86661], [21.37869, -26.82083], [21.69322, -26.86152], [21.7854, -26.79199], [21.77114, -26.69015], [21.83291, -26.65959], [21.90703, -26.66808], [22.06192, -26.61882], [22.21206, -26.3773], [22.41921, -26.23078], [22.56365, -26.19668], [22.70808, -25.99186], [22.86012, -25.50572], [23.03497, -25.29971], [23.47588, -25.29971], [23.9244, -25.64286], [24.18287, -25.62916], [24.36531, -25.773], [24.44703, -25.73021], [24.67319, -25.81749], [24.8946, -25.80723], [25.01718, -25.72507], [25.12266, -25.75931], [25.33076, -25.76616], [25.58543, -25.6343], [25.6643, -25.4491], [25.69661, -25.29284], [25.72702, -25.25503], [25.88571, -24.87802], [25.84295, -24.78661], [25.8515, -24.75727], [26.39409, -24.63468], [26.46346, -24.60358], [26.51667, -24.47219], [26.84165, -24.24885], [26.99749, -23.65486], [27.33768, -23.40917], [27.52393, -23.37952], [27.6066, -23.21894], [27.74154, -23.2137], [27.93539, -23.04941], [27.93729, -22.96194], [28.04752, -22.90243], [28.04562, -22.8394], [28.34874, -22.5694], [28.63287, -22.55887], [28.91889, -22.44299], [29.0151, -22.22907], [29.10881, -22.21202], [29.15268, -22.21399], [29.18974, -22.18599], [29.21955, -22.17771], [29.37703, -22.19581], [29.3533, -22.18363], [29.24648, -22.05967], [29.1974, -22.07472], [29.14501, -22.07275], [29.08495, -22.04867], [29.04108, -22.00563], [29.02191, -21.95665], [29.02191, -21.90647], [29.04023, -21.85864], [29.07763, -21.81877], [28.58114, -21.63455], [28.49942, -21.66634], [28.29416, -21.59037], [28.01669, -21.57624], [27.91407, -21.31621], [27.69171, -21.08409], [27.72972, -20.51735], [27.69361, -20.48531], [27.28865, -20.49873], [27.29831, -20.28935], [27.21278, -20.08244], [26.72246, -19.92707], [26.17227, -19.53709], [25.96226, -19.08152], [25.99837, -19.02943], [25.94326, -18.90362], [25.82353, -18.82808], [25.79217, -18.6355], [25.68859, -18.56165], [25.53465, -18.39041], [25.39972, -18.12691], [25.31799, -18.07091], [25.23909, -17.90832], [25.26433, -17.79571]]]]
23526           }
23527         }, {
23528           type: "Feature",
23529           properties: {
23530             iso1A2: "BY",
23531             iso1A3: "BLR",
23532             iso1N3: "112",
23533             wikidata: "Q184",
23534             nameEn: "Belarus",
23535             groups: ["151", "150", "UN"],
23536             callingCodes: ["375"]
23537           },
23538           geometry: {
23539             type: "MultiPolygon",
23540             coordinates: [[[[28.15217, 56.16964], [27.97865, 56.11849], [27.63065, 55.89687], [27.61683, 55.78558], [27.3541, 55.8089], [27.27804, 55.78299], [27.1559, 55.85032], [26.97153, 55.8102], [26.87448, 55.7172], [26.76872, 55.67658], [26.71802, 55.70645], [26.64888, 55.70515], [26.63231, 55.67968], [26.63167, 55.57887], [26.55094, 55.5093], [26.5522, 55.40277], [26.44937, 55.34832], [26.5709, 55.32572], [26.6714, 55.33902], [26.80929, 55.31642], [26.83266, 55.30444], [26.835, 55.28182], [26.73017, 55.24226], [26.72983, 55.21788], [26.68075, 55.19787], [26.69243, 55.16718], [26.54753, 55.14181], [26.51481, 55.16051], [26.46249, 55.12814], [26.35121, 55.1525], [26.30628, 55.12536], [26.23202, 55.10439], [26.26941, 55.08032], [26.20397, 54.99729], [26.13386, 54.98924], [26.05907, 54.94631], [25.99129, 54.95705], [25.89462, 54.93438], [25.74122, 54.80108], [25.75977, 54.57252], [25.68045, 54.5321], [25.64813, 54.48704], [25.62203, 54.4656], [25.63371, 54.42075], [25.5376, 54.33158], [25.55425, 54.31591], [25.68513, 54.31727], [25.78553, 54.23327], [25.78563, 54.15747], [25.71084, 54.16704], [25.64875, 54.1259], [25.54724, 54.14925], [25.51452, 54.17799], [25.56823, 54.25212], [25.509, 54.30267], [25.35559, 54.26544], [25.22705, 54.26271], [25.19199, 54.219], [25.0728, 54.13419], [24.991, 54.14241], [24.96894, 54.17589], [24.77131, 54.11091], [24.85311, 54.02862], [24.74279, 53.96663], [24.69185, 53.96543], [24.69652, 54.01901], [24.62275, 54.00217], [24.44411, 53.90076], [24.34128, 53.90076], [24.19638, 53.96405], [23.98837, 53.92554], [23.95098, 53.9613], [23.81309, 53.94205], [23.80543, 53.89558], [23.71726, 53.93379], [23.61677, 53.92691], [23.51284, 53.95052], [23.62004, 53.60942], [23.81995, 53.24131], [23.85657, 53.22923], [23.91393, 53.16469], [23.87548, 53.0831], [23.92184, 53.02079], [23.94689, 52.95919], [23.91805, 52.94016], [23.93763, 52.71332], [23.73615, 52.6149], [23.58296, 52.59868], [23.45112, 52.53774], [23.34141, 52.44845], [23.18196, 52.28812], [23.20071, 52.22848], [23.47859, 52.18215], [23.54314, 52.12148], [23.61, 52.11264], [23.64066, 52.07626], [23.68733, 51.9906], [23.61523, 51.92066], [23.62691, 51.78208], [23.53198, 51.74298], [23.57053, 51.55938], [23.56236, 51.53673], [23.62751, 51.50512], [23.6736, 51.50255], [23.60906, 51.62122], [23.7766, 51.66809], [23.91118, 51.63316], [23.8741, 51.59734], [23.99907, 51.58369], [24.13075, 51.66979], [24.3163, 51.75063], [24.29021, 51.80841], [24.37123, 51.88222], [24.98784, 51.91273], [25.20228, 51.97143], [25.46163, 51.92205], [25.73673, 51.91973], [25.80574, 51.94556], [25.83217, 51.92587], [26.00408, 51.92967], [26.19084, 51.86781], [26.39367, 51.87315], [26.46962, 51.80501], [26.69759, 51.82284], [26.80043, 51.75777], [26.9489, 51.73788], [26.99422, 51.76933], [27.20602, 51.77291], [27.20948, 51.66713], [27.26613, 51.65957], [27.24828, 51.60161], [27.47212, 51.61184], [27.51058, 51.5854], [27.55727, 51.63486], [27.71932, 51.60672], [27.67125, 51.50854], [27.76052, 51.47604], [27.85253, 51.62293], [27.91844, 51.61952], [27.95827, 51.56065], [28.10658, 51.57857], [28.23452, 51.66988], [28.37592, 51.54505], [28.47051, 51.59734], [28.64429, 51.5664], [28.69161, 51.44695], [28.73143, 51.46236], [28.75615, 51.41442], [28.78224, 51.45294], [28.76027, 51.48802], [28.81795, 51.55552], [28.95528, 51.59222], [28.99098, 51.56833], [29.1187, 51.65872], [29.16402, 51.64679], [29.20659, 51.56918], [29.25603, 51.57089], [29.25191, 51.49828], [29.32881, 51.37843], [29.42357, 51.4187], [29.49773, 51.39814], [29.54372, 51.48372], [29.7408, 51.53417], [29.77376, 51.4461], [30.17888, 51.51025], [30.34642, 51.42555], [30.36153, 51.33984], [30.56203, 51.25655], [30.64992, 51.35014], [30.51946, 51.59649], [30.68804, 51.82806], [30.76443, 51.89739], [30.90897, 52.00699], [30.95589, 52.07775], [31.13332, 52.1004], [31.25142, 52.04131], [31.38326, 52.12991], [31.7822, 52.11406], [31.77877, 52.18636], [31.6895, 52.1973], [31.70735, 52.26711], [31.57971, 52.32146], [31.62084, 52.33849], [31.61397, 52.48843], [31.56316, 52.51518], [31.63869, 52.55361], [31.50406, 52.69707], [31.57277, 52.71613], [31.592, 52.79011], [31.35667, 52.97854], [31.24147, 53.031], [31.32283, 53.04101], [31.33519, 53.08805], [31.3915, 53.09712], [31.36403, 53.13504], [31.40523, 53.21406], [31.56316, 53.19432], [31.62496, 53.22886], [31.787, 53.18033], [31.82373, 53.10042], [32.15368, 53.07594], [32.40773, 53.18856], [32.51725, 53.28431], [32.73257, 53.33494], [32.74968, 53.45597], [32.47777, 53.5548], [32.40499, 53.6656], [32.50112, 53.68594], [32.45717, 53.74039], [32.36663, 53.7166], [32.12621, 53.81586], [31.89137, 53.78099], [31.77028, 53.80015], [31.85019, 53.91801], [31.88744, 54.03653], [31.89599, 54.0837], [31.57002, 54.14535], [31.30791, 54.25315], [31.3177, 54.34067], [31.22945, 54.46585], [31.08543, 54.50361], [31.21399, 54.63113], [31.19339, 54.66947], [30.99187, 54.67046], [30.98226, 54.68872], [31.0262, 54.70698], [30.97127, 54.71967], [30.95479, 54.74346], [30.75165, 54.80699], [30.8264, 54.90062], [30.81759, 54.94064], [30.93144, 54.9585], [30.95754, 54.98609], [30.9081, 55.02232], [30.94243, 55.03964], [31.00972, 55.02783], [31.02071, 55.06167], [30.97369, 55.17134], [30.87944, 55.28223], [30.81946, 55.27931], [30.8257, 55.3313], [30.93144, 55.3914], [30.90123, 55.46621], [30.95204, 55.50667], [30.93419, 55.6185], [30.86003, 55.63169], [30.7845, 55.58514], [30.72957, 55.66268], [30.67464, 55.64176], [30.63344, 55.73079], [30.51037, 55.76568], [30.51346, 55.78982], [30.48257, 55.81066], [30.30987, 55.83592], [30.27776, 55.86819], [30.12136, 55.8358], [29.97975, 55.87281], [29.80672, 55.79569], [29.61446, 55.77716], [29.51283, 55.70294], [29.3604, 55.75862], [29.44692, 55.95978], [29.21717, 55.98971], [29.08299, 56.03427], [28.73418, 55.97131], [28.63668, 56.07262], [28.68337, 56.10173], [28.5529, 56.11705], [28.43068, 56.09407], [28.37987, 56.11399], [28.36888, 56.05805], [28.30571, 56.06035], [28.15217, 56.16964]]]]
23541           }
23542         }, {
23543           type: "Feature",
23544           properties: {
23545             iso1A2: "BZ",
23546             iso1A3: "BLZ",
23547             iso1N3: "084",
23548             wikidata: "Q242",
23549             nameEn: "Belize",
23550             groups: ["013", "003", "419", "019", "UN"],
23551             roadSpeedUnit: "mph",
23552             callingCodes: ["501"]
23553           },
23554           geometry: {
23555             type: "MultiPolygon",
23556             coordinates: [[[[-88.3268, 18.49048], [-88.48242, 18.49164], [-88.71505, 18.0707], [-88.8716, 17.89535], [-89.03839, 18.0067], [-89.15105, 17.95104], [-89.14985, 17.81563], [-89.15025, 17.04813], [-89.22683, 15.88619], [-89.17418, 15.90898], [-89.02415, 15.9063], [-88.95358, 15.88698], [-88.40779, 16.09624], [-86.92368, 17.61462], [-87.84815, 18.18511], [-87.85693, 18.18266], [-87.86657, 18.19971], [-87.87604, 18.18313], [-87.90671, 18.15213], [-88.03165, 18.16657], [-88.03238, 18.41778], [-88.26593, 18.47617], [-88.29909, 18.47591], [-88.3268, 18.49048]]]]
23557           }
23558         }, {
23559           type: "Feature",
23560           properties: {
23561             iso1A2: "CA",
23562             iso1A3: "CAN",
23563             iso1N3: "124",
23564             wikidata: "Q16",
23565             nameEn: "Canada",
23566             groups: ["021", "003", "019", "UN"],
23567             callingCodes: ["1"]
23568           },
23569           geometry: {
23570             type: "MultiPolygon",
23571             coordinates: [[[[-67.20349, 45.1722], [-67.19603, 45.16771], [-67.15965, 45.16179], [-67.11316, 45.11176], [-67.0216, 44.95333], [-66.96824, 44.90965], [-66.98249, 44.87071], [-66.96824, 44.83078], [-66.93432, 44.82597], [-67.16117, 44.20069], [-61.98255, 37.34815], [-56.27503, 47.39728], [-53.12387, 41.40385], [-46.37635, 57.3249], [-77.52957, 77.23408], [-68.21821, 80.48551], [-49.33696, 84.57952], [-140.97446, 84.39275], [-141.00116, 60.30648], [-140.5227, 60.22077], [-140.45648, 60.30919], [-139.98024, 60.18027], [-139.68991, 60.33693], [-139.05831, 60.35205], [-139.20603, 60.08896], [-139.05365, 59.99655], [-138.71149, 59.90728], [-138.62145, 59.76431], [-137.60623, 59.24465], [-137.4925, 58.89415], [-136.82619, 59.16198], [-136.52365, 59.16752], [-136.47323, 59.46617], [-136.33727, 59.44466], [-136.22381, 59.55526], [-136.31566, 59.59083], [-135.48007, 59.79937], [-135.03069, 59.56208], [-135.00267, 59.28745], [-134.7047, 59.2458], [-134.55699, 59.1297], [-134.48059, 59.13231], [-134.27175, 58.8634], [-133.84645, 58.73543], [-133.38523, 58.42773], [-131.8271, 56.62247], [-130.77769, 56.36185], [-130.33965, 56.10849], [-130.10173, 56.12178], [-130.00093, 56.00325], [-130.00857, 55.91344], [-130.15373, 55.74895], [-129.97513, 55.28029], [-130.08035, 55.21556], [-130.18765, 55.07744], [-130.27203, 54.97174], [-130.44184, 54.85377], [-130.64499, 54.76912], [-130.61931, 54.70835], [-133.92876, 54.62289], [-133.36909, 48.51151], [-125.03842, 48.53282], [-123.50039, 48.21223], [-123.15614, 48.35395], [-123.26565, 48.6959], [-123.0093, 48.76586], [-123.0093, 48.83186], [-123.32163, 49.00419], [-95.15355, 48.9996], [-95.15357, 49.384], [-95.12903, 49.37056], [-95.05825, 49.35311], [-95.01419, 49.35647], [-94.99532, 49.36579], [-94.95681, 49.37035], [-94.85381, 49.32492], [-94.8159, 49.32299], [-94.82487, 49.29483], [-94.77355, 49.11998], [-94.75017, 49.09931], [-94.687, 48.84077], [-94.70087, 48.8339], [-94.70486, 48.82365], [-94.69669, 48.80918], [-94.69335, 48.77883], [-94.58903, 48.71803], [-94.54885, 48.71543], [-94.53826, 48.70216], [-94.44258, 48.69223], [-94.4174, 48.71049], [-94.27153, 48.70232], [-94.25172, 48.68404], [-94.25104, 48.65729], [-94.23215, 48.65202], [-93.85769, 48.63284], [-93.83288, 48.62745], [-93.80676, 48.58232], [-93.80939, 48.52439], [-93.79267, 48.51631], [-93.66382, 48.51845], [-93.47022, 48.54357], [-93.44472, 48.59147], [-93.40693, 48.60948], [-93.39758, 48.60364], [-93.3712, 48.60599], [-93.33946, 48.62787], [-93.25391, 48.64266], [-92.94973, 48.60866], [-92.7287, 48.54005], [-92.6342, 48.54133], [-92.62747, 48.50278], [-92.69927, 48.49573], [-92.71323, 48.46081], [-92.65606, 48.43471], [-92.50712, 48.44921], [-92.45588, 48.40624], [-92.48147, 48.36609], [-92.37185, 48.22259], [-92.27167, 48.25046], [-92.30939, 48.31251], [-92.26662, 48.35651], [-92.202, 48.35252], [-92.14732, 48.36578], [-92.05339, 48.35958], [-91.98929, 48.25409], [-91.86125, 48.21278], [-91.71231, 48.19875], [-91.70451, 48.11805], [-91.55649, 48.10611], [-91.58025, 48.04339], [-91.45829, 48.07454], [-91.43248, 48.04912], [-91.25025, 48.08522], [-91.08016, 48.18096], [-90.87588, 48.2484], [-90.75045, 48.09143], [-90.56444, 48.12184], [-90.56312, 48.09488], [-90.07418, 48.11043], [-89.89974, 47.98109], [-89.77248, 48.02607], [-89.57972, 48.00023], [-89.48837, 48.01412], [-88.37033, 48.30586], [-84.85871, 46.88881], [-84.55635, 46.45974], [-84.47607, 46.45225], [-84.4481, 46.48972], [-84.42101, 46.49853], [-84.34174, 46.50683], [-84.29893, 46.49127], [-84.26351, 46.49508], [-84.2264, 46.53337], [-84.1945, 46.54061], [-84.17723, 46.52753], [-84.12885, 46.53068], [-84.11196, 46.50248], [-84.13451, 46.39218], [-84.11254, 46.32329], [-84.11615, 46.2681], [-84.09756, 46.25512], [-84.1096, 46.23987], [-83.95399, 46.05634], [-83.90453, 46.05922], [-83.83329, 46.12169], [-83.57017, 46.105], [-83.43746, 45.99749], [-83.59589, 45.82131], [-82.48419, 45.30225], [-82.42469, 42.992], [-82.4146, 42.97626], [-82.4253, 42.95423], [-82.45331, 42.93139], [-82.4826, 42.8068], [-82.46613, 42.76615], [-82.51063, 42.66025], [-82.51858, 42.611], [-82.57583, 42.5718], [-82.58873, 42.54984], [-82.64242, 42.55594], [-82.82964, 42.37355], [-83.02253, 42.33045], [-83.07837, 42.30978], [-83.09837, 42.28877], [-83.12724, 42.2376], [-83.14962, 42.04089], [-83.11184, 41.95671], [-82.67862, 41.67615], [-78.93684, 42.82887], [-78.90712, 42.89733], [-78.90905, 42.93022], [-78.93224, 42.95229], [-78.96312, 42.95509], [-78.98126, 42.97], [-79.02074, 42.98444], [-79.02424, 43.01983], [-78.99941, 43.05612], [-79.01055, 43.06659], [-79.07486, 43.07845], [-79.05671, 43.10937], [-79.06881, 43.12029], [-79.0427, 43.13934], [-79.04652, 43.16396], [-79.05384, 43.17418], [-79.05002, 43.20133], [-79.05544, 43.21224], [-79.05512, 43.25375], [-79.06921, 43.26183], [-79.25796, 43.54052], [-76.79706, 43.63099], [-76.43859, 44.09393], [-76.35324, 44.13493], [-76.31222, 44.19894], [-76.244, 44.19643], [-76.1664, 44.23051], [-76.16285, 44.28262], [-76.00018, 44.34896], [-75.95947, 44.34463], [-75.8217, 44.43176], [-75.76813, 44.51537], [-75.41441, 44.76614], [-75.2193, 44.87821], [-75.01363, 44.95608], [-74.99101, 44.98051], [-74.8447, 45.00606], [-74.66689, 45.00646], [-74.32699, 44.99029], [-73.35025, 45.00942], [-71.50067, 45.01357], [-71.48735, 45.07784], [-71.42778, 45.12624], [-71.40364, 45.21382], [-71.44252, 45.2361], [-71.37133, 45.24624], [-71.29371, 45.29996], [-71.22338, 45.25184], [-71.19723, 45.25438], [-71.14568, 45.24128], [-71.08364, 45.30623], [-71.01866, 45.31573], [-71.0107, 45.34819], [-70.95193, 45.33895], [-70.91169, 45.29849], [-70.89864, 45.2398], [-70.84816, 45.22698], [-70.80236, 45.37444], [-70.82638, 45.39828], [-70.78372, 45.43269], [-70.65383, 45.37592], [-70.62518, 45.42286], [-70.72651, 45.49771], [-70.68516, 45.56964], [-70.54019, 45.67291], [-70.38934, 45.73215], [-70.41523, 45.79497], [-70.25976, 45.89675], [-70.24694, 45.95138], [-70.31025, 45.96424], [-70.23855, 46.1453], [-70.29078, 46.18832], [-70.18547, 46.35357], [-70.05812, 46.41768], [-69.99966, 46.69543], [-69.22119, 47.46461], [-69.05148, 47.42012], [-69.05073, 47.30076], [-69.05039, 47.2456], [-68.89222, 47.1807], [-68.70125, 47.24399], [-68.60575, 47.24659], [-68.57914, 47.28431], [-68.38332, 47.28723], [-68.37458, 47.35851], [-68.23244, 47.35712], [-67.94843, 47.1925], [-67.87993, 47.10377], [-67.78578, 47.06473], [-67.78111, 45.9392], [-67.75196, 45.91814], [-67.80961, 45.87531], [-67.75654, 45.82324], [-67.80653, 45.80022], [-67.80705, 45.69528], [-67.6049, 45.60725], [-67.43815, 45.59162], [-67.42144, 45.50584], [-67.50578, 45.48971], [-67.42394, 45.37969], [-67.48201, 45.27351], [-67.34927, 45.122], [-67.29754, 45.14865], [-67.29748, 45.18173], [-67.27039, 45.1934], [-67.22751, 45.16344], [-67.20349, 45.1722]]]]
23572           }
23573         }, {
23574           type: "Feature",
23575           properties: {
23576             iso1A2: "CC",
23577             iso1A3: "CCK",
23578             iso1N3: "166",
23579             wikidata: "Q36004",
23580             nameEn: "Cocos (Keeling) Islands",
23581             country: "AU",
23582             groups: ["053", "009", "UN"],
23583             driveSide: "left",
23584             callingCodes: ["61"]
23585           },
23586           geometry: {
23587             type: "MultiPolygon",
23588             coordinates: [[[[96.61846, -10.82438], [96.02343, -12.68334], [97.93979, -12.33309], [96.61846, -10.82438]]]]
23589           }
23590         }, {
23591           type: "Feature",
23592           properties: {
23593             iso1A2: "CD",
23594             iso1A3: "COD",
23595             iso1N3: "180",
23596             wikidata: "Q974",
23597             nameEn: "Democratic Republic of the Congo",
23598             aliases: ["ZR"],
23599             groups: ["017", "202", "002", "UN"],
23600             callingCodes: ["243"]
23601           },
23602           geometry: {
23603             type: "MultiPolygon",
23604             coordinates: [[[[27.44012, 5.07349], [27.09575, 5.22305], [26.93064, 5.13535], [26.85579, 5.03887], [26.74572, 5.10685], [26.48595, 5.04984], [26.13371, 5.25594], [25.86073, 5.19455], [25.53271, 5.37431], [25.34558, 5.29101], [25.31256, 5.03668], [24.71816, 4.90509], [24.46719, 5.0915], [23.38847, 4.60013], [22.94817, 4.82392], [22.89094, 4.79321], [22.84691, 4.69887], [22.78526, 4.71423], [22.6928, 4.47285], [22.60915, 4.48821], [22.5431, 4.22041], [22.45504, 4.13039], [22.27682, 4.11347], [22.10721, 4.20723], [21.6405, 4.317], [21.55904, 4.25553], [21.25744, 4.33676], [21.21341, 4.29285], [21.11214, 4.33895], [21.08793, 4.39603], [20.90383, 4.44877], [20.60184, 4.42394], [18.62755, 3.47564], [18.63857, 3.19342], [18.10683, 2.26876], [18.08034, 1.58553], [17.85887, 1.04327], [17.86989, 0.58873], [17.95255, 0.48128], [17.93877, 0.32424], [17.81204, 0.23884], [17.66051, -0.26535], [17.72112, -0.52707], [17.32438, -0.99265], [16.97999, -1.12762], [16.70724, -1.45815], [16.50336, -1.8795], [16.16173, -2.16586], [16.22785, -2.59528], [16.1755, -3.25014], [16.21407, -3.2969], [15.89448, -3.9513], [15.53081, -4.042], [15.48121, -4.22062], [15.41785, -4.28381], [15.32693, -4.27282], [15.25411, -4.31121], [15.1978, -4.32388], [14.83101, -4.80838], [14.67948, -4.92093], [14.5059, -4.84956], [14.41499, -4.8825], [14.37366, -4.56125], [14.47284, -4.42941], [14.3957, -4.36623], [14.40672, -4.28381], [13.9108, -4.50906], [13.81162, -4.41842], [13.71794, -4.44864], [13.70417, -4.72601], [13.50305, -4.77818], [13.41764, -4.89897], [13.11182, -4.5942], [13.09648, -4.63739], [13.11195, -4.67745], [12.8733, -4.74346], [12.70868, -4.95505], [12.63465, -4.94632], [12.60251, -5.01715], [12.46297, -5.09408], [12.49815, -5.14058], [12.51589, -5.1332], [12.53586, -5.14658], [12.53599, -5.1618], [12.52301, -5.17481], [12.52318, -5.74353], [12.26557, -5.74031], [12.20376, -5.76338], [11.95767, -5.94705], [12.42245, -6.07585], [13.04371, -5.87078], [16.55507, -5.85631], [16.96282, -7.21787], [17.5828, -8.13784], [18.33635, -8.00126], [19.33698, -7.99743], [19.5469, -7.00195], [20.30218, -6.98955], [20.31846, -6.91953], [20.61689, -6.90876], [20.56263, -7.28566], [21.79824, -7.29628], [21.84856, -9.59871], [22.19039, -9.94628], [22.32604, -10.76291], [22.17954, -10.85884], [22.25951, -11.24911], [22.54205, -11.05784], [23.16602, -11.10577], [23.45631, -10.946], [23.86868, -11.02856], [24.00027, -10.89356], [24.34528, -11.06816], [24.42612, -11.44975], [25.34069, -11.19707], [25.33058, -11.65767], [26.01777, -11.91488], [26.88687, -12.01868], [27.04351, -11.61312], [27.22541, -11.60323], [27.21025, -11.76157], [27.59932, -12.22123], [28.33199, -12.41375], [29.01918, -13.41353], [29.60531, -13.21685], [29.65078, -13.41844], [29.81551, -13.44683], [29.8139, -12.14898], [29.48404, -12.23604], [29.4992, -12.43843], [29.18592, -12.37921], [28.48357, -11.87532], [28.37241, -11.57848], [28.65032, -10.65133], [28.62795, -9.92942], [28.68532, -9.78], [28.56208, -9.49122], [28.51627, -9.44726], [28.52636, -9.35379], [28.36562, -9.30091], [28.38526, -9.23393], [28.9711, -8.66935], [28.88917, -8.4831], [30.79243, -8.27382], [30.2567, -7.14121], [29.52552, -6.2731], [29.43673, -4.44845], [29.23708, -3.75856], [29.21463, -3.3514], [29.25633, -3.05471], [29.17258, -2.99385], [29.16037, -2.95457], [29.09797, -2.91935], [29.09119, -2.87871], [29.0505, -2.81774], [29.00404, -2.81978], [29.00167, -2.78523], [29.04081, -2.7416], [29.00357, -2.70596], [28.94346, -2.69124], [28.89793, -2.66111], [28.90226, -2.62385], [28.89288, -2.55848], [28.87943, -2.55165], [28.86193, -2.53185], [28.86209, -2.5231], [28.87497, -2.50887], [28.88846, -2.50493], [28.89342, -2.49017], [28.89132, -2.47557], [28.86846, -2.44866], [28.86826, -2.41888], [28.89601, -2.37321], [28.95642, -2.37321], [29.00051, -2.29001], [29.105, -2.27043], [29.17562, -2.12278], [29.11847, -1.90576], [29.24458, -1.69663], [29.24323, -1.66826], [29.36322, -1.50887], [29.45038, -1.5054], [29.53062, -1.40499], [29.59061, -1.39016], [29.58388, -0.89821], [29.63006, -0.8997], [29.62708, -0.71055], [29.67176, -0.55714], [29.67474, -0.47969], [29.65091, -0.46777], [29.72687, -0.08051], [29.7224, 0.07291], [29.77454, 0.16675], [29.81922, 0.16824], [29.87284, 0.39166], [29.97413, 0.52124], [29.95477, 0.64486], [29.98307, 0.84295], [30.1484, 0.89805], [30.22139, 0.99635], [30.24671, 1.14974], [30.48503, 1.21675], [31.30127, 2.11006], [31.28042, 2.17853], [31.20148, 2.2217], [31.1985, 2.29462], [31.12104, 2.27676], [31.07934, 2.30207], [31.06593, 2.35862], [30.96911, 2.41071], [30.91102, 2.33332], [30.83059, 2.42559], [30.74271, 2.43601], [30.75612, 2.5863], [30.8857, 2.83923], [30.8574, 2.9508], [30.77101, 3.04897], [30.84251, 3.26908], [30.93486, 3.40737], [30.94081, 3.50847], [30.85153, 3.48867], [30.85997, 3.5743], [30.80713, 3.60506], [30.78512, 3.67097], [30.56277, 3.62703], [30.57378, 3.74567], [30.55396, 3.84451], [30.47691, 3.83353], [30.27658, 3.95653], [30.22374, 3.93896], [30.1621, 4.10586], [30.06964, 4.13221], [29.79666, 4.37809], [29.82087, 4.56246], [29.49726, 4.7007], [29.43341, 4.50101], [29.22207, 4.34297], [29.03054, 4.48784], [28.8126, 4.48784], [28.6651, 4.42638], [28.20719, 4.35614], [27.79551, 4.59976], [27.76469, 4.79284], [27.65462, 4.89375], [27.56656, 4.89375], [27.44012, 5.07349]]]]
23605           }
23606         }, {
23607           type: "Feature",
23608           properties: {
23609             iso1A2: "CF",
23610             iso1A3: "CAF",
23611             iso1N3: "140",
23612             wikidata: "Q929",
23613             nameEn: "Central African Republic",
23614             groups: ["017", "202", "002", "UN"],
23615             callingCodes: ["236"]
23616           },
23617           geometry: {
23618             type: "MultiPolygon",
23619             coordinates: [[[[22.87758, 10.91915], [22.45889, 11.00246], [21.72139, 10.64136], [21.71479, 10.29932], [21.63553, 10.217], [21.52766, 10.2105], [21.34934, 9.95907], [21.26348, 9.97642], [20.82979, 9.44696], [20.36748, 9.11019], [19.06421, 9.00367], [18.86388, 8.87971], [19.11044, 8.68172], [18.79783, 8.25929], [18.67455, 8.22226], [18.62612, 8.14163], [18.64153, 8.08714], [18.6085, 8.05009], [18.02731, 8.01085], [17.93926, 7.95853], [17.67288, 7.98905], [16.8143, 7.53971], [16.6668, 7.67281], [16.658, 7.75353], [16.59415, 7.76444], [16.58315, 7.88657], [16.41583, 7.77971], [16.40703, 7.68809], [15.79942, 7.44149], [15.73118, 7.52006], [15.49743, 7.52179], [15.23397, 7.25135], [15.04717, 6.77085], [14.96311, 6.75693], [14.79966, 6.39043], [14.80122, 6.34866], [14.74206, 6.26356], [14.56149, 6.18928], [14.43073, 6.08867], [14.42917, 6.00508], [14.49455, 5.91683], [14.60974, 5.91838], [14.62375, 5.70466], [14.58951, 5.59777], [14.62531, 5.51411], [14.52724, 5.28319], [14.57083, 5.23979], [14.65489, 5.21343], [14.73383, 4.6135], [15.00825, 4.41458], [15.08609, 4.30282], [15.10644, 4.1362], [15.17482, 4.05131], [15.07686, 4.01805], [15.73522, 3.24348], [15.77725, 3.26835], [16.05449, 3.02306], [16.08252, 2.45708], [16.19357, 2.21537], [16.50126, 2.84739], [16.46701, 2.92512], [16.57598, 3.47999], [16.68283, 3.54257], [17.01746, 3.55136], [17.35649, 3.63045], [17.46876, 3.70515], [17.60966, 3.63705], [17.83421, 3.61068], [17.85842, 3.53378], [18.05656, 3.56893], [18.14902, 3.54476], [18.17323, 3.47665], [18.24148, 3.50302], [18.2723, 3.57992], [18.39558, 3.58212], [18.49245, 3.63924], [18.58711, 3.49423], [18.62755, 3.47564], [20.60184, 4.42394], [20.90383, 4.44877], [21.08793, 4.39603], [21.11214, 4.33895], [21.21341, 4.29285], [21.25744, 4.33676], [21.55904, 4.25553], [21.6405, 4.317], [22.10721, 4.20723], [22.27682, 4.11347], [22.45504, 4.13039], [22.5431, 4.22041], [22.60915, 4.48821], [22.6928, 4.47285], [22.78526, 4.71423], [22.84691, 4.69887], [22.89094, 4.79321], [22.94817, 4.82392], [23.38847, 4.60013], [24.46719, 5.0915], [24.71816, 4.90509], [25.31256, 5.03668], [25.34558, 5.29101], [25.53271, 5.37431], [25.86073, 5.19455], [26.13371, 5.25594], [26.48595, 5.04984], [26.74572, 5.10685], [26.85579, 5.03887], [26.93064, 5.13535], [27.09575, 5.22305], [27.44012, 5.07349], [27.26886, 5.25876], [27.23017, 5.37167], [27.28621, 5.56382], [27.22705, 5.62889], [27.22705, 5.71254], [26.51721, 6.09655], [26.58259, 6.1987], [26.32729, 6.36272], [26.38022, 6.63493], [25.90076, 7.09549], [25.37461, 7.33024], [25.35281, 7.42595], [25.20337, 7.50312], [25.20649, 7.61115], [25.29214, 7.66675], [25.25319, 7.8487], [24.98855, 7.96588], [24.85156, 8.16933], [24.35965, 8.26177], [24.13238, 8.36959], [24.25691, 8.69288], [23.51905, 8.71749], [23.59065, 8.99743], [23.44744, 8.99128], [23.4848, 9.16959], [23.56263, 9.19418], [23.64358, 9.28637], [23.64981, 9.44303], [23.62179, 9.53823], [23.69155, 9.67566], [23.67164, 9.86923], [23.3128, 10.45214], [23.02221, 10.69235], [22.87758, 10.91915]]]]
23620           }
23621         }, {
23622           type: "Feature",
23623           properties: {
23624             iso1A2: "CG",
23625             iso1A3: "COG",
23626             iso1N3: "178",
23627             wikidata: "Q971",
23628             nameEn: "Republic of the Congo",
23629             groups: ["017", "202", "002", "UN"],
23630             callingCodes: ["242"]
23631           },
23632           geometry: {
23633             type: "MultiPolygon",
23634             coordinates: [[[[18.62755, 3.47564], [18.58711, 3.49423], [18.49245, 3.63924], [18.39558, 3.58212], [18.2723, 3.57992], [18.24148, 3.50302], [18.17323, 3.47665], [18.14902, 3.54476], [18.05656, 3.56893], [17.85842, 3.53378], [17.83421, 3.61068], [17.60966, 3.63705], [17.46876, 3.70515], [17.35649, 3.63045], [17.01746, 3.55136], [16.68283, 3.54257], [16.57598, 3.47999], [16.46701, 2.92512], [16.50126, 2.84739], [16.19357, 2.21537], [16.15568, 2.18955], [16.08563, 2.19733], [16.05294, 1.9811], [16.14634, 1.70259], [16.02647, 1.65591], [16.02959, 1.76483], [15.48942, 1.98265], [15.34776, 1.91264], [15.22634, 2.03243], [15.00996, 1.98887], [14.61145, 2.17866], [13.29457, 2.16106], [13.13461, 1.57238], [13.25447, 1.32339], [13.15519, 1.23368], [13.89582, 1.4261], [14.25186, 1.39842], [14.48179, 0.9152], [14.26066, 0.57255], [14.10909, 0.58563], [13.88648, 0.26652], [13.90632, -0.2287], [14.06862, -0.20826], [14.2165, -0.38261], [14.41887, -0.44799], [14.52569, -0.57818], [14.41838, -1.89412], [14.25932, -1.97624], [14.23518, -2.15671], [14.16202, -2.23916], [14.23829, -2.33715], [14.10442, -2.49268], [13.85846, -2.46935], [13.92073, -2.35581], [13.75884, -2.09293], [13.47977, -2.43224], [13.02759, -2.33098], [12.82172, -1.91091], [12.61312, -1.8129], [12.44656, -1.92025], [12.47925, -2.32626], [12.04895, -2.41704], [11.96866, -2.33559], [11.74605, -2.39936], [11.57637, -2.33379], [11.64487, -2.61865], [11.5359, -2.85654], [11.64798, -2.81146], [11.80365, -3.00424], [11.70558, -3.0773], [11.70227, -3.17465], [11.96554, -3.30267], [11.8318, -3.5812], [11.92719, -3.62768], [11.87083, -3.71571], [11.68608, -3.68942], [11.57949, -3.52798], [11.48764, -3.51089], [11.22301, -3.69888], [11.12647, -3.94169], [10.75913, -4.39519], [11.50888, -5.33417], [12.00924, -5.02627], [12.16068, -4.90089], [12.20901, -4.75642], [12.25587, -4.79437], [12.32324, -4.78415], [12.40964, -4.60609], [12.64835, -4.55937], [12.76844, -4.38709], [12.87096, -4.40315], [12.91489, -4.47907], [13.09648, -4.63739], [13.11182, -4.5942], [13.41764, -4.89897], [13.50305, -4.77818], [13.70417, -4.72601], [13.71794, -4.44864], [13.81162, -4.41842], [13.9108, -4.50906], [14.40672, -4.28381], [14.3957, -4.36623], [14.47284, -4.42941], [14.37366, -4.56125], [14.41499, -4.8825], [14.5059, -4.84956], [14.67948, -4.92093], [14.83101, -4.80838], [15.1978, -4.32388], [15.25411, -4.31121], [15.32693, -4.27282], [15.41785, -4.28381], [15.48121, -4.22062], [15.53081, -4.042], [15.89448, -3.9513], [16.21407, -3.2969], [16.1755, -3.25014], [16.22785, -2.59528], [16.16173, -2.16586], [16.50336, -1.8795], [16.70724, -1.45815], [16.97999, -1.12762], [17.32438, -0.99265], [17.72112, -0.52707], [17.66051, -0.26535], [17.81204, 0.23884], [17.93877, 0.32424], [17.95255, 0.48128], [17.86989, 0.58873], [17.85887, 1.04327], [18.08034, 1.58553], [18.10683, 2.26876], [18.63857, 3.19342], [18.62755, 3.47564]]]]
23635           }
23636         }, {
23637           type: "Feature",
23638           properties: {
23639             iso1A2: "CH",
23640             iso1A3: "CHE",
23641             iso1N3: "756",
23642             wikidata: "Q39",
23643             nameEn: "Switzerland",
23644             groups: ["155", "150", "UN"],
23645             callingCodes: ["41"]
23646           },
23647           geometry: {
23648             type: "MultiPolygon",
23649             coordinates: [[[[8.72809, 47.69282], [8.72617, 47.69651], [8.73671, 47.7169], [8.70543, 47.73121], [8.74251, 47.75168], [8.71778, 47.76571], [8.68985, 47.75686], [8.68022, 47.78599], [8.65292, 47.80066], [8.64425, 47.76398], [8.62408, 47.7626], [8.61657, 47.79998], [8.56415, 47.80633], [8.56814, 47.78001], [8.48868, 47.77215], [8.45771, 47.7493], [8.44807, 47.72426], [8.40569, 47.69855], [8.4211, 47.68407], [8.40473, 47.67499], [8.41346, 47.66676], [8.42264, 47.66667], [8.44711, 47.65379], [8.4667, 47.65747], [8.46605, 47.64103], [8.49656, 47.64709], [8.5322, 47.64687], [8.52801, 47.66059], [8.56141, 47.67088], [8.57683, 47.66158], [8.6052, 47.67258], [8.61113, 47.66332], [8.62884, 47.65098], [8.62049, 47.63757], [8.60412, 47.63735], [8.61471, 47.64514], [8.60701, 47.65271], [8.59545, 47.64298], [8.60348, 47.61204], [8.57586, 47.59537], [8.55756, 47.62394], [8.51686, 47.63476], [8.50747, 47.61897], [8.45578, 47.60121], [8.46637, 47.58389], [8.48949, 47.588], [8.49431, 47.58107], [8.43235, 47.56617], [8.39477, 47.57826], [8.38273, 47.56608], [8.32735, 47.57133], [8.30277, 47.58607], [8.29524, 47.5919], [8.29722, 47.60603], [8.2824, 47.61225], [8.26313, 47.6103], [8.25863, 47.61571], [8.23809, 47.61204], [8.22577, 47.60385], [8.22011, 47.6181], [8.20617, 47.62141], [8.19378, 47.61636], [8.1652, 47.5945], [8.14947, 47.59558], [8.13823, 47.59147], [8.13662, 47.58432], [8.11543, 47.5841], [8.10395, 47.57918], [8.10002, 47.56504], [8.08557, 47.55768], [8.06663, 47.56374], [8.04383, 47.55443], [8.02136, 47.55096], [8.00113, 47.55616], [7.97581, 47.55493], [7.95682, 47.55789], [7.94494, 47.54511], [7.91251, 47.55031], [7.90673, 47.57674], [7.88664, 47.58854], [7.84412, 47.5841], [7.81901, 47.58798], [7.79486, 47.55691], [7.75261, 47.54599], [7.71961, 47.54219], [7.69642, 47.53297], [7.68101, 47.53232], [7.6656, 47.53752], [7.66174, 47.54554], [7.65083, 47.54662], [7.63338, 47.56256], [7.67655, 47.56435], [7.68904, 47.57133], [7.67115, 47.5871], [7.68486, 47.59601], [7.69385, 47.60099], [7.68229, 47.59905], [7.67395, 47.59212], [7.64599, 47.59695], [7.64213, 47.5944], [7.64309, 47.59151], [7.61929, 47.57683], [7.60459, 47.57869], [7.60523, 47.58519], [7.58945, 47.59017], [7.58386, 47.57536], [7.56684, 47.57785], [7.56548, 47.57617], [7.55689, 47.57232], [7.55652, 47.56779], [7.53634, 47.55553], [7.52831, 47.55347], [7.51723, 47.54578], [7.50873, 47.54546], [7.49691, 47.53821], [7.50588, 47.52856], [7.51904, 47.53515], [7.53199, 47.5284], [7.5229, 47.51644], [7.49804, 47.51798], [7.51076, 47.49651], [7.47534, 47.47932], [7.43356, 47.49712], [7.42923, 47.48628], [7.4583, 47.47216], [7.4462, 47.46264], [7.43088, 47.45846], [7.40308, 47.43638], [7.35603, 47.43432], [7.33526, 47.44186], [7.24669, 47.4205], [7.17026, 47.44312], [7.19583, 47.49455], [7.16249, 47.49025], [7.12781, 47.50371], [7.07425, 47.48863], [7.0231, 47.50522], [6.98425, 47.49432], [7.0024, 47.45264], [6.93953, 47.43388], [6.93744, 47.40714], [6.88542, 47.37262], [6.87959, 47.35335], [7.03125, 47.36996], [7.0564, 47.35134], [7.05305, 47.33304], [6.94316, 47.28747], [6.95108, 47.26428], [6.9508, 47.24338], [6.8489, 47.15933], [6.76788, 47.1208], [6.68823, 47.06616], [6.71531, 47.0494], [6.43341, 46.92703], [6.46456, 46.88865], [6.43216, 46.80336], [6.45209, 46.77502], [6.38351, 46.73171], [6.27135, 46.68251], [6.11084, 46.57649], [6.1567, 46.54402], [6.07269, 46.46244], [6.08427, 46.44305], [6.06407, 46.41676], [6.09926, 46.40768], [6.15016, 46.3778], [6.15985, 46.37721], [6.16987, 46.36759], [6.15738, 46.3491], [6.13876, 46.33844], [6.1198, 46.31157], [6.11697, 46.29547], [6.1013, 46.28512], [6.11926, 46.2634], [6.12446, 46.25059], [6.10071, 46.23772], [6.08563, 46.24651], [6.07072, 46.24085], [6.0633, 46.24583], [6.05029, 46.23518], [6.04602, 46.23127], [6.03342, 46.2383], [6.02461, 46.23313], [5.97542, 46.21525], [5.96515, 46.19638], [5.99573, 46.18587], [5.98846, 46.17046], [5.98188, 46.17392], [5.97508, 46.15863], [5.9641, 46.14412], [5.95781, 46.12925], [5.97893, 46.13303], [5.9871, 46.14499], [6.01791, 46.14228], [6.03614, 46.13712], [6.04564, 46.14031], [6.05203, 46.15191], [6.07491, 46.14879], [6.09199, 46.15191], [6.09926, 46.14373], [6.13397, 46.1406], [6.15305, 46.15194], [6.18116, 46.16187], [6.18871, 46.16644], [6.18707, 46.17999], [6.19552, 46.18401], [6.19807, 46.18369], [6.20539, 46.19163], [6.21114, 46.1927], [6.21273, 46.19409], [6.21603, 46.19507], [6.21844, 46.19837], [6.22222, 46.19888], [6.22175, 46.20045], [6.23544, 46.20714], [6.23913, 46.20511], [6.24821, 46.20531], [6.26007, 46.21165], [6.27694, 46.21566], [6.29663, 46.22688], [6.31041, 46.24417], [6.29474, 46.26221], [6.26749, 46.24745], [6.24952, 46.26255], [6.23775, 46.27822], [6.25137, 46.29014], [6.24826, 46.30175], [6.21981, 46.31304], [6.25432, 46.3632], [6.53358, 46.45431], [6.82312, 46.42661], [6.8024, 46.39171], [6.77152, 46.34784], [6.86052, 46.28512], [6.78968, 46.14058], [6.89321, 46.12548], [6.87868, 46.03855], [6.93862, 46.06502], [7.00946, 45.9944], [7.04151, 45.92435], [7.10685, 45.85653], [7.56343, 45.97421], [7.85949, 45.91485], [7.9049, 45.99945], [7.98881, 45.99867], [8.02906, 46.10331], [8.11383, 46.11577], [8.16866, 46.17817], [8.08814, 46.26692], [8.31162, 46.38044], [8.30648, 46.41587], [8.42464, 46.46367], [8.46317, 46.43712], [8.45032, 46.26869], [8.62242, 46.12112], [8.75697, 46.10395], [8.80778, 46.10085], [8.85617, 46.0748], [8.79414, 46.00913], [8.78585, 45.98973], [8.79362, 45.99207], [8.8319, 45.9879], [8.85121, 45.97239], [8.86688, 45.96135], [8.88904, 45.95465], [8.93649, 45.86775], [8.94372, 45.86587], [8.93504, 45.86245], [8.91129, 45.8388], [8.94737, 45.84285], [8.9621, 45.83707], [8.99663, 45.83466], [9.00324, 45.82055], [9.0298, 45.82127], [9.03279, 45.82865], [9.03793, 45.83548], [9.03505, 45.83976], [9.04059, 45.8464], [9.04546, 45.84968], [9.06642, 45.8761], [9.09065, 45.89906], [8.99257, 45.9698], [9.01618, 46.04928], [9.24503, 46.23616], [9.29226, 46.32717], [9.25502, 46.43743], [9.28136, 46.49685], [9.36128, 46.5081], [9.40487, 46.46621], [9.45936, 46.50873], [9.46117, 46.37481], [9.57015, 46.2958], [9.71273, 46.29266], [9.73086, 46.35071], [9.95249, 46.38045], [10.07055, 46.21668], [10.14439, 46.22992], [10.17862, 46.25626], [10.10506, 46.3372], [10.165, 46.41051], [10.03715, 46.44479], [10.10307, 46.61003], [10.23674, 46.63484], [10.25309, 46.57432], [10.46136, 46.53164], [10.49375, 46.62049], [10.44686, 46.64162], [10.40475, 46.63671], [10.38659, 46.67847], [10.47197, 46.85698], [10.48376, 46.93891], [10.36933, 47.00212], [10.30031, 46.92093], [10.24128, 46.93147], [10.22675, 46.86942], [10.10715, 46.84296], [9.98058, 46.91434], [9.88266, 46.93343], [9.87935, 47.01337], [9.60717, 47.06091], [9.55721, 47.04762], [9.54041, 47.06495], [9.47548, 47.05257], [9.47139, 47.06402], [9.51362, 47.08505], [9.52089, 47.10019], [9.51044, 47.13727], [9.48774, 47.17402], [9.4891, 47.19346], [9.50318, 47.22153], [9.52406, 47.24959], [9.53116, 47.27029], [9.54773, 47.2809], [9.55857, 47.29919], [9.58513, 47.31334], [9.59978, 47.34671], [9.62476, 47.36639], [9.65427, 47.36824], [9.66243, 47.37136], [9.6711, 47.37824], [9.67445, 47.38429], [9.67334, 47.39191], [9.6629, 47.39591], [9.65136, 47.40504], [9.65043, 47.41937], [9.6446, 47.43233], [9.64483, 47.43842], [9.65863, 47.44847], [9.65728, 47.45383], [9.6423, 47.45599], [9.62475, 47.45685], [9.62158, 47.45858], [9.60841, 47.47178], [9.60484, 47.46358], [9.60205, 47.46165], [9.59482, 47.46305], [9.58208, 47.48344], [9.56312, 47.49495], [9.55125, 47.53629], [9.25619, 47.65939], [9.18203, 47.65598], [9.17593, 47.65399], [9.1755, 47.65584], [9.1705, 47.65513], [9.15181, 47.66904], [9.13845, 47.66389], [9.09891, 47.67801], [9.02093, 47.6868], [8.94093, 47.65596], [8.89946, 47.64769], [8.87625, 47.65441], [8.87383, 47.67045], [8.85065, 47.68209], [8.86989, 47.70504], [8.82002, 47.71458], [8.80663, 47.73821], [8.77309, 47.72059], [8.76965, 47.7075], [8.79966, 47.70222], [8.79511, 47.67462], [8.75856, 47.68969], [8.72809, 47.69282]], [[8.95861, 45.96485], [8.96668, 45.98436], [8.97741, 45.98317], [8.97604, 45.96151], [8.95861, 45.96485]], [[8.70847, 47.68904], [8.68985, 47.69552], [8.66837, 47.68437], [8.65769, 47.68928], [8.67508, 47.6979], [8.66416, 47.71367], [8.70237, 47.71453], [8.71773, 47.69088], [8.70847, 47.68904]]]]
23650           }
23651         }, {
23652           type: "Feature",
23653           properties: {
23654             iso1A2: "CI",
23655             iso1A3: "CIV",
23656             iso1N3: "384",
23657             wikidata: "Q1008",
23658             nameEn: "C\xF4te d'Ivoire",
23659             groups: ["011", "202", "002", "UN"],
23660             callingCodes: ["225"]
23661           },
23662           geometry: {
23663             type: "MultiPolygon",
23664             coordinates: [[[[-7.52774, 3.7105], [-3.34019, 4.17519], [-3.10675, 5.08515], [-3.11073, 5.12675], [-3.063, 5.13665], [-2.96554, 5.10397], [-2.95261, 5.12477], [-2.75502, 5.10657], [-2.73074, 5.1364], [-2.77625, 5.34621], [-2.72737, 5.34789], [-2.76614, 5.60963], [-2.85378, 5.65156], [-2.93132, 5.62137], [-2.96671, 5.6415], [-2.95323, 5.71865], [-3.01896, 5.71697], [-3.25999, 6.62521], [-3.21954, 6.74407], [-3.23327, 6.81744], [-2.95438, 7.23737], [-2.97822, 7.27165], [-2.92339, 7.60847], [-2.79467, 7.86002], [-2.78395, 7.94974], [-2.74819, 7.92613], [-2.67787, 8.02055], [-2.61232, 8.02645], [-2.62901, 8.11495], [-2.49037, 8.20872], [-2.58243, 8.7789], [-2.66357, 9.01771], [-2.77799, 9.04949], [-2.69814, 9.22717], [-2.68802, 9.49343], [-2.76494, 9.40778], [-2.93012, 9.57403], [-3.00765, 9.74019], [-3.16609, 9.85147], [-3.19306, 9.93781], [-3.27228, 9.84981], [-3.31779, 9.91125], [-3.69703, 9.94279], [-4.25999, 9.76012], [-4.31392, 9.60062], [-4.6426, 9.70696], [-4.96621, 9.89132], [-4.96453, 9.99923], [-5.12465, 10.29788], [-5.39602, 10.2929], [-5.51058, 10.43177], [-5.65135, 10.46767], [-5.78124, 10.43952], [-5.99478, 10.19694], [-6.18851, 10.24244], [-6.1731, 10.46983], [-6.24795, 10.74248], [-6.325, 10.68624], [-6.40646, 10.69922], [-6.42847, 10.5694], [-6.52974, 10.59104], [-6.63541, 10.66893], [-6.68164, 10.35074], [-6.93921, 10.35291], [-7.01186, 10.25111], [-6.97444, 10.21644], [-7.00966, 10.15794], [-7.0603, 10.14711], [-7.13331, 10.24877], [-7.3707, 10.24677], [-7.44555, 10.44602], [-7.52261, 10.4655], [-7.54462, 10.40921], [-7.63048, 10.46334], [-7.92107, 10.15577], [-7.97971, 10.17117], [-8.01225, 10.1021], [-8.11921, 10.04577], [-8.15652, 9.94288], [-8.09434, 9.86936], [-8.14657, 9.55062], [-8.03463, 9.39604], [-7.85056, 9.41812], [-7.90777, 9.20456], [-7.73862, 9.08422], [-7.92518, 8.99332], [-7.95503, 8.81146], [-7.69882, 8.66148], [-7.65653, 8.36873], [-7.92518, 8.50652], [-8.22991, 8.48438], [-8.2411, 8.24196], [-8.062, 8.16071], [-7.98675, 8.20134], [-7.99919, 8.11023], [-7.94695, 8.00925], [-8.06449, 8.04989], [-8.13414, 7.87991], [-8.09931, 7.78626], [-8.21374, 7.54466], [-8.4003, 7.6285], [-8.47114, 7.55676], [-8.41935, 7.51203], [-8.37458, 7.25794], [-8.29249, 7.1691], [-8.31736, 6.82837], [-8.59456, 6.50612], [-8.48652, 6.43797], [-8.45666, 6.49977], [-8.38453, 6.35887], [-8.3298, 6.36381], [-8.17557, 6.28222], [-8.00642, 6.31684], [-7.90692, 6.27728], [-7.83478, 6.20309], [-7.8497, 6.08932], [-7.79747, 6.07696], [-7.78254, 5.99037], [-7.70294, 5.90625], [-7.67309, 5.94337], [-7.48155, 5.80974], [-7.46165, 5.84934], [-7.43677, 5.84687], [-7.43926, 5.74787], [-7.37209, 5.61173], [-7.43428, 5.42355], [-7.36463, 5.32944], [-7.46165, 5.26256], [-7.48901, 5.14118], [-7.55369, 5.08667], [-7.53876, 4.94294], [-7.59349, 4.8909], [-7.53259, 4.35145], [-7.52774, 3.7105]]]]
23665           }
23666         }, {
23667           type: "Feature",
23668           properties: {
23669             iso1A2: "CK",
23670             iso1A3: "COK",
23671             iso1N3: "184",
23672             wikidata: "Q26988",
23673             nameEn: "Cook Islands",
23674             country: "NZ",
23675             groups: ["061", "009", "UN"],
23676             driveSide: "left",
23677             callingCodes: ["682"]
23678           },
23679           geometry: {
23680             type: "MultiPolygon",
23681             coordinates: [[[[-168.15106, -10.26955], [-156.45576, -31.75456], [-156.48634, -15.52824], [-156.50903, -7.4975], [-168.15106, -10.26955]]]]
23682           }
23683         }, {
23684           type: "Feature",
23685           properties: {
23686             iso1A2: "CL",
23687             iso1A3: "CHL",
23688             iso1N3: "152",
23689             wikidata: "Q298",
23690             nameEn: "Chile",
23691             groups: ["005", "419", "019", "UN"],
23692             callingCodes: ["56"]
23693           },
23694           geometry: {
23695             type: "MultiPolygon",
23696             coordinates: [[[[-68.60702, -52.65781], [-68.41683, -52.33516], [-69.97824, -52.00845], [-71.99889, -51.98018], [-72.33873, -51.59954], [-72.31343, -50.58411], [-73.15765, -50.78337], [-73.55259, -49.92488], [-73.45156, -49.79461], [-73.09655, -49.14342], [-72.56894, -48.81116], [-72.54042, -48.52392], [-72.27662, -48.28727], [-72.50478, -47.80586], [-71.94152, -47.13595], [-71.68577, -46.55385], [-71.75614, -45.61611], [-71.35687, -45.22075], [-72.06985, -44.81756], [-71.26418, -44.75684], [-71.16436, -44.46244], [-71.81318, -44.38097], [-71.64206, -43.64774], [-72.14828, -42.85321], [-72.15541, -42.15941], [-71.74901, -42.11711], [-71.92726, -40.72714], [-71.37826, -38.91474], [-70.89532, -38.6923], [-71.24279, -37.20264], [-70.95047, -36.4321], [-70.38008, -36.02375], [-70.49416, -35.24145], [-69.87386, -34.13344], [-69.88099, -33.34489], [-70.55832, -31.51559], [-70.14479, -30.36595], [-69.8596, -30.26131], [-69.99507, -29.28351], [-69.80969, -29.07185], [-69.66709, -28.44055], [-69.22504, -27.95042], [-68.77586, -27.16029], [-68.43363, -27.08414], [-68.27677, -26.90626], [-68.59048, -26.49861], [-68.56909, -26.28146], [-68.38372, -26.15353], [-68.57622, -25.32505], [-68.38372, -25.08636], [-68.56909, -24.69831], [-68.24825, -24.42596], [-67.33563, -24.04237], [-66.99632, -22.99839], [-67.18382, -22.81525], [-67.54284, -22.89771], [-67.85114, -22.87076], [-68.18816, -21.28614], [-68.40403, -20.94562], [-68.53957, -20.91542], [-68.55383, -20.7355], [-68.44023, -20.62701], [-68.7276, -20.46178], [-68.74273, -20.08817], [-68.57132, -20.03134], [-68.54611, -19.84651], [-68.66761, -19.72118], [-68.41218, -19.40499], [-68.61989, -19.27584], [-68.80602, -19.08355], [-68.87082, -19.06003], [-68.94987, -18.93302], [-69.07432, -18.28259], [-69.14807, -18.16893], [-69.07496, -18.03715], [-69.28671, -17.94844], [-69.34126, -17.72753], [-69.46623, -17.60518], [-69.46897, -17.4988], [-69.66483, -17.65083], [-69.79087, -17.65563], [-69.82868, -17.72048], [-69.75305, -17.94605], [-69.81607, -18.12582], [-69.96732, -18.25992], [-70.16394, -18.31737], [-70.31267, -18.31258], [-70.378, -18.3495], [-70.59118, -18.35072], [-113.52687, -26.52828], [-68.11646, -58.14883], [-66.07313, -55.19618], [-67.11046, -54.94199], [-67.46182, -54.92205], [-68.01394, -54.8753], [-68.60733, -54.9125], [-68.60702, -52.65781]]]]
23697           }
23698         }, {
23699           type: "Feature",
23700           properties: {
23701             iso1A2: "CM",
23702             iso1A3: "CMR",
23703             iso1N3: "120",
23704             wikidata: "Q1009",
23705             nameEn: "Cameroon",
23706             groups: ["017", "202", "002", "UN"],
23707             callingCodes: ["237"]
23708           },
23709           geometry: {
23710             type: "MultiPolygon",
23711             coordinates: [[[[14.83314, 12.62963], [14.55058, 12.78256], [14.56101, 12.91036], [14.46881, 13.08259], [14.08251, 13.0797], [14.20204, 12.53405], [14.17523, 12.41916], [14.22215, 12.36533], [14.4843, 12.35223], [14.6474, 12.17466], [14.61612, 11.7798], [14.55207, 11.72001], [14.64591, 11.66166], [14.6124, 11.51283], [14.17821, 11.23831], [13.97489, 11.30258], [13.78945, 11.00154], [13.7403, 11.00593], [13.70753, 10.94451], [13.73434, 10.9255], [13.54964, 10.61236], [13.5705, 10.53183], [13.43644, 10.13326], [13.34111, 10.12299], [13.25025, 10.03647], [13.25323, 10.00127], [13.286, 9.9822], [13.27409, 9.93232], [13.24132, 9.91031], [13.25025, 9.86042], [13.29941, 9.8296], [13.25472, 9.76795], [13.22642, 9.57266], [13.02385, 9.49334], [12.85628, 9.36698], [12.91958, 9.33905], [12.90022, 9.11411], [12.81085, 8.91992], [12.79, 8.75361], [12.71701, 8.7595], [12.68722, 8.65938], [12.44146, 8.6152], [12.4489, 8.52536], [12.26123, 8.43696], [12.24782, 8.17904], [12.19271, 8.10826], [12.20909, 7.97553], [11.99908, 7.67302], [12.01844, 7.52981], [11.93205, 7.47812], [11.84864, 7.26098], [11.87396, 7.09398], [11.63117, 6.9905], [11.55818, 6.86186], [11.57755, 6.74059], [11.51499, 6.60892], [11.42264, 6.5882], [11.42041, 6.53789], [11.09495, 6.51717], [11.09644, 6.68437], [10.94302, 6.69325], [10.8179, 6.83377], [10.83727, 6.9358], [10.60789, 7.06885], [10.59746, 7.14719], [10.57214, 7.16345], [10.53639, 6.93432], [10.21466, 6.88996], [10.15135, 7.03781], [9.86314, 6.77756], [9.77824, 6.79088], [9.70674, 6.51717], [9.51757, 6.43874], [8.84209, 5.82562], [8.88156, 5.78857], [8.83687, 5.68483], [8.92029, 5.58403], [8.78027, 5.1243], [8.60302, 4.87353], [8.34397, 4.30689], [9.22018, 3.72052], [9.81162, 2.33797], [9.82123, 2.35097], [9.83754, 2.32428], [9.83238, 2.29079], [9.84716, 2.24676], [9.89012, 2.20457], [9.90749, 2.20049], [9.991, 2.16561], [11.3561, 2.17217], [11.37116, 2.29975], [13.28534, 2.25716], [13.29457, 2.16106], [14.61145, 2.17866], [15.00996, 1.98887], [15.22634, 2.03243], [15.34776, 1.91264], [15.48942, 1.98265], [16.02959, 1.76483], [16.02647, 1.65591], [16.14634, 1.70259], [16.05294, 1.9811], [16.08563, 2.19733], [16.15568, 2.18955], [16.19357, 2.21537], [16.08252, 2.45708], [16.05449, 3.02306], [15.77725, 3.26835], [15.73522, 3.24348], [15.07686, 4.01805], [15.17482, 4.05131], [15.10644, 4.1362], [15.08609, 4.30282], [15.00825, 4.41458], [14.73383, 4.6135], [14.65489, 5.21343], [14.57083, 5.23979], [14.52724, 5.28319], [14.62531, 5.51411], [14.58951, 5.59777], [14.62375, 5.70466], [14.60974, 5.91838], [14.49455, 5.91683], [14.42917, 6.00508], [14.43073, 6.08867], [14.56149, 6.18928], [14.74206, 6.26356], [14.80122, 6.34866], [14.79966, 6.39043], [14.96311, 6.75693], [15.04717, 6.77085], [15.23397, 7.25135], [15.49743, 7.52179], [15.56964, 7.58936], [15.59272, 7.7696], [15.50743, 7.79302], [15.20426, 8.50892], [15.09484, 8.65982], [14.83566, 8.80557], [14.35707, 9.19611], [14.37094, 9.2954], [13.97544, 9.6365], [14.01793, 9.73169], [14.1317, 9.82413], [14.20411, 10.00055], [14.4673, 10.00264], [14.80082, 9.93818], [14.95722, 9.97926], [15.05999, 9.94845], [15.14043, 9.99246], [15.24618, 9.99246], [15.41408, 9.92876], [15.68761, 9.99344], [15.50535, 10.1098], [15.30874, 10.31063], [15.23724, 10.47764], [15.14936, 10.53915], [15.15532, 10.62846], [15.06737, 10.80921], [15.09127, 10.87431], [15.04957, 11.02347], [15.10021, 11.04101], [15.0585, 11.40481], [15.13149, 11.5537], [15.06595, 11.71126], [15.11579, 11.79313], [15.04808, 11.8731], [15.05786, 12.0608], [15.0349, 12.10698], [15.00146, 12.1223], [14.96952, 12.0925], [14.89019, 12.16593], [14.90827, 12.3269], [14.83314, 12.62963]]]]
23712           }
23713         }, {
23714           type: "Feature",
23715           properties: {
23716             iso1A2: "CN",
23717             iso1A3: "CHN",
23718             iso1N3: "156",
23719             wikidata: "Q148",
23720             nameEn: "People's Republic of China"
23721           },
23722           geometry: null
23723         }, {
23724           type: "Feature",
23725           properties: {
23726             iso1A2: "CO",
23727             iso1A3: "COL",
23728             iso1N3: "170",
23729             wikidata: "Q739",
23730             nameEn: "Colombia",
23731             groups: ["005", "419", "019", "UN"],
23732             callingCodes: ["57"]
23733           },
23734           geometry: {
23735             type: "MultiPolygon",
23736             coordinates: [[[[-71.19849, 12.65801], [-81.58685, 18.0025], [-82.06974, 14.49418], [-82.56142, 11.91792], [-78.79327, 9.93766], [-77.58292, 9.22278], [-77.32389, 8.81247], [-77.45064, 8.49991], [-77.17257, 7.97422], [-77.57185, 7.51147], [-77.72514, 7.72348], [-77.72157, 7.47612], [-77.81426, 7.48319], [-77.89178, 7.22681], [-78.06168, 7.07793], [-82.12561, 4.00341], [-78.87137, 1.47457], [-78.42749, 1.15389], [-77.85677, 0.80197], [-77.7148, 0.85003], [-77.68613, 0.83029], [-77.66416, 0.81604], [-77.67815, 0.73863], [-77.49984, 0.64476], [-77.52001, 0.40782], [-76.89177, 0.24736], [-76.4094, 0.24015], [-76.41215, 0.38228], [-76.23441, 0.42294], [-75.82927, 0.09578], [-75.25764, -0.11943], [-75.18513, -0.0308], [-74.42701, -0.50218], [-74.26675, -0.97229], [-73.65312, -1.26222], [-72.92587, -2.44514], [-71.75223, -2.15058], [-70.94377, -2.23142], [-70.04609, -2.73906], [-70.71396, -3.7921], [-70.52393, -3.87553], [-70.3374, -3.79505], [-69.94708, -4.2431], [-69.43395, -1.42219], [-69.4215, -1.01853], [-69.59796, -0.75136], [-69.603, -0.51947], [-70.03658, -0.19681], [-70.04162, 0.55437], [-69.47696, 0.71065], [-69.20976, 0.57958], [-69.14422, 0.84172], [-69.26017, 1.06856], [-69.82987, 1.07864], [-69.83491, 1.69353], [-69.53746, 1.76408], [-69.38621, 1.70865], [-68.18128, 1.72881], [-68.26699, 1.83463], [-68.18632, 2.00091], [-67.9292, 1.82455], [-67.40488, 2.22258], [-67.299, 1.87494], [-67.15784, 1.80439], [-67.08222, 1.17441], [-66.85795, 1.22998], [-67.21967, 2.35778], [-67.65696, 2.81691], [-67.85862, 2.79173], [-67.85862, 2.86727], [-67.30945, 3.38393], [-67.50067, 3.75812], [-67.62671, 3.74303], [-67.85358, 4.53249], [-67.83341, 5.31104], [-67.59141, 5.5369], [-67.63914, 5.64963], [-67.58558, 5.84537], [-67.43513, 5.98835], [-67.4625, 6.20625], [-67.60654, 6.2891], [-69.41843, 6.1072], [-70.10716, 6.96516], [-70.7596, 7.09799], [-71.03941, 6.98163], [-71.37234, 7.01588], [-71.42212, 7.03854], [-71.44118, 7.02116], [-71.82441, 7.04314], [-72.04895, 7.03837], [-72.19437, 7.37034], [-72.43132, 7.40034], [-72.47415, 7.48928], [-72.45321, 7.57232], [-72.47827, 7.65604], [-72.46763, 7.79518], [-72.44454, 7.86031], [-72.46183, 7.90682], [-72.45806, 7.91141], [-72.47042, 7.92306], [-72.48183, 7.92909], [-72.48801, 7.94329], [-72.47213, 7.96106], [-72.39137, 8.03534], [-72.35163, 8.01163], [-72.36987, 8.19976], [-72.4042, 8.36513], [-72.65474, 8.61428], [-72.77415, 9.10165], [-72.94052, 9.10663], [-73.02119, 9.27584], [-73.36905, 9.16636], [-72.98085, 9.85253], [-72.88002, 10.44309], [-72.4767, 11.1117], [-72.24983, 11.14138], [-71.9675, 11.65536], [-71.3275, 11.85], [-70.92579, 11.96275], [-71.19849, 12.65801]]]]
23737           }
23738         }, {
23739           type: "Feature",
23740           properties: {
23741             iso1A2: "CP",
23742             iso1A3: "CPT",
23743             wikidata: "Q161258",
23744             nameEn: "Clipperton Island",
23745             country: "FR",
23746             groups: ["013", "003", "019", "UN"],
23747             isoStatus: "excRes"
23748           },
23749           geometry: {
23750             type: "MultiPolygon",
23751             coordinates: [[[[-110.36279, 9.79626], [-108.755, 9.84085], [-109.04145, 11.13245], [-110.36279, 9.79626]]]]
23752           }
23753         }, {
23754           type: "Feature",
23755           properties: {
23756             iso1A2: "CR",
23757             iso1A3: "CRI",
23758             iso1N3: "188",
23759             wikidata: "Q800",
23760             nameEn: "Costa Rica",
23761             groups: ["013", "003", "419", "019", "UN"],
23762             callingCodes: ["506"]
23763           },
23764           geometry: {
23765             type: "MultiPolygon",
23766             coordinates: [[[[-83.68276, 11.01562], [-83.66597, 10.79916], [-83.90838, 10.71161], [-84.68197, 11.07568], [-84.92439, 10.9497], [-85.60529, 11.22607], [-85.71223, 11.06868], [-86.14524, 11.09059], [-87.41779, 5.02401], [-82.94503, 7.93865], [-82.89978, 8.04083], [-82.89137, 8.05755], [-82.88641, 8.10219], [-82.9388, 8.26634], [-83.05209, 8.33394], [-82.93056, 8.43465], [-82.8679, 8.44042], [-82.8382, 8.48117], [-82.83322, 8.52464], [-82.83975, 8.54755], [-82.82739, 8.60153], [-82.8794, 8.6981], [-82.92068, 8.74832], [-82.91377, 8.774], [-82.88253, 8.83331], [-82.72126, 8.97125], [-82.93516, 9.07687], [-82.93516, 9.46741], [-82.84871, 9.4973], [-82.87919, 9.62645], [-82.77206, 9.59573], [-82.66667, 9.49746], [-82.61345, 9.49881], [-82.56507, 9.57279], [-82.51044, 9.65379], [-83.54024, 10.96805], [-83.68276, 11.01562]]]]
23767           }
23768         }, {
23769           type: "Feature",
23770           properties: {
23771             iso1A2: "CU",
23772             iso1A3: "CUB",
23773             iso1N3: "192",
23774             wikidata: "Q241",
23775             nameEn: "Cuba",
23776             groups: ["029", "003", "419", "019", "UN"],
23777             callingCodes: ["53"]
23778           },
23779           geometry: {
23780             type: "MultiPolygon",
23781             coordinates: [[[[-73.62304, 20.6935], [-82.02215, 24.23074], [-85.77883, 21.92705], [-74.81171, 18.82201], [-73.62304, 20.6935]]]]
23782           }
23783         }, {
23784           type: "Feature",
23785           properties: {
23786             iso1A2: "CV",
23787             iso1A3: "CPV",
23788             iso1N3: "132",
23789             wikidata: "Q1011",
23790             nameEn: "Cape Verde",
23791             groups: ["Q105472", "011", "202", "002", "UN"],
23792             callingCodes: ["238"]
23793           },
23794           geometry: {
23795             type: "MultiPolygon",
23796             coordinates: [[[[-28.81604, 14.57305], [-20.39702, 14.12816], [-23.37101, 19.134], [-28.81604, 14.57305]]]]
23797           }
23798         }, {
23799           type: "Feature",
23800           properties: {
23801             iso1A2: "CW",
23802             iso1A3: "CUW",
23803             iso1N3: "531",
23804             wikidata: "Q25279",
23805             nameEn: "Cura\xE7ao",
23806             aliases: ["NL-CW"],
23807             country: "NL",
23808             groups: ["Q1451600", "029", "003", "419", "019", "UN"],
23809             callingCodes: ["599"]
23810           },
23811           geometry: {
23812             type: "MultiPolygon",
23813             coordinates: [[[[-68.90012, 12.62309], [-69.59009, 12.46019], [-68.99639, 11.79035], [-68.33524, 11.78151], [-68.90012, 12.62309]]]]
23814           }
23815         }, {
23816           type: "Feature",
23817           properties: {
23818             iso1A2: "CX",
23819             iso1A3: "CXR",
23820             iso1N3: "162",
23821             wikidata: "Q31063",
23822             nameEn: "Christmas Island",
23823             country: "AU",
23824             groups: ["053", "009", "UN"],
23825             driveSide: "left",
23826             callingCodes: ["61"]
23827           },
23828           geometry: {
23829             type: "MultiPolygon",
23830             coordinates: [[[[105.66835, -9.31927], [104.67494, -11.2566], [106.66176, -11.14349], [105.66835, -9.31927]]]]
23831           }
23832         }, {
23833           type: "Feature",
23834           properties: {
23835             iso1A2: "CY",
23836             iso1A3: "CYP",
23837             iso1N3: "196",
23838             wikidata: "Q229",
23839             nameEn: "Republic of Cyprus",
23840             groups: ["Q644636", "EU", "145", "142", "UN"],
23841             driveSide: "left",
23842             callingCodes: ["357"]
23843           },
23844           geometry: {
23845             type: "MultiPolygon",
23846             coordinates: [[[[32.46489, 35.48584], [30.15137, 34.08517], [32.74412, 34.43926], [32.75515, 34.64985], [32.76136, 34.68318], [32.79433, 34.67883], [32.82717, 34.70622], [32.86014, 34.70585], [32.86167, 34.68734], [32.9068, 34.66102], [32.91398, 34.67343], [32.93043, 34.67091], [32.92807, 34.66736], [32.93449, 34.66241], [32.93693, 34.67027], [32.94379, 34.67111], [32.94683, 34.67907], [32.95539, 34.68471], [32.99135, 34.68061], [32.98668, 34.67268], [32.99014, 34.65518], [32.97736, 34.65277], [32.97079, 34.66112], [32.95325, 34.66462], [32.94796, 34.6587], [32.94976, 34.65204], [32.95471, 34.64528], [32.95323, 34.64075], [32.95891, 34.62919], [32.96718, 34.63446], [32.96968, 34.64046], [33.0138, 34.64424], [33.26744, 34.49942], [33.83531, 34.73974], [33.70575, 34.97947], [33.70639, 34.99303], [33.71514, 35.00294], [33.69731, 35.01754], [33.69938, 35.03123], [33.67678, 35.03866], [33.63765, 35.03869], [33.61215, 35.0527], [33.59658, 35.03635], [33.567, 35.04803], [33.57478, 35.06049], [33.53975, 35.08151], [33.48915, 35.06594], [33.47666, 35.00701], [33.45256, 35.00288], [33.45178, 35.02078], [33.47825, 35.04103], [33.48136, 35.0636], [33.46813, 35.10564], [33.41675, 35.16325], [33.4076, 35.20062], [33.38575, 35.2018], [33.37248, 35.18698], [33.3717, 35.1788], [33.36569, 35.17479], [33.35612, 35.17402], [33.35596, 35.17942], [33.34964, 35.17803], [33.35056, 35.18328], [33.31955, 35.18096], [33.3072, 35.16816], [33.27068, 35.16815], [33.15138, 35.19504], [33.11105, 35.15639], [33.08249, 35.17319], [33.01192, 35.15639], [32.94471, 35.09422], [32.86406, 35.1043], [32.85733, 35.07742], [32.70779, 35.14127], [32.70947, 35.18328], [32.64864, 35.19967], [32.60361, 35.16647], [32.46489, 35.48584]]], [[[33.74144, 35.01053], [33.7492, 35.01319], [33.74983, 35.02274], [33.74265, 35.02329], [33.73781, 35.02181], [33.7343, 35.01178], [33.74144, 35.01053]]], [[[33.77312, 34.9976], [33.75994, 35.00113], [33.75682, 34.99916], [33.76605, 34.99543], [33.76738, 34.99188], [33.7778, 34.98981], [33.77843, 34.988], [33.78149, 34.98854], [33.78318, 34.98699], [33.78571, 34.98951], [33.78917, 34.98854], [33.79191, 34.98914], [33.78516, 34.99582], [33.77553, 34.99518], [33.77312, 34.9976]]]]
23847           }
23848         }, {
23849           type: "Feature",
23850           properties: {
23851             iso1A2: "CZ",
23852             iso1A3: "CZE",
23853             iso1N3: "203",
23854             wikidata: "Q213",
23855             nameEn: "Czechia",
23856             groups: ["EU", "151", "150", "UN"],
23857             callingCodes: ["420"]
23858           },
23859           geometry: {
23860             type: "MultiPolygon",
23861             coordinates: [[[[14.82803, 50.86966], [14.79139, 50.81438], [14.70661, 50.84096], [14.61993, 50.86049], [14.63434, 50.8883], [14.65259, 50.90513], [14.64802, 50.93241], [14.58024, 50.91443], [14.56374, 50.922], [14.59702, 50.96148], [14.59908, 50.98685], [14.58215, 50.99306], [14.56432, 51.01008], [14.53438, 51.00374], [14.53321, 51.01679], [14.49873, 51.02242], [14.50809, 51.0427], [14.49991, 51.04692], [14.49154, 51.04382], [14.49202, 51.02286], [14.45827, 51.03712], [14.41335, 51.02086], [14.30098, 51.05515], [14.25665, 50.98935], [14.28776, 50.97718], [14.32353, 50.98556], [14.32793, 50.97379], [14.30251, 50.96606], [14.31422, 50.95243], [14.39848, 50.93866], [14.38691, 50.89907], [14.30098, 50.88448], [14.27123, 50.89386], [14.24314, 50.88761], [14.22331, 50.86049], [14.02982, 50.80662], [13.98864, 50.8177], [13.89113, 50.78533], [13.89444, 50.74142], [13.82942, 50.7251], [13.76316, 50.73487], [13.70204, 50.71771], [13.65977, 50.73096], [13.52474, 50.70394], [13.53748, 50.67654], [13.5226, 50.64721], [13.49742, 50.63133], [13.46413, 50.60102], [13.42189, 50.61243], [13.37485, 50.64931], [13.37805, 50.627], [13.32264, 50.60317], [13.32594, 50.58009], [13.29454, 50.57904], [13.25158, 50.59268], [13.19043, 50.50237], [13.13424, 50.51709], [13.08301, 50.50132], [13.0312, 50.50944], [13.02038, 50.4734], [13.02147, 50.44763], [12.98433, 50.42016], [12.94058, 50.40944], [12.82465, 50.45738], [12.73476, 50.43237], [12.73044, 50.42268], [12.70731, 50.39948], [12.67261, 50.41949], [12.51356, 50.39694], [12.48747, 50.37278], [12.49214, 50.35228], [12.48256, 50.34784], [12.46643, 50.35527], [12.43722, 50.33774], [12.43371, 50.32506], [12.39924, 50.32302], [12.40158, 50.29521], [12.36594, 50.28289], [12.35425, 50.23993], [12.33263, 50.24367], [12.32445, 50.20442], [12.33847, 50.19432], [12.32596, 50.17146], [12.29232, 50.17524], [12.28063, 50.19544], [12.28755, 50.22429], [12.23943, 50.24594], [12.24791, 50.25525], [12.26953, 50.25189], [12.25119, 50.27079], [12.20823, 50.2729], [12.18013, 50.32146], [12.10907, 50.32041], [12.13716, 50.27396], [12.09287, 50.25032], [12.19335, 50.19997], [12.21484, 50.16399], [12.1917, 50.13434], [12.2073, 50.10315], [12.23709, 50.10213], [12.27433, 50.0771], [12.26111, 50.06331], [12.30798, 50.05719], [12.49908, 49.97305], [12.47264, 49.94222], [12.55197, 49.92094], [12.48256, 49.83575], [12.46603, 49.78882], [12.40489, 49.76321], [12.4462, 49.70233], [12.52553, 49.68415], [12.53544, 49.61888], [12.56188, 49.6146], [12.60155, 49.52887], [12.64782, 49.52565], [12.64121, 49.47628], [12.669, 49.42935], [12.71227, 49.42363], [12.75854, 49.3989], [12.78168, 49.34618], [12.88414, 49.33541], [12.88249, 49.35479], [12.94859, 49.34079], [13.03618, 49.30417], [13.02957, 49.27399], [13.05883, 49.26259], [13.17665, 49.16713], [13.17019, 49.14339], [13.20405, 49.12303], [13.23689, 49.11412], [13.28242, 49.1228], [13.39479, 49.04812], [13.40802, 48.98851], [13.50221, 48.93752], [13.50552, 48.97441], [13.58319, 48.96899], [13.61624, 48.9462], [13.67739, 48.87886], [13.73854, 48.88538], [13.76994, 48.83537], [13.78977, 48.83319], [13.8096, 48.77877], [13.84023, 48.76988], [14.06151, 48.66873], [14.01482, 48.63788], [14.09104, 48.5943], [14.20691, 48.5898], [14.33909, 48.55852], [14.43076, 48.58855], [14.4587, 48.64695], [14.56139, 48.60429], [14.60808, 48.62881], [14.66762, 48.58215], [14.71794, 48.59794], [14.72756, 48.69502], [14.80584, 48.73489], [14.80821, 48.77711], [14.81545, 48.7874], [14.94773, 48.76268], [14.95641, 48.75915], [14.9758, 48.76857], [14.98112, 48.77524], [14.9782, 48.7766], [14.98032, 48.77959], [14.95072, 48.79101], [14.98917, 48.90082], [14.97612, 48.96983], [14.99878, 49.01444], [15.15534, 48.99056], [15.16358, 48.94278], [15.26177, 48.95766], [15.28305, 48.98831], [15.34823, 48.98444], [15.48027, 48.94481], [15.51357, 48.91549], [15.61622, 48.89541], [15.6921, 48.85973], [15.75341, 48.8516], [15.78087, 48.87644], [15.84404, 48.86921], [16.06034, 48.75436], [16.37345, 48.729], [16.40915, 48.74576], [16.46134, 48.80865], [16.67008, 48.77699], [16.68518, 48.7281], [16.71883, 48.73806], [16.79779, 48.70998], [16.90354, 48.71541], [16.93955, 48.60371], [17.00215, 48.70887], [17.11202, 48.82925], [17.19355, 48.87602], [17.29054, 48.85546], [17.3853, 48.80936], [17.45671, 48.85004], [17.5295, 48.81117], [17.7094, 48.86721], [17.73126, 48.87885], [17.77944, 48.92318], [17.87831, 48.92679], [17.91814, 49.01784], [18.06885, 49.03157], [18.1104, 49.08624], [18.15022, 49.24518], [18.18456, 49.28909], [18.36446, 49.3267], [18.4139, 49.36517], [18.4084, 49.40003], [18.44686, 49.39467], [18.54848, 49.47059], [18.53063, 49.49022], [18.57183, 49.51162], [18.6144, 49.49824], [18.67757, 49.50895], [18.74761, 49.492], [18.84521, 49.51672], [18.84786, 49.5446], [18.80479, 49.6815], [18.72838, 49.68163], [18.69817, 49.70473], [18.62676, 49.71983], [18.62943, 49.74603], [18.62645, 49.75002], [18.61368, 49.75426], [18.61278, 49.7618], [18.57183, 49.83334], [18.60341, 49.86256], [18.57045, 49.87849], [18.57697, 49.91565], [18.54299, 49.92537], [18.54495, 49.9079], [18.53423, 49.89906], [18.41604, 49.93498], [18.33562, 49.94747], [18.33278, 49.92415], [18.31914, 49.91565], [18.27794, 49.93863], [18.27107, 49.96779], [18.21752, 49.97309], [18.20241, 49.99958], [18.10628, 50.00223], [18.07898, 50.04535], [18.03212, 50.06574], [18.00396, 50.04954], [18.04585, 50.03311], [18.04585, 50.01194], [18.00191, 50.01723], [17.86886, 49.97452], [17.77669, 50.02253], [17.7506, 50.07896], [17.6888, 50.12037], [17.66683, 50.10275], [17.59404, 50.16437], [17.70528, 50.18812], [17.76296, 50.23382], [17.72176, 50.25665], [17.74648, 50.29966], [17.69292, 50.32859], [17.67764, 50.28977], [17.58889, 50.27837], [17.3702, 50.28123], [17.34548, 50.2628], [17.34273, 50.32947], [17.27681, 50.32246], [17.19991, 50.3654], [17.19579, 50.38817], [17.14498, 50.38117], [17.1224, 50.39494], [16.89229, 50.45117], [16.85933, 50.41093], [16.90877, 50.38642], [16.94448, 50.31281], [16.99803, 50.30316], [17.02138, 50.27772], [16.99803, 50.25753], [17.02825, 50.23118], [17.00353, 50.21449], [16.98018, 50.24172], [16.8456, 50.20834], [16.7014, 50.09659], [16.63137, 50.1142], [16.55446, 50.16613], [16.56407, 50.21009], [16.42674, 50.32509], [16.39379, 50.3207], [16.3622, 50.34875], [16.36495, 50.37679], [16.30289, 50.38292], [16.28118, 50.36891], [16.22821, 50.41054], [16.21585, 50.40627], [16.19526, 50.43291], [16.31413, 50.50274], [16.34572, 50.49575], [16.44597, 50.58041], [16.33611, 50.66579], [16.23174, 50.67101], [16.20839, 50.63096], [16.10265, 50.66405], [16.02437, 50.60046], [15.98317, 50.61528], [16.0175, 50.63009], [15.97219, 50.69799], [15.87331, 50.67188], [15.81683, 50.75666], [15.73186, 50.73885], [15.43798, 50.80833], [15.3803, 50.77187], [15.36656, 50.83956], [15.2773, 50.8907], [15.27043, 50.97724], [15.2361, 50.99886], [15.1743, 50.9833], [15.16744, 51.01959], [15.11937, 50.99021], [15.10152, 51.01095], [15.06218, 51.02269], [15.03895, 51.0123], [15.02433, 51.0242], [14.96419, 50.99108], [15.01088, 50.97984], [14.99852, 50.86817], [14.82803, 50.86966]]]]
23862           }
23863         }, {
23864           type: "Feature",
23865           properties: {
23866             iso1A2: "DE",
23867             iso1A3: "DEU",
23868             iso1N3: "276",
23869             wikidata: "Q183",
23870             nameEn: "Germany",
23871             groups: ["EU", "155", "150", "UN"],
23872             callingCodes: ["49"]
23873           },
23874           geometry: {
23875             type: "MultiPolygon",
23876             coordinates: [[[[8.70847, 47.68904], [8.71773, 47.69088], [8.70237, 47.71453], [8.66416, 47.71367], [8.67508, 47.6979], [8.65769, 47.68928], [8.66837, 47.68437], [8.68985, 47.69552], [8.70847, 47.68904]]], [[[8.72617, 47.69651], [8.72809, 47.69282], [8.75856, 47.68969], [8.79511, 47.67462], [8.79966, 47.70222], [8.76965, 47.7075], [8.77309, 47.72059], [8.80663, 47.73821], [8.82002, 47.71458], [8.86989, 47.70504], [8.85065, 47.68209], [8.87383, 47.67045], [8.87625, 47.65441], [8.89946, 47.64769], [8.94093, 47.65596], [9.02093, 47.6868], [9.09891, 47.67801], [9.13845, 47.66389], [9.15181, 47.66904], [9.1705, 47.65513], [9.1755, 47.65584], [9.17593, 47.65399], [9.18203, 47.65598], [9.25619, 47.65939], [9.55125, 47.53629], [9.72736, 47.53457], [9.76748, 47.5934], [9.80254, 47.59419], [9.82591, 47.58158], [9.8189, 47.54688], [9.87499, 47.52953], [9.87733, 47.54688], [9.92407, 47.53111], [9.96029, 47.53899], [10.00003, 47.48216], [10.03859, 47.48927], [10.07131, 47.45531], [10.09001, 47.46005], [10.1052, 47.4316], [10.06897, 47.40709], [10.09819, 47.35724], [10.11805, 47.37228], [10.16362, 47.36674], [10.17648, 47.38889], [10.2127, 47.38019], [10.22774, 47.38904], [10.23757, 47.37609], [10.19998, 47.32832], [10.2147, 47.31014], [10.17648, 47.29149], [10.17531, 47.27167], [10.23257, 47.27088], [10.33424, 47.30813], [10.39851, 47.37623], [10.4324, 47.38494], [10.4359, 47.41183], [10.47446, 47.43318], [10.46278, 47.47901], [10.44291, 47.48453], [10.4324, 47.50111], [10.44992, 47.5524], [10.43473, 47.58394], [10.47329, 47.58552], [10.48849, 47.54057], [10.56912, 47.53584], [10.60337, 47.56755], [10.63456, 47.5591], [10.68832, 47.55752], [10.6965, 47.54253], [10.7596, 47.53228], [10.77596, 47.51729], [10.88814, 47.53701], [10.91268, 47.51334], [10.86945, 47.5015], [10.87061, 47.4786], [10.90918, 47.48571], [10.93839, 47.48018], [10.92437, 47.46991], [10.98513, 47.42882], [10.97111, 47.41617], [10.97111, 47.39561], [11.11835, 47.39719], [11.12536, 47.41222], [11.20482, 47.43198], [11.25157, 47.43277], [11.22002, 47.3964], [11.27844, 47.39956], [11.29597, 47.42566], [11.33804, 47.44937], [11.4175, 47.44621], [11.38128, 47.47465], [11.4362, 47.51413], [11.52618, 47.50939], [11.58578, 47.52281], [11.58811, 47.55515], [11.60681, 47.57881], [11.63934, 47.59202], [11.84052, 47.58354], [11.85572, 47.60166], [12.0088, 47.62451], [12.02282, 47.61033], [12.05788, 47.61742], [12.13734, 47.60639], [12.17824, 47.61506], [12.18145, 47.61019], [12.17737, 47.60121], [12.18568, 47.6049], [12.20398, 47.60667], [12.20801, 47.61082], [12.19895, 47.64085], [12.18507, 47.65984], [12.18347, 47.66663], [12.16769, 47.68167], [12.16217, 47.70105], [12.18303, 47.70065], [12.22571, 47.71776], [12.2542, 47.7433], [12.26238, 47.73544], [12.24017, 47.69534], [12.26004, 47.67725], [12.27991, 47.68827], [12.336, 47.69534], [12.37222, 47.68433], [12.43883, 47.6977], [12.44117, 47.6741], [12.50076, 47.62293], [12.53816, 47.63553], [12.57438, 47.63238], [12.6071, 47.6741], [12.7357, 47.6787], [12.77777, 47.66689], [12.76492, 47.64485], [12.82101, 47.61493], [12.77427, 47.58025], [12.80699, 47.54477], [12.84672, 47.54556], [12.85256, 47.52741], [12.9624, 47.47452], [12.98344, 47.48716], [12.9998, 47.46267], [13.04537, 47.49426], [13.03252, 47.53373], [13.05355, 47.56291], [13.04537, 47.58183], [13.06641, 47.58577], [13.06407, 47.60075], [13.09562, 47.63304], [13.07692, 47.68814], [13.01382, 47.72116], [12.98578, 47.7078], [12.92969, 47.71094], [12.91333, 47.7178], [12.90274, 47.72513], [12.91711, 47.74026], [12.9353, 47.74788], [12.94371, 47.76281], [12.93202, 47.77302], [12.96311, 47.79957], [12.98543, 47.82896], [13.00588, 47.84374], [12.94163, 47.92927], [12.93886, 47.94046], [12.93642, 47.94436], [12.93419, 47.94063], [12.92668, 47.93879], [12.91985, 47.94069], [12.9211, 47.95135], [12.91683, 47.95647], [12.87476, 47.96195], [12.8549, 48.01122], [12.76141, 48.07373], [12.74973, 48.10885], [12.7617, 48.12796], [12.78595, 48.12445], [12.80676, 48.14979], [12.82673, 48.15245], [12.8362, 48.15876], [12.836, 48.1647], [12.84475, 48.16556], [12.87126, 48.20318], [12.95306, 48.20629], [13.02083, 48.25689], [13.0851, 48.27711], [13.126, 48.27867], [13.18093, 48.29577], [13.26039, 48.29422], [13.30897, 48.31575], [13.40709, 48.37292], [13.43929, 48.43386], [13.42527, 48.45711], [13.45727, 48.51092], [13.43695, 48.55776], [13.45214, 48.56472], [13.46967, 48.55157], [13.50663, 48.57506], [13.50131, 48.58091], [13.51291, 48.59023], [13.57535, 48.55912], [13.59705, 48.57013], [13.62508, 48.55501], [13.65186, 48.55092], [13.66113, 48.53558], [13.72802, 48.51208], [13.74816, 48.53058], [13.7513, 48.5624], [13.76921, 48.55324], [13.80519, 48.58026], [13.80038, 48.59487], [13.82609, 48.62345], [13.81901, 48.6761], [13.81283, 48.68426], [13.81791, 48.69832], [13.79337, 48.71375], [13.81863, 48.73257], [13.82266, 48.75544], [13.84023, 48.76988], [13.8096, 48.77877], [13.78977, 48.83319], [13.76994, 48.83537], [13.73854, 48.88538], [13.67739, 48.87886], [13.61624, 48.9462], [13.58319, 48.96899], [13.50552, 48.97441], [13.50221, 48.93752], [13.40802, 48.98851], [13.39479, 49.04812], [13.28242, 49.1228], [13.23689, 49.11412], [13.20405, 49.12303], [13.17019, 49.14339], [13.17665, 49.16713], [13.05883, 49.26259], [13.02957, 49.27399], [13.03618, 49.30417], [12.94859, 49.34079], [12.88249, 49.35479], [12.88414, 49.33541], [12.78168, 49.34618], [12.75854, 49.3989], [12.71227, 49.42363], [12.669, 49.42935], [12.64121, 49.47628], [12.64782, 49.52565], [12.60155, 49.52887], [12.56188, 49.6146], [12.53544, 49.61888], [12.52553, 49.68415], [12.4462, 49.70233], [12.40489, 49.76321], [12.46603, 49.78882], [12.48256, 49.83575], [12.55197, 49.92094], [12.47264, 49.94222], [12.49908, 49.97305], [12.30798, 50.05719], [12.26111, 50.06331], [12.27433, 50.0771], [12.23709, 50.10213], [12.2073, 50.10315], [12.1917, 50.13434], [12.21484, 50.16399], [12.19335, 50.19997], [12.09287, 50.25032], [12.13716, 50.27396], [12.10907, 50.32041], [12.18013, 50.32146], [12.20823, 50.2729], [12.25119, 50.27079], [12.26953, 50.25189], [12.24791, 50.25525], [12.23943, 50.24594], [12.28755, 50.22429], [12.28063, 50.19544], [12.29232, 50.17524], [12.32596, 50.17146], [12.33847, 50.19432], [12.32445, 50.20442], [12.33263, 50.24367], [12.35425, 50.23993], [12.36594, 50.28289], [12.40158, 50.29521], [12.39924, 50.32302], [12.43371, 50.32506], [12.43722, 50.33774], [12.46643, 50.35527], [12.48256, 50.34784], [12.49214, 50.35228], [12.48747, 50.37278], [12.51356, 50.39694], [12.67261, 50.41949], [12.70731, 50.39948], [12.73044, 50.42268], [12.73476, 50.43237], [12.82465, 50.45738], [12.94058, 50.40944], [12.98433, 50.42016], [13.02147, 50.44763], [13.02038, 50.4734], [13.0312, 50.50944], [13.08301, 50.50132], [13.13424, 50.51709], [13.19043, 50.50237], [13.25158, 50.59268], [13.29454, 50.57904], [13.32594, 50.58009], [13.32264, 50.60317], [13.37805, 50.627], [13.37485, 50.64931], [13.42189, 50.61243], [13.46413, 50.60102], [13.49742, 50.63133], [13.5226, 50.64721], [13.53748, 50.67654], [13.52474, 50.70394], [13.65977, 50.73096], [13.70204, 50.71771], [13.76316, 50.73487], [13.82942, 50.7251], [13.89444, 50.74142], [13.89113, 50.78533], [13.98864, 50.8177], [14.02982, 50.80662], [14.22331, 50.86049], [14.24314, 50.88761], [14.27123, 50.89386], [14.30098, 50.88448], [14.38691, 50.89907], [14.39848, 50.93866], [14.31422, 50.95243], [14.30251, 50.96606], [14.32793, 50.97379], [14.32353, 50.98556], [14.28776, 50.97718], [14.25665, 50.98935], [14.30098, 51.05515], [14.41335, 51.02086], [14.45827, 51.03712], [14.49202, 51.02286], [14.49154, 51.04382], [14.49991, 51.04692], [14.50809, 51.0427], [14.49873, 51.02242], [14.53321, 51.01679], [14.53438, 51.00374], [14.56432, 51.01008], [14.58215, 50.99306], [14.59908, 50.98685], [14.59702, 50.96148], [14.56374, 50.922], [14.58024, 50.91443], [14.64802, 50.93241], [14.65259, 50.90513], [14.63434, 50.8883], [14.61993, 50.86049], [14.70661, 50.84096], [14.79139, 50.81438], [14.82803, 50.86966], [14.81664, 50.88148], [14.89681, 50.9422], [14.89252, 50.94999], [14.92942, 50.99744], [14.95529, 51.04552], [14.97938, 51.07742], [14.98229, 51.11354], [14.99689, 51.12205], [14.99079, 51.14284], [14.99646, 51.14365], [15.00083, 51.14974], [14.99414, 51.15813], [14.99311, 51.16249], [15.0047, 51.16874], [15.01242, 51.21285], [15.04288, 51.28387], [14.98008, 51.33449], [14.96899, 51.38367], [14.9652, 51.44793], [14.94749, 51.47155], [14.73219, 51.52922], [14.72652, 51.53902], [14.73047, 51.54606], [14.71125, 51.56209], [14.7727, 51.61263], [14.75759, 51.62318], [14.75392, 51.67445], [14.69065, 51.70842], [14.66386, 51.73282], [14.64625, 51.79472], [14.60493, 51.80473], [14.59089, 51.83302], [14.6588, 51.88359], [14.6933, 51.9044], [14.70601, 51.92944], [14.7177, 51.94048], [14.72163, 51.95188], [14.71836, 51.95606], [14.7139, 51.95643], [14.70488, 51.97679], [14.71339, 52.00337], [14.76026, 52.06624], [14.72971, 52.09167], [14.6917, 52.10283], [14.67683, 52.13936], [14.70616, 52.16927], [14.68344, 52.19612], [14.71319, 52.22144], [14.70139, 52.25038], [14.58149, 52.28007], [14.56378, 52.33838], [14.55228, 52.35264], [14.54423, 52.42568], [14.63056, 52.48993], [14.60081, 52.53116], [14.6289, 52.57136], [14.61073, 52.59847], [14.22071, 52.81175], [14.13806, 52.82392], [14.12256, 52.84311], [14.15873, 52.87715], [14.14056, 52.95786], [14.25954, 53.00264], [14.35044, 53.05829], [14.38679, 53.13669], [14.36696, 53.16444], [14.37853, 53.20405], [14.40662, 53.21098], [14.45125, 53.26241], [14.44133, 53.27427], [14.4215, 53.27724], [14.35209, 53.49506], [14.3273, 53.50587], [14.30416, 53.55499], [14.31904, 53.61581], [14.2853, 53.63392], [14.28477, 53.65955], [14.27133, 53.66613], [14.2836, 53.67721], [14.26782, 53.69866], [14.27249, 53.74464], [14.21323, 53.8664], [14.20823, 53.90776], [14.18544, 53.91258], [14.20647, 53.91671], [14.22634, 53.9291], [14.20811, 54.12784], [13.93395, 54.84044], [12.85844, 54.82438], [11.90309, 54.38543], [11.00303, 54.63689], [10.31111, 54.65968], [10.16755, 54.73883], [9.89314, 54.84171], [9.73563, 54.8247], [9.61187, 54.85548], [9.62734, 54.88057], [9.58937, 54.88785], [9.4659, 54.83131], [9.43155, 54.82586], [9.41213, 54.84254], [9.38532, 54.83968], [9.36496, 54.81749], [9.33849, 54.80233], [9.32771, 54.80602], [9.2474, 54.8112], [9.23445, 54.83432], [9.24631, 54.84726], [9.20571, 54.85841], [9.14275, 54.87421], [9.04629, 54.87249], [8.92795, 54.90452], [8.81178, 54.90518], [8.76387, 54.8948], [8.63979, 54.91069], [8.55769, 54.91837], [8.45719, 55.06747], [8.02459, 55.09613], [5.45168, 54.20039], [6.91025, 53.44221], [7.00198, 53.32672], [7.19052, 53.31866], [7.21679, 53.20058], [7.22681, 53.18165], [7.17898, 53.13817], [7.21694, 53.00742], [7.07253, 52.81083], [7.04557, 52.63318], [6.77307, 52.65375], [6.71641, 52.62905], [6.69507, 52.488], [6.94293, 52.43597], [6.99041, 52.47235], [7.03417, 52.40237], [7.07044, 52.37805], [7.02703, 52.27941], [7.06365, 52.23789], [7.03729, 52.22695], [6.9897, 52.2271], [6.97189, 52.20329], [6.83984, 52.11728], [6.76117, 52.11895], [6.68128, 52.05052], [6.83035, 51.9905], [6.82357, 51.96711], [6.72319, 51.89518], [6.68386, 51.91861], [6.58556, 51.89386], [6.50231, 51.86313], [6.47179, 51.85395], [6.38815, 51.87257], [6.40704, 51.82771], [6.30593, 51.84998], [6.29872, 51.86801], [6.21443, 51.86801], [6.15349, 51.90439], [6.11551, 51.89769], [6.16902, 51.84094], [6.10337, 51.84829], [6.06705, 51.86136], [5.99848, 51.83195], [5.94568, 51.82786], [5.98665, 51.76944], [5.95003, 51.7493], [6.04091, 51.71821], [6.02767, 51.6742], [6.11759, 51.65609], [6.09055, 51.60564], [6.18017, 51.54096], [6.21724, 51.48568], [6.20654, 51.40049], [6.22641, 51.39948], [6.22674, 51.36135], [6.16977, 51.33169], [6.07889, 51.24432], [6.07889, 51.17038], [6.17384, 51.19589], [6.16706, 51.15677], [5.98292, 51.07469], [5.9541, 51.03496], [5.9134, 51.06736], [5.86735, 51.05182], [5.87849, 51.01969], [5.90493, 51.00198], [5.90296, 50.97356], [5.95282, 50.98728], [6.02697, 50.98303], [6.01615, 50.93367], [6.09297, 50.92066], [6.07486, 50.89307], [6.08805, 50.87223], [6.07693, 50.86025], [6.07431, 50.84674], [6.05702, 50.85179], [6.05623, 50.8572], [6.01921, 50.84435], [6.02328, 50.81694], [6.00462, 50.80065], [5.98404, 50.80988], [5.97497, 50.79992], [6.02624, 50.77453], [6.01976, 50.75398], [6.03889, 50.74618], [6.0326, 50.72647], [6.0406, 50.71848], [6.04428, 50.72861], [6.11707, 50.72231], [6.17852, 50.6245], [6.26957, 50.62444], [6.2476, 50.60392], [6.24888, 50.59869], [6.24005, 50.58732], [6.22581, 50.5907], [6.20281, 50.56952], [6.17739, 50.55875], [6.17802, 50.54179], [6.19735, 50.53576], [6.19579, 50.5313], [6.18716, 50.52653], [6.19193, 50.5212], [6.20599, 50.52089], [6.22335, 50.49578], [6.26637, 50.50272], [6.30809, 50.50058], [6.3465, 50.48833], [6.34005, 50.46083], [6.37219, 50.45397], [6.36852, 50.40776], [6.34406, 50.37994], [6.3688, 50.35898], [6.40785, 50.33557], [6.40641, 50.32425], [6.35701, 50.31139], [6.32488, 50.32333], [6.29949, 50.30887], [6.28797, 50.27458], [6.208, 50.25179], [6.16853, 50.2234], [6.18364, 50.20815], [6.18739, 50.1822], [6.14588, 50.17106], [6.14132, 50.14971], [6.15298, 50.14126], [6.1379, 50.12964], [6.12055, 50.09171], [6.11274, 50.05916], [6.13458, 50.04141], [6.13044, 50.02929], [6.14666, 50.02207], [6.13794, 50.01466], [6.13273, 50.02019], [6.1295, 50.01849], [6.13806, 50.01056], [6.14948, 50.00908], [6.14147, 49.99563], [6.1701, 49.98518], [6.16466, 49.97086], [6.17872, 49.9537], [6.18554, 49.95622], [6.18045, 49.96611], [6.19089, 49.96991], [6.19856, 49.95053], [6.22094, 49.94955], [6.22608, 49.929], [6.21882, 49.92403], [6.22926, 49.92096], [6.23496, 49.89972], [6.26146, 49.88203], [6.28874, 49.87592], [6.29692, 49.86685], [6.30963, 49.87021], [6.32303, 49.85133], [6.32098, 49.83728], [6.33585, 49.83785], [6.34267, 49.84974], [6.36576, 49.85032], [6.40022, 49.82029], [6.42521, 49.81591], [6.42905, 49.81091], [6.44131, 49.81443], [6.45425, 49.81164], [6.47111, 49.82263], [6.48718, 49.81267], [6.50647, 49.80916], [6.51215, 49.80124], [6.52121, 49.81338], [6.53122, 49.80666], [6.52169, 49.79787], [6.50534, 49.78952], [6.51669, 49.78336], [6.51056, 49.77515], [6.51828, 49.76855], [6.51646, 49.75961], [6.50174, 49.75292], [6.50193, 49.73291], [6.51805, 49.72425], [6.51397, 49.72058], [6.50261, 49.72718], [6.49535, 49.72645], [6.49694, 49.72205], [6.5042, 49.71808], [6.50647, 49.71353], [6.49785, 49.71118], [6.48014, 49.69767], [6.46048, 49.69092], [6.44654, 49.67799], [6.42937, 49.66857], [6.42726, 49.66078], [6.43768, 49.66021], [6.4413, 49.65722], [6.41861, 49.61723], [6.39822, 49.60081], [6.385, 49.59946], [6.37464, 49.58886], [6.38342, 49.5799], [6.38024, 49.57593], [6.36676, 49.57813], [6.35825, 49.57053], [6.38228, 49.55855], [6.38072, 49.55171], [6.35666, 49.52931], [6.36788, 49.50377], [6.36907, 49.48931], [6.36778, 49.46937], [6.38352, 49.46463], [6.39168, 49.4667], [6.40274, 49.46546], [6.42432, 49.47683], [6.55404, 49.42464], [6.533, 49.40748], [6.60091, 49.36864], [6.58807, 49.35358], [6.572, 49.35027], [6.60186, 49.31055], [6.66583, 49.28065], [6.69274, 49.21661], [6.71843, 49.2208], [6.73256, 49.20486], [6.71137, 49.18808], [6.73765, 49.16375], [6.78265, 49.16793], [6.83385, 49.15162], [6.84703, 49.15734], [6.86225, 49.18185], [6.85016, 49.19354], [6.85119, 49.20038], [6.83555, 49.21249], [6.85939, 49.22376], [6.89298, 49.20863], [6.91875, 49.22261], [6.93831, 49.2223], [6.94028, 49.21641], [6.95963, 49.203], [6.97273, 49.2099], [7.01318, 49.19018], [7.03459, 49.19096], [7.0274, 49.17042], [7.03178, 49.15734], [7.04662, 49.13724], [7.04409, 49.12123], [7.04843, 49.11422], [7.05548, 49.11185], [7.06642, 49.11415], [7.07162, 49.1255], [7.09007, 49.13094], [7.07859, 49.15031], [7.10715, 49.15631], [7.10384, 49.13787], [7.12504, 49.14253], [7.1358, 49.1282], [7.1593, 49.1204], [7.23473, 49.12971], [7.29514, 49.11426], [7.3195, 49.14231], [7.35995, 49.14399], [7.3662, 49.17308], [7.44052, 49.18354], [7.44455, 49.16765], [7.49473, 49.17], [7.49172, 49.13915], [7.53012, 49.09818], [7.56416, 49.08136], [7.62575, 49.07654], [7.63618, 49.05428], [7.75948, 49.04562], [7.79557, 49.06583], [7.86386, 49.03499], [7.93641, 49.05544], [7.97783, 49.03161], [8.14189, 48.97833], [8.22604, 48.97352], [8.20031, 48.95856], [8.19989, 48.95825], [8.12813, 48.87985], [8.10253, 48.81829], [8.06802, 48.78957], [8.0326, 48.79017], [8.01534, 48.76085], [7.96994, 48.75606], [7.96812, 48.72491], [7.89002, 48.66317], [7.84098, 48.64217], [7.80057, 48.5857], [7.80167, 48.54758], [7.80647, 48.51239], [7.76833, 48.48945], [7.73109, 48.39192], [7.74562, 48.32736], [7.69022, 48.30018], [7.6648, 48.22219], [7.57137, 48.12292], [7.56966, 48.03265], [7.62302, 47.97898], [7.55673, 47.87371], [7.52921, 47.77747], [7.54761, 47.72912], [7.53722, 47.71635], [7.51266, 47.70197], [7.51915, 47.68335], [7.52067, 47.66437], [7.53384, 47.65115], [7.5591, 47.63849], [7.57423, 47.61628], [7.58851, 47.60794], [7.59301, 47.60058], [7.58945, 47.59017], [7.60523, 47.58519], [7.60459, 47.57869], [7.61929, 47.57683], [7.64309, 47.59151], [7.64213, 47.5944], [7.64599, 47.59695], [7.67395, 47.59212], [7.68229, 47.59905], [7.69385, 47.60099], [7.68486, 47.59601], [7.67115, 47.5871], [7.68904, 47.57133], [7.67655, 47.56435], [7.63338, 47.56256], [7.65083, 47.54662], [7.66174, 47.54554], [7.6656, 47.53752], [7.68101, 47.53232], [7.69642, 47.53297], [7.71961, 47.54219], [7.75261, 47.54599], [7.79486, 47.55691], [7.81901, 47.58798], [7.84412, 47.5841], [7.88664, 47.58854], [7.90673, 47.57674], [7.91251, 47.55031], [7.94494, 47.54511], [7.95682, 47.55789], [7.97581, 47.55493], [8.00113, 47.55616], [8.02136, 47.55096], [8.04383, 47.55443], [8.06663, 47.56374], [8.08557, 47.55768], [8.10002, 47.56504], [8.10395, 47.57918], [8.11543, 47.5841], [8.13662, 47.58432], [8.13823, 47.59147], [8.14947, 47.59558], [8.1652, 47.5945], [8.19378, 47.61636], [8.20617, 47.62141], [8.22011, 47.6181], [8.22577, 47.60385], [8.23809, 47.61204], [8.25863, 47.61571], [8.26313, 47.6103], [8.2824, 47.61225], [8.29722, 47.60603], [8.29524, 47.5919], [8.30277, 47.58607], [8.32735, 47.57133], [8.35512, 47.57014], [8.38273, 47.56608], [8.39477, 47.57826], [8.43235, 47.56617], [8.49431, 47.58107], [8.48949, 47.588], [8.46637, 47.58389], [8.45578, 47.60121], [8.50747, 47.61897], [8.51686, 47.63476], [8.55756, 47.62394], [8.57586, 47.59537], [8.60348, 47.61204], [8.59545, 47.64298], [8.60701, 47.65271], [8.61471, 47.64514], [8.60412, 47.63735], [8.62049, 47.63757], [8.62884, 47.65098], [8.61113, 47.66332], [8.6052, 47.67258], [8.57683, 47.66158], [8.56141, 47.67088], [8.52801, 47.66059], [8.5322, 47.64687], [8.49656, 47.64709], [8.46605, 47.64103], [8.4667, 47.65747], [8.44711, 47.65379], [8.42264, 47.66667], [8.41346, 47.66676], [8.40473, 47.67499], [8.4211, 47.68407], [8.40569, 47.69855], [8.44807, 47.72426], [8.45771, 47.7493], [8.48868, 47.77215], [8.56814, 47.78001], [8.56415, 47.80633], [8.61657, 47.79998], [8.62408, 47.7626], [8.64425, 47.76398], [8.65292, 47.80066], [8.68022, 47.78599], [8.68985, 47.75686], [8.71778, 47.76571], [8.74251, 47.75168], [8.70543, 47.73121], [8.73671, 47.7169], [8.72617, 47.69651]]]]
23877           }
23878         }, {
23879           type: "Feature",
23880           properties: {
23881             iso1A2: "DG",
23882             iso1A3: "DGA",
23883             wikidata: "Q184851",
23884             nameEn: "Diego Garcia",
23885             country: "GB",
23886             groups: ["IO", "BOTS", "014", "202", "002", "UN"],
23887             isoStatus: "excRes",
23888             callingCodes: ["246"]
23889           },
23890           geometry: {
23891             type: "MultiPolygon",
23892             coordinates: [[[[73.14823, -7.76302], [73.09982, -6.07324], [71.43792, -7.73904], [73.14823, -7.76302]]]]
23893           }
23894         }, {
23895           type: "Feature",
23896           properties: {
23897             iso1A2: "DJ",
23898             iso1A3: "DJI",
23899             iso1N3: "262",
23900             wikidata: "Q977",
23901             nameEn: "Djibouti",
23902             groups: ["014", "202", "002", "UN"],
23903             callingCodes: ["253"]
23904           },
23905           geometry: {
23906             type: "MultiPolygon",
23907             coordinates: [[[[43.90659, 12.3823], [43.90659, 12.3823], [43.32909, 12.59711], [43.29075, 12.79154], [42.86195, 12.58747], [42.7996, 12.42629], [42.6957, 12.36201], [42.46941, 12.52661], [42.4037, 12.46478], [41.95461, 11.81157], [41.82878, 11.72361], [41.77727, 11.49902], [41.8096, 11.33606], [41.80056, 10.97127], [42.06302, 10.92599], [42.13691, 10.97586], [42.42669, 10.98493], [42.62989, 11.09711], [42.75111, 11.06992], [42.79037, 10.98493], [42.95776, 10.98533], [43.90659, 12.3823]]]]
23908           }
23909         }, {
23910           type: "Feature",
23911           properties: {
23912             iso1A2: "DK",
23913             iso1A3: "DNK",
23914             iso1N3: "208",
23915             wikidata: "Q756617",
23916             nameEn: "Kingdom of Denmark"
23917           },
23918           geometry: null
23919         }, {
23920           type: "Feature",
23921           properties: {
23922             iso1A2: "DM",
23923             iso1A3: "DMA",
23924             iso1N3: "212",
23925             wikidata: "Q784",
23926             nameEn: "Dominica",
23927             groups: ["029", "003", "419", "019", "UN"],
23928             driveSide: "left",
23929             roadSpeedUnit: "mph",
23930             callingCodes: ["1 767"]
23931           },
23932           geometry: {
23933             type: "MultiPolygon",
23934             coordinates: [[[[-61.32485, 14.91445], [-60.86656, 15.82603], [-61.95646, 15.5094], [-61.32485, 14.91445]]]]
23935           }
23936         }, {
23937           type: "Feature",
23938           properties: {
23939             iso1A2: "DO",
23940             iso1A3: "DOM",
23941             iso1N3: "214",
23942             wikidata: "Q786",
23943             nameEn: "Dominican Republic",
23944             groups: ["029", "003", "419", "019", "UN"],
23945             callingCodes: ["1 809", "1 829", "1 849"]
23946           },
23947           geometry: {
23948             type: "MultiPolygon",
23949             coordinates: [[[[-67.87844, 21.7938], [-72.38946, 20.27111], [-71.77419, 19.73128], [-71.75865, 19.70231], [-71.7429, 19.58445], [-71.71449, 19.55364], [-71.71268, 19.53374], [-71.6802, 19.45008], [-71.69448, 19.37866], [-71.77766, 19.33823], [-71.73229, 19.26686], [-71.62642, 19.21212], [-71.65337, 19.11759], [-71.69938, 19.10916], [-71.71088, 19.08353], [-71.74088, 19.0437], [-71.88102, 18.95007], [-71.77766, 18.95007], [-71.72624, 18.87802], [-71.71885, 18.78423], [-71.82556, 18.62551], [-71.95412, 18.64939], [-72.00201, 18.62312], [-71.88102, 18.50125], [-71.90875, 18.45821], [-71.69952, 18.34101], [-71.78271, 18.18302], [-71.75465, 18.14405], [-71.74994, 18.11115], [-71.73783, 18.07177], [-71.75671, 18.03456], [-72.29523, 17.48026], [-68.39466, 16.14167], [-67.87844, 21.7938]]]]
23950           }
23951         }, {
23952           type: "Feature",
23953           properties: {
23954             iso1A2: "DZ",
23955             iso1A3: "DZA",
23956             iso1N3: "012",
23957             wikidata: "Q262",
23958             nameEn: "Algeria",
23959             groups: ["015", "002", "UN"],
23960             callingCodes: ["213"]
23961           },
23962           geometry: {
23963             type: "MultiPolygon",
23964             coordinates: [[[[8.59123, 37.14286], [5.10072, 39.89531], [-2.27707, 35.35051], [-2.21248, 35.08532], [-2.21445, 35.04378], [-2.04734, 34.93218], [-1.97833, 34.93218], [-1.97469, 34.886], [-1.73707, 34.74226], [-1.84569, 34.61907], [-1.69788, 34.48056], [-1.78042, 34.39018], [-1.64666, 34.10405], [-1.73494, 33.71721], [-1.59508, 33.59929], [-1.67067, 33.27084], [-1.46249, 33.0499], [-1.54244, 32.95499], [-1.37794, 32.73628], [-0.9912, 32.52467], [-1.24998, 32.32993], [-1.24453, 32.1917], [-1.15735, 32.12096], [-1.22829, 32.07832], [-2.46166, 32.16603], [-2.93873, 32.06557], [-2.82784, 31.79459], [-3.66314, 31.6339], [-3.66386, 31.39202], [-3.77647, 31.31912], [-3.77103, 31.14984], [-3.54944, 31.0503], [-3.65418, 30.85566], [-3.64735, 30.67539], [-4.31774, 30.53229], [-4.6058, 30.28343], [-5.21671, 29.95253], [-5.58831, 29.48103], [-5.72121, 29.52322], [-5.75616, 29.61407], [-6.69965, 29.51623], [-6.78351, 29.44634], [-6.95824, 29.50924], [-7.61585, 29.36252], [-8.6715, 28.71194], [-8.66879, 27.6666], [-8.66674, 27.31569], [-4.83423, 24.99935], [1.15698, 21.12843], [1.20992, 20.73533], [3.24648, 19.81703], [3.12501, 19.1366], [3.36082, 18.9745], [4.26651, 19.14224], [5.8153, 19.45101], [7.38361, 20.79165], [7.48273, 20.87258], [11.96886, 23.51735], [11.62498, 24.26669], [11.41061, 24.21456], [10.85323, 24.5595], [10.33159, 24.5465], [10.02432, 24.98124], [10.03146, 25.35635], [9.38834, 26.19288], [9.51696, 26.39148], [9.89569, 26.57696], [9.78136, 29.40961], [9.3876, 30.16738], [9.55544, 30.23971], [9.07483, 32.07865], [8.35999, 32.50101], [8.31895, 32.83483], [8.1179, 33.05086], [8.11433, 33.10175], [7.83028, 33.18851], [7.73687, 33.42114], [7.54088, 33.7726], [7.52851, 34.06493], [7.66174, 34.20167], [7.74207, 34.16492], [7.81242, 34.21841], [7.86264, 34.3987], [8.20482, 34.57575], [8.29655, 34.72798], [8.25189, 34.92009], [8.30727, 34.95378], [8.3555, 35.10007], [8.47318, 35.23376], [8.30329, 35.29884], [8.36086, 35.47774], [8.35371, 35.66373], [8.26472, 35.73669], [8.2626, 35.91733], [8.40731, 36.42208], [8.18936, 36.44939], [8.16167, 36.48817], [8.47609, 36.66607], [8.46537, 36.7706], [8.57613, 36.78062], [8.67706, 36.8364], [8.62972, 36.86499], [8.64044, 36.9401], [8.59123, 37.14286]]]]
23965           }
23966         }, {
23967           type: "Feature",
23968           properties: {
23969             iso1A2: "EA",
23970             wikidata: "Q28868874",
23971             nameEn: "Ceuta, Melilla",
23972             country: "ES",
23973             level: "territory",
23974             isoStatus: "excRes"
23975           },
23976           geometry: null
23977         }, {
23978           type: "Feature",
23979           properties: {
23980             iso1A2: "EC",
23981             iso1A3: "ECU",
23982             iso1N3: "218",
23983             wikidata: "Q736",
23984             nameEn: "Ecuador"
23985           },
23986           geometry: null
23987         }, {
23988           type: "Feature",
23989           properties: {
23990             iso1A2: "EE",
23991             iso1A3: "EST",
23992             iso1N3: "233",
23993             wikidata: "Q191",
23994             nameEn: "Estonia",
23995             aliases: ["EW"],
23996             groups: ["EU", "154", "150", "UN"],
23997             callingCodes: ["372"]
23998           },
23999           geometry: {
24000             type: "MultiPolygon",
24001             coordinates: [[[[26.32936, 60.00121], [20.5104, 59.15546], [19.84909, 57.57876], [22.80496, 57.87798], [23.20055, 57.56697], [24.26221, 57.91787], [24.3579, 57.87471], [25.19484, 58.0831], [25.28237, 57.98539], [25.29581, 58.08288], [25.73499, 57.90193], [26.05949, 57.84744], [26.0324, 57.79037], [26.02456, 57.78342], [26.027, 57.78158], [26.0266, 57.77441], [26.02069, 57.77169], [26.02415, 57.76865], [26.03332, 57.7718], [26.0543, 57.76105], [26.08098, 57.76619], [26.2029, 57.7206], [26.1866, 57.6849], [26.29253, 57.59244], [26.46527, 57.56885], [26.54675, 57.51813], [26.90364, 57.62823], [27.34698, 57.52242], [27.31919, 57.57672], [27.40393, 57.62125], [27.3746, 57.66834], [27.52615, 57.72843], [27.50171, 57.78842], [27.56689, 57.83356], [27.78526, 57.83963], [27.81841, 57.89244], [27.67282, 57.92627], [27.62393, 58.09462], [27.48541, 58.22615], [27.55489, 58.39525], [27.36366, 58.78381], [27.74429, 58.98351], [27.80482, 59.1116], [27.87978, 59.18097], [27.90911, 59.24353], [28.00689, 59.28351], [28.14215, 59.28934], [28.19284, 59.35791], [28.20537, 59.36491], [28.21137, 59.38058], [28.19061, 59.39962], [28.04187, 59.47017], [27.85643, 59.58538], [26.90044, 59.63819], [26.32936, 60.00121]]]]
24002           }
24003         }, {
24004           type: "Feature",
24005           properties: {
24006             iso1A2: "EG",
24007             iso1A3: "EGY",
24008             iso1N3: "818",
24009             wikidata: "Q79",
24010             nameEn: "Egypt",
24011             groups: ["015", "002", "UN"],
24012             callingCodes: ["20"]
24013           },
24014           geometry: {
24015             type: "MultiPolygon",
24016             coordinates: [[[[33.62659, 31.82938], [26.92891, 33.39516], [24.8458, 31.39877], [25.01077, 30.73861], [24.71117, 30.17441], [24.99968, 29.24574], [24.99885, 21.99535], [33.17563, 22.00405], [34.0765, 22.00501], [37.8565, 22.00903], [34.4454, 27.91479], [34.8812, 29.36878], [34.92298, 29.45305], [34.26742, 31.21998], [34.24012, 31.29591], [34.23572, 31.2966], [34.21853, 31.32363], [34.052, 31.46619], [33.62659, 31.82938]]]]
24017           }
24018         }, {
24019           type: "Feature",
24020           properties: {
24021             iso1A2: "EH",
24022             iso1A3: "ESH",
24023             iso1N3: "732",
24024             wikidata: "Q6250",
24025             nameEn: "Western Sahara",
24026             groups: ["015", "002"],
24027             callingCodes: ["212"]
24028           },
24029           geometry: {
24030             type: "MultiPolygon",
24031             coordinates: [[[[-8.66879, 27.6666], [-8.77527, 27.66663], [-8.71787, 26.9898], [-9.08698, 26.98639], [-9.56957, 26.90042], [-9.81998, 26.71379], [-10.68417, 26.90984], [-11.35695, 26.8505], [-11.23622, 26.72023], [-11.38635, 26.611], [-11.62052, 26.05229], [-12.06001, 26.04442], [-12.12281, 25.13682], [-12.92147, 24.39502], [-13.00628, 24.01923], [-13.75627, 23.77231], [-14.10361, 22.75501], [-14.1291, 22.41636], [-14.48112, 22.00886], [-14.47329, 21.63839], [-14.78487, 21.36587], [-16.44269, 21.39745], [-16.9978, 21.36239], [-17.02707, 21.34022], [-17.21511, 21.34226], [-17.35589, 20.80492], [-17.0471, 20.76408], [-17.0695, 20.85742], [-17.06781, 20.92697], [-17.0396, 20.9961], [-17.0357, 21.05368], [-16.99806, 21.12142], [-16.95474, 21.33997], [-13.01525, 21.33343], [-13.08438, 22.53866], [-13.15313, 22.75649], [-13.10753, 22.89493], [-13.00412, 23.02297], [-12.5741, 23.28975], [-12.36213, 23.3187], [-12.14969, 23.41935], [-12.00251, 23.4538], [-12.0002, 25.9986], [-8.66721, 25.99918], [-8.66674, 27.31569], [-8.66879, 27.6666]]]]
24032           }
24033         }, {
24034           type: "Feature",
24035           properties: {
24036             iso1A2: "ER",
24037             iso1A3: "ERI",
24038             iso1N3: "232",
24039             wikidata: "Q986",
24040             nameEn: "Eritrea",
24041             groups: ["014", "202", "002", "UN"],
24042             callingCodes: ["291"]
24043           },
24044           geometry: {
24045             type: "MultiPolygon",
24046             coordinates: [[[[40.99158, 15.81743], [39.63762, 18.37348], [38.57727, 17.98125], [38.45916, 17.87167], [38.37133, 17.66269], [38.13362, 17.53906], [37.50967, 17.32199], [37.42694, 17.04041], [36.99777, 17.07172], [36.92193, 16.23451], [36.76371, 15.80831], [36.69761, 15.75323], [36.54276, 15.23478], [36.44337, 15.14963], [36.54376, 14.25597], [36.56536, 14.26177], [36.55659, 14.28237], [36.63364, 14.31172], [36.85787, 14.32201], [37.01622, 14.2561], [37.09486, 14.27155], [37.13206, 14.40746], [37.3106, 14.44657], [37.47319, 14.2149], [37.528, 14.18413], [37.91287, 14.89447], [38.0364, 14.72745], [38.25562, 14.67287], [38.3533, 14.51323], [38.45748, 14.41445], [38.78306, 14.4754], [38.98058, 14.54895], [39.02834, 14.63717], [39.16074, 14.65187], [39.14772, 14.61827], [39.19547, 14.56996], [39.23888, 14.56365], [39.26927, 14.48801], [39.2302, 14.44598], [39.2519, 14.40393], [39.37685, 14.54402], [39.52756, 14.49011], [39.50585, 14.55735], [39.58182, 14.60987], [39.76632, 14.54264], [39.9443, 14.41024], [40.07236, 14.54264], [40.14649, 14.53969], [40.21128, 14.39342], [40.25686, 14.41445], [40.9167, 14.11152], [41.25097, 13.60787], [41.62864, 13.38626], [42.05841, 12.80912], [42.21469, 12.75832], [42.2798, 12.6355], [42.4037, 12.46478], [42.46941, 12.52661], [42.6957, 12.36201], [42.7996, 12.42629], [42.86195, 12.58747], [43.29075, 12.79154], [40.99158, 15.81743]]]]
24047           }
24048         }, {
24049           type: "Feature",
24050           properties: {
24051             iso1A2: "ES",
24052             iso1A3: "ESP",
24053             iso1N3: "724",
24054             wikidata: "Q29",
24055             nameEn: "Spain"
24056           },
24057           geometry: null
24058         }, {
24059           type: "Feature",
24060           properties: {
24061             iso1A2: "ET",
24062             iso1A3: "ETH",
24063             iso1N3: "231",
24064             wikidata: "Q115",
24065             nameEn: "Ethiopia",
24066             groups: ["014", "202", "002", "UN"],
24067             callingCodes: ["251"]
24068           },
24069           geometry: {
24070             type: "MultiPolygon",
24071             coordinates: [[[[42.4037, 12.46478], [42.2798, 12.6355], [42.21469, 12.75832], [42.05841, 12.80912], [41.62864, 13.38626], [41.25097, 13.60787], [40.9167, 14.11152], [40.25686, 14.41445], [40.21128, 14.39342], [40.14649, 14.53969], [40.07236, 14.54264], [39.9443, 14.41024], [39.76632, 14.54264], [39.58182, 14.60987], [39.50585, 14.55735], [39.52756, 14.49011], [39.37685, 14.54402], [39.2519, 14.40393], [39.2302, 14.44598], [39.26927, 14.48801], [39.23888, 14.56365], [39.19547, 14.56996], [39.14772, 14.61827], [39.16074, 14.65187], [39.02834, 14.63717], [38.98058, 14.54895], [38.78306, 14.4754], [38.45748, 14.41445], [38.3533, 14.51323], [38.25562, 14.67287], [38.0364, 14.72745], [37.91287, 14.89447], [37.528, 14.18413], [37.47319, 14.2149], [37.3106, 14.44657], [37.13206, 14.40746], [37.09486, 14.27155], [37.01622, 14.2561], [36.85787, 14.32201], [36.63364, 14.31172], [36.55659, 14.28237], [36.56536, 14.26177], [36.54376, 14.25597], [36.44653, 13.95666], [36.48824, 13.83954], [36.38993, 13.56459], [36.24545, 13.36759], [36.13374, 12.92665], [36.16651, 12.88019], [36.14268, 12.70879], [36.01458, 12.72478], [35.70476, 12.67101], [35.24302, 11.91132], [35.11492, 11.85156], [35.05832, 11.71158], [35.09556, 11.56278], [34.95704, 11.24448], [35.01215, 11.19626], [34.93172, 10.95946], [34.97789, 10.91559], [34.97491, 10.86147], [34.86916, 10.78832], [34.86618, 10.74588], [34.77532, 10.69027], [34.77383, 10.74588], [34.59062, 10.89072], [34.4372, 10.781], [34.2823, 10.53508], [34.34783, 10.23914], [34.32102, 10.11599], [34.22718, 10.02506], [34.20484, 9.9033], [34.13186, 9.7492], [34.08717, 9.55243], [34.10229, 9.50238], [34.14304, 9.04654], [34.14453, 8.60204], [34.01346, 8.50041], [33.89579, 8.4842], [33.87195, 8.41938], [33.71407, 8.3678], [33.66938, 8.44442], [33.54575, 8.47094], [33.3119, 8.45474], [33.19721, 8.40317], [33.1853, 8.29264], [33.18083, 8.13047], [33.08401, 8.05822], [33.0006, 7.90333], [33.04944, 7.78989], [33.24637, 7.77939], [33.32531, 7.71297], [33.44745, 7.7543], [33.71407, 7.65983], [33.87642, 7.5491], [34.02984, 7.36449], [34.03878, 7.27437], [34.01495, 7.25664], [34.19369, 7.12807], [34.19369, 7.04382], [34.35753, 6.91963], [34.47669, 6.91076], [34.53925, 6.82794], [34.53776, 6.74808], [34.65096, 6.72589], [34.77459, 6.5957], [34.87736, 6.60161], [35.01738, 6.46991], [34.96227, 6.26415], [35.00546, 5.89387], [35.12611, 5.68937], [35.13058, 5.62118], [35.31188, 5.50106], [35.29938, 5.34042], [35.50792, 5.42431], [35.8576, 5.33413], [35.81968, 5.10757], [35.82118, 4.77382], [35.9419, 4.61933], [35.95449, 4.53244], [36.03924, 4.44406], [36.84474, 4.44518], [37.07724, 4.33503], [38.14168, 3.62487], [38.45812, 3.60445], [38.52336, 3.62551], [38.91938, 3.51198], [39.07736, 3.5267], [39.19954, 3.47834], [39.49444, 3.45521], [39.51551, 3.40895], [39.55132, 3.39634], [39.58339, 3.47434], [39.76808, 3.67058], [39.86043, 3.86974], [40.77498, 4.27683], [41.1754, 3.94079], [41.89488, 3.97375], [42.07619, 4.17667], [42.55853, 4.20518], [42.84526, 4.28357], [42.97746, 4.44032], [43.04177, 4.57923], [43.40263, 4.79289], [44.02436, 4.9451], [44.98104, 4.91821], [47.97917, 8.00124], [47.92477, 8.00111], [46.99339, 7.9989], [44.19222, 8.93028], [43.32613, 9.59205], [43.23518, 9.84605], [43.0937, 9.90579], [42.87643, 10.18441], [42.69452, 10.62672], [42.95776, 10.98533], [42.79037, 10.98493], [42.75111, 11.06992], [42.62989, 11.09711], [42.42669, 10.98493], [42.13691, 10.97586], [42.06302, 10.92599], [41.80056, 10.97127], [41.8096, 11.33606], [41.77727, 11.49902], [41.82878, 11.72361], [41.95461, 11.81157], [42.4037, 12.46478]]]]
24072           }
24073         }, {
24074           type: "Feature",
24075           properties: {
24076             iso1A2: "EU",
24077             iso1A3: "EUE",
24078             wikidata: "Q458",
24079             nameEn: "European Union",
24080             level: "union",
24081             isoStatus: "excRes"
24082           },
24083           geometry: null
24084         }, {
24085           type: "Feature",
24086           properties: {
24087             iso1A2: "FI",
24088             iso1A3: "FIN",
24089             iso1N3: "246",
24090             wikidata: "Q33",
24091             nameEn: "Finland",
24092             aliases: ["SF"]
24093           },
24094           geometry: null
24095         }, {
24096           type: "Feature",
24097           properties: {
24098             iso1A2: "FJ",
24099             iso1A3: "FJI",
24100             iso1N3: "242",
24101             wikidata: "Q712",
24102             nameEn: "Fiji",
24103             groups: ["054", "009", "UN"],
24104             driveSide: "left",
24105             callingCodes: ["679"]
24106           },
24107           geometry: {
24108             type: "MultiPolygon",
24109             coordinates: [[[[174.245, -23.1974], [179.99999, -22.5], [179.99999, -11.5], [174, -11.5], [174.245, -23.1974]]], [[[-176.76826, -14.95183], [-180, -14.96041], [-180, -22.90585], [-176.74538, -22.89767], [-176.76826, -14.95183]]]]
24110           }
24111         }, {
24112           type: "Feature",
24113           properties: {
24114             iso1A2: "FK",
24115             iso1A3: "FLK",
24116             iso1N3: "238",
24117             wikidata: "Q9648",
24118             nameEn: "Falkland Islands",
24119             country: "GB",
24120             groups: ["BOTS", "005", "419", "019", "UN"],
24121             driveSide: "left",
24122             roadSpeedUnit: "mph",
24123             roadHeightUnit: "ft",
24124             callingCodes: ["500"]
24125           },
24126           geometry: {
24127             type: "MultiPolygon",
24128             coordinates: [[[[-63.67376, -55.11859], [-54.56126, -51.26248], [-61.26735, -50.63919], [-63.67376, -55.11859]]]]
24129           }
24130         }, {
24131           type: "Feature",
24132           properties: {
24133             iso1A2: "FM",
24134             iso1A3: "FSM",
24135             iso1N3: "583",
24136             wikidata: "Q702",
24137             nameEn: "Federated States of Micronesia",
24138             groups: ["057", "009", "UN"],
24139             roadSpeedUnit: "mph",
24140             roadHeightUnit: "ft",
24141             callingCodes: ["691"]
24142           },
24143           geometry: {
24144             type: "MultiPolygon",
24145             coordinates: [[[[138.20583, 13.3783], [136.27107, 6.73747], [156.88247, -1.39237], [165.19726, 6.22546], [138.20583, 13.3783]]]]
24146           }
24147         }, {
24148           type: "Feature",
24149           properties: {
24150             iso1A2: "FO",
24151             iso1A3: "FRO",
24152             iso1N3: "234",
24153             wikidata: "Q4628",
24154             nameEn: "Faroe Islands",
24155             country: "DK",
24156             groups: ["154", "150", "UN"],
24157             callingCodes: ["298"]
24158           },
24159           geometry: {
24160             type: "MultiPolygon",
24161             coordinates: [[[[-8.51774, 62.35338], [-6.51083, 60.95272], [-5.70102, 62.77194], [-8.51774, 62.35338]]]]
24162           }
24163         }, {
24164           type: "Feature",
24165           properties: {
24166             iso1A2: "FR",
24167             iso1A3: "FRA",
24168             iso1N3: "250",
24169             wikidata: "Q142",
24170             nameEn: "France"
24171           },
24172           geometry: null
24173         }, {
24174           type: "Feature",
24175           properties: {
24176             iso1A2: "FX",
24177             iso1A3: "FXX",
24178             iso1N3: "249",
24179             wikidata: "Q212429",
24180             nameEn: "Metropolitan France",
24181             country: "FR",
24182             groups: ["EU", "155", "150", "UN"],
24183             isoStatus: "excRes",
24184             callingCodes: ["33"]
24185           },
24186           geometry: {
24187             type: "MultiPolygon",
24188             coordinates: [[[[2.55904, 51.07014], [2.18458, 51.52087], [1.17405, 50.74239], [-2.02963, 49.91866], [-2.09454, 49.46288], [-1.83944, 49.23037], [-2.00491, 48.86706], [-2.65349, 49.15373], [-6.28985, 48.93406], [-1.81005, 43.59738], [-1.77289, 43.38957], [-1.79319, 43.37497], [-1.78332, 43.36399], [-1.78714, 43.35476], [-1.77068, 43.34396], [-1.75334, 43.34107], [-1.75079, 43.3317], [-1.7397, 43.32979], [-1.73074, 43.29481], [-1.69407, 43.31378], [-1.62481, 43.30726], [-1.63052, 43.28591], [-1.61341, 43.25269], [-1.57674, 43.25269], [-1.55963, 43.28828], [-1.50992, 43.29481], [-1.45289, 43.27049], [-1.40942, 43.27272], [-1.3758, 43.24511], [-1.41562, 43.12815], [-1.47555, 43.08372], [-1.44067, 43.047], [-1.35272, 43.02658], [-1.34419, 43.09665], [-1.32209, 43.1127], [-1.27118, 43.11961], [-1.30052, 43.09581], [-1.30531, 43.06859], [-1.25244, 43.04164], [-1.22881, 43.05534], [-1.10333, 43.0059], [-1.00963, 42.99279], [-0.97133, 42.96239], [-0.81652, 42.95166], [-0.75478, 42.96916], [-0.72037, 42.92541], [-0.73422, 42.91228], [-0.72608, 42.89318], [-0.69837, 42.87945], [-0.67637, 42.88303], [-0.55497, 42.77846], [-0.50863, 42.82713], [-0.44334, 42.79939], [-0.41319, 42.80776], [-0.38833, 42.80132], [-0.3122, 42.84788], [-0.17939, 42.78974], [-0.16141, 42.79535], [-0.10519, 42.72761], [-0.02468, 42.68513], [0.17569, 42.73424], [0.25336, 42.7174], [0.29407, 42.67431], [0.36251, 42.72282], [0.40214, 42.69779], [0.67873, 42.69458], [0.65421, 42.75872], [0.66121, 42.84021], [0.711, 42.86372], [0.93089, 42.79154], [0.96166, 42.80629], [0.98292, 42.78754], [1.0804, 42.78569], [1.15928, 42.71407], [1.35562, 42.71944], [1.44197, 42.60217], [1.47986, 42.61346], [1.46718, 42.63296], [1.48043, 42.65203], [1.50867, 42.64483], [1.55418, 42.65669], [1.60085, 42.62703], [1.63485, 42.62957], [1.6625, 42.61982], [1.68267, 42.62533], [1.73452, 42.61515], [1.72588, 42.59098], [1.7858, 42.57698], [1.73683, 42.55492], [1.72515, 42.50338], [1.76335, 42.48863], [1.83037, 42.48395], [1.88853, 42.4501], [1.93663, 42.45439], [1.94292, 42.44316], [1.94061, 42.43333], [1.94084, 42.43039], [1.9574, 42.42401], [1.96482, 42.37787], [2.00488, 42.35399], [2.06241, 42.35906], [2.11621, 42.38393], [2.12789, 42.41291], [2.16599, 42.42314], [2.20578, 42.41633], [2.25551, 42.43757], [2.38504, 42.39977], [2.43299, 42.39423], [2.43508, 42.37568], [2.48457, 42.33933], [2.54382, 42.33406], [2.55516, 42.35351], [2.57934, 42.35808], [2.6747, 42.33974], [2.65311, 42.38771], [2.72056, 42.42298], [2.75497, 42.42578], [2.77464, 42.41046], [2.84335, 42.45724], [2.85675, 42.45444], [2.86983, 42.46843], [2.88413, 42.45938], [2.92107, 42.4573], [2.94283, 42.48174], [2.96518, 42.46692], [3.03734, 42.47363], [3.08167, 42.42748], [3.10027, 42.42621], [3.11379, 42.43646], [3.17156, 42.43545], [3.75438, 42.33445], [7.60802, 41.05927], [10.09675, 41.44089], [9.56115, 43.20816], [7.50102, 43.51859], [7.42422, 43.72209], [7.40903, 43.7296], [7.41113, 43.73156], [7.41291, 43.73168], [7.41298, 43.73311], [7.41233, 43.73439], [7.42062, 43.73977], [7.42299, 43.74176], [7.42443, 43.74087], [7.42809, 43.74396], [7.43013, 43.74895], [7.43624, 43.75014], [7.43708, 43.75197], [7.4389, 43.75151], [7.4379, 43.74963], [7.47823, 43.73341], [7.53006, 43.78405], [7.50423, 43.84345], [7.49355, 43.86551], [7.51162, 43.88301], [7.56075, 43.89932], [7.56858, 43.94506], [7.60771, 43.95772], [7.65266, 43.9763], [7.66848, 43.99943], [7.6597, 44.03009], [7.72508, 44.07578], [7.66878, 44.12795], [7.68694, 44.17487], [7.63245, 44.17877], [7.62155, 44.14881], [7.36364, 44.11882], [7.34547, 44.14359], [7.27827, 44.1462], [7.16929, 44.20352], [7.00764, 44.23736], [6.98221, 44.28289], [6.89171, 44.36637], [6.88784, 44.42043], [6.94504, 44.43112], [6.86233, 44.49834], [6.85507, 44.53072], [6.96042, 44.62129], [6.95133, 44.66264], [7.00582, 44.69364], [7.07484, 44.68073], [7.00401, 44.78782], [7.02217, 44.82519], [6.93499, 44.8664], [6.90774, 44.84322], [6.75518, 44.89915], [6.74519, 44.93661], [6.74791, 45.01939], [6.66981, 45.02324], [6.62803, 45.11175], [6.7697, 45.16044], [6.85144, 45.13226], [6.96706, 45.20841], [7.07074, 45.21228], [7.13115, 45.25386], [7.10572, 45.32924], [7.18019, 45.40071], [7.00037, 45.509], [6.98948, 45.63869], [6.80785, 45.71864], [6.80785, 45.83265], [6.95315, 45.85163], [7.04151, 45.92435], [7.00946, 45.9944], [6.93862, 46.06502], [6.87868, 46.03855], [6.89321, 46.12548], [6.78968, 46.14058], [6.86052, 46.28512], [6.77152, 46.34784], [6.8024, 46.39171], [6.82312, 46.42661], [6.53358, 46.45431], [6.25432, 46.3632], [6.21981, 46.31304], [6.24826, 46.30175], [6.25137, 46.29014], [6.23775, 46.27822], [6.24952, 46.26255], [6.26749, 46.24745], [6.29474, 46.26221], [6.31041, 46.24417], [6.29663, 46.22688], [6.27694, 46.21566], [6.26007, 46.21165], [6.24821, 46.20531], [6.23913, 46.20511], [6.23544, 46.20714], [6.22175, 46.20045], [6.22222, 46.19888], [6.21844, 46.19837], [6.21603, 46.19507], [6.21273, 46.19409], [6.21114, 46.1927], [6.20539, 46.19163], [6.19807, 46.18369], [6.19552, 46.18401], [6.18707, 46.17999], [6.18871, 46.16644], [6.18116, 46.16187], [6.15305, 46.15194], [6.13397, 46.1406], [6.09926, 46.14373], [6.09199, 46.15191], [6.07491, 46.14879], [6.05203, 46.15191], [6.04564, 46.14031], [6.03614, 46.13712], [6.01791, 46.14228], [5.9871, 46.14499], [5.97893, 46.13303], [5.95781, 46.12925], [5.9641, 46.14412], [5.97508, 46.15863], [5.98188, 46.17392], [5.98846, 46.17046], [5.99573, 46.18587], [5.96515, 46.19638], [5.97542, 46.21525], [6.02461, 46.23313], [6.03342, 46.2383], [6.04602, 46.23127], [6.05029, 46.23518], [6.0633, 46.24583], [6.07072, 46.24085], [6.08563, 46.24651], [6.10071, 46.23772], [6.12446, 46.25059], [6.11926, 46.2634], [6.1013, 46.28512], [6.11697, 46.29547], [6.1198, 46.31157], [6.13876, 46.33844], [6.15738, 46.3491], [6.16987, 46.36759], [6.15985, 46.37721], [6.15016, 46.3778], [6.09926, 46.40768], [6.06407, 46.41676], [6.08427, 46.44305], [6.07269, 46.46244], [6.1567, 46.54402], [6.11084, 46.57649], [6.27135, 46.68251], [6.38351, 46.73171], [6.45209, 46.77502], [6.43216, 46.80336], [6.46456, 46.88865], [6.43341, 46.92703], [6.71531, 47.0494], [6.68823, 47.06616], [6.76788, 47.1208], [6.8489, 47.15933], [6.9508, 47.24338], [6.95108, 47.26428], [6.94316, 47.28747], [7.05305, 47.33304], [7.0564, 47.35134], [7.03125, 47.36996], [6.87959, 47.35335], [6.88542, 47.37262], [6.93744, 47.40714], [6.93953, 47.43388], [7.0024, 47.45264], [6.98425, 47.49432], [7.0231, 47.50522], [7.07425, 47.48863], [7.12781, 47.50371], [7.16249, 47.49025], [7.19583, 47.49455], [7.17026, 47.44312], [7.24669, 47.4205], [7.33526, 47.44186], [7.35603, 47.43432], [7.40308, 47.43638], [7.43088, 47.45846], [7.4462, 47.46264], [7.4583, 47.47216], [7.42923, 47.48628], [7.43356, 47.49712], [7.47534, 47.47932], [7.51076, 47.49651], [7.49804, 47.51798], [7.5229, 47.51644], [7.53199, 47.5284], [7.51904, 47.53515], [7.50588, 47.52856], [7.49691, 47.53821], [7.50873, 47.54546], [7.51723, 47.54578], [7.52831, 47.55347], [7.53634, 47.55553], [7.55652, 47.56779], [7.55689, 47.57232], [7.56548, 47.57617], [7.56684, 47.57785], [7.58386, 47.57536], [7.58945, 47.59017], [7.59301, 47.60058], [7.58851, 47.60794], [7.57423, 47.61628], [7.5591, 47.63849], [7.53384, 47.65115], [7.52067, 47.66437], [7.51915, 47.68335], [7.51266, 47.70197], [7.53722, 47.71635], [7.54761, 47.72912], [7.52921, 47.77747], [7.55673, 47.87371], [7.62302, 47.97898], [7.56966, 48.03265], [7.57137, 48.12292], [7.6648, 48.22219], [7.69022, 48.30018], [7.74562, 48.32736], [7.73109, 48.39192], [7.76833, 48.48945], [7.80647, 48.51239], [7.80167, 48.54758], [7.80057, 48.5857], [7.84098, 48.64217], [7.89002, 48.66317], [7.96812, 48.72491], [7.96994, 48.75606], [8.01534, 48.76085], [8.0326, 48.79017], [8.06802, 48.78957], [8.10253, 48.81829], [8.12813, 48.87985], [8.19989, 48.95825], [8.20031, 48.95856], [8.22604, 48.97352], [8.14189, 48.97833], [7.97783, 49.03161], [7.93641, 49.05544], [7.86386, 49.03499], [7.79557, 49.06583], [7.75948, 49.04562], [7.63618, 49.05428], [7.62575, 49.07654], [7.56416, 49.08136], [7.53012, 49.09818], [7.49172, 49.13915], [7.49473, 49.17], [7.44455, 49.16765], [7.44052, 49.18354], [7.3662, 49.17308], [7.35995, 49.14399], [7.3195, 49.14231], [7.29514, 49.11426], [7.23473, 49.12971], [7.1593, 49.1204], [7.1358, 49.1282], [7.12504, 49.14253], [7.10384, 49.13787], [7.10715, 49.15631], [7.07859, 49.15031], [7.09007, 49.13094], [7.07162, 49.1255], [7.06642, 49.11415], [7.05548, 49.11185], [7.04843, 49.11422], [7.04409, 49.12123], [7.04662, 49.13724], [7.03178, 49.15734], [7.0274, 49.17042], [7.03459, 49.19096], [7.01318, 49.19018], [6.97273, 49.2099], [6.95963, 49.203], [6.94028, 49.21641], [6.93831, 49.2223], [6.91875, 49.22261], [6.89298, 49.20863], [6.85939, 49.22376], [6.83555, 49.21249], [6.85119, 49.20038], [6.85016, 49.19354], [6.86225, 49.18185], [6.84703, 49.15734], [6.83385, 49.15162], [6.78265, 49.16793], [6.73765, 49.16375], [6.71137, 49.18808], [6.73256, 49.20486], [6.71843, 49.2208], [6.69274, 49.21661], [6.66583, 49.28065], [6.60186, 49.31055], [6.572, 49.35027], [6.58807, 49.35358], [6.60091, 49.36864], [6.533, 49.40748], [6.55404, 49.42464], [6.42432, 49.47683], [6.40274, 49.46546], [6.39168, 49.4667], [6.38352, 49.46463], [6.36778, 49.46937], [6.3687, 49.4593], [6.28818, 49.48465], [6.27875, 49.503], [6.25029, 49.50609], [6.2409, 49.51408], [6.19543, 49.50536], [6.17386, 49.50934], [6.15366, 49.50226], [6.16115, 49.49297], [6.14321, 49.48796], [6.12814, 49.49365], [6.12346, 49.4735], [6.10325, 49.4707], [6.09845, 49.46351], [6.10072, 49.45268], [6.08373, 49.45594], [6.07887, 49.46399], [6.05553, 49.46663], [6.04176, 49.44801], [6.02743, 49.44845], [6.02648, 49.45451], [5.97693, 49.45513], [5.96876, 49.49053], [5.94224, 49.49608], [5.94128, 49.50034], [5.86571, 49.50015], [5.83389, 49.52152], [5.83467, 49.52717], [5.84466, 49.53027], [5.83648, 49.5425], [5.81664, 49.53775], [5.80871, 49.5425], [5.81838, 49.54777], [5.79195, 49.55228], [5.77435, 49.56298], [5.7577, 49.55915], [5.75649, 49.54321], [5.64505, 49.55146], [5.60909, 49.51228], [5.55001, 49.52729], [5.46541, 49.49825], [5.46734, 49.52648], [5.43713, 49.5707], [5.3974, 49.61596], [5.34837, 49.62889], [5.33851, 49.61599], [5.3137, 49.61225], [5.30214, 49.63055], [5.33039, 49.6555], [5.31465, 49.66846], [5.26232, 49.69456], [5.14545, 49.70287], [5.09249, 49.76193], [4.96714, 49.79872], [4.85464, 49.78995], [4.86965, 49.82271], [4.85134, 49.86457], [4.88529, 49.9236], [4.78827, 49.95609], [4.8382, 50.06738], [4.88602, 50.15182], [4.83279, 50.15331], [4.82438, 50.16878], [4.75237, 50.11314], [4.70064, 50.09384], [4.68695, 49.99685], [4.5414, 49.96911], [4.51098, 49.94659], [4.43488, 49.94122], [4.35051, 49.95315], [4.31963, 49.97043], [4.20532, 49.95803], [4.14239, 49.98034], [4.13508, 50.01976], [4.16294, 50.04719], [4.23101, 50.06945], [4.20147, 50.13535], [4.13561, 50.13078], [4.16014, 50.19239], [4.15524, 50.21103], [4.21945, 50.25539], [4.20651, 50.27333], [4.17861, 50.27443], [4.17347, 50.28838], [4.15524, 50.2833], [4.16808, 50.25786], [4.13665, 50.25609], [4.11954, 50.30425], [4.10957, 50.30234], [4.10237, 50.31247], [4.0689, 50.3254], [4.0268, 50.35793], [3.96771, 50.34989], [3.90781, 50.32814], [3.84314, 50.35219], [3.73911, 50.34809], [3.70987, 50.3191], [3.71009, 50.30305], [3.66976, 50.34563], [3.65709, 50.36873], [3.67262, 50.38663], [3.67494, 50.40239], [3.66153, 50.45165], [3.64426, 50.46275], [3.61014, 50.49568], [3.58361, 50.49049], [3.5683, 50.50192], [3.49509, 50.48885], [3.51564, 50.5256], [3.47385, 50.53397], [3.44629, 50.51009], [3.37693, 50.49538], [3.28575, 50.52724], [3.2729, 50.60718], [3.23951, 50.6585], [3.264, 50.67668], [3.2536, 50.68977], [3.26141, 50.69151], [3.26063, 50.70086], [3.24593, 50.71389], [3.22042, 50.71019], [3.20845, 50.71662], [3.19017, 50.72569], [3.20064, 50.73547], [3.18811, 50.74025], [3.18339, 50.74981], [3.16476, 50.76843], [3.15017, 50.79031], [3.1257, 50.78603], [3.11987, 50.79188], [3.11206, 50.79416], [3.10614, 50.78303], [3.09163, 50.77717], [3.04314, 50.77674], [3.00537, 50.76588], [2.96778, 50.75242], [2.95019, 50.75138], [2.90873, 50.702], [2.91036, 50.6939], [2.90069, 50.69263], [2.88504, 50.70656], [2.87937, 50.70298], [2.86985, 50.7033], [2.8483, 50.72276], [2.81056, 50.71773], [2.71165, 50.81295], [2.63331, 50.81457], [2.59093, 50.91751], [2.63074, 50.94746], [2.57551, 51.00326], [2.55904, 51.07014]], [[1.99838, 42.44682], [1.98378, 42.44697], [1.96125, 42.45364], [1.95606, 42.45785], [1.96215, 42.47854], [1.97003, 42.48081], [1.97227, 42.48487], [1.97697, 42.48568], [1.98022, 42.49569], [1.98916, 42.49351], [1.99766, 42.4858], [1.98579, 42.47486], [1.99216, 42.46208], [2.01564, 42.45171], [1.99838, 42.44682]]]]
24189           }
24190         }, {
24191           type: "Feature",
24192           properties: {
24193             iso1A2: "GA",
24194             iso1A3: "GAB",
24195             iso1N3: "266",
24196             wikidata: "Q1000",
24197             nameEn: "Gabon",
24198             groups: ["017", "202", "002", "UN"],
24199             callingCodes: ["241"]
24200           },
24201           geometry: {
24202             type: "MultiPolygon",
24203             coordinates: [[[[13.29457, 2.16106], [13.28534, 2.25716], [11.37116, 2.29975], [11.3561, 2.17217], [11.35307, 1.00251], [9.79648, 1.0019], [9.75065, 1.06753], [9.66433, 1.06723], [7.24416, -0.64092], [10.75913, -4.39519], [11.12647, -3.94169], [11.22301, -3.69888], [11.48764, -3.51089], [11.57949, -3.52798], [11.68608, -3.68942], [11.87083, -3.71571], [11.92719, -3.62768], [11.8318, -3.5812], [11.96554, -3.30267], [11.70227, -3.17465], [11.70558, -3.0773], [11.80365, -3.00424], [11.64798, -2.81146], [11.5359, -2.85654], [11.64487, -2.61865], [11.57637, -2.33379], [11.74605, -2.39936], [11.96866, -2.33559], [12.04895, -2.41704], [12.47925, -2.32626], [12.44656, -1.92025], [12.61312, -1.8129], [12.82172, -1.91091], [13.02759, -2.33098], [13.47977, -2.43224], [13.75884, -2.09293], [13.92073, -2.35581], [13.85846, -2.46935], [14.10442, -2.49268], [14.23829, -2.33715], [14.16202, -2.23916], [14.23518, -2.15671], [14.25932, -1.97624], [14.41838, -1.89412], [14.52569, -0.57818], [14.41887, -0.44799], [14.2165, -0.38261], [14.06862, -0.20826], [13.90632, -0.2287], [13.88648, 0.26652], [14.10909, 0.58563], [14.26066, 0.57255], [14.48179, 0.9152], [14.25186, 1.39842], [13.89582, 1.4261], [13.15519, 1.23368], [13.25447, 1.32339], [13.13461, 1.57238], [13.29457, 2.16106]]]]
24204           }
24205         }, {
24206           type: "Feature",
24207           properties: {
24208             iso1A2: "GB",
24209             iso1A3: "GBR",
24210             iso1N3: "826",
24211             wikidata: "Q145",
24212             ccTLD: ".uk",
24213             nameEn: "United Kingdom",
24214             aliases: ["UK"]
24215           },
24216           geometry: null
24217         }, {
24218           type: "Feature",
24219           properties: {
24220             iso1A2: "GD",
24221             iso1A3: "GRD",
24222             iso1N3: "308",
24223             wikidata: "Q769",
24224             nameEn: "Grenada",
24225             aliases: ["WG"],
24226             groups: ["029", "003", "419", "019", "UN"],
24227             driveSide: "left",
24228             roadSpeedUnit: "mph",
24229             callingCodes: ["1 473"]
24230           },
24231           geometry: {
24232             type: "MultiPolygon",
24233             coordinates: [[[[-62.64026, 12.69984], [-61.77886, 11.36496], [-59.94058, 12.34011], [-62.64026, 12.69984]]]]
24234           }
24235         }, {
24236           type: "Feature",
24237           properties: {
24238             iso1A2: "GE",
24239             iso1A3: "GEO",
24240             iso1N3: "268",
24241             wikidata: "Q230",
24242             nameEn: "Georgia",
24243             groups: ["145", "142", "UN"],
24244             callingCodes: ["995"]
24245           },
24246           geometry: {
24247             type: "MultiPolygon",
24248             coordinates: [[[[46.42738, 41.91323], [45.61676, 42.20768], [45.78692, 42.48358], [45.36501, 42.55268], [45.15318, 42.70598], [44.88754, 42.74934], [44.80941, 42.61277], [44.70002, 42.74679], [44.54202, 42.75699], [43.95517, 42.55396], [43.73119, 42.62043], [43.81453, 42.74297], [43.0419, 43.02413], [43.03322, 43.08883], [42.75889, 43.19651], [42.66667, 43.13917], [42.40563, 43.23226], [41.64935, 43.22331], [40.65957, 43.56212], [40.10657, 43.57344], [40.04445, 43.47776], [40.03312, 43.44262], [40.01007, 43.42411], [40.01552, 43.42025], [40.00853, 43.40578], [40.0078, 43.38551], [39.81147, 43.06294], [40.89217, 41.72528], [41.54366, 41.52185], [41.7148, 41.4932], [41.7124, 41.47417], [41.81939, 41.43621], [41.95134, 41.52466], [42.26387, 41.49346], [42.51772, 41.43606], [42.59202, 41.58183], [42.72794, 41.59714], [42.84471, 41.58912], [42.78995, 41.50126], [42.84899, 41.47265], [42.8785, 41.50516], [43.02956, 41.37891], [43.21707, 41.30331], [43.13373, 41.25503], [43.1945, 41.25242], [43.23096, 41.17536], [43.36118, 41.2028], [43.44973, 41.17666], [43.4717, 41.12611], [43.67712, 41.13398], [43.74717, 41.1117], [43.84835, 41.16329], [44.16591, 41.19141], [44.18148, 41.24644], [44.32139, 41.2079], [44.34337, 41.20312], [44.34417, 41.2382], [44.46791, 41.18204], [44.59322, 41.1933], [44.61462, 41.24018], [44.72814, 41.20338], [44.82084, 41.21513], [44.87887, 41.20195], [44.89911, 41.21366], [44.84358, 41.23088], [44.81749, 41.23488], [44.80053, 41.25949], [44.81437, 41.30371], [44.93493, 41.25685], [45.0133, 41.29747], [45.09867, 41.34065], [45.1797, 41.42231], [45.26285, 41.46433], [45.31352, 41.47168], [45.4006, 41.42402], [45.45973, 41.45898], [45.68389, 41.3539], [45.71035, 41.36208], [45.75705, 41.35157], [45.69946, 41.29545], [45.80842, 41.2229], [45.95786, 41.17956], [46.13221, 41.19479], [46.27698, 41.19011], [46.37661, 41.10805], [46.456, 41.09984], [46.48558, 41.0576], [46.55096, 41.1104], [46.63969, 41.09515], [46.66148, 41.20533], [46.72375, 41.28609], [46.63658, 41.37727], [46.4669, 41.43331], [46.40307, 41.48464], [46.33925, 41.4963], [46.29794, 41.5724], [46.34126, 41.57454], [46.34039, 41.5947], [46.3253, 41.60912], [46.28182, 41.60089], [46.26531, 41.63339], [46.24429, 41.59883], [46.19759, 41.62327], [46.17891, 41.72094], [46.20538, 41.77205], [46.23962, 41.75811], [46.30863, 41.79133], [46.3984, 41.84399], [46.42738, 41.91323]]]]
24249           }
24250         }, {
24251           type: "Feature",
24252           properties: {
24253             iso1A2: "GF",
24254             iso1A3: "GUF",
24255             iso1N3: "254",
24256             wikidata: "Q3769",
24257             nameEn: "French Guiana",
24258             country: "FR",
24259             groups: ["Q3320166", "EU", "005", "419", "019", "UN"],
24260             callingCodes: ["594"]
24261           },
24262           geometry: {
24263             type: "MultiPolygon",
24264             coordinates: [[[[-51.35485, 4.8383], [-53.7094, 6.2264], [-54.01074, 5.68785], [-54.01877, 5.52789], [-54.26916, 5.26909], [-54.4717, 4.91964], [-54.38444, 4.13222], [-54.19367, 3.84387], [-54.05128, 3.63557], [-53.98914, 3.627], [-53.9849, 3.58697], [-54.28534, 2.67798], [-54.42864, 2.42442], [-54.6084, 2.32856], [-54.16286, 2.10779], [-53.78743, 2.34412], [-52.96539, 2.1881], [-52.6906, 2.37298], [-52.31787, 3.17896], [-51.85573, 3.83427], [-51.82312, 3.85825], [-51.79599, 3.89336], [-51.61983, 4.14596], [-51.63798, 4.51394], [-51.35485, 4.8383]]]]
24265           }
24266         }, {
24267           type: "Feature",
24268           properties: {
24269             iso1A2: "GG",
24270             iso1A3: "GGY",
24271             iso1N3: "831",
24272             wikidata: "Q25230",
24273             nameEn: "Bailiwick of Guernsey",
24274             country: "GB"
24275           },
24276           geometry: null
24277         }, {
24278           type: "Feature",
24279           properties: {
24280             iso1A2: "GH",
24281             iso1A3: "GHA",
24282             iso1N3: "288",
24283             wikidata: "Q117",
24284             nameEn: "Ghana",
24285             groups: ["011", "202", "002", "UN"],
24286             callingCodes: ["233"]
24287           },
24288           geometry: {
24289             type: "MultiPolygon",
24290             coordinates: [[[[-0.13493, 11.14075], [-0.27374, 11.17157], [-0.28566, 11.12713], [-0.35955, 11.07801], [-0.38219, 11.12596], [-0.42391, 11.11661], [-0.44298, 11.04292], [-0.61937, 10.91305], [-0.67143, 10.99811], [-2.83373, 11.0067], [-2.94232, 10.64281], [-2.83108, 10.40252], [-2.74174, 9.83172], [-2.76534, 9.56589], [-2.68802, 9.49343], [-2.69814, 9.22717], [-2.77799, 9.04949], [-2.66357, 9.01771], [-2.58243, 8.7789], [-2.49037, 8.20872], [-2.62901, 8.11495], [-2.61232, 8.02645], [-2.67787, 8.02055], [-2.74819, 7.92613], [-2.78395, 7.94974], [-2.79467, 7.86002], [-2.92339, 7.60847], [-2.97822, 7.27165], [-2.95438, 7.23737], [-3.23327, 6.81744], [-3.21954, 6.74407], [-3.25999, 6.62521], [-3.01896, 5.71697], [-2.95323, 5.71865], [-2.96671, 5.6415], [-2.93132, 5.62137], [-2.85378, 5.65156], [-2.76614, 5.60963], [-2.72737, 5.34789], [-2.77625, 5.34621], [-2.73074, 5.1364], [-2.75502, 5.10657], [-2.95261, 5.12477], [-2.96554, 5.10397], [-3.063, 5.13665], [-3.11073, 5.12675], [-3.10675, 5.08515], [-3.34019, 4.17519], [1.07031, 5.15655], [1.27574, 5.93551], [1.19771, 6.11522], [1.19966, 6.17069], [1.09187, 6.17074], [1.05969, 6.22998], [1.03108, 6.24064], [0.99652, 6.33779], [0.89283, 6.33779], [0.71048, 6.53083], [0.74862, 6.56517], [0.63659, 6.63857], [0.6497, 6.73682], [0.58176, 6.76049], [0.57406, 6.80348], [0.52853, 6.82921], [0.56508, 6.92971], [0.52098, 6.94391], [0.52217, 6.9723], [0.59606, 7.01252], [0.65327, 7.31643], [0.62943, 7.41099], [0.57223, 7.39326], [0.52455, 7.45354], [0.51979, 7.58706], [0.58295, 7.62368], [0.62943, 7.85751], [0.58891, 8.12779], [0.6056, 8.13959], [0.61156, 8.18324], [0.5913, 8.19622], [0.63897, 8.25873], [0.73432, 8.29529], [0.64731, 8.48866], [0.47211, 8.59945], [0.37319, 8.75262], [0.52455, 8.87746], [0.45424, 9.04581], [0.56388, 9.40697], [0.49118, 9.48339], [0.36485, 9.49749], [0.33148, 9.44812], [0.25758, 9.42696], [0.2254, 9.47869], [0.31241, 9.50337], [0.30406, 9.521], [0.2409, 9.52335], [0.23851, 9.57389], [0.38153, 9.58682], [0.36008, 9.6256], [0.29334, 9.59387], [0.26712, 9.66437], [0.28261, 9.69022], [0.32313, 9.6491], [0.34816, 9.66907], [0.34816, 9.71607], [0.32075, 9.72781], [0.36366, 10.03309], [0.41252, 10.02018], [0.41371, 10.06361], [0.35293, 10.09412], [0.39584, 10.31112], [0.33028, 10.30408], [0.29453, 10.41546], [0.18846, 10.4096], [0.12886, 10.53149], [-0.05945, 10.63458], [-0.09141, 10.7147], [-0.07327, 10.71845], [-0.07183, 10.76794], [-0.0228, 10.81916], [-0.02685, 10.8783], [-908e-5, 10.91644], [-63e-4, 10.96417], [0.03355, 10.9807], [0.02395, 11.06229], [342e-5, 11.08317], [-514e-5, 11.10763], [-0.0275, 11.11202], [-0.05733, 11.08628], [-0.14462, 11.10811], [-0.13493, 11.14075]]]]
24291           }
24292         }, {
24293           type: "Feature",
24294           properties: {
24295             iso1A2: "GI",
24296             iso1A3: "GIB",
24297             iso1N3: "292",
24298             wikidata: "Q1410",
24299             nameEn: "Gibraltar",
24300             country: "GB",
24301             groups: ["Q12837", "BOTS", "039", "150", "UN"],
24302             callingCodes: ["350"]
24303           },
24304           geometry: {
24305             type: "MultiPolygon",
24306             coordinates: [[[[-5.34064, 36.03744], [-5.27801, 36.14942], [-5.33822, 36.15272], [-5.34536, 36.15501], [-5.40526, 36.15488], [-5.34064, 36.03744]]]]
24307           }
24308         }, {
24309           type: "Feature",
24310           properties: {
24311             iso1A2: "GL",
24312             iso1A3: "GRL",
24313             iso1N3: "304",
24314             wikidata: "Q223",
24315             nameEn: "Greenland",
24316             country: "DK",
24317             groups: ["Q1451600", "021", "003", "019", "UN"],
24318             callingCodes: ["299"]
24319           },
24320           geometry: {
24321             type: "MultiPolygon",
24322             coordinates: [[[[-49.33696, 84.57952], [-68.21821, 80.48551], [-77.52957, 77.23408], [-46.37635, 57.3249], [-9.68082, 72.73731], [-5.7106, 84.28058], [-49.33696, 84.57952]]]]
24323           }
24324         }, {
24325           type: "Feature",
24326           properties: {
24327             iso1A2: "GM",
24328             iso1A3: "GMB",
24329             iso1N3: "270",
24330             wikidata: "Q1005",
24331             nameEn: "The Gambia",
24332             groups: ["011", "202", "002", "UN"],
24333             callingCodes: ["220"]
24334           },
24335           geometry: {
24336             type: "MultiPolygon",
24337             coordinates: [[[[-15.14917, 13.57989], [-14.36795, 13.23033], [-13.79409, 13.34472], [-13.8955, 13.59126], [-14.34721, 13.46578], [-14.93719, 13.80173], [-15.36504, 13.79313], [-15.47902, 13.58758], [-17.43598, 13.59273], [-17.43966, 13.04579], [-16.74676, 13.06025], [-16.69343, 13.16791], [-15.80355, 13.16729], [-15.80478, 13.34832], [-15.26908, 13.37768], [-15.14917, 13.57989]]]]
24338           }
24339         }, {
24340           type: "Feature",
24341           properties: {
24342             iso1A2: "GN",
24343             iso1A3: "GIN",
24344             iso1N3: "324",
24345             wikidata: "Q1006",
24346             nameEn: "Guinea",
24347             groups: ["011", "202", "002", "UN"],
24348             callingCodes: ["224"]
24349           },
24350           geometry: {
24351             type: "MultiPolygon",
24352             coordinates: [[[[-11.37536, 12.40788], [-11.46267, 12.44559], [-11.91331, 12.42008], [-12.35415, 12.32758], [-12.87336, 12.51892], [-13.06603, 12.49342], [-13.05296, 12.64003], [-13.70523, 12.68013], [-13.7039, 12.60313], [-13.65089, 12.49515], [-13.64168, 12.42764], [-13.70851, 12.24978], [-13.92745, 12.24077], [-13.94589, 12.16869], [-13.7039, 12.00869], [-13.7039, 11.70195], [-14.09799, 11.63649], [-14.26623, 11.67486], [-14.31513, 11.60713], [-14.51173, 11.49708], [-14.66677, 11.51188], [-14.77786, 11.36323], [-14.95993, 10.99244], [-15.07174, 10.89557], [-15.96748, 10.162], [-14.36218, 8.64107], [-13.29911, 9.04245], [-13.18586, 9.0925], [-13.08953, 9.0409], [-12.94095, 9.26335], [-12.76788, 9.3133], [-12.47254, 9.86834], [-12.24262, 9.92386], [-12.12634, 9.87203], [-11.91023, 9.93927], [-11.89624, 9.99763], [-11.2118, 10.00098], [-10.6534, 9.29919], [-10.74484, 9.07998], [-10.5783, 9.06386], [-10.56197, 8.81225], [-10.47707, 8.67669], [-10.61422, 8.5314], [-10.70565, 8.29235], [-10.63934, 8.35326], [-10.54891, 8.31174], [-10.37257, 8.48941], [-10.27575, 8.48711], [-10.203, 8.47991], [-10.14579, 8.52665], [-10.05375, 8.50697], [-10.05873, 8.42578], [-9.77763, 8.54633], [-9.47415, 8.35195], [-9.50898, 8.18455], [-9.41445, 8.02448], [-9.44928, 7.9284], [-9.35724, 7.74111], [-9.37465, 7.62032], [-9.48161, 7.37122], [-9.41943, 7.41809], [-9.305, 7.42056], [-9.20798, 7.38109], [-9.18311, 7.30461], [-9.09107, 7.1985], [-8.93435, 7.2824], [-8.85724, 7.26019], [-8.8448, 7.35149], [-8.72789, 7.51429], [-8.67814, 7.69428], [-8.55874, 7.70167], [-8.55874, 7.62525], [-8.47114, 7.55676], [-8.4003, 7.6285], [-8.21374, 7.54466], [-8.09931, 7.78626], [-8.13414, 7.87991], [-8.06449, 8.04989], [-7.94695, 8.00925], [-7.99919, 8.11023], [-7.98675, 8.20134], [-8.062, 8.16071], [-8.2411, 8.24196], [-8.22991, 8.48438], [-7.92518, 8.50652], [-7.65653, 8.36873], [-7.69882, 8.66148], [-7.95503, 8.81146], [-7.92518, 8.99332], [-7.73862, 9.08422], [-7.90777, 9.20456], [-7.85056, 9.41812], [-8.03463, 9.39604], [-8.14657, 9.55062], [-8.09434, 9.86936], [-8.15652, 9.94288], [-8.11921, 10.04577], [-8.01225, 10.1021], [-7.97971, 10.17117], [-7.9578, 10.2703], [-8.10207, 10.44649], [-8.22711, 10.41722], [-8.32614, 10.69273], [-8.2667, 10.91762], [-8.35083, 11.06234], [-8.66923, 10.99397], [-8.40058, 11.37466], [-8.80854, 11.66715], [-8.94784, 12.34842], [-9.13689, 12.50875], [-9.38067, 12.48446], [-9.32097, 12.29009], [-9.63938, 12.18312], [-9.714, 12.0226], [-10.30604, 12.24634], [-10.71897, 11.91552], [-10.80355, 12.1053], [-10.99758, 12.24634], [-11.24136, 12.01286], [-11.50006, 12.17826], [-11.37536, 12.40788]]]]
24353           }
24354         }, {
24355           type: "Feature",
24356           properties: {
24357             iso1A2: "GP",
24358             iso1A3: "GLP",
24359             iso1N3: "312",
24360             wikidata: "Q17012",
24361             nameEn: "Guadeloupe",
24362             country: "FR",
24363             groups: ["Q3320166", "EU", "029", "003", "419", "019", "UN"],
24364             callingCodes: ["590"]
24365           },
24366           geometry: {
24367             type: "MultiPolygon",
24368             coordinates: [[[[-60.03183, 16.1129], [-61.60296, 16.73066], [-63.00549, 15.26166], [-60.03183, 16.1129]]]]
24369           }
24370         }, {
24371           type: "Feature",
24372           properties: {
24373             iso1A2: "GQ",
24374             iso1A3: "GNQ",
24375             iso1N3: "226",
24376             wikidata: "Q983",
24377             nameEn: "Equatorial Guinea",
24378             groups: ["017", "202", "002", "UN"],
24379             callingCodes: ["240"]
24380           },
24381           geometry: {
24382             type: "MultiPolygon",
24383             coordinates: [[[[9.22018, 3.72052], [8.34397, 4.30689], [7.71762, 0.6674], [3.35016, -3.29031], [9.66433, 1.06723], [9.75065, 1.06753], [9.79648, 1.0019], [11.35307, 1.00251], [11.3561, 2.17217], [9.991, 2.16561], [9.90749, 2.20049], [9.89012, 2.20457], [9.84716, 2.24676], [9.83238, 2.29079], [9.83754, 2.32428], [9.82123, 2.35097], [9.81162, 2.33797], [9.22018, 3.72052]]]]
24384           }
24385         }, {
24386           type: "Feature",
24387           properties: {
24388             iso1A2: "GR",
24389             iso1A3: "GRC",
24390             iso1N3: "300",
24391             wikidata: "Q41",
24392             nameEn: "Greece",
24393             aliases: ["EL"],
24394             groups: ["EU", "039", "150", "UN"],
24395             callingCodes: ["30"]
24396           },
24397           geometry: {
24398             type: "MultiPolygon",
24399             coordinates: [[[[26.03489, 40.73051], [26.0754, 40.72772], [26.08638, 40.73214], [26.12495, 40.74283], [26.12854, 40.77339], [26.15685, 40.80709], [26.21351, 40.83298], [26.20856, 40.86048], [26.26169, 40.9168], [26.29441, 40.89119], [26.28623, 40.93005], [26.32259, 40.94042], [26.35894, 40.94292], [26.33297, 40.98388], [26.3606, 41.02027], [26.31928, 41.07386], [26.32259, 41.24929], [26.39861, 41.25053], [26.5209, 41.33993], [26.5837, 41.32131], [26.62997, 41.34613], [26.61767, 41.42281], [26.59742, 41.48058], [26.59196, 41.60491], [26.5209, 41.62592], [26.47958, 41.67037], [26.35957, 41.71149], [26.30255, 41.70925], [26.2654, 41.71544], [26.22888, 41.74139], [26.21325, 41.73223], [26.16841, 41.74858], [26.06148, 41.70345], [26.07083, 41.64584], [26.15146, 41.60828], [26.14328, 41.55496], [26.17951, 41.55409], [26.176, 41.50072], [26.14796, 41.47533], [26.20288, 41.43943], [26.16548, 41.42278], [26.12926, 41.35878], [25.87919, 41.30526], [25.8266, 41.34563], [25.70507, 41.29209], [25.66183, 41.31316], [25.61042, 41.30614], [25.55082, 41.31667], [25.52394, 41.2798], [25.48187, 41.28506], [25.28322, 41.23411], [25.11611, 41.34212], [24.942, 41.38685], [24.90928, 41.40876], [24.86136, 41.39298], [24.82514, 41.4035], [24.8041, 41.34913], [24.71529, 41.41928], [24.61129, 41.42278], [24.52599, 41.56808], [24.30513, 41.51297], [24.27124, 41.57682], [24.18126, 41.51735], [24.10063, 41.54796], [24.06323, 41.53222], [24.06908, 41.46132], [23.96975, 41.44118], [23.91483, 41.47971], [23.89613, 41.45257], [23.80148, 41.43943], [23.76525, 41.40175], [23.67644, 41.41139], [23.63203, 41.37632], [23.52453, 41.40262], [23.40416, 41.39999], [23.33639, 41.36317], [23.31301, 41.40525], [23.22771, 41.37106], [23.21953, 41.33773], [23.1833, 41.31755], [22.93334, 41.34104], [22.81199, 41.3398], [22.76408, 41.32225], [22.74538, 41.16321], [22.71266, 41.13945], [22.65306, 41.18168], [22.62852, 41.14385], [22.58295, 41.11568], [22.5549, 41.13065], [22.42285, 41.11921], [22.26744, 41.16409], [22.17629, 41.15969], [22.1424, 41.12449], [22.06527, 41.15617], [21.90869, 41.09191], [21.91102, 41.04786], [21.7556, 40.92525], [21.69601, 40.9429], [21.57448, 40.86076], [21.53007, 40.90759], [21.41555, 40.9173], [21.35595, 40.87578], [21.25779, 40.86165], [21.21105, 40.8855], [21.15262, 40.85546], [20.97887, 40.85475], [20.98396, 40.79109], [20.95752, 40.76982], [20.98134, 40.76046], [21.05833, 40.66586], [21.03932, 40.56299], [20.96908, 40.51526], [20.94925, 40.46625], [20.83688, 40.47882], [20.7906, 40.42726], [20.78234, 40.35803], [20.71789, 40.27739], [20.67162, 40.09433], [20.62566, 40.0897], [20.61081, 40.07866], [20.55593, 40.06524], [20.51297, 40.08168], [20.48487, 40.06271], [20.42373, 40.06777], [20.37911, 39.99058], [20.31135, 39.99438], [20.41546, 39.82832], [20.41475, 39.81437], [20.38572, 39.78516], [20.30804, 39.81563], [20.29152, 39.80421], [20.31961, 39.72799], [20.27412, 39.69884], [20.22707, 39.67459], [20.22376, 39.64532], [20.15988, 39.652], [20.12956, 39.65805], [20.05189, 39.69112], [20.00957, 39.69227], [19.98042, 39.6504], [19.92466, 39.69533], [19.97622, 39.78684], [19.95905, 39.82857], [19.0384, 40.35325], [19.20409, 39.7532], [22.5213, 33.45682], [29.73302, 35.92555], [29.69611, 36.10365], [29.61805, 36.14179], [29.61002, 36.1731], [29.48192, 36.18377], [29.30783, 36.01033], [28.23708, 36.56812], [27.95037, 36.46155], [27.89482, 36.69898], [27.46117, 36.53789], [27.24613, 36.71622], [27.45627, 36.9008], [27.20312, 36.94571], [27.14757, 37.32], [26.95583, 37.64989], [26.99377, 37.69034], [27.16428, 37.72343], [27.05537, 37.9131], [26.21136, 38.17558], [26.24183, 38.44695], [26.32173, 38.48731], [26.21136, 38.65436], [26.61814, 38.81372], [26.70773, 39.0312], [26.43357, 39.43096], [25.94257, 39.39358], [25.61285, 40.17161], [26.04292, 40.3958], [25.94795, 40.72797], [26.03489, 40.73051]]]]
24400           }
24401         }, {
24402           type: "Feature",
24403           properties: {
24404             iso1A2: "GS",
24405             iso1A3: "SGS",
24406             iso1N3: "239",
24407             wikidata: "Q35086",
24408             nameEn: "South Georgia and South Sandwich Islands",
24409             country: "GB",
24410             groups: ["BOTS", "005", "419", "019", "UN"],
24411             driveSide: "left",
24412             roadSpeedUnit: "mph",
24413             roadHeightUnit: "ft",
24414             callingCodes: ["500"]
24415           },
24416           geometry: {
24417             type: "MultiPolygon",
24418             coordinates: [[[[-35.26394, -43.68272], [-53.39656, -59.87088], [-22.31757, -59.85974], [-35.26394, -43.68272]]]]
24419           }
24420         }, {
24421           type: "Feature",
24422           properties: {
24423             iso1A2: "GT",
24424             iso1A3: "GTM",
24425             iso1N3: "320",
24426             wikidata: "Q774",
24427             nameEn: "Guatemala",
24428             groups: ["013", "003", "419", "019", "UN"],
24429             callingCodes: ["502"]
24430           },
24431           geometry: {
24432             type: "MultiPolygon",
24433             coordinates: [[[[-89.14985, 17.81563], [-90.98678, 17.81655], [-90.99199, 17.25192], [-91.43809, 17.25373], [-91.04436, 16.92175], [-90.69064, 16.70697], [-90.61212, 16.49832], [-90.40499, 16.40524], [-90.44567, 16.07573], [-91.73182, 16.07371], [-92.20983, 15.26077], [-92.0621, 15.07406], [-92.1454, 14.98143], [-92.1423, 14.88647], [-92.18161, 14.84147], [-92.1454, 14.6804], [-92.2261, 14.53423], [-92.37213, 14.39277], [-90.55276, 12.8866], [-90.11344, 13.73679], [-90.10505, 13.85104], [-89.88937, 14.0396], [-89.81807, 14.07073], [-89.76103, 14.02923], [-89.73251, 14.04133], [-89.75569, 14.07073], [-89.70756, 14.1537], [-89.61844, 14.21937], [-89.52397, 14.22628], [-89.50614, 14.26084], [-89.58814, 14.33165], [-89.57441, 14.41637], [-89.39028, 14.44561], [-89.34776, 14.43013], [-89.35189, 14.47553], [-89.23719, 14.58046], [-89.15653, 14.57802], [-89.13132, 14.71582], [-89.23467, 14.85596], [-89.15149, 14.97775], [-89.18048, 14.99967], [-89.15149, 15.07392], [-88.97343, 15.14039], [-88.32467, 15.63665], [-88.31459, 15.66942], [-88.24022, 15.69247], [-88.22552, 15.72294], [-88.20359, 16.03858], [-88.40779, 16.09624], [-88.95358, 15.88698], [-89.02415, 15.9063], [-89.17418, 15.90898], [-89.22683, 15.88619], [-89.15025, 17.04813], [-89.14985, 17.81563]]]]
24434           }
24435         }, {
24436           type: "Feature",
24437           properties: {
24438             iso1A2: "GU",
24439             iso1A3: "GUM",
24440             iso1N3: "316",
24441             wikidata: "Q16635",
24442             nameEn: "Guam",
24443             aliases: ["US-GU"],
24444             country: "US",
24445             groups: ["Q1352230", "Q153732", "057", "009", "UN"],
24446             roadSpeedUnit: "mph",
24447             roadHeightUnit: "ft",
24448             callingCodes: ["1 671"]
24449           },
24450           geometry: {
24451             type: "MultiPolygon",
24452             coordinates: [[[[146.25931, 13.85876], [143.82485, 13.92273], [144.61642, 12.82462], [146.25931, 13.85876]]]]
24453           }
24454         }, {
24455           type: "Feature",
24456           properties: {
24457             iso1A2: "GW",
24458             iso1A3: "GNB",
24459             iso1N3: "624",
24460             wikidata: "Q1007",
24461             nameEn: "Guinea-Bissau",
24462             groups: ["011", "202", "002", "UN"],
24463             callingCodes: ["245"]
24464           },
24465           geometry: {
24466             type: "MultiPolygon",
24467             coordinates: [[[[-14.31513, 11.60713], [-14.26623, 11.67486], [-14.09799, 11.63649], [-13.7039, 11.70195], [-13.7039, 12.00869], [-13.94589, 12.16869], [-13.92745, 12.24077], [-13.70851, 12.24978], [-13.64168, 12.42764], [-13.65089, 12.49515], [-13.7039, 12.60313], [-13.70523, 12.68013], [-15.17582, 12.6847], [-15.67302, 12.42974], [-16.20591, 12.46157], [-16.38191, 12.36449], [-16.70562, 12.34803], [-17.4623, 11.92379], [-15.96748, 10.162], [-15.07174, 10.89557], [-14.95993, 10.99244], [-14.77786, 11.36323], [-14.66677, 11.51188], [-14.51173, 11.49708], [-14.31513, 11.60713]]]]
24468           }
24469         }, {
24470           type: "Feature",
24471           properties: {
24472             iso1A2: "GY",
24473             iso1A3: "GUY",
24474             iso1N3: "328",
24475             wikidata: "Q734",
24476             nameEn: "Guyana",
24477             groups: ["005", "419", "019", "UN"],
24478             driveSide: "left",
24479             callingCodes: ["592"]
24480           },
24481           geometry: {
24482             type: "MultiPolygon",
24483             coordinates: [[[[-56.84822, 6.73257], [-59.54058, 8.6862], [-59.98508, 8.53046], [-59.85562, 8.35213], [-59.80661, 8.28906], [-59.83156, 8.23261], [-59.97059, 8.20791], [-60.02407, 8.04557], [-60.38056, 7.8302], [-60.51959, 7.83373], [-60.64793, 7.56877], [-60.71923, 7.55817], [-60.59802, 7.33194], [-60.63367, 7.25061], [-60.54098, 7.14804], [-60.44116, 7.20817], [-60.28074, 7.1162], [-60.39419, 6.94847], [-60.54873, 6.8631], [-61.13632, 6.70922], [-61.20762, 6.58174], [-61.15058, 6.19558], [-61.4041, 5.95304], [-60.73204, 5.20931], [-60.32352, 5.21299], [-60.20944, 5.28754], [-59.98129, 5.07097], [-60.04189, 4.69801], [-60.15953, 4.53456], [-59.78878, 4.45637], [-59.69361, 4.34069], [-59.73353, 4.20399], [-59.51963, 3.91951], [-59.86899, 3.57089], [-59.79769, 3.37162], [-59.99733, 2.92312], [-59.91177, 2.36759], [-59.7264, 2.27497], [-59.74066, 1.87596], [-59.25583, 1.40559], [-58.92072, 1.31293], [-58.84229, 1.17749], [-58.53571, 1.29154], [-58.4858, 1.48399], [-58.33887, 1.58014], [-58.01873, 1.51966], [-57.97336, 1.64566], [-57.77281, 1.73344], [-57.55743, 1.69605], [-57.35073, 1.98327], [-57.23981, 1.95808], [-57.09109, 2.01854], [-57.07092, 1.95304], [-56.7659, 1.89509], [-56.47045, 1.95135], [-56.55439, 2.02003], [-56.70519, 2.02964], [-57.35891, 3.32121], [-58.0307, 3.95513], [-57.8699, 4.89394], [-57.37442, 5.0208], [-57.22536, 5.15605], [-57.31629, 5.33714], [-56.84822, 6.73257]]]]
24484           }
24485         }, {
24486           type: "Feature",
24487           properties: {
24488             iso1A2: "HK",
24489             iso1A3: "HKG",
24490             iso1N3: "344",
24491             wikidata: "Q8646",
24492             nameEn: "Hong Kong",
24493             country: "CN",
24494             groups: ["030", "142", "UN"],
24495             driveSide: "left",
24496             callingCodes: ["852"]
24497           },
24498           geometry: {
24499             type: "MultiPolygon",
24500             coordinates: [[[[113.92195, 22.13873], [114.50148, 22.15017], [114.44998, 22.55977], [114.25154, 22.55977], [114.22888, 22.5436], [114.22185, 22.55343], [114.20655, 22.55706], [114.18338, 22.55444], [114.17247, 22.55944], [114.1597, 22.56041], [114.15123, 22.55163], [114.1482, 22.54091], [114.13823, 22.54319], [114.12665, 22.54003], [114.11656, 22.53415], [114.11181, 22.52878], [114.1034, 22.5352], [114.09692, 22.53435], [114.09048, 22.53716], [114.08606, 22.53276], [114.07817, 22.52997], [114.07267, 22.51855], [114.06272, 22.51617], [114.05729, 22.51104], [114.05438, 22.5026], [114.03113, 22.5065], [113.86771, 22.42972], [113.81621, 22.2163], [113.83338, 22.1826], [113.92195, 22.13873]]]]
24501           }
24502         }, {
24503           type: "Feature",
24504           properties: {
24505             iso1A2: "HM",
24506             iso1A3: "HMD",
24507             iso1N3: "334",
24508             wikidata: "Q131198",
24509             nameEn: "Heard Island and McDonald Islands",
24510             country: "AU",
24511             groups: ["053", "009", "UN"],
24512             driveSide: "left"
24513           },
24514           geometry: {
24515             type: "MultiPolygon",
24516             coordinates: [[[[71.08716, -53.87687], [75.44182, -53.99822], [72.87012, -51.48322], [71.08716, -53.87687]]]]
24517           }
24518         }, {
24519           type: "Feature",
24520           properties: {
24521             iso1A2: "HN",
24522             iso1A3: "HND",
24523             iso1N3: "340",
24524             wikidata: "Q783",
24525             nameEn: "Honduras",
24526             groups: ["013", "003", "419", "019", "UN"],
24527             callingCodes: ["504"]
24528           },
24529           geometry: {
24530             type: "MultiPolygon",
24531             coordinates: [[[[-83.86109, 17.73736], [-88.20359, 16.03858], [-88.22552, 15.72294], [-88.24022, 15.69247], [-88.31459, 15.66942], [-88.32467, 15.63665], [-88.97343, 15.14039], [-89.15149, 15.07392], [-89.18048, 14.99967], [-89.15149, 14.97775], [-89.23467, 14.85596], [-89.13132, 14.71582], [-89.15653, 14.57802], [-89.23719, 14.58046], [-89.35189, 14.47553], [-89.34776, 14.43013], [-89.04187, 14.33644], [-88.94608, 14.20207], [-88.85785, 14.17763], [-88.815, 14.11652], [-88.73182, 14.10919], [-88.70661, 14.04317], [-88.49738, 13.97224], [-88.48982, 13.86458], [-88.25791, 13.91108], [-88.23018, 13.99915], [-88.07641, 13.98447], [-88.00331, 13.86948], [-87.7966, 13.91353], [-87.68821, 13.80829], [-87.73106, 13.75443], [-87.78148, 13.52906], [-87.71657, 13.50577], [-87.72115, 13.46083], [-87.73841, 13.44169], [-87.77354, 13.45767], [-87.83467, 13.44655], [-87.84675, 13.41078], [-87.80177, 13.35689], [-87.73714, 13.32715], [-87.69751, 13.25228], [-87.55124, 13.12523], [-87.37107, 12.98646], [-87.06306, 13.00892], [-87.03785, 12.98682], [-86.93197, 13.05313], [-86.93383, 13.18677], [-86.87066, 13.30641], [-86.71267, 13.30348], [-86.76812, 13.79605], [-86.35219, 13.77157], [-86.14801, 14.04317], [-86.00685, 14.08474], [-86.03458, 13.99181], [-85.75477, 13.8499], [-85.73964, 13.9698], [-85.45762, 14.11304], [-85.32149, 14.2562], [-85.18602, 14.24929], [-85.1575, 14.53934], [-84.90082, 14.80489], [-84.82596, 14.82212], [-84.70119, 14.68078], [-84.48373, 14.63249], [-84.10584, 14.76353], [-83.89551, 14.76697], [-83.62101, 14.89448], [-83.49268, 15.01158], [-83.13724, 15.00002], [-83.04763, 15.03256], [-82.06974, 14.49418], [-81.58685, 18.0025], [-83.86109, 17.73736]]]]
24532           }
24533         }, {
24534           type: "Feature",
24535           properties: {
24536             iso1A2: "HR",
24537             iso1A3: "HRV",
24538             iso1N3: "191",
24539             wikidata: "Q224",
24540             nameEn: "Croatia",
24541             groups: ["EU", "039", "150", "UN"],
24542             callingCodes: ["385"]
24543           },
24544           geometry: {
24545             type: "MultiPolygon",
24546             coordinates: [[[[17.6444, 42.88641], [17.5392, 42.92787], [17.70879, 42.97223], [17.64268, 43.08595], [17.46986, 43.16559], [17.286, 43.33065], [17.25579, 43.40353], [17.29699, 43.44542], [17.24411, 43.49376], [17.15828, 43.49376], [17.00585, 43.58037], [16.80736, 43.76011], [16.75316, 43.77157], [16.70922, 43.84887], [16.55472, 43.95326], [16.50528, 44.0244], [16.43629, 44.02826], [16.43662, 44.07523], [16.36864, 44.08263], [16.18688, 44.27012], [16.21346, 44.35231], [16.12969, 44.38275], [16.16814, 44.40679], [16.10566, 44.52586], [16.03012, 44.55572], [16.00884, 44.58605], [16.05828, 44.61538], [15.89348, 44.74964], [15.8255, 44.71501], [15.72584, 44.82334], [15.79472, 44.8455], [15.76096, 44.87045], [15.74723, 44.96818], [15.78568, 44.97401], [15.74585, 45.0638], [15.78842, 45.11519], [15.76371, 45.16508], [15.83512, 45.22459], [15.98412, 45.23088], [16.12153, 45.09616], [16.29036, 44.99732], [16.35404, 45.00241], [16.35863, 45.03529], [16.3749, 45.05206], [16.38219, 45.05139], [16.38309, 45.05955], [16.40023, 45.1147], [16.4634, 45.14522], [16.49155, 45.21153], [16.52982, 45.22713], [16.5501, 45.2212], [16.56559, 45.22307], [16.60194, 45.23042], [16.64962, 45.20714], [16.74845, 45.20393], [16.78219, 45.19002], [16.81137, 45.18434], [16.83804, 45.18951], [16.92405, 45.27607], [16.9385, 45.22742], [17.0415, 45.20759], [17.18438, 45.14764], [17.24325, 45.146], [17.25131, 45.14957], [17.26815, 45.18444], [17.32092, 45.16246], [17.33573, 45.14521], [17.41229, 45.13335], [17.4498, 45.16119], [17.45615, 45.12523], [17.47589, 45.12656], [17.51469, 45.10791], [17.59104, 45.10816], [17.66571, 45.13408], [17.84826, 45.04489], [17.87148, 45.04645], [17.93706, 45.08016], [17.97336, 45.12245], [17.97834, 45.13831], [17.99479, 45.14958], [18.01594, 45.15163], [18.03121, 45.12632], [18.1624, 45.07654], [18.24387, 45.13699], [18.32077, 45.1021], [18.41896, 45.11083], [18.47939, 45.05871], [18.65723, 45.07544], [18.78357, 44.97741], [18.80661, 44.93561], [18.76369, 44.93707], [18.76347, 44.90669], [18.8704, 44.85097], [19.01994, 44.85493], [18.98957, 44.90645], [19.02871, 44.92541], [19.06853, 44.89915], [19.15573, 44.95409], [19.05205, 44.97692], [19.1011, 45.01191], [19.07952, 45.14668], [19.14063, 45.12972], [19.19144, 45.17863], [19.43589, 45.17137], [19.41941, 45.23475], [19.28208, 45.23813], [19.10774, 45.29547], [18.97446, 45.37528], [18.99918, 45.49333], [19.08364, 45.48804], [19.07471, 45.53086], [18.94562, 45.53712], [18.88776, 45.57253], [18.96691, 45.66731], [18.90305, 45.71863], [18.85783, 45.85493], [18.81394, 45.91329], [18.80211, 45.87995], [18.6792, 45.92057], [18.57483, 45.80772], [18.44368, 45.73972], [18.12439, 45.78905], [18.08869, 45.76511], [17.99805, 45.79671], [17.87377, 45.78522], [17.66545, 45.84207], [17.56821, 45.93728], [17.35672, 45.95209], [17.14592, 46.16697], [16.8903, 46.28122], [16.8541, 46.36255], [16.7154, 46.39523], [16.6639, 46.45203], [16.59527, 46.47524], [16.52604, 46.47831], [16.5007, 46.49644], [16.44036, 46.5171], [16.38771, 46.53608], [16.37193, 46.55008], [16.29793, 46.5121], [16.26733, 46.51505], [16.26759, 46.50566], [16.23961, 46.49653], [16.25124, 46.48067], [16.27398, 46.42875], [16.27329, 46.41467], [16.30162, 46.40437], [16.30233, 46.37837], [16.18824, 46.38282], [16.14859, 46.40547], [16.05281, 46.39141], [16.05065, 46.3833], [16.07314, 46.36458], [16.07616, 46.3463], [15.97965, 46.30652], [15.79284, 46.25811], [15.78817, 46.21719], [15.75479, 46.20336], [15.75436, 46.21969], [15.67395, 46.22478], [15.6434, 46.21396], [15.64904, 46.19229], [15.59909, 46.14761], [15.6083, 46.11992], [15.62317, 46.09103], [15.72977, 46.04682], [15.71246, 46.01196], [15.70327, 46.00015], [15.70636, 45.92116], [15.67967, 45.90455], [15.68383, 45.88867], [15.68232, 45.86819], [15.70411, 45.8465], [15.66662, 45.84085], [15.64185, 45.82915], [15.57952, 45.84953], [15.52234, 45.82195], [15.47325, 45.8253], [15.47531, 45.79802], [15.40836, 45.79491], [15.25423, 45.72275], [15.30872, 45.69014], [15.34919, 45.71623], [15.4057, 45.64727], [15.38952, 45.63682], [15.34214, 45.64702], [15.34695, 45.63382], [15.31027, 45.6303], [15.27747, 45.60504], [15.29837, 45.5841], [15.30249, 45.53224], [15.38188, 45.48752], [15.33051, 45.45258], [15.27758, 45.46678], [15.16862, 45.42309], [15.05187, 45.49079], [15.02385, 45.48533], [14.92266, 45.52788], [14.90554, 45.47769], [14.81992, 45.45913], [14.80124, 45.49515], [14.71718, 45.53442], [14.68605, 45.53006], [14.69694, 45.57366], [14.59576, 45.62812], [14.60977, 45.66403], [14.57397, 45.67165], [14.53816, 45.6205], [14.5008, 45.60852], [14.49769, 45.54424], [14.36693, 45.48642], [14.32487, 45.47142], [14.27681, 45.4902], [14.26611, 45.48239], [14.24239, 45.50607], [14.22371, 45.50388], [14.20348, 45.46896], [14.07116, 45.48752], [14.00578, 45.52352], [13.96063, 45.50825], [13.99488, 45.47551], [13.97309, 45.45258], [13.90771, 45.45149], [13.88124, 45.42637], [13.81742, 45.43729], [13.7785, 45.46787], [13.67398, 45.4436], [13.62902, 45.45898], [13.56979, 45.4895], [13.45644, 45.59464], [13.05142, 45.33128], [13.12821, 44.48877], [16.15283, 42.18525], [18.45131, 42.21682], [18.54128, 42.39171], [18.52152, 42.42302], [18.43588, 42.48556], [18.44307, 42.51077], [18.43735, 42.55921], [18.36197, 42.61423], [18.24318, 42.6112], [17.88201, 42.83668], [17.80854, 42.9182], [17.7948, 42.89556], [17.68151, 42.92725], [17.6444, 42.88641]]]]
24547           }
24548         }, {
24549           type: "Feature",
24550           properties: {
24551             iso1A2: "HT",
24552             iso1A3: "HTI",
24553             iso1N3: "332",
24554             wikidata: "Q790",
24555             nameEn: "Haiti",
24556             aliases: ["RH"],
24557             groups: ["029", "003", "419", "019", "UN"],
24558             callingCodes: ["509"]
24559           },
24560           geometry: {
24561             type: "MultiPolygon",
24562             coordinates: [[[[-71.71885, 18.78423], [-71.72624, 18.87802], [-71.77766, 18.95007], [-71.88102, 18.95007], [-71.74088, 19.0437], [-71.71088, 19.08353], [-71.69938, 19.10916], [-71.65337, 19.11759], [-71.62642, 19.21212], [-71.73229, 19.26686], [-71.77766, 19.33823], [-71.69448, 19.37866], [-71.6802, 19.45008], [-71.71268, 19.53374], [-71.71449, 19.55364], [-71.7429, 19.58445], [-71.75865, 19.70231], [-71.77419, 19.73128], [-72.38946, 20.27111], [-73.37289, 20.43199], [-74.7289, 18.71009], [-74.76465, 18.06252], [-72.29523, 17.48026], [-71.75671, 18.03456], [-71.73783, 18.07177], [-71.74994, 18.11115], [-71.75465, 18.14405], [-71.78271, 18.18302], [-71.69952, 18.34101], [-71.90875, 18.45821], [-71.88102, 18.50125], [-72.00201, 18.62312], [-71.95412, 18.64939], [-71.82556, 18.62551], [-71.71885, 18.78423]]]]
24563           }
24564         }, {
24565           type: "Feature",
24566           properties: {
24567             iso1A2: "HU",
24568             iso1A3: "HUN",
24569             iso1N3: "348",
24570             wikidata: "Q28",
24571             nameEn: "Hungary",
24572             groups: ["EU", "151", "150", "UN"],
24573             callingCodes: ["36"]
24574           },
24575           geometry: {
24576             type: "MultiPolygon",
24577             coordinates: [[[[21.72525, 48.34628], [21.67134, 48.3989], [21.6068, 48.50365], [21.44063, 48.58456], [21.11516, 48.49546], [20.83248, 48.5824], [20.5215, 48.53336], [20.29943, 48.26104], [20.24312, 48.2784], [19.92452, 48.1283], [19.63338, 48.25006], [19.52489, 48.19791], [19.47957, 48.09437], [19.28182, 48.08336], [19.23924, 48.0595], [19.01952, 48.07052], [18.82176, 48.04206], [18.76134, 47.97499], [18.76821, 47.87469], [18.8506, 47.82308], [18.74074, 47.8157], [18.66521, 47.76772], [18.56496, 47.76588], [18.29305, 47.73541], [18.02938, 47.75665], [17.71215, 47.7548], [17.23699, 48.02094], [17.16001, 48.00636], [17.09786, 47.97336], [17.11022, 47.92461], [17.08275, 47.87719], [17.00997, 47.86245], [17.07039, 47.81129], [17.05048, 47.79377], [17.08893, 47.70928], [16.87538, 47.68895], [16.86509, 47.72268], [16.82938, 47.68432], [16.7511, 47.67878], [16.72089, 47.73469], [16.65679, 47.74197], [16.61183, 47.76171], [16.54779, 47.75074], [16.53514, 47.73837], [16.55129, 47.72268], [16.4222, 47.66537], [16.58699, 47.61772], [16.64193, 47.63114], [16.71059, 47.52692], [16.64821, 47.50155], [16.6718, 47.46139], [16.57152, 47.40868], [16.52414, 47.41007], [16.49908, 47.39416], [16.45104, 47.41181], [16.47782, 47.25918], [16.44142, 47.25079], [16.43663, 47.21127], [16.41739, 47.20649], [16.42801, 47.18422], [16.4523, 47.18812], [16.46442, 47.16845], [16.44932, 47.14418], [16.52863, 47.13974], [16.46134, 47.09395], [16.52176, 47.05747], [16.43936, 47.03548], [16.51369, 47.00084], [16.28202, 47.00159], [16.27594, 46.9643], [16.22403, 46.939], [16.19904, 46.94134], [16.10983, 46.867], [16.14365, 46.8547], [16.15711, 46.85434], [16.21892, 46.86961], [16.2365, 46.87775], [16.2941, 46.87137], [16.34547, 46.83836], [16.3408, 46.80641], [16.31303, 46.79838], [16.30966, 46.7787], [16.37816, 46.69975], [16.42641, 46.69228], [16.41863, 46.66238], [16.38594, 46.6549], [16.39217, 46.63673], [16.50139, 46.56684], [16.52885, 46.53303], [16.52604, 46.5051], [16.59527, 46.47524], [16.6639, 46.45203], [16.7154, 46.39523], [16.8541, 46.36255], [16.8903, 46.28122], [17.14592, 46.16697], [17.35672, 45.95209], [17.56821, 45.93728], [17.66545, 45.84207], [17.87377, 45.78522], [17.99805, 45.79671], [18.08869, 45.76511], [18.12439, 45.78905], [18.44368, 45.73972], [18.57483, 45.80772], [18.6792, 45.92057], [18.80211, 45.87995], [18.81394, 45.91329], [18.99712, 45.93537], [19.01284, 45.96529], [19.0791, 45.96458], [19.10388, 46.04015], [19.14543, 45.9998], [19.28826, 45.99694], [19.52473, 46.1171], [19.56113, 46.16824], [19.66007, 46.19005], [19.81491, 46.1313], [19.93508, 46.17553], [20.01816, 46.17696], [20.03533, 46.14509], [20.09713, 46.17315], [20.26068, 46.12332], [20.28324, 46.1438], [20.35573, 46.16629], [20.45377, 46.14405], [20.49718, 46.18721], [20.63863, 46.12728], [20.76085, 46.21002], [20.74574, 46.25467], [20.86797, 46.28884], [21.06572, 46.24897], [21.16872, 46.30118], [21.28061, 46.44941], [21.26929, 46.4993], [21.33214, 46.63035], [21.43926, 46.65109], [21.5151, 46.72147], [21.48935, 46.7577], [21.52028, 46.84118], [21.59307, 46.86935], [21.59581, 46.91628], [21.68645, 46.99595], [21.648, 47.03902], [21.78395, 47.11104], [21.94463, 47.38046], [22.01055, 47.37767], [22.03389, 47.42508], [22.00917, 47.50492], [22.31816, 47.76126], [22.41979, 47.7391], [22.46559, 47.76583], [22.67247, 47.7871], [22.76617, 47.8417], [22.77991, 47.87211], [22.89849, 47.95851], [22.84276, 47.98602], [22.87847, 48.04665], [22.81804, 48.11363], [22.73427, 48.12005], [22.66835, 48.09162], [22.58733, 48.10813], [22.59007, 48.15121], [22.49806, 48.25189], [22.38133, 48.23726], [22.2083, 48.42534], [22.14689, 48.4005], [21.83339, 48.36242], [21.8279, 48.33321], [21.72525, 48.34628]]]]
24578           }
24579         }, {
24580           type: "Feature",
24581           properties: {
24582             iso1A2: "IC",
24583             wikidata: "Q5813",
24584             nameEn: "Canary Islands",
24585             country: "ES",
24586             groups: ["Q3320166", "Q105472", "EU", "039", "150", "UN"],
24587             isoStatus: "excRes",
24588             callingCodes: ["34"]
24589           },
24590           geometry: {
24591             type: "MultiPolygon",
24592             coordinates: [[[[-12.00985, 30.24121], [-25.3475, 27.87574], [-14.43883, 27.02969], [-12.00985, 30.24121]]]]
24593           }
24594         }, {
24595           type: "Feature",
24596           properties: {
24597             iso1A2: "ID",
24598             iso1A3: "IDN",
24599             iso1N3: "360",
24600             wikidata: "Q252",
24601             nameEn: "Indonesia",
24602             aliases: ["RI"]
24603           },
24604           geometry: null
24605         }, {
24606           type: "Feature",
24607           properties: {
24608             iso1A2: "IE",
24609             iso1A3: "IRL",
24610             iso1N3: "372",
24611             wikidata: "Q27",
24612             nameEn: "Republic of Ireland",
24613             groups: ["EU", "Q22890", "154", "150", "UN"],
24614             driveSide: "left",
24615             callingCodes: ["353"]
24616           },
24617           geometry: {
24618             type: "MultiPolygon",
24619             coordinates: [[[[-6.26218, 54.09785], [-6.29003, 54.11278], [-6.32694, 54.09337], [-6.36279, 54.11248], [-6.36605, 54.07234], [-6.47849, 54.06947], [-6.62842, 54.03503], [-6.66264, 54.0666], [-6.6382, 54.17071], [-6.70175, 54.20218], [-6.74575, 54.18788], [-6.81583, 54.22791], [-6.85179, 54.29176], [-6.87775, 54.34682], [-7.02034, 54.4212], [-7.19145, 54.31296], [-7.14908, 54.22732], [-7.25012, 54.20063], [-7.26316, 54.13863], [-7.29493, 54.12013], [-7.29687, 54.1354], [-7.28017, 54.16714], [-7.29157, 54.17191], [-7.34005, 54.14698], [-7.30553, 54.11869], [-7.32834, 54.11475], [-7.44567, 54.1539], [-7.4799, 54.12239], [-7.55812, 54.12239], [-7.69501, 54.20731], [-7.81397, 54.20159], [-7.8596, 54.21779], [-7.87101, 54.29299], [-8.04555, 54.36292], [-8.179, 54.46763], [-8.04538, 54.48941], [-7.99812, 54.54427], [-7.8596, 54.53671], [-7.70315, 54.62077], [-7.93293, 54.66603], [-7.83352, 54.73854], [-7.75041, 54.7103], [-7.64449, 54.75265], [-7.54671, 54.74606], [-7.54508, 54.79401], [-7.47626, 54.83084], [-7.4473, 54.87003], [-7.44404, 54.9403], [-7.40004, 54.94498], [-7.4033, 55.00391], [-7.34464, 55.04688], [-7.2471, 55.06933], [-6.34755, 55.49206], [-7.75229, 55.93854], [-22.01468, 48.19557], [-6.03913, 51.13217], [-5.37267, 53.63269], [-6.26218, 54.09785]]]]
24620           }
24621         }, {
24622           type: "Feature",
24623           properties: {
24624             iso1A2: "IL",
24625             iso1A3: "ISR",
24626             iso1N3: "376",
24627             wikidata: "Q801",
24628             nameEn: "Israel",
24629             groups: ["145", "142", "UN"],
24630             callingCodes: ["972"]
24631           },
24632           geometry: {
24633             type: "MultiPolygon",
24634             coordinates: [[[[34.052, 31.46619], [34.29262, 31.70393], [34.48681, 31.59711], [34.56797, 31.54197], [34.48892, 31.48365], [34.40077, 31.40926], [34.36505, 31.36404], [34.37381, 31.30598], [34.36523, 31.28963], [34.29417, 31.24194], [34.26742, 31.21998], [34.92298, 29.45305], [34.97718, 29.54294], [34.98207, 29.58147], [35.02147, 29.66343], [35.14108, 30.07374], [35.19183, 30.34636], [35.16218, 30.43535], [35.19595, 30.50297], [35.21379, 30.60401], [35.29311, 30.71365], [35.33456, 30.81224], [35.33984, 30.8802], [35.41371, 30.95565], [35.43658, 31.12444], [35.40316, 31.25535], [35.47672, 31.49578], [35.39675, 31.49572], [35.22921, 31.37445], [35.13033, 31.3551], [35.02459, 31.35979], [34.92571, 31.34337], [34.88932, 31.37093], [34.87833, 31.39321], [34.89756, 31.43891], [34.93258, 31.47816], [34.94356, 31.50743], [34.9415, 31.55601], [34.95249, 31.59813], [35.00879, 31.65426], [35.08226, 31.69107], [35.10782, 31.71594], [35.11895, 31.71454], [35.12933, 31.7325], [35.13931, 31.73012], [35.15119, 31.73634], [35.15474, 31.73352], [35.16478, 31.73242], [35.18023, 31.72067], [35.20538, 31.72388], [35.21937, 31.71578], [35.22392, 31.71899], [35.23972, 31.70896], [35.24315, 31.71244], [35.2438, 31.7201], [35.24981, 31.72543], [35.25182, 31.73945], [35.26319, 31.74846], [35.25225, 31.7678], [35.26058, 31.79064], [35.25573, 31.81362], [35.26404, 31.82567], [35.251, 31.83085], [35.25753, 31.8387], [35.24816, 31.8458], [35.2304, 31.84222], [35.2249, 31.85433], [35.22817, 31.8638], [35.22567, 31.86745], [35.22294, 31.87889], [35.22014, 31.88264], [35.2136, 31.88241], [35.21276, 31.88153], [35.21016, 31.88237], [35.20945, 31.8815], [35.20791, 31.8821], [35.20673, 31.88151], [35.20381, 31.86716], [35.21128, 31.863], [35.216, 31.83894], [35.21469, 31.81835], [35.19461, 31.82687], [35.18169, 31.82542], [35.18603, 31.80901], [35.14174, 31.81325], [35.07677, 31.85627], [35.05617, 31.85685], [35.01978, 31.82944], [34.9724, 31.83352], [34.99712, 31.85569], [35.03489, 31.85919], [35.03978, 31.89276], [35.03489, 31.92448], [35.00124, 31.93264], [34.98682, 31.96935], [35.00261, 32.027], [34.9863, 32.09551], [34.99437, 32.10962], [34.98507, 32.12606], [34.99039, 32.14626], [34.96009, 32.17503], [34.95703, 32.19522], [34.98885, 32.20758], [35.01841, 32.23981], [35.02939, 32.2671], [35.01119, 32.28684], [35.01772, 32.33863], [35.04243, 32.35008], [35.05142, 32.3667], [35.0421, 32.38242], [35.05311, 32.4024], [35.05423, 32.41754], [35.07059, 32.4585], [35.08564, 32.46948], [35.09236, 32.47614], [35.10024, 32.47856], [35.10882, 32.4757], [35.15937, 32.50466], [35.2244, 32.55289], [35.25049, 32.52453], [35.29306, 32.50947], [35.30685, 32.51024], [35.35212, 32.52047], [35.40224, 32.50136], [35.42034, 32.46009], [35.41598, 32.45593], [35.41048, 32.43706], [35.42078, 32.41562], [35.55807, 32.38674], [35.55494, 32.42687], [35.57485, 32.48669], [35.56614, 32.64393], [35.59813, 32.65159], [35.61669, 32.67999], [35.66527, 32.681], [35.68467, 32.70715], [35.75983, 32.74803], [35.78745, 32.77938], [35.83758, 32.82817], [35.84021, 32.8725], [35.87012, 32.91976], [35.89298, 32.9456], [35.87188, 32.98028], [35.84802, 33.1031], [35.81911, 33.11077], [35.81911, 33.1336], [35.84285, 33.16673], [35.83846, 33.19397], [35.81647, 33.2028], [35.81295, 33.24841], [35.77513, 33.27342], [35.813, 33.3172], [35.77477, 33.33609], [35.62019, 33.27278], [35.62283, 33.24226], [35.58502, 33.26653], [35.58326, 33.28381], [35.56523, 33.28969], [35.55555, 33.25844], [35.54544, 33.25513], [35.54808, 33.236], [35.5362, 33.23196], [35.54228, 33.19865], [35.52573, 33.11921], [35.50335, 33.114], [35.50272, 33.09056], [35.448, 33.09264], [35.43059, 33.06659], [35.35223, 33.05617], [35.31429, 33.10515], [35.1924, 33.08743], [35.10645, 33.09318], [34.78515, 33.20368], [33.62659, 31.82938], [34.052, 31.46619]]]]
24635           }
24636         }, {
24637           type: "Feature",
24638           properties: {
24639             iso1A2: "IM",
24640             iso1A3: "IMN",
24641             iso1N3: "833",
24642             wikidata: "Q9676",
24643             nameEn: "Isle of Man",
24644             country: "GB",
24645             groups: ["Q185086", "154", "150", "UN"],
24646             driveSide: "left",
24647             roadSpeedUnit: "mph",
24648             roadHeightUnit: "ft",
24649             callingCodes: ["44 01624", "44 07624", "44 07524", "44 07924"]
24650           },
24651           geometry: {
24652             type: "MultiPolygon",
24653             coordinates: [[[[-3.98763, 54.07351], [-4.1819, 54.57861], [-5.6384, 53.81157], [-3.98763, 54.07351]]]]
24654           }
24655         }, {
24656           type: "Feature",
24657           properties: {
24658             iso1A2: "IN",
24659             iso1A3: "IND",
24660             iso1N3: "356",
24661             wikidata: "Q668",
24662             nameEn: "India"
24663           },
24664           geometry: null
24665         }, {
24666           type: "Feature",
24667           properties: {
24668             iso1A2: "IO",
24669             iso1A3: "IOT",
24670             iso1N3: "086",
24671             wikidata: "Q43448",
24672             nameEn: "British Indian Ocean Territory",
24673             country: "GB"
24674           },
24675           geometry: null
24676         }, {
24677           type: "Feature",
24678           properties: {
24679             iso1A2: "IQ",
24680             iso1A3: "IRQ",
24681             iso1N3: "368",
24682             wikidata: "Q796",
24683             nameEn: "Iraq",
24684             groups: ["145", "142", "UN"],
24685             callingCodes: ["964"]
24686           },
24687           geometry: {
24688             type: "MultiPolygon",
24689             coordinates: [[[[42.78887, 37.38615], [42.56725, 37.14878], [42.35724, 37.10998], [42.36697, 37.0627], [41.81736, 36.58782], [41.40058, 36.52502], [41.28864, 36.35368], [41.2564, 36.06012], [41.37027, 35.84095], [41.38184, 35.62502], [41.26569, 35.42708], [41.21654, 35.1508], [41.2345, 34.80049], [41.12388, 34.65742], [40.97676, 34.39788], [40.64314, 34.31604], [38.79171, 33.37328], [39.08202, 32.50304], [38.98762, 32.47694], [39.04251, 32.30203], [39.26157, 32.35555], [39.29903, 32.23259], [40.01521, 32.05667], [42.97601, 30.72204], [42.97796, 30.48295], [44.72255, 29.19736], [46.42415, 29.05947], [46.5527, 29.10283], [46.89695, 29.50584], [47.15166, 30.01044], [47.37192, 30.10421], [47.7095, 30.10453], [48.01114, 29.98906], [48.06782, 30.02906], [48.17332, 30.02448], [48.40479, 29.85763], [48.59531, 29.66815], [48.83867, 29.78572], [48.61441, 29.93675], [48.51011, 29.96238], [48.44785, 30.00148], [48.4494, 30.04456], [48.43384, 30.08233], [48.38869, 30.11062], [48.38714, 30.13485], [48.41671, 30.17254], [48.41117, 30.19846], [48.26393, 30.3408], [48.24385, 30.33846], [48.21279, 30.31644], [48.19425, 30.32796], [48.18321, 30.39703], [48.14585, 30.44133], [48.02443, 30.4789], [48.03221, 30.9967], [47.68219, 31.00004], [47.6804, 31.39086], [47.86337, 31.78422], [47.64771, 32.07666], [47.52474, 32.15972], [47.57144, 32.20583], [47.37529, 32.47808], [47.17218, 32.45393], [46.46788, 32.91992], [46.32298, 32.9731], [46.17198, 32.95612], [46.09103, 32.98354], [46.15175, 33.07229], [46.03966, 33.09577], [46.05367, 33.13097], [46.11905, 33.11924], [46.20623, 33.20395], [45.99919, 33.5082], [45.86687, 33.49263], [45.96183, 33.55751], [45.89801, 33.63661], [45.77814, 33.60938], [45.50261, 33.94968], [45.42789, 33.9458], [45.41077, 33.97421], [45.47264, 34.03099], [45.56176, 34.15088], [45.58667, 34.30147], [45.53552, 34.35148], [45.49171, 34.3439], [45.46697, 34.38221], [45.43879, 34.45949], [45.51883, 34.47692], [45.53219, 34.60441], [45.59074, 34.55558], [45.60224, 34.55057], [45.73923, 34.54416], [45.70031, 34.69277], [45.65672, 34.7222], [45.68284, 34.76624], [45.70031, 34.82322], [45.73641, 34.83975], [45.79682, 34.85133], [45.78904, 34.91135], [45.86532, 34.89858], [45.89477, 34.95805], [45.87864, 35.03441], [45.92173, 35.0465], [45.92203, 35.09538], [45.93108, 35.08148], [45.94756, 35.09188], [46.06508, 35.03699], [46.07747, 35.0838], [46.11763, 35.07551], [46.19116, 35.11097], [46.15642, 35.1268], [46.16229, 35.16984], [46.19738, 35.18536], [46.18457, 35.22561], [46.11367, 35.23729], [46.15474, 35.2883], [46.13152, 35.32548], [46.05358, 35.38568], [45.98453, 35.49848], [46.01518, 35.52012], [45.97584, 35.58132], [46.03028, 35.57416], [46.01307, 35.59756], [46.0165, 35.61501], [45.99452, 35.63574], [46.0117, 35.65059], [46.01631, 35.69139], [46.23736, 35.71414], [46.34166, 35.78363], [46.32921, 35.82655], [46.17198, 35.8013], [46.08325, 35.8581], [45.94711, 35.82218], [45.89784, 35.83708], [45.81442, 35.82107], [45.76145, 35.79898], [45.6645, 35.92872], [45.60018, 35.96069], [45.55245, 35.99943], [45.46594, 36.00042], [45.38275, 35.97156], [45.33916, 35.99424], [45.37652, 36.06222], [45.37312, 36.09917], [45.32235, 36.17383], [45.30038, 36.27769], [45.26261, 36.3001], [45.27394, 36.35846], [45.23953, 36.43257], [45.11811, 36.40751], [45.00759, 36.5402], [45.06985, 36.62645], [45.06985, 36.6814], [45.01537, 36.75128], [44.84725, 36.77622], [44.83479, 36.81362], [44.90173, 36.86096], [44.91199, 36.91468], [44.89862, 37.01897], [44.81611, 37.04383], [44.75229, 37.11958], [44.78319, 37.1431], [44.76698, 37.16162], [44.63179, 37.19229], [44.42631, 37.05825], [44.38117, 37.05825], [44.35315, 37.04955], [44.35937, 37.02843], [44.30645, 36.97373], [44.25975, 36.98119], [44.18503, 37.09551], [44.22239, 37.15756], [44.27998, 37.16501], [44.2613, 37.25055], [44.13521, 37.32486], [44.02002, 37.33229], [43.90949, 37.22453], [43.84878, 37.22205], [43.82699, 37.19477], [43.8052, 37.22825], [43.7009, 37.23692], [43.63085, 37.21957], [43.56702, 37.25675], [43.50787, 37.24436], [43.33508, 37.33105], [43.30083, 37.30629], [43.11403, 37.37436], [42.93705, 37.32015], [42.78887, 37.38615]]]]
24690           }
24691         }, {
24692           type: "Feature",
24693           properties: {
24694             iso1A2: "IR",
24695             iso1A3: "IRN",
24696             iso1N3: "364",
24697             wikidata: "Q794",
24698             nameEn: "Iran",
24699             groups: ["034", "142", "UN"],
24700             callingCodes: ["98"]
24701           },
24702           geometry: {
24703             type: "MultiPolygon",
24704             coordinates: [[[[44.96746, 39.42998], [44.88916, 39.59653], [44.81043, 39.62677], [44.71806, 39.71124], [44.65422, 39.72163], [44.6137, 39.78393], [44.47298, 39.68788], [44.48111, 39.61579], [44.41849, 39.56659], [44.42832, 39.4131], [44.37921, 39.4131], [44.29818, 39.378], [44.22452, 39.4169], [44.03667, 39.39223], [44.1043, 39.19842], [44.20946, 39.13975], [44.18863, 38.93881], [44.30322, 38.81581], [44.26155, 38.71427], [44.28065, 38.6465], [44.32058, 38.62752], [44.3207, 38.49799], [44.3119, 38.37887], [44.38309, 38.36117], [44.44386, 38.38295], [44.50115, 38.33939], [44.42476, 38.25763], [44.22509, 37.88859], [44.3883, 37.85433], [44.45948, 37.77065], [44.55498, 37.783], [44.62096, 37.71985], [44.56887, 37.6429], [44.61401, 37.60165], [44.58449, 37.45018], [44.81021, 37.2915], [44.75986, 37.21549], [44.7868, 37.16644], [44.78319, 37.1431], [44.75229, 37.11958], [44.81611, 37.04383], [44.89862, 37.01897], [44.91199, 36.91468], [44.90173, 36.86096], [44.83479, 36.81362], [44.84725, 36.77622], [45.01537, 36.75128], [45.06985, 36.6814], [45.06985, 36.62645], [45.00759, 36.5402], [45.11811, 36.40751], [45.23953, 36.43257], [45.27394, 36.35846], [45.26261, 36.3001], [45.30038, 36.27769], [45.32235, 36.17383], [45.37312, 36.09917], [45.37652, 36.06222], [45.33916, 35.99424], [45.38275, 35.97156], [45.46594, 36.00042], [45.55245, 35.99943], [45.60018, 35.96069], [45.6645, 35.92872], [45.76145, 35.79898], [45.81442, 35.82107], [45.89784, 35.83708], [45.94711, 35.82218], [46.08325, 35.8581], [46.17198, 35.8013], [46.32921, 35.82655], [46.34166, 35.78363], [46.23736, 35.71414], [46.01631, 35.69139], [46.0117, 35.65059], [45.99452, 35.63574], [46.0165, 35.61501], [46.01307, 35.59756], [46.03028, 35.57416], [45.97584, 35.58132], [46.01518, 35.52012], [45.98453, 35.49848], [46.05358, 35.38568], [46.13152, 35.32548], [46.15474, 35.2883], [46.11367, 35.23729], [46.18457, 35.22561], [46.19738, 35.18536], [46.16229, 35.16984], [46.15642, 35.1268], [46.19116, 35.11097], [46.11763, 35.07551], [46.07747, 35.0838], [46.06508, 35.03699], [45.94756, 35.09188], [45.93108, 35.08148], [45.92203, 35.09538], [45.92173, 35.0465], [45.87864, 35.03441], [45.89477, 34.95805], [45.86532, 34.89858], [45.78904, 34.91135], [45.79682, 34.85133], [45.73641, 34.83975], [45.70031, 34.82322], [45.68284, 34.76624], [45.65672, 34.7222], [45.70031, 34.69277], [45.73923, 34.54416], [45.60224, 34.55057], [45.59074, 34.55558], [45.53219, 34.60441], [45.51883, 34.47692], [45.43879, 34.45949], [45.46697, 34.38221], [45.49171, 34.3439], [45.53552, 34.35148], [45.58667, 34.30147], [45.56176, 34.15088], [45.47264, 34.03099], [45.41077, 33.97421], [45.42789, 33.9458], [45.50261, 33.94968], [45.77814, 33.60938], [45.89801, 33.63661], [45.96183, 33.55751], [45.86687, 33.49263], [45.99919, 33.5082], [46.20623, 33.20395], [46.11905, 33.11924], [46.05367, 33.13097], [46.03966, 33.09577], [46.15175, 33.07229], [46.09103, 32.98354], [46.17198, 32.95612], [46.32298, 32.9731], [46.46788, 32.91992], [47.17218, 32.45393], [47.37529, 32.47808], [47.57144, 32.20583], [47.52474, 32.15972], [47.64771, 32.07666], [47.86337, 31.78422], [47.6804, 31.39086], [47.68219, 31.00004], [48.03221, 30.9967], [48.02443, 30.4789], [48.14585, 30.44133], [48.18321, 30.39703], [48.19425, 30.32796], [48.21279, 30.31644], [48.24385, 30.33846], [48.26393, 30.3408], [48.41117, 30.19846], [48.41671, 30.17254], [48.38714, 30.13485], [48.38869, 30.11062], [48.43384, 30.08233], [48.4494, 30.04456], [48.44785, 30.00148], [48.51011, 29.96238], [48.61441, 29.93675], [48.83867, 29.78572], [49.98877, 27.87827], [50.37726, 27.89227], [54.39838, 25.68383], [55.14145, 25.62624], [55.81777, 26.18798], [56.2644, 26.58649], [56.68954, 26.76645], [56.79239, 26.41236], [56.82555, 25.7713], [56.86325, 25.03856], [61.46682, 24.57869], [61.6433, 25.27541], [61.683, 25.66638], [61.83968, 25.7538], [61.83831, 26.07249], [61.89391, 26.26251], [62.05117, 26.31647], [62.21304, 26.26601], [62.31484, 26.528], [62.77352, 26.64099], [63.1889, 26.65072], [63.18688, 26.83844], [63.25005, 26.84212], [63.25005, 27.08692], [63.32283, 27.14437], [63.19649, 27.25674], [62.80604, 27.22412], [62.79684, 27.34381], [62.84905, 27.47627], [62.7638, 28.02992], [62.79412, 28.28108], [62.59499, 28.24842], [62.40259, 28.42703], [61.93581, 28.55284], [61.65978, 28.77937], [61.53765, 29.00507], [61.31508, 29.38903], [60.87231, 29.86514], [61.80829, 30.84224], [61.78268, 30.92724], [61.8335, 30.97669], [61.83257, 31.0452], [61.80957, 31.12576], [61.80569, 31.16167], [61.70929, 31.37391], [60.84541, 31.49561], [60.86191, 32.22565], [60.56485, 33.12944], [60.88908, 33.50219], [60.91133, 33.55596], [60.69573, 33.56054], [60.57762, 33.59772], [60.5485, 33.73422], [60.5838, 33.80793], [60.50209, 34.13992], [60.66502, 34.31539], [60.91321, 34.30411], [60.72316, 34.52857], [60.99922, 34.63064], [61.00197, 34.70631], [61.06926, 34.82139], [61.12831, 35.09938], [61.0991, 35.27845], [61.18187, 35.30249], [61.27371, 35.61482], [61.22719, 35.67038], [61.26152, 35.80749], [61.22444, 35.92879], [61.12007, 35.95992], [61.22719, 36.12759], [61.1393, 36.38782], [61.18187, 36.55348], [61.14516, 36.64644], [60.34767, 36.63214], [60.00768, 37.04102], [59.74678, 37.12499], [59.55178, 37.13594], [59.39385, 37.34257], [59.39797, 37.47892], [59.33507, 37.53146], [59.22905, 37.51161], [58.9338, 37.67374], [58.6921, 37.64548], [58.5479, 37.70526], [58.47786, 37.6433], [58.39959, 37.63134], [58.22999, 37.6856], [58.21399, 37.77281], [57.79534, 37.89299], [57.35042, 37.98546], [57.37236, 38.09321], [57.21169, 38.28965], [57.03453, 38.18717], [56.73928, 38.27887], [56.62255, 38.24005], [56.43303, 38.26054], [56.32454, 38.18502], [56.33278, 38.08132], [55.97847, 38.08024], [55.76561, 38.12238], [55.44152, 38.08564], [55.13412, 37.94705], [54.851, 37.75739], [54.77684, 37.62264], [54.81804, 37.61285], [54.77822, 37.51597], [54.67247, 37.43532], [54.58664, 37.45809], [54.36211, 37.34912], [54.24565, 37.32047], [53.89734, 37.3464], [48.88288, 38.43975], [48.84969, 38.45015], [48.81072, 38.44853], [48.78979, 38.45026], [48.70001, 38.40564], [48.62217, 38.40198], [48.58793, 38.45076], [48.45084, 38.61013], [48.3146, 38.59958], [48.24773, 38.71883], [48.02581, 38.82705], [48.01409, 38.90333], [48.07734, 38.91616], [48.08627, 38.94434], [48.28437, 38.97186], [48.33884, 39.03022], [48.31239, 39.09278], [48.15361, 39.19419], [48.12404, 39.25208], [48.15984, 39.30028], [48.37385, 39.37584], [48.34264, 39.42935], [47.98977, 39.70999], [47.84774, 39.66285], [47.50099, 39.49615], [47.38978, 39.45999], [47.31301, 39.37492], [47.05927, 39.24846], [47.05771, 39.20143], [46.95341, 39.13505], [46.92539, 39.16644], [46.83822, 39.13143], [46.75752, 39.03231], [46.53497, 38.86548], [46.34059, 38.92076], [46.20601, 38.85262], [46.14785, 38.84206], [46.06766, 38.87861], [46.00228, 38.87376], [45.94624, 38.89072], [45.90266, 38.87739], [45.83883, 38.90768], [45.65172, 38.95199], [45.6155, 38.94304], [45.6131, 38.964], [45.44966, 38.99243], [45.44811, 39.04927], [45.40452, 39.07224], [45.40148, 39.09007], [45.30489, 39.18333], [45.16168, 39.21952], [45.08751, 39.35052], [45.05932, 39.36435], [44.96746, 39.42998]]]]
24705           }
24706         }, {
24707           type: "Feature",
24708           properties: {
24709             iso1A2: "IS",
24710             iso1A3: "ISL",
24711             iso1N3: "352",
24712             wikidata: "Q189",
24713             nameEn: "Iceland",
24714             groups: ["154", "150", "UN"],
24715             callingCodes: ["354"]
24716           },
24717           geometry: {
24718             type: "MultiPolygon",
24719             coordinates: [[[[-33.15676, 62.62995], [-8.25539, 63.0423], [-15.70914, 69.67442], [-33.15676, 62.62995]]]]
24720           }
24721         }, {
24722           type: "Feature",
24723           properties: {
24724             iso1A2: "IT",
24725             iso1A3: "ITA",
24726             iso1N3: "380",
24727             wikidata: "Q38",
24728             nameEn: "Italy",
24729             groups: ["EU", "039", "150", "UN"],
24730             callingCodes: ["39"]
24731           },
24732           geometry: {
24733             type: "MultiPolygon",
24734             coordinates: [[[[8.95861, 45.96485], [8.97604, 45.96151], [8.97741, 45.98317], [8.96668, 45.98436], [8.95861, 45.96485]]], [[[7.63035, 43.57419], [9.56115, 43.20816], [10.09675, 41.44089], [7.60802, 41.05927], [7.89009, 38.19924], [11.2718, 37.6713], [12.13667, 34.20326], [14.02721, 36.53141], [17.67657, 35.68918], [18.83516, 40.36999], [16.15283, 42.18525], [13.12821, 44.48877], [13.05142, 45.33128], [13.45644, 45.59464], [13.6076, 45.64761], [13.7198, 45.59352], [13.74587, 45.59811], [13.78445, 45.5825], [13.84106, 45.58185], [13.86771, 45.59898], [13.8695, 45.60835], [13.9191, 45.6322], [13.87933, 45.65207], [13.83422, 45.68703], [13.83332, 45.70855], [13.8235, 45.7176], [13.66986, 45.79955], [13.59784, 45.8072], [13.58858, 45.83503], [13.57563, 45.8425], [13.58644, 45.88173], [13.59565, 45.89446], [13.60857, 45.89907], [13.61931, 45.91782], [13.63815, 45.93607], [13.6329, 45.94894], [13.64307, 45.98326], [13.63458, 45.98947], [13.62074, 45.98388], [13.58903, 45.99009], [13.56759, 45.96991], [13.52963, 45.96588], [13.50104, 45.98078], [13.47474, 46.00546], [13.49702, 46.01832], [13.50998, 46.04498], [13.49568, 46.04839], [13.50104, 46.05986], [13.57072, 46.09022], [13.64053, 46.13587], [13.66472, 46.17392], [13.64451, 46.18966], [13.56682, 46.18703], [13.56114, 46.2054], [13.47587, 46.22725], [13.42218, 46.20758], [13.37671, 46.29668], [13.44808, 46.33507], [13.43418, 46.35992], [13.47019, 46.3621], [13.5763, 46.40915], [13.5763, 46.42613], [13.59777, 46.44137], [13.68684, 46.43881], [13.7148, 46.5222], [13.64088, 46.53438], [13.27627, 46.56059], [12.94445, 46.60401], [12.59992, 46.6595], [12.38708, 46.71529], [12.27591, 46.88651], [12.2006, 46.88854], [12.11675, 47.01241], [12.21781, 47.03996], [12.19254, 47.09331], [11.74789, 46.98484], [11.50739, 47.00644], [11.33355, 46.99862], [11.10618, 46.92966], [11.00764, 46.76896], [10.72974, 46.78972], [10.75753, 46.82258], [10.66405, 46.87614], [10.54783, 46.84505], [10.47197, 46.85698], [10.38659, 46.67847], [10.40475, 46.63671], [10.44686, 46.64162], [10.49375, 46.62049], [10.46136, 46.53164], [10.25309, 46.57432], [10.23674, 46.63484], [10.10307, 46.61003], [10.03715, 46.44479], [10.165, 46.41051], [10.10506, 46.3372], [10.17862, 46.25626], [10.14439, 46.22992], [10.07055, 46.21668], [9.95249, 46.38045], [9.73086, 46.35071], [9.71273, 46.29266], [9.57015, 46.2958], [9.46117, 46.37481], [9.45936, 46.50873], [9.40487, 46.46621], [9.36128, 46.5081], [9.28136, 46.49685], [9.25502, 46.43743], [9.29226, 46.32717], [9.24503, 46.23616], [9.01618, 46.04928], [8.99257, 45.9698], [9.09065, 45.89906], [9.06642, 45.8761], [9.04546, 45.84968], [9.04059, 45.8464], [9.03505, 45.83976], [9.03793, 45.83548], [9.03279, 45.82865], [9.0298, 45.82127], [9.00324, 45.82055], [8.99663, 45.83466], [8.9621, 45.83707], [8.94737, 45.84285], [8.91129, 45.8388], [8.93504, 45.86245], [8.94372, 45.86587], [8.93649, 45.86775], [8.88904, 45.95465], [8.86688, 45.96135], [8.85121, 45.97239], [8.8319, 45.9879], [8.79362, 45.99207], [8.78585, 45.98973], [8.79414, 46.00913], [8.85617, 46.0748], [8.80778, 46.10085], [8.75697, 46.10395], [8.62242, 46.12112], [8.45032, 46.26869], [8.46317, 46.43712], [8.42464, 46.46367], [8.30648, 46.41587], [8.31162, 46.38044], [8.08814, 46.26692], [8.16866, 46.17817], [8.11383, 46.11577], [8.02906, 46.10331], [7.98881, 45.99867], [7.9049, 45.99945], [7.85949, 45.91485], [7.56343, 45.97421], [7.10685, 45.85653], [7.04151, 45.92435], [6.95315, 45.85163], [6.80785, 45.83265], [6.80785, 45.71864], [6.98948, 45.63869], [7.00037, 45.509], [7.18019, 45.40071], [7.10572, 45.32924], [7.13115, 45.25386], [7.07074, 45.21228], [6.96706, 45.20841], [6.85144, 45.13226], [6.7697, 45.16044], [6.62803, 45.11175], [6.66981, 45.02324], [6.74791, 45.01939], [6.74519, 44.93661], [6.75518, 44.89915], [6.90774, 44.84322], [6.93499, 44.8664], [7.02217, 44.82519], [7.00401, 44.78782], [7.07484, 44.68073], [7.00582, 44.69364], [6.95133, 44.66264], [6.96042, 44.62129], [6.85507, 44.53072], [6.86233, 44.49834], [6.94504, 44.43112], [6.88784, 44.42043], [6.89171, 44.36637], [6.98221, 44.28289], [7.00764, 44.23736], [7.16929, 44.20352], [7.27827, 44.1462], [7.34547, 44.14359], [7.36364, 44.11882], [7.62155, 44.14881], [7.63245, 44.17877], [7.68694, 44.17487], [7.66878, 44.12795], [7.72508, 44.07578], [7.6597, 44.03009], [7.66848, 43.99943], [7.65266, 43.9763], [7.60771, 43.95772], [7.56858, 43.94506], [7.56075, 43.89932], [7.51162, 43.88301], [7.49355, 43.86551], [7.50423, 43.84345], [7.53006, 43.78405], [7.63035, 43.57419]], [[12.45181, 41.90056], [12.44834, 41.90095], [12.44582, 41.90194], [12.44815, 41.90326], [12.44984, 41.90545], [12.45091, 41.90625], [12.45543, 41.90738], [12.45561, 41.90629], [12.45762, 41.9058], [12.45755, 41.9033], [12.45826, 41.90281], [12.45834, 41.90174], [12.4577, 41.90115], [12.45691, 41.90125], [12.45626, 41.90172], [12.45435, 41.90143], [12.45446, 41.90028], [12.45181, 41.90056]], [[12.45648, 43.89369], [12.44184, 43.90498], [12.41641, 43.89991], [12.40935, 43.9024], [12.41233, 43.90956], [12.40733, 43.92379], [12.41551, 43.92984], [12.41165, 43.93769], [12.40506, 43.94325], [12.40415, 43.95485], [12.41414, 43.95273], [12.42005, 43.9578], [12.43662, 43.95698], [12.44684, 43.96597], [12.46205, 43.97463], [12.47853, 43.98052], [12.49406, 43.98492], [12.50678, 43.99113], [12.51463, 43.99122], [12.5154, 43.98508], [12.51064, 43.98165], [12.51109, 43.97201], [12.50622, 43.97131], [12.50875, 43.96198], [12.50655, 43.95796], [12.51427, 43.94897], [12.51553, 43.94096], [12.50496, 43.93017], [12.50269, 43.92363], [12.49724, 43.92248], [12.49247, 43.91774], [12.49429, 43.90973], [12.48771, 43.89706], [12.45648, 43.89369]]]]
24735           }
24736         }, {
24737           type: "Feature",
24738           properties: {
24739             iso1A2: "JE",
24740             iso1A3: "JEY",
24741             iso1N3: "832",
24742             wikidata: "Q785",
24743             nameEn: "Bailiwick of Jersey",
24744             country: "GB",
24745             groups: ["830", "Q185086", "154", "150", "UN"],
24746             driveSide: "left",
24747             roadSpeedUnit: "mph",
24748             roadHeightUnit: "ft",
24749             callingCodes: ["44 01534"]
24750           },
24751           geometry: {
24752             type: "MultiPolygon",
24753             coordinates: [[[[-2.00491, 48.86706], [-1.83944, 49.23037], [-2.09454, 49.46288], [-2.65349, 49.15373], [-2.00491, 48.86706]]]]
24754           }
24755         }, {
24756           type: "Feature",
24757           properties: {
24758             iso1A2: "JM",
24759             iso1A3: "JAM",
24760             iso1N3: "388",
24761             wikidata: "Q766",
24762             nameEn: "Jamaica",
24763             aliases: ["JA"],
24764             groups: ["029", "003", "419", "019", "UN"],
24765             driveSide: "left",
24766             callingCodes: ["1 876", "1 658"]
24767           },
24768           geometry: {
24769             type: "MultiPolygon",
24770             coordinates: [[[[-74.09729, 17.36817], [-78.9741, 19.59515], [-78.34606, 16.57862], [-74.09729, 17.36817]]]]
24771           }
24772         }, {
24773           type: "Feature",
24774           properties: {
24775             iso1A2: "JO",
24776             iso1A3: "JOR",
24777             iso1N3: "400",
24778             wikidata: "Q810",
24779             nameEn: "Jordan",
24780             groups: ["145", "142", "UN"],
24781             callingCodes: ["962"]
24782           },
24783           geometry: {
24784             type: "MultiPolygon",
24785             coordinates: [[[[39.04251, 32.30203], [38.98762, 32.47694], [39.08202, 32.50304], [38.79171, 33.37328], [36.83946, 32.31293], [36.40959, 32.37908], [36.23948, 32.50108], [36.20875, 32.49529], [36.20379, 32.52751], [36.08074, 32.51463], [36.02239, 32.65911], [35.96633, 32.66237], [35.93307, 32.71966], [35.88405, 32.71321], [35.75983, 32.74803], [35.68467, 32.70715], [35.66527, 32.681], [35.61669, 32.67999], [35.59813, 32.65159], [35.56614, 32.64393], [35.57485, 32.48669], [35.55494, 32.42687], [35.55807, 32.38674], [35.57111, 32.21877], [35.52012, 32.04076], [35.54375, 31.96587], [35.52758, 31.9131], [35.55941, 31.76535], [35.47672, 31.49578], [35.40316, 31.25535], [35.43658, 31.12444], [35.41371, 30.95565], [35.33984, 30.8802], [35.33456, 30.81224], [35.29311, 30.71365], [35.21379, 30.60401], [35.19595, 30.50297], [35.16218, 30.43535], [35.19183, 30.34636], [35.14108, 30.07374], [35.02147, 29.66343], [34.98207, 29.58147], [34.97718, 29.54294], [34.92298, 29.45305], [34.8812, 29.36878], [36.07081, 29.18469], [36.50005, 29.49696], [36.75083, 29.86903], [37.4971, 29.99949], [37.66395, 30.33245], [37.99354, 30.49998], [36.99791, 31.50081], [38.99233, 31.99721], [39.29903, 32.23259], [39.26157, 32.35555], [39.04251, 32.30203]]]]
24786           }
24787         }, {
24788           type: "Feature",
24789           properties: {
24790             iso1A2: "JP",
24791             iso1A3: "JPN",
24792             iso1N3: "392",
24793             wikidata: "Q17",
24794             nameEn: "Japan",
24795             groups: ["030", "142", "UN"],
24796             driveSide: "left",
24797             callingCodes: ["81"]
24798           },
24799           geometry: {
24800             type: "MultiPolygon",
24801             coordinates: [[[[145.82361, 43.38904], [145.23667, 43.76813], [145.82343, 44.571], [140.9182, 45.92937], [133.61399, 37.41], [129.2669, 34.87122], [122.26612, 25.98197], [123.92912, 17.8782], [155.16731, 23.60141], [145.82361, 43.38904]]]]
24802           }
24803         }, {
24804           type: "Feature",
24805           properties: {
24806             iso1A2: "KE",
24807             iso1A3: "KEN",
24808             iso1N3: "404",
24809             wikidata: "Q114",
24810             nameEn: "Kenya",
24811             groups: ["014", "202", "002", "UN"],
24812             driveSide: "left",
24813             callingCodes: ["254"]
24814           },
24815           geometry: {
24816             type: "MultiPolygon",
24817             coordinates: [[[[35.9419, 4.61933], [35.51424, 4.61643], [35.42366, 4.76969], [35.47843, 4.91872], [35.30992, 4.90402], [35.34151, 5.02364], [34.47601, 4.72162], [33.9873, 4.23316], [34.06046, 4.15235], [34.15429, 3.80464], [34.45815, 3.67385], [34.44922, 3.51627], [34.39112, 3.48802], [34.41794, 3.44342], [34.40006, 3.37949], [34.45815, 3.18319], [34.56242, 3.11478], [34.60114, 2.93034], [34.65774, 2.8753], [34.73967, 2.85447], [34.78137, 2.76223], [34.77244, 2.70272], [34.95267, 2.47209], [34.90947, 2.42447], [34.98692, 1.97348], [34.9899, 1.6668], [34.92734, 1.56109], [34.87819, 1.5596], [34.7918, 1.36752], [34.82606, 1.30944], [34.82606, 1.26626], [34.80223, 1.22754], [34.67562, 1.21265], [34.58029, 1.14712], [34.57427, 1.09868], [34.52369, 1.10692], [34.43349, 0.85254], [34.40041, 0.80266], [34.31516, 0.75693], [34.27345, 0.63182], [34.20196, 0.62289], [34.13493, 0.58118], [34.11408, 0.48884], [34.08727, 0.44713], [34.10067, 0.36372], [33.90936, 0.10581], [33.98449, -0.13079], [33.9264, -0.54188], [33.93107, -0.99298], [34.02286, -1.00779], [34.03084, -1.05101], [34.0824, -1.02264], [37.67199, -3.06222], [37.71745, -3.304], [37.5903, -3.42735], [37.63099, -3.50723], [37.75036, -3.54243], [37.81321, -3.69179], [39.21631, -4.67835], [39.44306, -4.93877], [39.62121, -4.68136], [41.75542, -1.85308], [41.56362, -1.66375], [41.56, -1.59812], [41.00099, -0.83068], [40.98767, 2.82959], [41.31368, 3.14314], [41.89488, 3.97375], [41.1754, 3.94079], [40.77498, 4.27683], [39.86043, 3.86974], [39.76808, 3.67058], [39.58339, 3.47434], [39.55132, 3.39634], [39.51551, 3.40895], [39.49444, 3.45521], [39.19954, 3.47834], [39.07736, 3.5267], [38.91938, 3.51198], [38.52336, 3.62551], [38.45812, 3.60445], [38.14168, 3.62487], [37.07724, 4.33503], [36.84474, 4.44518], [36.03924, 4.44406], [35.95449, 4.53244], [35.9419, 4.61933]]]]
24818           }
24819         }, {
24820           type: "Feature",
24821           properties: {
24822             iso1A2: "KG",
24823             iso1A3: "KGZ",
24824             iso1N3: "417",
24825             wikidata: "Q813",
24826             nameEn: "Kyrgyzstan",
24827             groups: ["143", "142", "UN"],
24828             callingCodes: ["996"]
24829           },
24830           geometry: {
24831             type: "MultiPolygon",
24832             coordinates: [[[[74.88756, 42.98612], [74.75, 42.99029], [74.70331, 43.02519], [74.64615, 43.05881], [74.57491, 43.13702], [74.22489, 43.24657], [73.55634, 43.03071], [73.50992, 42.82356], [73.44393, 42.43098], [71.88792, 42.83578], [71.62405, 42.76613], [71.53272, 42.8014], [71.2724, 42.77853], [71.22785, 42.69248], [71.17807, 42.67381], [71.15232, 42.60486], [70.97717, 42.50147], [70.85973, 42.30188], [70.94483, 42.26238], [71.13263, 42.28356], [71.28719, 42.18033], [70.69777, 41.92554], [70.17682, 41.5455], [70.48909, 41.40335], [70.67586, 41.47953], [70.78572, 41.36419], [70.77885, 41.24813], [70.86263, 41.23833], [70.9615, 41.16393], [71.02193, 41.19494], [71.11806, 41.15359], [71.25813, 41.18796], [71.27187, 41.11015], [71.34877, 41.16807], [71.40198, 41.09436], [71.46148, 41.13958], [71.43814, 41.19644], [71.46688, 41.31883], [71.57227, 41.29175], [71.6787, 41.42111], [71.65914, 41.49599], [71.73054, 41.54713], [71.71132, 41.43012], [71.76625, 41.4466], [71.83914, 41.3546], [71.91457, 41.2982], [71.85964, 41.19081], [72.07249, 41.11739], [72.10745, 41.15483], [72.16433, 41.16483], [72.17594, 41.15522], [72.14864, 41.13363], [72.1792, 41.10621], [72.21061, 41.05607], [72.17594, 41.02377], [72.18339, 40.99571], [72.324, 41.03381], [72.34026, 41.04539], [72.34757, 41.06104], [72.36138, 41.04384], [72.38511, 41.02785], [72.45206, 41.03018], [72.48742, 40.97136], [72.55109, 40.96046], [72.59136, 40.86947], [72.68157, 40.84942], [72.84291, 40.85512], [72.94454, 40.8094], [73.01869, 40.84681], [73.13267, 40.83512], [73.13412, 40.79122], [73.0612, 40.76678], [72.99133, 40.76457], [72.93296, 40.73089], [72.8722, 40.71111], [72.85372, 40.7116], [72.84754, 40.67229], [72.80137, 40.67856], [72.74866, 40.60873], [72.74894, 40.59592], [72.75982, 40.57273], [72.74862, 40.57131], [72.74768, 40.58051], [72.73995, 40.58409], [72.69579, 40.59778], [72.66713, 40.59076], [72.66713, 40.5219], [72.47795, 40.5532], [72.40517, 40.61917], [72.34406, 40.60144], [72.41714, 40.55736], [72.38384, 40.51535], [72.41513, 40.50856], [72.44191, 40.48222], [72.40346, 40.4007], [72.24368, 40.46091], [72.18648, 40.49893], [71.96401, 40.31907], [72.05464, 40.27586], [71.85002, 40.25647], [71.82646, 40.21872], [71.73054, 40.14818], [71.71719, 40.17886], [71.69621, 40.18492], [71.70569, 40.20391], [71.68386, 40.26984], [71.61931, 40.26775], [71.61725, 40.20615], [71.51549, 40.22986], [71.51215, 40.26943], [71.4246, 40.28619], [71.36663, 40.31593], [71.13042, 40.34106], [71.05901, 40.28765], [70.95789, 40.28761], [70.9818, 40.22392], [70.80495, 40.16813], [70.7928, 40.12797], [70.65827, 40.0981], [70.65946, 39.9878], [70.58912, 39.95211], [70.55033, 39.96619], [70.47557, 39.93216], [70.57384, 39.99394], [70.58297, 40.00891], [70.01283, 40.23288], [69.67001, 40.10639], [69.64704, 40.12165], [69.57615, 40.10524], [69.55555, 40.12296], [69.53794, 40.11833], [69.53855, 40.0887], [69.5057, 40.03277], [69.53615, 39.93991], [69.43557, 39.92877], [69.43134, 39.98431], [69.35649, 40.01994], [69.26938, 39.8127], [69.3594, 39.52516], [69.68677, 39.59281], [69.87491, 39.53882], [70.11111, 39.58223], [70.2869, 39.53141], [70.44757, 39.60128], [70.64087, 39.58792], [70.7854, 39.38933], [71.06418, 39.41586], [71.08752, 39.50704], [71.49814, 39.61397], [71.55856, 39.57588], [71.5517, 39.45722], [71.62688, 39.44056], [71.76816, 39.45456], [71.80164, 39.40631], [71.7522, 39.32031], [71.79202, 39.27355], [71.90601, 39.27674], [72.04059, 39.36704], [72.09689, 39.26823], [72.17242, 39.2661], [72.23834, 39.17248], [72.33173, 39.33093], [72.62027, 39.39696], [72.85934, 39.35116], [73.18454, 39.35536], [73.31912, 39.38615], [73.45096, 39.46677], [73.59831, 39.46425], [73.87018, 39.47879], [73.94683, 39.60733], [73.92354, 39.69565], [73.9051, 39.75073], [73.83006, 39.76136], [73.97049, 40.04378], [74.25533, 40.13191], [74.35063, 40.09742], [74.69875, 40.34668], [74.85996, 40.32857], [74.78168, 40.44886], [74.82013, 40.52197], [75.08243, 40.43945], [75.22834, 40.45382], [75.5854, 40.66874], [75.69663, 40.28642], [75.91361, 40.2948], [75.96168, 40.38064], [76.33659, 40.3482], [76.5261, 40.46114], [76.75681, 40.95354], [76.99302, 41.0696], [77.28004, 41.0033], [77.3693, 41.0375], [77.52723, 41.00227], [77.76206, 41.01574], [77.81287, 41.14307], [78.12873, 41.23091], [78.15757, 41.38565], [78.3732, 41.39603], [79.92977, 42.04113], [80.17842, 42.03211], [80.17807, 42.21166], [79.97364, 42.42816], [79.52921, 42.44778], [79.19763, 42.804], [78.91502, 42.76839], [78.48469, 42.89649], [75.82823, 42.94848], [75.72174, 42.79672], [75.29966, 42.86183], [75.22619, 42.85528], [74.88756, 42.98612]], [[70.74189, 39.86319], [70.63105, 39.77923], [70.59667, 39.83542], [70.54998, 39.85137], [70.52631, 39.86989], [70.53651, 39.89155], [70.74189, 39.86319]], [[71.86463, 39.98598], [71.84316, 39.95582], [71.7504, 39.93701], [71.71511, 39.96348], [71.78838, 40.01404], [71.86463, 39.98598]], [[71.21139, 40.03369], [71.1427, 39.95026], [71.23067, 39.93581], [71.16101, 39.88423], [71.10531, 39.91354], [71.04979, 39.89808], [71.10501, 39.95568], [71.09063, 39.99], [71.11668, 39.99291], [71.11037, 40.01984], [71.01035, 40.05481], [71.00236, 40.18154], [71.06305, 40.1771], [71.12218, 40.03052], [71.21139, 40.03369]]]]
24833           }
24834         }, {
24835           type: "Feature",
24836           properties: {
24837             iso1A2: "KH",
24838             iso1A3: "KHM",
24839             iso1N3: "116",
24840             wikidata: "Q424",
24841             nameEn: "Cambodia",
24842             groups: ["035", "142", "UN"],
24843             callingCodes: ["855"]
24844           },
24845           geometry: {
24846             type: "MultiPolygon",
24847             coordinates: [[[[105.87328, 11.55953], [105.81645, 11.56876], [105.80867, 11.60536], [105.8507, 11.66635], [105.88962, 11.67854], [105.95188, 11.63738], [106.00792, 11.7197], [106.02038, 11.77457], [106.06708, 11.77761], [106.13158, 11.73283], [106.18539, 11.75171], [106.26478, 11.72122], [106.30525, 11.67549], [106.37219, 11.69836], [106.44691, 11.66787], [106.45158, 11.68616], [106.41577, 11.76999], [106.44535, 11.8279], [106.44068, 11.86294], [106.4687, 11.86751], [106.4111, 11.97413], [106.70687, 11.96956], [106.79405, 12.0807], [106.92325, 12.06548], [106.99953, 12.08983], [107.15831, 12.27547], [107.34511, 12.33327], [107.42917, 12.24657], [107.4463, 12.29373], [107.55059, 12.36824], [107.5755, 12.52177], [107.55993, 12.7982], [107.49611, 12.88926], [107.49144, 13.01215], [107.62843, 13.3668], [107.61909, 13.52577], [107.53503, 13.73908], [107.45252, 13.78897], [107.46498, 13.91593], [107.44318, 13.99751], [107.38247, 13.99147], [107.35757, 14.02319], [107.37158, 14.07906], [107.33577, 14.11832], [107.40427, 14.24509], [107.39493, 14.32655], [107.44941, 14.41552], [107.48521, 14.40346], [107.52569, 14.54665], [107.52102, 14.59034], [107.55371, 14.628], [107.54361, 14.69092], [107.47238, 14.61523], [107.44435, 14.52785], [107.37897, 14.54443], [107.3276, 14.58812], [107.29803, 14.58963], [107.26534, 14.54292], [107.256, 14.48716], [107.21241, 14.48716], [107.17038, 14.41782], [107.09722, 14.3937], [107.03962, 14.45099], [107.04585, 14.41782], [106.98825, 14.36806], [106.9649, 14.3198], [106.90574, 14.33639], [106.8497, 14.29416], [106.80767, 14.31226], [106.73762, 14.42687], [106.63333, 14.44194], [106.59908, 14.50977], [106.57106, 14.50525], [106.54148, 14.59565], [106.50723, 14.58963], [106.45898, 14.55045], [106.47766, 14.50977], [106.43874, 14.52032], [106.40916, 14.45249], [106.32355, 14.44043], [106.25194, 14.48415], [106.21302, 14.36203], [106.00131, 14.36957], [105.99509, 14.32734], [106.02311, 14.30623], [106.04801, 14.20363], [106.10872, 14.18401], [106.11962, 14.11307], [106.18656, 14.06324], [106.16632, 14.01794], [106.10094, 13.98471], [106.10405, 13.9137], [105.90791, 13.92881], [105.78182, 14.02247], [105.78338, 14.08438], [105.5561, 14.15684], [105.44869, 14.10703], [105.36775, 14.09948], [105.2759, 14.17496], [105.20894, 14.34967], [105.17748, 14.34432], [105.14012, 14.23873], [105.08408, 14.20402], [105.02804, 14.23722], [104.97667, 14.38806], [104.69335, 14.42726], [104.55014, 14.36091], [104.27616, 14.39861], [103.93836, 14.3398], [103.70175, 14.38052], [103.71109, 14.4348], [103.53518, 14.42575], [103.39353, 14.35639], [103.16469, 14.33075], [102.93275, 14.19044], [102.91251, 14.01531], [102.77864, 13.93374], [102.72727, 13.77806], [102.56848, 13.69366], [102.5481, 13.6589], [102.58635, 13.6286], [102.62483, 13.60883], [102.57573, 13.60461], [102.5358, 13.56933], [102.44601, 13.5637], [102.36859, 13.57488], [102.33828, 13.55613], [102.361, 13.50551], [102.35563, 13.47307], [102.35692, 13.38274], [102.34611, 13.35618], [102.36001, 13.31142], [102.36146, 13.26006], [102.43422, 13.09061], [102.46011, 13.08057], [102.52275, 12.99813], [102.48694, 12.97537], [102.49335, 12.92711], [102.53053, 12.77506], [102.4994, 12.71736], [102.51963, 12.66117], [102.57567, 12.65358], [102.7796, 12.43781], [102.78116, 12.40284], [102.73134, 12.37091], [102.70176, 12.1686], [102.77026, 12.06815], [102.78427, 11.98746], [102.83957, 11.8519], [102.90973, 11.75613], [102.91449, 11.65512], [102.52395, 11.25257], [102.47649, 9.66162], [103.99198, 10.48391], [104.43778, 10.42386], [104.47963, 10.43046], [104.49869, 10.4057], [104.59018, 10.53073], [104.87933, 10.52833], [104.95094, 10.64003], [105.09571, 10.72722], [105.02722, 10.89236], [105.08326, 10.95656], [105.11449, 10.96332], [105.34011, 10.86179], [105.42884, 10.96878], [105.50045, 10.94586], [105.77751, 11.03671], [105.86376, 10.89839], [105.84603, 10.85873], [105.93403, 10.83853], [105.94535, 10.9168], [106.06708, 10.8098], [106.18539, 10.79451], [106.14301, 10.98176], [106.20095, 10.97795], [106.1757, 11.07301], [106.1527, 11.10476], [106.10444, 11.07879], [105.86782, 11.28343], [105.88962, 11.43605], [105.87328, 11.55953]]]]
24848           }
24849         }, {
24850           type: "Feature",
24851           properties: {
24852             iso1A2: "KI",
24853             iso1A3: "KIR",
24854             iso1N3: "296",
24855             wikidata: "Q710",
24856             nameEn: "Kiribati",
24857             groups: ["057", "009", "UN"],
24858             driveSide: "left",
24859             callingCodes: ["686"]
24860           },
24861           geometry: {
24862             type: "MultiPolygon",
24863             coordinates: [[[[169, 3.9], [169, -3.5], [178, -3.5], [178, 3.9], [169, 3.9]]], [[[-161.06795, 5.2462], [-158.12991, -1.86122], [-175.33482, -1.40631], [-175.31804, -7.54825], [-156.50903, -7.4975], [-156.48634, -15.52824], [-135.59706, -4.70473], [-161.06795, 5.2462]]]]
24864           }
24865         }, {
24866           type: "Feature",
24867           properties: {
24868             iso1A2: "KM",
24869             iso1A3: "COM",
24870             iso1N3: "174",
24871             wikidata: "Q970",
24872             nameEn: "Comoros",
24873             groups: ["014", "202", "002", "UN"],
24874             callingCodes: ["269"]
24875           },
24876           geometry: {
24877             type: "MultiPolygon",
24878             coordinates: [[[[42.63904, -10.02522], [43.28731, -13.97126], [45.4971, -11.75965], [42.63904, -10.02522]]]]
24879           }
24880         }, {
24881           type: "Feature",
24882           properties: {
24883             iso1A2: "KN",
24884             iso1A3: "KNA",
24885             iso1N3: "659",
24886             wikidata: "Q763",
24887             nameEn: "St. Kitts and Nevis",
24888             groups: ["029", "003", "419", "019", "UN"],
24889             driveSide: "left",
24890             roadSpeedUnit: "mph",
24891             callingCodes: ["1 869"]
24892           },
24893           geometry: {
24894             type: "MultiPolygon",
24895             coordinates: [[[[-62.29333, 17.43155], [-62.76692, 17.64353], [-63.09677, 17.21372], [-62.63813, 16.65446], [-62.29333, 17.43155]]]]
24896           }
24897         }, {
24898           type: "Feature",
24899           properties: {
24900             iso1A2: "KP",
24901             iso1A3: "PRK",
24902             iso1N3: "408",
24903             wikidata: "Q423",
24904             nameEn: "North Korea",
24905             groups: ["030", "142", "UN"],
24906             callingCodes: ["850"]
24907           },
24908           geometry: {
24909             type: "MultiPolygon",
24910             coordinates: [[[[130.26095, 42.9027], [130.09764, 42.91425], [130.12957, 42.98361], [129.96409, 42.97306], [129.95082, 43.01051], [129.8865, 43.00395], [129.85261, 42.96494], [129.83277, 42.86746], [129.80719, 42.79218], [129.7835, 42.76521], [129.77183, 42.69435], [129.75294, 42.59409], [129.72541, 42.43739], [129.60482, 42.44461], [129.54701, 42.37254], [129.42882, 42.44702], [129.28541, 42.41574], [129.22423, 42.3553], [129.22285, 42.26491], [129.15178, 42.17224], [128.96068, 42.06657], [128.94007, 42.03537], [128.04487, 42.01769], [128.15119, 41.74568], [128.30716, 41.60322], [128.20061, 41.40895], [128.18546, 41.41279], [128.12967, 41.37931], [128.03311, 41.39232], [128.02633, 41.42103], [127.92943, 41.44291], [127.29712, 41.49473], [127.17841, 41.59714], [126.90729, 41.79955], [126.60631, 41.65565], [126.53189, 41.35206], [126.242, 41.15454], [126.00335, 40.92835], [125.76869, 40.87908], [125.71172, 40.85223], [124.86913, 40.45387], [124.40719, 40.13655], [124.38556, 40.11047], [124.3322, 40.05573], [124.37089, 40.03004], [124.35029, 39.95639], [124.23201, 39.9248], [124.17532, 39.8232], [123.90497, 38.79949], [123.85601, 37.49093], [124.67666, 38.05679], [124.84224, 37.977], [124.87921, 37.80827], [125.06408, 37.66334], [125.37112, 37.62643], [125.81159, 37.72949], [126.13074, 37.70512], [126.18776, 37.74728], [126.19097, 37.81462], [126.24402, 37.83113], [126.43239, 37.84095], [126.46818, 37.80873], [126.56709, 37.76857], [126.59918, 37.76364], [126.66067, 37.7897], [126.68793, 37.83728], [126.68793, 37.9175], [126.67023, 37.95852], [126.84961, 38.0344], [126.88106, 38.10246], [126.95887, 38.1347], [126.95338, 38.17735], [127.04479, 38.25518], [127.15749, 38.30722], [127.38727, 38.33227], [127.49672, 38.30647], [127.55013, 38.32257], [128.02917, 38.31861], [128.27652, 38.41657], [128.31105, 38.58462], [128.37487, 38.62345], [128.65655, 38.61914], [131.95041, 41.5445], [130.65022, 42.32281], [130.66367, 42.38024], [130.64181, 42.41422], [130.60805, 42.4317], [130.56835, 42.43281], [130.55143, 42.52158], [130.50123, 42.61636], [130.44361, 42.54849], [130.41826, 42.6011], [130.2385, 42.71127], [130.23068, 42.80125], [130.26095, 42.9027]]]]
24911           }
24912         }, {
24913           type: "Feature",
24914           properties: {
24915             iso1A2: "KR",
24916             iso1A3: "KOR",
24917             iso1N3: "410",
24918             wikidata: "Q884",
24919             nameEn: "South Korea",
24920             groups: ["030", "142", "UN"],
24921             callingCodes: ["82"]
24922           },
24923           geometry: {
24924             type: "MultiPolygon",
24925             coordinates: [[[[133.11729, 37.53115], [128.65655, 38.61914], [128.37487, 38.62345], [128.31105, 38.58462], [128.27652, 38.41657], [128.02917, 38.31861], [127.55013, 38.32257], [127.49672, 38.30647], [127.38727, 38.33227], [127.15749, 38.30722], [127.04479, 38.25518], [126.95338, 38.17735], [126.95887, 38.1347], [126.88106, 38.10246], [126.84961, 38.0344], [126.67023, 37.95852], [126.68793, 37.9175], [126.68793, 37.83728], [126.66067, 37.7897], [126.59918, 37.76364], [126.56709, 37.76857], [126.46818, 37.80873], [126.43239, 37.84095], [126.24402, 37.83113], [126.19097, 37.81462], [126.18776, 37.74728], [126.13074, 37.70512], [125.81159, 37.72949], [125.37112, 37.62643], [125.06408, 37.66334], [124.87921, 37.80827], [124.84224, 37.977], [124.67666, 38.05679], [123.85601, 37.49093], [122.80525, 33.30571], [125.99728, 32.63328], [129.2669, 34.87122], [133.11729, 37.53115]]]]
24926           }
24927         }, {
24928           type: "Feature",
24929           properties: {
24930             iso1A2: "KW",
24931             iso1A3: "KWT",
24932             iso1N3: "414",
24933             wikidata: "Q817",
24934             nameEn: "Kuwait",
24935             groups: ["145", "142", "UN"],
24936             callingCodes: ["965"]
24937           },
24938           geometry: {
24939             type: "MultiPolygon",
24940             coordinates: [[[[49.00421, 28.81495], [48.59531, 29.66815], [48.40479, 29.85763], [48.17332, 30.02448], [48.06782, 30.02906], [48.01114, 29.98906], [47.7095, 30.10453], [47.37192, 30.10421], [47.15166, 30.01044], [46.89695, 29.50584], [46.5527, 29.10283], [47.46202, 29.0014], [47.58376, 28.83382], [47.59863, 28.66798], [47.70561, 28.5221], [48.42991, 28.53628], [49.00421, 28.81495]]]]
24941           }
24942         }, {
24943           type: "Feature",
24944           properties: {
24945             iso1A2: "KY",
24946             iso1A3: "CYM",
24947             iso1N3: "136",
24948             wikidata: "Q5785",
24949             nameEn: "Cayman Islands",
24950             country: "GB",
24951             groups: ["BOTS", "029", "003", "419", "019", "UN"],
24952             driveSide: "left",
24953             roadSpeedUnit: "mph",
24954             roadHeightUnit: "ft",
24955             callingCodes: ["1 345"]
24956           },
24957           geometry: {
24958             type: "MultiPolygon",
24959             coordinates: [[[[-82.11509, 19.60401], [-80.36068, 18.11751], [-79.32727, 20.06742], [-82.11509, 19.60401]]]]
24960           }
24961         }, {
24962           type: "Feature",
24963           properties: {
24964             iso1A2: "KZ",
24965             iso1A3: "KAZ",
24966             iso1N3: "398",
24967             wikidata: "Q232",
24968             nameEn: "Kazakhstan",
24969             groups: ["143", "142", "UN"],
24970             callingCodes: ["7"]
24971           },
24972           geometry: {
24973             type: "MultiPolygon",
24974             coordinates: [[[[68.90865, 55.38148], [68.19206, 55.18823], [68.26661, 55.09226], [68.21308, 54.98645], [65.20174, 54.55216], [65.24663, 54.35721], [65.11033, 54.33028], [64.97216, 54.4212], [63.97686, 54.29763], [64.02715, 54.22679], [63.91224, 54.20013], [63.80604, 54.27079], [62.58651, 54.05871], [62.56876, 53.94047], [62.45931, 53.90737], [62.38535, 54.03961], [62.00966, 54.04134], [62.03913, 53.94768], [61.65318, 54.02445], [61.56941, 53.95703], [61.47603, 54.08048], [61.3706, 54.08464], [61.26863, 53.92797], [60.99796, 53.93699], [61.14283, 53.90063], [61.22574, 53.80268], [60.90626, 53.62937], [61.55706, 53.57144], [61.57185, 53.50112], [61.37957, 53.45887], [61.29082, 53.50992], [61.14291, 53.41481], [61.19024, 53.30536], [62.14574, 53.09626], [62.12799, 52.99133], [62.0422, 52.96105], [61.23462, 53.03227], [61.05842, 52.92217], [60.71989, 52.75923], [60.71693, 52.66245], [60.84118, 52.63912], [60.84709, 52.52228], [60.98021, 52.50068], [61.05417, 52.35096], [60.78201, 52.22067], [60.72581, 52.15538], [60.48915, 52.15175], [60.19925, 51.99173], [59.99809, 51.98263], [60.09867, 51.87135], [60.50986, 51.7964], [60.36787, 51.66815], [60.5424, 51.61675], [60.92401, 51.61124], [60.95655, 51.48615], [61.50677, 51.40687], [61.55114, 51.32746], [61.6813, 51.25716], [61.56889, 51.23679], [61.4431, 50.80679], [60.81833, 50.6629], [60.31914, 50.67705], [60.17262, 50.83312], [60.01288, 50.8163], [59.81172, 50.54451], [59.51886, 50.49937], [59.48928, 50.64216], [58.87974, 50.70852], [58.3208, 51.15151], [57.75578, 51.13852], [57.74986, 50.93017], [57.44221, 50.88354], [57.17302, 51.11253], [56.17906, 50.93204], [56.11398, 50.7471], [55.67774, 50.54508], [54.72067, 51.03261], [54.56685, 51.01958], [54.71476, 50.61214], [54.55797, 50.52006], [54.41894, 50.61214], [54.46331, 50.85554], [54.12248, 51.11542], [53.69299, 51.23466], [53.46165, 51.49445], [52.54329, 51.48444], [52.36119, 51.74161], [51.8246, 51.67916], [51.77431, 51.49536], [51.301, 51.48799], [51.26254, 51.68466], [50.59695, 51.61859], [50.26859, 51.28677], [49.97277, 51.2405], [49.76866, 51.11067], [49.39001, 51.09396], [49.41959, 50.85927], [49.12673, 50.78639], [48.86936, 50.61589], [48.57946, 50.63278], [48.90782, 50.02281], [48.68352, 49.89546], [48.42564, 49.82283], [48.24519, 49.86099], [48.10044, 50.09242], [47.58551, 50.47867], [47.30448, 50.30894], [47.34589, 50.09308], [47.18319, 49.93721], [46.9078, 49.86707], [46.78398, 49.34026], [47.04658, 49.19834], [47.00857, 49.04921], [46.78392, 48.95352], [46.49011, 48.43019], [47.11516, 48.27188], [47.12107, 47.83687], [47.38731, 47.68176], [47.41689, 47.83687], [47.64973, 47.76559], [48.15348, 47.74545], [48.45173, 47.40818], [48.52326, 47.4102], [49.01136, 46.72716], [48.51142, 46.69268], [48.54988, 46.56267], [49.16518, 46.38542], [49.32259, 46.26944], [49.88945, 46.04554], [49.2134, 44.84989], [52.26048, 41.69249], [52.47884, 41.78034], [52.97575, 42.1308], [54.20635, 42.38477], [54.95182, 41.92424], [55.45471, 41.25609], [56.00314, 41.32584], [55.97584, 44.99322], [55.97584, 44.99328], [55.97584, 44.99338], [55.97584, 44.99343], [55.97584, 44.99348], [55.97584, 44.99353], [55.97584, 44.99359], [55.97584, 44.99369], [55.97584, 44.99374], [55.97584, 44.99384], [55.97584, 44.9939], [55.97584, 44.994], [55.97584, 44.99405], [55.97584, 44.99415], [55.97584, 44.99421], [55.97584, 44.99426], [55.97584, 44.99431], [55.97584, 44.99436], [55.97584, 44.99441], [55.97594, 44.99446], [55.97605, 44.99452], [55.97605, 44.99457], [55.97605, 44.99462], [55.97605, 44.99467], [55.97605, 44.99477], [55.97615, 44.99477], [55.97615, 44.99483], [55.97615, 44.99493], [55.97615, 44.99498], [55.97615, 44.99503], [55.97615, 44.99508], [55.97625, 44.99514], [55.97636, 44.99519], [55.97636, 44.99524], [55.97646, 44.99529], [55.97646, 44.99534], [55.97656, 44.99539], [55.97667, 44.99545], [55.97677, 44.9955], [55.97677, 44.99555], [55.97677, 44.9956], [55.97687, 44.9956], [55.97698, 44.99565], [55.97698, 44.9957], [55.97708, 44.99576], [55.97718, 44.99581], [55.97729, 44.99586], [55.97739, 44.99586], [55.97739, 44.99591], [55.97749, 44.99591], [55.9776, 44.99591], [55.9777, 44.99596], [55.9777, 44.99601], [55.9778, 44.99607], [55.97791, 44.99607], [55.97801, 44.99607], [55.97801, 44.99612], [55.97811, 44.99617], [55.97822, 44.99617], [55.97832, 44.99622], [55.97842, 44.99622], [58.59711, 45.58671], [61.01475, 44.41383], [62.01711, 43.51008], [63.34656, 43.64003], [64.53885, 43.56941], [64.96464, 43.74748], [65.18666, 43.48835], [65.53277, 43.31856], [65.85194, 42.85481], [66.09482, 42.93426], [66.00546, 41.94455], [66.53302, 41.87388], [66.69129, 41.1311], [67.9644, 41.14611], [67.98511, 41.02794], [68.08273, 41.08148], [68.1271, 41.0324], [67.96736, 40.83798], [68.49983, 40.56437], [68.63, 40.59358], [68.58444, 40.91447], [68.49983, 40.99669], [68.62221, 41.03019], [68.65662, 40.93861], [68.73945, 40.96989], [68.7217, 41.05025], [69.01308, 41.22804], [69.05006, 41.36183], [69.15137, 41.43078], [69.17701, 41.43769], [69.18528, 41.45175], [69.20439, 41.45391], [69.22671, 41.46298], [69.23332, 41.45847], [69.25059, 41.46693], [69.29778, 41.43673], [69.35554, 41.47211], [69.37468, 41.46555], [69.45081, 41.46246], [69.39485, 41.51518], [69.45751, 41.56863], [69.49545, 41.545], [70.94483, 42.26238], [70.85973, 42.30188], [70.97717, 42.50147], [71.15232, 42.60486], [71.17807, 42.67381], [71.22785, 42.69248], [71.2724, 42.77853], [71.53272, 42.8014], [71.62405, 42.76613], [71.88792, 42.83578], [73.44393, 42.43098], [73.50992, 42.82356], [73.55634, 43.03071], [74.22489, 43.24657], [74.57491, 43.13702], [74.64615, 43.05881], [74.70331, 43.02519], [74.75, 42.99029], [74.88756, 42.98612], [75.22619, 42.85528], [75.29966, 42.86183], [75.72174, 42.79672], [75.82823, 42.94848], [78.48469, 42.89649], [78.91502, 42.76839], [79.19763, 42.804], [79.52921, 42.44778], [79.97364, 42.42816], [80.17807, 42.21166], [80.26841, 42.23797], [80.16892, 42.61137], [80.26886, 42.8366], [80.38169, 42.83142], [80.58999, 42.9011], [80.3735, 43.01557], [80.62913, 43.141], [80.78817, 43.14235], [80.77771, 43.30065], [80.69718, 43.32589], [80.75156, 43.44948], [80.40031, 44.10986], [80.40229, 44.23319], [80.38384, 44.63073], [79.8987, 44.89957], [80.11169, 45.03352], [81.73278, 45.3504], [82.51374, 45.1755], [82.58474, 45.40027], [82.21792, 45.56619], [83.04622, 47.19053], [83.92184, 46.98912], [84.73077, 47.01394], [84.93995, 46.87399], [85.22443, 47.04816], [85.54294, 47.06171], [85.69696, 47.2898], [85.61067, 47.49753], [85.5169, 48.05493], [85.73581, 48.3939], [86.38069, 48.46064], [86.75343, 48.70331], [86.73568, 48.99918], [86.87238, 49.12432], [87.28386, 49.11626], [87.31465, 49.23603], [87.03071, 49.25142], [86.82606, 49.51796], [86.61307, 49.60239], [86.79056, 49.74787], [86.63674, 49.80136], [86.18709, 49.50259], [85.24047, 49.60239], [84.99198, 50.06793], [84.29385, 50.27257], [83.8442, 50.87375], [83.14607, 51.00796], [82.55443, 50.75412], [81.94999, 50.79307], [81.46581, 50.77658], [81.41248, 50.97524], [81.06091, 50.94833], [81.16999, 51.15662], [80.80318, 51.28262], [80.44819, 51.20855], [80.4127, 50.95581], [80.08138, 50.77658], [79.11255, 52.01171], [77.90383, 53.29807], [76.54243, 53.99329], [76.44076, 54.16017], [76.82266, 54.1798], [76.91052, 54.4677], [75.3668, 54.07439], [75.43398, 53.98652], [75.07405, 53.80831], [73.39218, 53.44623], [73.25412, 53.61532], [73.68921, 53.86522], [73.74778, 54.07194], [73.37963, 53.96132], [72.71026, 54.1161], [72.43415, 53.92685], [72.17477, 54.36303], [71.96141, 54.17736], [71.10379, 54.13326], [71.08706, 54.33376], [71.24185, 54.64965], [71.08288, 54.71253], [70.96009, 55.10558], [70.76493, 55.3027], [70.19179, 55.1476], [69.74917, 55.35545], [69.34224, 55.36344], [68.90865, 55.38148]]]]
24975           }
24976         }, {
24977           type: "Feature",
24978           properties: {
24979             iso1A2: "LA",
24980             iso1A3: "LAO",
24981             iso1N3: "418",
24982             wikidata: "Q819",
24983             nameEn: "Laos",
24984             groups: ["035", "142", "UN"],
24985             callingCodes: ["856"]
24986           },
24987           geometry: {
24988             type: "MultiPolygon",
24989             coordinates: [[[[102.1245, 22.43372], [102.03633, 22.46164], [101.98487, 22.42766], [101.91344, 22.44417], [101.90714, 22.38688], [101.86828, 22.38397], [101.7685, 22.50337], [101.68973, 22.46843], [101.61306, 22.27515], [101.56789, 22.28876], [101.53638, 22.24794], [101.60675, 22.13513], [101.57525, 22.13026], [101.62566, 21.96574], [101.7791, 21.83019], [101.74555, 21.72852], [101.83257, 21.61562], [101.80001, 21.57461], [101.7475, 21.5873], [101.7727, 21.51794], [101.74224, 21.48276], [101.74014, 21.30967], [101.84412, 21.25291], [101.83887, 21.20983], [101.76745, 21.21571], [101.79266, 21.19025], [101.7622, 21.14813], [101.70548, 21.14911], [101.66977, 21.20004], [101.60886, 21.17947], [101.59491, 21.18621], [101.6068, 21.23329], [101.54563, 21.25668], [101.29326, 21.17254], [101.2229, 21.23271], [101.26912, 21.36482], [101.19349, 21.41959], [101.2124, 21.56422], [101.15156, 21.56129], [101.16198, 21.52808], [101.00234, 21.39612], [100.80173, 21.2934], [100.72716, 21.31786], [100.63578, 21.05639], [100.55281, 21.02796], [100.50974, 20.88574], [100.64628, 20.88279], [100.60112, 20.8347], [100.51079, 20.82194], [100.36375, 20.82783], [100.1957, 20.68247], [100.08404, 20.36626], [100.09999, 20.31614], [100.09337, 20.26293], [100.11785, 20.24787], [100.1712, 20.24324], [100.16668, 20.2986], [100.22076, 20.31598], [100.25769, 20.3992], [100.33383, 20.4028], [100.37439, 20.35156], [100.41473, 20.25625], [100.44992, 20.23644], [100.4537, 20.19971], [100.47567, 20.19133], [100.51052, 20.14928], [100.55218, 20.17741], [100.58808, 20.15791], [100.5094, 19.87904], [100.398, 19.75047], [100.49604, 19.53504], [100.58219, 19.49164], [100.64606, 19.55884], [100.77231, 19.48324], [100.90302, 19.61901], [101.08928, 19.59748], [101.26545, 19.59242], [101.26991, 19.48324], [101.21347, 19.46223], [101.20604, 19.35296], [101.24911, 19.33334], [101.261, 19.12717], [101.35606, 19.04716], [101.25803, 18.89545], [101.22832, 18.73377], [101.27585, 18.68875], [101.06047, 18.43247], [101.18227, 18.34367], [101.15108, 18.25624], [101.19118, 18.2125], [101.1793, 18.0544], [101.02185, 17.87637], [100.96541, 17.57926], [101.15108, 17.47586], [101.44667, 17.7392], [101.72294, 17.92867], [101.78087, 18.07559], [101.88485, 18.02474], [102.11359, 18.21532], [102.45523, 17.97106], [102.59234, 17.96127], [102.60971, 17.95411], [102.61432, 17.92273], [102.5896, 17.84889], [102.59485, 17.83537], [102.68194, 17.80151], [102.69946, 17.81686], [102.67543, 17.84529], [102.68538, 17.86653], [102.75954, 17.89561], [102.79044, 17.93612], [102.81988, 17.94233], [102.86323, 17.97531], [102.95812, 18.0054], [102.9912, 17.9949], [103.01998, 17.97095], [103.0566, 18.00144], [103.07823, 18.03833], [103.07343, 18.12351], [103.1493, 18.17799], [103.14994, 18.23172], [103.17093, 18.2618], [103.29757, 18.30475], [103.23818, 18.34875], [103.24779, 18.37807], [103.30977, 18.4341], [103.41044, 18.4486], [103.47773, 18.42841], [103.60957, 18.40528], [103.699, 18.34125], [103.82449, 18.33979], [103.85642, 18.28666], [103.93916, 18.33914], [103.97725, 18.33631], [104.06533, 18.21656], [104.10927, 18.10826], [104.21776, 17.99335], [104.2757, 17.86139], [104.35432, 17.82871], [104.45404, 17.66788], [104.69867, 17.53038], [104.80061, 17.39367], [104.80716, 17.19025], [104.73712, 17.01404], [104.7373, 16.91125], [104.76442, 16.84752], [104.7397, 16.81005], [104.76099, 16.69302], [104.73349, 16.565], [104.88057, 16.37311], [105.00262, 16.25627], [105.06204, 16.09792], [105.42001, 16.00657], [105.38508, 15.987], [105.34115, 15.92737], [105.37959, 15.84074], [105.42285, 15.76971], [105.46573, 15.74742], [105.61756, 15.68792], [105.60446, 15.53301], [105.58191, 15.41031], [105.47635, 15.3796], [105.4692, 15.33709], [105.50662, 15.32054], [105.58043, 15.32724], [105.46661, 15.13132], [105.61162, 15.00037], [105.5121, 14.80802], [105.53864, 14.55731], [105.43783, 14.43865], [105.20894, 14.34967], [105.2759, 14.17496], [105.36775, 14.09948], [105.44869, 14.10703], [105.5561, 14.15684], [105.78338, 14.08438], [105.78182, 14.02247], [105.90791, 13.92881], [106.10405, 13.9137], [106.10094, 13.98471], [106.16632, 14.01794], [106.18656, 14.06324], [106.11962, 14.11307], [106.10872, 14.18401], [106.04801, 14.20363], [106.02311, 14.30623], [105.99509, 14.32734], [106.00131, 14.36957], [106.21302, 14.36203], [106.25194, 14.48415], [106.32355, 14.44043], [106.40916, 14.45249], [106.43874, 14.52032], [106.47766, 14.50977], [106.45898, 14.55045], [106.50723, 14.58963], [106.54148, 14.59565], [106.57106, 14.50525], [106.59908, 14.50977], [106.63333, 14.44194], [106.73762, 14.42687], [106.80767, 14.31226], [106.8497, 14.29416], [106.90574, 14.33639], [106.9649, 14.3198], [106.98825, 14.36806], [107.04585, 14.41782], [107.03962, 14.45099], [107.09722, 14.3937], [107.17038, 14.41782], [107.21241, 14.48716], [107.256, 14.48716], [107.26534, 14.54292], [107.29803, 14.58963], [107.3276, 14.58812], [107.37897, 14.54443], [107.44435, 14.52785], [107.47238, 14.61523], [107.54361, 14.69092], [107.51579, 14.79282], [107.59285, 14.87795], [107.48277, 14.93751], [107.46516, 15.00982], [107.61486, 15.0566], [107.61926, 15.13949], [107.58844, 15.20111], [107.62587, 15.2266], [107.60605, 15.37524], [107.62367, 15.42193], [107.53341, 15.40496], [107.50699, 15.48771], [107.3815, 15.49832], [107.34408, 15.62345], [107.27583, 15.62769], [107.27143, 15.71459], [107.21859, 15.74638], [107.21419, 15.83747], [107.34188, 15.89464], [107.39471, 15.88829], [107.46296, 16.01106], [107.44975, 16.08511], [107.33968, 16.05549], [107.25822, 16.13587], [107.14595, 16.17816], [107.15035, 16.26271], [107.09091, 16.3092], [107.02597, 16.31132], [106.97385, 16.30204], [106.96638, 16.34938], [106.88067, 16.43594], [106.88727, 16.52671], [106.84104, 16.55415], [106.74418, 16.41904], [106.65832, 16.47816], [106.66052, 16.56892], [106.61477, 16.60713], [106.58267, 16.6012], [106.59013, 16.62259], [106.55485, 16.68704], [106.55265, 16.86831], [106.52183, 16.87884], [106.51963, 16.92097], [106.54824, 16.92729], [106.55045, 17.0031], [106.50862, 16.9673], [106.43597, 17.01362], [106.31929, 17.20509], [106.29287, 17.3018], [106.24444, 17.24714], [106.18991, 17.28227], [106.09019, 17.36399], [105.85744, 17.63221], [105.76612, 17.67147], [105.60381, 17.89356], [105.64784, 17.96687], [105.46292, 18.22008], [105.38366, 18.15315], [105.15942, 18.38691], [105.10408, 18.43533], [105.1327, 18.58355], [105.19654, 18.64196], [105.12829, 18.70453], [104.64617, 18.85668], [104.5361, 18.97747], [103.87125, 19.31854], [104.06058, 19.43484], [104.10832, 19.51575], [104.05617, 19.61743], [104.06498, 19.66926], [104.23229, 19.70242], [104.41281, 19.70035], [104.53169, 19.61743], [104.64837, 19.62365], [104.68359, 19.72729], [104.8355, 19.80395], [104.8465, 19.91783], [104.9874, 20.09573], [104.91695, 20.15567], [104.86852, 20.14121], [104.61315, 20.24452], [104.62195, 20.36633], [104.72102, 20.40554], [104.66158, 20.47774], [104.47886, 20.37459], [104.40621, 20.3849], [104.38199, 20.47155], [104.63957, 20.6653], [104.27412, 20.91433], [104.11121, 20.96779], [103.98024, 20.91531], [103.82282, 20.8732], [103.73478, 20.6669], [103.68633, 20.66324], [103.45737, 20.82382], [103.38032, 20.79501], [103.21497, 20.89832], [103.12055, 20.89994], [103.03469, 21.05821], [102.97745, 21.05821], [102.89825, 21.24707], [102.80794, 21.25736], [102.88939, 21.3107], [102.94223, 21.46034], [102.86297, 21.4255], [102.98846, 21.58936], [102.97965, 21.74076], [102.86077, 21.71213], [102.85637, 21.84501], [102.81894, 21.83888], [102.82115, 21.73667], [102.74189, 21.66713], [102.67145, 21.65894], [102.62301, 21.91447], [102.49092, 21.99002], [102.51734, 22.02676], [102.18712, 22.30403], [102.14099, 22.40092], [102.1245, 22.43372]]]]
24990           }
24991         }, {
24992           type: "Feature",
24993           properties: {
24994             iso1A2: "LB",
24995             iso1A3: "LBN",
24996             iso1N3: "422",
24997             wikidata: "Q822",
24998             nameEn: "Lebanon",
24999             aliases: ["RL"],
25000             groups: ["145", "142", "UN"],
25001             callingCodes: ["961"]
25002           },
25003           geometry: {
25004             type: "MultiPolygon",
25005             coordinates: [[[[35.94816, 33.47886], [35.94465, 33.52774], [36.05723, 33.57904], [35.9341, 33.6596], [36.06778, 33.82927], [36.14517, 33.85118], [36.3967, 33.83365], [36.38263, 33.86579], [36.28589, 33.91981], [36.41078, 34.05253], [36.50576, 34.05982], [36.5128, 34.09916], [36.62537, 34.20251], [36.59195, 34.2316], [36.58667, 34.27667], [36.60778, 34.31009], [36.56556, 34.31881], [36.53039, 34.3798], [36.55853, 34.41609], [36.46179, 34.46541], [36.4442, 34.50165], [36.34745, 34.5002], [36.3369, 34.52629], [36.39846, 34.55672], [36.41429, 34.61175], [36.45299, 34.59438], [36.46003, 34.6378], [36.42941, 34.62505], [36.35384, 34.65447], [36.35135, 34.68516], [36.32399, 34.69334], [36.29165, 34.62991], [35.98718, 34.64977], [35.97386, 34.63322], [35.48515, 34.70851], [34.78515, 33.20368], [35.10645, 33.09318], [35.1924, 33.08743], [35.31429, 33.10515], [35.35223, 33.05617], [35.43059, 33.06659], [35.448, 33.09264], [35.50272, 33.09056], [35.50335, 33.114], [35.52573, 33.11921], [35.54228, 33.19865], [35.5362, 33.23196], [35.54808, 33.236], [35.54544, 33.25513], [35.55555, 33.25844], [35.56523, 33.28969], [35.58326, 33.28381], [35.58502, 33.26653], [35.62283, 33.24226], [35.62019, 33.27278], [35.77477, 33.33609], [35.81324, 33.36354], [35.82577, 33.40479], [35.88668, 33.43183], [35.94816, 33.47886]]]]
25006           }
25007         }, {
25008           type: "Feature",
25009           properties: {
25010             iso1A2: "LC",
25011             iso1A3: "LCA",
25012             iso1N3: "662",
25013             wikidata: "Q760",
25014             nameEn: "St. Lucia",
25015             aliases: ["WL"],
25016             groups: ["029", "003", "419", "019", "UN"],
25017             driveSide: "left",
25018             roadSpeedUnit: "mph",
25019             callingCodes: ["1 758"]
25020           },
25021           geometry: {
25022             type: "MultiPolygon",
25023             coordinates: [[[[-59.95997, 14.20285], [-61.69315, 14.26451], [-59.94058, 12.34011], [-59.95997, 14.20285]]]]
25024           }
25025         }, {
25026           type: "Feature",
25027           properties: {
25028             iso1A2: "LI",
25029             iso1A3: "LIE",
25030             iso1N3: "438",
25031             wikidata: "Q347",
25032             nameEn: "Liechtenstein",
25033             aliases: ["FL"],
25034             groups: ["155", "150", "UN"],
25035             callingCodes: ["423"]
25036           },
25037           geometry: {
25038             type: "MultiPolygon",
25039             coordinates: [[[[9.60717, 47.06091], [9.61216, 47.07732], [9.63395, 47.08443], [9.62623, 47.14685], [9.56539, 47.17124], [9.58264, 47.20673], [9.56981, 47.21926], [9.55176, 47.22585], [9.56766, 47.24281], [9.53116, 47.27029], [9.52406, 47.24959], [9.50318, 47.22153], [9.4891, 47.19346], [9.48774, 47.17402], [9.51044, 47.13727], [9.52089, 47.10019], [9.51362, 47.08505], [9.47139, 47.06402], [9.47548, 47.05257], [9.54041, 47.06495], [9.55721, 47.04762], [9.60717, 47.06091]]]]
25040           }
25041         }, {
25042           type: "Feature",
25043           properties: {
25044             iso1A2: "LK",
25045             iso1A3: "LKA",
25046             iso1N3: "144",
25047             wikidata: "Q854",
25048             nameEn: "Sri Lanka",
25049             groups: ["034", "142", "UN"],
25050             driveSide: "left",
25051             callingCodes: ["94"]
25052           },
25053           geometry: {
25054             type: "MultiPolygon",
25055             coordinates: [[[[76.59015, 5.591], [85.15017, 5.21497], [80.48418, 10.20786], [79.42124, 9.80115], [79.50447, 8.91876], [76.59015, 5.591]]]]
25056           }
25057         }, {
25058           type: "Feature",
25059           properties: {
25060             iso1A2: "LR",
25061             iso1A3: "LBR",
25062             iso1N3: "430",
25063             wikidata: "Q1014",
25064             nameEn: "Liberia",
25065             groups: ["011", "202", "002", "UN"],
25066             callingCodes: ["231"]
25067           },
25068           geometry: {
25069             type: "MultiPolygon",
25070             coordinates: [[[[-8.47114, 7.55676], [-8.55874, 7.62525], [-8.55874, 7.70167], [-8.67814, 7.69428], [-8.72789, 7.51429], [-8.8448, 7.35149], [-8.85724, 7.26019], [-8.93435, 7.2824], [-9.09107, 7.1985], [-9.18311, 7.30461], [-9.20798, 7.38109], [-9.305, 7.42056], [-9.41943, 7.41809], [-9.48161, 7.37122], [-9.37465, 7.62032], [-9.35724, 7.74111], [-9.44928, 7.9284], [-9.41445, 8.02448], [-9.50898, 8.18455], [-9.47415, 8.35195], [-9.77763, 8.54633], [-10.05873, 8.42578], [-10.05375, 8.50697], [-10.14579, 8.52665], [-10.203, 8.47991], [-10.27575, 8.48711], [-10.30084, 8.30008], [-10.31635, 8.28554], [-10.29839, 8.21283], [-10.35227, 8.15223], [-10.45023, 8.15627], [-10.51554, 8.1393], [-10.57523, 8.04829], [-10.60492, 8.04072], [-10.60422, 7.7739], [-11.29417, 7.21576], [-11.4027, 6.97746], [-11.50429, 6.92704], [-12.15048, 6.15992], [-7.52774, 3.7105], [-7.53259, 4.35145], [-7.59349, 4.8909], [-7.53876, 4.94294], [-7.55369, 5.08667], [-7.48901, 5.14118], [-7.46165, 5.26256], [-7.36463, 5.32944], [-7.43428, 5.42355], [-7.37209, 5.61173], [-7.43926, 5.74787], [-7.43677, 5.84687], [-7.46165, 5.84934], [-7.48155, 5.80974], [-7.67309, 5.94337], [-7.70294, 5.90625], [-7.78254, 5.99037], [-7.79747, 6.07696], [-7.8497, 6.08932], [-7.83478, 6.20309], [-7.90692, 6.27728], [-8.00642, 6.31684], [-8.17557, 6.28222], [-8.3298, 6.36381], [-8.38453, 6.35887], [-8.45666, 6.49977], [-8.48652, 6.43797], [-8.59456, 6.50612], [-8.31736, 6.82837], [-8.29249, 7.1691], [-8.37458, 7.25794], [-8.41935, 7.51203], [-8.47114, 7.55676]]]]
25071           }
25072         }, {
25073           type: "Feature",
25074           properties: {
25075             iso1A2: "LS",
25076             iso1A3: "LSO",
25077             iso1N3: "426",
25078             wikidata: "Q1013",
25079             nameEn: "Lesotho",
25080             groups: ["018", "202", "002", "UN"],
25081             driveSide: "left",
25082             callingCodes: ["266"]
25083           },
25084           geometry: {
25085             type: "MultiPolygon",
25086             coordinates: [[[[29.33204, -29.45598], [29.44883, -29.3772], [29.40524, -29.21246], [28.68043, -28.58744], [28.65091, -28.57025], [28.40612, -28.6215], [28.30518, -28.69531], [28.2348, -28.69471], [28.1317, -28.7293], [28.02503, -28.85991], [27.98675, -28.8787], [27.9392, -28.84864], [27.88933, -28.88156], [27.8907, -28.91612], [27.75458, -28.89839], [27.55974, -29.18954], [27.5158, -29.2261], [27.54258, -29.25575], [27.48679, -29.29349], [27.45125, -29.29708], [27.47254, -29.31968], [27.4358, -29.33465], [27.33464, -29.48161], [27.01016, -29.65439], [27.09489, -29.72796], [27.22719, -30.00718], [27.29603, -30.05473], [27.32555, -30.14785], [27.40778, -30.14577], [27.37293, -30.19401], [27.36649, -30.27246], [27.38108, -30.33456], [27.45452, -30.32239], [27.56901, -30.42504], [27.56781, -30.44562], [27.62137, -30.50509], [27.6521, -30.51707], [27.67819, -30.53437], [27.69467, -30.55862], [27.74814, -30.60635], [28.12073, -30.68072], [28.2319, -30.28476], [28.399, -30.1592], [28.68627, -30.12885], [28.80222, -30.10579], [28.9338, -30.05072], [29.16548, -29.91706], [29.12553, -29.76266], [29.28545, -29.58456], [29.33204, -29.45598]]]]
25087           }
25088         }, {
25089           type: "Feature",
25090           properties: {
25091             iso1A2: "LT",
25092             iso1A3: "LTU",
25093             iso1N3: "440",
25094             wikidata: "Q37",
25095             nameEn: "Lithuania",
25096             groups: ["EU", "154", "150", "UN"],
25097             callingCodes: ["370"]
25098           },
25099           geometry: {
25100             type: "MultiPolygon",
25101             coordinates: [[[[24.89005, 56.46666], [24.83686, 56.41565], [24.70022, 56.40483], [24.57353, 56.31525], [24.58143, 56.29125], [24.42746, 56.26522], [24.32334, 56.30226], [24.13139, 56.24881], [24.02657, 56.3231], [23.75726, 56.37282], [23.49803, 56.34307], [23.40486, 56.37689], [23.31606, 56.3827], [23.17312, 56.36795], [23.09531, 56.30511], [22.96988, 56.41213], [22.83048, 56.367], [22.69354, 56.36284], [22.56441, 56.39305], [22.3361, 56.4016], [22.09728, 56.42851], [22.00548, 56.41508], [21.74558, 56.33181], [21.57888, 56.31406], [21.49736, 56.29106], [21.24644, 56.16917], [21.15016, 56.07818], [20.68447, 56.04073], [20.60454, 55.40986], [20.95181, 55.27994], [21.26425, 55.24456], [21.35465, 55.28427], [21.38446, 55.29348], [21.46766, 55.21115], [21.51095, 55.18507], [21.55605, 55.20311], [21.64954, 55.1791], [21.85521, 55.09493], [21.96505, 55.07353], [21.99543, 55.08691], [22.03984, 55.07888], [22.02582, 55.05078], [22.06087, 55.02935], [22.11697, 55.02131], [22.14267, 55.05345], [22.31562, 55.0655], [22.47688, 55.04408], [22.58907, 55.07085], [22.60075, 55.01863], [22.65451, 54.97037], [22.68723, 54.9811], [22.76422, 54.92521], [22.85083, 54.88711], [22.87317, 54.79492], [22.73631, 54.72952], [22.73397, 54.66604], [22.75467, 54.6483], [22.74225, 54.64339], [22.7522, 54.63525], [22.68021, 54.58486], [22.71293, 54.56454], [22.67788, 54.532], [22.70208, 54.45312], [22.7253, 54.41732], [22.79705, 54.36264], [22.83756, 54.40827], [23.00584, 54.38514], [22.99649, 54.35927], [23.05726, 54.34565], [23.04323, 54.31567], [23.104, 54.29794], [23.13905, 54.31567], [23.15526, 54.31076], [23.15938, 54.29894], [23.24656, 54.25701], [23.3494, 54.25155], [23.39525, 54.21672], [23.42418, 54.17911], [23.45223, 54.17775], [23.49196, 54.14764], [23.52702, 54.04622], [23.48261, 53.98855], [23.51284, 53.95052], [23.61677, 53.92691], [23.71726, 53.93379], [23.80543, 53.89558], [23.81309, 53.94205], [23.95098, 53.9613], [23.98837, 53.92554], [24.19638, 53.96405], [24.34128, 53.90076], [24.44411, 53.90076], [24.62275, 54.00217], [24.69652, 54.01901], [24.69185, 53.96543], [24.74279, 53.96663], [24.85311, 54.02862], [24.77131, 54.11091], [24.96894, 54.17589], [24.991, 54.14241], [25.0728, 54.13419], [25.19199, 54.219], [25.22705, 54.26271], [25.35559, 54.26544], [25.509, 54.30267], [25.56823, 54.25212], [25.51452, 54.17799], [25.54724, 54.14925], [25.64875, 54.1259], [25.71084, 54.16704], [25.78563, 54.15747], [25.78553, 54.23327], [25.68513, 54.31727], [25.55425, 54.31591], [25.5376, 54.33158], [25.63371, 54.42075], [25.62203, 54.4656], [25.64813, 54.48704], [25.68045, 54.5321], [25.75977, 54.57252], [25.74122, 54.80108], [25.89462, 54.93438], [25.99129, 54.95705], [26.05907, 54.94631], [26.13386, 54.98924], [26.20397, 54.99729], [26.26941, 55.08032], [26.23202, 55.10439], [26.30628, 55.12536], [26.35121, 55.1525], [26.46249, 55.12814], [26.51481, 55.16051], [26.54753, 55.14181], [26.69243, 55.16718], [26.68075, 55.19787], [26.72983, 55.21788], [26.73017, 55.24226], [26.835, 55.28182], [26.83266, 55.30444], [26.80929, 55.31642], [26.6714, 55.33902], [26.5709, 55.32572], [26.44937, 55.34832], [26.5522, 55.40277], [26.55094, 55.5093], [26.63167, 55.57887], [26.63231, 55.67968], [26.58248, 55.6754], [26.46661, 55.70375], [26.39561, 55.71156], [26.18509, 55.86813], [26.03815, 55.95884], [25.90047, 56.0013], [25.85893, 56.00188], [25.81773, 56.05444], [25.69246, 56.08892], [25.68588, 56.14725], [25.53621, 56.16663], [25.39751, 56.15707], [25.23099, 56.19147], [25.09325, 56.1878], [25.05762, 56.26742], [24.89005, 56.46666]]]]
25102           }
25103         }, {
25104           type: "Feature",
25105           properties: {
25106             iso1A2: "LU",
25107             iso1A3: "LUX",
25108             iso1N3: "442",
25109             wikidata: "Q32",
25110             nameEn: "Luxembourg",
25111             groups: ["EU", "155", "150", "UN"],
25112             callingCodes: ["352"]
25113           },
25114           geometry: {
25115             type: "MultiPolygon",
25116             coordinates: [[[[6.1379, 50.12964], [6.1137, 50.13668], [6.12028, 50.16374], [6.08577, 50.17246], [6.06406, 50.15344], [6.03093, 50.16362], [6.02488, 50.18283], [5.96453, 50.17259], [5.95929, 50.13295], [5.89488, 50.11476], [5.8857, 50.07824], [5.85474, 50.06342], [5.86904, 50.04614], [5.8551, 50.02683], [5.81866, 50.01286], [5.82331, 49.99662], [5.83968, 49.9892], [5.83467, 49.97823], [5.81163, 49.97142], [5.80833, 49.96451], [5.77291, 49.96056], [5.77314, 49.93646], [5.73621, 49.89796], [5.78415, 49.87922], [5.75269, 49.8711], [5.75861, 49.85631], [5.74567, 49.85368], [5.75884, 49.84811], [5.74953, 49.84709], [5.74975, 49.83933], [5.74076, 49.83823], [5.7404, 49.83452], [5.74844, 49.82435], [5.74364, 49.82058], [5.74953, 49.81428], [5.75409, 49.79239], [5.78871, 49.7962], [5.82245, 49.75048], [5.83149, 49.74729], [5.82562, 49.72395], [5.84193, 49.72161], [5.86503, 49.72739], [5.88677, 49.70951], [5.86527, 49.69291], [5.86175, 49.67862], [5.9069, 49.66377], [5.90164, 49.6511], [5.90599, 49.63853], [5.88552, 49.63507], [5.88393, 49.62802], [5.87609, 49.62047], [5.8762, 49.60898], [5.84826, 49.5969], [5.84971, 49.58674], [5.86986, 49.58756], [5.87256, 49.57539], [5.8424, 49.56082], [5.84692, 49.55663], [5.84143, 49.5533], [5.81838, 49.54777], [5.80871, 49.5425], [5.81664, 49.53775], [5.83648, 49.5425], [5.84466, 49.53027], [5.83467, 49.52717], [5.83389, 49.52152], [5.86571, 49.50015], [5.94128, 49.50034], [5.94224, 49.49608], [5.96876, 49.49053], [5.97693, 49.45513], [6.02648, 49.45451], [6.02743, 49.44845], [6.04176, 49.44801], [6.05553, 49.46663], [6.07887, 49.46399], [6.08373, 49.45594], [6.10072, 49.45268], [6.09845, 49.46351], [6.10325, 49.4707], [6.12346, 49.4735], [6.12814, 49.49365], [6.14321, 49.48796], [6.16115, 49.49297], [6.15366, 49.50226], [6.17386, 49.50934], [6.19543, 49.50536], [6.2409, 49.51408], [6.25029, 49.50609], [6.27875, 49.503], [6.28818, 49.48465], [6.3687, 49.4593], [6.36778, 49.46937], [6.36907, 49.48931], [6.36788, 49.50377], [6.35666, 49.52931], [6.38072, 49.55171], [6.38228, 49.55855], [6.35825, 49.57053], [6.36676, 49.57813], [6.38024, 49.57593], [6.38342, 49.5799], [6.37464, 49.58886], [6.385, 49.59946], [6.39822, 49.60081], [6.41861, 49.61723], [6.4413, 49.65722], [6.43768, 49.66021], [6.42726, 49.66078], [6.42937, 49.66857], [6.44654, 49.67799], [6.46048, 49.69092], [6.48014, 49.69767], [6.49785, 49.71118], [6.50647, 49.71353], [6.5042, 49.71808], [6.49694, 49.72205], [6.49535, 49.72645], [6.50261, 49.72718], [6.51397, 49.72058], [6.51805, 49.72425], [6.50193, 49.73291], [6.50174, 49.75292], [6.51646, 49.75961], [6.51828, 49.76855], [6.51056, 49.77515], [6.51669, 49.78336], [6.50534, 49.78952], [6.52169, 49.79787], [6.53122, 49.80666], [6.52121, 49.81338], [6.51215, 49.80124], [6.50647, 49.80916], [6.48718, 49.81267], [6.47111, 49.82263], [6.45425, 49.81164], [6.44131, 49.81443], [6.42905, 49.81091], [6.42521, 49.81591], [6.40022, 49.82029], [6.36576, 49.85032], [6.34267, 49.84974], [6.33585, 49.83785], [6.32098, 49.83728], [6.32303, 49.85133], [6.30963, 49.87021], [6.29692, 49.86685], [6.28874, 49.87592], [6.26146, 49.88203], [6.23496, 49.89972], [6.22926, 49.92096], [6.21882, 49.92403], [6.22608, 49.929], [6.22094, 49.94955], [6.19856, 49.95053], [6.19089, 49.96991], [6.18045, 49.96611], [6.18554, 49.95622], [6.17872, 49.9537], [6.16466, 49.97086], [6.1701, 49.98518], [6.14147, 49.99563], [6.14948, 50.00908], [6.13806, 50.01056], [6.1295, 50.01849], [6.13273, 50.02019], [6.13794, 50.01466], [6.14666, 50.02207], [6.13044, 50.02929], [6.13458, 50.04141], [6.11274, 50.05916], [6.12055, 50.09171], [6.1379, 50.12964]]]]
25117           }
25118         }, {
25119           type: "Feature",
25120           properties: {
25121             iso1A2: "LV",
25122             iso1A3: "LVA",
25123             iso1N3: "428",
25124             wikidata: "Q211",
25125             nameEn: "Latvia",
25126             groups: ["EU", "154", "150", "UN"],
25127             callingCodes: ["371"]
25128           },
25129           geometry: {
25130             type: "MultiPolygon",
25131             coordinates: [[[[27.34698, 57.52242], [26.90364, 57.62823], [26.54675, 57.51813], [26.46527, 57.56885], [26.29253, 57.59244], [26.1866, 57.6849], [26.2029, 57.7206], [26.08098, 57.76619], [26.0543, 57.76105], [26.03332, 57.7718], [26.02415, 57.76865], [26.02069, 57.77169], [26.0266, 57.77441], [26.027, 57.78158], [26.02456, 57.78342], [26.0324, 57.79037], [26.05949, 57.84744], [25.73499, 57.90193], [25.29581, 58.08288], [25.28237, 57.98539], [25.19484, 58.0831], [24.3579, 57.87471], [24.26221, 57.91787], [23.20055, 57.56697], [22.80496, 57.87798], [19.84909, 57.57876], [19.64795, 57.06466], [20.68447, 56.04073], [21.15016, 56.07818], [21.24644, 56.16917], [21.49736, 56.29106], [21.57888, 56.31406], [21.74558, 56.33181], [22.00548, 56.41508], [22.09728, 56.42851], [22.3361, 56.4016], [22.56441, 56.39305], [22.69354, 56.36284], [22.83048, 56.367], [22.96988, 56.41213], [23.09531, 56.30511], [23.17312, 56.36795], [23.31606, 56.3827], [23.40486, 56.37689], [23.49803, 56.34307], [23.75726, 56.37282], [24.02657, 56.3231], [24.13139, 56.24881], [24.32334, 56.30226], [24.42746, 56.26522], [24.58143, 56.29125], [24.57353, 56.31525], [24.70022, 56.40483], [24.83686, 56.41565], [24.89005, 56.46666], [25.05762, 56.26742], [25.09325, 56.1878], [25.23099, 56.19147], [25.39751, 56.15707], [25.53621, 56.16663], [25.68588, 56.14725], [25.69246, 56.08892], [25.81773, 56.05444], [25.85893, 56.00188], [25.90047, 56.0013], [26.03815, 55.95884], [26.18509, 55.86813], [26.39561, 55.71156], [26.46661, 55.70375], [26.58248, 55.6754], [26.63231, 55.67968], [26.64888, 55.70515], [26.71802, 55.70645], [26.76872, 55.67658], [26.87448, 55.7172], [26.97153, 55.8102], [27.1559, 55.85032], [27.27804, 55.78299], [27.3541, 55.8089], [27.61683, 55.78558], [27.63065, 55.89687], [27.97865, 56.11849], [28.15217, 56.16964], [28.23716, 56.27588], [28.16599, 56.37806], [28.19057, 56.44637], [28.10069, 56.524], [28.13526, 56.57989], [28.04768, 56.59004], [27.86101, 56.88204], [27.66511, 56.83921], [27.86101, 57.29402], [27.52453, 57.42826], [27.56832, 57.53728], [27.34698, 57.52242]]]]
25132           }
25133         }, {
25134           type: "Feature",
25135           properties: {
25136             iso1A2: "LY",
25137             iso1A3: "LBY",
25138             iso1N3: "434",
25139             wikidata: "Q1016",
25140             nameEn: "Libya",
25141             groups: ["015", "002", "UN"],
25142             callingCodes: ["218"]
25143           },
25144           geometry: {
25145             type: "MultiPolygon",
25146             coordinates: [[[[26.92891, 33.39516], [11.58941, 33.36891], [11.55852, 33.1409], [11.51549, 33.09826], [11.46037, 32.6307], [11.57828, 32.48013], [11.53898, 32.4138], [11.04234, 32.2145], [10.7315, 31.97235], [10.62788, 31.96629], [10.48497, 31.72956], [10.31364, 31.72648], [10.12239, 31.42098], [10.29516, 30.90337], [9.88152, 30.34074], [9.76848, 30.34366], [9.55544, 30.23971], [9.3876, 30.16738], [9.78136, 29.40961], [9.89569, 26.57696], [9.51696, 26.39148], [9.38834, 26.19288], [10.03146, 25.35635], [10.02432, 24.98124], [10.33159, 24.5465], [10.85323, 24.5595], [11.41061, 24.21456], [11.62498, 24.26669], [11.96886, 23.51735], [13.5631, 23.16574], [14.22918, 22.61719], [14.99751, 23.00539], [15.99566, 23.49639], [23.99539, 19.49944], [23.99715, 20.00038], [24.99794, 19.99661], [24.99885, 21.99535], [24.99968, 29.24574], [24.71117, 30.17441], [25.01077, 30.73861], [24.8458, 31.39877], [26.92891, 33.39516]]]]
25147           }
25148         }, {
25149           type: "Feature",
25150           properties: {
25151             iso1A2: "MA",
25152             iso1A3: "MAR",
25153             iso1N3: "504",
25154             wikidata: "Q1028",
25155             nameEn: "Morocco",
25156             groups: ["015", "002", "UN"],
25157             callingCodes: ["212"]
25158           },
25159           geometry: {
25160             type: "MultiPolygon",
25161             coordinates: [[[[-2.27707, 35.35051], [-5.10878, 36.05227], [-7.2725, 35.73269], [-14.43883, 27.02969], [-17.27295, 21.93519], [-17.21511, 21.34226], [-17.02707, 21.34022], [-16.9978, 21.36239], [-16.44269, 21.39745], [-14.78487, 21.36587], [-14.47329, 21.63839], [-14.48112, 22.00886], [-14.1291, 22.41636], [-14.10361, 22.75501], [-13.75627, 23.77231], [-13.00628, 24.01923], [-12.92147, 24.39502], [-12.12281, 25.13682], [-12.06001, 26.04442], [-11.62052, 26.05229], [-11.38635, 26.611], [-11.23622, 26.72023], [-11.35695, 26.8505], [-10.68417, 26.90984], [-9.81998, 26.71379], [-9.56957, 26.90042], [-9.08698, 26.98639], [-8.71787, 26.9898], [-8.77527, 27.66663], [-8.66879, 27.6666], [-8.6715, 28.71194], [-7.61585, 29.36252], [-6.95824, 29.50924], [-6.78351, 29.44634], [-6.69965, 29.51623], [-5.75616, 29.61407], [-5.72121, 29.52322], [-5.58831, 29.48103], [-5.21671, 29.95253], [-4.6058, 30.28343], [-4.31774, 30.53229], [-3.64735, 30.67539], [-3.65418, 30.85566], [-3.54944, 31.0503], [-3.77103, 31.14984], [-3.77647, 31.31912], [-3.66386, 31.39202], [-3.66314, 31.6339], [-2.82784, 31.79459], [-2.93873, 32.06557], [-2.46166, 32.16603], [-1.22829, 32.07832], [-1.15735, 32.12096], [-1.24453, 32.1917], [-1.24998, 32.32993], [-0.9912, 32.52467], [-1.37794, 32.73628], [-1.54244, 32.95499], [-1.46249, 33.0499], [-1.67067, 33.27084], [-1.59508, 33.59929], [-1.73494, 33.71721], [-1.64666, 34.10405], [-1.78042, 34.39018], [-1.69788, 34.48056], [-1.84569, 34.61907], [-1.73707, 34.74226], [-1.97469, 34.886], [-1.97833, 34.93218], [-2.04734, 34.93218], [-2.21445, 35.04378], [-2.21248, 35.08532], [-2.27707, 35.35051]], [[-2.91909, 35.33927], [-2.92272, 35.27509], [-2.93893, 35.26737], [-2.95065, 35.26576], [-2.95431, 35.2728], [-2.96516, 35.27967], [-2.96826, 35.28296], [-2.96507, 35.28801], [-2.97035, 35.28852], [-2.96978, 35.29459], [-2.96648, 35.30475], [-2.96038, 35.31609], [-2.91909, 35.33927]], [[-3.90602, 35.21494], [-3.89343, 35.22728], [-3.88372, 35.20767], [-3.90602, 35.21494]], [[-4.30191, 35.17419], [-4.29436, 35.17149], [-4.30112, 35.17058], [-4.30191, 35.17419]], [[-2.40316, 35.16893], [-2.45965, 35.16527], [-2.43262, 35.20652], [-2.40316, 35.16893]], [[-5.38491, 35.92591], [-5.21179, 35.90091], [-5.34379, 35.8711], [-5.35844, 35.87375], [-5.37338, 35.88417], [-5.38491, 35.92591]]]]
25162           }
25163         }, {
25164           type: "Feature",
25165           properties: {
25166             iso1A2: "MC",
25167             iso1A3: "MCO",
25168             iso1N3: "492",
25169             wikidata: "Q235",
25170             nameEn: "Monaco",
25171             groups: ["155", "150", "UN"],
25172             callingCodes: ["377"]
25173           },
25174           geometry: {
25175             type: "MultiPolygon",
25176             coordinates: [[[[7.47823, 43.73341], [7.4379, 43.74963], [7.4389, 43.75151], [7.43708, 43.75197], [7.43624, 43.75014], [7.43013, 43.74895], [7.42809, 43.74396], [7.42443, 43.74087], [7.42299, 43.74176], [7.42062, 43.73977], [7.41233, 43.73439], [7.41298, 43.73311], [7.41291, 43.73168], [7.41113, 43.73156], [7.40903, 43.7296], [7.42422, 43.72209], [7.47823, 43.73341]]]]
25177           }
25178         }, {
25179           type: "Feature",
25180           properties: {
25181             iso1A2: "MD",
25182             iso1A3: "MDA",
25183             iso1N3: "498",
25184             wikidata: "Q217",
25185             nameEn: "Moldova",
25186             groups: ["151", "150", "UN"],
25187             callingCodes: ["373"]
25188           },
25189           geometry: {
25190             type: "MultiPolygon",
25191             coordinates: [[[[27.74422, 48.45926], [27.6658, 48.44034], [27.59027, 48.46311], [27.5889, 48.49224], [27.46942, 48.454], [27.44333, 48.41209], [27.37741, 48.41026], [27.37604, 48.44398], [27.32159, 48.4434], [27.27855, 48.37534], [27.13434, 48.37288], [27.08078, 48.43214], [27.0231, 48.42485], [27.03821, 48.37653], [26.93384, 48.36558], [26.85556, 48.41095], [26.71274, 48.40388], [26.82809, 48.31629], [26.79239, 48.29071], [26.6839, 48.35828], [26.62823, 48.25804], [26.81161, 48.25049], [26.87708, 48.19919], [26.94265, 48.1969], [26.98042, 48.15752], [26.96119, 48.13003], [27.04118, 48.12522], [27.02985, 48.09083], [27.15622, 47.98538], [27.1618, 47.92391], [27.29069, 47.73722], [27.25519, 47.71366], [27.32202, 47.64009], [27.3979, 47.59473], [27.47942, 47.48113], [27.55731, 47.46637], [27.60263, 47.32507], [27.68706, 47.28962], [27.73172, 47.29248], [27.81892, 47.1381], [28.09095, 46.97621], [28.12173, 46.82283], [28.24808, 46.64305], [28.22281, 46.50481], [28.25769, 46.43334], [28.18902, 46.35283], [28.19864, 46.31869], [28.10937, 46.22852], [28.13684, 46.18099], [28.08612, 46.01105], [28.13111, 45.92819], [28.16568, 45.6421], [28.08927, 45.6051], [28.18741, 45.47358], [28.21139, 45.46895], [28.30201, 45.54744], [28.41836, 45.51715], [28.43072, 45.48538], [28.51449, 45.49982], [28.49252, 45.56716], [28.54196, 45.58062], [28.51587, 45.6613], [28.47879, 45.66994], [28.52823, 45.73803], [28.70401, 45.78019], [28.69852, 45.81753], [28.78503, 45.83475], [28.74383, 45.96664], [28.98004, 46.00385], [29.00613, 46.04962], [28.94643, 46.09176], [29.06656, 46.19716], [28.94953, 46.25852], [28.98478, 46.31803], [29.004, 46.31495], [28.9306, 46.45699], [29.01241, 46.46177], [29.02409, 46.49582], [29.23547, 46.55435], [29.24886, 46.37912], [29.35357, 46.49505], [29.49914, 46.45889], [29.5939, 46.35472], [29.6763, 46.36041], [29.66359, 46.4215], [29.74496, 46.45605], [29.88329, 46.35851], [29.94114, 46.40114], [30.09103, 46.38694], [30.16794, 46.40967], [30.02511, 46.45132], [29.88916, 46.54302], [29.94409, 46.56002], [29.9743, 46.75325], [29.94522, 46.80055], [29.98814, 46.82358], [29.87405, 46.88199], [29.75458, 46.8604], [29.72986, 46.92234], [29.57056, 46.94766], [29.62137, 47.05069], [29.61038, 47.09932], [29.53044, 47.07851], [29.49732, 47.12878], [29.57696, 47.13581], [29.54996, 47.24962], [29.59665, 47.25521], [29.5733, 47.36508], [29.48678, 47.36043], [29.47854, 47.30366], [29.39889, 47.30179], [29.3261, 47.44664], [29.18603, 47.43387], [29.11743, 47.55001], [29.22414, 47.60012], [29.22242, 47.73607], [29.27255, 47.79953], [29.20663, 47.80367], [29.27804, 47.88893], [29.19839, 47.89261], [29.1723, 47.99013], [28.9306, 47.96255], [28.8414, 48.03392], [28.85232, 48.12506], [28.69896, 48.13106], [28.53921, 48.17453], [28.48428, 48.0737], [28.42454, 48.12047], [28.43701, 48.15832], [28.38712, 48.17567], [28.34009, 48.13147], [28.30609, 48.14018], [28.30586, 48.1597], [28.34912, 48.1787], [28.36996, 48.20543], [28.35519, 48.24957], [28.32508, 48.23384], [28.2856, 48.23202], [28.19314, 48.20749], [28.17666, 48.25963], [28.07504, 48.23494], [28.09873, 48.3124], [28.04527, 48.32661], [27.95883, 48.32368], [27.88391, 48.36699], [27.87533, 48.4037], [27.81902, 48.41874], [27.79225, 48.44244], [27.74422, 48.45926]]]]
25192           }
25193         }, {
25194           type: "Feature",
25195           properties: {
25196             iso1A2: "ME",
25197             iso1A3: "MNE",
25198             iso1N3: "499",
25199             wikidata: "Q236",
25200             nameEn: "Montenegro",
25201             groups: ["039", "150", "UN"],
25202             callingCodes: ["382"]
25203           },
25204           geometry: {
25205             type: "MultiPolygon",
25206             coordinates: [[[[19.22807, 43.5264], [19.15685, 43.53943], [19.13933, 43.5282], [19.04934, 43.50384], [19.01078, 43.55806], [18.91379, 43.50299], [18.95469, 43.49367], [18.96053, 43.45042], [19.01078, 43.43854], [19.04071, 43.397], [19.08673, 43.31453], [19.08206, 43.29668], [19.04233, 43.30008], [19.00844, 43.24988], [18.95001, 43.29327], [18.95819, 43.32899], [18.90911, 43.36383], [18.83912, 43.34795], [18.84794, 43.33735], [18.85342, 43.32426], [18.76538, 43.29838], [18.6976, 43.25243], [18.71747, 43.2286], [18.66605, 43.2056], [18.64735, 43.14766], [18.66254, 43.03928], [18.52232, 43.01451], [18.49076, 42.95553], [18.49661, 42.89306], [18.4935, 42.86433], [18.47633, 42.85829], [18.45921, 42.81682], [18.47324, 42.74992], [18.56789, 42.72074], [18.55221, 42.69045], [18.54603, 42.69171], [18.54841, 42.68328], [18.57373, 42.64429], [18.52232, 42.62279], [18.55504, 42.58409], [18.53751, 42.57376], [18.49778, 42.58409], [18.43735, 42.55921], [18.44307, 42.51077], [18.43588, 42.48556], [18.52152, 42.42302], [18.54128, 42.39171], [18.45131, 42.21682], [19.26406, 41.74971], [19.37597, 41.84849], [19.37451, 41.8842], [19.33812, 41.90669], [19.34601, 41.95675], [19.37691, 41.96977], [19.36867, 42.02564], [19.37548, 42.06835], [19.40687, 42.10024], [19.28623, 42.17745], [19.42, 42.33019], [19.42352, 42.36546], [19.4836, 42.40831], [19.65972, 42.62774], [19.73244, 42.66299], [19.77375, 42.58517], [19.74731, 42.57422], [19.76549, 42.50237], [19.82333, 42.46581], [19.9324, 42.51699], [20.00842, 42.5109], [20.01834, 42.54622], [20.07761, 42.55582], [20.0969, 42.65559], [20.02915, 42.71147], [20.02088, 42.74789], [20.04898, 42.77701], [20.2539, 42.76245], [20.27869, 42.81945], [20.35692, 42.8335], [20.34528, 42.90676], [20.16415, 42.97177], [20.14896, 42.99058], [20.12325, 42.96237], [20.05431, 42.99571], [20.04729, 43.02732], [19.98887, 43.0538], [19.96549, 43.11098], [19.92576, 43.08539], [19.79255, 43.11951], [19.76918, 43.16044], [19.64063, 43.19027], [19.62661, 43.2286], [19.54598, 43.25158], [19.52962, 43.31623], [19.48171, 43.32644], [19.44315, 43.38846], [19.22229, 43.47926], [19.22807, 43.5264]]]]
25207           }
25208         }, {
25209           type: "Feature",
25210           properties: {
25211             iso1A2: "MF",
25212             iso1A3: "MAF",
25213             iso1N3: "663",
25214             wikidata: "Q126125",
25215             nameEn: "Saint-Martin",
25216             country: "FR",
25217             groups: ["Q3320166", "EU", "029", "003", "419", "019", "UN"],
25218             callingCodes: ["590"]
25219           },
25220           geometry: {
25221             type: "MultiPolygon",
25222             coordinates: [[[[-62.93924, 18.02904], [-62.62718, 18.26185], [-63.35989, 18.06012], [-63.33064, 17.9615], [-63.13502, 18.05445], [-63.11042, 18.05339], [-63.09686, 18.04608], [-63.07759, 18.04943], [-63.0579, 18.06614], [-63.04039, 18.05619], [-63.02323, 18.05757], [-62.93924, 18.02904]]]]
25223           }
25224         }, {
25225           type: "Feature",
25226           properties: {
25227             iso1A2: "MG",
25228             iso1A3: "MDG",
25229             iso1N3: "450",
25230             wikidata: "Q1019",
25231             nameEn: "Madagascar",
25232             aliases: ["RM"],
25233             groups: ["014", "202", "002", "UN"],
25234             callingCodes: ["261"]
25235           },
25236           geometry: {
25237             type: "MultiPolygon",
25238             coordinates: [[[[51.93891, -10.85085], [45.84651, -12.77177], [42.14681, -19.63341], [45.80092, -33.00974], [51.93891, -10.85085]]]]
25239           }
25240         }, {
25241           type: "Feature",
25242           properties: {
25243             iso1A2: "MH",
25244             iso1A3: "MHL",
25245             iso1N3: "584",
25246             wikidata: "Q709",
25247             nameEn: "Marshall Islands",
25248             groups: ["057", "009", "UN"],
25249             roadSpeedUnit: "mph",
25250             callingCodes: ["692"]
25251           },
25252           geometry: {
25253             type: "MultiPolygon",
25254             coordinates: [[[[169, 3.9], [173.53711, 5.70687], [169.29099, 15.77133], [159.04653, 10.59067], [169, 3.9]]]]
25255           }
25256         }, {
25257           type: "Feature",
25258           properties: {
25259             iso1A2: "MK",
25260             iso1A3: "MKD",
25261             iso1N3: "807",
25262             wikidata: "Q221",
25263             nameEn: "North Macedonia",
25264             groups: ["039", "150", "UN"],
25265             callingCodes: ["389"]
25266           },
25267           geometry: {
25268             type: "MultiPolygon",
25269             coordinates: [[[[22.34773, 42.31725], [22.29275, 42.34913], [22.29605, 42.37477], [22.16384, 42.32103], [22.02908, 42.29848], [21.94405, 42.34669], [21.91595, 42.30392], [21.84654, 42.3247], [21.77176, 42.2648], [21.70111, 42.23789], [21.58992, 42.25915], [21.52145, 42.24465], [21.50823, 42.27156], [21.43882, 42.2789], [21.43882, 42.23609], [21.38428, 42.24465], [21.30496, 42.1418], [21.29913, 42.13954], [21.31983, 42.10993], [21.22728, 42.08909], [21.16614, 42.19815], [21.11491, 42.20794], [20.75464, 42.05229], [20.76786, 41.91839], [20.68523, 41.85318], [20.59524, 41.8818], [20.55976, 41.87068], [20.57144, 41.7897], [20.53405, 41.78099], [20.51301, 41.72433], [20.52937, 41.69292], [20.51769, 41.65975], [20.55508, 41.58113], [20.52103, 41.56473], [20.45809, 41.5549], [20.45331, 41.51436], [20.49039, 41.49277], [20.51301, 41.442], [20.55976, 41.4087], [20.52119, 41.34381], [20.49432, 41.33679], [20.51068, 41.2323], [20.59715, 41.13644], [20.58546, 41.11179], [20.59832, 41.09066], [20.63454, 41.0889], [20.65558, 41.08009], [20.71634, 40.91781], [20.73504, 40.9081], [20.81567, 40.89662], [20.83671, 40.92752], [20.94305, 40.92399], [20.97693, 40.90103], [20.97887, 40.85475], [21.15262, 40.85546], [21.21105, 40.8855], [21.25779, 40.86165], [21.35595, 40.87578], [21.41555, 40.9173], [21.53007, 40.90759], [21.57448, 40.86076], [21.69601, 40.9429], [21.7556, 40.92525], [21.91102, 41.04786], [21.90869, 41.09191], [22.06527, 41.15617], [22.1424, 41.12449], [22.17629, 41.15969], [22.26744, 41.16409], [22.42285, 41.11921], [22.5549, 41.13065], [22.58295, 41.11568], [22.62852, 41.14385], [22.65306, 41.18168], [22.71266, 41.13945], [22.74538, 41.16321], [22.76408, 41.32225], [22.81199, 41.3398], [22.93334, 41.34104], [22.96331, 41.35782], [22.95513, 41.63265], [23.03342, 41.71034], [23.01239, 41.76527], [22.96682, 41.77137], [22.90254, 41.87587], [22.86749, 42.02275], [22.67701, 42.06614], [22.51224, 42.15457], [22.50289, 42.19527], [22.47251, 42.20393], [22.38136, 42.30339], [22.34773, 42.31725]]]]
25270           }
25271         }, {
25272           type: "Feature",
25273           properties: {
25274             iso1A2: "ML",
25275             iso1A3: "MLI",
25276             iso1N3: "466",
25277             wikidata: "Q912",
25278             nameEn: "Mali",
25279             groups: ["011", "202", "002", "UN"],
25280             callingCodes: ["223"]
25281           },
25282           geometry: {
25283             type: "MultiPolygon",
25284             coordinates: [[[[-4.83423, 24.99935], [-6.57191, 25.0002], [-5.60725, 16.49919], [-5.33435, 16.33354], [-5.50165, 15.50061], [-9.32979, 15.50032], [-9.31106, 15.69412], [-9.33314, 15.7044], [-9.44673, 15.60553], [-9.40447, 15.4396], [-10.71721, 15.4223], [-10.90932, 15.11001], [-11.43483, 15.62339], [-11.70705, 15.51558], [-11.94903, 14.76143], [-12.23936, 14.76324], [-11.93043, 13.84505], [-12.06897, 13.71049], [-11.83345, 13.33333], [-11.63025, 13.39174], [-11.39935, 12.97808], [-11.37536, 12.40788], [-11.50006, 12.17826], [-11.24136, 12.01286], [-10.99758, 12.24634], [-10.80355, 12.1053], [-10.71897, 11.91552], [-10.30604, 12.24634], [-9.714, 12.0226], [-9.63938, 12.18312], [-9.32097, 12.29009], [-9.38067, 12.48446], [-9.13689, 12.50875], [-8.94784, 12.34842], [-8.80854, 11.66715], [-8.40058, 11.37466], [-8.66923, 10.99397], [-8.35083, 11.06234], [-8.2667, 10.91762], [-8.32614, 10.69273], [-8.22711, 10.41722], [-8.10207, 10.44649], [-7.9578, 10.2703], [-7.97971, 10.17117], [-7.92107, 10.15577], [-7.63048, 10.46334], [-7.54462, 10.40921], [-7.52261, 10.4655], [-7.44555, 10.44602], [-7.3707, 10.24677], [-7.13331, 10.24877], [-7.0603, 10.14711], [-7.00966, 10.15794], [-6.97444, 10.21644], [-7.01186, 10.25111], [-6.93921, 10.35291], [-6.68164, 10.35074], [-6.63541, 10.66893], [-6.52974, 10.59104], [-6.42847, 10.5694], [-6.40646, 10.69922], [-6.325, 10.68624], [-6.24795, 10.74248], [-6.1731, 10.46983], [-6.18851, 10.24244], [-5.99478, 10.19694], [-5.78124, 10.43952], [-5.65135, 10.46767], [-5.51058, 10.43177], [-5.46643, 10.56074], [-5.47083, 10.75329], [-5.41579, 10.84628], [-5.49284, 11.07538], [-5.32994, 11.13371], [-5.32553, 11.21578], [-5.25949, 11.24816], [-5.25509, 11.36905], [-5.20665, 11.43811], [-5.22867, 11.60421], [-5.29251, 11.61715], [-5.26389, 11.75728], [-5.40258, 11.8327], [-5.26389, 11.84778], [-5.07897, 11.97918], [-4.72893, 12.01579], [-4.70692, 12.06746], [-4.62987, 12.06531], [-4.62546, 12.13204], [-4.54841, 12.1385], [-4.57703, 12.19875], [-4.41412, 12.31922], [-4.47356, 12.71252], [-4.238, 12.71467], [-4.21819, 12.95722], [-4.34477, 13.12927], [-3.96501, 13.49778], [-3.90558, 13.44375], [-3.96282, 13.38164], [-3.7911, 13.36665], [-3.54454, 13.1781], [-3.4313, 13.1588], [-3.43507, 13.27272], [-3.23599, 13.29035], [-3.28396, 13.5422], [-3.26407, 13.70699], [-2.88189, 13.64921], [-2.90831, 13.81174], [-2.84667, 14.05532], [-2.66175, 14.14713], [-2.47587, 14.29671], [-2.10223, 14.14878], [-1.9992, 14.19011], [-1.97945, 14.47709], [-1.68083, 14.50023], [-1.32166, 14.72774], [-1.05875, 14.7921], [-0.72004, 15.08655], [-0.24673, 15.07805], [0.06588, 14.96961], [0.23859, 15.00135], [0.72632, 14.95898], [0.96711, 14.98275], [1.31275, 15.27978], [3.01806, 15.34571], [3.03134, 15.42221], [3.50368, 15.35934], [4.19893, 16.39923], [4.21787, 17.00118], [4.26762, 17.00432], [4.26651, 19.14224], [3.36082, 18.9745], [3.12501, 19.1366], [3.24648, 19.81703], [1.20992, 20.73533], [1.15698, 21.12843], [-4.83423, 24.99935]]]]
25285           }
25286         }, {
25287           type: "Feature",
25288           properties: {
25289             iso1A2: "MM",
25290             iso1A3: "MMR",
25291             iso1N3: "104",
25292             wikidata: "Q836",
25293             nameEn: "Myanmar",
25294             aliases: ["Burma", "BU"],
25295             groups: ["035", "142", "UN"],
25296             callingCodes: ["95"]
25297           },
25298           geometry: {
25299             type: "MultiPolygon",
25300             coordinates: [[[[92.62187, 21.87037], [92.59775, 21.6092], [92.68152, 21.28454], [92.60187, 21.24615], [92.55105, 21.3856], [92.43158, 21.37025], [92.37939, 21.47764], [92.20087, 21.337], [92.17752, 21.17445], [92.26071, 21.05697], [92.47409, 20.38654], [92.61042, 13.76986], [94.6371, 13.81803], [97.63455, 9.60854], [98.12555, 9.44056], [98.33094, 9.91973], [98.47298, 9.95782], [98.52291, 9.92389], [98.55174, 9.92804], [98.7391, 10.31488], [98.81944, 10.52761], [98.77275, 10.62548], [98.78511, 10.68351], [98.86819, 10.78336], [99.0069, 10.85485], [98.99701, 10.92962], [99.02337, 10.97217], [99.06938, 10.94857], [99.32756, 11.28545], [99.31573, 11.32081], [99.39485, 11.3925], [99.47598, 11.62434], [99.5672, 11.62732], [99.64108, 11.78948], [99.64891, 11.82699], [99.53424, 12.02317], [99.56445, 12.14805], [99.47519, 12.1353], [99.409, 12.60603], [99.29254, 12.68921], [99.18905, 12.84799], [99.18748, 12.9898], [99.10646, 13.05804], [99.12225, 13.19847], [99.20617, 13.20575], [99.16695, 13.72621], [98.97356, 14.04868], [98.56762, 14.37701], [98.24874, 14.83013], [98.18821, 15.13125], [98.22, 15.21327], [98.30446, 15.30667], [98.40522, 15.25268], [98.41906, 15.27103], [98.39351, 15.34177], [98.4866, 15.39154], [98.56027, 15.33471], [98.58598, 15.46821], [98.541, 15.65406], [98.59853, 15.87197], [98.57019, 16.04578], [98.69585, 16.13353], [98.8376, 16.11706], [98.92656, 16.36425], [98.84485, 16.42354], [98.68074, 16.27068], [98.63817, 16.47424], [98.57912, 16.55983], [98.5695, 16.62826], [98.51113, 16.64503], [98.51833, 16.676], [98.51472, 16.68521], [98.51579, 16.69433], [98.51043, 16.70107], [98.49713, 16.69022], [98.50253, 16.7139], [98.46994, 16.73613], [98.53833, 16.81934], [98.49603, 16.8446], [98.52624, 16.89979], [98.39441, 17.06266], [98.34566, 17.04822], [98.10439, 17.33847], [98.11185, 17.36829], [97.91829, 17.54504], [97.76407, 17.71595], [97.66794, 17.88005], [97.73723, 17.97912], [97.60841, 18.23846], [97.64116, 18.29778], [97.56219, 18.33885], [97.50383, 18.26844], [97.34522, 18.54596], [97.36444, 18.57138], [97.5258, 18.4939], [97.76752, 18.58097], [97.73836, 18.88478], [97.66487, 18.9371], [97.73654, 18.9812], [97.73797, 19.04261], [97.83479, 19.09972], [97.84024, 19.22217], [97.78606, 19.26769], [97.84186, 19.29526], [97.78769, 19.39429], [97.88423, 19.5041], [97.84715, 19.55782], [98.04364, 19.65755], [98.03314, 19.80941], [98.13829, 19.78541], [98.24884, 19.67876], [98.51182, 19.71303], [98.56065, 19.67807], [98.83661, 19.80931], [98.98679, 19.7419], [99.0735, 20.10298], [99.20328, 20.12877], [99.416, 20.08614], [99.52943, 20.14811], [99.5569, 20.20676], [99.46077, 20.36198], [99.46008, 20.39673], [99.68255, 20.32077], [99.81096, 20.33687], [99.86383, 20.44371], [99.88211, 20.44488], [99.88451, 20.44596], [99.89168, 20.44548], [99.89301, 20.44311], [99.89692, 20.44789], [99.90499, 20.4487], [99.91616, 20.44986], [99.95721, 20.46301], [100.08404, 20.36626], [100.1957, 20.68247], [100.36375, 20.82783], [100.51079, 20.82194], [100.60112, 20.8347], [100.64628, 20.88279], [100.50974, 20.88574], [100.55281, 21.02796], [100.63578, 21.05639], [100.72716, 21.31786], [100.80173, 21.2934], [101.00234, 21.39612], [101.16198, 21.52808], [101.15156, 21.56129], [101.11744, 21.77659], [100.87265, 21.67396], [100.72143, 21.51898], [100.57861, 21.45637], [100.4811, 21.46148], [100.42892, 21.54325], [100.35201, 21.53176], [100.25863, 21.47043], [100.18447, 21.51898], [100.1625, 21.48704], [100.12542, 21.50365], [100.10757, 21.59945], [100.17486, 21.65306], [100.12679, 21.70539], [100.04956, 21.66843], [99.98654, 21.71064], [99.94003, 21.82782], [99.99084, 21.97053], [99.96612, 22.05965], [99.85351, 22.04183], [99.47585, 22.13345], [99.33166, 22.09656], [99.1552, 22.15874], [99.19176, 22.16983], [99.17318, 22.18025], [99.28771, 22.4105], [99.37972, 22.50188], [99.38247, 22.57544], [99.31243, 22.73893], [99.45654, 22.85726], [99.43537, 22.94086], [99.54218, 22.90014], [99.52214, 23.08218], [99.34127, 23.13099], [99.25741, 23.09025], [99.04601, 23.12215], [99.05975, 23.16382], [98.88597, 23.18656], [98.92515, 23.29535], [98.93958, 23.31414], [98.87573, 23.33038], [98.92104, 23.36946], [98.87683, 23.48995], [98.82877, 23.47908], [98.80294, 23.5345], [98.88396, 23.59555], [98.81775, 23.694], [98.82933, 23.72921], [98.79607, 23.77947], [98.68209, 23.80492], [98.67797, 23.9644], [98.89632, 24.10612], [98.87998, 24.15624], [98.85319, 24.13042], [98.59256, 24.08371], [98.54476, 24.13119], [98.20666, 24.11406], [98.07806, 24.07988], [98.06703, 24.08028], [98.0607, 24.07812], [98.05671, 24.07961], [98.05302, 24.07408], [98.04709, 24.07616], [97.99583, 24.04932], [97.98691, 24.03897], [97.93951, 24.01953], [97.90998, 24.02094], [97.88616, 24.00463], [97.88414, 23.99405], [97.88814, 23.98605], [97.89683, 23.98389], [97.89676, 23.97931], [97.8955, 23.97758], [97.88811, 23.97446], [97.86545, 23.97723], [97.84328, 23.97603], [97.79416, 23.95663], [97.79456, 23.94836], [97.72302, 23.89288], [97.64667, 23.84574], [97.5247, 23.94032], [97.62363, 24.00506], [97.72903, 24.12606], [97.75305, 24.16902], [97.72799, 24.18883], [97.72998, 24.2302], [97.76799, 24.26365], [97.71941, 24.29652], [97.66723, 24.30027], [97.65624, 24.33781], [97.7098, 24.35658], [97.66998, 24.45288], [97.60029, 24.4401], [97.52757, 24.43748], [97.56286, 24.54535], [97.56525, 24.72838], [97.54675, 24.74202], [97.5542, 24.74943], [97.56383, 24.75535], [97.56648, 24.76475], [97.64354, 24.79171], [97.70181, 24.84557], [97.73127, 24.83015], [97.76481, 24.8289], [97.79949, 24.85655], [97.72903, 24.91332], [97.72216, 25.08508], [97.77023, 25.11492], [97.83614, 25.2715], [97.92541, 25.20815], [98.14925, 25.41547], [98.12591, 25.50722], [98.18084, 25.56298], [98.16848, 25.62739], [98.25774, 25.6051], [98.31268, 25.55307], [98.40606, 25.61129], [98.54064, 25.85129], [98.63128, 25.79937], [98.70818, 25.86241], [98.60763, 26.01512], [98.57085, 26.11547], [98.63128, 26.15492], [98.66884, 26.09165], [98.7329, 26.17218], [98.67797, 26.24487], [98.72741, 26.36183], [98.77547, 26.61994], [98.7333, 26.85615], [98.69582, 27.56499], [98.43353, 27.67086], [98.42529, 27.55404], [98.32641, 27.51385], [98.13964, 27.9478], [98.15337, 28.12114], [97.90069, 28.3776], [97.79632, 28.33168], [97.70705, 28.5056], [97.56835, 28.55628], [97.50518, 28.49716], [97.47085, 28.2688], [97.41729, 28.29783], [97.34547, 28.21385], [97.31292, 28.06784], [97.35412, 28.06663], [97.38845, 28.01329], [97.35824, 27.87256], [97.29919, 27.92233], [96.90112, 27.62149], [96.91431, 27.45752], [97.17422, 27.14052], [97.14675, 27.09041], [96.89132, 27.17474], [96.85287, 27.2065], [96.88445, 27.25046], [96.73888, 27.36638], [96.55761, 27.29928], [96.40779, 27.29818], [96.15591, 27.24572], [96.04949, 27.19428], [95.93002, 27.04149], [95.81603, 27.01335], [95.437, 26.7083], [95.30339, 26.65372], [95.23513, 26.68499], [95.05798, 26.45408], [95.12801, 26.38397], [95.11428, 26.1019], [95.18556, 26.07338], [94.80117, 25.49359], [94.68032, 25.47003], [94.57458, 25.20318], [94.74212, 25.13606], [94.73937, 25.00545], [94.60204, 24.70889], [94.5526, 24.70764], [94.50729, 24.59281], [94.45279, 24.56656], [94.32362, 24.27692], [94.30215, 24.23752], [94.14081, 23.83333], [93.92089, 23.95812], [93.80279, 23.92549], [93.75952, 24.0003], [93.62871, 24.00922], [93.50616, 23.94432], [93.46633, 23.97067], [93.41415, 24.07854], [93.34735, 24.10151], [93.32351, 24.04468], [93.36059, 23.93176], [93.3908, 23.92925], [93.3908, 23.7622], [93.43475, 23.68299], [93.38805, 23.4728], [93.39981, 23.38828], [93.38781, 23.36139], [93.36862, 23.35426], [93.38478, 23.13698], [93.2878, 23.00464], [93.12988, 23.05772], [93.134, 22.92498], [93.09417, 22.69459], [93.134, 22.59573], [93.11477, 22.54374], [93.13537, 22.45873], [93.18206, 22.43716], [93.19991, 22.25425], [93.14224, 22.24535], [93.15734, 22.18687], [93.04885, 22.20595], [92.99255, 22.05965], [92.99804, 21.98964], [92.93899, 22.02656], [92.89504, 21.95143], [92.86208, 22.05456], [92.70416, 22.16017], [92.67532, 22.03547], [92.60949, 21.97638], [92.62187, 21.87037]]]]
25301           }
25302         }, {
25303           type: "Feature",
25304           properties: {
25305             iso1A2: "MN",
25306             iso1A3: "MNG",
25307             iso1N3: "496",
25308             wikidata: "Q711",
25309             nameEn: "Mongolia",
25310             groups: ["030", "142", "UN"],
25311             callingCodes: ["976"]
25312           },
25313           geometry: {
25314             type: "MultiPolygon",
25315             coordinates: [[[[102.14032, 51.35566], [101.5044, 51.50467], [101.39085, 51.45753], [100.61116, 51.73028], [99.89203, 51.74903], [99.75578, 51.90108], [99.27888, 51.96876], [98.87768, 52.14563], [98.74142, 51.8637], [98.33222, 51.71832], [98.22053, 51.46579], [98.05257, 51.46696], [97.83305, 51.00248], [98.01472, 50.86652], [97.9693, 50.78044], [98.06393, 50.61262], [98.31373, 50.4996], [98.29481, 50.33561], [97.85197, 49.91339], [97.76871, 49.99861], [97.56432, 49.92801], [97.56811, 49.84265], [97.24639, 49.74737], [96.97388, 49.88413], [95.80056, 50.04239], [95.74757, 49.97915], [95.02465, 49.96941], [94.97166, 50.04725], [94.6121, 50.04239], [94.49477, 50.17832], [94.39258, 50.22193], [94.30823, 50.57498], [92.99595, 50.63183], [93.01109, 50.79001], [92.44714, 50.78762], [92.07173, 50.69585], [91.86048, 50.73734], [89.59711, 49.90851], [89.70687, 49.72535], [88.82499, 49.44808], [88.42449, 49.48821], [88.17223, 49.46934], [88.15543, 49.30314], [87.98977, 49.18147], [87.81333, 49.17354], [87.88171, 48.95853], [87.73822, 48.89582], [88.0788, 48.71436], [87.96361, 48.58478], [88.58939, 48.34531], [88.58316, 48.21893], [88.8011, 48.11302], [88.93186, 48.10263], [89.0711, 47.98528], [89.55453, 48.0423], [89.76624, 47.82745], [90.06512, 47.88177], [90.10871, 47.7375], [90.33598, 47.68303], [90.48854, 47.41826], [90.48542, 47.30438], [90.76108, 46.99399], [90.84035, 46.99525], [91.03649, 46.72916], [91.0147, 46.58171], [91.07696, 46.57315], [90.89639, 46.30711], [90.99672, 46.14207], [91.03026, 46.04194], [90.70907, 45.73437], [90.65114, 45.49314], [90.89169, 45.19667], [91.64048, 45.07408], [93.51161, 44.95964], [94.10003, 44.71016], [94.71959, 44.35284], [95.01191, 44.25274], [95.39772, 44.2805], [95.32891, 44.02407], [95.52594, 43.99353], [95.89543, 43.2528], [96.35658, 42.90363], [96.37926, 42.72055], [97.1777, 42.7964], [99.50671, 42.56535], [100.33297, 42.68231], [100.84979, 42.67087], [101.80515, 42.50074], [102.07645, 42.22519], [102.72403, 42.14675], [103.92804, 41.78246], [104.52258, 41.8706], [104.51667, 41.66113], [105.0123, 41.63188], [106.76517, 42.28741], [107.24774, 42.36107], [107.29755, 42.41395], [107.49681, 42.46221], [107.57258, 42.40898], [108.84489, 42.40246], [109.00679, 42.45302], [109.452, 42.44842], [109.89402, 42.63111], [110.08401, 42.6411], [110.4327, 42.78293], [111.0149, 43.3289], [111.59087, 43.51207], [111.79758, 43.6637], [111.93776, 43.68709], [111.96289, 43.81596], [111.40498, 44.3461], [111.76275, 44.98032], [111.98695, 45.09074], [112.4164, 45.06858], [112.74662, 44.86297], [113.70918, 44.72891], [114.5166, 45.27189], [114.54801, 45.38337], [114.74612, 45.43585], [114.94546, 45.37377], [115.60329, 45.44717], [116.16989, 45.68603], [116.27366, 45.78637], [116.24012, 45.8778], [116.26678, 45.96479], [116.58612, 46.30211], [116.75551, 46.33083], [116.83166, 46.38637], [117.36609, 46.36335], [117.41782, 46.57862], [117.60748, 46.59771], [117.69554, 46.50991], [118.30534, 46.73519], [118.78747, 46.68689], [118.8337, 46.77742], [118.89974, 46.77139], [118.92616, 46.72765], [119.00541, 46.74273], [119.10448, 46.65516], [119.24978, 46.64761], [119.32827, 46.61433], [119.42827, 46.63783], [119.65265, 46.62342], [119.68127, 46.59015], [119.77373, 46.62947], [119.80455, 46.67631], [119.89261, 46.66423], [119.91242, 46.90091], [119.85518, 46.92196], [119.71209, 47.19192], [119.62403, 47.24575], [119.56019, 47.24874], [119.54918, 47.29505], [119.31964, 47.42617], [119.35892, 47.48104], [119.13995, 47.53997], [119.12343, 47.66458], [118.7564, 47.76947], [118.55766, 47.99277], [118.29654, 48.00246], [118.22677, 48.03853], [118.11009, 48.04], [118.03676, 48.00982], [117.80196, 48.01661], [117.50181, 47.77216], [117.37875, 47.63627], [116.9723, 47.87285], [116.67405, 47.89039], [116.4465, 47.83662], [116.21879, 47.88505], [115.94296, 47.67741], [115.57128, 47.91988], [115.52082, 48.15367], [115.811, 48.25699], [115.78876, 48.51781], [116.06565, 48.81716], [116.03781, 48.87014], [116.71193, 49.83813], [116.62502, 49.92919], [116.22402, 50.04477], [115.73602, 49.87688], [115.26068, 49.97367], [114.9703, 50.19254], [114.325, 50.28098], [113.20216, 49.83356], [113.02647, 49.60772], [110.64493, 49.1816], [110.39891, 49.25083], [110.24373, 49.16676], [109.51325, 49.22859], [109.18017, 49.34709], [108.53969, 49.32325], [108.27937, 49.53167], [107.95387, 49.66659], [107.96116, 49.93191], [107.36407, 49.97612], [107.1174, 50.04239], [107.00007, 50.1977], [106.80326, 50.30177], [106.58373, 50.34044], [106.51122, 50.34408], [106.49628, 50.32436], [106.47156, 50.31909], [106.07865, 50.33474], [106.05562, 50.40582], [105.32528, 50.4648], [103.70343, 50.13952], [102.71178, 50.38873], [102.32194, 50.67982], [102.14032, 51.35566]]]]
25316           }
25317         }, {
25318           type: "Feature",
25319           properties: {
25320             iso1A2: "MO",
25321             iso1A3: "MAC",
25322             iso1N3: "446",
25323             wikidata: "Q14773",
25324             nameEn: "Macau",
25325             aliases: ["Macao"],
25326             country: "CN",
25327             groups: ["030", "142", "UN"],
25328             driveSide: "left",
25329             callingCodes: ["853"]
25330           },
25331           geometry: {
25332             type: "MultiPolygon",
25333             coordinates: [[[[113.54942, 22.14519], [113.54839, 22.10909], [113.57191, 22.07696], [113.63011, 22.10782], [113.60504, 22.20464], [113.57123, 22.20416], [113.56865, 22.20973], [113.5508, 22.21672], [113.54333, 22.21688], [113.54093, 22.21314], [113.53593, 22.2137], [113.53301, 22.21235], [113.53552, 22.20607], [113.52659, 22.18271], [113.54093, 22.15497], [113.54942, 22.14519]]]]
25334           }
25335         }, {
25336           type: "Feature",
25337           properties: {
25338             iso1A2: "MP",
25339             iso1A3: "MNP",
25340             iso1N3: "580",
25341             wikidata: "Q16644",
25342             nameEn: "Northern Mariana Islands",
25343             aliases: ["US-MP"],
25344             country: "US",
25345             groups: ["Q1352230", "Q153732", "057", "009", "UN"],
25346             roadSpeedUnit: "mph",
25347             callingCodes: ["1 670"]
25348           },
25349           geometry: {
25350             type: "MultiPolygon",
25351             coordinates: [[[[135.52896, 14.32623], [152.19114, 13.63487], [145.05972, 21.28731], [135.52896, 14.32623]]]]
25352           }
25353         }, {
25354           type: "Feature",
25355           properties: {
25356             iso1A2: "MQ",
25357             iso1A3: "MTQ",
25358             iso1N3: "474",
25359             wikidata: "Q17054",
25360             nameEn: "Martinique",
25361             country: "FR",
25362             groups: ["Q3320166", "EU", "029", "003", "419", "019", "UN"],
25363             callingCodes: ["596"]
25364           },
25365           geometry: {
25366             type: "MultiPolygon",
25367             coordinates: [[[[-59.95997, 14.20285], [-61.07821, 15.25109], [-61.69315, 14.26451], [-59.95997, 14.20285]]]]
25368           }
25369         }, {
25370           type: "Feature",
25371           properties: {
25372             iso1A2: "MR",
25373             iso1A3: "MRT",
25374             iso1N3: "478",
25375             wikidata: "Q1025",
25376             nameEn: "Mauritania",
25377             groups: ["011", "202", "002", "UN"],
25378             callingCodes: ["222"]
25379           },
25380           geometry: {
25381             type: "MultiPolygon",
25382             coordinates: [[[[-5.60725, 16.49919], [-6.57191, 25.0002], [-4.83423, 24.99935], [-8.66674, 27.31569], [-8.66721, 25.99918], [-12.0002, 25.9986], [-12.00251, 23.4538], [-12.14969, 23.41935], [-12.36213, 23.3187], [-12.5741, 23.28975], [-13.00412, 23.02297], [-13.10753, 22.89493], [-13.15313, 22.75649], [-13.08438, 22.53866], [-13.01525, 21.33343], [-16.95474, 21.33997], [-16.99806, 21.12142], [-17.0357, 21.05368], [-17.0396, 20.9961], [-17.06781, 20.92697], [-17.0695, 20.85742], [-17.0471, 20.76408], [-17.15288, 16.07139], [-16.50854, 16.09032], [-16.48967, 16.0496], [-16.44814, 16.09753], [-16.4429, 16.20605], [-16.27016, 16.51565], [-15.6509, 16.50315], [-15.00557, 16.64997], [-14.32144, 16.61495], [-13.80075, 16.13961], [-13.43135, 16.09022], [-13.11029, 15.52116], [-12.23936, 14.76324], [-11.94903, 14.76143], [-11.70705, 15.51558], [-11.43483, 15.62339], [-10.90932, 15.11001], [-10.71721, 15.4223], [-9.40447, 15.4396], [-9.44673, 15.60553], [-9.33314, 15.7044], [-9.31106, 15.69412], [-9.32979, 15.50032], [-5.50165, 15.50061], [-5.33435, 16.33354], [-5.60725, 16.49919]]]]
25383           }
25384         }, {
25385           type: "Feature",
25386           properties: {
25387             iso1A2: "MS",
25388             iso1A3: "MSR",
25389             iso1N3: "500",
25390             wikidata: "Q13353",
25391             nameEn: "Montserrat",
25392             country: "GB",
25393             groups: ["BOTS", "029", "003", "419", "019", "UN"],
25394             driveSide: "left",
25395             roadSpeedUnit: "mph",
25396             roadHeightUnit: "ft",
25397             callingCodes: ["1 664"]
25398           },
25399           geometry: {
25400             type: "MultiPolygon",
25401             coordinates: [[[[-61.91508, 16.51165], [-62.1023, 16.97277], [-62.58307, 16.68909], [-61.91508, 16.51165]]]]
25402           }
25403         }, {
25404           type: "Feature",
25405           properties: {
25406             iso1A2: "MT",
25407             iso1A3: "MLT",
25408             iso1N3: "470",
25409             wikidata: "Q233",
25410             nameEn: "Malta",
25411             groups: ["EU", "039", "150", "UN"],
25412             driveSide: "left",
25413             callingCodes: ["356"]
25414           },
25415           geometry: {
25416             type: "MultiPolygon",
25417             coordinates: [[[[15.70991, 35.79901], [14.07544, 36.41525], [13.27636, 35.20764], [15.70991, 35.79901]]]]
25418           }
25419         }, {
25420           type: "Feature",
25421           properties: {
25422             iso1A2: "MU",
25423             iso1A3: "MUS",
25424             iso1N3: "480",
25425             wikidata: "Q1027",
25426             nameEn: "Mauritius",
25427             groups: ["014", "202", "002", "UN"],
25428             driveSide: "left",
25429             callingCodes: ["230"]
25430           },
25431           geometry: {
25432             type: "MultiPolygon",
25433             coordinates: [[[[56.09755, -9.55401], [57.50644, -31.92637], [68.4673, -19.15185], [56.09755, -9.55401]]]]
25434           }
25435         }, {
25436           type: "Feature",
25437           properties: {
25438             iso1A2: "MV",
25439             iso1A3: "MDV",
25440             iso1N3: "462",
25441             wikidata: "Q826",
25442             nameEn: "Maldives",
25443             groups: ["034", "142", "UN"],
25444             driveSide: "left",
25445             callingCodes: ["960"]
25446           },
25447           geometry: {
25448             type: "MultiPolygon",
25449             coordinates: [[[[71.9161, 8.55531], [72.57428, -3.7623], [76.59015, 5.591], [71.9161, 8.55531]]]]
25450           }
25451         }, {
25452           type: "Feature",
25453           properties: {
25454             iso1A2: "MW",
25455             iso1A3: "MWI",
25456             iso1N3: "454",
25457             wikidata: "Q1020",
25458             nameEn: "Malawi",
25459             groups: ["014", "202", "002", "UN"],
25460             driveSide: "left",
25461             callingCodes: ["265"]
25462           },
25463           geometry: {
25464             type: "MultiPolygon",
25465             coordinates: [[[[33.48052, -9.62442], [33.31581, -9.48554], [33.14925, -9.49322], [32.99397, -9.36712], [32.95389, -9.40138], [33.00476, -9.5133], [33.00256, -9.63053], [33.05485, -9.61316], [33.10163, -9.66525], [33.12144, -9.58929], [33.2095, -9.61099], [33.31517, -9.82364], [33.36581, -9.81063], [33.37902, -9.9104], [33.31297, -10.05133], [33.53863, -10.20148], [33.54797, -10.36077], [33.70675, -10.56896], [33.47636, -10.78465], [33.28022, -10.84428], [33.25998, -10.88862], [33.39697, -11.15296], [33.29267, -11.3789], [33.29267, -11.43536], [33.23663, -11.40637], [33.24252, -11.59302], [33.32692, -11.59248], [33.33937, -11.91252], [33.25998, -12.14242], [33.3705, -12.34931], [33.47636, -12.32498], [33.54485, -12.35996], [33.37517, -12.54085], [33.28177, -12.54692], [33.18837, -12.61377], [33.05917, -12.59554], [32.94397, -12.76868], [32.96733, -12.88251], [33.02181, -12.88707], [32.98289, -13.12671], [33.0078, -13.19492], [32.86113, -13.47292], [32.84176, -13.52794], [32.73683, -13.57682], [32.68436, -13.55769], [32.66468, -13.60019], [32.68654, -13.64268], [32.7828, -13.64805], [32.84528, -13.71576], [32.76962, -13.77224], [32.79015, -13.80755], [32.88985, -13.82956], [32.99042, -13.95689], [33.02977, -14.05022], [33.07568, -13.98447], [33.16749, -13.93992], [33.24249, -14.00019], [33.66677, -14.61306], [33.7247, -14.4989], [33.88503, -14.51652], [33.92898, -14.47929], [34.08588, -14.48893], [34.18733, -14.43823], [34.22355, -14.43607], [34.34453, -14.3985], [34.35843, -14.38652], [34.39277, -14.39467], [34.4192, -14.43191], [34.44641, -14.47746], [34.45053, -14.49873], [34.47628, -14.53363], [34.48932, -14.53646], [34.49636, -14.55091], [34.52366, -14.5667], [34.53962, -14.59776], [34.55112, -14.64494], [34.53516, -14.67782], [34.52057, -14.68263], [34.54503, -14.74672], [34.567, -14.77345], [34.61522, -14.99583], [34.57503, -15.30619], [34.43126, -15.44778], [34.44981, -15.60864], [34.25195, -15.90321], [34.43126, -16.04737], [34.40344, -16.20923], [35.04805, -16.83167], [35.13771, -16.81687], [35.17017, -16.93521], [35.04805, -17.00027], [35.0923, -17.13235], [35.3062, -17.1244], [35.27065, -16.93817], [35.30929, -16.82871], [35.27219, -16.69402], [35.14235, -16.56812], [35.25828, -16.4792], [35.30157, -16.2211], [35.43355, -16.11371], [35.52365, -16.15414], [35.70107, -16.10147], [35.80487, -16.03907], [35.85303, -15.41913], [35.78799, -15.17428], [35.91812, -14.89514], [35.87212, -14.89478], [35.86945, -14.67481], [35.5299, -14.27714], [35.47989, -14.15594], [34.86229, -13.48958], [34.60253, -13.48487], [34.37831, -12.17408], [34.46088, -12.0174], [34.70739, -12.15652], [34.82903, -12.04837], [34.57917, -11.87849], [34.64241, -11.57499], [34.96296, -11.57354], [34.91153, -11.39799], [34.79375, -11.32245], [34.63305, -11.11731], [34.61161, -11.01611], [34.67047, -10.93796], [34.65946, -10.6828], [34.57581, -10.56271], [34.51911, -10.12279], [34.54499, -10.0678], [34.03865, -9.49398], [33.95829, -9.54066], [33.9638, -9.62206], [33.93298, -9.71647], [33.76677, -9.58516], [33.48052, -9.62442]]]]
25466           }
25467         }, {
25468           type: "Feature",
25469           properties: {
25470             iso1A2: "MX",
25471             iso1A3: "MEX",
25472             iso1N3: "484",
25473             wikidata: "Q96",
25474             nameEn: "Mexico",
25475             groups: ["013", "003", "419", "019", "UN"],
25476             callingCodes: ["52"]
25477           },
25478           geometry: {
25479             type: "MultiPolygon",
25480             coordinates: [[[[-117.1243, 32.53427], [-118.48109, 32.5991], [-120.12904, 18.41089], [-92.37213, 14.39277], [-92.2261, 14.53423], [-92.1454, 14.6804], [-92.18161, 14.84147], [-92.1423, 14.88647], [-92.1454, 14.98143], [-92.0621, 15.07406], [-92.20983, 15.26077], [-91.73182, 16.07371], [-90.44567, 16.07573], [-90.40499, 16.40524], [-90.61212, 16.49832], [-90.69064, 16.70697], [-91.04436, 16.92175], [-91.43809, 17.25373], [-90.99199, 17.25192], [-90.98678, 17.81655], [-89.14985, 17.81563], [-89.15105, 17.95104], [-89.03839, 18.0067], [-88.8716, 17.89535], [-88.71505, 18.0707], [-88.48242, 18.49164], [-88.3268, 18.49048], [-88.29909, 18.47591], [-88.26593, 18.47617], [-88.03238, 18.41778], [-88.03165, 18.16657], [-87.90671, 18.15213], [-87.87604, 18.18313], [-87.86657, 18.19971], [-87.85693, 18.18266], [-87.84815, 18.18511], [-86.92368, 17.61462], [-85.9092, 21.8218], [-96.92418, 25.97377], [-97.13927, 25.96583], [-97.35946, 25.92189], [-97.37332, 25.83854], [-97.42511, 25.83969], [-97.45669, 25.86874], [-97.49828, 25.89877], [-97.52025, 25.88518], [-97.66511, 26.01708], [-97.95155, 26.0625], [-97.97017, 26.05232], [-98.24603, 26.07191], [-98.27075, 26.09457], [-98.30491, 26.10475], [-98.35126, 26.15129], [-99.00546, 26.3925], [-99.03053, 26.41249], [-99.08477, 26.39849], [-99.53573, 27.30926], [-99.49744, 27.43746], [-99.482, 27.47128], [-99.48045, 27.49016], [-99.50208, 27.50021], [-99.52955, 27.49747], [-99.51478, 27.55836], [-99.55409, 27.61314], [-100.50029, 28.66117], [-100.51222, 28.70679], [-100.5075, 28.74066], [-100.52313, 28.75598], [-100.59809, 28.88197], [-100.63689, 28.90812], [-100.67294, 29.09744], [-100.79696, 29.24688], [-100.87982, 29.296], [-100.94056, 29.33371], [-100.94579, 29.34523], [-100.96725, 29.3477], [-101.01128, 29.36947], [-101.05686, 29.44738], [-101.47277, 29.7744], [-102.60596, 29.8192], [-103.15787, 28.93865], [-104.37752, 29.54255], [-104.39363, 29.55396], [-104.3969, 29.57105], [-104.5171, 29.64671], [-104.77674, 30.4236], [-106.00363, 31.39181], [-106.09025, 31.40569], [-106.20346, 31.46305], [-106.23711, 31.51262], [-106.24612, 31.54193], [-106.28084, 31.56173], [-106.30305, 31.62154], [-106.33419, 31.66303], [-106.34864, 31.69663], [-106.3718, 31.71165], [-106.38003, 31.73151], [-106.41773, 31.75196], [-106.43419, 31.75478], [-106.45244, 31.76523], [-106.46726, 31.75998], [-106.47298, 31.75054], [-106.48815, 31.74769], [-106.50111, 31.75714], [-106.50962, 31.76155], [-106.51251, 31.76922], [-106.52266, 31.77509], [-106.529, 31.784], [-108.20899, 31.78534], [-108.20979, 31.33316], [-111.07523, 31.33232], [-114.82011, 32.49609], [-114.79524, 32.55731], [-114.81141, 32.55543], [-114.80584, 32.62028], [-114.76736, 32.64094], [-114.71871, 32.71894], [-115.88053, 32.63624], [-117.1243, 32.53427]]]]
25481           }
25482         }, {
25483           type: "Feature",
25484           properties: {
25485             iso1A2: "MY",
25486             iso1A3: "MYS",
25487             iso1N3: "458",
25488             wikidata: "Q833",
25489             nameEn: "Malaysia"
25490           },
25491           geometry: null
25492         }, {
25493           type: "Feature",
25494           properties: {
25495             iso1A2: "MZ",
25496             iso1A3: "MOZ",
25497             iso1N3: "508",
25498             wikidata: "Q1029",
25499             nameEn: "Mozambique",
25500             groups: ["014", "202", "002", "UN"],
25501             driveSide: "left",
25502             callingCodes: ["258"]
25503           },
25504           geometry: {
25505             type: "MultiPolygon",
25506             coordinates: [[[[40.74206, -10.25691], [40.44265, -10.4618], [40.00295, -10.80255], [39.58249, -10.96043], [39.24395, -11.17433], [38.88996, -11.16978], [38.47258, -11.4199], [38.21598, -11.27289], [37.93618, -11.26228], [37.8388, -11.3123], [37.76614, -11.53352], [37.3936, -11.68949], [36.80309, -11.56836], [36.62068, -11.72884], [36.19094, -11.70008], [36.19094, -11.57593], [35.82767, -11.41081], [35.63599, -11.55927], [34.96296, -11.57354], [34.64241, -11.57499], [34.57917, -11.87849], [34.82903, -12.04837], [34.70739, -12.15652], [34.46088, -12.0174], [34.37831, -12.17408], [34.60253, -13.48487], [34.86229, -13.48958], [35.47989, -14.15594], [35.5299, -14.27714], [35.86945, -14.67481], [35.87212, -14.89478], [35.91812, -14.89514], [35.78799, -15.17428], [35.85303, -15.41913], [35.80487, -16.03907], [35.70107, -16.10147], [35.52365, -16.15414], [35.43355, -16.11371], [35.30157, -16.2211], [35.25828, -16.4792], [35.14235, -16.56812], [35.27219, -16.69402], [35.30929, -16.82871], [35.27065, -16.93817], [35.3062, -17.1244], [35.0923, -17.13235], [35.04805, -17.00027], [35.17017, -16.93521], [35.13771, -16.81687], [35.04805, -16.83167], [34.40344, -16.20923], [34.43126, -16.04737], [34.25195, -15.90321], [34.44981, -15.60864], [34.43126, -15.44778], [34.57503, -15.30619], [34.61522, -14.99583], [34.567, -14.77345], [34.54503, -14.74672], [34.52057, -14.68263], [34.53516, -14.67782], [34.55112, -14.64494], [34.53962, -14.59776], [34.52366, -14.5667], [34.49636, -14.55091], [34.48932, -14.53646], [34.47628, -14.53363], [34.45053, -14.49873], [34.44641, -14.47746], [34.4192, -14.43191], [34.39277, -14.39467], [34.35843, -14.38652], [34.34453, -14.3985], [34.22355, -14.43607], [34.18733, -14.43823], [34.08588, -14.48893], [33.92898, -14.47929], [33.88503, -14.51652], [33.7247, -14.4989], [33.66677, -14.61306], [33.24249, -14.00019], [30.22098, -14.99447], [30.41902, -15.62269], [30.42568, -15.9962], [30.91597, -15.99924], [30.97761, -16.05848], [31.13171, -15.98019], [31.30563, -16.01193], [31.42451, -16.15154], [31.67988, -16.19595], [31.90223, -16.34388], [31.91324, -16.41569], [32.02772, -16.43892], [32.28529, -16.43892], [32.42838, -16.4727], [32.71017, -16.59932], [32.69917, -16.66893], [32.78943, -16.70267], [32.97655, -16.70689], [32.91051, -16.89446], [32.84113, -16.92259], [32.96554, -17.11971], [33.00517, -17.30477], [33.0426, -17.3468], [32.96554, -17.48964], [32.98536, -17.55891], [33.0492, -17.60298], [32.94133, -17.99705], [33.03159, -18.35054], [33.02278, -18.4696], [32.88629, -18.51344], [32.88629, -18.58023], [32.95013, -18.69079], [32.9017, -18.7992], [32.82465, -18.77419], [32.70137, -18.84712], [32.73439, -18.92628], [32.69917, -18.94293], [32.72118, -19.02204], [32.84006, -19.0262], [32.87088, -19.09279], [32.85107, -19.29238], [32.77966, -19.36098], [32.78282, -19.47513], [32.84446, -19.48343], [32.84666, -19.68462], [32.95013, -19.67219], [33.06461, -19.77787], [33.01178, -20.02007], [32.93032, -20.03868], [32.85987, -20.16686], [32.85987, -20.27841], [32.66174, -20.56106], [32.55167, -20.56312], [32.48122, -20.63319], [32.51644, -20.91929], [32.37115, -21.133], [32.48236, -21.32873], [32.41234, -21.31246], [31.38336, -22.36919], [31.30611, -22.422], [31.55779, -23.176], [31.56539, -23.47268], [31.67942, -23.60858], [31.70223, -23.72695], [31.77445, -23.90082], [31.87707, -23.95293], [31.90368, -24.18892], [31.9835, -24.29983], [32.03196, -25.10785], [32.01676, -25.38117], [31.97875, -25.46356], [32.00631, -25.65044], [31.92649, -25.84216], [31.974, -25.95387], [32.00916, -25.999], [32.08599, -26.00978], [32.10435, -26.15656], [32.07352, -26.40185], [32.13409, -26.5317], [32.13315, -26.84345], [32.19409, -26.84032], [32.22302, -26.84136], [32.29584, -26.852], [32.35222, -26.86027], [34.51034, -26.91792], [42.99868, -12.65261], [40.74206, -10.25691]]]]
25507           }
25508         }, {
25509           type: "Feature",
25510           properties: {
25511             iso1A2: "NA",
25512             iso1A3: "NAM",
25513             iso1N3: "516",
25514             wikidata: "Q1030",
25515             nameEn: "Namibia",
25516             groups: ["018", "202", "002", "UN"],
25517             driveSide: "left",
25518             callingCodes: ["264"]
25519           },
25520           geometry: {
25521             type: "MultiPolygon",
25522             coordinates: [[[[14.28743, -17.38814], [13.95896, -17.43141], [13.36212, -16.98048], [12.97145, -16.98567], [12.52111, -17.24495], [12.07076, -17.15165], [11.75063, -17.25013], [10.5065, -17.25284], [12.51595, -32.27486], [16.45332, -28.63117], [16.46592, -28.57126], [16.59922, -28.53246], [16.90446, -28.057], [17.15405, -28.08573], [17.4579, -28.68718], [18.99885, -28.89165], [19.99882, -28.42622], [19.99817, -24.76768], [19.99912, -21.99991], [20.99751, -22.00026], [20.99904, -18.31743], [21.45556, -18.31795], [23.0996, -18.00075], [23.29618, -17.99855], [23.61088, -18.4881], [24.19416, -18.01919], [24.40577, -17.95726], [24.57485, -18.07151], [24.6303, -17.9863], [24.71887, -17.9218], [24.73364, -17.89338], [24.95586, -17.79674], [25.05895, -17.84452], [25.16882, -17.78253], [25.26433, -17.79571], [25.00198, -17.58221], [24.70864, -17.49501], [24.5621, -17.52963], [24.38712, -17.46818], [24.32811, -17.49082], [24.23619, -17.47489], [23.47474, -17.62877], [21.42741, -18.02787], [21.14283, -17.94318], [18.84226, -17.80375], [18.39229, -17.38927], [14.28743, -17.38814]]]]
25523           }
25524         }, {
25525           type: "Feature",
25526           properties: {
25527             iso1A2: "NC",
25528             iso1A3: "NCL",
25529             iso1N3: "540",
25530             wikidata: "Q33788",
25531             nameEn: "New Caledonia",
25532             country: "FR",
25533             groups: ["Q1451600", "054", "009", "UN"],
25534             callingCodes: ["687"]
25535           },
25536           geometry: {
25537             type: "MultiPolygon",
25538             coordinates: [[[[159.77159, -28.41151], [174.245, -23.1974], [156.73836, -14.50464], [159.77159, -28.41151]]]]
25539           }
25540         }, {
25541           type: "Feature",
25542           properties: {
25543             iso1A2: "NE",
25544             iso1A3: "NER",
25545             iso1N3: "562",
25546             wikidata: "Q1032",
25547             nameEn: "Niger",
25548             aliases: ["RN"],
25549             groups: ["011", "202", "002", "UN"],
25550             callingCodes: ["227"]
25551           },
25552           geometry: {
25553             type: "MultiPolygon",
25554             coordinates: [[[[14.22918, 22.61719], [13.5631, 23.16574], [11.96886, 23.51735], [7.48273, 20.87258], [7.38361, 20.79165], [5.8153, 19.45101], [4.26651, 19.14224], [4.26762, 17.00432], [4.21787, 17.00118], [4.19893, 16.39923], [3.50368, 15.35934], [3.03134, 15.42221], [3.01806, 15.34571], [1.31275, 15.27978], [0.96711, 14.98275], [0.72632, 14.95898], [0.23859, 15.00135], [0.16936, 14.51654], [0.38051, 14.05575], [0.61924, 13.68491], [0.77377, 13.6866], [0.77637, 13.64442], [0.99514, 13.5668], [1.02813, 13.46635], [1.20088, 13.38951], [1.24429, 13.39373], [1.28509, 13.35488], [1.24516, 13.33968], [1.21217, 13.37853], [1.18873, 13.31771], [0.99253, 13.37515], [0.99167, 13.10727], [2.26349, 12.41915], [2.05785, 12.35539], [2.39723, 11.89473], [2.45824, 11.98672], [2.39657, 12.10952], [2.37783, 12.24804], [2.6593, 12.30631], [2.83978, 12.40585], [3.25352, 12.01467], [3.31613, 11.88495], [3.48187, 11.86092], [3.59375, 11.70269], [3.61075, 11.69181], [3.67988, 11.75429], [3.67122, 11.80865], [3.63063, 11.83042], [3.61955, 11.91847], [3.67775, 11.97599], [3.63136, 12.11826], [3.66364, 12.25884], [3.65111, 12.52223], [3.94339, 12.74979], [4.10006, 12.98862], [4.14367, 13.17189], [4.14186, 13.47586], [4.23456, 13.47725], [4.4668, 13.68286], [4.87425, 13.78], [4.9368, 13.7345], [5.07396, 13.75052], [5.21026, 13.73627], [5.27797, 13.75474], [5.35437, 13.83567], [5.52957, 13.8845], [6.15771, 13.64564], [6.27411, 13.67835], [6.43053, 13.6006], [6.69617, 13.34057], [6.94445, 12.99825], [7.0521, 13.00076], [7.12676, 13.02445], [7.22399, 13.1293], [7.39241, 13.09717], [7.81085, 13.34902], [8.07997, 13.30847], [8.25185, 13.20369], [8.41853, 13.06166], [8.49493, 13.07519], [8.60431, 13.01768], [8.64251, 12.93985], [8.97413, 12.83661], [9.65995, 12.80614], [10.00373, 13.18171], [10.19993, 13.27129], [10.46731, 13.28819], [10.66004, 13.36422], [11.4535, 13.37773], [11.88236, 13.2527], [12.04209, 13.14452], [12.16189, 13.10056], [12.19315, 13.12423], [12.47095, 13.06673], [12.58033, 13.27805], [12.6793, 13.29157], [12.87376, 13.48919], [13.05085, 13.53984], [13.19844, 13.52802], [13.33213, 13.71195], [13.6302, 13.71094], [13.47559, 14.40881], [13.48259, 14.46704], [13.68573, 14.55276], [13.67878, 14.64013], [13.809, 14.72915], [13.78991, 14.87519], [13.86301, 15.04043], [14.37425, 15.72591], [15.50373, 16.89649], [15.6032, 18.77402], [15.75098, 19.93002], [15.99632, 20.35364], [15.6721, 20.70069], [15.59841, 20.74039], [15.56004, 20.79488], [15.55382, 20.86507], [15.57248, 20.92138], [15.62515, 20.95395], [15.28332, 21.44557], [15.20213, 21.49365], [15.19692, 21.99339], [14.99751, 23.00539], [14.22918, 22.61719]]]]
25555           }
25556         }, {
25557           type: "Feature",
25558           properties: {
25559             iso1A2: "NF",
25560             iso1A3: "NFK",
25561             iso1N3: "574",
25562             wikidata: "Q31057",
25563             nameEn: "Norfolk Island",
25564             country: "AU",
25565             groups: ["053", "009", "UN"],
25566             driveSide: "left",
25567             callingCodes: ["672 3"]
25568           },
25569           geometry: {
25570             type: "MultiPolygon",
25571             coordinates: [[[[169.82316, -28.16667], [166.29505, -28.29175], [167.94076, -30.60745], [169.82316, -28.16667]]]]
25572           }
25573         }, {
25574           type: "Feature",
25575           properties: {
25576             iso1A2: "NG",
25577             iso1A3: "NGA",
25578             iso1N3: "566",
25579             wikidata: "Q1033",
25580             nameEn: "Nigeria",
25581             groups: ["011", "202", "002", "UN"],
25582             callingCodes: ["234"]
25583           },
25584           geometry: {
25585             type: "MultiPolygon",
25586             coordinates: [[[[6.15771, 13.64564], [5.52957, 13.8845], [5.35437, 13.83567], [5.27797, 13.75474], [5.21026, 13.73627], [5.07396, 13.75052], [4.9368, 13.7345], [4.87425, 13.78], [4.4668, 13.68286], [4.23456, 13.47725], [4.14186, 13.47586], [4.14367, 13.17189], [4.10006, 12.98862], [3.94339, 12.74979], [3.65111, 12.52223], [3.66364, 12.25884], [3.63136, 12.11826], [3.67775, 11.97599], [3.61955, 11.91847], [3.63063, 11.83042], [3.67122, 11.80865], [3.67988, 11.75429], [3.61075, 11.69181], [3.59375, 11.70269], [3.49175, 11.29765], [3.71505, 11.13015], [3.84243, 10.59316], [3.78292, 10.40538], [3.6844, 10.46351], [3.57275, 10.27185], [3.66908, 10.18136], [3.54429, 9.87739], [3.35383, 9.83641], [3.32099, 9.78032], [3.34726, 9.70696], [3.25093, 9.61632], [3.13928, 9.47167], [3.14147, 9.28375], [3.08017, 9.10006], [2.77907, 9.06924], [2.67523, 7.87825], [2.73095, 7.7755], [2.73405, 7.5423], [2.78668, 7.5116], [2.79442, 7.43486], [2.74489, 7.42565], [2.76965, 7.13543], [2.71702, 6.95722], [2.74024, 6.92802], [2.73405, 6.78508], [2.78823, 6.76356], [2.78204, 6.70514], [2.7325, 6.64057], [2.74334, 6.57291], [2.70464, 6.50831], [2.70566, 6.38038], [2.74181, 6.13349], [5.87055, 3.78489], [8.34397, 4.30689], [8.60302, 4.87353], [8.78027, 5.1243], [8.92029, 5.58403], [8.83687, 5.68483], [8.88156, 5.78857], [8.84209, 5.82562], [9.51757, 6.43874], [9.70674, 6.51717], [9.77824, 6.79088], [9.86314, 6.77756], [10.15135, 7.03781], [10.21466, 6.88996], [10.53639, 6.93432], [10.57214, 7.16345], [10.59746, 7.14719], [10.60789, 7.06885], [10.83727, 6.9358], [10.8179, 6.83377], [10.94302, 6.69325], [11.09644, 6.68437], [11.09495, 6.51717], [11.42041, 6.53789], [11.42264, 6.5882], [11.51499, 6.60892], [11.57755, 6.74059], [11.55818, 6.86186], [11.63117, 6.9905], [11.87396, 7.09398], [11.84864, 7.26098], [11.93205, 7.47812], [12.01844, 7.52981], [11.99908, 7.67302], [12.20909, 7.97553], [12.19271, 8.10826], [12.24782, 8.17904], [12.26123, 8.43696], [12.4489, 8.52536], [12.44146, 8.6152], [12.68722, 8.65938], [12.71701, 8.7595], [12.79, 8.75361], [12.81085, 8.91992], [12.90022, 9.11411], [12.91958, 9.33905], [12.85628, 9.36698], [13.02385, 9.49334], [13.22642, 9.57266], [13.25472, 9.76795], [13.29941, 9.8296], [13.25025, 9.86042], [13.24132, 9.91031], [13.27409, 9.93232], [13.286, 9.9822], [13.25323, 10.00127], [13.25025, 10.03647], [13.34111, 10.12299], [13.43644, 10.13326], [13.5705, 10.53183], [13.54964, 10.61236], [13.73434, 10.9255], [13.70753, 10.94451], [13.7403, 11.00593], [13.78945, 11.00154], [13.97489, 11.30258], [14.17821, 11.23831], [14.6124, 11.51283], [14.64591, 11.66166], [14.55207, 11.72001], [14.61612, 11.7798], [14.6474, 12.17466], [14.4843, 12.35223], [14.22215, 12.36533], [14.17523, 12.41916], [14.20204, 12.53405], [14.08251, 13.0797], [13.6302, 13.71094], [13.33213, 13.71195], [13.19844, 13.52802], [13.05085, 13.53984], [12.87376, 13.48919], [12.6793, 13.29157], [12.58033, 13.27805], [12.47095, 13.06673], [12.19315, 13.12423], [12.16189, 13.10056], [12.04209, 13.14452], [11.88236, 13.2527], [11.4535, 13.37773], [10.66004, 13.36422], [10.46731, 13.28819], [10.19993, 13.27129], [10.00373, 13.18171], [9.65995, 12.80614], [8.97413, 12.83661], [8.64251, 12.93985], [8.60431, 13.01768], [8.49493, 13.07519], [8.41853, 13.06166], [8.25185, 13.20369], [8.07997, 13.30847], [7.81085, 13.34902], [7.39241, 13.09717], [7.22399, 13.1293], [7.12676, 13.02445], [7.0521, 13.00076], [6.94445, 12.99825], [6.69617, 13.34057], [6.43053, 13.6006], [6.27411, 13.67835], [6.15771, 13.64564]]]]
25587           }
25588         }, {
25589           type: "Feature",
25590           properties: {
25591             iso1A2: "NI",
25592             iso1A3: "NIC",
25593             iso1N3: "558",
25594             wikidata: "Q811",
25595             nameEn: "Nicaragua",
25596             groups: ["013", "003", "419", "019", "UN"],
25597             callingCodes: ["505"]
25598           },
25599           geometry: {
25600             type: "MultiPolygon",
25601             coordinates: [[[[-83.13724, 15.00002], [-83.49268, 15.01158], [-83.62101, 14.89448], [-83.89551, 14.76697], [-84.10584, 14.76353], [-84.48373, 14.63249], [-84.70119, 14.68078], [-84.82596, 14.82212], [-84.90082, 14.80489], [-85.1575, 14.53934], [-85.18602, 14.24929], [-85.32149, 14.2562], [-85.45762, 14.11304], [-85.73964, 13.9698], [-85.75477, 13.8499], [-86.03458, 13.99181], [-86.00685, 14.08474], [-86.14801, 14.04317], [-86.35219, 13.77157], [-86.76812, 13.79605], [-86.71267, 13.30348], [-86.87066, 13.30641], [-86.93383, 13.18677], [-86.93197, 13.05313], [-87.03785, 12.98682], [-87.06306, 13.00892], [-87.37107, 12.98646], [-87.55124, 13.12523], [-87.7346, 13.13228], [-88.11443, 12.63306], [-86.14524, 11.09059], [-85.71223, 11.06868], [-85.60529, 11.22607], [-84.92439, 10.9497], [-84.68197, 11.07568], [-83.90838, 10.71161], [-83.66597, 10.79916], [-83.68276, 11.01562], [-82.56142, 11.91792], [-82.06974, 14.49418], [-83.04763, 15.03256], [-83.13724, 15.00002]]]]
25602           }
25603         }, {
25604           type: "Feature",
25605           properties: {
25606             iso1A2: "NL",
25607             iso1A3: "NLD",
25608             iso1N3: "528",
25609             wikidata: "Q29999",
25610             nameEn: "Kingdom of the Netherlands"
25611           },
25612           geometry: null
25613         }, {
25614           type: "Feature",
25615           properties: {
25616             iso1A2: "NO",
25617             iso1A3: "NOR",
25618             iso1N3: "578",
25619             wikidata: "Q20",
25620             nameEn: "Norway"
25621           },
25622           geometry: null
25623         }, {
25624           type: "Feature",
25625           properties: {
25626             iso1A2: "NP",
25627             iso1A3: "NPL",
25628             iso1N3: "524",
25629             wikidata: "Q837",
25630             nameEn: "Nepal",
25631             groups: ["034", "142", "UN"],
25632             driveSide: "left",
25633             callingCodes: ["977"]
25634           },
25635           geometry: {
25636             type: "MultiPolygon",
25637             coordinates: [[[[88.13378, 27.88015], [87.82681, 27.95248], [87.72718, 27.80938], [87.56996, 27.84517], [87.11696, 27.84104], [87.03757, 27.94835], [86.75582, 28.04182], [86.74181, 28.10638], [86.56265, 28.09569], [86.51609, 27.96623], [86.42736, 27.91122], [86.22966, 27.9786], [86.18607, 28.17364], [86.088, 28.09264], [86.08333, 28.02121], [86.12069, 27.93047], [86.06309, 27.90021], [85.94946, 27.9401], [85.97813, 27.99023], [85.90743, 28.05144], [85.84672, 28.18187], [85.74864, 28.23126], [85.71907, 28.38064], [85.69105, 28.38475], [85.60854, 28.25045], [85.59765, 28.30529], [85.4233, 28.32996], [85.38127, 28.28336], [85.10729, 28.34092], [85.18668, 28.54076], [85.19135, 28.62825], [85.06059, 28.68562], [84.85511, 28.58041], [84.62317, 28.73887], [84.47528, 28.74023], [84.2231, 28.89571], [84.24801, 29.02783], [84.18107, 29.23451], [83.97559, 29.33091], [83.82303, 29.30513], [83.63156, 29.16249], [83.44787, 29.30513], [83.28131, 29.56813], [83.07116, 29.61957], [82.73024, 29.81695], [82.5341, 29.9735], [82.38622, 30.02608], [82.16984, 30.0692], [82.19475, 30.16884], [82.10757, 30.23745], [82.10135, 30.35439], [81.99082, 30.33423], [81.62033, 30.44703], [81.5459, 30.37688], [81.41018, 30.42153], [81.39928, 30.21862], [81.33355, 30.15303], [81.2623, 30.14596], [81.29032, 30.08806], [81.24362, 30.0126], [81.12842, 30.01395], [81.03953, 30.20059], [80.92547, 30.17193], [80.91143, 30.22173], [80.86673, 30.17321], [80.8778, 30.13384], [80.67076, 29.95732], [80.60226, 29.95732], [80.57179, 29.91422], [80.56247, 29.86661], [80.48997, 29.79566], [80.43458, 29.80466], [80.41554, 29.79451], [80.36803, 29.73865], [80.38428, 29.68513], [80.41858, 29.63581], [80.37939, 29.57098], [80.24322, 29.44299], [80.31428, 29.30784], [80.28626, 29.20327], [80.24112, 29.21414], [80.26602, 29.13938], [80.23178, 29.11626], [80.18085, 29.13649], [80.05743, 28.91479], [80.06957, 28.82763], [80.12125, 28.82346], [80.37188, 28.63371], [80.44504, 28.63098], [80.52443, 28.54897], [80.50575, 28.6706], [80.55142, 28.69182], [81.03471, 28.40054], [81.19847, 28.36284], [81.32923, 28.13521], [81.38683, 28.17638], [81.48179, 28.12148], [81.47867, 28.08303], [81.91223, 27.84995], [81.97214, 27.93322], [82.06554, 27.92222], [82.46405, 27.6716], [82.70378, 27.72122], [82.74119, 27.49838], [82.93261, 27.50328], [82.94938, 27.46036], [83.19413, 27.45632], [83.27197, 27.38309], [83.2673, 27.36235], [83.29999, 27.32778], [83.35136, 27.33885], [83.38872, 27.39276], [83.39495, 27.4798], [83.61288, 27.47013], [83.85595, 27.35797], [83.86182, 27.4241], [83.93306, 27.44939], [84.02229, 27.43836], [84.10791, 27.52399], [84.21376, 27.45218], [84.25735, 27.44941], [84.29315, 27.39], [84.62161, 27.33885], [84.69166, 27.21294], [84.64496, 27.04669], [84.793, 26.9968], [84.82913, 27.01989], [84.85754, 26.98984], [84.96687, 26.95599], [84.97186, 26.9149], [85.00536, 26.89523], [85.05592, 26.88991], [85.02635, 26.85381], [85.15883, 26.86966], [85.19291, 26.86909], [85.18046, 26.80519], [85.21159, 26.75933], [85.34302, 26.74954], [85.47752, 26.79292], [85.56471, 26.84133], [85.5757, 26.85955], [85.59461, 26.85161], [85.61621, 26.86721], [85.66239, 26.84822], [85.73483, 26.79613], [85.72315, 26.67471], [85.76907, 26.63076], [85.83126, 26.61134], [85.85126, 26.60866], [85.8492, 26.56667], [86.02729, 26.66756], [86.13596, 26.60651], [86.22513, 26.58863], [86.26235, 26.61886], [86.31564, 26.61925], [86.49726, 26.54218], [86.54258, 26.53819], [86.57073, 26.49825], [86.61313, 26.48658], [86.62686, 26.46891], [86.69124, 26.45169], [86.74025, 26.42386], [86.76797, 26.45892], [86.82898, 26.43919], [86.94543, 26.52076], [86.95912, 26.52076], [87.01559, 26.53228], [87.04691, 26.58685], [87.0707, 26.58571], [87.09147, 26.45039], [87.14751, 26.40542], [87.18863, 26.40558], [87.24682, 26.4143], [87.26587, 26.40592], [87.26568, 26.37294], [87.34568, 26.34787], [87.37314, 26.40815], [87.46566, 26.44058], [87.51571, 26.43106], [87.55274, 26.40596], [87.59175, 26.38342], [87.66803, 26.40294], [87.67893, 26.43501], [87.76004, 26.40711], [87.7918, 26.46737], [87.84193, 26.43663], [87.89085, 26.48565], [87.90115, 26.44923], [88.00895, 26.36029], [88.09414, 26.43732], [88.09963, 26.54195], [88.16452, 26.64111], [88.1659, 26.68177], [88.19107, 26.75516], [88.12302, 26.95324], [88.13422, 26.98705], [88.11719, 26.98758], [87.9887, 27.11045], [88.01587, 27.21388], [88.01646, 27.21612], [88.07277, 27.43007], [88.04008, 27.49223], [88.19107, 27.79285], [88.1973, 27.85067], [88.13378, 27.88015]]]]
25638           }
25639         }, {
25640           type: "Feature",
25641           properties: {
25642             iso1A2: "NR",
25643             iso1A3: "NRU",
25644             iso1N3: "520",
25645             wikidata: "Q697",
25646             nameEn: "Nauru",
25647             groups: ["057", "009", "UN"],
25648             driveSide: "left",
25649             callingCodes: ["674"]
25650           },
25651           geometry: {
25652             type: "MultiPolygon",
25653             coordinates: [[[[166.95155, 0.14829], [166.21778, -0.7977], [167.60042, -0.88259], [166.95155, 0.14829]]]]
25654           }
25655         }, {
25656           type: "Feature",
25657           properties: {
25658             iso1A2: "NU",
25659             iso1A3: "NIU",
25660             iso1N3: "570",
25661             wikidata: "Q34020",
25662             nameEn: "Niue",
25663             country: "NZ",
25664             groups: ["061", "009", "UN"],
25665             driveSide: "left",
25666             callingCodes: ["683"]
25667           },
25668           geometry: {
25669             type: "MultiPolygon",
25670             coordinates: [[[[-170.83899, -18.53439], [-170.82274, -20.44429], [-168.63096, -18.60489], [-170.83899, -18.53439]]]]
25671           }
25672         }, {
25673           type: "Feature",
25674           properties: {
25675             iso1A2: "NZ",
25676             iso1A3: "NZL",
25677             iso1N3: "554",
25678             wikidata: "Q664",
25679             nameEn: "New Zealand"
25680           },
25681           geometry: null
25682         }, {
25683           type: "Feature",
25684           properties: {
25685             iso1A2: "OM",
25686             iso1A3: "OMN",
25687             iso1N3: "512",
25688             wikidata: "Q842",
25689             nameEn: "Oman",
25690             groups: ["145", "142", "UN"],
25691             callingCodes: ["968"]
25692           },
25693           geometry: {
25694             type: "MultiPolygon",
25695             coordinates: [[[[56.82555, 25.7713], [56.79239, 26.41236], [56.68954, 26.76645], [56.2644, 26.58649], [55.81777, 26.18798], [56.08666, 26.05038], [56.15498, 26.06828], [56.19334, 25.9795], [56.13963, 25.82765], [56.17416, 25.77239], [56.13579, 25.73524], [56.14826, 25.66351], [56.18363, 25.65508], [56.20473, 25.61119], [56.25365, 25.60211], [56.26636, 25.60643], [56.25341, 25.61443], [56.26534, 25.62825], [56.82555, 25.7713]]], [[[56.26062, 25.33108], [56.23362, 25.31253], [56.25008, 25.28843], [56.24465, 25.27505], [56.20838, 25.25668], [56.20872, 25.24104], [56.24341, 25.22867], [56.27628, 25.23404], [56.34438, 25.26653], [56.35172, 25.30681], [56.3111, 25.30107], [56.3005, 25.31815], [56.26062, 25.33108]], [[56.28423, 25.26344], [56.27086, 25.26128], [56.2716, 25.27916], [56.28102, 25.28486], [56.29379, 25.2754], [56.28423, 25.26344]]], [[[61.45114, 22.55394], [56.86325, 25.03856], [56.3227, 24.97284], [56.34873, 24.93205], [56.30269, 24.88334], [56.20568, 24.85063], [56.20062, 24.78565], [56.13684, 24.73699], [56.06128, 24.74457], [56.03535, 24.81161], [55.97836, 24.87673], [55.97467, 24.89639], [56.05106, 24.87461], [56.05715, 24.95727], [55.96316, 25.00857], [55.90849, 24.96771], [55.85094, 24.96858], [55.81116, 24.9116], [55.81348, 24.80102], [55.83408, 24.77858], [55.83271, 24.68567], [55.76461, 24.5287], [55.83271, 24.41521], [55.83395, 24.32776], [55.80747, 24.31069], [55.79145, 24.27914], [55.76781, 24.26209], [55.75939, 24.26114], [55.75382, 24.2466], [55.75257, 24.23466], [55.76558, 24.23227], [55.77658, 24.23476], [55.83367, 24.20193], [55.95472, 24.2172], [56.01799, 24.07426], [55.8308, 24.01633], [55.73301, 24.05994], [55.48677, 23.94946], [55.57358, 23.669], [55.22634, 23.10378], [55.2137, 22.71065], [55.66469, 21.99658], [54.99756, 20.00083], [52.00311, 19.00083], [52.78009, 17.35124], [52.74267, 17.29519], [52.81185, 17.28568], [57.49095, 8.14549], [61.45114, 22.55394]]]]
25696           }
25697         }, {
25698           type: "Feature",
25699           properties: {
25700             iso1A2: "PA",
25701             iso1A3: "PAN",
25702             iso1N3: "591",
25703             wikidata: "Q804",
25704             nameEn: "Panama",
25705             groups: ["013", "003", "419", "019", "UN"],
25706             callingCodes: ["507"]
25707           },
25708           geometry: {
25709             type: "MultiPolygon",
25710             coordinates: [[[[-77.32389, 8.81247], [-77.58292, 9.22278], [-78.79327, 9.93766], [-82.51044, 9.65379], [-82.56507, 9.57279], [-82.61345, 9.49881], [-82.66667, 9.49746], [-82.77206, 9.59573], [-82.87919, 9.62645], [-82.84871, 9.4973], [-82.93516, 9.46741], [-82.93516, 9.07687], [-82.72126, 8.97125], [-82.88253, 8.83331], [-82.91377, 8.774], [-82.92068, 8.74832], [-82.8794, 8.6981], [-82.82739, 8.60153], [-82.83975, 8.54755], [-82.83322, 8.52464], [-82.8382, 8.48117], [-82.8679, 8.44042], [-82.93056, 8.43465], [-83.05209, 8.33394], [-82.9388, 8.26634], [-82.88641, 8.10219], [-82.89137, 8.05755], [-82.89978, 8.04083], [-82.94503, 7.93865], [-82.13751, 6.97312], [-78.06168, 7.07793], [-77.89178, 7.22681], [-77.81426, 7.48319], [-77.72157, 7.47612], [-77.72514, 7.72348], [-77.57185, 7.51147], [-77.17257, 7.97422], [-77.45064, 8.49991], [-77.32389, 8.81247]]]]
25711           }
25712         }, {
25713           type: "Feature",
25714           properties: {
25715             iso1A2: "PE",
25716             iso1A3: "PER",
25717             iso1N3: "604",
25718             wikidata: "Q419",
25719             nameEn: "Peru",
25720             groups: ["005", "419", "019", "UN"],
25721             callingCodes: ["51"]
25722           },
25723           geometry: {
25724             type: "MultiPolygon",
25725             coordinates: [[[[-74.26675, -0.97229], [-74.42701, -0.50218], [-75.18513, -0.0308], [-75.25764, -0.11943], [-75.40192, -0.17196], [-75.61997, -0.10012], [-75.60169, -0.18708], [-75.53615, -0.19213], [-75.22862, -0.60048], [-75.22862, -0.95588], [-75.3872, -0.9374], [-75.57429, -1.55961], [-76.05203, -2.12179], [-76.6324, -2.58397], [-77.94147, -3.05454], [-78.19369, -3.36431], [-78.14324, -3.47653], [-78.22642, -3.51113], [-78.24589, -3.39907], [-78.34362, -3.38633], [-78.68394, -4.60754], [-78.85149, -4.66795], [-79.01659, -5.01481], [-79.1162, -4.97774], [-79.26248, -4.95167], [-79.59402, -4.46848], [-79.79722, -4.47558], [-80.13945, -4.29786], [-80.39256, -4.48269], [-80.46386, -4.41516], [-80.32114, -4.21323], [-80.45023, -4.20938], [-80.4822, -4.05477], [-80.46386, -4.01342], [-80.13232, -3.90317], [-80.19926, -3.68894], [-80.18741, -3.63994], [-80.19848, -3.59249], [-80.21642, -3.5888], [-80.20535, -3.51667], [-80.22629, -3.501], [-80.23651, -3.48652], [-80.24586, -3.48677], [-80.24123, -3.46124], [-80.20647, -3.431], [-80.30602, -3.39149], [-84.52388, -3.36941], [-85.71054, -21.15413], [-70.59118, -18.35072], [-70.378, -18.3495], [-70.31267, -18.31258], [-70.16394, -18.31737], [-69.96732, -18.25992], [-69.81607, -18.12582], [-69.75305, -17.94605], [-69.82868, -17.72048], [-69.79087, -17.65563], [-69.66483, -17.65083], [-69.46897, -17.4988], [-69.46863, -17.37466], [-69.62883, -17.28142], [-69.16896, -16.72233], [-69.00853, -16.66769], [-69.04027, -16.57214], [-68.98358, -16.42165], [-68.79464, -16.33272], [-68.96238, -16.194], [-69.09986, -16.22693], [-69.20291, -16.16668], [-69.40336, -15.61358], [-69.14856, -15.23478], [-69.36254, -14.94634], [-68.88135, -14.18639], [-69.05265, -13.68546], [-68.8864, -13.40792], [-68.85615, -12.87769], [-68.65044, -12.50689], [-68.98115, -11.8979], [-69.57156, -10.94555], [-69.57835, -10.94051], [-69.90896, -10.92744], [-70.38791, -11.07096], [-70.51395, -10.92249], [-70.64134, -11.0108], [-70.62487, -9.80666], [-70.55429, -9.76692], [-70.58453, -9.58303], [-70.53373, -9.42628], [-71.23394, -9.9668], [-72.14742, -9.98049], [-72.31883, -9.5184], [-72.72216, -9.41397], [-73.21498, -9.40904], [-72.92886, -9.04074], [-73.76576, -7.89884], [-73.65485, -7.77897], [-73.96938, -7.58465], [-73.77011, -7.28944], [-73.73986, -6.87919], [-73.12983, -6.43852], [-73.24579, -6.05764], [-72.83973, -5.14765], [-72.64391, -5.0391], [-71.87003, -4.51661], [-70.96814, -4.36915], [-70.77601, -4.15717], [-70.33236, -4.15214], [-70.19582, -4.3607], [-70.11305, -4.27281], [-70.00888, -4.37833], [-69.94708, -4.2431], [-70.3374, -3.79505], [-70.52393, -3.87553], [-70.71396, -3.7921], [-70.04609, -2.73906], [-70.94377, -2.23142], [-71.75223, -2.15058], [-72.92587, -2.44514], [-73.65312, -1.26222], [-74.26675, -0.97229]]]]
25726           }
25727         }, {
25728           type: "Feature",
25729           properties: {
25730             iso1A2: "PF",
25731             iso1A3: "PYF",
25732             iso1N3: "258",
25733             wikidata: "Q30971",
25734             nameEn: "French Polynesia",
25735             country: "FR",
25736             groups: ["Q1451600", "061", "009", "UN"],
25737             callingCodes: ["689"]
25738           },
25739           geometry: {
25740             type: "MultiPolygon",
25741             coordinates: [[[[-135.59706, -4.70473], [-156.48634, -15.52824], [-156.45576, -31.75456], [-133.59543, -28.4709], [-135.59706, -4.70473]]]]
25742           }
25743         }, {
25744           type: "Feature",
25745           properties: {
25746             iso1A2: "PG",
25747             iso1A3: "PNG",
25748             iso1N3: "598",
25749             wikidata: "Q691",
25750             nameEn: "Papua New Guinea",
25751             groups: ["054", "009", "UN"],
25752             driveSide: "left",
25753             callingCodes: ["675"]
25754           },
25755           geometry: {
25756             type: "MultiPolygon",
25757             coordinates: [[[[141.03157, 2.12829], [140.99813, -6.3233], [140.85295, -6.72996], [140.90448, -6.85033], [141.01763, -6.90181], [141.01842, -9.35091], [141.88934, -9.36111], [142.19246, -9.15378], [142.48658, -9.36754], [143.29772, -9.33993], [143.87386, -9.02382], [145.2855, -9.62524], [156.73836, -14.50464], [154.74815, -7.33315], [155.60735, -6.92266], [155.69784, -6.92661], [155.92557, -6.84664], [156.03993, -6.65703], [156.03296, -6.55528], [160.43769, -4.17974], [141.03157, 2.12829]]]]
25758           }
25759         }, {
25760           type: "Feature",
25761           properties: {
25762             iso1A2: "PH",
25763             iso1A3: "PHL",
25764             iso1N3: "608",
25765             wikidata: "Q928",
25766             nameEn: "Philippines",
25767             aliases: ["PI", "RP"],
25768             groups: ["035", "142", "UN"],
25769             callingCodes: ["63"]
25770           },
25771           geometry: {
25772             type: "MultiPolygon",
25773             coordinates: [[[[129.19694, 7.84182], [121.8109, 21.77688], [120.69238, 21.52331], [118.82252, 14.67191], [115.39742, 10.92666], [116.79524, 7.43869], [117.17735, 7.52841], [117.93857, 6.89845], [117.98544, 6.27477], [119.52945, 5.35672], [118.93936, 4.09009], [118.06469, 4.16638], [121.14448, 2.12444], [129.19694, 7.84182]]]]
25774           }
25775         }, {
25776           type: "Feature",
25777           properties: {
25778             iso1A2: "PK",
25779             iso1A3: "PAK",
25780             iso1N3: "586",
25781             wikidata: "Q843",
25782             nameEn: "Pakistan",
25783             groups: ["034", "142", "UN"],
25784             driveSide: "left",
25785             callingCodes: ["92"]
25786           },
25787           geometry: {
25788             type: "MultiPolygon",
25789             coordinates: [[[[75.72737, 36.7529], [75.45562, 36.71971], [75.40481, 36.95382], [75.13839, 37.02622], [74.56453, 37.03023], [74.53739, 36.96224], [74.43389, 37.00977], [74.04856, 36.82648], [73.82685, 36.91421], [72.6323, 36.84601], [72.18135, 36.71838], [71.80267, 36.49924], [71.60491, 36.39429], [71.19505, 36.04134], [71.37969, 35.95865], [71.55273, 35.71483], [71.49917, 35.6267], [71.65435, 35.4479], [71.54294, 35.31037], [71.5541, 35.28776], [71.67495, 35.21262], [71.52938, 35.09023], [71.55273, 35.02615], [71.49917, 35.00478], [71.50329, 34.97328], [71.29472, 34.87728], [71.28356, 34.80882], [71.08718, 34.69034], [71.11602, 34.63047], [71.0089, 34.54568], [71.02401, 34.44835], [71.17662, 34.36769], [71.12815, 34.26619], [71.13078, 34.16503], [71.09453, 34.13524], [71.09307, 34.11961], [71.06933, 34.10564], [71.07345, 34.06242], [70.88119, 33.97933], [70.54336, 33.9463], [69.90203, 34.04194], [69.87307, 33.9689], [69.85671, 33.93719], [70.00503, 33.73528], [70.14236, 33.71701], [70.14785, 33.6553], [70.20141, 33.64387], [70.17062, 33.53535], [70.32775, 33.34496], [70.13686, 33.21064], [70.07369, 33.22557], [70.02563, 33.14282], [69.85259, 33.09451], [69.79766, 33.13247], [69.71526, 33.09911], [69.57656, 33.09911], [69.49004, 33.01509], [69.49854, 32.88843], [69.5436, 32.8768], [69.47082, 32.85834], [69.38018, 32.76601], [69.43649, 32.7302], [69.44747, 32.6678], [69.38155, 32.56601], [69.2868, 32.53938], [69.23599, 32.45946], [69.27932, 32.29119], [69.27032, 32.14141], [69.3225, 31.93186], [69.20577, 31.85957], [69.11514, 31.70782], [69.00939, 31.62249], [68.95995, 31.64822], [68.91078, 31.59687], [68.79997, 31.61665], [68.6956, 31.75687], [68.57475, 31.83158], [68.44222, 31.76446], [68.27605, 31.75863], [68.25614, 31.80357], [68.1655, 31.82691], [68.00071, 31.6564], [67.86887, 31.63536], [67.72056, 31.52304], [67.58323, 31.52772], [67.62374, 31.40473], [67.7748, 31.4188], [67.78854, 31.33203], [67.29964, 31.19586], [67.03323, 31.24519], [67.04147, 31.31561], [66.83273, 31.26867], [66.72561, 31.20526], [66.68166, 31.07597], [66.58175, 30.97532], [66.42645, 30.95309], [66.39194, 30.9408], [66.28413, 30.57001], [66.34869, 30.404], [66.23609, 30.06321], [66.36042, 29.9583], [66.24175, 29.85181], [65.04005, 29.53957], [64.62116, 29.58903], [64.19796, 29.50407], [64.12966, 29.39157], [63.5876, 29.50456], [62.47751, 29.40782], [60.87231, 29.86514], [61.31508, 29.38903], [61.53765, 29.00507], [61.65978, 28.77937], [61.93581, 28.55284], [62.40259, 28.42703], [62.59499, 28.24842], [62.79412, 28.28108], [62.7638, 28.02992], [62.84905, 27.47627], [62.79684, 27.34381], [62.80604, 27.22412], [63.19649, 27.25674], [63.32283, 27.14437], [63.25005, 27.08692], [63.25005, 26.84212], [63.18688, 26.83844], [63.1889, 26.65072], [62.77352, 26.64099], [62.31484, 26.528], [62.21304, 26.26601], [62.05117, 26.31647], [61.89391, 26.26251], [61.83831, 26.07249], [61.83968, 25.7538], [61.683, 25.66638], [61.6433, 25.27541], [61.46682, 24.57869], [68.11329, 23.53945], [68.20763, 23.85849], [68.39339, 23.96838], [68.74643, 23.97027], [68.7416, 24.31904], [68.90914, 24.33156], [68.97781, 24.26021], [69.07806, 24.29777], [69.19341, 24.25646], [69.29778, 24.28712], [69.59579, 24.29777], [69.73335, 24.17007], [70.03428, 24.172], [70.11712, 24.30915], [70.5667, 24.43787], [70.57906, 24.27774], [70.71502, 24.23517], [70.88393, 24.27398], [70.85784, 24.30903], [70.94985, 24.3791], [71.04461, 24.34657], [71.12838, 24.42662], [71.00341, 24.46038], [70.97594, 24.60904], [71.09405, 24.69017], [70.94002, 24.92843], [70.89148, 25.15064], [70.66695, 25.39314], [70.67382, 25.68186], [70.60378, 25.71898], [70.53649, 25.68928], [70.37444, 25.67443], [70.2687, 25.71156], [70.0985, 25.93238], [70.08193, 26.08094], [70.17532, 26.24118], [70.17532, 26.55362], [70.05584, 26.60398], [69.88555, 26.56836], [69.50904, 26.74892], [69.58519, 27.18109], [70.03136, 27.56627], [70.12502, 27.8057], [70.37307, 28.01208], [70.60927, 28.02178], [70.79054, 27.68423], [71.89921, 27.96035], [71.9244, 28.11555], [72.20329, 28.3869], [72.29495, 28.66367], [72.40402, 28.78283], [72.94272, 29.02487], [73.01337, 29.16422], [73.05886, 29.1878], [73.28094, 29.56646], [73.3962, 29.94707], [73.58665, 30.01848], [73.80299, 30.06969], [73.97225, 30.19829], [73.95736, 30.28466], [73.88993, 30.36305], [74.5616, 31.04153], [74.67971, 31.05479], [74.6852, 31.12771], [74.60006, 31.13711], [74.60281, 31.10419], [74.56023, 31.08303], [74.51629, 31.13829], [74.53223, 31.30321], [74.59773, 31.4136], [74.64713, 31.45605], [74.59319, 31.50197], [74.61517, 31.55698], [74.57498, 31.60382], [74.47771, 31.72227], [74.58907, 31.87824], [74.79919, 31.95983], [74.86236, 32.04485], [74.9269, 32.0658], [75.00793, 32.03786], [75.25649, 32.10187], [75.38046, 32.26836], [75.28259, 32.36556], [75.03265, 32.49538], [74.97634, 32.45367], [74.84725, 32.49075], [74.68362, 32.49298], [74.67431, 32.56676], [74.65251, 32.56416], [74.64424, 32.60985], [74.69542, 32.66792], [74.65345, 32.71225], [74.7113, 32.84219], [74.64675, 32.82604], [74.6289, 32.75561], [74.45312, 32.77755], [74.41467, 32.90563], [74.31227, 32.92795], [74.34875, 32.97823], [74.31854, 33.02891], [74.17571, 33.07495], [74.15374, 33.13477], [74.02144, 33.18908], [74.01366, 33.25199], [74.08782, 33.26232], [74.17983, 33.3679], [74.18121, 33.4745], [74.10115, 33.56392], [74.03576, 33.56718], [73.97367, 33.64061], [73.98968, 33.66155], [73.96423, 33.73071], [74.00891, 33.75437], [74.05898, 33.82089], [74.14001, 33.83002], [74.26086, 33.92237], [74.25262, 34.01577], [74.21554, 34.03853], [73.91341, 34.01235], [73.88732, 34.05105], [73.90677, 34.10504], [73.98208, 34.2522], [73.90517, 34.35317], [73.8475, 34.32935], [73.74862, 34.34183], [73.74999, 34.3781], [73.88732, 34.48911], [73.89419, 34.54568], [73.93951, 34.57169], [73.93401, 34.63386], [73.96423, 34.68244], [74.12897, 34.70073], [74.31239, 34.79626], [74.58083, 34.77386], [74.6663, 34.703], [75.01479, 34.64629], [75.38009, 34.55021], [75.75438, 34.51827], [76.04614, 34.67566], [76.15463, 34.6429], [76.47186, 34.78965], [76.67648, 34.76371], [76.74377, 34.84039], [76.74514, 34.92488], [76.87193, 34.96906], [76.99251, 34.93349], [77.11796, 35.05419], [76.93465, 35.39866], [76.85088, 35.39754], [76.75475, 35.52617], [76.77323, 35.66062], [76.50961, 35.8908], [76.33453, 35.84296], [76.14913, 35.82848], [76.15325, 35.9264], [75.93028, 36.13136], [76.00906, 36.17511], [76.0324, 36.41198], [75.92391, 36.56986], [75.72737, 36.7529]]]]
25790           }
25791         }, {
25792           type: "Feature",
25793           properties: {
25794             iso1A2: "PL",
25795             iso1A3: "POL",
25796             iso1N3: "616",
25797             wikidata: "Q36",
25798             nameEn: "Poland",
25799             groups: ["EU", "151", "150", "UN"],
25800             callingCodes: ["48"]
25801           },
25802           geometry: {
25803             type: "MultiPolygon",
25804             coordinates: [[[[18.57853, 55.25302], [14.20811, 54.12784], [14.22634, 53.9291], [14.20647, 53.91671], [14.18544, 53.91258], [14.20823, 53.90776], [14.21323, 53.8664], [14.27249, 53.74464], [14.26782, 53.69866], [14.2836, 53.67721], [14.27133, 53.66613], [14.28477, 53.65955], [14.2853, 53.63392], [14.31904, 53.61581], [14.30416, 53.55499], [14.3273, 53.50587], [14.35209, 53.49506], [14.4215, 53.27724], [14.44133, 53.27427], [14.45125, 53.26241], [14.40662, 53.21098], [14.37853, 53.20405], [14.36696, 53.16444], [14.38679, 53.13669], [14.35044, 53.05829], [14.25954, 53.00264], [14.14056, 52.95786], [14.15873, 52.87715], [14.12256, 52.84311], [14.13806, 52.82392], [14.22071, 52.81175], [14.61073, 52.59847], [14.6289, 52.57136], [14.60081, 52.53116], [14.63056, 52.48993], [14.54423, 52.42568], [14.55228, 52.35264], [14.56378, 52.33838], [14.58149, 52.28007], [14.70139, 52.25038], [14.71319, 52.22144], [14.68344, 52.19612], [14.70616, 52.16927], [14.67683, 52.13936], [14.6917, 52.10283], [14.72971, 52.09167], [14.76026, 52.06624], [14.71339, 52.00337], [14.70488, 51.97679], [14.7139, 51.95643], [14.71836, 51.95606], [14.72163, 51.95188], [14.7177, 51.94048], [14.70601, 51.92944], [14.6933, 51.9044], [14.6588, 51.88359], [14.59089, 51.83302], [14.60493, 51.80473], [14.64625, 51.79472], [14.66386, 51.73282], [14.69065, 51.70842], [14.75392, 51.67445], [14.75759, 51.62318], [14.7727, 51.61263], [14.71125, 51.56209], [14.73047, 51.54606], [14.72652, 51.53902], [14.73219, 51.52922], [14.94749, 51.47155], [14.9652, 51.44793], [14.96899, 51.38367], [14.98008, 51.33449], [15.04288, 51.28387], [15.01242, 51.21285], [15.0047, 51.16874], [14.99311, 51.16249], [14.99414, 51.15813], [15.00083, 51.14974], [14.99646, 51.14365], [14.99079, 51.14284], [14.99689, 51.12205], [14.98229, 51.11354], [14.97938, 51.07742], [14.95529, 51.04552], [14.92942, 50.99744], [14.89252, 50.94999], [14.89681, 50.9422], [14.81664, 50.88148], [14.82803, 50.86966], [14.99852, 50.86817], [15.01088, 50.97984], [14.96419, 50.99108], [15.02433, 51.0242], [15.03895, 51.0123], [15.06218, 51.02269], [15.10152, 51.01095], [15.11937, 50.99021], [15.16744, 51.01959], [15.1743, 50.9833], [15.2361, 50.99886], [15.27043, 50.97724], [15.2773, 50.8907], [15.36656, 50.83956], [15.3803, 50.77187], [15.43798, 50.80833], [15.73186, 50.73885], [15.81683, 50.75666], [15.87331, 50.67188], [15.97219, 50.69799], [16.0175, 50.63009], [15.98317, 50.61528], [16.02437, 50.60046], [16.10265, 50.66405], [16.20839, 50.63096], [16.23174, 50.67101], [16.33611, 50.66579], [16.44597, 50.58041], [16.34572, 50.49575], [16.31413, 50.50274], [16.19526, 50.43291], [16.21585, 50.40627], [16.22821, 50.41054], [16.28118, 50.36891], [16.30289, 50.38292], [16.36495, 50.37679], [16.3622, 50.34875], [16.39379, 50.3207], [16.42674, 50.32509], [16.56407, 50.21009], [16.55446, 50.16613], [16.63137, 50.1142], [16.7014, 50.09659], [16.8456, 50.20834], [16.98018, 50.24172], [17.00353, 50.21449], [17.02825, 50.23118], [16.99803, 50.25753], [17.02138, 50.27772], [16.99803, 50.30316], [16.94448, 50.31281], [16.90877, 50.38642], [16.85933, 50.41093], [16.89229, 50.45117], [17.1224, 50.39494], [17.14498, 50.38117], [17.19579, 50.38817], [17.19991, 50.3654], [17.27681, 50.32246], [17.34273, 50.32947], [17.34548, 50.2628], [17.3702, 50.28123], [17.58889, 50.27837], [17.67764, 50.28977], [17.69292, 50.32859], [17.74648, 50.29966], [17.72176, 50.25665], [17.76296, 50.23382], [17.70528, 50.18812], [17.59404, 50.16437], [17.66683, 50.10275], [17.6888, 50.12037], [17.7506, 50.07896], [17.77669, 50.02253], [17.86886, 49.97452], [18.00191, 50.01723], [18.04585, 50.01194], [18.04585, 50.03311], [18.00396, 50.04954], [18.03212, 50.06574], [18.07898, 50.04535], [18.10628, 50.00223], [18.20241, 49.99958], [18.21752, 49.97309], [18.27107, 49.96779], [18.27794, 49.93863], [18.31914, 49.91565], [18.33278, 49.92415], [18.33562, 49.94747], [18.41604, 49.93498], [18.53423, 49.89906], [18.54495, 49.9079], [18.54299, 49.92537], [18.57697, 49.91565], [18.57045, 49.87849], [18.60341, 49.86256], [18.57183, 49.83334], [18.61278, 49.7618], [18.61368, 49.75426], [18.62645, 49.75002], [18.62943, 49.74603], [18.62676, 49.71983], [18.69817, 49.70473], [18.72838, 49.68163], [18.80479, 49.6815], [18.84786, 49.5446], [18.84521, 49.51672], [18.94536, 49.52143], [18.97283, 49.49914], [18.9742, 49.39557], [19.18019, 49.41165], [19.25435, 49.53391], [19.36009, 49.53747], [19.37795, 49.574], [19.45348, 49.61583], [19.52626, 49.57311], [19.53313, 49.52856], [19.57845, 49.46077], [19.64162, 49.45184], [19.6375, 49.40897], [19.72127, 49.39288], [19.78581, 49.41701], [19.82237, 49.27806], [19.75286, 49.20751], [19.86409, 49.19316], [19.90529, 49.23532], [19.98494, 49.22904], [20.08238, 49.1813], [20.13738, 49.31685], [20.21977, 49.35265], [20.31453, 49.34817], [20.31728, 49.39914], [20.39939, 49.3896], [20.46422, 49.41612], [20.5631, 49.375], [20.61666, 49.41791], [20.72274, 49.41813], [20.77971, 49.35383], [20.9229, 49.29626], [20.98733, 49.30774], [21.09799, 49.37176], [21.041, 49.41791], [21.12477, 49.43666], [21.19756, 49.4054], [21.27858, 49.45988], [21.43376, 49.41433], [21.62328, 49.4447], [21.77983, 49.35443], [21.82927, 49.39467], [21.96385, 49.3437], [22.04427, 49.22136], [22.56155, 49.08865], [22.89122, 49.00725], [22.86336, 49.10513], [22.72009, 49.20288], [22.748, 49.32759], [22.69444, 49.49378], [22.64534, 49.53094], [22.78304, 49.65543], [22.80261, 49.69098], [22.83179, 49.69875], [22.99329, 49.84249], [23.28221, 50.0957], [23.67635, 50.33385], [23.71382, 50.38248], [23.79445, 50.40481], [23.99563, 50.41289], [24.03668, 50.44507], [24.07048, 50.5071], [24.0996, 50.60752], [24.0595, 50.71625], [23.95925, 50.79271], [23.99254, 50.83847], [24.0952, 50.83262], [24.14524, 50.86128], [24.04576, 50.90196], [23.92217, 51.00836], [23.90376, 51.07697], [23.80678, 51.18405], [23.63858, 51.32182], [23.69905, 51.40871], [23.62751, 51.50512], [23.56236, 51.53673], [23.57053, 51.55938], [23.53198, 51.74298], [23.62691, 51.78208], [23.61523, 51.92066], [23.68733, 51.9906], [23.64066, 52.07626], [23.61, 52.11264], [23.54314, 52.12148], [23.47859, 52.18215], [23.20071, 52.22848], [23.18196, 52.28812], [23.34141, 52.44845], [23.45112, 52.53774], [23.58296, 52.59868], [23.73615, 52.6149], [23.93763, 52.71332], [23.91805, 52.94016], [23.94689, 52.95919], [23.92184, 53.02079], [23.87548, 53.0831], [23.91393, 53.16469], [23.85657, 53.22923], [23.81995, 53.24131], [23.62004, 53.60942], [23.51284, 53.95052], [23.48261, 53.98855], [23.52702, 54.04622], [23.49196, 54.14764], [23.45223, 54.17775], [23.42418, 54.17911], [23.39525, 54.21672], [23.3494, 54.25155], [23.24656, 54.25701], [23.15938, 54.29894], [23.15526, 54.31076], [23.13905, 54.31567], [23.104, 54.29794], [23.04323, 54.31567], [23.05726, 54.34565], [22.99649, 54.35927], [23.00584, 54.38514], [22.83756, 54.40827], [22.79705, 54.36264], [21.41123, 54.32395], [20.63871, 54.3706], [19.8038, 54.44203], [19.64312, 54.45423], [18.57853, 55.25302]]]]
25805           }
25806         }, {
25807           type: "Feature",
25808           properties: {
25809             iso1A2: "PM",
25810             iso1A3: "SPM",
25811             iso1N3: "666",
25812             wikidata: "Q34617",
25813             nameEn: "Saint Pierre and Miquelon",
25814             country: "FR",
25815             groups: ["Q1451600", "021", "003", "019", "UN"],
25816             callingCodes: ["508"]
25817           },
25818           geometry: {
25819             type: "MultiPolygon",
25820             coordinates: [[[[-56.72993, 46.65575], [-55.90758, 46.6223], [-56.27503, 47.39728], [-56.72993, 46.65575]]]]
25821           }
25822         }, {
25823           type: "Feature",
25824           properties: {
25825             iso1A2: "PN",
25826             iso1A3: "PCN",
25827             iso1N3: "612",
25828             wikidata: "Q35672",
25829             nameEn: "Pitcairn Islands",
25830             country: "GB",
25831             groups: ["BOTS", "061", "009", "UN"],
25832             driveSide: "left",
25833             callingCodes: ["64"]
25834           },
25835           geometry: {
25836             type: "MultiPolygon",
25837             coordinates: [[[[-133.59543, -28.4709], [-122.0366, -24.55017], [-133.61511, -21.93325], [-133.59543, -28.4709]]]]
25838           }
25839         }, {
25840           type: "Feature",
25841           properties: {
25842             iso1A2: "PR",
25843             iso1A3: "PRI",
25844             iso1N3: "630",
25845             wikidata: "Q1183",
25846             nameEn: "Puerto Rico",
25847             aliases: ["US-PR"],
25848             country: "US",
25849             groups: ["Q1352230", "029", "003", "419", "019", "UN"],
25850             roadSpeedUnit: "mph",
25851             callingCodes: ["1 787", "1 939"]
25852           },
25853           geometry: {
25854             type: "MultiPolygon",
25855             coordinates: [[[[-65.27974, 17.56928], [-65.02435, 18.73231], [-67.99519, 18.97186], [-68.23894, 17.84663], [-65.27974, 17.56928]]]]
25856           }
25857         }, {
25858           type: "Feature",
25859           properties: {
25860             iso1A2: "PS",
25861             iso1A3: "PSE",
25862             iso1N3: "275",
25863             wikidata: "Q219060",
25864             nameEn: "Palestine"
25865           },
25866           geometry: null
25867         }, {
25868           type: "Feature",
25869           properties: {
25870             iso1A2: "PT",
25871             iso1A3: "PRT",
25872             iso1N3: "620",
25873             wikidata: "Q45",
25874             nameEn: "Portugal"
25875           },
25876           geometry: null
25877         }, {
25878           type: "Feature",
25879           properties: {
25880             iso1A2: "PW",
25881             iso1A3: "PLW",
25882             iso1N3: "585",
25883             wikidata: "Q695",
25884             nameEn: "Palau",
25885             groups: ["057", "009", "UN"],
25886             roadSpeedUnit: "mph",
25887             callingCodes: ["680"]
25888           },
25889           geometry: {
25890             type: "MultiPolygon",
25891             coordinates: [[[[128.97621, 3.08804], [136.39296, 1.54187], [136.04605, 12.45908], [128.97621, 3.08804]]]]
25892           }
25893         }, {
25894           type: "Feature",
25895           properties: {
25896             iso1A2: "PY",
25897             iso1A3: "PRY",
25898             iso1N3: "600",
25899             wikidata: "Q733",
25900             nameEn: "Paraguay",
25901             groups: ["005", "419", "019", "UN"],
25902             callingCodes: ["595"]
25903           },
25904           geometry: {
25905             type: "MultiPolygon",
25906             coordinates: [[[[-58.16225, -20.16193], [-58.23216, -19.80058], [-59.06965, -19.29148], [-60.00638, -19.2981], [-61.73723, -19.63958], [-61.93912, -20.10053], [-62.26883, -20.55311], [-62.2757, -21.06657], [-62.64455, -22.25091], [-62.51761, -22.37684], [-62.22768, -22.55807], [-61.9756, -23.0507], [-61.0782, -23.62932], [-60.99754, -23.80934], [-60.28163, -24.04436], [-60.03367, -24.00701], [-59.45482, -24.34787], [-59.33886, -24.49935], [-58.33055, -24.97099], [-58.25492, -24.92528], [-57.80821, -25.13863], [-57.57431, -25.47269], [-57.87176, -25.93604], [-58.1188, -26.16704], [-58.3198, -26.83443], [-58.65321, -27.14028], [-58.59549, -27.29973], [-58.04205, -27.2387], [-56.85337, -27.5165], [-56.18313, -27.29851], [-55.89195, -27.3467], [-55.74475, -27.44485], [-55.59094, -27.32444], [-55.62322, -27.1941], [-55.39611, -26.97679], [-55.25243, -26.93808], [-55.16948, -26.96068], [-55.06351, -26.80195], [-55.00584, -26.78754], [-54.80868, -26.55669], [-54.70732, -26.45099], [-54.69333, -26.37705], [-54.67359, -25.98607], [-54.60664, -25.9691], [-54.62063, -25.91213], [-54.59398, -25.59224], [-54.59509, -25.53696], [-54.60196, -25.48397], [-54.62033, -25.46026], [-54.4423, -25.13381], [-54.28207, -24.07305], [-54.32807, -24.01865], [-54.6238, -23.83078], [-55.02691, -23.97317], [-55.0518, -23.98666], [-55.12292, -23.99669], [-55.41784, -23.9657], [-55.44117, -23.9185], [-55.43585, -23.87157], [-55.5555, -23.28237], [-55.52288, -23.2595], [-55.5446, -23.22811], [-55.63849, -22.95122], [-55.62493, -22.62765], [-55.68742, -22.58407], [-55.6986, -22.56268], [-55.72366, -22.5519], [-55.741, -22.52018], [-55.74941, -22.46436], [-55.8331, -22.29008], [-56.23206, -22.25347], [-56.45893, -22.08072], [-56.5212, -22.11556], [-56.6508, -22.28387], [-57.98625, -22.09157], [-57.94642, -21.73799], [-57.88239, -21.6868], [-57.93492, -21.65505], [-57.84536, -20.93155], [-58.16225, -20.16193]]]]
25907           }
25908         }, {
25909           type: "Feature",
25910           properties: {
25911             iso1A2: "QA",
25912             iso1A3: "QAT",
25913             iso1N3: "634",
25914             wikidata: "Q846",
25915             nameEn: "Qatar",
25916             groups: ["145", "142", "UN"],
25917             callingCodes: ["974"]
25918           },
25919           geometry: {
25920             type: "MultiPolygon",
25921             coordinates: [[[[50.92992, 24.54396], [51.09638, 24.46907], [51.29972, 24.50747], [51.39468, 24.62785], [51.58834, 24.66608], [51.83108, 24.71675], [51.83682, 26.70231], [50.93865, 26.30758], [50.81266, 25.88946], [50.86149, 25.6965], [50.7801, 25.595], [50.80824, 25.54641], [50.57069, 25.57887], [50.8133, 24.74049], [50.92992, 24.54396]]]]
25922           }
25923         }, {
25924           type: "Feature",
25925           properties: {
25926             iso1A2: "RE",
25927             iso1A3: "REU",
25928             iso1N3: "638",
25929             wikidata: "Q17070",
25930             nameEn: "R\xE9union",
25931             country: "FR",
25932             groups: ["Q3320166", "EU", "014", "202", "002", "UN"],
25933             callingCodes: ["262"]
25934           },
25935           geometry: {
25936             type: "MultiPolygon",
25937             coordinates: [[[[53.37984, -21.23941], [56.73473, -21.9174], [56.62373, -20.2711], [53.37984, -21.23941]]]]
25938           }
25939         }, {
25940           type: "Feature",
25941           properties: {
25942             iso1A2: "RO",
25943             iso1A3: "ROU",
25944             iso1N3: "642",
25945             wikidata: "Q218",
25946             nameEn: "Romania",
25947             groups: ["EU", "151", "150", "UN"],
25948             callingCodes: ["40"]
25949           },
25950           geometry: {
25951             type: "MultiPolygon",
25952             coordinates: [[[[27.15622, 47.98538], [27.02985, 48.09083], [27.04118, 48.12522], [26.96119, 48.13003], [26.98042, 48.15752], [26.94265, 48.1969], [26.87708, 48.19919], [26.81161, 48.25049], [26.62823, 48.25804], [26.55202, 48.22445], [26.33504, 48.18418], [26.17711, 47.99246], [26.05901, 47.9897], [25.77723, 47.93919], [25.63878, 47.94924], [25.23778, 47.89403], [25.11144, 47.75203], [24.88896, 47.7234], [24.81893, 47.82031], [24.70632, 47.84428], [24.61994, 47.95062], [24.43578, 47.97131], [24.34926, 47.9244], [24.22566, 47.90231], [24.11281, 47.91487], [24.06466, 47.95317], [24.02999, 47.95087], [24.00801, 47.968], [23.98553, 47.96076], [23.96337, 47.96672], [23.94192, 47.94868], [23.89352, 47.94512], [23.8602, 47.9329], [23.80904, 47.98142], [23.75188, 47.99705], [23.66262, 47.98786], [23.63894, 48.00293], [23.5653, 48.00499], [23.52803, 48.01818], [23.4979, 47.96858], [23.33577, 48.0237], [23.27397, 48.08245], [23.15999, 48.12188], [23.1133, 48.08061], [23.08858, 48.00716], [23.0158, 47.99338], [22.92241, 48.02002], [22.94301, 47.96672], [22.89849, 47.95851], [22.77991, 47.87211], [22.76617, 47.8417], [22.67247, 47.7871], [22.46559, 47.76583], [22.41979, 47.7391], [22.31816, 47.76126], [22.00917, 47.50492], [22.03389, 47.42508], [22.01055, 47.37767], [21.94463, 47.38046], [21.78395, 47.11104], [21.648, 47.03902], [21.68645, 46.99595], [21.59581, 46.91628], [21.59307, 46.86935], [21.52028, 46.84118], [21.48935, 46.7577], [21.5151, 46.72147], [21.43926, 46.65109], [21.33214, 46.63035], [21.26929, 46.4993], [21.28061, 46.44941], [21.16872, 46.30118], [21.06572, 46.24897], [20.86797, 46.28884], [20.74574, 46.25467], [20.76085, 46.21002], [20.63863, 46.12728], [20.49718, 46.18721], [20.45377, 46.14405], [20.35573, 46.16629], [20.28324, 46.1438], [20.26068, 46.12332], [20.35862, 45.99356], [20.54818, 45.89939], [20.65645, 45.82801], [20.70069, 45.7493], [20.77416, 45.75601], [20.78446, 45.78522], [20.82364, 45.77738], [20.80361, 45.65875], [20.76798, 45.60969], [20.83321, 45.53567], [20.77217, 45.49788], [20.86026, 45.47295], [20.87948, 45.42743], [21.09894, 45.30144], [21.17612, 45.32566], [21.20392, 45.2677], [21.29398, 45.24148], [21.48278, 45.19557], [21.51299, 45.15345], [21.4505, 45.04294], [21.35855, 45.01941], [21.54938, 44.9327], [21.56328, 44.89502], [21.48202, 44.87199], [21.44013, 44.87613], [21.35643, 44.86364], [21.38802, 44.78133], [21.55007, 44.77304], [21.60019, 44.75208], [21.61942, 44.67059], [21.67504, 44.67107], [21.71692, 44.65349], [21.7795, 44.66165], [21.99364, 44.63395], [22.08016, 44.49844], [22.13234, 44.47444], [22.18315, 44.48179], [22.30844, 44.6619], [22.45301, 44.7194], [22.61917, 44.61489], [22.69196, 44.61587], [22.76749, 44.54446], [22.70981, 44.51852], [22.61368, 44.55719], [22.56493, 44.53419], [22.54021, 44.47836], [22.45436, 44.47258], [22.56012, 44.30712], [22.68166, 44.28206], [22.67173, 44.21564], [23.04988, 44.07694], [23.01674, 44.01946], [22.87873, 43.9844], [22.83753, 43.88055], [22.85314, 43.84452], [23.05288, 43.79494], [23.26772, 43.84843], [23.4507, 43.84936], [23.61687, 43.79289], [23.73978, 43.80627], [24.18149, 43.68218], [24.35364, 43.70211], [24.50264, 43.76314], [24.62281, 43.74082], [24.73542, 43.68523], [24.96682, 43.72693], [25.10718, 43.6831], [25.17144, 43.70261], [25.39528, 43.61866], [25.72792, 43.69263], [25.94911, 43.85745], [26.05584, 43.90925], [26.10115, 43.96908], [26.38764, 44.04356], [26.62712, 44.05698], [26.95141, 44.13555], [27.26845, 44.12602], [27.39757, 44.0141], [27.60834, 44.01206], [27.64542, 44.04958], [27.73468, 43.95326], [27.92008, 44.00761], [27.99558, 43.84193], [28.23293, 43.76], [29.24336, 43.70874], [30.04414, 45.08461], [29.69272, 45.19227], [29.65428, 45.25629], [29.68175, 45.26885], [29.59798, 45.38857], [29.42632, 45.44545], [29.24779, 45.43388], [28.96077, 45.33164], [28.94292, 45.28045], [28.81383, 45.3384], [28.78911, 45.24179], [28.71358, 45.22631], [28.5735, 45.24759], [28.34554, 45.32102], [28.28504, 45.43907], [28.21139, 45.46895], [28.18741, 45.47358], [28.08927, 45.6051], [28.16568, 45.6421], [28.13111, 45.92819], [28.08612, 46.01105], [28.13684, 46.18099], [28.10937, 46.22852], [28.19864, 46.31869], [28.18902, 46.35283], [28.25769, 46.43334], [28.22281, 46.50481], [28.24808, 46.64305], [28.12173, 46.82283], [28.09095, 46.97621], [27.81892, 47.1381], [27.73172, 47.29248], [27.68706, 47.28962], [27.60263, 47.32507], [27.55731, 47.46637], [27.47942, 47.48113], [27.3979, 47.59473], [27.32202, 47.64009], [27.25519, 47.71366], [27.29069, 47.73722], [27.1618, 47.92391], [27.15622, 47.98538]]]]
25953           }
25954         }, {
25955           type: "Feature",
25956           properties: {
25957             iso1A2: "RS",
25958             iso1A3: "SRB",
25959             iso1N3: "688",
25960             wikidata: "Q403",
25961             nameEn: "Serbia",
25962             groups: ["039", "150", "UN"],
25963             callingCodes: ["381"]
25964           },
25965           geometry: {
25966             type: "MultiPolygon",
25967             coordinates: [[[[19.66007, 46.19005], [19.56113, 46.16824], [19.52473, 46.1171], [19.28826, 45.99694], [19.14543, 45.9998], [19.10388, 46.04015], [19.0791, 45.96458], [19.01284, 45.96529], [18.99712, 45.93537], [18.81394, 45.91329], [18.85783, 45.85493], [18.90305, 45.71863], [18.96691, 45.66731], [18.88776, 45.57253], [18.94562, 45.53712], [19.07471, 45.53086], [19.08364, 45.48804], [18.99918, 45.49333], [18.97446, 45.37528], [19.10774, 45.29547], [19.28208, 45.23813], [19.41941, 45.23475], [19.43589, 45.17137], [19.19144, 45.17863], [19.14063, 45.12972], [19.07952, 45.14668], [19.1011, 45.01191], [19.05205, 44.97692], [19.15573, 44.95409], [19.06853, 44.89915], [19.02871, 44.92541], [18.98957, 44.90645], [19.01994, 44.85493], [19.18183, 44.92055], [19.36722, 44.88164], [19.32543, 44.74058], [19.26388, 44.65412], [19.16699, 44.52197], [19.13369, 44.52521], [19.12278, 44.50132], [19.14837, 44.45253], [19.14681, 44.41463], [19.11785, 44.40313], [19.10749, 44.39421], [19.10704, 44.38249], [19.10365, 44.37795], [19.10298, 44.36924], [19.11865, 44.36712], [19.1083, 44.3558], [19.11547, 44.34218], [19.13556, 44.338], [19.13332, 44.31492], [19.16741, 44.28648], [19.18328, 44.28383], [19.20508, 44.2917], [19.23306, 44.26097], [19.26945, 44.26957], [19.32464, 44.27185], [19.34773, 44.23244], [19.3588, 44.18353], [19.40927, 44.16722], [19.43905, 44.13088], [19.47338, 44.15034], [19.48386, 44.14332], [19.47321, 44.1193], [19.51167, 44.08158], [19.55999, 44.06894], [19.57467, 44.04716], [19.61991, 44.05254], [19.61836, 44.01464], [19.56498, 43.99922], [19.52515, 43.95573], [19.38439, 43.96611], [19.24363, 44.01502], [19.23465, 43.98764], [19.3986, 43.79668], [19.5176, 43.71403], [19.50455, 43.58385], [19.42696, 43.57987], [19.41941, 43.54056], [19.36653, 43.60921], [19.33426, 43.58833], [19.2553, 43.5938], [19.24774, 43.53061], [19.22807, 43.5264], [19.22229, 43.47926], [19.44315, 43.38846], [19.48171, 43.32644], [19.52962, 43.31623], [19.54598, 43.25158], [19.62661, 43.2286], [19.64063, 43.19027], [19.76918, 43.16044], [19.79255, 43.11951], [19.92576, 43.08539], [19.96549, 43.11098], [19.98887, 43.0538], [20.04729, 43.02732], [20.05431, 42.99571], [20.12325, 42.96237], [20.14896, 42.99058], [20.16415, 42.97177], [20.34528, 42.90676], [20.35692, 42.8335], [20.40594, 42.84853], [20.43734, 42.83157], [20.53484, 42.8885], [20.48692, 42.93208], [20.59929, 43.01067], [20.64557, 43.00826], [20.69515, 43.09641], [20.59929, 43.20492], [20.68688, 43.21335], [20.73811, 43.25068], [20.82145, 43.26769], [20.88685, 43.21697], [20.83727, 43.17842], [20.96287, 43.12416], [21.00749, 43.13984], [21.05378, 43.10707], [21.08952, 43.13471], [21.14465, 43.11089], [21.16734, 42.99694], [21.2041, 43.02277], [21.23877, 43.00848], [21.23534, 42.95523], [21.2719, 42.8994], [21.32974, 42.90424], [21.36941, 42.87397], [21.44047, 42.87276], [21.39045, 42.74888], [21.47498, 42.74695], [21.59154, 42.72643], [21.58755, 42.70418], [21.6626, 42.67813], [21.75025, 42.70125], [21.79413, 42.65923], [21.75672, 42.62695], [21.7327, 42.55041], [21.70522, 42.54176], [21.7035, 42.51899], [21.62556, 42.45106], [21.64209, 42.41081], [21.62887, 42.37664], [21.59029, 42.38042], [21.57021, 42.3647], [21.53467, 42.36809], [21.5264, 42.33634], [21.56772, 42.30946], [21.58992, 42.25915], [21.70111, 42.23789], [21.77176, 42.2648], [21.84654, 42.3247], [21.91595, 42.30392], [21.94405, 42.34669], [22.02908, 42.29848], [22.16384, 42.32103], [22.29605, 42.37477], [22.29275, 42.34913], [22.34773, 42.31725], [22.45919, 42.33822], [22.47498, 42.3915], [22.51961, 42.3991], [22.55669, 42.50144], [22.43983, 42.56851], [22.4997, 42.74144], [22.43309, 42.82057], [22.54302, 42.87774], [22.74826, 42.88701], [22.78397, 42.98253], [22.89521, 43.03625], [22.98104, 43.11199], [23.00806, 43.19279], [22.89727, 43.22417], [22.82036, 43.33665], [22.53397, 43.47225], [22.47582, 43.6558], [22.41043, 43.69566], [22.35558, 43.81281], [22.41449, 44.00514], [22.61688, 44.06534], [22.61711, 44.16938], [22.67173, 44.21564], [22.68166, 44.28206], [22.56012, 44.30712], [22.45436, 44.47258], [22.54021, 44.47836], [22.56493, 44.53419], [22.61368, 44.55719], [22.70981, 44.51852], [22.76749, 44.54446], [22.69196, 44.61587], [22.61917, 44.61489], [22.45301, 44.7194], [22.30844, 44.6619], [22.18315, 44.48179], [22.13234, 44.47444], [22.08016, 44.49844], [21.99364, 44.63395], [21.7795, 44.66165], [21.71692, 44.65349], [21.67504, 44.67107], [21.61942, 44.67059], [21.60019, 44.75208], [21.55007, 44.77304], [21.38802, 44.78133], [21.35643, 44.86364], [21.44013, 44.87613], [21.48202, 44.87199], [21.56328, 44.89502], [21.54938, 44.9327], [21.35855, 45.01941], [21.4505, 45.04294], [21.51299, 45.15345], [21.48278, 45.19557], [21.29398, 45.24148], [21.20392, 45.2677], [21.17612, 45.32566], [21.09894, 45.30144], [20.87948, 45.42743], [20.86026, 45.47295], [20.77217, 45.49788], [20.83321, 45.53567], [20.76798, 45.60969], [20.80361, 45.65875], [20.82364, 45.77738], [20.78446, 45.78522], [20.77416, 45.75601], [20.70069, 45.7493], [20.65645, 45.82801], [20.54818, 45.89939], [20.35862, 45.99356], [20.26068, 46.12332], [20.09713, 46.17315], [20.03533, 46.14509], [20.01816, 46.17696], [19.93508, 46.17553], [19.81491, 46.1313], [19.66007, 46.19005]]]]
25968           }
25969         }, {
25970           type: "Feature",
25971           properties: {
25972             iso1A2: "RU",
25973             iso1A3: "RUS",
25974             iso1N3: "643",
25975             wikidata: "Q159",
25976             nameEn: "Russia"
25977           },
25978           geometry: null
25979         }, {
25980           type: "Feature",
25981           properties: {
25982             iso1A2: "RW",
25983             iso1A3: "RWA",
25984             iso1N3: "646",
25985             wikidata: "Q1037",
25986             nameEn: "Rwanda",
25987             groups: ["014", "202", "002", "UN"],
25988             callingCodes: ["250"]
25989           },
25990           geometry: {
25991             type: "MultiPolygon",
25992             coordinates: [[[[30.47194, -1.0555], [30.35212, -1.06896], [30.16369, -1.34303], [29.912, -1.48269], [29.82657, -1.31187], [29.59061, -1.39016], [29.53062, -1.40499], [29.45038, -1.5054], [29.36322, -1.50887], [29.24323, -1.66826], [29.24458, -1.69663], [29.11847, -1.90576], [29.17562, -2.12278], [29.105, -2.27043], [29.00051, -2.29001], [28.95642, -2.37321], [28.89601, -2.37321], [28.86826, -2.41888], [28.86846, -2.44866], [28.89132, -2.47557], [28.89342, -2.49017], [28.88846, -2.50493], [28.87497, -2.50887], [28.86209, -2.5231], [28.86193, -2.53185], [28.87943, -2.55165], [28.89288, -2.55848], [28.90226, -2.62385], [28.89793, -2.66111], [28.94346, -2.69124], [29.00357, -2.70596], [29.04081, -2.7416], [29.0562, -2.58632], [29.32234, -2.6483], [29.36805, -2.82933], [29.88237, -2.75105], [29.95911, -2.33348], [30.14034, -2.43626], [30.42933, -2.31064], [30.54501, -2.41404], [30.83915, -2.35795], [30.89303, -2.08223], [30.80802, -1.91477], [30.84079, -1.64652], [30.71974, -1.43244], [30.57123, -1.33264], [30.50889, -1.16412], [30.45116, -1.10641], [30.47194, -1.0555]]]]
25993           }
25994         }, {
25995           type: "Feature",
25996           properties: {
25997             iso1A2: "SA",
25998             iso1A3: "SAU",
25999             iso1N3: "682",
26000             wikidata: "Q851",
26001             nameEn: "Saudi Arabia",
26002             groups: ["145", "142", "UN"],
26003             callingCodes: ["966"]
26004           },
26005           geometry: {
26006             type: "MultiPolygon",
26007             coordinates: [[[[40.01521, 32.05667], [39.29903, 32.23259], [38.99233, 31.99721], [36.99791, 31.50081], [37.99354, 30.49998], [37.66395, 30.33245], [37.4971, 29.99949], [36.75083, 29.86903], [36.50005, 29.49696], [36.07081, 29.18469], [34.8812, 29.36878], [34.4454, 27.91479], [37.8565, 22.00903], [39.63762, 18.37348], [40.99158, 15.81743], [42.15205, 16.40211], [42.76801, 16.40371], [42.94625, 16.39721], [42.94351, 16.49467], [42.97215, 16.51093], [43.11601, 16.53166], [43.15274, 16.67248], [43.22066, 16.65179], [43.21325, 16.74416], [43.25857, 16.75304], [43.26303, 16.79479], [43.24801, 16.80613], [43.22956, 16.80613], [43.22012, 16.83932], [43.18338, 16.84852], [43.1398, 16.90696], [43.19328, 16.94703], [43.1813, 16.98438], [43.18233, 17.02673], [43.23967, 17.03428], [43.17787, 17.14717], [43.20156, 17.25901], [43.32653, 17.31179], [43.22533, 17.38343], [43.29185, 17.53224], [43.43005, 17.56148], [43.70631, 17.35762], [44.50126, 17.47475], [46.31018, 17.20464], [46.76494, 17.29151], [47.00571, 16.94765], [47.48245, 17.10808], [47.58351, 17.50366], [48.19996, 18.20584], [49.04884, 18.59899], [52.00311, 19.00083], [54.99756, 20.00083], [55.66469, 21.99658], [55.2137, 22.71065], [55.13599, 22.63334], [52.56622, 22.94341], [51.59617, 24.12041], [51.58871, 24.27256], [51.41644, 24.39615], [51.58834, 24.66608], [51.39468, 24.62785], [51.29972, 24.50747], [51.09638, 24.46907], [50.92992, 24.54396], [50.8133, 24.74049], [50.57069, 25.57887], [50.302, 25.87592], [50.26923, 26.08243], [50.38162, 26.53976], [50.71771, 26.73086], [50.37726, 27.89227], [49.98877, 27.87827], [49.00421, 28.81495], [48.42991, 28.53628], [47.70561, 28.5221], [47.59863, 28.66798], [47.58376, 28.83382], [47.46202, 29.0014], [46.5527, 29.10283], [46.42415, 29.05947], [44.72255, 29.19736], [42.97796, 30.48295], [42.97601, 30.72204], [40.01521, 32.05667]]]]
26008           }
26009         }, {
26010           type: "Feature",
26011           properties: {
26012             iso1A2: "SB",
26013             iso1A3: "SLB",
26014             iso1N3: "090",
26015             wikidata: "Q685",
26016             nameEn: "Solomon Islands",
26017             groups: ["054", "009", "UN"],
26018             driveSide: "left",
26019             callingCodes: ["677"]
26020           },
26021           geometry: {
26022             type: "MultiPolygon",
26023             coordinates: [[[[172.71443, -12.01327], [160.43769, -4.17974], [156.03296, -6.55528], [156.03993, -6.65703], [155.92557, -6.84664], [155.69784, -6.92661], [155.60735, -6.92266], [154.74815, -7.33315], [156.73836, -14.50464], [172.71443, -12.01327]]]]
26024           }
26025         }, {
26026           type: "Feature",
26027           properties: {
26028             iso1A2: "SC",
26029             iso1A3: "SYC",
26030             iso1N3: "690",
26031             wikidata: "Q1042",
26032             nameEn: "Seychelles",
26033             groups: ["014", "202", "002", "UN"],
26034             driveSide: "left",
26035             callingCodes: ["248"]
26036           },
26037           geometry: {
26038             type: "MultiPolygon",
26039             coordinates: [[[[43.75112, -10.38913], [54.83239, -10.93575], [66.3222, 5.65313], [43.75112, -10.38913]]]]
26040           }
26041         }, {
26042           type: "Feature",
26043           properties: {
26044             iso1A2: "SD",
26045             iso1A3: "SDN",
26046             iso1N3: "729",
26047             wikidata: "Q1049",
26048             nameEn: "Sudan",
26049             groups: ["015", "002", "UN"],
26050             callingCodes: ["249"]
26051           },
26052           geometry: {
26053             type: "MultiPolygon",
26054             coordinates: [[[[37.8565, 22.00903], [34.0765, 22.00501], [33.99686, 21.76784], [33.57251, 21.72406], [33.17563, 22.00405], [24.99885, 21.99535], [24.99794, 19.99661], [23.99715, 20.00038], [23.99539, 19.49944], [23.99997, 15.69575], [23.62785, 15.7804], [23.38812, 15.69649], [23.10792, 15.71297], [22.93201, 15.55107], [22.92579, 15.47007], [22.99584, 15.40105], [22.99584, 15.22989], [22.66115, 14.86308], [22.70474, 14.69149], [22.38562, 14.58907], [22.44944, 14.24986], [22.55997, 14.23024], [22.5553, 14.11704], [22.22995, 13.96754], [22.08674, 13.77863], [22.29689, 13.3731], [22.1599, 13.19281], [22.02914, 13.13976], [21.94819, 13.05637], [21.81432, 12.81362], [21.89371, 12.68001], [21.98711, 12.63292], [22.15679, 12.66634], [22.22684, 12.74682], [22.46345, 12.61925], [22.38873, 12.45514], [22.50548, 12.16769], [22.48369, 12.02766], [22.64092, 12.07485], [22.54907, 11.64372], [22.7997, 11.40424], [22.93124, 11.41645], [22.97249, 11.21955], [22.87758, 10.91915], [23.02221, 10.69235], [23.3128, 10.45214], [23.67164, 9.86923], [23.69155, 9.67566], [24.09319, 9.66572], [24.12744, 9.73784], [24.49389, 9.79962], [24.84653, 9.80643], [24.97739, 9.9081], [25.05688, 10.06776], [25.0918, 10.33718], [25.78141, 10.42599], [25.93163, 10.38159], [25.93241, 10.17941], [26.21338, 9.91545], [26.35815, 9.57946], [26.70685, 9.48735], [27.14427, 9.62858], [27.90704, 9.61323], [28.99983, 9.67155], [29.06988, 9.74826], [29.53844, 9.75133], [29.54, 10.07949], [29.94629, 10.29245], [30.00389, 10.28633], [30.53005, 9.95992], [30.82893, 9.71451], [30.84605, 9.7498], [31.28504, 9.75287], [31.77539, 10.28939], [31.99177, 10.65065], [32.46967, 11.04662], [32.39358, 11.18207], [32.39578, 11.70208], [32.10079, 11.95203], [32.73921, 11.95203], [32.73921, 12.22757], [33.25876, 12.22111], [33.13988, 11.43248], [33.26977, 10.83632], [33.24645, 10.77913], [33.52294, 10.64382], [33.66604, 10.44254], [33.80913, 10.32994], [33.90159, 10.17179], [33.96984, 10.15446], [33.99185, 9.99623], [33.96323, 9.80972], [33.9082, 9.762], [33.87958, 9.49937], [34.10229, 9.50238], [34.08717, 9.55243], [34.13186, 9.7492], [34.20484, 9.9033], [34.22718, 10.02506], [34.32102, 10.11599], [34.34783, 10.23914], [34.2823, 10.53508], [34.4372, 10.781], [34.59062, 10.89072], [34.77383, 10.74588], [34.77532, 10.69027], [34.86618, 10.74588], [34.86916, 10.78832], [34.97491, 10.86147], [34.97789, 10.91559], [34.93172, 10.95946], [35.01215, 11.19626], [34.95704, 11.24448], [35.09556, 11.56278], [35.05832, 11.71158], [35.11492, 11.85156], [35.24302, 11.91132], [35.70476, 12.67101], [36.01458, 12.72478], [36.14268, 12.70879], [36.16651, 12.88019], [36.13374, 12.92665], [36.24545, 13.36759], [36.38993, 13.56459], [36.48824, 13.83954], [36.44653, 13.95666], [36.54376, 14.25597], [36.44337, 15.14963], [36.54276, 15.23478], [36.69761, 15.75323], [36.76371, 15.80831], [36.92193, 16.23451], [36.99777, 17.07172], [37.42694, 17.04041], [37.50967, 17.32199], [38.13362, 17.53906], [38.37133, 17.66269], [38.45916, 17.87167], [38.57727, 17.98125], [39.63762, 18.37348], [37.8565, 22.00903]]]]
26055           }
26056         }, {
26057           type: "Feature",
26058           properties: {
26059             iso1A2: "SE",
26060             iso1A3: "SWE",
26061             iso1N3: "752",
26062             wikidata: "Q34",
26063             nameEn: "Sweden",
26064             groups: ["EU", "154", "150", "UN"],
26065             callingCodes: ["46"]
26066           },
26067           geometry: {
26068             type: "MultiPolygon",
26069             coordinates: [[[[24.15791, 65.85385], [23.90497, 66.15802], [23.71339, 66.21299], [23.64982, 66.30603], [23.67591, 66.3862], [23.63776, 66.43568], [23.85959, 66.56434], [23.89488, 66.772], [23.98059, 66.79585], [23.98563, 66.84149], [23.56214, 67.17038], [23.58735, 67.20752], [23.54701, 67.25435], [23.75372, 67.29914], [23.75372, 67.43688], [23.39577, 67.46974], [23.54701, 67.59306], [23.45627, 67.85297], [23.65793, 67.9497], [23.40081, 68.05545], [23.26469, 68.15134], [23.15377, 68.14759], [23.10336, 68.26551], [22.73028, 68.40881], [22.00429, 68.50692], [21.03001, 68.88969], [20.90649, 68.89696], [20.85104, 68.93142], [20.91658, 68.96764], [20.78802, 69.03087], [20.55258, 69.06069], [20.0695, 69.04469], [20.28444, 68.93283], [20.33435, 68.80174], [20.22027, 68.67246], [19.95647, 68.55546], [20.22027, 68.48759], [19.93508, 68.35911], [18.97255, 68.52416], [18.63032, 68.50849], [18.39503, 68.58672], [18.1241, 68.53721], [18.13836, 68.20874], [17.90787, 67.96537], [17.30416, 68.11591], [16.7409, 67.91037], [16.38441, 67.52923], [16.12774, 67.52106], [16.09922, 67.4364], [16.39154, 67.21653], [16.35589, 67.06419], [15.37197, 66.48217], [15.49318, 66.28509], [15.05113, 66.15572], [14.53778, 66.12399], [14.50926, 65.31786], [13.64276, 64.58402], [14.11117, 64.46674], [14.16051, 64.18725], [13.98222, 64.00953], [13.23411, 64.09087], [12.74105, 64.02171], [12.14928, 63.59373], [12.19919, 63.47935], [11.98529, 63.27487], [12.19919, 63.00104], [12.07085, 62.6297], [12.29187, 62.25699], [12.14746, 61.7147], [12.40595, 61.57226], [12.57707, 61.56547], [12.86939, 61.35427], [12.69115, 61.06584], [12.2277, 61.02442], [12.59133, 60.50559], [12.52003, 60.13846], [12.36317, 59.99259], [12.15641, 59.8926], [11.87121, 59.86039], [11.92112, 59.69531], [11.69297, 59.59442], [11.8213, 59.24985], [11.65732, 58.90177], [11.45199, 58.89604], [11.4601, 58.99022], [11.34459, 59.11672], [11.15367, 59.07862], [11.08911, 58.98745], [10.64958, 58.89391], [10.40861, 58.38489], [12.16597, 56.60205], [12.07466, 56.29488], [12.65312, 56.04345], [12.6372, 55.91371], [12.88472, 55.63369], [12.60345, 55.42675], [12.84405, 55.13257], [14.28399, 55.1553], [14.89259, 55.5623], [15.79951, 55.54655], [19.64795, 57.06466], [19.84909, 57.57876], [20.5104, 59.15546], [19.08191, 60.19152], [19.23413, 60.61414], [20.15877, 63.06556], [24.14112, 65.39731], [24.15107, 65.81427], [24.14798, 65.83466], [24.15791, 65.85385]]]]
26070           }
26071         }, {
26072           type: "Feature",
26073           properties: {
26074             iso1A2: "SG",
26075             iso1A3: "SGP",
26076             iso1N3: "702",
26077             wikidata: "Q334",
26078             nameEn: "Singapore",
26079             groups: ["035", "142", "UN"],
26080             driveSide: "left",
26081             callingCodes: ["65"]
26082           },
26083           geometry: {
26084             type: "MultiPolygon",
26085             coordinates: [[[[104.00131, 1.42405], [103.93384, 1.42926], [103.89565, 1.42841], [103.86383, 1.46288], [103.81181, 1.47953], [103.76395, 1.45183], [103.74161, 1.4502], [103.7219, 1.46108], [103.67468, 1.43166], [103.62738, 1.35255], [103.56591, 1.19719], [103.66049, 1.18825], [103.74084, 1.12902], [104.03085, 1.26954], [104.12282, 1.27714], [104.08072, 1.35998], [104.09162, 1.39694], [104.08871, 1.42015], [104.07348, 1.43322], [104.04622, 1.44691], [104.02277, 1.4438], [104.00131, 1.42405]]]]
26086           }
26087         }, {
26088           type: "Feature",
26089           properties: {
26090             iso1A2: "SH",
26091             iso1A3: "SHN",
26092             iso1N3: "654",
26093             wikidata: "Q192184",
26094             nameEn: "Saint Helena, Ascension and Tristan da Cunha",
26095             country: "GB"
26096           },
26097           geometry: null
26098         }, {
26099           type: "Feature",
26100           properties: {
26101             iso1A2: "SI",
26102             iso1A3: "SVN",
26103             iso1N3: "705",
26104             wikidata: "Q215",
26105             nameEn: "Slovenia",
26106             groups: ["EU", "039", "150", "UN"],
26107             callingCodes: ["386"]
26108           },
26109           geometry: {
26110             type: "MultiPolygon",
26111             coordinates: [[[[16.50139, 46.56684], [16.39217, 46.63673], [16.38594, 46.6549], [16.41863, 46.66238], [16.42641, 46.69228], [16.37816, 46.69975], [16.30966, 46.7787], [16.31303, 46.79838], [16.3408, 46.80641], [16.34547, 46.83836], [16.2941, 46.87137], [16.2365, 46.87775], [16.21892, 46.86961], [16.15711, 46.85434], [16.14365, 46.8547], [16.10983, 46.867], [16.05786, 46.83927], [15.99054, 46.82772], [15.99126, 46.78199], [15.98432, 46.74991], [15.99769, 46.7266], [16.02808, 46.71094], [16.04347, 46.68694], [16.04036, 46.6549], [15.99988, 46.67947], [15.98512, 46.68463], [15.94864, 46.68769], [15.87691, 46.7211], [15.8162, 46.71897], [15.78518, 46.70712], [15.76771, 46.69863], [15.73823, 46.70011], [15.72279, 46.69548], [15.69523, 46.69823], [15.67411, 46.70735], [15.6543, 46.70616], [15.6543, 46.69228], [15.6365, 46.6894], [15.63255, 46.68069], [15.62317, 46.67947], [15.59826, 46.68908], [15.54533, 46.66985], [15.55333, 46.64988], [15.54431, 46.6312], [15.46906, 46.61321], [15.45514, 46.63697], [15.41235, 46.65556], [15.23711, 46.63994], [15.14215, 46.66131], [15.01451, 46.641], [14.98024, 46.6009], [14.96002, 46.63459], [14.92283, 46.60848], [14.87129, 46.61], [14.86419, 46.59411], [14.83549, 46.56614], [14.81836, 46.51046], [14.72185, 46.49974], [14.66892, 46.44936], [14.5942, 46.43434], [14.56463, 46.37208], [14.52176, 46.42617], [14.45877, 46.41717], [14.42608, 46.44614], [14.314, 46.43327], [14.28326, 46.44315], [14.15989, 46.43327], [14.12097, 46.47724], [14.04002, 46.49117], [14.00422, 46.48474], [13.89837, 46.52331], [13.7148, 46.5222], [13.68684, 46.43881], [13.59777, 46.44137], [13.5763, 46.42613], [13.5763, 46.40915], [13.47019, 46.3621], [13.43418, 46.35992], [13.44808, 46.33507], [13.37671, 46.29668], [13.42218, 46.20758], [13.47587, 46.22725], [13.56114, 46.2054], [13.56682, 46.18703], [13.64451, 46.18966], [13.66472, 46.17392], [13.64053, 46.13587], [13.57072, 46.09022], [13.50104, 46.05986], [13.49568, 46.04839], [13.50998, 46.04498], [13.49702, 46.01832], [13.47474, 46.00546], [13.50104, 45.98078], [13.52963, 45.96588], [13.56759, 45.96991], [13.58903, 45.99009], [13.62074, 45.98388], [13.63458, 45.98947], [13.64307, 45.98326], [13.6329, 45.94894], [13.63815, 45.93607], [13.61931, 45.91782], [13.60857, 45.89907], [13.59565, 45.89446], [13.58644, 45.88173], [13.57563, 45.8425], [13.58858, 45.83503], [13.59784, 45.8072], [13.66986, 45.79955], [13.8235, 45.7176], [13.83332, 45.70855], [13.83422, 45.68703], [13.87933, 45.65207], [13.9191, 45.6322], [13.8695, 45.60835], [13.86771, 45.59898], [13.84106, 45.58185], [13.78445, 45.5825], [13.74587, 45.59811], [13.7198, 45.59352], [13.6076, 45.64761], [13.45644, 45.59464], [13.56979, 45.4895], [13.62902, 45.45898], [13.67398, 45.4436], [13.7785, 45.46787], [13.81742, 45.43729], [13.88124, 45.42637], [13.90771, 45.45149], [13.97309, 45.45258], [13.99488, 45.47551], [13.96063, 45.50825], [14.00578, 45.52352], [14.07116, 45.48752], [14.20348, 45.46896], [14.22371, 45.50388], [14.24239, 45.50607], [14.26611, 45.48239], [14.27681, 45.4902], [14.32487, 45.47142], [14.36693, 45.48642], [14.49769, 45.54424], [14.5008, 45.60852], [14.53816, 45.6205], [14.57397, 45.67165], [14.60977, 45.66403], [14.59576, 45.62812], [14.69694, 45.57366], [14.68605, 45.53006], [14.71718, 45.53442], [14.80124, 45.49515], [14.81992, 45.45913], [14.90554, 45.47769], [14.92266, 45.52788], [15.02385, 45.48533], [15.05187, 45.49079], [15.16862, 45.42309], [15.27758, 45.46678], [15.33051, 45.45258], [15.38188, 45.48752], [15.30249, 45.53224], [15.29837, 45.5841], [15.27747, 45.60504], [15.31027, 45.6303], [15.34695, 45.63382], [15.34214, 45.64702], [15.38952, 45.63682], [15.4057, 45.64727], [15.34919, 45.71623], [15.30872, 45.69014], [15.25423, 45.72275], [15.40836, 45.79491], [15.47531, 45.79802], [15.47325, 45.8253], [15.52234, 45.82195], [15.57952, 45.84953], [15.64185, 45.82915], [15.66662, 45.84085], [15.70411, 45.8465], [15.68232, 45.86819], [15.68383, 45.88867], [15.67967, 45.90455], [15.70636, 45.92116], [15.70327, 46.00015], [15.71246, 46.01196], [15.72977, 46.04682], [15.62317, 46.09103], [15.6083, 46.11992], [15.59909, 46.14761], [15.64904, 46.19229], [15.6434, 46.21396], [15.67395, 46.22478], [15.75436, 46.21969], [15.75479, 46.20336], [15.78817, 46.21719], [15.79284, 46.25811], [15.97965, 46.30652], [16.07616, 46.3463], [16.07314, 46.36458], [16.05065, 46.3833], [16.05281, 46.39141], [16.14859, 46.40547], [16.18824, 46.38282], [16.30233, 46.37837], [16.30162, 46.40437], [16.27329, 46.41467], [16.27398, 46.42875], [16.25124, 46.48067], [16.23961, 46.49653], [16.26759, 46.50566], [16.26733, 46.51505], [16.29793, 46.5121], [16.37193, 46.55008], [16.38771, 46.53608], [16.44036, 46.5171], [16.5007, 46.49644], [16.52604, 46.47831], [16.59527, 46.47524], [16.52604, 46.5051], [16.52885, 46.53303], [16.50139, 46.56684]]]]
26112           }
26113         }, {
26114           type: "Feature",
26115           properties: {
26116             iso1A2: "SJ",
26117             iso1A3: "SJM",
26118             iso1N3: "744",
26119             wikidata: "Q842829",
26120             nameEn: "Svalbard and Jan Mayen",
26121             country: "NO"
26122           },
26123           geometry: null
26124         }, {
26125           type: "Feature",
26126           properties: {
26127             iso1A2: "SK",
26128             iso1A3: "SVK",
26129             iso1N3: "703",
26130             wikidata: "Q214",
26131             nameEn: "Slovakia",
26132             groups: ["EU", "151", "150", "UN"],
26133             callingCodes: ["421"]
26134           },
26135           geometry: {
26136             type: "MultiPolygon",
26137             coordinates: [[[[19.82237, 49.27806], [19.78581, 49.41701], [19.72127, 49.39288], [19.6375, 49.40897], [19.64162, 49.45184], [19.57845, 49.46077], [19.53313, 49.52856], [19.52626, 49.57311], [19.45348, 49.61583], [19.37795, 49.574], [19.36009, 49.53747], [19.25435, 49.53391], [19.18019, 49.41165], [18.9742, 49.39557], [18.97283, 49.49914], [18.94536, 49.52143], [18.84521, 49.51672], [18.74761, 49.492], [18.67757, 49.50895], [18.6144, 49.49824], [18.57183, 49.51162], [18.53063, 49.49022], [18.54848, 49.47059], [18.44686, 49.39467], [18.4084, 49.40003], [18.4139, 49.36517], [18.36446, 49.3267], [18.18456, 49.28909], [18.15022, 49.24518], [18.1104, 49.08624], [18.06885, 49.03157], [17.91814, 49.01784], [17.87831, 48.92679], [17.77944, 48.92318], [17.73126, 48.87885], [17.7094, 48.86721], [17.5295, 48.81117], [17.45671, 48.85004], [17.3853, 48.80936], [17.29054, 48.85546], [17.19355, 48.87602], [17.11202, 48.82925], [17.00215, 48.70887], [16.93955, 48.60371], [16.94611, 48.53614], [16.85204, 48.44968], [16.8497, 48.38321], [16.83588, 48.3844], [16.83317, 48.38138], [16.84243, 48.35258], [16.90903, 48.32519], [16.89461, 48.31332], [16.97701, 48.17385], [17.02919, 48.13996], [17.05735, 48.14179], [17.09168, 48.09366], [17.07039, 48.0317], [17.16001, 48.00636], [17.23699, 48.02094], [17.71215, 47.7548], [18.02938, 47.75665], [18.29305, 47.73541], [18.56496, 47.76588], [18.66521, 47.76772], [18.74074, 47.8157], [18.8506, 47.82308], [18.76821, 47.87469], [18.76134, 47.97499], [18.82176, 48.04206], [19.01952, 48.07052], [19.23924, 48.0595], [19.28182, 48.08336], [19.47957, 48.09437], [19.52489, 48.19791], [19.63338, 48.25006], [19.92452, 48.1283], [20.24312, 48.2784], [20.29943, 48.26104], [20.5215, 48.53336], [20.83248, 48.5824], [21.11516, 48.49546], [21.44063, 48.58456], [21.6068, 48.50365], [21.67134, 48.3989], [21.72525, 48.34628], [21.8279, 48.33321], [21.83339, 48.36242], [22.14689, 48.4005], [22.16023, 48.56548], [22.21379, 48.6218], [22.34151, 48.68893], [22.42934, 48.92857], [22.48296, 48.99172], [22.54338, 49.01424], [22.56155, 49.08865], [22.04427, 49.22136], [21.96385, 49.3437], [21.82927, 49.39467], [21.77983, 49.35443], [21.62328, 49.4447], [21.43376, 49.41433], [21.27858, 49.45988], [21.19756, 49.4054], [21.12477, 49.43666], [21.041, 49.41791], [21.09799, 49.37176], [20.98733, 49.30774], [20.9229, 49.29626], [20.77971, 49.35383], [20.72274, 49.41813], [20.61666, 49.41791], [20.5631, 49.375], [20.46422, 49.41612], [20.39939, 49.3896], [20.31728, 49.39914], [20.31453, 49.34817], [20.21977, 49.35265], [20.13738, 49.31685], [20.08238, 49.1813], [19.98494, 49.22904], [19.90529, 49.23532], [19.86409, 49.19316], [19.75286, 49.20751], [19.82237, 49.27806]]]]
26138           }
26139         }, {
26140           type: "Feature",
26141           properties: {
26142             iso1A2: "SL",
26143             iso1A3: "SLE",
26144             iso1N3: "694",
26145             wikidata: "Q1044",
26146             nameEn: "Sierra Leone",
26147             groups: ["011", "202", "002", "UN"],
26148             callingCodes: ["232"]
26149           },
26150           geometry: {
26151             type: "MultiPolygon",
26152             coordinates: [[[[-10.27575, 8.48711], [-10.37257, 8.48941], [-10.54891, 8.31174], [-10.63934, 8.35326], [-10.70565, 8.29235], [-10.61422, 8.5314], [-10.47707, 8.67669], [-10.56197, 8.81225], [-10.5783, 9.06386], [-10.74484, 9.07998], [-10.6534, 9.29919], [-11.2118, 10.00098], [-11.89624, 9.99763], [-11.91023, 9.93927], [-12.12634, 9.87203], [-12.24262, 9.92386], [-12.47254, 9.86834], [-12.76788, 9.3133], [-12.94095, 9.26335], [-13.08953, 9.0409], [-13.18586, 9.0925], [-13.29911, 9.04245], [-14.36218, 8.64107], [-12.15048, 6.15992], [-11.50429, 6.92704], [-11.4027, 6.97746], [-11.29417, 7.21576], [-10.60422, 7.7739], [-10.60492, 8.04072], [-10.57523, 8.04829], [-10.51554, 8.1393], [-10.45023, 8.15627], [-10.35227, 8.15223], [-10.29839, 8.21283], [-10.31635, 8.28554], [-10.30084, 8.30008], [-10.27575, 8.48711]]]]
26153           }
26154         }, {
26155           type: "Feature",
26156           properties: {
26157             iso1A2: "SM",
26158             iso1A3: "SMR",
26159             iso1N3: "674",
26160             wikidata: "Q238",
26161             nameEn: "San Marino",
26162             groups: ["039", "150", "UN"],
26163             callingCodes: ["378"]
26164           },
26165           geometry: {
26166             type: "MultiPolygon",
26167             coordinates: [[[[12.45648, 43.89369], [12.48771, 43.89706], [12.49429, 43.90973], [12.49247, 43.91774], [12.49724, 43.92248], [12.50269, 43.92363], [12.50496, 43.93017], [12.51553, 43.94096], [12.51427, 43.94897], [12.50655, 43.95796], [12.50875, 43.96198], [12.50622, 43.97131], [12.51109, 43.97201], [12.51064, 43.98165], [12.5154, 43.98508], [12.51463, 43.99122], [12.50678, 43.99113], [12.49406, 43.98492], [12.47853, 43.98052], [12.46205, 43.97463], [12.44684, 43.96597], [12.43662, 43.95698], [12.42005, 43.9578], [12.41414, 43.95273], [12.40415, 43.95485], [12.40506, 43.94325], [12.41165, 43.93769], [12.41551, 43.92984], [12.40733, 43.92379], [12.41233, 43.90956], [12.40935, 43.9024], [12.41641, 43.89991], [12.44184, 43.90498], [12.45648, 43.89369]]]]
26168           }
26169         }, {
26170           type: "Feature",
26171           properties: {
26172             iso1A2: "SN",
26173             iso1A3: "SEN",
26174             iso1N3: "686",
26175             wikidata: "Q1041",
26176             nameEn: "Senegal",
26177             groups: ["011", "202", "002", "UN"],
26178             callingCodes: ["221"]
26179           },
26180           geometry: {
26181             type: "MultiPolygon",
26182             coordinates: [[[[-14.32144, 16.61495], [-15.00557, 16.64997], [-15.6509, 16.50315], [-16.27016, 16.51565], [-16.4429, 16.20605], [-16.44814, 16.09753], [-16.48967, 16.0496], [-16.50854, 16.09032], [-17.15288, 16.07139], [-18.35085, 14.63444], [-17.43598, 13.59273], [-15.47902, 13.58758], [-15.36504, 13.79313], [-14.93719, 13.80173], [-14.34721, 13.46578], [-13.8955, 13.59126], [-13.79409, 13.34472], [-14.36795, 13.23033], [-15.14917, 13.57989], [-15.26908, 13.37768], [-15.80478, 13.34832], [-15.80355, 13.16729], [-16.69343, 13.16791], [-16.74676, 13.06025], [-17.43966, 13.04579], [-17.4623, 11.92379], [-16.70562, 12.34803], [-16.38191, 12.36449], [-16.20591, 12.46157], [-15.67302, 12.42974], [-15.17582, 12.6847], [-13.70523, 12.68013], [-13.05296, 12.64003], [-13.06603, 12.49342], [-12.87336, 12.51892], [-12.35415, 12.32758], [-11.91331, 12.42008], [-11.46267, 12.44559], [-11.37536, 12.40788], [-11.39935, 12.97808], [-11.63025, 13.39174], [-11.83345, 13.33333], [-12.06897, 13.71049], [-11.93043, 13.84505], [-12.23936, 14.76324], [-13.11029, 15.52116], [-13.43135, 16.09022], [-13.80075, 16.13961], [-14.32144, 16.61495]]]]
26183           }
26184         }, {
26185           type: "Feature",
26186           properties: {
26187             iso1A2: "SO",
26188             iso1A3: "SOM",
26189             iso1N3: "706",
26190             wikidata: "Q1045",
26191             nameEn: "Somalia",
26192             groups: ["014", "202", "002", "UN"],
26193             callingCodes: ["252"]
26194           },
26195           geometry: {
26196             type: "MultiPolygon",
26197             coordinates: [[[[51.12877, 12.56479], [43.90659, 12.3823], [42.95776, 10.98533], [42.69452, 10.62672], [42.87643, 10.18441], [43.0937, 9.90579], [43.23518, 9.84605], [43.32613, 9.59205], [44.19222, 8.93028], [46.99339, 7.9989], [47.92477, 8.00111], [47.97917, 8.00124], [44.98104, 4.91821], [44.02436, 4.9451], [43.40263, 4.79289], [43.04177, 4.57923], [42.97746, 4.44032], [42.84526, 4.28357], [42.55853, 4.20518], [42.07619, 4.17667], [41.89488, 3.97375], [41.31368, 3.14314], [40.98767, 2.82959], [41.00099, -0.83068], [41.56, -1.59812], [41.56362, -1.66375], [41.75542, -1.85308], [57.49095, 8.14549], [51.12877, 12.56479]]]]
26198           }
26199         }, {
26200           type: "Feature",
26201           properties: {
26202             iso1A2: "SR",
26203             iso1A3: "SUR",
26204             iso1N3: "740",
26205             wikidata: "Q730",
26206             nameEn: "Suriname",
26207             groups: ["005", "419", "019", "UN"],
26208             driveSide: "left",
26209             callingCodes: ["597"]
26210           },
26211           geometry: {
26212             type: "MultiPolygon",
26213             coordinates: [[[[-54.26916, 5.26909], [-54.01877, 5.52789], [-54.01074, 5.68785], [-53.7094, 6.2264], [-56.84822, 6.73257], [-57.31629, 5.33714], [-57.22536, 5.15605], [-57.37442, 5.0208], [-57.8699, 4.89394], [-58.0307, 3.95513], [-57.35891, 3.32121], [-56.70519, 2.02964], [-56.55439, 2.02003], [-56.47045, 1.95135], [-55.99278, 1.83137], [-55.89863, 1.89861], [-55.92159, 2.05236], [-56.13054, 2.27723], [-55.96292, 2.53188], [-55.71493, 2.40342], [-55.01919, 2.564], [-54.6084, 2.32856], [-54.42864, 2.42442], [-54.28534, 2.67798], [-53.9849, 3.58697], [-53.98914, 3.627], [-54.05128, 3.63557], [-54.19367, 3.84387], [-54.38444, 4.13222], [-54.4717, 4.91964], [-54.26916, 5.26909]]]]
26214           }
26215         }, {
26216           type: "Feature",
26217           properties: {
26218             iso1A2: "SS",
26219             iso1A3: "SSD",
26220             iso1N3: "728",
26221             wikidata: "Q958",
26222             nameEn: "South Sudan",
26223             groups: ["014", "202", "002", "UN"],
26224             callingCodes: ["211"]
26225           },
26226           geometry: {
26227             type: "MultiPolygon",
26228             coordinates: [[[[34.10229, 9.50238], [33.87958, 9.49937], [33.9082, 9.762], [33.96323, 9.80972], [33.99185, 9.99623], [33.96984, 10.15446], [33.90159, 10.17179], [33.80913, 10.32994], [33.66604, 10.44254], [33.52294, 10.64382], [33.24645, 10.77913], [33.26977, 10.83632], [33.13988, 11.43248], [33.25876, 12.22111], [32.73921, 12.22757], [32.73921, 11.95203], [32.10079, 11.95203], [32.39578, 11.70208], [32.39358, 11.18207], [32.46967, 11.04662], [31.99177, 10.65065], [31.77539, 10.28939], [31.28504, 9.75287], [30.84605, 9.7498], [30.82893, 9.71451], [30.53005, 9.95992], [30.00389, 10.28633], [29.94629, 10.29245], [29.54, 10.07949], [29.53844, 9.75133], [29.06988, 9.74826], [28.99983, 9.67155], [27.90704, 9.61323], [27.14427, 9.62858], [26.70685, 9.48735], [26.35815, 9.57946], [26.21338, 9.91545], [25.93241, 10.17941], [25.93163, 10.38159], [25.78141, 10.42599], [25.0918, 10.33718], [25.05688, 10.06776], [24.97739, 9.9081], [24.84653, 9.80643], [24.49389, 9.79962], [24.12744, 9.73784], [24.09319, 9.66572], [23.69155, 9.67566], [23.62179, 9.53823], [23.64981, 9.44303], [23.64358, 9.28637], [23.56263, 9.19418], [23.4848, 9.16959], [23.44744, 8.99128], [23.59065, 8.99743], [23.51905, 8.71749], [24.25691, 8.69288], [24.13238, 8.36959], [24.35965, 8.26177], [24.85156, 8.16933], [24.98855, 7.96588], [25.25319, 7.8487], [25.29214, 7.66675], [25.20649, 7.61115], [25.20337, 7.50312], [25.35281, 7.42595], [25.37461, 7.33024], [25.90076, 7.09549], [26.38022, 6.63493], [26.32729, 6.36272], [26.58259, 6.1987], [26.51721, 6.09655], [27.22705, 5.71254], [27.22705, 5.62889], [27.28621, 5.56382], [27.23017, 5.37167], [27.26886, 5.25876], [27.44012, 5.07349], [27.56656, 4.89375], [27.65462, 4.89375], [27.76469, 4.79284], [27.79551, 4.59976], [28.20719, 4.35614], [28.6651, 4.42638], [28.8126, 4.48784], [29.03054, 4.48784], [29.22207, 4.34297], [29.43341, 4.50101], [29.49726, 4.7007], [29.82087, 4.56246], [29.79666, 4.37809], [30.06964, 4.13221], [30.1621, 4.10586], [30.22374, 3.93896], [30.27658, 3.95653], [30.47691, 3.83353], [30.55396, 3.84451], [30.57378, 3.74567], [30.56277, 3.62703], [30.78512, 3.67097], [30.80713, 3.60506], [30.85997, 3.5743], [30.85153, 3.48867], [30.97601, 3.693], [31.16666, 3.79853], [31.29476, 3.8015], [31.50478, 3.67814], [31.50776, 3.63652], [31.72075, 3.74354], [31.81459, 3.82083], [31.86821, 3.78664], [31.96205, 3.6499], [31.95907, 3.57408], [32.05187, 3.589], [32.08491, 3.56287], [32.08866, 3.53543], [32.19888, 3.50867], [32.20782, 3.6053], [32.41337, 3.748], [32.72021, 3.77327], [32.89746, 3.81339], [33.02852, 3.89296], [33.18356, 3.77812], [33.51264, 3.75068], [33.9873, 4.23316], [34.47601, 4.72162], [35.34151, 5.02364], [35.30992, 4.90402], [35.47843, 4.91872], [35.42366, 4.76969], [35.51424, 4.61643], [35.9419, 4.61933], [35.82118, 4.77382], [35.81968, 5.10757], [35.8576, 5.33413], [35.50792, 5.42431], [35.29938, 5.34042], [35.31188, 5.50106], [35.13058, 5.62118], [35.12611, 5.68937], [35.00546, 5.89387], [34.96227, 6.26415], [35.01738, 6.46991], [34.87736, 6.60161], [34.77459, 6.5957], [34.65096, 6.72589], [34.53776, 6.74808], [34.53925, 6.82794], [34.47669, 6.91076], [34.35753, 6.91963], [34.19369, 7.04382], [34.19369, 7.12807], [34.01495, 7.25664], [34.03878, 7.27437], [34.02984, 7.36449], [33.87642, 7.5491], [33.71407, 7.65983], [33.44745, 7.7543], [33.32531, 7.71297], [33.24637, 7.77939], [33.04944, 7.78989], [33.0006, 7.90333], [33.08401, 8.05822], [33.18083, 8.13047], [33.1853, 8.29264], [33.19721, 8.40317], [33.3119, 8.45474], [33.54575, 8.47094], [33.66938, 8.44442], [33.71407, 8.3678], [33.87195, 8.41938], [33.89579, 8.4842], [34.01346, 8.50041], [34.14453, 8.60204], [34.14304, 9.04654], [34.10229, 9.50238]]]]
26229           }
26230         }, {
26231           type: "Feature",
26232           properties: {
26233             iso1A2: "ST",
26234             iso1A3: "STP",
26235             iso1N3: "678",
26236             wikidata: "Q1039",
26237             nameEn: "S\xE3o Tom\xE9 and Principe",
26238             groups: ["017", "202", "002", "UN"],
26239             callingCodes: ["239"]
26240           },
26241           geometry: {
26242             type: "MultiPolygon",
26243             coordinates: [[[[4.34149, 1.91417], [6.6507, -0.28606], [7.9035, 1.92304], [4.34149, 1.91417]]]]
26244           }
26245         }, {
26246           type: "Feature",
26247           properties: {
26248             iso1A2: "SV",
26249             iso1A3: "SLV",
26250             iso1N3: "222",
26251             wikidata: "Q792",
26252             nameEn: "El Salvador",
26253             groups: ["013", "003", "419", "019", "UN"],
26254             callingCodes: ["503"]
26255           },
26256           geometry: {
26257             type: "MultiPolygon",
26258             coordinates: [[[[-89.34776, 14.43013], [-89.39028, 14.44561], [-89.57441, 14.41637], [-89.58814, 14.33165], [-89.50614, 14.26084], [-89.52397, 14.22628], [-89.61844, 14.21937], [-89.70756, 14.1537], [-89.75569, 14.07073], [-89.73251, 14.04133], [-89.76103, 14.02923], [-89.81807, 14.07073], [-89.88937, 14.0396], [-90.10505, 13.85104], [-90.11344, 13.73679], [-90.55276, 12.8866], [-88.11443, 12.63306], [-87.7346, 13.13228], [-87.55124, 13.12523], [-87.69751, 13.25228], [-87.73714, 13.32715], [-87.80177, 13.35689], [-87.84675, 13.41078], [-87.83467, 13.44655], [-87.77354, 13.45767], [-87.73841, 13.44169], [-87.72115, 13.46083], [-87.71657, 13.50577], [-87.78148, 13.52906], [-87.73106, 13.75443], [-87.68821, 13.80829], [-87.7966, 13.91353], [-88.00331, 13.86948], [-88.07641, 13.98447], [-88.23018, 13.99915], [-88.25791, 13.91108], [-88.48982, 13.86458], [-88.49738, 13.97224], [-88.70661, 14.04317], [-88.73182, 14.10919], [-88.815, 14.11652], [-88.85785, 14.17763], [-88.94608, 14.20207], [-89.04187, 14.33644], [-89.34776, 14.43013]]]]
26259           }
26260         }, {
26261           type: "Feature",
26262           properties: {
26263             iso1A2: "SX",
26264             iso1A3: "SXM",
26265             iso1N3: "534",
26266             wikidata: "Q26273",
26267             nameEn: "Sint Maarten",
26268             aliases: ["NL-SX"],
26269             country: "NL",
26270             groups: ["Q1451600", "029", "003", "419", "019", "UN"],
26271             callingCodes: ["1 721"]
26272           },
26273           geometry: {
26274             type: "MultiPolygon",
26275             coordinates: [[[[-63.33064, 17.9615], [-63.1055, 17.86651], [-62.93924, 18.02904], [-63.02323, 18.05757], [-63.04039, 18.05619], [-63.0579, 18.06614], [-63.07759, 18.04943], [-63.09686, 18.04608], [-63.11042, 18.05339], [-63.13502, 18.05445], [-63.33064, 17.9615]]]]
26276           }
26277         }, {
26278           type: "Feature",
26279           properties: {
26280             iso1A2: "SY",
26281             iso1A3: "SYR",
26282             iso1N3: "760",
26283             wikidata: "Q858",
26284             nameEn: "Syria",
26285             groups: ["145", "142", "UN"],
26286             callingCodes: ["963"]
26287           },
26288           geometry: {
26289             type: "MultiPolygon",
26290             coordinates: [[[[42.23683, 37.2863], [42.21548, 37.28026], [42.20454, 37.28715], [42.22381, 37.30238], [42.22257, 37.31395], [42.2112, 37.32491], [42.19301, 37.31323], [42.18225, 37.28569], [42.00894, 37.17209], [41.515, 37.08084], [41.21937, 37.07665], [40.90856, 37.13147], [40.69136, 37.0996], [39.81589, 36.75538], [39.21538, 36.66834], [39.03217, 36.70911], [38.74042, 36.70629], [38.55908, 36.84429], [38.38859, 36.90064], [38.21064, 36.91842], [37.81974, 36.76055], [37.68048, 36.75065], [37.49103, 36.66904], [37.47253, 36.63243], [37.21988, 36.6736], [37.16177, 36.66069], [37.10894, 36.6704], [37.08279, 36.63495], [37.02088, 36.66422], [37.01647, 36.69512], [37.04619, 36.71101], [37.04399, 36.73483], [36.99886, 36.74012], [36.99557, 36.75997], [36.66727, 36.82901], [36.61581, 36.74629], [36.62681, 36.71189], [36.57398, 36.65186], [36.58829, 36.58295], [36.54206, 36.49539], [36.6081, 36.33772], [36.65653, 36.33861], [36.68672, 36.23677], [36.6125, 36.22592], [36.50463, 36.2419], [36.4617, 36.20461], [36.39206, 36.22088], [36.37474, 36.01163], [36.33956, 35.98687], [36.30099, 36.00985], [36.28338, 36.00273], [36.29769, 35.96086], [36.27678, 35.94839], [36.25366, 35.96264], [36.19973, 35.95195], [36.17441, 35.92076], [36.1623, 35.80925], [36.14029, 35.81015], [36.13919, 35.83692], [36.11827, 35.85923], [35.99829, 35.88242], [36.01844, 35.92403], [36.00514, 35.94113], [35.98499, 35.94107], [35.931, 35.92109], [35.51152, 36.10954], [35.48515, 34.70851], [35.97386, 34.63322], [35.98718, 34.64977], [36.29165, 34.62991], [36.32399, 34.69334], [36.35135, 34.68516], [36.35384, 34.65447], [36.42941, 34.62505], [36.46003, 34.6378], [36.45299, 34.59438], [36.41429, 34.61175], [36.39846, 34.55672], [36.3369, 34.52629], [36.34745, 34.5002], [36.4442, 34.50165], [36.46179, 34.46541], [36.55853, 34.41609], [36.53039, 34.3798], [36.56556, 34.31881], [36.60778, 34.31009], [36.58667, 34.27667], [36.59195, 34.2316], [36.62537, 34.20251], [36.5128, 34.09916], [36.50576, 34.05982], [36.41078, 34.05253], [36.28589, 33.91981], [36.38263, 33.86579], [36.3967, 33.83365], [36.14517, 33.85118], [36.06778, 33.82927], [35.9341, 33.6596], [36.05723, 33.57904], [35.94465, 33.52774], [35.94816, 33.47886], [35.88668, 33.43183], [35.82577, 33.40479], [35.81324, 33.36354], [35.77477, 33.33609], [35.813, 33.3172], [35.77513, 33.27342], [35.81295, 33.24841], [35.81647, 33.2028], [35.83846, 33.19397], [35.84285, 33.16673], [35.81911, 33.1336], [35.81911, 33.11077], [35.84802, 33.1031], [35.87188, 32.98028], [35.89298, 32.9456], [35.87012, 32.91976], [35.84021, 32.8725], [35.83758, 32.82817], [35.78745, 32.77938], [35.75983, 32.74803], [35.88405, 32.71321], [35.93307, 32.71966], [35.96633, 32.66237], [36.02239, 32.65911], [36.08074, 32.51463], [36.20379, 32.52751], [36.20875, 32.49529], [36.23948, 32.50108], [36.40959, 32.37908], [36.83946, 32.31293], [38.79171, 33.37328], [40.64314, 34.31604], [40.97676, 34.39788], [41.12388, 34.65742], [41.2345, 34.80049], [41.21654, 35.1508], [41.26569, 35.42708], [41.38184, 35.62502], [41.37027, 35.84095], [41.2564, 36.06012], [41.28864, 36.35368], [41.40058, 36.52502], [41.81736, 36.58782], [42.36697, 37.0627], [42.35724, 37.10998], [42.32313, 37.17814], [42.34735, 37.22548], [42.2824, 37.2798], [42.26039, 37.27017], [42.23683, 37.2863]]]]
26291           }
26292         }, {
26293           type: "Feature",
26294           properties: {
26295             iso1A2: "SZ",
26296             iso1A3: "SWZ",
26297             iso1N3: "748",
26298             wikidata: "Q1050",
26299             nameEn: "Eswatini",
26300             aliases: ["Swaziland"],
26301             groups: ["018", "202", "002", "UN"],
26302             driveSide: "left",
26303             callingCodes: ["268"]
26304           },
26305           geometry: {
26306             type: "MultiPolygon",
26307             coordinates: [[[[31.86881, -25.99973], [31.4175, -25.71886], [31.31237, -25.7431], [31.13073, -25.91558], [30.95819, -26.26303], [30.78927, -26.48271], [30.81101, -26.84722], [30.88826, -26.79622], [30.97757, -26.92706], [30.96088, -27.0245], [31.15027, -27.20151], [31.49834, -27.31549], [31.97592, -27.31675], [31.97463, -27.11057], [32.00893, -26.8096], [32.09664, -26.80721], [32.13315, -26.84345], [32.13409, -26.5317], [32.07352, -26.40185], [32.10435, -26.15656], [32.08599, -26.00978], [32.00916, -25.999], [31.974, -25.95387], [31.86881, -25.99973]]]]
26308           }
26309         }, {
26310           type: "Feature",
26311           properties: {
26312             iso1A2: "TA",
26313             iso1A3: "TAA",
26314             wikidata: "Q220982",
26315             nameEn: "Tristan da Cunha",
26316             aliases: ["SH-TA"],
26317             country: "GB",
26318             groups: ["SH", "BOTS", "011", "202", "002", "UN"],
26319             isoStatus: "excRes",
26320             driveSide: "left",
26321             roadSpeedUnit: "mph",
26322             roadHeightUnit: "ft",
26323             callingCodes: ["290 8", "44 20"]
26324           },
26325           geometry: {
26326             type: "MultiPolygon",
26327             coordinates: [[[[-13.38232, -34.07258], [-16.67337, -41.9188], [-5.88482, -41.4829], [-13.38232, -34.07258]]]]
26328           }
26329         }, {
26330           type: "Feature",
26331           properties: {
26332             iso1A2: "TC",
26333             iso1A3: "TCA",
26334             iso1N3: "796",
26335             wikidata: "Q18221",
26336             nameEn: "Turks and Caicos Islands",
26337             country: "GB",
26338             groups: ["BOTS", "029", "003", "419", "019", "UN"],
26339             driveSide: "left",
26340             roadSpeedUnit: "mph",
26341             roadHeightUnit: "ft",
26342             callingCodes: ["1 649"]
26343           },
26344           geometry: {
26345             type: "MultiPolygon",
26346             coordinates: [[[[-71.70065, 25.7637], [-72.98446, 20.4801], [-69.80718, 21.35956], [-71.70065, 25.7637]]]]
26347           }
26348         }, {
26349           type: "Feature",
26350           properties: {
26351             iso1A2: "TD",
26352             iso1A3: "TCD",
26353             iso1N3: "148",
26354             wikidata: "Q657",
26355             nameEn: "Chad",
26356             groups: ["017", "202", "002", "UN"],
26357             callingCodes: ["235"]
26358           },
26359           geometry: {
26360             type: "MultiPolygon",
26361             coordinates: [[[[23.99539, 19.49944], [15.99566, 23.49639], [14.99751, 23.00539], [15.19692, 21.99339], [15.20213, 21.49365], [15.28332, 21.44557], [15.62515, 20.95395], [15.57248, 20.92138], [15.55382, 20.86507], [15.56004, 20.79488], [15.59841, 20.74039], [15.6721, 20.70069], [15.99632, 20.35364], [15.75098, 19.93002], [15.6032, 18.77402], [15.50373, 16.89649], [14.37425, 15.72591], [13.86301, 15.04043], [13.78991, 14.87519], [13.809, 14.72915], [13.67878, 14.64013], [13.68573, 14.55276], [13.48259, 14.46704], [13.47559, 14.40881], [13.6302, 13.71094], [14.08251, 13.0797], [14.46881, 13.08259], [14.56101, 12.91036], [14.55058, 12.78256], [14.83314, 12.62963], [14.90827, 12.3269], [14.89019, 12.16593], [14.96952, 12.0925], [15.00146, 12.1223], [15.0349, 12.10698], [15.05786, 12.0608], [15.04808, 11.8731], [15.11579, 11.79313], [15.06595, 11.71126], [15.13149, 11.5537], [15.0585, 11.40481], [15.10021, 11.04101], [15.04957, 11.02347], [15.09127, 10.87431], [15.06737, 10.80921], [15.15532, 10.62846], [15.14936, 10.53915], [15.23724, 10.47764], [15.30874, 10.31063], [15.50535, 10.1098], [15.68761, 9.99344], [15.41408, 9.92876], [15.24618, 9.99246], [15.14043, 9.99246], [15.05999, 9.94845], [14.95722, 9.97926], [14.80082, 9.93818], [14.4673, 10.00264], [14.20411, 10.00055], [14.1317, 9.82413], [14.01793, 9.73169], [13.97544, 9.6365], [14.37094, 9.2954], [14.35707, 9.19611], [14.83566, 8.80557], [15.09484, 8.65982], [15.20426, 8.50892], [15.50743, 7.79302], [15.59272, 7.7696], [15.56964, 7.58936], [15.49743, 7.52179], [15.73118, 7.52006], [15.79942, 7.44149], [16.40703, 7.68809], [16.41583, 7.77971], [16.58315, 7.88657], [16.59415, 7.76444], [16.658, 7.75353], [16.6668, 7.67281], [16.8143, 7.53971], [17.67288, 7.98905], [17.93926, 7.95853], [18.02731, 8.01085], [18.6085, 8.05009], [18.64153, 8.08714], [18.62612, 8.14163], [18.67455, 8.22226], [18.79783, 8.25929], [19.11044, 8.68172], [18.86388, 8.87971], [19.06421, 9.00367], [20.36748, 9.11019], [20.82979, 9.44696], [21.26348, 9.97642], [21.34934, 9.95907], [21.52766, 10.2105], [21.63553, 10.217], [21.71479, 10.29932], [21.72139, 10.64136], [22.45889, 11.00246], [22.87758, 10.91915], [22.97249, 11.21955], [22.93124, 11.41645], [22.7997, 11.40424], [22.54907, 11.64372], [22.64092, 12.07485], [22.48369, 12.02766], [22.50548, 12.16769], [22.38873, 12.45514], [22.46345, 12.61925], [22.22684, 12.74682], [22.15679, 12.66634], [21.98711, 12.63292], [21.89371, 12.68001], [21.81432, 12.81362], [21.94819, 13.05637], [22.02914, 13.13976], [22.1599, 13.19281], [22.29689, 13.3731], [22.08674, 13.77863], [22.22995, 13.96754], [22.5553, 14.11704], [22.55997, 14.23024], [22.44944, 14.24986], [22.38562, 14.58907], [22.70474, 14.69149], [22.66115, 14.86308], [22.99584, 15.22989], [22.99584, 15.40105], [22.92579, 15.47007], [22.93201, 15.55107], [23.10792, 15.71297], [23.38812, 15.69649], [23.62785, 15.7804], [23.99997, 15.69575], [23.99539, 19.49944]]]]
26362           }
26363         }, {
26364           type: "Feature",
26365           properties: {
26366             iso1A2: "TF",
26367             iso1A3: "ATF",
26368             iso1N3: "260",
26369             wikidata: "Q129003",
26370             nameEn: "French Southern Territories",
26371             country: "FR"
26372           },
26373           geometry: null
26374         }, {
26375           type: "Feature",
26376           properties: {
26377             iso1A2: "TG",
26378             iso1A3: "TGO",
26379             iso1N3: "768",
26380             wikidata: "Q945",
26381             nameEn: "Togo",
26382             groups: ["011", "202", "002", "UN"],
26383             callingCodes: ["228"]
26384           },
26385           geometry: {
26386             type: "MultiPolygon",
26387             coordinates: [[[[0.50388, 11.01011], [-0.13493, 11.14075], [-0.14462, 11.10811], [-0.05733, 11.08628], [-0.0275, 11.11202], [-514e-5, 11.10763], [342e-5, 11.08317], [0.02395, 11.06229], [0.03355, 10.9807], [-63e-4, 10.96417], [-908e-5, 10.91644], [-0.02685, 10.8783], [-0.0228, 10.81916], [-0.07183, 10.76794], [-0.07327, 10.71845], [-0.09141, 10.7147], [-0.05945, 10.63458], [0.12886, 10.53149], [0.18846, 10.4096], [0.29453, 10.41546], [0.33028, 10.30408], [0.39584, 10.31112], [0.35293, 10.09412], [0.41371, 10.06361], [0.41252, 10.02018], [0.36366, 10.03309], [0.32075, 9.72781], [0.34816, 9.71607], [0.34816, 9.66907], [0.32313, 9.6491], [0.28261, 9.69022], [0.26712, 9.66437], [0.29334, 9.59387], [0.36008, 9.6256], [0.38153, 9.58682], [0.23851, 9.57389], [0.2409, 9.52335], [0.30406, 9.521], [0.31241, 9.50337], [0.2254, 9.47869], [0.25758, 9.42696], [0.33148, 9.44812], [0.36485, 9.49749], [0.49118, 9.48339], [0.56388, 9.40697], [0.45424, 9.04581], [0.52455, 8.87746], [0.37319, 8.75262], [0.47211, 8.59945], [0.64731, 8.48866], [0.73432, 8.29529], [0.63897, 8.25873], [0.5913, 8.19622], [0.61156, 8.18324], [0.6056, 8.13959], [0.58891, 8.12779], [0.62943, 7.85751], [0.58295, 7.62368], [0.51979, 7.58706], [0.52455, 7.45354], [0.57223, 7.39326], [0.62943, 7.41099], [0.65327, 7.31643], [0.59606, 7.01252], [0.52217, 6.9723], [0.52098, 6.94391], [0.56508, 6.92971], [0.52853, 6.82921], [0.57406, 6.80348], [0.58176, 6.76049], [0.6497, 6.73682], [0.63659, 6.63857], [0.74862, 6.56517], [0.71048, 6.53083], [0.89283, 6.33779], [0.99652, 6.33779], [1.03108, 6.24064], [1.05969, 6.22998], [1.09187, 6.17074], [1.19966, 6.17069], [1.19771, 6.11522], [1.27574, 5.93551], [1.67336, 6.02702], [1.62913, 6.24075], [1.79826, 6.28221], [1.76906, 6.43189], [1.58105, 6.68619], [1.61812, 6.74843], [1.55877, 6.99737], [1.64249, 6.99562], [1.61838, 9.0527], [1.5649, 9.16941], [1.41746, 9.3226], [1.33675, 9.54765], [1.36624, 9.5951], [1.35507, 9.99525], [0.77666, 10.37665], [0.80358, 10.71459], [0.8804, 10.803], [0.91245, 10.99597], [0.66104, 10.99964], [0.4958, 10.93269], [0.50521, 10.98035], [0.48852, 10.98561], [0.50388, 11.01011]]]]
26388           }
26389         }, {
26390           type: "Feature",
26391           properties: {
26392             iso1A2: "TH",
26393             iso1A3: "THA",
26394             iso1N3: "764",
26395             wikidata: "Q869",
26396             nameEn: "Thailand",
26397             groups: ["035", "142", "UN"],
26398             driveSide: "left",
26399             callingCodes: ["66"]
26400           },
26401           geometry: {
26402             type: "MultiPolygon",
26403             coordinates: [[[[100.08404, 20.36626], [99.95721, 20.46301], [99.91616, 20.44986], [99.90499, 20.4487], [99.89692, 20.44789], [99.89301, 20.44311], [99.89168, 20.44548], [99.88451, 20.44596], [99.88211, 20.44488], [99.86383, 20.44371], [99.81096, 20.33687], [99.68255, 20.32077], [99.46008, 20.39673], [99.46077, 20.36198], [99.5569, 20.20676], [99.52943, 20.14811], [99.416, 20.08614], [99.20328, 20.12877], [99.0735, 20.10298], [98.98679, 19.7419], [98.83661, 19.80931], [98.56065, 19.67807], [98.51182, 19.71303], [98.24884, 19.67876], [98.13829, 19.78541], [98.03314, 19.80941], [98.04364, 19.65755], [97.84715, 19.55782], [97.88423, 19.5041], [97.78769, 19.39429], [97.84186, 19.29526], [97.78606, 19.26769], [97.84024, 19.22217], [97.83479, 19.09972], [97.73797, 19.04261], [97.73654, 18.9812], [97.66487, 18.9371], [97.73836, 18.88478], [97.76752, 18.58097], [97.5258, 18.4939], [97.36444, 18.57138], [97.34522, 18.54596], [97.50383, 18.26844], [97.56219, 18.33885], [97.64116, 18.29778], [97.60841, 18.23846], [97.73723, 17.97912], [97.66794, 17.88005], [97.76407, 17.71595], [97.91829, 17.54504], [98.11185, 17.36829], [98.10439, 17.33847], [98.34566, 17.04822], [98.39441, 17.06266], [98.52624, 16.89979], [98.49603, 16.8446], [98.53833, 16.81934], [98.46994, 16.73613], [98.50253, 16.7139], [98.49713, 16.69022], [98.51043, 16.70107], [98.51579, 16.69433], [98.51472, 16.68521], [98.51833, 16.676], [98.51113, 16.64503], [98.5695, 16.62826], [98.57912, 16.55983], [98.63817, 16.47424], [98.68074, 16.27068], [98.84485, 16.42354], [98.92656, 16.36425], [98.8376, 16.11706], [98.69585, 16.13353], [98.57019, 16.04578], [98.59853, 15.87197], [98.541, 15.65406], [98.58598, 15.46821], [98.56027, 15.33471], [98.4866, 15.39154], [98.39351, 15.34177], [98.41906, 15.27103], [98.40522, 15.25268], [98.30446, 15.30667], [98.22, 15.21327], [98.18821, 15.13125], [98.24874, 14.83013], [98.56762, 14.37701], [98.97356, 14.04868], [99.16695, 13.72621], [99.20617, 13.20575], [99.12225, 13.19847], [99.10646, 13.05804], [99.18748, 12.9898], [99.18905, 12.84799], [99.29254, 12.68921], [99.409, 12.60603], [99.47519, 12.1353], [99.56445, 12.14805], [99.53424, 12.02317], [99.64891, 11.82699], [99.64108, 11.78948], [99.5672, 11.62732], [99.47598, 11.62434], [99.39485, 11.3925], [99.31573, 11.32081], [99.32756, 11.28545], [99.06938, 10.94857], [99.02337, 10.97217], [98.99701, 10.92962], [99.0069, 10.85485], [98.86819, 10.78336], [98.78511, 10.68351], [98.77275, 10.62548], [98.81944, 10.52761], [98.7391, 10.31488], [98.55174, 9.92804], [98.52291, 9.92389], [98.47298, 9.95782], [98.33094, 9.91973], [98.12555, 9.44056], [97.63455, 9.60854], [97.19814, 8.18901], [99.31854, 5.99868], [99.50117, 6.44501], [99.91873, 6.50233], [100.0756, 6.4045], [100.12, 6.42105], [100.19511, 6.72559], [100.29651, 6.68439], [100.30828, 6.66462], [100.31618, 6.66781], [100.31884, 6.66423], [100.32671, 6.66526], [100.32607, 6.65933], [100.31929, 6.65413], [100.35413, 6.54932], [100.41152, 6.52299], [100.41791, 6.5189], [100.42351, 6.51762], [100.43027, 6.52389], [100.66986, 6.45086], [100.74361, 6.50811], [100.74822, 6.46231], [100.81045, 6.45086], [100.85884, 6.24929], [101.10313, 6.25617], [101.12618, 6.19431], [101.06165, 6.14161], [101.12388, 6.11411], [101.087, 5.9193], [101.02708, 5.91013], [100.98815, 5.79464], [101.14062, 5.61613], [101.25755, 5.71065], [101.25524, 5.78633], [101.58019, 5.93534], [101.69773, 5.75881], [101.75074, 5.79091], [101.80144, 5.74505], [101.89188, 5.8386], [101.91776, 5.84269], [101.92819, 5.85511], [101.94712, 5.98421], [101.9714, 6.00575], [101.97114, 6.01992], [101.99209, 6.04075], [102.01835, 6.05407], [102.09182, 6.14161], [102.07732, 6.193], [102.08127, 6.22679], [102.09086, 6.23546], [102.46318, 7.22462], [102.47649, 9.66162], [102.52395, 11.25257], [102.91449, 11.65512], [102.90973, 11.75613], [102.83957, 11.8519], [102.78427, 11.98746], [102.77026, 12.06815], [102.70176, 12.1686], [102.73134, 12.37091], [102.78116, 12.40284], [102.7796, 12.43781], [102.57567, 12.65358], [102.51963, 12.66117], [102.4994, 12.71736], [102.53053, 12.77506], [102.49335, 12.92711], [102.48694, 12.97537], [102.52275, 12.99813], [102.46011, 13.08057], [102.43422, 13.09061], [102.36146, 13.26006], [102.36001, 13.31142], [102.34611, 13.35618], [102.35692, 13.38274], [102.35563, 13.47307], [102.361, 13.50551], [102.33828, 13.55613], [102.36859, 13.57488], [102.44601, 13.5637], [102.5358, 13.56933], [102.57573, 13.60461], [102.62483, 13.60883], [102.58635, 13.6286], [102.5481, 13.6589], [102.56848, 13.69366], [102.72727, 13.77806], [102.77864, 13.93374], [102.91251, 14.01531], [102.93275, 14.19044], [103.16469, 14.33075], [103.39353, 14.35639], [103.53518, 14.42575], [103.71109, 14.4348], [103.70175, 14.38052], [103.93836, 14.3398], [104.27616, 14.39861], [104.55014, 14.36091], [104.69335, 14.42726], [104.97667, 14.38806], [105.02804, 14.23722], [105.08408, 14.20402], [105.14012, 14.23873], [105.17748, 14.34432], [105.20894, 14.34967], [105.43783, 14.43865], [105.53864, 14.55731], [105.5121, 14.80802], [105.61162, 15.00037], [105.46661, 15.13132], [105.58043, 15.32724], [105.50662, 15.32054], [105.4692, 15.33709], [105.47635, 15.3796], [105.58191, 15.41031], [105.60446, 15.53301], [105.61756, 15.68792], [105.46573, 15.74742], [105.42285, 15.76971], [105.37959, 15.84074], [105.34115, 15.92737], [105.38508, 15.987], [105.42001, 16.00657], [105.06204, 16.09792], [105.00262, 16.25627], [104.88057, 16.37311], [104.73349, 16.565], [104.76099, 16.69302], [104.7397, 16.81005], [104.76442, 16.84752], [104.7373, 16.91125], [104.73712, 17.01404], [104.80716, 17.19025], [104.80061, 17.39367], [104.69867, 17.53038], [104.45404, 17.66788], [104.35432, 17.82871], [104.2757, 17.86139], [104.21776, 17.99335], [104.10927, 18.10826], [104.06533, 18.21656], [103.97725, 18.33631], [103.93916, 18.33914], [103.85642, 18.28666], [103.82449, 18.33979], [103.699, 18.34125], [103.60957, 18.40528], [103.47773, 18.42841], [103.41044, 18.4486], [103.30977, 18.4341], [103.24779, 18.37807], [103.23818, 18.34875], [103.29757, 18.30475], [103.17093, 18.2618], [103.14994, 18.23172], [103.1493, 18.17799], [103.07343, 18.12351], [103.07823, 18.03833], [103.0566, 18.00144], [103.01998, 17.97095], [102.9912, 17.9949], [102.95812, 18.0054], [102.86323, 17.97531], [102.81988, 17.94233], [102.79044, 17.93612], [102.75954, 17.89561], [102.68538, 17.86653], [102.67543, 17.84529], [102.69946, 17.81686], [102.68194, 17.80151], [102.59485, 17.83537], [102.5896, 17.84889], [102.61432, 17.92273], [102.60971, 17.95411], [102.59234, 17.96127], [102.45523, 17.97106], [102.11359, 18.21532], [101.88485, 18.02474], [101.78087, 18.07559], [101.72294, 17.92867], [101.44667, 17.7392], [101.15108, 17.47586], [100.96541, 17.57926], [101.02185, 17.87637], [101.1793, 18.0544], [101.19118, 18.2125], [101.15108, 18.25624], [101.18227, 18.34367], [101.06047, 18.43247], [101.27585, 18.68875], [101.22832, 18.73377], [101.25803, 18.89545], [101.35606, 19.04716], [101.261, 19.12717], [101.24911, 19.33334], [101.20604, 19.35296], [101.21347, 19.46223], [101.26991, 19.48324], [101.26545, 19.59242], [101.08928, 19.59748], [100.90302, 19.61901], [100.77231, 19.48324], [100.64606, 19.55884], [100.58219, 19.49164], [100.49604, 19.53504], [100.398, 19.75047], [100.5094, 19.87904], [100.58808, 20.15791], [100.55218, 20.17741], [100.51052, 20.14928], [100.47567, 20.19133], [100.4537, 20.19971], [100.44992, 20.23644], [100.41473, 20.25625], [100.37439, 20.35156], [100.33383, 20.4028], [100.25769, 20.3992], [100.22076, 20.31598], [100.16668, 20.2986], [100.1712, 20.24324], [100.11785, 20.24787], [100.09337, 20.26293], [100.09999, 20.31614], [100.08404, 20.36626]]]]
26404           }
26405         }, {
26406           type: "Feature",
26407           properties: {
26408             iso1A2: "TJ",
26409             iso1A3: "TJK",
26410             iso1N3: "762",
26411             wikidata: "Q863",
26412             nameEn: "Tajikistan",
26413             groups: ["143", "142", "UN"],
26414             callingCodes: ["992"]
26415           },
26416           geometry: {
26417             type: "MultiPolygon",
26418             coordinates: [[[[70.45251, 41.04438], [70.38028, 41.02014], [70.36655, 40.90296], [69.69434, 40.62615], [69.59441, 40.70181], [69.53021, 40.77621], [69.38327, 40.7918], [69.32834, 40.70233], [69.3455, 40.57988], [69.2643, 40.57506], [69.21063, 40.54469], [69.27066, 40.49274], [69.28525, 40.41894], [69.30774, 40.36102], [69.33794, 40.34819], [69.32833, 40.29794], [69.30808, 40.2821], [69.24817, 40.30357], [69.25229, 40.26362], [69.30104, 40.24502], [69.30448, 40.18774], [69.2074, 40.21488], [69.15659, 40.2162], [69.04544, 40.22904], [68.85832, 40.20885], [68.84357, 40.18604], [68.79276, 40.17555], [68.77902, 40.20492], [68.5332, 40.14826], [68.52771, 40.11676], [68.62796, 40.07789], [69.01523, 40.15771], [69.01935, 40.11466], [68.96579, 40.06949], [68.84906, 40.04952], [68.93695, 39.91167], [68.88889, 39.87163], [68.63071, 39.85265], [68.61972, 39.68905], [68.54166, 39.53929], [68.12053, 39.56317], [67.70992, 39.66156], [67.62889, 39.60234], [67.44899, 39.57799], [67.46547, 39.53564], [67.39681, 39.52505], [67.46822, 39.46146], [67.45998, 39.315], [67.36522, 39.31287], [67.33226, 39.23739], [67.67833, 39.14479], [67.68915, 39.00775], [68.09704, 39.02589], [68.19743, 38.85985], [68.06948, 38.82115], [68.12877, 38.73677], [68.05598, 38.71641], [68.0807, 38.64136], [68.05873, 38.56087], [68.11366, 38.47169], [68.06274, 38.39435], [68.13289, 38.40822], [68.40343, 38.19484], [68.27159, 37.91477], [68.12635, 37.93], [67.81566, 37.43107], [67.8474, 37.31594], [67.78329, 37.1834], [67.7803, 37.08978], [67.87917, 37.0591], [68.02194, 36.91923], [68.18542, 37.02074], [68.27605, 37.00977], [68.29253, 37.10621], [68.41201, 37.10402], [68.41888, 37.13906], [68.61851, 37.19815], [68.6798, 37.27906], [68.81438, 37.23862], [68.80889, 37.32494], [68.91189, 37.26704], [68.88168, 37.33368], [68.96407, 37.32603], [69.03274, 37.25174], [69.25152, 37.09426], [69.39529, 37.16752], [69.45022, 37.23315], [69.36645, 37.40462], [69.44954, 37.4869], [69.51888, 37.5844], [69.80041, 37.5746], [69.84435, 37.60616], [69.93362, 37.61378], [69.95971, 37.5659], [70.15015, 37.52519], [70.28243, 37.66706], [70.27694, 37.81258], [70.1863, 37.84296], [70.17206, 37.93276], [70.4898, 38.12546], [70.54673, 38.24541], [70.60407, 38.28046], [70.61526, 38.34774], [70.64966, 38.34999], [70.69189, 38.37031], [70.6761, 38.39144], [70.67438, 38.40597], [70.69807, 38.41861], [70.72485, 38.4131], [70.75455, 38.4252], [70.77132, 38.45548], [70.78581, 38.45502], [70.78702, 38.45031], [70.79766, 38.44944], [70.80521, 38.44447], [70.81697, 38.44507], [70.82538, 38.45394], [70.84376, 38.44688], [70.88719, 38.46826], [70.92728, 38.43021], [70.98693, 38.48862], [71.03545, 38.44779], [71.0556, 38.40176], [71.09542, 38.42517], [71.10592, 38.42077], [71.10957, 38.40671], [71.1451, 38.40106], [71.21291, 38.32797], [71.33114, 38.30339], [71.33869, 38.27335], [71.37803, 38.25641], [71.36444, 38.15358], [71.29878, 38.04429], [71.28922, 38.01272], [71.27622, 37.99946], [71.27278, 37.96496], [71.24969, 37.93031], [71.2809, 37.91995], [71.296, 37.93403], [71.32871, 37.88564], [71.51565, 37.95349], [71.58843, 37.92425], [71.59255, 37.79956], [71.55752, 37.78677], [71.54324, 37.77104], [71.53053, 37.76534], [71.55234, 37.73209], [71.54186, 37.69691], [71.51972, 37.61945], [71.5065, 37.60912], [71.49693, 37.53527], [71.50616, 37.50733], [71.5256, 37.47971], [71.49612, 37.4279], [71.47685, 37.40281], [71.4862, 37.33405], [71.49821, 37.31975], [71.50674, 37.31502], [71.48536, 37.26017], [71.4824, 37.24921], [71.48339, 37.23937], [71.47386, 37.2269], [71.4555, 37.21418], [71.4494, 37.18137], [71.44127, 37.11856], [71.43097, 37.05855], [71.45578, 37.03094], [71.46923, 36.99925], [71.48481, 36.93218], [71.51502, 36.89128], [71.57195, 36.74943], [71.67083, 36.67346], [71.83229, 36.68084], [72.31676, 36.98115], [72.54095, 37.00007], [72.66381, 37.02014], [72.79693, 37.22222], [73.06884, 37.31729], [73.29633, 37.46495], [73.77197, 37.4417], [73.76647, 37.33913], [73.61129, 37.27469], [73.64974, 37.23643], [73.82552, 37.22659], [73.8564, 37.26158], [74.20308, 37.34208], [74.23339, 37.41116], [74.41055, 37.3948], [74.56161, 37.37734], [74.68383, 37.3948], [74.8294, 37.3435], [74.88887, 37.23275], [75.12328, 37.31839], [75.09719, 37.37297], [75.15899, 37.41443], [75.06011, 37.52779], [74.94338, 37.55501], [74.8912, 37.67576], [75.00935, 37.77486], [74.92416, 37.83428], [74.9063, 38.03033], [74.82665, 38.07359], [74.80331, 38.19889], [74.69894, 38.22155], [74.69619, 38.42947], [74.51217, 38.47034], [74.17022, 38.65504], [73.97933, 38.52945], [73.79806, 38.61106], [73.80656, 38.66449], [73.7033, 38.84782], [73.7445, 38.93867], [73.82964, 38.91517], [73.81728, 39.04007], [73.75823, 39.023], [73.60638, 39.24534], [73.54572, 39.27567], [73.55396, 39.3543], [73.5004, 39.38402], [73.59241, 39.40843], [73.59831, 39.46425], [73.45096, 39.46677], [73.31912, 39.38615], [73.18454, 39.35536], [72.85934, 39.35116], [72.62027, 39.39696], [72.33173, 39.33093], [72.23834, 39.17248], [72.17242, 39.2661], [72.09689, 39.26823], [72.04059, 39.36704], [71.90601, 39.27674], [71.79202, 39.27355], [71.7522, 39.32031], [71.80164, 39.40631], [71.76816, 39.45456], [71.62688, 39.44056], [71.5517, 39.45722], [71.55856, 39.57588], [71.49814, 39.61397], [71.08752, 39.50704], [71.06418, 39.41586], [70.7854, 39.38933], [70.64087, 39.58792], [70.44757, 39.60128], [70.2869, 39.53141], [70.11111, 39.58223], [69.87491, 39.53882], [69.68677, 39.59281], [69.3594, 39.52516], [69.26938, 39.8127], [69.35649, 40.01994], [69.43134, 39.98431], [69.43557, 39.92877], [69.53615, 39.93991], [69.5057, 40.03277], [69.53855, 40.0887], [69.53794, 40.11833], [69.55555, 40.12296], [69.57615, 40.10524], [69.64704, 40.12165], [69.67001, 40.10639], [70.01283, 40.23288], [70.58297, 40.00891], [70.57384, 39.99394], [70.47557, 39.93216], [70.55033, 39.96619], [70.58912, 39.95211], [70.65946, 39.9878], [70.65827, 40.0981], [70.7928, 40.12797], [70.80495, 40.16813], [70.9818, 40.22392], [70.8607, 40.217], [70.62342, 40.17396], [70.56394, 40.26421], [70.57149, 40.3442], [70.37511, 40.38605], [70.32626, 40.45174], [70.49871, 40.52503], [70.80009, 40.72825], [70.45251, 41.04438]]], [[[70.68112, 40.90612], [70.6158, 40.97661], [70.56077, 41.00642], [70.54223, 40.98787], [70.57501, 40.98941], [70.6721, 40.90555], [70.68112, 40.90612]]], [[[70.74189, 39.86319], [70.53651, 39.89155], [70.52631, 39.86989], [70.54998, 39.85137], [70.59667, 39.83542], [70.63105, 39.77923], [70.74189, 39.86319]]]]
26419           }
26420         }, {
26421           type: "Feature",
26422           properties: {
26423             iso1A2: "TK",
26424             iso1A3: "TKL",
26425             iso1N3: "772",
26426             wikidata: "Q36823",
26427             nameEn: "Tokelau",
26428             country: "NZ",
26429             groups: ["061", "009", "UN"],
26430             driveSide: "left",
26431             callingCodes: ["690"]
26432           },
26433           geometry: {
26434             type: "MultiPolygon",
26435             coordinates: [[[[-168.251, -9.44289], [-174.18635, -7.80441], [-174.17993, -10.13616], [-168.251, -9.44289]]]]
26436           }
26437         }, {
26438           type: "Feature",
26439           properties: {
26440             iso1A2: "TL",
26441             iso1A3: "TLS",
26442             iso1N3: "626",
26443             wikidata: "Q574",
26444             nameEn: "East Timor",
26445             aliases: ["Timor-Leste", "TP"],
26446             groups: ["035", "142", "UN"],
26447             driveSide: "left",
26448             callingCodes: ["670"]
26449           },
26450           geometry: {
26451             type: "MultiPolygon",
26452             coordinates: [[[[124.46701, -9.13002], [124.94011, -8.85617], [124.97742, -9.08128], [125.11764, -8.96359], [125.18632, -9.03142], [125.18907, -9.16434], [125.09434, -9.19669], [125.04044, -9.17093], [124.97892, -9.19281], [125.09025, -9.46406], [125.68138, -9.85176], [127.55165, -9.05052], [127.42116, -8.22471], [125.87691, -8.31789], [125.58506, -7.95311], [124.92337, -8.75859], [124.33472, -9.11416], [124.04628, -9.22671], [124.04286, -9.34243], [124.10539, -9.41206], [124.14517, -9.42324], [124.21247, -9.36904], [124.28115, -9.42189], [124.28115, -9.50453], [124.3535, -9.48493], [124.35258, -9.43002], [124.38554, -9.3582], [124.45971, -9.30263], [124.46701, -9.13002]]]]
26453           }
26454         }, {
26455           type: "Feature",
26456           properties: {
26457             iso1A2: "TM",
26458             iso1A3: "TKM",
26459             iso1N3: "795",
26460             wikidata: "Q874",
26461             nameEn: "Turkmenistan",
26462             groups: ["143", "142", "UN"],
26463             callingCodes: ["993"]
26464           },
26465           geometry: {
26466             type: "MultiPolygon",
26467             coordinates: [[[[60.5078, 41.21694], [60.06581, 41.4363], [60.18117, 41.60082], [60.06032, 41.76287], [60.08504, 41.80997], [60.33223, 41.75058], [59.95046, 41.97966], [60.0356, 42.01028], [60.04659, 42.08982], [59.96419, 42.1428], [60.00539, 42.212], [59.94633, 42.27655], [59.4341, 42.29738], [59.2955, 42.37064], [59.17317, 42.52248], [58.93422, 42.5407], [58.6266, 42.79314], [58.57991, 42.64988], [58.27504, 42.69632], [58.14321, 42.62159], [58.29427, 42.56497], [58.51674, 42.30348], [58.40688, 42.29535], [58.3492, 42.43335], [57.99214, 42.50021], [57.90975, 42.4374], [57.92897, 42.24047], [57.84932, 42.18555], [57.6296, 42.16519], [57.30275, 42.14076], [57.03633, 41.92043], [56.96218, 41.80383], [57.03359, 41.41777], [57.13796, 41.36625], [57.03423, 41.25435], [56.00314, 41.32584], [55.45471, 41.25609], [54.95182, 41.92424], [54.20635, 42.38477], [52.97575, 42.1308], [52.47884, 41.78034], [52.26048, 41.69249], [51.7708, 40.29239], [53.89734, 37.3464], [54.24565, 37.32047], [54.36211, 37.34912], [54.58664, 37.45809], [54.67247, 37.43532], [54.77822, 37.51597], [54.81804, 37.61285], [54.77684, 37.62264], [54.851, 37.75739], [55.13412, 37.94705], [55.44152, 38.08564], [55.76561, 38.12238], [55.97847, 38.08024], [56.33278, 38.08132], [56.32454, 38.18502], [56.43303, 38.26054], [56.62255, 38.24005], [56.73928, 38.27887], [57.03453, 38.18717], [57.21169, 38.28965], [57.37236, 38.09321], [57.35042, 37.98546], [57.79534, 37.89299], [58.21399, 37.77281], [58.22999, 37.6856], [58.39959, 37.63134], [58.47786, 37.6433], [58.5479, 37.70526], [58.6921, 37.64548], [58.9338, 37.67374], [59.22905, 37.51161], [59.33507, 37.53146], [59.39797, 37.47892], [59.39385, 37.34257], [59.55178, 37.13594], [59.74678, 37.12499], [60.00768, 37.04102], [60.34767, 36.63214], [61.14516, 36.64644], [61.18187, 36.55348], [61.1393, 36.38782], [61.22719, 36.12759], [61.12007, 35.95992], [61.22444, 35.92879], [61.26152, 35.80749], [61.22719, 35.67038], [61.27371, 35.61482], [61.58742, 35.43803], [61.77693, 35.41341], [61.97743, 35.4604], [62.05709, 35.43803], [62.15871, 35.33278], [62.29191, 35.25964], [62.29878, 35.13312], [62.48006, 35.28796], [62.62288, 35.22067], [62.74098, 35.25432], [62.90853, 35.37086], [63.0898, 35.43131], [63.12276, 35.53196], [63.10079, 35.63024], [63.23262, 35.67487], [63.10318, 35.81782], [63.12276, 35.86208], [63.29579, 35.85985], [63.53475, 35.90881], [63.56496, 35.95106], [63.98519, 36.03773], [64.05385, 36.10433], [64.43288, 36.24401], [64.57295, 36.34362], [64.62514, 36.44311], [64.61141, 36.6351], [64.97945, 37.21913], [65.51778, 37.23881], [65.64263, 37.34388], [65.64137, 37.45061], [65.72274, 37.55438], [66.30993, 37.32409], [66.55743, 37.35409], [66.52303, 37.39827], [66.65761, 37.45497], [66.52852, 37.58568], [66.53676, 37.80084], [66.67684, 37.96776], [66.56697, 38.0435], [66.41042, 38.02403], [66.24013, 38.16238], [65.83913, 38.25733], [65.55873, 38.29052], [64.32576, 38.98691], [64.19086, 38.95561], [63.70778, 39.22349], [63.6913, 39.27666], [62.43337, 39.98528], [62.34273, 40.43206], [62.11751, 40.58242], [61.87856, 41.12257], [61.4446, 41.29407], [61.39732, 41.19873], [61.33199, 41.14946], [61.22212, 41.14946], [61.03261, 41.25691], [60.5078, 41.21694]]]]
26468           }
26469         }, {
26470           type: "Feature",
26471           properties: {
26472             iso1A2: "TN",
26473             iso1A3: "TUN",
26474             iso1N3: "788",
26475             wikidata: "Q948",
26476             nameEn: "Tunisia",
26477             groups: ["015", "002", "UN"],
26478             callingCodes: ["216"]
26479           },
26480           geometry: {
26481             type: "MultiPolygon",
26482             coordinates: [[[[11.2718, 37.6713], [7.89009, 38.19924], [8.59123, 37.14286], [8.64044, 36.9401], [8.62972, 36.86499], [8.67706, 36.8364], [8.57613, 36.78062], [8.46537, 36.7706], [8.47609, 36.66607], [8.16167, 36.48817], [8.18936, 36.44939], [8.40731, 36.42208], [8.2626, 35.91733], [8.26472, 35.73669], [8.35371, 35.66373], [8.36086, 35.47774], [8.30329, 35.29884], [8.47318, 35.23376], [8.3555, 35.10007], [8.30727, 34.95378], [8.25189, 34.92009], [8.29655, 34.72798], [8.20482, 34.57575], [7.86264, 34.3987], [7.81242, 34.21841], [7.74207, 34.16492], [7.66174, 34.20167], [7.52851, 34.06493], [7.54088, 33.7726], [7.73687, 33.42114], [7.83028, 33.18851], [8.11433, 33.10175], [8.1179, 33.05086], [8.31895, 32.83483], [8.35999, 32.50101], [9.07483, 32.07865], [9.55544, 30.23971], [9.76848, 30.34366], [9.88152, 30.34074], [10.29516, 30.90337], [10.12239, 31.42098], [10.31364, 31.72648], [10.48497, 31.72956], [10.62788, 31.96629], [10.7315, 31.97235], [11.04234, 32.2145], [11.53898, 32.4138], [11.57828, 32.48013], [11.46037, 32.6307], [11.51549, 33.09826], [11.55852, 33.1409], [11.58941, 33.36891], [11.2718, 37.6713]]]]
26483           }
26484         }, {
26485           type: "Feature",
26486           properties: {
26487             iso1A2: "TO",
26488             iso1A3: "TON",
26489             iso1N3: "776",
26490             wikidata: "Q678",
26491             nameEn: "Tonga",
26492             groups: ["061", "009", "UN"],
26493             driveSide: "left",
26494             callingCodes: ["676"]
26495           },
26496           geometry: {
26497             type: "MultiPolygon",
26498             coordinates: [[[[-176.74538, -22.89767], [-180, -22.90585], [-180, -24.21376], [-173.10761, -24.19665], [-173.13438, -14.94228], [-176.76826, -14.95183], [-176.74538, -22.89767]]]]
26499           }
26500         }, {
26501           type: "Feature",
26502           properties: {
26503             iso1A2: "TR",
26504             iso1A3: "TUR",
26505             iso1N3: "792",
26506             wikidata: "Q43",
26507             nameEn: "Turkey",
26508             groups: ["145", "142", "UN"],
26509             callingCodes: ["90"]
26510           },
26511           geometry: {
26512             type: "MultiPolygon",
26513             coordinates: [[[[41.54366, 41.52185], [40.89217, 41.72528], [34.8305, 42.4581], [28.32297, 41.98371], [28.02971, 41.98066], [27.91479, 41.97902], [27.83492, 41.99709], [27.81235, 41.94803], [27.69949, 41.97515], [27.55191, 41.90928], [27.52379, 41.93756], [27.45478, 41.96591], [27.27411, 42.10409], [27.22376, 42.10152], [27.19251, 42.06028], [27.08486, 42.08735], [27.03277, 42.0809], [26.95638, 42.00741], [26.79143, 41.97386], [26.62996, 41.97644], [26.56051, 41.92995], [26.57961, 41.90024], [26.53968, 41.82653], [26.36952, 41.82265], [26.33589, 41.76802], [26.32952, 41.73637], [26.35957, 41.71149], [26.47958, 41.67037], [26.5209, 41.62592], [26.59196, 41.60491], [26.59742, 41.48058], [26.61767, 41.42281], [26.62997, 41.34613], [26.5837, 41.32131], [26.5209, 41.33993], [26.39861, 41.25053], [26.32259, 41.24929], [26.31928, 41.07386], [26.3606, 41.02027], [26.33297, 40.98388], [26.35894, 40.94292], [26.32259, 40.94042], [26.28623, 40.93005], [26.29441, 40.89119], [26.26169, 40.9168], [26.20856, 40.86048], [26.21351, 40.83298], [26.15685, 40.80709], [26.12854, 40.77339], [26.12495, 40.74283], [26.08638, 40.73214], [26.0754, 40.72772], [26.03489, 40.73051], [25.94795, 40.72797], [26.04292, 40.3958], [25.61285, 40.17161], [25.94257, 39.39358], [26.43357, 39.43096], [26.70773, 39.0312], [26.61814, 38.81372], [26.21136, 38.65436], [26.32173, 38.48731], [26.24183, 38.44695], [26.21136, 38.17558], [27.05537, 37.9131], [27.16428, 37.72343], [26.99377, 37.69034], [26.95583, 37.64989], [27.14757, 37.32], [27.20312, 36.94571], [27.45627, 36.9008], [27.24613, 36.71622], [27.46117, 36.53789], [27.89482, 36.69898], [27.95037, 36.46155], [28.23708, 36.56812], [29.30783, 36.01033], [29.48192, 36.18377], [29.61002, 36.1731], [29.61805, 36.14179], [29.69611, 36.10365], [29.73302, 35.92555], [32.82353, 35.70297], [35.51152, 36.10954], [35.931, 35.92109], [35.98499, 35.94107], [36.00514, 35.94113], [36.01844, 35.92403], [35.99829, 35.88242], [36.11827, 35.85923], [36.13919, 35.83692], [36.14029, 35.81015], [36.1623, 35.80925], [36.17441, 35.92076], [36.19973, 35.95195], [36.25366, 35.96264], [36.27678, 35.94839], [36.29769, 35.96086], [36.28338, 36.00273], [36.30099, 36.00985], [36.33956, 35.98687], [36.37474, 36.01163], [36.39206, 36.22088], [36.4617, 36.20461], [36.50463, 36.2419], [36.6125, 36.22592], [36.68672, 36.23677], [36.65653, 36.33861], [36.6081, 36.33772], [36.54206, 36.49539], [36.58829, 36.58295], [36.57398, 36.65186], [36.62681, 36.71189], [36.61581, 36.74629], [36.66727, 36.82901], [36.99557, 36.75997], [36.99886, 36.74012], [37.04399, 36.73483], [37.04619, 36.71101], [37.01647, 36.69512], [37.02088, 36.66422], [37.08279, 36.63495], [37.10894, 36.6704], [37.16177, 36.66069], [37.21988, 36.6736], [37.47253, 36.63243], [37.49103, 36.66904], [37.68048, 36.75065], [37.81974, 36.76055], [38.21064, 36.91842], [38.38859, 36.90064], [38.55908, 36.84429], [38.74042, 36.70629], [39.03217, 36.70911], [39.21538, 36.66834], [39.81589, 36.75538], [40.69136, 37.0996], [40.90856, 37.13147], [41.21937, 37.07665], [41.515, 37.08084], [42.00894, 37.17209], [42.18225, 37.28569], [42.19301, 37.31323], [42.2112, 37.32491], [42.22257, 37.31395], [42.22381, 37.30238], [42.20454, 37.28715], [42.21548, 37.28026], [42.23683, 37.2863], [42.26039, 37.27017], [42.2824, 37.2798], [42.34735, 37.22548], [42.32313, 37.17814], [42.35724, 37.10998], [42.56725, 37.14878], [42.78887, 37.38615], [42.93705, 37.32015], [43.11403, 37.37436], [43.30083, 37.30629], [43.33508, 37.33105], [43.50787, 37.24436], [43.56702, 37.25675], [43.63085, 37.21957], [43.7009, 37.23692], [43.8052, 37.22825], [43.82699, 37.19477], [43.84878, 37.22205], [43.90949, 37.22453], [44.02002, 37.33229], [44.13521, 37.32486], [44.2613, 37.25055], [44.27998, 37.16501], [44.22239, 37.15756], [44.18503, 37.09551], [44.25975, 36.98119], [44.30645, 36.97373], [44.35937, 37.02843], [44.35315, 37.04955], [44.38117, 37.05825], [44.42631, 37.05825], [44.63179, 37.19229], [44.76698, 37.16162], [44.78319, 37.1431], [44.7868, 37.16644], [44.75986, 37.21549], [44.81021, 37.2915], [44.58449, 37.45018], [44.61401, 37.60165], [44.56887, 37.6429], [44.62096, 37.71985], [44.55498, 37.783], [44.45948, 37.77065], [44.3883, 37.85433], [44.22509, 37.88859], [44.42476, 38.25763], [44.50115, 38.33939], [44.44386, 38.38295], [44.38309, 38.36117], [44.3119, 38.37887], [44.3207, 38.49799], [44.32058, 38.62752], [44.28065, 38.6465], [44.26155, 38.71427], [44.30322, 38.81581], [44.18863, 38.93881], [44.20946, 39.13975], [44.1043, 39.19842], [44.03667, 39.39223], [44.22452, 39.4169], [44.29818, 39.378], [44.37921, 39.4131], [44.42832, 39.4131], [44.41849, 39.56659], [44.48111, 39.61579], [44.47298, 39.68788], [44.6137, 39.78393], [44.65422, 39.72163], [44.71806, 39.71124], [44.81043, 39.62677], [44.80977, 39.65768], [44.75779, 39.7148], [44.61845, 39.8281], [44.46635, 39.97733], [44.26973, 40.04866], [44.1778, 40.02845], [44.1057, 40.03555], [43.92307, 40.01787], [43.65688, 40.11199], [43.65221, 40.14889], [43.71136, 40.16673], [43.59928, 40.34019], [43.60862, 40.43267], [43.54791, 40.47413], [43.63664, 40.54159], [43.7425, 40.66805], [43.74872, 40.7365], [43.67712, 40.84846], [43.67712, 40.93084], [43.58683, 40.98961], [43.47319, 41.02251], [43.44984, 41.0988], [43.4717, 41.12611], [43.44973, 41.17666], [43.36118, 41.2028], [43.23096, 41.17536], [43.1945, 41.25242], [43.13373, 41.25503], [43.21707, 41.30331], [43.02956, 41.37891], [42.8785, 41.50516], [42.84899, 41.47265], [42.78995, 41.50126], [42.84471, 41.58912], [42.72794, 41.59714], [42.59202, 41.58183], [42.51772, 41.43606], [42.26387, 41.49346], [41.95134, 41.52466], [41.81939, 41.43621], [41.7124, 41.47417], [41.7148, 41.4932], [41.54366, 41.52185]]]]
26514           }
26515         }, {
26516           type: "Feature",
26517           properties: {
26518             iso1A2: "TT",
26519             iso1A3: "TTO",
26520             iso1N3: "780",
26521             wikidata: "Q754",
26522             nameEn: "Trinidad and Tobago",
26523             groups: ["029", "003", "419", "019", "UN"],
26524             driveSide: "left",
26525             callingCodes: ["1 868"]
26526           },
26527           geometry: {
26528             type: "MultiPolygon",
26529             coordinates: [[[[-61.62505, 11.18974], [-62.08693, 10.04435], [-60.89962, 9.81445], [-60.07172, 11.77667], [-61.62505, 11.18974]]]]
26530           }
26531         }, {
26532           type: "Feature",
26533           properties: {
26534             iso1A2: "TV",
26535             iso1A3: "TUV",
26536             iso1N3: "798",
26537             wikidata: "Q672",
26538             nameEn: "Tuvalu",
26539             groups: ["061", "009", "UN"],
26540             driveSide: "left",
26541             callingCodes: ["688"]
26542           },
26543           geometry: {
26544             type: "MultiPolygon",
26545             coordinates: [[[[174, -5], [174, -11.5], [179.99999, -11.5], [179.99999, -5], [174, -5]]]]
26546           }
26547         }, {
26548           type: "Feature",
26549           properties: {
26550             iso1A2: "TW",
26551             iso1A3: "TWN",
26552             iso1N3: "158",
26553             wikidata: "Q865",
26554             nameEn: "Taiwan",
26555             aliases: ["RC"],
26556             groups: ["030", "142"],
26557             callingCodes: ["886"]
26558           },
26559           geometry: {
26560             type: "MultiPolygon",
26561             coordinates: [[[[121.8109, 21.77688], [122.26612, 25.98197], [120.49232, 25.22863], [118.56434, 24.49266], [118.42453, 24.54644], [118.35291, 24.51645], [118.28244, 24.51231], [118.11703, 24.39734], [120.69238, 21.52331], [121.8109, 21.77688]]]]
26562           }
26563         }, {
26564           type: "Feature",
26565           properties: {
26566             iso1A2: "TZ",
26567             iso1A3: "TZA",
26568             iso1N3: "834",
26569             wikidata: "Q924",
26570             nameEn: "Tanzania",
26571             groups: ["014", "202", "002", "UN"],
26572             driveSide: "left",
26573             callingCodes: ["255"]
26574           },
26575           geometry: {
26576             type: "MultiPolygon",
26577             coordinates: [[[[30.80408, -0.99911], [30.76635, -0.9852], [30.70631, -1.01175], [30.64166, -1.06601], [30.47194, -1.0555], [30.45116, -1.10641], [30.50889, -1.16412], [30.57123, -1.33264], [30.71974, -1.43244], [30.84079, -1.64652], [30.80802, -1.91477], [30.89303, -2.08223], [30.83915, -2.35795], [30.54501, -2.41404], [30.41789, -2.66266], [30.52747, -2.65841], [30.40662, -2.86151], [30.4987, -2.9573], [30.57926, -2.89791], [30.6675, -2.98987], [30.83823, -2.97837], [30.84165, -3.25152], [30.45915, -3.56532], [30.22042, -4.01738], [30.03323, -4.26631], [29.88172, -4.35743], [29.82885, -4.36153], [29.77289, -4.41733], [29.75109, -4.45836], [29.63827, -4.44681], [29.43673, -4.44845], [29.52552, -6.2731], [30.2567, -7.14121], [30.79243, -8.27382], [31.00796, -8.58615], [31.37533, -8.60769], [31.57147, -8.70619], [31.57147, -8.81388], [31.71158, -8.91386], [31.81587, -8.88618], [31.94663, -8.93846], [31.94196, -9.02303], [31.98866, -9.07069], [32.08206, -9.04609], [32.16146, -9.05993], [32.25486, -9.13371], [32.43543, -9.11988], [32.49147, -9.14754], [32.53661, -9.24281], [32.75611, -9.28583], [32.76233, -9.31963], [32.95389, -9.40138], [32.99397, -9.36712], [33.14925, -9.49322], [33.31581, -9.48554], [33.48052, -9.62442], [33.76677, -9.58516], [33.93298, -9.71647], [33.9638, -9.62206], [33.95829, -9.54066], [34.03865, -9.49398], [34.54499, -10.0678], [34.51911, -10.12279], [34.57581, -10.56271], [34.65946, -10.6828], [34.67047, -10.93796], [34.61161, -11.01611], [34.63305, -11.11731], [34.79375, -11.32245], [34.91153, -11.39799], [34.96296, -11.57354], [35.63599, -11.55927], [35.82767, -11.41081], [36.19094, -11.57593], [36.19094, -11.70008], [36.62068, -11.72884], [36.80309, -11.56836], [37.3936, -11.68949], [37.76614, -11.53352], [37.8388, -11.3123], [37.93618, -11.26228], [38.21598, -11.27289], [38.47258, -11.4199], [38.88996, -11.16978], [39.24395, -11.17433], [39.58249, -10.96043], [40.00295, -10.80255], [40.44265, -10.4618], [40.74206, -10.25691], [40.14328, -4.64201], [39.62121, -4.68136], [39.44306, -4.93877], [39.21631, -4.67835], [37.81321, -3.69179], [37.75036, -3.54243], [37.63099, -3.50723], [37.5903, -3.42735], [37.71745, -3.304], [37.67199, -3.06222], [34.0824, -1.02264], [34.03084, -1.05101], [34.02286, -1.00779], [33.93107, -0.99298], [30.80408, -0.99911]]]]
26578           }
26579         }, {
26580           type: "Feature",
26581           properties: {
26582             iso1A2: "UA",
26583             iso1A3: "UKR",
26584             iso1N3: "804",
26585             wikidata: "Q212",
26586             nameEn: "Ukraine",
26587             groups: ["151", "150", "UN"],
26588             callingCodes: ["380"]
26589           },
26590           geometry: {
26591             type: "MultiPolygon",
26592             coordinates: [[[[33.57318, 46.10317], [33.61467, 46.13561], [33.63854, 46.14147], [33.61517, 46.22615], [33.646, 46.23028], [33.74047, 46.18555], [33.79715, 46.20482], [33.85234, 46.19863], [33.91549, 46.15938], [34.05272, 46.10838], [34.07311, 46.11769], [34.12929, 46.10494], [34.181, 46.06804], [34.25111, 46.0532], [34.33912, 46.06114], [34.41221, 46.00245], [34.44155, 45.95995], [34.48729, 45.94267], [34.52011, 45.95097], [34.55889, 45.99347], [34.60861, 45.99347], [34.66679, 45.97136], [34.75479, 45.90705], [34.80153, 45.90047], [34.79905, 45.81009], [34.96015, 45.75634], [35.23066, 45.79231], [37.62608, 46.82615], [38.12112, 46.86078], [38.3384, 46.98085], [38.22955, 47.12069], [38.23049, 47.2324], [38.32112, 47.2585], [38.33074, 47.30508], [38.22225, 47.30788], [38.28954, 47.39255], [38.28679, 47.53552], [38.35062, 47.61631], [38.76379, 47.69346], [38.79628, 47.81109], [38.87979, 47.87719], [39.73935, 47.82876], [39.82213, 47.96396], [39.77544, 48.04206], [39.88256, 48.04482], [39.83724, 48.06501], [39.94847, 48.22811], [40.00752, 48.22445], [39.99241, 48.31768], [39.97325, 48.31399], [39.9693, 48.29904], [39.95248, 48.29972], [39.91465, 48.26743], [39.90041, 48.3049], [39.84273, 48.30947], [39.84136, 48.33321], [39.94847, 48.35055], [39.88794, 48.44226], [39.86196, 48.46633], [39.84548, 48.57821], [39.79764, 48.58668], [39.67226, 48.59368], [39.71765, 48.68673], [39.73104, 48.7325], [39.79466, 48.83739], [39.97182, 48.79398], [40.08168, 48.87443], [40.03636, 48.91957], [39.98967, 48.86901], [39.78368, 48.91596], [39.74874, 48.98675], [39.72649, 48.9754], [39.71353, 48.98959], [39.6683, 48.99454], [39.6836, 49.05121], [39.93437, 49.05709], [40.01988, 49.1761], [40.22176, 49.25683], [40.18331, 49.34996], [40.14912, 49.37681], [40.1141, 49.38798], [40.03087, 49.45452], [40.03636, 49.52321], [40.16683, 49.56865], [40.13249, 49.61672], [39.84548, 49.56064], [39.65047, 49.61761], [39.59142, 49.73758], [39.44496, 49.76067], [39.27968, 49.75976], [39.1808, 49.88911], [38.9391, 49.79524], [38.90477, 49.86787], [38.73311, 49.90238], [38.68677, 50.00904], [38.65688, 49.97176], [38.35408, 50.00664], [38.32524, 50.08866], [38.18517, 50.08161], [38.21675, 49.98104], [38.02999, 49.90592], [38.02999, 49.94482], [37.90776, 50.04194], [37.79515, 50.08425], [37.75807, 50.07896], [37.61113, 50.21976], [37.62879, 50.24481], [37.62486, 50.29966], [37.47243, 50.36277], [37.48204, 50.46079], [37.08468, 50.34935], [36.91762, 50.34963], [36.69377, 50.26982], [36.64571, 50.218], [36.56655, 50.2413], [36.58371, 50.28563], [36.47817, 50.31457], [36.30101, 50.29088], [36.20763, 50.3943], [36.06893, 50.45205], [35.8926, 50.43829], [35.80388, 50.41356], [35.73659, 50.35489], [35.61711, 50.35707], [35.58003, 50.45117], [35.47463, 50.49247], [35.39464, 50.64751], [35.48116, 50.66405], [35.47704, 50.77274], [35.41367, 50.80227], [35.39307, 50.92145], [35.32598, 50.94524], [35.40837, 51.04119], [35.31774, 51.08434], [35.20375, 51.04723], [35.12685, 51.16191], [35.14058, 51.23162], [34.97304, 51.2342], [34.82472, 51.17483], [34.6874, 51.18], [34.6613, 51.25053], [34.38802, 51.2746], [34.31661, 51.23936], [34.23009, 51.26429], [34.33446, 51.363], [34.22048, 51.4187], [34.30562, 51.5205], [34.17599, 51.63253], [34.07765, 51.67065], [34.42922, 51.72852], [34.41136, 51.82793], [34.09413, 52.00835], [34.11199, 52.14087], [34.05239, 52.20132], [33.78789, 52.37204], [33.55718, 52.30324], [33.48027, 52.31499], [33.51323, 52.35779], [33.18913, 52.3754], [32.89937, 52.2461], [32.85405, 52.27888], [32.69475, 52.25535], [32.54781, 52.32423], [32.3528, 52.32842], [32.38988, 52.24946], [32.33083, 52.23685], [32.34044, 52.1434], [32.2777, 52.10266], [32.23331, 52.08085], [32.08813, 52.03319], [31.92159, 52.05144], [31.96141, 52.08015], [31.85018, 52.11305], [31.81722, 52.09955], [31.7822, 52.11406], [31.38326, 52.12991], [31.25142, 52.04131], [31.13332, 52.1004], [30.95589, 52.07775], [30.90897, 52.00699], [30.76443, 51.89739], [30.68804, 51.82806], [30.51946, 51.59649], [30.64992, 51.35014], [30.56203, 51.25655], [30.36153, 51.33984], [30.34642, 51.42555], [30.17888, 51.51025], [29.77376, 51.4461], [29.7408, 51.53417], [29.54372, 51.48372], [29.49773, 51.39814], [29.42357, 51.4187], [29.32881, 51.37843], [29.25191, 51.49828], [29.25603, 51.57089], [29.20659, 51.56918], [29.16402, 51.64679], [29.1187, 51.65872], [28.99098, 51.56833], [28.95528, 51.59222], [28.81795, 51.55552], [28.76027, 51.48802], [28.78224, 51.45294], [28.75615, 51.41442], [28.73143, 51.46236], [28.69161, 51.44695], [28.64429, 51.5664], [28.47051, 51.59734], [28.37592, 51.54505], [28.23452, 51.66988], [28.10658, 51.57857], [27.95827, 51.56065], [27.91844, 51.61952], [27.85253, 51.62293], [27.76052, 51.47604], [27.67125, 51.50854], [27.71932, 51.60672], [27.55727, 51.63486], [27.51058, 51.5854], [27.47212, 51.61184], [27.24828, 51.60161], [27.26613, 51.65957], [27.20948, 51.66713], [27.20602, 51.77291], [26.99422, 51.76933], [26.9489, 51.73788], [26.80043, 51.75777], [26.69759, 51.82284], [26.46962, 51.80501], [26.39367, 51.87315], [26.19084, 51.86781], [26.00408, 51.92967], [25.83217, 51.92587], [25.80574, 51.94556], [25.73673, 51.91973], [25.46163, 51.92205], [25.20228, 51.97143], [24.98784, 51.91273], [24.37123, 51.88222], [24.29021, 51.80841], [24.3163, 51.75063], [24.13075, 51.66979], [23.99907, 51.58369], [23.8741, 51.59734], [23.91118, 51.63316], [23.7766, 51.66809], [23.60906, 51.62122], [23.6736, 51.50255], [23.62751, 51.50512], [23.69905, 51.40871], [23.63858, 51.32182], [23.80678, 51.18405], [23.90376, 51.07697], [23.92217, 51.00836], [24.04576, 50.90196], [24.14524, 50.86128], [24.0952, 50.83262], [23.99254, 50.83847], [23.95925, 50.79271], [24.0595, 50.71625], [24.0996, 50.60752], [24.07048, 50.5071], [24.03668, 50.44507], [23.99563, 50.41289], [23.79445, 50.40481], [23.71382, 50.38248], [23.67635, 50.33385], [23.28221, 50.0957], [22.99329, 49.84249], [22.83179, 49.69875], [22.80261, 49.69098], [22.78304, 49.65543], [22.64534, 49.53094], [22.69444, 49.49378], [22.748, 49.32759], [22.72009, 49.20288], [22.86336, 49.10513], [22.89122, 49.00725], [22.56155, 49.08865], [22.54338, 49.01424], [22.48296, 48.99172], [22.42934, 48.92857], [22.34151, 48.68893], [22.21379, 48.6218], [22.16023, 48.56548], [22.14689, 48.4005], [22.2083, 48.42534], [22.38133, 48.23726], [22.49806, 48.25189], [22.59007, 48.15121], [22.58733, 48.10813], [22.66835, 48.09162], [22.73427, 48.12005], [22.81804, 48.11363], [22.87847, 48.04665], [22.84276, 47.98602], [22.89849, 47.95851], [22.94301, 47.96672], [22.92241, 48.02002], [23.0158, 47.99338], [23.08858, 48.00716], [23.1133, 48.08061], [23.15999, 48.12188], [23.27397, 48.08245], [23.33577, 48.0237], [23.4979, 47.96858], [23.52803, 48.01818], [23.5653, 48.00499], [23.63894, 48.00293], [23.66262, 47.98786], [23.75188, 47.99705], [23.80904, 47.98142], [23.8602, 47.9329], [23.89352, 47.94512], [23.94192, 47.94868], [23.96337, 47.96672], [23.98553, 47.96076], [24.00801, 47.968], [24.02999, 47.95087], [24.06466, 47.95317], [24.11281, 47.91487], [24.22566, 47.90231], [24.34926, 47.9244], [24.43578, 47.97131], [24.61994, 47.95062], [24.70632, 47.84428], [24.81893, 47.82031], [24.88896, 47.7234], [25.11144, 47.75203], [25.23778, 47.89403], [25.63878, 47.94924], [25.77723, 47.93919], [26.05901, 47.9897], [26.17711, 47.99246], [26.33504, 48.18418], [26.55202, 48.22445], [26.62823, 48.25804], [26.6839, 48.35828], [26.79239, 48.29071], [26.82809, 48.31629], [26.71274, 48.40388], [26.85556, 48.41095], [26.93384, 48.36558], [27.03821, 48.37653], [27.0231, 48.42485], [27.08078, 48.43214], [27.13434, 48.37288], [27.27855, 48.37534], [27.32159, 48.4434], [27.37604, 48.44398], [27.37741, 48.41026], [27.44333, 48.41209], [27.46942, 48.454], [27.5889, 48.49224], [27.59027, 48.46311], [27.6658, 48.44034], [27.74422, 48.45926], [27.79225, 48.44244], [27.81902, 48.41874], [27.87533, 48.4037], [27.88391, 48.36699], [27.95883, 48.32368], [28.04527, 48.32661], [28.09873, 48.3124], [28.07504, 48.23494], [28.17666, 48.25963], [28.19314, 48.20749], [28.2856, 48.23202], [28.32508, 48.23384], [28.35519, 48.24957], [28.36996, 48.20543], [28.34912, 48.1787], [28.30586, 48.1597], [28.30609, 48.14018], [28.34009, 48.13147], [28.38712, 48.17567], [28.43701, 48.15832], [28.42454, 48.12047], [28.48428, 48.0737], [28.53921, 48.17453], [28.69896, 48.13106], [28.85232, 48.12506], [28.8414, 48.03392], [28.9306, 47.96255], [29.1723, 47.99013], [29.19839, 47.89261], [29.27804, 47.88893], [29.20663, 47.80367], [29.27255, 47.79953], [29.22242, 47.73607], [29.22414, 47.60012], [29.11743, 47.55001], [29.18603, 47.43387], [29.3261, 47.44664], [29.39889, 47.30179], [29.47854, 47.30366], [29.48678, 47.36043], [29.5733, 47.36508], [29.59665, 47.25521], [29.54996, 47.24962], [29.57696, 47.13581], [29.49732, 47.12878], [29.53044, 47.07851], [29.61038, 47.09932], [29.62137, 47.05069], [29.57056, 46.94766], [29.72986, 46.92234], [29.75458, 46.8604], [29.87405, 46.88199], [29.98814, 46.82358], [29.94522, 46.80055], [29.9743, 46.75325], [29.94409, 46.56002], [29.88916, 46.54302], [30.02511, 46.45132], [30.16794, 46.40967], [30.09103, 46.38694], [29.94114, 46.40114], [29.88329, 46.35851], [29.74496, 46.45605], [29.66359, 46.4215], [29.6763, 46.36041], [29.5939, 46.35472], [29.49914, 46.45889], [29.35357, 46.49505], [29.24886, 46.37912], [29.23547, 46.55435], [29.02409, 46.49582], [29.01241, 46.46177], [28.9306, 46.45699], [29.004, 46.31495], [28.98478, 46.31803], [28.94953, 46.25852], [29.06656, 46.19716], [28.94643, 46.09176], [29.00613, 46.04962], [28.98004, 46.00385], [28.74383, 45.96664], [28.78503, 45.83475], [28.69852, 45.81753], [28.70401, 45.78019], [28.52823, 45.73803], [28.47879, 45.66994], [28.51587, 45.6613], [28.54196, 45.58062], [28.49252, 45.56716], [28.51449, 45.49982], [28.43072, 45.48538], [28.41836, 45.51715], [28.30201, 45.54744], [28.21139, 45.46895], [28.28504, 45.43907], [28.34554, 45.32102], [28.5735, 45.24759], [28.71358, 45.22631], [28.78911, 45.24179], [28.81383, 45.3384], [28.94292, 45.28045], [28.96077, 45.33164], [29.24779, 45.43388], [29.42632, 45.44545], [29.59798, 45.38857], [29.68175, 45.26885], [29.65428, 45.25629], [29.69272, 45.19227], [30.04414, 45.08461], [31.62627, 45.50633], [33.54017, 46.0123], [33.59087, 46.06013], [33.57318, 46.10317]]]]
26593           }
26594         }, {
26595           type: "Feature",
26596           properties: {
26597             iso1A2: "UG",
26598             iso1A3: "UGA",
26599             iso1N3: "800",
26600             wikidata: "Q1036",
26601             nameEn: "Uganda",
26602             groups: ["014", "202", "002", "UN"],
26603             driveSide: "left",
26604             callingCodes: ["256"]
26605           },
26606           geometry: {
26607             type: "MultiPolygon",
26608             coordinates: [[[[33.93107, -0.99298], [33.9264, -0.54188], [33.98449, -0.13079], [33.90936, 0.10581], [34.10067, 0.36372], [34.08727, 0.44713], [34.11408, 0.48884], [34.13493, 0.58118], [34.20196, 0.62289], [34.27345, 0.63182], [34.31516, 0.75693], [34.40041, 0.80266], [34.43349, 0.85254], [34.52369, 1.10692], [34.57427, 1.09868], [34.58029, 1.14712], [34.67562, 1.21265], [34.80223, 1.22754], [34.82606, 1.26626], [34.82606, 1.30944], [34.7918, 1.36752], [34.87819, 1.5596], [34.92734, 1.56109], [34.9899, 1.6668], [34.98692, 1.97348], [34.90947, 2.42447], [34.95267, 2.47209], [34.77244, 2.70272], [34.78137, 2.76223], [34.73967, 2.85447], [34.65774, 2.8753], [34.60114, 2.93034], [34.56242, 3.11478], [34.45815, 3.18319], [34.40006, 3.37949], [34.41794, 3.44342], [34.39112, 3.48802], [34.44922, 3.51627], [34.45815, 3.67385], [34.15429, 3.80464], [34.06046, 4.15235], [33.9873, 4.23316], [33.51264, 3.75068], [33.18356, 3.77812], [33.02852, 3.89296], [32.89746, 3.81339], [32.72021, 3.77327], [32.41337, 3.748], [32.20782, 3.6053], [32.19888, 3.50867], [32.08866, 3.53543], [32.08491, 3.56287], [32.05187, 3.589], [31.95907, 3.57408], [31.96205, 3.6499], [31.86821, 3.78664], [31.81459, 3.82083], [31.72075, 3.74354], [31.50776, 3.63652], [31.50478, 3.67814], [31.29476, 3.8015], [31.16666, 3.79853], [30.97601, 3.693], [30.85153, 3.48867], [30.94081, 3.50847], [30.93486, 3.40737], [30.84251, 3.26908], [30.77101, 3.04897], [30.8574, 2.9508], [30.8857, 2.83923], [30.75612, 2.5863], [30.74271, 2.43601], [30.83059, 2.42559], [30.91102, 2.33332], [30.96911, 2.41071], [31.06593, 2.35862], [31.07934, 2.30207], [31.12104, 2.27676], [31.1985, 2.29462], [31.20148, 2.2217], [31.28042, 2.17853], [31.30127, 2.11006], [30.48503, 1.21675], [30.24671, 1.14974], [30.22139, 0.99635], [30.1484, 0.89805], [29.98307, 0.84295], [29.95477, 0.64486], [29.97413, 0.52124], [29.87284, 0.39166], [29.81922, 0.16824], [29.77454, 0.16675], [29.7224, 0.07291], [29.72687, -0.08051], [29.65091, -0.46777], [29.67474, -0.47969], [29.67176, -0.55714], [29.62708, -0.71055], [29.63006, -0.8997], [29.58388, -0.89821], [29.59061, -1.39016], [29.82657, -1.31187], [29.912, -1.48269], [30.16369, -1.34303], [30.35212, -1.06896], [30.47194, -1.0555], [30.64166, -1.06601], [30.70631, -1.01175], [30.76635, -0.9852], [30.80408, -0.99911], [33.93107, -0.99298]]]]
26609           }
26610         }, {
26611           type: "Feature",
26612           properties: {
26613             iso1A2: "UM",
26614             iso1A3: "UMI",
26615             iso1N3: "581",
26616             wikidata: "Q16645",
26617             nameEn: "United States Minor Outlying Islands",
26618             country: "US"
26619           },
26620           geometry: null
26621         }, {
26622           type: "Feature",
26623           properties: {
26624             iso1A2: "UN",
26625             wikidata: "Q1065",
26626             nameEn: "United Nations",
26627             level: "unitedNations",
26628             isoStatus: "excRes"
26629           },
26630           geometry: null
26631         }, {
26632           type: "Feature",
26633           properties: {
26634             iso1A2: "US",
26635             iso1A3: "USA",
26636             iso1N3: "840",
26637             wikidata: "Q30",
26638             nameEn: "United States of America"
26639           },
26640           geometry: null
26641         }, {
26642           type: "Feature",
26643           properties: {
26644             iso1A2: "UY",
26645             iso1A3: "URY",
26646             iso1N3: "858",
26647             wikidata: "Q77",
26648             nameEn: "Uruguay",
26649             groups: ["005", "419", "019", "UN"],
26650             callingCodes: ["598"]
26651           },
26652           geometry: {
26653             type: "MultiPolygon",
26654             coordinates: [[[[-57.65132, -30.19229], [-57.61478, -30.25165], [-57.64859, -30.35095], [-57.89115, -30.49572], [-57.8024, -30.77193], [-57.89476, -30.95994], [-57.86729, -31.06352], [-57.9908, -31.34924], [-57.98127, -31.3872], [-58.07569, -31.44916], [-58.0023, -31.53084], [-58.00076, -31.65016], [-58.20252, -31.86966], [-58.10036, -32.25338], [-58.22362, -32.52416], [-58.1224, -32.98842], [-58.40475, -33.11777], [-58.44442, -33.84033], [-58.34425, -34.15035], [-57.83001, -34.69099], [-54.78916, -36.21945], [-52.83257, -34.01481], [-53.37138, -33.74313], [-53.39593, -33.75169], [-53.44031, -33.69344], [-53.52794, -33.68908], [-53.53459, -33.16843], [-53.1111, -32.71147], [-53.37671, -32.57005], [-53.39572, -32.58596], [-53.76024, -32.0751], [-54.17384, -31.86168], [-55.50821, -30.91349], [-55.50841, -30.9027], [-55.51862, -30.89828], [-55.52712, -30.89997], [-55.53276, -30.90218], [-55.53431, -30.89714], [-55.54572, -30.89051], [-55.55218, -30.88193], [-55.55373, -30.8732], [-55.5634, -30.8686], [-55.58866, -30.84117], [-55.87388, -31.05053], [-56.4619, -30.38457], [-56.4795, -30.3899], [-56.49267, -30.39471], [-56.90236, -30.02578], [-57.22502, -30.26121], [-57.65132, -30.19229]]]]
26655           }
26656         }, {
26657           type: "Feature",
26658           properties: {
26659             iso1A2: "UZ",
26660             iso1A3: "UZB",
26661             iso1N3: "860",
26662             wikidata: "Q265",
26663             nameEn: "Uzbekistan",
26664             groups: ["143", "142", "UN"],
26665             callingCodes: ["998"]
26666           },
26667           geometry: {
26668             type: "MultiPolygon",
26669             coordinates: [[[[65.85194, 42.85481], [65.53277, 43.31856], [65.18666, 43.48835], [64.96464, 43.74748], [64.53885, 43.56941], [63.34656, 43.64003], [62.01711, 43.51008], [61.01475, 44.41383], [58.59711, 45.58671], [55.97842, 44.99622], [55.97832, 44.99622], [55.97822, 44.99617], [55.97811, 44.99617], [55.97801, 44.99612], [55.97801, 44.99607], [55.97791, 44.99607], [55.9778, 44.99607], [55.9777, 44.99601], [55.9777, 44.99596], [55.9776, 44.99591], [55.97749, 44.99591], [55.97739, 44.99591], [55.97739, 44.99586], [55.97729, 44.99586], [55.97718, 44.99581], [55.97708, 44.99576], [55.97698, 44.9957], [55.97698, 44.99565], [55.97687, 44.9956], [55.97677, 44.9956], [55.97677, 44.99555], [55.97677, 44.9955], [55.97667, 44.99545], [55.97656, 44.99539], [55.97646, 44.99534], [55.97646, 44.99529], [55.97636, 44.99524], [55.97636, 44.99519], [55.97625, 44.99514], [55.97615, 44.99508], [55.97615, 44.99503], [55.97615, 44.99498], [55.97615, 44.99493], [55.97615, 44.99483], [55.97615, 44.99477], [55.97605, 44.99477], [55.97605, 44.99467], [55.97605, 44.99462], [55.97605, 44.99457], [55.97605, 44.99452], [55.97594, 44.99446], [55.97584, 44.99441], [55.97584, 44.99436], [55.97584, 44.99431], [55.97584, 44.99426], [55.97584, 44.99421], [55.97584, 44.99415], [55.97584, 44.99405], [55.97584, 44.994], [55.97584, 44.9939], [55.97584, 44.99384], [55.97584, 44.99374], [55.97584, 44.99369], [55.97584, 44.99359], [55.97584, 44.99353], [55.97584, 44.99348], [55.97584, 44.99343], [55.97584, 44.99338], [55.97584, 44.99328], [55.97584, 44.99322], [56.00314, 41.32584], [57.03423, 41.25435], [57.13796, 41.36625], [57.03359, 41.41777], [56.96218, 41.80383], [57.03633, 41.92043], [57.30275, 42.14076], [57.6296, 42.16519], [57.84932, 42.18555], [57.92897, 42.24047], [57.90975, 42.4374], [57.99214, 42.50021], [58.3492, 42.43335], [58.40688, 42.29535], [58.51674, 42.30348], [58.29427, 42.56497], [58.14321, 42.62159], [58.27504, 42.69632], [58.57991, 42.64988], [58.6266, 42.79314], [58.93422, 42.5407], [59.17317, 42.52248], [59.2955, 42.37064], [59.4341, 42.29738], [59.94633, 42.27655], [60.00539, 42.212], [59.96419, 42.1428], [60.04659, 42.08982], [60.0356, 42.01028], [59.95046, 41.97966], [60.33223, 41.75058], [60.08504, 41.80997], [60.06032, 41.76287], [60.18117, 41.60082], [60.06581, 41.4363], [60.5078, 41.21694], [61.03261, 41.25691], [61.22212, 41.14946], [61.33199, 41.14946], [61.39732, 41.19873], [61.4446, 41.29407], [61.87856, 41.12257], [62.11751, 40.58242], [62.34273, 40.43206], [62.43337, 39.98528], [63.6913, 39.27666], [63.70778, 39.22349], [64.19086, 38.95561], [64.32576, 38.98691], [65.55873, 38.29052], [65.83913, 38.25733], [66.24013, 38.16238], [66.41042, 38.02403], [66.56697, 38.0435], [66.67684, 37.96776], [66.53676, 37.80084], [66.52852, 37.58568], [66.65761, 37.45497], [66.52303, 37.39827], [66.55743, 37.35409], [66.64699, 37.32958], [66.95598, 37.40162], [67.08232, 37.35469], [67.13039, 37.27168], [67.2224, 37.24545], [67.2581, 37.17216], [67.51868, 37.26102], [67.78329, 37.1834], [67.8474, 37.31594], [67.81566, 37.43107], [68.12635, 37.93], [68.27159, 37.91477], [68.40343, 38.19484], [68.13289, 38.40822], [68.06274, 38.39435], [68.11366, 38.47169], [68.05873, 38.56087], [68.0807, 38.64136], [68.05598, 38.71641], [68.12877, 38.73677], [68.06948, 38.82115], [68.19743, 38.85985], [68.09704, 39.02589], [67.68915, 39.00775], [67.67833, 39.14479], [67.33226, 39.23739], [67.36522, 39.31287], [67.45998, 39.315], [67.46822, 39.46146], [67.39681, 39.52505], [67.46547, 39.53564], [67.44899, 39.57799], [67.62889, 39.60234], [67.70992, 39.66156], [68.12053, 39.56317], [68.54166, 39.53929], [68.61972, 39.68905], [68.63071, 39.85265], [68.88889, 39.87163], [68.93695, 39.91167], [68.84906, 40.04952], [68.96579, 40.06949], [69.01935, 40.11466], [69.01523, 40.15771], [68.62796, 40.07789], [68.52771, 40.11676], [68.5332, 40.14826], [68.77902, 40.20492], [68.79276, 40.17555], [68.84357, 40.18604], [68.85832, 40.20885], [69.04544, 40.22904], [69.15659, 40.2162], [69.2074, 40.21488], [69.30448, 40.18774], [69.30104, 40.24502], [69.25229, 40.26362], [69.24817, 40.30357], [69.30808, 40.2821], [69.32833, 40.29794], [69.33794, 40.34819], [69.30774, 40.36102], [69.28525, 40.41894], [69.27066, 40.49274], [69.21063, 40.54469], [69.2643, 40.57506], [69.3455, 40.57988], [69.32834, 40.70233], [69.38327, 40.7918], [69.53021, 40.77621], [69.59441, 40.70181], [69.69434, 40.62615], [70.36655, 40.90296], [70.38028, 41.02014], [70.45251, 41.04438], [70.80009, 40.72825], [70.49871, 40.52503], [70.32626, 40.45174], [70.37511, 40.38605], [70.57149, 40.3442], [70.56394, 40.26421], [70.62342, 40.17396], [70.8607, 40.217], [70.9818, 40.22392], [70.95789, 40.28761], [71.05901, 40.28765], [71.13042, 40.34106], [71.36663, 40.31593], [71.4246, 40.28619], [71.51215, 40.26943], [71.51549, 40.22986], [71.61725, 40.20615], [71.61931, 40.26775], [71.68386, 40.26984], [71.70569, 40.20391], [71.69621, 40.18492], [71.71719, 40.17886], [71.73054, 40.14818], [71.82646, 40.21872], [71.85002, 40.25647], [72.05464, 40.27586], [71.96401, 40.31907], [72.18648, 40.49893], [72.24368, 40.46091], [72.40346, 40.4007], [72.44191, 40.48222], [72.41513, 40.50856], [72.38384, 40.51535], [72.41714, 40.55736], [72.34406, 40.60144], [72.40517, 40.61917], [72.47795, 40.5532], [72.66713, 40.5219], [72.66713, 40.59076], [72.69579, 40.59778], [72.73995, 40.58409], [72.74768, 40.58051], [72.74862, 40.57131], [72.75982, 40.57273], [72.74894, 40.59592], [72.74866, 40.60873], [72.80137, 40.67856], [72.84754, 40.67229], [72.85372, 40.7116], [72.8722, 40.71111], [72.93296, 40.73089], [72.99133, 40.76457], [73.0612, 40.76678], [73.13412, 40.79122], [73.13267, 40.83512], [73.01869, 40.84681], [72.94454, 40.8094], [72.84291, 40.85512], [72.68157, 40.84942], [72.59136, 40.86947], [72.55109, 40.96046], [72.48742, 40.97136], [72.45206, 41.03018], [72.38511, 41.02785], [72.36138, 41.04384], [72.34757, 41.06104], [72.34026, 41.04539], [72.324, 41.03381], [72.18339, 40.99571], [72.17594, 41.02377], [72.21061, 41.05607], [72.1792, 41.10621], [72.14864, 41.13363], [72.17594, 41.15522], [72.16433, 41.16483], [72.10745, 41.15483], [72.07249, 41.11739], [71.85964, 41.19081], [71.91457, 41.2982], [71.83914, 41.3546], [71.76625, 41.4466], [71.71132, 41.43012], [71.73054, 41.54713], [71.65914, 41.49599], [71.6787, 41.42111], [71.57227, 41.29175], [71.46688, 41.31883], [71.43814, 41.19644], [71.46148, 41.13958], [71.40198, 41.09436], [71.34877, 41.16807], [71.27187, 41.11015], [71.25813, 41.18796], [71.11806, 41.15359], [71.02193, 41.19494], [70.9615, 41.16393], [70.86263, 41.23833], [70.77885, 41.24813], [70.78572, 41.36419], [70.67586, 41.47953], [70.48909, 41.40335], [70.17682, 41.5455], [70.69777, 41.92554], [71.28719, 42.18033], [71.13263, 42.28356], [70.94483, 42.26238], [69.49545, 41.545], [69.45751, 41.56863], [69.39485, 41.51518], [69.45081, 41.46246], [69.37468, 41.46555], [69.35554, 41.47211], [69.29778, 41.43673], [69.25059, 41.46693], [69.23332, 41.45847], [69.22671, 41.46298], [69.20439, 41.45391], [69.18528, 41.45175], [69.17701, 41.43769], [69.15137, 41.43078], [69.05006, 41.36183], [69.01308, 41.22804], [68.7217, 41.05025], [68.73945, 40.96989], [68.65662, 40.93861], [68.62221, 41.03019], [68.49983, 40.99669], [68.58444, 40.91447], [68.63, 40.59358], [68.49983, 40.56437], [67.96736, 40.83798], [68.1271, 41.0324], [68.08273, 41.08148], [67.98511, 41.02794], [67.9644, 41.14611], [66.69129, 41.1311], [66.53302, 41.87388], [66.00546, 41.94455], [66.09482, 42.93426], [65.85194, 42.85481]], [[70.68112, 40.90612], [70.6721, 40.90555], [70.57501, 40.98941], [70.54223, 40.98787], [70.56077, 41.00642], [70.6158, 40.97661], [70.68112, 40.90612]]], [[[71.21139, 40.03369], [71.12218, 40.03052], [71.06305, 40.1771], [71.00236, 40.18154], [71.01035, 40.05481], [71.11037, 40.01984], [71.11668, 39.99291], [71.09063, 39.99], [71.10501, 39.95568], [71.04979, 39.89808], [71.10531, 39.91354], [71.16101, 39.88423], [71.23067, 39.93581], [71.1427, 39.95026], [71.21139, 40.03369]]], [[[71.86463, 39.98598], [71.78838, 40.01404], [71.71511, 39.96348], [71.7504, 39.93701], [71.84316, 39.95582], [71.86463, 39.98598]]]]
26670           }
26671         }, {
26672           type: "Feature",
26673           properties: {
26674             iso1A2: "VA",
26675             iso1A3: "VAT",
26676             iso1N3: "336",
26677             wikidata: "Q237",
26678             nameEn: "Vatican City",
26679             aliases: ["Holy See"],
26680             groups: ["039", "150"],
26681             callingCodes: ["379", "39 06"]
26682           },
26683           geometry: {
26684             type: "MultiPolygon",
26685             coordinates: [[[[12.45181, 41.90056], [12.45446, 41.90028], [12.45435, 41.90143], [12.45626, 41.90172], [12.45691, 41.90125], [12.4577, 41.90115], [12.45834, 41.90174], [12.45826, 41.90281], [12.45755, 41.9033], [12.45762, 41.9058], [12.45561, 41.90629], [12.45543, 41.90738], [12.45091, 41.90625], [12.44984, 41.90545], [12.44815, 41.90326], [12.44582, 41.90194], [12.44834, 41.90095], [12.45181, 41.90056]]]]
26686           }
26687         }, {
26688           type: "Feature",
26689           properties: {
26690             iso1A2: "VC",
26691             iso1A3: "VCT",
26692             iso1N3: "670",
26693             wikidata: "Q757",
26694             nameEn: "St. Vincent and the Grenadines",
26695             aliases: ["WV"],
26696             groups: ["029", "003", "419", "019", "UN"],
26697             driveSide: "left",
26698             roadSpeedUnit: "mph",
26699             callingCodes: ["1 784"]
26700           },
26701           geometry: {
26702             type: "MultiPolygon",
26703             coordinates: [[[[-62.64026, 12.69984], [-59.94058, 12.34011], [-61.69315, 14.26451], [-62.64026, 12.69984]]]]
26704           }
26705         }, {
26706           type: "Feature",
26707           properties: {
26708             iso1A2: "VE",
26709             iso1A3: "VEN",
26710             iso1N3: "862",
26711             wikidata: "Q717",
26712             nameEn: "Venezuela",
26713             aliases: ["YV"],
26714             groups: ["005", "419", "019", "UN"],
26715             callingCodes: ["58"]
26716           },
26717           geometry: {
26718             type: "MultiPolygon",
26719             coordinates: [[[[-71.22331, 13.01387], [-70.92579, 11.96275], [-71.3275, 11.85], [-71.9675, 11.65536], [-72.24983, 11.14138], [-72.4767, 11.1117], [-72.88002, 10.44309], [-72.98085, 9.85253], [-73.36905, 9.16636], [-73.02119, 9.27584], [-72.94052, 9.10663], [-72.77415, 9.10165], [-72.65474, 8.61428], [-72.4042, 8.36513], [-72.36987, 8.19976], [-72.35163, 8.01163], [-72.39137, 8.03534], [-72.47213, 7.96106], [-72.48801, 7.94329], [-72.48183, 7.92909], [-72.47042, 7.92306], [-72.45806, 7.91141], [-72.46183, 7.90682], [-72.44454, 7.86031], [-72.46763, 7.79518], [-72.47827, 7.65604], [-72.45321, 7.57232], [-72.47415, 7.48928], [-72.43132, 7.40034], [-72.19437, 7.37034], [-72.04895, 7.03837], [-71.82441, 7.04314], [-71.44118, 7.02116], [-71.42212, 7.03854], [-71.37234, 7.01588], [-71.03941, 6.98163], [-70.7596, 7.09799], [-70.10716, 6.96516], [-69.41843, 6.1072], [-67.60654, 6.2891], [-67.4625, 6.20625], [-67.43513, 5.98835], [-67.58558, 5.84537], [-67.63914, 5.64963], [-67.59141, 5.5369], [-67.83341, 5.31104], [-67.85358, 4.53249], [-67.62671, 3.74303], [-67.50067, 3.75812], [-67.30945, 3.38393], [-67.85862, 2.86727], [-67.85862, 2.79173], [-67.65696, 2.81691], [-67.21967, 2.35778], [-66.85795, 1.22998], [-66.28507, 0.74585], [-65.6727, 1.01353], [-65.50158, 0.92086], [-65.57288, 0.62856], [-65.11657, 1.12046], [-64.38932, 1.5125], [-64.34654, 1.35569], [-64.08274, 1.64792], [-64.06135, 1.94722], [-63.39827, 2.16098], [-63.39114, 2.4317], [-64.0257, 2.48156], [-64.02908, 2.79797], [-64.48379, 3.7879], [-64.84028, 4.24665], [-64.72977, 4.28931], [-64.57648, 4.12576], [-64.14512, 4.12932], [-63.99183, 3.90172], [-63.86082, 3.94796], [-63.70218, 3.91417], [-63.67099, 4.01731], [-63.50611, 3.83592], [-63.42233, 3.89995], [-63.4464, 3.9693], [-63.21111, 3.96219], [-62.98296, 3.59935], [-62.7655, 3.73099], [-62.74411, 4.03331], [-62.57656, 4.04754], [-62.44822, 4.18621], [-62.13094, 4.08309], [-61.54629, 4.2822], [-61.48569, 4.43149], [-61.29675, 4.44216], [-61.31457, 4.54167], [-61.15703, 4.49839], [-60.98303, 4.54167], [-60.86539, 4.70512], [-60.5802, 4.94312], [-60.73204, 5.20931], [-61.4041, 5.95304], [-61.15058, 6.19558], [-61.20762, 6.58174], [-61.13632, 6.70922], [-60.54873, 6.8631], [-60.39419, 6.94847], [-60.28074, 7.1162], [-60.44116, 7.20817], [-60.54098, 7.14804], [-60.63367, 7.25061], [-60.59802, 7.33194], [-60.71923, 7.55817], [-60.64793, 7.56877], [-60.51959, 7.83373], [-60.38056, 7.8302], [-60.02407, 8.04557], [-59.97059, 8.20791], [-59.83156, 8.23261], [-59.80661, 8.28906], [-59.85562, 8.35213], [-59.98508, 8.53046], [-59.54058, 8.6862], [-60.89962, 9.81445], [-62.08693, 10.04435], [-61.62505, 11.18974], [-63.73917, 11.92623], [-63.19938, 16.44103], [-67.89186, 12.4116], [-68.01417, 11.77722], [-68.33524, 11.78151], [-68.99639, 11.79035], [-71.22331, 13.01387]]]]
26720           }
26721         }, {
26722           type: "Feature",
26723           properties: {
26724             iso1A2: "VG",
26725             iso1A3: "VGB",
26726             iso1N3: "092",
26727             wikidata: "Q25305",
26728             nameEn: "British Virgin Islands",
26729             country: "GB",
26730             groups: ["BOTS", "029", "003", "419", "019", "UN"],
26731             driveSide: "left",
26732             roadSpeedUnit: "mph",
26733             roadHeightUnit: "ft",
26734             callingCodes: ["1 284"]
26735           },
26736           geometry: {
26737             type: "MultiPolygon",
26738             coordinates: [[[[-64.47127, 17.55688], [-63.88746, 19.15706], [-65.02435, 18.73231], [-64.86027, 18.39056], [-64.64673, 18.36549], [-64.47127, 17.55688]]]]
26739           }
26740         }, {
26741           type: "Feature",
26742           properties: {
26743             iso1A2: "VI",
26744             iso1A3: "VIR",
26745             iso1N3: "850",
26746             wikidata: "Q11703",
26747             nameEn: "United States Virgin Islands",
26748             aliases: ["US-VI"],
26749             country: "US",
26750             groups: ["Q1352230", "029", "003", "419", "019", "UN"],
26751             driveSide: "left",
26752             roadSpeedUnit: "mph",
26753             roadHeightUnit: "ft",
26754             callingCodes: ["1 340"]
26755           },
26756           geometry: {
26757             type: "MultiPolygon",
26758             coordinates: [[[[-65.02435, 18.73231], [-65.27974, 17.56928], [-64.47127, 17.55688], [-64.64673, 18.36549], [-64.86027, 18.39056], [-65.02435, 18.73231]]]]
26759           }
26760         }, {
26761           type: "Feature",
26762           properties: {
26763             iso1A2: "VN",
26764             iso1A3: "VNM",
26765             iso1N3: "704",
26766             wikidata: "Q881",
26767             nameEn: "Vietnam",
26768             groups: ["035", "142", "UN"],
26769             callingCodes: ["84"]
26770           },
26771           geometry: {
26772             type: "MultiPolygon",
26773             coordinates: [[[[108.10003, 21.47338], [108.0569, 21.53604], [108.02926, 21.54997], [107.97932, 21.54503], [107.97383, 21.53961], [107.97074, 21.54072], [107.96774, 21.53601], [107.95232, 21.5388], [107.92652, 21.58906], [107.90006, 21.5905], [107.86114, 21.65128], [107.80355, 21.66141], [107.66967, 21.60787], [107.56537, 21.61945], [107.54047, 21.5934], [107.49065, 21.59774], [107.49532, 21.62958], [107.47197, 21.6672], [107.41593, 21.64839], [107.38636, 21.59774], [107.35989, 21.60063], [107.35834, 21.6672], [107.29296, 21.74674], [107.24625, 21.7077], [107.20734, 21.71493], [107.10771, 21.79879], [107.02615, 21.81981], [107.00964, 21.85948], [107.06101, 21.88982], [107.05634, 21.92303], [106.99252, 21.95191], [106.97228, 21.92592], [106.92714, 21.93459], [106.9178, 21.97357], [106.81038, 21.97934], [106.74345, 22.00965], [106.72551, 21.97923], [106.69276, 21.96013], [106.68274, 21.99811], [106.70142, 22.02409], [106.6983, 22.15102], [106.67495, 22.1885], [106.69986, 22.22309], [106.6516, 22.33977], [106.55976, 22.34841], [106.57221, 22.37], [106.55665, 22.46498], [106.58395, 22.474], [106.61269, 22.60301], [106.65316, 22.5757], [106.71698, 22.58432], [106.72321, 22.63606], [106.76293, 22.73491], [106.82404, 22.7881], [106.83685, 22.8098], [106.81271, 22.8226], [106.78422, 22.81532], [106.71128, 22.85982], [106.71387, 22.88296], [106.6734, 22.89587], [106.6516, 22.86862], [106.60179, 22.92884], [106.55976, 22.92311], [106.51306, 22.94891], [106.49749, 22.91164], [106.34961, 22.86718], [106.27022, 22.87722], [106.19705, 22.98475], [106.00179, 22.99049], [105.99568, 22.94178], [105.90119, 22.94168], [105.8726, 22.92756], [105.72382, 23.06641], [105.57594, 23.075], [105.56037, 23.16806], [105.49966, 23.20669], [105.42805, 23.30824], [105.40782, 23.28107], [105.32376, 23.39684], [105.22569, 23.27249], [105.17276, 23.28679], [105.11672, 23.25247], [105.07002, 23.26248], [104.98712, 23.19176], [104.96532, 23.20463], [104.9486, 23.17235], [104.91435, 23.18666], [104.87992, 23.17141], [104.87382, 23.12854], [104.79478, 23.12934], [104.8334, 23.01484], [104.86765, 22.95178], [104.84942, 22.93631], [104.77114, 22.90017], [104.72755, 22.81984], [104.65283, 22.83419], [104.60457, 22.81841], [104.58122, 22.85571], [104.47225, 22.75813], [104.35593, 22.69353], [104.25683, 22.76534], [104.27084, 22.8457], [104.11384, 22.80363], [104.03734, 22.72945], [104.01088, 22.51823], [103.99247, 22.51958], [103.97384, 22.50634], [103.96783, 22.51173], [103.96352, 22.50584], [103.95191, 22.5134], [103.94513, 22.52553], [103.93286, 22.52703], [103.87904, 22.56683], [103.64506, 22.79979], [103.56255, 22.69499], [103.57812, 22.65764], [103.52675, 22.59155], [103.43646, 22.70648], [103.43179, 22.75816], [103.32282, 22.8127], [103.28079, 22.68063], [103.18895, 22.64471], [103.15782, 22.59873], [103.17961, 22.55705], [103.07843, 22.50097], [103.0722, 22.44775], [102.9321, 22.48659], [102.8636, 22.60735], [102.60675, 22.73376], [102.57095, 22.7036], [102.51802, 22.77969], [102.46665, 22.77108], [102.42618, 22.69212], [102.38415, 22.67919], [102.41061, 22.64184], [102.25339, 22.4607], [102.26428, 22.41321], [102.16621, 22.43336], [102.14099, 22.40092], [102.18712, 22.30403], [102.51734, 22.02676], [102.49092, 21.99002], [102.62301, 21.91447], [102.67145, 21.65894], [102.74189, 21.66713], [102.82115, 21.73667], [102.81894, 21.83888], [102.85637, 21.84501], [102.86077, 21.71213], [102.97965, 21.74076], [102.98846, 21.58936], [102.86297, 21.4255], [102.94223, 21.46034], [102.88939, 21.3107], [102.80794, 21.25736], [102.89825, 21.24707], [102.97745, 21.05821], [103.03469, 21.05821], [103.12055, 20.89994], [103.21497, 20.89832], [103.38032, 20.79501], [103.45737, 20.82382], [103.68633, 20.66324], [103.73478, 20.6669], [103.82282, 20.8732], [103.98024, 20.91531], [104.11121, 20.96779], [104.27412, 20.91433], [104.63957, 20.6653], [104.38199, 20.47155], [104.40621, 20.3849], [104.47886, 20.37459], [104.66158, 20.47774], [104.72102, 20.40554], [104.62195, 20.36633], [104.61315, 20.24452], [104.86852, 20.14121], [104.91695, 20.15567], [104.9874, 20.09573], [104.8465, 19.91783], [104.8355, 19.80395], [104.68359, 19.72729], [104.64837, 19.62365], [104.53169, 19.61743], [104.41281, 19.70035], [104.23229, 19.70242], [104.06498, 19.66926], [104.05617, 19.61743], [104.10832, 19.51575], [104.06058, 19.43484], [103.87125, 19.31854], [104.5361, 18.97747], [104.64617, 18.85668], [105.12829, 18.70453], [105.19654, 18.64196], [105.1327, 18.58355], [105.10408, 18.43533], [105.15942, 18.38691], [105.38366, 18.15315], [105.46292, 18.22008], [105.64784, 17.96687], [105.60381, 17.89356], [105.76612, 17.67147], [105.85744, 17.63221], [106.09019, 17.36399], [106.18991, 17.28227], [106.24444, 17.24714], [106.29287, 17.3018], [106.31929, 17.20509], [106.43597, 17.01362], [106.50862, 16.9673], [106.55045, 17.0031], [106.54824, 16.92729], [106.51963, 16.92097], [106.52183, 16.87884], [106.55265, 16.86831], [106.55485, 16.68704], [106.59013, 16.62259], [106.58267, 16.6012], [106.61477, 16.60713], [106.66052, 16.56892], [106.65832, 16.47816], [106.74418, 16.41904], [106.84104, 16.55415], [106.88727, 16.52671], [106.88067, 16.43594], [106.96638, 16.34938], [106.97385, 16.30204], [107.02597, 16.31132], [107.09091, 16.3092], [107.15035, 16.26271], [107.14595, 16.17816], [107.25822, 16.13587], [107.33968, 16.05549], [107.44975, 16.08511], [107.46296, 16.01106], [107.39471, 15.88829], [107.34188, 15.89464], [107.21419, 15.83747], [107.21859, 15.74638], [107.27143, 15.71459], [107.27583, 15.62769], [107.34408, 15.62345], [107.3815, 15.49832], [107.50699, 15.48771], [107.53341, 15.40496], [107.62367, 15.42193], [107.60605, 15.37524], [107.62587, 15.2266], [107.58844, 15.20111], [107.61926, 15.13949], [107.61486, 15.0566], [107.46516, 15.00982], [107.48277, 14.93751], [107.59285, 14.87795], [107.51579, 14.79282], [107.54361, 14.69092], [107.55371, 14.628], [107.52102, 14.59034], [107.52569, 14.54665], [107.48521, 14.40346], [107.44941, 14.41552], [107.39493, 14.32655], [107.40427, 14.24509], [107.33577, 14.11832], [107.37158, 14.07906], [107.35757, 14.02319], [107.38247, 13.99147], [107.44318, 13.99751], [107.46498, 13.91593], [107.45252, 13.78897], [107.53503, 13.73908], [107.61909, 13.52577], [107.62843, 13.3668], [107.49144, 13.01215], [107.49611, 12.88926], [107.55993, 12.7982], [107.5755, 12.52177], [107.55059, 12.36824], [107.4463, 12.29373], [107.42917, 12.24657], [107.34511, 12.33327], [107.15831, 12.27547], [106.99953, 12.08983], [106.92325, 12.06548], [106.79405, 12.0807], [106.70687, 11.96956], [106.4111, 11.97413], [106.4687, 11.86751], [106.44068, 11.86294], [106.44535, 11.8279], [106.41577, 11.76999], [106.45158, 11.68616], [106.44691, 11.66787], [106.37219, 11.69836], [106.30525, 11.67549], [106.26478, 11.72122], [106.18539, 11.75171], [106.13158, 11.73283], [106.06708, 11.77761], [106.02038, 11.77457], [106.00792, 11.7197], [105.95188, 11.63738], [105.88962, 11.67854], [105.8507, 11.66635], [105.80867, 11.60536], [105.81645, 11.56876], [105.87328, 11.55953], [105.88962, 11.43605], [105.86782, 11.28343], [106.10444, 11.07879], [106.1527, 11.10476], [106.1757, 11.07301], [106.20095, 10.97795], [106.14301, 10.98176], [106.18539, 10.79451], [106.06708, 10.8098], [105.94535, 10.9168], [105.93403, 10.83853], [105.84603, 10.85873], [105.86376, 10.89839], [105.77751, 11.03671], [105.50045, 10.94586], [105.42884, 10.96878], [105.34011, 10.86179], [105.11449, 10.96332], [105.08326, 10.95656], [105.02722, 10.89236], [105.09571, 10.72722], [104.95094, 10.64003], [104.87933, 10.52833], [104.59018, 10.53073], [104.49869, 10.4057], [104.47963, 10.43046], [104.43778, 10.42386], [103.99198, 10.48391], [102.47649, 9.66162], [104.81582, 8.03101], [109.55486, 8.10026], [111.60491, 13.57105], [108.00365, 17.98159], [108.10003, 21.47338]]]]
26774           }
26775         }, {
26776           type: "Feature",
26777           properties: {
26778             iso1A2: "VU",
26779             iso1A3: "VUT",
26780             iso1N3: "548",
26781             wikidata: "Q686",
26782             nameEn: "Vanuatu",
26783             groups: ["054", "009", "UN"],
26784             callingCodes: ["678"]
26785           },
26786           geometry: {
26787             type: "MultiPolygon",
26788             coordinates: [[[[156.73836, -14.50464], [174.245, -23.1974], [172.71443, -12.01327], [156.73836, -14.50464]]]]
26789           }
26790         }, {
26791           type: "Feature",
26792           properties: {
26793             iso1A2: "WF",
26794             iso1A3: "WLF",
26795             iso1N3: "876",
26796             wikidata: "Q35555",
26797             nameEn: "Wallis and Futuna",
26798             country: "FR",
26799             groups: ["Q1451600", "061", "009", "UN"],
26800             callingCodes: ["681"]
26801           },
26802           geometry: {
26803             type: "MultiPolygon",
26804             coordinates: [[[[-178.66551, -14.32452], [-176.76826, -14.95183], [-175.59809, -12.61507], [-178.66551, -14.32452]]]]
26805           }
26806         }, {
26807           type: "Feature",
26808           properties: {
26809             iso1A2: "WS",
26810             iso1A3: "WSM",
26811             iso1N3: "882",
26812             wikidata: "Q683",
26813             nameEn: "Samoa",
26814             groups: ["061", "009", "UN"],
26815             driveSide: "left",
26816             callingCodes: ["685"]
26817           },
26818           geometry: {
26819             type: "MultiPolygon",
26820             coordinates: [[[[-173.74402, -14.26669], [-170.99605, -15.1275], [-171.39864, -10.21587], [-173.74402, -14.26669]]]]
26821           }
26822         }, {
26823           type: "Feature",
26824           properties: {
26825             iso1A2: "XK",
26826             iso1A3: "XKX",
26827             wikidata: "Q1246",
26828             nameEn: "Kosovo",
26829             aliases: ["KV"],
26830             groups: ["039", "150"],
26831             isoStatus: "usrAssn",
26832             callingCodes: ["383"]
26833           },
26834           geometry: {
26835             type: "MultiPolygon",
26836             coordinates: [[[[21.39045, 42.74888], [21.44047, 42.87276], [21.36941, 42.87397], [21.32974, 42.90424], [21.2719, 42.8994], [21.23534, 42.95523], [21.23877, 43.00848], [21.2041, 43.02277], [21.16734, 42.99694], [21.14465, 43.11089], [21.08952, 43.13471], [21.05378, 43.10707], [21.00749, 43.13984], [20.96287, 43.12416], [20.83727, 43.17842], [20.88685, 43.21697], [20.82145, 43.26769], [20.73811, 43.25068], [20.68688, 43.21335], [20.59929, 43.20492], [20.69515, 43.09641], [20.64557, 43.00826], [20.59929, 43.01067], [20.48692, 42.93208], [20.53484, 42.8885], [20.43734, 42.83157], [20.40594, 42.84853], [20.35692, 42.8335], [20.27869, 42.81945], [20.2539, 42.76245], [20.04898, 42.77701], [20.02088, 42.74789], [20.02915, 42.71147], [20.0969, 42.65559], [20.07761, 42.55582], [20.17127, 42.50469], [20.21797, 42.41237], [20.24399, 42.32168], [20.34479, 42.32656], [20.3819, 42.3029], [20.48857, 42.25444], [20.56955, 42.12097], [20.55633, 42.08173], [20.59434, 42.03879], [20.63069, 41.94913], [20.57946, 41.91593], [20.59524, 41.8818], [20.68523, 41.85318], [20.76786, 41.91839], [20.75464, 42.05229], [21.11491, 42.20794], [21.16614, 42.19815], [21.22728, 42.08909], [21.31983, 42.10993], [21.29913, 42.13954], [21.30496, 42.1418], [21.38428, 42.24465], [21.43882, 42.23609], [21.43882, 42.2789], [21.50823, 42.27156], [21.52145, 42.24465], [21.58992, 42.25915], [21.56772, 42.30946], [21.5264, 42.33634], [21.53467, 42.36809], [21.57021, 42.3647], [21.59029, 42.38042], [21.62887, 42.37664], [21.64209, 42.41081], [21.62556, 42.45106], [21.7035, 42.51899], [21.70522, 42.54176], [21.7327, 42.55041], [21.75672, 42.62695], [21.79413, 42.65923], [21.75025, 42.70125], [21.6626, 42.67813], [21.58755, 42.70418], [21.59154, 42.72643], [21.47498, 42.74695], [21.39045, 42.74888]]]]
26837           }
26838         }, {
26839           type: "Feature",
26840           properties: {
26841             iso1A2: "YE",
26842             iso1A3: "YEM",
26843             iso1N3: "887",
26844             wikidata: "Q805",
26845             nameEn: "Yemen",
26846             groups: ["145", "142", "UN"],
26847             callingCodes: ["967"]
26848           },
26849           geometry: {
26850             type: "MultiPolygon",
26851             coordinates: [[[[57.49095, 8.14549], [52.81185, 17.28568], [52.74267, 17.29519], [52.78009, 17.35124], [52.00311, 19.00083], [49.04884, 18.59899], [48.19996, 18.20584], [47.58351, 17.50366], [47.48245, 17.10808], [47.00571, 16.94765], [46.76494, 17.29151], [46.31018, 17.20464], [44.50126, 17.47475], [43.70631, 17.35762], [43.43005, 17.56148], [43.29185, 17.53224], [43.22533, 17.38343], [43.32653, 17.31179], [43.20156, 17.25901], [43.17787, 17.14717], [43.23967, 17.03428], [43.18233, 17.02673], [43.1813, 16.98438], [43.19328, 16.94703], [43.1398, 16.90696], [43.18338, 16.84852], [43.22012, 16.83932], [43.22956, 16.80613], [43.24801, 16.80613], [43.26303, 16.79479], [43.25857, 16.75304], [43.21325, 16.74416], [43.22066, 16.65179], [43.15274, 16.67248], [43.11601, 16.53166], [42.97215, 16.51093], [42.94351, 16.49467], [42.94625, 16.39721], [42.76801, 16.40371], [42.15205, 16.40211], [40.99158, 15.81743], [43.29075, 12.79154], [43.32909, 12.59711], [43.90659, 12.3823], [51.12877, 12.56479], [57.49095, 8.14549]]]]
26852           }
26853         }, {
26854           type: "Feature",
26855           properties: {
26856             iso1A2: "YT",
26857             iso1A3: "MYT",
26858             iso1N3: "175",
26859             wikidata: "Q17063",
26860             nameEn: "Mayotte",
26861             country: "FR",
26862             groups: ["Q3320166", "EU", "014", "202", "002", "UN"],
26863             callingCodes: ["262"]
26864           },
26865           geometry: {
26866             type: "MultiPolygon",
26867             coordinates: [[[[43.28731, -13.97126], [45.54824, -13.22353], [45.4971, -11.75965], [43.28731, -13.97126]]]]
26868           }
26869         }, {
26870           type: "Feature",
26871           properties: {
26872             iso1A2: "ZA",
26873             iso1A3: "ZAF",
26874             iso1N3: "710",
26875             wikidata: "Q258",
26876             nameEn: "South Africa",
26877             groups: ["018", "202", "002", "UN"],
26878             driveSide: "left",
26879             callingCodes: ["27"]
26880           },
26881           geometry: {
26882             type: "MultiPolygon",
26883             coordinates: [[[[31.30611, -22.422], [31.16344, -22.32599], [31.08932, -22.34884], [30.86696, -22.28907], [30.6294, -22.32599], [30.48686, -22.31368], [30.38614, -22.34533], [30.28351, -22.35587], [30.2265, -22.2961], [30.13147, -22.30841], [29.92242, -22.19408], [29.76848, -22.14128], [29.64609, -22.12917], [29.37703, -22.19581], [29.21955, -22.17771], [29.18974, -22.18599], [29.15268, -22.21399], [29.10881, -22.21202], [29.0151, -22.22907], [28.91889, -22.44299], [28.63287, -22.55887], [28.34874, -22.5694], [28.04562, -22.8394], [28.04752, -22.90243], [27.93729, -22.96194], [27.93539, -23.04941], [27.74154, -23.2137], [27.6066, -23.21894], [27.52393, -23.37952], [27.33768, -23.40917], [26.99749, -23.65486], [26.84165, -24.24885], [26.51667, -24.47219], [26.46346, -24.60358], [26.39409, -24.63468], [25.8515, -24.75727], [25.84295, -24.78661], [25.88571, -24.87802], [25.72702, -25.25503], [25.69661, -25.29284], [25.6643, -25.4491], [25.58543, -25.6343], [25.33076, -25.76616], [25.12266, -25.75931], [25.01718, -25.72507], [24.8946, -25.80723], [24.67319, -25.81749], [24.44703, -25.73021], [24.36531, -25.773], [24.18287, -25.62916], [23.9244, -25.64286], [23.47588, -25.29971], [23.03497, -25.29971], [22.86012, -25.50572], [22.70808, -25.99186], [22.56365, -26.19668], [22.41921, -26.23078], [22.21206, -26.3773], [22.06192, -26.61882], [21.90703, -26.66808], [21.83291, -26.65959], [21.77114, -26.69015], [21.7854, -26.79199], [21.69322, -26.86152], [21.37869, -26.82083], [21.13353, -26.86661], [20.87031, -26.80047], [20.68596, -26.9039], [20.63275, -26.78181], [20.61754, -26.4692], [20.86081, -26.14892], [20.64795, -25.47827], [20.29826, -24.94869], [20.03678, -24.81004], [20.02809, -24.78725], [19.99817, -24.76768], [19.99882, -28.42622], [18.99885, -28.89165], [17.4579, -28.68718], [17.15405, -28.08573], [16.90446, -28.057], [16.59922, -28.53246], [16.46592, -28.57126], [16.45332, -28.63117], [12.51595, -32.27486], [38.88176, -48.03306], [34.51034, -26.91792], [32.35222, -26.86027], [32.29584, -26.852], [32.22302, -26.84136], [32.19409, -26.84032], [32.13315, -26.84345], [32.09664, -26.80721], [32.00893, -26.8096], [31.97463, -27.11057], [31.97592, -27.31675], [31.49834, -27.31549], [31.15027, -27.20151], [30.96088, -27.0245], [30.97757, -26.92706], [30.88826, -26.79622], [30.81101, -26.84722], [30.78927, -26.48271], [30.95819, -26.26303], [31.13073, -25.91558], [31.31237, -25.7431], [31.4175, -25.71886], [31.86881, -25.99973], [31.974, -25.95387], [31.92649, -25.84216], [32.00631, -25.65044], [31.97875, -25.46356], [32.01676, -25.38117], [32.03196, -25.10785], [31.9835, -24.29983], [31.90368, -24.18892], [31.87707, -23.95293], [31.77445, -23.90082], [31.70223, -23.72695], [31.67942, -23.60858], [31.56539, -23.47268], [31.55779, -23.176], [31.30611, -22.422]], [[29.33204, -29.45598], [29.28545, -29.58456], [29.12553, -29.76266], [29.16548, -29.91706], [28.9338, -30.05072], [28.80222, -30.10579], [28.68627, -30.12885], [28.399, -30.1592], [28.2319, -30.28476], [28.12073, -30.68072], [27.74814, -30.60635], [27.69467, -30.55862], [27.67819, -30.53437], [27.6521, -30.51707], [27.62137, -30.50509], [27.56781, -30.44562], [27.56901, -30.42504], [27.45452, -30.32239], [27.38108, -30.33456], [27.36649, -30.27246], [27.37293, -30.19401], [27.40778, -30.14577], [27.32555, -30.14785], [27.29603, -30.05473], [27.22719, -30.00718], [27.09489, -29.72796], [27.01016, -29.65439], [27.33464, -29.48161], [27.4358, -29.33465], [27.47254, -29.31968], [27.45125, -29.29708], [27.48679, -29.29349], [27.54258, -29.25575], [27.5158, -29.2261], [27.55974, -29.18954], [27.75458, -28.89839], [27.8907, -28.91612], [27.88933, -28.88156], [27.9392, -28.84864], [27.98675, -28.8787], [28.02503, -28.85991], [28.1317, -28.7293], [28.2348, -28.69471], [28.30518, -28.69531], [28.40612, -28.6215], [28.65091, -28.57025], [28.68043, -28.58744], [29.40524, -29.21246], [29.44883, -29.3772], [29.33204, -29.45598]]]]
26884           }
26885         }, {
26886           type: "Feature",
26887           properties: {
26888             iso1A2: "ZM",
26889             iso1A3: "ZMB",
26890             iso1N3: "894",
26891             wikidata: "Q953",
26892             nameEn: "Zambia",
26893             groups: ["014", "202", "002", "UN"],
26894             driveSide: "left",
26895             callingCodes: ["260"]
26896           },
26897           geometry: {
26898             type: "MultiPolygon",
26899             coordinates: [[[[32.95389, -9.40138], [32.76233, -9.31963], [32.75611, -9.28583], [32.53661, -9.24281], [32.49147, -9.14754], [32.43543, -9.11988], [32.25486, -9.13371], [32.16146, -9.05993], [32.08206, -9.04609], [31.98866, -9.07069], [31.94196, -9.02303], [31.94663, -8.93846], [31.81587, -8.88618], [31.71158, -8.91386], [31.57147, -8.81388], [31.57147, -8.70619], [31.37533, -8.60769], [31.00796, -8.58615], [30.79243, -8.27382], [28.88917, -8.4831], [28.9711, -8.66935], [28.38526, -9.23393], [28.36562, -9.30091], [28.52636, -9.35379], [28.51627, -9.44726], [28.56208, -9.49122], [28.68532, -9.78], [28.62795, -9.92942], [28.65032, -10.65133], [28.37241, -11.57848], [28.48357, -11.87532], [29.18592, -12.37921], [29.4992, -12.43843], [29.48404, -12.23604], [29.8139, -12.14898], [29.81551, -13.44683], [29.65078, -13.41844], [29.60531, -13.21685], [29.01918, -13.41353], [28.33199, -12.41375], [27.59932, -12.22123], [27.21025, -11.76157], [27.22541, -11.60323], [27.04351, -11.61312], [26.88687, -12.01868], [26.01777, -11.91488], [25.33058, -11.65767], [25.34069, -11.19707], [24.42612, -11.44975], [24.34528, -11.06816], [24.00027, -10.89356], [24.02603, -11.15368], [23.98804, -12.13149], [24.06672, -12.29058], [23.90937, -12.844], [24.03339, -12.99091], [21.97988, -13.00148], [22.00323, -16.18028], [22.17217, -16.50269], [23.20038, -17.47563], [23.47474, -17.62877], [24.23619, -17.47489], [24.32811, -17.49082], [24.38712, -17.46818], [24.5621, -17.52963], [24.70864, -17.49501], [25.00198, -17.58221], [25.26433, -17.79571], [25.51646, -17.86232], [25.6827, -17.81987], [25.85738, -17.91403], [25.85892, -17.97726], [26.08925, -17.98168], [26.0908, -17.93021], [26.21601, -17.88608], [26.55918, -17.99638], [26.68403, -18.07411], [26.74314, -18.0199], [26.89926, -17.98756], [27.14196, -17.81398], [27.30736, -17.60487], [27.61377, -17.34378], [27.62795, -17.24365], [27.83141, -16.96274], [28.73725, -16.5528], [28.76199, -16.51575], [28.81454, -16.48611], [28.8501, -16.04537], [28.9243, -15.93987], [29.01298, -15.93805], [29.21955, -15.76589], [29.4437, -15.68702], [29.8317, -15.6126], [30.35574, -15.6513], [30.41902, -15.62269], [30.22098, -14.99447], [33.24249, -14.00019], [33.16749, -13.93992], [33.07568, -13.98447], [33.02977, -14.05022], [32.99042, -13.95689], [32.88985, -13.82956], [32.79015, -13.80755], [32.76962, -13.77224], [32.84528, -13.71576], [32.7828, -13.64805], [32.68654, -13.64268], [32.66468, -13.60019], [32.68436, -13.55769], [32.73683, -13.57682], [32.84176, -13.52794], [32.86113, -13.47292], [33.0078, -13.19492], [32.98289, -13.12671], [33.02181, -12.88707], [32.96733, -12.88251], [32.94397, -12.76868], [33.05917, -12.59554], [33.18837, -12.61377], [33.28177, -12.54692], [33.37517, -12.54085], [33.54485, -12.35996], [33.47636, -12.32498], [33.3705, -12.34931], [33.25998, -12.14242], [33.33937, -11.91252], [33.32692, -11.59248], [33.24252, -11.59302], [33.23663, -11.40637], [33.29267, -11.43536], [33.29267, -11.3789], [33.39697, -11.15296], [33.25998, -10.88862], [33.28022, -10.84428], [33.47636, -10.78465], [33.70675, -10.56896], [33.54797, -10.36077], [33.53863, -10.20148], [33.31297, -10.05133], [33.37902, -9.9104], [33.36581, -9.81063], [33.31517, -9.82364], [33.2095, -9.61099], [33.12144, -9.58929], [33.10163, -9.66525], [33.05485, -9.61316], [33.00256, -9.63053], [33.00476, -9.5133], [32.95389, -9.40138]]]]
26900           }
26901         }, {
26902           type: "Feature",
26903           properties: {
26904             iso1A2: "ZW",
26905             iso1A3: "ZWE",
26906             iso1N3: "716",
26907             wikidata: "Q954",
26908             nameEn: "Zimbabwe",
26909             groups: ["014", "202", "002", "UN"],
26910             driveSide: "left",
26911             callingCodes: ["263"]
26912           },
26913           geometry: {
26914             type: "MultiPolygon",
26915             coordinates: [[[[30.41902, -15.62269], [30.35574, -15.6513], [29.8317, -15.6126], [29.4437, -15.68702], [29.21955, -15.76589], [29.01298, -15.93805], [28.9243, -15.93987], [28.8501, -16.04537], [28.81454, -16.48611], [28.76199, -16.51575], [28.73725, -16.5528], [27.83141, -16.96274], [27.62795, -17.24365], [27.61377, -17.34378], [27.30736, -17.60487], [27.14196, -17.81398], [26.89926, -17.98756], [26.74314, -18.0199], [26.68403, -18.07411], [26.55918, -17.99638], [26.21601, -17.88608], [26.0908, -17.93021], [26.08925, -17.98168], [25.85892, -17.97726], [25.85738, -17.91403], [25.6827, -17.81987], [25.51646, -17.86232], [25.26433, -17.79571], [25.23909, -17.90832], [25.31799, -18.07091], [25.39972, -18.12691], [25.53465, -18.39041], [25.68859, -18.56165], [25.79217, -18.6355], [25.82353, -18.82808], [25.94326, -18.90362], [25.99837, -19.02943], [25.96226, -19.08152], [26.17227, -19.53709], [26.72246, -19.92707], [27.21278, -20.08244], [27.29831, -20.28935], [27.28865, -20.49873], [27.69361, -20.48531], [27.72972, -20.51735], [27.69171, -21.08409], [27.91407, -21.31621], [28.01669, -21.57624], [28.29416, -21.59037], [28.49942, -21.66634], [28.58114, -21.63455], [29.07763, -21.81877], [29.04023, -21.85864], [29.02191, -21.90647], [29.02191, -21.95665], [29.04108, -22.00563], [29.08495, -22.04867], [29.14501, -22.07275], [29.1974, -22.07472], [29.24648, -22.05967], [29.3533, -22.18363], [29.37703, -22.19581], [29.64609, -22.12917], [29.76848, -22.14128], [29.92242, -22.19408], [30.13147, -22.30841], [30.2265, -22.2961], [30.28351, -22.35587], [30.38614, -22.34533], [30.48686, -22.31368], [30.6294, -22.32599], [30.86696, -22.28907], [31.08932, -22.34884], [31.16344, -22.32599], [31.30611, -22.422], [31.38336, -22.36919], [32.41234, -21.31246], [32.48236, -21.32873], [32.37115, -21.133], [32.51644, -20.91929], [32.48122, -20.63319], [32.55167, -20.56312], [32.66174, -20.56106], [32.85987, -20.27841], [32.85987, -20.16686], [32.93032, -20.03868], [33.01178, -20.02007], [33.06461, -19.77787], [32.95013, -19.67219], [32.84666, -19.68462], [32.84446, -19.48343], [32.78282, -19.47513], [32.77966, -19.36098], [32.85107, -19.29238], [32.87088, -19.09279], [32.84006, -19.0262], [32.72118, -19.02204], [32.69917, -18.94293], [32.73439, -18.92628], [32.70137, -18.84712], [32.82465, -18.77419], [32.9017, -18.7992], [32.95013, -18.69079], [32.88629, -18.58023], [32.88629, -18.51344], [33.02278, -18.4696], [33.03159, -18.35054], [32.94133, -17.99705], [33.0492, -17.60298], [32.98536, -17.55891], [32.96554, -17.48964], [33.0426, -17.3468], [33.00517, -17.30477], [32.96554, -17.11971], [32.84113, -16.92259], [32.91051, -16.89446], [32.97655, -16.70689], [32.78943, -16.70267], [32.69917, -16.66893], [32.71017, -16.59932], [32.42838, -16.4727], [32.28529, -16.43892], [32.02772, -16.43892], [31.91324, -16.41569], [31.90223, -16.34388], [31.67988, -16.19595], [31.42451, -16.15154], [31.30563, -16.01193], [31.13171, -15.98019], [30.97761, -16.05848], [30.91597, -15.99924], [30.42568, -15.9962], [30.41902, -15.62269]]]]
26916           }
26917         }];
26918         var borders_default = {
26919           type: type,
26920           features: features
26921         }; // src/country-coder.ts
26922
26923         var borders = borders_default;
26924         var whichPolygonGetter = {};
26925         var featuresByCode = {};
26926         var idFilterRegex = /(?=(?!^(and|the|of|el|la|de)$))(\b(and|the|of|el|la|de)\b)|[-_ .,'()&[\]/]/gi;
26927
26928         function canonicalID(id) {
26929           var s = id || "";
26930
26931           if (s.charAt(0) === ".") {
26932             return s.toUpperCase();
26933           } else {
26934             return s.replace(idFilterRegex, "").toUpperCase();
26935           }
26936         }
26937
26938         var levels = ["subterritory", "territory", "subcountryGroup", "country", "sharedLandform", "intermediateRegion", "subregion", "region", "subunion", "union", "unitedNations", "world"];
26939         loadDerivedDataAndCaches(borders);
26940
26941         function loadDerivedDataAndCaches(borders2) {
26942           var identifierProps = ["iso1A2", "iso1A3", "m49", "wikidata", "emojiFlag", "ccTLD", "nameEn"];
26943           var geometryFeatures = [];
26944
26945           for (var i in borders2.features) {
26946             var feature2 = borders2.features[i];
26947             feature2.properties.id = feature2.properties.iso1A2 || feature2.properties.m49 || feature2.properties.wikidata;
26948             loadM49(feature2);
26949             loadTLD(feature2);
26950             loadIsoStatus(feature2);
26951             loadLevel(feature2);
26952             loadGroups(feature2);
26953             loadFlag(feature2);
26954             cacheFeatureByIDs(feature2);
26955             if (feature2.geometry) geometryFeatures.push(feature2);
26956           }
26957
26958           for (var _i in borders2.features) {
26959             var _feature = borders2.features[_i];
26960             _feature.properties.groups = _feature.properties.groups.map(function (groupID) {
26961               return featuresByCode[groupID].properties.id;
26962             });
26963             loadMembersForGroupsOf(_feature);
26964           }
26965
26966           for (var _i2 in borders2.features) {
26967             var _feature2 = borders2.features[_i2];
26968             loadRoadSpeedUnit(_feature2);
26969             loadRoadHeightUnit(_feature2);
26970             loadDriveSide(_feature2);
26971             loadCallingCodes(_feature2);
26972             loadGroupGroups(_feature2);
26973           }
26974
26975           for (var _i3 in borders2.features) {
26976             var _feature3 = borders2.features[_i3];
26977
26978             _feature3.properties.groups.sort(function (groupID1, groupID2) {
26979               return levels.indexOf(featuresByCode[groupID1].properties.level) - levels.indexOf(featuresByCode[groupID2].properties.level);
26980             });
26981
26982             if (_feature3.properties.members) _feature3.properties.members.sort(function (id1, id2) {
26983               var diff = levels.indexOf(featuresByCode[id1].properties.level) - levels.indexOf(featuresByCode[id2].properties.level);
26984
26985               if (diff === 0) {
26986                 return borders2.features.indexOf(featuresByCode[id1]) - borders2.features.indexOf(featuresByCode[id2]);
26987               }
26988
26989               return diff;
26990             });
26991           }
26992
26993           var geometryOnlyCollection = {
26994             type: "FeatureCollection",
26995             features: geometryFeatures
26996           };
26997           whichPolygonGetter = whichPolygon_1(geometryOnlyCollection);
26998
26999           function loadGroups(feature2) {
27000             var props = feature2.properties;
27001
27002             if (!props.groups) {
27003               props.groups = [];
27004             }
27005
27006             if (feature2.geometry && props.country) {
27007               props.groups.push(props.country);
27008             }
27009
27010             if (props.m49 !== "001") {
27011               props.groups.push("001");
27012             }
27013           }
27014
27015           function loadM49(feature2) {
27016             var props = feature2.properties;
27017
27018             if (!props.m49 && props.iso1N3) {
27019               props.m49 = props.iso1N3;
27020             }
27021           }
27022
27023           function loadTLD(feature2) {
27024             var props = feature2.properties;
27025             if (props.level === "unitedNations") return;
27026
27027             if (!props.ccTLD && props.iso1A2) {
27028               props.ccTLD = "." + props.iso1A2.toLowerCase();
27029             }
27030           }
27031
27032           function loadIsoStatus(feature2) {
27033             var props = feature2.properties;
27034
27035             if (!props.isoStatus && props.iso1A2) {
27036               props.isoStatus = "official";
27037             }
27038           }
27039
27040           function loadLevel(feature2) {
27041             var props = feature2.properties;
27042             if (props.level) return;
27043
27044             if (!props.country) {
27045               props.level = "country";
27046             } else if (!props.iso1A2 || props.isoStatus === "official") {
27047               props.level = "territory";
27048             } else {
27049               props.level = "subterritory";
27050             }
27051           }
27052
27053           function loadGroupGroups(feature2) {
27054             var props = feature2.properties;
27055             if (feature2.geometry || !props.members) return;
27056             var featureLevelIndex = levels.indexOf(props.level);
27057             var sharedGroups = [];
27058
27059             var _loop = function _loop(_i4) {
27060               var memberID = props.members[_i4];
27061               var member = featuresByCode[memberID];
27062               var memberGroups = member.properties.groups.filter(function (groupID) {
27063                 return groupID !== feature2.properties.id && featureLevelIndex < levels.indexOf(featuresByCode[groupID].properties.level);
27064               });
27065
27066               if (_i4 === "0") {
27067                 sharedGroups = memberGroups;
27068               } else {
27069                 sharedGroups = sharedGroups.filter(function (groupID) {
27070                   return memberGroups.indexOf(groupID) !== -1;
27071                 });
27072               }
27073             };
27074
27075             for (var _i4 in props.members) {
27076               _loop(_i4);
27077             }
27078
27079             props.groups = props.groups.concat(sharedGroups.filter(function (groupID) {
27080               return props.groups.indexOf(groupID) === -1;
27081             }));
27082
27083             for (var j in sharedGroups) {
27084               var groupFeature = featuresByCode[sharedGroups[j]];
27085
27086               if (groupFeature.properties.members.indexOf(props.id) === -1) {
27087                 groupFeature.properties.members.push(props.id);
27088               }
27089             }
27090           }
27091
27092           function loadRoadSpeedUnit(feature2) {
27093             var props = feature2.properties;
27094
27095             if (feature2.geometry) {
27096               if (!props.roadSpeedUnit) props.roadSpeedUnit = "km/h";
27097             } else if (props.members) {
27098               var vals = Array.from(new Set(props.members.map(function (id) {
27099                 var member = featuresByCode[id];
27100                 if (member.geometry) return member.properties.roadSpeedUnit || "km/h";
27101               }).filter(Boolean)));
27102               if (vals.length === 1) props.roadSpeedUnit = vals[0];
27103             }
27104           }
27105
27106           function loadRoadHeightUnit(feature2) {
27107             var props = feature2.properties;
27108
27109             if (feature2.geometry) {
27110               if (!props.roadHeightUnit) props.roadHeightUnit = "m";
27111             } else if (props.members) {
27112               var vals = Array.from(new Set(props.members.map(function (id) {
27113                 var member = featuresByCode[id];
27114                 if (member.geometry) return member.properties.roadHeightUnit || "m";
27115               }).filter(Boolean)));
27116               if (vals.length === 1) props.roadHeightUnit = vals[0];
27117             }
27118           }
27119
27120           function loadDriveSide(feature2) {
27121             var props = feature2.properties;
27122
27123             if (feature2.geometry) {
27124               if (!props.driveSide) props.driveSide = "right";
27125             } else if (props.members) {
27126               var vals = Array.from(new Set(props.members.map(function (id) {
27127                 var member = featuresByCode[id];
27128                 if (member.geometry) return member.properties.driveSide || "right";
27129               }).filter(Boolean)));
27130               if (vals.length === 1) props.driveSide = vals[0];
27131             }
27132           }
27133
27134           function loadCallingCodes(feature2) {
27135             var props = feature2.properties;
27136
27137             if (!feature2.geometry && props.members) {
27138               props.callingCodes = Array.from(new Set(props.members.reduce(function (array, id) {
27139                 var member = featuresByCode[id];
27140                 if (member.geometry && member.properties.callingCodes) return array.concat(member.properties.callingCodes);
27141                 return array;
27142               }, [])));
27143             }
27144           }
27145
27146           function loadFlag(feature2) {
27147             if (!feature2.properties.iso1A2) return;
27148             var flag = feature2.properties.iso1A2.replace(/./g, function (_char) {
27149               return String.fromCodePoint(_char.charCodeAt(0) + 127397);
27150             });
27151             feature2.properties.emojiFlag = flag;
27152           }
27153
27154           function loadMembersForGroupsOf(feature2) {
27155             for (var j in feature2.properties.groups) {
27156               var groupID = feature2.properties.groups[j];
27157               var groupFeature = featuresByCode[groupID];
27158               if (!groupFeature.properties.members) groupFeature.properties.members = [];
27159               groupFeature.properties.members.push(feature2.properties.id);
27160             }
27161           }
27162
27163           function cacheFeatureByIDs(feature2) {
27164             var ids = [];
27165
27166             for (var k in identifierProps) {
27167               var prop = identifierProps[k];
27168               var id = feature2.properties[prop];
27169               if (id) ids.push(id);
27170             }
27171
27172             if (feature2.properties.aliases) {
27173               for (var j in feature2.properties.aliases) {
27174                 ids.push(feature2.properties.aliases[j]);
27175               }
27176             }
27177
27178             for (var _i5 in ids) {
27179               var _id = canonicalID(ids[_i5]);
27180
27181               featuresByCode[_id] = feature2;
27182             }
27183           }
27184         }
27185
27186         function locArray(loc) {
27187           if (Array.isArray(loc)) {
27188             return loc;
27189           } else if (loc.coordinates) {
27190             return loc.coordinates;
27191           }
27192
27193           return loc.geometry.coordinates;
27194         }
27195
27196         function smallestFeature(loc) {
27197           var query = locArray(loc);
27198           var featureProperties = whichPolygonGetter(query);
27199           if (!featureProperties) return null;
27200           return featuresByCode[featureProperties.id];
27201         }
27202
27203         function countryFeature(loc) {
27204           var feature2 = smallestFeature(loc);
27205           if (!feature2) return null;
27206           var countryCode = feature2.properties.country || feature2.properties.iso1A2;
27207           return featuresByCode[countryCode] || null;
27208         }
27209
27210         var defaultOpts = {
27211           level: void 0,
27212           maxLevel: void 0,
27213           withProp: void 0
27214         };
27215
27216         function featureForLoc(loc, opts) {
27217           var targetLevel = opts.level || "country";
27218           var maxLevel = opts.maxLevel || "world";
27219           var withProp = opts.withProp;
27220           var targetLevelIndex = levels.indexOf(targetLevel);
27221           if (targetLevelIndex === -1) return null;
27222           var maxLevelIndex = levels.indexOf(maxLevel);
27223           if (maxLevelIndex === -1) return null;
27224           if (maxLevelIndex < targetLevelIndex) return null;
27225
27226           if (targetLevel === "country") {
27227             var fastFeature = countryFeature(loc);
27228
27229             if (fastFeature) {
27230               if (!withProp || fastFeature.properties[withProp]) {
27231                 return fastFeature;
27232               }
27233             }
27234           }
27235
27236           var features2 = featuresContaining(loc);
27237
27238           for (var i in features2) {
27239             var feature2 = features2[i];
27240             var levelIndex = levels.indexOf(feature2.properties.level);
27241
27242             if (feature2.properties.level === targetLevel || levelIndex > targetLevelIndex && levelIndex <= maxLevelIndex) {
27243               if (!withProp || feature2.properties[withProp]) {
27244                 return feature2;
27245               }
27246             }
27247           }
27248
27249           return null;
27250         }
27251
27252         function featureForID(id) {
27253           var stringID;
27254
27255           if (typeof id === "number") {
27256             stringID = id.toString();
27257
27258             if (stringID.length === 1) {
27259               stringID = "00" + stringID;
27260             } else if (stringID.length === 2) {
27261               stringID = "0" + stringID;
27262             }
27263           } else {
27264             stringID = canonicalID(id);
27265           }
27266
27267           return featuresByCode[stringID] || null;
27268         }
27269
27270         function smallestFeaturesForBbox(bbox) {
27271           return whichPolygonGetter.bbox(bbox).map(function (props) {
27272             return featuresByCode[props.id];
27273           });
27274         }
27275
27276         function smallestOrMatchingFeature(query) {
27277           if (_typeof(query) === "object") {
27278             return smallestFeature(query);
27279           }
27280
27281           return featureForID(query);
27282         }
27283
27284         function feature$1(query) {
27285           var opts = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : defaultOpts;
27286
27287           if (_typeof(query) === "object") {
27288             return featureForLoc(query, opts);
27289           }
27290
27291           return featureForID(query);
27292         }
27293
27294         function iso1A2Code(query) {
27295           var opts = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : defaultOpts;
27296           opts.withProp = "iso1A2";
27297           var match = feature$1(query, opts);
27298           if (!match) return null;
27299           return match.properties.iso1A2 || null;
27300         }
27301
27302         function featuresContaining(query, strict) {
27303           var matchingFeatures;
27304
27305           if (Array.isArray(query) && query.length === 4) {
27306             matchingFeatures = smallestFeaturesForBbox(query);
27307           } else {
27308             var smallestOrMatching = smallestOrMatchingFeature(query);
27309             matchingFeatures = smallestOrMatching ? [smallestOrMatching] : [];
27310           }
27311
27312           if (!matchingFeatures.length) return [];
27313           var returnFeatures;
27314
27315           if (!strict || _typeof(query) === "object") {
27316             returnFeatures = matchingFeatures.slice();
27317           } else {
27318             returnFeatures = [];
27319           }
27320
27321           for (var j in matchingFeatures) {
27322             var properties = matchingFeatures[j].properties;
27323
27324             for (var i in properties.groups) {
27325               var groupID = properties.groups[i];
27326               var groupFeature = featuresByCode[groupID];
27327
27328               if (returnFeatures.indexOf(groupFeature) === -1) {
27329                 returnFeatures.push(groupFeature);
27330               }
27331             }
27332           }
27333
27334           return returnFeatures;
27335         }
27336
27337         function featuresIn(id, strict) {
27338           var feature2 = featureForID(id);
27339           if (!feature2) return [];
27340           var features2 = [];
27341
27342           if (!strict) {
27343             features2.push(feature2);
27344           }
27345
27346           var properties = feature2.properties;
27347
27348           if (properties.members) {
27349             for (var i in properties.members) {
27350               var memberID = properties.members[i];
27351               features2.push(featuresByCode[memberID]);
27352             }
27353           }
27354
27355           return features2;
27356         }
27357
27358         function aggregateFeature(id) {
27359           var features2 = featuresIn(id, false);
27360           if (features2.length === 0) return null;
27361           var aggregateCoordinates = [];
27362
27363           for (var i in features2) {
27364             var feature2 = features2[i];
27365
27366             if (feature2.geometry && feature2.geometry.type === "MultiPolygon" && feature2.geometry.coordinates) {
27367               aggregateCoordinates = aggregateCoordinates.concat(feature2.geometry.coordinates);
27368             }
27369           }
27370
27371           return {
27372             type: "Feature",
27373             properties: features2[0].properties,
27374             geometry: {
27375               type: "MultiPolygon",
27376               coordinates: aggregateCoordinates
27377             }
27378           };
27379         }
27380
27381         function roadSpeedUnit(query) {
27382           var feature2 = smallestOrMatchingFeature(query);
27383           return feature2 && feature2.properties.roadSpeedUnit || null;
27384         }
27385
27386         var RADIUS = 6378137;
27387         var FLATTENING = 1 / 298.257223563;
27388         var POLAR_RADIUS = 6356752.3142;
27389         var wgs84 = {
27390           RADIUS: RADIUS,
27391           FLATTENING: FLATTENING,
27392           POLAR_RADIUS: POLAR_RADIUS
27393         };
27394
27395         var geometry_1 = geometry;
27396         var ring = ringArea;
27397
27398         function geometry(_) {
27399           var area = 0,
27400               i;
27401
27402           switch (_.type) {
27403             case 'Polygon':
27404               return polygonArea(_.coordinates);
27405
27406             case 'MultiPolygon':
27407               for (i = 0; i < _.coordinates.length; i++) {
27408                 area += polygonArea(_.coordinates[i]);
27409               }
27410
27411               return area;
27412
27413             case 'Point':
27414             case 'MultiPoint':
27415             case 'LineString':
27416             case 'MultiLineString':
27417               return 0;
27418
27419             case 'GeometryCollection':
27420               for (i = 0; i < _.geometries.length; i++) {
27421                 area += geometry(_.geometries[i]);
27422               }
27423
27424               return area;
27425           }
27426         }
27427
27428         function polygonArea(coords) {
27429           var area = 0;
27430
27431           if (coords && coords.length > 0) {
27432             area += Math.abs(ringArea(coords[0]));
27433
27434             for (var i = 1; i < coords.length; i++) {
27435               area -= Math.abs(ringArea(coords[i]));
27436             }
27437           }
27438
27439           return area;
27440         }
27441         /**
27442          * Calculate the approximate area of the polygon were it projected onto
27443          *     the earth.  Note that this area will be positive if ring is oriented
27444          *     clockwise, otherwise it will be negative.
27445          *
27446          * Reference:
27447          * Robert. G. Chamberlain and William H. Duquette, "Some Algorithms for
27448          *     Polygons on a Sphere", JPL Publication 07-03, Jet Propulsion
27449          *     Laboratory, Pasadena, CA, June 2007 http://trs-new.jpl.nasa.gov/dspace/handle/2014/40409
27450          *
27451          * Returns:
27452          * {float} The approximate signed geodesic area of the polygon in square
27453          *     meters.
27454          */
27455
27456
27457         function ringArea(coords) {
27458           var p1,
27459               p2,
27460               p3,
27461               lowerIndex,
27462               middleIndex,
27463               upperIndex,
27464               i,
27465               area = 0,
27466               coordsLength = coords.length;
27467
27468           if (coordsLength > 2) {
27469             for (i = 0; i < coordsLength; i++) {
27470               if (i === coordsLength - 2) {
27471                 // i = N-2
27472                 lowerIndex = coordsLength - 2;
27473                 middleIndex = coordsLength - 1;
27474                 upperIndex = 0;
27475               } else if (i === coordsLength - 1) {
27476                 // i = N-1
27477                 lowerIndex = coordsLength - 1;
27478                 middleIndex = 0;
27479                 upperIndex = 1;
27480               } else {
27481                 // i = 0 to N-3
27482                 lowerIndex = i;
27483                 middleIndex = i + 1;
27484                 upperIndex = i + 2;
27485               }
27486
27487               p1 = coords[lowerIndex];
27488               p2 = coords[middleIndex];
27489               p3 = coords[upperIndex];
27490               area += (rad(p3[0]) - rad(p1[0])) * Math.sin(rad(p2[1]));
27491             }
27492
27493             area = area * wgs84.RADIUS * wgs84.RADIUS / 2;
27494           }
27495
27496           return area;
27497         }
27498
27499         function rad(_) {
27500           return _ * Math.PI / 180;
27501         }
27502
27503         var geojsonArea = {
27504           geometry: geometry_1,
27505           ring: ring
27506         };
27507
27508         var $includes = arrayIncludes.includes;
27509
27510
27511         // `Array.prototype.includes` method
27512         // https://tc39.es/ecma262/#sec-array.prototype.includes
27513         _export({ target: 'Array', proto: true }, {
27514           includes: function includes(el /* , fromIndex = 0 */) {
27515             return $includes(this, el, arguments.length > 1 ? arguments[1] : undefined);
27516           }
27517         });
27518
27519         // https://tc39.es/ecma262/#sec-array.prototype-@@unscopables
27520         addToUnscopables('includes');
27521
27522         var validateCenter_1$1 = function validateCenter(center) {
27523           var validCenterLengths = [2, 3];
27524
27525           if (!Array.isArray(center) || !validCenterLengths.includes(center.length)) {
27526             throw new Error("ERROR! Center has to be an array of length two or three");
27527           }
27528
27529           var _center = _slicedToArray(center, 2),
27530               lng = _center[0],
27531               lat = _center[1];
27532
27533           if (typeof lng !== "number" || typeof lat !== "number") {
27534             throw new Error("ERROR! Longitude and Latitude has to be numbers but where ".concat(_typeof(lng), " and ").concat(_typeof(lat)));
27535           }
27536
27537           if (lng > 180 || lng < -180) {
27538             throw new Error("ERROR! Longitude has to be between -180 and 180 but was ".concat(lng));
27539           }
27540
27541           if (lat > 90 || lat < -90) {
27542             throw new Error("ERROR! Latitude has to be between -90 and 90 but was ".concat(lat));
27543           }
27544         };
27545
27546         var validateCenter$1 = {
27547           validateCenter: validateCenter_1$1
27548         };
27549
27550         var validateRadius_1$1 = function validateRadius(radius) {
27551           if (typeof radius !== "number") {
27552             throw new Error("ERROR! Radius has to be a positive number but was: ".concat(_typeof(radius)));
27553           }
27554
27555           if (radius <= 0) {
27556             throw new Error("ERROR! Radius has to be a positive number but was: ".concat(radius));
27557           }
27558         };
27559
27560         var validateRadius$1 = {
27561           validateRadius: validateRadius_1$1
27562         };
27563
27564         var validateNumberOfEdges_1$1 = function validateNumberOfEdges(numberOfEdges) {
27565           if (typeof numberOfEdges !== "number") {
27566             var ARGUMENT_TYPE = Array.isArray(numberOfEdges) ? "array" : _typeof(numberOfEdges);
27567             throw new Error("ERROR! Number of edges has to be a number but was: ".concat(ARGUMENT_TYPE));
27568           }
27569
27570           if (numberOfEdges < 3) {
27571             throw new Error("ERROR! Number of edges has to be at least 3 but was: ".concat(numberOfEdges));
27572           }
27573         };
27574
27575         var validateNumberOfEdges$1 = {
27576           validateNumberOfEdges: validateNumberOfEdges_1$1
27577         };
27578
27579         var validateEarthRadius_1$1 = function validateEarthRadius(earthRadius) {
27580           if (typeof earthRadius !== "number") {
27581             var ARGUMENT_TYPE = Array.isArray(earthRadius) ? "array" : _typeof(earthRadius);
27582             throw new Error("ERROR! Earth radius has to be a number but was: ".concat(ARGUMENT_TYPE));
27583           }
27584
27585           if (earthRadius <= 0) {
27586             throw new Error("ERROR! Earth radius has to be a positive number but was: ".concat(earthRadius));
27587           }
27588         };
27589
27590         var validateEarthRadius$1 = {
27591           validateEarthRadius: validateEarthRadius_1$1
27592         };
27593
27594         var validateBearing_1$1 = function validateBearing(bearing) {
27595           if (typeof bearing !== "number") {
27596             var ARGUMENT_TYPE = Array.isArray(bearing) ? "array" : _typeof(bearing);
27597             throw new Error("ERROR! Bearing has to be a number but was: ".concat(ARGUMENT_TYPE));
27598           }
27599         };
27600
27601         var validateBearing$1 = {
27602           validateBearing: validateBearing_1$1
27603         };
27604
27605         var validateCenter = validateCenter$1.validateCenter;
27606         var validateRadius = validateRadius$1.validateRadius;
27607         var validateNumberOfEdges = validateNumberOfEdges$1.validateNumberOfEdges;
27608         var validateEarthRadius = validateEarthRadius$1.validateEarthRadius;
27609         var validateBearing = validateBearing$1.validateBearing;
27610
27611         function validateInput$1(_ref) {
27612           var center = _ref.center,
27613               radius = _ref.radius,
27614               numberOfEdges = _ref.numberOfEdges,
27615               earthRadius = _ref.earthRadius,
27616               bearing = _ref.bearing;
27617           validateCenter(center);
27618           validateRadius(radius);
27619           validateNumberOfEdges(numberOfEdges);
27620           validateEarthRadius(earthRadius);
27621           validateBearing(bearing);
27622         }
27623
27624         var validateCenter_1 = validateCenter;
27625         var validateRadius_1 = validateRadius;
27626         var validateNumberOfEdges_1 = validateNumberOfEdges;
27627         var validateEarthRadius_1 = validateEarthRadius;
27628         var validateBearing_1 = validateBearing;
27629         var validateInput_1 = validateInput$1;
27630         var inputValidation = {
27631           validateCenter: validateCenter_1,
27632           validateRadius: validateRadius_1,
27633           validateNumberOfEdges: validateNumberOfEdges_1,
27634           validateEarthRadius: validateEarthRadius_1,
27635           validateBearing: validateBearing_1,
27636           validateInput: validateInput_1
27637         };
27638
27639         var validateInput = inputValidation.validateInput;
27640         var defaultEarthRadius = 6378137; // equatorial Earth radius
27641
27642         function toRadians(angleInDegrees) {
27643           return angleInDegrees * Math.PI / 180;
27644         }
27645
27646         function toDegrees(angleInRadians) {
27647           return angleInRadians * 180 / Math.PI;
27648         }
27649
27650         function offset(c1, distance, earthRadius, bearing) {
27651           var lat1 = toRadians(c1[1]);
27652           var lon1 = toRadians(c1[0]);
27653           var dByR = distance / earthRadius;
27654           var lat = Math.asin(Math.sin(lat1) * Math.cos(dByR) + Math.cos(lat1) * Math.sin(dByR) * Math.cos(bearing));
27655           var lon = lon1 + Math.atan2(Math.sin(bearing) * Math.sin(dByR) * Math.cos(lat1), Math.cos(dByR) - Math.sin(lat1) * Math.sin(lat));
27656           return [toDegrees(lon), toDegrees(lat)];
27657         }
27658
27659         var circleToPolygon = function circleToPolygon(center, radius, options) {
27660           var n = getNumberOfEdges(options);
27661           var earthRadius = getEarthRadius(options);
27662           var bearing = getBearing(options);
27663           var direction = getDirection(options); // validateInput() throws error on invalid input and do nothing on valid input
27664
27665           validateInput({
27666             center: center,
27667             radius: radius,
27668             numberOfEdges: n,
27669             earthRadius: earthRadius,
27670             bearing: bearing
27671           });
27672           var start = toRadians(bearing);
27673           var coordinates = [];
27674
27675           for (var i = 0; i < n; ++i) {
27676             coordinates.push(offset(center, radius, earthRadius, start + direction * 2 * Math.PI * -i / n));
27677           }
27678
27679           coordinates.push(coordinates[0]);
27680           return {
27681             type: "Polygon",
27682             coordinates: [coordinates]
27683           };
27684         };
27685
27686         function getNumberOfEdges(options) {
27687           if (isUndefinedOrNull(options)) {
27688             return 32;
27689           } else if (isObjectNotArray(options)) {
27690             var numberOfEdges = options.numberOfEdges;
27691             return numberOfEdges === undefined ? 32 : numberOfEdges;
27692           }
27693
27694           return options;
27695         }
27696
27697         function getEarthRadius(options) {
27698           if (isUndefinedOrNull(options)) {
27699             return defaultEarthRadius;
27700           } else if (isObjectNotArray(options)) {
27701             var earthRadius = options.earthRadius;
27702             return earthRadius === undefined ? defaultEarthRadius : earthRadius;
27703           }
27704
27705           return defaultEarthRadius;
27706         }
27707
27708         function getDirection(options) {
27709           if (isObjectNotArray(options) && options.rightHandRule) {
27710             return -1;
27711           }
27712
27713           return 1;
27714         }
27715
27716         function getBearing(options) {
27717           if (isUndefinedOrNull(options)) {
27718             return 0;
27719           } else if (isObjectNotArray(options)) {
27720             var bearing = options.bearing;
27721             return bearing === undefined ? 0 : bearing;
27722           }
27723
27724           return 0;
27725         }
27726
27727         function isObjectNotArray(argument) {
27728           return argument !== null && _typeof(argument) === "object" && !Array.isArray(argument);
27729         }
27730
27731         function isUndefinedOrNull(argument) {
27732           return argument === null || argument === undefined;
27733         }
27734
27735         // `Number.EPSILON` constant
27736         // https://tc39.es/ecma262/#sec-number.epsilon
27737         _export({ target: 'Number', stat: true }, {
27738           EPSILON: Math.pow(2, -52)
27739         });
27740
27741         var quot = /"/g;
27742
27743         // `CreateHTML` abstract operation
27744         // https://tc39.es/ecma262/#sec-createhtml
27745         var createHtml = function (string, tag, attribute, value) {
27746           var S = String(requireObjectCoercible(string));
27747           var p1 = '<' + tag;
27748           if (attribute !== '') p1 += ' ' + attribute + '="' + String(value).replace(quot, '&quot;') + '"';
27749           return p1 + '>' + S + '</' + tag + '>';
27750         };
27751
27752         // check the existence of a method, lowercase
27753         // of a tag and escaping quotes in arguments
27754         var stringHtmlForced = function (METHOD_NAME) {
27755           return fails(function () {
27756             var test = ''[METHOD_NAME]('"');
27757             return test !== test.toLowerCase() || test.split('"').length > 3;
27758           });
27759         };
27760
27761         // `String.prototype.link` method
27762         // https://tc39.es/ecma262/#sec-string.prototype.link
27763         _export({ target: 'String', proto: true, forced: stringHtmlForced('link') }, {
27764           link: function link(url) {
27765             return createHtml(this, 'a', 'href', url);
27766           }
27767         });
27768
27769         /**
27770          * splaytree v3.1.0
27771          * Fast Splay tree for Node and browser
27772          *
27773          * @author Alexander Milevski <info@w8r.name>
27774          * @license MIT
27775          * @preserve
27776          */
27777         var Node =
27778         /** @class */
27779         function () {
27780           function Node(key, data) {
27781             this.next = null;
27782             this.key = key;
27783             this.data = data;
27784             this.left = null;
27785             this.right = null;
27786           }
27787
27788           return Node;
27789         }();
27790         /* follows "An implementation of top-down splaying"
27791          * by D. Sleator <sleator@cs.cmu.edu> March 1992
27792          */
27793
27794
27795         function DEFAULT_COMPARE(a, b) {
27796           return a > b ? 1 : a < b ? -1 : 0;
27797         }
27798         /**
27799          * Simple top down splay, not requiring i to be in the tree t.
27800          */
27801
27802
27803         function splay(i, t, comparator) {
27804           var N = new Node(null, null);
27805           var l = N;
27806           var r = N;
27807
27808           while (true) {
27809             var cmp = comparator(i, t.key); //if (i < t.key) {
27810
27811             if (cmp < 0) {
27812               if (t.left === null) break; //if (i < t.left.key) {
27813
27814               if (comparator(i, t.left.key) < 0) {
27815                 var y = t.left;
27816                 /* rotate right */
27817
27818                 t.left = y.right;
27819                 y.right = t;
27820                 t = y;
27821                 if (t.left === null) break;
27822               }
27823
27824               r.left = t;
27825               /* link right */
27826
27827               r = t;
27828               t = t.left; //} else if (i > t.key) {
27829             } else if (cmp > 0) {
27830               if (t.right === null) break; //if (i > t.right.key) {
27831
27832               if (comparator(i, t.right.key) > 0) {
27833                 var y = t.right;
27834                 /* rotate left */
27835
27836                 t.right = y.left;
27837                 y.left = t;
27838                 t = y;
27839                 if (t.right === null) break;
27840               }
27841
27842               l.right = t;
27843               /* link left */
27844
27845               l = t;
27846               t = t.right;
27847             } else break;
27848           }
27849           /* assemble */
27850
27851
27852           l.right = t.left;
27853           r.left = t.right;
27854           t.left = N.right;
27855           t.right = N.left;
27856           return t;
27857         }
27858
27859         function insert(i, data, t, comparator) {
27860           var node = new Node(i, data);
27861
27862           if (t === null) {
27863             node.left = node.right = null;
27864             return node;
27865           }
27866
27867           t = splay(i, t, comparator);
27868           var cmp = comparator(i, t.key);
27869
27870           if (cmp < 0) {
27871             node.left = t.left;
27872             node.right = t;
27873             t.left = null;
27874           } else if (cmp >= 0) {
27875             node.right = t.right;
27876             node.left = t;
27877             t.right = null;
27878           }
27879
27880           return node;
27881         }
27882
27883         function split(key, v, comparator) {
27884           var left = null;
27885           var right = null;
27886
27887           if (v) {
27888             v = splay(key, v, comparator);
27889             var cmp = comparator(v.key, key);
27890
27891             if (cmp === 0) {
27892               left = v.left;
27893               right = v.right;
27894             } else if (cmp < 0) {
27895               right = v.right;
27896               v.right = null;
27897               left = v;
27898             } else {
27899               left = v.left;
27900               v.left = null;
27901               right = v;
27902             }
27903           }
27904
27905           return {
27906             left: left,
27907             right: right
27908           };
27909         }
27910
27911         function merge$3(left, right, comparator) {
27912           if (right === null) return left;
27913           if (left === null) return right;
27914           right = splay(left.key, right, comparator);
27915           right.left = left;
27916           return right;
27917         }
27918         /**
27919          * Prints level of the tree
27920          */
27921
27922
27923         function printRow(root, prefix, isTail, out, printNode) {
27924           if (root) {
27925             out("" + prefix + (isTail ? '└── ' : '├── ') + printNode(root) + "\n");
27926             var indent = prefix + (isTail ? '    ' : '│   ');
27927             if (root.left) printRow(root.left, indent, false, out, printNode);
27928             if (root.right) printRow(root.right, indent, true, out, printNode);
27929           }
27930         }
27931
27932         var Tree =
27933         /** @class */
27934         function () {
27935           function Tree(comparator) {
27936             if (comparator === void 0) {
27937               comparator = DEFAULT_COMPARE;
27938             }
27939
27940             this._root = null;
27941             this._size = 0;
27942             this._comparator = comparator;
27943           }
27944           /**
27945            * Inserts a key, allows duplicates
27946            */
27947
27948
27949           Tree.prototype.insert = function (key, data) {
27950             this._size++;
27951             return this._root = insert(key, data, this._root, this._comparator);
27952           };
27953           /**
27954            * Adds a key, if it is not present in the tree
27955            */
27956
27957
27958           Tree.prototype.add = function (key, data) {
27959             var node = new Node(key, data);
27960
27961             if (this._root === null) {
27962               node.left = node.right = null;
27963               this._size++;
27964               this._root = node;
27965             }
27966
27967             var comparator = this._comparator;
27968             var t = splay(key, this._root, comparator);
27969             var cmp = comparator(key, t.key);
27970             if (cmp === 0) this._root = t;else {
27971               if (cmp < 0) {
27972                 node.left = t.left;
27973                 node.right = t;
27974                 t.left = null;
27975               } else if (cmp > 0) {
27976                 node.right = t.right;
27977                 node.left = t;
27978                 t.right = null;
27979               }
27980
27981               this._size++;
27982               this._root = node;
27983             }
27984             return this._root;
27985           };
27986           /**
27987            * @param  {Key} key
27988            * @return {Node|null}
27989            */
27990
27991
27992           Tree.prototype.remove = function (key) {
27993             this._root = this._remove(key, this._root, this._comparator);
27994           };
27995           /**
27996            * Deletes i from the tree if it's there
27997            */
27998
27999
28000           Tree.prototype._remove = function (i, t, comparator) {
28001             var x;
28002             if (t === null) return null;
28003             t = splay(i, t, comparator);
28004             var cmp = comparator(i, t.key);
28005
28006             if (cmp === 0) {
28007               /* found it */
28008               if (t.left === null) {
28009                 x = t.right;
28010               } else {
28011                 x = splay(i, t.left, comparator);
28012                 x.right = t.right;
28013               }
28014
28015               this._size--;
28016               return x;
28017             }
28018
28019             return t;
28020             /* It wasn't there */
28021           };
28022           /**
28023            * Removes and returns the node with smallest key
28024            */
28025
28026
28027           Tree.prototype.pop = function () {
28028             var node = this._root;
28029
28030             if (node) {
28031               while (node.left) {
28032                 node = node.left;
28033               }
28034
28035               this._root = splay(node.key, this._root, this._comparator);
28036               this._root = this._remove(node.key, this._root, this._comparator);
28037               return {
28038                 key: node.key,
28039                 data: node.data
28040               };
28041             }
28042
28043             return null;
28044           };
28045           /**
28046            * Find without splaying
28047            */
28048
28049
28050           Tree.prototype.findStatic = function (key) {
28051             var current = this._root;
28052             var compare = this._comparator;
28053
28054             while (current) {
28055               var cmp = compare(key, current.key);
28056               if (cmp === 0) return current;else if (cmp < 0) current = current.left;else current = current.right;
28057             }
28058
28059             return null;
28060           };
28061
28062           Tree.prototype.find = function (key) {
28063             if (this._root) {
28064               this._root = splay(key, this._root, this._comparator);
28065               if (this._comparator(key, this._root.key) !== 0) return null;
28066             }
28067
28068             return this._root;
28069           };
28070
28071           Tree.prototype.contains = function (key) {
28072             var current = this._root;
28073             var compare = this._comparator;
28074
28075             while (current) {
28076               var cmp = compare(key, current.key);
28077               if (cmp === 0) return true;else if (cmp < 0) current = current.left;else current = current.right;
28078             }
28079
28080             return false;
28081           };
28082
28083           Tree.prototype.forEach = function (visitor, ctx) {
28084             var current = this._root;
28085             var Q = [];
28086             /* Initialize stack s */
28087
28088             var done = false;
28089
28090             while (!done) {
28091               if (current !== null) {
28092                 Q.push(current);
28093                 current = current.left;
28094               } else {
28095                 if (Q.length !== 0) {
28096                   current = Q.pop();
28097                   visitor.call(ctx, current);
28098                   current = current.right;
28099                 } else done = true;
28100               }
28101             }
28102
28103             return this;
28104           };
28105           /**
28106            * Walk key range from `low` to `high`. Stops if `fn` returns a value.
28107            */
28108
28109
28110           Tree.prototype.range = function (low, high, fn, ctx) {
28111             var Q = [];
28112             var compare = this._comparator;
28113             var node = this._root;
28114             var cmp;
28115
28116             while (Q.length !== 0 || node) {
28117               if (node) {
28118                 Q.push(node);
28119                 node = node.left;
28120               } else {
28121                 node = Q.pop();
28122                 cmp = compare(node.key, high);
28123
28124                 if (cmp > 0) {
28125                   break;
28126                 } else if (compare(node.key, low) >= 0) {
28127                   if (fn.call(ctx, node)) return this; // stop if smth is returned
28128                 }
28129
28130                 node = node.right;
28131               }
28132             }
28133
28134             return this;
28135           };
28136           /**
28137            * Returns array of keys
28138            */
28139
28140
28141           Tree.prototype.keys = function () {
28142             var keys = [];
28143             this.forEach(function (_a) {
28144               var key = _a.key;
28145               return keys.push(key);
28146             });
28147             return keys;
28148           };
28149           /**
28150            * Returns array of all the data in the nodes
28151            */
28152
28153
28154           Tree.prototype.values = function () {
28155             var values = [];
28156             this.forEach(function (_a) {
28157               var data = _a.data;
28158               return values.push(data);
28159             });
28160             return values;
28161           };
28162
28163           Tree.prototype.min = function () {
28164             if (this._root) return this.minNode(this._root).key;
28165             return null;
28166           };
28167
28168           Tree.prototype.max = function () {
28169             if (this._root) return this.maxNode(this._root).key;
28170             return null;
28171           };
28172
28173           Tree.prototype.minNode = function (t) {
28174             if (t === void 0) {
28175               t = this._root;
28176             }
28177
28178             if (t) while (t.left) {
28179               t = t.left;
28180             }
28181             return t;
28182           };
28183
28184           Tree.prototype.maxNode = function (t) {
28185             if (t === void 0) {
28186               t = this._root;
28187             }
28188
28189             if (t) while (t.right) {
28190               t = t.right;
28191             }
28192             return t;
28193           };
28194           /**
28195            * Returns node at given index
28196            */
28197
28198
28199           Tree.prototype.at = function (index) {
28200             var current = this._root;
28201             var done = false;
28202             var i = 0;
28203             var Q = [];
28204
28205             while (!done) {
28206               if (current) {
28207                 Q.push(current);
28208                 current = current.left;
28209               } else {
28210                 if (Q.length > 0) {
28211                   current = Q.pop();
28212                   if (i === index) return current;
28213                   i++;
28214                   current = current.right;
28215                 } else done = true;
28216               }
28217             }
28218
28219             return null;
28220           };
28221
28222           Tree.prototype.next = function (d) {
28223             var root = this._root;
28224             var successor = null;
28225
28226             if (d.right) {
28227               successor = d.right;
28228
28229               while (successor.left) {
28230                 successor = successor.left;
28231               }
28232
28233               return successor;
28234             }
28235
28236             var comparator = this._comparator;
28237
28238             while (root) {
28239               var cmp = comparator(d.key, root.key);
28240               if (cmp === 0) break;else if (cmp < 0) {
28241                 successor = root;
28242                 root = root.left;
28243               } else root = root.right;
28244             }
28245
28246             return successor;
28247           };
28248
28249           Tree.prototype.prev = function (d) {
28250             var root = this._root;
28251             var predecessor = null;
28252
28253             if (d.left !== null) {
28254               predecessor = d.left;
28255
28256               while (predecessor.right) {
28257                 predecessor = predecessor.right;
28258               }
28259
28260               return predecessor;
28261             }
28262
28263             var comparator = this._comparator;
28264
28265             while (root) {
28266               var cmp = comparator(d.key, root.key);
28267               if (cmp === 0) break;else if (cmp < 0) root = root.left;else {
28268                 predecessor = root;
28269                 root = root.right;
28270               }
28271             }
28272
28273             return predecessor;
28274           };
28275
28276           Tree.prototype.clear = function () {
28277             this._root = null;
28278             this._size = 0;
28279             return this;
28280           };
28281
28282           Tree.prototype.toList = function () {
28283             return toList(this._root);
28284           };
28285           /**
28286            * Bulk-load items. Both array have to be same size
28287            */
28288
28289
28290           Tree.prototype.load = function (keys, values, presort) {
28291             if (values === void 0) {
28292               values = [];
28293             }
28294
28295             if (presort === void 0) {
28296               presort = false;
28297             }
28298
28299             var size = keys.length;
28300             var comparator = this._comparator; // sort if needed
28301
28302             if (presort) sort(keys, values, 0, size - 1, comparator);
28303
28304             if (this._root === null) {
28305               // empty tree
28306               this._root = loadRecursive(keys, values, 0, size);
28307               this._size = size;
28308             } else {
28309               // that re-builds the whole tree from two in-order traversals
28310               var mergedList = mergeLists(this.toList(), createList(keys, values), comparator);
28311               size = this._size + size;
28312               this._root = sortedListToBST({
28313                 head: mergedList
28314               }, 0, size);
28315             }
28316
28317             return this;
28318           };
28319
28320           Tree.prototype.isEmpty = function () {
28321             return this._root === null;
28322           };
28323
28324           Object.defineProperty(Tree.prototype, "size", {
28325             get: function get() {
28326               return this._size;
28327             },
28328             enumerable: true,
28329             configurable: true
28330           });
28331           Object.defineProperty(Tree.prototype, "root", {
28332             get: function get() {
28333               return this._root;
28334             },
28335             enumerable: true,
28336             configurable: true
28337           });
28338
28339           Tree.prototype.toString = function (printNode) {
28340             if (printNode === void 0) {
28341               printNode = function printNode(n) {
28342                 return String(n.key);
28343               };
28344             }
28345
28346             var out = [];
28347             printRow(this._root, '', true, function (v) {
28348               return out.push(v);
28349             }, printNode);
28350             return out.join('');
28351           };
28352
28353           Tree.prototype.update = function (key, newKey, newData) {
28354             var comparator = this._comparator;
28355
28356             var _a = split(key, this._root, comparator),
28357                 left = _a.left,
28358                 right = _a.right;
28359
28360             if (comparator(key, newKey) < 0) {
28361               right = insert(newKey, newData, right, comparator);
28362             } else {
28363               left = insert(newKey, newData, left, comparator);
28364             }
28365
28366             this._root = merge$3(left, right, comparator);
28367           };
28368
28369           Tree.prototype.split = function (key) {
28370             return split(key, this._root, this._comparator);
28371           };
28372
28373           return Tree;
28374         }();
28375
28376         function loadRecursive(keys, values, start, end) {
28377           var size = end - start;
28378
28379           if (size > 0) {
28380             var middle = start + Math.floor(size / 2);
28381             var key = keys[middle];
28382             var data = values[middle];
28383             var node = new Node(key, data);
28384             node.left = loadRecursive(keys, values, start, middle);
28385             node.right = loadRecursive(keys, values, middle + 1, end);
28386             return node;
28387           }
28388
28389           return null;
28390         }
28391
28392         function createList(keys, values) {
28393           var head = new Node(null, null);
28394           var p = head;
28395
28396           for (var i = 0; i < keys.length; i++) {
28397             p = p.next = new Node(keys[i], values[i]);
28398           }
28399
28400           p.next = null;
28401           return head.next;
28402         }
28403
28404         function toList(root) {
28405           var current = root;
28406           var Q = [];
28407           var done = false;
28408           var head = new Node(null, null);
28409           var p = head;
28410
28411           while (!done) {
28412             if (current) {
28413               Q.push(current);
28414               current = current.left;
28415             } else {
28416               if (Q.length > 0) {
28417                 current = p = p.next = Q.pop();
28418                 current = current.right;
28419               } else done = true;
28420             }
28421           }
28422
28423           p.next = null; // that'll work even if the tree was empty
28424
28425           return head.next;
28426         }
28427
28428         function sortedListToBST(list, start, end) {
28429           var size = end - start;
28430
28431           if (size > 0) {
28432             var middle = start + Math.floor(size / 2);
28433             var left = sortedListToBST(list, start, middle);
28434             var root = list.head;
28435             root.left = left;
28436             list.head = list.head.next;
28437             root.right = sortedListToBST(list, middle + 1, end);
28438             return root;
28439           }
28440
28441           return null;
28442         }
28443
28444         function mergeLists(l1, l2, compare) {
28445           var head = new Node(null, null); // dummy
28446
28447           var p = head;
28448           var p1 = l1;
28449           var p2 = l2;
28450
28451           while (p1 !== null && p2 !== null) {
28452             if (compare(p1.key, p2.key) < 0) {
28453               p.next = p1;
28454               p1 = p1.next;
28455             } else {
28456               p.next = p2;
28457               p2 = p2.next;
28458             }
28459
28460             p = p.next;
28461           }
28462
28463           if (p1 !== null) {
28464             p.next = p1;
28465           } else if (p2 !== null) {
28466             p.next = p2;
28467           }
28468
28469           return head.next;
28470         }
28471
28472         function sort(keys, values, left, right, compare) {
28473           if (left >= right) return;
28474           var pivot = keys[left + right >> 1];
28475           var i = left - 1;
28476           var j = right + 1;
28477
28478           while (true) {
28479             do {
28480               i++;
28481             } while (compare(keys[i], pivot) < 0);
28482
28483             do {
28484               j--;
28485             } while (compare(keys[j], pivot) > 0);
28486
28487             if (i >= j) break;
28488             var tmp = keys[i];
28489             keys[i] = keys[j];
28490             keys[j] = tmp;
28491             tmp = values[i];
28492             values[i] = values[j];
28493             values[j] = tmp;
28494           }
28495
28496           sort(keys, values, left, j, compare);
28497           sort(keys, values, j + 1, right, compare);
28498         }
28499
28500         function _classCallCheck(instance, Constructor) {
28501           if (!(instance instanceof Constructor)) {
28502             throw new TypeError("Cannot call a class as a function");
28503           }
28504         }
28505
28506         function _defineProperties(target, props) {
28507           for (var i = 0; i < props.length; i++) {
28508             var descriptor = props[i];
28509             descriptor.enumerable = descriptor.enumerable || false;
28510             descriptor.configurable = true;
28511             if ("value" in descriptor) descriptor.writable = true;
28512             Object.defineProperty(target, descriptor.key, descriptor);
28513           }
28514         }
28515
28516         function _createClass(Constructor, protoProps, staticProps) {
28517           if (protoProps) _defineProperties(Constructor.prototype, protoProps);
28518           if (staticProps) _defineProperties(Constructor, staticProps);
28519           return Constructor;
28520         }
28521         /**
28522          * A bounding box has the format:
28523          *
28524          *  { ll: { x: xmin, y: ymin }, ur: { x: xmax, y: ymax } }
28525          *
28526          */
28527
28528
28529         var isInBbox = function isInBbox(bbox, point) {
28530           return bbox.ll.x <= point.x && point.x <= bbox.ur.x && bbox.ll.y <= point.y && point.y <= bbox.ur.y;
28531         };
28532         /* Returns either null, or a bbox (aka an ordered pair of points)
28533          * If there is only one point of overlap, a bbox with identical points
28534          * will be returned */
28535
28536
28537         var getBboxOverlap = function getBboxOverlap(b1, b2) {
28538           // check if the bboxes overlap at all
28539           if (b2.ur.x < b1.ll.x || b1.ur.x < b2.ll.x || b2.ur.y < b1.ll.y || b1.ur.y < b2.ll.y) return null; // find the middle two X values
28540
28541           var lowerX = b1.ll.x < b2.ll.x ? b2.ll.x : b1.ll.x;
28542           var upperX = b1.ur.x < b2.ur.x ? b1.ur.x : b2.ur.x; // find the middle two Y values
28543
28544           var lowerY = b1.ll.y < b2.ll.y ? b2.ll.y : b1.ll.y;
28545           var upperY = b1.ur.y < b2.ur.y ? b1.ur.y : b2.ur.y; // put those middle values together to get the overlap
28546
28547           return {
28548             ll: {
28549               x: lowerX,
28550               y: lowerY
28551             },
28552             ur: {
28553               x: upperX,
28554               y: upperY
28555             }
28556           };
28557         };
28558         /* Javascript doesn't do integer math. Everything is
28559          * floating point with percision Number.EPSILON.
28560          *
28561          * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/EPSILON
28562          */
28563
28564
28565         var epsilon = Number.EPSILON; // IE Polyfill
28566
28567         if (epsilon === undefined) epsilon = Math.pow(2, -52);
28568         var EPSILON_SQ = epsilon * epsilon;
28569         /* FLP comparator */
28570
28571         var cmp = function cmp(a, b) {
28572           // check if they're both 0
28573           if (-epsilon < a && a < epsilon) {
28574             if (-epsilon < b && b < epsilon) {
28575               return 0;
28576             }
28577           } // check if they're flp equal
28578
28579
28580           var ab = a - b;
28581
28582           if (ab * ab < EPSILON_SQ * a * b) {
28583             return 0;
28584           } // normal comparison
28585
28586
28587           return a < b ? -1 : 1;
28588         };
28589         /**
28590          * This class rounds incoming values sufficiently so that
28591          * floating points problems are, for the most part, avoided.
28592          *
28593          * Incoming points are have their x & y values tested against
28594          * all previously seen x & y values. If either is 'too close'
28595          * to a previously seen value, it's value is 'snapped' to the
28596          * previously seen value.
28597          *
28598          * All points should be rounded by this class before being
28599          * stored in any data structures in the rest of this algorithm.
28600          */
28601
28602
28603         var PtRounder = /*#__PURE__*/function () {
28604           function PtRounder() {
28605             _classCallCheck(this, PtRounder);
28606
28607             this.reset();
28608           }
28609
28610           _createClass(PtRounder, [{
28611             key: "reset",
28612             value: function reset() {
28613               this.xRounder = new CoordRounder();
28614               this.yRounder = new CoordRounder();
28615             }
28616           }, {
28617             key: "round",
28618             value: function round(x, y) {
28619               return {
28620                 x: this.xRounder.round(x),
28621                 y: this.yRounder.round(y)
28622               };
28623             }
28624           }]);
28625
28626           return PtRounder;
28627         }();
28628
28629         var CoordRounder = /*#__PURE__*/function () {
28630           function CoordRounder() {
28631             _classCallCheck(this, CoordRounder);
28632
28633             this.tree = new Tree(); // preseed with 0 so we don't end up with values < Number.EPSILON
28634
28635             this.round(0);
28636           } // Note: this can rounds input values backwards or forwards.
28637           //       You might ask, why not restrict this to just rounding
28638           //       forwards? Wouldn't that allow left endpoints to always
28639           //       remain left endpoints during splitting (never change to
28640           //       right). No - it wouldn't, because we snap intersections
28641           //       to endpoints (to establish independence from the segment
28642           //       angle for t-intersections).
28643
28644
28645           _createClass(CoordRounder, [{
28646             key: "round",
28647             value: function round(coord) {
28648               var node = this.tree.add(coord);
28649               var prevNode = this.tree.prev(node);
28650
28651               if (prevNode !== null && cmp(node.key, prevNode.key) === 0) {
28652                 this.tree.remove(coord);
28653                 return prevNode.key;
28654               }
28655
28656               var nextNode = this.tree.next(node);
28657
28658               if (nextNode !== null && cmp(node.key, nextNode.key) === 0) {
28659                 this.tree.remove(coord);
28660                 return nextNode.key;
28661               }
28662
28663               return coord;
28664             }
28665           }]);
28666
28667           return CoordRounder;
28668         }(); // singleton available by import
28669
28670
28671         var rounder = new PtRounder();
28672         /* Cross Product of two vectors with first point at origin */
28673
28674         var crossProduct = function crossProduct(a, b) {
28675           return a.x * b.y - a.y * b.x;
28676         };
28677         /* Dot Product of two vectors with first point at origin */
28678
28679
28680         var dotProduct = function dotProduct(a, b) {
28681           return a.x * b.x + a.y * b.y;
28682         };
28683         /* Comparator for two vectors with same starting point */
28684
28685
28686         var compareVectorAngles = function compareVectorAngles(basePt, endPt1, endPt2) {
28687           var v1 = {
28688             x: endPt1.x - basePt.x,
28689             y: endPt1.y - basePt.y
28690           };
28691           var v2 = {
28692             x: endPt2.x - basePt.x,
28693             y: endPt2.y - basePt.y
28694           };
28695           var kross = crossProduct(v1, v2);
28696           return cmp(kross, 0);
28697         };
28698
28699         var length = function length(v) {
28700           return Math.sqrt(dotProduct(v, v));
28701         };
28702         /* Get the sine of the angle from pShared -> pAngle to pShaed -> pBase */
28703
28704
28705         var sineOfAngle = function sineOfAngle(pShared, pBase, pAngle) {
28706           var vBase = {
28707             x: pBase.x - pShared.x,
28708             y: pBase.y - pShared.y
28709           };
28710           var vAngle = {
28711             x: pAngle.x - pShared.x,
28712             y: pAngle.y - pShared.y
28713           };
28714           return crossProduct(vAngle, vBase) / length(vAngle) / length(vBase);
28715         };
28716         /* Get the cosine of the angle from pShared -> pAngle to pShaed -> pBase */
28717
28718
28719         var cosineOfAngle = function cosineOfAngle(pShared, pBase, pAngle) {
28720           var vBase = {
28721             x: pBase.x - pShared.x,
28722             y: pBase.y - pShared.y
28723           };
28724           var vAngle = {
28725             x: pAngle.x - pShared.x,
28726             y: pAngle.y - pShared.y
28727           };
28728           return dotProduct(vAngle, vBase) / length(vAngle) / length(vBase);
28729         };
28730         /* Get the x coordinate where the given line (defined by a point and vector)
28731          * crosses the horizontal line with the given y coordiante.
28732          * In the case of parrallel lines (including overlapping ones) returns null. */
28733
28734
28735         var horizontalIntersection = function horizontalIntersection(pt, v, y) {
28736           if (v.y === 0) return null;
28737           return {
28738             x: pt.x + v.x / v.y * (y - pt.y),
28739             y: y
28740           };
28741         };
28742         /* Get the y coordinate where the given line (defined by a point and vector)
28743          * crosses the vertical line with the given x coordiante.
28744          * In the case of parrallel lines (including overlapping ones) returns null. */
28745
28746
28747         var verticalIntersection = function verticalIntersection(pt, v, x) {
28748           if (v.x === 0) return null;
28749           return {
28750             x: x,
28751             y: pt.y + v.y / v.x * (x - pt.x)
28752           };
28753         };
28754         /* Get the intersection of two lines, each defined by a base point and a vector.
28755          * In the case of parrallel lines (including overlapping ones) returns null. */
28756
28757
28758         var intersection = function intersection(pt1, v1, pt2, v2) {
28759           // take some shortcuts for vertical and horizontal lines
28760           // this also ensures we don't calculate an intersection and then discover
28761           // it's actually outside the bounding box of the line
28762           if (v1.x === 0) return verticalIntersection(pt2, v2, pt1.x);
28763           if (v2.x === 0) return verticalIntersection(pt1, v1, pt2.x);
28764           if (v1.y === 0) return horizontalIntersection(pt2, v2, pt1.y);
28765           if (v2.y === 0) return horizontalIntersection(pt1, v1, pt2.y); // General case for non-overlapping segments.
28766           // This algorithm is based on Schneider and Eberly.
28767           // http://www.cimec.org.ar/~ncalvo/Schneider_Eberly.pdf - pg 244
28768
28769           var kross = crossProduct(v1, v2);
28770           if (kross == 0) return null;
28771           var ve = {
28772             x: pt2.x - pt1.x,
28773             y: pt2.y - pt1.y
28774           };
28775           var d1 = crossProduct(ve, v1) / kross;
28776           var d2 = crossProduct(ve, v2) / kross; // take the average of the two calculations to minimize rounding error
28777
28778           var x1 = pt1.x + d2 * v1.x,
28779               x2 = pt2.x + d1 * v2.x;
28780           var y1 = pt1.y + d2 * v1.y,
28781               y2 = pt2.y + d1 * v2.y;
28782           var x = (x1 + x2) / 2;
28783           var y = (y1 + y2) / 2;
28784           return {
28785             x: x,
28786             y: y
28787           };
28788         };
28789
28790         var SweepEvent = /*#__PURE__*/function () {
28791           _createClass(SweepEvent, null, [{
28792             key: "compare",
28793             // for ordering sweep events in the sweep event queue
28794             value: function compare(a, b) {
28795               // favor event with a point that the sweep line hits first
28796               var ptCmp = SweepEvent.comparePoints(a.point, b.point);
28797               if (ptCmp !== 0) return ptCmp; // the points are the same, so link them if needed
28798
28799               if (a.point !== b.point) a.link(b); // favor right events over left
28800
28801               if (a.isLeft !== b.isLeft) return a.isLeft ? 1 : -1; // we have two matching left or right endpoints
28802               // ordering of this case is the same as for their segments
28803
28804               return Segment.compare(a.segment, b.segment);
28805             } // for ordering points in sweep line order
28806
28807           }, {
28808             key: "comparePoints",
28809             value: function comparePoints(aPt, bPt) {
28810               if (aPt.x < bPt.x) return -1;
28811               if (aPt.x > bPt.x) return 1;
28812               if (aPt.y < bPt.y) return -1;
28813               if (aPt.y > bPt.y) return 1;
28814               return 0;
28815             } // Warning: 'point' input will be modified and re-used (for performance)
28816
28817           }]);
28818
28819           function SweepEvent(point, isLeft) {
28820             _classCallCheck(this, SweepEvent);
28821
28822             if (point.events === undefined) point.events = [this];else point.events.push(this);
28823             this.point = point;
28824             this.isLeft = isLeft; // this.segment, this.otherSE set by factory
28825           }
28826
28827           _createClass(SweepEvent, [{
28828             key: "link",
28829             value: function link(other) {
28830               if (other.point === this.point) {
28831                 throw new Error('Tried to link already linked events');
28832               }
28833
28834               var otherEvents = other.point.events;
28835
28836               for (var i = 0, iMax = otherEvents.length; i < iMax; i++) {
28837                 var evt = otherEvents[i];
28838                 this.point.events.push(evt);
28839                 evt.point = this.point;
28840               }
28841
28842               this.checkForConsuming();
28843             }
28844             /* Do a pass over our linked events and check to see if any pair
28845              * of segments match, and should be consumed. */
28846
28847           }, {
28848             key: "checkForConsuming",
28849             value: function checkForConsuming() {
28850               // FIXME: The loops in this method run O(n^2) => no good.
28851               //        Maintain little ordered sweep event trees?
28852               //        Can we maintaining an ordering that avoids the need
28853               //        for the re-sorting with getLeftmostComparator in geom-out?
28854               // Compare each pair of events to see if other events also match
28855               var numEvents = this.point.events.length;
28856
28857               for (var i = 0; i < numEvents; i++) {
28858                 var evt1 = this.point.events[i];
28859                 if (evt1.segment.consumedBy !== undefined) continue;
28860
28861                 for (var j = i + 1; j < numEvents; j++) {
28862                   var evt2 = this.point.events[j];
28863                   if (evt2.consumedBy !== undefined) continue;
28864                   if (evt1.otherSE.point.events !== evt2.otherSE.point.events) continue;
28865                   evt1.segment.consume(evt2.segment);
28866                 }
28867               }
28868             }
28869           }, {
28870             key: "getAvailableLinkedEvents",
28871             value: function getAvailableLinkedEvents() {
28872               // point.events is always of length 2 or greater
28873               var events = [];
28874
28875               for (var i = 0, iMax = this.point.events.length; i < iMax; i++) {
28876                 var evt = this.point.events[i];
28877
28878                 if (evt !== this && !evt.segment.ringOut && evt.segment.isInResult()) {
28879                   events.push(evt);
28880                 }
28881               }
28882
28883               return events;
28884             }
28885             /**
28886              * Returns a comparator function for sorting linked events that will
28887              * favor the event that will give us the smallest left-side angle.
28888              * All ring construction starts as low as possible heading to the right,
28889              * so by always turning left as sharp as possible we'll get polygons
28890              * without uncessary loops & holes.
28891              *
28892              * The comparator function has a compute cache such that it avoids
28893              * re-computing already-computed values.
28894              */
28895
28896           }, {
28897             key: "getLeftmostComparator",
28898             value: function getLeftmostComparator(baseEvent) {
28899               var _this = this;
28900
28901               var cache = new Map();
28902
28903               var fillCache = function fillCache(linkedEvent) {
28904                 var nextEvent = linkedEvent.otherSE;
28905                 cache.set(linkedEvent, {
28906                   sine: sineOfAngle(_this.point, baseEvent.point, nextEvent.point),
28907                   cosine: cosineOfAngle(_this.point, baseEvent.point, nextEvent.point)
28908                 });
28909               };
28910
28911               return function (a, b) {
28912                 if (!cache.has(a)) fillCache(a);
28913                 if (!cache.has(b)) fillCache(b);
28914
28915                 var _cache$get = cache.get(a),
28916                     asine = _cache$get.sine,
28917                     acosine = _cache$get.cosine;
28918
28919                 var _cache$get2 = cache.get(b),
28920                     bsine = _cache$get2.sine,
28921                     bcosine = _cache$get2.cosine; // both on or above x-axis
28922
28923
28924                 if (asine >= 0 && bsine >= 0) {
28925                   if (acosine < bcosine) return 1;
28926                   if (acosine > bcosine) return -1;
28927                   return 0;
28928                 } // both below x-axis
28929
28930
28931                 if (asine < 0 && bsine < 0) {
28932                   if (acosine < bcosine) return -1;
28933                   if (acosine > bcosine) return 1;
28934                   return 0;
28935                 } // one above x-axis, one below
28936
28937
28938                 if (bsine < asine) return -1;
28939                 if (bsine > asine) return 1;
28940                 return 0;
28941               };
28942             }
28943           }]);
28944
28945           return SweepEvent;
28946         }(); // segments and sweep events when all else is identical
28947
28948
28949         var segmentId = 0;
28950
28951         var Segment = /*#__PURE__*/function () {
28952           _createClass(Segment, null, [{
28953             key: "compare",
28954
28955             /* This compare() function is for ordering segments in the sweep
28956              * line tree, and does so according to the following criteria:
28957              *
28958              * Consider the vertical line that lies an infinestimal step to the
28959              * right of the right-more of the two left endpoints of the input
28960              * segments. Imagine slowly moving a point up from negative infinity
28961              * in the increasing y direction. Which of the two segments will that
28962              * point intersect first? That segment comes 'before' the other one.
28963              *
28964              * If neither segment would be intersected by such a line, (if one
28965              * or more of the segments are vertical) then the line to be considered
28966              * is directly on the right-more of the two left inputs.
28967              */
28968             value: function compare(a, b) {
28969               var alx = a.leftSE.point.x;
28970               var blx = b.leftSE.point.x;
28971               var arx = a.rightSE.point.x;
28972               var brx = b.rightSE.point.x; // check if they're even in the same vertical plane
28973
28974               if (brx < alx) return 1;
28975               if (arx < blx) return -1;
28976               var aly = a.leftSE.point.y;
28977               var bly = b.leftSE.point.y;
28978               var ary = a.rightSE.point.y;
28979               var bry = b.rightSE.point.y; // is left endpoint of segment B the right-more?
28980
28981               if (alx < blx) {
28982                 // are the two segments in the same horizontal plane?
28983                 if (bly < aly && bly < ary) return 1;
28984                 if (bly > aly && bly > ary) return -1; // is the B left endpoint colinear to segment A?
28985
28986                 var aCmpBLeft = a.comparePoint(b.leftSE.point);
28987                 if (aCmpBLeft < 0) return 1;
28988                 if (aCmpBLeft > 0) return -1; // is the A right endpoint colinear to segment B ?
28989
28990                 var bCmpARight = b.comparePoint(a.rightSE.point);
28991                 if (bCmpARight !== 0) return bCmpARight; // colinear segments, consider the one with left-more
28992                 // left endpoint to be first (arbitrary?)
28993
28994                 return -1;
28995               } // is left endpoint of segment A the right-more?
28996
28997
28998               if (alx > blx) {
28999                 if (aly < bly && aly < bry) return -1;
29000                 if (aly > bly && aly > bry) return 1; // is the A left endpoint colinear to segment B?
29001
29002                 var bCmpALeft = b.comparePoint(a.leftSE.point);
29003                 if (bCmpALeft !== 0) return bCmpALeft; // is the B right endpoint colinear to segment A?
29004
29005                 var aCmpBRight = a.comparePoint(b.rightSE.point);
29006                 if (aCmpBRight < 0) return 1;
29007                 if (aCmpBRight > 0) return -1; // colinear segments, consider the one with left-more
29008                 // left endpoint to be first (arbitrary?)
29009
29010                 return 1;
29011               } // if we get here, the two left endpoints are in the same
29012               // vertical plane, ie alx === blx
29013               // consider the lower left-endpoint to come first
29014
29015
29016               if (aly < bly) return -1;
29017               if (aly > bly) return 1; // left endpoints are identical
29018               // check for colinearity by using the left-more right endpoint
29019               // is the A right endpoint more left-more?
29020
29021               if (arx < brx) {
29022                 var _bCmpARight = b.comparePoint(a.rightSE.point);
29023
29024                 if (_bCmpARight !== 0) return _bCmpARight;
29025               } // is the B right endpoint more left-more?
29026
29027
29028               if (arx > brx) {
29029                 var _aCmpBRight = a.comparePoint(b.rightSE.point);
29030
29031                 if (_aCmpBRight < 0) return 1;
29032                 if (_aCmpBRight > 0) return -1;
29033               }
29034
29035               if (arx !== brx) {
29036                 // are these two [almost] vertical segments with opposite orientation?
29037                 // if so, the one with the lower right endpoint comes first
29038                 var ay = ary - aly;
29039                 var ax = arx - alx;
29040                 var by = bry - bly;
29041                 var bx = brx - blx;
29042                 if (ay > ax && by < bx) return 1;
29043                 if (ay < ax && by > bx) return -1;
29044               } // we have colinear segments with matching orientation
29045               // consider the one with more left-more right endpoint to be first
29046
29047
29048               if (arx > brx) return 1;
29049               if (arx < brx) return -1; // if we get here, two two right endpoints are in the same
29050               // vertical plane, ie arx === brx
29051               // consider the lower right-endpoint to come first
29052
29053               if (ary < bry) return -1;
29054               if (ary > bry) return 1; // right endpoints identical as well, so the segments are idential
29055               // fall back on creation order as consistent tie-breaker
29056
29057               if (a.id < b.id) return -1;
29058               if (a.id > b.id) return 1; // identical segment, ie a === b
29059
29060               return 0;
29061             }
29062             /* Warning: a reference to ringWindings input will be stored,
29063              *  and possibly will be later modified */
29064
29065           }]);
29066
29067           function Segment(leftSE, rightSE, rings, windings) {
29068             _classCallCheck(this, Segment);
29069
29070             this.id = ++segmentId;
29071             this.leftSE = leftSE;
29072             leftSE.segment = this;
29073             leftSE.otherSE = rightSE;
29074             this.rightSE = rightSE;
29075             rightSE.segment = this;
29076             rightSE.otherSE = leftSE;
29077             this.rings = rings;
29078             this.windings = windings; // left unset for performance, set later in algorithm
29079             // this.ringOut, this.consumedBy, this.prev
29080           }
29081
29082           _createClass(Segment, [{
29083             key: "replaceRightSE",
29084
29085             /* When a segment is split, the rightSE is replaced with a new sweep event */
29086             value: function replaceRightSE(newRightSE) {
29087               this.rightSE = newRightSE;
29088               this.rightSE.segment = this;
29089               this.rightSE.otherSE = this.leftSE;
29090               this.leftSE.otherSE = this.rightSE;
29091             }
29092           }, {
29093             key: "bbox",
29094             value: function bbox() {
29095               var y1 = this.leftSE.point.y;
29096               var y2 = this.rightSE.point.y;
29097               return {
29098                 ll: {
29099                   x: this.leftSE.point.x,
29100                   y: y1 < y2 ? y1 : y2
29101                 },
29102                 ur: {
29103                   x: this.rightSE.point.x,
29104                   y: y1 > y2 ? y1 : y2
29105                 }
29106               };
29107             }
29108             /* A vector from the left point to the right */
29109
29110           }, {
29111             key: "vector",
29112             value: function vector() {
29113               return {
29114                 x: this.rightSE.point.x - this.leftSE.point.x,
29115                 y: this.rightSE.point.y - this.leftSE.point.y
29116               };
29117             }
29118           }, {
29119             key: "isAnEndpoint",
29120             value: function isAnEndpoint(pt) {
29121               return pt.x === this.leftSE.point.x && pt.y === this.leftSE.point.y || pt.x === this.rightSE.point.x && pt.y === this.rightSE.point.y;
29122             }
29123             /* Compare this segment with a point.
29124              *
29125              * A point P is considered to be colinear to a segment if there
29126              * exists a distance D such that if we travel along the segment
29127              * from one * endpoint towards the other a distance D, we find
29128              * ourselves at point P.
29129              *
29130              * Return value indicates:
29131              *
29132              *   1: point lies above the segment (to the left of vertical)
29133              *   0: point is colinear to segment
29134              *  -1: point lies below the segment (to the right of vertical)
29135              */
29136
29137           }, {
29138             key: "comparePoint",
29139             value: function comparePoint(point) {
29140               if (this.isAnEndpoint(point)) return 0;
29141               var lPt = this.leftSE.point;
29142               var rPt = this.rightSE.point;
29143               var v = this.vector(); // Exactly vertical segments.
29144
29145               if (lPt.x === rPt.x) {
29146                 if (point.x === lPt.x) return 0;
29147                 return point.x < lPt.x ? 1 : -1;
29148               } // Nearly vertical segments with an intersection.
29149               // Check to see where a point on the line with matching Y coordinate is.
29150
29151
29152               var yDist = (point.y - lPt.y) / v.y;
29153               var xFromYDist = lPt.x + yDist * v.x;
29154               if (point.x === xFromYDist) return 0; // General case.
29155               // Check to see where a point on the line with matching X coordinate is.
29156
29157               var xDist = (point.x - lPt.x) / v.x;
29158               var yFromXDist = lPt.y + xDist * v.y;
29159               if (point.y === yFromXDist) return 0;
29160               return point.y < yFromXDist ? -1 : 1;
29161             }
29162             /**
29163              * Given another segment, returns the first non-trivial intersection
29164              * between the two segments (in terms of sweep line ordering), if it exists.
29165              *
29166              * A 'non-trivial' intersection is one that will cause one or both of the
29167              * segments to be split(). As such, 'trivial' vs. 'non-trivial' intersection:
29168              *
29169              *   * endpoint of segA with endpoint of segB --> trivial
29170              *   * endpoint of segA with point along segB --> non-trivial
29171              *   * endpoint of segB with point along segA --> non-trivial
29172              *   * point along segA with point along segB --> non-trivial
29173              *
29174              * If no non-trivial intersection exists, return null
29175              * Else, return null.
29176              */
29177
29178           }, {
29179             key: "getIntersection",
29180             value: function getIntersection(other) {
29181               // If bboxes don't overlap, there can't be any intersections
29182               var tBbox = this.bbox();
29183               var oBbox = other.bbox();
29184               var bboxOverlap = getBboxOverlap(tBbox, oBbox);
29185               if (bboxOverlap === null) return null; // We first check to see if the endpoints can be considered intersections.
29186               // This will 'snap' intersections to endpoints if possible, and will
29187               // handle cases of colinearity.
29188
29189               var tlp = this.leftSE.point;
29190               var trp = this.rightSE.point;
29191               var olp = other.leftSE.point;
29192               var orp = other.rightSE.point; // does each endpoint touch the other segment?
29193               // note that we restrict the 'touching' definition to only allow segments
29194               // to touch endpoints that lie forward from where we are in the sweep line pass
29195
29196               var touchesOtherLSE = isInBbox(tBbox, olp) && this.comparePoint(olp) === 0;
29197               var touchesThisLSE = isInBbox(oBbox, tlp) && other.comparePoint(tlp) === 0;
29198               var touchesOtherRSE = isInBbox(tBbox, orp) && this.comparePoint(orp) === 0;
29199               var touchesThisRSE = isInBbox(oBbox, trp) && other.comparePoint(trp) === 0; // do left endpoints match?
29200
29201               if (touchesThisLSE && touchesOtherLSE) {
29202                 // these two cases are for colinear segments with matching left
29203                 // endpoints, and one segment being longer than the other
29204                 if (touchesThisRSE && !touchesOtherRSE) return trp;
29205                 if (!touchesThisRSE && touchesOtherRSE) return orp; // either the two segments match exactly (two trival intersections)
29206                 // or just on their left endpoint (one trivial intersection
29207
29208                 return null;
29209               } // does this left endpoint matches (other doesn't)
29210
29211
29212               if (touchesThisLSE) {
29213                 // check for segments that just intersect on opposing endpoints
29214                 if (touchesOtherRSE) {
29215                   if (tlp.x === orp.x && tlp.y === orp.y) return null;
29216                 } // t-intersection on left endpoint
29217
29218
29219                 return tlp;
29220               } // does other left endpoint matches (this doesn't)
29221
29222
29223               if (touchesOtherLSE) {
29224                 // check for segments that just intersect on opposing endpoints
29225                 if (touchesThisRSE) {
29226                   if (trp.x === olp.x && trp.y === olp.y) return null;
29227                 } // t-intersection on left endpoint
29228
29229
29230                 return olp;
29231               } // trivial intersection on right endpoints
29232
29233
29234               if (touchesThisRSE && touchesOtherRSE) return null; // t-intersections on just one right endpoint
29235
29236               if (touchesThisRSE) return trp;
29237               if (touchesOtherRSE) return orp; // None of our endpoints intersect. Look for a general intersection between
29238               // infinite lines laid over the segments
29239
29240               var pt = intersection(tlp, this.vector(), olp, other.vector()); // are the segments parrallel? Note that if they were colinear with overlap,
29241               // they would have an endpoint intersection and that case was already handled above
29242
29243               if (pt === null) return null; // is the intersection found between the lines not on the segments?
29244
29245               if (!isInBbox(bboxOverlap, pt)) return null; // round the the computed point if needed
29246
29247               return rounder.round(pt.x, pt.y);
29248             }
29249             /**
29250              * Split the given segment into multiple segments on the given points.
29251              *  * Each existing segment will retain its leftSE and a new rightSE will be
29252              *    generated for it.
29253              *  * A new segment will be generated which will adopt the original segment's
29254              *    rightSE, and a new leftSE will be generated for it.
29255              *  * If there are more than two points given to split on, new segments
29256              *    in the middle will be generated with new leftSE and rightSE's.
29257              *  * An array of the newly generated SweepEvents will be returned.
29258              *
29259              * Warning: input array of points is modified
29260              */
29261
29262           }, {
29263             key: "split",
29264             value: function split(point) {
29265               var newEvents = [];
29266               var alreadyLinked = point.events !== undefined;
29267               var newLeftSE = new SweepEvent(point, true);
29268               var newRightSE = new SweepEvent(point, false);
29269               var oldRightSE = this.rightSE;
29270               this.replaceRightSE(newRightSE);
29271               newEvents.push(newRightSE);
29272               newEvents.push(newLeftSE);
29273               var newSeg = new Segment(newLeftSE, oldRightSE, this.rings.slice(), this.windings.slice()); // when splitting a nearly vertical downward-facing segment,
29274               // sometimes one of the resulting new segments is vertical, in which
29275               // case its left and right events may need to be swapped
29276
29277               if (SweepEvent.comparePoints(newSeg.leftSE.point, newSeg.rightSE.point) > 0) {
29278                 newSeg.swapEvents();
29279               }
29280
29281               if (SweepEvent.comparePoints(this.leftSE.point, this.rightSE.point) > 0) {
29282                 this.swapEvents();
29283               } // in the point we just used to create new sweep events with was already
29284               // linked to other events, we need to check if either of the affected
29285               // segments should be consumed
29286
29287
29288               if (alreadyLinked) {
29289                 newLeftSE.checkForConsuming();
29290                 newRightSE.checkForConsuming();
29291               }
29292
29293               return newEvents;
29294             }
29295             /* Swap which event is left and right */
29296
29297           }, {
29298             key: "swapEvents",
29299             value: function swapEvents() {
29300               var tmpEvt = this.rightSE;
29301               this.rightSE = this.leftSE;
29302               this.leftSE = tmpEvt;
29303               this.leftSE.isLeft = true;
29304               this.rightSE.isLeft = false;
29305
29306               for (var i = 0, iMax = this.windings.length; i < iMax; i++) {
29307                 this.windings[i] *= -1;
29308               }
29309             }
29310             /* Consume another segment. We take their rings under our wing
29311              * and mark them as consumed. Use for perfectly overlapping segments */
29312
29313           }, {
29314             key: "consume",
29315             value: function consume(other) {
29316               var consumer = this;
29317               var consumee = other;
29318
29319               while (consumer.consumedBy) {
29320                 consumer = consumer.consumedBy;
29321               }
29322
29323               while (consumee.consumedBy) {
29324                 consumee = consumee.consumedBy;
29325               }
29326
29327               var cmp = Segment.compare(consumer, consumee);
29328               if (cmp === 0) return; // already consumed
29329               // the winner of the consumption is the earlier segment
29330               // according to sweep line ordering
29331
29332               if (cmp > 0) {
29333                 var tmp = consumer;
29334                 consumer = consumee;
29335                 consumee = tmp;
29336               } // make sure a segment doesn't consume it's prev
29337
29338
29339               if (consumer.prev === consumee) {
29340                 var _tmp = consumer;
29341                 consumer = consumee;
29342                 consumee = _tmp;
29343               }
29344
29345               for (var i = 0, iMax = consumee.rings.length; i < iMax; i++) {
29346                 var ring = consumee.rings[i];
29347                 var winding = consumee.windings[i];
29348                 var index = consumer.rings.indexOf(ring);
29349
29350                 if (index === -1) {
29351                   consumer.rings.push(ring);
29352                   consumer.windings.push(winding);
29353                 } else consumer.windings[index] += winding;
29354               }
29355
29356               consumee.rings = null;
29357               consumee.windings = null;
29358               consumee.consumedBy = consumer; // mark sweep events consumed as to maintain ordering in sweep event queue
29359
29360               consumee.leftSE.consumedBy = consumer.leftSE;
29361               consumee.rightSE.consumedBy = consumer.rightSE;
29362             }
29363             /* The first segment previous segment chain that is in the result */
29364
29365           }, {
29366             key: "prevInResult",
29367             value: function prevInResult() {
29368               if (this._prevInResult !== undefined) return this._prevInResult;
29369               if (!this.prev) this._prevInResult = null;else if (this.prev.isInResult()) this._prevInResult = this.prev;else this._prevInResult = this.prev.prevInResult();
29370               return this._prevInResult;
29371             }
29372           }, {
29373             key: "beforeState",
29374             value: function beforeState() {
29375               if (this._beforeState !== undefined) return this._beforeState;
29376               if (!this.prev) this._beforeState = {
29377                 rings: [],
29378                 windings: [],
29379                 multiPolys: []
29380               };else {
29381                 var seg = this.prev.consumedBy || this.prev;
29382                 this._beforeState = seg.afterState();
29383               }
29384               return this._beforeState;
29385             }
29386           }, {
29387             key: "afterState",
29388             value: function afterState() {
29389               if (this._afterState !== undefined) return this._afterState;
29390               var beforeState = this.beforeState();
29391               this._afterState = {
29392                 rings: beforeState.rings.slice(0),
29393                 windings: beforeState.windings.slice(0),
29394                 multiPolys: []
29395               };
29396               var ringsAfter = this._afterState.rings;
29397               var windingsAfter = this._afterState.windings;
29398               var mpsAfter = this._afterState.multiPolys; // calculate ringsAfter, windingsAfter
29399
29400               for (var i = 0, iMax = this.rings.length; i < iMax; i++) {
29401                 var ring = this.rings[i];
29402                 var winding = this.windings[i];
29403                 var index = ringsAfter.indexOf(ring);
29404
29405                 if (index === -1) {
29406                   ringsAfter.push(ring);
29407                   windingsAfter.push(winding);
29408                 } else windingsAfter[index] += winding;
29409               } // calcualte polysAfter
29410
29411
29412               var polysAfter = [];
29413               var polysExclude = [];
29414
29415               for (var _i = 0, _iMax = ringsAfter.length; _i < _iMax; _i++) {
29416                 if (windingsAfter[_i] === 0) continue; // non-zero rule
29417
29418                 var _ring = ringsAfter[_i];
29419                 var poly = _ring.poly;
29420                 if (polysExclude.indexOf(poly) !== -1) continue;
29421                 if (_ring.isExterior) polysAfter.push(poly);else {
29422                   if (polysExclude.indexOf(poly) === -1) polysExclude.push(poly);
29423
29424                   var _index = polysAfter.indexOf(_ring.poly);
29425
29426                   if (_index !== -1) polysAfter.splice(_index, 1);
29427                 }
29428               } // calculate multiPolysAfter
29429
29430
29431               for (var _i2 = 0, _iMax2 = polysAfter.length; _i2 < _iMax2; _i2++) {
29432                 var mp = polysAfter[_i2].multiPoly;
29433                 if (mpsAfter.indexOf(mp) === -1) mpsAfter.push(mp);
29434               }
29435
29436               return this._afterState;
29437             }
29438             /* Is this segment part of the final result? */
29439
29440           }, {
29441             key: "isInResult",
29442             value: function isInResult() {
29443               // if we've been consumed, we're not in the result
29444               if (this.consumedBy) return false;
29445               if (this._isInResult !== undefined) return this._isInResult;
29446               var mpsBefore = this.beforeState().multiPolys;
29447               var mpsAfter = this.afterState().multiPolys;
29448
29449               switch (operation.type) {
29450                 case 'union':
29451                   {
29452                     // UNION - included iff:
29453                     //  * On one side of us there is 0 poly interiors AND
29454                     //  * On the other side there is 1 or more.
29455                     var noBefores = mpsBefore.length === 0;
29456                     var noAfters = mpsAfter.length === 0;
29457                     this._isInResult = noBefores !== noAfters;
29458                     break;
29459                   }
29460
29461                 case 'intersection':
29462                   {
29463                     // INTERSECTION - included iff:
29464                     //  * on one side of us all multipolys are rep. with poly interiors AND
29465                     //  * on the other side of us, not all multipolys are repsented
29466                     //    with poly interiors
29467                     var least;
29468                     var most;
29469
29470                     if (mpsBefore.length < mpsAfter.length) {
29471                       least = mpsBefore.length;
29472                       most = mpsAfter.length;
29473                     } else {
29474                       least = mpsAfter.length;
29475                       most = mpsBefore.length;
29476                     }
29477
29478                     this._isInResult = most === operation.numMultiPolys && least < most;
29479                     break;
29480                   }
29481
29482                 case 'xor':
29483                   {
29484                     // XOR - included iff:
29485                     //  * the difference between the number of multipolys represented
29486                     //    with poly interiors on our two sides is an odd number
29487                     var diff = Math.abs(mpsBefore.length - mpsAfter.length);
29488                     this._isInResult = diff % 2 === 1;
29489                     break;
29490                   }
29491
29492                 case 'difference':
29493                   {
29494                     // DIFFERENCE included iff:
29495                     //  * on exactly one side, we have just the subject
29496                     var isJustSubject = function isJustSubject(mps) {
29497                       return mps.length === 1 && mps[0].isSubject;
29498                     };
29499
29500                     this._isInResult = isJustSubject(mpsBefore) !== isJustSubject(mpsAfter);
29501                     break;
29502                   }
29503
29504                 default:
29505                   throw new Error("Unrecognized operation type found ".concat(operation.type));
29506               }
29507
29508               return this._isInResult;
29509             }
29510           }], [{
29511             key: "fromRing",
29512             value: function fromRing(pt1, pt2, ring) {
29513               var leftPt, rightPt, winding; // ordering the two points according to sweep line ordering
29514
29515               var cmpPts = SweepEvent.comparePoints(pt1, pt2);
29516
29517               if (cmpPts < 0) {
29518                 leftPt = pt1;
29519                 rightPt = pt2;
29520                 winding = 1;
29521               } else if (cmpPts > 0) {
29522                 leftPt = pt2;
29523                 rightPt = pt1;
29524                 winding = -1;
29525               } else throw new Error("Tried to create degenerate segment at [".concat(pt1.x, ", ").concat(pt1.y, "]"));
29526
29527               var leftSE = new SweepEvent(leftPt, true);
29528               var rightSE = new SweepEvent(rightPt, false);
29529               return new Segment(leftSE, rightSE, [ring], [winding]);
29530             }
29531           }]);
29532
29533           return Segment;
29534         }();
29535
29536         var RingIn = /*#__PURE__*/function () {
29537           function RingIn(geomRing, poly, isExterior) {
29538             _classCallCheck(this, RingIn);
29539
29540             if (!Array.isArray(geomRing) || geomRing.length === 0) {
29541               throw new Error('Input geometry is not a valid Polygon or MultiPolygon');
29542             }
29543
29544             this.poly = poly;
29545             this.isExterior = isExterior;
29546             this.segments = [];
29547
29548             if (typeof geomRing[0][0] !== 'number' || typeof geomRing[0][1] !== 'number') {
29549               throw new Error('Input geometry is not a valid Polygon or MultiPolygon');
29550             }
29551
29552             var firstPoint = rounder.round(geomRing[0][0], geomRing[0][1]);
29553             this.bbox = {
29554               ll: {
29555                 x: firstPoint.x,
29556                 y: firstPoint.y
29557               },
29558               ur: {
29559                 x: firstPoint.x,
29560                 y: firstPoint.y
29561               }
29562             };
29563             var prevPoint = firstPoint;
29564
29565             for (var i = 1, iMax = geomRing.length; i < iMax; i++) {
29566               if (typeof geomRing[i][0] !== 'number' || typeof geomRing[i][1] !== 'number') {
29567                 throw new Error('Input geometry is not a valid Polygon or MultiPolygon');
29568               }
29569
29570               var point = rounder.round(geomRing[i][0], geomRing[i][1]); // skip repeated points
29571
29572               if (point.x === prevPoint.x && point.y === prevPoint.y) continue;
29573               this.segments.push(Segment.fromRing(prevPoint, point, this));
29574               if (point.x < this.bbox.ll.x) this.bbox.ll.x = point.x;
29575               if (point.y < this.bbox.ll.y) this.bbox.ll.y = point.y;
29576               if (point.x > this.bbox.ur.x) this.bbox.ur.x = point.x;
29577               if (point.y > this.bbox.ur.y) this.bbox.ur.y = point.y;
29578               prevPoint = point;
29579             } // add segment from last to first if last is not the same as first
29580
29581
29582             if (firstPoint.x !== prevPoint.x || firstPoint.y !== prevPoint.y) {
29583               this.segments.push(Segment.fromRing(prevPoint, firstPoint, this));
29584             }
29585           }
29586
29587           _createClass(RingIn, [{
29588             key: "getSweepEvents",
29589             value: function getSweepEvents() {
29590               var sweepEvents = [];
29591
29592               for (var i = 0, iMax = this.segments.length; i < iMax; i++) {
29593                 var segment = this.segments[i];
29594                 sweepEvents.push(segment.leftSE);
29595                 sweepEvents.push(segment.rightSE);
29596               }
29597
29598               return sweepEvents;
29599             }
29600           }]);
29601
29602           return RingIn;
29603         }();
29604
29605         var PolyIn = /*#__PURE__*/function () {
29606           function PolyIn(geomPoly, multiPoly) {
29607             _classCallCheck(this, PolyIn);
29608
29609             if (!Array.isArray(geomPoly)) {
29610               throw new Error('Input geometry is not a valid Polygon or MultiPolygon');
29611             }
29612
29613             this.exteriorRing = new RingIn(geomPoly[0], this, true); // copy by value
29614
29615             this.bbox = {
29616               ll: {
29617                 x: this.exteriorRing.bbox.ll.x,
29618                 y: this.exteriorRing.bbox.ll.y
29619               },
29620               ur: {
29621                 x: this.exteriorRing.bbox.ur.x,
29622                 y: this.exteriorRing.bbox.ur.y
29623               }
29624             };
29625             this.interiorRings = [];
29626
29627             for (var i = 1, iMax = geomPoly.length; i < iMax; i++) {
29628               var ring = new RingIn(geomPoly[i], this, false);
29629               if (ring.bbox.ll.x < this.bbox.ll.x) this.bbox.ll.x = ring.bbox.ll.x;
29630               if (ring.bbox.ll.y < this.bbox.ll.y) this.bbox.ll.y = ring.bbox.ll.y;
29631               if (ring.bbox.ur.x > this.bbox.ur.x) this.bbox.ur.x = ring.bbox.ur.x;
29632               if (ring.bbox.ur.y > this.bbox.ur.y) this.bbox.ur.y = ring.bbox.ur.y;
29633               this.interiorRings.push(ring);
29634             }
29635
29636             this.multiPoly = multiPoly;
29637           }
29638
29639           _createClass(PolyIn, [{
29640             key: "getSweepEvents",
29641             value: function getSweepEvents() {
29642               var sweepEvents = this.exteriorRing.getSweepEvents();
29643
29644               for (var i = 0, iMax = this.interiorRings.length; i < iMax; i++) {
29645                 var ringSweepEvents = this.interiorRings[i].getSweepEvents();
29646
29647                 for (var j = 0, jMax = ringSweepEvents.length; j < jMax; j++) {
29648                   sweepEvents.push(ringSweepEvents[j]);
29649                 }
29650               }
29651
29652               return sweepEvents;
29653             }
29654           }]);
29655
29656           return PolyIn;
29657         }();
29658
29659         var MultiPolyIn = /*#__PURE__*/function () {
29660           function MultiPolyIn(geom, isSubject) {
29661             _classCallCheck(this, MultiPolyIn);
29662
29663             if (!Array.isArray(geom)) {
29664               throw new Error('Input geometry is not a valid Polygon or MultiPolygon');
29665             }
29666
29667             try {
29668               // if the input looks like a polygon, convert it to a multipolygon
29669               if (typeof geom[0][0][0] === 'number') geom = [geom];
29670             } catch (ex) {// The input is either malformed or has empty arrays.
29671               // In either case, it will be handled later on.
29672             }
29673
29674             this.polys = [];
29675             this.bbox = {
29676               ll: {
29677                 x: Number.POSITIVE_INFINITY,
29678                 y: Number.POSITIVE_INFINITY
29679               },
29680               ur: {
29681                 x: Number.NEGATIVE_INFINITY,
29682                 y: Number.NEGATIVE_INFINITY
29683               }
29684             };
29685
29686             for (var i = 0, iMax = geom.length; i < iMax; i++) {
29687               var poly = new PolyIn(geom[i], this);
29688               if (poly.bbox.ll.x < this.bbox.ll.x) this.bbox.ll.x = poly.bbox.ll.x;
29689               if (poly.bbox.ll.y < this.bbox.ll.y) this.bbox.ll.y = poly.bbox.ll.y;
29690               if (poly.bbox.ur.x > this.bbox.ur.x) this.bbox.ur.x = poly.bbox.ur.x;
29691               if (poly.bbox.ur.y > this.bbox.ur.y) this.bbox.ur.y = poly.bbox.ur.y;
29692               this.polys.push(poly);
29693             }
29694
29695             this.isSubject = isSubject;
29696           }
29697
29698           _createClass(MultiPolyIn, [{
29699             key: "getSweepEvents",
29700             value: function getSweepEvents() {
29701               var sweepEvents = [];
29702
29703               for (var i = 0, iMax = this.polys.length; i < iMax; i++) {
29704                 var polySweepEvents = this.polys[i].getSweepEvents();
29705
29706                 for (var j = 0, jMax = polySweepEvents.length; j < jMax; j++) {
29707                   sweepEvents.push(polySweepEvents[j]);
29708                 }
29709               }
29710
29711               return sweepEvents;
29712             }
29713           }]);
29714
29715           return MultiPolyIn;
29716         }();
29717
29718         var RingOut = /*#__PURE__*/function () {
29719           _createClass(RingOut, null, [{
29720             key: "factory",
29721
29722             /* Given the segments from the sweep line pass, compute & return a series
29723              * of closed rings from all the segments marked to be part of the result */
29724             value: function factory(allSegments) {
29725               var ringsOut = [];
29726
29727               for (var i = 0, iMax = allSegments.length; i < iMax; i++) {
29728                 var segment = allSegments[i];
29729                 if (!segment.isInResult() || segment.ringOut) continue;
29730                 var prevEvent = null;
29731                 var event = segment.leftSE;
29732                 var nextEvent = segment.rightSE;
29733                 var events = [event];
29734                 var startingPoint = event.point;
29735                 var intersectionLEs = [];
29736                 /* Walk the chain of linked events to form a closed ring */
29737
29738                 while (true) {
29739                   prevEvent = event;
29740                   event = nextEvent;
29741                   events.push(event);
29742                   /* Is the ring complete? */
29743
29744                   if (event.point === startingPoint) break;
29745
29746                   while (true) {
29747                     var availableLEs = event.getAvailableLinkedEvents();
29748                     /* Did we hit a dead end? This shouldn't happen. Indicates some earlier
29749                      * part of the algorithm malfunctioned... please file a bug report. */
29750
29751                     if (availableLEs.length === 0) {
29752                       var firstPt = events[0].point;
29753                       var lastPt = events[events.length - 1].point;
29754                       throw new Error("Unable to complete output ring starting at [".concat(firstPt.x, ",") + " ".concat(firstPt.y, "]. Last matching segment found ends at") + " [".concat(lastPt.x, ", ").concat(lastPt.y, "]."));
29755                     }
29756                     /* Only one way to go, so cotinue on the path */
29757
29758
29759                     if (availableLEs.length === 1) {
29760                       nextEvent = availableLEs[0].otherSE;
29761                       break;
29762                     }
29763                     /* We must have an intersection. Check for a completed loop */
29764
29765
29766                     var indexLE = null;
29767
29768                     for (var j = 0, jMax = intersectionLEs.length; j < jMax; j++) {
29769                       if (intersectionLEs[j].point === event.point) {
29770                         indexLE = j;
29771                         break;
29772                       }
29773                     }
29774                     /* Found a completed loop. Cut that off and make a ring */
29775
29776
29777                     if (indexLE !== null) {
29778                       var intersectionLE = intersectionLEs.splice(indexLE)[0];
29779                       var ringEvents = events.splice(intersectionLE.index);
29780                       ringEvents.unshift(ringEvents[0].otherSE);
29781                       ringsOut.push(new RingOut(ringEvents.reverse()));
29782                       continue;
29783                     }
29784                     /* register the intersection */
29785
29786
29787                     intersectionLEs.push({
29788                       index: events.length,
29789                       point: event.point
29790                     });
29791                     /* Choose the left-most option to continue the walk */
29792
29793                     var comparator = event.getLeftmostComparator(prevEvent);
29794                     nextEvent = availableLEs.sort(comparator)[0].otherSE;
29795                     break;
29796                   }
29797                 }
29798
29799                 ringsOut.push(new RingOut(events));
29800               }
29801
29802               return ringsOut;
29803             }
29804           }]);
29805
29806           function RingOut(events) {
29807             _classCallCheck(this, RingOut);
29808
29809             this.events = events;
29810
29811             for (var i = 0, iMax = events.length; i < iMax; i++) {
29812               events[i].segment.ringOut = this;
29813             }
29814
29815             this.poly = null;
29816           }
29817
29818           _createClass(RingOut, [{
29819             key: "getGeom",
29820             value: function getGeom() {
29821               // Remove superfluous points (ie extra points along a straight line),
29822               var prevPt = this.events[0].point;
29823               var points = [prevPt];
29824
29825               for (var i = 1, iMax = this.events.length - 1; i < iMax; i++) {
29826                 var _pt = this.events[i].point;
29827                 var _nextPt = this.events[i + 1].point;
29828                 if (compareVectorAngles(_pt, prevPt, _nextPt) === 0) continue;
29829                 points.push(_pt);
29830                 prevPt = _pt;
29831               } // ring was all (within rounding error of angle calc) colinear points
29832
29833
29834               if (points.length === 1) return null; // check if the starting point is necessary
29835
29836               var pt = points[0];
29837               var nextPt = points[1];
29838               if (compareVectorAngles(pt, prevPt, nextPt) === 0) points.shift();
29839               points.push(points[0]);
29840               var step = this.isExteriorRing() ? 1 : -1;
29841               var iStart = this.isExteriorRing() ? 0 : points.length - 1;
29842               var iEnd = this.isExteriorRing() ? points.length : -1;
29843               var orderedPoints = [];
29844
29845               for (var _i = iStart; _i != iEnd; _i += step) {
29846                 orderedPoints.push([points[_i].x, points[_i].y]);
29847               }
29848
29849               return orderedPoints;
29850             }
29851           }, {
29852             key: "isExteriorRing",
29853             value: function isExteriorRing() {
29854               if (this._isExteriorRing === undefined) {
29855                 var enclosing = this.enclosingRing();
29856                 this._isExteriorRing = enclosing ? !enclosing.isExteriorRing() : true;
29857               }
29858
29859               return this._isExteriorRing;
29860             }
29861           }, {
29862             key: "enclosingRing",
29863             value: function enclosingRing() {
29864               if (this._enclosingRing === undefined) {
29865                 this._enclosingRing = this._calcEnclosingRing();
29866               }
29867
29868               return this._enclosingRing;
29869             }
29870             /* Returns the ring that encloses this one, if any */
29871
29872           }, {
29873             key: "_calcEnclosingRing",
29874             value: function _calcEnclosingRing() {
29875               // start with the ealier sweep line event so that the prevSeg
29876               // chain doesn't lead us inside of a loop of ours
29877               var leftMostEvt = this.events[0];
29878
29879               for (var i = 1, iMax = this.events.length; i < iMax; i++) {
29880                 var evt = this.events[i];
29881                 if (SweepEvent.compare(leftMostEvt, evt) > 0) leftMostEvt = evt;
29882               }
29883
29884               var prevSeg = leftMostEvt.segment.prevInResult();
29885               var prevPrevSeg = prevSeg ? prevSeg.prevInResult() : null;
29886
29887               while (true) {
29888                 // no segment found, thus no ring can enclose us
29889                 if (!prevSeg) return null; // no segments below prev segment found, thus the ring of the prev
29890                 // segment must loop back around and enclose us
29891
29892                 if (!prevPrevSeg) return prevSeg.ringOut; // if the two segments are of different rings, the ring of the prev
29893                 // segment must either loop around us or the ring of the prev prev
29894                 // seg, which would make us and the ring of the prev peers
29895
29896                 if (prevPrevSeg.ringOut !== prevSeg.ringOut) {
29897                   if (prevPrevSeg.ringOut.enclosingRing() !== prevSeg.ringOut) {
29898                     return prevSeg.ringOut;
29899                   } else return prevSeg.ringOut.enclosingRing();
29900                 } // two segments are from the same ring, so this was a penisula
29901                 // of that ring. iterate downward, keep searching
29902
29903
29904                 prevSeg = prevPrevSeg.prevInResult();
29905                 prevPrevSeg = prevSeg ? prevSeg.prevInResult() : null;
29906               }
29907             }
29908           }]);
29909
29910           return RingOut;
29911         }();
29912
29913         var PolyOut = /*#__PURE__*/function () {
29914           function PolyOut(exteriorRing) {
29915             _classCallCheck(this, PolyOut);
29916
29917             this.exteriorRing = exteriorRing;
29918             exteriorRing.poly = this;
29919             this.interiorRings = [];
29920           }
29921
29922           _createClass(PolyOut, [{
29923             key: "addInterior",
29924             value: function addInterior(ring) {
29925               this.interiorRings.push(ring);
29926               ring.poly = this;
29927             }
29928           }, {
29929             key: "getGeom",
29930             value: function getGeom() {
29931               var geom = [this.exteriorRing.getGeom()]; // exterior ring was all (within rounding error of angle calc) colinear points
29932
29933               if (geom[0] === null) return null;
29934
29935               for (var i = 0, iMax = this.interiorRings.length; i < iMax; i++) {
29936                 var ringGeom = this.interiorRings[i].getGeom(); // interior ring was all (within rounding error of angle calc) colinear points
29937
29938                 if (ringGeom === null) continue;
29939                 geom.push(ringGeom);
29940               }
29941
29942               return geom;
29943             }
29944           }]);
29945
29946           return PolyOut;
29947         }();
29948
29949         var MultiPolyOut = /*#__PURE__*/function () {
29950           function MultiPolyOut(rings) {
29951             _classCallCheck(this, MultiPolyOut);
29952
29953             this.rings = rings;
29954             this.polys = this._composePolys(rings);
29955           }
29956
29957           _createClass(MultiPolyOut, [{
29958             key: "getGeom",
29959             value: function getGeom() {
29960               var geom = [];
29961
29962               for (var i = 0, iMax = this.polys.length; i < iMax; i++) {
29963                 var polyGeom = this.polys[i].getGeom(); // exterior ring was all (within rounding error of angle calc) colinear points
29964
29965                 if (polyGeom === null) continue;
29966                 geom.push(polyGeom);
29967               }
29968
29969               return geom;
29970             }
29971           }, {
29972             key: "_composePolys",
29973             value: function _composePolys(rings) {
29974               var polys = [];
29975
29976               for (var i = 0, iMax = rings.length; i < iMax; i++) {
29977                 var ring = rings[i];
29978                 if (ring.poly) continue;
29979                 if (ring.isExteriorRing()) polys.push(new PolyOut(ring));else {
29980                   var enclosingRing = ring.enclosingRing();
29981                   if (!enclosingRing.poly) polys.push(new PolyOut(enclosingRing));
29982                   enclosingRing.poly.addInterior(ring);
29983                 }
29984               }
29985
29986               return polys;
29987             }
29988           }]);
29989
29990           return MultiPolyOut;
29991         }();
29992         /**
29993          * NOTE:  We must be careful not to change any segments while
29994          *        they are in the SplayTree. AFAIK, there's no way to tell
29995          *        the tree to rebalance itself - thus before splitting
29996          *        a segment that's in the tree, we remove it from the tree,
29997          *        do the split, then re-insert it. (Even though splitting a
29998          *        segment *shouldn't* change its correct position in the
29999          *        sweep line tree, the reality is because of rounding errors,
30000          *        it sometimes does.)
30001          */
30002
30003
30004         var SweepLine = /*#__PURE__*/function () {
30005           function SweepLine(queue) {
30006             var comparator = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : Segment.compare;
30007
30008             _classCallCheck(this, SweepLine);
30009
30010             this.queue = queue;
30011             this.tree = new Tree(comparator);
30012             this.segments = [];
30013           }
30014
30015           _createClass(SweepLine, [{
30016             key: "process",
30017             value: function process(event) {
30018               var segment = event.segment;
30019               var newEvents = []; // if we've already been consumed by another segment,
30020               // clean up our body parts and get out
30021
30022               if (event.consumedBy) {
30023                 if (event.isLeft) this.queue.remove(event.otherSE);else this.tree.remove(segment);
30024                 return newEvents;
30025               }
30026
30027               var node = event.isLeft ? this.tree.insert(segment) : this.tree.find(segment);
30028               if (!node) throw new Error("Unable to find segment #".concat(segment.id, " ") + "[".concat(segment.leftSE.point.x, ", ").concat(segment.leftSE.point.y, "] -> ") + "[".concat(segment.rightSE.point.x, ", ").concat(segment.rightSE.point.y, "] ") + 'in SweepLine tree. Please submit a bug report.');
30029               var prevNode = node;
30030               var nextNode = node;
30031               var prevSeg = undefined;
30032               var nextSeg = undefined; // skip consumed segments still in tree
30033
30034               while (prevSeg === undefined) {
30035                 prevNode = this.tree.prev(prevNode);
30036                 if (prevNode === null) prevSeg = null;else if (prevNode.key.consumedBy === undefined) prevSeg = prevNode.key;
30037               } // skip consumed segments still in tree
30038
30039
30040               while (nextSeg === undefined) {
30041                 nextNode = this.tree.next(nextNode);
30042                 if (nextNode === null) nextSeg = null;else if (nextNode.key.consumedBy === undefined) nextSeg = nextNode.key;
30043               }
30044
30045               if (event.isLeft) {
30046                 // Check for intersections against the previous segment in the sweep line
30047                 var prevMySplitter = null;
30048
30049                 if (prevSeg) {
30050                   var prevInter = prevSeg.getIntersection(segment);
30051
30052                   if (prevInter !== null) {
30053                     if (!segment.isAnEndpoint(prevInter)) prevMySplitter = prevInter;
30054
30055                     if (!prevSeg.isAnEndpoint(prevInter)) {
30056                       var newEventsFromSplit = this._splitSafely(prevSeg, prevInter);
30057
30058                       for (var i = 0, iMax = newEventsFromSplit.length; i < iMax; i++) {
30059                         newEvents.push(newEventsFromSplit[i]);
30060                       }
30061                     }
30062                   }
30063                 } // Check for intersections against the next segment in the sweep line
30064
30065
30066                 var nextMySplitter = null;
30067
30068                 if (nextSeg) {
30069                   var nextInter = nextSeg.getIntersection(segment);
30070
30071                   if (nextInter !== null) {
30072                     if (!segment.isAnEndpoint(nextInter)) nextMySplitter = nextInter;
30073
30074                     if (!nextSeg.isAnEndpoint(nextInter)) {
30075                       var _newEventsFromSplit = this._splitSafely(nextSeg, nextInter);
30076
30077                       for (var _i = 0, _iMax = _newEventsFromSplit.length; _i < _iMax; _i++) {
30078                         newEvents.push(_newEventsFromSplit[_i]);
30079                       }
30080                     }
30081                   }
30082                 } // For simplicity, even if we find more than one intersection we only
30083                 // spilt on the 'earliest' (sweep-line style) of the intersections.
30084                 // The other intersection will be handled in a future process().
30085
30086
30087                 if (prevMySplitter !== null || nextMySplitter !== null) {
30088                   var mySplitter = null;
30089                   if (prevMySplitter === null) mySplitter = nextMySplitter;else if (nextMySplitter === null) mySplitter = prevMySplitter;else {
30090                     var cmpSplitters = SweepEvent.comparePoints(prevMySplitter, nextMySplitter);
30091                     mySplitter = cmpSplitters <= 0 ? prevMySplitter : nextMySplitter;
30092                   } // Rounding errors can cause changes in ordering,
30093                   // so remove afected segments and right sweep events before splitting
30094
30095                   this.queue.remove(segment.rightSE);
30096                   newEvents.push(segment.rightSE);
30097
30098                   var _newEventsFromSplit2 = segment.split(mySplitter);
30099
30100                   for (var _i2 = 0, _iMax2 = _newEventsFromSplit2.length; _i2 < _iMax2; _i2++) {
30101                     newEvents.push(_newEventsFromSplit2[_i2]);
30102                   }
30103                 }
30104
30105                 if (newEvents.length > 0) {
30106                   // We found some intersections, so re-do the current event to
30107                   // make sure sweep line ordering is totally consistent for later
30108                   // use with the segment 'prev' pointers
30109                   this.tree.remove(segment);
30110                   newEvents.push(event);
30111                 } else {
30112                   // done with left event
30113                   this.segments.push(segment);
30114                   segment.prev = prevSeg;
30115                 }
30116               } else {
30117                 // event.isRight
30118                 // since we're about to be removed from the sweep line, check for
30119                 // intersections between our previous and next segments
30120                 if (prevSeg && nextSeg) {
30121                   var inter = prevSeg.getIntersection(nextSeg);
30122
30123                   if (inter !== null) {
30124                     if (!prevSeg.isAnEndpoint(inter)) {
30125                       var _newEventsFromSplit3 = this._splitSafely(prevSeg, inter);
30126
30127                       for (var _i3 = 0, _iMax3 = _newEventsFromSplit3.length; _i3 < _iMax3; _i3++) {
30128                         newEvents.push(_newEventsFromSplit3[_i3]);
30129                       }
30130                     }
30131
30132                     if (!nextSeg.isAnEndpoint(inter)) {
30133                       var _newEventsFromSplit4 = this._splitSafely(nextSeg, inter);
30134
30135                       for (var _i4 = 0, _iMax4 = _newEventsFromSplit4.length; _i4 < _iMax4; _i4++) {
30136                         newEvents.push(_newEventsFromSplit4[_i4]);
30137                       }
30138                     }
30139                   }
30140                 }
30141
30142                 this.tree.remove(segment);
30143               }
30144
30145               return newEvents;
30146             }
30147             /* Safely split a segment that is currently in the datastructures
30148              * IE - a segment other than the one that is currently being processed. */
30149
30150           }, {
30151             key: "_splitSafely",
30152             value: function _splitSafely(seg, pt) {
30153               // Rounding errors can cause changes in ordering,
30154               // so remove afected segments and right sweep events before splitting
30155               // removeNode() doesn't work, so have re-find the seg
30156               // https://github.com/w8r/splay-tree/pull/5
30157               this.tree.remove(seg);
30158               var rightSE = seg.rightSE;
30159               this.queue.remove(rightSE);
30160               var newEvents = seg.split(pt);
30161               newEvents.push(rightSE); // splitting can trigger consumption
30162
30163               if (seg.consumedBy === undefined) this.tree.insert(seg);
30164               return newEvents;
30165             }
30166           }]);
30167
30168           return SweepLine;
30169         }();
30170
30171         var POLYGON_CLIPPING_MAX_QUEUE_SIZE = typeof process !== 'undefined' && process.env.POLYGON_CLIPPING_MAX_QUEUE_SIZE || 1000000;
30172         var POLYGON_CLIPPING_MAX_SWEEPLINE_SEGMENTS = typeof process !== 'undefined' && process.env.POLYGON_CLIPPING_MAX_SWEEPLINE_SEGMENTS || 1000000;
30173
30174         var Operation = /*#__PURE__*/function () {
30175           function Operation() {
30176             _classCallCheck(this, Operation);
30177           }
30178
30179           _createClass(Operation, [{
30180             key: "run",
30181             value: function run(type, geom, moreGeoms) {
30182               operation.type = type;
30183               rounder.reset();
30184               /* Convert inputs to MultiPoly objects */
30185
30186               var multipolys = [new MultiPolyIn(geom, true)];
30187
30188               for (var i = 0, iMax = moreGeoms.length; i < iMax; i++) {
30189                 multipolys.push(new MultiPolyIn(moreGeoms[i], false));
30190               }
30191
30192               operation.numMultiPolys = multipolys.length;
30193               /* BBox optimization for difference operation
30194                * If the bbox of a multipolygon that's part of the clipping doesn't
30195                * intersect the bbox of the subject at all, we can just drop that
30196                * multiploygon. */
30197
30198               if (operation.type === 'difference') {
30199                 // in place removal
30200                 var subject = multipolys[0];
30201                 var _i = 1;
30202
30203                 while (_i < multipolys.length) {
30204                   if (getBboxOverlap(multipolys[_i].bbox, subject.bbox) !== null) _i++;else multipolys.splice(_i, 1);
30205                 }
30206               }
30207               /* BBox optimization for intersection operation
30208                * If we can find any pair of multipolygons whose bbox does not overlap,
30209                * then the result will be empty. */
30210
30211
30212               if (operation.type === 'intersection') {
30213                 // TODO: this is O(n^2) in number of polygons. By sorting the bboxes,
30214                 //       it could be optimized to O(n * ln(n))
30215                 for (var _i2 = 0, _iMax = multipolys.length; _i2 < _iMax; _i2++) {
30216                   var mpA = multipolys[_i2];
30217
30218                   for (var j = _i2 + 1, jMax = multipolys.length; j < jMax; j++) {
30219                     if (getBboxOverlap(mpA.bbox, multipolys[j].bbox) === null) return [];
30220                   }
30221                 }
30222               }
30223               /* Put segment endpoints in a priority queue */
30224
30225
30226               var queue = new Tree(SweepEvent.compare);
30227
30228               for (var _i3 = 0, _iMax2 = multipolys.length; _i3 < _iMax2; _i3++) {
30229                 var sweepEvents = multipolys[_i3].getSweepEvents();
30230
30231                 for (var _j = 0, _jMax = sweepEvents.length; _j < _jMax; _j++) {
30232                   queue.insert(sweepEvents[_j]);
30233
30234                   if (queue.size > POLYGON_CLIPPING_MAX_QUEUE_SIZE) {
30235                     // prevents an infinite loop, an otherwise common manifestation of bugs
30236                     throw new Error('Infinite loop when putting segment endpoints in a priority queue ' + '(queue size too big). Please file a bug report.');
30237                   }
30238                 }
30239               }
30240               /* Pass the sweep line over those endpoints */
30241
30242
30243               var sweepLine = new SweepLine(queue);
30244               var prevQueueSize = queue.size;
30245               var node = queue.pop();
30246
30247               while (node) {
30248                 var evt = node.key;
30249
30250                 if (queue.size === prevQueueSize) {
30251                   // prevents an infinite loop, an otherwise common manifestation of bugs
30252                   var seg = evt.segment;
30253                   throw new Error("Unable to pop() ".concat(evt.isLeft ? 'left' : 'right', " SweepEvent ") + "[".concat(evt.point.x, ", ").concat(evt.point.y, "] from segment #").concat(seg.id, " ") + "[".concat(seg.leftSE.point.x, ", ").concat(seg.leftSE.point.y, "] -> ") + "[".concat(seg.rightSE.point.x, ", ").concat(seg.rightSE.point.y, "] from queue. ") + 'Please file a bug report.');
30254                 }
30255
30256                 if (queue.size > POLYGON_CLIPPING_MAX_QUEUE_SIZE) {
30257                   // prevents an infinite loop, an otherwise common manifestation of bugs
30258                   throw new Error('Infinite loop when passing sweep line over endpoints ' + '(queue size too big). Please file a bug report.');
30259                 }
30260
30261                 if (sweepLine.segments.length > POLYGON_CLIPPING_MAX_SWEEPLINE_SEGMENTS) {
30262                   // prevents an infinite loop, an otherwise common manifestation of bugs
30263                   throw new Error('Infinite loop when passing sweep line over endpoints ' + '(too many sweep line segments). Please file a bug report.');
30264                 }
30265
30266                 var newEvents = sweepLine.process(evt);
30267
30268                 for (var _i4 = 0, _iMax3 = newEvents.length; _i4 < _iMax3; _i4++) {
30269                   var _evt = newEvents[_i4];
30270                   if (_evt.consumedBy === undefined) queue.insert(_evt);
30271                 }
30272
30273                 prevQueueSize = queue.size;
30274                 node = queue.pop();
30275               } // free some memory we don't need anymore
30276
30277
30278               rounder.reset();
30279               /* Collect and compile segments we're keeping into a multipolygon */
30280
30281               var ringsOut = RingOut.factory(sweepLine.segments);
30282               var result = new MultiPolyOut(ringsOut);
30283               return result.getGeom();
30284             }
30285           }]);
30286
30287           return Operation;
30288         }(); // singleton available by import
30289
30290
30291         var operation = new Operation();
30292
30293         var union = function union(geom) {
30294           for (var _len = arguments.length, moreGeoms = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
30295             moreGeoms[_key - 1] = arguments[_key];
30296           }
30297
30298           return operation.run('union', geom, moreGeoms);
30299         };
30300
30301         var intersection$1 = function intersection(geom) {
30302           for (var _len2 = arguments.length, moreGeoms = new Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) {
30303             moreGeoms[_key2 - 1] = arguments[_key2];
30304           }
30305
30306           return operation.run('intersection', geom, moreGeoms);
30307         };
30308
30309         var xor = function xor(geom) {
30310           for (var _len3 = arguments.length, moreGeoms = new Array(_len3 > 1 ? _len3 - 1 : 0), _key3 = 1; _key3 < _len3; _key3++) {
30311             moreGeoms[_key3 - 1] = arguments[_key3];
30312           }
30313
30314           return operation.run('xor', geom, moreGeoms);
30315         };
30316
30317         var difference = function difference(subjectGeom) {
30318           for (var _len4 = arguments.length, clippingGeoms = new Array(_len4 > 1 ? _len4 - 1 : 0), _key4 = 1; _key4 < _len4; _key4++) {
30319             clippingGeoms[_key4 - 1] = arguments[_key4];
30320           }
30321
30322           return operation.run('difference', subjectGeom, clippingGeoms);
30323         };
30324
30325         var index = {
30326           union: union,
30327           intersection: intersection$1,
30328           xor: xor,
30329           difference: difference
30330         };
30331
30332         var geojsonPrecision = createCommonjsModule(function (module) {
30333           (function () {
30334             function parse(t, coordinatePrecision, extrasPrecision) {
30335               function point(p) {
30336                 return p.map(function (e, index) {
30337                   if (index < 2) {
30338                     return 1 * e.toFixed(coordinatePrecision);
30339                   } else {
30340                     return 1 * e.toFixed(extrasPrecision);
30341                   }
30342                 });
30343               }
30344
30345               function multi(l) {
30346                 return l.map(point);
30347               }
30348
30349               function poly(p) {
30350                 return p.map(multi);
30351               }
30352
30353               function multiPoly(m) {
30354                 return m.map(poly);
30355               }
30356
30357               function geometry(obj) {
30358                 if (!obj) {
30359                   return {};
30360                 }
30361
30362                 switch (obj.type) {
30363                   case "Point":
30364                     obj.coordinates = point(obj.coordinates);
30365                     return obj;
30366
30367                   case "LineString":
30368                   case "MultiPoint":
30369                     obj.coordinates = multi(obj.coordinates);
30370                     return obj;
30371
30372                   case "Polygon":
30373                   case "MultiLineString":
30374                     obj.coordinates = poly(obj.coordinates);
30375                     return obj;
30376
30377                   case "MultiPolygon":
30378                     obj.coordinates = multiPoly(obj.coordinates);
30379                     return obj;
30380
30381                   case "GeometryCollection":
30382                     obj.geometries = obj.geometries.map(geometry);
30383                     return obj;
30384
30385                   default:
30386                     return {};
30387                 }
30388               }
30389
30390               function feature(obj) {
30391                 obj.geometry = geometry(obj.geometry);
30392                 return obj;
30393               }
30394
30395               function featureCollection(f) {
30396                 f.features = f.features.map(feature);
30397                 return f;
30398               }
30399
30400               function geometryCollection(g) {
30401                 g.geometries = g.geometries.map(geometry);
30402                 return g;
30403               }
30404
30405               if (!t) {
30406                 return t;
30407               }
30408
30409               switch (t.type) {
30410                 case "Feature":
30411                   return feature(t);
30412
30413                 case "GeometryCollection":
30414                   return geometryCollection(t);
30415
30416                 case "FeatureCollection":
30417                   return featureCollection(t);
30418
30419                 case "Point":
30420                 case "LineString":
30421                 case "Polygon":
30422                 case "MultiPoint":
30423                 case "MultiPolygon":
30424                 case "MultiLineString":
30425                   return geometry(t);
30426
30427                 default:
30428                   return t;
30429               }
30430             }
30431
30432             module.exports = parse;
30433             module.exports.parse = parse;
30434           })();
30435         });
30436
30437         var FORCED$3 = fails(function () {
30438           return new Date(NaN).toJSON() !== null
30439             || Date.prototype.toJSON.call({ toISOString: function () { return 1; } }) !== 1;
30440         });
30441
30442         // `Date.prototype.toJSON` method
30443         // https://tc39.es/ecma262/#sec-date.prototype.tojson
30444         _export({ target: 'Date', proto: true, forced: FORCED$3 }, {
30445           // eslint-disable-next-line no-unused-vars -- required for `.length`
30446           toJSON: function toJSON(key) {
30447             var O = toObject(this);
30448             var pv = toPrimitive(O);
30449             return typeof pv == 'number' && !isFinite(pv) ? null : O.toISOString();
30450           }
30451         });
30452
30453         // `URL.prototype.toJSON` method
30454         // https://url.spec.whatwg.org/#dom-url-tojson
30455         _export({ target: 'URL', proto: true, enumerable: true }, {
30456           toJSON: function toJSON() {
30457             return URL.prototype.toString.call(this);
30458           }
30459         });
30460
30461         function isObject$3(obj) {
30462           return _typeof(obj) === 'object' && obj !== null;
30463         }
30464
30465         function forEach(obj, cb) {
30466           if (Array.isArray(obj)) {
30467             obj.forEach(cb);
30468           } else if (isObject$3(obj)) {
30469             Object.keys(obj).forEach(function (key) {
30470               var val = obj[key];
30471               cb(val, key);
30472             });
30473           }
30474         }
30475
30476         function getTreeDepth(obj) {
30477           var depth = 0;
30478
30479           if (Array.isArray(obj) || isObject$3(obj)) {
30480             forEach(obj, function (val) {
30481               if (Array.isArray(val) || isObject$3(val)) {
30482                 var tmpDepth = getTreeDepth(val);
30483
30484                 if (tmpDepth > depth) {
30485                   depth = tmpDepth;
30486                 }
30487               }
30488             });
30489             return depth + 1;
30490           }
30491
30492           return depth;
30493         }
30494
30495         function stringify(obj, options) {
30496           options = options || {};
30497           var indent = JSON.stringify([1], null, get(options, 'indent', 2)).slice(2, -3);
30498           var addMargin = get(options, 'margins', false);
30499           var addArrayMargin = get(options, 'arrayMargins', false);
30500           var addObjectMargin = get(options, 'objectMargins', false);
30501           var maxLength = indent === '' ? Infinity : get(options, 'maxLength', 80);
30502           var maxNesting = get(options, 'maxNesting', Infinity);
30503           return function _stringify(obj, currentIndent, reserved) {
30504             if (obj && typeof obj.toJSON === 'function') {
30505               obj = obj.toJSON();
30506             }
30507
30508             var string = JSON.stringify(obj);
30509
30510             if (string === undefined) {
30511               return string;
30512             }
30513
30514             var length = maxLength - currentIndent.length - reserved;
30515             var treeDepth = getTreeDepth(obj);
30516
30517             if (treeDepth <= maxNesting && string.length <= length) {
30518               var prettified = prettify(string, {
30519                 addMargin: addMargin,
30520                 addArrayMargin: addArrayMargin,
30521                 addObjectMargin: addObjectMargin
30522               });
30523
30524               if (prettified.length <= length) {
30525                 return prettified;
30526               }
30527             }
30528
30529             if (isObject$3(obj)) {
30530               var nextIndent = currentIndent + indent;
30531               var items = [];
30532               var delimiters;
30533
30534               var comma = function comma(array, index) {
30535                 return index === array.length - 1 ? 0 : 1;
30536               };
30537
30538               if (Array.isArray(obj)) {
30539                 for (var index = 0; index < obj.length; index++) {
30540                   items.push(_stringify(obj[index], nextIndent, comma(obj, index)) || 'null');
30541                 }
30542
30543                 delimiters = '[]';
30544               } else {
30545                 Object.keys(obj).forEach(function (key, index, array) {
30546                   var keyPart = JSON.stringify(key) + ': ';
30547
30548                   var value = _stringify(obj[key], nextIndent, keyPart.length + comma(array, index));
30549
30550                   if (value !== undefined) {
30551                     items.push(keyPart + value);
30552                   }
30553                 });
30554                 delimiters = '{}';
30555               }
30556
30557               if (items.length > 0) {
30558                 return [delimiters[0], indent + items.join(',\n' + nextIndent), delimiters[1]].join('\n' + currentIndent);
30559               }
30560             }
30561
30562             return string;
30563           }(obj, '', 0);
30564         } // Note: This regex matches even invalid JSON strings, but since we’re
30565         // working on the output of `JSON.stringify` we know that only valid strings
30566         // are present (unless the user supplied a weird `options.indent` but in
30567         // that case we don’t care since the output would be invalid anyway).
30568
30569
30570         var stringOrChar = /("(?:[^\\"]|\\.)*")|[:,\][}{]/g;
30571
30572         function prettify(string, options) {
30573           options = options || {};
30574           var tokens = {
30575             '{': '{',
30576             '}': '}',
30577             '[': '[',
30578             ']': ']',
30579             ',': ', ',
30580             ':': ': '
30581           };
30582
30583           if (options.addMargin || options.addObjectMargin) {
30584             tokens['{'] = '{ ';
30585             tokens['}'] = ' }';
30586           }
30587
30588           if (options.addMargin || options.addArrayMargin) {
30589             tokens['['] = '[ ';
30590             tokens[']'] = ' ]';
30591           }
30592
30593           return string.replace(stringOrChar, function (match, string) {
30594             return string ? match : tokens[match];
30595           });
30596         }
30597
30598         function get(options, name, defaultValue) {
30599           return name in options ? options[name] : defaultValue;
30600         }
30601
30602         var jsonStringifyPrettyCompact = stringify;
30603
30604         var _default = /*#__PURE__*/function () {
30605           // constructor
30606           //
30607           // `fc`  Optional FeatureCollection of known features
30608           //
30609           // Optionally pass a GeoJSON FeatureCollection of known features which we can refer to later.
30610           // Each feature must have a filename-like `id`, for example: `something.geojson`
30611           //
30612           // {
30613           //   "type": "FeatureCollection"
30614           //   "features": [
30615           //     {
30616           //       "type": "Feature",
30617           //       "id": "philly_metro.geojson",
30618           //       "properties": { … },
30619           //       "geometry": { … }
30620           //     }
30621           //   ]
30622           // }
30623           function _default(fc) {
30624             var _this = this;
30625
30626             _classCallCheck$1(this, _default);
30627
30628             // The _cache retains resolved features, so if you ask for the same thing multiple times
30629             // we don't repeat the expensive resolving/clipping operations.
30630             //
30631             // Each feature has a stable identifier that is used as the cache key.
30632             // The identifiers look like:
30633             // - for point locations, the stringified point:          e.g. '[8.67039,49.41882]'
30634             // - for geojson locations, the geojson id:               e.g. 'de-hamburg.geojson'
30635             // - for countrycoder locations, feature.id property:     e.g. 'Q2'  (countrycoder uses Wikidata identifiers)
30636             // - for aggregated locationSets, +[include]-[exclude]:   e.g '+[Q2]-[Q18,Q27611]'
30637             this._cache = {}; // When strict mode = true, throw on invalid locations or locationSets.
30638             // When strict mode = false, return `null` for invalid locations or locationSets.
30639
30640             this._strict = true; // process input FeatureCollection
30641
30642             if (fc && fc.type === 'FeatureCollection' && Array.isArray(fc.features)) {
30643               fc.features.forEach(function (feature) {
30644                 feature.properties = feature.properties || {};
30645                 var props = feature.properties; // Get `id` from either `id` or `properties`
30646
30647                 var id = feature.id || props.id;
30648                 if (!id || !/^\S+\.geojson$/i.test(id)) return; // Ensure `id` exists and is lowercase
30649
30650                 id = id.toLowerCase();
30651                 feature.id = id;
30652                 props.id = id; // Ensure `area` property exists
30653
30654                 if (!props.area) {
30655                   var area = geojsonArea.geometry(feature.geometry) / 1e6; // m² to km²
30656
30657                   props.area = Number(area.toFixed(2));
30658                 }
30659
30660                 _this._cache[id] = feature;
30661               });
30662             } // Replace CountryCoder world geometry to be a polygon covering the world.
30663
30664
30665             var world = _cloneDeep(feature$1('Q2'));
30666
30667             world.geometry = {
30668               type: 'Polygon',
30669               coordinates: [[[-180, -90], [180, -90], [180, 90], [-180, 90], [-180, -90]]]
30670             };
30671             world.id = 'Q2';
30672             world.properties.id = 'Q2';
30673             world.properties.area = geojsonArea.geometry(world.geometry) / 1e6; // m² to km²
30674
30675             this._cache.Q2 = world;
30676           } // validateLocation
30677           // `location`  The location to validate
30678           //
30679           // Pass a `location` value to validate
30680           //
30681           // Returns a result like:
30682           //   {
30683           //     type:     'point', 'geojson', or 'countrycoder'
30684           //     location:  the queried location
30685           //     id:        the stable identifier for the feature
30686           //   }
30687           // or `null` if the location is invalid
30688           //
30689
30690
30691           _createClass$1(_default, [{
30692             key: "validateLocation",
30693             value: function validateLocation(location) {
30694               if (Array.isArray(location) && (location.length === 2 || location.length === 3)) {
30695                 // [lon, lat] or [lon, lat, radius] point?
30696                 var lon = location[0];
30697                 var lat = location[1];
30698                 var radius = location[2];
30699
30700                 if (Number.isFinite(lon) && lon >= -180 && lon <= 180 && Number.isFinite(lat) && lat >= -90 && lat <= 90 && (location.length === 2 || Number.isFinite(radius) && radius > 0)) {
30701                   var id = '[' + location.toString() + ']';
30702                   return {
30703                     type: 'point',
30704                     location: location,
30705                     id: id
30706                   };
30707                 }
30708               } else if (typeof location === 'string' && /^\S+\.geojson$/i.test(location)) {
30709                 // a .geojson filename?
30710                 var _id = location.toLowerCase();
30711
30712                 if (this._cache[_id]) {
30713                   return {
30714                     type: 'geojson',
30715                     location: location,
30716                     id: _id
30717                   };
30718                 }
30719               } else if (typeof location === 'string' || typeof location === 'number') {
30720                 // a country-coder value?
30721                 var feature = feature$1(location);
30722
30723                 if (feature) {
30724                   // Use wikidata QID as the identifier, since that seems to be the one
30725                   // property that everything in CountryCoder is guaranteed to have.
30726                   var _id2 = feature.properties.wikidata;
30727                   return {
30728                     type: 'countrycoder',
30729                     location: location,
30730                     id: _id2
30731                   };
30732                 }
30733               }
30734
30735               if (this._strict) {
30736                 throw new Error("validateLocation:  Invalid location: \"".concat(location, "\"."));
30737               } else {
30738                 return null;
30739               }
30740             } // resolveLocation
30741             // `location`  The location to resolve
30742             //
30743             // Pass a `location` value to resolve
30744             //
30745             // Returns a result like:
30746             //   {
30747             //     type:      'point', 'geojson', or 'countrycoder'
30748             //     location:  the queried location
30749             //     id:        a stable identifier for the feature
30750             //     feature:   the resolved GeoJSON feature
30751             //   }
30752             //  or `null` if the location is invalid
30753             //
30754
30755           }, {
30756             key: "resolveLocation",
30757             value: function resolveLocation(location) {
30758               var valid = this.validateLocation(location);
30759               if (!valid) return null;
30760               var id = valid.id; // Return a result from cache if we can
30761
30762               if (this._cache[id]) {
30763                 return Object.assign(valid, {
30764                   feature: this._cache[id]
30765                 });
30766               } // A [lon,lat] coordinate pair?
30767
30768
30769               if (valid.type === 'point') {
30770                 var lon = location[0];
30771                 var lat = location[1];
30772                 var radius = location[2] || 25; // km
30773
30774                 var EDGES = 10;
30775                 var PRECISION = 3;
30776                 var area = Math.PI * radius * radius;
30777                 var feature = this._cache[id] = geojsonPrecision({
30778                   type: 'Feature',
30779                   id: id,
30780                   properties: {
30781                     id: id,
30782                     area: Number(area.toFixed(2))
30783                   },
30784                   geometry: circleToPolygon([lon, lat], radius * 1000, EDGES) // km to m
30785
30786                 }, PRECISION);
30787                 return Object.assign(valid, {
30788                   feature: feature
30789                 }); // A .geojson filename?
30790               } else if (valid.type === 'geojson') ; else if (valid.type === 'countrycoder') {
30791                 var _feature = _cloneDeep(feature$1(id));
30792
30793                 var props = _feature.properties; // -> This block of code is weird and requires some explanation. <-
30794                 // CountryCoder includes higher level features which are made up of members.
30795                 // These features don't have their own geometry, but CountryCoder provides an
30796                 //   `aggregateFeature` method to combine these members into a MultiPolygon.
30797                 // In the past, Turf/JSTS/martinez could not handle the aggregated features,
30798                 //   so we'd iteratively union them all together.  (this was slow)
30799                 // But now mfogel/polygon-clipping handles these MultiPolygons like a boss.
30800                 // This approach also has the benefit of removing all the internal boaders and
30801                 //   simplifying the regional polygons a lot.
30802
30803                 if (Array.isArray(props.members)) {
30804                   var aggregate = aggregateFeature(id);
30805                   aggregate.geometry.coordinates = _clip([aggregate], 'UNION').geometry.coordinates;
30806                   _feature.geometry = aggregate.geometry;
30807                 } // Ensure `area` property exists
30808
30809
30810                 if (!props.area) {
30811                   var _area = geojsonArea.geometry(_feature.geometry) / 1e6; // m² to km²
30812
30813
30814                   props.area = Number(_area.toFixed(2));
30815                 } // Ensure `id` property exists
30816
30817
30818                 _feature.id = id;
30819                 props.id = id;
30820                 this._cache[id] = _feature;
30821                 return Object.assign(valid, {
30822                   feature: _feature
30823                 });
30824               }
30825
30826               if (this._strict) {
30827                 throw new Error("resolveLocation:  Couldn't resolve location \"".concat(location, "\"."));
30828               } else {
30829                 return null;
30830               }
30831             } // validateLocationSet
30832             // `locationSet`  the locationSet to validate
30833             //
30834             // Pass a locationSet Object to validate like:
30835             //   {
30836             //     include: [ Array of locations ],
30837             //     exclude: [ Array of locations ]
30838             //   }
30839             //
30840             // Returns a result like:
30841             //   {
30842             //     type:         'locationset'
30843             //     locationSet:  the queried locationSet
30844             //     id:           the stable identifier for the feature
30845             //   }
30846             // or `null` if the locationSet is invalid
30847             //
30848
30849           }, {
30850             key: "validateLocationSet",
30851             value: function validateLocationSet(locationSet) {
30852               locationSet = locationSet || {};
30853               var validator = this.validateLocation.bind(this);
30854               var include = (locationSet.include || []).map(validator).filter(Boolean);
30855               var exclude = (locationSet.exclude || []).map(validator).filter(Boolean);
30856
30857               if (!include.length) {
30858                 if (this._strict) {
30859                   throw new Error("validateLocationSet:  LocationSet includes nothing.");
30860                 } else {
30861                   // non-strict mode, replace an empty locationSet with one that includes "the world"
30862                   locationSet.include = ['Q2'];
30863                   include = [{
30864                     type: 'countrycoder',
30865                     location: 'Q2',
30866                     id: 'Q2'
30867                   }];
30868                 }
30869               } // Generate stable identifier
30870
30871
30872               include.sort(_sortLocations);
30873               var id = '+[' + include.map(function (d) {
30874                 return d.id;
30875               }).join(',') + ']';
30876
30877               if (exclude.length) {
30878                 exclude.sort(_sortLocations);
30879                 id += '-[' + exclude.map(function (d) {
30880                   return d.id;
30881                 }).join(',') + ']';
30882               }
30883
30884               return {
30885                 type: 'locationset',
30886                 locationSet: locationSet,
30887                 id: id
30888               };
30889             } // resolveLocationSet
30890             // `locationSet`  the locationSet to resolve
30891             //
30892             // Pass a locationSet Object to validate like:
30893             //   {
30894             //     include: [ Array of locations ],
30895             //     exclude: [ Array of locations ]
30896             //   }
30897             //
30898             // Returns a result like:
30899             //   {
30900             //     type:         'locationset'
30901             //     locationSet:  the queried locationSet
30902             //     id:           the stable identifier for the feature
30903             //     feature:      the resolved GeoJSON feature
30904             //   }
30905             // or `null` if the locationSet is invalid
30906             //
30907
30908           }, {
30909             key: "resolveLocationSet",
30910             value: function resolveLocationSet(locationSet) {
30911               locationSet = locationSet || {};
30912               var valid = this.validateLocationSet(locationSet);
30913               if (!valid) return null;
30914               var id = valid.id; // Return a result from cache if we can
30915
30916               if (this._cache[id]) {
30917                 return Object.assign(valid, {
30918                   feature: this._cache[id]
30919                 });
30920               }
30921
30922               var resolver = this.resolveLocation.bind(this);
30923               var includes = (locationSet.include || []).map(resolver).filter(Boolean);
30924               var excludes = (locationSet.exclude || []).map(resolver).filter(Boolean); // Return quickly if it's a single included location..
30925
30926               if (includes.length === 1 && excludes.length === 0) {
30927                 return Object.assign(valid, {
30928                   feature: includes[0].feature
30929                 });
30930               } // Calculate unions
30931
30932
30933               var includeGeoJSON = _clip(includes.map(function (d) {
30934                 return d.feature;
30935               }), 'UNION');
30936
30937               var excludeGeoJSON = _clip(excludes.map(function (d) {
30938                 return d.feature;
30939               }), 'UNION'); // Calculate difference, update `area` and return result
30940
30941
30942               var resultGeoJSON = excludeGeoJSON ? _clip([includeGeoJSON, excludeGeoJSON], 'DIFFERENCE') : includeGeoJSON;
30943               var area = geojsonArea.geometry(resultGeoJSON.geometry) / 1e6; // m² to km²
30944
30945               resultGeoJSON.id = id;
30946               resultGeoJSON.properties = {
30947                 id: id,
30948                 area: Number(area.toFixed(2))
30949               };
30950               this._cache[id] = resultGeoJSON;
30951               return Object.assign(valid, {
30952                 feature: resultGeoJSON
30953               });
30954             } // strict
30955             //
30956
30957           }, {
30958             key: "strict",
30959             value: function strict(val) {
30960               if (val === undefined) {
30961                 // get
30962                 return this._strict;
30963               } else {
30964                 // set
30965                 this._strict = val;
30966                 return this;
30967               }
30968             } // cache
30969             // convenience method to access the internal cache
30970
30971           }, {
30972             key: "cache",
30973             value: function cache() {
30974               return this._cache;
30975             } // stringify
30976             // convenience method to prettyStringify the given object
30977
30978           }, {
30979             key: "stringify",
30980             value: function stringify(obj, options) {
30981               return jsonStringifyPrettyCompact(obj, options);
30982             }
30983           }]);
30984
30985           return _default;
30986         }(); // Wrap the mfogel/polygon-clipping library and return a GeoJSON feature.
30987
30988         function _clip(features, which) {
30989           if (!Array.isArray(features) || !features.length) return null;
30990           var fn = {
30991             UNION: index.union,
30992             DIFFERENCE: index.difference
30993           }[which];
30994           var args = features.map(function (feature) {
30995             return feature.geometry.coordinates;
30996           });
30997           var coords = fn.apply(null, args);
30998           return {
30999             type: 'Feature',
31000             properties: {},
31001             geometry: {
31002               type: whichType(coords),
31003               coordinates: coords
31004             }
31005           }; // is this a Polygon or a MultiPolygon?
31006
31007           function whichType(coords) {
31008             var a = Array.isArray(coords);
31009             var b = a && Array.isArray(coords[0]);
31010             var c = b && Array.isArray(coords[0][0]);
31011             var d = c && Array.isArray(coords[0][0][0]);
31012             return d ? 'MultiPolygon' : 'Polygon';
31013           }
31014         }
31015
31016         function _cloneDeep(obj) {
31017           return JSON.parse(JSON.stringify(obj));
31018         } // Sorting the location lists is ok because they end up unioned together.
31019         // This sorting makes it possible to generate a deterministic id.
31020
31021
31022         function _sortLocations(a, b) {
31023           var rank = {
31024             countrycoder: 1,
31025             geojson: 2,
31026             point: 3
31027           };
31028           var aRank = rank[a.type];
31029           var bRank = rank[b.type];
31030           return aRank > bRank ? 1 : aRank < bRank ? -1 : a.id.localeCompare(b.id);
31031         }
31032
31033         // `Number.MAX_SAFE_INTEGER` constant
31034         // https://tc39.es/ecma262/#sec-number.max_safe_integer
31035         _export({ target: 'Number', stat: true }, {
31036           MAX_SAFE_INTEGER: 0x1FFFFFFFFFFFFF
31037         });
31038
31039         var aesJs = createCommonjsModule(function (module, exports) {
31040           (function (root) {
31041
31042             function checkInt(value) {
31043               return parseInt(value) === value;
31044             }
31045
31046             function checkInts(arrayish) {
31047               if (!checkInt(arrayish.length)) {
31048                 return false;
31049               }
31050
31051               for (var i = 0; i < arrayish.length; i++) {
31052                 if (!checkInt(arrayish[i]) || arrayish[i] < 0 || arrayish[i] > 255) {
31053                   return false;
31054                 }
31055               }
31056
31057               return true;
31058             }
31059
31060             function coerceArray(arg, copy) {
31061               // ArrayBuffer view
31062               if (arg.buffer && arg.name === 'Uint8Array') {
31063                 if (copy) {
31064                   if (arg.slice) {
31065                     arg = arg.slice();
31066                   } else {
31067                     arg = Array.prototype.slice.call(arg);
31068                   }
31069                 }
31070
31071                 return arg;
31072               } // It's an array; check it is a valid representation of a byte
31073
31074
31075               if (Array.isArray(arg)) {
31076                 if (!checkInts(arg)) {
31077                   throw new Error('Array contains invalid value: ' + arg);
31078                 }
31079
31080                 return new Uint8Array(arg);
31081               } // Something else, but behaves like an array (maybe a Buffer? Arguments?)
31082
31083
31084               if (checkInt(arg.length) && checkInts(arg)) {
31085                 return new Uint8Array(arg);
31086               }
31087
31088               throw new Error('unsupported array-like object');
31089             }
31090
31091             function createArray(length) {
31092               return new Uint8Array(length);
31093             }
31094
31095             function copyArray(sourceArray, targetArray, targetStart, sourceStart, sourceEnd) {
31096               if (sourceStart != null || sourceEnd != null) {
31097                 if (sourceArray.slice) {
31098                   sourceArray = sourceArray.slice(sourceStart, sourceEnd);
31099                 } else {
31100                   sourceArray = Array.prototype.slice.call(sourceArray, sourceStart, sourceEnd);
31101                 }
31102               }
31103
31104               targetArray.set(sourceArray, targetStart);
31105             }
31106
31107             var convertUtf8 = function () {
31108               function toBytes(text) {
31109                 var result = [],
31110                     i = 0;
31111                 text = encodeURI(text);
31112
31113                 while (i < text.length) {
31114                   var c = text.charCodeAt(i++); // if it is a % sign, encode the following 2 bytes as a hex value
31115
31116                   if (c === 37) {
31117                     result.push(parseInt(text.substr(i, 2), 16));
31118                     i += 2; // otherwise, just the actual byte
31119                   } else {
31120                     result.push(c);
31121                   }
31122                 }
31123
31124                 return coerceArray(result);
31125               }
31126
31127               function fromBytes(bytes) {
31128                 var result = [],
31129                     i = 0;
31130
31131                 while (i < bytes.length) {
31132                   var c = bytes[i];
31133
31134                   if (c < 128) {
31135                     result.push(String.fromCharCode(c));
31136                     i++;
31137                   } else if (c > 191 && c < 224) {
31138                     result.push(String.fromCharCode((c & 0x1f) << 6 | bytes[i + 1] & 0x3f));
31139                     i += 2;
31140                   } else {
31141                     result.push(String.fromCharCode((c & 0x0f) << 12 | (bytes[i + 1] & 0x3f) << 6 | bytes[i + 2] & 0x3f));
31142                     i += 3;
31143                   }
31144                 }
31145
31146                 return result.join('');
31147               }
31148
31149               return {
31150                 toBytes: toBytes,
31151                 fromBytes: fromBytes
31152               };
31153             }();
31154
31155             var convertHex = function () {
31156               function toBytes(text) {
31157                 var result = [];
31158
31159                 for (var i = 0; i < text.length; i += 2) {
31160                   result.push(parseInt(text.substr(i, 2), 16));
31161                 }
31162
31163                 return result;
31164               } // http://ixti.net/development/javascript/2011/11/11/base64-encodedecode-of-utf8-in-browser-with-js.html
31165
31166
31167               var Hex = '0123456789abcdef';
31168
31169               function fromBytes(bytes) {
31170                 var result = [];
31171
31172                 for (var i = 0; i < bytes.length; i++) {
31173                   var v = bytes[i];
31174                   result.push(Hex[(v & 0xf0) >> 4] + Hex[v & 0x0f]);
31175                 }
31176
31177                 return result.join('');
31178               }
31179
31180               return {
31181                 toBytes: toBytes,
31182                 fromBytes: fromBytes
31183               };
31184             }(); // Number of rounds by keysize
31185
31186
31187             var numberOfRounds = {
31188               16: 10,
31189               24: 12,
31190               32: 14
31191             }; // Round constant words
31192
31193             var rcon = [0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91]; // S-box and Inverse S-box (S is for Substitution)
31194
31195             var S = [0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76, 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0, 0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15, 0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75, 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84, 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf, 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8, 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2, 0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73, 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb, 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79, 0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08, 0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a, 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e, 0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf, 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16];
31196             var Si = [0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb, 0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb, 0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e, 0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25, 0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92, 0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84, 0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06, 0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b, 0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73, 0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e, 0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b, 0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4, 0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f, 0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef, 0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61, 0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d]; // Transformations for encryption
31197
31198             var T1 = [0xc66363a5, 0xf87c7c84, 0xee777799, 0xf67b7b8d, 0xfff2f20d, 0xd66b6bbd, 0xde6f6fb1, 0x91c5c554, 0x60303050, 0x02010103, 0xce6767a9, 0x562b2b7d, 0xe7fefe19, 0xb5d7d762, 0x4dababe6, 0xec76769a, 0x8fcaca45, 0x1f82829d, 0x89c9c940, 0xfa7d7d87, 0xeffafa15, 0xb25959eb, 0x8e4747c9, 0xfbf0f00b, 0x41adadec, 0xb3d4d467, 0x5fa2a2fd, 0x45afafea, 0x239c9cbf, 0x53a4a4f7, 0xe4727296, 0x9bc0c05b, 0x75b7b7c2, 0xe1fdfd1c, 0x3d9393ae, 0x4c26266a, 0x6c36365a, 0x7e3f3f41, 0xf5f7f702, 0x83cccc4f, 0x6834345c, 0x51a5a5f4, 0xd1e5e534, 0xf9f1f108, 0xe2717193, 0xabd8d873, 0x62313153, 0x2a15153f, 0x0804040c, 0x95c7c752, 0x46232365, 0x9dc3c35e, 0x30181828, 0x379696a1, 0x0a05050f, 0x2f9a9ab5, 0x0e070709, 0x24121236, 0x1b80809b, 0xdfe2e23d, 0xcdebeb26, 0x4e272769, 0x7fb2b2cd, 0xea75759f, 0x1209091b, 0x1d83839e, 0x582c2c74, 0x341a1a2e, 0x361b1b2d, 0xdc6e6eb2, 0xb45a5aee, 0x5ba0a0fb, 0xa45252f6, 0x763b3b4d, 0xb7d6d661, 0x7db3b3ce, 0x5229297b, 0xdde3e33e, 0x5e2f2f71, 0x13848497, 0xa65353f5, 0xb9d1d168, 0x00000000, 0xc1eded2c, 0x40202060, 0xe3fcfc1f, 0x79b1b1c8, 0xb65b5bed, 0xd46a6abe, 0x8dcbcb46, 0x67bebed9, 0x7239394b, 0x944a4ade, 0x984c4cd4, 0xb05858e8, 0x85cfcf4a, 0xbbd0d06b, 0xc5efef2a, 0x4faaaae5, 0xedfbfb16, 0x864343c5, 0x9a4d4dd7, 0x66333355, 0x11858594, 0x8a4545cf, 0xe9f9f910, 0x04020206, 0xfe7f7f81, 0xa05050f0, 0x783c3c44, 0x259f9fba, 0x4ba8a8e3, 0xa25151f3, 0x5da3a3fe, 0x804040c0, 0x058f8f8a, 0x3f9292ad, 0x219d9dbc, 0x70383848, 0xf1f5f504, 0x63bcbcdf, 0x77b6b6c1, 0xafdada75, 0x42212163, 0x20101030, 0xe5ffff1a, 0xfdf3f30e, 0xbfd2d26d, 0x81cdcd4c, 0x180c0c14, 0x26131335, 0xc3ecec2f, 0xbe5f5fe1, 0x359797a2, 0x884444cc, 0x2e171739, 0x93c4c457, 0x55a7a7f2, 0xfc7e7e82, 0x7a3d3d47, 0xc86464ac, 0xba5d5de7, 0x3219192b, 0xe6737395, 0xc06060a0, 0x19818198, 0x9e4f4fd1, 0xa3dcdc7f, 0x44222266, 0x542a2a7e, 0x3b9090ab, 0x0b888883, 0x8c4646ca, 0xc7eeee29, 0x6bb8b8d3, 0x2814143c, 0xa7dede79, 0xbc5e5ee2, 0x160b0b1d, 0xaddbdb76, 0xdbe0e03b, 0x64323256, 0x743a3a4e, 0x140a0a1e, 0x924949db, 0x0c06060a, 0x4824246c, 0xb85c5ce4, 0x9fc2c25d, 0xbdd3d36e, 0x43acacef, 0xc46262a6, 0x399191a8, 0x319595a4, 0xd3e4e437, 0xf279798b, 0xd5e7e732, 0x8bc8c843, 0x6e373759, 0xda6d6db7, 0x018d8d8c, 0xb1d5d564, 0x9c4e4ed2, 0x49a9a9e0, 0xd86c6cb4, 0xac5656fa, 0xf3f4f407, 0xcfeaea25, 0xca6565af, 0xf47a7a8e, 0x47aeaee9, 0x10080818, 0x6fbabad5, 0xf0787888, 0x4a25256f, 0x5c2e2e72, 0x381c1c24, 0x57a6a6f1, 0x73b4b4c7, 0x97c6c651, 0xcbe8e823, 0xa1dddd7c, 0xe874749c, 0x3e1f1f21, 0x964b4bdd, 0x61bdbddc, 0x0d8b8b86, 0x0f8a8a85, 0xe0707090, 0x7c3e3e42, 0x71b5b5c4, 0xcc6666aa, 0x904848d8, 0x06030305, 0xf7f6f601, 0x1c0e0e12, 0xc26161a3, 0x6a35355f, 0xae5757f9, 0x69b9b9d0, 0x17868691, 0x99c1c158, 0x3a1d1d27, 0x279e9eb9, 0xd9e1e138, 0xebf8f813, 0x2b9898b3, 0x22111133, 0xd26969bb, 0xa9d9d970, 0x078e8e89, 0x339494a7, 0x2d9b9bb6, 0x3c1e1e22, 0x15878792, 0xc9e9e920, 0x87cece49, 0xaa5555ff, 0x50282878, 0xa5dfdf7a, 0x038c8c8f, 0x59a1a1f8, 0x09898980, 0x1a0d0d17, 0x65bfbfda, 0xd7e6e631, 0x844242c6, 0xd06868b8, 0x824141c3, 0x299999b0, 0x5a2d2d77, 0x1e0f0f11, 0x7bb0b0cb, 0xa85454fc, 0x6dbbbbd6, 0x2c16163a];
31199             var T2 = [0xa5c66363, 0x84f87c7c, 0x99ee7777, 0x8df67b7b, 0x0dfff2f2, 0xbdd66b6b, 0xb1de6f6f, 0x5491c5c5, 0x50603030, 0x03020101, 0xa9ce6767, 0x7d562b2b, 0x19e7fefe, 0x62b5d7d7, 0xe64dabab, 0x9aec7676, 0x458fcaca, 0x9d1f8282, 0x4089c9c9, 0x87fa7d7d, 0x15effafa, 0xebb25959, 0xc98e4747, 0x0bfbf0f0, 0xec41adad, 0x67b3d4d4, 0xfd5fa2a2, 0xea45afaf, 0xbf239c9c, 0xf753a4a4, 0x96e47272, 0x5b9bc0c0, 0xc275b7b7, 0x1ce1fdfd, 0xae3d9393, 0x6a4c2626, 0x5a6c3636, 0x417e3f3f, 0x02f5f7f7, 0x4f83cccc, 0x5c683434, 0xf451a5a5, 0x34d1e5e5, 0x08f9f1f1, 0x93e27171, 0x73abd8d8, 0x53623131, 0x3f2a1515, 0x0c080404, 0x5295c7c7, 0x65462323, 0x5e9dc3c3, 0x28301818, 0xa1379696, 0x0f0a0505, 0xb52f9a9a, 0x090e0707, 0x36241212, 0x9b1b8080, 0x3ddfe2e2, 0x26cdebeb, 0x694e2727, 0xcd7fb2b2, 0x9fea7575, 0x1b120909, 0x9e1d8383, 0x74582c2c, 0x2e341a1a, 0x2d361b1b, 0xb2dc6e6e, 0xeeb45a5a, 0xfb5ba0a0, 0xf6a45252, 0x4d763b3b, 0x61b7d6d6, 0xce7db3b3, 0x7b522929, 0x3edde3e3, 0x715e2f2f, 0x97138484, 0xf5a65353, 0x68b9d1d1, 0x00000000, 0x2cc1eded, 0x60402020, 0x1fe3fcfc, 0xc879b1b1, 0xedb65b5b, 0xbed46a6a, 0x468dcbcb, 0xd967bebe, 0x4b723939, 0xde944a4a, 0xd4984c4c, 0xe8b05858, 0x4a85cfcf, 0x6bbbd0d0, 0x2ac5efef, 0xe54faaaa, 0x16edfbfb, 0xc5864343, 0xd79a4d4d, 0x55663333, 0x94118585, 0xcf8a4545, 0x10e9f9f9, 0x06040202, 0x81fe7f7f, 0xf0a05050, 0x44783c3c, 0xba259f9f, 0xe34ba8a8, 0xf3a25151, 0xfe5da3a3, 0xc0804040, 0x8a058f8f, 0xad3f9292, 0xbc219d9d, 0x48703838, 0x04f1f5f5, 0xdf63bcbc, 0xc177b6b6, 0x75afdada, 0x63422121, 0x30201010, 0x1ae5ffff, 0x0efdf3f3, 0x6dbfd2d2, 0x4c81cdcd, 0x14180c0c, 0x35261313, 0x2fc3ecec, 0xe1be5f5f, 0xa2359797, 0xcc884444, 0x392e1717, 0x5793c4c4, 0xf255a7a7, 0x82fc7e7e, 0x477a3d3d, 0xacc86464, 0xe7ba5d5d, 0x2b321919, 0x95e67373, 0xa0c06060, 0x98198181, 0xd19e4f4f, 0x7fa3dcdc, 0x66442222, 0x7e542a2a, 0xab3b9090, 0x830b8888, 0xca8c4646, 0x29c7eeee, 0xd36bb8b8, 0x3c281414, 0x79a7dede, 0xe2bc5e5e, 0x1d160b0b, 0x76addbdb, 0x3bdbe0e0, 0x56643232, 0x4e743a3a, 0x1e140a0a, 0xdb924949, 0x0a0c0606, 0x6c482424, 0xe4b85c5c, 0x5d9fc2c2, 0x6ebdd3d3, 0xef43acac, 0xa6c46262, 0xa8399191, 0xa4319595, 0x37d3e4e4, 0x8bf27979, 0x32d5e7e7, 0x438bc8c8, 0x596e3737, 0xb7da6d6d, 0x8c018d8d, 0x64b1d5d5, 0xd29c4e4e, 0xe049a9a9, 0xb4d86c6c, 0xfaac5656, 0x07f3f4f4, 0x25cfeaea, 0xafca6565, 0x8ef47a7a, 0xe947aeae, 0x18100808, 0xd56fbaba, 0x88f07878, 0x6f4a2525, 0x725c2e2e, 0x24381c1c, 0xf157a6a6, 0xc773b4b4, 0x5197c6c6, 0x23cbe8e8, 0x7ca1dddd, 0x9ce87474, 0x213e1f1f, 0xdd964b4b, 0xdc61bdbd, 0x860d8b8b, 0x850f8a8a, 0x90e07070, 0x427c3e3e, 0xc471b5b5, 0xaacc6666, 0xd8904848, 0x05060303, 0x01f7f6f6, 0x121c0e0e, 0xa3c26161, 0x5f6a3535, 0xf9ae5757, 0xd069b9b9, 0x91178686, 0x5899c1c1, 0x273a1d1d, 0xb9279e9e, 0x38d9e1e1, 0x13ebf8f8, 0xb32b9898, 0x33221111, 0xbbd26969, 0x70a9d9d9, 0x89078e8e, 0xa7339494, 0xb62d9b9b, 0x223c1e1e, 0x92158787, 0x20c9e9e9, 0x4987cece, 0xffaa5555, 0x78502828, 0x7aa5dfdf, 0x8f038c8c, 0xf859a1a1, 0x80098989, 0x171a0d0d, 0xda65bfbf, 0x31d7e6e6, 0xc6844242, 0xb8d06868, 0xc3824141, 0xb0299999, 0x775a2d2d, 0x111e0f0f, 0xcb7bb0b0, 0xfca85454, 0xd66dbbbb, 0x3a2c1616];
31200             var T3 = [0x63a5c663, 0x7c84f87c, 0x7799ee77, 0x7b8df67b, 0xf20dfff2, 0x6bbdd66b, 0x6fb1de6f, 0xc55491c5, 0x30506030, 0x01030201, 0x67a9ce67, 0x2b7d562b, 0xfe19e7fe, 0xd762b5d7, 0xabe64dab, 0x769aec76, 0xca458fca, 0x829d1f82, 0xc94089c9, 0x7d87fa7d, 0xfa15effa, 0x59ebb259, 0x47c98e47, 0xf00bfbf0, 0xadec41ad, 0xd467b3d4, 0xa2fd5fa2, 0xafea45af, 0x9cbf239c, 0xa4f753a4, 0x7296e472, 0xc05b9bc0, 0xb7c275b7, 0xfd1ce1fd, 0x93ae3d93, 0x266a4c26, 0x365a6c36, 0x3f417e3f, 0xf702f5f7, 0xcc4f83cc, 0x345c6834, 0xa5f451a5, 0xe534d1e5, 0xf108f9f1, 0x7193e271, 0xd873abd8, 0x31536231, 0x153f2a15, 0x040c0804, 0xc75295c7, 0x23654623, 0xc35e9dc3, 0x18283018, 0x96a13796, 0x050f0a05, 0x9ab52f9a, 0x07090e07, 0x12362412, 0x809b1b80, 0xe23ddfe2, 0xeb26cdeb, 0x27694e27, 0xb2cd7fb2, 0x759fea75, 0x091b1209, 0x839e1d83, 0x2c74582c, 0x1a2e341a, 0x1b2d361b, 0x6eb2dc6e, 0x5aeeb45a, 0xa0fb5ba0, 0x52f6a452, 0x3b4d763b, 0xd661b7d6, 0xb3ce7db3, 0x297b5229, 0xe33edde3, 0x2f715e2f, 0x84971384, 0x53f5a653, 0xd168b9d1, 0x00000000, 0xed2cc1ed, 0x20604020, 0xfc1fe3fc, 0xb1c879b1, 0x5bedb65b, 0x6abed46a, 0xcb468dcb, 0xbed967be, 0x394b7239, 0x4ade944a, 0x4cd4984c, 0x58e8b058, 0xcf4a85cf, 0xd06bbbd0, 0xef2ac5ef, 0xaae54faa, 0xfb16edfb, 0x43c58643, 0x4dd79a4d, 0x33556633, 0x85941185, 0x45cf8a45, 0xf910e9f9, 0x02060402, 0x7f81fe7f, 0x50f0a050, 0x3c44783c, 0x9fba259f, 0xa8e34ba8, 0x51f3a251, 0xa3fe5da3, 0x40c08040, 0x8f8a058f, 0x92ad3f92, 0x9dbc219d, 0x38487038, 0xf504f1f5, 0xbcdf63bc, 0xb6c177b6, 0xda75afda, 0x21634221, 0x10302010, 0xff1ae5ff, 0xf30efdf3, 0xd26dbfd2, 0xcd4c81cd, 0x0c14180c, 0x13352613, 0xec2fc3ec, 0x5fe1be5f, 0x97a23597, 0x44cc8844, 0x17392e17, 0xc45793c4, 0xa7f255a7, 0x7e82fc7e, 0x3d477a3d, 0x64acc864, 0x5de7ba5d, 0x192b3219, 0x7395e673, 0x60a0c060, 0x81981981, 0x4fd19e4f, 0xdc7fa3dc, 0x22664422, 0x2a7e542a, 0x90ab3b90, 0x88830b88, 0x46ca8c46, 0xee29c7ee, 0xb8d36bb8, 0x143c2814, 0xde79a7de, 0x5ee2bc5e, 0x0b1d160b, 0xdb76addb, 0xe03bdbe0, 0x32566432, 0x3a4e743a, 0x0a1e140a, 0x49db9249, 0x060a0c06, 0x246c4824, 0x5ce4b85c, 0xc25d9fc2, 0xd36ebdd3, 0xacef43ac, 0x62a6c462, 0x91a83991, 0x95a43195, 0xe437d3e4, 0x798bf279, 0xe732d5e7, 0xc8438bc8, 0x37596e37, 0x6db7da6d, 0x8d8c018d, 0xd564b1d5, 0x4ed29c4e, 0xa9e049a9, 0x6cb4d86c, 0x56faac56, 0xf407f3f4, 0xea25cfea, 0x65afca65, 0x7a8ef47a, 0xaee947ae, 0x08181008, 0xbad56fba, 0x7888f078, 0x256f4a25, 0x2e725c2e, 0x1c24381c, 0xa6f157a6, 0xb4c773b4, 0xc65197c6, 0xe823cbe8, 0xdd7ca1dd, 0x749ce874, 0x1f213e1f, 0x4bdd964b, 0xbddc61bd, 0x8b860d8b, 0x8a850f8a, 0x7090e070, 0x3e427c3e, 0xb5c471b5, 0x66aacc66, 0x48d89048, 0x03050603, 0xf601f7f6, 0x0e121c0e, 0x61a3c261, 0x355f6a35, 0x57f9ae57, 0xb9d069b9, 0x86911786, 0xc15899c1, 0x1d273a1d, 0x9eb9279e, 0xe138d9e1, 0xf813ebf8, 0x98b32b98, 0x11332211, 0x69bbd269, 0xd970a9d9, 0x8e89078e, 0x94a73394, 0x9bb62d9b, 0x1e223c1e, 0x87921587, 0xe920c9e9, 0xce4987ce, 0x55ffaa55, 0x28785028, 0xdf7aa5df, 0x8c8f038c, 0xa1f859a1, 0x89800989, 0x0d171a0d, 0xbfda65bf, 0xe631d7e6, 0x42c68442, 0x68b8d068, 0x41c38241, 0x99b02999, 0x2d775a2d, 0x0f111e0f, 0xb0cb7bb0, 0x54fca854, 0xbbd66dbb, 0x163a2c16];
31201             var T4 = [0x6363a5c6, 0x7c7c84f8, 0x777799ee, 0x7b7b8df6, 0xf2f20dff, 0x6b6bbdd6, 0x6f6fb1de, 0xc5c55491, 0x30305060, 0x01010302, 0x6767a9ce, 0x2b2b7d56, 0xfefe19e7, 0xd7d762b5, 0xababe64d, 0x76769aec, 0xcaca458f, 0x82829d1f, 0xc9c94089, 0x7d7d87fa, 0xfafa15ef, 0x5959ebb2, 0x4747c98e, 0xf0f00bfb, 0xadadec41, 0xd4d467b3, 0xa2a2fd5f, 0xafafea45, 0x9c9cbf23, 0xa4a4f753, 0x727296e4, 0xc0c05b9b, 0xb7b7c275, 0xfdfd1ce1, 0x9393ae3d, 0x26266a4c, 0x36365a6c, 0x3f3f417e, 0xf7f702f5, 0xcccc4f83, 0x34345c68, 0xa5a5f451, 0xe5e534d1, 0xf1f108f9, 0x717193e2, 0xd8d873ab, 0x31315362, 0x15153f2a, 0x04040c08, 0xc7c75295, 0x23236546, 0xc3c35e9d, 0x18182830, 0x9696a137, 0x05050f0a, 0x9a9ab52f, 0x0707090e, 0x12123624, 0x80809b1b, 0xe2e23ddf, 0xebeb26cd, 0x2727694e, 0xb2b2cd7f, 0x75759fea, 0x09091b12, 0x83839e1d, 0x2c2c7458, 0x1a1a2e34, 0x1b1b2d36, 0x6e6eb2dc, 0x5a5aeeb4, 0xa0a0fb5b, 0x5252f6a4, 0x3b3b4d76, 0xd6d661b7, 0xb3b3ce7d, 0x29297b52, 0xe3e33edd, 0x2f2f715e, 0x84849713, 0x5353f5a6, 0xd1d168b9, 0x00000000, 0xeded2cc1, 0x20206040, 0xfcfc1fe3, 0xb1b1c879, 0x5b5bedb6, 0x6a6abed4, 0xcbcb468d, 0xbebed967, 0x39394b72, 0x4a4ade94, 0x4c4cd498, 0x5858e8b0, 0xcfcf4a85, 0xd0d06bbb, 0xefef2ac5, 0xaaaae54f, 0xfbfb16ed, 0x4343c586, 0x4d4dd79a, 0x33335566, 0x85859411, 0x4545cf8a, 0xf9f910e9, 0x02020604, 0x7f7f81fe, 0x5050f0a0, 0x3c3c4478, 0x9f9fba25, 0xa8a8e34b, 0x5151f3a2, 0xa3a3fe5d, 0x4040c080, 0x8f8f8a05, 0x9292ad3f, 0x9d9dbc21, 0x38384870, 0xf5f504f1, 0xbcbcdf63, 0xb6b6c177, 0xdada75af, 0x21216342, 0x10103020, 0xffff1ae5, 0xf3f30efd, 0xd2d26dbf, 0xcdcd4c81, 0x0c0c1418, 0x13133526, 0xecec2fc3, 0x5f5fe1be, 0x9797a235, 0x4444cc88, 0x1717392e, 0xc4c45793, 0xa7a7f255, 0x7e7e82fc, 0x3d3d477a, 0x6464acc8, 0x5d5de7ba, 0x19192b32, 0x737395e6, 0x6060a0c0, 0x81819819, 0x4f4fd19e, 0xdcdc7fa3, 0x22226644, 0x2a2a7e54, 0x9090ab3b, 0x8888830b, 0x4646ca8c, 0xeeee29c7, 0xb8b8d36b, 0x14143c28, 0xdede79a7, 0x5e5ee2bc, 0x0b0b1d16, 0xdbdb76ad, 0xe0e03bdb, 0x32325664, 0x3a3a4e74, 0x0a0a1e14, 0x4949db92, 0x06060a0c, 0x24246c48, 0x5c5ce4b8, 0xc2c25d9f, 0xd3d36ebd, 0xacacef43, 0x6262a6c4, 0x9191a839, 0x9595a431, 0xe4e437d3, 0x79798bf2, 0xe7e732d5, 0xc8c8438b, 0x3737596e, 0x6d6db7da, 0x8d8d8c01, 0xd5d564b1, 0x4e4ed29c, 0xa9a9e049, 0x6c6cb4d8, 0x5656faac, 0xf4f407f3, 0xeaea25cf, 0x6565afca, 0x7a7a8ef4, 0xaeaee947, 0x08081810, 0xbabad56f, 0x787888f0, 0x25256f4a, 0x2e2e725c, 0x1c1c2438, 0xa6a6f157, 0xb4b4c773, 0xc6c65197, 0xe8e823cb, 0xdddd7ca1, 0x74749ce8, 0x1f1f213e, 0x4b4bdd96, 0xbdbddc61, 0x8b8b860d, 0x8a8a850f, 0x707090e0, 0x3e3e427c, 0xb5b5c471, 0x6666aacc, 0x4848d890, 0x03030506, 0xf6f601f7, 0x0e0e121c, 0x6161a3c2, 0x35355f6a, 0x5757f9ae, 0xb9b9d069, 0x86869117, 0xc1c15899, 0x1d1d273a, 0x9e9eb927, 0xe1e138d9, 0xf8f813eb, 0x9898b32b, 0x11113322, 0x6969bbd2, 0xd9d970a9, 0x8e8e8907, 0x9494a733, 0x9b9bb62d, 0x1e1e223c, 0x87879215, 0xe9e920c9, 0xcece4987, 0x5555ffaa, 0x28287850, 0xdfdf7aa5, 0x8c8c8f03, 0xa1a1f859, 0x89898009, 0x0d0d171a, 0xbfbfda65, 0xe6e631d7, 0x4242c684, 0x6868b8d0, 0x4141c382, 0x9999b029, 0x2d2d775a, 0x0f0f111e, 0xb0b0cb7b, 0x5454fca8, 0xbbbbd66d, 0x16163a2c]; // Transformations for decryption
31202
31203             var T5 = [0x51f4a750, 0x7e416553, 0x1a17a4c3, 0x3a275e96, 0x3bab6bcb, 0x1f9d45f1, 0xacfa58ab, 0x4be30393, 0x2030fa55, 0xad766df6, 0x88cc7691, 0xf5024c25, 0x4fe5d7fc, 0xc52acbd7, 0x26354480, 0xb562a38f, 0xdeb15a49, 0x25ba1b67, 0x45ea0e98, 0x5dfec0e1, 0xc32f7502, 0x814cf012, 0x8d4697a3, 0x6bd3f9c6, 0x038f5fe7, 0x15929c95, 0xbf6d7aeb, 0x955259da, 0xd4be832d, 0x587421d3, 0x49e06929, 0x8ec9c844, 0x75c2896a, 0xf48e7978, 0x99583e6b, 0x27b971dd, 0xbee14fb6, 0xf088ad17, 0xc920ac66, 0x7dce3ab4, 0x63df4a18, 0xe51a3182, 0x97513360, 0x62537f45, 0xb16477e0, 0xbb6bae84, 0xfe81a01c, 0xf9082b94, 0x70486858, 0x8f45fd19, 0x94de6c87, 0x527bf8b7, 0xab73d323, 0x724b02e2, 0xe31f8f57, 0x6655ab2a, 0xb2eb2807, 0x2fb5c203, 0x86c57b9a, 0xd33708a5, 0x302887f2, 0x23bfa5b2, 0x02036aba, 0xed16825c, 0x8acf1c2b, 0xa779b492, 0xf307f2f0, 0x4e69e2a1, 0x65daf4cd, 0x0605bed5, 0xd134621f, 0xc4a6fe8a, 0x342e539d, 0xa2f355a0, 0x058ae132, 0xa4f6eb75, 0x0b83ec39, 0x4060efaa, 0x5e719f06, 0xbd6e1051, 0x3e218af9, 0x96dd063d, 0xdd3e05ae, 0x4de6bd46, 0x91548db5, 0x71c45d05, 0x0406d46f, 0x605015ff, 0x1998fb24, 0xd6bde997, 0x894043cc, 0x67d99e77, 0xb0e842bd, 0x07898b88, 0xe7195b38, 0x79c8eedb, 0xa17c0a47, 0x7c420fe9, 0xf8841ec9, 0x00000000, 0x09808683, 0x322bed48, 0x1e1170ac, 0x6c5a724e, 0xfd0efffb, 0x0f853856, 0x3daed51e, 0x362d3927, 0x0a0fd964, 0x685ca621, 0x9b5b54d1, 0x24362e3a, 0x0c0a67b1, 0x9357e70f, 0xb4ee96d2, 0x1b9b919e, 0x80c0c54f, 0x61dc20a2, 0x5a774b69, 0x1c121a16, 0xe293ba0a, 0xc0a02ae5, 0x3c22e043, 0x121b171d, 0x0e090d0b, 0xf28bc7ad, 0x2db6a8b9, 0x141ea9c8, 0x57f11985, 0xaf75074c, 0xee99ddbb, 0xa37f60fd, 0xf701269f, 0x5c72f5bc, 0x44663bc5, 0x5bfb7e34, 0x8b432976, 0xcb23c6dc, 0xb6edfc68, 0xb8e4f163, 0xd731dcca, 0x42638510, 0x13972240, 0x84c61120, 0x854a247d, 0xd2bb3df8, 0xaef93211, 0xc729a16d, 0x1d9e2f4b, 0xdcb230f3, 0x0d8652ec, 0x77c1e3d0, 0x2bb3166c, 0xa970b999, 0x119448fa, 0x47e96422, 0xa8fc8cc4, 0xa0f03f1a, 0x567d2cd8, 0x223390ef, 0x87494ec7, 0xd938d1c1, 0x8ccaa2fe, 0x98d40b36, 0xa6f581cf, 0xa57ade28, 0xdab78e26, 0x3fadbfa4, 0x2c3a9de4, 0x5078920d, 0x6a5fcc9b, 0x547e4662, 0xf68d13c2, 0x90d8b8e8, 0x2e39f75e, 0x82c3aff5, 0x9f5d80be, 0x69d0937c, 0x6fd52da9, 0xcf2512b3, 0xc8ac993b, 0x10187da7, 0xe89c636e, 0xdb3bbb7b, 0xcd267809, 0x6e5918f4, 0xec9ab701, 0x834f9aa8, 0xe6956e65, 0xaaffe67e, 0x21bccf08, 0xef15e8e6, 0xbae79bd9, 0x4a6f36ce, 0xea9f09d4, 0x29b07cd6, 0x31a4b2af, 0x2a3f2331, 0xc6a59430, 0x35a266c0, 0x744ebc37, 0xfc82caa6, 0xe090d0b0, 0x33a7d815, 0xf104984a, 0x41ecdaf7, 0x7fcd500e, 0x1791f62f, 0x764dd68d, 0x43efb04d, 0xccaa4d54, 0xe49604df, 0x9ed1b5e3, 0x4c6a881b, 0xc12c1fb8, 0x4665517f, 0x9d5eea04, 0x018c355d, 0xfa877473, 0xfb0b412e, 0xb3671d5a, 0x92dbd252, 0xe9105633, 0x6dd64713, 0x9ad7618c, 0x37a10c7a, 0x59f8148e, 0xeb133c89, 0xcea927ee, 0xb761c935, 0xe11ce5ed, 0x7a47b13c, 0x9cd2df59, 0x55f2733f, 0x1814ce79, 0x73c737bf, 0x53f7cdea, 0x5ffdaa5b, 0xdf3d6f14, 0x7844db86, 0xcaaff381, 0xb968c43e, 0x3824342c, 0xc2a3405f, 0x161dc372, 0xbce2250c, 0x283c498b, 0xff0d9541, 0x39a80171, 0x080cb3de, 0xd8b4e49c, 0x6456c190, 0x7bcb8461, 0xd532b670, 0x486c5c74, 0xd0b85742];
31204             var T6 = [0x5051f4a7, 0x537e4165, 0xc31a17a4, 0x963a275e, 0xcb3bab6b, 0xf11f9d45, 0xabacfa58, 0x934be303, 0x552030fa, 0xf6ad766d, 0x9188cc76, 0x25f5024c, 0xfc4fe5d7, 0xd7c52acb, 0x80263544, 0x8fb562a3, 0x49deb15a, 0x6725ba1b, 0x9845ea0e, 0xe15dfec0, 0x02c32f75, 0x12814cf0, 0xa38d4697, 0xc66bd3f9, 0xe7038f5f, 0x9515929c, 0xebbf6d7a, 0xda955259, 0x2dd4be83, 0xd3587421, 0x2949e069, 0x448ec9c8, 0x6a75c289, 0x78f48e79, 0x6b99583e, 0xdd27b971, 0xb6bee14f, 0x17f088ad, 0x66c920ac, 0xb47dce3a, 0x1863df4a, 0x82e51a31, 0x60975133, 0x4562537f, 0xe0b16477, 0x84bb6bae, 0x1cfe81a0, 0x94f9082b, 0x58704868, 0x198f45fd, 0x8794de6c, 0xb7527bf8, 0x23ab73d3, 0xe2724b02, 0x57e31f8f, 0x2a6655ab, 0x07b2eb28, 0x032fb5c2, 0x9a86c57b, 0xa5d33708, 0xf2302887, 0xb223bfa5, 0xba02036a, 0x5ced1682, 0x2b8acf1c, 0x92a779b4, 0xf0f307f2, 0xa14e69e2, 0xcd65daf4, 0xd50605be, 0x1fd13462, 0x8ac4a6fe, 0x9d342e53, 0xa0a2f355, 0x32058ae1, 0x75a4f6eb, 0x390b83ec, 0xaa4060ef, 0x065e719f, 0x51bd6e10, 0xf93e218a, 0x3d96dd06, 0xaedd3e05, 0x464de6bd, 0xb591548d, 0x0571c45d, 0x6f0406d4, 0xff605015, 0x241998fb, 0x97d6bde9, 0xcc894043, 0x7767d99e, 0xbdb0e842, 0x8807898b, 0x38e7195b, 0xdb79c8ee, 0x47a17c0a, 0xe97c420f, 0xc9f8841e, 0x00000000, 0x83098086, 0x48322bed, 0xac1e1170, 0x4e6c5a72, 0xfbfd0eff, 0x560f8538, 0x1e3daed5, 0x27362d39, 0x640a0fd9, 0x21685ca6, 0xd19b5b54, 0x3a24362e, 0xb10c0a67, 0x0f9357e7, 0xd2b4ee96, 0x9e1b9b91, 0x4f80c0c5, 0xa261dc20, 0x695a774b, 0x161c121a, 0x0ae293ba, 0xe5c0a02a, 0x433c22e0, 0x1d121b17, 0x0b0e090d, 0xadf28bc7, 0xb92db6a8, 0xc8141ea9, 0x8557f119, 0x4caf7507, 0xbbee99dd, 0xfda37f60, 0x9ff70126, 0xbc5c72f5, 0xc544663b, 0x345bfb7e, 0x768b4329, 0xdccb23c6, 0x68b6edfc, 0x63b8e4f1, 0xcad731dc, 0x10426385, 0x40139722, 0x2084c611, 0x7d854a24, 0xf8d2bb3d, 0x11aef932, 0x6dc729a1, 0x4b1d9e2f, 0xf3dcb230, 0xec0d8652, 0xd077c1e3, 0x6c2bb316, 0x99a970b9, 0xfa119448, 0x2247e964, 0xc4a8fc8c, 0x1aa0f03f, 0xd8567d2c, 0xef223390, 0xc787494e, 0xc1d938d1, 0xfe8ccaa2, 0x3698d40b, 0xcfa6f581, 0x28a57ade, 0x26dab78e, 0xa43fadbf, 0xe42c3a9d, 0x0d507892, 0x9b6a5fcc, 0x62547e46, 0xc2f68d13, 0xe890d8b8, 0x5e2e39f7, 0xf582c3af, 0xbe9f5d80, 0x7c69d093, 0xa96fd52d, 0xb3cf2512, 0x3bc8ac99, 0xa710187d, 0x6ee89c63, 0x7bdb3bbb, 0x09cd2678, 0xf46e5918, 0x01ec9ab7, 0xa8834f9a, 0x65e6956e, 0x7eaaffe6, 0x0821bccf, 0xe6ef15e8, 0xd9bae79b, 0xce4a6f36, 0xd4ea9f09, 0xd629b07c, 0xaf31a4b2, 0x312a3f23, 0x30c6a594, 0xc035a266, 0x37744ebc, 0xa6fc82ca, 0xb0e090d0, 0x1533a7d8, 0x4af10498, 0xf741ecda, 0x0e7fcd50, 0x2f1791f6, 0x8d764dd6, 0x4d43efb0, 0x54ccaa4d, 0xdfe49604, 0xe39ed1b5, 0x1b4c6a88, 0xb8c12c1f, 0x7f466551, 0x049d5eea, 0x5d018c35, 0x73fa8774, 0x2efb0b41, 0x5ab3671d, 0x5292dbd2, 0x33e91056, 0x136dd647, 0x8c9ad761, 0x7a37a10c, 0x8e59f814, 0x89eb133c, 0xeecea927, 0x35b761c9, 0xede11ce5, 0x3c7a47b1, 0x599cd2df, 0x3f55f273, 0x791814ce, 0xbf73c737, 0xea53f7cd, 0x5b5ffdaa, 0x14df3d6f, 0x867844db, 0x81caaff3, 0x3eb968c4, 0x2c382434, 0x5fc2a340, 0x72161dc3, 0x0cbce225, 0x8b283c49, 0x41ff0d95, 0x7139a801, 0xde080cb3, 0x9cd8b4e4, 0x906456c1, 0x617bcb84, 0x70d532b6, 0x74486c5c, 0x42d0b857];
31205             var T7 = [0xa75051f4, 0x65537e41, 0xa4c31a17, 0x5e963a27, 0x6bcb3bab, 0x45f11f9d, 0x58abacfa, 0x03934be3, 0xfa552030, 0x6df6ad76, 0x769188cc, 0x4c25f502, 0xd7fc4fe5, 0xcbd7c52a, 0x44802635, 0xa38fb562, 0x5a49deb1, 0x1b6725ba, 0x0e9845ea, 0xc0e15dfe, 0x7502c32f, 0xf012814c, 0x97a38d46, 0xf9c66bd3, 0x5fe7038f, 0x9c951592, 0x7aebbf6d, 0x59da9552, 0x832dd4be, 0x21d35874, 0x692949e0, 0xc8448ec9, 0x896a75c2, 0x7978f48e, 0x3e6b9958, 0x71dd27b9, 0x4fb6bee1, 0xad17f088, 0xac66c920, 0x3ab47dce, 0x4a1863df, 0x3182e51a, 0x33609751, 0x7f456253, 0x77e0b164, 0xae84bb6b, 0xa01cfe81, 0x2b94f908, 0x68587048, 0xfd198f45, 0x6c8794de, 0xf8b7527b, 0xd323ab73, 0x02e2724b, 0x8f57e31f, 0xab2a6655, 0x2807b2eb, 0xc2032fb5, 0x7b9a86c5, 0x08a5d337, 0x87f23028, 0xa5b223bf, 0x6aba0203, 0x825ced16, 0x1c2b8acf, 0xb492a779, 0xf2f0f307, 0xe2a14e69, 0xf4cd65da, 0xbed50605, 0x621fd134, 0xfe8ac4a6, 0x539d342e, 0x55a0a2f3, 0xe132058a, 0xeb75a4f6, 0xec390b83, 0xefaa4060, 0x9f065e71, 0x1051bd6e, 0x8af93e21, 0x063d96dd, 0x05aedd3e, 0xbd464de6, 0x8db59154, 0x5d0571c4, 0xd46f0406, 0x15ff6050, 0xfb241998, 0xe997d6bd, 0x43cc8940, 0x9e7767d9, 0x42bdb0e8, 0x8b880789, 0x5b38e719, 0xeedb79c8, 0x0a47a17c, 0x0fe97c42, 0x1ec9f884, 0x00000000, 0x86830980, 0xed48322b, 0x70ac1e11, 0x724e6c5a, 0xfffbfd0e, 0x38560f85, 0xd51e3dae, 0x3927362d, 0xd9640a0f, 0xa621685c, 0x54d19b5b, 0x2e3a2436, 0x67b10c0a, 0xe70f9357, 0x96d2b4ee, 0x919e1b9b, 0xc54f80c0, 0x20a261dc, 0x4b695a77, 0x1a161c12, 0xba0ae293, 0x2ae5c0a0, 0xe0433c22, 0x171d121b, 0x0d0b0e09, 0xc7adf28b, 0xa8b92db6, 0xa9c8141e, 0x198557f1, 0x074caf75, 0xddbbee99, 0x60fda37f, 0x269ff701, 0xf5bc5c72, 0x3bc54466, 0x7e345bfb, 0x29768b43, 0xc6dccb23, 0xfc68b6ed, 0xf163b8e4, 0xdccad731, 0x85104263, 0x22401397, 0x112084c6, 0x247d854a, 0x3df8d2bb, 0x3211aef9, 0xa16dc729, 0x2f4b1d9e, 0x30f3dcb2, 0x52ec0d86, 0xe3d077c1, 0x166c2bb3, 0xb999a970, 0x48fa1194, 0x642247e9, 0x8cc4a8fc, 0x3f1aa0f0, 0x2cd8567d, 0x90ef2233, 0x4ec78749, 0xd1c1d938, 0xa2fe8cca, 0x0b3698d4, 0x81cfa6f5, 0xde28a57a, 0x8e26dab7, 0xbfa43fad, 0x9de42c3a, 0x920d5078, 0xcc9b6a5f, 0x4662547e, 0x13c2f68d, 0xb8e890d8, 0xf75e2e39, 0xaff582c3, 0x80be9f5d, 0x937c69d0, 0x2da96fd5, 0x12b3cf25, 0x993bc8ac, 0x7da71018, 0x636ee89c, 0xbb7bdb3b, 0x7809cd26, 0x18f46e59, 0xb701ec9a, 0x9aa8834f, 0x6e65e695, 0xe67eaaff, 0xcf0821bc, 0xe8e6ef15, 0x9bd9bae7, 0x36ce4a6f, 0x09d4ea9f, 0x7cd629b0, 0xb2af31a4, 0x23312a3f, 0x9430c6a5, 0x66c035a2, 0xbc37744e, 0xcaa6fc82, 0xd0b0e090, 0xd81533a7, 0x984af104, 0xdaf741ec, 0x500e7fcd, 0xf62f1791, 0xd68d764d, 0xb04d43ef, 0x4d54ccaa, 0x04dfe496, 0xb5e39ed1, 0x881b4c6a, 0x1fb8c12c, 0x517f4665, 0xea049d5e, 0x355d018c, 0x7473fa87, 0x412efb0b, 0x1d5ab367, 0xd25292db, 0x5633e910, 0x47136dd6, 0x618c9ad7, 0x0c7a37a1, 0x148e59f8, 0x3c89eb13, 0x27eecea9, 0xc935b761, 0xe5ede11c, 0xb13c7a47, 0xdf599cd2, 0x733f55f2, 0xce791814, 0x37bf73c7, 0xcdea53f7, 0xaa5b5ffd, 0x6f14df3d, 0xdb867844, 0xf381caaf, 0xc43eb968, 0x342c3824, 0x405fc2a3, 0xc372161d, 0x250cbce2, 0x498b283c, 0x9541ff0d, 0x017139a8, 0xb3de080c, 0xe49cd8b4, 0xc1906456, 0x84617bcb, 0xb670d532, 0x5c74486c, 0x5742d0b8];
31206             var T8 = [0xf4a75051, 0x4165537e, 0x17a4c31a, 0x275e963a, 0xab6bcb3b, 0x9d45f11f, 0xfa58abac, 0xe303934b, 0x30fa5520, 0x766df6ad, 0xcc769188, 0x024c25f5, 0xe5d7fc4f, 0x2acbd7c5, 0x35448026, 0x62a38fb5, 0xb15a49de, 0xba1b6725, 0xea0e9845, 0xfec0e15d, 0x2f7502c3, 0x4cf01281, 0x4697a38d, 0xd3f9c66b, 0x8f5fe703, 0x929c9515, 0x6d7aebbf, 0x5259da95, 0xbe832dd4, 0x7421d358, 0xe0692949, 0xc9c8448e, 0xc2896a75, 0x8e7978f4, 0x583e6b99, 0xb971dd27, 0xe14fb6be, 0x88ad17f0, 0x20ac66c9, 0xce3ab47d, 0xdf4a1863, 0x1a3182e5, 0x51336097, 0x537f4562, 0x6477e0b1, 0x6bae84bb, 0x81a01cfe, 0x082b94f9, 0x48685870, 0x45fd198f, 0xde6c8794, 0x7bf8b752, 0x73d323ab, 0x4b02e272, 0x1f8f57e3, 0x55ab2a66, 0xeb2807b2, 0xb5c2032f, 0xc57b9a86, 0x3708a5d3, 0x2887f230, 0xbfa5b223, 0x036aba02, 0x16825ced, 0xcf1c2b8a, 0x79b492a7, 0x07f2f0f3, 0x69e2a14e, 0xdaf4cd65, 0x05bed506, 0x34621fd1, 0xa6fe8ac4, 0x2e539d34, 0xf355a0a2, 0x8ae13205, 0xf6eb75a4, 0x83ec390b, 0x60efaa40, 0x719f065e, 0x6e1051bd, 0x218af93e, 0xdd063d96, 0x3e05aedd, 0xe6bd464d, 0x548db591, 0xc45d0571, 0x06d46f04, 0x5015ff60, 0x98fb2419, 0xbde997d6, 0x4043cc89, 0xd99e7767, 0xe842bdb0, 0x898b8807, 0x195b38e7, 0xc8eedb79, 0x7c0a47a1, 0x420fe97c, 0x841ec9f8, 0x00000000, 0x80868309, 0x2bed4832, 0x1170ac1e, 0x5a724e6c, 0x0efffbfd, 0x8538560f, 0xaed51e3d, 0x2d392736, 0x0fd9640a, 0x5ca62168, 0x5b54d19b, 0x362e3a24, 0x0a67b10c, 0x57e70f93, 0xee96d2b4, 0x9b919e1b, 0xc0c54f80, 0xdc20a261, 0x774b695a, 0x121a161c, 0x93ba0ae2, 0xa02ae5c0, 0x22e0433c, 0x1b171d12, 0x090d0b0e, 0x8bc7adf2, 0xb6a8b92d, 0x1ea9c814, 0xf1198557, 0x75074caf, 0x99ddbbee, 0x7f60fda3, 0x01269ff7, 0x72f5bc5c, 0x663bc544, 0xfb7e345b, 0x4329768b, 0x23c6dccb, 0xedfc68b6, 0xe4f163b8, 0x31dccad7, 0x63851042, 0x97224013, 0xc6112084, 0x4a247d85, 0xbb3df8d2, 0xf93211ae, 0x29a16dc7, 0x9e2f4b1d, 0xb230f3dc, 0x8652ec0d, 0xc1e3d077, 0xb3166c2b, 0x70b999a9, 0x9448fa11, 0xe9642247, 0xfc8cc4a8, 0xf03f1aa0, 0x7d2cd856, 0x3390ef22, 0x494ec787, 0x38d1c1d9, 0xcaa2fe8c, 0xd40b3698, 0xf581cfa6, 0x7ade28a5, 0xb78e26da, 0xadbfa43f, 0x3a9de42c, 0x78920d50, 0x5fcc9b6a, 0x7e466254, 0x8d13c2f6, 0xd8b8e890, 0x39f75e2e, 0xc3aff582, 0x5d80be9f, 0xd0937c69, 0xd52da96f, 0x2512b3cf, 0xac993bc8, 0x187da710, 0x9c636ee8, 0x3bbb7bdb, 0x267809cd, 0x5918f46e, 0x9ab701ec, 0x4f9aa883, 0x956e65e6, 0xffe67eaa, 0xbccf0821, 0x15e8e6ef, 0xe79bd9ba, 0x6f36ce4a, 0x9f09d4ea, 0xb07cd629, 0xa4b2af31, 0x3f23312a, 0xa59430c6, 0xa266c035, 0x4ebc3774, 0x82caa6fc, 0x90d0b0e0, 0xa7d81533, 0x04984af1, 0xecdaf741, 0xcd500e7f, 0x91f62f17, 0x4dd68d76, 0xefb04d43, 0xaa4d54cc, 0x9604dfe4, 0xd1b5e39e, 0x6a881b4c, 0x2c1fb8c1, 0x65517f46, 0x5eea049d, 0x8c355d01, 0x877473fa, 0x0b412efb, 0x671d5ab3, 0xdbd25292, 0x105633e9, 0xd647136d, 0xd7618c9a, 0xa10c7a37, 0xf8148e59, 0x133c89eb, 0xa927eece, 0x61c935b7, 0x1ce5ede1, 0x47b13c7a, 0xd2df599c, 0xf2733f55, 0x14ce7918, 0xc737bf73, 0xf7cdea53, 0xfdaa5b5f, 0x3d6f14df, 0x44db8678, 0xaff381ca, 0x68c43eb9, 0x24342c38, 0xa3405fc2, 0x1dc37216, 0xe2250cbc, 0x3c498b28, 0x0d9541ff, 0xa8017139, 0x0cb3de08, 0xb4e49cd8, 0x56c19064, 0xcb84617b, 0x32b670d5, 0x6c5c7448, 0xb85742d0]; // Transformations for decryption key expansion
31207
31208             var U1 = [0x00000000, 0x0e090d0b, 0x1c121a16, 0x121b171d, 0x3824342c, 0x362d3927, 0x24362e3a, 0x2a3f2331, 0x70486858, 0x7e416553, 0x6c5a724e, 0x62537f45, 0x486c5c74, 0x4665517f, 0x547e4662, 0x5a774b69, 0xe090d0b0, 0xee99ddbb, 0xfc82caa6, 0xf28bc7ad, 0xd8b4e49c, 0xd6bde997, 0xc4a6fe8a, 0xcaaff381, 0x90d8b8e8, 0x9ed1b5e3, 0x8ccaa2fe, 0x82c3aff5, 0xa8fc8cc4, 0xa6f581cf, 0xb4ee96d2, 0xbae79bd9, 0xdb3bbb7b, 0xd532b670, 0xc729a16d, 0xc920ac66, 0xe31f8f57, 0xed16825c, 0xff0d9541, 0xf104984a, 0xab73d323, 0xa57ade28, 0xb761c935, 0xb968c43e, 0x9357e70f, 0x9d5eea04, 0x8f45fd19, 0x814cf012, 0x3bab6bcb, 0x35a266c0, 0x27b971dd, 0x29b07cd6, 0x038f5fe7, 0x0d8652ec, 0x1f9d45f1, 0x119448fa, 0x4be30393, 0x45ea0e98, 0x57f11985, 0x59f8148e, 0x73c737bf, 0x7dce3ab4, 0x6fd52da9, 0x61dc20a2, 0xad766df6, 0xa37f60fd, 0xb16477e0, 0xbf6d7aeb, 0x955259da, 0x9b5b54d1, 0x894043cc, 0x87494ec7, 0xdd3e05ae, 0xd33708a5, 0xc12c1fb8, 0xcf2512b3, 0xe51a3182, 0xeb133c89, 0xf9082b94, 0xf701269f, 0x4de6bd46, 0x43efb04d, 0x51f4a750, 0x5ffdaa5b, 0x75c2896a, 0x7bcb8461, 0x69d0937c, 0x67d99e77, 0x3daed51e, 0x33a7d815, 0x21bccf08, 0x2fb5c203, 0x058ae132, 0x0b83ec39, 0x1998fb24, 0x1791f62f, 0x764dd68d, 0x7844db86, 0x6a5fcc9b, 0x6456c190, 0x4e69e2a1, 0x4060efaa, 0x527bf8b7, 0x5c72f5bc, 0x0605bed5, 0x080cb3de, 0x1a17a4c3, 0x141ea9c8, 0x3e218af9, 0x302887f2, 0x223390ef, 0x2c3a9de4, 0x96dd063d, 0x98d40b36, 0x8acf1c2b, 0x84c61120, 0xaef93211, 0xa0f03f1a, 0xb2eb2807, 0xbce2250c, 0xe6956e65, 0xe89c636e, 0xfa877473, 0xf48e7978, 0xdeb15a49, 0xd0b85742, 0xc2a3405f, 0xccaa4d54, 0x41ecdaf7, 0x4fe5d7fc, 0x5dfec0e1, 0x53f7cdea, 0x79c8eedb, 0x77c1e3d0, 0x65daf4cd, 0x6bd3f9c6, 0x31a4b2af, 0x3fadbfa4, 0x2db6a8b9, 0x23bfa5b2, 0x09808683, 0x07898b88, 0x15929c95, 0x1b9b919e, 0xa17c0a47, 0xaf75074c, 0xbd6e1051, 0xb3671d5a, 0x99583e6b, 0x97513360, 0x854a247d, 0x8b432976, 0xd134621f, 0xdf3d6f14, 0xcd267809, 0xc32f7502, 0xe9105633, 0xe7195b38, 0xf5024c25, 0xfb0b412e, 0x9ad7618c, 0x94de6c87, 0x86c57b9a, 0x88cc7691, 0xa2f355a0, 0xacfa58ab, 0xbee14fb6, 0xb0e842bd, 0xea9f09d4, 0xe49604df, 0xf68d13c2, 0xf8841ec9, 0xd2bb3df8, 0xdcb230f3, 0xcea927ee, 0xc0a02ae5, 0x7a47b13c, 0x744ebc37, 0x6655ab2a, 0x685ca621, 0x42638510, 0x4c6a881b, 0x5e719f06, 0x5078920d, 0x0a0fd964, 0x0406d46f, 0x161dc372, 0x1814ce79, 0x322bed48, 0x3c22e043, 0x2e39f75e, 0x2030fa55, 0xec9ab701, 0xe293ba0a, 0xf088ad17, 0xfe81a01c, 0xd4be832d, 0xdab78e26, 0xc8ac993b, 0xc6a59430, 0x9cd2df59, 0x92dbd252, 0x80c0c54f, 0x8ec9c844, 0xa4f6eb75, 0xaaffe67e, 0xb8e4f163, 0xb6edfc68, 0x0c0a67b1, 0x02036aba, 0x10187da7, 0x1e1170ac, 0x342e539d, 0x3a275e96, 0x283c498b, 0x26354480, 0x7c420fe9, 0x724b02e2, 0x605015ff, 0x6e5918f4, 0x44663bc5, 0x4a6f36ce, 0x587421d3, 0x567d2cd8, 0x37a10c7a, 0x39a80171, 0x2bb3166c, 0x25ba1b67, 0x0f853856, 0x018c355d, 0x13972240, 0x1d9e2f4b, 0x47e96422, 0x49e06929, 0x5bfb7e34, 0x55f2733f, 0x7fcd500e, 0x71c45d05, 0x63df4a18, 0x6dd64713, 0xd731dcca, 0xd938d1c1, 0xcb23c6dc, 0xc52acbd7, 0xef15e8e6, 0xe11ce5ed, 0xf307f2f0, 0xfd0efffb, 0xa779b492, 0xa970b999, 0xbb6bae84, 0xb562a38f, 0x9f5d80be, 0x91548db5, 0x834f9aa8, 0x8d4697a3];
31209             var U2 = [0x00000000, 0x0b0e090d, 0x161c121a, 0x1d121b17, 0x2c382434, 0x27362d39, 0x3a24362e, 0x312a3f23, 0x58704868, 0x537e4165, 0x4e6c5a72, 0x4562537f, 0x74486c5c, 0x7f466551, 0x62547e46, 0x695a774b, 0xb0e090d0, 0xbbee99dd, 0xa6fc82ca, 0xadf28bc7, 0x9cd8b4e4, 0x97d6bde9, 0x8ac4a6fe, 0x81caaff3, 0xe890d8b8, 0xe39ed1b5, 0xfe8ccaa2, 0xf582c3af, 0xc4a8fc8c, 0xcfa6f581, 0xd2b4ee96, 0xd9bae79b, 0x7bdb3bbb, 0x70d532b6, 0x6dc729a1, 0x66c920ac, 0x57e31f8f, 0x5ced1682, 0x41ff0d95, 0x4af10498, 0x23ab73d3, 0x28a57ade, 0x35b761c9, 0x3eb968c4, 0x0f9357e7, 0x049d5eea, 0x198f45fd, 0x12814cf0, 0xcb3bab6b, 0xc035a266, 0xdd27b971, 0xd629b07c, 0xe7038f5f, 0xec0d8652, 0xf11f9d45, 0xfa119448, 0x934be303, 0x9845ea0e, 0x8557f119, 0x8e59f814, 0xbf73c737, 0xb47dce3a, 0xa96fd52d, 0xa261dc20, 0xf6ad766d, 0xfda37f60, 0xe0b16477, 0xebbf6d7a, 0xda955259, 0xd19b5b54, 0xcc894043, 0xc787494e, 0xaedd3e05, 0xa5d33708, 0xb8c12c1f, 0xb3cf2512, 0x82e51a31, 0x89eb133c, 0x94f9082b, 0x9ff70126, 0x464de6bd, 0x4d43efb0, 0x5051f4a7, 0x5b5ffdaa, 0x6a75c289, 0x617bcb84, 0x7c69d093, 0x7767d99e, 0x1e3daed5, 0x1533a7d8, 0x0821bccf, 0x032fb5c2, 0x32058ae1, 0x390b83ec, 0x241998fb, 0x2f1791f6, 0x8d764dd6, 0x867844db, 0x9b6a5fcc, 0x906456c1, 0xa14e69e2, 0xaa4060ef, 0xb7527bf8, 0xbc5c72f5, 0xd50605be, 0xde080cb3, 0xc31a17a4, 0xc8141ea9, 0xf93e218a, 0xf2302887, 0xef223390, 0xe42c3a9d, 0x3d96dd06, 0x3698d40b, 0x2b8acf1c, 0x2084c611, 0x11aef932, 0x1aa0f03f, 0x07b2eb28, 0x0cbce225, 0x65e6956e, 0x6ee89c63, 0x73fa8774, 0x78f48e79, 0x49deb15a, 0x42d0b857, 0x5fc2a340, 0x54ccaa4d, 0xf741ecda, 0xfc4fe5d7, 0xe15dfec0, 0xea53f7cd, 0xdb79c8ee, 0xd077c1e3, 0xcd65daf4, 0xc66bd3f9, 0xaf31a4b2, 0xa43fadbf, 0xb92db6a8, 0xb223bfa5, 0x83098086, 0x8807898b, 0x9515929c, 0x9e1b9b91, 0x47a17c0a, 0x4caf7507, 0x51bd6e10, 0x5ab3671d, 0x6b99583e, 0x60975133, 0x7d854a24, 0x768b4329, 0x1fd13462, 0x14df3d6f, 0x09cd2678, 0x02c32f75, 0x33e91056, 0x38e7195b, 0x25f5024c, 0x2efb0b41, 0x8c9ad761, 0x8794de6c, 0x9a86c57b, 0x9188cc76, 0xa0a2f355, 0xabacfa58, 0xb6bee14f, 0xbdb0e842, 0xd4ea9f09, 0xdfe49604, 0xc2f68d13, 0xc9f8841e, 0xf8d2bb3d, 0xf3dcb230, 0xeecea927, 0xe5c0a02a, 0x3c7a47b1, 0x37744ebc, 0x2a6655ab, 0x21685ca6, 0x10426385, 0x1b4c6a88, 0x065e719f, 0x0d507892, 0x640a0fd9, 0x6f0406d4, 0x72161dc3, 0x791814ce, 0x48322bed, 0x433c22e0, 0x5e2e39f7, 0x552030fa, 0x01ec9ab7, 0x0ae293ba, 0x17f088ad, 0x1cfe81a0, 0x2dd4be83, 0x26dab78e, 0x3bc8ac99, 0x30c6a594, 0x599cd2df, 0x5292dbd2, 0x4f80c0c5, 0x448ec9c8, 0x75a4f6eb, 0x7eaaffe6, 0x63b8e4f1, 0x68b6edfc, 0xb10c0a67, 0xba02036a, 0xa710187d, 0xac1e1170, 0x9d342e53, 0x963a275e, 0x8b283c49, 0x80263544, 0xe97c420f, 0xe2724b02, 0xff605015, 0xf46e5918, 0xc544663b, 0xce4a6f36, 0xd3587421, 0xd8567d2c, 0x7a37a10c, 0x7139a801, 0x6c2bb316, 0x6725ba1b, 0x560f8538, 0x5d018c35, 0x40139722, 0x4b1d9e2f, 0x2247e964, 0x2949e069, 0x345bfb7e, 0x3f55f273, 0x0e7fcd50, 0x0571c45d, 0x1863df4a, 0x136dd647, 0xcad731dc, 0xc1d938d1, 0xdccb23c6, 0xd7c52acb, 0xe6ef15e8, 0xede11ce5, 0xf0f307f2, 0xfbfd0eff, 0x92a779b4, 0x99a970b9, 0x84bb6bae, 0x8fb562a3, 0xbe9f5d80, 0xb591548d, 0xa8834f9a, 0xa38d4697];
31210             var U3 = [0x00000000, 0x0d0b0e09, 0x1a161c12, 0x171d121b, 0x342c3824, 0x3927362d, 0x2e3a2436, 0x23312a3f, 0x68587048, 0x65537e41, 0x724e6c5a, 0x7f456253, 0x5c74486c, 0x517f4665, 0x4662547e, 0x4b695a77, 0xd0b0e090, 0xddbbee99, 0xcaa6fc82, 0xc7adf28b, 0xe49cd8b4, 0xe997d6bd, 0xfe8ac4a6, 0xf381caaf, 0xb8e890d8, 0xb5e39ed1, 0xa2fe8cca, 0xaff582c3, 0x8cc4a8fc, 0x81cfa6f5, 0x96d2b4ee, 0x9bd9bae7, 0xbb7bdb3b, 0xb670d532, 0xa16dc729, 0xac66c920, 0x8f57e31f, 0x825ced16, 0x9541ff0d, 0x984af104, 0xd323ab73, 0xde28a57a, 0xc935b761, 0xc43eb968, 0xe70f9357, 0xea049d5e, 0xfd198f45, 0xf012814c, 0x6bcb3bab, 0x66c035a2, 0x71dd27b9, 0x7cd629b0, 0x5fe7038f, 0x52ec0d86, 0x45f11f9d, 0x48fa1194, 0x03934be3, 0x0e9845ea, 0x198557f1, 0x148e59f8, 0x37bf73c7, 0x3ab47dce, 0x2da96fd5, 0x20a261dc, 0x6df6ad76, 0x60fda37f, 0x77e0b164, 0x7aebbf6d, 0x59da9552, 0x54d19b5b, 0x43cc8940, 0x4ec78749, 0x05aedd3e, 0x08a5d337, 0x1fb8c12c, 0x12b3cf25, 0x3182e51a, 0x3c89eb13, 0x2b94f908, 0x269ff701, 0xbd464de6, 0xb04d43ef, 0xa75051f4, 0xaa5b5ffd, 0x896a75c2, 0x84617bcb, 0x937c69d0, 0x9e7767d9, 0xd51e3dae, 0xd81533a7, 0xcf0821bc, 0xc2032fb5, 0xe132058a, 0xec390b83, 0xfb241998, 0xf62f1791, 0xd68d764d, 0xdb867844, 0xcc9b6a5f, 0xc1906456, 0xe2a14e69, 0xefaa4060, 0xf8b7527b, 0xf5bc5c72, 0xbed50605, 0xb3de080c, 0xa4c31a17, 0xa9c8141e, 0x8af93e21, 0x87f23028, 0x90ef2233, 0x9de42c3a, 0x063d96dd, 0x0b3698d4, 0x1c2b8acf, 0x112084c6, 0x3211aef9, 0x3f1aa0f0, 0x2807b2eb, 0x250cbce2, 0x6e65e695, 0x636ee89c, 0x7473fa87, 0x7978f48e, 0x5a49deb1, 0x5742d0b8, 0x405fc2a3, 0x4d54ccaa, 0xdaf741ec, 0xd7fc4fe5, 0xc0e15dfe, 0xcdea53f7, 0xeedb79c8, 0xe3d077c1, 0xf4cd65da, 0xf9c66bd3, 0xb2af31a4, 0xbfa43fad, 0xa8b92db6, 0xa5b223bf, 0x86830980, 0x8b880789, 0x9c951592, 0x919e1b9b, 0x0a47a17c, 0x074caf75, 0x1051bd6e, 0x1d5ab367, 0x3e6b9958, 0x33609751, 0x247d854a, 0x29768b43, 0x621fd134, 0x6f14df3d, 0x7809cd26, 0x7502c32f, 0x5633e910, 0x5b38e719, 0x4c25f502, 0x412efb0b, 0x618c9ad7, 0x6c8794de, 0x7b9a86c5, 0x769188cc, 0x55a0a2f3, 0x58abacfa, 0x4fb6bee1, 0x42bdb0e8, 0x09d4ea9f, 0x04dfe496, 0x13c2f68d, 0x1ec9f884, 0x3df8d2bb, 0x30f3dcb2, 0x27eecea9, 0x2ae5c0a0, 0xb13c7a47, 0xbc37744e, 0xab2a6655, 0xa621685c, 0x85104263, 0x881b4c6a, 0x9f065e71, 0x920d5078, 0xd9640a0f, 0xd46f0406, 0xc372161d, 0xce791814, 0xed48322b, 0xe0433c22, 0xf75e2e39, 0xfa552030, 0xb701ec9a, 0xba0ae293, 0xad17f088, 0xa01cfe81, 0x832dd4be, 0x8e26dab7, 0x993bc8ac, 0x9430c6a5, 0xdf599cd2, 0xd25292db, 0xc54f80c0, 0xc8448ec9, 0xeb75a4f6, 0xe67eaaff, 0xf163b8e4, 0xfc68b6ed, 0x67b10c0a, 0x6aba0203, 0x7da71018, 0x70ac1e11, 0x539d342e, 0x5e963a27, 0x498b283c, 0x44802635, 0x0fe97c42, 0x02e2724b, 0x15ff6050, 0x18f46e59, 0x3bc54466, 0x36ce4a6f, 0x21d35874, 0x2cd8567d, 0x0c7a37a1, 0x017139a8, 0x166c2bb3, 0x1b6725ba, 0x38560f85, 0x355d018c, 0x22401397, 0x2f4b1d9e, 0x642247e9, 0x692949e0, 0x7e345bfb, 0x733f55f2, 0x500e7fcd, 0x5d0571c4, 0x4a1863df, 0x47136dd6, 0xdccad731, 0xd1c1d938, 0xc6dccb23, 0xcbd7c52a, 0xe8e6ef15, 0xe5ede11c, 0xf2f0f307, 0xfffbfd0e, 0xb492a779, 0xb999a970, 0xae84bb6b, 0xa38fb562, 0x80be9f5d, 0x8db59154, 0x9aa8834f, 0x97a38d46];
31211             var U4 = [0x00000000, 0x090d0b0e, 0x121a161c, 0x1b171d12, 0x24342c38, 0x2d392736, 0x362e3a24, 0x3f23312a, 0x48685870, 0x4165537e, 0x5a724e6c, 0x537f4562, 0x6c5c7448, 0x65517f46, 0x7e466254, 0x774b695a, 0x90d0b0e0, 0x99ddbbee, 0x82caa6fc, 0x8bc7adf2, 0xb4e49cd8, 0xbde997d6, 0xa6fe8ac4, 0xaff381ca, 0xd8b8e890, 0xd1b5e39e, 0xcaa2fe8c, 0xc3aff582, 0xfc8cc4a8, 0xf581cfa6, 0xee96d2b4, 0xe79bd9ba, 0x3bbb7bdb, 0x32b670d5, 0x29a16dc7, 0x20ac66c9, 0x1f8f57e3, 0x16825ced, 0x0d9541ff, 0x04984af1, 0x73d323ab, 0x7ade28a5, 0x61c935b7, 0x68c43eb9, 0x57e70f93, 0x5eea049d, 0x45fd198f, 0x4cf01281, 0xab6bcb3b, 0xa266c035, 0xb971dd27, 0xb07cd629, 0x8f5fe703, 0x8652ec0d, 0x9d45f11f, 0x9448fa11, 0xe303934b, 0xea0e9845, 0xf1198557, 0xf8148e59, 0xc737bf73, 0xce3ab47d, 0xd52da96f, 0xdc20a261, 0x766df6ad, 0x7f60fda3, 0x6477e0b1, 0x6d7aebbf, 0x5259da95, 0x5b54d19b, 0x4043cc89, 0x494ec787, 0x3e05aedd, 0x3708a5d3, 0x2c1fb8c1, 0x2512b3cf, 0x1a3182e5, 0x133c89eb, 0x082b94f9, 0x01269ff7, 0xe6bd464d, 0xefb04d43, 0xf4a75051, 0xfdaa5b5f, 0xc2896a75, 0xcb84617b, 0xd0937c69, 0xd99e7767, 0xaed51e3d, 0xa7d81533, 0xbccf0821, 0xb5c2032f, 0x8ae13205, 0x83ec390b, 0x98fb2419, 0x91f62f17, 0x4dd68d76, 0x44db8678, 0x5fcc9b6a, 0x56c19064, 0x69e2a14e, 0x60efaa40, 0x7bf8b752, 0x72f5bc5c, 0x05bed506, 0x0cb3de08, 0x17a4c31a, 0x1ea9c814, 0x218af93e, 0x2887f230, 0x3390ef22, 0x3a9de42c, 0xdd063d96, 0xd40b3698, 0xcf1c2b8a, 0xc6112084, 0xf93211ae, 0xf03f1aa0, 0xeb2807b2, 0xe2250cbc, 0x956e65e6, 0x9c636ee8, 0x877473fa, 0x8e7978f4, 0xb15a49de, 0xb85742d0, 0xa3405fc2, 0xaa4d54cc, 0xecdaf741, 0xe5d7fc4f, 0xfec0e15d, 0xf7cdea53, 0xc8eedb79, 0xc1e3d077, 0xdaf4cd65, 0xd3f9c66b, 0xa4b2af31, 0xadbfa43f, 0xb6a8b92d, 0xbfa5b223, 0x80868309, 0x898b8807, 0x929c9515, 0x9b919e1b, 0x7c0a47a1, 0x75074caf, 0x6e1051bd, 0x671d5ab3, 0x583e6b99, 0x51336097, 0x4a247d85, 0x4329768b, 0x34621fd1, 0x3d6f14df, 0x267809cd, 0x2f7502c3, 0x105633e9, 0x195b38e7, 0x024c25f5, 0x0b412efb, 0xd7618c9a, 0xde6c8794, 0xc57b9a86, 0xcc769188, 0xf355a0a2, 0xfa58abac, 0xe14fb6be, 0xe842bdb0, 0x9f09d4ea, 0x9604dfe4, 0x8d13c2f6, 0x841ec9f8, 0xbb3df8d2, 0xb230f3dc, 0xa927eece, 0xa02ae5c0, 0x47b13c7a, 0x4ebc3774, 0x55ab2a66, 0x5ca62168, 0x63851042, 0x6a881b4c, 0x719f065e, 0x78920d50, 0x0fd9640a, 0x06d46f04, 0x1dc37216, 0x14ce7918, 0x2bed4832, 0x22e0433c, 0x39f75e2e, 0x30fa5520, 0x9ab701ec, 0x93ba0ae2, 0x88ad17f0, 0x81a01cfe, 0xbe832dd4, 0xb78e26da, 0xac993bc8, 0xa59430c6, 0xd2df599c, 0xdbd25292, 0xc0c54f80, 0xc9c8448e, 0xf6eb75a4, 0xffe67eaa, 0xe4f163b8, 0xedfc68b6, 0x0a67b10c, 0x036aba02, 0x187da710, 0x1170ac1e, 0x2e539d34, 0x275e963a, 0x3c498b28, 0x35448026, 0x420fe97c, 0x4b02e272, 0x5015ff60, 0x5918f46e, 0x663bc544, 0x6f36ce4a, 0x7421d358, 0x7d2cd856, 0xa10c7a37, 0xa8017139, 0xb3166c2b, 0xba1b6725, 0x8538560f, 0x8c355d01, 0x97224013, 0x9e2f4b1d, 0xe9642247, 0xe0692949, 0xfb7e345b, 0xf2733f55, 0xcd500e7f, 0xc45d0571, 0xdf4a1863, 0xd647136d, 0x31dccad7, 0x38d1c1d9, 0x23c6dccb, 0x2acbd7c5, 0x15e8e6ef, 0x1ce5ede1, 0x07f2f0f3, 0x0efffbfd, 0x79b492a7, 0x70b999a9, 0x6bae84bb, 0x62a38fb5, 0x5d80be9f, 0x548db591, 0x4f9aa883, 0x4697a38d];
31212
31213             function convertToInt32(bytes) {
31214               var result = [];
31215
31216               for (var i = 0; i < bytes.length; i += 4) {
31217                 result.push(bytes[i] << 24 | bytes[i + 1] << 16 | bytes[i + 2] << 8 | bytes[i + 3]);
31218               }
31219
31220               return result;
31221             }
31222
31223             var AES = function AES(key) {
31224               if (!(this instanceof AES)) {
31225                 throw Error('AES must be instanitated with `new`');
31226               }
31227
31228               Object.defineProperty(this, 'key', {
31229                 value: coerceArray(key, true)
31230               });
31231
31232               this._prepare();
31233             };
31234
31235             AES.prototype._prepare = function () {
31236               var rounds = numberOfRounds[this.key.length];
31237
31238               if (rounds == null) {
31239                 throw new Error('invalid key size (must be 16, 24 or 32 bytes)');
31240               } // encryption round keys
31241
31242
31243               this._Ke = []; // decryption round keys
31244
31245               this._Kd = [];
31246
31247               for (var i = 0; i <= rounds; i++) {
31248                 this._Ke.push([0, 0, 0, 0]);
31249
31250                 this._Kd.push([0, 0, 0, 0]);
31251               }
31252
31253               var roundKeyCount = (rounds + 1) * 4;
31254               var KC = this.key.length / 4; // convert the key into ints
31255
31256               var tk = convertToInt32(this.key); // copy values into round key arrays
31257
31258               var index;
31259
31260               for (var i = 0; i < KC; i++) {
31261                 index = i >> 2;
31262                 this._Ke[index][i % 4] = tk[i];
31263                 this._Kd[rounds - index][i % 4] = tk[i];
31264               } // key expansion (fips-197 section 5.2)
31265
31266
31267               var rconpointer = 0;
31268               var t = KC,
31269                   tt;
31270
31271               while (t < roundKeyCount) {
31272                 tt = tk[KC - 1];
31273                 tk[0] ^= S[tt >> 16 & 0xFF] << 24 ^ S[tt >> 8 & 0xFF] << 16 ^ S[tt & 0xFF] << 8 ^ S[tt >> 24 & 0xFF] ^ rcon[rconpointer] << 24;
31274                 rconpointer += 1; // key expansion (for non-256 bit)
31275
31276                 if (KC != 8) {
31277                   for (var i = 1; i < KC; i++) {
31278                     tk[i] ^= tk[i - 1];
31279                   } // key expansion for 256-bit keys is "slightly different" (fips-197)
31280
31281                 } else {
31282                   for (var i = 1; i < KC / 2; i++) {
31283                     tk[i] ^= tk[i - 1];
31284                   }
31285
31286                   tt = tk[KC / 2 - 1];
31287                   tk[KC / 2] ^= S[tt & 0xFF] ^ S[tt >> 8 & 0xFF] << 8 ^ S[tt >> 16 & 0xFF] << 16 ^ S[tt >> 24 & 0xFF] << 24;
31288
31289                   for (var i = KC / 2 + 1; i < KC; i++) {
31290                     tk[i] ^= tk[i - 1];
31291                   }
31292                 } // copy values into round key arrays
31293
31294
31295                 var i = 0,
31296                     r,
31297                     c;
31298
31299                 while (i < KC && t < roundKeyCount) {
31300                   r = t >> 2;
31301                   c = t % 4;
31302                   this._Ke[r][c] = tk[i];
31303                   this._Kd[rounds - r][c] = tk[i++];
31304                   t++;
31305                 }
31306               } // inverse-cipher-ify the decryption round key (fips-197 section 5.3)
31307
31308
31309               for (var r = 1; r < rounds; r++) {
31310                 for (var c = 0; c < 4; c++) {
31311                   tt = this._Kd[r][c];
31312                   this._Kd[r][c] = U1[tt >> 24 & 0xFF] ^ U2[tt >> 16 & 0xFF] ^ U3[tt >> 8 & 0xFF] ^ U4[tt & 0xFF];
31313                 }
31314               }
31315             };
31316
31317             AES.prototype.encrypt = function (plaintext) {
31318               if (plaintext.length != 16) {
31319                 throw new Error('invalid plaintext size (must be 16 bytes)');
31320               }
31321
31322               var rounds = this._Ke.length - 1;
31323               var a = [0, 0, 0, 0]; // convert plaintext to (ints ^ key)
31324
31325               var t = convertToInt32(plaintext);
31326
31327               for (var i = 0; i < 4; i++) {
31328                 t[i] ^= this._Ke[0][i];
31329               } // apply round transforms
31330
31331
31332               for (var r = 1; r < rounds; r++) {
31333                 for (var i = 0; i < 4; i++) {
31334                   a[i] = T1[t[i] >> 24 & 0xff] ^ T2[t[(i + 1) % 4] >> 16 & 0xff] ^ T3[t[(i + 2) % 4] >> 8 & 0xff] ^ T4[t[(i + 3) % 4] & 0xff] ^ this._Ke[r][i];
31335                 }
31336
31337                 t = a.slice();
31338               } // the last round is special
31339
31340
31341               var result = createArray(16),
31342                   tt;
31343
31344               for (var i = 0; i < 4; i++) {
31345                 tt = this._Ke[rounds][i];
31346                 result[4 * i] = (S[t[i] >> 24 & 0xff] ^ tt >> 24) & 0xff;
31347                 result[4 * i + 1] = (S[t[(i + 1) % 4] >> 16 & 0xff] ^ tt >> 16) & 0xff;
31348                 result[4 * i + 2] = (S[t[(i + 2) % 4] >> 8 & 0xff] ^ tt >> 8) & 0xff;
31349                 result[4 * i + 3] = (S[t[(i + 3) % 4] & 0xff] ^ tt) & 0xff;
31350               }
31351
31352               return result;
31353             };
31354
31355             AES.prototype.decrypt = function (ciphertext) {
31356               if (ciphertext.length != 16) {
31357                 throw new Error('invalid ciphertext size (must be 16 bytes)');
31358               }
31359
31360               var rounds = this._Kd.length - 1;
31361               var a = [0, 0, 0, 0]; // convert plaintext to (ints ^ key)
31362
31363               var t = convertToInt32(ciphertext);
31364
31365               for (var i = 0; i < 4; i++) {
31366                 t[i] ^= this._Kd[0][i];
31367               } // apply round transforms
31368
31369
31370               for (var r = 1; r < rounds; r++) {
31371                 for (var i = 0; i < 4; i++) {
31372                   a[i] = T5[t[i] >> 24 & 0xff] ^ T6[t[(i + 3) % 4] >> 16 & 0xff] ^ T7[t[(i + 2) % 4] >> 8 & 0xff] ^ T8[t[(i + 1) % 4] & 0xff] ^ this._Kd[r][i];
31373                 }
31374
31375                 t = a.slice();
31376               } // the last round is special
31377
31378
31379               var result = createArray(16),
31380                   tt;
31381
31382               for (var i = 0; i < 4; i++) {
31383                 tt = this._Kd[rounds][i];
31384                 result[4 * i] = (Si[t[i] >> 24 & 0xff] ^ tt >> 24) & 0xff;
31385                 result[4 * i + 1] = (Si[t[(i + 3) % 4] >> 16 & 0xff] ^ tt >> 16) & 0xff;
31386                 result[4 * i + 2] = (Si[t[(i + 2) % 4] >> 8 & 0xff] ^ tt >> 8) & 0xff;
31387                 result[4 * i + 3] = (Si[t[(i + 1) % 4] & 0xff] ^ tt) & 0xff;
31388               }
31389
31390               return result;
31391             };
31392             /**
31393              *  Mode Of Operation - Electonic Codebook (ECB)
31394              */
31395
31396
31397             var ModeOfOperationECB = function ModeOfOperationECB(key) {
31398               if (!(this instanceof ModeOfOperationECB)) {
31399                 throw Error('AES must be instanitated with `new`');
31400               }
31401
31402               this.description = "Electronic Code Block";
31403               this.name = "ecb";
31404               this._aes = new AES(key);
31405             };
31406
31407             ModeOfOperationECB.prototype.encrypt = function (plaintext) {
31408               plaintext = coerceArray(plaintext);
31409
31410               if (plaintext.length % 16 !== 0) {
31411                 throw new Error('invalid plaintext size (must be multiple of 16 bytes)');
31412               }
31413
31414               var ciphertext = createArray(plaintext.length);
31415               var block = createArray(16);
31416
31417               for (var i = 0; i < plaintext.length; i += 16) {
31418                 copyArray(plaintext, block, 0, i, i + 16);
31419                 block = this._aes.encrypt(block);
31420                 copyArray(block, ciphertext, i);
31421               }
31422
31423               return ciphertext;
31424             };
31425
31426             ModeOfOperationECB.prototype.decrypt = function (ciphertext) {
31427               ciphertext = coerceArray(ciphertext);
31428
31429               if (ciphertext.length % 16 !== 0) {
31430                 throw new Error('invalid ciphertext size (must be multiple of 16 bytes)');
31431               }
31432
31433               var plaintext = createArray(ciphertext.length);
31434               var block = createArray(16);
31435
31436               for (var i = 0; i < ciphertext.length; i += 16) {
31437                 copyArray(ciphertext, block, 0, i, i + 16);
31438                 block = this._aes.decrypt(block);
31439                 copyArray(block, plaintext, i);
31440               }
31441
31442               return plaintext;
31443             };
31444             /**
31445              *  Mode Of Operation - Cipher Block Chaining (CBC)
31446              */
31447
31448
31449             var ModeOfOperationCBC = function ModeOfOperationCBC(key, iv) {
31450               if (!(this instanceof ModeOfOperationCBC)) {
31451                 throw Error('AES must be instanitated with `new`');
31452               }
31453
31454               this.description = "Cipher Block Chaining";
31455               this.name = "cbc";
31456
31457               if (!iv) {
31458                 iv = createArray(16);
31459               } else if (iv.length != 16) {
31460                 throw new Error('invalid initialation vector size (must be 16 bytes)');
31461               }
31462
31463               this._lastCipherblock = coerceArray(iv, true);
31464               this._aes = new AES(key);
31465             };
31466
31467             ModeOfOperationCBC.prototype.encrypt = function (plaintext) {
31468               plaintext = coerceArray(plaintext);
31469
31470               if (plaintext.length % 16 !== 0) {
31471                 throw new Error('invalid plaintext size (must be multiple of 16 bytes)');
31472               }
31473
31474               var ciphertext = createArray(plaintext.length);
31475               var block = createArray(16);
31476
31477               for (var i = 0; i < plaintext.length; i += 16) {
31478                 copyArray(plaintext, block, 0, i, i + 16);
31479
31480                 for (var j = 0; j < 16; j++) {
31481                   block[j] ^= this._lastCipherblock[j];
31482                 }
31483
31484                 this._lastCipherblock = this._aes.encrypt(block);
31485                 copyArray(this._lastCipherblock, ciphertext, i);
31486               }
31487
31488               return ciphertext;
31489             };
31490
31491             ModeOfOperationCBC.prototype.decrypt = function (ciphertext) {
31492               ciphertext = coerceArray(ciphertext);
31493
31494               if (ciphertext.length % 16 !== 0) {
31495                 throw new Error('invalid ciphertext size (must be multiple of 16 bytes)');
31496               }
31497
31498               var plaintext = createArray(ciphertext.length);
31499               var block = createArray(16);
31500
31501               for (var i = 0; i < ciphertext.length; i += 16) {
31502                 copyArray(ciphertext, block, 0, i, i + 16);
31503                 block = this._aes.decrypt(block);
31504
31505                 for (var j = 0; j < 16; j++) {
31506                   plaintext[i + j] = block[j] ^ this._lastCipherblock[j];
31507                 }
31508
31509                 copyArray(ciphertext, this._lastCipherblock, 0, i, i + 16);
31510               }
31511
31512               return plaintext;
31513             };
31514             /**
31515              *  Mode Of Operation - Cipher Feedback (CFB)
31516              */
31517
31518
31519             var ModeOfOperationCFB = function ModeOfOperationCFB(key, iv, segmentSize) {
31520               if (!(this instanceof ModeOfOperationCFB)) {
31521                 throw Error('AES must be instanitated with `new`');
31522               }
31523
31524               this.description = "Cipher Feedback";
31525               this.name = "cfb";
31526
31527               if (!iv) {
31528                 iv = createArray(16);
31529               } else if (iv.length != 16) {
31530                 throw new Error('invalid initialation vector size (must be 16 size)');
31531               }
31532
31533               if (!segmentSize) {
31534                 segmentSize = 1;
31535               }
31536
31537               this.segmentSize = segmentSize;
31538               this._shiftRegister = coerceArray(iv, true);
31539               this._aes = new AES(key);
31540             };
31541
31542             ModeOfOperationCFB.prototype.encrypt = function (plaintext) {
31543               if (plaintext.length % this.segmentSize != 0) {
31544                 throw new Error('invalid plaintext size (must be segmentSize bytes)');
31545               }
31546
31547               var encrypted = coerceArray(plaintext, true);
31548               var xorSegment;
31549
31550               for (var i = 0; i < encrypted.length; i += this.segmentSize) {
31551                 xorSegment = this._aes.encrypt(this._shiftRegister);
31552
31553                 for (var j = 0; j < this.segmentSize; j++) {
31554                   encrypted[i + j] ^= xorSegment[j];
31555                 } // Shift the register
31556
31557
31558                 copyArray(this._shiftRegister, this._shiftRegister, 0, this.segmentSize);
31559                 copyArray(encrypted, this._shiftRegister, 16 - this.segmentSize, i, i + this.segmentSize);
31560               }
31561
31562               return encrypted;
31563             };
31564
31565             ModeOfOperationCFB.prototype.decrypt = function (ciphertext) {
31566               if (ciphertext.length % this.segmentSize != 0) {
31567                 throw new Error('invalid ciphertext size (must be segmentSize bytes)');
31568               }
31569
31570               var plaintext = coerceArray(ciphertext, true);
31571               var xorSegment;
31572
31573               for (var i = 0; i < plaintext.length; i += this.segmentSize) {
31574                 xorSegment = this._aes.encrypt(this._shiftRegister);
31575
31576                 for (var j = 0; j < this.segmentSize; j++) {
31577                   plaintext[i + j] ^= xorSegment[j];
31578                 } // Shift the register
31579
31580
31581                 copyArray(this._shiftRegister, this._shiftRegister, 0, this.segmentSize);
31582                 copyArray(ciphertext, this._shiftRegister, 16 - this.segmentSize, i, i + this.segmentSize);
31583               }
31584
31585               return plaintext;
31586             };
31587             /**
31588              *  Mode Of Operation - Output Feedback (OFB)
31589              */
31590
31591
31592             var ModeOfOperationOFB = function ModeOfOperationOFB(key, iv) {
31593               if (!(this instanceof ModeOfOperationOFB)) {
31594                 throw Error('AES must be instanitated with `new`');
31595               }
31596
31597               this.description = "Output Feedback";
31598               this.name = "ofb";
31599
31600               if (!iv) {
31601                 iv = createArray(16);
31602               } else if (iv.length != 16) {
31603                 throw new Error('invalid initialation vector size (must be 16 bytes)');
31604               }
31605
31606               this._lastPrecipher = coerceArray(iv, true);
31607               this._lastPrecipherIndex = 16;
31608               this._aes = new AES(key);
31609             };
31610
31611             ModeOfOperationOFB.prototype.encrypt = function (plaintext) {
31612               var encrypted = coerceArray(plaintext, true);
31613
31614               for (var i = 0; i < encrypted.length; i++) {
31615                 if (this._lastPrecipherIndex === 16) {
31616                   this._lastPrecipher = this._aes.encrypt(this._lastPrecipher);
31617                   this._lastPrecipherIndex = 0;
31618                 }
31619
31620                 encrypted[i] ^= this._lastPrecipher[this._lastPrecipherIndex++];
31621               }
31622
31623               return encrypted;
31624             }; // Decryption is symetric
31625
31626
31627             ModeOfOperationOFB.prototype.decrypt = ModeOfOperationOFB.prototype.encrypt;
31628             /**
31629              *  Counter object for CTR common mode of operation
31630              */
31631
31632             var Counter = function Counter(initialValue) {
31633               if (!(this instanceof Counter)) {
31634                 throw Error('Counter must be instanitated with `new`');
31635               } // We allow 0, but anything false-ish uses the default 1
31636
31637
31638               if (initialValue !== 0 && !initialValue) {
31639                 initialValue = 1;
31640               }
31641
31642               if (typeof initialValue === 'number') {
31643                 this._counter = createArray(16);
31644                 this.setValue(initialValue);
31645               } else {
31646                 this.setBytes(initialValue);
31647               }
31648             };
31649
31650             Counter.prototype.setValue = function (value) {
31651               if (typeof value !== 'number' || parseInt(value) != value) {
31652                 throw new Error('invalid counter value (must be an integer)');
31653               } // We cannot safely handle numbers beyond the safe range for integers
31654
31655
31656               if (value > Number.MAX_SAFE_INTEGER) {
31657                 throw new Error('integer value out of safe range');
31658               }
31659
31660               for (var index = 15; index >= 0; --index) {
31661                 this._counter[index] = value % 256;
31662                 value = parseInt(value / 256);
31663               }
31664             };
31665
31666             Counter.prototype.setBytes = function (bytes) {
31667               bytes = coerceArray(bytes, true);
31668
31669               if (bytes.length != 16) {
31670                 throw new Error('invalid counter bytes size (must be 16 bytes)');
31671               }
31672
31673               this._counter = bytes;
31674             };
31675
31676             Counter.prototype.increment = function () {
31677               for (var i = 15; i >= 0; i--) {
31678                 if (this._counter[i] === 255) {
31679                   this._counter[i] = 0;
31680                 } else {
31681                   this._counter[i]++;
31682                   break;
31683                 }
31684               }
31685             };
31686             /**
31687              *  Mode Of Operation - Counter (CTR)
31688              */
31689
31690
31691             var ModeOfOperationCTR = function ModeOfOperationCTR(key, counter) {
31692               if (!(this instanceof ModeOfOperationCTR)) {
31693                 throw Error('AES must be instanitated with `new`');
31694               }
31695
31696               this.description = "Counter";
31697               this.name = "ctr";
31698
31699               if (!(counter instanceof Counter)) {
31700                 counter = new Counter(counter);
31701               }
31702
31703               this._counter = counter;
31704               this._remainingCounter = null;
31705               this._remainingCounterIndex = 16;
31706               this._aes = new AES(key);
31707             };
31708
31709             ModeOfOperationCTR.prototype.encrypt = function (plaintext) {
31710               var encrypted = coerceArray(plaintext, true);
31711
31712               for (var i = 0; i < encrypted.length; i++) {
31713                 if (this._remainingCounterIndex === 16) {
31714                   this._remainingCounter = this._aes.encrypt(this._counter._counter);
31715                   this._remainingCounterIndex = 0;
31716
31717                   this._counter.increment();
31718                 }
31719
31720                 encrypted[i] ^= this._remainingCounter[this._remainingCounterIndex++];
31721               }
31722
31723               return encrypted;
31724             }; // Decryption is symetric
31725
31726
31727             ModeOfOperationCTR.prototype.decrypt = ModeOfOperationCTR.prototype.encrypt; ///////////////////////
31728             // Padding
31729             // See:https://tools.ietf.org/html/rfc2315
31730
31731             function pkcs7pad(data) {
31732               data = coerceArray(data, true);
31733               var padder = 16 - data.length % 16;
31734               var result = createArray(data.length + padder);
31735               copyArray(data, result);
31736
31737               for (var i = data.length; i < result.length; i++) {
31738                 result[i] = padder;
31739               }
31740
31741               return result;
31742             }
31743
31744             function pkcs7strip(data) {
31745               data = coerceArray(data, true);
31746
31747               if (data.length < 16) {
31748                 throw new Error('PKCS#7 invalid length');
31749               }
31750
31751               var padder = data[data.length - 1];
31752
31753               if (padder > 16) {
31754                 throw new Error('PKCS#7 padding byte out of range');
31755               }
31756
31757               var length = data.length - padder;
31758
31759               for (var i = 0; i < padder; i++) {
31760                 if (data[length + i] !== padder) {
31761                   throw new Error('PKCS#7 invalid padding byte');
31762                 }
31763               }
31764
31765               var result = createArray(length);
31766               copyArray(data, result, 0, 0, length);
31767               return result;
31768             } ///////////////////////
31769             // Exporting
31770             // The block cipher
31771
31772
31773             var aesjs = {
31774               AES: AES,
31775               Counter: Counter,
31776               ModeOfOperation: {
31777                 ecb: ModeOfOperationECB,
31778                 cbc: ModeOfOperationCBC,
31779                 cfb: ModeOfOperationCFB,
31780                 ofb: ModeOfOperationOFB,
31781                 ctr: ModeOfOperationCTR
31782               },
31783               utils: {
31784                 hex: convertHex,
31785                 utf8: convertUtf8
31786               },
31787               padding: {
31788                 pkcs7: {
31789                   pad: pkcs7pad,
31790                   strip: pkcs7strip
31791                 }
31792               },
31793               _arrayTest: {
31794                 coerceArray: coerceArray,
31795                 createArray: createArray,
31796                 copyArray: copyArray
31797               }
31798             }; // node.js
31799
31800             {
31801               module.exports = aesjs; // RequireJS/AMD
31802               // http://www.requirejs.org/docs/api.html
31803               // https://github.com/amdjs/amdjs-api/wiki/AMD
31804             }
31805           })();
31806         });
31807
31808         // We can use keys that are 128 bits (16 bytes), 192 bits (24 bytes) or 256 bits (32 bytes).
31809         // To generate a random key:  window.crypto.getRandomValues(new Uint8Array(16));
31810         // This default signing key is built into iD and can be used to mask/unmask sensitive values.
31811
31812         var DEFAULT_128 = [250, 157, 60, 79, 142, 134, 229, 129, 138, 126, 210, 129, 29, 71, 160, 208];
31813         function utilAesEncrypt(text, key) {
31814           key = key || DEFAULT_128;
31815           var textBytes = aesJs.utils.utf8.toBytes(text);
31816           var aesCtr = new aesJs.ModeOfOperation.ctr(key);
31817           var encryptedBytes = aesCtr.encrypt(textBytes);
31818           var encryptedHex = aesJs.utils.hex.fromBytes(encryptedBytes);
31819           return encryptedHex;
31820         }
31821         function utilAesDecrypt(encryptedHex, key) {
31822           key = key || DEFAULT_128;
31823           var encryptedBytes = aesJs.utils.hex.toBytes(encryptedHex);
31824           var aesCtr = new aesJs.ModeOfOperation.ctr(key);
31825           var decryptedBytes = aesCtr.decrypt(encryptedBytes);
31826           var text = aesJs.utils.utf8.fromBytes(decryptedBytes);
31827           return text;
31828         }
31829
31830         function utilCleanTags(tags) {
31831           var out = {};
31832
31833           for (var k in tags) {
31834             if (!k) continue;
31835             var v = tags[k];
31836
31837             if (v !== undefined) {
31838               out[k] = cleanValue(k, v);
31839             }
31840           }
31841
31842           return out;
31843
31844           function cleanValue(k, v) {
31845             function keepSpaces(k) {
31846               return /_hours|_times|:conditional$/.test(k);
31847             }
31848
31849             function skip(k) {
31850               return /^(description|note|fixme)$/.test(k);
31851             }
31852
31853             if (skip(k)) return v;
31854             var cleaned = v.split(';').map(function (s) {
31855               return s.trim();
31856             }).join(keepSpaces(k) ? '; ' : ';'); // The code below is not intended to validate websites and emails.
31857             // It is only intended to prevent obvious copy-paste errors. (#2323)
31858             // clean website- and email-like tags
31859
31860             if (k.indexOf('website') !== -1 || k.indexOf('email') !== -1 || cleaned.indexOf('http') === 0) {
31861               cleaned = cleaned.replace(/[\u200B-\u200F\uFEFF]/g, ''); // strip LRM and other zero width chars
31862             }
31863
31864             return cleaned;
31865           }
31866         }
31867
31868         var _detected;
31869
31870         function utilDetect(refresh) {
31871           if (_detected && !refresh) return _detected;
31872           _detected = {};
31873           var ua = navigator.userAgent;
31874           var m = null;
31875           /* Browser */
31876
31877           m = ua.match(/(edge)\/?\s*(\.?\d+(\.\d+)*)/i); // Edge
31878
31879           if (m !== null) {
31880             _detected.browser = m[1];
31881             _detected.version = m[2];
31882           }
31883
31884           if (!_detected.browser) {
31885             m = ua.match(/Trident\/.*rv:([0-9]{1,}[\.0-9]{0,})/i); // IE11
31886
31887             if (m !== null) {
31888               _detected.browser = 'msie';
31889               _detected.version = m[1];
31890             }
31891           }
31892
31893           if (!_detected.browser) {
31894             m = ua.match(/(opr)\/?\s*(\.?\d+(\.\d+)*)/i); // Opera 15+
31895
31896             if (m !== null) {
31897               _detected.browser = 'Opera';
31898               _detected.version = m[2];
31899             }
31900           }
31901
31902           if (!_detected.browser) {
31903             m = ua.match(/(opera|chrome|safari|firefox|msie)\/?\s*(\.?\d+(\.\d+)*)/i);
31904
31905             if (m !== null) {
31906               _detected.browser = m[1];
31907               _detected.version = m[2];
31908               m = ua.match(/version\/([\.\d]+)/i);
31909               if (m !== null) _detected.version = m[1];
31910             }
31911           }
31912
31913           if (!_detected.browser) {
31914             _detected.browser = navigator.appName;
31915             _detected.version = navigator.appVersion;
31916           } // keep major.minor version only..
31917
31918
31919           _detected.version = _detected.version.split(/\W/).slice(0, 2).join('.'); // detect other browser capabilities
31920           // Legacy Opera has incomplete svg style support. See #715
31921
31922           _detected.opera = _detected.browser.toLowerCase() === 'opera' && parseFloat(_detected.version) < 15;
31923
31924           if (_detected.browser.toLowerCase() === 'msie') {
31925             _detected.ie = true;
31926             _detected.browser = 'Internet Explorer';
31927             _detected.support = parseFloat(_detected.version) >= 11;
31928           } else {
31929             _detected.ie = false;
31930             _detected.support = true;
31931           }
31932
31933           _detected.filedrop = window.FileReader && 'ondrop' in window;
31934           _detected.download = !(_detected.ie || _detected.browser.toLowerCase() === 'edge');
31935           _detected.cssfilters = !(_detected.ie || _detected.browser.toLowerCase() === 'edge');
31936           /* Platform */
31937
31938           if (/Win/.test(ua)) {
31939             _detected.os = 'win';
31940             _detected.platform = 'Windows';
31941           } else if (/Mac/.test(ua)) {
31942             _detected.os = 'mac';
31943             _detected.platform = 'Macintosh';
31944           } else if (/X11/.test(ua) || /Linux/.test(ua)) {
31945             _detected.os = 'linux';
31946             _detected.platform = 'Linux';
31947           } else {
31948             _detected.os = 'win';
31949             _detected.platform = 'Unknown';
31950           }
31951
31952           _detected.isMobileWebKit = (/\b(iPad|iPhone|iPod)\b/.test(ua) || // HACK: iPadOS 13+ requests desktop sites by default by using a Mac user agent,
31953           // so assume any "mac" with multitouch is actually iOS
31954           navigator.platform === 'MacIntel' && 'maxTouchPoints' in navigator && navigator.maxTouchPoints > 1) && /WebKit/.test(ua) && !/Edge/.test(ua) && !window.MSStream;
31955           /* Locale */
31956           // An array of locales requested by the browser in priority order.
31957
31958           _detected.browserLocales = Array.from(new Set( // remove duplicates
31959           [navigator.language].concat(navigator.languages || []).concat([// old property for backwards compatibility
31960           navigator.userLanguage]) // remove any undefined values
31961           .filter(Boolean)));
31962           /* Host */
31963
31964           var loc = window.top.location;
31965           var origin = loc.origin;
31966
31967           if (!origin) {
31968             // for unpatched IE11
31969             origin = loc.protocol + '//' + loc.hostname + (loc.port ? ':' + loc.port : '');
31970           }
31971
31972           _detected.host = origin + loc.pathname;
31973           return _detected;
31974         }
31975
31976         // Like selection.property('value', ...), but avoids no-op value sets,
31977         // which can result in layout/repaint thrashing in some situations.
31978         function utilGetSetValue(selection, value) {
31979           function d3_selection_value(value) {
31980             function valueNull() {
31981               delete this.value;
31982             }
31983
31984             function valueConstant() {
31985               if (this.value !== value) {
31986                 this.value = value;
31987               }
31988             }
31989
31990             function valueFunction() {
31991               var x = value.apply(this, arguments);
31992
31993               if (x === null || x === undefined) {
31994                 delete this.value;
31995               } else if (this.value !== x) {
31996                 this.value = x;
31997               }
31998             }
31999
32000             return value === null || value === undefined ? valueNull : typeof value === 'function' ? valueFunction : valueConstant;
32001           }
32002
32003           if (arguments.length === 1) {
32004             return selection.property('value');
32005           }
32006
32007           return selection.each(d3_selection_value(value));
32008         }
32009
32010         function utilKeybinding(namespace) {
32011           var _keybindings = {};
32012
32013           function testBindings(d3_event, isCapturing) {
32014             var didMatch = false;
32015             var bindings = Object.keys(_keybindings).map(function (id) {
32016               return _keybindings[id];
32017             });
32018             var i, binding; // Most key shortcuts will accept either lower or uppercase ('h' or 'H'),
32019             // so we don't strictly match on the shift key, but we prioritize
32020             // shifted keybindings first, and fallback to unshifted only if no match.
32021             // (This lets us differentiate between '←'/'⇧←' or '⌘Z'/'⌘⇧Z')
32022             // priority match shifted keybindings first
32023
32024             for (i = 0; i < bindings.length; i++) {
32025               binding = bindings[i];
32026               if (!binding.event.modifiers.shiftKey) continue; // no shift
32027
32028               if (!!binding.capture !== isCapturing) continue;
32029
32030               if (matches(d3_event, binding, true)) {
32031                 binding.callback(d3_event);
32032                 didMatch = true; // match a max of one binding per event
32033
32034                 break;
32035               }
32036             }
32037
32038             if (didMatch) return; // then unshifted keybindings
32039
32040             for (i = 0; i < bindings.length; i++) {
32041               binding = bindings[i];
32042               if (binding.event.modifiers.shiftKey) continue; // shift
32043
32044               if (!!binding.capture !== isCapturing) continue;
32045
32046               if (matches(d3_event, binding, false)) {
32047                 binding.callback(d3_event);
32048                 break;
32049               }
32050             }
32051
32052             function matches(d3_event, binding, testShift) {
32053               var event = d3_event;
32054               var isMatch = false;
32055               var tryKeyCode = true; // Prefer a match on `KeyboardEvent.key`
32056
32057               if (event.key !== undefined) {
32058                 tryKeyCode = event.key.charCodeAt(0) > 255; // outside ISO-Latin-1
32059
32060                 isMatch = true;
32061
32062                 if (binding.event.key === undefined) {
32063                   isMatch = false;
32064                 } else if (Array.isArray(binding.event.key)) {
32065                   if (binding.event.key.map(function (s) {
32066                     return s.toLowerCase();
32067                   }).indexOf(event.key.toLowerCase()) === -1) {
32068                     isMatch = false;
32069                   }
32070                 } else {
32071                   if (event.key.toLowerCase() !== binding.event.key.toLowerCase()) {
32072                     isMatch = false;
32073                   }
32074                 }
32075               } // Fallback match on `KeyboardEvent.keyCode`, can happen if:
32076               // - browser doesn't support `KeyboardEvent.key`
32077               // - `KeyboardEvent.key` is outside ISO-Latin-1 range (cyrillic?)
32078
32079
32080               if (!isMatch && tryKeyCode) {
32081                 isMatch = event.keyCode === binding.event.keyCode;
32082               }
32083
32084               if (!isMatch) return false; // test modifier keys
32085
32086               if (!(event.ctrlKey && event.altKey)) {
32087                 // if both are set, assume AltGr and skip it - #4096
32088                 if (event.ctrlKey !== binding.event.modifiers.ctrlKey) return false;
32089                 if (event.altKey !== binding.event.modifiers.altKey) return false;
32090               }
32091
32092               if (event.metaKey !== binding.event.modifiers.metaKey) return false;
32093               if (testShift && event.shiftKey !== binding.event.modifiers.shiftKey) return false;
32094               return true;
32095             }
32096           }
32097
32098           function capture(d3_event) {
32099             testBindings(d3_event, true);
32100           }
32101
32102           function bubble(d3_event) {
32103             var tagName = select(d3_event.target).node().tagName;
32104
32105             if (tagName === 'INPUT' || tagName === 'SELECT' || tagName === 'TEXTAREA') {
32106               return;
32107             }
32108
32109             testBindings(d3_event, false);
32110           }
32111
32112           function keybinding(selection) {
32113             selection = selection || select(document);
32114             selection.on('keydown.capture.' + namespace, capture, true);
32115             selection.on('keydown.bubble.' + namespace, bubble, false);
32116             return keybinding;
32117           } // was: keybinding.off()
32118
32119
32120           keybinding.unbind = function (selection) {
32121             _keybindings = [];
32122             selection = selection || select(document);
32123             selection.on('keydown.capture.' + namespace, null);
32124             selection.on('keydown.bubble.' + namespace, null);
32125             return keybinding;
32126           };
32127
32128           keybinding.clear = function () {
32129             _keybindings = {};
32130             return keybinding;
32131           }; // Remove one or more keycode bindings.
32132
32133
32134           keybinding.off = function (codes, capture) {
32135             var arr = utilArrayUniq([].concat(codes));
32136
32137             for (var i = 0; i < arr.length; i++) {
32138               var id = arr[i] + (capture ? '-capture' : '-bubble');
32139               delete _keybindings[id];
32140             }
32141
32142             return keybinding;
32143           }; // Add one or more keycode bindings.
32144
32145
32146           keybinding.on = function (codes, callback, capture) {
32147             if (typeof callback !== 'function') {
32148               return keybinding.off(codes, capture);
32149             }
32150
32151             var arr = utilArrayUniq([].concat(codes));
32152
32153             for (var i = 0; i < arr.length; i++) {
32154               var id = arr[i] + (capture ? '-capture' : '-bubble');
32155               var binding = {
32156                 id: id,
32157                 capture: capture,
32158                 callback: callback,
32159                 event: {
32160                   key: undefined,
32161                   // preferred
32162                   keyCode: 0,
32163                   // fallback
32164                   modifiers: {
32165                     shiftKey: false,
32166                     ctrlKey: false,
32167                     altKey: false,
32168                     metaKey: false
32169                   }
32170                 }
32171               };
32172
32173               if (_keybindings[id]) {
32174                 console.warn('warning: duplicate keybinding for "' + id + '"'); // eslint-disable-line no-console
32175               }
32176
32177               _keybindings[id] = binding;
32178               var matches = arr[i].toLowerCase().match(/(?:(?:[^+⇧⌃⌥⌘])+|[⇧⌃⌥⌘]|\+\+|^\+$)/g);
32179
32180               for (var j = 0; j < matches.length; j++) {
32181                 // Normalise matching errors
32182                 if (matches[j] === '++') matches[j] = '+';
32183
32184                 if (matches[j] in utilKeybinding.modifierCodes) {
32185                   var prop = utilKeybinding.modifierProperties[utilKeybinding.modifierCodes[matches[j]]];
32186                   binding.event.modifiers[prop] = true;
32187                 } else {
32188                   binding.event.key = utilKeybinding.keys[matches[j]] || matches[j];
32189
32190                   if (matches[j] in utilKeybinding.keyCodes) {
32191                     binding.event.keyCode = utilKeybinding.keyCodes[matches[j]];
32192                   }
32193                 }
32194               }
32195             }
32196
32197             return keybinding;
32198           };
32199
32200           return keybinding;
32201         }
32202         /*
32203          * See https://github.com/keithamus/jwerty
32204          */
32205
32206         utilKeybinding.modifierCodes = {
32207           // Shift key, ⇧
32208           '⇧': 16,
32209           shift: 16,
32210           // CTRL key, on Mac: ⌃
32211           '⌃': 17,
32212           ctrl: 17,
32213           // ALT key, on Mac: ⌥ (Alt)
32214           '⌥': 18,
32215           alt: 18,
32216           option: 18,
32217           // META, on Mac: ⌘ (CMD), on Windows (Win), on Linux (Super)
32218           '⌘': 91,
32219           meta: 91,
32220           cmd: 91,
32221           'super': 91,
32222           win: 91
32223         };
32224         utilKeybinding.modifierProperties = {
32225           16: 'shiftKey',
32226           17: 'ctrlKey',
32227           18: 'altKey',
32228           91: 'metaKey'
32229         };
32230         utilKeybinding.plusKeys = ['plus', 'ffplus', '=', 'ffequals', '≠', '±'];
32231         utilKeybinding.minusKeys = ['_', '-', 'ffminus', 'dash', '–', '—'];
32232         utilKeybinding.keys = {
32233           // Backspace key, on Mac: ⌫ (Backspace)
32234           '⌫': 'Backspace',
32235           backspace: 'Backspace',
32236           // Tab Key, on Mac: ⇥ (Tab), on Windows ⇥⇥
32237           '⇥': 'Tab',
32238           '⇆': 'Tab',
32239           tab: 'Tab',
32240           // Return key, ↩
32241           '↩': 'Enter',
32242           '↵': 'Enter',
32243           '⏎': 'Enter',
32244           'return': 'Enter',
32245           enter: 'Enter',
32246           '⌅': 'Enter',
32247           // Pause/Break key
32248           'pause': 'Pause',
32249           'pause-break': 'Pause',
32250           // Caps Lock key, ⇪
32251           '⇪': 'CapsLock',
32252           caps: 'CapsLock',
32253           'caps-lock': 'CapsLock',
32254           // Escape key, on Mac: ⎋, on Windows: Esc
32255           '⎋': ['Escape', 'Esc'],
32256           escape: ['Escape', 'Esc'],
32257           esc: ['Escape', 'Esc'],
32258           // Space key
32259           space: [' ', 'Spacebar'],
32260           // Page-Up key, or pgup, on Mac: ↖
32261           '↖': 'PageUp',
32262           pgup: 'PageUp',
32263           'page-up': 'PageUp',
32264           // Page-Down key, or pgdown, on Mac: ↘
32265           '↘': 'PageDown',
32266           pgdown: 'PageDown',
32267           'page-down': 'PageDown',
32268           // END key, on Mac: ⇟
32269           '⇟': 'End',
32270           end: 'End',
32271           // HOME key, on Mac: ⇞
32272           '⇞': 'Home',
32273           home: 'Home',
32274           // Insert key, or ins
32275           ins: 'Insert',
32276           insert: 'Insert',
32277           // Delete key, on Mac: ⌦ (Delete)
32278           '⌦': ['Delete', 'Del'],
32279           del: ['Delete', 'Del'],
32280           'delete': ['Delete', 'Del'],
32281           // Left Arrow Key, or ←
32282           '←': ['ArrowLeft', 'Left'],
32283           left: ['ArrowLeft', 'Left'],
32284           'arrow-left': ['ArrowLeft', 'Left'],
32285           // Up Arrow Key, or ↑
32286           '↑': ['ArrowUp', 'Up'],
32287           up: ['ArrowUp', 'Up'],
32288           'arrow-up': ['ArrowUp', 'Up'],
32289           // Right Arrow Key, or →
32290           '→': ['ArrowRight', 'Right'],
32291           right: ['ArrowRight', 'Right'],
32292           'arrow-right': ['ArrowRight', 'Right'],
32293           // Up Arrow Key, or ↓
32294           '↓': ['ArrowDown', 'Down'],
32295           down: ['ArrowDown', 'Down'],
32296           'arrow-down': ['ArrowDown', 'Down'],
32297           // odities, stuff for backward compatibility (browsers and code):
32298           // Num-Multiply, or *
32299           '*': ['*', 'Multiply'],
32300           star: ['*', 'Multiply'],
32301           asterisk: ['*', 'Multiply'],
32302           multiply: ['*', 'Multiply'],
32303           // Num-Plus or +
32304           '+': ['+', 'Add'],
32305           'plus': ['+', 'Add'],
32306           // Num-Subtract, or -
32307           '-': ['-', 'Subtract'],
32308           subtract: ['-', 'Subtract'],
32309           'dash': ['-', 'Subtract'],
32310           // Semicolon
32311           semicolon: ';',
32312           // = or equals
32313           equals: '=',
32314           // Comma, or ,
32315           comma: ',',
32316           // Period, or ., or full-stop
32317           period: '.',
32318           'full-stop': '.',
32319           // Slash, or /, or forward-slash
32320           slash: '/',
32321           'forward-slash': '/',
32322           // Tick, or `, or back-quote
32323           tick: '`',
32324           'back-quote': '`',
32325           // Open bracket, or [
32326           'open-bracket': '[',
32327           // Back slash, or \
32328           'back-slash': '\\',
32329           // Close backet, or ]
32330           'close-bracket': ']',
32331           // Apostrophe, or Quote, or '
32332           quote: '\'',
32333           apostrophe: '\'',
32334           // NUMPAD 0-9
32335           'num-0': '0',
32336           'num-1': '1',
32337           'num-2': '2',
32338           'num-3': '3',
32339           'num-4': '4',
32340           'num-5': '5',
32341           'num-6': '6',
32342           'num-7': '7',
32343           'num-8': '8',
32344           'num-9': '9',
32345           // F1-F25
32346           f1: 'F1',
32347           f2: 'F2',
32348           f3: 'F3',
32349           f4: 'F4',
32350           f5: 'F5',
32351           f6: 'F6',
32352           f7: 'F7',
32353           f8: 'F8',
32354           f9: 'F9',
32355           f10: 'F10',
32356           f11: 'F11',
32357           f12: 'F12',
32358           f13: 'F13',
32359           f14: 'F14',
32360           f15: 'F15',
32361           f16: 'F16',
32362           f17: 'F17',
32363           f18: 'F18',
32364           f19: 'F19',
32365           f20: 'F20',
32366           f21: 'F21',
32367           f22: 'F22',
32368           f23: 'F23',
32369           f24: 'F24',
32370           f25: 'F25'
32371         };
32372         utilKeybinding.keyCodes = {
32373           // Backspace key, on Mac: ⌫ (Backspace)
32374           '⌫': 8,
32375           backspace: 8,
32376           // Tab Key, on Mac: ⇥ (Tab), on Windows ⇥⇥
32377           '⇥': 9,
32378           '⇆': 9,
32379           tab: 9,
32380           // Return key, ↩
32381           '↩': 13,
32382           '↵': 13,
32383           '⏎': 13,
32384           'return': 13,
32385           enter: 13,
32386           '⌅': 13,
32387           // Pause/Break key
32388           'pause': 19,
32389           'pause-break': 19,
32390           // Caps Lock key, ⇪
32391           '⇪': 20,
32392           caps: 20,
32393           'caps-lock': 20,
32394           // Escape key, on Mac: ⎋, on Windows: Esc
32395           '⎋': 27,
32396           escape: 27,
32397           esc: 27,
32398           // Space key
32399           space: 32,
32400           // Page-Up key, or pgup, on Mac: ↖
32401           '↖': 33,
32402           pgup: 33,
32403           'page-up': 33,
32404           // Page-Down key, or pgdown, on Mac: ↘
32405           '↘': 34,
32406           pgdown: 34,
32407           'page-down': 34,
32408           // END key, on Mac: ⇟
32409           '⇟': 35,
32410           end: 35,
32411           // HOME key, on Mac: ⇞
32412           '⇞': 36,
32413           home: 36,
32414           // Insert key, or ins
32415           ins: 45,
32416           insert: 45,
32417           // Delete key, on Mac: ⌦ (Delete)
32418           '⌦': 46,
32419           del: 46,
32420           'delete': 46,
32421           // Left Arrow Key, or ←
32422           '←': 37,
32423           left: 37,
32424           'arrow-left': 37,
32425           // Up Arrow Key, or ↑
32426           '↑': 38,
32427           up: 38,
32428           'arrow-up': 38,
32429           // Right Arrow Key, or →
32430           '→': 39,
32431           right: 39,
32432           'arrow-right': 39,
32433           // Up Arrow Key, or ↓
32434           '↓': 40,
32435           down: 40,
32436           'arrow-down': 40,
32437           // odities, printing characters that come out wrong:
32438           // Firefox Equals
32439           'ffequals': 61,
32440           // Num-Multiply, or *
32441           '*': 106,
32442           star: 106,
32443           asterisk: 106,
32444           multiply: 106,
32445           // Num-Plus or +
32446           '+': 107,
32447           'plus': 107,
32448           // Num-Subtract, or -
32449           '-': 109,
32450           subtract: 109,
32451           // Vertical Bar / Pipe
32452           '|': 124,
32453           // Firefox Plus
32454           'ffplus': 171,
32455           // Firefox Minus
32456           'ffminus': 173,
32457           // Semicolon
32458           ';': 186,
32459           semicolon: 186,
32460           // = or equals
32461           '=': 187,
32462           'equals': 187,
32463           // Comma, or ,
32464           ',': 188,
32465           comma: 188,
32466           // Dash / Underscore key
32467           'dash': 189,
32468           // Period, or ., or full-stop
32469           '.': 190,
32470           period: 190,
32471           'full-stop': 190,
32472           // Slash, or /, or forward-slash
32473           '/': 191,
32474           slash: 191,
32475           'forward-slash': 191,
32476           // Tick, or `, or back-quote
32477           '`': 192,
32478           tick: 192,
32479           'back-quote': 192,
32480           // Open bracket, or [
32481           '[': 219,
32482           'open-bracket': 219,
32483           // Back slash, or \
32484           '\\': 220,
32485           'back-slash': 220,
32486           // Close backet, or ]
32487           ']': 221,
32488           'close-bracket': 221,
32489           // Apostrophe, or Quote, or '
32490           '\'': 222,
32491           quote: 222,
32492           apostrophe: 222
32493         }; // NUMPAD 0-9
32494
32495         var i = 95,
32496             n = 0;
32497
32498         while (++i < 106) {
32499           utilKeybinding.keyCodes['num-' + n] = i;
32500           ++n;
32501         } // 0-9
32502
32503
32504         i = 47;
32505         n = 0;
32506
32507         while (++i < 58) {
32508           utilKeybinding.keyCodes[n] = i;
32509           ++n;
32510         } // F1-F25
32511
32512
32513         i = 111;
32514         n = 1;
32515
32516         while (++i < 136) {
32517           utilKeybinding.keyCodes['f' + n] = i;
32518           ++n;
32519         } // a-z
32520
32521
32522         i = 64;
32523
32524         while (++i < 91) {
32525           utilKeybinding.keyCodes[String.fromCharCode(i).toLowerCase()] = i;
32526         }
32527
32528         function utilObjectOmit(obj, omitKeys) {
32529           return Object.keys(obj).reduce(function (result, key) {
32530             if (omitKeys.indexOf(key) === -1) {
32531               result[key] = obj[key]; // keep
32532             }
32533
32534             return result;
32535           }, {});
32536         }
32537
32538         // Copies a variable number of methods from source to target.
32539         function utilRebind(target, source) {
32540           var i = 1,
32541               n = arguments.length,
32542               method;
32543
32544           while (++i < n) {
32545             target[method = arguments[i]] = d3_rebind(target, source, source[method]);
32546           }
32547
32548           return target;
32549         } // Method is assumed to be a standard D3 getter-setter:
32550         // If passed with no arguments, gets the value.
32551         // If passed with arguments, sets the value and returns the target.
32552
32553         function d3_rebind(target, source, method) {
32554           return function () {
32555             var value = method.apply(source, arguments);
32556             return value === source ? target : value;
32557           };
32558         }
32559
32560         // A per-domain session mutex backed by a cookie and dead man's
32561         // switch. If the session crashes, the mutex will auto-release
32562         // after 5 seconds.
32563         // This accepts a string and returns an object that complies with utilSessionMutexType
32564         function utilSessionMutex(name) {
32565           var mutex = {};
32566           var intervalID;
32567
32568           function renew() {
32569             var expires = new Date();
32570             expires.setSeconds(expires.getSeconds() + 5);
32571             document.cookie = name + '=1; expires=' + expires.toUTCString() + '; sameSite=strict';
32572           }
32573
32574           mutex.lock = function () {
32575             if (intervalID) return true;
32576             var cookie = document.cookie.replace(new RegExp('(?:(?:^|.*;)\\s*' + name + '\\s*\\=\\s*([^;]*).*$)|^.*$'), '$1');
32577             if (cookie) return false;
32578             renew();
32579             intervalID = window.setInterval(renew, 4000);
32580             return true;
32581           };
32582
32583           mutex.unlock = function () {
32584             if (!intervalID) return;
32585             document.cookie = name + '=; expires=Thu, 01 Jan 1970 00:00:00 GMT; sameSite=strict';
32586             clearInterval(intervalID);
32587             intervalID = null;
32588           };
32589
32590           mutex.locked = function () {
32591             return !!intervalID;
32592           };
32593
32594           return mutex;
32595         }
32596
32597         function utilTiler() {
32598           var _size = [256, 256];
32599           var _scale = 256;
32600           var _tileSize = 256;
32601           var _zoomExtent = [0, 20];
32602           var _translate = [_size[0] / 2, _size[1] / 2];
32603           var _margin = 0;
32604           var _skipNullIsland = false;
32605
32606           function clamp(num, min, max) {
32607             return Math.max(min, Math.min(num, max));
32608           }
32609
32610           function nearNullIsland(tile) {
32611             var x = tile[0];
32612             var y = tile[1];
32613             var z = tile[2];
32614
32615             if (z >= 7) {
32616               var center = Math.pow(2, z - 1);
32617               var width = Math.pow(2, z - 6);
32618               var min = center - width / 2;
32619               var max = center + width / 2 - 1;
32620               return x >= min && x <= max && y >= min && y <= max;
32621             }
32622
32623             return false;
32624           }
32625
32626           function tiler() {
32627             var z = geoScaleToZoom(_scale / (2 * Math.PI), _tileSize);
32628             var z0 = clamp(Math.round(z), _zoomExtent[0], _zoomExtent[1]);
32629             var tileMin = 0;
32630             var tileMax = Math.pow(2, z0) - 1;
32631             var log2ts = Math.log(_tileSize) * Math.LOG2E;
32632             var k = Math.pow(2, z - z0 + log2ts);
32633             var origin = [(_translate[0] - _scale / 2) / k, (_translate[1] - _scale / 2) / k];
32634             var cols = range$1(clamp(Math.floor(-origin[0]) - _margin, tileMin, tileMax + 1), clamp(Math.ceil(_size[0] / k - origin[0]) + _margin, tileMin, tileMax + 1));
32635             var rows = range$1(clamp(Math.floor(-origin[1]) - _margin, tileMin, tileMax + 1), clamp(Math.ceil(_size[1] / k - origin[1]) + _margin, tileMin, tileMax + 1));
32636             var tiles = [];
32637
32638             for (var i = 0; i < rows.length; i++) {
32639               var y = rows[i];
32640
32641               for (var j = 0; j < cols.length; j++) {
32642                 var x = cols[j];
32643
32644                 if (i >= _margin && i <= rows.length - _margin && j >= _margin && j <= cols.length - _margin) {
32645                   tiles.unshift([x, y, z0]); // tiles in view at beginning
32646                 } else {
32647                   tiles.push([x, y, z0]); // tiles in margin at the end
32648                 }
32649               }
32650             }
32651
32652             tiles.translate = origin;
32653             tiles.scale = k;
32654             return tiles;
32655           }
32656           /**
32657            * getTiles() returns an array of tiles that cover the map view
32658            */
32659
32660
32661           tiler.getTiles = function (projection) {
32662             var origin = [projection.scale() * Math.PI - projection.translate()[0], projection.scale() * Math.PI - projection.translate()[1]];
32663             this.size(projection.clipExtent()[1]).scale(projection.scale() * 2 * Math.PI).translate(projection.translate());
32664             var tiles = tiler();
32665             var ts = tiles.scale;
32666             return tiles.map(function (tile) {
32667               if (_skipNullIsland && nearNullIsland(tile)) {
32668                 return false;
32669               }
32670
32671               var x = tile[0] * ts - origin[0];
32672               var y = tile[1] * ts - origin[1];
32673               return {
32674                 id: tile.toString(),
32675                 xyz: tile,
32676                 extent: geoExtent(projection.invert([x, y + ts]), projection.invert([x + ts, y]))
32677               };
32678             }).filter(Boolean);
32679           };
32680           /**
32681            * getGeoJSON() returns a FeatureCollection for debugging tiles
32682            */
32683
32684
32685           tiler.getGeoJSON = function (projection) {
32686             var features = tiler.getTiles(projection).map(function (tile) {
32687               return {
32688                 type: 'Feature',
32689                 properties: {
32690                   id: tile.id,
32691                   name: tile.id
32692                 },
32693                 geometry: {
32694                   type: 'Polygon',
32695                   coordinates: [tile.extent.polygon()]
32696                 }
32697               };
32698             });
32699             return {
32700               type: 'FeatureCollection',
32701               features: features
32702             };
32703           };
32704
32705           tiler.tileSize = function (val) {
32706             if (!arguments.length) return _tileSize;
32707             _tileSize = val;
32708             return tiler;
32709           };
32710
32711           tiler.zoomExtent = function (val) {
32712             if (!arguments.length) return _zoomExtent;
32713             _zoomExtent = val;
32714             return tiler;
32715           };
32716
32717           tiler.size = function (val) {
32718             if (!arguments.length) return _size;
32719             _size = val;
32720             return tiler;
32721           };
32722
32723           tiler.scale = function (val) {
32724             if (!arguments.length) return _scale;
32725             _scale = val;
32726             return tiler;
32727           };
32728
32729           tiler.translate = function (val) {
32730             if (!arguments.length) return _translate;
32731             _translate = val;
32732             return tiler;
32733           }; // number to extend the rows/columns beyond those covering the viewport
32734
32735
32736           tiler.margin = function (val) {
32737             if (!arguments.length) return _margin;
32738             _margin = +val;
32739             return tiler;
32740           };
32741
32742           tiler.skipNullIsland = function (val) {
32743             if (!arguments.length) return _skipNullIsland;
32744             _skipNullIsland = val;
32745             return tiler;
32746           };
32747
32748           return tiler;
32749         }
32750
32751         function utilTriggerEvent(target, type) {
32752           target.each(function () {
32753             var evt = document.createEvent('HTMLEvents');
32754             evt.initEvent(type, true, true);
32755             this.dispatchEvent(evt);
32756           });
32757         }
32758
32759         var _mainLocations = coreLocations(); // singleton
32760         // `coreLocations` maintains an internal index of all the boundaries/geofences used by iD.
32761         // It's used by presets, community index, background imagery, to know where in the world these things are valid.
32762         // These geofences should be defined by `locationSet` objects:
32763         //
32764         // let locationSet = {
32765         //   include: [ Array of locations ],
32766         //   exclude: [ Array of locations ]
32767         // };
32768         //
32769         // For more info see the location-conflation and country-coder projects, see:
32770         // https://github.com/ideditor/location-conflation
32771         // https://github.com/ideditor/country-coder
32772         //
32773
32774         function coreLocations() {
32775           var _this = {};
32776           var _resolvedFeatures = {}; // cache of *resolved* locationSet features
32777
32778           var _loco = new _default(); // instance of a location-conflation resolver
32779
32780
32781           var _wp; // instance of a which-polygon index
32782           // pre-resolve the worldwide locationSet
32783
32784
32785           var world = {
32786             locationSet: {
32787               include: ['Q2']
32788             }
32789           };
32790           resolveLocationSet(world);
32791           rebuildIndex();
32792           var _queue = [];
32793
32794           var _deferred = new Set();
32795
32796           var _inProcess; // Returns a Promise to process the queue
32797
32798
32799           function processQueue() {
32800             if (!_queue.length) return Promise.resolve(); // console.log(`queue length ${_queue.length}`);
32801
32802             var chunk = _queue.pop();
32803
32804             return new Promise(function (resolvePromise) {
32805               var handle = window.requestIdleCallback(function () {
32806                 _deferred["delete"](handle); // const t0 = performance.now();
32807
32808
32809                 chunk.forEach(resolveLocationSet); // const t1 = performance.now();
32810                 // console.log('chunk processed in ' + (t1 - t0) + ' ms');
32811
32812                 resolvePromise();
32813               });
32814
32815               _deferred.add(handle);
32816             }).then(function () {
32817               return processQueue();
32818             });
32819           } // Pass an Object with a `locationSet` property,
32820           // Performs the locationSet resolution, caches the result, and sets a `locationSetID` property on the object.
32821
32822
32823           function resolveLocationSet(obj) {
32824             if (obj.locationSetID) return; // work was done already
32825
32826             try {
32827               var locationSet = obj.locationSet;
32828
32829               if (!locationSet) {
32830                 throw new Error('object missing locationSet property');
32831               }
32832
32833               if (!locationSet.include) {
32834                 // missing `include`, default to worldwide include
32835                 locationSet.include = ['Q2']; // https://github.com/openstreetmap/iD/pull/8305#discussion_r662344647
32836               }
32837
32838               var resolved = _loco.resolveLocationSet(locationSet);
32839
32840               var locationSetID = resolved.id;
32841               obj.locationSetID = locationSetID;
32842
32843               if (!resolved.feature.geometry.coordinates.length || !resolved.feature.properties.area) {
32844                 throw new Error("locationSet ".concat(locationSetID, " resolves to an empty feature."));
32845               }
32846
32847               if (!_resolvedFeatures[locationSetID]) {
32848                 // First time seeing this locationSet feature
32849                 var feature = JSON.parse(JSON.stringify(resolved.feature)); // deep clone
32850
32851                 feature.id = locationSetID; // Important: always use the locationSet `id` (`+[Q30]`), not the feature `id` (`Q30`)
32852
32853                 feature.properties.id = locationSetID;
32854                 _resolvedFeatures[locationSetID] = feature; // insert into cache
32855               }
32856             } catch (err) {
32857               obj.locationSet = {
32858                 include: ['Q2']
32859               }; // default worldwide
32860
32861               obj.locationSetID = '+[Q2]';
32862             }
32863           } // Rebuilds the whichPolygon index with whatever features have been resolved.
32864
32865
32866           function rebuildIndex() {
32867             _wp = whichPolygon_1({
32868               features: Object.values(_resolvedFeatures)
32869             });
32870           } //
32871           // `mergeCustomGeoJSON`
32872           //  Accepts an FeatureCollection-like object containing custom locations
32873           //  Each feature must have a filename-like `id`, for example: `something.geojson`
32874           //
32875           //  {
32876           //    "type": "FeatureCollection"
32877           //    "features": [
32878           //      {
32879           //        "type": "Feature",
32880           //        "id": "philly_metro.geojson",
32881           //        "properties": { … },
32882           //        "geometry": { … }
32883           //      }
32884           //    ]
32885           //  }
32886           //
32887
32888
32889           _this.mergeCustomGeoJSON = function (fc) {
32890             if (fc && fc.type === 'FeatureCollection' && Array.isArray(fc.features)) {
32891               fc.features.forEach(function (feature) {
32892                 feature.properties = feature.properties || {};
32893                 var props = feature.properties; // Get `id` from either `id` or `properties`
32894
32895                 var id = feature.id || props.id;
32896                 if (!id || !/^\S+\.geojson$/i.test(id)) return; // Ensure `id` exists and is lowercase
32897
32898                 id = id.toLowerCase();
32899                 feature.id = id;
32900                 props.id = id; // Ensure `area` property exists
32901
32902                 if (!props.area) {
32903                   var area = geojsonArea.geometry(feature.geometry) / 1e6; // m² to km²
32904
32905                   props.area = Number(area.toFixed(2));
32906                 }
32907
32908                 _loco._cache[id] = feature;
32909               });
32910             }
32911           }; //
32912           // `mergeLocationSets`
32913           //  Accepts an Array of Objects containing `locationSet` properties.
32914           //  The locationSets will be resolved and indexed in the background.
32915           //  [
32916           //   { id: 'preset1', locationSet: {…} },
32917           //   { id: 'preset2', locationSet: {…} },
32918           //   { id: 'preset3', locationSet: {…} },
32919           //   …
32920           //  ]
32921           //  After resolving and indexing, the Objects will be decorated with a
32922           //  `locationSetID` property.
32923           //  [
32924           //   { id: 'preset1', locationSet: {…}, locationSetID: '+[Q2]' },
32925           //   { id: 'preset2', locationSet: {…}, locationSetID: '+[Q30]' },
32926           //   { id: 'preset3', locationSet: {…}, locationSetID: '+[Q2]' },
32927           //   …
32928           //  ]
32929           //
32930           //  Returns a Promise fulfilled when the resolving/indexing has been completed
32931           //  This will take some seconds but happen in the background during browser idle time.
32932           //
32933
32934
32935           _this.mergeLocationSets = function (objects) {
32936             if (!Array.isArray(objects)) return Promise.reject('nothing to do'); // Resolve all locationSets -> geojson, processing data in chunks
32937             //
32938             // Because this will happen during idle callbacks, we want to choose a chunk size
32939             // that won't make the browser stutter too badly.  LocationSets that are a simple
32940             // country coder include will resolve instantly, but ones that involve complex
32941             // include/exclude operations will take some milliseconds longer.
32942             //
32943             // Some discussion and performance results on these tickets:
32944             // https://github.com/ideditor/location-conflation/issues/26
32945             // https://github.com/osmlab/name-suggestion-index/issues/4784#issuecomment-742003434
32946
32947             _queue = _queue.concat(utilArrayChunk(objects, 200));
32948
32949             if (!_inProcess) {
32950               _inProcess = processQueue().then(function () {
32951                 rebuildIndex();
32952                 _inProcess = null;
32953                 return objects;
32954               });
32955             }
32956
32957             return _inProcess;
32958           }; //
32959           // `locationSetID`
32960           // Returns a locationSetID for a given locationSet (fallback to `+[Q2]`, world)
32961           // (The locationset doesn't necessarily need to be resolved to compute its `id`)
32962           //
32963           // Arguments
32964           //   `locationSet`: A locationSet, e.g. `{ include: ['us'] }`
32965           // Returns
32966           //   The locationSetID, e.g. `+[Q30]`
32967           //
32968
32969
32970           _this.locationSetID = function (locationSet) {
32971             var locationSetID;
32972
32973             try {
32974               locationSetID = _loco.validateLocationSet(locationSet).id;
32975             } catch (err) {
32976               locationSetID = '+[Q2]'; // the world
32977             }
32978
32979             return locationSetID;
32980           }; //
32981           // `feature`
32982           // Returns the resolved GeoJSON feature for a given locationSetID (fallback to 'world')
32983           //
32984           // Arguments
32985           //   `locationSetID`: id of the form like `+[Q30]`  (United States)
32986           // Returns
32987           //   A GeoJSON feature:
32988           //   {
32989           //     type: 'Feature',
32990           //     id: '+[Q30]',
32991           //     properties: { id: '+[Q30]', area: 21817019.17, … },
32992           //     geometry: { … }
32993           //   }
32994
32995
32996           _this.feature = function (locationSetID) {
32997             return _resolvedFeatures[locationSetID] || _resolvedFeatures['+[Q2]'];
32998           }; //
32999           // `locationsAt`
33000           // Find all the resolved locationSets valid at the given location.
33001           // Results include the area (in km²) to facilitate sorting.
33002           //
33003           // Arguments
33004           //   `loc`: the [lon,lat] location to query, e.g. `[-74.4813, 40.7967]`
33005           // Returns
33006           //   Object of locationSetIDs to areas (in km²)
33007           //   {
33008           //     "+[Q2]": 511207893.3958111,
33009           //     "+[Q30]": 21817019.17,
33010           //     "+[new_jersey.geojson]": 22390.77,
33011           //     …
33012           //   }
33013           //
33014
33015
33016           _this.locationsAt = function (loc) {
33017             var result = {};
33018             (_wp(loc, true) || []).forEach(function (prop) {
33019               return result[prop.id] = prop.area;
33020             });
33021             return result;
33022           }; //
33023           // `query`
33024           // Execute a query directly against which-polygon
33025           // https://github.com/mapbox/which-polygon
33026           //
33027           // Arguments
33028           //   `loc`: the [lon,lat] location to query,
33029           //   `multi`: `true` to return all results, `false` to return first result
33030           // Returns
33031           //   Array of GeoJSON *properties* for the locationSet features that exist at `loc`
33032           //
33033
33034
33035           _this.query = function (loc, multi) {
33036             return _wp(loc, multi);
33037           }; // Direct access to the location-conflation resolver
33038
33039
33040           _this.loco = function () {
33041             return _loco;
33042           }; // Direct access to the which-polygon index
33043
33044
33045           _this.wp = function () {
33046             return _wp;
33047           };
33048
33049           return _this;
33050         }
33051
33052         var $findIndex = arrayIteration.findIndex;
33053
33054
33055         var FIND_INDEX = 'findIndex';
33056         var SKIPS_HOLES = true;
33057
33058         // Shouldn't skip holes
33059         if (FIND_INDEX in []) Array(1)[FIND_INDEX](function () { SKIPS_HOLES = false; });
33060
33061         // `Array.prototype.findIndex` method
33062         // https://tc39.es/ecma262/#sec-array.prototype.findindex
33063         _export({ target: 'Array', proto: true, forced: SKIPS_HOLES }, {
33064           findIndex: function findIndex(callbackfn /* , that = undefined */) {
33065             return $findIndex(this, callbackfn, arguments.length > 1 ? arguments[1] : undefined);
33066           }
33067         });
33068
33069         // https://tc39.es/ecma262/#sec-array.prototype-@@unscopables
33070         addToUnscopables(FIND_INDEX);
33071
33072         var notARegexp = function (it) {
33073           if (isRegexp(it)) {
33074             throw TypeError("The method doesn't accept regular expressions");
33075           } return it;
33076         };
33077
33078         var MATCH = wellKnownSymbol('match');
33079
33080         var correctIsRegexpLogic = function (METHOD_NAME) {
33081           var regexp = /./;
33082           try {
33083             '/./'[METHOD_NAME](regexp);
33084           } catch (error1) {
33085             try {
33086               regexp[MATCH] = false;
33087               return '/./'[METHOD_NAME](regexp);
33088             } catch (error2) { /* empty */ }
33089           } return false;
33090         };
33091
33092         // `String.prototype.includes` method
33093         // https://tc39.es/ecma262/#sec-string.prototype.includes
33094         _export({ target: 'String', proto: true, forced: !correctIsRegexpLogic('includes') }, {
33095           includes: function includes(searchString /* , position = 0 */) {
33096             return !!~String(requireObjectCoercible(this))
33097               .indexOf(notARegexp(searchString), arguments.length > 1 ? arguments[1] : undefined);
33098           }
33099         });
33100
33101         var _mainLocalizer = coreLocalizer(); // singleton
33102
33103
33104         var _t = _mainLocalizer.t;
33105         // coreLocalizer manages language and locale parameters including translated strings
33106         //
33107
33108         function coreLocalizer() {
33109           var localizer = {};
33110           var _dataLanguages = {}; // `_dataLocales` is an object containing all _supported_ locale codes -> language info.
33111           // * `rtl` - right-to-left or left-to-right text direction
33112           // * `pct` - the percent of strings translated; 1 = 100%, full coverage
33113           //
33114           // {
33115           // en: { rtl: false, pct: {…} },
33116           // de: { rtl: false, pct: {…} },
33117           // …
33118           // }
33119
33120           var _dataLocales = {}; // `localeStrings` is an object containing all _loaded_ locale codes -> string data.
33121           // {
33122           // en: { icons: {…}, toolbar: {…}, modes: {…}, operations: {…}, … },
33123           // de: { icons: {…}, toolbar: {…}, modes: {…}, operations: {…}, … },
33124           // …
33125           // }
33126
33127           var _localeStrings = {}; // the current locale
33128
33129           var _localeCode = 'en-US'; // `_localeCodes` must contain `_localeCode` first, optionally followed by fallbacks
33130
33131           var _localeCodes = ['en-US', 'en'];
33132           var _languageCode = 'en';
33133           var _textDirection = 'ltr';
33134           var _usesMetric = false;
33135           var _languageNames = {};
33136           var _scriptNames = {}; // getters for the current locale parameters
33137
33138           localizer.localeCode = function () {
33139             return _localeCode;
33140           };
33141
33142           localizer.localeCodes = function () {
33143             return _localeCodes;
33144           };
33145
33146           localizer.languageCode = function () {
33147             return _languageCode;
33148           };
33149
33150           localizer.textDirection = function () {
33151             return _textDirection;
33152           };
33153
33154           localizer.usesMetric = function () {
33155             return _usesMetric;
33156           };
33157
33158           localizer.languageNames = function () {
33159             return _languageNames;
33160           };
33161
33162           localizer.scriptNames = function () {
33163             return _scriptNames;
33164           }; // The client app may want to manually set the locale, regardless of the
33165           // settings provided by the browser
33166
33167
33168           var _preferredLocaleCodes = [];
33169
33170           localizer.preferredLocaleCodes = function (codes) {
33171             if (!arguments.length) return _preferredLocaleCodes;
33172
33173             if (typeof codes === 'string') {
33174               // be generous and accept delimited strings as input
33175               _preferredLocaleCodes = codes.split(/,|;| /gi).filter(Boolean);
33176             } else {
33177               _preferredLocaleCodes = codes;
33178             }
33179
33180             return localizer;
33181           };
33182
33183           var _loadPromise;
33184
33185           localizer.ensureLoaded = function () {
33186             if (_loadPromise) return _loadPromise;
33187             var filesToFetch = [// load the list of languages
33188             'languages', // load the list of supported locales
33189             'locales'];
33190             var localeDirs = {
33191               general: 'locales',
33192               tagging: 'https://cdn.jsdelivr.net/npm/@openstreetmap/id-tagging-schema@3/dist/translations'
33193             };
33194             var fileMap = _mainFileFetcher.fileMap();
33195
33196             for (var scopeId in localeDirs) {
33197               var key = "locales_index_".concat(scopeId);
33198               fileMap[key] = localeDirs[scopeId] + '/index.min.json';
33199               filesToFetch.push(key);
33200             }
33201
33202             return _loadPromise = Promise.all(filesToFetch.map(function (key) {
33203               return _mainFileFetcher.get(key);
33204             })).then(function (results) {
33205               _dataLanguages = results[0];
33206               _dataLocales = results[1];
33207               var indexes = results.slice(2);
33208
33209               var requestedLocales = (_preferredLocaleCodes || []). // List of locales preferred by the browser in priority order.
33210               concat(utilDetect().browserLocales) // fallback to English since it's the only guaranteed complete language
33211               .concat(['en']);
33212
33213               _localeCodes = localesToUseFrom(requestedLocales); // Run iD in the highest-priority locale; the rest are fallbacks
33214
33215               _localeCode = _localeCodes[0];
33216               var loadStringsPromises = [];
33217               indexes.forEach(function (index, i) {
33218                 // Will always return the index for `en` if nothing else
33219                 var fullCoverageIndex = _localeCodes.findIndex(function (locale) {
33220                   return index[locale] && index[locale].pct === 1;
33221                 }); // We only need to load locales up until we find one with full coverage
33222
33223
33224                 _localeCodes.slice(0, fullCoverageIndex + 1).forEach(function (code) {
33225                   var scopeId = Object.keys(localeDirs)[i];
33226                   var directory = Object.values(localeDirs)[i];
33227                   if (index[code]) loadStringsPromises.push(localizer.loadLocale(code, scopeId, directory));
33228                 });
33229               });
33230               return Promise.all(loadStringsPromises);
33231             }).then(function () {
33232               updateForCurrentLocale();
33233             })["catch"](function (err) {
33234               return console.error(err);
33235             }); // eslint-disable-line
33236           }; // Returns the locales from `requestedLocales` supported by iD that we should use
33237
33238
33239           function localesToUseFrom(requestedLocales) {
33240             var supportedLocales = _dataLocales;
33241             var toUse = [];
33242
33243             for (var i in requestedLocales) {
33244               var locale = requestedLocales[i];
33245               if (supportedLocales[locale]) toUse.push(locale);
33246
33247               if (locale.includes('-')) {
33248                 // Full locale ('es-ES'), add fallback to the base ('es')
33249                 var langPart = locale.split('-')[0];
33250                 if (supportedLocales[langPart]) toUse.push(langPart);
33251               }
33252             } // remove duplicates
33253
33254
33255             return utilArrayUniq(toUse);
33256           }
33257
33258           function updateForCurrentLocale() {
33259             if (!_localeCode) return;
33260             _languageCode = _localeCode.split('-')[0];
33261             var currentData = _dataLocales[_localeCode] || _dataLocales[_languageCode];
33262             var hash = utilStringQs(window.location.hash);
33263
33264             if (hash.rtl === 'true') {
33265               _textDirection = 'rtl';
33266             } else if (hash.rtl === 'false') {
33267               _textDirection = 'ltr';
33268             } else {
33269               _textDirection = currentData && currentData.rtl ? 'rtl' : 'ltr';
33270             }
33271
33272             var locale = _localeCode;
33273             if (locale.toLowerCase() === 'en-us') locale = 'en';
33274             _languageNames = _localeStrings.general[locale].languageNames;
33275             _scriptNames = _localeStrings.general[locale].scriptNames;
33276             _usesMetric = _localeCode.slice(-3).toLowerCase() !== '-us';
33277           }
33278           /* Locales */
33279           // Returns a Promise to load the strings for the requested locale
33280
33281
33282           localizer.loadLocale = function (locale, scopeId, directory) {
33283             // US English is the default
33284             if (locale.toLowerCase() === 'en-us') locale = 'en';
33285
33286             if (_localeStrings[scopeId] && _localeStrings[scopeId][locale]) {
33287               // already loaded
33288               return Promise.resolve(locale);
33289             }
33290
33291             var fileMap = _mainFileFetcher.fileMap();
33292             var key = "locale_".concat(scopeId, "_").concat(locale);
33293             fileMap[key] = "".concat(directory, "/").concat(locale, ".min.json");
33294             return _mainFileFetcher.get(key).then(function (d) {
33295               if (!_localeStrings[scopeId]) _localeStrings[scopeId] = {};
33296               _localeStrings[scopeId][locale] = d[locale];
33297               return locale;
33298             });
33299           };
33300
33301           localizer.pluralRule = function (number) {
33302             return pluralRule(number, _localeCode);
33303           }; // Returns the plural rule for the given `number` with the given `localeCode`.
33304           // One of: `zero`, `one`, `two`, `few`, `many`, `other`
33305
33306
33307           function pluralRule(number, localeCode) {
33308             // modern browsers have this functionality built-in
33309             var rules = 'Intl' in window && Intl.PluralRules && new Intl.PluralRules(localeCode);
33310
33311             if (rules) {
33312               return rules.select(number);
33313             } // fallback to basic one/other, as in English
33314
33315
33316             if (number === 1) return 'one';
33317             return 'other';
33318           }
33319           /**
33320           * Try to find that string in `locale` or the current `_localeCode` matching
33321           * the given `stringId`. If no string can be found in the requested locale,
33322           * we'll recurse down all the `_localeCodes` until one is found.
33323           *
33324           * @param  {string}   stringId      string identifier
33325           * @param  {object?}  replacements  token replacements and default string
33326           * @param  {string?}  locale        locale to use (defaults to currentLocale)
33327           * @return {string?}  localized string
33328           */
33329
33330
33331           localizer.tInfo = function (origStringId, replacements, locale) {
33332             var stringId = origStringId.trim();
33333             var scopeId = 'general';
33334
33335             if (stringId[0] === '_') {
33336               var split = stringId.split('.');
33337               scopeId = split[0].slice(1);
33338               stringId = split.slice(1).join('.');
33339             }
33340
33341             locale = locale || _localeCode;
33342             var path = stringId.split('.').map(function (s) {
33343               return s.replace(/<TX_DOT>/g, '.');
33344             }).reverse();
33345             var stringsKey = locale; // US English is the default
33346
33347             if (stringsKey.toLowerCase() === 'en-us') stringsKey = 'en';
33348             var result = _localeStrings && _localeStrings[scopeId] && _localeStrings[scopeId][stringsKey];
33349
33350             while (result !== undefined && path.length) {
33351               result = result[path.pop()];
33352             }
33353
33354             if (result !== undefined) {
33355               if (replacements) {
33356                 if (_typeof(result) === 'object' && Object.keys(result).length) {
33357                   // If plural forms are provided, dig one level deeper based on the
33358                   // first numeric token replacement provided.
33359                   var number = Object.values(replacements).find(function (value) {
33360                     return typeof value === 'number';
33361                   });
33362
33363                   if (number !== undefined) {
33364                     var rule = pluralRule(number, locale);
33365
33366                     if (result[rule]) {
33367                       result = result[rule];
33368                     } else {
33369                       // We're pretty sure this should be a plural but no string
33370                       // could be found for the given rule. Just pick the first
33371                       // string and hope it makes sense.
33372                       result = Object.values(result)[0];
33373                     }
33374                   }
33375                 }
33376
33377                 if (typeof result === 'string') {
33378                   for (var key in replacements) {
33379                     var value = replacements[key];
33380
33381                     if (typeof value === 'number') {
33382                       if (value.toLocaleString) {
33383                         // format numbers for the locale
33384                         value = value.toLocaleString(locale, {
33385                           style: 'decimal',
33386                           useGrouping: true,
33387                           minimumFractionDigits: 0
33388                         });
33389                       } else {
33390                         value = value.toString();
33391                       }
33392                     }
33393
33394                     var token = "{".concat(key, "}");
33395                     var regex = new RegExp(token, 'g');
33396                     result = result.replace(regex, value);
33397                   }
33398                 }
33399               }
33400
33401               if (typeof result === 'string') {
33402                 // found a localized string!
33403                 return {
33404                   text: result,
33405                   locale: locale
33406                 };
33407               }
33408             } // no localized string found...
33409             // attempt to fallback to a lower-priority language
33410
33411
33412             var index = _localeCodes.indexOf(locale);
33413
33414             if (index >= 0 && index < _localeCodes.length - 1) {
33415               // eventually this will be 'en' or another locale with 100% coverage
33416               var fallback = _localeCodes[index + 1];
33417               return localizer.tInfo(origStringId, replacements, fallback);
33418             }
33419
33420             if (replacements && 'default' in replacements) {
33421               // Fallback to a default value if one is specified in `replacements`
33422               return {
33423                 text: replacements["default"],
33424                 locale: null
33425               };
33426             }
33427
33428             var missing = "Missing ".concat(locale, " translation: ").concat(origStringId);
33429             if (typeof console !== 'undefined') console.error(missing); // eslint-disable-line
33430
33431             return {
33432               text: missing,
33433               locale: 'en'
33434             };
33435           };
33436
33437           localizer.hasTextForStringId = function (stringId) {
33438             return !!localizer.tInfo(stringId, {
33439               "default": 'nothing found'
33440             }).locale;
33441           }; // Returns only the localized text, discarding the locale info
33442
33443
33444           localizer.t = function (stringId, replacements, locale) {
33445             return localizer.tInfo(stringId, replacements, locale).text;
33446           }; // Returns the localized text wrapped in an HTML element encoding the locale info
33447
33448
33449           localizer.t.html = function (stringId, replacements, locale) {
33450             var info = localizer.tInfo(stringId, replacements, locale); // text may be empty or undefined if `replacements.default` is
33451
33452             return info.text ? localizer.htmlForLocalizedText(info.text, info.locale) : '';
33453           };
33454
33455           localizer.htmlForLocalizedText = function (text, localeCode) {
33456             return "<span class=\"localized-text\" lang=\"".concat(localeCode || 'unknown', "\">").concat(text, "</span>");
33457           };
33458
33459           localizer.languageName = function (code, options) {
33460             if (_languageNames[code]) {
33461               // name in locale language
33462               // e.g. "German"
33463               return _languageNames[code];
33464             } // sometimes we only want the local name
33465
33466
33467             if (options && options.localOnly) return null;
33468             var langInfo = _dataLanguages[code];
33469
33470             if (langInfo) {
33471               if (langInfo.nativeName) {
33472                 // name in native language
33473                 // e.g. "Deutsch (de)"
33474                 return localizer.t('translate.language_and_code', {
33475                   language: langInfo.nativeName,
33476                   code: code
33477                 });
33478               } else if (langInfo.base && langInfo.script) {
33479                 var base = langInfo.base; // the code of the language this is based on
33480
33481                 if (_languageNames[base]) {
33482                   // base language name in locale language
33483                   var scriptCode = langInfo.script;
33484                   var script = _scriptNames[scriptCode] || scriptCode; // e.g. "Serbian (Cyrillic)"
33485
33486                   return localizer.t('translate.language_and_code', {
33487                     language: _languageNames[base],
33488                     code: script
33489                   });
33490                 } else if (_dataLanguages[base] && _dataLanguages[base].nativeName) {
33491                   // e.g. "српски (sr-Cyrl)"
33492                   return localizer.t('translate.language_and_code', {
33493                     language: _dataLanguages[base].nativeName,
33494                     code: code
33495                   });
33496                 }
33497               }
33498             }
33499
33500             return code; // if not found, use the code
33501           };
33502
33503           return localizer;
33504         }
33505
33506         // `presetCollection` is a wrapper around an `Array` of presets `collection`,
33507         // and decorated with some extra methods for searching and matching geometry
33508         //
33509
33510         function presetCollection(collection) {
33511           var MAXRESULTS = 50;
33512           var _this = {};
33513           var _memo = {};
33514           _this.collection = collection;
33515
33516           _this.item = function (id) {
33517             if (_memo[id]) return _memo[id];
33518
33519             var found = _this.collection.find(function (d) {
33520               return d.id === id;
33521             });
33522
33523             if (found) _memo[id] = found;
33524             return found;
33525           };
33526
33527           _this.index = function (id) {
33528             return _this.collection.findIndex(function (d) {
33529               return d.id === id;
33530             });
33531           };
33532
33533           _this.matchGeometry = function (geometry) {
33534             return presetCollection(_this.collection.filter(function (d) {
33535               return d.matchGeometry(geometry);
33536             }));
33537           };
33538
33539           _this.matchAllGeometry = function (geometries) {
33540             return presetCollection(_this.collection.filter(function (d) {
33541               return d && d.matchAllGeometry(geometries);
33542             }));
33543           };
33544
33545           _this.matchAnyGeometry = function (geometries) {
33546             return presetCollection(_this.collection.filter(function (d) {
33547               return geometries.some(function (geom) {
33548                 return d.matchGeometry(geom);
33549               });
33550             }));
33551           };
33552
33553           _this.fallback = function (geometry) {
33554             var id = geometry;
33555             if (id === 'vertex') id = 'point';
33556             return _this.item(id);
33557           };
33558
33559           _this.search = function (value, geometry, loc) {
33560             if (!value) return _this; // don't remove diacritical characters since we're assuming the user is being intentional
33561
33562             value = value.toLowerCase().trim(); // match at name beginning or just after a space (e.g. "office" -> match "Law Office")
33563
33564             function leading(a) {
33565               var index = a.indexOf(value);
33566               return index === 0 || a[index - 1] === ' ';
33567             } // match at name beginning only
33568
33569
33570             function leadingStrict(a) {
33571               var index = a.indexOf(value);
33572               return index === 0;
33573             }
33574
33575             function sortPresets(nameProp) {
33576               return function sortNames(a, b) {
33577                 var aCompare = a[nameProp]();
33578                 var bCompare = b[nameProp](); // priority if search string matches preset name exactly - #4325
33579
33580                 if (value === aCompare) return -1;
33581                 if (value === bCompare) return 1; // priority for higher matchScore
33582
33583                 var i = b.originalScore - a.originalScore;
33584                 if (i !== 0) return i; // priority if search string appears earlier in preset name
33585
33586                 i = aCompare.indexOf(value) - bCompare.indexOf(value);
33587                 if (i !== 0) return i; // priority for shorter preset names
33588
33589                 return aCompare.length - bCompare.length;
33590               };
33591             }
33592
33593             var pool = _this.collection;
33594
33595             if (Array.isArray(loc)) {
33596               var validLocations = _mainLocations.locationsAt(loc);
33597               pool = pool.filter(function (a) {
33598                 return !a.locationSetID || validLocations[a.locationSetID];
33599               });
33600             }
33601
33602             var searchable = pool.filter(function (a) {
33603               return a.searchable !== false && a.suggestion !== true;
33604             });
33605             var suggestions = pool.filter(function (a) {
33606               return a.suggestion === true;
33607             }); // matches value to preset.name
33608
33609             var leadingNames = searchable.filter(function (a) {
33610               return leading(a.searchName());
33611             }).sort(sortPresets('searchName')); // matches value to preset suggestion name
33612
33613             var leadingSuggestions = suggestions.filter(function (a) {
33614               return leadingStrict(a.searchName());
33615             }).sort(sortPresets('searchName'));
33616             var leadingNamesStripped = searchable.filter(function (a) {
33617               return leading(a.searchNameStripped());
33618             }).sort(sortPresets('searchNameStripped'));
33619             var leadingSuggestionsStripped = suggestions.filter(function (a) {
33620               return leadingStrict(a.searchNameStripped());
33621             }).sort(sortPresets('searchNameStripped')); // matches value to preset.terms values
33622
33623             var leadingTerms = searchable.filter(function (a) {
33624               return (a.terms() || []).some(leading);
33625             });
33626             var leadingSuggestionTerms = suggestions.filter(function (a) {
33627               return (a.terms() || []).some(leading);
33628             }); // matches value to preset.tags values
33629
33630             var leadingTagValues = searchable.filter(function (a) {
33631               return Object.values(a.tags || {}).filter(function (val) {
33632                 return val !== '*';
33633               }).some(leading);
33634             }); // finds close matches to value in preset.name
33635
33636             var similarName = searchable.map(function (a) {
33637               return {
33638                 preset: a,
33639                 dist: utilEditDistance(value, a.searchName())
33640               };
33641             }).filter(function (a) {
33642               return a.dist + Math.min(value.length - a.preset.searchName().length, 0) < 3;
33643             }).sort(function (a, b) {
33644               return a.dist - b.dist;
33645             }).map(function (a) {
33646               return a.preset;
33647             }); // finds close matches to value to preset suggestion name
33648
33649             var similarSuggestions = suggestions.map(function (a) {
33650               return {
33651                 preset: a,
33652                 dist: utilEditDistance(value, a.searchName())
33653               };
33654             }).filter(function (a) {
33655               return a.dist + Math.min(value.length - a.preset.searchName().length, 0) < 1;
33656             }).sort(function (a, b) {
33657               return a.dist - b.dist;
33658             }).map(function (a) {
33659               return a.preset;
33660             }); // finds close matches to value in preset.terms
33661
33662             var similarTerms = searchable.filter(function (a) {
33663               return (a.terms() || []).some(function (b) {
33664                 return utilEditDistance(value, b) + Math.min(value.length - b.length, 0) < 3;
33665               });
33666             });
33667             var results = leadingNames.concat(leadingSuggestions, leadingNamesStripped, leadingSuggestionsStripped, leadingTerms, leadingSuggestionTerms, leadingTagValues, similarName, similarSuggestions, similarTerms).slice(0, MAXRESULTS - 1);
33668
33669             if (geometry) {
33670               if (typeof geometry === 'string') {
33671                 results.push(_this.fallback(geometry));
33672               } else {
33673                 geometry.forEach(function (geom) {
33674                   return results.push(_this.fallback(geom));
33675                 });
33676               }
33677             }
33678
33679             return presetCollection(utilArrayUniq(results));
33680           };
33681
33682           return _this;
33683         }
33684
33685         // `presetCategory` builds a `presetCollection` of member presets,
33686         // decorated with some extra methods for searching and matching geometry
33687         //
33688
33689         function presetCategory(categoryID, category, allPresets) {
33690           var _this = Object.assign({}, category); // shallow copy
33691
33692
33693           var _searchName; // cache
33694
33695
33696           var _searchNameStripped; // cache
33697
33698
33699           _this.id = categoryID;
33700           _this.members = presetCollection((category.members || []).map(function (presetID) {
33701             return allPresets[presetID];
33702           }).filter(Boolean));
33703           _this.geometry = _this.members.collection.reduce(function (acc, preset) {
33704             for (var i in preset.geometry) {
33705               var geometry = preset.geometry[i];
33706
33707               if (acc.indexOf(geometry) === -1) {
33708                 acc.push(geometry);
33709               }
33710             }
33711
33712             return acc;
33713           }, []);
33714
33715           _this.matchGeometry = function (geom) {
33716             return _this.geometry.indexOf(geom) >= 0;
33717           };
33718
33719           _this.matchAllGeometry = function (geometries) {
33720             return _this.members.collection.some(function (preset) {
33721               return preset.matchAllGeometry(geometries);
33722             });
33723           };
33724
33725           _this.matchScore = function () {
33726             return -1;
33727           };
33728
33729           _this.name = function () {
33730             return _t("_tagging.presets.categories.".concat(categoryID, ".name"), {
33731               'default': categoryID
33732             });
33733           };
33734
33735           _this.nameLabel = function () {
33736             return _t.html("_tagging.presets.categories.".concat(categoryID, ".name"), {
33737               'default': categoryID
33738             });
33739           };
33740
33741           _this.terms = function () {
33742             return [];
33743           };
33744
33745           _this.searchName = function () {
33746             if (!_searchName) {
33747               _searchName = (_this.suggestion ? _this.originalName : _this.name()).toLowerCase();
33748             }
33749
33750             return _searchName;
33751           };
33752
33753           _this.searchNameStripped = function () {
33754             if (!_searchNameStripped) {
33755               _searchNameStripped = _this.searchName(); // split combined diacritical characters into their parts
33756
33757               if (_searchNameStripped.normalize) _searchNameStripped = _searchNameStripped.normalize('NFD'); // remove diacritics
33758
33759               _searchNameStripped = _searchNameStripped.replace(/[\u0300-\u036f]/g, '');
33760             }
33761
33762             return _searchNameStripped;
33763           };
33764
33765           return _this;
33766         }
33767
33768         // `presetField` decorates a given `field` Object
33769         // with some extra methods for searching and matching geometry
33770         //
33771
33772         function presetField(fieldID, field) {
33773           var _this = Object.assign({}, field); // shallow copy
33774
33775
33776           _this.id = fieldID; // for use in classes, element ids, css selectors
33777
33778           _this.safeid = utilSafeClassName(fieldID);
33779
33780           _this.matchGeometry = function (geom) {
33781             return !_this.geometry || _this.geometry.indexOf(geom) !== -1;
33782           };
33783
33784           _this.matchAllGeometry = function (geometries) {
33785             return !_this.geometry || geometries.every(function (geom) {
33786               return _this.geometry.indexOf(geom) !== -1;
33787             });
33788           };
33789
33790           _this.t = function (scope, options) {
33791             return _t("_tagging.presets.fields.".concat(fieldID, ".").concat(scope), options);
33792           };
33793
33794           _this.t.html = function (scope, options) {
33795             return _t.html("_tagging.presets.fields.".concat(fieldID, ".").concat(scope), options);
33796           };
33797
33798           _this.hasTextForStringId = function (scope) {
33799             return _mainLocalizer.hasTextForStringId("_tagging.presets.fields.".concat(fieldID, ".").concat(scope));
33800           };
33801
33802           _this.title = function () {
33803             return _this.overrideLabel || _this.t('label', {
33804               'default': fieldID
33805             });
33806           };
33807
33808           _this.label = function () {
33809             return _this.overrideLabel || _this.t.html('label', {
33810               'default': fieldID
33811             });
33812           };
33813
33814           var _placeholder = _this.placeholder;
33815
33816           _this.placeholder = function () {
33817             return _this.t('placeholder', {
33818               'default': _placeholder
33819             });
33820           };
33821
33822           _this.originalTerms = (_this.terms || []).join();
33823
33824           _this.terms = function () {
33825             return _this.t('terms', {
33826               'default': _this.originalTerms
33827             }).toLowerCase().trim().split(/\s*,+\s*/);
33828           };
33829
33830           _this.increment = _this.type === 'number' ? _this.increment || 1 : undefined;
33831           return _this;
33832         }
33833
33834         // `Array.prototype.lastIndexOf` method
33835         // https://tc39.es/ecma262/#sec-array.prototype.lastindexof
33836         // eslint-disable-next-line es/no-array-prototype-lastindexof -- required for testing
33837         _export({ target: 'Array', proto: true, forced: arrayLastIndexOf !== [].lastIndexOf }, {
33838           lastIndexOf: arrayLastIndexOf
33839         });
33840
33841         // `presetPreset` decorates a given `preset` Object
33842         // with some extra methods for searching and matching geometry
33843         //
33844
33845         function presetPreset(presetID, preset, addable, allFields, allPresets) {
33846           allFields = allFields || {};
33847           allPresets = allPresets || {};
33848
33849           var _this = Object.assign({}, preset); // shallow copy
33850
33851
33852           var _addable = addable || false;
33853
33854           var _resolvedFields; // cache
33855
33856
33857           var _resolvedMoreFields; // cache
33858
33859
33860           var _searchName; // cache
33861
33862
33863           var _searchNameStripped; // cache
33864
33865
33866           _this.id = presetID;
33867           _this.safeid = utilSafeClassName(presetID); // for use in css classes, selectors, element ids
33868
33869           _this.originalTerms = (_this.terms || []).join();
33870           _this.originalName = _this.name || '';
33871           _this.originalScore = _this.matchScore || 1;
33872           _this.originalReference = _this.reference || {};
33873           _this.originalFields = _this.fields || [];
33874           _this.originalMoreFields = _this.moreFields || [];
33875
33876           _this.fields = function () {
33877             return _resolvedFields || (_resolvedFields = resolve('fields'));
33878           };
33879
33880           _this.moreFields = function () {
33881             return _resolvedMoreFields || (_resolvedMoreFields = resolve('moreFields'));
33882           };
33883
33884           _this.resetFields = function () {
33885             return _resolvedFields = _resolvedMoreFields = null;
33886           };
33887
33888           _this.tags = _this.tags || {};
33889           _this.addTags = _this.addTags || _this.tags;
33890           _this.removeTags = _this.removeTags || _this.addTags;
33891           _this.geometry = _this.geometry || [];
33892
33893           _this.matchGeometry = function (geom) {
33894             return _this.geometry.indexOf(geom) >= 0;
33895           };
33896
33897           _this.matchAllGeometry = function (geoms) {
33898             return geoms.every(_this.matchGeometry);
33899           };
33900
33901           _this.matchScore = function (entityTags) {
33902             var tags = _this.tags;
33903             var seen = {};
33904             var score = 0; // match on tags
33905
33906             for (var k in tags) {
33907               seen[k] = true;
33908
33909               if (entityTags[k] === tags[k]) {
33910                 score += _this.originalScore;
33911               } else if (tags[k] === '*' && k in entityTags) {
33912                 score += _this.originalScore / 2;
33913               } else {
33914                 return -1;
33915               }
33916             } // boost score for additional matches in addTags - #6802
33917
33918
33919             var addTags = _this.addTags;
33920
33921             for (var _k in addTags) {
33922               if (!seen[_k] && entityTags[_k] === addTags[_k]) {
33923                 score += _this.originalScore;
33924               }
33925             }
33926
33927             return score;
33928           };
33929
33930           _this.t = function (scope, options) {
33931             var textID = "_tagging.presets.presets.".concat(presetID, ".").concat(scope);
33932             return _t(textID, options);
33933           };
33934
33935           _this.t.html = function (scope, options) {
33936             var textID = "_tagging.presets.presets.".concat(presetID, ".").concat(scope);
33937             return _t.html(textID, options);
33938           };
33939
33940           _this.name = function () {
33941             return _this.t('name', {
33942               'default': _this.originalName
33943             });
33944           };
33945
33946           _this.nameLabel = function () {
33947             return _this.t.html('name', {
33948               'default': _this.originalName
33949             });
33950           };
33951
33952           _this.subtitle = function () {
33953             if (_this.suggestion) {
33954               var path = presetID.split('/');
33955               path.pop(); // remove brand name
33956
33957               return _t('_tagging.presets.presets.' + path.join('/') + '.name');
33958             }
33959
33960             return null;
33961           };
33962
33963           _this.subtitleLabel = function () {
33964             if (_this.suggestion) {
33965               var path = presetID.split('/');
33966               path.pop(); // remove brand name
33967
33968               return _t.html('_tagging.presets.presets.' + path.join('/') + '.name');
33969             }
33970
33971             return null;
33972           };
33973
33974           _this.terms = function () {
33975             return _this.t('terms', {
33976               'default': _this.originalTerms
33977             }).toLowerCase().trim().split(/\s*,+\s*/);
33978           };
33979
33980           _this.searchName = function () {
33981             if (!_searchName) {
33982               _searchName = (_this.suggestion ? _this.originalName : _this.name()).toLowerCase();
33983             }
33984
33985             return _searchName;
33986           };
33987
33988           _this.searchNameStripped = function () {
33989             if (!_searchNameStripped) {
33990               _searchNameStripped = _this.searchName(); // split combined diacritical characters into their parts
33991
33992               if (_searchNameStripped.normalize) _searchNameStripped = _searchNameStripped.normalize('NFD'); // remove diacritics
33993
33994               _searchNameStripped = _searchNameStripped.replace(/[\u0300-\u036f]/g, '');
33995             }
33996
33997             return _searchNameStripped;
33998           };
33999
34000           _this.isFallback = function () {
34001             var tagCount = Object.keys(_this.tags).length;
34002             return tagCount === 0 || tagCount === 1 && _this.tags.hasOwnProperty('area');
34003           };
34004
34005           _this.addable = function (val) {
34006             if (!arguments.length) return _addable;
34007             _addable = val;
34008             return _this;
34009           };
34010
34011           _this.reference = function () {
34012             // Lookup documentation on Wikidata...
34013             var qid = _this.tags.wikidata || _this.tags['flag:wikidata'] || _this.tags['brand:wikidata'] || _this.tags['network:wikidata'] || _this.tags['operator:wikidata'];
34014
34015             if (qid) {
34016               return {
34017                 qid: qid
34018               };
34019             } // Lookup documentation on OSM Wikibase...
34020
34021
34022             var key = _this.originalReference.key || Object.keys(utilObjectOmit(_this.tags, 'name'))[0];
34023             var value = _this.originalReference.value || _this.tags[key];
34024
34025             if (value === '*') {
34026               return {
34027                 key: key
34028               };
34029             } else {
34030               return {
34031                 key: key,
34032                 value: value
34033               };
34034             }
34035           };
34036
34037           _this.unsetTags = function (tags, geometry, ignoringKeys, skipFieldDefaults) {
34038             // allow manually keeping some tags
34039             var removeTags = ignoringKeys ? utilObjectOmit(_this.removeTags, ignoringKeys) : _this.removeTags;
34040             tags = utilObjectOmit(tags, Object.keys(removeTags));
34041
34042             if (geometry && !skipFieldDefaults) {
34043               _this.fields().forEach(function (field) {
34044                 if (field.matchGeometry(geometry) && field.key && field["default"] === tags[field.key]) {
34045                   delete tags[field.key];
34046                 }
34047               });
34048             }
34049
34050             delete tags.area;
34051             return tags;
34052           };
34053
34054           _this.setTags = function (tags, geometry, skipFieldDefaults) {
34055             var addTags = _this.addTags;
34056             tags = Object.assign({}, tags); // shallow copy
34057
34058             for (var k in addTags) {
34059               if (addTags[k] === '*') {
34060                 // if this tag is ancillary, don't override an existing value since any value is okay
34061                 if (_this.tags[k] || !tags[k] || tags[k] === 'no') {
34062                   tags[k] = 'yes';
34063                 }
34064               } else {
34065                 tags[k] = addTags[k];
34066               }
34067             } // Add area=yes if necessary.
34068             // This is necessary if the geometry is already an area (e.g. user drew an area) AND any of:
34069             // 1. chosen preset could be either an area or a line (`barrier=city_wall`)
34070             // 2. chosen preset doesn't have a key in osmAreaKeys (`railway=station`)
34071
34072
34073             if (!addTags.hasOwnProperty('area')) {
34074               delete tags.area;
34075
34076               if (geometry === 'area') {
34077                 var needsAreaTag = true;
34078
34079                 if (_this.geometry.indexOf('line') === -1) {
34080                   for (var _k2 in addTags) {
34081                     if (_k2 in osmAreaKeys) {
34082                       needsAreaTag = false;
34083                       break;
34084                     }
34085                   }
34086                 }
34087
34088                 if (needsAreaTag) {
34089                   tags.area = 'yes';
34090                 }
34091               }
34092             }
34093
34094             if (geometry && !skipFieldDefaults) {
34095               _this.fields().forEach(function (field) {
34096                 if (field.matchGeometry(geometry) && field.key && !tags[field.key] && field["default"]) {
34097                   tags[field.key] = field["default"];
34098                 }
34099               });
34100             }
34101
34102             return tags;
34103           }; // For a preset without fields, use the fields of the parent preset.
34104           // Replace {preset} placeholders with the fields of the specified presets.
34105
34106
34107           function resolve(which) {
34108             var fieldIDs = which === 'fields' ? _this.originalFields : _this.originalMoreFields;
34109             var resolved = [];
34110             fieldIDs.forEach(function (fieldID) {
34111               var match = fieldID.match(/\{(.*)\}/);
34112
34113               if (match !== null) {
34114                 // a presetID wrapped in braces {}
34115                 resolved = resolved.concat(inheritFields(match[1], which));
34116               } else if (allFields[fieldID]) {
34117                 // a normal fieldID
34118                 resolved.push(allFields[fieldID]);
34119               } else {
34120                 console.log("Cannot resolve \"".concat(fieldID, "\" found in ").concat(_this.id, ".").concat(which)); // eslint-disable-line no-console
34121               }
34122             }); // no fields resolved, so use the parent's if possible
34123
34124             if (!resolved.length) {
34125               var endIndex = _this.id.lastIndexOf('/');
34126
34127               var parentID = endIndex && _this.id.substring(0, endIndex);
34128
34129               if (parentID) {
34130                 resolved = inheritFields(parentID, which);
34131               }
34132             }
34133
34134             return utilArrayUniq(resolved); // returns an array of fields to inherit from the given presetID, if found
34135
34136             function inheritFields(presetID, which) {
34137               var parent = allPresets[presetID];
34138               if (!parent) return [];
34139
34140               if (which === 'fields') {
34141                 return parent.fields().filter(shouldInherit);
34142               } else if (which === 'moreFields') {
34143                 return parent.moreFields();
34144               } else {
34145                 return [];
34146               }
34147             } // Skip `fields` for the keys which define the preset.
34148             // These are usually `typeCombo` fields like `shop=*`
34149
34150
34151             function shouldInherit(f) {
34152               if (f.key && _this.tags[f.key] !== undefined && // inherit anyway if multiple values are allowed or just a checkbox
34153               f.type !== 'multiCombo' && f.type !== 'semiCombo' && f.type !== 'manyCombo' && f.type !== 'check') return false;
34154               return true;
34155             }
34156           }
34157
34158           return _this;
34159         }
34160
34161         var _mainPresetIndex = presetIndex(); // singleton
34162         // `presetIndex` wraps a `presetCollection`
34163         // with methods for loading new data and returning defaults
34164         //
34165
34166         function presetIndex() {
34167           var dispatch = dispatch$8('favoritePreset', 'recentsChange');
34168           var MAXRECENTS = 30; // seed the preset lists with geometry fallbacks
34169
34170           var POINT = presetPreset('point', {
34171             name: 'Point',
34172             tags: {},
34173             geometry: ['point', 'vertex'],
34174             matchScore: 0.1
34175           });
34176           var LINE = presetPreset('line', {
34177             name: 'Line',
34178             tags: {},
34179             geometry: ['line'],
34180             matchScore: 0.1
34181           });
34182           var AREA = presetPreset('area', {
34183             name: 'Area',
34184             tags: {
34185               area: 'yes'
34186             },
34187             geometry: ['area'],
34188             matchScore: 0.1
34189           });
34190           var RELATION = presetPreset('relation', {
34191             name: 'Relation',
34192             tags: {},
34193             geometry: ['relation'],
34194             matchScore: 0.1
34195           });
34196
34197           var _this = presetCollection([POINT, LINE, AREA, RELATION]);
34198
34199           var _presets = {
34200             point: POINT,
34201             line: LINE,
34202             area: AREA,
34203             relation: RELATION
34204           };
34205           var _defaults = {
34206             point: presetCollection([POINT]),
34207             vertex: presetCollection([POINT]),
34208             line: presetCollection([LINE]),
34209             area: presetCollection([AREA]),
34210             relation: presetCollection([RELATION])
34211           };
34212           var _fields = {};
34213           var _categories = {};
34214           var _universal = [];
34215           var _addablePresetIDs = null; // Set of preset IDs that the user can add
34216
34217           var _recents;
34218
34219           var _favorites; // Index of presets by (geometry, tag key).
34220
34221
34222           var _geometryIndex = {
34223             point: {},
34224             vertex: {},
34225             line: {},
34226             area: {},
34227             relation: {}
34228           };
34229
34230           var _loadPromise;
34231
34232           _this.ensureLoaded = function () {
34233             if (_loadPromise) return _loadPromise;
34234             return _loadPromise = Promise.all([_mainFileFetcher.get('preset_categories'), _mainFileFetcher.get('preset_defaults'), _mainFileFetcher.get('preset_presets'), _mainFileFetcher.get('preset_fields')]).then(function (vals) {
34235               _this.merge({
34236                 categories: vals[0],
34237                 defaults: vals[1],
34238                 presets: vals[2],
34239                 fields: vals[3]
34240               });
34241
34242               osmSetAreaKeys(_this.areaKeys());
34243               osmSetPointTags(_this.pointTags());
34244               osmSetVertexTags(_this.vertexTags());
34245             });
34246           }; // `merge` accepts an object containing new preset data (all properties optional):
34247           // {
34248           //   fields: {},
34249           //   presets: {},
34250           //   categories: {},
34251           //   defaults: {},
34252           //   featureCollection: {}
34253           //}
34254
34255
34256           _this.merge = function (d) {
34257             var newLocationSets = []; // Merge Fields
34258
34259             if (d.fields) {
34260               Object.keys(d.fields).forEach(function (fieldID) {
34261                 var f = d.fields[fieldID];
34262
34263                 if (f) {
34264                   // add or replace
34265                   f = presetField(fieldID, f);
34266                   if (f.locationSet) newLocationSets.push(f);
34267                   _fields[fieldID] = f;
34268                 } else {
34269                   // remove
34270                   delete _fields[fieldID];
34271                 }
34272               });
34273             } // Merge Presets
34274
34275
34276             if (d.presets) {
34277               Object.keys(d.presets).forEach(function (presetID) {
34278                 var p = d.presets[presetID];
34279
34280                 if (p) {
34281                   // add or replace
34282                   var isAddable = !_addablePresetIDs || _addablePresetIDs.has(presetID);
34283
34284                   p = presetPreset(presetID, p, isAddable, _fields, _presets);
34285                   if (p.locationSet) newLocationSets.push(p);
34286                   _presets[presetID] = p;
34287                 } else {
34288                   // remove (but not if it's a fallback)
34289                   var existing = _presets[presetID];
34290
34291                   if (existing && !existing.isFallback()) {
34292                     delete _presets[presetID];
34293                   }
34294                 }
34295               });
34296             } // Merge Categories
34297
34298
34299             if (d.categories) {
34300               Object.keys(d.categories).forEach(function (categoryID) {
34301                 var c = d.categories[categoryID];
34302
34303                 if (c) {
34304                   // add or replace
34305                   c = presetCategory(categoryID, c, _presets);
34306                   if (c.locationSet) newLocationSets.push(c);
34307                   _categories[categoryID] = c;
34308                 } else {
34309                   // remove
34310                   delete _categories[categoryID];
34311                 }
34312               });
34313             } // Rebuild _this.collection after changing presets and categories
34314
34315
34316             _this.collection = Object.values(_presets).concat(Object.values(_categories)); // Merge Defaults
34317
34318             if (d.defaults) {
34319               Object.keys(d.defaults).forEach(function (geometry) {
34320                 var def = d.defaults[geometry];
34321
34322                 if (Array.isArray(def)) {
34323                   // add or replace
34324                   _defaults[geometry] = presetCollection(def.map(function (id) {
34325                     return _presets[id] || _categories[id];
34326                   }).filter(Boolean));
34327                 } else {
34328                   // remove
34329                   delete _defaults[geometry];
34330                 }
34331               });
34332             } // Rebuild universal fields array
34333
34334
34335             _universal = Object.values(_fields).filter(function (field) {
34336               return field.universal;
34337             }); // Reset all the preset fields - they'll need to be resolved again
34338
34339             Object.values(_presets).forEach(function (preset) {
34340               return preset.resetFields();
34341             }); // Rebuild geometry index
34342
34343             _geometryIndex = {
34344               point: {},
34345               vertex: {},
34346               line: {},
34347               area: {},
34348               relation: {}
34349             };
34350
34351             _this.collection.forEach(function (preset) {
34352               (preset.geometry || []).forEach(function (geometry) {
34353                 var g = _geometryIndex[geometry];
34354
34355                 for (var key in preset.tags) {
34356                   (g[key] = g[key] || []).push(preset);
34357                 }
34358               });
34359             }); // Merge Custom Features
34360
34361
34362             if (d.featureCollection && Array.isArray(d.featureCollection.features)) {
34363               _mainLocations.mergeCustomGeoJSON(d.featureCollection);
34364             } // Resolve all locationSet features.
34365
34366
34367             if (newLocationSets.length) {
34368               _mainLocations.mergeLocationSets(newLocationSets);
34369             }
34370
34371             return _this;
34372           };
34373
34374           _this.match = function (entity, resolver) {
34375             return resolver["transient"](entity, 'presetMatch', function () {
34376               var geometry = entity.geometry(resolver); // Treat entities on addr:interpolation lines as points, not vertices - #3241
34377
34378               if (geometry === 'vertex' && entity.isOnAddressLine(resolver)) {
34379                 geometry = 'point';
34380               }
34381
34382               var entityExtent = entity.extent(resolver);
34383               return _this.matchTags(entity.tags, geometry, entityExtent.center());
34384             });
34385           };
34386
34387           _this.matchTags = function (tags, geometry, loc) {
34388             var geometryMatches = _geometryIndex[geometry];
34389             var address;
34390             var best = -1;
34391             var match;
34392             var validLocations;
34393
34394             if (Array.isArray(loc)) {
34395               validLocations = _mainLocations.locationsAt(loc);
34396             }
34397
34398             for (var k in tags) {
34399               // If any part of an address is present, allow fallback to "Address" preset - #4353
34400               if (/^addr:/.test(k) && geometryMatches['addr:*']) {
34401                 address = geometryMatches['addr:*'][0];
34402               }
34403
34404               var keyMatches = geometryMatches[k];
34405               if (!keyMatches) continue;
34406
34407               for (var i = 0; i < keyMatches.length; i++) {
34408                 var candidate = keyMatches[i]; // discard candidate preset if location is not valid at `loc`
34409
34410                 if (validLocations && candidate.locationSetID) {
34411                   if (!validLocations[candidate.locationSetID]) continue;
34412                 }
34413
34414                 var score = candidate.matchScore(tags);
34415
34416                 if (score > best) {
34417                   best = score;
34418                   match = candidate;
34419                 }
34420               }
34421             }
34422
34423             if (address && (!match || match.isFallback())) {
34424               match = address;
34425             }
34426
34427             return match || _this.fallback(geometry);
34428           };
34429
34430           _this.allowsVertex = function (entity, resolver) {
34431             if (entity.type !== 'node') return false;
34432             if (Object.keys(entity.tags).length === 0) return true;
34433             return resolver["transient"](entity, 'vertexMatch', function () {
34434               // address lines allow vertices to act as standalone points
34435               if (entity.isOnAddressLine(resolver)) return true;
34436               var geometries = osmNodeGeometriesForTags(entity.tags);
34437               if (geometries.vertex) return true;
34438               if (geometries.point) return false; // allow vertices for unspecified points
34439
34440               return true;
34441             });
34442           }; // Because of the open nature of tagging, iD will never have a complete
34443           // list of tags used in OSM, so we want it to have logic like "assume
34444           // that a closed way with an amenity tag is an area, unless the amenity
34445           // is one of these specific types". This function computes a structure
34446           // that allows testing of such conditions, based on the presets designated
34447           // as as supporting (or not supporting) the area geometry.
34448           //
34449           // The returned object L is a keeplist/discardlist of tags. A closed way
34450           // with a tag (k, v) is considered to be an area if `k in L && !(v in L[k])`
34451           // (see `Way#isArea()`). In other words, the keys of L form the keeplist,
34452           // and the subkeys form the discardlist.
34453
34454
34455           _this.areaKeys = function () {
34456             // The ignore list is for keys that imply lines. (We always add `area=yes` for exceptions)
34457             var ignore = ['barrier', 'highway', 'footway', 'railway', 'junction', 'type'];
34458             var areaKeys = {}; // ignore name-suggestion-index and deprecated presets
34459
34460             var presets = _this.collection.filter(function (p) {
34461               return !p.suggestion && !p.replacement;
34462             }); // keeplist
34463
34464
34465             presets.forEach(function (p) {
34466               var keys = p.tags && Object.keys(p.tags);
34467               var key = keys && keys.length && keys[0]; // pick the first tag
34468
34469               if (!key) return;
34470               if (ignore.indexOf(key) !== -1) return;
34471
34472               if (p.geometry.indexOf('area') !== -1) {
34473                 // probably an area..
34474                 areaKeys[key] = areaKeys[key] || {};
34475               }
34476             }); // discardlist
34477
34478             presets.forEach(function (p) {
34479               var key;
34480
34481               for (key in p.addTags) {
34482                 // examine all addTags to get a better sense of what can be tagged on lines - #6800
34483                 var value = p.addTags[key];
34484
34485                 if (key in areaKeys && // probably an area...
34486                 p.geometry.indexOf('line') !== -1 && // but sometimes a line
34487                 value !== '*') {
34488                   areaKeys[key][value] = true;
34489                 }
34490               }
34491             });
34492             return areaKeys;
34493           };
34494
34495           _this.pointTags = function () {
34496             return _this.collection.reduce(function (pointTags, d) {
34497               // ignore name-suggestion-index, deprecated, and generic presets
34498               if (d.suggestion || d.replacement || d.searchable === false) return pointTags; // only care about the primary tag
34499
34500               var keys = d.tags && Object.keys(d.tags);
34501               var key = keys && keys.length && keys[0]; // pick the first tag
34502
34503               if (!key) return pointTags; // if this can be a point
34504
34505               if (d.geometry.indexOf('point') !== -1) {
34506                 pointTags[key] = pointTags[key] || {};
34507                 pointTags[key][d.tags[key]] = true;
34508               }
34509
34510               return pointTags;
34511             }, {});
34512           };
34513
34514           _this.vertexTags = function () {
34515             return _this.collection.reduce(function (vertexTags, d) {
34516               // ignore name-suggestion-index, deprecated, and generic presets
34517               if (d.suggestion || d.replacement || d.searchable === false) return vertexTags; // only care about the primary tag
34518
34519               var keys = d.tags && Object.keys(d.tags);
34520               var key = keys && keys.length && keys[0]; // pick the first tag
34521
34522               if (!key) return vertexTags; // if this can be a vertex
34523
34524               if (d.geometry.indexOf('vertex') !== -1) {
34525                 vertexTags[key] = vertexTags[key] || {};
34526                 vertexTags[key][d.tags[key]] = true;
34527               }
34528
34529               return vertexTags;
34530             }, {});
34531           };
34532
34533           _this.field = function (id) {
34534             return _fields[id];
34535           };
34536
34537           _this.universal = function () {
34538             return _universal;
34539           };
34540
34541           _this.defaults = function (geometry, n, startWithRecents, loc) {
34542             var recents = [];
34543
34544             if (startWithRecents) {
34545               recents = _this.recent().matchGeometry(geometry).collection.slice(0, 4);
34546             }
34547
34548             var defaults;
34549
34550             if (_addablePresetIDs) {
34551               defaults = Array.from(_addablePresetIDs).map(function (id) {
34552                 var preset = _this.item(id);
34553
34554                 if (preset && preset.matchGeometry(geometry)) return preset;
34555                 return null;
34556               }).filter(Boolean);
34557             } else {
34558               defaults = _defaults[geometry].collection.concat(_this.fallback(geometry));
34559             }
34560
34561             var result = presetCollection(utilArrayUniq(recents.concat(defaults)).slice(0, n - 1));
34562
34563             if (Array.isArray(loc)) {
34564               var validLocations = _mainLocations.locationsAt(loc);
34565               result.collection = result.collection.filter(function (a) {
34566                 return !a.locationSetID || validLocations[a.locationSetID];
34567               });
34568             }
34569
34570             return result;
34571           }; // pass a Set of addable preset ids
34572
34573
34574           _this.addablePresetIDs = function (val) {
34575             if (!arguments.length) return _addablePresetIDs; // accept and convert arrays
34576
34577             if (Array.isArray(val)) val = new Set(val);
34578             _addablePresetIDs = val;
34579
34580             if (_addablePresetIDs) {
34581               // reset all presets
34582               _this.collection.forEach(function (p) {
34583                 // categories aren't addable
34584                 if (p.addable) p.addable(_addablePresetIDs.has(p.id));
34585               });
34586             } else {
34587               _this.collection.forEach(function (p) {
34588                 if (p.addable) p.addable(true);
34589               });
34590             }
34591
34592             return _this;
34593           };
34594
34595           _this.recent = function () {
34596             return presetCollection(utilArrayUniq(_this.getRecents().map(function (d) {
34597               return d.preset;
34598             })));
34599           };
34600
34601           function RibbonItem(preset, source) {
34602             var item = {};
34603             item.preset = preset;
34604             item.source = source;
34605
34606             item.isFavorite = function () {
34607               return item.source === 'favorite';
34608             };
34609
34610             item.isRecent = function () {
34611               return item.source === 'recent';
34612             };
34613
34614             item.matches = function (preset) {
34615               return item.preset.id === preset.id;
34616             };
34617
34618             item.minified = function () {
34619               return {
34620                 pID: item.preset.id
34621               };
34622             };
34623
34624             return item;
34625           }
34626
34627           function ribbonItemForMinified(d, source) {
34628             if (d && d.pID) {
34629               var preset = _this.item(d.pID);
34630
34631               if (!preset) return null;
34632               return RibbonItem(preset, source);
34633             }
34634
34635             return null;
34636           }
34637
34638           _this.getGenericRibbonItems = function () {
34639             return ['point', 'line', 'area'].map(function (id) {
34640               return RibbonItem(_this.item(id), 'generic');
34641             });
34642           };
34643
34644           _this.getAddable = function () {
34645             if (!_addablePresetIDs) return [];
34646             return _addablePresetIDs.map(function (id) {
34647               var preset = _this.item(id);
34648
34649               if (preset) return RibbonItem(preset, 'addable');
34650               return null;
34651             }).filter(Boolean);
34652           };
34653
34654           function setRecents(items) {
34655             _recents = items;
34656             var minifiedItems = items.map(function (d) {
34657               return d.minified();
34658             });
34659             corePreferences('preset_recents', JSON.stringify(minifiedItems));
34660             dispatch.call('recentsChange');
34661           }
34662
34663           _this.getRecents = function () {
34664             if (!_recents) {
34665               // fetch from local storage
34666               _recents = (JSON.parse(corePreferences('preset_recents')) || []).reduce(function (acc, d) {
34667                 var item = ribbonItemForMinified(d, 'recent');
34668                 if (item && item.preset.addable()) acc.push(item);
34669                 return acc;
34670               }, []);
34671             }
34672
34673             return _recents;
34674           };
34675
34676           _this.addRecent = function (preset, besidePreset, after) {
34677             var recents = _this.getRecents();
34678
34679             var beforeItem = _this.recentMatching(besidePreset);
34680
34681             var toIndex = recents.indexOf(beforeItem);
34682             if (after) toIndex += 1;
34683             var newItem = RibbonItem(preset, 'recent');
34684             recents.splice(toIndex, 0, newItem);
34685             setRecents(recents);
34686           };
34687
34688           _this.removeRecent = function (preset) {
34689             var item = _this.recentMatching(preset);
34690
34691             if (item) {
34692               var items = _this.getRecents();
34693
34694               items.splice(items.indexOf(item), 1);
34695               setRecents(items);
34696             }
34697           };
34698
34699           _this.recentMatching = function (preset) {
34700             var items = _this.getRecents();
34701
34702             for (var i in items) {
34703               if (items[i].matches(preset)) {
34704                 return items[i];
34705               }
34706             }
34707
34708             return null;
34709           };
34710
34711           _this.moveItem = function (items, fromIndex, toIndex) {
34712             if (fromIndex === toIndex || fromIndex < 0 || toIndex < 0 || fromIndex >= items.length || toIndex >= items.length) return null;
34713             items.splice(toIndex, 0, items.splice(fromIndex, 1)[0]);
34714             return items;
34715           };
34716
34717           _this.moveRecent = function (item, beforeItem) {
34718             var recents = _this.getRecents();
34719
34720             var fromIndex = recents.indexOf(item);
34721             var toIndex = recents.indexOf(beforeItem);
34722
34723             var items = _this.moveItem(recents, fromIndex, toIndex);
34724
34725             if (items) setRecents(items);
34726           };
34727
34728           _this.setMostRecent = function (preset) {
34729             if (preset.searchable === false) return;
34730
34731             var items = _this.getRecents();
34732
34733             var item = _this.recentMatching(preset);
34734
34735             if (item) {
34736               items.splice(items.indexOf(item), 1);
34737             } else {
34738               item = RibbonItem(preset, 'recent');
34739             } // remove the last recent (first in, first out)
34740
34741
34742             while (items.length >= MAXRECENTS) {
34743               items.pop();
34744             } // prepend array
34745
34746
34747             items.unshift(item);
34748             setRecents(items);
34749           };
34750
34751           function setFavorites(items) {
34752             _favorites = items;
34753             var minifiedItems = items.map(function (d) {
34754               return d.minified();
34755             });
34756             corePreferences('preset_favorites', JSON.stringify(minifiedItems)); // call update
34757
34758             dispatch.call('favoritePreset');
34759           }
34760
34761           _this.addFavorite = function (preset, besidePreset, after) {
34762             var favorites = _this.getFavorites();
34763
34764             var beforeItem = _this.favoriteMatching(besidePreset);
34765
34766             var toIndex = favorites.indexOf(beforeItem);
34767             if (after) toIndex += 1;
34768             var newItem = RibbonItem(preset, 'favorite');
34769             favorites.splice(toIndex, 0, newItem);
34770             setFavorites(favorites);
34771           };
34772
34773           _this.toggleFavorite = function (preset) {
34774             var favs = _this.getFavorites();
34775
34776             var favorite = _this.favoriteMatching(preset);
34777
34778             if (favorite) {
34779               favs.splice(favs.indexOf(favorite), 1);
34780             } else {
34781               // only allow 10 favorites
34782               if (favs.length === 10) {
34783                 // remove the last favorite (last in, first out)
34784                 favs.pop();
34785               } // append array
34786
34787
34788               favs.push(RibbonItem(preset, 'favorite'));
34789             }
34790
34791             setFavorites(favs);
34792           };
34793
34794           _this.removeFavorite = function (preset) {
34795             var item = _this.favoriteMatching(preset);
34796
34797             if (item) {
34798               var items = _this.getFavorites();
34799
34800               items.splice(items.indexOf(item), 1);
34801               setFavorites(items);
34802             }
34803           };
34804
34805           _this.getFavorites = function () {
34806             if (!_favorites) {
34807               // fetch from local storage
34808               var rawFavorites = JSON.parse(corePreferences('preset_favorites'));
34809
34810               if (!rawFavorites) {
34811                 rawFavorites = [];
34812                 corePreferences('preset_favorites', JSON.stringify(rawFavorites));
34813               }
34814
34815               _favorites = rawFavorites.reduce(function (output, d) {
34816                 var item = ribbonItemForMinified(d, 'favorite');
34817                 if (item && item.preset.addable()) output.push(item);
34818                 return output;
34819               }, []);
34820             }
34821
34822             return _favorites;
34823           };
34824
34825           _this.favoriteMatching = function (preset) {
34826             var favs = _this.getFavorites();
34827
34828             for (var index in favs) {
34829               if (favs[index].matches(preset)) {
34830                 return favs[index];
34831               }
34832             }
34833
34834             return null;
34835           };
34836
34837           return utilRebind(_this, dispatch, 'on');
34838         }
34839
34840         function utilTagText(entity) {
34841           var obj = entity && entity.tags || {};
34842           return Object.keys(obj).map(function (k) {
34843             return k + '=' + obj[k];
34844           }).join(', ');
34845         }
34846         function utilTotalExtent(array, graph) {
34847           var extent = geoExtent();
34848           var val, entity;
34849
34850           for (var i = 0; i < array.length; i++) {
34851             val = array[i];
34852             entity = typeof val === 'string' ? graph.hasEntity(val) : val;
34853
34854             if (entity) {
34855               extent._extend(entity.extent(graph));
34856             }
34857           }
34858
34859           return extent;
34860         }
34861         function utilTagDiff(oldTags, newTags) {
34862           var tagDiff = [];
34863           var keys = utilArrayUnion(Object.keys(oldTags), Object.keys(newTags)).sort();
34864           keys.forEach(function (k) {
34865             var oldVal = oldTags[k];
34866             var newVal = newTags[k];
34867
34868             if ((oldVal || oldVal === '') && (newVal === undefined || newVal !== oldVal)) {
34869               tagDiff.push({
34870                 type: '-',
34871                 key: k,
34872                 oldVal: oldVal,
34873                 newVal: newVal,
34874                 display: '- ' + k + '=' + oldVal
34875               });
34876             }
34877
34878             if ((newVal || newVal === '') && (oldVal === undefined || newVal !== oldVal)) {
34879               tagDiff.push({
34880                 type: '+',
34881                 key: k,
34882                 oldVal: oldVal,
34883                 newVal: newVal,
34884                 display: '+ ' + k + '=' + newVal
34885               });
34886             }
34887           });
34888           return tagDiff;
34889         }
34890         function utilEntitySelector(ids) {
34891           return ids.length ? '.' + ids.join(',.') : 'nothing';
34892         } // returns an selector to select entity ids for:
34893         //  - entityIDs passed in
34894         //  - shallow descendant entityIDs for any of those entities that are relations
34895
34896         function utilEntityOrMemberSelector(ids, graph) {
34897           var seen = new Set(ids);
34898           ids.forEach(collectShallowDescendants);
34899           return utilEntitySelector(Array.from(seen));
34900
34901           function collectShallowDescendants(id) {
34902             var entity = graph.hasEntity(id);
34903             if (!entity || entity.type !== 'relation') return;
34904             entity.members.map(function (member) {
34905               return member.id;
34906             }).forEach(function (id) {
34907               seen.add(id);
34908             });
34909           }
34910         } // returns an selector to select entity ids for:
34911         //  - entityIDs passed in
34912         //  - deep descendant entityIDs for any of those entities that are relations
34913
34914         function utilEntityOrDeepMemberSelector(ids, graph) {
34915           return utilEntitySelector(utilEntityAndDeepMemberIDs(ids, graph));
34916         } // returns an selector to select entity ids for:
34917         //  - entityIDs passed in
34918         //  - deep descendant entityIDs for any of those entities that are relations
34919
34920         function utilEntityAndDeepMemberIDs(ids, graph) {
34921           var seen = new Set();
34922           ids.forEach(collectDeepDescendants);
34923           return Array.from(seen);
34924
34925           function collectDeepDescendants(id) {
34926             if (seen.has(id)) return;
34927             seen.add(id);
34928             var entity = graph.hasEntity(id);
34929             if (!entity || entity.type !== 'relation') return;
34930             entity.members.map(function (member) {
34931               return member.id;
34932             }).forEach(collectDeepDescendants); // recurse
34933           }
34934         } // returns an selector to select entity ids for:
34935         //  - deep descendant entityIDs for any of those entities that are relations
34936
34937         function utilDeepMemberSelector(ids, graph, skipMultipolgonMembers) {
34938           var idsSet = new Set(ids);
34939           var seen = new Set();
34940           var returners = new Set();
34941           ids.forEach(collectDeepDescendants);
34942           return utilEntitySelector(Array.from(returners));
34943
34944           function collectDeepDescendants(id) {
34945             if (seen.has(id)) return;
34946             seen.add(id);
34947
34948             if (!idsSet.has(id)) {
34949               returners.add(id);
34950             }
34951
34952             var entity = graph.hasEntity(id);
34953             if (!entity || entity.type !== 'relation') return;
34954             if (skipMultipolgonMembers && entity.isMultipolygon()) return;
34955             entity.members.map(function (member) {
34956               return member.id;
34957             }).forEach(collectDeepDescendants); // recurse
34958           }
34959         } // Adds or removes highlight styling for the specified entities
34960
34961         function utilHighlightEntities(ids, highlighted, context) {
34962           context.surface().selectAll(utilEntityOrDeepMemberSelector(ids, context.graph())).classed('highlighted', highlighted);
34963         } // returns an Array that is the union of:
34964         //  - nodes for any nodeIDs passed in
34965         //  - child nodes of any wayIDs passed in
34966         //  - descendant member and child nodes of relationIDs passed in
34967
34968         function utilGetAllNodes(ids, graph) {
34969           var seen = new Set();
34970           var nodes = new Set();
34971           ids.forEach(collectNodes);
34972           return Array.from(nodes);
34973
34974           function collectNodes(id) {
34975             if (seen.has(id)) return;
34976             seen.add(id);
34977             var entity = graph.hasEntity(id);
34978             if (!entity) return;
34979
34980             if (entity.type === 'node') {
34981               nodes.add(entity);
34982             } else if (entity.type === 'way') {
34983               entity.nodes.forEach(collectNodes);
34984             } else {
34985               entity.members.map(function (member) {
34986                 return member.id;
34987               }).forEach(collectNodes); // recurse
34988             }
34989           }
34990         }
34991         function utilDisplayName(entity) {
34992           var localizedNameKey = 'name:' + _mainLocalizer.languageCode().toLowerCase();
34993           var name = entity.tags[localizedNameKey] || entity.tags.name || '';
34994           if (name) return name;
34995           var tags = {
34996             direction: entity.tags.direction,
34997             from: entity.tags.from,
34998             network: entity.tags.cycle_network || entity.tags.network,
34999             ref: entity.tags.ref,
35000             to: entity.tags.to,
35001             via: entity.tags.via
35002           };
35003           var keyComponents = [];
35004
35005           if (tags.network) {
35006             keyComponents.push('network');
35007           }
35008
35009           if (tags.ref) {
35010             keyComponents.push('ref');
35011           } // Routes may need more disambiguation based on direction or destination
35012
35013
35014           if (entity.tags.route) {
35015             if (tags.direction) {
35016               keyComponents.push('direction');
35017             } else if (tags.from && tags.to) {
35018               keyComponents.push('from');
35019               keyComponents.push('to');
35020
35021               if (tags.via) {
35022                 keyComponents.push('via');
35023               }
35024             }
35025           }
35026
35027           if (keyComponents.length) {
35028             name = _t('inspector.display_name.' + keyComponents.join('_'), tags);
35029           }
35030
35031           return name;
35032         }
35033         function utilDisplayNameForPath(entity) {
35034           var name = utilDisplayName(entity);
35035           var isFirefox = utilDetect().browser.toLowerCase().indexOf('firefox') > -1;
35036
35037           if (!isFirefox && name && rtlRegex.test(name)) {
35038             name = fixRTLTextForSvg(name);
35039           }
35040
35041           return name;
35042         }
35043         function utilDisplayType(id) {
35044           return {
35045             n: _t('inspector.node'),
35046             w: _t('inspector.way'),
35047             r: _t('inspector.relation')
35048           }[id.charAt(0)];
35049         } // `utilDisplayLabel`
35050         // Returns a string suitable for display
35051         // By default returns something like name/ref, fallback to preset type, fallback to OSM type
35052         //   "Main Street" or "Tertiary Road"
35053         // If `verbose=true`, include both preset name and feature name.
35054         //   "Tertiary Road Main Street"
35055         //
35056
35057         function utilDisplayLabel(entity, graphOrGeometry, verbose) {
35058           var result;
35059           var displayName = utilDisplayName(entity);
35060           var preset = typeof graphOrGeometry === 'string' ? _mainPresetIndex.matchTags(entity.tags, graphOrGeometry) : _mainPresetIndex.match(entity, graphOrGeometry);
35061           var presetName = preset && (preset.suggestion ? preset.subtitle() : preset.name());
35062
35063           if (verbose) {
35064             result = [presetName, displayName].filter(Boolean).join(' ');
35065           } else {
35066             result = displayName || presetName;
35067           } // Fallback to the OSM type (node/way/relation)
35068
35069
35070           return result || utilDisplayType(entity.id);
35071         }
35072         function utilEntityRoot(entityType) {
35073           return {
35074             node: 'n',
35075             way: 'w',
35076             relation: 'r'
35077           }[entityType];
35078         } // Returns a single object containing the tags of all the given entities.
35079         // Example:
35080         // {
35081         //   highway: 'service',
35082         //   service: 'parking_aisle'
35083         // }
35084         //           +
35085         // {
35086         //   highway: 'service',
35087         //   service: 'driveway',
35088         //   width: '3'
35089         // }
35090         //           =
35091         // {
35092         //   highway: 'service',
35093         //   service: [ 'driveway', 'parking_aisle' ],
35094         //   width: [ '3', undefined ]
35095         // }
35096
35097         function utilCombinedTags(entityIDs, graph) {
35098           var tags = {};
35099           var tagCounts = {};
35100           var allKeys = new Set();
35101           var entities = entityIDs.map(function (entityID) {
35102             return graph.hasEntity(entityID);
35103           }).filter(Boolean); // gather the aggregate keys
35104
35105           entities.forEach(function (entity) {
35106             var keys = Object.keys(entity.tags).filter(Boolean);
35107             keys.forEach(function (key) {
35108               allKeys.add(key);
35109             });
35110           });
35111           entities.forEach(function (entity) {
35112             allKeys.forEach(function (key) {
35113               var value = entity.tags[key]; // purposely allow `undefined`
35114
35115               if (!tags.hasOwnProperty(key)) {
35116                 // first value, set as raw
35117                 tags[key] = value;
35118               } else {
35119                 if (!Array.isArray(tags[key])) {
35120                   if (tags[key] !== value) {
35121                     // first alternate value, replace single value with array
35122                     tags[key] = [tags[key], value];
35123                   }
35124                 } else {
35125                   // type is array
35126                   if (tags[key].indexOf(value) === -1) {
35127                     // subsequent alternate value, add to array
35128                     tags[key].push(value);
35129                   }
35130                 }
35131               }
35132
35133               var tagHash = key + '=' + value;
35134               if (!tagCounts[tagHash]) tagCounts[tagHash] = 0;
35135               tagCounts[tagHash] += 1;
35136             });
35137           });
35138
35139           for (var key in tags) {
35140             if (!Array.isArray(tags[key])) continue; // sort values by frequency then alphabetically
35141
35142             tags[key] = tags[key].sort(function (val1, val2) {
35143               var key = key; // capture
35144
35145               var count2 = tagCounts[key + '=' + val2];
35146               var count1 = tagCounts[key + '=' + val1];
35147
35148               if (count2 !== count1) {
35149                 return count2 - count1;
35150               }
35151
35152               if (val2 && val1) {
35153                 return val1.localeCompare(val2);
35154               }
35155
35156               return val1 ? 1 : -1;
35157             });
35158           }
35159
35160           return tags;
35161         }
35162         function utilStringQs(str) {
35163           var i = 0; // advance past any leading '?' or '#' characters
35164
35165           while (i < str.length && (str[i] === '?' || str[i] === '#')) {
35166             i++;
35167           }
35168
35169           str = str.slice(i);
35170           return str.split('&').reduce(function (obj, pair) {
35171             var parts = pair.split('=');
35172
35173             if (parts.length === 2) {
35174               obj[parts[0]] = null === parts[1] ? '' : decodeURIComponent(parts[1]);
35175             }
35176
35177             return obj;
35178           }, {});
35179         }
35180         function utilQsString(obj, noencode) {
35181           // encode everything except special characters used in certain hash parameters:
35182           // "/" in map states, ":", ",", {" and "}" in background
35183           function softEncode(s) {
35184             return encodeURIComponent(s).replace(/(%2F|%3A|%2C|%7B|%7D)/g, decodeURIComponent);
35185           }
35186
35187           return Object.keys(obj).sort().map(function (key) {
35188             return encodeURIComponent(key) + '=' + (noencode ? softEncode(obj[key]) : encodeURIComponent(obj[key]));
35189           }).join('&');
35190         }
35191         function utilPrefixDOMProperty(property) {
35192           var prefixes = ['webkit', 'ms', 'moz', 'o'];
35193           var i = -1;
35194           var n = prefixes.length;
35195           var s = document.body;
35196           if (property in s) return property;
35197           property = property.substr(0, 1).toUpperCase() + property.substr(1);
35198
35199           while (++i < n) {
35200             if (prefixes[i] + property in s) {
35201               return prefixes[i] + property;
35202             }
35203           }
35204
35205           return false;
35206         }
35207         function utilPrefixCSSProperty(property) {
35208           var prefixes = ['webkit', 'ms', 'Moz', 'O'];
35209           var i = -1;
35210           var n = prefixes.length;
35211           var s = document.body.style;
35212
35213           if (property.toLowerCase() in s) {
35214             return property.toLowerCase();
35215           }
35216
35217           while (++i < n) {
35218             if (prefixes[i] + property in s) {
35219               return '-' + prefixes[i].toLowerCase() + property.replace(/([A-Z])/g, '-$1').toLowerCase();
35220             }
35221           }
35222
35223           return false;
35224         }
35225         var transformProperty;
35226         function utilSetTransform(el, x, y, scale) {
35227           var prop = transformProperty = transformProperty || utilPrefixCSSProperty('Transform');
35228           var translate = utilDetect().opera ? 'translate(' + x + 'px,' + y + 'px)' : 'translate3d(' + x + 'px,' + y + 'px,0)';
35229           return el.style(prop, translate + (scale ? ' scale(' + scale + ')' : ''));
35230         } // Calculates Levenshtein distance between two strings
35231         // see:  https://en.wikipedia.org/wiki/Levenshtein_distance
35232         // first converts the strings to lowercase and replaces diacritic marks with ascii equivalents.
35233
35234         function utilEditDistance(a, b) {
35235           a = remove$6(a.toLowerCase());
35236           b = remove$6(b.toLowerCase());
35237           if (a.length === 0) return b.length;
35238           if (b.length === 0) return a.length;
35239           var matrix = [];
35240           var i, j;
35241
35242           for (i = 0; i <= b.length; i++) {
35243             matrix[i] = [i];
35244           }
35245
35246           for (j = 0; j <= a.length; j++) {
35247             matrix[0][j] = j;
35248           }
35249
35250           for (i = 1; i <= b.length; i++) {
35251             for (j = 1; j <= a.length; j++) {
35252               if (b.charAt(i - 1) === a.charAt(j - 1)) {
35253                 matrix[i][j] = matrix[i - 1][j - 1];
35254               } else {
35255                 matrix[i][j] = Math.min(matrix[i - 1][j - 1] + 1, // substitution
35256                 Math.min(matrix[i][j - 1] + 1, // insertion
35257                 matrix[i - 1][j] + 1)); // deletion
35258               }
35259             }
35260           }
35261
35262           return matrix[b.length][a.length];
35263         } // a d3.mouse-alike which
35264         // 1. Only works on HTML elements, not SVG
35265         // 2. Does not cause style recalculation
35266
35267         function utilFastMouse(container) {
35268           var rect = container.getBoundingClientRect();
35269           var rectLeft = rect.left;
35270           var rectTop = rect.top;
35271           var clientLeft = +container.clientLeft;
35272           var clientTop = +container.clientTop;
35273           return function (e) {
35274             return [e.clientX - rectLeft - clientLeft, e.clientY - rectTop - clientTop];
35275           };
35276         }
35277         function utilAsyncMap(inputs, func, callback) {
35278           var remaining = inputs.length;
35279           var results = [];
35280           var errors = [];
35281           inputs.forEach(function (d, i) {
35282             func(d, function done(err, data) {
35283               errors[i] = err;
35284               results[i] = data;
35285               remaining--;
35286               if (!remaining) callback(errors, results);
35287             });
35288           });
35289         } // wraps an index to an interval [0..length-1]
35290
35291         function utilWrap(index, length) {
35292           if (index < 0) {
35293             index += Math.ceil(-index / length) * length;
35294           }
35295
35296           return index % length;
35297         }
35298         /**
35299          * a replacement for functor
35300          *
35301          * @param {*} value any value
35302          * @returns {Function} a function that returns that value or the value if it's a function
35303          */
35304
35305         function utilFunctor(value) {
35306           if (typeof value === 'function') return value;
35307           return function () {
35308             return value;
35309           };
35310         }
35311         function utilNoAuto(selection) {
35312           var isText = selection.size() && selection.node().tagName.toLowerCase() === 'textarea';
35313           return selection // assign 'new-password' even for non-password fields to prevent browsers (Chrome) ignoring 'off'
35314           .attr('autocomplete', 'new-password').attr('autocorrect', 'off').attr('autocapitalize', 'off').attr('spellcheck', isText ? 'true' : 'false');
35315         } // https://stackoverflow.com/questions/194846/is-there-any-kind-of-hash-code-function-in-javascript
35316         // https://werxltd.com/wp/2010/05/13/javascript-implementation-of-javas-string-hashcode-method/
35317
35318         function utilHashcode(str) {
35319           var hash = 0;
35320
35321           if (str.length === 0) {
35322             return hash;
35323           }
35324
35325           for (var i = 0; i < str.length; i++) {
35326             var _char = str.charCodeAt(i);
35327
35328             hash = (hash << 5) - hash + _char;
35329             hash = hash & hash; // Convert to 32bit integer
35330           }
35331
35332           return hash;
35333         } // Returns version of `str` with all runs of special characters replaced by `_`;
35334         // suitable for HTML ids, classes, selectors, etc.
35335
35336         function utilSafeClassName(str) {
35337           return str.toLowerCase().replace(/[^a-z0-9]+/g, '_');
35338         } // Returns string based on `val` that is highly unlikely to collide with an id
35339         // used previously or that's present elsewhere in the document. Useful for preventing
35340         // browser-provided autofills or when embedding iD on pages with unknown elements.
35341
35342         function utilUniqueDomId(val) {
35343           return 'ideditor-' + utilSafeClassName(val.toString()) + '-' + new Date().getTime().toString();
35344         } // Returns the length of `str` in unicode characters. This can be less than
35345         // `String.length()` since a single unicode character can be composed of multiple
35346         // JavaScript UTF-16 code units.
35347
35348         function utilUnicodeCharsCount(str) {
35349           // Native ES2015 implementations of `Array.from` split strings into unicode characters
35350           return Array.from(str).length;
35351         } // Returns a new string representing `str` cut from its start to `limit` length
35352         // in unicode characters. Note that this runs the risk of splitting graphemes.
35353
35354         function utilUnicodeCharsTruncated(str, limit) {
35355           return Array.from(str).slice(0, limit).join('');
35356         } // Variation of d3.json (https://github.com/d3/d3-fetch/blob/master/src/json.js)
35357
35358         function utilFetchJson(resourse, init) {
35359           return fetch(resourse, init).then(function (response) {
35360             // fetch in PhantomJS tests may return ok=false and status=0 even if it's okay
35361             if (!response.ok && response.status !== 0 || !response.json) throw new Error(response.status + ' ' + response.statusText);
35362             if (response.status === 204 || response.status === 205) return;
35363             return response.json();
35364           });
35365         }
35366
35367         function osmEntity(attrs) {
35368           // For prototypal inheritance.
35369           if (this instanceof osmEntity) return; // Create the appropriate subtype.
35370
35371           if (attrs && attrs.type) {
35372             return osmEntity[attrs.type].apply(this, arguments);
35373           } else if (attrs && attrs.id) {
35374             return osmEntity[osmEntity.id.type(attrs.id)].apply(this, arguments);
35375           } // Initialize a generic Entity (used only in tests).
35376
35377
35378           return new osmEntity().initialize(arguments);
35379         }
35380
35381         osmEntity.id = function (type) {
35382           return osmEntity.id.fromOSM(type, osmEntity.id.next[type]--);
35383         };
35384
35385         osmEntity.id.next = {
35386           changeset: -1,
35387           node: -1,
35388           way: -1,
35389           relation: -1
35390         };
35391
35392         osmEntity.id.fromOSM = function (type, id) {
35393           return type[0] + id;
35394         };
35395
35396         osmEntity.id.toOSM = function (id) {
35397           return id.slice(1);
35398         };
35399
35400         osmEntity.id.type = function (id) {
35401           return {
35402             'c': 'changeset',
35403             'n': 'node',
35404             'w': 'way',
35405             'r': 'relation'
35406           }[id[0]];
35407         }; // A function suitable for use as the second argument to d3.selection#data().
35408
35409
35410         osmEntity.key = function (entity) {
35411           return entity.id + 'v' + (entity.v || 0);
35412         };
35413
35414         var _deprecatedTagValuesByKey;
35415
35416         osmEntity.deprecatedTagValuesByKey = function (dataDeprecated) {
35417           if (!_deprecatedTagValuesByKey) {
35418             _deprecatedTagValuesByKey = {};
35419             dataDeprecated.forEach(function (d) {
35420               var oldKeys = Object.keys(d.old);
35421
35422               if (oldKeys.length === 1) {
35423                 var oldKey = oldKeys[0];
35424                 var oldValue = d.old[oldKey];
35425
35426                 if (oldValue !== '*') {
35427                   if (!_deprecatedTagValuesByKey[oldKey]) {
35428                     _deprecatedTagValuesByKey[oldKey] = [oldValue];
35429                   } else {
35430                     _deprecatedTagValuesByKey[oldKey].push(oldValue);
35431                   }
35432                 }
35433               }
35434             });
35435           }
35436
35437           return _deprecatedTagValuesByKey;
35438         };
35439
35440         osmEntity.prototype = {
35441           tags: {},
35442           initialize: function initialize(sources) {
35443             for (var i = 0; i < sources.length; ++i) {
35444               var source = sources[i];
35445
35446               for (var prop in source) {
35447                 if (Object.prototype.hasOwnProperty.call(source, prop)) {
35448                   if (source[prop] === undefined) {
35449                     delete this[prop];
35450                   } else {
35451                     this[prop] = source[prop];
35452                   }
35453                 }
35454               }
35455             }
35456
35457             if (!this.id && this.type) {
35458               this.id = osmEntity.id(this.type);
35459             }
35460
35461             if (!this.hasOwnProperty('visible')) {
35462               this.visible = true;
35463             }
35464
35465             if (debug) {
35466               Object.freeze(this);
35467               Object.freeze(this.tags);
35468               if (this.loc) Object.freeze(this.loc);
35469               if (this.nodes) Object.freeze(this.nodes);
35470               if (this.members) Object.freeze(this.members);
35471             }
35472
35473             return this;
35474           },
35475           copy: function copy(resolver, copies) {
35476             if (copies[this.id]) return copies[this.id];
35477             var copy = osmEntity(this, {
35478               id: undefined,
35479               user: undefined,
35480               version: undefined
35481             });
35482             copies[this.id] = copy;
35483             return copy;
35484           },
35485           osmId: function osmId() {
35486             return osmEntity.id.toOSM(this.id);
35487           },
35488           isNew: function isNew() {
35489             return this.osmId() < 0;
35490           },
35491           update: function update(attrs) {
35492             return osmEntity(this, attrs, {
35493               v: 1 + (this.v || 0)
35494             });
35495           },
35496           mergeTags: function mergeTags(tags) {
35497             var merged = Object.assign({}, this.tags); // shallow copy
35498
35499             var changed = false;
35500
35501             for (var k in tags) {
35502               var t1 = merged[k];
35503               var t2 = tags[k];
35504
35505               if (!t1) {
35506                 changed = true;
35507                 merged[k] = t2;
35508               } else if (t1 !== t2) {
35509                 changed = true;
35510                 merged[k] = utilUnicodeCharsTruncated(utilArrayUnion(t1.split(/;\s*/), t2.split(/;\s*/)).join(';'), 255 // avoid exceeding character limit; see also services/osm.js -> maxCharsForTagValue()
35511                 );
35512               }
35513             }
35514
35515             return changed ? this.update({
35516               tags: merged
35517             }) : this;
35518           },
35519           intersects: function intersects(extent, resolver) {
35520             return this.extent(resolver).intersects(extent);
35521           },
35522           hasNonGeometryTags: function hasNonGeometryTags() {
35523             return Object.keys(this.tags).some(function (k) {
35524               return k !== 'area';
35525             });
35526           },
35527           hasParentRelations: function hasParentRelations(resolver) {
35528             return resolver.parentRelations(this).length > 0;
35529           },
35530           hasInterestingTags: function hasInterestingTags() {
35531             return Object.keys(this.tags).some(osmIsInterestingTag);
35532           },
35533           isHighwayIntersection: function isHighwayIntersection() {
35534             return false;
35535           },
35536           isDegenerate: function isDegenerate() {
35537             return true;
35538           },
35539           deprecatedTags: function deprecatedTags(dataDeprecated) {
35540             var tags = this.tags; // if there are no tags, none can be deprecated
35541
35542             if (Object.keys(tags).length === 0) return [];
35543             var deprecated = [];
35544             dataDeprecated.forEach(function (d) {
35545               var oldKeys = Object.keys(d.old);
35546
35547               if (d.replace) {
35548                 var hasExistingValues = Object.keys(d.replace).some(function (replaceKey) {
35549                   if (!tags[replaceKey] || d.old[replaceKey]) return false;
35550                   var replaceValue = d.replace[replaceKey];
35551                   if (replaceValue === '*') return false;
35552                   if (replaceValue === tags[replaceKey]) return false;
35553                   return true;
35554                 }); // don't flag deprecated tags if the upgrade path would overwrite existing data - #7843
35555
35556                 if (hasExistingValues) return;
35557               }
35558
35559               var matchesDeprecatedTags = oldKeys.every(function (oldKey) {
35560                 if (!tags[oldKey]) return false;
35561                 if (d.old[oldKey] === '*') return true;
35562                 if (d.old[oldKey] === tags[oldKey]) return true;
35563                 var vals = tags[oldKey].split(';').filter(Boolean);
35564
35565                 if (vals.length === 0) {
35566                   return false;
35567                 } else if (vals.length > 1) {
35568                   return vals.indexOf(d.old[oldKey]) !== -1;
35569                 } else {
35570                   if (tags[oldKey] === d.old[oldKey]) {
35571                     if (d.replace && d.old[oldKey] === d.replace[oldKey]) {
35572                       var replaceKeys = Object.keys(d.replace);
35573                       return !replaceKeys.every(function (replaceKey) {
35574                         return tags[replaceKey] === d.replace[replaceKey];
35575                       });
35576                     } else {
35577                       return true;
35578                     }
35579                   }
35580                 }
35581
35582                 return false;
35583               });
35584
35585               if (matchesDeprecatedTags) {
35586                 deprecated.push(d);
35587               }
35588             });
35589             return deprecated;
35590           }
35591         };
35592
35593         function osmLanes(entity) {
35594           if (entity.type !== 'way') return null;
35595           if (!entity.tags.highway) return null;
35596           var tags = entity.tags;
35597           var isOneWay = entity.isOneWay();
35598           var laneCount = getLaneCount(tags, isOneWay);
35599           var maxspeed = parseMaxspeed(tags);
35600           var laneDirections = parseLaneDirections(tags, isOneWay, laneCount);
35601           var forward = laneDirections.forward;
35602           var backward = laneDirections.backward;
35603           var bothways = laneDirections.bothways; // parse the piped string 'x|y|z' format
35604
35605           var turnLanes = {};
35606           turnLanes.unspecified = parseTurnLanes(tags['turn:lanes']);
35607           turnLanes.forward = parseTurnLanes(tags['turn:lanes:forward']);
35608           turnLanes.backward = parseTurnLanes(tags['turn:lanes:backward']);
35609           var maxspeedLanes = {};
35610           maxspeedLanes.unspecified = parseMaxspeedLanes(tags['maxspeed:lanes'], maxspeed);
35611           maxspeedLanes.forward = parseMaxspeedLanes(tags['maxspeed:lanes:forward'], maxspeed);
35612           maxspeedLanes.backward = parseMaxspeedLanes(tags['maxspeed:lanes:backward'], maxspeed);
35613           var psvLanes = {};
35614           psvLanes.unspecified = parseMiscLanes(tags['psv:lanes']);
35615           psvLanes.forward = parseMiscLanes(tags['psv:lanes:forward']);
35616           psvLanes.backward = parseMiscLanes(tags['psv:lanes:backward']);
35617           var busLanes = {};
35618           busLanes.unspecified = parseMiscLanes(tags['bus:lanes']);
35619           busLanes.forward = parseMiscLanes(tags['bus:lanes:forward']);
35620           busLanes.backward = parseMiscLanes(tags['bus:lanes:backward']);
35621           var taxiLanes = {};
35622           taxiLanes.unspecified = parseMiscLanes(tags['taxi:lanes']);
35623           taxiLanes.forward = parseMiscLanes(tags['taxi:lanes:forward']);
35624           taxiLanes.backward = parseMiscLanes(tags['taxi:lanes:backward']);
35625           var hovLanes = {};
35626           hovLanes.unspecified = parseMiscLanes(tags['hov:lanes']);
35627           hovLanes.forward = parseMiscLanes(tags['hov:lanes:forward']);
35628           hovLanes.backward = parseMiscLanes(tags['hov:lanes:backward']);
35629           var hgvLanes = {};
35630           hgvLanes.unspecified = parseMiscLanes(tags['hgv:lanes']);
35631           hgvLanes.forward = parseMiscLanes(tags['hgv:lanes:forward']);
35632           hgvLanes.backward = parseMiscLanes(tags['hgv:lanes:backward']);
35633           var bicyclewayLanes = {};
35634           bicyclewayLanes.unspecified = parseBicycleWay(tags['bicycleway:lanes']);
35635           bicyclewayLanes.forward = parseBicycleWay(tags['bicycleway:lanes:forward']);
35636           bicyclewayLanes.backward = parseBicycleWay(tags['bicycleway:lanes:backward']);
35637           var lanesObj = {
35638             forward: [],
35639             backward: [],
35640             unspecified: []
35641           }; // map forward/backward/unspecified of each lane type to lanesObj
35642
35643           mapToLanesObj(lanesObj, turnLanes, 'turnLane');
35644           mapToLanesObj(lanesObj, maxspeedLanes, 'maxspeed');
35645           mapToLanesObj(lanesObj, psvLanes, 'psv');
35646           mapToLanesObj(lanesObj, busLanes, 'bus');
35647           mapToLanesObj(lanesObj, taxiLanes, 'taxi');
35648           mapToLanesObj(lanesObj, hovLanes, 'hov');
35649           mapToLanesObj(lanesObj, hgvLanes, 'hgv');
35650           mapToLanesObj(lanesObj, bicyclewayLanes, 'bicycleway');
35651           return {
35652             metadata: {
35653               count: laneCount,
35654               oneway: isOneWay,
35655               forward: forward,
35656               backward: backward,
35657               bothways: bothways,
35658               turnLanes: turnLanes,
35659               maxspeed: maxspeed,
35660               maxspeedLanes: maxspeedLanes,
35661               psvLanes: psvLanes,
35662               busLanes: busLanes,
35663               taxiLanes: taxiLanes,
35664               hovLanes: hovLanes,
35665               hgvLanes: hgvLanes,
35666               bicyclewayLanes: bicyclewayLanes
35667             },
35668             lanes: lanesObj
35669           };
35670         }
35671
35672         function getLaneCount(tags, isOneWay) {
35673           var count;
35674
35675           if (tags.lanes) {
35676             count = parseInt(tags.lanes, 10);
35677
35678             if (count > 0) {
35679               return count;
35680             }
35681           }
35682
35683           switch (tags.highway) {
35684             case 'trunk':
35685             case 'motorway':
35686               count = isOneWay ? 2 : 4;
35687               break;
35688
35689             default:
35690               count = isOneWay ? 1 : 2;
35691               break;
35692           }
35693
35694           return count;
35695         }
35696
35697         function parseMaxspeed(tags) {
35698           var maxspeed = tags.maxspeed;
35699           if (!maxspeed) return;
35700           var maxspeedRegex = /^([0-9][\.0-9]+?)(?:[ ]?(?:km\/h|kmh|kph|mph|knots))?$/;
35701           if (!maxspeedRegex.test(maxspeed)) return;
35702           return parseInt(maxspeed, 10);
35703         }
35704
35705         function parseLaneDirections(tags, isOneWay, laneCount) {
35706           var forward = parseInt(tags['lanes:forward'], 10);
35707           var backward = parseInt(tags['lanes:backward'], 10);
35708           var bothways = parseInt(tags['lanes:both_ways'], 10) > 0 ? 1 : 0;
35709
35710           if (parseInt(tags.oneway, 10) === -1) {
35711             forward = 0;
35712             bothways = 0;
35713             backward = laneCount;
35714           } else if (isOneWay) {
35715             forward = laneCount;
35716             bothways = 0;
35717             backward = 0;
35718           } else if (isNaN(forward) && isNaN(backward)) {
35719             backward = Math.floor((laneCount - bothways) / 2);
35720             forward = laneCount - bothways - backward;
35721           } else if (isNaN(forward)) {
35722             if (backward > laneCount - bothways) {
35723               backward = laneCount - bothways;
35724             }
35725
35726             forward = laneCount - bothways - backward;
35727           } else if (isNaN(backward)) {
35728             if (forward > laneCount - bothways) {
35729               forward = laneCount - bothways;
35730             }
35731
35732             backward = laneCount - bothways - forward;
35733           }
35734
35735           return {
35736             forward: forward,
35737             backward: backward,
35738             bothways: bothways
35739           };
35740         }
35741
35742         function parseTurnLanes(tag) {
35743           if (!tag) return;
35744           var validValues = ['left', 'slight_left', 'sharp_left', 'through', 'right', 'slight_right', 'sharp_right', 'reverse', 'merge_to_left', 'merge_to_right', 'none'];
35745           return tag.split('|').map(function (s) {
35746             if (s === '') s = 'none';
35747             return s.split(';').map(function (d) {
35748               return validValues.indexOf(d) === -1 ? 'unknown' : d;
35749             });
35750           });
35751         }
35752
35753         function parseMaxspeedLanes(tag, maxspeed) {
35754           if (!tag) return;
35755           return tag.split('|').map(function (s) {
35756             if (s === 'none') return s;
35757             var m = parseInt(s, 10);
35758             if (s === '' || m === maxspeed) return null;
35759             return isNaN(m) ? 'unknown' : m;
35760           });
35761         }
35762
35763         function parseMiscLanes(tag) {
35764           if (!tag) return;
35765           var validValues = ['yes', 'no', 'designated'];
35766           return tag.split('|').map(function (s) {
35767             if (s === '') s = 'no';
35768             return validValues.indexOf(s) === -1 ? 'unknown' : s;
35769           });
35770         }
35771
35772         function parseBicycleWay(tag) {
35773           if (!tag) return;
35774           var validValues = ['yes', 'no', 'designated', 'lane'];
35775           return tag.split('|').map(function (s) {
35776             if (s === '') s = 'no';
35777             return validValues.indexOf(s) === -1 ? 'unknown' : s;
35778           });
35779         }
35780
35781         function mapToLanesObj(lanesObj, data, key) {
35782           if (data.forward) {
35783             data.forward.forEach(function (l, i) {
35784               if (!lanesObj.forward[i]) lanesObj.forward[i] = {};
35785               lanesObj.forward[i][key] = l;
35786             });
35787           }
35788
35789           if (data.backward) {
35790             data.backward.forEach(function (l, i) {
35791               if (!lanesObj.backward[i]) lanesObj.backward[i] = {};
35792               lanesObj.backward[i][key] = l;
35793             });
35794           }
35795
35796           if (data.unspecified) {
35797             data.unspecified.forEach(function (l, i) {
35798               if (!lanesObj.unspecified[i]) lanesObj.unspecified[i] = {};
35799               lanesObj.unspecified[i][key] = l;
35800             });
35801           }
35802         }
35803
35804         function osmWay() {
35805           if (!(this instanceof osmWay)) {
35806             return new osmWay().initialize(arguments);
35807           } else if (arguments.length) {
35808             this.initialize(arguments);
35809           }
35810         }
35811         osmEntity.way = osmWay;
35812         osmWay.prototype = Object.create(osmEntity.prototype);
35813         Object.assign(osmWay.prototype, {
35814           type: 'way',
35815           nodes: [],
35816           copy: function copy(resolver, copies) {
35817             if (copies[this.id]) return copies[this.id];
35818             var copy = osmEntity.prototype.copy.call(this, resolver, copies);
35819             var nodes = this.nodes.map(function (id) {
35820               return resolver.entity(id).copy(resolver, copies).id;
35821             });
35822             copy = copy.update({
35823               nodes: nodes
35824             });
35825             copies[this.id] = copy;
35826             return copy;
35827           },
35828           extent: function extent(resolver) {
35829             return resolver["transient"](this, 'extent', function () {
35830               var extent = geoExtent();
35831
35832               for (var i = 0; i < this.nodes.length; i++) {
35833                 var node = resolver.hasEntity(this.nodes[i]);
35834
35835                 if (node) {
35836                   extent._extend(node.extent());
35837                 }
35838               }
35839
35840               return extent;
35841             });
35842           },
35843           first: function first() {
35844             return this.nodes[0];
35845           },
35846           last: function last() {
35847             return this.nodes[this.nodes.length - 1];
35848           },
35849           contains: function contains(node) {
35850             return this.nodes.indexOf(node) >= 0;
35851           },
35852           affix: function affix(node) {
35853             if (this.nodes[0] === node) return 'prefix';
35854             if (this.nodes[this.nodes.length - 1] === node) return 'suffix';
35855           },
35856           layer: function layer() {
35857             // explicit layer tag, clamp between -10, 10..
35858             if (isFinite(this.tags.layer)) {
35859               return Math.max(-10, Math.min(+this.tags.layer, 10));
35860             } // implied layer tag..
35861
35862
35863             if (this.tags.covered === 'yes') return -1;
35864             if (this.tags.location === 'overground') return 1;
35865             if (this.tags.location === 'underground') return -1;
35866             if (this.tags.location === 'underwater') return -10;
35867             if (this.tags.power === 'line') return 10;
35868             if (this.tags.power === 'minor_line') return 10;
35869             if (this.tags.aerialway) return 10;
35870             if (this.tags.bridge) return 1;
35871             if (this.tags.cutting) return -1;
35872             if (this.tags.tunnel) return -1;
35873             if (this.tags.waterway) return -1;
35874             if (this.tags.man_made === 'pipeline') return -10;
35875             if (this.tags.boundary) return -10;
35876             return 0;
35877           },
35878           // the approximate width of the line based on its tags except its `width` tag
35879           impliedLineWidthMeters: function impliedLineWidthMeters() {
35880             var averageWidths = {
35881               highway: {
35882                 // width is for single lane
35883                 motorway: 5,
35884                 motorway_link: 5,
35885                 trunk: 4.5,
35886                 trunk_link: 4.5,
35887                 primary: 4,
35888                 secondary: 4,
35889                 tertiary: 4,
35890                 primary_link: 4,
35891                 secondary_link: 4,
35892                 tertiary_link: 4,
35893                 unclassified: 4,
35894                 road: 4,
35895                 living_street: 4,
35896                 bus_guideway: 4,
35897                 pedestrian: 4,
35898                 residential: 3.5,
35899                 service: 3.5,
35900                 track: 3,
35901                 cycleway: 2.5,
35902                 bridleway: 2,
35903                 corridor: 2,
35904                 steps: 2,
35905                 path: 1.5,
35906                 footway: 1.5
35907               },
35908               railway: {
35909                 // width includes ties and rail bed, not just track gauge
35910                 rail: 2.5,
35911                 light_rail: 2.5,
35912                 tram: 2.5,
35913                 subway: 2.5,
35914                 monorail: 2.5,
35915                 funicular: 2.5,
35916                 disused: 2.5,
35917                 preserved: 2.5,
35918                 miniature: 1.5,
35919                 narrow_gauge: 1.5
35920               },
35921               waterway: {
35922                 river: 50,
35923                 canal: 25,
35924                 stream: 5,
35925                 tidal_channel: 5,
35926                 fish_pass: 2.5,
35927                 drain: 2.5,
35928                 ditch: 1.5
35929               }
35930             };
35931
35932             for (var key in averageWidths) {
35933               if (this.tags[key] && averageWidths[key][this.tags[key]]) {
35934                 var width = averageWidths[key][this.tags[key]];
35935
35936                 if (key === 'highway') {
35937                   var laneCount = this.tags.lanes && parseInt(this.tags.lanes, 10);
35938                   if (!laneCount) laneCount = this.isOneWay() ? 1 : 2;
35939                   return width * laneCount;
35940                 }
35941
35942                 return width;
35943               }
35944             }
35945
35946             return null;
35947           },
35948           isOneWay: function isOneWay() {
35949             // explicit oneway tag..
35950             var values = {
35951               'yes': true,
35952               '1': true,
35953               '-1': true,
35954               'reversible': true,
35955               'alternating': true,
35956               'no': false,
35957               '0': false
35958             };
35959
35960             if (values[this.tags.oneway] !== undefined) {
35961               return values[this.tags.oneway];
35962             } // implied oneway tag..
35963
35964
35965             for (var key in this.tags) {
35966               if (key in osmOneWayTags && this.tags[key] in osmOneWayTags[key]) {
35967                 return true;
35968               }
35969             }
35970
35971             return false;
35972           },
35973           // Some identifier for tag that implies that this way is "sided",
35974           // i.e. the right side is the 'inside' (e.g. the right side of a
35975           // natural=cliff is lower).
35976           sidednessIdentifier: function sidednessIdentifier() {
35977             for (var key in this.tags) {
35978               var value = this.tags[key];
35979
35980               if (key in osmRightSideIsInsideTags && value in osmRightSideIsInsideTags[key]) {
35981                 if (osmRightSideIsInsideTags[key][value] === true) {
35982                   return key;
35983                 } else {
35984                   // if the map's value is something other than a
35985                   // literal true, we should use it so we can
35986                   // special case some keys (e.g. natural=coastline
35987                   // is handled differently to other naturals).
35988                   return osmRightSideIsInsideTags[key][value];
35989                 }
35990               }
35991             }
35992
35993             return null;
35994           },
35995           isSided: function isSided() {
35996             if (this.tags.two_sided === 'yes') {
35997               return false;
35998             }
35999
36000             return this.sidednessIdentifier() !== null;
36001           },
36002           lanes: function lanes() {
36003             return osmLanes(this);
36004           },
36005           isClosed: function isClosed() {
36006             return this.nodes.length > 1 && this.first() === this.last();
36007           },
36008           isConvex: function isConvex(resolver) {
36009             if (!this.isClosed() || this.isDegenerate()) return null;
36010             var nodes = utilArrayUniq(resolver.childNodes(this));
36011             var coords = nodes.map(function (n) {
36012               return n.loc;
36013             });
36014             var curr = 0;
36015             var prev = 0;
36016
36017             for (var i = 0; i < coords.length; i++) {
36018               var o = coords[(i + 1) % coords.length];
36019               var a = coords[i];
36020               var b = coords[(i + 2) % coords.length];
36021               var res = geoVecCross(a, b, o);
36022               curr = res > 0 ? 1 : res < 0 ? -1 : 0;
36023
36024               if (curr === 0) {
36025                 continue;
36026               } else if (prev && curr !== prev) {
36027                 return false;
36028               }
36029
36030               prev = curr;
36031             }
36032
36033             return true;
36034           },
36035           // returns an object with the tag that implies this is an area, if any
36036           tagSuggestingArea: function tagSuggestingArea() {
36037             return osmTagSuggestingArea(this.tags);
36038           },
36039           isArea: function isArea() {
36040             if (this.tags.area === 'yes') return true;
36041             if (!this.isClosed() || this.tags.area === 'no') return false;
36042             return this.tagSuggestingArea() !== null;
36043           },
36044           isDegenerate: function isDegenerate() {
36045             return new Set(this.nodes).size < (this.isArea() ? 3 : 2);
36046           },
36047           areAdjacent: function areAdjacent(n1, n2) {
36048             for (var i = 0; i < this.nodes.length; i++) {
36049               if (this.nodes[i] === n1) {
36050                 if (this.nodes[i - 1] === n2) return true;
36051                 if (this.nodes[i + 1] === n2) return true;
36052               }
36053             }
36054
36055             return false;
36056           },
36057           geometry: function geometry(graph) {
36058             return graph["transient"](this, 'geometry', function () {
36059               return this.isArea() ? 'area' : 'line';
36060             });
36061           },
36062           // returns an array of objects representing the segments between the nodes in this way
36063           segments: function segments(graph) {
36064             function segmentExtent(graph) {
36065               var n1 = graph.hasEntity(this.nodes[0]);
36066               var n2 = graph.hasEntity(this.nodes[1]);
36067               return n1 && n2 && geoExtent([[Math.min(n1.loc[0], n2.loc[0]), Math.min(n1.loc[1], n2.loc[1])], [Math.max(n1.loc[0], n2.loc[0]), Math.max(n1.loc[1], n2.loc[1])]]);
36068             }
36069
36070             return graph["transient"](this, 'segments', function () {
36071               var segments = [];
36072
36073               for (var i = 0; i < this.nodes.length - 1; i++) {
36074                 segments.push({
36075                   id: this.id + '-' + i,
36076                   wayId: this.id,
36077                   index: i,
36078                   nodes: [this.nodes[i], this.nodes[i + 1]],
36079                   extent: segmentExtent
36080                 });
36081               }
36082
36083               return segments;
36084             });
36085           },
36086           // If this way is not closed, append the beginning node to the end of the nodelist to close it.
36087           close: function close() {
36088             if (this.isClosed() || !this.nodes.length) return this;
36089             var nodes = this.nodes.slice();
36090             nodes = nodes.filter(noRepeatNodes);
36091             nodes.push(nodes[0]);
36092             return this.update({
36093               nodes: nodes
36094             });
36095           },
36096           // If this way is closed, remove any connector nodes from the end of the nodelist to unclose it.
36097           unclose: function unclose() {
36098             if (!this.isClosed()) return this;
36099             var nodes = this.nodes.slice();
36100             var connector = this.first();
36101             var i = nodes.length - 1; // remove trailing connectors..
36102
36103             while (i > 0 && nodes.length > 1 && nodes[i] === connector) {
36104               nodes.splice(i, 1);
36105               i = nodes.length - 1;
36106             }
36107
36108             nodes = nodes.filter(noRepeatNodes);
36109             return this.update({
36110               nodes: nodes
36111             });
36112           },
36113           // Adds a node (id) in front of the node which is currently at position index.
36114           // If index is undefined, the node will be added to the end of the way for linear ways,
36115           //   or just before the final connecting node for circular ways.
36116           // Consecutive duplicates are eliminated including existing ones.
36117           // Circularity is always preserved when adding a node.
36118           addNode: function addNode(id, index) {
36119             var nodes = this.nodes.slice();
36120             var isClosed = this.isClosed();
36121             var max = isClosed ? nodes.length - 1 : nodes.length;
36122
36123             if (index === undefined) {
36124               index = max;
36125             }
36126
36127             if (index < 0 || index > max) {
36128               throw new RangeError('index ' + index + ' out of range 0..' + max);
36129             } // If this is a closed way, remove all connector nodes except the first one
36130             // (there may be duplicates) and adjust index if necessary..
36131
36132
36133             if (isClosed) {
36134               var connector = this.first(); // leading connectors..
36135
36136               var i = 1;
36137
36138               while (i < nodes.length && nodes.length > 2 && nodes[i] === connector) {
36139                 nodes.splice(i, 1);
36140                 if (index > i) index--;
36141               } // trailing connectors..
36142
36143
36144               i = nodes.length - 1;
36145
36146               while (i > 0 && nodes.length > 1 && nodes[i] === connector) {
36147                 nodes.splice(i, 1);
36148                 if (index > i) index--;
36149                 i = nodes.length - 1;
36150               }
36151             }
36152
36153             nodes.splice(index, 0, id);
36154             nodes = nodes.filter(noRepeatNodes); // If the way was closed before, append a connector node to keep it closed..
36155
36156             if (isClosed && (nodes.length === 1 || nodes[0] !== nodes[nodes.length - 1])) {
36157               nodes.push(nodes[0]);
36158             }
36159
36160             return this.update({
36161               nodes: nodes
36162             });
36163           },
36164           // Replaces the node which is currently at position index with the given node (id).
36165           // Consecutive duplicates are eliminated including existing ones.
36166           // Circularity is preserved when updating a node.
36167           updateNode: function updateNode(id, index) {
36168             var nodes = this.nodes.slice();
36169             var isClosed = this.isClosed();
36170             var max = nodes.length - 1;
36171
36172             if (index === undefined || index < 0 || index > max) {
36173               throw new RangeError('index ' + index + ' out of range 0..' + max);
36174             } // If this is a closed way, remove all connector nodes except the first one
36175             // (there may be duplicates) and adjust index if necessary..
36176
36177
36178             if (isClosed) {
36179               var connector = this.first(); // leading connectors..
36180
36181               var i = 1;
36182
36183               while (i < nodes.length && nodes.length > 2 && nodes[i] === connector) {
36184                 nodes.splice(i, 1);
36185                 if (index > i) index--;
36186               } // trailing connectors..
36187
36188
36189               i = nodes.length - 1;
36190
36191               while (i > 0 && nodes.length > 1 && nodes[i] === connector) {
36192                 nodes.splice(i, 1);
36193                 if (index === i) index = 0; // update leading connector instead
36194
36195                 i = nodes.length - 1;
36196               }
36197             }
36198
36199             nodes.splice(index, 1, id);
36200             nodes = nodes.filter(noRepeatNodes); // If the way was closed before, append a connector node to keep it closed..
36201
36202             if (isClosed && (nodes.length === 1 || nodes[0] !== nodes[nodes.length - 1])) {
36203               nodes.push(nodes[0]);
36204             }
36205
36206             return this.update({
36207               nodes: nodes
36208             });
36209           },
36210           // Replaces each occurrence of node id needle with replacement.
36211           // Consecutive duplicates are eliminated including existing ones.
36212           // Circularity is preserved.
36213           replaceNode: function replaceNode(needleID, replacementID) {
36214             var nodes = this.nodes.slice();
36215             var isClosed = this.isClosed();
36216
36217             for (var i = 0; i < nodes.length; i++) {
36218               if (nodes[i] === needleID) {
36219                 nodes[i] = replacementID;
36220               }
36221             }
36222
36223             nodes = nodes.filter(noRepeatNodes); // If the way was closed before, append a connector node to keep it closed..
36224
36225             if (isClosed && (nodes.length === 1 || nodes[0] !== nodes[nodes.length - 1])) {
36226               nodes.push(nodes[0]);
36227             }
36228
36229             return this.update({
36230               nodes: nodes
36231             });
36232           },
36233           // Removes each occurrence of node id.
36234           // Consecutive duplicates are eliminated including existing ones.
36235           // Circularity is preserved.
36236           removeNode: function removeNode(id) {
36237             var nodes = this.nodes.slice();
36238             var isClosed = this.isClosed();
36239             nodes = nodes.filter(function (node) {
36240               return node !== id;
36241             }).filter(noRepeatNodes); // If the way was closed before, append a connector node to keep it closed..
36242
36243             if (isClosed && (nodes.length === 1 || nodes[0] !== nodes[nodes.length - 1])) {
36244               nodes.push(nodes[0]);
36245             }
36246
36247             return this.update({
36248               nodes: nodes
36249             });
36250           },
36251           asJXON: function asJXON(changeset_id) {
36252             var r = {
36253               way: {
36254                 '@id': this.osmId(),
36255                 '@version': this.version || 0,
36256                 nd: this.nodes.map(function (id) {
36257                   return {
36258                     keyAttributes: {
36259                       ref: osmEntity.id.toOSM(id)
36260                     }
36261                   };
36262                 }, this),
36263                 tag: Object.keys(this.tags).map(function (k) {
36264                   return {
36265                     keyAttributes: {
36266                       k: k,
36267                       v: this.tags[k]
36268                     }
36269                   };
36270                 }, this)
36271               }
36272             };
36273
36274             if (changeset_id) {
36275               r.way['@changeset'] = changeset_id;
36276             }
36277
36278             return r;
36279           },
36280           asGeoJSON: function asGeoJSON(resolver) {
36281             return resolver["transient"](this, 'GeoJSON', function () {
36282               var coordinates = resolver.childNodes(this).map(function (n) {
36283                 return n.loc;
36284               });
36285
36286               if (this.isArea() && this.isClosed()) {
36287                 return {
36288                   type: 'Polygon',
36289                   coordinates: [coordinates]
36290                 };
36291               } else {
36292                 return {
36293                   type: 'LineString',
36294                   coordinates: coordinates
36295                 };
36296               }
36297             });
36298           },
36299           area: function area(resolver) {
36300             return resolver["transient"](this, 'area', function () {
36301               var nodes = resolver.childNodes(this);
36302               var json = {
36303                 type: 'Polygon',
36304                 coordinates: [nodes.map(function (n) {
36305                   return n.loc;
36306                 })]
36307               };
36308
36309               if (!this.isClosed() && nodes.length) {
36310                 json.coordinates[0].push(nodes[0].loc);
36311               }
36312
36313               var area = d3_geoArea(json); // Heuristic for detecting counterclockwise winding order. Assumes
36314               // that OpenStreetMap polygons are not hemisphere-spanning.
36315
36316               if (area > 2 * Math.PI) {
36317                 json.coordinates[0] = json.coordinates[0].reverse();
36318                 area = d3_geoArea(json);
36319               }
36320
36321               return isNaN(area) ? 0 : area;
36322             });
36323           }
36324         }); // Filter function to eliminate consecutive duplicates.
36325
36326         function noRepeatNodes(node, i, arr) {
36327           return i === 0 || node !== arr[i - 1];
36328         }
36329
36330         //
36331         // 1. Relation tagged with `type=multipolygon` and no interesting tags.
36332         // 2. One and only one member with the `outer` role. Must be a way with interesting tags.
36333         // 3. No members without a role.
36334         //
36335         // Old multipolygons are no longer recommended but are still rendered as areas by iD.
36336
36337         function osmOldMultipolygonOuterMemberOfRelation(entity, graph) {
36338           if (entity.type !== 'relation' || !entity.isMultipolygon() || Object.keys(entity.tags).filter(osmIsInterestingTag).length > 1) {
36339             return false;
36340           }
36341
36342           var outerMember;
36343
36344           for (var memberIndex in entity.members) {
36345             var member = entity.members[memberIndex];
36346
36347             if (!member.role || member.role === 'outer') {
36348               if (outerMember) return false;
36349               if (member.type !== 'way') return false;
36350               if (!graph.hasEntity(member.id)) return false;
36351               outerMember = graph.entity(member.id);
36352
36353               if (Object.keys(outerMember.tags).filter(osmIsInterestingTag).length === 0) {
36354                 return false;
36355               }
36356             }
36357           }
36358
36359           return outerMember;
36360         } // For fixing up rendering of multipolygons with tags on the outer member.
36361         // https://github.com/openstreetmap/iD/issues/613
36362
36363         function osmIsOldMultipolygonOuterMember(entity, graph) {
36364           if (entity.type !== 'way' || Object.keys(entity.tags).filter(osmIsInterestingTag).length === 0) {
36365             return false;
36366           }
36367
36368           var parents = graph.parentRelations(entity);
36369           if (parents.length !== 1) return false;
36370           var parent = parents[0];
36371
36372           if (!parent.isMultipolygon() || Object.keys(parent.tags).filter(osmIsInterestingTag).length > 1) {
36373             return false;
36374           }
36375
36376           var members = parent.members,
36377               member;
36378
36379           for (var i = 0; i < members.length; i++) {
36380             member = members[i];
36381
36382             if (member.id === entity.id && member.role && member.role !== 'outer') {
36383               // Not outer member
36384               return false;
36385             }
36386
36387             if (member.id !== entity.id && (!member.role || member.role === 'outer')) {
36388               // Not a simple multipolygon
36389               return false;
36390             }
36391           }
36392
36393           return parent;
36394         }
36395         function osmOldMultipolygonOuterMember(entity, graph) {
36396           if (entity.type !== 'way') return false;
36397           var parents = graph.parentRelations(entity);
36398           if (parents.length !== 1) return false;
36399           var parent = parents[0];
36400
36401           if (!parent.isMultipolygon() || Object.keys(parent.tags).filter(osmIsInterestingTag).length > 1) {
36402             return false;
36403           }
36404
36405           var members = parent.members,
36406               member,
36407               outerMember;
36408
36409           for (var i = 0; i < members.length; i++) {
36410             member = members[i];
36411
36412             if (!member.role || member.role === 'outer') {
36413               if (outerMember) return false; // Not a simple multipolygon
36414
36415               outerMember = member;
36416             }
36417           }
36418
36419           if (!outerMember) return false;
36420           var outerEntity = graph.hasEntity(outerMember.id);
36421
36422           if (!outerEntity || !Object.keys(outerEntity.tags).filter(osmIsInterestingTag).length) {
36423             return false;
36424           }
36425
36426           return outerEntity;
36427         } // Join `toJoin` array into sequences of connecting ways.
36428         // Segments which share identical start/end nodes will, as much as possible,
36429         // be connected with each other.
36430         //
36431         // The return value is a nested array. Each constituent array contains elements
36432         // of `toJoin` which have been determined to connect.
36433         //
36434         // Each consitituent array also has a `nodes` property whose value is an
36435         // ordered array of member nodes, with appropriate order reversal and
36436         // start/end coordinate de-duplication.
36437         //
36438         // Members of `toJoin` must have, at minimum, `type` and `id` properties.
36439         // Thus either an array of `osmWay`s or a relation member array may be used.
36440         //
36441         // If an member is an `osmWay`, its tags and childnodes may be reversed via
36442         // `actionReverse` in the output.
36443         //
36444         // The returned sequences array also has an `actions` array property, containing
36445         // any reversal actions that should be applied to the graph, should the calling
36446         // code attempt to actually join the given ways.
36447         //
36448         // Incomplete members (those for which `graph.hasEntity(element.id)` returns
36449         // false) and non-way members are ignored.
36450         //
36451
36452         function osmJoinWays(toJoin, graph) {
36453           function resolve(member) {
36454             return graph.childNodes(graph.entity(member.id));
36455           }
36456
36457           function reverse(item) {
36458             var action = actionReverse(item.id, {
36459               reverseOneway: true
36460             });
36461             sequences.actions.push(action);
36462             return item instanceof osmWay ? action(graph).entity(item.id) : item;
36463           } // make a copy containing only the items to join
36464
36465
36466           toJoin = toJoin.filter(function (member) {
36467             return member.type === 'way' && graph.hasEntity(member.id);
36468           }); // Are the things we are joining relation members or `osmWays`?
36469           // If `osmWays`, skip the "prefer a forward path" code below (see #4872)
36470
36471           var i;
36472           var joinAsMembers = true;
36473
36474           for (i = 0; i < toJoin.length; i++) {
36475             if (toJoin[i] instanceof osmWay) {
36476               joinAsMembers = false;
36477               break;
36478             }
36479           }
36480
36481           var sequences = [];
36482           sequences.actions = [];
36483
36484           while (toJoin.length) {
36485             // start a new sequence
36486             var item = toJoin.shift();
36487             var currWays = [item];
36488             var currNodes = resolve(item).slice(); // add to it
36489
36490             while (toJoin.length) {
36491               var start = currNodes[0];
36492               var end = currNodes[currNodes.length - 1];
36493               var fn = null;
36494               var nodes = null; // Find the next way/member to join.
36495
36496               for (i = 0; i < toJoin.length; i++) {
36497                 item = toJoin[i];
36498                 nodes = resolve(item); // (for member ordering only, not way ordering - see #4872)
36499                 // Strongly prefer to generate a forward path that preserves the order
36500                 // of the members array. For multipolygons and most relations, member
36501                 // order does not matter - but for routes, it does. (see #4589)
36502                 // If we started this sequence backwards (i.e. next member way attaches to
36503                 // the start node and not the end node), reverse the initial way before continuing.
36504
36505                 if (joinAsMembers && currWays.length === 1 && nodes[0] !== end && nodes[nodes.length - 1] !== end && (nodes[nodes.length - 1] === start || nodes[0] === start)) {
36506                   currWays[0] = reverse(currWays[0]);
36507                   currNodes.reverse();
36508                   start = currNodes[0];
36509                   end = currNodes[currNodes.length - 1];
36510                 }
36511
36512                 if (nodes[0] === end) {
36513                   fn = currNodes.push; // join to end
36514
36515                   nodes = nodes.slice(1);
36516                   break;
36517                 } else if (nodes[nodes.length - 1] === end) {
36518                   fn = currNodes.push; // join to end
36519
36520                   nodes = nodes.slice(0, -1).reverse();
36521                   item = reverse(item);
36522                   break;
36523                 } else if (nodes[nodes.length - 1] === start) {
36524                   fn = currNodes.unshift; // join to beginning
36525
36526                   nodes = nodes.slice(0, -1);
36527                   break;
36528                 } else if (nodes[0] === start) {
36529                   fn = currNodes.unshift; // join to beginning
36530
36531                   nodes = nodes.slice(1).reverse();
36532                   item = reverse(item);
36533                   break;
36534                 } else {
36535                   fn = nodes = null;
36536                 }
36537               }
36538
36539               if (!nodes) {
36540                 // couldn't find a joinable way/member
36541                 break;
36542               }
36543
36544               fn.apply(currWays, [item]);
36545               fn.apply(currNodes, nodes);
36546               toJoin.splice(i, 1);
36547             }
36548
36549             currWays.nodes = currNodes;
36550             sequences.push(currWays);
36551           }
36552
36553           return sequences;
36554         }
36555
36556         function actionAddMember(relationId, member, memberIndex, insertPair) {
36557           return function action(graph) {
36558             var relation = graph.entity(relationId); // There are some special rules for Public Transport v2 routes.
36559
36560             var isPTv2 = /stop|platform/.test(member.role);
36561
36562             if ((isNaN(memberIndex) || insertPair) && member.type === 'way' && !isPTv2) {
36563               // Try to perform sensible inserts based on how the ways join together
36564               graph = addWayMember(relation, graph);
36565             } else {
36566               // see https://wiki.openstreetmap.org/wiki/Public_transport#Service_routes
36567               // Stops and Platforms for PTv2 should be ordered first.
36568               // hack: We do not currently have the ability to place them in the exactly correct order.
36569               if (isPTv2 && isNaN(memberIndex)) {
36570                 memberIndex = 0;
36571               }
36572
36573               graph = graph.replace(relation.addMember(member, memberIndex));
36574             }
36575
36576             return graph;
36577           }; // Add a way member into the relation "wherever it makes sense".
36578           // In this situation we were not supplied a memberIndex.
36579
36580           function addWayMember(relation, graph) {
36581             var groups, tempWay, item, i, j, k; // remove PTv2 stops and platforms before doing anything.
36582
36583             var PTv2members = [];
36584             var members = [];
36585
36586             for (i = 0; i < relation.members.length; i++) {
36587               var m = relation.members[i];
36588
36589               if (/stop|platform/.test(m.role)) {
36590                 PTv2members.push(m);
36591               } else {
36592                 members.push(m);
36593               }
36594             }
36595
36596             relation = relation.update({
36597               members: members
36598             });
36599
36600             if (insertPair) {
36601               // We're adding a member that must stay paired with an existing member.
36602               // (This feature is used by `actionSplit`)
36603               //
36604               // This is tricky because the members may exist multiple times in the
36605               // member list, and with different A-B/B-A ordering and different roles.
36606               // (e.g. a bus route that loops out and back - #4589).
36607               //
36608               // Replace the existing member with a temporary way,
36609               // so that `osmJoinWays` can treat the pair like a single way.
36610               tempWay = osmWay({
36611                 id: 'wTemp',
36612                 nodes: insertPair.nodes
36613               });
36614               graph = graph.replace(tempWay);
36615               var tempMember = {
36616                 id: tempWay.id,
36617                 type: 'way',
36618                 role: member.role
36619               };
36620               var tempRelation = relation.replaceMember({
36621                 id: insertPair.originalID
36622               }, tempMember, true);
36623               groups = utilArrayGroupBy(tempRelation.members, 'type');
36624               groups.way = groups.way || [];
36625             } else {
36626               // Add the member anywhere, one time. Just push and let `osmJoinWays` decide where to put it.
36627               groups = utilArrayGroupBy(relation.members, 'type');
36628               groups.way = groups.way || [];
36629               groups.way.push(member);
36630             }
36631
36632             members = withIndex(groups.way);
36633             var joined = osmJoinWays(members, graph); // `joined` might not contain all of the way members,
36634             // But will contain only the completed (downloaded) members
36635
36636             for (i = 0; i < joined.length; i++) {
36637               var segment = joined[i];
36638               var nodes = segment.nodes.slice();
36639               var startIndex = segment[0].index; // j = array index in `members` where this segment starts
36640
36641               for (j = 0; j < members.length; j++) {
36642                 if (members[j].index === startIndex) {
36643                   break;
36644                 }
36645               } // k = each member in segment
36646
36647
36648               for (k = 0; k < segment.length; k++) {
36649                 item = segment[k];
36650                 var way = graph.entity(item.id); // If this is a paired item, generate members in correct order and role
36651
36652                 if (tempWay && item.id === tempWay.id) {
36653                   if (nodes[0].id === insertPair.nodes[0]) {
36654                     item.pair = [{
36655                       id: insertPair.originalID,
36656                       type: 'way',
36657                       role: item.role
36658                     }, {
36659                       id: insertPair.insertedID,
36660                       type: 'way',
36661                       role: item.role
36662                     }];
36663                   } else {
36664                     item.pair = [{
36665                       id: insertPair.insertedID,
36666                       type: 'way',
36667                       role: item.role
36668                     }, {
36669                       id: insertPair.originalID,
36670                       type: 'way',
36671                       role: item.role
36672                     }];
36673                   }
36674                 } // reorder `members` if necessary
36675
36676
36677                 if (k > 0) {
36678                   if (j + k >= members.length || item.index !== members[j + k].index) {
36679                     moveMember(members, item.index, j + k);
36680                   }
36681                 }
36682
36683                 nodes.splice(0, way.nodes.length - 1);
36684               }
36685             }
36686
36687             if (tempWay) {
36688               graph = graph.remove(tempWay);
36689             } // Final pass: skip dead items, split pairs, remove index properties
36690
36691
36692             var wayMembers = [];
36693
36694             for (i = 0; i < members.length; i++) {
36695               item = members[i];
36696               if (item.index === -1) continue;
36697
36698               if (item.pair) {
36699                 wayMembers.push(item.pair[0]);
36700                 wayMembers.push(item.pair[1]);
36701               } else {
36702                 wayMembers.push(utilObjectOmit(item, ['index']));
36703               }
36704             } // Put stops and platforms first, then nodes, ways, relations
36705             // This is recommended for Public Transport v2 routes:
36706             // see https://wiki.openstreetmap.org/wiki/Public_transport#Service_routes
36707
36708
36709             var newMembers = PTv2members.concat(groups.node || [], wayMembers, groups.relation || []);
36710             return graph.replace(relation.update({
36711               members: newMembers
36712             })); // `moveMember()` changes the `members` array in place by splicing
36713             // the item with `.index = findIndex` to where it belongs,
36714             // and marking the old position as "dead" with `.index = -1`
36715             //
36716             // j=5, k=0                jk
36717             // segment                 5 4 7 6
36718             // members       0 1 2 3 4 5 6 7 8 9        keep 5 in j+k
36719             //
36720             // j=5, k=1                j k
36721             // segment                 5 4 7 6
36722             // members       0 1 2 3 4 5 6 7 8 9        move 4 to j+k
36723             // members       0 1 2 3 x 5 4 6 7 8 9      moved
36724             //
36725             // j=5, k=2                j   k
36726             // segment                 5 4 7 6
36727             // members       0 1 2 3 x 5 4 6 7 8 9      move 7 to j+k
36728             // members       0 1 2 3 x 5 4 7 6 x 8 9    moved
36729             //
36730             // j=5, k=3                j     k
36731             // segment                 5 4 7 6
36732             // members       0 1 2 3 x 5 4 7 6 x 8 9    keep 6 in j+k
36733             //
36734
36735             function moveMember(arr, findIndex, toIndex) {
36736               var i;
36737
36738               for (i = 0; i < arr.length; i++) {
36739                 if (arr[i].index === findIndex) {
36740                   break;
36741                 }
36742               }
36743
36744               var item = Object.assign({}, arr[i]); // shallow copy
36745
36746               arr[i].index = -1; // mark as dead
36747
36748               item.index = toIndex;
36749               arr.splice(toIndex, 0, item);
36750             } // This is the same as `Relation.indexedMembers`,
36751             // Except we don't want to index all the members, only the ways
36752
36753
36754             function withIndex(arr) {
36755               var result = new Array(arr.length);
36756
36757               for (var i = 0; i < arr.length; i++) {
36758                 result[i] = Object.assign({}, arr[i]); // shallow copy
36759
36760                 result[i].index = i;
36761               }
36762
36763               return result;
36764             }
36765           }
36766         }
36767
36768         function actionAddMidpoint(midpoint, node) {
36769           return function (graph) {
36770             graph = graph.replace(node.move(midpoint.loc));
36771             var parents = utilArrayIntersection(graph.parentWays(graph.entity(midpoint.edge[0])), graph.parentWays(graph.entity(midpoint.edge[1])));
36772             parents.forEach(function (way) {
36773               for (var i = 0; i < way.nodes.length - 1; i++) {
36774                 if (geoEdgeEqual([way.nodes[i], way.nodes[i + 1]], midpoint.edge)) {
36775                   graph = graph.replace(graph.entity(way.id).addNode(node.id, i + 1)); // Add only one midpoint on doubled-back segments,
36776                   // turning them into self-intersections.
36777
36778                   return;
36779                 }
36780               }
36781             });
36782             return graph;
36783           };
36784         }
36785
36786         // https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/AddNodeToWayAction.as
36787         function actionAddVertex(wayId, nodeId, index) {
36788           return function (graph) {
36789             return graph.replace(graph.entity(wayId).addNode(nodeId, index));
36790           };
36791         }
36792
36793         function actionChangeMember(relationId, member, memberIndex) {
36794           return function (graph) {
36795             return graph.replace(graph.entity(relationId).updateMember(member, memberIndex));
36796           };
36797         }
36798
36799         function actionChangePreset(entityID, oldPreset, newPreset, skipFieldDefaults) {
36800           return function action(graph) {
36801             var entity = graph.entity(entityID);
36802             var geometry = entity.geometry(graph);
36803             var tags = entity.tags; // preserve tags that the new preset might care about, if any
36804
36805             if (oldPreset) tags = oldPreset.unsetTags(tags, geometry, newPreset && newPreset.addTags ? Object.keys(newPreset.addTags) : null);
36806             if (newPreset) tags = newPreset.setTags(tags, geometry, skipFieldDefaults);
36807             return graph.replace(entity.update({
36808               tags: tags
36809             }));
36810           };
36811         }
36812
36813         function actionChangeTags(entityId, tags) {
36814           return function (graph) {
36815             var entity = graph.entity(entityId);
36816             return graph.replace(entity.update({
36817               tags: tags
36818             }));
36819           };
36820         }
36821
36822         function osmNode() {
36823           if (!(this instanceof osmNode)) {
36824             return new osmNode().initialize(arguments);
36825           } else if (arguments.length) {
36826             this.initialize(arguments);
36827           }
36828         }
36829         osmEntity.node = osmNode;
36830         osmNode.prototype = Object.create(osmEntity.prototype);
36831         Object.assign(osmNode.prototype, {
36832           type: 'node',
36833           loc: [9999, 9999],
36834           extent: function extent() {
36835             return new geoExtent(this.loc);
36836           },
36837           geometry: function geometry(graph) {
36838             return graph["transient"](this, 'geometry', function () {
36839               return graph.isPoi(this) ? 'point' : 'vertex';
36840             });
36841           },
36842           move: function move(loc) {
36843             return this.update({
36844               loc: loc
36845             });
36846           },
36847           isDegenerate: function isDegenerate() {
36848             return !(Array.isArray(this.loc) && this.loc.length === 2 && this.loc[0] >= -180 && this.loc[0] <= 180 && this.loc[1] >= -90 && this.loc[1] <= 90);
36849           },
36850           // Inspect tags and geometry to determine which direction(s) this node/vertex points
36851           directions: function directions(resolver, projection) {
36852             var val;
36853             var i; // which tag to use?
36854
36855             if (this.isHighwayIntersection(resolver) && (this.tags.stop || '').toLowerCase() === 'all') {
36856               // all-way stop tag on a highway intersection
36857               val = 'all';
36858             } else {
36859               // generic direction tag
36860               val = (this.tags.direction || '').toLowerCase(); // better suffix-style direction tag
36861
36862               var re = /:direction$/i;
36863               var keys = Object.keys(this.tags);
36864
36865               for (i = 0; i < keys.length; i++) {
36866                 if (re.test(keys[i])) {
36867                   val = this.tags[keys[i]].toLowerCase();
36868                   break;
36869                 }
36870               }
36871             }
36872
36873             if (val === '') return [];
36874             var cardinal = {
36875               north: 0,
36876               n: 0,
36877               northnortheast: 22,
36878               nne: 22,
36879               northeast: 45,
36880               ne: 45,
36881               eastnortheast: 67,
36882               ene: 67,
36883               east: 90,
36884               e: 90,
36885               eastsoutheast: 112,
36886               ese: 112,
36887               southeast: 135,
36888               se: 135,
36889               southsoutheast: 157,
36890               sse: 157,
36891               south: 180,
36892               s: 180,
36893               southsouthwest: 202,
36894               ssw: 202,
36895               southwest: 225,
36896               sw: 225,
36897               westsouthwest: 247,
36898               wsw: 247,
36899               west: 270,
36900               w: 270,
36901               westnorthwest: 292,
36902               wnw: 292,
36903               northwest: 315,
36904               nw: 315,
36905               northnorthwest: 337,
36906               nnw: 337
36907             };
36908             var values = val.split(';');
36909             var results = [];
36910             values.forEach(function (v) {
36911               // swap cardinal for numeric directions
36912               if (cardinal[v] !== undefined) {
36913                 v = cardinal[v];
36914               } // numeric direction - just add to results
36915
36916
36917               if (v !== '' && !isNaN(+v)) {
36918                 results.push(+v);
36919                 return;
36920               } // string direction - inspect parent ways
36921
36922
36923               var lookBackward = this.tags['traffic_sign:backward'] || v === 'backward' || v === 'both' || v === 'all';
36924               var lookForward = this.tags['traffic_sign:forward'] || v === 'forward' || v === 'both' || v === 'all';
36925               if (!lookForward && !lookBackward) return;
36926               var nodeIds = {};
36927               resolver.parentWays(this).forEach(function (parent) {
36928                 var nodes = parent.nodes;
36929
36930                 for (i = 0; i < nodes.length; i++) {
36931                   if (nodes[i] === this.id) {
36932                     // match current entity
36933                     if (lookForward && i > 0) {
36934                       nodeIds[nodes[i - 1]] = true; // look back to prev node
36935                     }
36936
36937                     if (lookBackward && i < nodes.length - 1) {
36938                       nodeIds[nodes[i + 1]] = true; // look ahead to next node
36939                     }
36940                   }
36941                 }
36942               }, this);
36943               Object.keys(nodeIds).forEach(function (nodeId) {
36944                 // +90 because geoAngle returns angle from X axis, not Y (north)
36945                 results.push(geoAngle(this, resolver.entity(nodeId), projection) * (180 / Math.PI) + 90);
36946               }, this);
36947             }, this);
36948             return utilArrayUniq(results);
36949           },
36950           isCrossing: function isCrossing() {
36951             return this.tags.highway === 'crossing' || this.tags.railway && this.tags.railway.indexOf('crossing') !== -1;
36952           },
36953           isEndpoint: function isEndpoint(resolver) {
36954             return resolver["transient"](this, 'isEndpoint', function () {
36955               var id = this.id;
36956               return resolver.parentWays(this).filter(function (parent) {
36957                 return !parent.isClosed() && !!parent.affix(id);
36958               }).length > 0;
36959             });
36960           },
36961           isConnected: function isConnected(resolver) {
36962             return resolver["transient"](this, 'isConnected', function () {
36963               var parents = resolver.parentWays(this);
36964
36965               if (parents.length > 1) {
36966                 // vertex is connected to multiple parent ways
36967                 for (var i in parents) {
36968                   if (parents[i].geometry(resolver) === 'line' && parents[i].hasInterestingTags()) return true;
36969                 }
36970               } else if (parents.length === 1) {
36971                 var way = parents[0];
36972                 var nodes = way.nodes.slice();
36973
36974                 if (way.isClosed()) {
36975                   nodes.pop();
36976                 } // ignore connecting node if closed
36977                 // return true if vertex appears multiple times (way is self intersecting)
36978
36979
36980                 return nodes.indexOf(this.id) !== nodes.lastIndexOf(this.id);
36981               }
36982
36983               return false;
36984             });
36985           },
36986           parentIntersectionWays: function parentIntersectionWays(resolver) {
36987             return resolver["transient"](this, 'parentIntersectionWays', function () {
36988               return resolver.parentWays(this).filter(function (parent) {
36989                 return (parent.tags.highway || parent.tags.waterway || parent.tags.railway || parent.tags.aeroway) && parent.geometry(resolver) === 'line';
36990               });
36991             });
36992           },
36993           isIntersection: function isIntersection(resolver) {
36994             return this.parentIntersectionWays(resolver).length > 1;
36995           },
36996           isHighwayIntersection: function isHighwayIntersection(resolver) {
36997             return resolver["transient"](this, 'isHighwayIntersection', function () {
36998               return resolver.parentWays(this).filter(function (parent) {
36999                 return parent.tags.highway && parent.geometry(resolver) === 'line';
37000               }).length > 1;
37001             });
37002           },
37003           isOnAddressLine: function isOnAddressLine(resolver) {
37004             return resolver["transient"](this, 'isOnAddressLine', function () {
37005               return resolver.parentWays(this).filter(function (parent) {
37006                 return parent.tags.hasOwnProperty('addr:interpolation') && parent.geometry(resolver) === 'line';
37007               }).length > 0;
37008             });
37009           },
37010           asJXON: function asJXON(changeset_id) {
37011             var r = {
37012               node: {
37013                 '@id': this.osmId(),
37014                 '@lon': this.loc[0],
37015                 '@lat': this.loc[1],
37016                 '@version': this.version || 0,
37017                 tag: Object.keys(this.tags).map(function (k) {
37018                   return {
37019                     keyAttributes: {
37020                       k: k,
37021                       v: this.tags[k]
37022                     }
37023                   };
37024                 }, this)
37025               }
37026             };
37027             if (changeset_id) r.node['@changeset'] = changeset_id;
37028             return r;
37029           },
37030           asGeoJSON: function asGeoJSON() {
37031             return {
37032               type: 'Point',
37033               coordinates: this.loc
37034             };
37035           }
37036         });
37037
37038         function actionCircularize(wayId, projection, maxAngle) {
37039           maxAngle = (maxAngle || 20) * Math.PI / 180;
37040
37041           var action = function action(graph, t) {
37042             if (t === null || !isFinite(t)) t = 1;
37043             t = Math.min(Math.max(+t, 0), 1);
37044             var way = graph.entity(wayId);
37045             var origNodes = {};
37046             graph.childNodes(way).forEach(function (node) {
37047               if (!origNodes[node.id]) origNodes[node.id] = node;
37048             });
37049
37050             if (!way.isConvex(graph)) {
37051               graph = action.makeConvex(graph);
37052             }
37053
37054             var nodes = utilArrayUniq(graph.childNodes(way));
37055             var keyNodes = nodes.filter(function (n) {
37056               return graph.parentWays(n).length !== 1;
37057             });
37058             var points = nodes.map(function (n) {
37059               return projection(n.loc);
37060             });
37061             var keyPoints = keyNodes.map(function (n) {
37062               return projection(n.loc);
37063             });
37064             var centroid = points.length === 2 ? geoVecInterp(points[0], points[1], 0.5) : d3_polygonCentroid(points);
37065             var radius = d3_median(points, function (p) {
37066               return geoVecLength(centroid, p);
37067             });
37068             var sign = d3_polygonArea(points) > 0 ? 1 : -1;
37069             var ids, i, j, k; // we need at least two key nodes for the algorithm to work
37070
37071             if (!keyNodes.length) {
37072               keyNodes = [nodes[0]];
37073               keyPoints = [points[0]];
37074             }
37075
37076             if (keyNodes.length === 1) {
37077               var index = nodes.indexOf(keyNodes[0]);
37078               var oppositeIndex = Math.floor((index + nodes.length / 2) % nodes.length);
37079               keyNodes.push(nodes[oppositeIndex]);
37080               keyPoints.push(points[oppositeIndex]);
37081             } // key points and nodes are those connected to the ways,
37082             // they are projected onto the circle, in between nodes are moved
37083             // to constant intervals between key nodes, extra in between nodes are
37084             // added if necessary.
37085
37086
37087             for (i = 0; i < keyPoints.length; i++) {
37088               var nextKeyNodeIndex = (i + 1) % keyNodes.length;
37089               var startNode = keyNodes[i];
37090               var endNode = keyNodes[nextKeyNodeIndex];
37091               var startNodeIndex = nodes.indexOf(startNode);
37092               var endNodeIndex = nodes.indexOf(endNode);
37093               var numberNewPoints = -1;
37094               var indexRange = endNodeIndex - startNodeIndex;
37095               var nearNodes = {};
37096               var inBetweenNodes = [];
37097               var startAngle, endAngle, totalAngle, eachAngle;
37098               var angle, loc, node, origNode;
37099
37100               if (indexRange < 0) {
37101                 indexRange += nodes.length;
37102               } // position this key node
37103
37104
37105               var distance = geoVecLength(centroid, keyPoints[i]) || 1e-4;
37106               keyPoints[i] = [centroid[0] + (keyPoints[i][0] - centroid[0]) / distance * radius, centroid[1] + (keyPoints[i][1] - centroid[1]) / distance * radius];
37107               loc = projection.invert(keyPoints[i]);
37108               node = keyNodes[i];
37109               origNode = origNodes[node.id];
37110               node = node.move(geoVecInterp(origNode.loc, loc, t));
37111               graph = graph.replace(node); // figure out the between delta angle we want to match to
37112
37113               startAngle = Math.atan2(keyPoints[i][1] - centroid[1], keyPoints[i][0] - centroid[0]);
37114               endAngle = Math.atan2(keyPoints[nextKeyNodeIndex][1] - centroid[1], keyPoints[nextKeyNodeIndex][0] - centroid[0]);
37115               totalAngle = endAngle - startAngle; // detects looping around -pi/pi
37116
37117               if (totalAngle * sign > 0) {
37118                 totalAngle = -sign * (2 * Math.PI - Math.abs(totalAngle));
37119               }
37120
37121               do {
37122                 numberNewPoints++;
37123                 eachAngle = totalAngle / (indexRange + numberNewPoints);
37124               } while (Math.abs(eachAngle) > maxAngle); // move existing nodes
37125
37126
37127               for (j = 1; j < indexRange; j++) {
37128                 angle = startAngle + j * eachAngle;
37129                 loc = projection.invert([centroid[0] + Math.cos(angle) * radius, centroid[1] + Math.sin(angle) * radius]);
37130                 node = nodes[(j + startNodeIndex) % nodes.length];
37131                 origNode = origNodes[node.id];
37132                 nearNodes[node.id] = angle;
37133                 node = node.move(geoVecInterp(origNode.loc, loc, t));
37134                 graph = graph.replace(node);
37135               } // add new in between nodes if necessary
37136
37137
37138               for (j = 0; j < numberNewPoints; j++) {
37139                 angle = startAngle + (indexRange + j) * eachAngle;
37140                 loc = projection.invert([centroid[0] + Math.cos(angle) * radius, centroid[1] + Math.sin(angle) * radius]); // choose a nearnode to use as the original
37141
37142                 var min = Infinity;
37143
37144                 for (var nodeId in nearNodes) {
37145                   var nearAngle = nearNodes[nodeId];
37146                   var dist = Math.abs(nearAngle - angle);
37147
37148                   if (dist < min) {
37149                     min = dist;
37150                     origNode = origNodes[nodeId];
37151                   }
37152                 }
37153
37154                 node = osmNode({
37155                   loc: geoVecInterp(origNode.loc, loc, t)
37156                 });
37157                 graph = graph.replace(node);
37158                 nodes.splice(endNodeIndex + j, 0, node);
37159                 inBetweenNodes.push(node.id);
37160               } // Check for other ways that share these keyNodes..
37161               // If keyNodes are adjacent in both ways,
37162               // we can add inBetweenNodes to that shared way too..
37163
37164
37165               if (indexRange === 1 && inBetweenNodes.length) {
37166                 var startIndex1 = way.nodes.lastIndexOf(startNode.id);
37167                 var endIndex1 = way.nodes.lastIndexOf(endNode.id);
37168                 var wayDirection1 = endIndex1 - startIndex1;
37169
37170                 if (wayDirection1 < -1) {
37171                   wayDirection1 = 1;
37172                 }
37173
37174                 var parentWays = graph.parentWays(keyNodes[i]);
37175
37176                 for (j = 0; j < parentWays.length; j++) {
37177                   var sharedWay = parentWays[j];
37178                   if (sharedWay === way) continue;
37179
37180                   if (sharedWay.areAdjacent(startNode.id, endNode.id)) {
37181                     var startIndex2 = sharedWay.nodes.lastIndexOf(startNode.id);
37182                     var endIndex2 = sharedWay.nodes.lastIndexOf(endNode.id);
37183                     var wayDirection2 = endIndex2 - startIndex2;
37184                     var insertAt = endIndex2;
37185
37186                     if (wayDirection2 < -1) {
37187                       wayDirection2 = 1;
37188                     }
37189
37190                     if (wayDirection1 !== wayDirection2) {
37191                       inBetweenNodes.reverse();
37192                       insertAt = startIndex2;
37193                     }
37194
37195                     for (k = 0; k < inBetweenNodes.length; k++) {
37196                       sharedWay = sharedWay.addNode(inBetweenNodes[k], insertAt + k);
37197                     }
37198
37199                     graph = graph.replace(sharedWay);
37200                   }
37201                 }
37202               }
37203             } // update the way to have all the new nodes
37204
37205
37206             ids = nodes.map(function (n) {
37207               return n.id;
37208             });
37209             ids.push(ids[0]);
37210             way = way.update({
37211               nodes: ids
37212             });
37213             graph = graph.replace(way);
37214             return graph;
37215           };
37216
37217           action.makeConvex = function (graph) {
37218             var way = graph.entity(wayId);
37219             var nodes = utilArrayUniq(graph.childNodes(way));
37220             var points = nodes.map(function (n) {
37221               return projection(n.loc);
37222             });
37223             var sign = d3_polygonArea(points) > 0 ? 1 : -1;
37224             var hull = d3_polygonHull(points);
37225             var i, j; // D3 convex hulls go counterclockwise..
37226
37227             if (sign === -1) {
37228               nodes.reverse();
37229               points.reverse();
37230             }
37231
37232             for (i = 0; i < hull.length - 1; i++) {
37233               var startIndex = points.indexOf(hull[i]);
37234               var endIndex = points.indexOf(hull[i + 1]);
37235               var indexRange = endIndex - startIndex;
37236
37237               if (indexRange < 0) {
37238                 indexRange += nodes.length;
37239               } // move interior nodes to the surface of the convex hull..
37240
37241
37242               for (j = 1; j < indexRange; j++) {
37243                 var point = geoVecInterp(hull[i], hull[i + 1], j / indexRange);
37244                 var node = nodes[(j + startIndex) % nodes.length].move(projection.invert(point));
37245                 graph = graph.replace(node);
37246               }
37247             }
37248
37249             return graph;
37250           };
37251
37252           action.disabled = function (graph) {
37253             if (!graph.entity(wayId).isClosed()) {
37254               return 'not_closed';
37255             } //disable when already circular
37256
37257
37258             var way = graph.entity(wayId);
37259             var nodes = utilArrayUniq(graph.childNodes(way));
37260             var points = nodes.map(function (n) {
37261               return projection(n.loc);
37262             });
37263             var hull = d3_polygonHull(points);
37264             var epsilonAngle = Math.PI / 180;
37265
37266             if (hull.length !== points.length || hull.length < 3) {
37267               return false;
37268             }
37269
37270             var centroid = d3_polygonCentroid(points);
37271             var radius = geoVecLengthSquare(centroid, points[0]);
37272             var i, actualPoint; // compare distances between centroid and points
37273
37274             for (i = 0; i < hull.length; i++) {
37275               actualPoint = hull[i];
37276               var actualDist = geoVecLengthSquare(actualPoint, centroid);
37277               var diff = Math.abs(actualDist - radius); //compare distances with epsilon-error (5%)
37278
37279               if (diff > 0.05 * radius) {
37280                 return false;
37281               }
37282             } //check if central angles are smaller than maxAngle
37283
37284
37285             for (i = 0; i < hull.length; i++) {
37286               actualPoint = hull[i];
37287               var nextPoint = hull[(i + 1) % hull.length];
37288               var startAngle = Math.atan2(actualPoint[1] - centroid[1], actualPoint[0] - centroid[0]);
37289               var endAngle = Math.atan2(nextPoint[1] - centroid[1], nextPoint[0] - centroid[0]);
37290               var angle = endAngle - startAngle;
37291
37292               if (angle < 0) {
37293                 angle = -angle;
37294               }
37295
37296               if (angle > Math.PI) {
37297                 angle = 2 * Math.PI - angle;
37298               }
37299
37300               if (angle > maxAngle + epsilonAngle) {
37301                 return false;
37302               }
37303             }
37304
37305             return 'already_circular';
37306           };
37307
37308           action.transitionable = true;
37309           return action;
37310         }
37311
37312         function actionDeleteWay(wayID) {
37313           function canDeleteNode(node, graph) {
37314             // don't delete nodes still attached to ways or relations
37315             if (graph.parentWays(node).length || graph.parentRelations(node).length) return false;
37316             var geometries = osmNodeGeometriesForTags(node.tags); // don't delete if this node can be a standalone point
37317
37318             if (geometries.point) return false; // delete if this node only be a vertex
37319
37320             if (geometries.vertex) return true; // iD doesn't know if this should be a point or vertex,
37321             // so only delete if there are no interesting tags
37322
37323             return !node.hasInterestingTags();
37324           }
37325
37326           var action = function action(graph) {
37327             var way = graph.entity(wayID);
37328             graph.parentRelations(way).forEach(function (parent) {
37329               parent = parent.removeMembersWithID(wayID);
37330               graph = graph.replace(parent);
37331
37332               if (parent.isDegenerate()) {
37333                 graph = actionDeleteRelation(parent.id)(graph);
37334               }
37335             });
37336             new Set(way.nodes).forEach(function (nodeID) {
37337               graph = graph.replace(way.removeNode(nodeID));
37338               var node = graph.entity(nodeID);
37339
37340               if (canDeleteNode(node, graph)) {
37341                 graph = graph.remove(node);
37342               }
37343             });
37344             return graph.remove(way);
37345           };
37346
37347           return action;
37348         }
37349
37350         function actionDeleteMultiple(ids) {
37351           var actions = {
37352             way: actionDeleteWay,
37353             node: actionDeleteNode,
37354             relation: actionDeleteRelation
37355           };
37356
37357           var action = function action(graph) {
37358             ids.forEach(function (id) {
37359               if (graph.hasEntity(id)) {
37360                 // It may have been deleted already.
37361                 graph = actions[graph.entity(id).type](id)(graph);
37362               }
37363             });
37364             return graph;
37365           };
37366
37367           return action;
37368         }
37369
37370         function actionDeleteRelation(relationID, allowUntaggedMembers) {
37371           function canDeleteEntity(entity, graph) {
37372             return !graph.parentWays(entity).length && !graph.parentRelations(entity).length && !entity.hasInterestingTags() && !allowUntaggedMembers;
37373           }
37374
37375           var action = function action(graph) {
37376             var relation = graph.entity(relationID);
37377             graph.parentRelations(relation).forEach(function (parent) {
37378               parent = parent.removeMembersWithID(relationID);
37379               graph = graph.replace(parent);
37380
37381               if (parent.isDegenerate()) {
37382                 graph = actionDeleteRelation(parent.id)(graph);
37383               }
37384             });
37385             var memberIDs = utilArrayUniq(relation.members.map(function (m) {
37386               return m.id;
37387             }));
37388             memberIDs.forEach(function (memberID) {
37389               graph = graph.replace(relation.removeMembersWithID(memberID));
37390               var entity = graph.entity(memberID);
37391
37392               if (canDeleteEntity(entity, graph)) {
37393                 graph = actionDeleteMultiple([memberID])(graph);
37394               }
37395             });
37396             return graph.remove(relation);
37397           };
37398
37399           return action;
37400         }
37401
37402         function actionDeleteNode(nodeId) {
37403           var action = function action(graph) {
37404             var node = graph.entity(nodeId);
37405             graph.parentWays(node).forEach(function (parent) {
37406               parent = parent.removeNode(nodeId);
37407               graph = graph.replace(parent);
37408
37409               if (parent.isDegenerate()) {
37410                 graph = actionDeleteWay(parent.id)(graph);
37411               }
37412             });
37413             graph.parentRelations(node).forEach(function (parent) {
37414               parent = parent.removeMembersWithID(nodeId);
37415               graph = graph.replace(parent);
37416
37417               if (parent.isDegenerate()) {
37418                 graph = actionDeleteRelation(parent.id)(graph);
37419               }
37420             });
37421             return graph.remove(node);
37422           };
37423
37424           return action;
37425         }
37426
37427         //
37428         // First choose a node to be the survivor, with preference given
37429         // to an existing (not new) node.
37430         //
37431         // Tags and relation memberships of of non-surviving nodes are merged
37432         // to the survivor.
37433         //
37434         // This is the inverse of `iD.actionDisconnect`.
37435         //
37436         // Reference:
37437         //   https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/MergeNodesAction.as
37438         //   https://github.com/openstreetmap/josm/blob/mirror/src/org/openstreetmap/josm/actions/MergeNodesAction.java
37439         //
37440
37441         function actionConnect(nodeIDs) {
37442           var action = function action(graph) {
37443             var survivor;
37444             var node;
37445             var parents;
37446             var i, j; // Choose a survivor node, prefer an existing (not new) node - #4974
37447
37448             for (i = 0; i < nodeIDs.length; i++) {
37449               survivor = graph.entity(nodeIDs[i]);
37450               if (survivor.version) break; // found one
37451             } // Replace all non-surviving nodes with the survivor and merge tags.
37452
37453
37454             for (i = 0; i < nodeIDs.length; i++) {
37455               node = graph.entity(nodeIDs[i]);
37456               if (node.id === survivor.id) continue;
37457               parents = graph.parentWays(node);
37458
37459               for (j = 0; j < parents.length; j++) {
37460                 graph = graph.replace(parents[j].replaceNode(node.id, survivor.id));
37461               }
37462
37463               parents = graph.parentRelations(node);
37464
37465               for (j = 0; j < parents.length; j++) {
37466                 graph = graph.replace(parents[j].replaceMember(node, survivor));
37467               }
37468
37469               survivor = survivor.mergeTags(node.tags);
37470               graph = actionDeleteNode(node.id)(graph);
37471             }
37472
37473             graph = graph.replace(survivor); // find and delete any degenerate ways created by connecting adjacent vertices
37474
37475             parents = graph.parentWays(survivor);
37476
37477             for (i = 0; i < parents.length; i++) {
37478               if (parents[i].isDegenerate()) {
37479                 graph = actionDeleteWay(parents[i].id)(graph);
37480               }
37481             }
37482
37483             return graph;
37484           };
37485
37486           action.disabled = function (graph) {
37487             var seen = {};
37488             var restrictionIDs = [];
37489             var survivor;
37490             var node, way;
37491             var relations, relation, role;
37492             var i, j, k; // Choose a survivor node, prefer an existing (not new) node - #4974
37493
37494             for (i = 0; i < nodeIDs.length; i++) {
37495               survivor = graph.entity(nodeIDs[i]);
37496               if (survivor.version) break; // found one
37497             } // 1. disable if the nodes being connected have conflicting relation roles
37498
37499
37500             for (i = 0; i < nodeIDs.length; i++) {
37501               node = graph.entity(nodeIDs[i]);
37502               relations = graph.parentRelations(node);
37503
37504               for (j = 0; j < relations.length; j++) {
37505                 relation = relations[j];
37506                 role = relation.memberById(node.id).role || ''; // if this node is a via node in a restriction, remember for later
37507
37508                 if (relation.hasFromViaTo()) {
37509                   restrictionIDs.push(relation.id);
37510                 }
37511
37512                 if (seen[relation.id] !== undefined && seen[relation.id] !== role) {
37513                   return 'relation';
37514                 } else {
37515                   seen[relation.id] = role;
37516                 }
37517               }
37518             } // gather restrictions for parent ways
37519
37520
37521             for (i = 0; i < nodeIDs.length; i++) {
37522               node = graph.entity(nodeIDs[i]);
37523               var parents = graph.parentWays(node);
37524
37525               for (j = 0; j < parents.length; j++) {
37526                 var parent = parents[j];
37527                 relations = graph.parentRelations(parent);
37528
37529                 for (k = 0; k < relations.length; k++) {
37530                   relation = relations[k];
37531
37532                   if (relation.hasFromViaTo()) {
37533                     restrictionIDs.push(relation.id);
37534                   }
37535                 }
37536               }
37537             } // test restrictions
37538
37539
37540             restrictionIDs = utilArrayUniq(restrictionIDs);
37541
37542             for (i = 0; i < restrictionIDs.length; i++) {
37543               relation = graph.entity(restrictionIDs[i]);
37544               if (!relation.isComplete(graph)) continue;
37545               var memberWays = relation.members.filter(function (m) {
37546                 return m.type === 'way';
37547               }).map(function (m) {
37548                 return graph.entity(m.id);
37549               });
37550               memberWays = utilArrayUniq(memberWays);
37551               var f = relation.memberByRole('from');
37552               var t = relation.memberByRole('to');
37553               var isUturn = f.id === t.id; // 2a. disable if connection would damage a restriction
37554               // (a key node is a node at the junction of ways)
37555
37556               var nodes = {
37557                 from: [],
37558                 via: [],
37559                 to: [],
37560                 keyfrom: [],
37561                 keyto: []
37562               };
37563
37564               for (j = 0; j < relation.members.length; j++) {
37565                 collectNodes(relation.members[j], nodes);
37566               }
37567
37568               nodes.keyfrom = utilArrayUniq(nodes.keyfrom.filter(hasDuplicates));
37569               nodes.keyto = utilArrayUniq(nodes.keyto.filter(hasDuplicates));
37570               var filter = keyNodeFilter(nodes.keyfrom, nodes.keyto);
37571               nodes.from = nodes.from.filter(filter);
37572               nodes.via = nodes.via.filter(filter);
37573               nodes.to = nodes.to.filter(filter);
37574               var connectFrom = false;
37575               var connectVia = false;
37576               var connectTo = false;
37577               var connectKeyFrom = false;
37578               var connectKeyTo = false;
37579
37580               for (j = 0; j < nodeIDs.length; j++) {
37581                 var n = nodeIDs[j];
37582
37583                 if (nodes.from.indexOf(n) !== -1) {
37584                   connectFrom = true;
37585                 }
37586
37587                 if (nodes.via.indexOf(n) !== -1) {
37588                   connectVia = true;
37589                 }
37590
37591                 if (nodes.to.indexOf(n) !== -1) {
37592                   connectTo = true;
37593                 }
37594
37595                 if (nodes.keyfrom.indexOf(n) !== -1) {
37596                   connectKeyFrom = true;
37597                 }
37598
37599                 if (nodes.keyto.indexOf(n) !== -1) {
37600                   connectKeyTo = true;
37601                 }
37602               }
37603
37604               if (connectFrom && connectTo && !isUturn) {
37605                 return 'restriction';
37606               }
37607
37608               if (connectFrom && connectVia) {
37609                 return 'restriction';
37610               }
37611
37612               if (connectTo && connectVia) {
37613                 return 'restriction';
37614               } // connecting to a key node -
37615               // if both nodes are on a member way (i.e. part of the turn restriction),
37616               // the connecting node must be adjacent to the key node.
37617
37618
37619               if (connectKeyFrom || connectKeyTo) {
37620                 if (nodeIDs.length !== 2) {
37621                   return 'restriction';
37622                 }
37623
37624                 var n0 = null;
37625                 var n1 = null;
37626
37627                 for (j = 0; j < memberWays.length; j++) {
37628                   way = memberWays[j];
37629
37630                   if (way.contains(nodeIDs[0])) {
37631                     n0 = nodeIDs[0];
37632                   }
37633
37634                   if (way.contains(nodeIDs[1])) {
37635                     n1 = nodeIDs[1];
37636                   }
37637                 }
37638
37639                 if (n0 && n1) {
37640                   // both nodes are part of the restriction
37641                   var ok = false;
37642
37643                   for (j = 0; j < memberWays.length; j++) {
37644                     way = memberWays[j];
37645
37646                     if (way.areAdjacent(n0, n1)) {
37647                       ok = true;
37648                       break;
37649                     }
37650                   }
37651
37652                   if (!ok) {
37653                     return 'restriction';
37654                   }
37655                 }
37656               } // 2b. disable if nodes being connected will destroy a member way in a restriction
37657               // (to test, make a copy and try actually connecting the nodes)
37658
37659
37660               for (j = 0; j < memberWays.length; j++) {
37661                 way = memberWays[j].update({}); // make copy
37662
37663                 for (k = 0; k < nodeIDs.length; k++) {
37664                   if (nodeIDs[k] === survivor.id) continue;
37665
37666                   if (way.areAdjacent(nodeIDs[k], survivor.id)) {
37667                     way = way.removeNode(nodeIDs[k]);
37668                   } else {
37669                     way = way.replaceNode(nodeIDs[k], survivor.id);
37670                   }
37671                 }
37672
37673                 if (way.isDegenerate()) {
37674                   return 'restriction';
37675                 }
37676               }
37677             }
37678
37679             return false; // if a key node appears multiple times (indexOf !== lastIndexOf) it's a FROM-VIA or TO-VIA junction
37680
37681             function hasDuplicates(n, i, arr) {
37682               return arr.indexOf(n) !== arr.lastIndexOf(n);
37683             }
37684
37685             function keyNodeFilter(froms, tos) {
37686               return function (n) {
37687                 return froms.indexOf(n) === -1 && tos.indexOf(n) === -1;
37688               };
37689             }
37690
37691             function collectNodes(member, collection) {
37692               var entity = graph.hasEntity(member.id);
37693               if (!entity) return;
37694               var role = member.role || '';
37695
37696               if (!collection[role]) {
37697                 collection[role] = [];
37698               }
37699
37700               if (member.type === 'node') {
37701                 collection[role].push(member.id);
37702
37703                 if (role === 'via') {
37704                   collection.keyfrom.push(member.id);
37705                   collection.keyto.push(member.id);
37706                 }
37707               } else if (member.type === 'way') {
37708                 collection[role].push.apply(collection[role], entity.nodes);
37709
37710                 if (role === 'from' || role === 'via') {
37711                   collection.keyfrom.push(entity.first());
37712                   collection.keyfrom.push(entity.last());
37713                 }
37714
37715                 if (role === 'to' || role === 'via') {
37716                   collection.keyto.push(entity.first());
37717                   collection.keyto.push(entity.last());
37718                 }
37719               }
37720             }
37721           };
37722
37723           return action;
37724         }
37725
37726         function actionCopyEntities(ids, fromGraph) {
37727           var _copies = {};
37728
37729           var action = function action(graph) {
37730             ids.forEach(function (id) {
37731               fromGraph.entity(id).copy(fromGraph, _copies);
37732             });
37733
37734             for (var id in _copies) {
37735               graph = graph.replace(_copies[id]);
37736             }
37737
37738             return graph;
37739           };
37740
37741           action.copies = function () {
37742             return _copies;
37743           };
37744
37745           return action;
37746         }
37747
37748         function actionDeleteMember(relationId, memberIndex) {
37749           return function (graph) {
37750             var relation = graph.entity(relationId).removeMember(memberIndex);
37751             graph = graph.replace(relation);
37752
37753             if (relation.isDegenerate()) {
37754               graph = actionDeleteRelation(relation.id)(graph);
37755             }
37756
37757             return graph;
37758           };
37759         }
37760
37761         function actionDiscardTags(difference, discardTags) {
37762           discardTags = discardTags || {};
37763           return function (graph) {
37764             difference.modified().forEach(checkTags);
37765             difference.created().forEach(checkTags);
37766             return graph;
37767
37768             function checkTags(entity) {
37769               var keys = Object.keys(entity.tags);
37770               var didDiscard = false;
37771               var tags = {};
37772
37773               for (var i = 0; i < keys.length; i++) {
37774                 var k = keys[i];
37775
37776                 if (discardTags[k] || !entity.tags[k]) {
37777                   didDiscard = true;
37778                 } else {
37779                   tags[k] = entity.tags[k];
37780                 }
37781               }
37782
37783               if (didDiscard) {
37784                 graph = graph.replace(entity.update({
37785                   tags: tags
37786                 }));
37787               }
37788             }
37789           };
37790         }
37791
37792         //
37793         // Optionally, disconnect only the given ways.
37794         //
37795         // For testing convenience, accepts an ID to assign to the (first) new node.
37796         // Normally, this will be undefined and the way will automatically
37797         // be assigned a new ID.
37798         //
37799         // This is the inverse of `iD.actionConnect`.
37800         //
37801         // Reference:
37802         //   https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/UnjoinNodeAction.as
37803         //   https://github.com/openstreetmap/josm/blob/mirror/src/org/openstreetmap/josm/actions/UnGlueAction.java
37804         //
37805
37806         function actionDisconnect(nodeId, newNodeId) {
37807           var wayIds;
37808
37809           var action = function action(graph) {
37810             var node = graph.entity(nodeId);
37811             var connections = action.connections(graph);
37812             connections.forEach(function (connection) {
37813               var way = graph.entity(connection.wayID);
37814               var newNode = osmNode({
37815                 id: newNodeId,
37816                 loc: node.loc,
37817                 tags: node.tags
37818               });
37819               graph = graph.replace(newNode);
37820
37821               if (connection.index === 0 && way.isArea()) {
37822                 // replace shared node with shared node..
37823                 graph = graph.replace(way.replaceNode(way.nodes[0], newNode.id));
37824               } else if (way.isClosed() && connection.index === way.nodes.length - 1) {
37825                 // replace closing node with new new node..
37826                 graph = graph.replace(way.unclose().addNode(newNode.id));
37827               } else {
37828                 // replace shared node with multiple new nodes..
37829                 graph = graph.replace(way.updateNode(newNode.id, connection.index));
37830               }
37831             });
37832             return graph;
37833           };
37834
37835           action.connections = function (graph) {
37836             var candidates = [];
37837             var keeping = false;
37838             var parentWays = graph.parentWays(graph.entity(nodeId));
37839             var way, waynode;
37840
37841             for (var i = 0; i < parentWays.length; i++) {
37842               way = parentWays[i];
37843
37844               if (wayIds && wayIds.indexOf(way.id) === -1) {
37845                 keeping = true;
37846                 continue;
37847               }
37848
37849               if (way.isArea() && way.nodes[0] === nodeId) {
37850                 candidates.push({
37851                   wayID: way.id,
37852                   index: 0
37853                 });
37854               } else {
37855                 for (var j = 0; j < way.nodes.length; j++) {
37856                   waynode = way.nodes[j];
37857
37858                   if (waynode === nodeId) {
37859                     if (way.isClosed() && parentWays.length > 1 && wayIds && wayIds.indexOf(way.id) !== -1 && j === way.nodes.length - 1) {
37860                       continue;
37861                     }
37862
37863                     candidates.push({
37864                       wayID: way.id,
37865                       index: j
37866                     });
37867                   }
37868                 }
37869               }
37870             }
37871
37872             return keeping ? candidates : candidates.slice(1);
37873           };
37874
37875           action.disabled = function (graph) {
37876             var connections = action.connections(graph);
37877             if (connections.length === 0) return 'not_connected';
37878             var parentWays = graph.parentWays(graph.entity(nodeId));
37879             var seenRelationIds = {};
37880             var sharedRelation;
37881             parentWays.forEach(function (way) {
37882               var relations = graph.parentRelations(way);
37883               relations.forEach(function (relation) {
37884                 if (relation.id in seenRelationIds) {
37885                   if (wayIds) {
37886                     if (wayIds.indexOf(way.id) !== -1 || wayIds.indexOf(seenRelationIds[relation.id]) !== -1) {
37887                       sharedRelation = relation;
37888                     }
37889                   } else {
37890                     sharedRelation = relation;
37891                   }
37892                 } else {
37893                   seenRelationIds[relation.id] = way.id;
37894                 }
37895               });
37896             });
37897             if (sharedRelation) return 'relation';
37898           };
37899
37900           action.limitWays = function (val) {
37901             if (!arguments.length) return wayIds;
37902             wayIds = val;
37903             return action;
37904           };
37905
37906           return action;
37907         }
37908
37909         function actionExtract(entityID, projection) {
37910           var extractedNodeID;
37911
37912           var action = function action(graph) {
37913             var entity = graph.entity(entityID);
37914
37915             if (entity.type === 'node') {
37916               return extractFromNode(entity, graph);
37917             }
37918
37919             return extractFromWayOrRelation(entity, graph);
37920           };
37921
37922           function extractFromNode(node, graph) {
37923             extractedNodeID = node.id; // Create a new node to replace the one we will detach
37924
37925             var replacement = osmNode({
37926               loc: node.loc
37927             });
37928             graph = graph.replace(replacement); // Process each way in turn, updating the graph as we go
37929
37930             graph = graph.parentWays(node).reduce(function (accGraph, parentWay) {
37931               return accGraph.replace(parentWay.replaceNode(entityID, replacement.id));
37932             }, graph); // Process any relations too
37933
37934             return graph.parentRelations(node).reduce(function (accGraph, parentRel) {
37935               return accGraph.replace(parentRel.replaceMember(node, replacement));
37936             }, graph);
37937           }
37938
37939           function extractFromWayOrRelation(entity, graph) {
37940             var fromGeometry = entity.geometry(graph);
37941             var keysToCopyAndRetain = ['source', 'wheelchair'];
37942             var keysToRetain = ['area'];
37943             var buildingKeysToRetain = ['architect', 'building', 'height', 'layer'];
37944             var extractedLoc = d3_geoPath(projection).centroid(entity.asGeoJSON(graph));
37945             extractedLoc = extractedLoc && projection.invert(extractedLoc);
37946
37947             if (!extractedLoc || !isFinite(extractedLoc[0]) || !isFinite(extractedLoc[1])) {
37948               extractedLoc = entity.extent(graph).center();
37949             }
37950
37951             var indoorAreaValues = {
37952               area: true,
37953               corridor: true,
37954               elevator: true,
37955               level: true,
37956               room: true
37957             };
37958             var isBuilding = entity.tags.building && entity.tags.building !== 'no' || entity.tags['building:part'] && entity.tags['building:part'] !== 'no';
37959             var isIndoorArea = fromGeometry === 'area' && entity.tags.indoor && indoorAreaValues[entity.tags.indoor];
37960             var entityTags = Object.assign({}, entity.tags); // shallow copy
37961
37962             var pointTags = {};
37963
37964             for (var key in entityTags) {
37965               if (entity.type === 'relation' && key === 'type') {
37966                 continue;
37967               }
37968
37969               if (keysToRetain.indexOf(key) !== -1) {
37970                 continue;
37971               }
37972
37973               if (isBuilding) {
37974                 // don't transfer building-related tags
37975                 if (buildingKeysToRetain.indexOf(key) !== -1 || key.match(/^building:.{1,}/) || key.match(/^roof:.{1,}/)) continue;
37976               } // leave `indoor` tag on the area
37977
37978
37979               if (isIndoorArea && key === 'indoor') {
37980                 continue;
37981               } // copy the tag from the entity to the point
37982
37983
37984               pointTags[key] = entityTags[key]; // leave addresses and some other tags so they're on both features
37985
37986               if (keysToCopyAndRetain.indexOf(key) !== -1 || key.match(/^addr:.{1,}/)) {
37987                 continue;
37988               } else if (isIndoorArea && key === 'level') {
37989                 // leave `level` on both features
37990                 continue;
37991               } // remove the tag from the entity
37992
37993
37994               delete entityTags[key];
37995             }
37996
37997             if (!isBuilding && !isIndoorArea && fromGeometry === 'area') {
37998               // ensure that areas keep area geometry
37999               entityTags.area = 'yes';
38000             }
38001
38002             var replacement = osmNode({
38003               loc: extractedLoc,
38004               tags: pointTags
38005             });
38006             graph = graph.replace(replacement);
38007             extractedNodeID = replacement.id;
38008             return graph.replace(entity.update({
38009               tags: entityTags
38010             }));
38011           }
38012
38013           action.getExtractedNodeID = function () {
38014             return extractedNodeID;
38015           };
38016
38017           return action;
38018         }
38019
38020         //
38021         // This is the inverse of `iD.actionSplit`.
38022         //
38023         // Reference:
38024         //   https://github.com/systemed/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/MergeWaysAction.as
38025         //   https://github.com/openstreetmap/josm/blob/mirror/src/org/openstreetmap/josm/actions/CombineWayAction.java
38026         //
38027
38028         function actionJoin(ids) {
38029           function groupEntitiesByGeometry(graph) {
38030             var entities = ids.map(function (id) {
38031               return graph.entity(id);
38032             });
38033             return Object.assign({
38034               line: []
38035             }, utilArrayGroupBy(entities, function (entity) {
38036               return entity.geometry(graph);
38037             }));
38038           }
38039
38040           var action = function action(graph) {
38041             var ways = ids.map(graph.entity, graph);
38042             var survivorID = ways[0].id; // if any of the ways are sided (e.g. coastline, cliff, kerb)
38043             // sort them first so they establish the overall order - #6033
38044
38045             ways.sort(function (a, b) {
38046               var aSided = a.isSided();
38047               var bSided = b.isSided();
38048               return aSided && !bSided ? -1 : bSided && !aSided ? 1 : 0;
38049             }); // Prefer to keep an existing way.
38050
38051             for (var i = 0; i < ways.length; i++) {
38052               if (!ways[i].isNew()) {
38053                 survivorID = ways[i].id;
38054                 break;
38055               }
38056             }
38057
38058             var sequences = osmJoinWays(ways, graph);
38059             var joined = sequences[0]; // We might need to reverse some of these ways before joining them.  #4688
38060             // `joined.actions` property will contain any actions we need to apply.
38061
38062             graph = sequences.actions.reduce(function (g, action) {
38063               return action(g);
38064             }, graph);
38065             var survivor = graph.entity(survivorID);
38066             survivor = survivor.update({
38067               nodes: joined.nodes.map(function (n) {
38068                 return n.id;
38069               })
38070             });
38071             graph = graph.replace(survivor);
38072             joined.forEach(function (way) {
38073               if (way.id === survivorID) return;
38074               graph.parentRelations(way).forEach(function (parent) {
38075                 graph = graph.replace(parent.replaceMember(way, survivor));
38076               });
38077               survivor = survivor.mergeTags(way.tags);
38078               graph = graph.replace(survivor);
38079               graph = actionDeleteWay(way.id)(graph);
38080             }); // Finds if the join created a single-member multipolygon,
38081             // and if so turns it into a basic area instead
38082
38083             function checkForSimpleMultipolygon() {
38084               if (!survivor.isClosed()) return;
38085               var multipolygons = graph.parentMultipolygons(survivor).filter(function (multipolygon) {
38086                 // find multipolygons where the survivor is the only member
38087                 return multipolygon.members.length === 1;
38088               }); // skip if this is the single member of multiple multipolygons
38089
38090               if (multipolygons.length !== 1) return;
38091               var multipolygon = multipolygons[0];
38092
38093               for (var key in survivor.tags) {
38094                 if (multipolygon.tags[key] && // don't collapse if tags cannot be cleanly merged
38095                 multipolygon.tags[key] !== survivor.tags[key]) return;
38096               }
38097
38098               survivor = survivor.mergeTags(multipolygon.tags);
38099               graph = graph.replace(survivor);
38100               graph = actionDeleteRelation(multipolygon.id, true
38101               /* allow untagged members */
38102               )(graph);
38103               var tags = Object.assign({}, survivor.tags);
38104
38105               if (survivor.geometry(graph) !== 'area') {
38106                 // ensure the feature persists as an area
38107                 tags.area = 'yes';
38108               }
38109
38110               delete tags.type; // remove type=multipolygon
38111
38112               survivor = survivor.update({
38113                 tags: tags
38114               });
38115               graph = graph.replace(survivor);
38116             }
38117
38118             checkForSimpleMultipolygon();
38119             return graph;
38120           }; // Returns the number of nodes the resultant way is expected to have
38121
38122
38123           action.resultingWayNodesLength = function (graph) {
38124             return ids.reduce(function (count, id) {
38125               return count + graph.entity(id).nodes.length;
38126             }, 0) - ids.length - 1;
38127           };
38128
38129           action.disabled = function (graph) {
38130             var geometries = groupEntitiesByGeometry(graph);
38131
38132             if (ids.length < 2 || ids.length !== geometries.line.length) {
38133               return 'not_eligible';
38134             }
38135
38136             var joined = osmJoinWays(ids.map(graph.entity, graph), graph);
38137
38138             if (joined.length > 1) {
38139               return 'not_adjacent';
38140             } // Loop through all combinations of path-pairs
38141             // to check potential intersections between all pairs
38142
38143
38144             for (var i = 0; i < ids.length - 1; i++) {
38145               for (var j = i + 1; j < ids.length; j++) {
38146                 var path1 = graph.childNodes(graph.entity(ids[i])).map(function (e) {
38147                   return e.loc;
38148                 });
38149                 var path2 = graph.childNodes(graph.entity(ids[j])).map(function (e) {
38150                   return e.loc;
38151                 });
38152                 var intersections = geoPathIntersections(path1, path2); // Check if intersections are just nodes lying on top of
38153                 // each other/the line, as opposed to crossing it
38154
38155                 var common = utilArrayIntersection(joined[0].nodes.map(function (n) {
38156                   return n.loc.toString();
38157                 }), intersections.map(function (n) {
38158                   return n.toString();
38159                 }));
38160
38161                 if (common.length !== intersections.length) {
38162                   return 'paths_intersect';
38163                 }
38164               }
38165             }
38166
38167             var nodeIds = joined[0].nodes.map(function (n) {
38168               return n.id;
38169             }).slice(1, -1);
38170             var relation;
38171             var tags = {};
38172             var conflicting = false;
38173             joined[0].forEach(function (way) {
38174               var parents = graph.parentRelations(way);
38175               parents.forEach(function (parent) {
38176                 if (parent.isRestriction() && parent.members.some(function (m) {
38177                   return nodeIds.indexOf(m.id) >= 0;
38178                 })) {
38179                   relation = parent;
38180                 }
38181               });
38182
38183               for (var k in way.tags) {
38184                 if (!(k in tags)) {
38185                   tags[k] = way.tags[k];
38186                 } else if (tags[k] && osmIsInterestingTag(k) && tags[k] !== way.tags[k]) {
38187                   conflicting = true;
38188                 }
38189               }
38190             });
38191
38192             if (relation) {
38193               return 'restriction';
38194             }
38195
38196             if (conflicting) {
38197               return 'conflicting_tags';
38198             }
38199           };
38200
38201           return action;
38202         }
38203
38204         function actionMerge(ids) {
38205           function groupEntitiesByGeometry(graph) {
38206             var entities = ids.map(function (id) {
38207               return graph.entity(id);
38208             });
38209             return Object.assign({
38210               point: [],
38211               area: [],
38212               line: [],
38213               relation: []
38214             }, utilArrayGroupBy(entities, function (entity) {
38215               return entity.geometry(graph);
38216             }));
38217           }
38218
38219           var action = function action(graph) {
38220             var geometries = groupEntitiesByGeometry(graph);
38221             var target = geometries.area[0] || geometries.line[0];
38222             var points = geometries.point;
38223             points.forEach(function (point) {
38224               target = target.mergeTags(point.tags);
38225               graph = graph.replace(target);
38226               graph.parentRelations(point).forEach(function (parent) {
38227                 graph = graph.replace(parent.replaceMember(point, target));
38228               });
38229               var nodes = utilArrayUniq(graph.childNodes(target));
38230               var removeNode = point;
38231
38232               for (var i = 0; i < nodes.length; i++) {
38233                 var node = nodes[i];
38234
38235                 if (graph.parentWays(node).length > 1 || graph.parentRelations(node).length || node.hasInterestingTags()) {
38236                   continue;
38237                 } // Found an uninteresting child node on the target way.
38238                 // Move orig point into its place to preserve point's history. #3683
38239
38240
38241                 graph = graph.replace(point.update({
38242                   tags: {},
38243                   loc: node.loc
38244                 }));
38245                 target = target.replaceNode(node.id, point.id);
38246                 graph = graph.replace(target);
38247                 removeNode = node;
38248                 break;
38249               }
38250
38251               graph = graph.remove(removeNode);
38252             });
38253
38254             if (target.tags.area === 'yes') {
38255               var tags = Object.assign({}, target.tags); // shallow copy
38256
38257               delete tags.area;
38258
38259               if (osmTagSuggestingArea(tags)) {
38260                 // remove the `area` tag if area geometry is now implied - #3851
38261                 target = target.update({
38262                   tags: tags
38263                 });
38264                 graph = graph.replace(target);
38265               }
38266             }
38267
38268             return graph;
38269           };
38270
38271           action.disabled = function (graph) {
38272             var geometries = groupEntitiesByGeometry(graph);
38273
38274             if (geometries.point.length === 0 || geometries.area.length + geometries.line.length !== 1 || geometries.relation.length !== 0) {
38275               return 'not_eligible';
38276             }
38277           };
38278
38279           return action;
38280         }
38281
38282         //
38283         // 1. move all the nodes to a common location
38284         // 2. `actionConnect` them
38285
38286         function actionMergeNodes(nodeIDs, loc) {
38287           // If there is a single "interesting" node, use that as the location.
38288           // Otherwise return the average location of all the nodes.
38289           function chooseLoc(graph) {
38290             if (!nodeIDs.length) return null;
38291             var sum = [0, 0];
38292             var interestingCount = 0;
38293             var interestingLoc;
38294
38295             for (var i = 0; i < nodeIDs.length; i++) {
38296               var node = graph.entity(nodeIDs[i]);
38297
38298               if (node.hasInterestingTags()) {
38299                 interestingLoc = ++interestingCount === 1 ? node.loc : null;
38300               }
38301
38302               sum = geoVecAdd(sum, node.loc);
38303             }
38304
38305             return interestingLoc || geoVecScale(sum, 1 / nodeIDs.length);
38306           }
38307
38308           var action = function action(graph) {
38309             if (nodeIDs.length < 2) return graph;
38310             var toLoc = loc;
38311
38312             if (!toLoc) {
38313               toLoc = chooseLoc(graph);
38314             }
38315
38316             for (var i = 0; i < nodeIDs.length; i++) {
38317               var node = graph.entity(nodeIDs[i]);
38318
38319               if (node.loc !== toLoc) {
38320                 graph = graph.replace(node.move(toLoc));
38321               }
38322             }
38323
38324             return actionConnect(nodeIDs)(graph);
38325           };
38326
38327           action.disabled = function (graph) {
38328             if (nodeIDs.length < 2) return 'not_eligible';
38329
38330             for (var i = 0; i < nodeIDs.length; i++) {
38331               var entity = graph.entity(nodeIDs[i]);
38332               if (entity.type !== 'node') return 'not_eligible';
38333             }
38334
38335             return actionConnect(nodeIDs).disabled(graph);
38336           };
38337
38338           return action;
38339         }
38340
38341         function osmChangeset() {
38342           if (!(this instanceof osmChangeset)) {
38343             return new osmChangeset().initialize(arguments);
38344           } else if (arguments.length) {
38345             this.initialize(arguments);
38346           }
38347         }
38348         osmEntity.changeset = osmChangeset;
38349         osmChangeset.prototype = Object.create(osmEntity.prototype);
38350         Object.assign(osmChangeset.prototype, {
38351           type: 'changeset',
38352           extent: function extent() {
38353             return new geoExtent();
38354           },
38355           geometry: function geometry() {
38356             return 'changeset';
38357           },
38358           asJXON: function asJXON() {
38359             return {
38360               osm: {
38361                 changeset: {
38362                   tag: Object.keys(this.tags).map(function (k) {
38363                     return {
38364                       '@k': k,
38365                       '@v': this.tags[k]
38366                     };
38367                   }, this),
38368                   '@version': 0.6,
38369                   '@generator': 'iD'
38370                 }
38371               }
38372             };
38373           },
38374           // Generate [osmChange](http://wiki.openstreetmap.org/wiki/OsmChange)
38375           // XML. Returns a string.
38376           osmChangeJXON: function osmChangeJXON(changes) {
38377             var changeset_id = this.id;
38378
38379             function nest(x, order) {
38380               var groups = {};
38381
38382               for (var i = 0; i < x.length; i++) {
38383                 var tagName = Object.keys(x[i])[0];
38384                 if (!groups[tagName]) groups[tagName] = [];
38385                 groups[tagName].push(x[i][tagName]);
38386               }
38387
38388               var ordered = {};
38389               order.forEach(function (o) {
38390                 if (groups[o]) ordered[o] = groups[o];
38391               });
38392               return ordered;
38393             } // sort relations in a changeset by dependencies
38394
38395
38396             function sort(changes) {
38397               // find a referenced relation in the current changeset
38398               function resolve(item) {
38399                 return relations.find(function (relation) {
38400                   return item.keyAttributes.type === 'relation' && item.keyAttributes.ref === relation['@id'];
38401                 });
38402               } // a new item is an item that has not been already processed
38403
38404
38405               function isNew(item) {
38406                 return !sorted[item['@id']] && !processing.find(function (proc) {
38407                   return proc['@id'] === item['@id'];
38408                 });
38409               }
38410
38411               var processing = [];
38412               var sorted = {};
38413               var relations = changes.relation;
38414               if (!relations) return changes;
38415
38416               for (var i = 0; i < relations.length; i++) {
38417                 var relation = relations[i]; // skip relation if already sorted
38418
38419                 if (!sorted[relation['@id']]) {
38420                   processing.push(relation);
38421                 }
38422
38423                 while (processing.length > 0) {
38424                   var next = processing[0],
38425                       deps = next.member.map(resolve).filter(Boolean).filter(isNew);
38426
38427                   if (deps.length === 0) {
38428                     sorted[next['@id']] = next;
38429                     processing.shift();
38430                   } else {
38431                     processing = deps.concat(processing);
38432                   }
38433                 }
38434               }
38435
38436               changes.relation = Object.values(sorted);
38437               return changes;
38438             }
38439
38440             function rep(entity) {
38441               return entity.asJXON(changeset_id);
38442             }
38443
38444             return {
38445               osmChange: {
38446                 '@version': 0.6,
38447                 '@generator': 'iD',
38448                 'create': sort(nest(changes.created.map(rep), ['node', 'way', 'relation'])),
38449                 'modify': nest(changes.modified.map(rep), ['node', 'way', 'relation']),
38450                 'delete': Object.assign(nest(changes.deleted.map(rep), ['relation', 'way', 'node']), {
38451                   '@if-unused': true
38452                 })
38453               }
38454             };
38455           },
38456           asGeoJSON: function asGeoJSON() {
38457             return {};
38458           }
38459         });
38460
38461         function osmNote() {
38462           if (!(this instanceof osmNote)) {
38463             return new osmNote().initialize(arguments);
38464           } else if (arguments.length) {
38465             this.initialize(arguments);
38466           }
38467         }
38468
38469         osmNote.id = function () {
38470           return osmNote.id.next--;
38471         };
38472
38473         osmNote.id.next = -1;
38474         Object.assign(osmNote.prototype, {
38475           type: 'note',
38476           initialize: function initialize(sources) {
38477             for (var i = 0; i < sources.length; ++i) {
38478               var source = sources[i];
38479
38480               for (var prop in source) {
38481                 if (Object.prototype.hasOwnProperty.call(source, prop)) {
38482                   if (source[prop] === undefined) {
38483                     delete this[prop];
38484                   } else {
38485                     this[prop] = source[prop];
38486                   }
38487                 }
38488               }
38489             }
38490
38491             if (!this.id) {
38492               this.id = osmNote.id().toString();
38493             }
38494
38495             return this;
38496           },
38497           extent: function extent() {
38498             return new geoExtent(this.loc);
38499           },
38500           update: function update(attrs) {
38501             return osmNote(this, attrs); // {v: 1 + (this.v || 0)}
38502           },
38503           isNew: function isNew() {
38504             return this.id < 0;
38505           },
38506           move: function move(loc) {
38507             return this.update({
38508               loc: loc
38509             });
38510           }
38511         });
38512
38513         function osmRelation() {
38514           if (!(this instanceof osmRelation)) {
38515             return new osmRelation().initialize(arguments);
38516           } else if (arguments.length) {
38517             this.initialize(arguments);
38518           }
38519         }
38520         osmEntity.relation = osmRelation;
38521         osmRelation.prototype = Object.create(osmEntity.prototype);
38522
38523         osmRelation.creationOrder = function (a, b) {
38524           var aId = parseInt(osmEntity.id.toOSM(a.id), 10);
38525           var bId = parseInt(osmEntity.id.toOSM(b.id), 10);
38526           if (aId < 0 || bId < 0) return aId - bId;
38527           return bId - aId;
38528         };
38529
38530         Object.assign(osmRelation.prototype, {
38531           type: 'relation',
38532           members: [],
38533           copy: function copy(resolver, copies) {
38534             if (copies[this.id]) return copies[this.id];
38535             var copy = osmEntity.prototype.copy.call(this, resolver, copies);
38536             var members = this.members.map(function (member) {
38537               return Object.assign({}, member, {
38538                 id: resolver.entity(member.id).copy(resolver, copies).id
38539               });
38540             });
38541             copy = copy.update({
38542               members: members
38543             });
38544             copies[this.id] = copy;
38545             return copy;
38546           },
38547           extent: function extent(resolver, memo) {
38548             return resolver["transient"](this, 'extent', function () {
38549               if (memo && memo[this.id]) return geoExtent();
38550               memo = memo || {};
38551               memo[this.id] = true;
38552               var extent = geoExtent();
38553
38554               for (var i = 0; i < this.members.length; i++) {
38555                 var member = resolver.hasEntity(this.members[i].id);
38556
38557                 if (member) {
38558                   extent._extend(member.extent(resolver, memo));
38559                 }
38560               }
38561
38562               return extent;
38563             });
38564           },
38565           geometry: function geometry(graph) {
38566             return graph["transient"](this, 'geometry', function () {
38567               return this.isMultipolygon() ? 'area' : 'relation';
38568             });
38569           },
38570           isDegenerate: function isDegenerate() {
38571             return this.members.length === 0;
38572           },
38573           // Return an array of members, each extended with an 'index' property whose value
38574           // is the member index.
38575           indexedMembers: function indexedMembers() {
38576             var result = new Array(this.members.length);
38577
38578             for (var i = 0; i < this.members.length; i++) {
38579               result[i] = Object.assign({}, this.members[i], {
38580                 index: i
38581               });
38582             }
38583
38584             return result;
38585           },
38586           // Return the first member with the given role. A copy of the member object
38587           // is returned, extended with an 'index' property whose value is the member index.
38588           memberByRole: function memberByRole(role) {
38589             for (var i = 0; i < this.members.length; i++) {
38590               if (this.members[i].role === role) {
38591                 return Object.assign({}, this.members[i], {
38592                   index: i
38593                 });
38594               }
38595             }
38596           },
38597           // Same as memberByRole, but returns all members with the given role
38598           membersByRole: function membersByRole(role) {
38599             var result = [];
38600
38601             for (var i = 0; i < this.members.length; i++) {
38602               if (this.members[i].role === role) {
38603                 result.push(Object.assign({}, this.members[i], {
38604                   index: i
38605                 }));
38606               }
38607             }
38608
38609             return result;
38610           },
38611           // Return the first member with the given id. A copy of the member object
38612           // is returned, extended with an 'index' property whose value is the member index.
38613           memberById: function memberById(id) {
38614             for (var i = 0; i < this.members.length; i++) {
38615               if (this.members[i].id === id) {
38616                 return Object.assign({}, this.members[i], {
38617                   index: i
38618                 });
38619               }
38620             }
38621           },
38622           // Return the first member with the given id and role. A copy of the member object
38623           // is returned, extended with an 'index' property whose value is the member index.
38624           memberByIdAndRole: function memberByIdAndRole(id, role) {
38625             for (var i = 0; i < this.members.length; i++) {
38626               if (this.members[i].id === id && this.members[i].role === role) {
38627                 return Object.assign({}, this.members[i], {
38628                   index: i
38629                 });
38630               }
38631             }
38632           },
38633           addMember: function addMember(member, index) {
38634             var members = this.members.slice();
38635             members.splice(index === undefined ? members.length : index, 0, member);
38636             return this.update({
38637               members: members
38638             });
38639           },
38640           updateMember: function updateMember(member, index) {
38641             var members = this.members.slice();
38642             members.splice(index, 1, Object.assign({}, members[index], member));
38643             return this.update({
38644               members: members
38645             });
38646           },
38647           removeMember: function removeMember(index) {
38648             var members = this.members.slice();
38649             members.splice(index, 1);
38650             return this.update({
38651               members: members
38652             });
38653           },
38654           removeMembersWithID: function removeMembersWithID(id) {
38655             var members = this.members.filter(function (m) {
38656               return m.id !== id;
38657             });
38658             return this.update({
38659               members: members
38660             });
38661           },
38662           moveMember: function moveMember(fromIndex, toIndex) {
38663             var members = this.members.slice();
38664             members.splice(toIndex, 0, members.splice(fromIndex, 1)[0]);
38665             return this.update({
38666               members: members
38667             });
38668           },
38669           // Wherever a member appears with id `needle.id`, replace it with a member
38670           // with id `replacement.id`, type `replacement.type`, and the original role,
38671           // By default, adding a duplicate member (by id and role) is prevented.
38672           // Return an updated relation.
38673           replaceMember: function replaceMember(needle, replacement, keepDuplicates) {
38674             if (!this.memberById(needle.id)) return this;
38675             var members = [];
38676
38677             for (var i = 0; i < this.members.length; i++) {
38678               var member = this.members[i];
38679
38680               if (member.id !== needle.id) {
38681                 members.push(member);
38682               } else if (keepDuplicates || !this.memberByIdAndRole(replacement.id, member.role)) {
38683                 members.push({
38684                   id: replacement.id,
38685                   type: replacement.type,
38686                   role: member.role
38687                 });
38688               }
38689             }
38690
38691             return this.update({
38692               members: members
38693             });
38694           },
38695           asJXON: function asJXON(changeset_id) {
38696             var r = {
38697               relation: {
38698                 '@id': this.osmId(),
38699                 '@version': this.version || 0,
38700                 member: this.members.map(function (member) {
38701                   return {
38702                     keyAttributes: {
38703                       type: member.type,
38704                       role: member.role,
38705                       ref: osmEntity.id.toOSM(member.id)
38706                     }
38707                   };
38708                 }, this),
38709                 tag: Object.keys(this.tags).map(function (k) {
38710                   return {
38711                     keyAttributes: {
38712                       k: k,
38713                       v: this.tags[k]
38714                     }
38715                   };
38716                 }, this)
38717               }
38718             };
38719
38720             if (changeset_id) {
38721               r.relation['@changeset'] = changeset_id;
38722             }
38723
38724             return r;
38725           },
38726           asGeoJSON: function asGeoJSON(resolver) {
38727             return resolver["transient"](this, 'GeoJSON', function () {
38728               if (this.isMultipolygon()) {
38729                 return {
38730                   type: 'MultiPolygon',
38731                   coordinates: this.multipolygon(resolver)
38732                 };
38733               } else {
38734                 return {
38735                   type: 'FeatureCollection',
38736                   properties: this.tags,
38737                   features: this.members.map(function (member) {
38738                     return Object.assign({
38739                       role: member.role
38740                     }, resolver.entity(member.id).asGeoJSON(resolver));
38741                   })
38742                 };
38743               }
38744             });
38745           },
38746           area: function area(resolver) {
38747             return resolver["transient"](this, 'area', function () {
38748               return d3_geoArea(this.asGeoJSON(resolver));
38749             });
38750           },
38751           isMultipolygon: function isMultipolygon() {
38752             return this.tags.type === 'multipolygon';
38753           },
38754           isComplete: function isComplete(resolver) {
38755             for (var i = 0; i < this.members.length; i++) {
38756               if (!resolver.hasEntity(this.members[i].id)) {
38757                 return false;
38758               }
38759             }
38760
38761             return true;
38762           },
38763           hasFromViaTo: function hasFromViaTo() {
38764             return this.members.some(function (m) {
38765               return m.role === 'from';
38766             }) && this.members.some(function (m) {
38767               return m.role === 'via';
38768             }) && this.members.some(function (m) {
38769               return m.role === 'to';
38770             });
38771           },
38772           isRestriction: function isRestriction() {
38773             return !!(this.tags.type && this.tags.type.match(/^restriction:?/));
38774           },
38775           isValidRestriction: function isValidRestriction() {
38776             if (!this.isRestriction()) return false;
38777             var froms = this.members.filter(function (m) {
38778               return m.role === 'from';
38779             });
38780             var vias = this.members.filter(function (m) {
38781               return m.role === 'via';
38782             });
38783             var tos = this.members.filter(function (m) {
38784               return m.role === 'to';
38785             });
38786             if (froms.length !== 1 && this.tags.restriction !== 'no_entry') return false;
38787             if (froms.some(function (m) {
38788               return m.type !== 'way';
38789             })) return false;
38790             if (tos.length !== 1 && this.tags.restriction !== 'no_exit') return false;
38791             if (tos.some(function (m) {
38792               return m.type !== 'way';
38793             })) return false;
38794             if (vias.length === 0) return false;
38795             if (vias.length > 1 && vias.some(function (m) {
38796               return m.type !== 'way';
38797             })) return false;
38798             return true;
38799           },
38800           // Returns an array [A0, ... An], each Ai being an array of node arrays [Nds0, ... Ndsm],
38801           // where Nds0 is an outer ring and subsequent Ndsi's (if any i > 0) being inner rings.
38802           //
38803           // This corresponds to the structure needed for rendering a multipolygon path using a
38804           // `evenodd` fill rule, as well as the structure of a GeoJSON MultiPolygon geometry.
38805           //
38806           // In the case of invalid geometries, this function will still return a result which
38807           // includes the nodes of all way members, but some Nds may be unclosed and some inner
38808           // rings not matched with the intended outer ring.
38809           //
38810           multipolygon: function multipolygon(resolver) {
38811             var outers = this.members.filter(function (m) {
38812               return 'outer' === (m.role || 'outer');
38813             });
38814             var inners = this.members.filter(function (m) {
38815               return 'inner' === m.role;
38816             });
38817             outers = osmJoinWays(outers, resolver);
38818             inners = osmJoinWays(inners, resolver);
38819
38820             var sequenceToLineString = function sequenceToLineString(sequence) {
38821               if (sequence.nodes.length > 2 && sequence.nodes[0] !== sequence.nodes[sequence.nodes.length - 1]) {
38822                 // close unclosed parts to ensure correct area rendering - #2945
38823                 sequence.nodes.push(sequence.nodes[0]);
38824               }
38825
38826               return sequence.nodes.map(function (node) {
38827                 return node.loc;
38828               });
38829             };
38830
38831             outers = outers.map(sequenceToLineString);
38832             inners = inners.map(sequenceToLineString);
38833             var result = outers.map(function (o) {
38834               // Heuristic for detecting counterclockwise winding order. Assumes
38835               // that OpenStreetMap polygons are not hemisphere-spanning.
38836               return [d3_geoArea({
38837                 type: 'Polygon',
38838                 coordinates: [o]
38839               }) > 2 * Math.PI ? o.reverse() : o];
38840             });
38841
38842             function findOuter(inner) {
38843               var o, outer;
38844
38845               for (o = 0; o < outers.length; o++) {
38846                 outer = outers[o];
38847
38848                 if (geoPolygonContainsPolygon(outer, inner)) {
38849                   return o;
38850                 }
38851               }
38852
38853               for (o = 0; o < outers.length; o++) {
38854                 outer = outers[o];
38855
38856                 if (geoPolygonIntersectsPolygon(outer, inner, false)) {
38857                   return o;
38858                 }
38859               }
38860             }
38861
38862             for (var i = 0; i < inners.length; i++) {
38863               var inner = inners[i];
38864
38865               if (d3_geoArea({
38866                 type: 'Polygon',
38867                 coordinates: [inner]
38868               }) < 2 * Math.PI) {
38869                 inner = inner.reverse();
38870               }
38871
38872               var o = findOuter(inners[i]);
38873
38874               if (o !== undefined) {
38875                 result[o].push(inners[i]);
38876               } else {
38877                 result.push([inners[i]]); // Invalid geometry
38878               }
38879             }
38880
38881             return result;
38882           }
38883         });
38884
38885         var QAItem = /*#__PURE__*/function () {
38886           function QAItem(loc, service, itemType, id, props) {
38887             _classCallCheck$1(this, QAItem);
38888
38889             // Store required properties
38890             this.loc = loc;
38891             this.service = service.title;
38892             this.itemType = itemType; // All issues must have an ID for selection, use generic if none specified
38893
38894             this.id = id ? id : "".concat(QAItem.id());
38895             this.update(props); // Some QA services have marker icons to differentiate issues
38896
38897             if (service && typeof service.getIcon === 'function') {
38898               this.icon = service.getIcon(itemType);
38899             }
38900           }
38901
38902           _createClass$1(QAItem, [{
38903             key: "update",
38904             value: function update(props) {
38905               var _this = this;
38906
38907               // You can't override this initial information
38908               var loc = this.loc,
38909                   service = this.service,
38910                   itemType = this.itemType,
38911                   id = this.id;
38912               Object.keys(props).forEach(function (prop) {
38913                 return _this[prop] = props[prop];
38914               });
38915               this.loc = loc;
38916               this.service = service;
38917               this.itemType = itemType;
38918               this.id = id;
38919               return this;
38920             } // Generic handling for newly created QAItems
38921
38922           }], [{
38923             key: "id",
38924             value: function id() {
38925               return this.nextId--;
38926             }
38927           }]);
38928
38929           return QAItem;
38930         }();
38931         QAItem.nextId = -1;
38932
38933         //
38934         // Optionally, split only the given ways, if multiple ways share
38935         // the given node.
38936         //
38937         // This is the inverse of `iD.actionJoin`.
38938         //
38939         // For testing convenience, accepts an ID to assign to the new way.
38940         // Normally, this will be undefined and the way will automatically
38941         // be assigned a new ID.
38942         //
38943         // Reference:
38944         //   https://github.com/systemed/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/SplitWayAction.as
38945         //
38946
38947         function actionSplit(nodeIds, newWayIds) {
38948           // accept single ID for backwards-compatiblity
38949           if (typeof nodeIds === 'string') nodeIds = [nodeIds];
38950
38951           var _wayIDs; // the strategy for picking which way will have a new version and which way is newly created
38952
38953
38954           var _keepHistoryOn = 'longest'; // 'longest', 'first'
38955           // The IDs of the ways actually created by running this action
38956
38957           var _createdWayIDs = [];
38958
38959           function dist(graph, nA, nB) {
38960             var locA = graph.entity(nA).loc;
38961             var locB = graph.entity(nB).loc;
38962             var epsilon = 1e-6;
38963             return locA && locB ? geoSphericalDistance(locA, locB) : epsilon;
38964           } // If the way is closed, we need to search for a partner node
38965           // to split the way at.
38966           //
38967           // The following looks for a node that is both far away from
38968           // the initial node in terms of way segment length and nearby
38969           // in terms of beeline-distance. This assures that areas get
38970           // split on the most "natural" points (independent of the number
38971           // of nodes).
38972           // For example: bone-shaped areas get split across their waist
38973           // line, circles across the diameter.
38974
38975
38976           function splitArea(nodes, idxA, graph) {
38977             var lengths = new Array(nodes.length);
38978             var length;
38979             var i;
38980             var best = 0;
38981             var idxB;
38982
38983             function wrap(index) {
38984               return utilWrap(index, nodes.length);
38985             } // calculate lengths
38986
38987
38988             length = 0;
38989
38990             for (i = wrap(idxA + 1); i !== idxA; i = wrap(i + 1)) {
38991               length += dist(graph, nodes[i], nodes[wrap(i - 1)]);
38992               lengths[i] = length;
38993             }
38994
38995             length = 0;
38996
38997             for (i = wrap(idxA - 1); i !== idxA; i = wrap(i - 1)) {
38998               length += dist(graph, nodes[i], nodes[wrap(i + 1)]);
38999
39000               if (length < lengths[i]) {
39001                 lengths[i] = length;
39002               }
39003             } // determine best opposite node to split
39004
39005
39006             for (i = 0; i < nodes.length; i++) {
39007               var cost = lengths[i] / dist(graph, nodes[idxA], nodes[i]);
39008
39009               if (cost > best) {
39010                 idxB = i;
39011                 best = cost;
39012               }
39013             }
39014
39015             return idxB;
39016           }
39017
39018           function totalLengthBetweenNodes(graph, nodes) {
39019             var totalLength = 0;
39020
39021             for (var i = 0; i < nodes.length - 1; i++) {
39022               totalLength += dist(graph, nodes[i], nodes[i + 1]);
39023             }
39024
39025             return totalLength;
39026           }
39027
39028           function split(graph, nodeId, wayA, newWayId) {
39029             var wayB = osmWay({
39030               id: newWayId,
39031               tags: wayA.tags
39032             }); // `wayB` is the NEW way
39033
39034             var origNodes = wayA.nodes.slice();
39035             var nodesA;
39036             var nodesB;
39037             var isArea = wayA.isArea();
39038             var isOuter = osmIsOldMultipolygonOuterMember(wayA, graph);
39039
39040             if (wayA.isClosed()) {
39041               var nodes = wayA.nodes.slice(0, -1);
39042               var idxA = nodes.indexOf(nodeId);
39043               var idxB = splitArea(nodes, idxA, graph);
39044
39045               if (idxB < idxA) {
39046                 nodesA = nodes.slice(idxA).concat(nodes.slice(0, idxB + 1));
39047                 nodesB = nodes.slice(idxB, idxA + 1);
39048               } else {
39049                 nodesA = nodes.slice(idxA, idxB + 1);
39050                 nodesB = nodes.slice(idxB).concat(nodes.slice(0, idxA + 1));
39051               }
39052             } else {
39053               var idx = wayA.nodes.indexOf(nodeId, 1);
39054               nodesA = wayA.nodes.slice(0, idx + 1);
39055               nodesB = wayA.nodes.slice(idx);
39056             }
39057
39058             var lengthA = totalLengthBetweenNodes(graph, nodesA);
39059             var lengthB = totalLengthBetweenNodes(graph, nodesB);
39060
39061             if (_keepHistoryOn === 'longest' && lengthB > lengthA) {
39062               // keep the history on the longer way, regardless of the node count
39063               wayA = wayA.update({
39064                 nodes: nodesB
39065               });
39066               wayB = wayB.update({
39067                 nodes: nodesA
39068               });
39069               var temp = lengthA;
39070               lengthA = lengthB;
39071               lengthB = temp;
39072             } else {
39073               wayA = wayA.update({
39074                 nodes: nodesA
39075               });
39076               wayB = wayB.update({
39077                 nodes: nodesB
39078               });
39079             }
39080
39081             if (wayA.tags.step_count) {
39082               // divide up the the step count proportionally between the two ways
39083               var stepCount = parseFloat(wayA.tags.step_count);
39084
39085               if (stepCount && // ensure a number
39086               isFinite(stepCount) && // ensure positive
39087               stepCount > 0 && // ensure integer
39088               Math.round(stepCount) === stepCount) {
39089                 var tagsA = Object.assign({}, wayA.tags);
39090                 var tagsB = Object.assign({}, wayB.tags);
39091                 var ratioA = lengthA / (lengthA + lengthB);
39092                 var countA = Math.round(stepCount * ratioA);
39093                 tagsA.step_count = countA.toString();
39094                 tagsB.step_count = (stepCount - countA).toString();
39095                 wayA = wayA.update({
39096                   tags: tagsA
39097                 });
39098                 wayB = wayB.update({
39099                   tags: tagsB
39100                 });
39101               }
39102             }
39103
39104             graph = graph.replace(wayA);
39105             graph = graph.replace(wayB);
39106             graph.parentRelations(wayA).forEach(function (relation) {
39107               var member; // Turn restrictions - make sure:
39108               // 1. Splitting a FROM/TO way - only `wayA` OR `wayB` remains in relation
39109               //    (whichever one is connected to the VIA node/ways)
39110               // 2. Splitting a VIA way - `wayB` remains in relation as a VIA way
39111
39112               if (relation.hasFromViaTo()) {
39113                 var f = relation.memberByRole('from');
39114                 var v = relation.membersByRole('via');
39115                 var t = relation.memberByRole('to');
39116                 var i; // 1. split a FROM/TO
39117
39118                 if (f.id === wayA.id || t.id === wayA.id) {
39119                   var keepB = false;
39120
39121                   if (v.length === 1 && v[0].type === 'node') {
39122                     // check via node
39123                     keepB = wayB.contains(v[0].id);
39124                   } else {
39125                     // check via way(s)
39126                     for (i = 0; i < v.length; i++) {
39127                       if (v[i].type === 'way') {
39128                         var wayVia = graph.hasEntity(v[i].id);
39129
39130                         if (wayVia && utilArrayIntersection(wayB.nodes, wayVia.nodes).length) {
39131                           keepB = true;
39132                           break;
39133                         }
39134                       }
39135                     }
39136                   }
39137
39138                   if (keepB) {
39139                     relation = relation.replaceMember(wayA, wayB);
39140                     graph = graph.replace(relation);
39141                   } // 2. split a VIA
39142
39143                 } else {
39144                   for (i = 0; i < v.length; i++) {
39145                     if (v[i].type === 'way' && v[i].id === wayA.id) {
39146                       member = {
39147                         id: wayB.id,
39148                         type: 'way',
39149                         role: 'via'
39150                       };
39151                       graph = actionAddMember(relation.id, member, v[i].index + 1)(graph);
39152                       break;
39153                     }
39154                   }
39155                 } // All other relations (Routes, Multipolygons, etc):
39156                 // 1. Both `wayA` and `wayB` remain in the relation
39157                 // 2. But must be inserted as a pair (see `actionAddMember` for details)
39158
39159               } else {
39160                 if (relation === isOuter) {
39161                   graph = graph.replace(relation.mergeTags(wayA.tags));
39162                   graph = graph.replace(wayA.update({
39163                     tags: {}
39164                   }));
39165                   graph = graph.replace(wayB.update({
39166                     tags: {}
39167                   }));
39168                 }
39169
39170                 member = {
39171                   id: wayB.id,
39172                   type: 'way',
39173                   role: relation.memberById(wayA.id).role
39174                 };
39175                 var insertPair = {
39176                   originalID: wayA.id,
39177                   insertedID: wayB.id,
39178                   nodes: origNodes
39179                 };
39180                 graph = actionAddMember(relation.id, member, undefined, insertPair)(graph);
39181               }
39182             });
39183
39184             if (!isOuter && isArea) {
39185               var multipolygon = osmRelation({
39186                 tags: Object.assign({}, wayA.tags, {
39187                   type: 'multipolygon'
39188                 }),
39189                 members: [{
39190                   id: wayA.id,
39191                   role: 'outer',
39192                   type: 'way'
39193                 }, {
39194                   id: wayB.id,
39195                   role: 'outer',
39196                   type: 'way'
39197                 }]
39198               });
39199               graph = graph.replace(multipolygon);
39200               graph = graph.replace(wayA.update({
39201                 tags: {}
39202               }));
39203               graph = graph.replace(wayB.update({
39204                 tags: {}
39205               }));
39206             }
39207
39208             _createdWayIDs.push(wayB.id);
39209
39210             return graph;
39211           }
39212
39213           var action = function action(graph) {
39214             _createdWayIDs = [];
39215             var newWayIndex = 0;
39216
39217             for (var i = 0; i < nodeIds.length; i++) {
39218               var nodeId = nodeIds[i];
39219               var candidates = action.waysForNode(nodeId, graph);
39220
39221               for (var j = 0; j < candidates.length; j++) {
39222                 graph = split(graph, nodeId, candidates[j], newWayIds && newWayIds[newWayIndex]);
39223                 newWayIndex += 1;
39224               }
39225             }
39226
39227             return graph;
39228           };
39229
39230           action.getCreatedWayIDs = function () {
39231             return _createdWayIDs;
39232           };
39233
39234           action.waysForNode = function (nodeId, graph) {
39235             var node = graph.entity(nodeId);
39236             var splittableParents = graph.parentWays(node).filter(isSplittable);
39237
39238             if (!_wayIDs) {
39239               // If the ways to split aren't specified, only split the lines.
39240               // If there are no lines to split, split the areas.
39241               var hasLine = splittableParents.some(function (parent) {
39242                 return parent.geometry(graph) === 'line';
39243               });
39244
39245               if (hasLine) {
39246                 return splittableParents.filter(function (parent) {
39247                   return parent.geometry(graph) === 'line';
39248                 });
39249               }
39250             }
39251
39252             return splittableParents;
39253
39254             function isSplittable(parent) {
39255               // If the ways to split are specified, ignore everything else.
39256               if (_wayIDs && _wayIDs.indexOf(parent.id) === -1) return false; // We can fake splitting closed ways at their endpoints...
39257
39258               if (parent.isClosed()) return true; // otherwise, we can't split nodes at their endpoints.
39259
39260               for (var i = 1; i < parent.nodes.length - 1; i++) {
39261                 if (parent.nodes[i] === nodeId) return true;
39262               }
39263
39264               return false;
39265             }
39266           };
39267
39268           action.ways = function (graph) {
39269             return utilArrayUniq([].concat.apply([], nodeIds.map(function (nodeId) {
39270               return action.waysForNode(nodeId, graph);
39271             })));
39272           };
39273
39274           action.disabled = function (graph) {
39275             for (var i = 0; i < nodeIds.length; i++) {
39276               var nodeId = nodeIds[i];
39277               var candidates = action.waysForNode(nodeId, graph);
39278
39279               if (candidates.length === 0 || _wayIDs && _wayIDs.length !== candidates.length) {
39280                 return 'not_eligible';
39281               }
39282             }
39283           };
39284
39285           action.limitWays = function (val) {
39286             if (!arguments.length) return _wayIDs;
39287             _wayIDs = val;
39288             return action;
39289           };
39290
39291           action.keepHistoryOn = function (val) {
39292             if (!arguments.length) return _keepHistoryOn;
39293             _keepHistoryOn = val;
39294             return action;
39295           };
39296
39297           return action;
39298         }
39299
39300         function coreGraph(other, mutable) {
39301           if (!(this instanceof coreGraph)) return new coreGraph(other, mutable);
39302
39303           if (other instanceof coreGraph) {
39304             var base = other.base();
39305             this.entities = Object.assign(Object.create(base.entities), other.entities);
39306             this._parentWays = Object.assign(Object.create(base.parentWays), other._parentWays);
39307             this._parentRels = Object.assign(Object.create(base.parentRels), other._parentRels);
39308           } else {
39309             this.entities = Object.create({});
39310             this._parentWays = Object.create({});
39311             this._parentRels = Object.create({});
39312             this.rebase(other || [], [this]);
39313           }
39314
39315           this.transients = {};
39316           this._childNodes = {};
39317           this.frozen = !mutable;
39318         }
39319         coreGraph.prototype = {
39320           hasEntity: function hasEntity(id) {
39321             return this.entities[id];
39322           },
39323           entity: function entity(id) {
39324             var entity = this.entities[id]; //https://github.com/openstreetmap/iD/issues/3973#issuecomment-307052376
39325
39326             if (!entity) {
39327               entity = this.entities.__proto__[id]; // eslint-disable-line no-proto
39328             }
39329
39330             if (!entity) {
39331               throw new Error('entity ' + id + ' not found');
39332             }
39333
39334             return entity;
39335           },
39336           geometry: function geometry(id) {
39337             return this.entity(id).geometry(this);
39338           },
39339           "transient": function transient(entity, key, fn) {
39340             var id = entity.id;
39341             var transients = this.transients[id] || (this.transients[id] = {});
39342
39343             if (transients[key] !== undefined) {
39344               return transients[key];
39345             }
39346
39347             transients[key] = fn.call(entity);
39348             return transients[key];
39349           },
39350           parentWays: function parentWays(entity) {
39351             var parents = this._parentWays[entity.id];
39352             var result = [];
39353
39354             if (parents) {
39355               parents.forEach(function (id) {
39356                 result.push(this.entity(id));
39357               }, this);
39358             }
39359
39360             return result;
39361           },
39362           isPoi: function isPoi(entity) {
39363             var parents = this._parentWays[entity.id];
39364             return !parents || parents.size === 0;
39365           },
39366           isShared: function isShared(entity) {
39367             var parents = this._parentWays[entity.id];
39368             return parents && parents.size > 1;
39369           },
39370           parentRelations: function parentRelations(entity) {
39371             var parents = this._parentRels[entity.id];
39372             var result = [];
39373
39374             if (parents) {
39375               parents.forEach(function (id) {
39376                 result.push(this.entity(id));
39377               }, this);
39378             }
39379
39380             return result;
39381           },
39382           parentMultipolygons: function parentMultipolygons(entity) {
39383             return this.parentRelations(entity).filter(function (relation) {
39384               return relation.isMultipolygon();
39385             });
39386           },
39387           childNodes: function childNodes(entity) {
39388             if (this._childNodes[entity.id]) return this._childNodes[entity.id];
39389             if (!entity.nodes) return [];
39390             var nodes = [];
39391
39392             for (var i = 0; i < entity.nodes.length; i++) {
39393               nodes[i] = this.entity(entity.nodes[i]);
39394             }
39395             this._childNodes[entity.id] = nodes;
39396             return this._childNodes[entity.id];
39397           },
39398           base: function base() {
39399             return {
39400               'entities': Object.getPrototypeOf(this.entities),
39401               'parentWays': Object.getPrototypeOf(this._parentWays),
39402               'parentRels': Object.getPrototypeOf(this._parentRels)
39403             };
39404           },
39405           // Unlike other graph methods, rebase mutates in place. This is because it
39406           // is used only during the history operation that merges newly downloaded
39407           // data into each state. To external consumers, it should appear as if the
39408           // graph always contained the newly downloaded data.
39409           rebase: function rebase(entities, stack, force) {
39410             var base = this.base();
39411             var i, j, k, id;
39412
39413             for (i = 0; i < entities.length; i++) {
39414               var entity = entities[i];
39415               if (!entity.visible || !force && base.entities[entity.id]) continue; // Merging data into the base graph
39416
39417               base.entities[entity.id] = entity;
39418
39419               this._updateCalculated(undefined, entity, base.parentWays, base.parentRels); // Restore provisionally-deleted nodes that are discovered to have an extant parent
39420
39421
39422               if (entity.type === 'way') {
39423                 for (j = 0; j < entity.nodes.length; j++) {
39424                   id = entity.nodes[j];
39425
39426                   for (k = 1; k < stack.length; k++) {
39427                     var ents = stack[k].entities;
39428
39429                     if (ents.hasOwnProperty(id) && ents[id] === undefined) {
39430                       delete ents[id];
39431                     }
39432                   }
39433                 }
39434               }
39435             }
39436
39437             for (i = 0; i < stack.length; i++) {
39438               stack[i]._updateRebased();
39439             }
39440           },
39441           _updateRebased: function _updateRebased() {
39442             var base = this.base();
39443             Object.keys(this._parentWays).forEach(function (child) {
39444               if (base.parentWays[child]) {
39445                 base.parentWays[child].forEach(function (id) {
39446                   if (!this.entities.hasOwnProperty(id)) {
39447                     this._parentWays[child].add(id);
39448                   }
39449                 }, this);
39450               }
39451             }, this);
39452             Object.keys(this._parentRels).forEach(function (child) {
39453               if (base.parentRels[child]) {
39454                 base.parentRels[child].forEach(function (id) {
39455                   if (!this.entities.hasOwnProperty(id)) {
39456                     this._parentRels[child].add(id);
39457                   }
39458                 }, this);
39459               }
39460             }, this);
39461             this.transients = {}; // this._childNodes is not updated, under the assumption that
39462             // ways are always downloaded with their child nodes.
39463           },
39464           // Updates calculated properties (parentWays, parentRels) for the specified change
39465           _updateCalculated: function _updateCalculated(oldentity, entity, parentWays, parentRels) {
39466             parentWays = parentWays || this._parentWays;
39467             parentRels = parentRels || this._parentRels;
39468             var type = entity && entity.type || oldentity && oldentity.type;
39469             var removed, added, i;
39470
39471             if (type === 'way') {
39472               // Update parentWays
39473               if (oldentity && entity) {
39474                 removed = utilArrayDifference(oldentity.nodes, entity.nodes);
39475                 added = utilArrayDifference(entity.nodes, oldentity.nodes);
39476               } else if (oldentity) {
39477                 removed = oldentity.nodes;
39478                 added = [];
39479               } else if (entity) {
39480                 removed = [];
39481                 added = entity.nodes;
39482               }
39483
39484               for (i = 0; i < removed.length; i++) {
39485                 // make a copy of prototype property, store as own property, and update..
39486                 parentWays[removed[i]] = new Set(parentWays[removed[i]]);
39487                 parentWays[removed[i]]["delete"](oldentity.id);
39488               }
39489
39490               for (i = 0; i < added.length; i++) {
39491                 // make a copy of prototype property, store as own property, and update..
39492                 parentWays[added[i]] = new Set(parentWays[added[i]]);
39493                 parentWays[added[i]].add(entity.id);
39494               }
39495             } else if (type === 'relation') {
39496               // Update parentRels
39497               // diff only on the IDs since the same entity can be a member multiple times with different roles
39498               var oldentityMemberIDs = oldentity ? oldentity.members.map(function (m) {
39499                 return m.id;
39500               }) : [];
39501               var entityMemberIDs = entity ? entity.members.map(function (m) {
39502                 return m.id;
39503               }) : [];
39504
39505               if (oldentity && entity) {
39506                 removed = utilArrayDifference(oldentityMemberIDs, entityMemberIDs);
39507                 added = utilArrayDifference(entityMemberIDs, oldentityMemberIDs);
39508               } else if (oldentity) {
39509                 removed = oldentityMemberIDs;
39510                 added = [];
39511               } else if (entity) {
39512                 removed = [];
39513                 added = entityMemberIDs;
39514               }
39515
39516               for (i = 0; i < removed.length; i++) {
39517                 // make a copy of prototype property, store as own property, and update..
39518                 parentRels[removed[i]] = new Set(parentRels[removed[i]]);
39519                 parentRels[removed[i]]["delete"](oldentity.id);
39520               }
39521
39522               for (i = 0; i < added.length; i++) {
39523                 // make a copy of prototype property, store as own property, and update..
39524                 parentRels[added[i]] = new Set(parentRels[added[i]]);
39525                 parentRels[added[i]].add(entity.id);
39526               }
39527             }
39528           },
39529           replace: function replace(entity) {
39530             if (this.entities[entity.id] === entity) return this;
39531             return this.update(function () {
39532               this._updateCalculated(this.entities[entity.id], entity);
39533
39534               this.entities[entity.id] = entity;
39535             });
39536           },
39537           remove: function remove(entity) {
39538             return this.update(function () {
39539               this._updateCalculated(entity, undefined);
39540
39541               this.entities[entity.id] = undefined;
39542             });
39543           },
39544           revert: function revert(id) {
39545             var baseEntity = this.base().entities[id];
39546             var headEntity = this.entities[id];
39547             if (headEntity === baseEntity) return this;
39548             return this.update(function () {
39549               this._updateCalculated(headEntity, baseEntity);
39550
39551               delete this.entities[id];
39552             });
39553           },
39554           update: function update() {
39555             var graph = this.frozen ? coreGraph(this, true) : this;
39556
39557             for (var i = 0; i < arguments.length; i++) {
39558               arguments[i].call(graph, graph);
39559             }
39560
39561             if (this.frozen) graph.frozen = true;
39562             return graph;
39563           },
39564           // Obliterates any existing entities
39565           load: function load(entities) {
39566             var base = this.base();
39567             this.entities = Object.create(base.entities);
39568
39569             for (var i in entities) {
39570               this.entities[i] = entities[i];
39571
39572               this._updateCalculated(base.entities[i], this.entities[i]);
39573             }
39574
39575             return this;
39576           }
39577         };
39578
39579         function osmTurn(turn) {
39580           if (!(this instanceof osmTurn)) {
39581             return new osmTurn(turn);
39582           }
39583
39584           Object.assign(this, turn);
39585         }
39586         function osmIntersection(graph, startVertexId, maxDistance) {
39587           maxDistance = maxDistance || 30; // in meters
39588
39589           var vgraph = coreGraph(); // virtual graph
39590
39591           var i, j, k;
39592
39593           function memberOfRestriction(entity) {
39594             return graph.parentRelations(entity).some(function (r) {
39595               return r.isRestriction();
39596             });
39597           }
39598
39599           function isRoad(way) {
39600             if (way.isArea() || way.isDegenerate()) return false;
39601             var roads = {
39602               'motorway': true,
39603               'motorway_link': true,
39604               'trunk': true,
39605               'trunk_link': true,
39606               'primary': true,
39607               'primary_link': true,
39608               'secondary': true,
39609               'secondary_link': true,
39610               'tertiary': true,
39611               'tertiary_link': true,
39612               'residential': true,
39613               'unclassified': true,
39614               'living_street': true,
39615               'service': true,
39616               'road': true,
39617               'track': true
39618             };
39619             return roads[way.tags.highway];
39620           }
39621
39622           var startNode = graph.entity(startVertexId);
39623           var checkVertices = [startNode];
39624           var checkWays;
39625           var vertices = [];
39626           var vertexIds = [];
39627           var vertex;
39628           var ways = [];
39629           var wayIds = [];
39630           var way;
39631           var nodes = [];
39632           var node;
39633           var parents = [];
39634           var parent; // `actions` will store whatever actions must be performed to satisfy
39635           // preconditions for adding a turn restriction to this intersection.
39636           //  - Remove any existing degenerate turn restrictions (missing from/to, etc)
39637           //  - Reverse oneways so that they are drawn in the forward direction
39638           //  - Split ways on key vertices
39639
39640           var actions = []; // STEP 1:  walk the graph outwards from starting vertex to search
39641           //  for more key vertices and ways to include in the intersection..
39642
39643           while (checkVertices.length) {
39644             vertex = checkVertices.pop(); // check this vertex for parent ways that are roads
39645
39646             checkWays = graph.parentWays(vertex);
39647             var hasWays = false;
39648
39649             for (i = 0; i < checkWays.length; i++) {
39650               way = checkWays[i];
39651               if (!isRoad(way) && !memberOfRestriction(way)) continue;
39652               ways.push(way); // it's a road, or it's already in a turn restriction
39653
39654               hasWays = true; // check the way's children for more key vertices
39655
39656               nodes = utilArrayUniq(graph.childNodes(way));
39657
39658               for (j = 0; j < nodes.length; j++) {
39659                 node = nodes[j];
39660                 if (node === vertex) continue; // same thing
39661
39662                 if (vertices.indexOf(node) !== -1) continue; // seen it already
39663
39664                 if (geoSphericalDistance(node.loc, startNode.loc) > maxDistance) continue; // too far from start
39665                 // a key vertex will have parents that are also roads
39666
39667                 var hasParents = false;
39668                 parents = graph.parentWays(node);
39669
39670                 for (k = 0; k < parents.length; k++) {
39671                   parent = parents[k];
39672                   if (parent === way) continue; // same thing
39673
39674                   if (ways.indexOf(parent) !== -1) continue; // seen it already
39675
39676                   if (!isRoad(parent)) continue; // not a road
39677
39678                   hasParents = true;
39679                   break;
39680                 }
39681
39682                 if (hasParents) {
39683                   checkVertices.push(node);
39684                 }
39685               }
39686             }
39687
39688             if (hasWays) {
39689               vertices.push(vertex);
39690             }
39691           }
39692
39693           vertices = utilArrayUniq(vertices);
39694           ways = utilArrayUniq(ways); // STEP 2:  Build a virtual graph containing only the entities in the intersection..
39695           // Everything done after this step should act on the virtual graph
39696           // Any actions that must be performed later to the main graph go in `actions` array
39697
39698           ways.forEach(function (way) {
39699             graph.childNodes(way).forEach(function (node) {
39700               vgraph = vgraph.replace(node);
39701             });
39702             vgraph = vgraph.replace(way);
39703             graph.parentRelations(way).forEach(function (relation) {
39704               if (relation.isRestriction()) {
39705                 if (relation.isValidRestriction(graph)) {
39706                   vgraph = vgraph.replace(relation);
39707                 } else if (relation.isComplete(graph)) {
39708                   actions.push(actionDeleteRelation(relation.id));
39709                 }
39710               }
39711             });
39712           }); // STEP 3:  Force all oneways to be drawn in the forward direction
39713
39714           ways.forEach(function (w) {
39715             var way = vgraph.entity(w.id);
39716
39717             if (way.tags.oneway === '-1') {
39718               var action = actionReverse(way.id, {
39719                 reverseOneway: true
39720               });
39721               actions.push(action);
39722               vgraph = action(vgraph);
39723             }
39724           }); // STEP 4:  Split ways on key vertices
39725
39726           var origCount = osmEntity.id.next.way;
39727           vertices.forEach(function (v) {
39728             // This is an odd way to do it, but we need to find all the ways that
39729             // will be split here, then split them one at a time to ensure that these
39730             // actions can be replayed on the main graph exactly in the same order.
39731             // (It is unintuitive, but the order of ways returned from graph.parentWays()
39732             // is arbitrary, depending on how the main graph and vgraph were built)
39733             var splitAll = actionSplit([v.id]).keepHistoryOn('first');
39734
39735             if (!splitAll.disabled(vgraph)) {
39736               splitAll.ways(vgraph).forEach(function (way) {
39737                 var splitOne = actionSplit([v.id]).limitWays([way.id]).keepHistoryOn('first');
39738                 actions.push(splitOne);
39739                 vgraph = splitOne(vgraph);
39740               });
39741             }
39742           }); // In here is where we should also split the intersection at nearby junction.
39743           //   for https://github.com/mapbox/iD-internal/issues/31
39744           // nearbyVertices.forEach(function(v) {
39745           // });
39746           // Reasons why we reset the way id count here:
39747           //  1. Continuity with way ids created by the splits so that we can replay
39748           //     these actions later if the user decides to create a turn restriction
39749           //  2. Avoids churning way ids just by hovering over a vertex
39750           //     and displaying the turn restriction editor
39751
39752           osmEntity.id.next.way = origCount; // STEP 5:  Update arrays to point to vgraph entities
39753
39754           vertexIds = vertices.map(function (v) {
39755             return v.id;
39756           });
39757           vertices = [];
39758           ways = [];
39759           vertexIds.forEach(function (id) {
39760             var vertex = vgraph.entity(id);
39761             var parents = vgraph.parentWays(vertex);
39762             vertices.push(vertex);
39763             ways = ways.concat(parents);
39764           });
39765           vertices = utilArrayUniq(vertices);
39766           ways = utilArrayUniq(ways);
39767           vertexIds = vertices.map(function (v) {
39768             return v.id;
39769           });
39770           wayIds = ways.map(function (w) {
39771             return w.id;
39772           }); // STEP 6:  Update the ways with some metadata that will be useful for
39773           // walking the intersection graph later and rendering turn arrows.
39774
39775           function withMetadata(way, vertexIds) {
39776             var __oneWay = way.isOneWay(); // which affixes are key vertices?
39777
39778
39779             var __first = vertexIds.indexOf(way.first()) !== -1;
39780
39781             var __last = vertexIds.indexOf(way.last()) !== -1; // what roles is this way eligible for?
39782
39783
39784             var __via = __first && __last;
39785
39786             var __from = __first && !__oneWay || __last;
39787
39788             var __to = __first || __last && !__oneWay;
39789
39790             return way.update({
39791               __first: __first,
39792               __last: __last,
39793               __from: __from,
39794               __via: __via,
39795               __to: __to,
39796               __oneWay: __oneWay
39797             });
39798           }
39799
39800           ways = [];
39801           wayIds.forEach(function (id) {
39802             var way = withMetadata(vgraph.entity(id), vertexIds);
39803             vgraph = vgraph.replace(way);
39804             ways.push(way);
39805           }); // STEP 7:  Simplify - This is an iterative process where we:
39806           //  1. Find trivial vertices with only 2 parents
39807           //  2. trim off the leaf way from those vertices and remove from vgraph
39808
39809           var keepGoing;
39810           var removeWayIds = [];
39811           var removeVertexIds = [];
39812
39813           do {
39814             keepGoing = false;
39815             checkVertices = vertexIds.slice();
39816
39817             for (i = 0; i < checkVertices.length; i++) {
39818               var vertexId = checkVertices[i];
39819               vertex = vgraph.hasEntity(vertexId);
39820
39821               if (!vertex) {
39822                 if (vertexIds.indexOf(vertexId) !== -1) {
39823                   vertexIds.splice(vertexIds.indexOf(vertexId), 1); // stop checking this one
39824                 }
39825
39826                 removeVertexIds.push(vertexId);
39827                 continue;
39828               }
39829
39830               parents = vgraph.parentWays(vertex);
39831
39832               if (parents.length < 3) {
39833                 if (vertexIds.indexOf(vertexId) !== -1) {
39834                   vertexIds.splice(vertexIds.indexOf(vertexId), 1); // stop checking this one
39835                 }
39836               }
39837
39838               if (parents.length === 2) {
39839                 // vertex with 2 parents is trivial
39840                 var a = parents[0];
39841                 var b = parents[1];
39842                 var aIsLeaf = a && !a.__via;
39843                 var bIsLeaf = b && !b.__via;
39844                 var leaf, survivor;
39845
39846                 if (aIsLeaf && !bIsLeaf) {
39847                   leaf = a;
39848                   survivor = b;
39849                 } else if (!aIsLeaf && bIsLeaf) {
39850                   leaf = b;
39851                   survivor = a;
39852                 }
39853
39854                 if (leaf && survivor) {
39855                   survivor = withMetadata(survivor, vertexIds); // update survivor way
39856
39857                   vgraph = vgraph.replace(survivor).remove(leaf); // update graph
39858
39859                   removeWayIds.push(leaf.id);
39860                   keepGoing = true;
39861                 }
39862               }
39863
39864               parents = vgraph.parentWays(vertex);
39865
39866               if (parents.length < 2) {
39867                 // vertex is no longer a key vertex
39868                 if (vertexIds.indexOf(vertexId) !== -1) {
39869                   vertexIds.splice(vertexIds.indexOf(vertexId), 1); // stop checking this one
39870                 }
39871
39872                 removeVertexIds.push(vertexId);
39873                 keepGoing = true;
39874               }
39875
39876               if (parents.length < 1) {
39877                 // vertex is no longer attached to anything
39878                 vgraph = vgraph.remove(vertex);
39879               }
39880             }
39881           } while (keepGoing);
39882
39883           vertices = vertices.filter(function (vertex) {
39884             return removeVertexIds.indexOf(vertex.id) === -1;
39885           }).map(function (vertex) {
39886             return vgraph.entity(vertex.id);
39887           });
39888           ways = ways.filter(function (way) {
39889             return removeWayIds.indexOf(way.id) === -1;
39890           }).map(function (way) {
39891             return vgraph.entity(way.id);
39892           }); // OK!  Here is our intersection..
39893
39894           var intersection = {
39895             graph: vgraph,
39896             actions: actions,
39897             vertices: vertices,
39898             ways: ways
39899           }; // Get all the valid turns through this intersection given a starting way id.
39900           // This operates on the virtual graph for everything.
39901           //
39902           // Basically, walk through all possible paths from starting way,
39903           //   honoring the existing turn restrictions as we go (watch out for loops!)
39904           //
39905           // For each path found, generate and return a `osmTurn` datastructure.
39906           //
39907
39908           intersection.turns = function (fromWayId, maxViaWay) {
39909             if (!fromWayId) return [];
39910             if (!maxViaWay) maxViaWay = 0;
39911             var vgraph = intersection.graph;
39912             var keyVertexIds = intersection.vertices.map(function (v) {
39913               return v.id;
39914             });
39915             var start = vgraph.entity(fromWayId);
39916             if (!start || !(start.__from || start.__via)) return []; // maxViaWay=0   from-*-to              (0 vias)
39917             // maxViaWay=1   from-*-via-*-to        (1 via max)
39918             // maxViaWay=2   from-*-via-*-via-*-to  (2 vias max)
39919
39920             var maxPathLength = maxViaWay * 2 + 3;
39921             var turns = [];
39922             step(start);
39923             return turns; // traverse the intersection graph and find all the valid paths
39924
39925             function step(entity, currPath, currRestrictions, matchedRestriction) {
39926               currPath = (currPath || []).slice(); // shallow copy
39927
39928               if (currPath.length >= maxPathLength) return;
39929               currPath.push(entity.id);
39930               currRestrictions = (currRestrictions || []).slice(); // shallow copy
39931
39932               var i, j;
39933
39934               if (entity.type === 'node') {
39935                 var parents = vgraph.parentWays(entity);
39936                 var nextWays = []; // which ways can we step into?
39937
39938                 for (i = 0; i < parents.length; i++) {
39939                   var way = parents[i]; // if next way is a oneway incoming to this vertex, skip
39940
39941                   if (way.__oneWay && way.nodes[0] !== entity.id) continue; // if we have seen it before (allowing for an initial u-turn), skip
39942
39943                   if (currPath.indexOf(way.id) !== -1 && currPath.length >= 3) continue; // Check all "current" restrictions (where we've already walked the `FROM`)
39944
39945                   var restrict = null;
39946
39947                   for (j = 0; j < currRestrictions.length; j++) {
39948                     var restriction = currRestrictions[j];
39949                     var f = restriction.memberByRole('from');
39950                     var v = restriction.membersByRole('via');
39951                     var t = restriction.memberByRole('to');
39952                     var isOnly = /^only_/.test(restriction.tags.restriction); // Does the current path match this turn restriction?
39953
39954                     var matchesFrom = f.id === fromWayId;
39955                     var matchesViaTo = false;
39956                     var isAlongOnlyPath = false;
39957
39958                     if (t.id === way.id) {
39959                       // match TO
39960                       if (v.length === 1 && v[0].type === 'node') {
39961                         // match VIA node
39962                         matchesViaTo = v[0].id === entity.id && (matchesFrom && currPath.length === 2 || !matchesFrom && currPath.length > 2);
39963                       } else {
39964                         // match all VIA ways
39965                         var pathVias = [];
39966
39967                         for (k = 2; k < currPath.length; k += 2) {
39968                           // k = 2 skips FROM
39969                           pathVias.push(currPath[k]); // (path goes way-node-way...)
39970                         }
39971
39972                         var restrictionVias = [];
39973
39974                         for (k = 0; k < v.length; k++) {
39975                           if (v[k].type === 'way') {
39976                             restrictionVias.push(v[k].id);
39977                           }
39978                         }
39979
39980                         var diff = utilArrayDifference(pathVias, restrictionVias);
39981                         matchesViaTo = !diff.length;
39982                       }
39983                     } else if (isOnly) {
39984                       for (k = 0; k < v.length; k++) {
39985                         // way doesn't match TO, but is one of the via ways along the path of an "only"
39986                         if (v[k].type === 'way' && v[k].id === way.id) {
39987                           isAlongOnlyPath = true;
39988                           break;
39989                         }
39990                       }
39991                     }
39992
39993                     if (matchesViaTo) {
39994                       if (isOnly) {
39995                         restrict = {
39996                           id: restriction.id,
39997                           direct: matchesFrom,
39998                           from: f.id,
39999                           only: true,
40000                           end: true
40001                         };
40002                       } else {
40003                         restrict = {
40004                           id: restriction.id,
40005                           direct: matchesFrom,
40006                           from: f.id,
40007                           no: true,
40008                           end: true
40009                         };
40010                       }
40011                     } else {
40012                       // indirect - caused by a different nearby restriction
40013                       if (isAlongOnlyPath) {
40014                         restrict = {
40015                           id: restriction.id,
40016                           direct: false,
40017                           from: f.id,
40018                           only: true,
40019                           end: false
40020                         };
40021                       } else if (isOnly) {
40022                         restrict = {
40023                           id: restriction.id,
40024                           direct: false,
40025                           from: f.id,
40026                           no: true,
40027                           end: true
40028                         };
40029                       }
40030                     } // stop looking if we find a "direct" restriction (matching FROM, VIA, TO)
40031
40032
40033                     if (restrict && restrict.direct) break;
40034                   }
40035
40036                   nextWays.push({
40037                     way: way,
40038                     restrict: restrict
40039                   });
40040                 }
40041
40042                 nextWays.forEach(function (nextWay) {
40043                   step(nextWay.way, currPath, currRestrictions, nextWay.restrict);
40044                 });
40045               } else {
40046                 // entity.type === 'way'
40047                 if (currPath.length >= 3) {
40048                   // this is a "complete" path..
40049                   var turnPath = currPath.slice(); // shallow copy
40050                   // an indirect restriction - only include the partial path (starting at FROM)
40051
40052                   if (matchedRestriction && matchedRestriction.direct === false) {
40053                     for (i = 0; i < turnPath.length; i++) {
40054                       if (turnPath[i] === matchedRestriction.from) {
40055                         turnPath = turnPath.slice(i);
40056                         break;
40057                       }
40058                     }
40059                   }
40060
40061                   var turn = pathToTurn(turnPath);
40062
40063                   if (turn) {
40064                     if (matchedRestriction) {
40065                       turn.restrictionID = matchedRestriction.id;
40066                       turn.no = matchedRestriction.no;
40067                       turn.only = matchedRestriction.only;
40068                       turn.direct = matchedRestriction.direct;
40069                     }
40070
40071                     turns.push(osmTurn(turn));
40072                   }
40073
40074                   if (currPath[0] === currPath[2]) return; // if we made a u-turn - stop here
40075                 }
40076
40077                 if (matchedRestriction && matchedRestriction.end) return; // don't advance any further
40078                 // which nodes can we step into?
40079
40080                 var n1 = vgraph.entity(entity.first());
40081                 var n2 = vgraph.entity(entity.last());
40082                 var dist = geoSphericalDistance(n1.loc, n2.loc);
40083                 var nextNodes = [];
40084
40085                 if (currPath.length > 1) {
40086                   if (dist > maxDistance) return; // the next node is too far
40087
40088                   if (!entity.__via) return; // this way is a leaf / can't be a via
40089                 }
40090
40091                 if (!entity.__oneWay && // bidirectional..
40092                 keyVertexIds.indexOf(n1.id) !== -1 && // key vertex..
40093                 currPath.indexOf(n1.id) === -1) {
40094                   // haven't seen it yet..
40095                   nextNodes.push(n1); // can advance to first node
40096                 }
40097
40098                 if (keyVertexIds.indexOf(n2.id) !== -1 && // key vertex..
40099                 currPath.indexOf(n2.id) === -1) {
40100                   // haven't seen it yet..
40101                   nextNodes.push(n2); // can advance to last node
40102                 }
40103
40104                 nextNodes.forEach(function (nextNode) {
40105                   // gather restrictions FROM this way
40106                   var fromRestrictions = vgraph.parentRelations(entity).filter(function (r) {
40107                     if (!r.isRestriction()) return false;
40108                     var f = r.memberByRole('from');
40109                     if (!f || f.id !== entity.id) return false;
40110                     var isOnly = /^only_/.test(r.tags.restriction);
40111                     if (!isOnly) return true; // `only_` restrictions only matter along the direction of the VIA - #4849
40112
40113                     var isOnlyVia = false;
40114                     var v = r.membersByRole('via');
40115
40116                     if (v.length === 1 && v[0].type === 'node') {
40117                       // via node
40118                       isOnlyVia = v[0].id === nextNode.id;
40119                     } else {
40120                       // via way(s)
40121                       for (var i = 0; i < v.length; i++) {
40122                         if (v[i].type !== 'way') continue;
40123                         var viaWay = vgraph.entity(v[i].id);
40124
40125                         if (viaWay.first() === nextNode.id || viaWay.last() === nextNode.id) {
40126                           isOnlyVia = true;
40127                           break;
40128                         }
40129                       }
40130                     }
40131
40132                     return isOnlyVia;
40133                   });
40134                   step(nextNode, currPath, currRestrictions.concat(fromRestrictions), false);
40135                 });
40136               }
40137             } // assumes path is alternating way-node-way of odd length
40138
40139
40140             function pathToTurn(path) {
40141               if (path.length < 3) return;
40142               var fromWayId, fromNodeId, fromVertexId;
40143               var toWayId, toNodeId, toVertexId;
40144               var viaWayIds, viaNodeId, isUturn;
40145               fromWayId = path[0];
40146               toWayId = path[path.length - 1];
40147
40148               if (path.length === 3 && fromWayId === toWayId) {
40149                 // u turn
40150                 var way = vgraph.entity(fromWayId);
40151                 if (way.__oneWay) return null;
40152                 isUturn = true;
40153                 viaNodeId = fromVertexId = toVertexId = path[1];
40154                 fromNodeId = toNodeId = adjacentNode(fromWayId, viaNodeId);
40155               } else {
40156                 isUturn = false;
40157                 fromVertexId = path[1];
40158                 fromNodeId = adjacentNode(fromWayId, fromVertexId);
40159                 toVertexId = path[path.length - 2];
40160                 toNodeId = adjacentNode(toWayId, toVertexId);
40161
40162                 if (path.length === 3) {
40163                   viaNodeId = path[1];
40164                 } else {
40165                   viaWayIds = path.filter(function (entityId) {
40166                     return entityId[0] === 'w';
40167                   });
40168                   viaWayIds = viaWayIds.slice(1, viaWayIds.length - 1); // remove first, last
40169                 }
40170               }
40171
40172               return {
40173                 key: path.join('_'),
40174                 path: path,
40175                 from: {
40176                   node: fromNodeId,
40177                   way: fromWayId,
40178                   vertex: fromVertexId
40179                 },
40180                 via: {
40181                   node: viaNodeId,
40182                   ways: viaWayIds
40183                 },
40184                 to: {
40185                   node: toNodeId,
40186                   way: toWayId,
40187                   vertex: toVertexId
40188                 },
40189                 u: isUturn
40190               };
40191
40192               function adjacentNode(wayId, affixId) {
40193                 var nodes = vgraph.entity(wayId).nodes;
40194                 return affixId === nodes[0] ? nodes[1] : nodes[nodes.length - 2];
40195               }
40196             }
40197           };
40198
40199           return intersection;
40200         }
40201         function osmInferRestriction(graph, turn, projection) {
40202           var fromWay = graph.entity(turn.from.way);
40203           var fromNode = graph.entity(turn.from.node);
40204           var fromVertex = graph.entity(turn.from.vertex);
40205           var toWay = graph.entity(turn.to.way);
40206           var toNode = graph.entity(turn.to.node);
40207           var toVertex = graph.entity(turn.to.vertex);
40208           var fromOneWay = fromWay.tags.oneway === 'yes';
40209           var toOneWay = toWay.tags.oneway === 'yes';
40210           var angle = (geoAngle(fromVertex, fromNode, projection) - geoAngle(toVertex, toNode, projection)) * 180 / Math.PI;
40211
40212           while (angle < 0) {
40213             angle += 360;
40214           }
40215
40216           if (fromNode === toNode) {
40217             return 'no_u_turn';
40218           }
40219
40220           if ((angle < 23 || angle > 336) && fromOneWay && toOneWay) {
40221             return 'no_u_turn'; // wider tolerance for u-turn if both ways are oneway
40222           }
40223
40224           if ((angle < 40 || angle > 319) && fromOneWay && toOneWay && turn.from.vertex !== turn.to.vertex) {
40225             return 'no_u_turn'; // even wider tolerance for u-turn if there is a via way (from !== to)
40226           }
40227
40228           if (angle < 158) {
40229             return 'no_right_turn';
40230           }
40231
40232           if (angle > 202) {
40233             return 'no_left_turn';
40234           }
40235
40236           return 'no_straight_on';
40237         }
40238
40239         function actionMergePolygon(ids, newRelationId) {
40240           function groupEntities(graph) {
40241             var entities = ids.map(function (id) {
40242               return graph.entity(id);
40243             });
40244             var geometryGroups = utilArrayGroupBy(entities, function (entity) {
40245               if (entity.type === 'way' && entity.isClosed()) {
40246                 return 'closedWay';
40247               } else if (entity.type === 'relation' && entity.isMultipolygon()) {
40248                 return 'multipolygon';
40249               } else {
40250                 return 'other';
40251               }
40252             });
40253             return Object.assign({
40254               closedWay: [],
40255               multipolygon: [],
40256               other: []
40257             }, geometryGroups);
40258           }
40259
40260           var action = function action(graph) {
40261             var entities = groupEntities(graph); // An array representing all the polygons that are part of the multipolygon.
40262             //
40263             // Each element is itself an array of objects with an id property, and has a
40264             // locs property which is an array of the locations forming the polygon.
40265
40266             var polygons = entities.multipolygon.reduce(function (polygons, m) {
40267               return polygons.concat(osmJoinWays(m.members, graph));
40268             }, []).concat(entities.closedWay.map(function (d) {
40269               var member = [{
40270                 id: d.id
40271               }];
40272               member.nodes = graph.childNodes(d);
40273               return member;
40274             })); // contained is an array of arrays of boolean values,
40275             // where contained[j][k] is true iff the jth way is
40276             // contained by the kth way.
40277
40278             var contained = polygons.map(function (w, i) {
40279               return polygons.map(function (d, n) {
40280                 if (i === n) return null;
40281                 return geoPolygonContainsPolygon(d.nodes.map(function (n) {
40282                   return n.loc;
40283                 }), w.nodes.map(function (n) {
40284                   return n.loc;
40285                 }));
40286               });
40287             }); // Sort all polygons as either outer or inner ways
40288
40289             var members = [];
40290             var outer = true;
40291
40292             while (polygons.length) {
40293               extractUncontained(polygons);
40294               polygons = polygons.filter(isContained);
40295               contained = contained.filter(isContained).map(filterContained);
40296             }
40297
40298             function isContained(d, i) {
40299               return contained[i].some(function (val) {
40300                 return val;
40301               });
40302             }
40303
40304             function filterContained(d) {
40305               return d.filter(isContained);
40306             }
40307
40308             function extractUncontained(polygons) {
40309               polygons.forEach(function (d, i) {
40310                 if (!isContained(d, i)) {
40311                   d.forEach(function (member) {
40312                     members.push({
40313                       type: 'way',
40314                       id: member.id,
40315                       role: outer ? 'outer' : 'inner'
40316                     });
40317                   });
40318                 }
40319               });
40320               outer = !outer;
40321             } // Move all tags to one relation
40322
40323
40324             var relation = entities.multipolygon[0] || osmRelation({
40325               id: newRelationId,
40326               tags: {
40327                 type: 'multipolygon'
40328               }
40329             });
40330             entities.multipolygon.slice(1).forEach(function (m) {
40331               relation = relation.mergeTags(m.tags);
40332               graph = graph.remove(m);
40333             });
40334             entities.closedWay.forEach(function (way) {
40335               function isThisOuter(m) {
40336                 return m.id === way.id && m.role !== 'inner';
40337               }
40338
40339               if (members.some(isThisOuter)) {
40340                 relation = relation.mergeTags(way.tags);
40341                 graph = graph.replace(way.update({
40342                   tags: {}
40343                 }));
40344               }
40345             });
40346             return graph.replace(relation.update({
40347               members: members,
40348               tags: utilObjectOmit(relation.tags, ['area'])
40349             }));
40350           };
40351
40352           action.disabled = function (graph) {
40353             var entities = groupEntities(graph);
40354
40355             if (entities.other.length > 0 || entities.closedWay.length + entities.multipolygon.length < 2) {
40356               return 'not_eligible';
40357             }
40358
40359             if (!entities.multipolygon.every(function (r) {
40360               return r.isComplete(graph);
40361             })) {
40362               return 'incomplete_relation';
40363             }
40364
40365             if (!entities.multipolygon.length) {
40366               var sharedMultipolygons = [];
40367               entities.closedWay.forEach(function (way, i) {
40368                 if (i === 0) {
40369                   sharedMultipolygons = graph.parentMultipolygons(way);
40370                 } else {
40371                   sharedMultipolygons = utilArrayIntersection(sharedMultipolygons, graph.parentMultipolygons(way));
40372                 }
40373               });
40374               sharedMultipolygons = sharedMultipolygons.filter(function (relation) {
40375                 return relation.members.length === entities.closedWay.length;
40376               });
40377
40378               if (sharedMultipolygons.length) {
40379                 // don't create a new multipolygon if it'd be redundant
40380                 return 'not_eligible';
40381               }
40382             } else if (entities.closedWay.some(function (way) {
40383               return utilArrayIntersection(graph.parentMultipolygons(way), entities.multipolygon).length;
40384             })) {
40385               // don't add a way to a multipolygon again if it's already a member
40386               return 'not_eligible';
40387             }
40388           };
40389
40390           return action;
40391         }
40392
40393         var FORCED$2 = descriptors && fails(function () {
40394           // eslint-disable-next-line es/no-object-getownpropertydescriptor -- safe
40395           return Object.getOwnPropertyDescriptor(RegExp.prototype, 'flags').get.call({ dotAll: true, sticky: true }) !== 'sy';
40396         });
40397
40398         // `RegExp.prototype.flags` getter
40399         // https://tc39.es/ecma262/#sec-get-regexp.prototype.flags
40400         if (FORCED$2) objectDefineProperty.f(RegExp.prototype, 'flags', {
40401           configurable: true,
40402           get: regexpFlags
40403         });
40404
40405         var fastDeepEqual = function equal(a, b) {
40406           if (a === b) return true;
40407
40408           if (a && b && _typeof(a) == 'object' && _typeof(b) == 'object') {
40409             if (a.constructor !== b.constructor) return false;
40410             var length, i, keys;
40411
40412             if (Array.isArray(a)) {
40413               length = a.length;
40414               if (length != b.length) return false;
40415
40416               for (i = length; i-- !== 0;) {
40417                 if (!equal(a[i], b[i])) return false;
40418               }
40419
40420               return true;
40421             }
40422
40423             if (a.constructor === RegExp) return a.source === b.source && a.flags === b.flags;
40424             if (a.valueOf !== Object.prototype.valueOf) return a.valueOf() === b.valueOf();
40425             if (a.toString !== Object.prototype.toString) return a.toString() === b.toString();
40426             keys = Object.keys(a);
40427             length = keys.length;
40428             if (length !== Object.keys(b).length) return false;
40429
40430             for (i = length; i-- !== 0;) {
40431               if (!Object.prototype.hasOwnProperty.call(b, keys[i])) return false;
40432             }
40433
40434             for (i = length; i-- !== 0;) {
40435               var key = keys[i];
40436               if (!equal(a[key], b[key])) return false;
40437             }
40438
40439             return true;
40440           } // true if both NaN, false otherwise
40441
40442
40443           return a !== a && b !== b;
40444         };
40445
40446         // J. W. Hunt and M. D. McIlroy, An algorithm for differential buffer
40447         // comparison, Bell Telephone Laboratories CSTR #41 (1976)
40448         // http://www.cs.dartmouth.edu/~doug/
40449         // https://en.wikipedia.org/wiki/Longest_common_subsequence_problem
40450         //
40451         // Expects two arrays, finds longest common sequence
40452
40453         function LCS(buffer1, buffer2) {
40454           var equivalenceClasses = {};
40455
40456           for (var j = 0; j < buffer2.length; j++) {
40457             var item = buffer2[j];
40458
40459             if (equivalenceClasses[item]) {
40460               equivalenceClasses[item].push(j);
40461             } else {
40462               equivalenceClasses[item] = [j];
40463             }
40464           }
40465
40466           var NULLRESULT = {
40467             buffer1index: -1,
40468             buffer2index: -1,
40469             chain: null
40470           };
40471           var candidates = [NULLRESULT];
40472
40473           for (var i = 0; i < buffer1.length; i++) {
40474             var _item = buffer1[i];
40475             var buffer2indices = equivalenceClasses[_item] || [];
40476             var r = 0;
40477             var c = candidates[0];
40478
40479             for (var jx = 0; jx < buffer2indices.length; jx++) {
40480               var _j = buffer2indices[jx];
40481               var s = void 0;
40482
40483               for (s = r; s < candidates.length; s++) {
40484                 if (candidates[s].buffer2index < _j && (s === candidates.length - 1 || candidates[s + 1].buffer2index > _j)) {
40485                   break;
40486                 }
40487               }
40488
40489               if (s < candidates.length) {
40490                 var newCandidate = {
40491                   buffer1index: i,
40492                   buffer2index: _j,
40493                   chain: candidates[s]
40494                 };
40495
40496                 if (r === candidates.length) {
40497                   candidates.push(c);
40498                 } else {
40499                   candidates[r] = c;
40500                 }
40501
40502                 r = s + 1;
40503                 c = newCandidate;
40504
40505                 if (r === candidates.length) {
40506                   break; // no point in examining further (j)s
40507                 }
40508               }
40509             }
40510
40511             candidates[r] = c;
40512           } // At this point, we know the LCS: it's in the reverse of the
40513           // linked-list through .chain of candidates[candidates.length - 1].
40514
40515
40516           return candidates[candidates.length - 1];
40517         } // We apply the LCS to build a 'comm'-style picture of the
40518         // offsets and lengths of mismatched chunks in the input
40519         // buffers. This is used by diff3MergeRegions.
40520
40521
40522         function diffIndices(buffer1, buffer2) {
40523           var lcs = LCS(buffer1, buffer2);
40524           var result = [];
40525           var tail1 = buffer1.length;
40526           var tail2 = buffer2.length;
40527
40528           for (var candidate = lcs; candidate !== null; candidate = candidate.chain) {
40529             var mismatchLength1 = tail1 - candidate.buffer1index - 1;
40530             var mismatchLength2 = tail2 - candidate.buffer2index - 1;
40531             tail1 = candidate.buffer1index;
40532             tail2 = candidate.buffer2index;
40533
40534             if (mismatchLength1 || mismatchLength2) {
40535               result.push({
40536                 buffer1: [tail1 + 1, mismatchLength1],
40537                 buffer1Content: buffer1.slice(tail1 + 1, tail1 + 1 + mismatchLength1),
40538                 buffer2: [tail2 + 1, mismatchLength2],
40539                 buffer2Content: buffer2.slice(tail2 + 1, tail2 + 1 + mismatchLength2)
40540               });
40541             }
40542           }
40543
40544           result.reverse();
40545           return result;
40546         } // We apply the LCS to build a JSON representation of a
40547         // independently derived from O, returns a fairly complicated
40548         // internal representation of merge decisions it's taken. The
40549         // interested reader may wish to consult
40550         //
40551         // Sanjeev Khanna, Keshav Kunal, and Benjamin C. Pierce.
40552         // 'A Formal Investigation of ' In Arvind and Prasad,
40553         // editors, Foundations of Software Technology and Theoretical
40554         // Computer Science (FSTTCS), December 2007.
40555         //
40556         // (http://www.cis.upenn.edu/~bcpierce/papers/diff3-short.pdf)
40557         //
40558
40559
40560         function diff3MergeRegions(a, o, b) {
40561           // "hunks" are array subsets where `a` or `b` are different from `o`
40562           // https://www.gnu.org/software/diffutils/manual/html_node/diff3-Hunks.html
40563           var hunks = [];
40564
40565           function addHunk(h, ab) {
40566             hunks.push({
40567               ab: ab,
40568               oStart: h.buffer1[0],
40569               oLength: h.buffer1[1],
40570               // length of o to remove
40571               abStart: h.buffer2[0],
40572               abLength: h.buffer2[1] // length of a/b to insert
40573               // abContent: (ab === 'a' ? a : b).slice(h.buffer2[0], h.buffer2[0] + h.buffer2[1])
40574
40575             });
40576           }
40577
40578           diffIndices(o, a).forEach(function (item) {
40579             return addHunk(item, 'a');
40580           });
40581           diffIndices(o, b).forEach(function (item) {
40582             return addHunk(item, 'b');
40583           });
40584           hunks.sort(function (x, y) {
40585             return x.oStart - y.oStart;
40586           });
40587           var results = [];
40588           var currOffset = 0;
40589
40590           function advanceTo(endOffset) {
40591             if (endOffset > currOffset) {
40592               results.push({
40593                 stable: true,
40594                 buffer: 'o',
40595                 bufferStart: currOffset,
40596                 bufferLength: endOffset - currOffset,
40597                 bufferContent: o.slice(currOffset, endOffset)
40598               });
40599               currOffset = endOffset;
40600             }
40601           }
40602
40603           while (hunks.length) {
40604             var hunk = hunks.shift();
40605             var regionStart = hunk.oStart;
40606             var regionEnd = hunk.oStart + hunk.oLength;
40607             var regionHunks = [hunk];
40608             advanceTo(regionStart); // Try to pull next overlapping hunk into this region
40609
40610             while (hunks.length) {
40611               var nextHunk = hunks[0];
40612               var nextHunkStart = nextHunk.oStart;
40613               if (nextHunkStart > regionEnd) break; // no overlap
40614
40615               regionEnd = Math.max(regionEnd, nextHunkStart + nextHunk.oLength);
40616               regionHunks.push(hunks.shift());
40617             }
40618
40619             if (regionHunks.length === 1) {
40620               // Only one hunk touches this region, meaning that there is no conflict here.
40621               // Either `a` or `b` is inserting into a region of `o` unchanged by the other.
40622               if (hunk.abLength > 0) {
40623                 var buffer = hunk.ab === 'a' ? a : b;
40624                 results.push({
40625                   stable: true,
40626                   buffer: hunk.ab,
40627                   bufferStart: hunk.abStart,
40628                   bufferLength: hunk.abLength,
40629                   bufferContent: buffer.slice(hunk.abStart, hunk.abStart + hunk.abLength)
40630                 });
40631               }
40632             } else {
40633               // A true a/b conflict. Determine the bounds involved from `a`, `o`, and `b`.
40634               // Effectively merge all the `a` hunks into one giant hunk, then do the
40635               // same for the `b` hunks; then, correct for skew in the regions of `o`
40636               // that each side changed, and report appropriate spans for the three sides.
40637               var bounds = {
40638                 a: [a.length, -1, o.length, -1],
40639                 b: [b.length, -1, o.length, -1]
40640               };
40641
40642               while (regionHunks.length) {
40643                 hunk = regionHunks.shift();
40644                 var oStart = hunk.oStart;
40645                 var oEnd = oStart + hunk.oLength;
40646                 var abStart = hunk.abStart;
40647                 var abEnd = abStart + hunk.abLength;
40648                 var _b = bounds[hunk.ab];
40649                 _b[0] = Math.min(abStart, _b[0]);
40650                 _b[1] = Math.max(abEnd, _b[1]);
40651                 _b[2] = Math.min(oStart, _b[2]);
40652                 _b[3] = Math.max(oEnd, _b[3]);
40653               }
40654
40655               var aStart = bounds.a[0] + (regionStart - bounds.a[2]);
40656               var aEnd = bounds.a[1] + (regionEnd - bounds.a[3]);
40657               var bStart = bounds.b[0] + (regionStart - bounds.b[2]);
40658               var bEnd = bounds.b[1] + (regionEnd - bounds.b[3]);
40659               var result = {
40660                 stable: false,
40661                 aStart: aStart,
40662                 aLength: aEnd - aStart,
40663                 aContent: a.slice(aStart, aEnd),
40664                 oStart: regionStart,
40665                 oLength: regionEnd - regionStart,
40666                 oContent: o.slice(regionStart, regionEnd),
40667                 bStart: bStart,
40668                 bLength: bEnd - bStart,
40669                 bContent: b.slice(bStart, bEnd)
40670               };
40671               results.push(result);
40672             }
40673
40674             currOffset = regionEnd;
40675           }
40676
40677           advanceTo(o.length);
40678           return results;
40679         } // Applies the output of diff3MergeRegions to actually
40680         // construct the merged buffer; the returned result alternates
40681         // between 'ok' and 'conflict' blocks.
40682         // A "false conflict" is where `a` and `b` both change the same from `o`
40683
40684
40685         function diff3Merge(a, o, b, options) {
40686           var defaults = {
40687             excludeFalseConflicts: true,
40688             stringSeparator: /\s+/
40689           };
40690           options = Object.assign(defaults, options);
40691           var aString = typeof a === 'string';
40692           var oString = typeof o === 'string';
40693           var bString = typeof b === 'string';
40694           if (aString) a = a.split(options.stringSeparator);
40695           if (oString) o = o.split(options.stringSeparator);
40696           if (bString) b = b.split(options.stringSeparator);
40697           var results = [];
40698           var regions = diff3MergeRegions(a, o, b);
40699           var okBuffer = [];
40700
40701           function flushOk() {
40702             if (okBuffer.length) {
40703               results.push({
40704                 ok: okBuffer
40705               });
40706             }
40707
40708             okBuffer = [];
40709           }
40710
40711           function isFalseConflict(a, b) {
40712             if (a.length !== b.length) return false;
40713
40714             for (var i = 0; i < a.length; i++) {
40715               if (a[i] !== b[i]) return false;
40716             }
40717
40718             return true;
40719           }
40720
40721           regions.forEach(function (region) {
40722             if (region.stable) {
40723               var _okBuffer;
40724
40725               (_okBuffer = okBuffer).push.apply(_okBuffer, _toConsumableArray(region.bufferContent));
40726             } else {
40727               if (options.excludeFalseConflicts && isFalseConflict(region.aContent, region.bContent)) {
40728                 var _okBuffer2;
40729
40730                 (_okBuffer2 = okBuffer).push.apply(_okBuffer2, _toConsumableArray(region.aContent));
40731               } else {
40732                 flushOk();
40733                 results.push({
40734                   conflict: {
40735                     a: region.aContent,
40736                     aIndex: region.aStart,
40737                     o: region.oContent,
40738                     oIndex: region.oStart,
40739                     b: region.bContent,
40740                     bIndex: region.bStart
40741                   }
40742                 });
40743               }
40744             }
40745           });
40746           flushOk();
40747           return results;
40748         }
40749
40750         function actionMergeRemoteChanges(id, localGraph, remoteGraph, discardTags, formatUser) {
40751           discardTags = discardTags || {};
40752           var _option = 'safe'; // 'safe', 'force_local', 'force_remote'
40753
40754           var _conflicts = [];
40755
40756           function user(d) {
40757             return typeof formatUser === 'function' ? formatUser(d) : d;
40758           }
40759
40760           function mergeLocation(remote, target) {
40761             function pointEqual(a, b) {
40762               var epsilon = 1e-6;
40763               return Math.abs(a[0] - b[0]) < epsilon && Math.abs(a[1] - b[1]) < epsilon;
40764             }
40765
40766             if (_option === 'force_local' || pointEqual(target.loc, remote.loc)) {
40767               return target;
40768             }
40769
40770             if (_option === 'force_remote') {
40771               return target.update({
40772                 loc: remote.loc
40773               });
40774             }
40775
40776             _conflicts.push(_t('merge_remote_changes.conflict.location', {
40777               user: user(remote.user)
40778             }));
40779
40780             return target;
40781           }
40782
40783           function mergeNodes(base, remote, target) {
40784             if (_option === 'force_local' || fastDeepEqual(target.nodes, remote.nodes)) {
40785               return target;
40786             }
40787
40788             if (_option === 'force_remote') {
40789               return target.update({
40790                 nodes: remote.nodes
40791               });
40792             }
40793
40794             var ccount = _conflicts.length;
40795             var o = base.nodes || [];
40796             var a = target.nodes || [];
40797             var b = remote.nodes || [];
40798             var nodes = [];
40799             var hunks = diff3Merge(a, o, b, {
40800               excludeFalseConflicts: true
40801             });
40802
40803             for (var i = 0; i < hunks.length; i++) {
40804               var hunk = hunks[i];
40805
40806               if (hunk.ok) {
40807                 nodes.push.apply(nodes, hunk.ok);
40808               } else {
40809                 // for all conflicts, we can assume c.a !== c.b
40810                 // because `diff3Merge` called with `true` option to exclude false conflicts..
40811                 var c = hunk.conflict;
40812
40813                 if (fastDeepEqual(c.o, c.a)) {
40814                   // only changed remotely
40815                   nodes.push.apply(nodes, c.b);
40816                 } else if (fastDeepEqual(c.o, c.b)) {
40817                   // only changed locally
40818                   nodes.push.apply(nodes, c.a);
40819                 } else {
40820                   // changed both locally and remotely
40821                   _conflicts.push(_t('merge_remote_changes.conflict.nodelist', {
40822                     user: user(remote.user)
40823                   }));
40824
40825                   break;
40826                 }
40827               }
40828             }
40829
40830             return _conflicts.length === ccount ? target.update({
40831               nodes: nodes
40832             }) : target;
40833           }
40834
40835           function mergeChildren(targetWay, children, updates, graph) {
40836             function isUsed(node, targetWay) {
40837               var hasInterestingParent = graph.parentWays(node).some(function (way) {
40838                 return way.id !== targetWay.id;
40839               });
40840               return node.hasInterestingTags() || hasInterestingParent || graph.parentRelations(node).length > 0;
40841             }
40842
40843             var ccount = _conflicts.length;
40844
40845             for (var i = 0; i < children.length; i++) {
40846               var id = children[i];
40847               var node = graph.hasEntity(id); // remove unused childNodes..
40848
40849               if (targetWay.nodes.indexOf(id) === -1) {
40850                 if (node && !isUsed(node, targetWay)) {
40851                   updates.removeIds.push(id);
40852                 }
40853
40854                 continue;
40855               } // restore used childNodes..
40856
40857
40858               var local = localGraph.hasEntity(id);
40859               var remote = remoteGraph.hasEntity(id);
40860               var target;
40861
40862               if (_option === 'force_remote' && remote && remote.visible) {
40863                 updates.replacements.push(remote);
40864               } else if (_option === 'force_local' && local) {
40865                 target = osmEntity(local);
40866
40867                 if (remote) {
40868                   target = target.update({
40869                     version: remote.version
40870                   });
40871                 }
40872
40873                 updates.replacements.push(target);
40874               } else if (_option === 'safe' && local && remote && local.version !== remote.version) {
40875                 target = osmEntity(local, {
40876                   version: remote.version
40877                 });
40878
40879                 if (remote.visible) {
40880                   target = mergeLocation(remote, target);
40881                 } else {
40882                   _conflicts.push(_t('merge_remote_changes.conflict.deleted', {
40883                     user: user(remote.user)
40884                   }));
40885                 }
40886
40887                 if (_conflicts.length !== ccount) break;
40888                 updates.replacements.push(target);
40889               }
40890             }
40891
40892             return targetWay;
40893           }
40894
40895           function updateChildren(updates, graph) {
40896             for (var i = 0; i < updates.replacements.length; i++) {
40897               graph = graph.replace(updates.replacements[i]);
40898             }
40899
40900             if (updates.removeIds.length) {
40901               graph = actionDeleteMultiple(updates.removeIds)(graph);
40902             }
40903
40904             return graph;
40905           }
40906
40907           function mergeMembers(remote, target) {
40908             if (_option === 'force_local' || fastDeepEqual(target.members, remote.members)) {
40909               return target;
40910             }
40911
40912             if (_option === 'force_remote') {
40913               return target.update({
40914                 members: remote.members
40915               });
40916             }
40917
40918             _conflicts.push(_t('merge_remote_changes.conflict.memberlist', {
40919               user: user(remote.user)
40920             }));
40921
40922             return target;
40923           }
40924
40925           function mergeTags(base, remote, target) {
40926             if (_option === 'force_local' || fastDeepEqual(target.tags, remote.tags)) {
40927               return target;
40928             }
40929
40930             if (_option === 'force_remote') {
40931               return target.update({
40932                 tags: remote.tags
40933               });
40934             }
40935
40936             var ccount = _conflicts.length;
40937             var o = base.tags || {};
40938             var a = target.tags || {};
40939             var b = remote.tags || {};
40940             var keys = utilArrayUnion(utilArrayUnion(Object.keys(o), Object.keys(a)), Object.keys(b)).filter(function (k) {
40941               return !discardTags[k];
40942             });
40943             var tags = Object.assign({}, a); // shallow copy
40944
40945             var changed = false;
40946
40947             for (var i = 0; i < keys.length; i++) {
40948               var k = keys[i];
40949
40950               if (o[k] !== b[k] && a[k] !== b[k]) {
40951                 // changed remotely..
40952                 if (o[k] !== a[k]) {
40953                   // changed locally..
40954                   _conflicts.push(_t('merge_remote_changes.conflict.tags', {
40955                     tag: k,
40956                     local: a[k],
40957                     remote: b[k],
40958                     user: user(remote.user)
40959                   }));
40960                 } else {
40961                   // unchanged locally, accept remote change..
40962                   if (b.hasOwnProperty(k)) {
40963                     tags[k] = b[k];
40964                   } else {
40965                     delete tags[k];
40966                   }
40967
40968                   changed = true;
40969                 }
40970               }
40971             }
40972
40973             return changed && _conflicts.length === ccount ? target.update({
40974               tags: tags
40975             }) : target;
40976           } //  `graph.base()` is the common ancestor of the two graphs.
40977           //  `localGraph` contains user's edits up to saving
40978           //  `remoteGraph` contains remote edits to modified nodes
40979           //  `graph` must be a descendent of `localGraph` and may include
40980           //      some conflict resolution actions performed on it.
40981           //
40982           //                  --- ... --- `localGraph` -- ... -- `graph`
40983           //                 /
40984           //  `graph.base()` --- ... --- `remoteGraph`
40985           //
40986
40987
40988           var action = function action(graph) {
40989             var updates = {
40990               replacements: [],
40991               removeIds: []
40992             };
40993             var base = graph.base().entities[id];
40994             var local = localGraph.entity(id);
40995             var remote = remoteGraph.entity(id);
40996             var target = osmEntity(local, {
40997               version: remote.version
40998             }); // delete/undelete
40999
41000             if (!remote.visible) {
41001               if (_option === 'force_remote') {
41002                 return actionDeleteMultiple([id])(graph);
41003               } else if (_option === 'force_local') {
41004                 if (target.type === 'way') {
41005                   target = mergeChildren(target, utilArrayUniq(local.nodes), updates, graph);
41006                   graph = updateChildren(updates, graph);
41007                 }
41008
41009                 return graph.replace(target);
41010               } else {
41011                 _conflicts.push(_t('merge_remote_changes.conflict.deleted', {
41012                   user: user(remote.user)
41013                 }));
41014
41015                 return graph; // do nothing
41016               }
41017             } // merge
41018
41019
41020             if (target.type === 'node') {
41021               target = mergeLocation(remote, target);
41022             } else if (target.type === 'way') {
41023               // pull in any child nodes that may not be present locally..
41024               graph.rebase(remoteGraph.childNodes(remote), [graph], false);
41025               target = mergeNodes(base, remote, target);
41026               target = mergeChildren(target, utilArrayUnion(local.nodes, remote.nodes), updates, graph);
41027             } else if (target.type === 'relation') {
41028               target = mergeMembers(remote, target);
41029             }
41030
41031             target = mergeTags(base, remote, target);
41032
41033             if (!_conflicts.length) {
41034               graph = updateChildren(updates, graph).replace(target);
41035             }
41036
41037             return graph;
41038           };
41039
41040           action.withOption = function (opt) {
41041             _option = opt;
41042             return action;
41043           };
41044
41045           action.conflicts = function () {
41046             return _conflicts;
41047           };
41048
41049           return action;
41050         }
41051
41052         // https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/MoveNodeAction.as
41053
41054         function actionMove(moveIDs, tryDelta, projection, cache) {
41055           var _delta = tryDelta;
41056
41057           function setupCache(graph) {
41058             function canMove(nodeID) {
41059               // Allow movement of any node that is in the selectedIDs list..
41060               if (moveIDs.indexOf(nodeID) !== -1) return true; // Allow movement of a vertex where 2 ways meet..
41061
41062               var parents = graph.parentWays(graph.entity(nodeID));
41063               if (parents.length < 3) return true; // Restrict movement of a vertex where >2 ways meet, unless all parentWays are moving too..
41064
41065               var parentsMoving = parents.every(function (way) {
41066                 return cache.moving[way.id];
41067               });
41068               if (!parentsMoving) delete cache.moving[nodeID];
41069               return parentsMoving;
41070             }
41071
41072             function cacheEntities(ids) {
41073               for (var i = 0; i < ids.length; i++) {
41074                 var id = ids[i];
41075                 if (cache.moving[id]) continue;
41076                 cache.moving[id] = true;
41077                 var entity = graph.hasEntity(id);
41078                 if (!entity) continue;
41079
41080                 if (entity.type === 'node') {
41081                   cache.nodes.push(id);
41082                   cache.startLoc[id] = entity.loc;
41083                 } else if (entity.type === 'way') {
41084                   cache.ways.push(id);
41085                   cacheEntities(entity.nodes);
41086                 } else {
41087                   cacheEntities(entity.members.map(function (member) {
41088                     return member.id;
41089                   }));
41090                 }
41091               }
41092             }
41093
41094             function cacheIntersections(ids) {
41095               function isEndpoint(way, id) {
41096                 return !way.isClosed() && !!way.affix(id);
41097               }
41098
41099               for (var i = 0; i < ids.length; i++) {
41100                 var id = ids[i]; // consider only intersections with 1 moved and 1 unmoved way.
41101
41102                 var childNodes = graph.childNodes(graph.entity(id));
41103
41104                 for (var j = 0; j < childNodes.length; j++) {
41105                   var node = childNodes[j];
41106                   var parents = graph.parentWays(node);
41107                   if (parents.length !== 2) continue;
41108                   var moved = graph.entity(id);
41109                   var unmoved = null;
41110
41111                   for (var k = 0; k < parents.length; k++) {
41112                     var way = parents[k];
41113
41114                     if (!cache.moving[way.id]) {
41115                       unmoved = way;
41116                       break;
41117                     }
41118                   }
41119
41120                   if (!unmoved) continue; // exclude ways that are overly connected..
41121
41122                   if (utilArrayIntersection(moved.nodes, unmoved.nodes).length > 2) continue;
41123                   if (moved.isArea() || unmoved.isArea()) continue;
41124                   cache.intersections.push({
41125                     nodeId: node.id,
41126                     movedId: moved.id,
41127                     unmovedId: unmoved.id,
41128                     movedIsEP: isEndpoint(moved, node.id),
41129                     unmovedIsEP: isEndpoint(unmoved, node.id)
41130                   });
41131                 }
41132               }
41133             }
41134
41135             if (!cache) {
41136               cache = {};
41137             }
41138
41139             if (!cache.ok) {
41140               cache.moving = {};
41141               cache.intersections = [];
41142               cache.replacedVertex = {};
41143               cache.startLoc = {};
41144               cache.nodes = [];
41145               cache.ways = [];
41146               cacheEntities(moveIDs);
41147               cacheIntersections(cache.ways);
41148               cache.nodes = cache.nodes.filter(canMove);
41149               cache.ok = true;
41150             }
41151           } // Place a vertex where the moved vertex used to be, to preserve way shape..
41152           //
41153           //  Start:
41154           //      b ---- e
41155           //     / \
41156           //    /   \
41157           //   /     \
41158           //  a       c
41159           //
41160           //      *               node '*' added to preserve shape
41161           //     / \
41162           //    /   b ---- e      way `b,e` moved here:
41163           //   /     \
41164           //  a       c
41165           //
41166           //
41167
41168
41169           function replaceMovedVertex(nodeId, wayId, graph, delta) {
41170             var way = graph.entity(wayId);
41171             var moved = graph.entity(nodeId);
41172             var movedIndex = way.nodes.indexOf(nodeId);
41173             var len, prevIndex, nextIndex;
41174
41175             if (way.isClosed()) {
41176               len = way.nodes.length - 1;
41177               prevIndex = (movedIndex + len - 1) % len;
41178               nextIndex = (movedIndex + len + 1) % len;
41179             } else {
41180               len = way.nodes.length;
41181               prevIndex = movedIndex - 1;
41182               nextIndex = movedIndex + 1;
41183             }
41184
41185             var prev = graph.hasEntity(way.nodes[prevIndex]);
41186             var next = graph.hasEntity(way.nodes[nextIndex]); // Don't add orig vertex at endpoint..
41187
41188             if (!prev || !next) return graph;
41189             var key = wayId + '_' + nodeId;
41190             var orig = cache.replacedVertex[key];
41191
41192             if (!orig) {
41193               orig = osmNode();
41194               cache.replacedVertex[key] = orig;
41195               cache.startLoc[orig.id] = cache.startLoc[nodeId];
41196             }
41197
41198             var start, end;
41199
41200             if (delta) {
41201               start = projection(cache.startLoc[nodeId]);
41202               end = projection.invert(geoVecAdd(start, delta));
41203             } else {
41204               end = cache.startLoc[nodeId];
41205             }
41206
41207             orig = orig.move(end);
41208             var angle = Math.abs(geoAngle(orig, prev, projection) - geoAngle(orig, next, projection)) * 180 / Math.PI; // Don't add orig vertex if it would just make a straight line..
41209
41210             if (angle > 175 && angle < 185) return graph; // moving forward or backward along way?
41211
41212             var p1 = [prev.loc, orig.loc, moved.loc, next.loc].map(projection);
41213             var p2 = [prev.loc, moved.loc, orig.loc, next.loc].map(projection);
41214             var d1 = geoPathLength(p1);
41215             var d2 = geoPathLength(p2);
41216             var insertAt = d1 <= d2 ? movedIndex : nextIndex; // moving around closed loop?
41217
41218             if (way.isClosed() && insertAt === 0) insertAt = len;
41219             way = way.addNode(orig.id, insertAt);
41220             return graph.replace(orig).replace(way);
41221           } // Remove duplicate vertex that might have been added by
41222           // replaceMovedVertex.  This is done after the unzorro checks.
41223
41224
41225           function removeDuplicateVertices(wayId, graph) {
41226             var way = graph.entity(wayId);
41227             var epsilon = 1e-6;
41228             var prev, curr;
41229
41230             function isInteresting(node, graph) {
41231               return graph.parentWays(node).length > 1 || graph.parentRelations(node).length || node.hasInterestingTags();
41232             }
41233
41234             for (var i = 0; i < way.nodes.length; i++) {
41235               curr = graph.entity(way.nodes[i]);
41236
41237               if (prev && curr && geoVecEqual(prev.loc, curr.loc, epsilon)) {
41238                 if (!isInteresting(prev, graph)) {
41239                   way = way.removeNode(prev.id);
41240                   graph = graph.replace(way).remove(prev);
41241                 } else if (!isInteresting(curr, graph)) {
41242                   way = way.removeNode(curr.id);
41243                   graph = graph.replace(way).remove(curr);
41244                 }
41245               }
41246
41247               prev = curr;
41248             }
41249
41250             return graph;
41251           } // Reorder nodes around intersections that have moved..
41252           //
41253           //  Start:                way1.nodes: b,e         (moving)
41254           //  a - b - c ----- d     way2.nodes: a,b,c,d     (static)
41255           //      |                 vertex: b
41256           //      e                 isEP1: true,  isEP2, false
41257           //
41258           //  way1 `b,e` moved here:
41259           //  a ----- c = b - d
41260           //              |
41261           //              e
41262           //
41263           //  reorder nodes         way1.nodes: b,e
41264           //  a ----- c - b - d     way2.nodes: a,c,b,d
41265           //              |
41266           //              e
41267           //
41268
41269
41270           function unZorroIntersection(intersection, graph) {
41271             var vertex = graph.entity(intersection.nodeId);
41272             var way1 = graph.entity(intersection.movedId);
41273             var way2 = graph.entity(intersection.unmovedId);
41274             var isEP1 = intersection.movedIsEP;
41275             var isEP2 = intersection.unmovedIsEP; // don't move the vertex if it is the endpoint of both ways.
41276
41277             if (isEP1 && isEP2) return graph;
41278             var nodes1 = graph.childNodes(way1).filter(function (n) {
41279               return n !== vertex;
41280             });
41281             var nodes2 = graph.childNodes(way2).filter(function (n) {
41282               return n !== vertex;
41283             });
41284             if (way1.isClosed() && way1.first() === vertex.id) nodes1.push(nodes1[0]);
41285             if (way2.isClosed() && way2.first() === vertex.id) nodes2.push(nodes2[0]);
41286             var edge1 = !isEP1 && geoChooseEdge(nodes1, projection(vertex.loc), projection);
41287             var edge2 = !isEP2 && geoChooseEdge(nodes2, projection(vertex.loc), projection);
41288             var loc; // snap vertex to nearest edge (or some point between them)..
41289
41290             if (!isEP1 && !isEP2) {
41291               var epsilon = 1e-6,
41292                   maxIter = 10;
41293
41294               for (var i = 0; i < maxIter; i++) {
41295                 loc = geoVecInterp(edge1.loc, edge2.loc, 0.5);
41296                 edge1 = geoChooseEdge(nodes1, projection(loc), projection);
41297                 edge2 = geoChooseEdge(nodes2, projection(loc), projection);
41298                 if (Math.abs(edge1.distance - edge2.distance) < epsilon) break;
41299               }
41300             } else if (!isEP1) {
41301               loc = edge1.loc;
41302             } else {
41303               loc = edge2.loc;
41304             }
41305
41306             graph = graph.replace(vertex.move(loc)); // if zorro happened, reorder nodes..
41307
41308             if (!isEP1 && edge1.index !== way1.nodes.indexOf(vertex.id)) {
41309               way1 = way1.removeNode(vertex.id).addNode(vertex.id, edge1.index);
41310               graph = graph.replace(way1);
41311             }
41312
41313             if (!isEP2 && edge2.index !== way2.nodes.indexOf(vertex.id)) {
41314               way2 = way2.removeNode(vertex.id).addNode(vertex.id, edge2.index);
41315               graph = graph.replace(way2);
41316             }
41317
41318             return graph;
41319           }
41320
41321           function cleanupIntersections(graph) {
41322             for (var i = 0; i < cache.intersections.length; i++) {
41323               var obj = cache.intersections[i];
41324               graph = replaceMovedVertex(obj.nodeId, obj.movedId, graph, _delta);
41325               graph = replaceMovedVertex(obj.nodeId, obj.unmovedId, graph, null);
41326               graph = unZorroIntersection(obj, graph);
41327               graph = removeDuplicateVertices(obj.movedId, graph);
41328               graph = removeDuplicateVertices(obj.unmovedId, graph);
41329             }
41330
41331             return graph;
41332           } // check if moving way endpoint can cross an unmoved way, if so limit delta..
41333
41334
41335           function limitDelta(graph) {
41336             function moveNode(loc) {
41337               return geoVecAdd(projection(loc), _delta);
41338             }
41339
41340             for (var i = 0; i < cache.intersections.length; i++) {
41341               var obj = cache.intersections[i]; // Don't limit movement if this is vertex joins 2 endpoints..
41342
41343               if (obj.movedIsEP && obj.unmovedIsEP) continue; // Don't limit movement if this vertex is not an endpoint anyway..
41344
41345               if (!obj.movedIsEP) continue;
41346               var node = graph.entity(obj.nodeId);
41347               var start = projection(node.loc);
41348               var end = geoVecAdd(start, _delta);
41349               var movedNodes = graph.childNodes(graph.entity(obj.movedId));
41350               var movedPath = movedNodes.map(function (n) {
41351                 return moveNode(n.loc);
41352               });
41353               var unmovedNodes = graph.childNodes(graph.entity(obj.unmovedId));
41354               var unmovedPath = unmovedNodes.map(function (n) {
41355                 return projection(n.loc);
41356               });
41357               var hits = geoPathIntersections(movedPath, unmovedPath);
41358
41359               for (var j = 0; i < hits.length; i++) {
41360                 if (geoVecEqual(hits[j], end)) continue;
41361                 var edge = geoChooseEdge(unmovedNodes, end, projection);
41362                 _delta = geoVecSubtract(projection(edge.loc), start);
41363               }
41364             }
41365           }
41366
41367           var action = function action(graph) {
41368             if (_delta[0] === 0 && _delta[1] === 0) return graph;
41369             setupCache(graph);
41370
41371             if (cache.intersections.length) {
41372               limitDelta(graph);
41373             }
41374
41375             for (var i = 0; i < cache.nodes.length; i++) {
41376               var node = graph.entity(cache.nodes[i]);
41377               var start = projection(node.loc);
41378               var end = geoVecAdd(start, _delta);
41379               graph = graph.replace(node.move(projection.invert(end)));
41380             }
41381
41382             if (cache.intersections.length) {
41383               graph = cleanupIntersections(graph);
41384             }
41385
41386             return graph;
41387           };
41388
41389           action.delta = function () {
41390             return _delta;
41391           };
41392
41393           return action;
41394         }
41395
41396         function actionMoveMember(relationId, fromIndex, toIndex) {
41397           return function (graph) {
41398             return graph.replace(graph.entity(relationId).moveMember(fromIndex, toIndex));
41399           };
41400         }
41401
41402         function actionMoveNode(nodeID, toLoc) {
41403           var action = function action(graph, t) {
41404             if (t === null || !isFinite(t)) t = 1;
41405             t = Math.min(Math.max(+t, 0), 1);
41406             var node = graph.entity(nodeID);
41407             return graph.replace(node.move(geoVecInterp(node.loc, toLoc, t)));
41408           };
41409
41410           action.transitionable = true;
41411           return action;
41412         }
41413
41414         function actionNoop() {
41415           return function (graph) {
41416             return graph;
41417           };
41418         }
41419
41420         function actionOrthogonalize(wayID, projection, vertexID, degThresh, ep) {
41421           var epsilon = ep || 1e-4;
41422           var threshold = degThresh || 13; // degrees within right or straight to alter
41423           // We test normalized dot products so we can compare as cos(angle)
41424
41425           var lowerThreshold = Math.cos((90 - threshold) * Math.PI / 180);
41426           var upperThreshold = Math.cos(threshold * Math.PI / 180);
41427
41428           var action = function action(graph, t) {
41429             if (t === null || !isFinite(t)) t = 1;
41430             t = Math.min(Math.max(+t, 0), 1);
41431             var way = graph.entity(wayID);
41432             way = way.removeNode(''); // sanity check - remove any consecutive duplicates
41433
41434             if (way.tags.nonsquare) {
41435               var tags = Object.assign({}, way.tags); // since we're squaring, remove indication that this is physically unsquare
41436
41437               delete tags.nonsquare;
41438               way = way.update({
41439                 tags: tags
41440               });
41441             }
41442
41443             graph = graph.replace(way);
41444             var isClosed = way.isClosed();
41445             var nodes = graph.childNodes(way).slice(); // shallow copy
41446
41447             if (isClosed) nodes.pop();
41448
41449             if (vertexID !== undefined) {
41450               nodes = nodeSubset(nodes, vertexID, isClosed);
41451               if (nodes.length !== 3) return graph;
41452             } // note: all geometry functions here use the unclosed node/point/coord list
41453
41454
41455             var nodeCount = {};
41456             var points = [];
41457             var corner = {
41458               i: 0,
41459               dotp: 1
41460             };
41461             var node, point, loc, score, motions, i, j;
41462
41463             for (i = 0; i < nodes.length; i++) {
41464               node = nodes[i];
41465               nodeCount[node.id] = (nodeCount[node.id] || 0) + 1;
41466               points.push({
41467                 id: node.id,
41468                 coord: projection(node.loc)
41469               });
41470             }
41471
41472             if (points.length === 3) {
41473               // move only one vertex for right triangle
41474               for (i = 0; i < 1000; i++) {
41475                 motions = points.map(calcMotion);
41476                 points[corner.i].coord = geoVecAdd(points[corner.i].coord, motions[corner.i]);
41477                 score = corner.dotp;
41478
41479                 if (score < epsilon) {
41480                   break;
41481                 }
41482               }
41483
41484               node = graph.entity(nodes[corner.i].id);
41485               loc = projection.invert(points[corner.i].coord);
41486               graph = graph.replace(node.move(geoVecInterp(node.loc, loc, t)));
41487             } else {
41488               var straights = [];
41489               var simplified = []; // Remove points from nearly straight sections..
41490               // This produces a simplified shape to orthogonalize
41491
41492               for (i = 0; i < points.length; i++) {
41493                 point = points[i];
41494                 var dotp = 0;
41495
41496                 if (isClosed || i > 0 && i < points.length - 1) {
41497                   var a = points[(i - 1 + points.length) % points.length];
41498                   var b = points[(i + 1) % points.length];
41499                   dotp = Math.abs(geoOrthoNormalizedDotProduct(a.coord, b.coord, point.coord));
41500                 }
41501
41502                 if (dotp > upperThreshold) {
41503                   straights.push(point);
41504                 } else {
41505                   simplified.push(point);
41506                 }
41507               } // Orthogonalize the simplified shape
41508
41509
41510               var bestPoints = clonePoints(simplified);
41511               var originalPoints = clonePoints(simplified);
41512               score = Infinity;
41513
41514               for (i = 0; i < 1000; i++) {
41515                 motions = simplified.map(calcMotion);
41516
41517                 for (j = 0; j < motions.length; j++) {
41518                   simplified[j].coord = geoVecAdd(simplified[j].coord, motions[j]);
41519                 }
41520
41521                 var newScore = geoOrthoCalcScore(simplified, isClosed, epsilon, threshold);
41522
41523                 if (newScore < score) {
41524                   bestPoints = clonePoints(simplified);
41525                   score = newScore;
41526                 }
41527
41528                 if (score < epsilon) {
41529                   break;
41530                 }
41531               }
41532
41533               var bestCoords = bestPoints.map(function (p) {
41534                 return p.coord;
41535               });
41536               if (isClosed) bestCoords.push(bestCoords[0]); // move the nodes that should move
41537
41538               for (i = 0; i < bestPoints.length; i++) {
41539                 point = bestPoints[i];
41540
41541                 if (!geoVecEqual(originalPoints[i].coord, point.coord)) {
41542                   node = graph.entity(point.id);
41543                   loc = projection.invert(point.coord);
41544                   graph = graph.replace(node.move(geoVecInterp(node.loc, loc, t)));
41545                 }
41546               } // move the nodes along straight segments
41547
41548
41549               for (i = 0; i < straights.length; i++) {
41550                 point = straights[i];
41551                 if (nodeCount[point.id] > 1) continue; // skip self-intersections
41552
41553                 node = graph.entity(point.id);
41554
41555                 if (t === 1 && graph.parentWays(node).length === 1 && graph.parentRelations(node).length === 0 && !node.hasInterestingTags()) {
41556                   // remove uninteresting points..
41557                   graph = actionDeleteNode(node.id)(graph);
41558                 } else {
41559                   // move interesting points to the nearest edge..
41560                   var choice = geoVecProject(point.coord, bestCoords);
41561
41562                   if (choice) {
41563                     loc = projection.invert(choice.target);
41564                     graph = graph.replace(node.move(geoVecInterp(node.loc, loc, t)));
41565                   }
41566                 }
41567               }
41568             }
41569
41570             return graph;
41571
41572             function clonePoints(array) {
41573               return array.map(function (p) {
41574                 return {
41575                   id: p.id,
41576                   coord: [p.coord[0], p.coord[1]]
41577                 };
41578               });
41579             }
41580
41581             function calcMotion(point, i, array) {
41582               // don't try to move the endpoints of a non-closed way.
41583               if (!isClosed && (i === 0 || i === array.length - 1)) return [0, 0]; // don't try to move a node that appears more than once (self intersection)
41584
41585               if (nodeCount[array[i].id] > 1) return [0, 0];
41586               var a = array[(i - 1 + array.length) % array.length].coord;
41587               var origin = point.coord;
41588               var b = array[(i + 1) % array.length].coord;
41589               var p = geoVecSubtract(a, origin);
41590               var q = geoVecSubtract(b, origin);
41591               var scale = 2 * Math.min(geoVecLength(p), geoVecLength(q));
41592               p = geoVecNormalize(p);
41593               q = geoVecNormalize(q);
41594               var dotp = p[0] * q[0] + p[1] * q[1];
41595               var val = Math.abs(dotp);
41596
41597               if (val < lowerThreshold) {
41598                 // nearly orthogonal
41599                 corner.i = i;
41600                 corner.dotp = val;
41601                 var vec = geoVecNormalize(geoVecAdd(p, q));
41602                 return geoVecScale(vec, 0.1 * dotp * scale);
41603               }
41604
41605               return [0, 0]; // do nothing
41606             }
41607           }; // if we are only orthogonalizing one vertex,
41608           // get that vertex and the previous and next
41609
41610
41611           function nodeSubset(nodes, vertexID, isClosed) {
41612             var first = isClosed ? 0 : 1;
41613             var last = isClosed ? nodes.length : nodes.length - 1;
41614
41615             for (var i = first; i < last; i++) {
41616               if (nodes[i].id === vertexID) {
41617                 return [nodes[(i - 1 + nodes.length) % nodes.length], nodes[i], nodes[(i + 1) % nodes.length]];
41618               }
41619             }
41620
41621             return [];
41622           }
41623
41624           action.disabled = function (graph) {
41625             var way = graph.entity(wayID);
41626             way = way.removeNode(''); // sanity check - remove any consecutive duplicates
41627
41628             graph = graph.replace(way);
41629             var isClosed = way.isClosed();
41630             var nodes = graph.childNodes(way).slice(); // shallow copy
41631
41632             if (isClosed) nodes.pop();
41633             var allowStraightAngles = false;
41634
41635             if (vertexID !== undefined) {
41636               allowStraightAngles = true;
41637               nodes = nodeSubset(nodes, vertexID, isClosed);
41638               if (nodes.length !== 3) return 'end_vertex';
41639             }
41640
41641             var coords = nodes.map(function (n) {
41642               return projection(n.loc);
41643             });
41644             var score = geoOrthoCanOrthogonalize(coords, isClosed, epsilon, threshold, allowStraightAngles);
41645
41646             if (score === null) {
41647               return 'not_squarish';
41648             } else if (score === 0) {
41649               return 'square_enough';
41650             } else {
41651               return false;
41652             }
41653           };
41654
41655           action.transitionable = true;
41656           return action;
41657         }
41658
41659         //
41660         // `turn` must be an `osmTurn` object
41661         // see osm/intersection.js, pathToTurn()
41662         //
41663         // This specifies a restriction of type `restriction` when traveling from
41664         // `turn.from.way` toward `turn.to.way` via `turn.via.node` OR `turn.via.ways`.
41665         // (The action does not check that these entities form a valid intersection.)
41666         //
41667         // From, to, and via ways should be split before calling this action.
41668         // (old versions of the code would split the ways here, but we no longer do it)
41669         //
41670         // For testing convenience, accepts a restrictionID to assign to the new
41671         // relation. Normally, this will be undefined and the relation will
41672         // automatically be assigned a new ID.
41673         //
41674
41675         function actionRestrictTurn(turn, restrictionType, restrictionID) {
41676           return function (graph) {
41677             var fromWay = graph.entity(turn.from.way);
41678             var toWay = graph.entity(turn.to.way);
41679             var viaNode = turn.via.node && graph.entity(turn.via.node);
41680             var viaWays = turn.via.ways && turn.via.ways.map(function (id) {
41681               return graph.entity(id);
41682             });
41683             var members = [];
41684             members.push({
41685               id: fromWay.id,
41686               type: 'way',
41687               role: 'from'
41688             });
41689
41690             if (viaNode) {
41691               members.push({
41692                 id: viaNode.id,
41693                 type: 'node',
41694                 role: 'via'
41695               });
41696             } else if (viaWays) {
41697               viaWays.forEach(function (viaWay) {
41698                 members.push({
41699                   id: viaWay.id,
41700                   type: 'way',
41701                   role: 'via'
41702                 });
41703               });
41704             }
41705
41706             members.push({
41707               id: toWay.id,
41708               type: 'way',
41709               role: 'to'
41710             });
41711             return graph.replace(osmRelation({
41712               id: restrictionID,
41713               tags: {
41714                 type: 'restriction',
41715                 restriction: restrictionType
41716               },
41717               members: members
41718             }));
41719           };
41720         }
41721
41722         function actionRevert(id) {
41723           var action = function action(graph) {
41724             var entity = graph.hasEntity(id),
41725                 base = graph.base().entities[id];
41726
41727             if (entity && !base) {
41728               // entity will be removed..
41729               if (entity.type === 'node') {
41730                 graph.parentWays(entity).forEach(function (parent) {
41731                   parent = parent.removeNode(id);
41732                   graph = graph.replace(parent);
41733
41734                   if (parent.isDegenerate()) {
41735                     graph = actionDeleteWay(parent.id)(graph);
41736                   }
41737                 });
41738               }
41739
41740               graph.parentRelations(entity).forEach(function (parent) {
41741                 parent = parent.removeMembersWithID(id);
41742                 graph = graph.replace(parent);
41743
41744                 if (parent.isDegenerate()) {
41745                   graph = actionDeleteRelation(parent.id)(graph);
41746                 }
41747               });
41748             }
41749
41750             return graph.revert(id);
41751           };
41752
41753           return action;
41754         }
41755
41756         function actionRotate(rotateIds, pivot, angle, projection) {
41757           var action = function action(graph) {
41758             return graph.update(function (graph) {
41759               utilGetAllNodes(rotateIds, graph).forEach(function (node) {
41760                 var point = geoRotate([projection(node.loc)], angle, pivot)[0];
41761                 graph = graph.replace(node.move(projection.invert(point)));
41762               });
41763             });
41764           };
41765
41766           return action;
41767         }
41768
41769         function actionScale(ids, pivotLoc, scaleFactor, projection) {
41770           return function (graph) {
41771             return graph.update(function (graph) {
41772               var point, radial;
41773               utilGetAllNodes(ids, graph).forEach(function (node) {
41774                 point = projection(node.loc);
41775                 radial = [point[0] - pivotLoc[0], point[1] - pivotLoc[1]];
41776                 point = [pivotLoc[0] + scaleFactor * radial[0], pivotLoc[1] + scaleFactor * radial[1]];
41777                 graph = graph.replace(node.move(projection.invert(point)));
41778               });
41779             });
41780           };
41781         }
41782
41783         /* Align nodes along their common axis */
41784
41785         function actionStraightenNodes(nodeIDs, projection) {
41786           function positionAlongWay(a, o, b) {
41787             return geoVecDot(a, b, o) / geoVecDot(b, b, o);
41788           } // returns the endpoints of the long axis of symmetry of the `points` bounding rect
41789
41790
41791           function getEndpoints(points) {
41792             var ssr = geoGetSmallestSurroundingRectangle(points); // Choose line pq = axis of symmetry.
41793             // The shape's surrounding rectangle has 2 axes of symmetry.
41794             // Snap points to the long axis
41795
41796             var p1 = [(ssr.poly[0][0] + ssr.poly[1][0]) / 2, (ssr.poly[0][1] + ssr.poly[1][1]) / 2];
41797             var q1 = [(ssr.poly[2][0] + ssr.poly[3][0]) / 2, (ssr.poly[2][1] + ssr.poly[3][1]) / 2];
41798             var p2 = [(ssr.poly[3][0] + ssr.poly[4][0]) / 2, (ssr.poly[3][1] + ssr.poly[4][1]) / 2];
41799             var q2 = [(ssr.poly[1][0] + ssr.poly[2][0]) / 2, (ssr.poly[1][1] + ssr.poly[2][1]) / 2];
41800             var isLong = geoVecLength(p1, q1) > geoVecLength(p2, q2);
41801
41802             if (isLong) {
41803               return [p1, q1];
41804             }
41805
41806             return [p2, q2];
41807           }
41808
41809           var action = function action(graph, t) {
41810             if (t === null || !isFinite(t)) t = 1;
41811             t = Math.min(Math.max(+t, 0), 1);
41812             var nodes = nodeIDs.map(function (id) {
41813               return graph.entity(id);
41814             });
41815             var points = nodes.map(function (n) {
41816               return projection(n.loc);
41817             });
41818             var endpoints = getEndpoints(points);
41819             var startPoint = endpoints[0];
41820             var endPoint = endpoints[1]; // Move points onto the line connecting the endpoints
41821
41822             for (var i = 0; i < points.length; i++) {
41823               var node = nodes[i];
41824               var point = points[i];
41825               var u = positionAlongWay(point, startPoint, endPoint);
41826               var point2 = geoVecInterp(startPoint, endPoint, u);
41827               var loc2 = projection.invert(point2);
41828               graph = graph.replace(node.move(geoVecInterp(node.loc, loc2, t)));
41829             }
41830
41831             return graph;
41832           };
41833
41834           action.disabled = function (graph) {
41835             var nodes = nodeIDs.map(function (id) {
41836               return graph.entity(id);
41837             });
41838             var points = nodes.map(function (n) {
41839               return projection(n.loc);
41840             });
41841             var endpoints = getEndpoints(points);
41842             var startPoint = endpoints[0];
41843             var endPoint = endpoints[1];
41844             var maxDistance = 0;
41845
41846             for (var i = 0; i < points.length; i++) {
41847               var point = points[i];
41848               var u = positionAlongWay(point, startPoint, endPoint);
41849               var p = geoVecInterp(startPoint, endPoint, u);
41850               var dist = geoVecLength(p, point);
41851
41852               if (!isNaN(dist) && dist > maxDistance) {
41853                 maxDistance = dist;
41854               }
41855             }
41856
41857             if (maxDistance < 0.0001) {
41858               return 'straight_enough';
41859             }
41860           };
41861
41862           action.transitionable = true;
41863           return action;
41864         }
41865
41866         /*
41867          * Based on https://github.com/openstreetmap/potlatch2/net/systemeD/potlatch2/tools/Straighten.as
41868          */
41869
41870         function actionStraightenWay(selectedIDs, projection) {
41871           function positionAlongWay(a, o, b) {
41872             return geoVecDot(a, b, o) / geoVecDot(b, b, o);
41873           } // Return all selected ways as a continuous, ordered array of nodes
41874
41875
41876           function allNodes(graph) {
41877             var nodes = [];
41878             var startNodes = [];
41879             var endNodes = [];
41880             var remainingWays = [];
41881             var selectedWays = selectedIDs.filter(function (w) {
41882               return graph.entity(w).type === 'way';
41883             });
41884             var selectedNodes = selectedIDs.filter(function (n) {
41885               return graph.entity(n).type === 'node';
41886             });
41887
41888             for (var i = 0; i < selectedWays.length; i++) {
41889               var way = graph.entity(selectedWays[i]);
41890               nodes = way.nodes.slice(0);
41891               remainingWays.push(nodes);
41892               startNodes.push(nodes[0]);
41893               endNodes.push(nodes[nodes.length - 1]);
41894             } // Remove duplicate end/startNodes (duplicate nodes cannot be at the line end,
41895             //   and need to be removed so currNode difference calculation below works)
41896             // i.e. ["n-1", "n-1", "n-2"] => ["n-2"]
41897
41898
41899             startNodes = startNodes.filter(function (n) {
41900               return startNodes.indexOf(n) === startNodes.lastIndexOf(n);
41901             });
41902             endNodes = endNodes.filter(function (n) {
41903               return endNodes.indexOf(n) === endNodes.lastIndexOf(n);
41904             }); // Choose the initial endpoint to start from
41905
41906             var currNode = utilArrayDifference(startNodes, endNodes).concat(utilArrayDifference(endNodes, startNodes))[0];
41907             var nextWay = [];
41908             nodes = []; // Create nested function outside of loop to avoid "function in loop" lint error
41909
41910             var getNextWay = function getNextWay(currNode, remainingWays) {
41911               return remainingWays.filter(function (way) {
41912                 return way[0] === currNode || way[way.length - 1] === currNode;
41913               })[0];
41914             }; // Add nodes to end of nodes array, until all ways are added
41915
41916
41917             while (remainingWays.length) {
41918               nextWay = getNextWay(currNode, remainingWays);
41919               remainingWays = utilArrayDifference(remainingWays, [nextWay]);
41920
41921               if (nextWay[0] !== currNode) {
41922                 nextWay.reverse();
41923               }
41924
41925               nodes = nodes.concat(nextWay);
41926               currNode = nodes[nodes.length - 1];
41927             } // If user selected 2 nodes to straighten between, then slice nodes array to those nodes
41928
41929
41930             if (selectedNodes.length === 2) {
41931               var startNodeIdx = nodes.indexOf(selectedNodes[0]);
41932               var endNodeIdx = nodes.indexOf(selectedNodes[1]);
41933               var sortedStartEnd = [startNodeIdx, endNodeIdx];
41934               sortedStartEnd.sort(function (a, b) {
41935                 return a - b;
41936               });
41937               nodes = nodes.slice(sortedStartEnd[0], sortedStartEnd[1] + 1);
41938             }
41939
41940             return nodes.map(function (n) {
41941               return graph.entity(n);
41942             });
41943           }
41944
41945           function shouldKeepNode(node, graph) {
41946             return graph.parentWays(node).length > 1 || graph.parentRelations(node).length || node.hasInterestingTags();
41947           }
41948
41949           var action = function action(graph, t) {
41950             if (t === null || !isFinite(t)) t = 1;
41951             t = Math.min(Math.max(+t, 0), 1);
41952             var nodes = allNodes(graph);
41953             var points = nodes.map(function (n) {
41954               return projection(n.loc);
41955             });
41956             var startPoint = points[0];
41957             var endPoint = points[points.length - 1];
41958             var toDelete = [];
41959             var i;
41960
41961             for (i = 1; i < points.length - 1; i++) {
41962               var node = nodes[i];
41963               var point = points[i];
41964
41965               if (t < 1 || shouldKeepNode(node, graph)) {
41966                 var u = positionAlongWay(point, startPoint, endPoint);
41967                 var p = geoVecInterp(startPoint, endPoint, u);
41968                 var loc2 = projection.invert(p);
41969                 graph = graph.replace(node.move(geoVecInterp(node.loc, loc2, t)));
41970               } else {
41971                 // safe to delete
41972                 if (toDelete.indexOf(node) === -1) {
41973                   toDelete.push(node);
41974                 }
41975               }
41976             }
41977
41978             for (i = 0; i < toDelete.length; i++) {
41979               graph = actionDeleteNode(toDelete[i].id)(graph);
41980             }
41981
41982             return graph;
41983           };
41984
41985           action.disabled = function (graph) {
41986             // check way isn't too bendy
41987             var nodes = allNodes(graph);
41988             var points = nodes.map(function (n) {
41989               return projection(n.loc);
41990             });
41991             var startPoint = points[0];
41992             var endPoint = points[points.length - 1];
41993             var threshold = 0.2 * geoVecLength(startPoint, endPoint);
41994             var i;
41995
41996             if (threshold === 0) {
41997               return 'too_bendy';
41998             }
41999
42000             var maxDistance = 0;
42001
42002             for (i = 1; i < points.length - 1; i++) {
42003               var point = points[i];
42004               var u = positionAlongWay(point, startPoint, endPoint);
42005               var p = geoVecInterp(startPoint, endPoint, u);
42006               var dist = geoVecLength(p, point); // to bendy if point is off by 20% of total start/end distance in projected space
42007
42008               if (isNaN(dist) || dist > threshold) {
42009                 return 'too_bendy';
42010               } else if (dist > maxDistance) {
42011                 maxDistance = dist;
42012               }
42013             }
42014
42015             var keepingAllNodes = nodes.every(function (node, i) {
42016               return i === 0 || i === nodes.length - 1 || shouldKeepNode(node, graph);
42017             });
42018
42019             if (maxDistance < 0.0001 && // Allow straightening even if already straight in order to remove extraneous nodes
42020             keepingAllNodes) {
42021               return 'straight_enough';
42022             }
42023           };
42024
42025           action.transitionable = true;
42026           return action;
42027         }
42028
42029         //
42030         // `turn` must be an `osmTurn` object with a `restrictionID` property.
42031         // see osm/intersection.js, pathToTurn()
42032         //
42033
42034         function actionUnrestrictTurn(turn) {
42035           return function (graph) {
42036             return actionDeleteRelation(turn.restrictionID)(graph);
42037           };
42038         }
42039
42040         /* Reflect the given area around its axis of symmetry */
42041
42042         function actionReflect(reflectIds, projection) {
42043           var _useLongAxis = true;
42044
42045           var action = function action(graph, t) {
42046             if (t === null || !isFinite(t)) t = 1;
42047             t = Math.min(Math.max(+t, 0), 1);
42048             var nodes = utilGetAllNodes(reflectIds, graph);
42049             var points = nodes.map(function (n) {
42050               return projection(n.loc);
42051             });
42052             var ssr = geoGetSmallestSurroundingRectangle(points); // Choose line pq = axis of symmetry.
42053             // The shape's surrounding rectangle has 2 axes of symmetry.
42054             // Reflect across the longer axis by default.
42055
42056             var p1 = [(ssr.poly[0][0] + ssr.poly[1][0]) / 2, (ssr.poly[0][1] + ssr.poly[1][1]) / 2];
42057             var q1 = [(ssr.poly[2][0] + ssr.poly[3][0]) / 2, (ssr.poly[2][1] + ssr.poly[3][1]) / 2];
42058             var p2 = [(ssr.poly[3][0] + ssr.poly[4][0]) / 2, (ssr.poly[3][1] + ssr.poly[4][1]) / 2];
42059             var q2 = [(ssr.poly[1][0] + ssr.poly[2][0]) / 2, (ssr.poly[1][1] + ssr.poly[2][1]) / 2];
42060             var p, q;
42061             var isLong = geoVecLength(p1, q1) > geoVecLength(p2, q2);
42062
42063             if (_useLongAxis && isLong || !_useLongAxis && !isLong) {
42064               p = p1;
42065               q = q1;
42066             } else {
42067               p = p2;
42068               q = q2;
42069             } // reflect c across pq
42070             // http://math.stackexchange.com/questions/65503/point-reflection-over-a-line
42071
42072
42073             var dx = q[0] - p[0];
42074             var dy = q[1] - p[1];
42075             var a = (dx * dx - dy * dy) / (dx * dx + dy * dy);
42076             var b = 2 * dx * dy / (dx * dx + dy * dy);
42077
42078             for (var i = 0; i < nodes.length; i++) {
42079               var node = nodes[i];
42080               var c = projection(node.loc);
42081               var c2 = [a * (c[0] - p[0]) + b * (c[1] - p[1]) + p[0], b * (c[0] - p[0]) - a * (c[1] - p[1]) + p[1]];
42082               var loc2 = projection.invert(c2);
42083               node = node.move(geoVecInterp(node.loc, loc2, t));
42084               graph = graph.replace(node);
42085             }
42086
42087             return graph;
42088           };
42089
42090           action.useLongAxis = function (val) {
42091             if (!arguments.length) return _useLongAxis;
42092             _useLongAxis = val;
42093             return action;
42094           };
42095
42096           action.transitionable = true;
42097           return action;
42098         }
42099
42100         function actionUpgradeTags(entityId, oldTags, replaceTags) {
42101           return function (graph) {
42102             var entity = graph.entity(entityId);
42103             var tags = Object.assign({}, entity.tags); // shallow copy
42104
42105             var transferValue;
42106             var semiIndex;
42107
42108             for (var oldTagKey in oldTags) {
42109               if (!(oldTagKey in tags)) continue; // wildcard match
42110
42111               if (oldTags[oldTagKey] === '*') {
42112                 // note the value since we might need to transfer it
42113                 transferValue = tags[oldTagKey];
42114                 delete tags[oldTagKey]; // exact match
42115               } else if (oldTags[oldTagKey] === tags[oldTagKey]) {
42116                 delete tags[oldTagKey]; // match is within semicolon-delimited values
42117               } else {
42118                 var vals = tags[oldTagKey].split(';').filter(Boolean);
42119                 var oldIndex = vals.indexOf(oldTags[oldTagKey]);
42120
42121                 if (vals.length === 1 || oldIndex === -1) {
42122                   delete tags[oldTagKey];
42123                 } else {
42124                   if (replaceTags && replaceTags[oldTagKey]) {
42125                     // replacing a value within a semicolon-delimited value, note the index
42126                     semiIndex = oldIndex;
42127                   }
42128
42129                   vals.splice(oldIndex, 1);
42130                   tags[oldTagKey] = vals.join(';');
42131                 }
42132               }
42133             }
42134
42135             if (replaceTags) {
42136               for (var replaceKey in replaceTags) {
42137                 var replaceValue = replaceTags[replaceKey];
42138
42139                 if (replaceValue === '*') {
42140                   if (tags[replaceKey] && tags[replaceKey] !== 'no') {
42141                     // allow any pre-existing value except `no` (troll tag)
42142                     continue;
42143                   } else {
42144                     // otherwise assume `yes` is okay
42145                     tags[replaceKey] = 'yes';
42146                   }
42147                 } else if (replaceValue === '$1') {
42148                   tags[replaceKey] = transferValue;
42149                 } else {
42150                   if (tags[replaceKey] && oldTags[replaceKey] && semiIndex !== undefined) {
42151                     // don't override preexisting values
42152                     var existingVals = tags[replaceKey].split(';').filter(Boolean);
42153
42154                     if (existingVals.indexOf(replaceValue) === -1) {
42155                       existingVals.splice(semiIndex, 0, replaceValue);
42156                       tags[replaceKey] = existingVals.join(';');
42157                     }
42158                   } else {
42159                     tags[replaceKey] = replaceValue;
42160                   }
42161                 }
42162               }
42163             }
42164
42165             return graph.replace(entity.update({
42166               tags: tags
42167             }));
42168           };
42169         }
42170
42171         function behaviorEdit(context) {
42172           function behavior() {
42173             context.map().minzoom(context.minEditableZoom());
42174           }
42175
42176           behavior.off = function () {
42177             context.map().minzoom(0);
42178           };
42179
42180           return behavior;
42181         }
42182
42183         /*
42184            The hover behavior adds the `.hover` class on pointerover to all elements to which
42185            the identical datum is bound, and removes it on pointerout.
42186
42187            The :hover pseudo-class is insufficient for iD's purposes because a datum's visual
42188            representation may consist of several elements scattered throughout the DOM hierarchy.
42189            Only one of these elements can have the :hover pseudo-class, but all of them will
42190            have the .hover class.
42191          */
42192
42193         function behaviorHover(context) {
42194           var dispatch = dispatch$8('hover');
42195
42196           var _selection = select(null);
42197
42198           var _newNodeId = null;
42199           var _initialNodeID = null;
42200
42201           var _altDisables;
42202
42203           var _ignoreVertex;
42204
42205           var _targets = []; // use pointer events on supported platforms; fallback to mouse events
42206
42207           var _pointerPrefix = 'PointerEvent' in window ? 'pointer' : 'mouse';
42208
42209           function keydown(d3_event) {
42210             if (_altDisables && d3_event.keyCode === utilKeybinding.modifierCodes.alt) {
42211               _selection.selectAll('.hover').classed('hover-suppressed', true).classed('hover', false);
42212
42213               _selection.classed('hover-disabled', true);
42214
42215               dispatch.call('hover', this, null);
42216             }
42217           }
42218
42219           function keyup(d3_event) {
42220             if (_altDisables && d3_event.keyCode === utilKeybinding.modifierCodes.alt) {
42221               _selection.selectAll('.hover-suppressed').classed('hover-suppressed', false).classed('hover', true);
42222
42223               _selection.classed('hover-disabled', false);
42224
42225               dispatch.call('hover', this, _targets);
42226             }
42227           }
42228
42229           function behavior(selection) {
42230             _selection = selection;
42231             _targets = [];
42232
42233             if (_initialNodeID) {
42234               _newNodeId = _initialNodeID;
42235               _initialNodeID = null;
42236             } else {
42237               _newNodeId = null;
42238             }
42239
42240             _selection.on(_pointerPrefix + 'over.hover', pointerover).on(_pointerPrefix + 'out.hover', pointerout) // treat pointerdown as pointerover for touch devices
42241             .on(_pointerPrefix + 'down.hover', pointerover);
42242
42243             select(window).on(_pointerPrefix + 'up.hover pointercancel.hover', pointerout, true).on('keydown.hover', keydown).on('keyup.hover', keyup);
42244
42245             function eventTarget(d3_event) {
42246               var datum = d3_event.target && d3_event.target.__data__;
42247               if (_typeof(datum) !== 'object') return null;
42248
42249               if (!(datum instanceof osmEntity) && datum.properties && datum.properties.entity instanceof osmEntity) {
42250                 return datum.properties.entity;
42251               }
42252
42253               return datum;
42254             }
42255
42256             function pointerover(d3_event) {
42257               // ignore mouse hovers with buttons pressed unless dragging
42258               if (context.mode().id.indexOf('drag') === -1 && (!d3_event.pointerType || d3_event.pointerType === 'mouse') && d3_event.buttons) return;
42259               var target = eventTarget(d3_event);
42260
42261               if (target && _targets.indexOf(target) === -1) {
42262                 _targets.push(target);
42263
42264                 updateHover(d3_event, _targets);
42265               }
42266             }
42267
42268             function pointerout(d3_event) {
42269               var target = eventTarget(d3_event);
42270
42271               var index = _targets.indexOf(target);
42272
42273               if (index !== -1) {
42274                 _targets.splice(index);
42275
42276                 updateHover(d3_event, _targets);
42277               }
42278             }
42279
42280             function allowsVertex(d) {
42281               return d.geometry(context.graph()) === 'vertex' || _mainPresetIndex.allowsVertex(d, context.graph());
42282             }
42283
42284             function modeAllowsHover(target) {
42285               var mode = context.mode();
42286
42287               if (mode.id === 'add-point') {
42288                 return mode.preset.matchGeometry('vertex') || target.type !== 'way' && target.geometry(context.graph()) !== 'vertex';
42289               }
42290
42291               return true;
42292             }
42293
42294             function updateHover(d3_event, targets) {
42295               _selection.selectAll('.hover').classed('hover', false);
42296
42297               _selection.selectAll('.hover-suppressed').classed('hover-suppressed', false);
42298
42299               var mode = context.mode();
42300
42301               if (!_newNodeId && (mode.id === 'draw-line' || mode.id === 'draw-area')) {
42302                 var node = targets.find(function (target) {
42303                   return target instanceof osmEntity && target.type === 'node';
42304                 });
42305                 _newNodeId = node && node.id;
42306               }
42307
42308               targets = targets.filter(function (datum) {
42309                 if (datum instanceof osmEntity) {
42310                   // If drawing a way, don't hover on a node that was just placed. #3974
42311                   return datum.id !== _newNodeId && (datum.type !== 'node' || !_ignoreVertex || allowsVertex(datum)) && modeAllowsHover(datum);
42312                 }
42313
42314                 return true;
42315               });
42316               var selector = '';
42317
42318               for (var i in targets) {
42319                 var datum = targets[i]; // What are we hovering over?
42320
42321                 if (datum.__featurehash__) {
42322                   // hovering custom data
42323                   selector += ', .data' + datum.__featurehash__;
42324                 } else if (datum instanceof QAItem) {
42325                   selector += ', .' + datum.service + '.itemId-' + datum.id;
42326                 } else if (datum instanceof osmNote) {
42327                   selector += ', .note-' + datum.id;
42328                 } else if (datum instanceof osmEntity) {
42329                   selector += ', .' + datum.id;
42330
42331                   if (datum.type === 'relation') {
42332                     for (var j in datum.members) {
42333                       selector += ', .' + datum.members[j].id;
42334                     }
42335                   }
42336                 }
42337               }
42338
42339               var suppressed = _altDisables && d3_event && d3_event.altKey;
42340
42341               if (selector.trim().length) {
42342                 // remove the first comma
42343                 selector = selector.slice(1);
42344
42345                 _selection.selectAll(selector).classed(suppressed ? 'hover-suppressed' : 'hover', true);
42346               }
42347
42348               dispatch.call('hover', this, !suppressed && targets);
42349             }
42350           }
42351
42352           behavior.off = function (selection) {
42353             selection.selectAll('.hover').classed('hover', false);
42354             selection.selectAll('.hover-suppressed').classed('hover-suppressed', false);
42355             selection.classed('hover-disabled', false);
42356             selection.on(_pointerPrefix + 'over.hover', null).on(_pointerPrefix + 'out.hover', null).on(_pointerPrefix + 'down.hover', null);
42357             select(window).on(_pointerPrefix + 'up.hover pointercancel.hover', null, true).on('keydown.hover', null).on('keyup.hover', null);
42358           };
42359
42360           behavior.altDisables = function (val) {
42361             if (!arguments.length) return _altDisables;
42362             _altDisables = val;
42363             return behavior;
42364           };
42365
42366           behavior.ignoreVertex = function (val) {
42367             if (!arguments.length) return _ignoreVertex;
42368             _ignoreVertex = val;
42369             return behavior;
42370           };
42371
42372           behavior.initialNodeID = function (nodeId) {
42373             _initialNodeID = nodeId;
42374             return behavior;
42375           };
42376
42377           return utilRebind(behavior, dispatch, 'on');
42378         }
42379
42380         var _disableSpace = false;
42381         var _lastSpace = null;
42382         function behaviorDraw(context) {
42383           var dispatch = dispatch$8('move', 'down', 'downcancel', 'click', 'clickWay', 'clickNode', 'undo', 'cancel', 'finish');
42384           var keybinding = utilKeybinding('draw');
42385
42386           var _hover = behaviorHover(context).altDisables(true).ignoreVertex(true).on('hover', context.ui().sidebar.hover);
42387
42388           var _edit = behaviorEdit(context);
42389
42390           var _closeTolerance = 4;
42391           var _tolerance = 12;
42392           var _mouseLeave = false;
42393           var _lastMouse = null;
42394
42395           var _lastPointerUpEvent;
42396
42397           var _downPointer; // use pointer events on supported platforms; fallback to mouse events
42398
42399
42400           var _pointerPrefix = 'PointerEvent' in window ? 'pointer' : 'mouse'; // related code
42401           // - `mode/drag_node.js` `datum()`
42402
42403
42404           function datum(d3_event) {
42405             var mode = context.mode();
42406             var isNote = mode && mode.id.indexOf('note') !== -1;
42407             if (d3_event.altKey || isNote) return {};
42408             var element;
42409
42410             if (d3_event.type === 'keydown') {
42411               element = _lastMouse && _lastMouse.target;
42412             } else {
42413               element = d3_event.target;
42414             } // When drawing, snap only to touch targets..
42415             // (this excludes area fills and active drawing elements)
42416
42417
42418             var d = element.__data__;
42419             return d && d.properties && d.properties.target ? d : {};
42420           }
42421
42422           function pointerdown(d3_event) {
42423             if (_downPointer) return;
42424             var pointerLocGetter = utilFastMouse(this);
42425             _downPointer = {
42426               id: d3_event.pointerId || 'mouse',
42427               pointerLocGetter: pointerLocGetter,
42428               downTime: +new Date(),
42429               downLoc: pointerLocGetter(d3_event)
42430             };
42431             dispatch.call('down', this, d3_event, datum(d3_event));
42432           }
42433
42434           function pointerup(d3_event) {
42435             if (!_downPointer || _downPointer.id !== (d3_event.pointerId || 'mouse')) return;
42436             var downPointer = _downPointer;
42437             _downPointer = null;
42438             _lastPointerUpEvent = d3_event;
42439             if (downPointer.isCancelled) return;
42440             var t2 = +new Date();
42441             var p2 = downPointer.pointerLocGetter(d3_event);
42442             var dist = geoVecLength(downPointer.downLoc, p2);
42443
42444             if (dist < _closeTolerance || dist < _tolerance && t2 - downPointer.downTime < 500) {
42445               // Prevent a quick second click
42446               select(window).on('click.draw-block', function () {
42447                 d3_event.stopPropagation();
42448               }, true);
42449               context.map().dblclickZoomEnable(false);
42450               window.setTimeout(function () {
42451                 context.map().dblclickZoomEnable(true);
42452                 select(window).on('click.draw-block', null);
42453               }, 500);
42454               click(d3_event, p2);
42455             }
42456           }
42457
42458           function pointermove(d3_event) {
42459             if (_downPointer && _downPointer.id === (d3_event.pointerId || 'mouse') && !_downPointer.isCancelled) {
42460               var p2 = _downPointer.pointerLocGetter(d3_event);
42461
42462               var dist = geoVecLength(_downPointer.downLoc, p2);
42463
42464               if (dist >= _closeTolerance) {
42465                 _downPointer.isCancelled = true;
42466                 dispatch.call('downcancel', this);
42467               }
42468             }
42469
42470             if (d3_event.pointerType && d3_event.pointerType !== 'mouse' || d3_event.buttons || _downPointer) return; // HACK: Mobile Safari likes to send one or more `mouse` type pointermove
42471             // events immediately after non-mouse pointerup events; detect and ignore them.
42472
42473             if (_lastPointerUpEvent && _lastPointerUpEvent.pointerType !== 'mouse' && d3_event.timeStamp - _lastPointerUpEvent.timeStamp < 100) return;
42474             _lastMouse = d3_event;
42475             dispatch.call('move', this, d3_event, datum(d3_event));
42476           }
42477
42478           function pointercancel(d3_event) {
42479             if (_downPointer && _downPointer.id === (d3_event.pointerId || 'mouse')) {
42480               if (!_downPointer.isCancelled) {
42481                 dispatch.call('downcancel', this);
42482               }
42483
42484               _downPointer = null;
42485             }
42486           }
42487
42488           function mouseenter() {
42489             _mouseLeave = false;
42490           }
42491
42492           function mouseleave() {
42493             _mouseLeave = true;
42494           }
42495
42496           function allowsVertex(d) {
42497             return d.geometry(context.graph()) === 'vertex' || _mainPresetIndex.allowsVertex(d, context.graph());
42498           } // related code
42499           // - `mode/drag_node.js`     `doMove()`
42500           // - `behavior/draw.js`      `click()`
42501           // - `behavior/draw_way.js`  `move()`
42502
42503
42504           function click(d3_event, loc) {
42505             var d = datum(d3_event);
42506             var target = d && d.properties && d.properties.entity;
42507             var mode = context.mode();
42508
42509             if (target && target.type === 'node' && allowsVertex(target)) {
42510               // Snap to a node
42511               dispatch.call('clickNode', this, target, d);
42512               return;
42513             } else if (target && target.type === 'way' && (mode.id !== 'add-point' || mode.preset.matchGeometry('vertex'))) {
42514               // Snap to a way
42515               var choice = geoChooseEdge(context.graph().childNodes(target), loc, context.projection, context.activeID());
42516
42517               if (choice) {
42518                 var edge = [target.nodes[choice.index - 1], target.nodes[choice.index]];
42519                 dispatch.call('clickWay', this, choice.loc, edge, d);
42520                 return;
42521               }
42522             } else if (mode.id !== 'add-point' || mode.preset.matchGeometry('point')) {
42523               var locLatLng = context.projection.invert(loc);
42524               dispatch.call('click', this, locLatLng, d);
42525             }
42526           } // treat a spacebar press like a click
42527
42528
42529           function space(d3_event) {
42530             d3_event.preventDefault();
42531             d3_event.stopPropagation();
42532             var currSpace = context.map().mouse();
42533
42534             if (_disableSpace && _lastSpace) {
42535               var dist = geoVecLength(_lastSpace, currSpace);
42536
42537               if (dist > _tolerance) {
42538                 _disableSpace = false;
42539               }
42540             }
42541
42542             if (_disableSpace || _mouseLeave || !_lastMouse) return; // user must move mouse or release space bar to allow another click
42543
42544             _lastSpace = currSpace;
42545             _disableSpace = true;
42546             select(window).on('keyup.space-block', function () {
42547               d3_event.preventDefault();
42548               d3_event.stopPropagation();
42549               _disableSpace = false;
42550               select(window).on('keyup.space-block', null);
42551             }); // get the current mouse position
42552
42553             var loc = context.map().mouse() || // or the map center if the mouse has never entered the map
42554             context.projection(context.map().center());
42555             click(d3_event, loc);
42556           }
42557
42558           function backspace(d3_event) {
42559             d3_event.preventDefault();
42560             dispatch.call('undo');
42561           }
42562
42563           function del(d3_event) {
42564             d3_event.preventDefault();
42565             dispatch.call('cancel');
42566           }
42567
42568           function ret(d3_event) {
42569             d3_event.preventDefault();
42570             dispatch.call('finish');
42571           }
42572
42573           function behavior(selection) {
42574             context.install(_hover);
42575             context.install(_edit);
42576             _downPointer = null;
42577             keybinding.on('⌫', backspace).on('⌦', del).on('⎋', ret).on('↩', ret).on('space', space).on('⌥space', space);
42578             selection.on('mouseenter.draw', mouseenter).on('mouseleave.draw', mouseleave).on(_pointerPrefix + 'down.draw', pointerdown).on(_pointerPrefix + 'move.draw', pointermove);
42579             select(window).on(_pointerPrefix + 'up.draw', pointerup, true).on('pointercancel.draw', pointercancel, true);
42580             select(document).call(keybinding);
42581             return behavior;
42582           }
42583
42584           behavior.off = function (selection) {
42585             context.ui().sidebar.hover.cancel();
42586             context.uninstall(_hover);
42587             context.uninstall(_edit);
42588             selection.on('mouseenter.draw', null).on('mouseleave.draw', null).on(_pointerPrefix + 'down.draw', null).on(_pointerPrefix + 'move.draw', null);
42589             select(window).on(_pointerPrefix + 'up.draw', null).on('pointercancel.draw', null); // note: keyup.space-block, click.draw-block should remain
42590
42591             select(document).call(keybinding.unbind);
42592           };
42593
42594           behavior.hover = function () {
42595             return _hover;
42596           };
42597
42598           return utilRebind(behavior, dispatch, 'on');
42599         }
42600
42601         function initRange(domain, range) {
42602           switch (arguments.length) {
42603             case 0:
42604               break;
42605
42606             case 1:
42607               this.range(domain);
42608               break;
42609
42610             default:
42611               this.range(range).domain(domain);
42612               break;
42613           }
42614
42615           return this;
42616         }
42617
42618         function constants(x) {
42619           return function () {
42620             return x;
42621           };
42622         }
42623
42624         function number(x) {
42625           return +x;
42626         }
42627
42628         var unit = [0, 1];
42629         function identity$1(x) {
42630           return x;
42631         }
42632
42633         function normalize(a, b) {
42634           return (b -= a = +a) ? function (x) {
42635             return (x - a) / b;
42636           } : constants(isNaN(b) ? NaN : 0.5);
42637         }
42638
42639         function clamper(a, b) {
42640           var t;
42641           if (a > b) t = a, a = b, b = t;
42642           return function (x) {
42643             return Math.max(a, Math.min(b, x));
42644           };
42645         } // normalize(a, b)(x) takes a domain value x in [a,b] and returns the corresponding parameter t in [0,1].
42646         // interpolate(a, b)(t) takes a parameter t in [0,1] and returns the corresponding range value x in [a,b].
42647
42648
42649         function bimap(domain, range, interpolate) {
42650           var d0 = domain[0],
42651               d1 = domain[1],
42652               r0 = range[0],
42653               r1 = range[1];
42654           if (d1 < d0) d0 = normalize(d1, d0), r0 = interpolate(r1, r0);else d0 = normalize(d0, d1), r0 = interpolate(r0, r1);
42655           return function (x) {
42656             return r0(d0(x));
42657           };
42658         }
42659
42660         function polymap(domain, range, interpolate) {
42661           var j = Math.min(domain.length, range.length) - 1,
42662               d = new Array(j),
42663               r = new Array(j),
42664               i = -1; // Reverse descending domains.
42665
42666           if (domain[j] < domain[0]) {
42667             domain = domain.slice().reverse();
42668             range = range.slice().reverse();
42669           }
42670
42671           while (++i < j) {
42672             d[i] = normalize(domain[i], domain[i + 1]);
42673             r[i] = interpolate(range[i], range[i + 1]);
42674           }
42675
42676           return function (x) {
42677             var i = bisectRight(domain, x, 1, j) - 1;
42678             return r[i](d[i](x));
42679           };
42680         }
42681
42682         function copy(source, target) {
42683           return target.domain(source.domain()).range(source.range()).interpolate(source.interpolate()).clamp(source.clamp()).unknown(source.unknown());
42684         }
42685         function transformer() {
42686           var domain = unit,
42687               range = unit,
42688               interpolate = interpolate$1,
42689               transform,
42690               untransform,
42691               unknown,
42692               clamp = identity$1,
42693               piecewise,
42694               output,
42695               input;
42696
42697           function rescale() {
42698             var n = Math.min(domain.length, range.length);
42699             if (clamp !== identity$1) clamp = clamper(domain[0], domain[n - 1]);
42700             piecewise = n > 2 ? polymap : bimap;
42701             output = input = null;
42702             return scale;
42703           }
42704
42705           function scale(x) {
42706             return x == null || isNaN(x = +x) ? unknown : (output || (output = piecewise(domain.map(transform), range, interpolate)))(transform(clamp(x)));
42707           }
42708
42709           scale.invert = function (y) {
42710             return clamp(untransform((input || (input = piecewise(range, domain.map(transform), d3_interpolateNumber)))(y)));
42711           };
42712
42713           scale.domain = function (_) {
42714             return arguments.length ? (domain = Array.from(_, number), rescale()) : domain.slice();
42715           };
42716
42717           scale.range = function (_) {
42718             return arguments.length ? (range = Array.from(_), rescale()) : range.slice();
42719           };
42720
42721           scale.rangeRound = function (_) {
42722             return range = Array.from(_), interpolate = interpolateRound, rescale();
42723           };
42724
42725           scale.clamp = function (_) {
42726             return arguments.length ? (clamp = _ ? true : identity$1, rescale()) : clamp !== identity$1;
42727           };
42728
42729           scale.interpolate = function (_) {
42730             return arguments.length ? (interpolate = _, rescale()) : interpolate;
42731           };
42732
42733           scale.unknown = function (_) {
42734             return arguments.length ? (unknown = _, scale) : unknown;
42735           };
42736
42737           return function (t, u) {
42738             transform = t, untransform = u;
42739             return rescale();
42740           };
42741         }
42742         function continuous() {
42743           return transformer()(identity$1, identity$1);
42744         }
42745
42746         function formatDecimal (x) {
42747           return Math.abs(x = Math.round(x)) >= 1e21 ? x.toLocaleString("en").replace(/,/g, "") : x.toString(10);
42748         } // Computes the decimal coefficient and exponent of the specified number x with
42749         // significant digits p, where x is positive and p is in [1, 21] or undefined.
42750         // For example, formatDecimalParts(1.23) returns ["123", 0].
42751
42752         function formatDecimalParts(x, p) {
42753           if ((i = (x = p ? x.toExponential(p - 1) : x.toExponential()).indexOf("e")) < 0) return null; // NaN, ±Infinity
42754
42755           var i,
42756               coefficient = x.slice(0, i); // The string returned by toExponential either has the form \d\.\d+e[-+]\d+
42757           // (e.g., 1.2e+3) or the form \de[-+]\d+ (e.g., 1e+3).
42758
42759           return [coefficient.length > 1 ? coefficient[0] + coefficient.slice(2) : coefficient, +x.slice(i + 1)];
42760         }
42761
42762         function exponent (x) {
42763           return x = formatDecimalParts(Math.abs(x)), x ? x[1] : NaN;
42764         }
42765
42766         function formatGroup (grouping, thousands) {
42767           return function (value, width) {
42768             var i = value.length,
42769                 t = [],
42770                 j = 0,
42771                 g = grouping[0],
42772                 length = 0;
42773
42774             while (i > 0 && g > 0) {
42775               if (length + g + 1 > width) g = Math.max(1, width - length);
42776               t.push(value.substring(i -= g, i + g));
42777               if ((length += g + 1) > width) break;
42778               g = grouping[j = (j + 1) % grouping.length];
42779             }
42780
42781             return t.reverse().join(thousands);
42782           };
42783         }
42784
42785         function formatNumerals (numerals) {
42786           return function (value) {
42787             return value.replace(/[0-9]/g, function (i) {
42788               return numerals[+i];
42789             });
42790           };
42791         }
42792
42793         // [[fill]align][sign][symbol][0][width][,][.precision][~][type]
42794         var re = /^(?:(.)?([<>=^]))?([+\-( ])?([$#])?(0)?(\d+)?(,)?(\.\d+)?(~)?([a-z%])?$/i;
42795         function formatSpecifier(specifier) {
42796           if (!(match = re.exec(specifier))) throw new Error("invalid format: " + specifier);
42797           var match;
42798           return new FormatSpecifier({
42799             fill: match[1],
42800             align: match[2],
42801             sign: match[3],
42802             symbol: match[4],
42803             zero: match[5],
42804             width: match[6],
42805             comma: match[7],
42806             precision: match[8] && match[8].slice(1),
42807             trim: match[9],
42808             type: match[10]
42809           });
42810         }
42811         formatSpecifier.prototype = FormatSpecifier.prototype; // instanceof
42812
42813         function FormatSpecifier(specifier) {
42814           this.fill = specifier.fill === undefined ? " " : specifier.fill + "";
42815           this.align = specifier.align === undefined ? ">" : specifier.align + "";
42816           this.sign = specifier.sign === undefined ? "-" : specifier.sign + "";
42817           this.symbol = specifier.symbol === undefined ? "" : specifier.symbol + "";
42818           this.zero = !!specifier.zero;
42819           this.width = specifier.width === undefined ? undefined : +specifier.width;
42820           this.comma = !!specifier.comma;
42821           this.precision = specifier.precision === undefined ? undefined : +specifier.precision;
42822           this.trim = !!specifier.trim;
42823           this.type = specifier.type === undefined ? "" : specifier.type + "";
42824         }
42825
42826         FormatSpecifier.prototype.toString = function () {
42827           return this.fill + this.align + this.sign + this.symbol + (this.zero ? "0" : "") + (this.width === undefined ? "" : Math.max(1, this.width | 0)) + (this.comma ? "," : "") + (this.precision === undefined ? "" : "." + Math.max(0, this.precision | 0)) + (this.trim ? "~" : "") + this.type;
42828         };
42829
42830         // Trims insignificant zeros, e.g., replaces 1.2000k with 1.2k.
42831         function formatTrim (s) {
42832           out: for (var n = s.length, i = 1, i0 = -1, i1; i < n; ++i) {
42833             switch (s[i]) {
42834               case ".":
42835                 i0 = i1 = i;
42836                 break;
42837
42838               case "0":
42839                 if (i0 === 0) i0 = i;
42840                 i1 = i;
42841                 break;
42842
42843               default:
42844                 if (!+s[i]) break out;
42845                 if (i0 > 0) i0 = 0;
42846                 break;
42847             }
42848           }
42849
42850           return i0 > 0 ? s.slice(0, i0) + s.slice(i1 + 1) : s;
42851         }
42852
42853         var nativeToPrecision = 1.0.toPrecision;
42854
42855         var FORCED$1 = fails(function () {
42856           // IE7-
42857           return nativeToPrecision.call(1, undefined) !== '1';
42858         }) || !fails(function () {
42859           // V8 ~ Android 4.3-
42860           nativeToPrecision.call({});
42861         });
42862
42863         // `Number.prototype.toPrecision` method
42864         // https://tc39.es/ecma262/#sec-number.prototype.toprecision
42865         _export({ target: 'Number', proto: true, forced: FORCED$1 }, {
42866           toPrecision: function toPrecision(precision) {
42867             return precision === undefined
42868               ? nativeToPrecision.call(thisNumberValue(this))
42869               : nativeToPrecision.call(thisNumberValue(this), precision);
42870           }
42871         });
42872
42873         var prefixExponent;
42874         function formatPrefixAuto (x, p) {
42875           var d = formatDecimalParts(x, p);
42876           if (!d) return x + "";
42877           var coefficient = d[0],
42878               exponent = d[1],
42879               i = exponent - (prefixExponent = Math.max(-8, Math.min(8, Math.floor(exponent / 3))) * 3) + 1,
42880               n = coefficient.length;
42881           return i === n ? coefficient : i > n ? coefficient + new Array(i - n + 1).join("0") : i > 0 ? coefficient.slice(0, i) + "." + coefficient.slice(i) : "0." + new Array(1 - i).join("0") + formatDecimalParts(x, Math.max(0, p + i - 1))[0]; // less than 1y!
42882         }
42883
42884         function formatRounded (x, p) {
42885           var d = formatDecimalParts(x, p);
42886           if (!d) return x + "";
42887           var coefficient = d[0],
42888               exponent = d[1];
42889           return exponent < 0 ? "0." + new Array(-exponent).join("0") + coefficient : coefficient.length > exponent + 1 ? coefficient.slice(0, exponent + 1) + "." + coefficient.slice(exponent + 1) : coefficient + new Array(exponent - coefficient.length + 2).join("0");
42890         }
42891
42892         var formatTypes = {
42893           "%": function _(x, p) {
42894             return (x * 100).toFixed(p);
42895           },
42896           "b": function b(x) {
42897             return Math.round(x).toString(2);
42898           },
42899           "c": function c(x) {
42900             return x + "";
42901           },
42902           "d": formatDecimal,
42903           "e": function e(x, p) {
42904             return x.toExponential(p);
42905           },
42906           "f": function f(x, p) {
42907             return x.toFixed(p);
42908           },
42909           "g": function g(x, p) {
42910             return x.toPrecision(p);
42911           },
42912           "o": function o(x) {
42913             return Math.round(x).toString(8);
42914           },
42915           "p": function p(x, _p) {
42916             return formatRounded(x * 100, _p);
42917           },
42918           "r": formatRounded,
42919           "s": formatPrefixAuto,
42920           "X": function X(x) {
42921             return Math.round(x).toString(16).toUpperCase();
42922           },
42923           "x": function x(_x) {
42924             return Math.round(_x).toString(16);
42925           }
42926         };
42927
42928         function identity (x) {
42929           return x;
42930         }
42931
42932         var map$1 = Array.prototype.map,
42933             prefixes = ["y", "z", "a", "f", "p", "n", "µ", "m", "", "k", "M", "G", "T", "P", "E", "Z", "Y"];
42934         function formatLocale (locale) {
42935           var group = locale.grouping === undefined || locale.thousands === undefined ? identity : formatGroup(map$1.call(locale.grouping, Number), locale.thousands + ""),
42936               currencyPrefix = locale.currency === undefined ? "" : locale.currency[0] + "",
42937               currencySuffix = locale.currency === undefined ? "" : locale.currency[1] + "",
42938               decimal = locale.decimal === undefined ? "." : locale.decimal + "",
42939               numerals = locale.numerals === undefined ? identity : formatNumerals(map$1.call(locale.numerals, String)),
42940               percent = locale.percent === undefined ? "%" : locale.percent + "",
42941               minus = locale.minus === undefined ? "−" : locale.minus + "",
42942               nan = locale.nan === undefined ? "NaN" : locale.nan + "";
42943
42944           function newFormat(specifier) {
42945             specifier = formatSpecifier(specifier);
42946             var fill = specifier.fill,
42947                 align = specifier.align,
42948                 sign = specifier.sign,
42949                 symbol = specifier.symbol,
42950                 zero = specifier.zero,
42951                 width = specifier.width,
42952                 comma = specifier.comma,
42953                 precision = specifier.precision,
42954                 trim = specifier.trim,
42955                 type = specifier.type; // The "n" type is an alias for ",g".
42956
42957             if (type === "n") comma = true, type = "g"; // The "" type, and any invalid type, is an alias for ".12~g".
42958             else if (!formatTypes[type]) precision === undefined && (precision = 12), trim = true, type = "g"; // If zero fill is specified, padding goes after sign and before digits.
42959
42960             if (zero || fill === "0" && align === "=") zero = true, fill = "0", align = "="; // Compute the prefix and suffix.
42961             // For SI-prefix, the suffix is lazily computed.
42962
42963             var prefix = symbol === "$" ? currencyPrefix : symbol === "#" && /[boxX]/.test(type) ? "0" + type.toLowerCase() : "",
42964                 suffix = symbol === "$" ? currencySuffix : /[%p]/.test(type) ? percent : ""; // What format function should we use?
42965             // Is this an integer type?
42966             // Can this type generate exponential notation?
42967
42968             var formatType = formatTypes[type],
42969                 maybeSuffix = /[defgprs%]/.test(type); // Set the default precision if not specified,
42970             // or clamp the specified precision to the supported range.
42971             // For significant precision, it must be in [1, 21].
42972             // For fixed precision, it must be in [0, 20].
42973
42974             precision = precision === undefined ? 6 : /[gprs]/.test(type) ? Math.max(1, Math.min(21, precision)) : Math.max(0, Math.min(20, precision));
42975
42976             function format(value) {
42977               var valuePrefix = prefix,
42978                   valueSuffix = suffix,
42979                   i,
42980                   n,
42981                   c;
42982
42983               if (type === "c") {
42984                 valueSuffix = formatType(value) + valueSuffix;
42985                 value = "";
42986               } else {
42987                 value = +value; // Determine the sign. -0 is not less than 0, but 1 / -0 is!
42988
42989                 var valueNegative = value < 0 || 1 / value < 0; // Perform the initial formatting.
42990
42991                 value = isNaN(value) ? nan : formatType(Math.abs(value), precision); // Trim insignificant zeros.
42992
42993                 if (trim) value = formatTrim(value); // If a negative value rounds to zero after formatting, and no explicit positive sign is requested, hide the sign.
42994
42995                 if (valueNegative && +value === 0 && sign !== "+") valueNegative = false; // Compute the prefix and suffix.
42996
42997                 valuePrefix = (valueNegative ? sign === "(" ? sign : minus : sign === "-" || sign === "(" ? "" : sign) + valuePrefix;
42998                 valueSuffix = (type === "s" ? prefixes[8 + prefixExponent / 3] : "") + valueSuffix + (valueNegative && sign === "(" ? ")" : ""); // Break the formatted value into the integer “value” part that can be
42999                 // grouped, and fractional or exponential “suffix” part that is not.
43000
43001                 if (maybeSuffix) {
43002                   i = -1, n = value.length;
43003
43004                   while (++i < n) {
43005                     if (c = value.charCodeAt(i), 48 > c || c > 57) {
43006                       valueSuffix = (c === 46 ? decimal + value.slice(i + 1) : value.slice(i)) + valueSuffix;
43007                       value = value.slice(0, i);
43008                       break;
43009                     }
43010                   }
43011                 }
43012               } // If the fill character is not "0", grouping is applied before padding.
43013
43014
43015               if (comma && !zero) value = group(value, Infinity); // Compute the padding.
43016
43017               var length = valuePrefix.length + value.length + valueSuffix.length,
43018                   padding = length < width ? new Array(width - length + 1).join(fill) : ""; // If the fill character is "0", grouping is applied after padding.
43019
43020               if (comma && zero) value = group(padding + value, padding.length ? width - valueSuffix.length : Infinity), padding = ""; // Reconstruct the final output based on the desired alignment.
43021
43022               switch (align) {
43023                 case "<":
43024                   value = valuePrefix + value + valueSuffix + padding;
43025                   break;
43026
43027                 case "=":
43028                   value = valuePrefix + padding + value + valueSuffix;
43029                   break;
43030
43031                 case "^":
43032                   value = padding.slice(0, length = padding.length >> 1) + valuePrefix + value + valueSuffix + padding.slice(length);
43033                   break;
43034
43035                 default:
43036                   value = padding + valuePrefix + value + valueSuffix;
43037                   break;
43038               }
43039
43040               return numerals(value);
43041             }
43042
43043             format.toString = function () {
43044               return specifier + "";
43045             };
43046
43047             return format;
43048           }
43049
43050           function formatPrefix(specifier, value) {
43051             var f = newFormat((specifier = formatSpecifier(specifier), specifier.type = "f", specifier)),
43052                 e = Math.max(-8, Math.min(8, Math.floor(exponent(value) / 3))) * 3,
43053                 k = Math.pow(10, -e),
43054                 prefix = prefixes[8 + e / 3];
43055             return function (value) {
43056               return f(k * value) + prefix;
43057             };
43058           }
43059
43060           return {
43061             format: newFormat,
43062             formatPrefix: formatPrefix
43063           };
43064         }
43065
43066         var locale;
43067         var format;
43068         var formatPrefix;
43069         defaultLocale({
43070           thousands: ",",
43071           grouping: [3],
43072           currency: ["$", ""]
43073         });
43074         function defaultLocale(definition) {
43075           locale = formatLocale(definition);
43076           format = locale.format;
43077           formatPrefix = locale.formatPrefix;
43078           return locale;
43079         }
43080
43081         function precisionFixed (step) {
43082           return Math.max(0, -exponent(Math.abs(step)));
43083         }
43084
43085         function precisionPrefix (step, value) {
43086           return Math.max(0, Math.max(-8, Math.min(8, Math.floor(exponent(value) / 3))) * 3 - exponent(Math.abs(step)));
43087         }
43088
43089         function precisionRound (step, max) {
43090           step = Math.abs(step), max = Math.abs(max) - step;
43091           return Math.max(0, exponent(max) - exponent(step)) + 1;
43092         }
43093
43094         function tickFormat(start, stop, count, specifier) {
43095           var step = tickStep(start, stop, count),
43096               precision;
43097           specifier = formatSpecifier(specifier == null ? ",f" : specifier);
43098
43099           switch (specifier.type) {
43100             case "s":
43101               {
43102                 var value = Math.max(Math.abs(start), Math.abs(stop));
43103                 if (specifier.precision == null && !isNaN(precision = precisionPrefix(step, value))) specifier.precision = precision;
43104                 return formatPrefix(specifier, value);
43105               }
43106
43107             case "":
43108             case "e":
43109             case "g":
43110             case "p":
43111             case "r":
43112               {
43113                 if (specifier.precision == null && !isNaN(precision = precisionRound(step, Math.max(Math.abs(start), Math.abs(stop))))) specifier.precision = precision - (specifier.type === "e");
43114                 break;
43115               }
43116
43117             case "f":
43118             case "%":
43119               {
43120                 if (specifier.precision == null && !isNaN(precision = precisionFixed(step))) specifier.precision = precision - (specifier.type === "%") * 2;
43121                 break;
43122               }
43123           }
43124
43125           return format(specifier);
43126         }
43127
43128         function linearish(scale) {
43129           var domain = scale.domain;
43130
43131           scale.ticks = function (count) {
43132             var d = domain();
43133             return ticks(d[0], d[d.length - 1], count == null ? 10 : count);
43134           };
43135
43136           scale.tickFormat = function (count, specifier) {
43137             var d = domain();
43138             return tickFormat(d[0], d[d.length - 1], count == null ? 10 : count, specifier);
43139           };
43140
43141           scale.nice = function (count) {
43142             if (count == null) count = 10;
43143             var d = domain();
43144             var i0 = 0;
43145             var i1 = d.length - 1;
43146             var start = d[i0];
43147             var stop = d[i1];
43148             var prestep;
43149             var step;
43150             var maxIter = 10;
43151
43152             if (stop < start) {
43153               step = start, start = stop, stop = step;
43154               step = i0, i0 = i1, i1 = step;
43155             }
43156
43157             while (maxIter-- > 0) {
43158               step = tickIncrement(start, stop, count);
43159
43160               if (step === prestep) {
43161                 d[i0] = start;
43162                 d[i1] = stop;
43163                 return domain(d);
43164               } else if (step > 0) {
43165                 start = Math.floor(start / step) * step;
43166                 stop = Math.ceil(stop / step) * step;
43167               } else if (step < 0) {
43168                 start = Math.ceil(start * step) / step;
43169                 stop = Math.floor(stop * step) / step;
43170               } else {
43171                 break;
43172               }
43173
43174               prestep = step;
43175             }
43176
43177             return scale;
43178           };
43179
43180           return scale;
43181         }
43182         function linear() {
43183           var scale = continuous();
43184
43185           scale.copy = function () {
43186             return copy(scale, linear());
43187           };
43188
43189           initRange.apply(scale, arguments);
43190           return linearish(scale);
43191         }
43192
43193         // eslint-disable-next-line es/no-math-expm1 -- safe
43194         var $expm1 = Math.expm1;
43195         var exp$1 = Math.exp;
43196
43197         // `Math.expm1` method implementation
43198         // https://tc39.es/ecma262/#sec-math.expm1
43199         var mathExpm1 = (!$expm1
43200           // Old FF bug
43201           || $expm1(10) > 22025.465794806719 || $expm1(10) < 22025.4657948067165168
43202           // Tor Browser bug
43203           || $expm1(-2e-17) != -2e-17
43204         ) ? function expm1(x) {
43205           return (x = +x) == 0 ? x : x > -1e-6 && x < 1e-6 ? x + x * x / 2 : exp$1(x) - 1;
43206         } : $expm1;
43207
43208         function quantize() {
43209           var x0 = 0,
43210               x1 = 1,
43211               n = 1,
43212               domain = [0.5],
43213               range = [0, 1],
43214               unknown;
43215
43216           function scale(x) {
43217             return x != null && x <= x ? range[bisectRight(domain, x, 0, n)] : unknown;
43218           }
43219
43220           function rescale() {
43221             var i = -1;
43222             domain = new Array(n);
43223
43224             while (++i < n) {
43225               domain[i] = ((i + 1) * x1 - (i - n) * x0) / (n + 1);
43226             }
43227
43228             return scale;
43229           }
43230
43231           scale.domain = function (_) {
43232             var _ref, _ref2;
43233
43234             return arguments.length ? ((_ref = _, _ref2 = _slicedToArray(_ref, 2), x0 = _ref2[0], x1 = _ref2[1], _ref), x0 = +x0, x1 = +x1, rescale()) : [x0, x1];
43235           };
43236
43237           scale.range = function (_) {
43238             return arguments.length ? (n = (range = Array.from(_)).length - 1, rescale()) : range.slice();
43239           };
43240
43241           scale.invertExtent = function (y) {
43242             var i = range.indexOf(y);
43243             return i < 0 ? [NaN, NaN] : i < 1 ? [x0, domain[0]] : i >= n ? [domain[n - 1], x1] : [domain[i - 1], domain[i]];
43244           };
43245
43246           scale.unknown = function (_) {
43247             return arguments.length ? (unknown = _, scale) : scale;
43248           };
43249
43250           scale.thresholds = function () {
43251             return domain.slice();
43252           };
43253
43254           scale.copy = function () {
43255             return quantize().domain([x0, x1]).range(range).unknown(unknown);
43256           };
43257
43258           return initRange.apply(linearish(scale), arguments);
43259         }
43260
43261         // https://github.com/tc39/proposal-string-pad-start-end
43262
43263
43264
43265
43266         var ceil = Math.ceil;
43267
43268         // `String.prototype.{ padStart, padEnd }` methods implementation
43269         var createMethod = function (IS_END) {
43270           return function ($this, maxLength, fillString) {
43271             var S = String(requireObjectCoercible($this));
43272             var stringLength = S.length;
43273             var fillStr = fillString === undefined ? ' ' : String(fillString);
43274             var intMaxLength = toLength(maxLength);
43275             var fillLen, stringFiller;
43276             if (intMaxLength <= stringLength || fillStr == '') return S;
43277             fillLen = intMaxLength - stringLength;
43278             stringFiller = stringRepeat.call(fillStr, ceil(fillLen / fillStr.length));
43279             if (stringFiller.length > fillLen) stringFiller = stringFiller.slice(0, fillLen);
43280             return IS_END ? S + stringFiller : stringFiller + S;
43281           };
43282         };
43283
43284         var stringPad = {
43285           // `String.prototype.padStart` method
43286           // https://tc39.es/ecma262/#sec-string.prototype.padstart
43287           start: createMethod(false),
43288           // `String.prototype.padEnd` method
43289           // https://tc39.es/ecma262/#sec-string.prototype.padend
43290           end: createMethod(true)
43291         };
43292
43293         var padStart = stringPad.start;
43294
43295         var abs$1 = Math.abs;
43296         var DatePrototype = Date.prototype;
43297         var getTime = DatePrototype.getTime;
43298         var nativeDateToISOString = DatePrototype.toISOString;
43299
43300         // `Date.prototype.toISOString` method implementation
43301         // https://tc39.es/ecma262/#sec-date.prototype.toisostring
43302         // PhantomJS / old WebKit fails here:
43303         var dateToIsoString = (fails(function () {
43304           return nativeDateToISOString.call(new Date(-5e13 - 1)) != '0385-07-25T07:06:39.999Z';
43305         }) || !fails(function () {
43306           nativeDateToISOString.call(new Date(NaN));
43307         })) ? function toISOString() {
43308           if (!isFinite(getTime.call(this))) throw RangeError('Invalid time value');
43309           var date = this;
43310           var year = date.getUTCFullYear();
43311           var milliseconds = date.getUTCMilliseconds();
43312           var sign = year < 0 ? '-' : year > 9999 ? '+' : '';
43313           return sign + padStart(abs$1(year), sign ? 6 : 4, 0) +
43314             '-' + padStart(date.getUTCMonth() + 1, 2, 0) +
43315             '-' + padStart(date.getUTCDate(), 2, 0) +
43316             'T' + padStart(date.getUTCHours(), 2, 0) +
43317             ':' + padStart(date.getUTCMinutes(), 2, 0) +
43318             ':' + padStart(date.getUTCSeconds(), 2, 0) +
43319             '.' + padStart(milliseconds, 3, 0) +
43320             'Z';
43321         } : nativeDateToISOString;
43322
43323         // `Date.prototype.toISOString` method
43324         // https://tc39.es/ecma262/#sec-date.prototype.toisostring
43325         // PhantomJS / old WebKit has a broken implementations
43326         _export({ target: 'Date', proto: true, forced: Date.prototype.toISOString !== dateToIsoString }, {
43327           toISOString: dateToIsoString
43328         });
43329
43330         function behaviorBreathe() {
43331           var duration = 800;
43332           var steps = 4;
43333           var selector = '.selected.shadow, .selected .shadow';
43334
43335           var _selected = select(null);
43336
43337           var _classed = '';
43338           var _params = {};
43339           var _done = false;
43340
43341           var _timer;
43342
43343           function ratchetyInterpolator(a, b, steps, units) {
43344             a = parseFloat(a);
43345             b = parseFloat(b);
43346             var sample = quantize().domain([0, 1]).range(d3_quantize(d3_interpolateNumber(a, b), steps));
43347             return function (t) {
43348               return String(sample(t)) + (units || '');
43349             };
43350           }
43351
43352           function reset(selection) {
43353             selection.style('stroke-opacity', null).style('stroke-width', null).style('fill-opacity', null).style('r', null);
43354           }
43355
43356           function setAnimationParams(transition, fromTo) {
43357             var toFrom = fromTo === 'from' ? 'to' : 'from';
43358             transition.styleTween('stroke-opacity', function (d) {
43359               return ratchetyInterpolator(_params[d.id][toFrom].opacity, _params[d.id][fromTo].opacity, steps);
43360             }).styleTween('stroke-width', function (d) {
43361               return ratchetyInterpolator(_params[d.id][toFrom].width, _params[d.id][fromTo].width, steps, 'px');
43362             }).styleTween('fill-opacity', function (d) {
43363               return ratchetyInterpolator(_params[d.id][toFrom].opacity, _params[d.id][fromTo].opacity, steps);
43364             }).styleTween('r', function (d) {
43365               return ratchetyInterpolator(_params[d.id][toFrom].width, _params[d.id][fromTo].width, steps, 'px');
43366             });
43367           }
43368
43369           function calcAnimationParams(selection) {
43370             selection.call(reset).each(function (d) {
43371               var s = select(this);
43372               var tag = s.node().tagName;
43373               var p = {
43374                 'from': {},
43375                 'to': {}
43376               };
43377               var opacity;
43378               var width; // determine base opacity and width
43379
43380               if (tag === 'circle') {
43381                 opacity = parseFloat(s.style('fill-opacity') || 0.5);
43382                 width = parseFloat(s.style('r') || 15.5);
43383               } else {
43384                 opacity = parseFloat(s.style('stroke-opacity') || 0.7);
43385                 width = parseFloat(s.style('stroke-width') || 10);
43386               } // calculate from/to interpolation params..
43387
43388
43389               p.tag = tag;
43390               p.from.opacity = opacity * 0.6;
43391               p.to.opacity = opacity * 1.25;
43392               p.from.width = width * 0.7;
43393               p.to.width = width * (tag === 'circle' ? 1.5 : 1);
43394               _params[d.id] = p;
43395             });
43396           }
43397
43398           function run(surface, fromTo) {
43399             var toFrom = fromTo === 'from' ? 'to' : 'from';
43400             var currSelected = surface.selectAll(selector);
43401             var currClassed = surface.attr('class');
43402
43403             if (_done || currSelected.empty()) {
43404               _selected.call(reset);
43405
43406               _selected = select(null);
43407               return;
43408             }
43409
43410             if (!fastDeepEqual(currSelected.data(), _selected.data()) || currClassed !== _classed) {
43411               _selected.call(reset);
43412
43413               _classed = currClassed;
43414               _selected = currSelected.call(calcAnimationParams);
43415             }
43416
43417             var didCallNextRun = false;
43418
43419             _selected.transition().duration(duration).call(setAnimationParams, fromTo).on('end', function () {
43420               // `end` event is called for each selected element, but we want
43421               // it to run only once
43422               if (!didCallNextRun) {
43423                 surface.call(run, toFrom);
43424                 didCallNextRun = true;
43425               } // if entity was deselected, remove breathe styling
43426
43427
43428               if (!select(this).classed('selected')) {
43429                 reset(select(this));
43430               }
43431             });
43432           }
43433
43434           function behavior(surface) {
43435             _done = false;
43436             _timer = timer(function () {
43437               // wait for elements to actually become selected
43438               if (surface.selectAll(selector).empty()) {
43439                 return false;
43440               }
43441
43442               surface.call(run, 'from');
43443
43444               _timer.stop();
43445
43446               return true;
43447             }, 20);
43448           }
43449
43450           behavior.restartIfNeeded = function (surface) {
43451             if (_selected.empty()) {
43452               surface.call(run, 'from');
43453
43454               if (_timer) {
43455                 _timer.stop();
43456               }
43457             }
43458           };
43459
43460           behavior.off = function () {
43461             _done = true;
43462
43463             if (_timer) {
43464               _timer.stop();
43465             }
43466
43467             _selected.interrupt().call(reset);
43468           };
43469
43470           return behavior;
43471         }
43472
43473         /* Creates a keybinding behavior for an operation */
43474         function behaviorOperation(context) {
43475           var _operation;
43476
43477           function keypress(d3_event) {
43478             // prevent operations during low zoom selection
43479             if (!context.map().withinEditableZoom()) return;
43480             if (_operation.availableForKeypress && !_operation.availableForKeypress()) return;
43481             d3_event.preventDefault();
43482
43483             var disabled = _operation.disabled();
43484
43485             if (disabled) {
43486               context.ui().flash.duration(4000).iconName('#iD-operation-' + _operation.id).iconClass('operation disabled').label(_operation.tooltip)();
43487             } else {
43488               context.ui().flash.duration(2000).iconName('#iD-operation-' + _operation.id).iconClass('operation').label(_operation.annotation() || _operation.title)();
43489               if (_operation.point) _operation.point(null);
43490
43491               _operation();
43492             }
43493           }
43494
43495           function behavior() {
43496             if (_operation && _operation.available()) {
43497               context.keybinding().on(_operation.keys, keypress);
43498             }
43499
43500             return behavior;
43501           }
43502
43503           behavior.off = function () {
43504             context.keybinding().off(_operation.keys);
43505           };
43506
43507           behavior.which = function (_) {
43508             if (!arguments.length) return _operation;
43509             _operation = _;
43510             return behavior;
43511           };
43512
43513           return behavior;
43514         }
43515
43516         function operationCircularize(context, selectedIDs) {
43517           var _extent;
43518
43519           var _actions = selectedIDs.map(getAction).filter(Boolean);
43520
43521           var _amount = _actions.length === 1 ? 'single' : 'multiple';
43522
43523           var _coords = utilGetAllNodes(selectedIDs, context.graph()).map(function (n) {
43524             return n.loc;
43525           });
43526
43527           function getAction(entityID) {
43528             var entity = context.entity(entityID);
43529             if (entity.type !== 'way' || new Set(entity.nodes).size <= 1) return null;
43530
43531             if (!_extent) {
43532               _extent = entity.extent(context.graph());
43533             } else {
43534               _extent = _extent.extend(entity.extent(context.graph()));
43535             }
43536
43537             return actionCircularize(entityID, context.projection);
43538           }
43539
43540           var operation = function operation() {
43541             if (!_actions.length) return;
43542
43543             var combinedAction = function combinedAction(graph, t) {
43544               _actions.forEach(function (action) {
43545                 if (!action.disabled(graph)) {
43546                   graph = action(graph, t);
43547                 }
43548               });
43549
43550               return graph;
43551             };
43552
43553             combinedAction.transitionable = true;
43554             context.perform(combinedAction, operation.annotation());
43555             window.setTimeout(function () {
43556               context.validator().validate();
43557             }, 300); // after any transition
43558           };
43559
43560           operation.available = function () {
43561             return _actions.length && selectedIDs.length === _actions.length;
43562           }; // don't cache this because the visible extent could change
43563
43564
43565           operation.disabled = function () {
43566             if (!_actions.length) return '';
43567
43568             var actionDisableds = _actions.map(function (action) {
43569               return action.disabled(context.graph());
43570             }).filter(Boolean);
43571
43572             if (actionDisableds.length === _actions.length) {
43573               // none of the features can be circularized
43574               if (new Set(actionDisableds).size > 1) {
43575                 return 'multiple_blockers';
43576               }
43577
43578               return actionDisableds[0];
43579             } else if (_extent.percentContainedIn(context.map().extent()) < 0.8) {
43580               return 'too_large';
43581             } else if (someMissing()) {
43582               return 'not_downloaded';
43583             } else if (selectedIDs.some(context.hasHiddenConnections)) {
43584               return 'connected_to_hidden';
43585             }
43586
43587             return false;
43588
43589             function someMissing() {
43590               if (context.inIntro()) return false;
43591               var osm = context.connection();
43592
43593               if (osm) {
43594                 var missing = _coords.filter(function (loc) {
43595                   return !osm.isDataLoaded(loc);
43596                 });
43597
43598                 if (missing.length) {
43599                   missing.forEach(function (loc) {
43600                     context.loadTileAtLoc(loc);
43601                   });
43602                   return true;
43603                 }
43604               }
43605
43606               return false;
43607             }
43608           };
43609
43610           operation.tooltip = function () {
43611             var disable = operation.disabled();
43612             return disable ? _t('operations.circularize.' + disable + '.' + _amount) : _t('operations.circularize.description.' + _amount);
43613           };
43614
43615           operation.annotation = function () {
43616             return _t('operations.circularize.annotation.feature', {
43617               n: _actions.length
43618             });
43619           };
43620
43621           operation.id = 'circularize';
43622           operation.keys = [_t('operations.circularize.key')];
43623           operation.title = _t('operations.circularize.title');
43624           operation.behavior = behaviorOperation(context).which(operation);
43625           return operation;
43626         }
43627
43628         // For example, ⌘Z -> Ctrl+Z
43629
43630         var uiCmd = function uiCmd(code) {
43631           var detected = utilDetect();
43632
43633           if (detected.os === 'mac') {
43634             return code;
43635           }
43636
43637           if (detected.os === 'win') {
43638             if (code === '⌘⇧Z') return 'Ctrl+Y';
43639           }
43640
43641           var result = '',
43642               replacements = {
43643             '⌘': 'Ctrl',
43644             '⇧': 'Shift',
43645             '⌥': 'Alt',
43646             '⌫': 'Backspace',
43647             '⌦': 'Delete'
43648           };
43649
43650           for (var i = 0; i < code.length; i++) {
43651             if (code[i] in replacements) {
43652               result += replacements[code[i]] + (i < code.length - 1 ? '+' : '');
43653             } else {
43654               result += code[i];
43655             }
43656           }
43657
43658           return result;
43659         }; // return a display-focused string for a given keyboard code
43660
43661         uiCmd.display = function (code) {
43662           if (code.length !== 1) return code;
43663           var detected = utilDetect();
43664           var mac = detected.os === 'mac';
43665           var replacements = {
43666             '⌘': mac ? '⌘ ' + _t('shortcuts.key.cmd') : _t('shortcuts.key.ctrl'),
43667             '⇧': mac ? '⇧ ' + _t('shortcuts.key.shift') : _t('shortcuts.key.shift'),
43668             '⌥': mac ? '⌥ ' + _t('shortcuts.key.option') : _t('shortcuts.key.alt'),
43669             '⌃': mac ? '⌃ ' + _t('shortcuts.key.ctrl') : _t('shortcuts.key.ctrl'),
43670             '⌫': mac ? '⌫ ' + _t('shortcuts.key.delete') : _t('shortcuts.key.backspace'),
43671             '⌦': mac ? '⌦ ' + _t('shortcuts.key.del') : _t('shortcuts.key.del'),
43672             '↖': mac ? '↖ ' + _t('shortcuts.key.pgup') : _t('shortcuts.key.pgup'),
43673             '↘': mac ? '↘ ' + _t('shortcuts.key.pgdn') : _t('shortcuts.key.pgdn'),
43674             '⇞': mac ? '⇞ ' + _t('shortcuts.key.home') : _t('shortcuts.key.home'),
43675             '⇟': mac ? '⇟ ' + _t('shortcuts.key.end') : _t('shortcuts.key.end'),
43676             '↵': mac ? '⏎ ' + _t('shortcuts.key.return') : _t('shortcuts.key.enter'),
43677             '⎋': mac ? '⎋ ' + _t('shortcuts.key.esc') : _t('shortcuts.key.esc'),
43678             '☰': mac ? '☰ ' + _t('shortcuts.key.menu') : _t('shortcuts.key.menu')
43679           };
43680           return replacements[code] || code;
43681         };
43682
43683         function operationDelete(context, selectedIDs) {
43684           var multi = selectedIDs.length === 1 ? 'single' : 'multiple';
43685           var action = actionDeleteMultiple(selectedIDs);
43686           var nodes = utilGetAllNodes(selectedIDs, context.graph());
43687           var coords = nodes.map(function (n) {
43688             return n.loc;
43689           });
43690           var extent = utilTotalExtent(selectedIDs, context.graph());
43691
43692           var operation = function operation() {
43693             var nextSelectedID;
43694             var nextSelectedLoc;
43695
43696             if (selectedIDs.length === 1) {
43697               var id = selectedIDs[0];
43698               var entity = context.entity(id);
43699               var geometry = entity.geometry(context.graph());
43700               var parents = context.graph().parentWays(entity);
43701               var parent = parents[0]; // Select the next closest node in the way.
43702
43703               if (geometry === 'vertex') {
43704                 var nodes = parent.nodes;
43705                 var i = nodes.indexOf(id);
43706
43707                 if (i === 0) {
43708                   i++;
43709                 } else if (i === nodes.length - 1) {
43710                   i--;
43711                 } else {
43712                   var a = geoSphericalDistance(entity.loc, context.entity(nodes[i - 1]).loc);
43713                   var b = geoSphericalDistance(entity.loc, context.entity(nodes[i + 1]).loc);
43714                   i = a < b ? i - 1 : i + 1;
43715                 }
43716
43717                 nextSelectedID = nodes[i];
43718                 nextSelectedLoc = context.entity(nextSelectedID).loc;
43719               }
43720             }
43721
43722             context.perform(action, operation.annotation());
43723             context.validator().validate();
43724
43725             if (nextSelectedID && nextSelectedLoc) {
43726               if (context.hasEntity(nextSelectedID)) {
43727                 context.enter(modeSelect(context, [nextSelectedID]).follow(true));
43728               } else {
43729                 context.map().centerEase(nextSelectedLoc);
43730                 context.enter(modeBrowse(context));
43731               }
43732             } else {
43733               context.enter(modeBrowse(context));
43734             }
43735           };
43736
43737           operation.available = function () {
43738             return true;
43739           };
43740
43741           operation.disabled = function () {
43742             if (extent.percentContainedIn(context.map().extent()) < 0.8) {
43743               return 'too_large';
43744             } else if (someMissing()) {
43745               return 'not_downloaded';
43746             } else if (selectedIDs.some(context.hasHiddenConnections)) {
43747               return 'connected_to_hidden';
43748             } else if (selectedIDs.some(protectedMember)) {
43749               return 'part_of_relation';
43750             } else if (selectedIDs.some(incompleteRelation)) {
43751               return 'incomplete_relation';
43752             } else if (selectedIDs.some(hasWikidataTag)) {
43753               return 'has_wikidata_tag';
43754             }
43755
43756             return false;
43757
43758             function someMissing() {
43759               if (context.inIntro()) return false;
43760               var osm = context.connection();
43761
43762               if (osm) {
43763                 var missing = coords.filter(function (loc) {
43764                   return !osm.isDataLoaded(loc);
43765                 });
43766
43767                 if (missing.length) {
43768                   missing.forEach(function (loc) {
43769                     context.loadTileAtLoc(loc);
43770                   });
43771                   return true;
43772                 }
43773               }
43774
43775               return false;
43776             }
43777
43778             function hasWikidataTag(id) {
43779               var entity = context.entity(id);
43780               return entity.tags.wikidata && entity.tags.wikidata.trim().length > 0;
43781             }
43782
43783             function incompleteRelation(id) {
43784               var entity = context.entity(id);
43785               return entity.type === 'relation' && !entity.isComplete(context.graph());
43786             }
43787
43788             function protectedMember(id) {
43789               var entity = context.entity(id);
43790               if (entity.type !== 'way') return false;
43791               var parents = context.graph().parentRelations(entity);
43792
43793               for (var i = 0; i < parents.length; i++) {
43794                 var parent = parents[i];
43795                 var type = parent.tags.type;
43796                 var role = parent.memberById(id).role || 'outer';
43797
43798                 if (type === 'route' || type === 'boundary' || type === 'multipolygon' && role === 'outer') {
43799                   return true;
43800                 }
43801               }
43802
43803               return false;
43804             }
43805           };
43806
43807           operation.tooltip = function () {
43808             var disable = operation.disabled();
43809             return disable ? _t('operations.delete.' + disable + '.' + multi) : _t('operations.delete.description.' + multi);
43810           };
43811
43812           operation.annotation = function () {
43813             return selectedIDs.length === 1 ? _t('operations.delete.annotation.' + context.graph().geometry(selectedIDs[0])) : _t('operations.delete.annotation.feature', {
43814               n: selectedIDs.length
43815             });
43816           };
43817
43818           operation.id = 'delete';
43819           operation.keys = [uiCmd('⌘⌫'), uiCmd('⌘⌦'), uiCmd('⌦')];
43820           operation.title = _t('operations.delete.title');
43821           operation.behavior = behaviorOperation(context).which(operation);
43822           return operation;
43823         }
43824
43825         function operationOrthogonalize(context, selectedIDs) {
43826           var _extent;
43827
43828           var _type;
43829
43830           var _actions = selectedIDs.map(chooseAction).filter(Boolean);
43831
43832           var _amount = _actions.length === 1 ? 'single' : 'multiple';
43833
43834           var _coords = utilGetAllNodes(selectedIDs, context.graph()).map(function (n) {
43835             return n.loc;
43836           });
43837
43838           function chooseAction(entityID) {
43839             var entity = context.entity(entityID);
43840             var geometry = entity.geometry(context.graph());
43841
43842             if (!_extent) {
43843               _extent = entity.extent(context.graph());
43844             } else {
43845               _extent = _extent.extend(entity.extent(context.graph()));
43846             } // square a line/area
43847
43848
43849             if (entity.type === 'way' && new Set(entity.nodes).size > 2) {
43850               if (_type && _type !== 'feature') return null;
43851               _type = 'feature';
43852               return actionOrthogonalize(entityID, context.projection); // square a single vertex
43853             } else if (geometry === 'vertex') {
43854               if (_type && _type !== 'corner') return null;
43855               _type = 'corner';
43856               var graph = context.graph();
43857               var parents = graph.parentWays(entity);
43858
43859               if (parents.length === 1) {
43860                 var way = parents[0];
43861
43862                 if (way.nodes.indexOf(entityID) !== -1) {
43863                   return actionOrthogonalize(way.id, context.projection, entityID);
43864                 }
43865               }
43866             }
43867
43868             return null;
43869           }
43870
43871           var operation = function operation() {
43872             if (!_actions.length) return;
43873
43874             var combinedAction = function combinedAction(graph, t) {
43875               _actions.forEach(function (action) {
43876                 if (!action.disabled(graph)) {
43877                   graph = action(graph, t);
43878                 }
43879               });
43880
43881               return graph;
43882             };
43883
43884             combinedAction.transitionable = true;
43885             context.perform(combinedAction, operation.annotation());
43886             window.setTimeout(function () {
43887               context.validator().validate();
43888             }, 300); // after any transition
43889           };
43890
43891           operation.available = function () {
43892             return _actions.length && selectedIDs.length === _actions.length;
43893           }; // don't cache this because the visible extent could change
43894
43895
43896           operation.disabled = function () {
43897             if (!_actions.length) return '';
43898
43899             var actionDisableds = _actions.map(function (action) {
43900               return action.disabled(context.graph());
43901             }).filter(Boolean);
43902
43903             if (actionDisableds.length === _actions.length) {
43904               // none of the features can be squared
43905               if (new Set(actionDisableds).size > 1) {
43906                 return 'multiple_blockers';
43907               }
43908
43909               return actionDisableds[0];
43910             } else if (_extent && _extent.percentContainedIn(context.map().extent()) < 0.8) {
43911               return 'too_large';
43912             } else if (someMissing()) {
43913               return 'not_downloaded';
43914             } else if (selectedIDs.some(context.hasHiddenConnections)) {
43915               return 'connected_to_hidden';
43916             }
43917
43918             return false;
43919
43920             function someMissing() {
43921               if (context.inIntro()) return false;
43922               var osm = context.connection();
43923
43924               if (osm) {
43925                 var missing = _coords.filter(function (loc) {
43926                   return !osm.isDataLoaded(loc);
43927                 });
43928
43929                 if (missing.length) {
43930                   missing.forEach(function (loc) {
43931                     context.loadTileAtLoc(loc);
43932                   });
43933                   return true;
43934                 }
43935               }
43936
43937               return false;
43938             }
43939           };
43940
43941           operation.tooltip = function () {
43942             var disable = operation.disabled();
43943             return disable ? _t('operations.orthogonalize.' + disable + '.' + _amount) : _t('operations.orthogonalize.description.' + _type + '.' + _amount);
43944           };
43945
43946           operation.annotation = function () {
43947             return _t('operations.orthogonalize.annotation.' + _type, {
43948               n: _actions.length
43949             });
43950           };
43951
43952           operation.id = 'orthogonalize';
43953           operation.keys = [_t('operations.orthogonalize.key')];
43954           operation.title = _t('operations.orthogonalize.title');
43955           operation.behavior = behaviorOperation(context).which(operation);
43956           return operation;
43957         }
43958
43959         function operationReflectShort(context, selectedIDs) {
43960           return operationReflect(context, selectedIDs, 'short');
43961         }
43962         function operationReflectLong(context, selectedIDs) {
43963           return operationReflect(context, selectedIDs, 'long');
43964         }
43965         function operationReflect(context, selectedIDs, axis) {
43966           axis = axis || 'long';
43967           var multi = selectedIDs.length === 1 ? 'single' : 'multiple';
43968           var nodes = utilGetAllNodes(selectedIDs, context.graph());
43969           var coords = nodes.map(function (n) {
43970             return n.loc;
43971           });
43972           var extent = utilTotalExtent(selectedIDs, context.graph());
43973
43974           var operation = function operation() {
43975             var action = actionReflect(selectedIDs, context.projection).useLongAxis(Boolean(axis === 'long'));
43976             context.perform(action, operation.annotation());
43977             window.setTimeout(function () {
43978               context.validator().validate();
43979             }, 300); // after any transition
43980           };
43981
43982           operation.available = function () {
43983             return nodes.length >= 3;
43984           }; // don't cache this because the visible extent could change
43985
43986
43987           operation.disabled = function () {
43988             if (extent.percentContainedIn(context.map().extent()) < 0.8) {
43989               return 'too_large';
43990             } else if (someMissing()) {
43991               return 'not_downloaded';
43992             } else if (selectedIDs.some(context.hasHiddenConnections)) {
43993               return 'connected_to_hidden';
43994             } else if (selectedIDs.some(incompleteRelation)) {
43995               return 'incomplete_relation';
43996             }
43997
43998             return false;
43999
44000             function someMissing() {
44001               if (context.inIntro()) return false;
44002               var osm = context.connection();
44003
44004               if (osm) {
44005                 var missing = coords.filter(function (loc) {
44006                   return !osm.isDataLoaded(loc);
44007                 });
44008
44009                 if (missing.length) {
44010                   missing.forEach(function (loc) {
44011                     context.loadTileAtLoc(loc);
44012                   });
44013                   return true;
44014                 }
44015               }
44016
44017               return false;
44018             }
44019
44020             function incompleteRelation(id) {
44021               var entity = context.entity(id);
44022               return entity.type === 'relation' && !entity.isComplete(context.graph());
44023             }
44024           };
44025
44026           operation.tooltip = function () {
44027             var disable = operation.disabled();
44028             return disable ? _t('operations.reflect.' + disable + '.' + multi) : _t('operations.reflect.description.' + axis + '.' + multi);
44029           };
44030
44031           operation.annotation = function () {
44032             return _t('operations.reflect.annotation.' + axis + '.feature', {
44033               n: selectedIDs.length
44034             });
44035           };
44036
44037           operation.id = 'reflect-' + axis;
44038           operation.keys = [_t('operations.reflect.key.' + axis)];
44039           operation.title = _t('operations.reflect.title.' + axis);
44040           operation.behavior = behaviorOperation(context).which(operation);
44041           return operation;
44042         }
44043
44044         function operationMove(context, selectedIDs) {
44045           var multi = selectedIDs.length === 1 ? 'single' : 'multiple';
44046           var nodes = utilGetAllNodes(selectedIDs, context.graph());
44047           var coords = nodes.map(function (n) {
44048             return n.loc;
44049           });
44050           var extent = utilTotalExtent(selectedIDs, context.graph());
44051
44052           var operation = function operation() {
44053             context.enter(modeMove(context, selectedIDs));
44054           };
44055
44056           operation.available = function () {
44057             return selectedIDs.length > 0;
44058           };
44059
44060           operation.disabled = function () {
44061             if (extent.percentContainedIn(context.map().extent()) < 0.8) {
44062               return 'too_large';
44063             } else if (someMissing()) {
44064               return 'not_downloaded';
44065             } else if (selectedIDs.some(context.hasHiddenConnections)) {
44066               return 'connected_to_hidden';
44067             } else if (selectedIDs.some(incompleteRelation)) {
44068               return 'incomplete_relation';
44069             }
44070
44071             return false;
44072
44073             function someMissing() {
44074               if (context.inIntro()) return false;
44075               var osm = context.connection();
44076
44077               if (osm) {
44078                 var missing = coords.filter(function (loc) {
44079                   return !osm.isDataLoaded(loc);
44080                 });
44081
44082                 if (missing.length) {
44083                   missing.forEach(function (loc) {
44084                     context.loadTileAtLoc(loc);
44085                   });
44086                   return true;
44087                 }
44088               }
44089
44090               return false;
44091             }
44092
44093             function incompleteRelation(id) {
44094               var entity = context.entity(id);
44095               return entity.type === 'relation' && !entity.isComplete(context.graph());
44096             }
44097           };
44098
44099           operation.tooltip = function () {
44100             var disable = operation.disabled();
44101             return disable ? _t('operations.move.' + disable + '.' + multi) : _t('operations.move.description.' + multi);
44102           };
44103
44104           operation.annotation = function () {
44105             return selectedIDs.length === 1 ? _t('operations.move.annotation.' + context.graph().geometry(selectedIDs[0])) : _t('operations.move.annotation.feature', {
44106               n: selectedIDs.length
44107             });
44108           };
44109
44110           operation.id = 'move';
44111           operation.keys = [_t('operations.move.key')];
44112           operation.title = _t('operations.move.title');
44113           operation.behavior = behaviorOperation(context).which(operation);
44114           operation.mouseOnly = true;
44115           return operation;
44116         }
44117
44118         function modeRotate(context, entityIDs) {
44119           var _tolerancePx = 4; // see also behaviorDrag, behaviorSelect, modeMove
44120
44121           var mode = {
44122             id: 'rotate',
44123             button: 'browse'
44124           };
44125           var keybinding = utilKeybinding('rotate');
44126           var behaviors = [behaviorEdit(context), operationCircularize(context, entityIDs).behavior, operationDelete(context, entityIDs).behavior, operationMove(context, entityIDs).behavior, operationOrthogonalize(context, entityIDs).behavior, operationReflectLong(context, entityIDs).behavior, operationReflectShort(context, entityIDs).behavior];
44127           var annotation = entityIDs.length === 1 ? _t('operations.rotate.annotation.' + context.graph().geometry(entityIDs[0])) : _t('operations.rotate.annotation.feature', {
44128             n: entityIDs.length
44129           });
44130
44131           var _prevGraph;
44132
44133           var _prevAngle;
44134
44135           var _prevTransform;
44136
44137           var _pivot; // use pointer events on supported platforms; fallback to mouse events
44138
44139
44140           var _pointerPrefix = 'PointerEvent' in window ? 'pointer' : 'mouse';
44141
44142           function doRotate(d3_event) {
44143             var fn;
44144
44145             if (context.graph() !== _prevGraph) {
44146               fn = context.perform;
44147             } else {
44148               fn = context.replace;
44149             } // projection changed, recalculate _pivot
44150
44151
44152             var projection = context.projection;
44153             var currTransform = projection.transform();
44154
44155             if (!_prevTransform || currTransform.k !== _prevTransform.k || currTransform.x !== _prevTransform.x || currTransform.y !== _prevTransform.y) {
44156               var nodes = utilGetAllNodes(entityIDs, context.graph());
44157               var points = nodes.map(function (n) {
44158                 return projection(n.loc);
44159               });
44160               _pivot = getPivot(points);
44161               _prevAngle = undefined;
44162             }
44163
44164             var currMouse = context.map().mouse(d3_event);
44165             var currAngle = Math.atan2(currMouse[1] - _pivot[1], currMouse[0] - _pivot[0]);
44166             if (typeof _prevAngle === 'undefined') _prevAngle = currAngle;
44167             var delta = currAngle - _prevAngle;
44168             fn(actionRotate(entityIDs, _pivot, delta, projection));
44169             _prevTransform = currTransform;
44170             _prevAngle = currAngle;
44171             _prevGraph = context.graph();
44172           }
44173
44174           function getPivot(points) {
44175             var _pivot;
44176
44177             if (points.length === 1) {
44178               _pivot = points[0];
44179             } else if (points.length === 2) {
44180               _pivot = geoVecInterp(points[0], points[1], 0.5);
44181             } else {
44182               var polygonHull = d3_polygonHull(points);
44183
44184               if (polygonHull.length === 2) {
44185                 _pivot = geoVecInterp(points[0], points[1], 0.5);
44186               } else {
44187                 _pivot = d3_polygonCentroid(d3_polygonHull(points));
44188               }
44189             }
44190
44191             return _pivot;
44192           }
44193
44194           function finish(d3_event) {
44195             d3_event.stopPropagation();
44196             context.replace(actionNoop(), annotation);
44197             context.enter(modeSelect(context, entityIDs));
44198           }
44199
44200           function cancel() {
44201             if (_prevGraph) context.pop(); // remove the rotate
44202
44203             context.enter(modeSelect(context, entityIDs));
44204           }
44205
44206           function undone() {
44207             context.enter(modeBrowse(context));
44208           }
44209
44210           mode.enter = function () {
44211             _prevGraph = null;
44212             context.features().forceVisible(entityIDs);
44213             behaviors.forEach(context.install);
44214             var downEvent;
44215             context.surface().on(_pointerPrefix + 'down.modeRotate', function (d3_event) {
44216               downEvent = d3_event;
44217             });
44218             select(window).on(_pointerPrefix + 'move.modeRotate', doRotate, true).on(_pointerPrefix + 'up.modeRotate', function (d3_event) {
44219               if (!downEvent) return;
44220               var mapNode = context.container().select('.main-map').node();
44221               var pointGetter = utilFastMouse(mapNode);
44222               var p1 = pointGetter(downEvent);
44223               var p2 = pointGetter(d3_event);
44224               var dist = geoVecLength(p1, p2);
44225               if (dist <= _tolerancePx) finish(d3_event);
44226               downEvent = null;
44227             }, true);
44228             context.history().on('undone.modeRotate', undone);
44229             keybinding.on('⎋', cancel).on('↩', finish);
44230             select(document).call(keybinding);
44231           };
44232
44233           mode.exit = function () {
44234             behaviors.forEach(context.uninstall);
44235             context.surface().on(_pointerPrefix + 'down.modeRotate', null);
44236             select(window).on(_pointerPrefix + 'move.modeRotate', null, true).on(_pointerPrefix + 'up.modeRotate', null, true);
44237             context.history().on('undone.modeRotate', null);
44238             select(document).call(keybinding.unbind);
44239             context.features().forceVisible([]);
44240           };
44241
44242           mode.selectedIDs = function () {
44243             if (!arguments.length) return entityIDs; // no assign
44244
44245             return mode;
44246           };
44247
44248           return mode;
44249         }
44250
44251         function operationRotate(context, selectedIDs) {
44252           var multi = selectedIDs.length === 1 ? 'single' : 'multiple';
44253           var nodes = utilGetAllNodes(selectedIDs, context.graph());
44254           var coords = nodes.map(function (n) {
44255             return n.loc;
44256           });
44257           var extent = utilTotalExtent(selectedIDs, context.graph());
44258
44259           var operation = function operation() {
44260             context.enter(modeRotate(context, selectedIDs));
44261           };
44262
44263           operation.available = function () {
44264             return nodes.length >= 2;
44265           };
44266
44267           operation.disabled = function () {
44268             if (extent.percentContainedIn(context.map().extent()) < 0.8) {
44269               return 'too_large';
44270             } else if (someMissing()) {
44271               return 'not_downloaded';
44272             } else if (selectedIDs.some(context.hasHiddenConnections)) {
44273               return 'connected_to_hidden';
44274             } else if (selectedIDs.some(incompleteRelation)) {
44275               return 'incomplete_relation';
44276             }
44277
44278             return false;
44279
44280             function someMissing() {
44281               if (context.inIntro()) return false;
44282               var osm = context.connection();
44283
44284               if (osm) {
44285                 var missing = coords.filter(function (loc) {
44286                   return !osm.isDataLoaded(loc);
44287                 });
44288
44289                 if (missing.length) {
44290                   missing.forEach(function (loc) {
44291                     context.loadTileAtLoc(loc);
44292                   });
44293                   return true;
44294                 }
44295               }
44296
44297               return false;
44298             }
44299
44300             function incompleteRelation(id) {
44301               var entity = context.entity(id);
44302               return entity.type === 'relation' && !entity.isComplete(context.graph());
44303             }
44304           };
44305
44306           operation.tooltip = function () {
44307             var disable = operation.disabled();
44308             return disable ? _t('operations.rotate.' + disable + '.' + multi) : _t('operations.rotate.description.' + multi);
44309           };
44310
44311           operation.annotation = function () {
44312             return selectedIDs.length === 1 ? _t('operations.rotate.annotation.' + context.graph().geometry(selectedIDs[0])) : _t('operations.rotate.annotation.feature', {
44313               n: selectedIDs.length
44314             });
44315           };
44316
44317           operation.id = 'rotate';
44318           operation.keys = [_t('operations.rotate.key')];
44319           operation.title = _t('operations.rotate.title');
44320           operation.behavior = behaviorOperation(context).which(operation);
44321           operation.mouseOnly = true;
44322           return operation;
44323         }
44324
44325         function modeMove(context, entityIDs, baseGraph) {
44326           var _tolerancePx = 4; // see also behaviorDrag, behaviorSelect, modeRotate
44327
44328           var mode = {
44329             id: 'move',
44330             button: 'browse'
44331           };
44332           var keybinding = utilKeybinding('move');
44333           var behaviors = [behaviorEdit(context), operationCircularize(context, entityIDs).behavior, operationDelete(context, entityIDs).behavior, operationOrthogonalize(context, entityIDs).behavior, operationReflectLong(context, entityIDs).behavior, operationReflectShort(context, entityIDs).behavior, operationRotate(context, entityIDs).behavior];
44334           var annotation = entityIDs.length === 1 ? _t('operations.move.annotation.' + context.graph().geometry(entityIDs[0])) : _t('operations.move.annotation.feature', {
44335             n: entityIDs.length
44336           });
44337
44338           var _prevGraph;
44339
44340           var _cache;
44341
44342           var _origin;
44343
44344           var _nudgeInterval; // use pointer events on supported platforms; fallback to mouse events
44345
44346
44347           var _pointerPrefix = 'PointerEvent' in window ? 'pointer' : 'mouse';
44348
44349           function doMove(nudge) {
44350             nudge = nudge || [0, 0];
44351             var fn;
44352
44353             if (_prevGraph !== context.graph()) {
44354               _cache = {};
44355               _origin = context.map().mouseCoordinates();
44356               fn = context.perform;
44357             } else {
44358               fn = context.overwrite;
44359             }
44360
44361             var currMouse = context.map().mouse();
44362             var origMouse = context.projection(_origin);
44363             var delta = geoVecSubtract(geoVecSubtract(currMouse, origMouse), nudge);
44364             fn(actionMove(entityIDs, delta, context.projection, _cache));
44365             _prevGraph = context.graph();
44366           }
44367
44368           function startNudge(nudge) {
44369             if (_nudgeInterval) window.clearInterval(_nudgeInterval);
44370             _nudgeInterval = window.setInterval(function () {
44371               context.map().pan(nudge);
44372               doMove(nudge);
44373             }, 50);
44374           }
44375
44376           function stopNudge() {
44377             if (_nudgeInterval) {
44378               window.clearInterval(_nudgeInterval);
44379               _nudgeInterval = null;
44380             }
44381           }
44382
44383           function move() {
44384             doMove();
44385             var nudge = geoViewportEdge(context.map().mouse(), context.map().dimensions());
44386
44387             if (nudge) {
44388               startNudge(nudge);
44389             } else {
44390               stopNudge();
44391             }
44392           }
44393
44394           function finish(d3_event) {
44395             d3_event.stopPropagation();
44396             context.replace(actionNoop(), annotation);
44397             context.enter(modeSelect(context, entityIDs));
44398             stopNudge();
44399           }
44400
44401           function cancel() {
44402             if (baseGraph) {
44403               while (context.graph() !== baseGraph) {
44404                 context.pop();
44405               } // reset to baseGraph
44406
44407
44408               context.enter(modeBrowse(context));
44409             } else {
44410               if (_prevGraph) context.pop(); // remove the move
44411
44412               context.enter(modeSelect(context, entityIDs));
44413             }
44414
44415             stopNudge();
44416           }
44417
44418           function undone() {
44419             context.enter(modeBrowse(context));
44420           }
44421
44422           mode.enter = function () {
44423             _origin = context.map().mouseCoordinates();
44424             _prevGraph = null;
44425             _cache = {};
44426             context.features().forceVisible(entityIDs);
44427             behaviors.forEach(context.install);
44428             var downEvent;
44429             context.surface().on(_pointerPrefix + 'down.modeMove', function (d3_event) {
44430               downEvent = d3_event;
44431             });
44432             select(window).on(_pointerPrefix + 'move.modeMove', move, true).on(_pointerPrefix + 'up.modeMove', function (d3_event) {
44433               if (!downEvent) return;
44434               var mapNode = context.container().select('.main-map').node();
44435               var pointGetter = utilFastMouse(mapNode);
44436               var p1 = pointGetter(downEvent);
44437               var p2 = pointGetter(d3_event);
44438               var dist = geoVecLength(p1, p2);
44439               if (dist <= _tolerancePx) finish(d3_event);
44440               downEvent = null;
44441             }, true);
44442             context.history().on('undone.modeMove', undone);
44443             keybinding.on('⎋', cancel).on('↩', finish);
44444             select(document).call(keybinding);
44445           };
44446
44447           mode.exit = function () {
44448             stopNudge();
44449             behaviors.forEach(function (behavior) {
44450               context.uninstall(behavior);
44451             });
44452             context.surface().on(_pointerPrefix + 'down.modeMove', null);
44453             select(window).on(_pointerPrefix + 'move.modeMove', null, true).on(_pointerPrefix + 'up.modeMove', null, true);
44454             context.history().on('undone.modeMove', null);
44455             select(document).call(keybinding.unbind);
44456             context.features().forceVisible([]);
44457           };
44458
44459           mode.selectedIDs = function () {
44460             if (!arguments.length) return entityIDs; // no assign
44461
44462             return mode;
44463           };
44464
44465           return mode;
44466         }
44467
44468         function behaviorPaste(context) {
44469           function doPaste(d3_event) {
44470             // prevent paste during low zoom selection
44471             if (!context.map().withinEditableZoom()) return;
44472             d3_event.preventDefault();
44473             var baseGraph = context.graph();
44474             var mouse = context.map().mouse();
44475             var projection = context.projection;
44476             var viewport = geoExtent(projection.clipExtent()).polygon();
44477             if (!geoPointInPolygon(mouse, viewport)) return;
44478             var oldIDs = context.copyIDs();
44479             if (!oldIDs.length) return;
44480             var extent = geoExtent();
44481             var oldGraph = context.copyGraph();
44482             var newIDs = [];
44483             var action = actionCopyEntities(oldIDs, oldGraph);
44484             context.perform(action);
44485             var copies = action.copies();
44486             var originals = new Set();
44487             Object.values(copies).forEach(function (entity) {
44488               originals.add(entity.id);
44489             });
44490
44491             for (var id in copies) {
44492               var oldEntity = oldGraph.entity(id);
44493               var newEntity = copies[id];
44494
44495               extent._extend(oldEntity.extent(oldGraph)); // Exclude child nodes from newIDs if their parent way was also copied.
44496
44497
44498               var parents = context.graph().parentWays(newEntity);
44499               var parentCopied = parents.some(function (parent) {
44500                 return originals.has(parent.id);
44501               });
44502
44503               if (!parentCopied) {
44504                 newIDs.push(newEntity.id);
44505               }
44506             } // Put pasted objects where mouse pointer is..
44507
44508
44509             var copyPoint = context.copyLonLat() && projection(context.copyLonLat()) || projection(extent.center());
44510             var delta = geoVecSubtract(mouse, copyPoint);
44511             context.perform(actionMove(newIDs, delta, projection));
44512             context.enter(modeMove(context, newIDs, baseGraph));
44513           }
44514
44515           function behavior() {
44516             context.keybinding().on(uiCmd('⌘V'), doPaste);
44517             return behavior;
44518           }
44519
44520           behavior.off = function () {
44521             context.keybinding().off(uiCmd('⌘V'));
44522           };
44523
44524           return behavior;
44525         }
44526
44527         // `String.prototype.repeat` method
44528         // https://tc39.es/ecma262/#sec-string.prototype.repeat
44529         _export({ target: 'String', proto: true }, {
44530           repeat: stringRepeat
44531         });
44532
44533         /*
44534             `behaviorDrag` is like `d3_behavior.drag`, with the following differences:
44535
44536             * The `origin` function is expected to return an [x, y] tuple rather than an
44537               {x, y} object.
44538             * The events are `start`, `move`, and `end`.
44539               (https://github.com/mbostock/d3/issues/563)
44540             * The `start` event is not dispatched until the first cursor movement occurs.
44541               (https://github.com/mbostock/d3/pull/368)
44542             * The `move` event has a `point` and `delta` [x, y] tuple properties rather
44543               than `x`, `y`, `dx`, and `dy` properties.
44544             * The `end` event is not dispatched if no movement occurs.
44545             * An `off` function is available that unbinds the drag's internal event handlers.
44546          */
44547
44548         function behaviorDrag() {
44549           var dispatch = dispatch$8('start', 'move', 'end'); // see also behaviorSelect
44550
44551           var _tolerancePx = 1; // keep this low to facilitate pixel-perfect micromapping
44552
44553           var _penTolerancePx = 4; // styluses can be touchy so require greater movement - #1981
44554
44555           var _origin = null;
44556           var _selector = '';
44557
44558           var _targetNode;
44559
44560           var _targetEntity;
44561
44562           var _surface;
44563
44564           var _pointerId; // use pointer events on supported platforms; fallback to mouse events
44565
44566
44567           var _pointerPrefix = 'PointerEvent' in window ? 'pointer' : 'mouse';
44568
44569           var d3_event_userSelectProperty = utilPrefixCSSProperty('UserSelect');
44570
44571           var d3_event_userSelectSuppress = function d3_event_userSelectSuppress() {
44572             var selection$1 = selection();
44573             var select = selection$1.style(d3_event_userSelectProperty);
44574             selection$1.style(d3_event_userSelectProperty, 'none');
44575             return function () {
44576               selection$1.style(d3_event_userSelectProperty, select);
44577             };
44578           };
44579
44580           function pointerdown(d3_event) {
44581             if (_pointerId) return;
44582             _pointerId = d3_event.pointerId || 'mouse';
44583             _targetNode = this; // only force reflow once per drag
44584
44585             var pointerLocGetter = utilFastMouse(_surface || _targetNode.parentNode);
44586             var offset;
44587             var startOrigin = pointerLocGetter(d3_event);
44588             var started = false;
44589             var selectEnable = d3_event_userSelectSuppress();
44590             select(window).on(_pointerPrefix + 'move.drag', pointermove).on(_pointerPrefix + 'up.drag pointercancel.drag', pointerup, true);
44591
44592             if (_origin) {
44593               offset = _origin.call(_targetNode, _targetEntity);
44594               offset = [offset[0] - startOrigin[0], offset[1] - startOrigin[1]];
44595             } else {
44596               offset = [0, 0];
44597             }
44598
44599             d3_event.stopPropagation();
44600
44601             function pointermove(d3_event) {
44602               if (_pointerId !== (d3_event.pointerId || 'mouse')) return;
44603               var p = pointerLocGetter(d3_event);
44604
44605               if (!started) {
44606                 var dist = geoVecLength(startOrigin, p);
44607                 var tolerance = d3_event.pointerType === 'pen' ? _penTolerancePx : _tolerancePx; // don't start until the drag has actually moved somewhat
44608
44609                 if (dist < tolerance) return;
44610                 started = true;
44611                 dispatch.call('start', this, d3_event, _targetEntity); // Don't send a `move` event in the same cycle as `start` since dragging
44612                 // a midpoint will convert the target to a node.
44613               } else {
44614                 startOrigin = p;
44615                 d3_event.stopPropagation();
44616                 d3_event.preventDefault();
44617                 var dx = p[0] - startOrigin[0];
44618                 var dy = p[1] - startOrigin[1];
44619                 dispatch.call('move', this, d3_event, _targetEntity, [p[0] + offset[0], p[1] + offset[1]], [dx, dy]);
44620               }
44621             }
44622
44623             function pointerup(d3_event) {
44624               if (_pointerId !== (d3_event.pointerId || 'mouse')) return;
44625               _pointerId = null;
44626
44627               if (started) {
44628                 dispatch.call('end', this, d3_event, _targetEntity);
44629                 d3_event.preventDefault();
44630               }
44631
44632               select(window).on(_pointerPrefix + 'move.drag', null).on(_pointerPrefix + 'up.drag pointercancel.drag', null);
44633               selectEnable();
44634             }
44635           }
44636
44637           function behavior(selection) {
44638             var matchesSelector = utilPrefixDOMProperty('matchesSelector');
44639             var delegate = pointerdown;
44640
44641             if (_selector) {
44642               delegate = function delegate(d3_event) {
44643                 var root = this;
44644                 var target = d3_event.target;
44645
44646                 for (; target && target !== root; target = target.parentNode) {
44647                   var datum = target.__data__;
44648                   _targetEntity = datum instanceof osmNote ? datum : datum && datum.properties && datum.properties.entity;
44649
44650                   if (_targetEntity && target[matchesSelector](_selector)) {
44651                     return pointerdown.call(target, d3_event);
44652                   }
44653                 }
44654               };
44655             }
44656
44657             selection.on(_pointerPrefix + 'down.drag' + _selector, delegate);
44658           }
44659
44660           behavior.off = function (selection) {
44661             selection.on(_pointerPrefix + 'down.drag' + _selector, null);
44662           };
44663
44664           behavior.selector = function (_) {
44665             if (!arguments.length) return _selector;
44666             _selector = _;
44667             return behavior;
44668           };
44669
44670           behavior.origin = function (_) {
44671             if (!arguments.length) return _origin;
44672             _origin = _;
44673             return behavior;
44674           };
44675
44676           behavior.cancel = function () {
44677             select(window).on(_pointerPrefix + 'move.drag', null).on(_pointerPrefix + 'up.drag pointercancel.drag', null);
44678             return behavior;
44679           };
44680
44681           behavior.targetNode = function (_) {
44682             if (!arguments.length) return _targetNode;
44683             _targetNode = _;
44684             return behavior;
44685           };
44686
44687           behavior.targetEntity = function (_) {
44688             if (!arguments.length) return _targetEntity;
44689             _targetEntity = _;
44690             return behavior;
44691           };
44692
44693           behavior.surface = function (_) {
44694             if (!arguments.length) return _surface;
44695             _surface = _;
44696             return behavior;
44697           };
44698
44699           return utilRebind(behavior, dispatch, 'on');
44700         }
44701
44702         function modeDragNode(context) {
44703           var mode = {
44704             id: 'drag-node',
44705             button: 'browse'
44706           };
44707           var hover = behaviorHover(context).altDisables(true).on('hover', context.ui().sidebar.hover);
44708           var edit = behaviorEdit(context);
44709
44710           var _nudgeInterval;
44711
44712           var _restoreSelectedIDs = [];
44713           var _wasMidpoint = false;
44714           var _isCancelled = false;
44715
44716           var _activeEntity;
44717
44718           var _startLoc;
44719
44720           var _lastLoc;
44721
44722           function startNudge(d3_event, entity, nudge) {
44723             if (_nudgeInterval) window.clearInterval(_nudgeInterval);
44724             _nudgeInterval = window.setInterval(function () {
44725               context.map().pan(nudge);
44726               doMove(d3_event, entity, nudge);
44727             }, 50);
44728           }
44729
44730           function stopNudge() {
44731             if (_nudgeInterval) {
44732               window.clearInterval(_nudgeInterval);
44733               _nudgeInterval = null;
44734             }
44735           }
44736
44737           function moveAnnotation(entity) {
44738             return _t('operations.move.annotation.' + entity.geometry(context.graph()));
44739           }
44740
44741           function connectAnnotation(nodeEntity, targetEntity) {
44742             var nodeGeometry = nodeEntity.geometry(context.graph());
44743             var targetGeometry = targetEntity.geometry(context.graph());
44744
44745             if (nodeGeometry === 'vertex' && targetGeometry === 'vertex') {
44746               var nodeParentWayIDs = context.graph().parentWays(nodeEntity);
44747               var targetParentWayIDs = context.graph().parentWays(targetEntity);
44748               var sharedParentWays = utilArrayIntersection(nodeParentWayIDs, targetParentWayIDs); // if both vertices are part of the same way
44749
44750               if (sharedParentWays.length !== 0) {
44751                 // if the nodes are next to each other, they are merged
44752                 if (sharedParentWays[0].areAdjacent(nodeEntity.id, targetEntity.id)) {
44753                   return _t('operations.connect.annotation.from_vertex.to_adjacent_vertex');
44754                 }
44755
44756                 return _t('operations.connect.annotation.from_vertex.to_sibling_vertex');
44757               }
44758             }
44759
44760             return _t('operations.connect.annotation.from_' + nodeGeometry + '.to_' + targetGeometry);
44761           }
44762
44763           function shouldSnapToNode(target) {
44764             if (!_activeEntity) return false;
44765             return _activeEntity.geometry(context.graph()) !== 'vertex' || target.geometry(context.graph()) === 'vertex' || _mainPresetIndex.allowsVertex(target, context.graph());
44766           }
44767
44768           function origin(entity) {
44769             return context.projection(entity.loc);
44770           }
44771
44772           function keydown(d3_event) {
44773             if (d3_event.keyCode === utilKeybinding.modifierCodes.alt) {
44774               if (context.surface().classed('nope')) {
44775                 context.surface().classed('nope-suppressed', true);
44776               }
44777
44778               context.surface().classed('nope', false).classed('nope-disabled', true);
44779             }
44780           }
44781
44782           function keyup(d3_event) {
44783             if (d3_event.keyCode === utilKeybinding.modifierCodes.alt) {
44784               if (context.surface().classed('nope-suppressed')) {
44785                 context.surface().classed('nope', true);
44786               }
44787
44788               context.surface().classed('nope-suppressed', false).classed('nope-disabled', false);
44789             }
44790           }
44791
44792           function start(d3_event, entity) {
44793             _wasMidpoint = entity.type === 'midpoint';
44794             var hasHidden = context.features().hasHiddenConnections(entity, context.graph());
44795             _isCancelled = !context.editable() || d3_event.shiftKey || hasHidden;
44796
44797             if (_isCancelled) {
44798               if (hasHidden) {
44799                 context.ui().flash.duration(4000).iconName('#iD-icon-no').label(_t('modes.drag_node.connected_to_hidden'))();
44800               }
44801
44802               return drag.cancel();
44803             }
44804
44805             if (_wasMidpoint) {
44806               var midpoint = entity;
44807               entity = osmNode();
44808               context.perform(actionAddMidpoint(midpoint, entity));
44809               entity = context.entity(entity.id); // get post-action entity
44810
44811               var vertex = context.surface().selectAll('.' + entity.id);
44812               drag.targetNode(vertex.node()).targetEntity(entity);
44813             } else {
44814               context.perform(actionNoop());
44815             }
44816
44817             _activeEntity = entity;
44818             _startLoc = entity.loc;
44819             hover.ignoreVertex(entity.geometry(context.graph()) === 'vertex');
44820             context.surface().selectAll('.' + _activeEntity.id).classed('active', true);
44821             context.enter(mode);
44822           } // related code
44823           // - `behavior/draw.js` `datum()`
44824
44825
44826           function datum(d3_event) {
44827             if (!d3_event || d3_event.altKey) {
44828               return {};
44829             } else {
44830               // When dragging, snap only to touch targets..
44831               // (this excludes area fills and active drawing elements)
44832               var d = d3_event.target.__data__;
44833               return d && d.properties && d.properties.target ? d : {};
44834             }
44835           }
44836
44837           function doMove(d3_event, entity, nudge) {
44838             nudge = nudge || [0, 0];
44839             var currPoint = d3_event && d3_event.point || context.projection(_lastLoc);
44840             var currMouse = geoVecSubtract(currPoint, nudge);
44841             var loc = context.projection.invert(currMouse);
44842             var target, edge;
44843
44844             if (!_nudgeInterval) {
44845               // If not nudging at the edge of the viewport, try to snap..
44846               // related code
44847               // - `mode/drag_node.js`     `doMove()`
44848               // - `behavior/draw.js`      `click()`
44849               // - `behavior/draw_way.js`  `move()`
44850               var d = datum(d3_event);
44851               target = d && d.properties && d.properties.entity;
44852               var targetLoc = target && target.loc;
44853               var targetNodes = d && d.properties && d.properties.nodes;
44854
44855               if (targetLoc) {
44856                 // snap to node/vertex - a point target with `.loc`
44857                 if (shouldSnapToNode(target)) {
44858                   loc = targetLoc;
44859                 }
44860               } else if (targetNodes) {
44861                 // snap to way - a line target with `.nodes`
44862                 edge = geoChooseEdge(targetNodes, context.map().mouse(), context.projection, end.id);
44863
44864                 if (edge) {
44865                   loc = edge.loc;
44866                 }
44867               }
44868             }
44869
44870             context.replace(actionMoveNode(entity.id, loc)); // Below here: validations
44871
44872             var isInvalid = false; // Check if this connection to `target` could cause relations to break..
44873
44874             if (target) {
44875               isInvalid = hasRelationConflict(entity, target, edge, context.graph());
44876             } // Check if this drag causes the geometry to break..
44877
44878
44879             if (!isInvalid) {
44880               isInvalid = hasInvalidGeometry(entity, context.graph());
44881             }
44882
44883             var nope = context.surface().classed('nope');
44884
44885             if (isInvalid === 'relation' || isInvalid === 'restriction') {
44886               if (!nope) {
44887                 // about to nope - show hint
44888                 context.ui().flash.duration(4000).iconName('#iD-icon-no').label(_t('operations.connect.' + isInvalid, {
44889                   relation: _mainPresetIndex.item('type/restriction').name()
44890                 }))();
44891               }
44892             } else if (isInvalid) {
44893               var errorID = isInvalid === 'line' ? 'lines' : 'areas';
44894               context.ui().flash.duration(3000).iconName('#iD-icon-no').label(_t('self_intersection.error.' + errorID))();
44895             } else {
44896               if (nope) {
44897                 // about to un-nope, remove hint
44898                 context.ui().flash.duration(1).label('')();
44899               }
44900             }
44901
44902             var nopeDisabled = context.surface().classed('nope-disabled');
44903
44904             if (nopeDisabled) {
44905               context.surface().classed('nope', false).classed('nope-suppressed', isInvalid);
44906             } else {
44907               context.surface().classed('nope', isInvalid).classed('nope-suppressed', false);
44908             }
44909
44910             _lastLoc = loc;
44911           } // Uses `actionConnect.disabled()` to know whether this connection is ok..
44912
44913
44914           function hasRelationConflict(entity, target, edge, graph) {
44915             var testGraph = graph.update(); // copy
44916             // if snapping to way - add midpoint there and consider that the target..
44917
44918             if (edge) {
44919               var midpoint = osmNode();
44920               var action = actionAddMidpoint({
44921                 loc: edge.loc,
44922                 edge: [target.nodes[edge.index - 1], target.nodes[edge.index]]
44923               }, midpoint);
44924               testGraph = action(testGraph);
44925               target = midpoint;
44926             } // can we connect to it?
44927
44928
44929             var ids = [entity.id, target.id];
44930             return actionConnect(ids).disabled(testGraph);
44931           }
44932
44933           function hasInvalidGeometry(entity, graph) {
44934             var parents = graph.parentWays(entity);
44935             var i, j, k;
44936
44937             for (i = 0; i < parents.length; i++) {
44938               var parent = parents[i];
44939               var nodes = [];
44940               var activeIndex = null; // which multipolygon ring contains node being dragged
44941               // test any parent multipolygons for valid geometry
44942
44943               var relations = graph.parentRelations(parent);
44944
44945               for (j = 0; j < relations.length; j++) {
44946                 if (!relations[j].isMultipolygon()) continue;
44947                 var rings = osmJoinWays(relations[j].members, graph); // find active ring and test it for self intersections
44948
44949                 for (k = 0; k < rings.length; k++) {
44950                   nodes = rings[k].nodes;
44951
44952                   if (nodes.find(function (n) {
44953                     return n.id === entity.id;
44954                   })) {
44955                     activeIndex = k;
44956
44957                     if (geoHasSelfIntersections(nodes, entity.id)) {
44958                       return 'multipolygonMember';
44959                     }
44960                   }
44961
44962                   rings[k].coords = nodes.map(function (n) {
44963                     return n.loc;
44964                   });
44965                 } // test active ring for intersections with other rings in the multipolygon
44966
44967
44968                 for (k = 0; k < rings.length; k++) {
44969                   if (k === activeIndex) continue; // make sure active ring doesn't cross passive rings
44970
44971                   if (geoHasLineIntersections(rings[activeIndex].nodes, rings[k].nodes, entity.id)) {
44972                     return 'multipolygonRing';
44973                   }
44974                 }
44975               } // If we still haven't tested this node's parent way for self-intersections.
44976               // (because it's not a member of a multipolygon), test it now.
44977
44978
44979               if (activeIndex === null) {
44980                 nodes = parent.nodes.map(function (nodeID) {
44981                   return graph.entity(nodeID);
44982                 });
44983
44984                 if (nodes.length && geoHasSelfIntersections(nodes, entity.id)) {
44985                   return parent.geometry(graph);
44986                 }
44987               }
44988             }
44989
44990             return false;
44991           }
44992
44993           function move(d3_event, entity, point) {
44994             if (_isCancelled) return;
44995             d3_event.stopPropagation();
44996             context.surface().classed('nope-disabled', d3_event.altKey);
44997             _lastLoc = context.projection.invert(point);
44998             doMove(d3_event, entity);
44999             var nudge = geoViewportEdge(point, context.map().dimensions());
45000
45001             if (nudge) {
45002               startNudge(d3_event, entity, nudge);
45003             } else {
45004               stopNudge();
45005             }
45006           }
45007
45008           function end(d3_event, entity) {
45009             if (_isCancelled) return;
45010             var wasPoint = entity.geometry(context.graph()) === 'point';
45011             var d = datum(d3_event);
45012             var nope = d && d.properties && d.properties.nope || context.surface().classed('nope');
45013             var target = d && d.properties && d.properties.entity; // entity to snap to
45014
45015             if (nope) {
45016               // bounce back
45017               context.perform(_actionBounceBack(entity.id, _startLoc));
45018             } else if (target && target.type === 'way') {
45019               var choice = geoChooseEdge(context.graph().childNodes(target), context.map().mouse(), context.projection, entity.id);
45020               context.replace(actionAddMidpoint({
45021                 loc: choice.loc,
45022                 edge: [target.nodes[choice.index - 1], target.nodes[choice.index]]
45023               }, entity), connectAnnotation(entity, target));
45024             } else if (target && target.type === 'node' && shouldSnapToNode(target)) {
45025               context.replace(actionConnect([target.id, entity.id]), connectAnnotation(entity, target));
45026             } else if (_wasMidpoint) {
45027               context.replace(actionNoop(), _t('operations.add.annotation.vertex'));
45028             } else {
45029               context.replace(actionNoop(), moveAnnotation(entity));
45030             }
45031
45032             if (wasPoint) {
45033               context.enter(modeSelect(context, [entity.id]));
45034             } else {
45035               var reselection = _restoreSelectedIDs.filter(function (id) {
45036                 return context.graph().hasEntity(id);
45037               });
45038
45039               if (reselection.length) {
45040                 context.enter(modeSelect(context, reselection));
45041               } else {
45042                 context.enter(modeBrowse(context));
45043               }
45044             }
45045           }
45046
45047           function _actionBounceBack(nodeID, toLoc) {
45048             var moveNode = actionMoveNode(nodeID, toLoc);
45049
45050             var action = function action(graph, t) {
45051               // last time through, pop off the bounceback perform.
45052               // it will then overwrite the initial perform with a moveNode that does nothing
45053               if (t === 1) context.pop();
45054               return moveNode(graph, t);
45055             };
45056
45057             action.transitionable = true;
45058             return action;
45059           }
45060
45061           function cancel() {
45062             drag.cancel();
45063             context.enter(modeBrowse(context));
45064           }
45065
45066           var drag = behaviorDrag().selector('.layer-touch.points .target').surface(context.container().select('.main-map').node()).origin(origin).on('start', start).on('move', move).on('end', end);
45067
45068           mode.enter = function () {
45069             context.install(hover);
45070             context.install(edit);
45071             select(window).on('keydown.dragNode', keydown).on('keyup.dragNode', keyup);
45072             context.history().on('undone.drag-node', cancel);
45073           };
45074
45075           mode.exit = function () {
45076             context.ui().sidebar.hover.cancel();
45077             context.uninstall(hover);
45078             context.uninstall(edit);
45079             select(window).on('keydown.dragNode', null).on('keyup.dragNode', null);
45080             context.history().on('undone.drag-node', null);
45081             _activeEntity = null;
45082             context.surface().classed('nope', false).classed('nope-suppressed', false).classed('nope-disabled', false).selectAll('.active').classed('active', false);
45083             stopNudge();
45084           };
45085
45086           mode.selectedIDs = function () {
45087             if (!arguments.length) return _activeEntity ? [_activeEntity.id] : []; // no assign
45088
45089             return mode;
45090           };
45091
45092           mode.activeID = function () {
45093             if (!arguments.length) return _activeEntity && _activeEntity.id; // no assign
45094
45095             return mode;
45096           };
45097
45098           mode.restoreSelectedIDs = function (_) {
45099             if (!arguments.length) return _restoreSelectedIDs;
45100             _restoreSelectedIDs = _;
45101             return mode;
45102           };
45103
45104           mode.behavior = drag;
45105           return mode;
45106         }
45107
45108         // Safari bug https://bugs.webkit.org/show_bug.cgi?id=200829
45109         var NON_GENERIC = !!nativePromiseConstructor && fails(function () {
45110           nativePromiseConstructor.prototype['finally'].call({ then: function () { /* empty */ } }, function () { /* empty */ });
45111         });
45112
45113         // `Promise.prototype.finally` method
45114         // https://tc39.es/ecma262/#sec-promise.prototype.finally
45115         _export({ target: 'Promise', proto: true, real: true, forced: NON_GENERIC }, {
45116           'finally': function (onFinally) {
45117             var C = speciesConstructor(this, getBuiltIn('Promise'));
45118             var isFunction = typeof onFinally == 'function';
45119             return this.then(
45120               isFunction ? function (x) {
45121                 return promiseResolve(C, onFinally()).then(function () { return x; });
45122               } : onFinally,
45123               isFunction ? function (e) {
45124                 return promiseResolve(C, onFinally()).then(function () { throw e; });
45125               } : onFinally
45126             );
45127           }
45128         });
45129
45130         // makes sure that native promise-based APIs `Promise#finally` properly works with patched `Promise#then`
45131         if (typeof nativePromiseConstructor == 'function') {
45132           var method = getBuiltIn('Promise').prototype['finally'];
45133           if (nativePromiseConstructor.prototype['finally'] !== method) {
45134             redefine(nativePromiseConstructor.prototype, 'finally', method, { unsafe: true });
45135           }
45136         }
45137
45138         function quickselect(arr, k, left, right, compare) {
45139           quickselectStep(arr, k, left || 0, right || arr.length - 1, compare || defaultCompare);
45140         }
45141
45142         function quickselectStep(arr, k, left, right, compare) {
45143           while (right > left) {
45144             if (right - left > 600) {
45145               var n = right - left + 1;
45146               var m = k - left + 1;
45147               var z = Math.log(n);
45148               var s = 0.5 * Math.exp(2 * z / 3);
45149               var sd = 0.5 * Math.sqrt(z * s * (n - s) / n) * (m - n / 2 < 0 ? -1 : 1);
45150               var newLeft = Math.max(left, Math.floor(k - m * s / n + sd));
45151               var newRight = Math.min(right, Math.floor(k + (n - m) * s / n + sd));
45152               quickselectStep(arr, k, newLeft, newRight, compare);
45153             }
45154
45155             var t = arr[k];
45156             var i = left;
45157             var j = right;
45158             swap(arr, left, k);
45159             if (compare(arr[right], t) > 0) swap(arr, left, right);
45160
45161             while (i < j) {
45162               swap(arr, i, j);
45163               i++;
45164               j--;
45165
45166               while (compare(arr[i], t) < 0) {
45167                 i++;
45168               }
45169
45170               while (compare(arr[j], t) > 0) {
45171                 j--;
45172               }
45173             }
45174
45175             if (compare(arr[left], t) === 0) swap(arr, left, j);else {
45176               j++;
45177               swap(arr, j, right);
45178             }
45179             if (j <= k) left = j + 1;
45180             if (k <= j) right = j - 1;
45181           }
45182         }
45183
45184         function swap(arr, i, j) {
45185           var tmp = arr[i];
45186           arr[i] = arr[j];
45187           arr[j] = tmp;
45188         }
45189
45190         function defaultCompare(a, b) {
45191           return a < b ? -1 : a > b ? 1 : 0;
45192         }
45193
45194         var RBush = /*#__PURE__*/function () {
45195           function RBush() {
45196             var maxEntries = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 9;
45197
45198             _classCallCheck$1(this, RBush);
45199
45200             // max entries in a node is 9 by default; min node fill is 40% for best performance
45201             this._maxEntries = Math.max(4, maxEntries);
45202             this._minEntries = Math.max(2, Math.ceil(this._maxEntries * 0.4));
45203             this.clear();
45204           }
45205
45206           _createClass$1(RBush, [{
45207             key: "all",
45208             value: function all() {
45209               return this._all(this.data, []);
45210             }
45211           }, {
45212             key: "search",
45213             value: function search(bbox) {
45214               var node = this.data;
45215               var result = [];
45216               if (!intersects(bbox, node)) return result;
45217               var toBBox = this.toBBox;
45218               var nodesToSearch = [];
45219
45220               while (node) {
45221                 for (var i = 0; i < node.children.length; i++) {
45222                   var child = node.children[i];
45223                   var childBBox = node.leaf ? toBBox(child) : child;
45224
45225                   if (intersects(bbox, childBBox)) {
45226                     if (node.leaf) result.push(child);else if (contains(bbox, childBBox)) this._all(child, result);else nodesToSearch.push(child);
45227                   }
45228                 }
45229
45230                 node = nodesToSearch.pop();
45231               }
45232
45233               return result;
45234             }
45235           }, {
45236             key: "collides",
45237             value: function collides(bbox) {
45238               var node = this.data;
45239               if (!intersects(bbox, node)) return false;
45240               var nodesToSearch = [];
45241
45242               while (node) {
45243                 for (var i = 0; i < node.children.length; i++) {
45244                   var child = node.children[i];
45245                   var childBBox = node.leaf ? this.toBBox(child) : child;
45246
45247                   if (intersects(bbox, childBBox)) {
45248                     if (node.leaf || contains(bbox, childBBox)) return true;
45249                     nodesToSearch.push(child);
45250                   }
45251                 }
45252
45253                 node = nodesToSearch.pop();
45254               }
45255
45256               return false;
45257             }
45258           }, {
45259             key: "load",
45260             value: function load(data) {
45261               if (!(data && data.length)) return this;
45262
45263               if (data.length < this._minEntries) {
45264                 for (var i = 0; i < data.length; i++) {
45265                   this.insert(data[i]);
45266                 }
45267
45268                 return this;
45269               } // recursively build the tree with the given data from scratch using OMT algorithm
45270
45271
45272               var node = this._build(data.slice(), 0, data.length - 1, 0);
45273
45274               if (!this.data.children.length) {
45275                 // save as is if tree is empty
45276                 this.data = node;
45277               } else if (this.data.height === node.height) {
45278                 // split root if trees have the same height
45279                 this._splitRoot(this.data, node);
45280               } else {
45281                 if (this.data.height < node.height) {
45282                   // swap trees if inserted one is bigger
45283                   var tmpNode = this.data;
45284                   this.data = node;
45285                   node = tmpNode;
45286                 } // insert the small tree into the large tree at appropriate level
45287
45288
45289                 this._insert(node, this.data.height - node.height - 1, true);
45290               }
45291
45292               return this;
45293             }
45294           }, {
45295             key: "insert",
45296             value: function insert(item) {
45297               if (item) this._insert(item, this.data.height - 1);
45298               return this;
45299             }
45300           }, {
45301             key: "clear",
45302             value: function clear() {
45303               this.data = createNode([]);
45304               return this;
45305             }
45306           }, {
45307             key: "remove",
45308             value: function remove(item, equalsFn) {
45309               if (!item) return this;
45310               var node = this.data;
45311               var bbox = this.toBBox(item);
45312               var path = [];
45313               var indexes = [];
45314               var i, parent, goingUp; // depth-first iterative tree traversal
45315
45316               while (node || path.length) {
45317                 if (!node) {
45318                   // go up
45319                   node = path.pop();
45320                   parent = path[path.length - 1];
45321                   i = indexes.pop();
45322                   goingUp = true;
45323                 }
45324
45325                 if (node.leaf) {
45326                   // check current node
45327                   var index = findItem(item, node.children, equalsFn);
45328
45329                   if (index !== -1) {
45330                     // item found, remove the item and condense tree upwards
45331                     node.children.splice(index, 1);
45332                     path.push(node);
45333
45334                     this._condense(path);
45335
45336                     return this;
45337                   }
45338                 }
45339
45340                 if (!goingUp && !node.leaf && contains(node, bbox)) {
45341                   // go down
45342                   path.push(node);
45343                   indexes.push(i);
45344                   i = 0;
45345                   parent = node;
45346                   node = node.children[0];
45347                 } else if (parent) {
45348                   // go right
45349                   i++;
45350                   node = parent.children[i];
45351                   goingUp = false;
45352                 } else node = null; // nothing found
45353
45354               }
45355
45356               return this;
45357             }
45358           }, {
45359             key: "toBBox",
45360             value: function toBBox(item) {
45361               return item;
45362             }
45363           }, {
45364             key: "compareMinX",
45365             value: function compareMinX(a, b) {
45366               return a.minX - b.minX;
45367             }
45368           }, {
45369             key: "compareMinY",
45370             value: function compareMinY(a, b) {
45371               return a.minY - b.minY;
45372             }
45373           }, {
45374             key: "toJSON",
45375             value: function toJSON() {
45376               return this.data;
45377             }
45378           }, {
45379             key: "fromJSON",
45380             value: function fromJSON(data) {
45381               this.data = data;
45382               return this;
45383             }
45384           }, {
45385             key: "_all",
45386             value: function _all(node, result) {
45387               var nodesToSearch = [];
45388
45389               while (node) {
45390                 if (node.leaf) result.push.apply(result, _toConsumableArray(node.children));else nodesToSearch.push.apply(nodesToSearch, _toConsumableArray(node.children));
45391                 node = nodesToSearch.pop();
45392               }
45393
45394               return result;
45395             }
45396           }, {
45397             key: "_build",
45398             value: function _build(items, left, right, height) {
45399               var N = right - left + 1;
45400               var M = this._maxEntries;
45401               var node;
45402
45403               if (N <= M) {
45404                 // reached leaf level; return leaf
45405                 node = createNode(items.slice(left, right + 1));
45406                 calcBBox(node, this.toBBox);
45407                 return node;
45408               }
45409
45410               if (!height) {
45411                 // target height of the bulk-loaded tree
45412                 height = Math.ceil(Math.log(N) / Math.log(M)); // target number of root entries to maximize storage utilization
45413
45414                 M = Math.ceil(N / Math.pow(M, height - 1));
45415               }
45416
45417               node = createNode([]);
45418               node.leaf = false;
45419               node.height = height; // split the items into M mostly square tiles
45420
45421               var N2 = Math.ceil(N / M);
45422               var N1 = N2 * Math.ceil(Math.sqrt(M));
45423               multiSelect(items, left, right, N1, this.compareMinX);
45424
45425               for (var i = left; i <= right; i += N1) {
45426                 var right2 = Math.min(i + N1 - 1, right);
45427                 multiSelect(items, i, right2, N2, this.compareMinY);
45428
45429                 for (var j = i; j <= right2; j += N2) {
45430                   var right3 = Math.min(j + N2 - 1, right2); // pack each entry recursively
45431
45432                   node.children.push(this._build(items, j, right3, height - 1));
45433                 }
45434               }
45435
45436               calcBBox(node, this.toBBox);
45437               return node;
45438             }
45439           }, {
45440             key: "_chooseSubtree",
45441             value: function _chooseSubtree(bbox, node, level, path) {
45442               while (true) {
45443                 path.push(node);
45444                 if (node.leaf || path.length - 1 === level) break;
45445                 var minArea = Infinity;
45446                 var minEnlargement = Infinity;
45447                 var targetNode = void 0;
45448
45449                 for (var i = 0; i < node.children.length; i++) {
45450                   var child = node.children[i];
45451                   var area = bboxArea(child);
45452                   var enlargement = enlargedArea(bbox, child) - area; // choose entry with the least area enlargement
45453
45454                   if (enlargement < minEnlargement) {
45455                     minEnlargement = enlargement;
45456                     minArea = area < minArea ? area : minArea;
45457                     targetNode = child;
45458                   } else if (enlargement === minEnlargement) {
45459                     // otherwise choose one with the smallest area
45460                     if (area < minArea) {
45461                       minArea = area;
45462                       targetNode = child;
45463                     }
45464                   }
45465                 }
45466
45467                 node = targetNode || node.children[0];
45468               }
45469
45470               return node;
45471             }
45472           }, {
45473             key: "_insert",
45474             value: function _insert(item, level, isNode) {
45475               var bbox = isNode ? item : this.toBBox(item);
45476               var insertPath = []; // find the best node for accommodating the item, saving all nodes along the path too
45477
45478               var node = this._chooseSubtree(bbox, this.data, level, insertPath); // put the item into the node
45479
45480
45481               node.children.push(item);
45482               extend$1(node, bbox); // split on node overflow; propagate upwards if necessary
45483
45484               while (level >= 0) {
45485                 if (insertPath[level].children.length > this._maxEntries) {
45486                   this._split(insertPath, level);
45487
45488                   level--;
45489                 } else break;
45490               } // adjust bboxes along the insertion path
45491
45492
45493               this._adjustParentBBoxes(bbox, insertPath, level);
45494             } // split overflowed node into two
45495
45496           }, {
45497             key: "_split",
45498             value: function _split(insertPath, level) {
45499               var node = insertPath[level];
45500               var M = node.children.length;
45501               var m = this._minEntries;
45502
45503               this._chooseSplitAxis(node, m, M);
45504
45505               var splitIndex = this._chooseSplitIndex(node, m, M);
45506
45507               var newNode = createNode(node.children.splice(splitIndex, node.children.length - splitIndex));
45508               newNode.height = node.height;
45509               newNode.leaf = node.leaf;
45510               calcBBox(node, this.toBBox);
45511               calcBBox(newNode, this.toBBox);
45512               if (level) insertPath[level - 1].children.push(newNode);else this._splitRoot(node, newNode);
45513             }
45514           }, {
45515             key: "_splitRoot",
45516             value: function _splitRoot(node, newNode) {
45517               // split root node
45518               this.data = createNode([node, newNode]);
45519               this.data.height = node.height + 1;
45520               this.data.leaf = false;
45521               calcBBox(this.data, this.toBBox);
45522             }
45523           }, {
45524             key: "_chooseSplitIndex",
45525             value: function _chooseSplitIndex(node, m, M) {
45526               var index;
45527               var minOverlap = Infinity;
45528               var minArea = Infinity;
45529
45530               for (var i = m; i <= M - m; i++) {
45531                 var bbox1 = distBBox(node, 0, i, this.toBBox);
45532                 var bbox2 = distBBox(node, i, M, this.toBBox);
45533                 var overlap = intersectionArea(bbox1, bbox2);
45534                 var area = bboxArea(bbox1) + bboxArea(bbox2); // choose distribution with minimum overlap
45535
45536                 if (overlap < minOverlap) {
45537                   minOverlap = overlap;
45538                   index = i;
45539                   minArea = area < minArea ? area : minArea;
45540                 } else if (overlap === minOverlap) {
45541                   // otherwise choose distribution with minimum area
45542                   if (area < minArea) {
45543                     minArea = area;
45544                     index = i;
45545                   }
45546                 }
45547               }
45548
45549               return index || M - m;
45550             } // sorts node children by the best axis for split
45551
45552           }, {
45553             key: "_chooseSplitAxis",
45554             value: function _chooseSplitAxis(node, m, M) {
45555               var compareMinX = node.leaf ? this.compareMinX : compareNodeMinX;
45556               var compareMinY = node.leaf ? this.compareMinY : compareNodeMinY;
45557
45558               var xMargin = this._allDistMargin(node, m, M, compareMinX);
45559
45560               var yMargin = this._allDistMargin(node, m, M, compareMinY); // if total distributions margin value is minimal for x, sort by minX,
45561               // otherwise it's already sorted by minY
45562
45563
45564               if (xMargin < yMargin) node.children.sort(compareMinX);
45565             } // total margin of all possible split distributions where each node is at least m full
45566
45567           }, {
45568             key: "_allDistMargin",
45569             value: function _allDistMargin(node, m, M, compare) {
45570               node.children.sort(compare);
45571               var toBBox = this.toBBox;
45572               var leftBBox = distBBox(node, 0, m, toBBox);
45573               var rightBBox = distBBox(node, M - m, M, toBBox);
45574               var margin = bboxMargin(leftBBox) + bboxMargin(rightBBox);
45575
45576               for (var i = m; i < M - m; i++) {
45577                 var child = node.children[i];
45578                 extend$1(leftBBox, node.leaf ? toBBox(child) : child);
45579                 margin += bboxMargin(leftBBox);
45580               }
45581
45582               for (var _i = M - m - 1; _i >= m; _i--) {
45583                 var _child = node.children[_i];
45584                 extend$1(rightBBox, node.leaf ? toBBox(_child) : _child);
45585                 margin += bboxMargin(rightBBox);
45586               }
45587
45588               return margin;
45589             }
45590           }, {
45591             key: "_adjustParentBBoxes",
45592             value: function _adjustParentBBoxes(bbox, path, level) {
45593               // adjust bboxes along the given tree path
45594               for (var i = level; i >= 0; i--) {
45595                 extend$1(path[i], bbox);
45596               }
45597             }
45598           }, {
45599             key: "_condense",
45600             value: function _condense(path) {
45601               // go through the path, removing empty nodes and updating bboxes
45602               for (var i = path.length - 1, siblings; i >= 0; i--) {
45603                 if (path[i].children.length === 0) {
45604                   if (i > 0) {
45605                     siblings = path[i - 1].children;
45606                     siblings.splice(siblings.indexOf(path[i]), 1);
45607                   } else this.clear();
45608                 } else calcBBox(path[i], this.toBBox);
45609               }
45610             }
45611           }]);
45612
45613           return RBush;
45614         }();
45615
45616         function findItem(item, items, equalsFn) {
45617           if (!equalsFn) return items.indexOf(item);
45618
45619           for (var i = 0; i < items.length; i++) {
45620             if (equalsFn(item, items[i])) return i;
45621           }
45622
45623           return -1;
45624         } // calculate node's bbox from bboxes of its children
45625
45626
45627         function calcBBox(node, toBBox) {
45628           distBBox(node, 0, node.children.length, toBBox, node);
45629         } // min bounding rectangle of node children from k to p-1
45630
45631
45632         function distBBox(node, k, p, toBBox, destNode) {
45633           if (!destNode) destNode = createNode(null);
45634           destNode.minX = Infinity;
45635           destNode.minY = Infinity;
45636           destNode.maxX = -Infinity;
45637           destNode.maxY = -Infinity;
45638
45639           for (var i = k; i < p; i++) {
45640             var child = node.children[i];
45641             extend$1(destNode, node.leaf ? toBBox(child) : child);
45642           }
45643
45644           return destNode;
45645         }
45646
45647         function extend$1(a, b) {
45648           a.minX = Math.min(a.minX, b.minX);
45649           a.minY = Math.min(a.minY, b.minY);
45650           a.maxX = Math.max(a.maxX, b.maxX);
45651           a.maxY = Math.max(a.maxY, b.maxY);
45652           return a;
45653         }
45654
45655         function compareNodeMinX(a, b) {
45656           return a.minX - b.minX;
45657         }
45658
45659         function compareNodeMinY(a, b) {
45660           return a.minY - b.minY;
45661         }
45662
45663         function bboxArea(a) {
45664           return (a.maxX - a.minX) * (a.maxY - a.minY);
45665         }
45666
45667         function bboxMargin(a) {
45668           return a.maxX - a.minX + (a.maxY - a.minY);
45669         }
45670
45671         function enlargedArea(a, b) {
45672           return (Math.max(b.maxX, a.maxX) - Math.min(b.minX, a.minX)) * (Math.max(b.maxY, a.maxY) - Math.min(b.minY, a.minY));
45673         }
45674
45675         function intersectionArea(a, b) {
45676           var minX = Math.max(a.minX, b.minX);
45677           var minY = Math.max(a.minY, b.minY);
45678           var maxX = Math.min(a.maxX, b.maxX);
45679           var maxY = Math.min(a.maxY, b.maxY);
45680           return Math.max(0, maxX - minX) * Math.max(0, maxY - minY);
45681         }
45682
45683         function contains(a, b) {
45684           return a.minX <= b.minX && a.minY <= b.minY && b.maxX <= a.maxX && b.maxY <= a.maxY;
45685         }
45686
45687         function intersects(a, b) {
45688           return b.minX <= a.maxX && b.minY <= a.maxY && b.maxX >= a.minX && b.maxY >= a.minY;
45689         }
45690
45691         function createNode(children) {
45692           return {
45693             children: children,
45694             height: 1,
45695             leaf: true,
45696             minX: Infinity,
45697             minY: Infinity,
45698             maxX: -Infinity,
45699             maxY: -Infinity
45700           };
45701         } // sort an array so that items come in groups of n unsorted items, with groups sorted between each other;
45702         // combines selection algorithm with binary divide & conquer approach
45703
45704
45705         function multiSelect(arr, left, right, n, compare) {
45706           var stack = [left, right];
45707
45708           while (stack.length) {
45709             right = stack.pop();
45710             left = stack.pop();
45711             if (right - left <= n) continue;
45712             var mid = left + Math.ceil((right - left) / n / 2) * n;
45713             quickselect(arr, mid, left, right, compare);
45714             stack.push(left, mid, mid, right);
45715           }
45716         }
45717
45718         function responseText(response) {
45719           if (!response.ok) throw new Error(response.status + " " + response.statusText);
45720           return response.text();
45721         }
45722
45723         function d3_text (input, init) {
45724           return fetch(input, init).then(responseText);
45725         }
45726
45727         function responseJson(response) {
45728           if (!response.ok) throw new Error(response.status + " " + response.statusText);
45729           if (response.status === 204 || response.status === 205) return;
45730           return response.json();
45731         }
45732
45733         function d3_json (input, init) {
45734           return fetch(input, init).then(responseJson);
45735         }
45736
45737         function parser(type) {
45738           return function (input, init) {
45739             return d3_text(input, init).then(function (text) {
45740               return new DOMParser().parseFromString(text, type);
45741             });
45742           };
45743         }
45744
45745         var d3_xml = parser("application/xml");
45746         var svg = parser("image/svg+xml");
45747
45748         var tiler$6 = utilTiler();
45749         var dispatch$7 = dispatch$8('loaded');
45750         var _tileZoom$3 = 14;
45751         var _krUrlRoot = 'https://www.keepright.at';
45752         var _krData = {
45753           errorTypes: {},
45754           localizeStrings: {}
45755         }; // This gets reassigned if reset
45756
45757         var _cache$2;
45758
45759         var _krRuleset = [// no 20 - multiple node on same spot - these are mostly boundaries overlapping roads
45760         30, 40, 50, 60, 70, 90, 100, 110, 120, 130, 150, 160, 170, 180, 190, 191, 192, 193, 194, 195, 196, 197, 198, 200, 201, 202, 203, 204, 205, 206, 207, 208, 210, 220, 230, 231, 232, 270, 280, 281, 282, 283, 284, 285, 290, 291, 292, 293, 294, 295, 296, 297, 298, 300, 310, 311, 312, 313, 320, 350, 360, 370, 380, 390, 400, 401, 402, 410, 411, 412, 413];
45761
45762         function abortRequest$6(controller) {
45763           if (controller) {
45764             controller.abort();
45765           }
45766         }
45767
45768         function abortUnwantedRequests$3(cache, tiles) {
45769           Object.keys(cache.inflightTile).forEach(function (k) {
45770             var wanted = tiles.find(function (tile) {
45771               return k === tile.id;
45772             });
45773
45774             if (!wanted) {
45775               abortRequest$6(cache.inflightTile[k]);
45776               delete cache.inflightTile[k];
45777             }
45778           });
45779         }
45780
45781         function encodeIssueRtree$2(d) {
45782           return {
45783             minX: d.loc[0],
45784             minY: d.loc[1],
45785             maxX: d.loc[0],
45786             maxY: d.loc[1],
45787             data: d
45788           };
45789         } // Replace or remove QAItem from rtree
45790
45791
45792         function updateRtree$3(item, replace) {
45793           _cache$2.rtree.remove(item, function (a, b) {
45794             return a.data.id === b.data.id;
45795           });
45796
45797           if (replace) {
45798             _cache$2.rtree.insert(item);
45799           }
45800         }
45801
45802         function tokenReplacements(d) {
45803           if (!(d instanceof QAItem)) return;
45804           var htmlRegex = new RegExp(/<\/[a-z][\s\S]*>/);
45805           var replacements = {};
45806           var issueTemplate = _krData.errorTypes[d.whichType];
45807
45808           if (!issueTemplate) {
45809             /* eslint-disable no-console */
45810             console.log('No Template: ', d.whichType);
45811             console.log('  ', d.description);
45812             /* eslint-enable no-console */
45813
45814             return;
45815           } // some descriptions are just fixed text
45816
45817
45818           if (!issueTemplate.regex) return; // regex pattern should match description with variable details captured
45819
45820           var errorRegex = new RegExp(issueTemplate.regex, 'i');
45821           var errorMatch = errorRegex.exec(d.description);
45822
45823           if (!errorMatch) {
45824             /* eslint-disable no-console */
45825             console.log('Unmatched: ', d.whichType);
45826             console.log('  ', d.description);
45827             console.log('  ', errorRegex);
45828             /* eslint-enable no-console */
45829
45830             return;
45831           }
45832
45833           for (var i = 1; i < errorMatch.length; i++) {
45834             // skip first
45835             var capture = errorMatch[i];
45836             var idType = void 0;
45837             idType = 'IDs' in issueTemplate ? issueTemplate.IDs[i - 1] : '';
45838
45839             if (idType && capture) {
45840               // link IDs if present in the capture
45841               capture = parseError(capture, idType);
45842             } else if (htmlRegex.test(capture)) {
45843               // escape any html in non-IDs
45844               capture = '\\' + capture + '\\';
45845             } else {
45846               var compare = capture.toLowerCase();
45847
45848               if (_krData.localizeStrings[compare]) {
45849                 // some replacement strings can be localized
45850                 capture = _t('QA.keepRight.error_parts.' + _krData.localizeStrings[compare]);
45851               }
45852             }
45853
45854             replacements['var' + i] = capture;
45855           }
45856
45857           return replacements;
45858         }
45859
45860         function parseError(capture, idType) {
45861           var compare = capture.toLowerCase();
45862
45863           if (_krData.localizeStrings[compare]) {
45864             // some replacement strings can be localized
45865             capture = _t('QA.keepRight.error_parts.' + _krData.localizeStrings[compare]);
45866           }
45867
45868           switch (idType) {
45869             // link a string like "this node"
45870             case 'this':
45871               capture = linkErrorObject(capture);
45872               break;
45873
45874             case 'url':
45875               capture = linkURL(capture);
45876               break;
45877             // link an entity ID
45878
45879             case 'n':
45880             case 'w':
45881             case 'r':
45882               capture = linkEntity(idType + capture);
45883               break;
45884             // some errors have more complex ID lists/variance
45885
45886             case '20':
45887               capture = parse20(capture);
45888               break;
45889
45890             case '211':
45891               capture = parse211(capture);
45892               break;
45893
45894             case '231':
45895               capture = parse231(capture);
45896               break;
45897
45898             case '294':
45899               capture = parse294(capture);
45900               break;
45901
45902             case '370':
45903               capture = parse370(capture);
45904               break;
45905           }
45906
45907           return capture;
45908
45909           function linkErrorObject(d) {
45910             return "<a class=\"error_object_link\">".concat(d, "</a>");
45911           }
45912
45913           function linkEntity(d) {
45914             return "<a class=\"error_entity_link\">".concat(d, "</a>");
45915           }
45916
45917           function linkURL(d) {
45918             return "<a class=\"kr_external_link\" target=\"_blank\" href=\"".concat(d, "\">").concat(d, "</a>");
45919           } // arbitrary node list of form: #ID, #ID, #ID...
45920
45921
45922           function parse211(capture) {
45923             var newList = [];
45924             var items = capture.split(', ');
45925             items.forEach(function (item) {
45926               // ID has # at the front
45927               var id = linkEntity('n' + item.slice(1));
45928               newList.push(id);
45929             });
45930             return newList.join(', ');
45931           } // arbitrary way list of form: #ID(layer),#ID(layer),#ID(layer)...
45932
45933
45934           function parse231(capture) {
45935             var newList = []; // unfortunately 'layer' can itself contain commas, so we split on '),'
45936
45937             var items = capture.split('),');
45938             items.forEach(function (item) {
45939               var match = item.match(/\#(\d+)\((.+)\)?/);
45940
45941               if (match !== null && match.length > 2) {
45942                 newList.push(linkEntity('w' + match[1]) + ' ' + _t('QA.keepRight.errorTypes.231.layer', {
45943                   layer: match[2]
45944                 }));
45945               }
45946             });
45947             return newList.join(', ');
45948           } // arbitrary node/relation list of form: from node #ID,to relation #ID,to node #ID...
45949
45950
45951           function parse294(capture) {
45952             var newList = [];
45953             var items = capture.split(',');
45954             items.forEach(function (item) {
45955               // item of form "from/to node/relation #ID"
45956               item = item.split(' '); // to/from role is more clear in quotes
45957
45958               var role = "\"".concat(item[0], "\""); // first letter of node/relation provides the type
45959
45960               var idType = item[1].slice(0, 1); // ID has # at the front
45961
45962               var id = item[2].slice(1);
45963               id = linkEntity(idType + id);
45964               newList.push("".concat(role, " ").concat(item[1], " ").concat(id));
45965             });
45966             return newList.join(', ');
45967           } // may or may not include the string "(including the name 'name')"
45968
45969
45970           function parse370(capture) {
45971             if (!capture) return '';
45972             var match = capture.match(/\(including the name (\'.+\')\)/);
45973
45974             if (match && match.length) {
45975               return _t('QA.keepRight.errorTypes.370.including_the_name', {
45976                 name: match[1]
45977               });
45978             }
45979
45980             return '';
45981           } // arbitrary node list of form: #ID,#ID,#ID...
45982
45983
45984           function parse20(capture) {
45985             var newList = [];
45986             var items = capture.split(',');
45987             items.forEach(function (item) {
45988               // ID has # at the front
45989               var id = linkEntity('n' + item.slice(1));
45990               newList.push(id);
45991             });
45992             return newList.join(', ');
45993           }
45994         }
45995
45996         var serviceKeepRight = {
45997           title: 'keepRight',
45998           init: function init() {
45999             _mainFileFetcher.get('keepRight').then(function (d) {
46000               return _krData = d;
46001             });
46002
46003             if (!_cache$2) {
46004               this.reset();
46005             }
46006
46007             this.event = utilRebind(this, dispatch$7, 'on');
46008           },
46009           reset: function reset() {
46010             if (_cache$2) {
46011               Object.values(_cache$2.inflightTile).forEach(abortRequest$6);
46012             }
46013
46014             _cache$2 = {
46015               data: {},
46016               loadedTile: {},
46017               inflightTile: {},
46018               inflightPost: {},
46019               closed: {},
46020               rtree: new RBush()
46021             };
46022           },
46023           // KeepRight API:  http://osm.mueschelsoft.de/keepright/interfacing.php
46024           loadIssues: function loadIssues(projection) {
46025             var _this = this;
46026
46027             var options = {
46028               format: 'geojson',
46029               ch: _krRuleset
46030             }; // determine the needed tiles to cover the view
46031
46032             var tiles = tiler$6.zoomExtent([_tileZoom$3, _tileZoom$3]).getTiles(projection); // abort inflight requests that are no longer needed
46033
46034             abortUnwantedRequests$3(_cache$2, tiles); // issue new requests..
46035
46036             tiles.forEach(function (tile) {
46037               if (_cache$2.loadedTile[tile.id] || _cache$2.inflightTile[tile.id]) return;
46038
46039               var _tile$extent$rectangl = tile.extent.rectangle(),
46040                   _tile$extent$rectangl2 = _slicedToArray(_tile$extent$rectangl, 4),
46041                   left = _tile$extent$rectangl2[0],
46042                   top = _tile$extent$rectangl2[1],
46043                   right = _tile$extent$rectangl2[2],
46044                   bottom = _tile$extent$rectangl2[3];
46045
46046               var params = Object.assign({}, options, {
46047                 left: left,
46048                 bottom: bottom,
46049                 right: right,
46050                 top: top
46051               });
46052               var url = "".concat(_krUrlRoot, "/export.php?") + utilQsString(params);
46053               var controller = new AbortController();
46054               _cache$2.inflightTile[tile.id] = controller;
46055               d3_json(url, {
46056                 signal: controller.signal
46057               }).then(function (data) {
46058                 delete _cache$2.inflightTile[tile.id];
46059                 _cache$2.loadedTile[tile.id] = true;
46060
46061                 if (!data || !data.features || !data.features.length) {
46062                   throw new Error('No Data');
46063                 }
46064
46065                 data.features.forEach(function (feature) {
46066                   var _feature$properties = feature.properties,
46067                       itemType = _feature$properties.error_type,
46068                       id = _feature$properties.error_id,
46069                       _feature$properties$c = _feature$properties.comment,
46070                       comment = _feature$properties$c === void 0 ? null : _feature$properties$c,
46071                       objectId = _feature$properties.object_id,
46072                       objectType = _feature$properties.object_type,
46073                       schema = _feature$properties.schema,
46074                       title = _feature$properties.title;
46075                   var loc = feature.geometry.coordinates,
46076                       _feature$properties$d = feature.properties.description,
46077                       description = _feature$properties$d === void 0 ? '' : _feature$properties$d; // if there is a parent, save its error type e.g.:
46078                   //  Error 191 = "highway-highway"
46079                   //  Error 190 = "intersections without junctions"  (parent)
46080
46081                   var issueTemplate = _krData.errorTypes[itemType];
46082                   var parentIssueType = (Math.floor(itemType / 10) * 10).toString(); // try to handle error type directly, fallback to parent error type.
46083
46084                   var whichType = issueTemplate ? itemType : parentIssueType;
46085                   var whichTemplate = _krData.errorTypes[whichType]; // Rewrite a few of the errors at this point..
46086                   // This is done to make them easier to linkify and translate.
46087
46088                   switch (whichType) {
46089                     case '170':
46090                       description = "This feature has a FIXME tag: ".concat(description);
46091                       break;
46092
46093                     case '292':
46094                     case '293':
46095                       description = description.replace('A turn-', 'This turn-');
46096                       break;
46097
46098                     case '294':
46099                     case '295':
46100                     case '296':
46101                     case '297':
46102                     case '298':
46103                       description = "This turn-restriction~".concat(description);
46104                       break;
46105
46106                     case '300':
46107                       description = 'This highway is missing a maxspeed tag';
46108                       break;
46109
46110                     case '411':
46111                     case '412':
46112                     case '413':
46113                       description = "This feature~".concat(description);
46114                       break;
46115                   } // move markers slightly so it doesn't obscure the geometry,
46116                   // then move markers away from other coincident markers
46117
46118
46119                   var coincident = false;
46120
46121                   do {
46122                     // first time, move marker up. after that, move marker right.
46123                     var delta = coincident ? [0.00001, 0] : [0, 0.00001];
46124                     loc = geoVecAdd(loc, delta);
46125                     var bbox = geoExtent(loc).bbox();
46126                     coincident = _cache$2.rtree.search(bbox).length;
46127                   } while (coincident);
46128
46129                   var d = new QAItem(loc, _this, itemType, id, {
46130                     comment: comment,
46131                     description: description,
46132                     whichType: whichType,
46133                     parentIssueType: parentIssueType,
46134                     severity: whichTemplate.severity || 'error',
46135                     objectId: objectId,
46136                     objectType: objectType,
46137                     schema: schema,
46138                     title: title
46139                   });
46140                   d.replacements = tokenReplacements(d);
46141                   _cache$2.data[id] = d;
46142
46143                   _cache$2.rtree.insert(encodeIssueRtree$2(d));
46144                 });
46145                 dispatch$7.call('loaded');
46146               })["catch"](function () {
46147                 delete _cache$2.inflightTile[tile.id];
46148                 _cache$2.loadedTile[tile.id] = true;
46149               });
46150             });
46151           },
46152           postUpdate: function postUpdate(d, callback) {
46153             var _this2 = this;
46154
46155             if (_cache$2.inflightPost[d.id]) {
46156               return callback({
46157                 message: 'Error update already inflight',
46158                 status: -2
46159               }, d);
46160             }
46161
46162             var params = {
46163               schema: d.schema,
46164               id: d.id
46165             };
46166
46167             if (d.newStatus) {
46168               params.st = d.newStatus;
46169             }
46170
46171             if (d.newComment !== undefined) {
46172               params.co = d.newComment;
46173             } // NOTE: This throws a CORS err, but it seems successful.
46174             // We don't care too much about the response, so this is fine.
46175
46176
46177             var url = "".concat(_krUrlRoot, "/comment.php?") + utilQsString(params);
46178             var controller = new AbortController();
46179             _cache$2.inflightPost[d.id] = controller; // Since this is expected to throw an error just continue as if it worked
46180             // (worst case scenario the request truly fails and issue will show up if iD restarts)
46181
46182             d3_json(url, {
46183               signal: controller.signal
46184             })["finally"](function () {
46185               delete _cache$2.inflightPost[d.id];
46186
46187               if (d.newStatus === 'ignore') {
46188                 // ignore permanently (false positive)
46189                 _this2.removeItem(d);
46190               } else if (d.newStatus === 'ignore_t') {
46191                 // ignore temporarily (error fixed)
46192                 _this2.removeItem(d);
46193
46194                 _cache$2.closed["".concat(d.schema, ":").concat(d.id)] = true;
46195               } else {
46196                 d = _this2.replaceItem(d.update({
46197                   comment: d.newComment,
46198                   newComment: undefined,
46199                   newState: undefined
46200                 }));
46201               }
46202
46203               if (callback) callback(null, d);
46204             });
46205           },
46206           // Get all cached QAItems covering the viewport
46207           getItems: function getItems(projection) {
46208             var viewport = projection.clipExtent();
46209             var min = [viewport[0][0], viewport[1][1]];
46210             var max = [viewport[1][0], viewport[0][1]];
46211             var bbox = geoExtent(projection.invert(min), projection.invert(max)).bbox();
46212             return _cache$2.rtree.search(bbox).map(function (d) {
46213               return d.data;
46214             });
46215           },
46216           // Get a QAItem from cache
46217           // NOTE: Don't change method name until UI v3 is merged
46218           getError: function getError(id) {
46219             return _cache$2.data[id];
46220           },
46221           // Replace a single QAItem in the cache
46222           replaceItem: function replaceItem(item) {
46223             if (!(item instanceof QAItem) || !item.id) return;
46224             _cache$2.data[item.id] = item;
46225             updateRtree$3(encodeIssueRtree$2(item), true); // true = replace
46226
46227             return item;
46228           },
46229           // Remove a single QAItem from the cache
46230           removeItem: function removeItem(item) {
46231             if (!(item instanceof QAItem) || !item.id) return;
46232             delete _cache$2.data[item.id];
46233             updateRtree$3(encodeIssueRtree$2(item), false); // false = remove
46234           },
46235           issueURL: function issueURL(item) {
46236             return "".concat(_krUrlRoot, "/report_map.php?schema=").concat(item.schema, "&error=").concat(item.id);
46237           },
46238           // Get an array of issues closed during this session.
46239           // Used to populate `closed:keepright` changeset tag
46240           getClosedIDs: function getClosedIDs() {
46241             return Object.keys(_cache$2.closed).sort();
46242           }
46243         };
46244
46245         var tiler$5 = utilTiler();
46246         var dispatch$6 = dispatch$8('loaded');
46247         var _tileZoom$2 = 14;
46248         var _impOsmUrls = {
46249           ow: 'https://grab.community.improve-osm.org/directionOfFlowService',
46250           mr: 'https://grab.community.improve-osm.org/missingGeoService',
46251           tr: 'https://grab.community.improve-osm.org/turnRestrictionService'
46252         };
46253         var _impOsmData = {
46254           icons: {}
46255         }; // This gets reassigned if reset
46256
46257         var _cache$1;
46258
46259         function abortRequest$5(i) {
46260           Object.values(i).forEach(function (controller) {
46261             if (controller) {
46262               controller.abort();
46263             }
46264           });
46265         }
46266
46267         function abortUnwantedRequests$2(cache, tiles) {
46268           Object.keys(cache.inflightTile).forEach(function (k) {
46269             var wanted = tiles.find(function (tile) {
46270               return k === tile.id;
46271             });
46272
46273             if (!wanted) {
46274               abortRequest$5(cache.inflightTile[k]);
46275               delete cache.inflightTile[k];
46276             }
46277           });
46278         }
46279
46280         function encodeIssueRtree$1(d) {
46281           return {
46282             minX: d.loc[0],
46283             minY: d.loc[1],
46284             maxX: d.loc[0],
46285             maxY: d.loc[1],
46286             data: d
46287           };
46288         } // Replace or remove QAItem from rtree
46289
46290
46291         function updateRtree$2(item, replace) {
46292           _cache$1.rtree.remove(item, function (a, b) {
46293             return a.data.id === b.data.id;
46294           });
46295
46296           if (replace) {
46297             _cache$1.rtree.insert(item);
46298           }
46299         }
46300
46301         function linkErrorObject(d) {
46302           return "<a class=\"error_object_link\">".concat(d, "</a>");
46303         }
46304
46305         function linkEntity(d) {
46306           return "<a class=\"error_entity_link\">".concat(d, "</a>");
46307         }
46308
46309         function pointAverage(points) {
46310           if (points.length) {
46311             var sum = points.reduce(function (acc, point) {
46312               return geoVecAdd(acc, [point.lon, point.lat]);
46313             }, [0, 0]);
46314             return geoVecScale(sum, 1 / points.length);
46315           } else {
46316             return [0, 0];
46317           }
46318         }
46319
46320         function relativeBearing(p1, p2) {
46321           var angle = Math.atan2(p2.lon - p1.lon, p2.lat - p1.lat);
46322
46323           if (angle < 0) {
46324             angle += 2 * Math.PI;
46325           } // Return degrees
46326
46327
46328           return angle * 180 / Math.PI;
46329         } // Assuming range [0,360)
46330
46331
46332         function cardinalDirection(bearing) {
46333           var dir = 45 * Math.round(bearing / 45);
46334           var compass = {
46335             0: 'north',
46336             45: 'northeast',
46337             90: 'east',
46338             135: 'southeast',
46339             180: 'south',
46340             225: 'southwest',
46341             270: 'west',
46342             315: 'northwest',
46343             360: 'north'
46344           };
46345           return _t("QA.improveOSM.directions.".concat(compass[dir]));
46346         } // Errors shouldn't obscure each other
46347
46348
46349         function preventCoincident$1(loc, bumpUp) {
46350           var coincident = false;
46351
46352           do {
46353             // first time, move marker up. after that, move marker right.
46354             var delta = coincident ? [0.00001, 0] : bumpUp ? [0, 0.00001] : [0, 0];
46355             loc = geoVecAdd(loc, delta);
46356             var bbox = geoExtent(loc).bbox();
46357             coincident = _cache$1.rtree.search(bbox).length;
46358           } while (coincident);
46359
46360           return loc;
46361         }
46362
46363         var serviceImproveOSM = {
46364           title: 'improveOSM',
46365           init: function init() {
46366             _mainFileFetcher.get('qa_data').then(function (d) {
46367               return _impOsmData = d.improveOSM;
46368             });
46369
46370             if (!_cache$1) {
46371               this.reset();
46372             }
46373
46374             this.event = utilRebind(this, dispatch$6, 'on');
46375           },
46376           reset: function reset() {
46377             if (_cache$1) {
46378               Object.values(_cache$1.inflightTile).forEach(abortRequest$5);
46379             }
46380
46381             _cache$1 = {
46382               data: {},
46383               loadedTile: {},
46384               inflightTile: {},
46385               inflightPost: {},
46386               closed: {},
46387               rtree: new RBush()
46388             };
46389           },
46390           loadIssues: function loadIssues(projection) {
46391             var _this = this;
46392
46393             var options = {
46394               client: 'iD',
46395               status: 'OPEN',
46396               zoom: '19' // Use a high zoom so that clusters aren't returned
46397
46398             }; // determine the needed tiles to cover the view
46399
46400             var tiles = tiler$5.zoomExtent([_tileZoom$2, _tileZoom$2]).getTiles(projection); // abort inflight requests that are no longer needed
46401
46402             abortUnwantedRequests$2(_cache$1, tiles); // issue new requests..
46403
46404             tiles.forEach(function (tile) {
46405               if (_cache$1.loadedTile[tile.id] || _cache$1.inflightTile[tile.id]) return;
46406
46407               var _tile$extent$rectangl = tile.extent.rectangle(),
46408                   _tile$extent$rectangl2 = _slicedToArray(_tile$extent$rectangl, 4),
46409                   east = _tile$extent$rectangl2[0],
46410                   north = _tile$extent$rectangl2[1],
46411                   west = _tile$extent$rectangl2[2],
46412                   south = _tile$extent$rectangl2[3];
46413
46414               var params = Object.assign({}, options, {
46415                 east: east,
46416                 south: south,
46417                 west: west,
46418                 north: north
46419               }); // 3 separate requests to store for each tile
46420
46421               var requests = {};
46422               Object.keys(_impOsmUrls).forEach(function (k) {
46423                 // We exclude WATER from missing geometry as it doesn't seem useful
46424                 // We use most confident one-way and turn restrictions only, still have false positives
46425                 var kParams = Object.assign({}, params, k === 'mr' ? {
46426                   type: 'PARKING,ROAD,BOTH,PATH'
46427                 } : {
46428                   confidenceLevel: 'C1'
46429                 });
46430                 var url = "".concat(_impOsmUrls[k], "/search?") + utilQsString(kParams);
46431                 var controller = new AbortController();
46432                 requests[k] = controller;
46433                 d3_json(url, {
46434                   signal: controller.signal
46435                 }).then(function (data) {
46436                   delete _cache$1.inflightTile[tile.id][k];
46437
46438                   if (!Object.keys(_cache$1.inflightTile[tile.id]).length) {
46439                     delete _cache$1.inflightTile[tile.id];
46440                     _cache$1.loadedTile[tile.id] = true;
46441                   } // Road segments at high zoom == oneways
46442
46443
46444                   if (data.roadSegments) {
46445                     data.roadSegments.forEach(function (feature) {
46446                       // Position error at the approximate middle of the segment
46447                       var points = feature.points,
46448                           wayId = feature.wayId,
46449                           fromNodeId = feature.fromNodeId,
46450                           toNodeId = feature.toNodeId;
46451                       var itemId = "".concat(wayId).concat(fromNodeId).concat(toNodeId);
46452                       var mid = points.length / 2;
46453                       var loc; // Even number of points, find midpoint of the middle two
46454                       // Odd number of points, use position of very middle point
46455
46456                       if (mid % 1 === 0) {
46457                         loc = pointAverage([points[mid - 1], points[mid]]);
46458                       } else {
46459                         mid = points[Math.floor(mid)];
46460                         loc = [mid.lon, mid.lat];
46461                       } // One-ways can land on same segment in opposite direction
46462
46463
46464                       loc = preventCoincident$1(loc, false);
46465                       var d = new QAItem(loc, _this, k, itemId, {
46466                         issueKey: k,
46467                         // used as a category
46468                         identifier: {
46469                           // used to post changes
46470                           wayId: wayId,
46471                           fromNodeId: fromNodeId,
46472                           toNodeId: toNodeId
46473                         },
46474                         objectId: wayId,
46475                         objectType: 'way'
46476                       }); // Variables used in the description
46477
46478                       d.replacements = {
46479                         percentage: feature.percentOfTrips,
46480                         num_trips: feature.numberOfTrips,
46481                         highway: linkErrorObject(_t('QA.keepRight.error_parts.highway')),
46482                         from_node: linkEntity('n' + feature.fromNodeId),
46483                         to_node: linkEntity('n' + feature.toNodeId)
46484                       };
46485                       _cache$1.data[d.id] = d;
46486
46487                       _cache$1.rtree.insert(encodeIssueRtree$1(d));
46488                     });
46489                   } // Tiles at high zoom == missing roads
46490
46491
46492                   if (data.tiles) {
46493                     data.tiles.forEach(function (feature) {
46494                       var type = feature.type,
46495                           x = feature.x,
46496                           y = feature.y,
46497                           numberOfTrips = feature.numberOfTrips;
46498                       var geoType = type.toLowerCase();
46499                       var itemId = "".concat(geoType).concat(x).concat(y).concat(numberOfTrips); // Average of recorded points should land on the missing geometry
46500                       // Missing geometry could happen to land on another error
46501
46502                       var loc = pointAverage(feature.points);
46503                       loc = preventCoincident$1(loc, false);
46504                       var d = new QAItem(loc, _this, "".concat(k, "-").concat(geoType), itemId, {
46505                         issueKey: k,
46506                         identifier: {
46507                           x: x,
46508                           y: y
46509                         }
46510                       });
46511                       d.replacements = {
46512                         num_trips: numberOfTrips,
46513                         geometry_type: _t("QA.improveOSM.geometry_types.".concat(geoType))
46514                       }; // -1 trips indicates data came from a 3rd party
46515
46516                       if (numberOfTrips === -1) {
46517                         d.desc = _t('QA.improveOSM.error_types.mr.description_alt', d.replacements);
46518                       }
46519
46520                       _cache$1.data[d.id] = d;
46521
46522                       _cache$1.rtree.insert(encodeIssueRtree$1(d));
46523                     });
46524                   } // Entities at high zoom == turn restrictions
46525
46526
46527                   if (data.entities) {
46528                     data.entities.forEach(function (feature) {
46529                       var point = feature.point,
46530                           id = feature.id,
46531                           segments = feature.segments,
46532                           numberOfPasses = feature.numberOfPasses,
46533                           turnType = feature.turnType;
46534                       var itemId = "".concat(id.replace(/[,:+#]/g, '_')); // Turn restrictions could be missing at same junction
46535                       // We also want to bump the error up so node is accessible
46536
46537                       var loc = preventCoincident$1([point.lon, point.lat], true); // Elements are presented in a strange way
46538
46539                       var ids = id.split(',');
46540                       var from_way = ids[0];
46541                       var via_node = ids[3];
46542                       var to_way = ids[2].split(':')[1];
46543                       var d = new QAItem(loc, _this, k, itemId, {
46544                         issueKey: k,
46545                         identifier: id,
46546                         objectId: via_node,
46547                         objectType: 'node'
46548                       }); // Travel direction along from_way clarifies the turn restriction
46549
46550                       var _segments$0$points = _slicedToArray(segments[0].points, 2),
46551                           p1 = _segments$0$points[0],
46552                           p2 = _segments$0$points[1];
46553
46554                       var dir_of_travel = cardinalDirection(relativeBearing(p1, p2)); // Variables used in the description
46555
46556                       d.replacements = {
46557                         num_passed: numberOfPasses,
46558                         num_trips: segments[0].numberOfTrips,
46559                         turn_restriction: turnType.toLowerCase(),
46560                         from_way: linkEntity('w' + from_way),
46561                         to_way: linkEntity('w' + to_way),
46562                         travel_direction: dir_of_travel,
46563                         junction: linkErrorObject(_t('QA.keepRight.error_parts.this_node'))
46564                       };
46565                       _cache$1.data[d.id] = d;
46566
46567                       _cache$1.rtree.insert(encodeIssueRtree$1(d));
46568
46569                       dispatch$6.call('loaded');
46570                     });
46571                   }
46572                 })["catch"](function () {
46573                   delete _cache$1.inflightTile[tile.id][k];
46574
46575                   if (!Object.keys(_cache$1.inflightTile[tile.id]).length) {
46576                     delete _cache$1.inflightTile[tile.id];
46577                     _cache$1.loadedTile[tile.id] = true;
46578                   }
46579                 });
46580               });
46581               _cache$1.inflightTile[tile.id] = requests;
46582             });
46583           },
46584           getComments: function getComments(item) {
46585             var _this2 = this;
46586
46587             // If comments already retrieved no need to do so again
46588             if (item.comments) {
46589               return Promise.resolve(item);
46590             }
46591
46592             var key = item.issueKey;
46593             var qParams = {};
46594
46595             if (key === 'ow') {
46596               qParams = item.identifier;
46597             } else if (key === 'mr') {
46598               qParams.tileX = item.identifier.x;
46599               qParams.tileY = item.identifier.y;
46600             } else if (key === 'tr') {
46601               qParams.targetId = item.identifier;
46602             }
46603
46604             var url = "".concat(_impOsmUrls[key], "/retrieveComments?") + utilQsString(qParams);
46605
46606             var cacheComments = function cacheComments(data) {
46607               // Assign directly for immediate use afterwards
46608               // comments are served newest to oldest
46609               item.comments = data.comments ? data.comments.reverse() : [];
46610
46611               _this2.replaceItem(item);
46612             };
46613
46614             return d3_json(url).then(cacheComments).then(function () {
46615               return item;
46616             });
46617           },
46618           postUpdate: function postUpdate(d, callback) {
46619             if (!serviceOsm.authenticated()) {
46620               // Username required in payload
46621               return callback({
46622                 message: 'Not Authenticated',
46623                 status: -3
46624               }, d);
46625             }
46626
46627             if (_cache$1.inflightPost[d.id]) {
46628               return callback({
46629                 message: 'Error update already inflight',
46630                 status: -2
46631               }, d);
46632             } // Payload can only be sent once username is established
46633
46634
46635             serviceOsm.userDetails(sendPayload.bind(this));
46636
46637             function sendPayload(err, user) {
46638               var _this3 = this;
46639
46640               if (err) {
46641                 return callback(err, d);
46642               }
46643
46644               var key = d.issueKey;
46645               var url = "".concat(_impOsmUrls[key], "/comment");
46646               var payload = {
46647                 username: user.display_name,
46648                 targetIds: [d.identifier]
46649               };
46650
46651               if (d.newStatus) {
46652                 payload.status = d.newStatus;
46653                 payload.text = 'status changed';
46654               } // Comment take place of default text
46655
46656
46657               if (d.newComment) {
46658                 payload.text = d.newComment;
46659               }
46660
46661               var controller = new AbortController();
46662               _cache$1.inflightPost[d.id] = controller;
46663               var options = {
46664                 method: 'POST',
46665                 signal: controller.signal,
46666                 body: JSON.stringify(payload)
46667               };
46668               d3_json(url, options).then(function () {
46669                 delete _cache$1.inflightPost[d.id]; // Just a comment, update error in cache
46670
46671                 if (!d.newStatus) {
46672                   var now = new Date();
46673                   var comments = d.comments ? d.comments : [];
46674                   comments.push({
46675                     username: payload.username,
46676                     text: payload.text,
46677                     timestamp: now.getTime() / 1000
46678                   });
46679
46680                   _this3.replaceItem(d.update({
46681                     comments: comments,
46682                     newComment: undefined
46683                   }));
46684                 } else {
46685                   _this3.removeItem(d);
46686
46687                   if (d.newStatus === 'SOLVED') {
46688                     // Keep track of the number of issues closed per type to tag the changeset
46689                     if (!(d.issueKey in _cache$1.closed)) {
46690                       _cache$1.closed[d.issueKey] = 0;
46691                     }
46692
46693                     _cache$1.closed[d.issueKey] += 1;
46694                   }
46695                 }
46696
46697                 if (callback) callback(null, d);
46698               })["catch"](function (err) {
46699                 delete _cache$1.inflightPost[d.id];
46700                 if (callback) callback(err.message);
46701               });
46702             }
46703           },
46704           // Get all cached QAItems covering the viewport
46705           getItems: function getItems(projection) {
46706             var viewport = projection.clipExtent();
46707             var min = [viewport[0][0], viewport[1][1]];
46708             var max = [viewport[1][0], viewport[0][1]];
46709             var bbox = geoExtent(projection.invert(min), projection.invert(max)).bbox();
46710             return _cache$1.rtree.search(bbox).map(function (d) {
46711               return d.data;
46712             });
46713           },
46714           // Get a QAItem from cache
46715           // NOTE: Don't change method name until UI v3 is merged
46716           getError: function getError(id) {
46717             return _cache$1.data[id];
46718           },
46719           // get the name of the icon to display for this item
46720           getIcon: function getIcon(itemType) {
46721             return _impOsmData.icons[itemType];
46722           },
46723           // Replace a single QAItem in the cache
46724           replaceItem: function replaceItem(issue) {
46725             if (!(issue instanceof QAItem) || !issue.id) return;
46726             _cache$1.data[issue.id] = issue;
46727             updateRtree$2(encodeIssueRtree$1(issue), true); // true = replace
46728
46729             return issue;
46730           },
46731           // Remove a single QAItem from the cache
46732           removeItem: function removeItem(issue) {
46733             if (!(issue instanceof QAItem) || !issue.id) return;
46734             delete _cache$1.data[issue.id];
46735             updateRtree$2(encodeIssueRtree$1(issue), false); // false = remove
46736           },
46737           // Used to populate `closed:improveosm:*` changeset tags
46738           getClosedCounts: function getClosedCounts() {
46739             return _cache$1.closed;
46740           }
46741         };
46742
46743         var defaults$5 = createCommonjsModule(function (module) {
46744           function getDefaults() {
46745             return {
46746               baseUrl: null,
46747               breaks: false,
46748               gfm: true,
46749               headerIds: true,
46750               headerPrefix: '',
46751               highlight: null,
46752               langPrefix: 'language-',
46753               mangle: true,
46754               pedantic: false,
46755               renderer: null,
46756               sanitize: false,
46757               sanitizer: null,
46758               silent: false,
46759               smartLists: false,
46760               smartypants: false,
46761               tokenizer: null,
46762               walkTokens: null,
46763               xhtml: false
46764             };
46765           }
46766
46767           function changeDefaults(newDefaults) {
46768             module.exports.defaults = newDefaults;
46769           }
46770
46771           module.exports = {
46772             defaults: getDefaults(),
46773             getDefaults: getDefaults,
46774             changeDefaults: changeDefaults
46775           };
46776         });
46777
46778         /**
46779          * Helpers
46780          */
46781         var escapeTest = /[&<>"']/;
46782         var escapeReplace = /[&<>"']/g;
46783         var escapeTestNoEncode = /[<>"']|&(?!#?\w+;)/;
46784         var escapeReplaceNoEncode = /[<>"']|&(?!#?\w+;)/g;
46785         var escapeReplacements = {
46786           '&': '&amp;',
46787           '<': '&lt;',
46788           '>': '&gt;',
46789           '"': '&quot;',
46790           "'": '&#39;'
46791         };
46792
46793         var getEscapeReplacement = function getEscapeReplacement(ch) {
46794           return escapeReplacements[ch];
46795         };
46796
46797         function escape$3(html, encode) {
46798           if (encode) {
46799             if (escapeTest.test(html)) {
46800               return html.replace(escapeReplace, getEscapeReplacement);
46801             }
46802           } else {
46803             if (escapeTestNoEncode.test(html)) {
46804               return html.replace(escapeReplaceNoEncode, getEscapeReplacement);
46805             }
46806           }
46807
46808           return html;
46809         }
46810
46811         var unescapeTest = /&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/ig;
46812
46813         function unescape$2(html) {
46814           // explicitly match decimal, hex, and named HTML entities
46815           return html.replace(unescapeTest, function (_, n) {
46816             n = n.toLowerCase();
46817             if (n === 'colon') return ':';
46818
46819             if (n.charAt(0) === '#') {
46820               return n.charAt(1) === 'x' ? String.fromCharCode(parseInt(n.substring(2), 16)) : String.fromCharCode(+n.substring(1));
46821             }
46822
46823             return '';
46824           });
46825         }
46826
46827         var caret = /(^|[^\[])\^/g;
46828
46829         function edit$1(regex, opt) {
46830           regex = regex.source || regex;
46831           opt = opt || '';
46832           var obj = {
46833             replace: function replace(name, val) {
46834               val = val.source || val;
46835               val = val.replace(caret, '$1');
46836               regex = regex.replace(name, val);
46837               return obj;
46838             },
46839             getRegex: function getRegex() {
46840               return new RegExp(regex, opt);
46841             }
46842           };
46843           return obj;
46844         }
46845
46846         var nonWordAndColonTest = /[^\w:]/g;
46847         var originIndependentUrl = /^$|^[a-z][a-z0-9+.-]*:|^[?#]/i;
46848
46849         function cleanUrl$1(sanitize, base, href) {
46850           if (sanitize) {
46851             var prot;
46852
46853             try {
46854               prot = decodeURIComponent(unescape$2(href)).replace(nonWordAndColonTest, '').toLowerCase();
46855             } catch (e) {
46856               return null;
46857             }
46858
46859             if (prot.indexOf('javascript:') === 0 || prot.indexOf('vbscript:') === 0 || prot.indexOf('data:') === 0) {
46860               return null;
46861             }
46862           }
46863
46864           if (base && !originIndependentUrl.test(href)) {
46865             href = resolveUrl$1(base, href);
46866           }
46867
46868           try {
46869             href = encodeURI(href).replace(/%25/g, '%');
46870           } catch (e) {
46871             return null;
46872           }
46873
46874           return href;
46875         }
46876
46877         var baseUrls = {};
46878         var justDomain = /^[^:]+:\/*[^/]*$/;
46879         var protocol = /^([^:]+:)[\s\S]*$/;
46880         var domain = /^([^:]+:\/*[^/]*)[\s\S]*$/;
46881
46882         function resolveUrl$1(base, href) {
46883           if (!baseUrls[' ' + base]) {
46884             // we can ignore everything in base after the last slash of its path component,
46885             // but we might need to add _that_
46886             // https://tools.ietf.org/html/rfc3986#section-3
46887             if (justDomain.test(base)) {
46888               baseUrls[' ' + base] = base + '/';
46889             } else {
46890               baseUrls[' ' + base] = rtrim$1(base, '/', true);
46891             }
46892           }
46893
46894           base = baseUrls[' ' + base];
46895           var relativeBase = base.indexOf(':') === -1;
46896
46897           if (href.substring(0, 2) === '//') {
46898             if (relativeBase) {
46899               return href;
46900             }
46901
46902             return base.replace(protocol, '$1') + href;
46903           } else if (href.charAt(0) === '/') {
46904             if (relativeBase) {
46905               return href;
46906             }
46907
46908             return base.replace(domain, '$1') + href;
46909           } else {
46910             return base + href;
46911           }
46912         }
46913
46914         var noopTest$1 = {
46915           exec: function noopTest() {}
46916         };
46917
46918         function merge$2(obj) {
46919           var i = 1,
46920               target,
46921               key;
46922
46923           for (; i < arguments.length; i++) {
46924             target = arguments[i];
46925
46926             for (key in target) {
46927               if (Object.prototype.hasOwnProperty.call(target, key)) {
46928                 obj[key] = target[key];
46929               }
46930             }
46931           }
46932
46933           return obj;
46934         }
46935
46936         function splitCells$1(tableRow, count) {
46937           // ensure that every cell-delimiting pipe has a space
46938           // before it to distinguish it from an escaped pipe
46939           var row = tableRow.replace(/\|/g, function (match, offset, str) {
46940             var escaped = false,
46941                 curr = offset;
46942
46943             while (--curr >= 0 && str[curr] === '\\') {
46944               escaped = !escaped;
46945             }
46946
46947             if (escaped) {
46948               // odd number of slashes means | is escaped
46949               // so we leave it alone
46950               return '|';
46951             } else {
46952               // add space before unescaped |
46953               return ' |';
46954             }
46955           }),
46956               cells = row.split(/ \|/);
46957           var i = 0;
46958
46959           if (cells.length > count) {
46960             cells.splice(count);
46961           } else {
46962             while (cells.length < count) {
46963               cells.push('');
46964             }
46965           }
46966
46967           for (; i < cells.length; i++) {
46968             // leading or trailing whitespace is ignored per the gfm spec
46969             cells[i] = cells[i].trim().replace(/\\\|/g, '|');
46970           }
46971
46972           return cells;
46973         } // Remove trailing 'c's. Equivalent to str.replace(/c*$/, '').
46974         // /c*$/ is vulnerable to REDOS.
46975         // invert: Remove suffix of non-c chars instead. Default falsey.
46976
46977
46978         function rtrim$1(str, c, invert) {
46979           var l = str.length;
46980
46981           if (l === 0) {
46982             return '';
46983           } // Length of suffix matching the invert condition.
46984
46985
46986           var suffLen = 0; // Step left until we fail to match the invert condition.
46987
46988           while (suffLen < l) {
46989             var currChar = str.charAt(l - suffLen - 1);
46990
46991             if (currChar === c && !invert) {
46992               suffLen++;
46993             } else if (currChar !== c && invert) {
46994               suffLen++;
46995             } else {
46996               break;
46997             }
46998           }
46999
47000           return str.substr(0, l - suffLen);
47001         }
47002
47003         function findClosingBracket$1(str, b) {
47004           if (str.indexOf(b[1]) === -1) {
47005             return -1;
47006           }
47007
47008           var l = str.length;
47009           var level = 0,
47010               i = 0;
47011
47012           for (; i < l; i++) {
47013             if (str[i] === '\\') {
47014               i++;
47015             } else if (str[i] === b[0]) {
47016               level++;
47017             } else if (str[i] === b[1]) {
47018               level--;
47019
47020               if (level < 0) {
47021                 return i;
47022               }
47023             }
47024           }
47025
47026           return -1;
47027         }
47028
47029         function checkSanitizeDeprecation$1(opt) {
47030           if (opt && opt.sanitize && !opt.silent) {
47031             console.warn('marked(): sanitize and sanitizer parameters are deprecated since version 0.7.0, should not be used and will be removed in the future. Read more here: https://marked.js.org/#/USING_ADVANCED.md#options');
47032           }
47033         } // copied from https://stackoverflow.com/a/5450113/806777
47034
47035
47036         function repeatString$1(pattern, count) {
47037           if (count < 1) {
47038             return '';
47039           }
47040
47041           var result = '';
47042
47043           while (count > 1) {
47044             if (count & 1) {
47045               result += pattern;
47046             }
47047
47048             count >>= 1;
47049             pattern += pattern;
47050           }
47051
47052           return result + pattern;
47053         }
47054
47055         var helpers = {
47056           escape: escape$3,
47057           unescape: unescape$2,
47058           edit: edit$1,
47059           cleanUrl: cleanUrl$1,
47060           resolveUrl: resolveUrl$1,
47061           noopTest: noopTest$1,
47062           merge: merge$2,
47063           splitCells: splitCells$1,
47064           rtrim: rtrim$1,
47065           findClosingBracket: findClosingBracket$1,
47066           checkSanitizeDeprecation: checkSanitizeDeprecation$1,
47067           repeatString: repeatString$1
47068         };
47069
47070         var defaults$4 = defaults$5.defaults;
47071         var rtrim = helpers.rtrim,
47072             splitCells = helpers.splitCells,
47073             _escape = helpers.escape,
47074             findClosingBracket = helpers.findClosingBracket;
47075
47076         function outputLink(cap, link, raw) {
47077           var href = link.href;
47078           var title = link.title ? _escape(link.title) : null;
47079           var text = cap[1].replace(/\\([\[\]])/g, '$1');
47080
47081           if (cap[0].charAt(0) !== '!') {
47082             return {
47083               type: 'link',
47084               raw: raw,
47085               href: href,
47086               title: title,
47087               text: text
47088             };
47089           } else {
47090             return {
47091               type: 'image',
47092               raw: raw,
47093               href: href,
47094               title: title,
47095               text: _escape(text)
47096             };
47097           }
47098         }
47099
47100         function indentCodeCompensation(raw, text) {
47101           var matchIndentToCode = raw.match(/^(\s+)(?:```)/);
47102
47103           if (matchIndentToCode === null) {
47104             return text;
47105           }
47106
47107           var indentToCode = matchIndentToCode[1];
47108           return text.split('\n').map(function (node) {
47109             var matchIndentInNode = node.match(/^\s+/);
47110
47111             if (matchIndentInNode === null) {
47112               return node;
47113             }
47114
47115             var _matchIndentInNode = _slicedToArray(matchIndentInNode, 1),
47116                 indentInNode = _matchIndentInNode[0];
47117
47118             if (indentInNode.length >= indentToCode.length) {
47119               return node.slice(indentToCode.length);
47120             }
47121
47122             return node;
47123           }).join('\n');
47124         }
47125         /**
47126          * Tokenizer
47127          */
47128
47129
47130         var Tokenizer_1 = /*#__PURE__*/function () {
47131           function Tokenizer(options) {
47132             _classCallCheck$1(this, Tokenizer);
47133
47134             this.options = options || defaults$4;
47135           }
47136
47137           _createClass$1(Tokenizer, [{
47138             key: "space",
47139             value: function space(src) {
47140               var cap = this.rules.block.newline.exec(src);
47141
47142               if (cap) {
47143                 if (cap[0].length > 1) {
47144                   return {
47145                     type: 'space',
47146                     raw: cap[0]
47147                   };
47148                 }
47149
47150                 return {
47151                   raw: '\n'
47152                 };
47153               }
47154             }
47155           }, {
47156             key: "code",
47157             value: function code(src) {
47158               var cap = this.rules.block.code.exec(src);
47159
47160               if (cap) {
47161                 var text = cap[0].replace(/^ {1,4}/gm, '');
47162                 return {
47163                   type: 'code',
47164                   raw: cap[0],
47165                   codeBlockStyle: 'indented',
47166                   text: !this.options.pedantic ? rtrim(text, '\n') : text
47167                 };
47168               }
47169             }
47170           }, {
47171             key: "fences",
47172             value: function fences(src) {
47173               var cap = this.rules.block.fences.exec(src);
47174
47175               if (cap) {
47176                 var raw = cap[0];
47177                 var text = indentCodeCompensation(raw, cap[3] || '');
47178                 return {
47179                   type: 'code',
47180                   raw: raw,
47181                   lang: cap[2] ? cap[2].trim() : cap[2],
47182                   text: text
47183                 };
47184               }
47185             }
47186           }, {
47187             key: "heading",
47188             value: function heading(src) {
47189               var cap = this.rules.block.heading.exec(src);
47190
47191               if (cap) {
47192                 var text = cap[2].trim(); // remove trailing #s
47193
47194                 if (/#$/.test(text)) {
47195                   var trimmed = rtrim(text, '#');
47196
47197                   if (this.options.pedantic) {
47198                     text = trimmed.trim();
47199                   } else if (!trimmed || / $/.test(trimmed)) {
47200                     // CommonMark requires space before trailing #s
47201                     text = trimmed.trim();
47202                   }
47203                 }
47204
47205                 return {
47206                   type: 'heading',
47207                   raw: cap[0],
47208                   depth: cap[1].length,
47209                   text: text
47210                 };
47211               }
47212             }
47213           }, {
47214             key: "nptable",
47215             value: function nptable(src) {
47216               var cap = this.rules.block.nptable.exec(src);
47217
47218               if (cap) {
47219                 var item = {
47220                   type: 'table',
47221                   header: splitCells(cap[1].replace(/^ *| *\| *$/g, '')),
47222                   align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
47223                   cells: cap[3] ? cap[3].replace(/\n$/, '').split('\n') : [],
47224                   raw: cap[0]
47225                 };
47226
47227                 if (item.header.length === item.align.length) {
47228                   var l = item.align.length;
47229                   var i;
47230
47231                   for (i = 0; i < l; i++) {
47232                     if (/^ *-+: *$/.test(item.align[i])) {
47233                       item.align[i] = 'right';
47234                     } else if (/^ *:-+: *$/.test(item.align[i])) {
47235                       item.align[i] = 'center';
47236                     } else if (/^ *:-+ *$/.test(item.align[i])) {
47237                       item.align[i] = 'left';
47238                     } else {
47239                       item.align[i] = null;
47240                     }
47241                   }
47242
47243                   l = item.cells.length;
47244
47245                   for (i = 0; i < l; i++) {
47246                     item.cells[i] = splitCells(item.cells[i], item.header.length);
47247                   }
47248
47249                   return item;
47250                 }
47251               }
47252             }
47253           }, {
47254             key: "hr",
47255             value: function hr(src) {
47256               var cap = this.rules.block.hr.exec(src);
47257
47258               if (cap) {
47259                 return {
47260                   type: 'hr',
47261                   raw: cap[0]
47262                 };
47263               }
47264             }
47265           }, {
47266             key: "blockquote",
47267             value: function blockquote(src) {
47268               var cap = this.rules.block.blockquote.exec(src);
47269
47270               if (cap) {
47271                 var text = cap[0].replace(/^ *> ?/gm, '');
47272                 return {
47273                   type: 'blockquote',
47274                   raw: cap[0],
47275                   text: text
47276                 };
47277               }
47278             }
47279           }, {
47280             key: "list",
47281             value: function list(src) {
47282               var cap = this.rules.block.list.exec(src);
47283
47284               if (cap) {
47285                 var raw = cap[0];
47286                 var bull = cap[2];
47287                 var isordered = bull.length > 1;
47288                 var list = {
47289                   type: 'list',
47290                   raw: raw,
47291                   ordered: isordered,
47292                   start: isordered ? +bull.slice(0, -1) : '',
47293                   loose: false,
47294                   items: []
47295                 }; // Get each top-level item.
47296
47297                 var itemMatch = cap[0].match(this.rules.block.item);
47298                 var next = false,
47299                     item,
47300                     space,
47301                     bcurr,
47302                     bnext,
47303                     addBack,
47304                     loose,
47305                     istask,
47306                     ischecked,
47307                     endMatch;
47308                 var l = itemMatch.length;
47309                 bcurr = this.rules.block.listItemStart.exec(itemMatch[0]);
47310
47311                 for (var i = 0; i < l; i++) {
47312                   item = itemMatch[i];
47313                   raw = item;
47314
47315                   if (!this.options.pedantic) {
47316                     // Determine if current item contains the end of the list
47317                     endMatch = item.match(new RegExp('\\n\\s*\\n {0,' + (bcurr[0].length - 1) + '}\\S'));
47318
47319                     if (endMatch) {
47320                       addBack = item.length - endMatch.index + itemMatch.slice(i + 1).join('\n').length;
47321                       list.raw = list.raw.substring(0, list.raw.length - addBack);
47322                       item = item.substring(0, endMatch.index);
47323                       raw = item;
47324                       l = i + 1;
47325                     }
47326                   } // Determine whether the next list item belongs here.
47327                   // Backpedal if it does not belong in this list.
47328
47329
47330                   if (i !== l - 1) {
47331                     bnext = this.rules.block.listItemStart.exec(itemMatch[i + 1]);
47332
47333                     if (!this.options.pedantic ? bnext[1].length >= bcurr[0].length || bnext[1].length > 3 : bnext[1].length > bcurr[1].length) {
47334                       // nested list or continuation
47335                       itemMatch.splice(i, 2, itemMatch[i] + (!this.options.pedantic && bnext[1].length < bcurr[0].length && !itemMatch[i].match(/\n$/) ? '' : '\n') + itemMatch[i + 1]);
47336                       i--;
47337                       l--;
47338                       continue;
47339                     } else if ( // different bullet style
47340                     !this.options.pedantic || this.options.smartLists ? bnext[2][bnext[2].length - 1] !== bull[bull.length - 1] : isordered === (bnext[2].length === 1)) {
47341                       addBack = itemMatch.slice(i + 1).join('\n').length;
47342                       list.raw = list.raw.substring(0, list.raw.length - addBack);
47343                       i = l - 1;
47344                     }
47345
47346                     bcurr = bnext;
47347                   } // Remove the list item's bullet
47348                   // so it is seen as the next token.
47349
47350
47351                   space = item.length;
47352                   item = item.replace(/^ *([*+-]|\d+[.)]) ?/, ''); // Outdent whatever the
47353                   // list item contains. Hacky.
47354
47355                   if (~item.indexOf('\n ')) {
47356                     space -= item.length;
47357                     item = !this.options.pedantic ? item.replace(new RegExp('^ {1,' + space + '}', 'gm'), '') : item.replace(/^ {1,4}/gm, '');
47358                   } // trim item newlines at end
47359
47360
47361                   item = rtrim(item, '\n');
47362
47363                   if (i !== l - 1) {
47364                     raw = raw + '\n';
47365                   } // Determine whether item is loose or not.
47366                   // Use: /(^|\n)(?! )[^\n]+\n\n(?!\s*$)/
47367                   // for discount behavior.
47368
47369
47370                   loose = next || /\n\n(?!\s*$)/.test(raw);
47371
47372                   if (i !== l - 1) {
47373                     next = raw.slice(-2) === '\n\n';
47374                     if (!loose) loose = next;
47375                   }
47376
47377                   if (loose) {
47378                     list.loose = true;
47379                   } // Check for task list items
47380
47381
47382                   if (this.options.gfm) {
47383                     istask = /^\[[ xX]\] /.test(item);
47384                     ischecked = undefined;
47385
47386                     if (istask) {
47387                       ischecked = item[1] !== ' ';
47388                       item = item.replace(/^\[[ xX]\] +/, '');
47389                     }
47390                   }
47391
47392                   list.items.push({
47393                     type: 'list_item',
47394                     raw: raw,
47395                     task: istask,
47396                     checked: ischecked,
47397                     loose: loose,
47398                     text: item
47399                   });
47400                 }
47401
47402                 return list;
47403               }
47404             }
47405           }, {
47406             key: "html",
47407             value: function html(src) {
47408               var cap = this.rules.block.html.exec(src);
47409
47410               if (cap) {
47411                 return {
47412                   type: this.options.sanitize ? 'paragraph' : 'html',
47413                   raw: cap[0],
47414                   pre: !this.options.sanitizer && (cap[1] === 'pre' || cap[1] === 'script' || cap[1] === 'style'),
47415                   text: this.options.sanitize ? this.options.sanitizer ? this.options.sanitizer(cap[0]) : _escape(cap[0]) : cap[0]
47416                 };
47417               }
47418             }
47419           }, {
47420             key: "def",
47421             value: function def(src) {
47422               var cap = this.rules.block.def.exec(src);
47423
47424               if (cap) {
47425                 if (cap[3]) cap[3] = cap[3].substring(1, cap[3].length - 1);
47426                 var tag = cap[1].toLowerCase().replace(/\s+/g, ' ');
47427                 return {
47428                   type: 'def',
47429                   tag: tag,
47430                   raw: cap[0],
47431                   href: cap[2],
47432                   title: cap[3]
47433                 };
47434               }
47435             }
47436           }, {
47437             key: "table",
47438             value: function table(src) {
47439               var cap = this.rules.block.table.exec(src);
47440
47441               if (cap) {
47442                 var item = {
47443                   type: 'table',
47444                   header: splitCells(cap[1].replace(/^ *| *\| *$/g, '')),
47445                   align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
47446                   cells: cap[3] ? cap[3].replace(/\n$/, '').split('\n') : []
47447                 };
47448
47449                 if (item.header.length === item.align.length) {
47450                   item.raw = cap[0];
47451                   var l = item.align.length;
47452                   var i;
47453
47454                   for (i = 0; i < l; i++) {
47455                     if (/^ *-+: *$/.test(item.align[i])) {
47456                       item.align[i] = 'right';
47457                     } else if (/^ *:-+: *$/.test(item.align[i])) {
47458                       item.align[i] = 'center';
47459                     } else if (/^ *:-+ *$/.test(item.align[i])) {
47460                       item.align[i] = 'left';
47461                     } else {
47462                       item.align[i] = null;
47463                     }
47464                   }
47465
47466                   l = item.cells.length;
47467
47468                   for (i = 0; i < l; i++) {
47469                     item.cells[i] = splitCells(item.cells[i].replace(/^ *\| *| *\| *$/g, ''), item.header.length);
47470                   }
47471
47472                   return item;
47473                 }
47474               }
47475             }
47476           }, {
47477             key: "lheading",
47478             value: function lheading(src) {
47479               var cap = this.rules.block.lheading.exec(src);
47480
47481               if (cap) {
47482                 return {
47483                   type: 'heading',
47484                   raw: cap[0],
47485                   depth: cap[2].charAt(0) === '=' ? 1 : 2,
47486                   text: cap[1]
47487                 };
47488               }
47489             }
47490           }, {
47491             key: "paragraph",
47492             value: function paragraph(src) {
47493               var cap = this.rules.block.paragraph.exec(src);
47494
47495               if (cap) {
47496                 return {
47497                   type: 'paragraph',
47498                   raw: cap[0],
47499                   text: cap[1].charAt(cap[1].length - 1) === '\n' ? cap[1].slice(0, -1) : cap[1]
47500                 };
47501               }
47502             }
47503           }, {
47504             key: "text",
47505             value: function text(src) {
47506               var cap = this.rules.block.text.exec(src);
47507
47508               if (cap) {
47509                 return {
47510                   type: 'text',
47511                   raw: cap[0],
47512                   text: cap[0]
47513                 };
47514               }
47515             }
47516           }, {
47517             key: "escape",
47518             value: function escape(src) {
47519               var cap = this.rules.inline.escape.exec(src);
47520
47521               if (cap) {
47522                 return {
47523                   type: 'escape',
47524                   raw: cap[0],
47525                   text: _escape(cap[1])
47526                 };
47527               }
47528             }
47529           }, {
47530             key: "tag",
47531             value: function tag(src, inLink, inRawBlock) {
47532               var cap = this.rules.inline.tag.exec(src);
47533
47534               if (cap) {
47535                 if (!inLink && /^<a /i.test(cap[0])) {
47536                   inLink = true;
47537                 } else if (inLink && /^<\/a>/i.test(cap[0])) {
47538                   inLink = false;
47539                 }
47540
47541                 if (!inRawBlock && /^<(pre|code|kbd|script)(\s|>)/i.test(cap[0])) {
47542                   inRawBlock = true;
47543                 } else if (inRawBlock && /^<\/(pre|code|kbd|script)(\s|>)/i.test(cap[0])) {
47544                   inRawBlock = false;
47545                 }
47546
47547                 return {
47548                   type: this.options.sanitize ? 'text' : 'html',
47549                   raw: cap[0],
47550                   inLink: inLink,
47551                   inRawBlock: inRawBlock,
47552                   text: this.options.sanitize ? this.options.sanitizer ? this.options.sanitizer(cap[0]) : _escape(cap[0]) : cap[0]
47553                 };
47554               }
47555             }
47556           }, {
47557             key: "link",
47558             value: function link(src) {
47559               var cap = this.rules.inline.link.exec(src);
47560
47561               if (cap) {
47562                 var trimmedUrl = cap[2].trim();
47563
47564                 if (!this.options.pedantic && /^</.test(trimmedUrl)) {
47565                   // commonmark requires matching angle brackets
47566                   if (!/>$/.test(trimmedUrl)) {
47567                     return;
47568                   } // ending angle bracket cannot be escaped
47569
47570
47571                   var rtrimSlash = rtrim(trimmedUrl.slice(0, -1), '\\');
47572
47573                   if ((trimmedUrl.length - rtrimSlash.length) % 2 === 0) {
47574                     return;
47575                   }
47576                 } else {
47577                   // find closing parenthesis
47578                   var lastParenIndex = findClosingBracket(cap[2], '()');
47579
47580                   if (lastParenIndex > -1) {
47581                     var start = cap[0].indexOf('!') === 0 ? 5 : 4;
47582                     var linkLen = start + cap[1].length + lastParenIndex;
47583                     cap[2] = cap[2].substring(0, lastParenIndex);
47584                     cap[0] = cap[0].substring(0, linkLen).trim();
47585                     cap[3] = '';
47586                   }
47587                 }
47588
47589                 var href = cap[2];
47590                 var title = '';
47591
47592                 if (this.options.pedantic) {
47593                   // split pedantic href and title
47594                   var link = /^([^'"]*[^\s])\s+(['"])(.*)\2/.exec(href);
47595
47596                   if (link) {
47597                     href = link[1];
47598                     title = link[3];
47599                   }
47600                 } else {
47601                   title = cap[3] ? cap[3].slice(1, -1) : '';
47602                 }
47603
47604                 href = href.trim();
47605
47606                 if (/^</.test(href)) {
47607                   if (this.options.pedantic && !/>$/.test(trimmedUrl)) {
47608                     // pedantic allows starting angle bracket without ending angle bracket
47609                     href = href.slice(1);
47610                   } else {
47611                     href = href.slice(1, -1);
47612                   }
47613                 }
47614
47615                 return outputLink(cap, {
47616                   href: href ? href.replace(this.rules.inline._escapes, '$1') : href,
47617                   title: title ? title.replace(this.rules.inline._escapes, '$1') : title
47618                 }, cap[0]);
47619               }
47620             }
47621           }, {
47622             key: "reflink",
47623             value: function reflink(src, links) {
47624               var cap;
47625
47626               if ((cap = this.rules.inline.reflink.exec(src)) || (cap = this.rules.inline.nolink.exec(src))) {
47627                 var link = (cap[2] || cap[1]).replace(/\s+/g, ' ');
47628                 link = links[link.toLowerCase()];
47629
47630                 if (!link || !link.href) {
47631                   var text = cap[0].charAt(0);
47632                   return {
47633                     type: 'text',
47634                     raw: text,
47635                     text: text
47636                   };
47637                 }
47638
47639                 return outputLink(cap, link, cap[0]);
47640               }
47641             }
47642           }, {
47643             key: "emStrong",
47644             value: function emStrong(src, maskedSrc) {
47645               var prevChar = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : '';
47646               var match = this.rules.inline.emStrong.lDelim.exec(src);
47647               if (!match) return; // _ can't be between two alphanumerics. \p{L}\p{N} includes non-english alphabet/numbers as well
47648
47649               if (match[3] && prevChar.match(/(?:[0-9A-Za-z\xAA\xB2\xB3\xB5\xB9\xBA\xBC-\xBE\xC0-\xD6\xD8-\xF6\xF8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0370-\u0374\u0376\u0377\u037A-\u037D\u037F\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5\u03F7-\u0481\u048A-\u052F\u0531-\u0556\u0559\u0560-\u0588\u05D0-\u05EA\u05EF-\u05F2\u0620-\u064A\u0660-\u0669\u066E\u066F\u0671-\u06D3\u06D5\u06E5\u06E6\u06EE-\u06FC\u06FF\u0710\u0712-\u072F\u074D-\u07A5\u07B1\u07C0-\u07EA\u07F4\u07F5\u07FA\u0800-\u0815\u081A\u0824\u0828\u0840-\u0858\u0860-\u086A\u08A0-\u08B4\u08B6-\u08C7\u0904-\u0939\u093D\u0950\u0958-\u0961\u0966-\u096F\u0971-\u0980\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09BD\u09CE\u09DC\u09DD\u09DF-\u09E1\u09E6-\u09F1\u09F4-\u09F9\u09FC\u0A05-\u0A0A\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39\u0A59-\u0A5C\u0A5E\u0A66-\u0A6F\u0A72-\u0A74\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABD\u0AD0\u0AE0\u0AE1\u0AE6-\u0AEF\u0AF9\u0B05-\u0B0C\u0B0F\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B3D\u0B5C\u0B5D\u0B5F-\u0B61\u0B66-\u0B6F\u0B71-\u0B77\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BD0\u0BE6-\u0BF2\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C39\u0C3D\u0C58-\u0C5A\u0C60\u0C61\u0C66-\u0C6F\u0C78-\u0C7E\u0C80\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CBD\u0CDE\u0CE0\u0CE1\u0CE6-\u0CEF\u0CF1\u0CF2\u0D04-\u0D0C\u0D0E-\u0D10\u0D12-\u0D3A\u0D3D\u0D4E\u0D54-\u0D56\u0D58-\u0D61\u0D66-\u0D78\u0D7A-\u0D7F\u0D85-\u0D96\u0D9A-\u0DB1\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0DE6-\u0DEF\u0E01-\u0E30\u0E32\u0E33\u0E40-\u0E46\u0E50-\u0E59\u0E81\u0E82\u0E84\u0E86-\u0E8A\u0E8C-\u0EA3\u0EA5\u0EA7-\u0EB0\u0EB2\u0EB3\u0EBD\u0EC0-\u0EC4\u0EC6\u0ED0-\u0ED9\u0EDC-\u0EDF\u0F00\u0F20-\u0F33\u0F40-\u0F47\u0F49-\u0F6C\u0F88-\u0F8C\u1000-\u102A\u103F-\u1049\u1050-\u1055\u105A-\u105D\u1061\u1065\u1066\u106E-\u1070\u1075-\u1081\u108E\u1090-\u1099\u10A0-\u10C5\u10C7\u10CD\u10D0-\u10FA\u10FC-\u1248\u124A-\u124D\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310\u1312-\u1315\u1318-\u135A\u1369-\u137C\u1380-\u138F\u13A0-\u13F5\u13F8-\u13FD\u1401-\u166C\u166F-\u167F\u1681-\u169A\u16A0-\u16EA\u16EE-\u16F8\u1700-\u170C\u170E-\u1711\u1720-\u1731\u1740-\u1751\u1760-\u176C\u176E-\u1770\u1780-\u17B3\u17D7\u17DC\u17E0-\u17E9\u17F0-\u17F9\u1810-\u1819\u1820-\u1878\u1880-\u1884\u1887-\u18A8\u18AA\u18B0-\u18F5\u1900-\u191E\u1946-\u196D\u1970-\u1974\u1980-\u19AB\u19B0-\u19C9\u19D0-\u19DA\u1A00-\u1A16\u1A20-\u1A54\u1A80-\u1A89\u1A90-\u1A99\u1AA7\u1B05-\u1B33\u1B45-\u1B4B\u1B50-\u1B59\u1B83-\u1BA0\u1BAE-\u1BE5\u1C00-\u1C23\u1C40-\u1C49\u1C4D-\u1C7D\u1C80-\u1C88\u1C90-\u1CBA\u1CBD-\u1CBF\u1CE9-\u1CEC\u1CEE-\u1CF3\u1CF5\u1CF6\u1CFA\u1D00-\u1DBF\u1E00-\u1F15\u1F18-\u1F1D\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u2070\u2071\u2074-\u2079\u207F-\u2089\u2090-\u209C\u2102\u2107\u210A-\u2113\u2115\u2119-\u211D\u2124\u2126\u2128\u212A-\u212D\u212F-\u2139\u213C-\u213F\u2145-\u2149\u214E\u2150-\u2189\u2460-\u249B\u24EA-\u24FF\u2776-\u2793\u2C00-\u2C2E\u2C30-\u2C5E\u2C60-\u2CE4\u2CEB-\u2CEE\u2CF2\u2CF3\u2CFD\u2D00-\u2D25\u2D27\u2D2D\u2D30-\u2D67\u2D6F\u2D80-\u2D96\u2DA0-\u2DA6\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE\u2DD0-\u2DD6\u2DD8-\u2DDE\u2E2F\u3005-\u3007\u3021-\u3029\u3031-\u3035\u3038-\u303C\u3041-\u3096\u309D-\u309F\u30A1-\u30FA\u30FC-\u30FF\u3105-\u312F\u3131-\u318E\u3192-\u3195\u31A0-\u31BF\u31F0-\u31FF\u3220-\u3229\u3248-\u324F\u3251-\u325F\u3280-\u3289\u32B1-\u32BF\u3400-\u4DBF\u4E00-\u9FFC\uA000-\uA48C\uA4D0-\uA4FD\uA500-\uA60C\uA610-\uA62B\uA640-\uA66E\uA67F-\uA69D\uA6A0-\uA6EF\uA717-\uA71F\uA722-\uA788\uA78B-\uA7BF\uA7C2-\uA7CA\uA7F5-\uA801\uA803-\uA805\uA807-\uA80A\uA80C-\uA822\uA830-\uA835\uA840-\uA873\uA882-\uA8B3\uA8D0-\uA8D9\uA8F2-\uA8F7\uA8FB\uA8FD\uA8FE\uA900-\uA925\uA930-\uA946\uA960-\uA97C\uA984-\uA9B2\uA9CF-\uA9D9\uA9E0-\uA9E4\uA9E6-\uA9FE\uAA00-\uAA28\uAA40-\uAA42\uAA44-\uAA4B\uAA50-\uAA59\uAA60-\uAA76\uAA7A\uAA7E-\uAAAF\uAAB1\uAAB5\uAAB6\uAAB9-\uAABD\uAAC0\uAAC2\uAADB-\uAADD\uAAE0-\uAAEA\uAAF2-\uAAF4\uAB01-\uAB06\uAB09-\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E\uAB30-\uAB5A\uAB5C-\uAB69\uAB70-\uABE2\uABF0-\uABF9\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFA6D\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFB1D\uFB1F-\uFB28\uFB2A-\uFB36\uFB38-\uFB3C\uFB3E\uFB40\uFB41\uFB43\uFB44\uFB46-\uFBB1\uFBD3-\uFD3D\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFB\uFE70-\uFE74\uFE76-\uFEFC\uFF10-\uFF19\uFF21-\uFF3A\uFF41-\uFF5A\uFF66-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF\uFFD2-\uFFD7\uFFDA-\uFFDC]|\uD800[\uDC00-\uDC0B\uDC0D-\uDC26\uDC28-\uDC3A\uDC3C\uDC3D\uDC3F-\uDC4D\uDC50-\uDC5D\uDC80-\uDCFA\uDD07-\uDD33\uDD40-\uDD78\uDD8A\uDD8B\uDE80-\uDE9C\uDEA0-\uDED0\uDEE1-\uDEFB\uDF00-\uDF23\uDF2D-\uDF4A\uDF50-\uDF75\uDF80-\uDF9D\uDFA0-\uDFC3\uDFC8-\uDFCF\uDFD1-\uDFD5]|\uD801[\uDC00-\uDC9D\uDCA0-\uDCA9\uDCB0-\uDCD3\uDCD8-\uDCFB\uDD00-\uDD27\uDD30-\uDD63\uDE00-\uDF36\uDF40-\uDF55\uDF60-\uDF67]|\uD802[\uDC00-\uDC05\uDC08\uDC0A-\uDC35\uDC37\uDC38\uDC3C\uDC3F-\uDC55\uDC58-\uDC76\uDC79-\uDC9E\uDCA7-\uDCAF\uDCE0-\uDCF2\uDCF4\uDCF5\uDCFB-\uDD1B\uDD20-\uDD39\uDD80-\uDDB7\uDDBC-\uDDCF\uDDD2-\uDE00\uDE10-\uDE13\uDE15-\uDE17\uDE19-\uDE35\uDE40-\uDE48\uDE60-\uDE7E\uDE80-\uDE9F\uDEC0-\uDEC7\uDEC9-\uDEE4\uDEEB-\uDEEF\uDF00-\uDF35\uDF40-\uDF55\uDF58-\uDF72\uDF78-\uDF91\uDFA9-\uDFAF]|\uD803[\uDC00-\uDC48\uDC80-\uDCB2\uDCC0-\uDCF2\uDCFA-\uDD23\uDD30-\uDD39\uDE60-\uDE7E\uDE80-\uDEA9\uDEB0\uDEB1\uDF00-\uDF27\uDF30-\uDF45\uDF51-\uDF54\uDFB0-\uDFCB\uDFE0-\uDFF6]|\uD804[\uDC03-\uDC37\uDC52-\uDC6F\uDC83-\uDCAF\uDCD0-\uDCE8\uDCF0-\uDCF9\uDD03-\uDD26\uDD36-\uDD3F\uDD44\uDD47\uDD50-\uDD72\uDD76\uDD83-\uDDB2\uDDC1-\uDDC4\uDDD0-\uDDDA\uDDDC\uDDE1-\uDDF4\uDE00-\uDE11\uDE13-\uDE2B\uDE80-\uDE86\uDE88\uDE8A-\uDE8D\uDE8F-\uDE9D\uDE9F-\uDEA8\uDEB0-\uDEDE\uDEF0-\uDEF9\uDF05-\uDF0C\uDF0F\uDF10\uDF13-\uDF28\uDF2A-\uDF30\uDF32\uDF33\uDF35-\uDF39\uDF3D\uDF50\uDF5D-\uDF61]|\uD805[\uDC00-\uDC34\uDC47-\uDC4A\uDC50-\uDC59\uDC5F-\uDC61\uDC80-\uDCAF\uDCC4\uDCC5\uDCC7\uDCD0-\uDCD9\uDD80-\uDDAE\uDDD8-\uDDDB\uDE00-\uDE2F\uDE44\uDE50-\uDE59\uDE80-\uDEAA\uDEB8\uDEC0-\uDEC9\uDF00-\uDF1A\uDF30-\uDF3B]|\uD806[\uDC00-\uDC2B\uDCA0-\uDCF2\uDCFF-\uDD06\uDD09\uDD0C-\uDD13\uDD15\uDD16\uDD18-\uDD2F\uDD3F\uDD41\uDD50-\uDD59\uDDA0-\uDDA7\uDDAA-\uDDD0\uDDE1\uDDE3\uDE00\uDE0B-\uDE32\uDE3A\uDE50\uDE5C-\uDE89\uDE9D\uDEC0-\uDEF8]|\uD807[\uDC00-\uDC08\uDC0A-\uDC2E\uDC40\uDC50-\uDC6C\uDC72-\uDC8F\uDD00-\uDD06\uDD08\uDD09\uDD0B-\uDD30\uDD46\uDD50-\uDD59\uDD60-\uDD65\uDD67\uDD68\uDD6A-\uDD89\uDD98\uDDA0-\uDDA9\uDEE0-\uDEF2\uDFB0\uDFC0-\uDFD4]|\uD808[\uDC00-\uDF99]|\uD809[\uDC00-\uDC6E\uDC80-\uDD43]|[\uD80C\uD81C-\uD820\uD822\uD840-\uD868\uD86A-\uD86C\uD86F-\uD872\uD874-\uD879\uD880-\uD883][\uDC00-\uDFFF]|\uD80D[\uDC00-\uDC2E]|\uD811[\uDC00-\uDE46]|\uD81A[\uDC00-\uDE38\uDE40-\uDE5E\uDE60-\uDE69\uDED0-\uDEED\uDF00-\uDF2F\uDF40-\uDF43\uDF50-\uDF59\uDF5B-\uDF61\uDF63-\uDF77\uDF7D-\uDF8F]|\uD81B[\uDE40-\uDE96\uDF00-\uDF4A\uDF50\uDF93-\uDF9F\uDFE0\uDFE1\uDFE3]|\uD821[\uDC00-\uDFF7]|\uD823[\uDC00-\uDCD5\uDD00-\uDD08]|\uD82C[\uDC00-\uDD1E\uDD50-\uDD52\uDD64-\uDD67\uDD70-\uDEFB]|\uD82F[\uDC00-\uDC6A\uDC70-\uDC7C\uDC80-\uDC88\uDC90-\uDC99]|\uD834[\uDEE0-\uDEF3\uDF60-\uDF78]|\uD835[\uDC00-\uDC54\uDC56-\uDC9C\uDC9E\uDC9F\uDCA2\uDCA5\uDCA6\uDCA9-\uDCAC\uDCAE-\uDCB9\uDCBB\uDCBD-\uDCC3\uDCC5-\uDD05\uDD07-\uDD0A\uDD0D-\uDD14\uDD16-\uDD1C\uDD1E-\uDD39\uDD3B-\uDD3E\uDD40-\uDD44\uDD46\uDD4A-\uDD50\uDD52-\uDEA5\uDEA8-\uDEC0\uDEC2-\uDEDA\uDEDC-\uDEFA\uDEFC-\uDF14\uDF16-\uDF34\uDF36-\uDF4E\uDF50-\uDF6E\uDF70-\uDF88\uDF8A-\uDFA8\uDFAA-\uDFC2\uDFC4-\uDFCB\uDFCE-\uDFFF]|\uD838[\uDD00-\uDD2C\uDD37-\uDD3D\uDD40-\uDD49\uDD4E\uDEC0-\uDEEB\uDEF0-\uDEF9]|\uD83A[\uDC00-\uDCC4\uDCC7-\uDCCF\uDD00-\uDD43\uDD4B\uDD50-\uDD59]|\uD83B[\uDC71-\uDCAB\uDCAD-\uDCAF\uDCB1-\uDCB4\uDD01-\uDD2D\uDD2F-\uDD3D\uDE00-\uDE03\uDE05-\uDE1F\uDE21\uDE22\uDE24\uDE27\uDE29-\uDE32\uDE34-\uDE37\uDE39\uDE3B\uDE42\uDE47\uDE49\uDE4B\uDE4D-\uDE4F\uDE51\uDE52\uDE54\uDE57\uDE59\uDE5B\uDE5D\uDE5F\uDE61\uDE62\uDE64\uDE67-\uDE6A\uDE6C-\uDE72\uDE74-\uDE77\uDE79-\uDE7C\uDE7E\uDE80-\uDE89\uDE8B-\uDE9B\uDEA1-\uDEA3\uDEA5-\uDEA9\uDEAB-\uDEBB]|\uD83C[\uDD00-\uDD0C]|\uD83E[\uDFF0-\uDFF9]|\uD869[\uDC00-\uDEDD\uDF00-\uDFFF]|\uD86D[\uDC00-\uDF34\uDF40-\uDFFF]|\uD86E[\uDC00-\uDC1D\uDC20-\uDFFF]|\uD873[\uDC00-\uDEA1\uDEB0-\uDFFF]|\uD87A[\uDC00-\uDFE0]|\uD87E[\uDC00-\uDE1D]|\uD884[\uDC00-\uDF4A])/)) return;
47650               var nextChar = match[1] || match[2] || '';
47651
47652               if (!nextChar || nextChar && (prevChar === '' || this.rules.inline.punctuation.exec(prevChar))) {
47653                 var lLength = match[0].length - 1;
47654                 var rDelim,
47655                     rLength,
47656                     delimTotal = lLength,
47657                     midDelimTotal = 0;
47658                 var endReg = match[0][0] === '*' ? this.rules.inline.emStrong.rDelimAst : this.rules.inline.emStrong.rDelimUnd;
47659                 endReg.lastIndex = 0; // Clip maskedSrc to same section of string as src (move to lexer?)
47660
47661                 maskedSrc = maskedSrc.slice(-1 * src.length + lLength);
47662
47663                 while ((match = endReg.exec(maskedSrc)) != null) {
47664                   rDelim = match[1] || match[2] || match[3] || match[4] || match[5] || match[6];
47665                   if (!rDelim) continue; // skip single * in __abc*abc__
47666
47667                   rLength = rDelim.length;
47668
47669                   if (match[3] || match[4]) {
47670                     // found another Left Delim
47671                     delimTotal += rLength;
47672                     continue;
47673                   } else if (match[5] || match[6]) {
47674                     // either Left or Right Delim
47675                     if (lLength % 3 && !((lLength + rLength) % 3)) {
47676                       midDelimTotal += rLength;
47677                       continue; // CommonMark Emphasis Rules 9-10
47678                     }
47679                   }
47680
47681                   delimTotal -= rLength;
47682                   if (delimTotal > 0) continue; // Haven't found enough closing delimiters
47683                   // Remove extra characters. *a*** -> *a*
47684
47685                   rLength = Math.min(rLength, rLength + delimTotal + midDelimTotal); // Create `em` if smallest delimiter has odd char count. *a***
47686
47687                   if (Math.min(lLength, rLength) % 2) {
47688                     return {
47689                       type: 'em',
47690                       raw: src.slice(0, lLength + match.index + rLength + 1),
47691                       text: src.slice(1, lLength + match.index + rLength)
47692                     };
47693                   } // Create 'strong' if smallest delimiter has even char count. **a***
47694
47695
47696                   return {
47697                     type: 'strong',
47698                     raw: src.slice(0, lLength + match.index + rLength + 1),
47699                     text: src.slice(2, lLength + match.index + rLength - 1)
47700                   };
47701                 }
47702               }
47703             }
47704           }, {
47705             key: "codespan",
47706             value: function codespan(src) {
47707               var cap = this.rules.inline.code.exec(src);
47708
47709               if (cap) {
47710                 var text = cap[2].replace(/\n/g, ' ');
47711                 var hasNonSpaceChars = /[^ ]/.test(text);
47712                 var hasSpaceCharsOnBothEnds = /^ /.test(text) && / $/.test(text);
47713
47714                 if (hasNonSpaceChars && hasSpaceCharsOnBothEnds) {
47715                   text = text.substring(1, text.length - 1);
47716                 }
47717
47718                 text = _escape(text, true);
47719                 return {
47720                   type: 'codespan',
47721                   raw: cap[0],
47722                   text: text
47723                 };
47724               }
47725             }
47726           }, {
47727             key: "br",
47728             value: function br(src) {
47729               var cap = this.rules.inline.br.exec(src);
47730
47731               if (cap) {
47732                 return {
47733                   type: 'br',
47734                   raw: cap[0]
47735                 };
47736               }
47737             }
47738           }, {
47739             key: "del",
47740             value: function del(src) {
47741               var cap = this.rules.inline.del.exec(src);
47742
47743               if (cap) {
47744                 return {
47745                   type: 'del',
47746                   raw: cap[0],
47747                   text: cap[2]
47748                 };
47749               }
47750             }
47751           }, {
47752             key: "autolink",
47753             value: function autolink(src, mangle) {
47754               var cap = this.rules.inline.autolink.exec(src);
47755
47756               if (cap) {
47757                 var text, href;
47758
47759                 if (cap[2] === '@') {
47760                   text = _escape(this.options.mangle ? mangle(cap[1]) : cap[1]);
47761                   href = 'mailto:' + text;
47762                 } else {
47763                   text = _escape(cap[1]);
47764                   href = text;
47765                 }
47766
47767                 return {
47768                   type: 'link',
47769                   raw: cap[0],
47770                   text: text,
47771                   href: href,
47772                   tokens: [{
47773                     type: 'text',
47774                     raw: text,
47775                     text: text
47776                   }]
47777                 };
47778               }
47779             }
47780           }, {
47781             key: "url",
47782             value: function url(src, mangle) {
47783               var cap;
47784
47785               if (cap = this.rules.inline.url.exec(src)) {
47786                 var text, href;
47787
47788                 if (cap[2] === '@') {
47789                   text = _escape(this.options.mangle ? mangle(cap[0]) : cap[0]);
47790                   href = 'mailto:' + text;
47791                 } else {
47792                   // do extended autolink path validation
47793                   var prevCapZero;
47794
47795                   do {
47796                     prevCapZero = cap[0];
47797                     cap[0] = this.rules.inline._backpedal.exec(cap[0])[0];
47798                   } while (prevCapZero !== cap[0]);
47799
47800                   text = _escape(cap[0]);
47801
47802                   if (cap[1] === 'www.') {
47803                     href = 'http://' + text;
47804                   } else {
47805                     href = text;
47806                   }
47807                 }
47808
47809                 return {
47810                   type: 'link',
47811                   raw: cap[0],
47812                   text: text,
47813                   href: href,
47814                   tokens: [{
47815                     type: 'text',
47816                     raw: text,
47817                     text: text
47818                   }]
47819                 };
47820               }
47821             }
47822           }, {
47823             key: "inlineText",
47824             value: function inlineText(src, inRawBlock, smartypants) {
47825               var cap = this.rules.inline.text.exec(src);
47826
47827               if (cap) {
47828                 var text;
47829
47830                 if (inRawBlock) {
47831                   text = this.options.sanitize ? this.options.sanitizer ? this.options.sanitizer(cap[0]) : _escape(cap[0]) : cap[0];
47832                 } else {
47833                   text = _escape(this.options.smartypants ? smartypants(cap[0]) : cap[0]);
47834                 }
47835
47836                 return {
47837                   type: 'text',
47838                   raw: cap[0],
47839                   text: text
47840                 };
47841               }
47842             }
47843           }]);
47844
47845           return Tokenizer;
47846         }();
47847
47848         var noopTest = helpers.noopTest,
47849             edit = helpers.edit,
47850             merge$1 = helpers.merge;
47851         /**
47852          * Block-Level Grammar
47853          */
47854
47855         var block$1 = {
47856           newline: /^(?: *(?:\n|$))+/,
47857           code: /^( {4}[^\n]+(?:\n(?: *(?:\n|$))*)?)+/,
47858           fences: /^ {0,3}(`{3,}(?=[^`\n]*\n)|~{3,})([^\n]*)\n(?:|([\s\S]*?)\n)(?: {0,3}\1[~`]* *(?:\n+|$)|$)/,
47859           hr: /^ {0,3}((?:- *){3,}|(?:_ *){3,}|(?:\* *){3,})(?:\n+|$)/,
47860           heading: /^ {0,3}(#{1,6})(?=\s|$)(.*)(?:\n+|$)/,
47861           blockquote: /^( {0,3}> ?(paragraph|[^\n]*)(?:\n|$))+/,
47862           list: /^( {0,3})(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?! {0,3}bull )\n*|\s*$)/,
47863           html: '^ {0,3}(?:' // optional indentation
47864           + '<(script|pre|style)[\\s>][\\s\\S]*?(?:</\\1>[^\\n]*\\n+|$)' // (1)
47865           + '|comment[^\\n]*(\\n+|$)' // (2)
47866           + '|<\\?[\\s\\S]*?(?:\\?>\\n*|$)' // (3)
47867           + '|<![A-Z][\\s\\S]*?(?:>\\n*|$)' // (4)
47868           + '|<!\\[CDATA\\[[\\s\\S]*?(?:\\]\\]>\\n*|$)' // (5)
47869           + '|</?(tag)(?: +|\\n|/?>)[\\s\\S]*?(?:(?:\\n *)+\\n|$)' // (6)
47870           + '|<(?!script|pre|style)([a-z][\\w-]*)(?:attribute)*? */?>(?=[ \\t]*(?:\\n|$))[\\s\\S]*?(?:(?:\\n *)+\\n|$)' // (7) open tag
47871           + '|</(?!script|pre|style)[a-z][\\w-]*\\s*>(?=[ \\t]*(?:\\n|$))[\\s\\S]*?(?:(?:\\n *)+\\n|$)' // (7) closing tag
47872           + ')',
47873           def: /^ {0,3}\[(label)\]: *\n? *<?([^\s>]+)>?(?:(?: +\n? *| *\n *)(title))? *(?:\n+|$)/,
47874           nptable: noopTest,
47875           table: noopTest,
47876           lheading: /^([^\n]+)\n {0,3}(=+|-+) *(?:\n+|$)/,
47877           // regex template, placeholders will be replaced according to different paragraph
47878           // interruption rules of commonmark and the original markdown spec:
47879           _paragraph: /^([^\n]+(?:\n(?!hr|heading|lheading|blockquote|fences|list|html| +\n)[^\n]+)*)/,
47880           text: /^[^\n]+/
47881         };
47882         block$1._label = /(?!\s*\])(?:\\[\[\]]|[^\[\]])+/;
47883         block$1._title = /(?:"(?:\\"?|[^"\\])*"|'[^'\n]*(?:\n[^'\n]+)*\n?'|\([^()]*\))/;
47884         block$1.def = edit(block$1.def).replace('label', block$1._label).replace('title', block$1._title).getRegex();
47885         block$1.bullet = /(?:[*+-]|\d{1,9}[.)])/;
47886         block$1.item = /^( *)(bull) ?[^\n]*(?:\n(?! *bull ?)[^\n]*)*/;
47887         block$1.item = edit(block$1.item, 'gm').replace(/bull/g, block$1.bullet).getRegex();
47888         block$1.listItemStart = edit(/^( *)(bull) */).replace('bull', block$1.bullet).getRegex();
47889         block$1.list = edit(block$1.list).replace(/bull/g, block$1.bullet).replace('hr', '\\n+(?=\\1?(?:(?:- *){3,}|(?:_ *){3,}|(?:\\* *){3,})(?:\\n+|$))').replace('def', '\\n+(?=' + block$1.def.source + ')').getRegex();
47890         block$1._tag = 'address|article|aside|base|basefont|blockquote|body|caption' + '|center|col|colgroup|dd|details|dialog|dir|div|dl|dt|fieldset|figcaption' + '|figure|footer|form|frame|frameset|h[1-6]|head|header|hr|html|iframe' + '|legend|li|link|main|menu|menuitem|meta|nav|noframes|ol|optgroup|option' + '|p|param|section|source|summary|table|tbody|td|tfoot|th|thead|title|tr' + '|track|ul';
47891         block$1._comment = /<!--(?!-?>)[\s\S]*?(?:-->|$)/;
47892         block$1.html = edit(block$1.html, 'i').replace('comment', block$1._comment).replace('tag', block$1._tag).replace('attribute', / +[a-zA-Z:_][\w.:-]*(?: *= *"[^"\n]*"| *= *'[^'\n]*'| *= *[^\s"'=<>`]+)?/).getRegex();
47893         block$1.paragraph = edit(block$1._paragraph).replace('hr', block$1.hr).replace('heading', ' {0,3}#{1,6} ').replace('|lheading', '') // setex headings don't interrupt commonmark paragraphs
47894         .replace('blockquote', ' {0,3}>').replace('fences', ' {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n').replace('list', ' {0,3}(?:[*+-]|1[.)]) ') // only lists starting from 1 can interrupt
47895         .replace('html', '</?(?:tag)(?: +|\\n|/?>)|<(?:script|pre|style|!--)').replace('tag', block$1._tag) // pars can be interrupted by type (6) html blocks
47896         .getRegex();
47897         block$1.blockquote = edit(block$1.blockquote).replace('paragraph', block$1.paragraph).getRegex();
47898         /**
47899          * Normal Block Grammar
47900          */
47901
47902         block$1.normal = merge$1({}, block$1);
47903         /**
47904          * GFM Block Grammar
47905          */
47906
47907         block$1.gfm = merge$1({}, block$1.normal, {
47908           nptable: '^ *([^|\\n ].*\\|.*)\\n' // Header
47909           + ' {0,3}([-:]+ *\\|[-| :]*)' // Align
47910           + '(?:\\n((?:(?!\\n|hr|heading|blockquote|code|fences|list|html).*(?:\\n|$))*)\\n*|$)',
47911           // Cells
47912           table: '^ *\\|(.+)\\n' // Header
47913           + ' {0,3}\\|?( *[-:]+[-| :]*)' // Align
47914           + '(?:\\n *((?:(?!\\n|hr|heading|blockquote|code|fences|list|html).*(?:\\n|$))*)\\n*|$)' // Cells
47915
47916         });
47917         block$1.gfm.nptable = edit(block$1.gfm.nptable).replace('hr', block$1.hr).replace('heading', ' {0,3}#{1,6} ').replace('blockquote', ' {0,3}>').replace('code', ' {4}[^\\n]').replace('fences', ' {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n').replace('list', ' {0,3}(?:[*+-]|1[.)]) ') // only lists starting from 1 can interrupt
47918         .replace('html', '</?(?:tag)(?: +|\\n|/?>)|<(?:script|pre|style|!--)').replace('tag', block$1._tag) // tables can be interrupted by type (6) html blocks
47919         .getRegex();
47920         block$1.gfm.table = edit(block$1.gfm.table).replace('hr', block$1.hr).replace('heading', ' {0,3}#{1,6} ').replace('blockquote', ' {0,3}>').replace('code', ' {4}[^\\n]').replace('fences', ' {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n').replace('list', ' {0,3}(?:[*+-]|1[.)]) ') // only lists starting from 1 can interrupt
47921         .replace('html', '</?(?:tag)(?: +|\\n|/?>)|<(?:script|pre|style|!--)').replace('tag', block$1._tag) // tables can be interrupted by type (6) html blocks
47922         .getRegex();
47923         /**
47924          * Pedantic grammar (original John Gruber's loose markdown specification)
47925          */
47926
47927         block$1.pedantic = merge$1({}, block$1.normal, {
47928           html: edit('^ *(?:comment *(?:\\n|\\s*$)' + '|<(tag)[\\s\\S]+?</\\1> *(?:\\n{2,}|\\s*$)' // closed tag
47929           + '|<tag(?:"[^"]*"|\'[^\']*\'|\\s[^\'"/>\\s]*)*?/?> *(?:\\n{2,}|\\s*$))').replace('comment', block$1._comment).replace(/tag/g, '(?!(?:' + 'a|em|strong|small|s|cite|q|dfn|abbr|data|time|code|var|samp|kbd|sub' + '|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo|span|br|wbr|ins|del|img)' + '\\b)\\w+(?!:|[^\\w\\s@]*@)\\b').getRegex(),
47930           def: /^ *\[([^\]]+)\]: *<?([^\s>]+)>?(?: +(["(][^\n]+[")]))? *(?:\n+|$)/,
47931           heading: /^(#{1,6})(.*)(?:\n+|$)/,
47932           fences: noopTest,
47933           // fences not supported
47934           paragraph: edit(block$1.normal._paragraph).replace('hr', block$1.hr).replace('heading', ' *#{1,6} *[^\n]').replace('lheading', block$1.lheading).replace('blockquote', ' {0,3}>').replace('|fences', '').replace('|list', '').replace('|html', '').getRegex()
47935         });
47936         /**
47937          * Inline-Level Grammar
47938          */
47939
47940         var inline$1 = {
47941           escape: /^\\([!"#$%&'()*+,\-./:;<=>?@\[\]\\^_`{|}~])/,
47942           autolink: /^<(scheme:[^\s\x00-\x1f<>]*|email)>/,
47943           url: noopTest,
47944           tag: '^comment' + '|^</[a-zA-Z][\\w:-]*\\s*>' // self-closing tag
47945           + '|^<[a-zA-Z][\\w-]*(?:attribute)*?\\s*/?>' // open tag
47946           + '|^<\\?[\\s\\S]*?\\?>' // processing instruction, e.g. <?php ?>
47947           + '|^<![a-zA-Z]+\\s[\\s\\S]*?>' // declaration, e.g. <!DOCTYPE html>
47948           + '|^<!\\[CDATA\\[[\\s\\S]*?\\]\\]>',
47949           // CDATA section
47950           link: /^!?\[(label)\]\(\s*(href)(?:\s+(title))?\s*\)/,
47951           reflink: /^!?\[(label)\]\[(?!\s*\])((?:\\[\[\]]?|[^\[\]\\])+)\]/,
47952           nolink: /^!?\[(?!\s*\])((?:\[[^\[\]]*\]|\\[\[\]]|[^\[\]])*)\](?:\[\])?/,
47953           reflinkSearch: 'reflink|nolink(?!\\()',
47954           emStrong: {
47955             lDelim: /^(?:\*+(?:([punct_])|[^\s*]))|^_+(?:([punct*])|([^\s_]))/,
47956             //        (1) and (2) can only be a Right Delimiter. (3) and (4) can only be Left.  (5) and (6) can be either Left or Right.
47957             //        () Skip other delimiter (1) #***                   (2) a***#, a***                   (3) #***a, ***a                 (4) ***#              (5) #***#                 (6) a***a
47958             rDelimAst: /\_\_[^_*]*?\*[^_*]*?\_\_|[punct_](\*+)(?=[\s]|$)|[^punct*_\s](\*+)(?=[punct_\s]|$)|[punct_\s](\*+)(?=[^punct*_\s])|[\s](\*+)(?=[punct_])|[punct_](\*+)(?=[punct_])|[^punct*_\s](\*+)(?=[^punct*_\s])/,
47959             rDelimUnd: /\*\*[^_*]*?\_[^_*]*?\*\*|[punct*](\_+)(?=[\s]|$)|[^punct*_\s](\_+)(?=[punct*\s]|$)|[punct*\s](\_+)(?=[^punct*_\s])|[\s](\_+)(?=[punct*])|[punct*](\_+)(?=[punct*])/ // ^- Not allowed for _
47960
47961           },
47962           code: /^(`+)([^`]|[^`][\s\S]*?[^`])\1(?!`)/,
47963           br: /^( {2,}|\\)\n(?!\s*$)/,
47964           del: noopTest,
47965           text: /^(`+|[^`])(?:(?= {2,}\n)|[\s\S]*?(?:(?=[\\<!\[`*_]|\b_|$)|[^ ](?= {2,}\n)))/,
47966           punctuation: /^([\spunctuation])/
47967         }; // list of punctuation marks from CommonMark spec
47968         // without * and _ to handle the different emphasis markers * and _
47969
47970         inline$1._punctuation = '!"#$%&\'()+\\-.,/:;<=>?@\\[\\]`^{|}~';
47971         inline$1.punctuation = edit(inline$1.punctuation).replace(/punctuation/g, inline$1._punctuation).getRegex(); // sequences em should skip over [title](link), `code`, <html>
47972
47973         inline$1.blockSkip = /\[[^\]]*?\]\([^\)]*?\)|`[^`]*?`|<[^>]*?>/g;
47974         inline$1.escapedEmSt = /\\\*|\\_/g;
47975         inline$1._comment = edit(block$1._comment).replace('(?:-->|$)', '-->').getRegex();
47976         inline$1.emStrong.lDelim = edit(inline$1.emStrong.lDelim).replace(/punct/g, inline$1._punctuation).getRegex();
47977         inline$1.emStrong.rDelimAst = edit(inline$1.emStrong.rDelimAst, 'g').replace(/punct/g, inline$1._punctuation).getRegex();
47978         inline$1.emStrong.rDelimUnd = edit(inline$1.emStrong.rDelimUnd, 'g').replace(/punct/g, inline$1._punctuation).getRegex();
47979         inline$1._escapes = /\\([!"#$%&'()*+,\-./:;<=>?@\[\]\\^_`{|}~])/g;
47980         inline$1._scheme = /[a-zA-Z][a-zA-Z0-9+.-]{1,31}/;
47981         inline$1._email = /[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+(@)[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)+(?![-_])/;
47982         inline$1.autolink = edit(inline$1.autolink).replace('scheme', inline$1._scheme).replace('email', inline$1._email).getRegex();
47983         inline$1._attribute = /\s+[a-zA-Z:_][\w.:-]*(?:\s*=\s*"[^"]*"|\s*=\s*'[^']*'|\s*=\s*[^\s"'=<>`]+)?/;
47984         inline$1.tag = edit(inline$1.tag).replace('comment', inline$1._comment).replace('attribute', inline$1._attribute).getRegex();
47985         inline$1._label = /(?:\[(?:\\.|[^\[\]\\])*\]|\\.|`[^`]*`|[^\[\]\\`])*?/;
47986         inline$1._href = /<(?:\\.|[^\n<>\\])+>|[^\s\x00-\x1f]*/;
47987         inline$1._title = /"(?:\\"?|[^"\\])*"|'(?:\\'?|[^'\\])*'|\((?:\\\)?|[^)\\])*\)/;
47988         inline$1.link = edit(inline$1.link).replace('label', inline$1._label).replace('href', inline$1._href).replace('title', inline$1._title).getRegex();
47989         inline$1.reflink = edit(inline$1.reflink).replace('label', inline$1._label).getRegex();
47990         inline$1.reflinkSearch = edit(inline$1.reflinkSearch, 'g').replace('reflink', inline$1.reflink).replace('nolink', inline$1.nolink).getRegex();
47991         /**
47992          * Normal Inline Grammar
47993          */
47994
47995         inline$1.normal = merge$1({}, inline$1);
47996         /**
47997          * Pedantic Inline Grammar
47998          */
47999
48000         inline$1.pedantic = merge$1({}, inline$1.normal, {
48001           strong: {
48002             start: /^__|\*\*/,
48003             middle: /^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,
48004             endAst: /\*\*(?!\*)/g,
48005             endUnd: /__(?!_)/g
48006           },
48007           em: {
48008             start: /^_|\*/,
48009             middle: /^()\*(?=\S)([\s\S]*?\S)\*(?!\*)|^_(?=\S)([\s\S]*?\S)_(?!_)/,
48010             endAst: /\*(?!\*)/g,
48011             endUnd: /_(?!_)/g
48012           },
48013           link: edit(/^!?\[(label)\]\((.*?)\)/).replace('label', inline$1._label).getRegex(),
48014           reflink: edit(/^!?\[(label)\]\s*\[([^\]]*)\]/).replace('label', inline$1._label).getRegex()
48015         });
48016         /**
48017          * GFM Inline Grammar
48018          */
48019
48020         inline$1.gfm = merge$1({}, inline$1.normal, {
48021           escape: edit(inline$1.escape).replace('])', '~|])').getRegex(),
48022           _extended_email: /[A-Za-z0-9._+-]+(@)[a-zA-Z0-9-_]+(?:\.[a-zA-Z0-9-_]*[a-zA-Z0-9])+(?![-_])/,
48023           url: /^((?:ftp|https?):\/\/|www\.)(?:[a-zA-Z0-9\-]+\.?)+[^\s<]*|^email/,
48024           _backpedal: /(?:[^?!.,:;*_~()&]+|\([^)]*\)|&(?![a-zA-Z0-9]+;$)|[?!.,:;*_~)]+(?!$))+/,
48025           del: /^(~~?)(?=[^\s~])([\s\S]*?[^\s~])\1(?=[^~]|$)/,
48026           text: /^([`~]+|[^`~])(?:(?= {2,}\n)|(?=[a-zA-Z0-9.!#$%&'*+\/=?_`{\|}~-]+@)|[\s\S]*?(?:(?=[\\<!\[`*~_]|\b_|https?:\/\/|ftp:\/\/|www\.|$)|[^ ](?= {2,}\n)|[^a-zA-Z0-9.!#$%&'*+\/=?_`{\|}~-](?=[a-zA-Z0-9.!#$%&'*+\/=?_`{\|}~-]+@)))/
48027         });
48028         inline$1.gfm.url = edit(inline$1.gfm.url, 'i').replace('email', inline$1.gfm._extended_email).getRegex();
48029         /**
48030          * GFM + Line Breaks Inline Grammar
48031          */
48032
48033         inline$1.breaks = merge$1({}, inline$1.gfm, {
48034           br: edit(inline$1.br).replace('{2,}', '*').getRegex(),
48035           text: edit(inline$1.gfm.text).replace('\\b_', '\\b_| {2,}\\n').replace(/\{2,\}/g, '*').getRegex()
48036         });
48037         var rules = {
48038           block: block$1,
48039           inline: inline$1
48040         };
48041
48042         var defaults$3 = defaults$5.defaults;
48043         var block = rules.block,
48044             inline = rules.inline;
48045         var repeatString = helpers.repeatString;
48046         /**
48047          * smartypants text replacement
48048          */
48049
48050         function smartypants(text) {
48051           return text // em-dashes
48052           .replace(/---/g, "\u2014") // en-dashes
48053           .replace(/--/g, "\u2013") // opening singles
48054           .replace(/(^|[-\u2014/(\[{"\s])'/g, "$1\u2018") // closing singles & apostrophes
48055           .replace(/'/g, "\u2019") // opening doubles
48056           .replace(/(^|[-\u2014/(\[{\u2018\s])"/g, "$1\u201C") // closing doubles
48057           .replace(/"/g, "\u201D") // ellipses
48058           .replace(/\.{3}/g, "\u2026");
48059         }
48060         /**
48061          * mangle email addresses
48062          */
48063
48064
48065         function mangle(text) {
48066           var out = '',
48067               i,
48068               ch;
48069           var l = text.length;
48070
48071           for (i = 0; i < l; i++) {
48072             ch = text.charCodeAt(i);
48073
48074             if (Math.random() > 0.5) {
48075               ch = 'x' + ch.toString(16);
48076             }
48077
48078             out += '&#' + ch + ';';
48079           }
48080
48081           return out;
48082         }
48083         /**
48084          * Block Lexer
48085          */
48086
48087
48088         var Lexer_1 = /*#__PURE__*/function () {
48089           function Lexer(options) {
48090             _classCallCheck$1(this, Lexer);
48091
48092             this.tokens = [];
48093             this.tokens.links = Object.create(null);
48094             this.options = options || defaults$3;
48095             this.options.tokenizer = this.options.tokenizer || new Tokenizer_1();
48096             this.tokenizer = this.options.tokenizer;
48097             this.tokenizer.options = this.options;
48098             var rules = {
48099               block: block.normal,
48100               inline: inline.normal
48101             };
48102
48103             if (this.options.pedantic) {
48104               rules.block = block.pedantic;
48105               rules.inline = inline.pedantic;
48106             } else if (this.options.gfm) {
48107               rules.block = block.gfm;
48108
48109               if (this.options.breaks) {
48110                 rules.inline = inline.breaks;
48111               } else {
48112                 rules.inline = inline.gfm;
48113               }
48114             }
48115
48116             this.tokenizer.rules = rules;
48117           }
48118           /**
48119            * Expose Rules
48120            */
48121
48122
48123           _createClass$1(Lexer, [{
48124             key: "lex",
48125             value:
48126             /**
48127              * Preprocessing
48128              */
48129             function lex(src) {
48130               src = src.replace(/\r\n|\r/g, '\n').replace(/\t/g, '    ');
48131               this.blockTokens(src, this.tokens, true);
48132               this.inline(this.tokens);
48133               return this.tokens;
48134             }
48135             /**
48136              * Lexing
48137              */
48138
48139           }, {
48140             key: "blockTokens",
48141             value: function blockTokens(src) {
48142               var tokens = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : [];
48143               var top = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true;
48144
48145               if (this.options.pedantic) {
48146                 src = src.replace(/^ +$/gm, '');
48147               }
48148
48149               var token, i, l, lastToken;
48150
48151               while (src) {
48152                 // newline
48153                 if (token = this.tokenizer.space(src)) {
48154                   src = src.substring(token.raw.length);
48155
48156                   if (token.type) {
48157                     tokens.push(token);
48158                   }
48159
48160                   continue;
48161                 } // code
48162
48163
48164                 if (token = this.tokenizer.code(src)) {
48165                   src = src.substring(token.raw.length);
48166                   lastToken = tokens[tokens.length - 1]; // An indented code block cannot interrupt a paragraph.
48167
48168                   if (lastToken && lastToken.type === 'paragraph') {
48169                     lastToken.raw += '\n' + token.raw;
48170                     lastToken.text += '\n' + token.text;
48171                   } else {
48172                     tokens.push(token);
48173                   }
48174
48175                   continue;
48176                 } // fences
48177
48178
48179                 if (token = this.tokenizer.fences(src)) {
48180                   src = src.substring(token.raw.length);
48181                   tokens.push(token);
48182                   continue;
48183                 } // heading
48184
48185
48186                 if (token = this.tokenizer.heading(src)) {
48187                   src = src.substring(token.raw.length);
48188                   tokens.push(token);
48189                   continue;
48190                 } // table no leading pipe (gfm)
48191
48192
48193                 if (token = this.tokenizer.nptable(src)) {
48194                   src = src.substring(token.raw.length);
48195                   tokens.push(token);
48196                   continue;
48197                 } // hr
48198
48199
48200                 if (token = this.tokenizer.hr(src)) {
48201                   src = src.substring(token.raw.length);
48202                   tokens.push(token);
48203                   continue;
48204                 } // blockquote
48205
48206
48207                 if (token = this.tokenizer.blockquote(src)) {
48208                   src = src.substring(token.raw.length);
48209                   token.tokens = this.blockTokens(token.text, [], top);
48210                   tokens.push(token);
48211                   continue;
48212                 } // list
48213
48214
48215                 if (token = this.tokenizer.list(src)) {
48216                   src = src.substring(token.raw.length);
48217                   l = token.items.length;
48218
48219                   for (i = 0; i < l; i++) {
48220                     token.items[i].tokens = this.blockTokens(token.items[i].text, [], false);
48221                   }
48222
48223                   tokens.push(token);
48224                   continue;
48225                 } // html
48226
48227
48228                 if (token = this.tokenizer.html(src)) {
48229                   src = src.substring(token.raw.length);
48230                   tokens.push(token);
48231                   continue;
48232                 } // def
48233
48234
48235                 if (top && (token = this.tokenizer.def(src))) {
48236                   src = src.substring(token.raw.length);
48237
48238                   if (!this.tokens.links[token.tag]) {
48239                     this.tokens.links[token.tag] = {
48240                       href: token.href,
48241                       title: token.title
48242                     };
48243                   }
48244
48245                   continue;
48246                 } // table (gfm)
48247
48248
48249                 if (token = this.tokenizer.table(src)) {
48250                   src = src.substring(token.raw.length);
48251                   tokens.push(token);
48252                   continue;
48253                 } // lheading
48254
48255
48256                 if (token = this.tokenizer.lheading(src)) {
48257                   src = src.substring(token.raw.length);
48258                   tokens.push(token);
48259                   continue;
48260                 } // top-level paragraph
48261
48262
48263                 if (top && (token = this.tokenizer.paragraph(src))) {
48264                   src = src.substring(token.raw.length);
48265                   tokens.push(token);
48266                   continue;
48267                 } // text
48268
48269
48270                 if (token = this.tokenizer.text(src)) {
48271                   src = src.substring(token.raw.length);
48272                   lastToken = tokens[tokens.length - 1];
48273
48274                   if (lastToken && lastToken.type === 'text') {
48275                     lastToken.raw += '\n' + token.raw;
48276                     lastToken.text += '\n' + token.text;
48277                   } else {
48278                     tokens.push(token);
48279                   }
48280
48281                   continue;
48282                 }
48283
48284                 if (src) {
48285                   var errMsg = 'Infinite loop on byte: ' + src.charCodeAt(0);
48286
48287                   if (this.options.silent) {
48288                     console.error(errMsg);
48289                     break;
48290                   } else {
48291                     throw new Error(errMsg);
48292                   }
48293                 }
48294               }
48295
48296               return tokens;
48297             }
48298           }, {
48299             key: "inline",
48300             value: function inline(tokens) {
48301               var i, j, k, l2, row, token;
48302               var l = tokens.length;
48303
48304               for (i = 0; i < l; i++) {
48305                 token = tokens[i];
48306
48307                 switch (token.type) {
48308                   case 'paragraph':
48309                   case 'text':
48310                   case 'heading':
48311                     {
48312                       token.tokens = [];
48313                       this.inlineTokens(token.text, token.tokens);
48314                       break;
48315                     }
48316
48317                   case 'table':
48318                     {
48319                       token.tokens = {
48320                         header: [],
48321                         cells: []
48322                       }; // header
48323
48324                       l2 = token.header.length;
48325
48326                       for (j = 0; j < l2; j++) {
48327                         token.tokens.header[j] = [];
48328                         this.inlineTokens(token.header[j], token.tokens.header[j]);
48329                       } // cells
48330
48331
48332                       l2 = token.cells.length;
48333
48334                       for (j = 0; j < l2; j++) {
48335                         row = token.cells[j];
48336                         token.tokens.cells[j] = [];
48337
48338                         for (k = 0; k < row.length; k++) {
48339                           token.tokens.cells[j][k] = [];
48340                           this.inlineTokens(row[k], token.tokens.cells[j][k]);
48341                         }
48342                       }
48343
48344                       break;
48345                     }
48346
48347                   case 'blockquote':
48348                     {
48349                       this.inline(token.tokens);
48350                       break;
48351                     }
48352
48353                   case 'list':
48354                     {
48355                       l2 = token.items.length;
48356
48357                       for (j = 0; j < l2; j++) {
48358                         this.inline(token.items[j].tokens);
48359                       }
48360
48361                       break;
48362                     }
48363                 }
48364               }
48365
48366               return tokens;
48367             }
48368             /**
48369              * Lexing/Compiling
48370              */
48371
48372           }, {
48373             key: "inlineTokens",
48374             value: function inlineTokens(src) {
48375               var tokens = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : [];
48376               var inLink = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
48377               var inRawBlock = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false;
48378               var token, lastToken; // String with links masked to avoid interference with em and strong
48379
48380               var maskedSrc = src;
48381               var match;
48382               var keepPrevChar, prevChar; // Mask out reflinks
48383
48384               if (this.tokens.links) {
48385                 var links = Object.keys(this.tokens.links);
48386
48387                 if (links.length > 0) {
48388                   while ((match = this.tokenizer.rules.inline.reflinkSearch.exec(maskedSrc)) != null) {
48389                     if (links.includes(match[0].slice(match[0].lastIndexOf('[') + 1, -1))) {
48390                       maskedSrc = maskedSrc.slice(0, match.index) + '[' + repeatString('a', match[0].length - 2) + ']' + maskedSrc.slice(this.tokenizer.rules.inline.reflinkSearch.lastIndex);
48391                     }
48392                   }
48393                 }
48394               } // Mask out other blocks
48395
48396
48397               while ((match = this.tokenizer.rules.inline.blockSkip.exec(maskedSrc)) != null) {
48398                 maskedSrc = maskedSrc.slice(0, match.index) + '[' + repeatString('a', match[0].length - 2) + ']' + maskedSrc.slice(this.tokenizer.rules.inline.blockSkip.lastIndex);
48399               } // Mask out escaped em & strong delimiters
48400
48401
48402               while ((match = this.tokenizer.rules.inline.escapedEmSt.exec(maskedSrc)) != null) {
48403                 maskedSrc = maskedSrc.slice(0, match.index) + '++' + maskedSrc.slice(this.tokenizer.rules.inline.escapedEmSt.lastIndex);
48404               }
48405
48406               while (src) {
48407                 if (!keepPrevChar) {
48408                   prevChar = '';
48409                 }
48410
48411                 keepPrevChar = false; // escape
48412
48413                 if (token = this.tokenizer.escape(src)) {
48414                   src = src.substring(token.raw.length);
48415                   tokens.push(token);
48416                   continue;
48417                 } // tag
48418
48419
48420                 if (token = this.tokenizer.tag(src, inLink, inRawBlock)) {
48421                   src = src.substring(token.raw.length);
48422                   inLink = token.inLink;
48423                   inRawBlock = token.inRawBlock;
48424                   var _lastToken = tokens[tokens.length - 1];
48425
48426                   if (_lastToken && token.type === 'text' && _lastToken.type === 'text') {
48427                     _lastToken.raw += token.raw;
48428                     _lastToken.text += token.text;
48429                   } else {
48430                     tokens.push(token);
48431                   }
48432
48433                   continue;
48434                 } // link
48435
48436
48437                 if (token = this.tokenizer.link(src)) {
48438                   src = src.substring(token.raw.length);
48439
48440                   if (token.type === 'link') {
48441                     token.tokens = this.inlineTokens(token.text, [], true, inRawBlock);
48442                   }
48443
48444                   tokens.push(token);
48445                   continue;
48446                 } // reflink, nolink
48447
48448
48449                 if (token = this.tokenizer.reflink(src, this.tokens.links)) {
48450                   src = src.substring(token.raw.length);
48451                   var _lastToken2 = tokens[tokens.length - 1];
48452
48453                   if (token.type === 'link') {
48454                     token.tokens = this.inlineTokens(token.text, [], true, inRawBlock);
48455                     tokens.push(token);
48456                   } else if (_lastToken2 && token.type === 'text' && _lastToken2.type === 'text') {
48457                     _lastToken2.raw += token.raw;
48458                     _lastToken2.text += token.text;
48459                   } else {
48460                     tokens.push(token);
48461                   }
48462
48463                   continue;
48464                 } // em & strong
48465
48466
48467                 if (token = this.tokenizer.emStrong(src, maskedSrc, prevChar)) {
48468                   src = src.substring(token.raw.length);
48469                   token.tokens = this.inlineTokens(token.text, [], inLink, inRawBlock);
48470                   tokens.push(token);
48471                   continue;
48472                 } // code
48473
48474
48475                 if (token = this.tokenizer.codespan(src)) {
48476                   src = src.substring(token.raw.length);
48477                   tokens.push(token);
48478                   continue;
48479                 } // br
48480
48481
48482                 if (token = this.tokenizer.br(src)) {
48483                   src = src.substring(token.raw.length);
48484                   tokens.push(token);
48485                   continue;
48486                 } // del (gfm)
48487
48488
48489                 if (token = this.tokenizer.del(src)) {
48490                   src = src.substring(token.raw.length);
48491                   token.tokens = this.inlineTokens(token.text, [], inLink, inRawBlock);
48492                   tokens.push(token);
48493                   continue;
48494                 } // autolink
48495
48496
48497                 if (token = this.tokenizer.autolink(src, mangle)) {
48498                   src = src.substring(token.raw.length);
48499                   tokens.push(token);
48500                   continue;
48501                 } // url (gfm)
48502
48503
48504                 if (!inLink && (token = this.tokenizer.url(src, mangle))) {
48505                   src = src.substring(token.raw.length);
48506                   tokens.push(token);
48507                   continue;
48508                 } // text
48509
48510
48511                 if (token = this.tokenizer.inlineText(src, inRawBlock, smartypants)) {
48512                   src = src.substring(token.raw.length);
48513
48514                   if (token.raw.slice(-1) !== '_') {
48515                     // Track prevChar before string of ____ started
48516                     prevChar = token.raw.slice(-1);
48517                   }
48518
48519                   keepPrevChar = true;
48520                   lastToken = tokens[tokens.length - 1];
48521
48522                   if (lastToken && lastToken.type === 'text') {
48523                     lastToken.raw += token.raw;
48524                     lastToken.text += token.text;
48525                   } else {
48526                     tokens.push(token);
48527                   }
48528
48529                   continue;
48530                 }
48531
48532                 if (src) {
48533                   var errMsg = 'Infinite loop on byte: ' + src.charCodeAt(0);
48534
48535                   if (this.options.silent) {
48536                     console.error(errMsg);
48537                     break;
48538                   } else {
48539                     throw new Error(errMsg);
48540                   }
48541                 }
48542               }
48543
48544               return tokens;
48545             }
48546           }], [{
48547             key: "rules",
48548             get: function get() {
48549               return {
48550                 block: block,
48551                 inline: inline
48552               };
48553             }
48554             /**
48555              * Static Lex Method
48556              */
48557
48558           }, {
48559             key: "lex",
48560             value: function lex(src, options) {
48561               var lexer = new Lexer(options);
48562               return lexer.lex(src);
48563             }
48564             /**
48565              * Static Lex Inline Method
48566              */
48567
48568           }, {
48569             key: "lexInline",
48570             value: function lexInline(src, options) {
48571               var lexer = new Lexer(options);
48572               return lexer.inlineTokens(src);
48573             }
48574           }]);
48575
48576           return Lexer;
48577         }();
48578
48579         var defaults$2 = defaults$5.defaults;
48580         var cleanUrl = helpers.cleanUrl,
48581             escape$2 = helpers.escape;
48582         /**
48583          * Renderer
48584          */
48585
48586         var Renderer_1 = /*#__PURE__*/function () {
48587           function Renderer(options) {
48588             _classCallCheck$1(this, Renderer);
48589
48590             this.options = options || defaults$2;
48591           }
48592
48593           _createClass$1(Renderer, [{
48594             key: "code",
48595             value: function code(_code, infostring, escaped) {
48596               var lang = (infostring || '').match(/\S*/)[0];
48597
48598               if (this.options.highlight) {
48599                 var out = this.options.highlight(_code, lang);
48600
48601                 if (out != null && out !== _code) {
48602                   escaped = true;
48603                   _code = out;
48604                 }
48605               }
48606
48607               _code = _code.replace(/\n$/, '') + '\n';
48608
48609               if (!lang) {
48610                 return '<pre><code>' + (escaped ? _code : escape$2(_code, true)) + '</code></pre>\n';
48611               }
48612
48613               return '<pre><code class="' + this.options.langPrefix + escape$2(lang, true) + '">' + (escaped ? _code : escape$2(_code, true)) + '</code></pre>\n';
48614             }
48615           }, {
48616             key: "blockquote",
48617             value: function blockquote(quote) {
48618               return '<blockquote>\n' + quote + '</blockquote>\n';
48619             }
48620           }, {
48621             key: "html",
48622             value: function html(_html) {
48623               return _html;
48624             }
48625           }, {
48626             key: "heading",
48627             value: function heading(text, level, raw, slugger) {
48628               if (this.options.headerIds) {
48629                 return '<h' + level + ' id="' + this.options.headerPrefix + slugger.slug(raw) + '">' + text + '</h' + level + '>\n';
48630               } // ignore IDs
48631
48632
48633               return '<h' + level + '>' + text + '</h' + level + '>\n';
48634             }
48635           }, {
48636             key: "hr",
48637             value: function hr() {
48638               return this.options.xhtml ? '<hr/>\n' : '<hr>\n';
48639             }
48640           }, {
48641             key: "list",
48642             value: function list(body, ordered, start) {
48643               var type = ordered ? 'ol' : 'ul',
48644                   startatt = ordered && start !== 1 ? ' start="' + start + '"' : '';
48645               return '<' + type + startatt + '>\n' + body + '</' + type + '>\n';
48646             }
48647           }, {
48648             key: "listitem",
48649             value: function listitem(text) {
48650               return '<li>' + text + '</li>\n';
48651             }
48652           }, {
48653             key: "checkbox",
48654             value: function checkbox(checked) {
48655               return '<input ' + (checked ? 'checked="" ' : '') + 'disabled="" type="checkbox"' + (this.options.xhtml ? ' /' : '') + '> ';
48656             }
48657           }, {
48658             key: "paragraph",
48659             value: function paragraph(text) {
48660               return '<p>' + text + '</p>\n';
48661             }
48662           }, {
48663             key: "table",
48664             value: function table(header, body) {
48665               if (body) body = '<tbody>' + body + '</tbody>';
48666               return '<table>\n' + '<thead>\n' + header + '</thead>\n' + body + '</table>\n';
48667             }
48668           }, {
48669             key: "tablerow",
48670             value: function tablerow(content) {
48671               return '<tr>\n' + content + '</tr>\n';
48672             }
48673           }, {
48674             key: "tablecell",
48675             value: function tablecell(content, flags) {
48676               var type = flags.header ? 'th' : 'td';
48677               var tag = flags.align ? '<' + type + ' align="' + flags.align + '">' : '<' + type + '>';
48678               return tag + content + '</' + type + '>\n';
48679             } // span level renderer
48680
48681           }, {
48682             key: "strong",
48683             value: function strong(text) {
48684               return '<strong>' + text + '</strong>';
48685             }
48686           }, {
48687             key: "em",
48688             value: function em(text) {
48689               return '<em>' + text + '</em>';
48690             }
48691           }, {
48692             key: "codespan",
48693             value: function codespan(text) {
48694               return '<code>' + text + '</code>';
48695             }
48696           }, {
48697             key: "br",
48698             value: function br() {
48699               return this.options.xhtml ? '<br/>' : '<br>';
48700             }
48701           }, {
48702             key: "del",
48703             value: function del(text) {
48704               return '<del>' + text + '</del>';
48705             }
48706           }, {
48707             key: "link",
48708             value: function link(href, title, text) {
48709               href = cleanUrl(this.options.sanitize, this.options.baseUrl, href);
48710
48711               if (href === null) {
48712                 return text;
48713               }
48714
48715               var out = '<a href="' + escape$2(href) + '"';
48716
48717               if (title) {
48718                 out += ' title="' + title + '"';
48719               }
48720
48721               out += '>' + text + '</a>';
48722               return out;
48723             }
48724           }, {
48725             key: "image",
48726             value: function image(href, title, text) {
48727               href = cleanUrl(this.options.sanitize, this.options.baseUrl, href);
48728
48729               if (href === null) {
48730                 return text;
48731               }
48732
48733               var out = '<img src="' + href + '" alt="' + text + '"';
48734
48735               if (title) {
48736                 out += ' title="' + title + '"';
48737               }
48738
48739               out += this.options.xhtml ? '/>' : '>';
48740               return out;
48741             }
48742           }, {
48743             key: "text",
48744             value: function text(_text) {
48745               return _text;
48746             }
48747           }]);
48748
48749           return Renderer;
48750         }();
48751
48752         /**
48753          * TextRenderer
48754          * returns only the textual part of the token
48755          */
48756         var TextRenderer_1 = /*#__PURE__*/function () {
48757           function TextRenderer() {
48758             _classCallCheck$1(this, TextRenderer);
48759           }
48760
48761           _createClass$1(TextRenderer, [{
48762             key: "strong",
48763             value: // no need for block level renderers
48764             function strong(text) {
48765               return text;
48766             }
48767           }, {
48768             key: "em",
48769             value: function em(text) {
48770               return text;
48771             }
48772           }, {
48773             key: "codespan",
48774             value: function codespan(text) {
48775               return text;
48776             }
48777           }, {
48778             key: "del",
48779             value: function del(text) {
48780               return text;
48781             }
48782           }, {
48783             key: "html",
48784             value: function html(text) {
48785               return text;
48786             }
48787           }, {
48788             key: "text",
48789             value: function text(_text) {
48790               return _text;
48791             }
48792           }, {
48793             key: "link",
48794             value: function link(href, title, text) {
48795               return '' + text;
48796             }
48797           }, {
48798             key: "image",
48799             value: function image(href, title, text) {
48800               return '' + text;
48801             }
48802           }, {
48803             key: "br",
48804             value: function br() {
48805               return '';
48806             }
48807           }]);
48808
48809           return TextRenderer;
48810         }();
48811
48812         /**
48813          * Slugger generates header id
48814          */
48815         var Slugger_1 = /*#__PURE__*/function () {
48816           function Slugger() {
48817             _classCallCheck$1(this, Slugger);
48818
48819             this.seen = {};
48820           }
48821
48822           _createClass$1(Slugger, [{
48823             key: "serialize",
48824             value: function serialize(value) {
48825               return value.toLowerCase().trim() // remove html tags
48826               .replace(/<[!\/a-z].*?>/ig, '') // remove unwanted chars
48827               .replace(/[\u2000-\u206F\u2E00-\u2E7F\\'!"#$%&()*+,./:;<=>?@[\]^`{|}~]/g, '').replace(/\s/g, '-');
48828             }
48829             /**
48830              * Finds the next safe (unique) slug to use
48831              */
48832
48833           }, {
48834             key: "getNextSafeSlug",
48835             value: function getNextSafeSlug(originalSlug, isDryRun) {
48836               var slug = originalSlug;
48837               var occurenceAccumulator = 0;
48838
48839               if (this.seen.hasOwnProperty(slug)) {
48840                 occurenceAccumulator = this.seen[originalSlug];
48841
48842                 do {
48843                   occurenceAccumulator++;
48844                   slug = originalSlug + '-' + occurenceAccumulator;
48845                 } while (this.seen.hasOwnProperty(slug));
48846               }
48847
48848               if (!isDryRun) {
48849                 this.seen[originalSlug] = occurenceAccumulator;
48850                 this.seen[slug] = 0;
48851               }
48852
48853               return slug;
48854             }
48855             /**
48856              * Convert string to unique id
48857              * @param {object} options
48858              * @param {boolean} options.dryrun Generates the next unique slug without updating the internal accumulator.
48859              */
48860
48861           }, {
48862             key: "slug",
48863             value: function slug(value) {
48864               var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
48865               var slug = this.serialize(value);
48866               return this.getNextSafeSlug(slug, options.dryrun);
48867             }
48868           }]);
48869
48870           return Slugger;
48871         }();
48872
48873         var defaults$1 = defaults$5.defaults;
48874         var unescape$1 = helpers.unescape;
48875         /**
48876          * Parsing & Compiling
48877          */
48878
48879         var Parser_1 = /*#__PURE__*/function () {
48880           function Parser(options) {
48881             _classCallCheck$1(this, Parser);
48882
48883             this.options = options || defaults$1;
48884             this.options.renderer = this.options.renderer || new Renderer_1();
48885             this.renderer = this.options.renderer;
48886             this.renderer.options = this.options;
48887             this.textRenderer = new TextRenderer_1();
48888             this.slugger = new Slugger_1();
48889           }
48890           /**
48891            * Static Parse Method
48892            */
48893
48894
48895           _createClass$1(Parser, [{
48896             key: "parse",
48897             value:
48898             /**
48899              * Parse Loop
48900              */
48901             function parse(tokens) {
48902               var top = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
48903               var out = '',
48904                   i,
48905                   j,
48906                   k,
48907                   l2,
48908                   l3,
48909                   row,
48910                   cell,
48911                   header,
48912                   body,
48913                   token,
48914                   ordered,
48915                   start,
48916                   loose,
48917                   itemBody,
48918                   item,
48919                   checked,
48920                   task,
48921                   checkbox;
48922               var l = tokens.length;
48923
48924               for (i = 0; i < l; i++) {
48925                 token = tokens[i];
48926
48927                 switch (token.type) {
48928                   case 'space':
48929                     {
48930                       continue;
48931                     }
48932
48933                   case 'hr':
48934                     {
48935                       out += this.renderer.hr();
48936                       continue;
48937                     }
48938
48939                   case 'heading':
48940                     {
48941                       out += this.renderer.heading(this.parseInline(token.tokens), token.depth, unescape$1(this.parseInline(token.tokens, this.textRenderer)), this.slugger);
48942                       continue;
48943                     }
48944
48945                   case 'code':
48946                     {
48947                       out += this.renderer.code(token.text, token.lang, token.escaped);
48948                       continue;
48949                     }
48950
48951                   case 'table':
48952                     {
48953                       header = ''; // header
48954
48955                       cell = '';
48956                       l2 = token.header.length;
48957
48958                       for (j = 0; j < l2; j++) {
48959                         cell += this.renderer.tablecell(this.parseInline(token.tokens.header[j]), {
48960                           header: true,
48961                           align: token.align[j]
48962                         });
48963                       }
48964
48965                       header += this.renderer.tablerow(cell);
48966                       body = '';
48967                       l2 = token.cells.length;
48968
48969                       for (j = 0; j < l2; j++) {
48970                         row = token.tokens.cells[j];
48971                         cell = '';
48972                         l3 = row.length;
48973
48974                         for (k = 0; k < l3; k++) {
48975                           cell += this.renderer.tablecell(this.parseInline(row[k]), {
48976                             header: false,
48977                             align: token.align[k]
48978                           });
48979                         }
48980
48981                         body += this.renderer.tablerow(cell);
48982                       }
48983
48984                       out += this.renderer.table(header, body);
48985                       continue;
48986                     }
48987
48988                   case 'blockquote':
48989                     {
48990                       body = this.parse(token.tokens);
48991                       out += this.renderer.blockquote(body);
48992                       continue;
48993                     }
48994
48995                   case 'list':
48996                     {
48997                       ordered = token.ordered;
48998                       start = token.start;
48999                       loose = token.loose;
49000                       l2 = token.items.length;
49001                       body = '';
49002
49003                       for (j = 0; j < l2; j++) {
49004                         item = token.items[j];
49005                         checked = item.checked;
49006                         task = item.task;
49007                         itemBody = '';
49008
49009                         if (item.task) {
49010                           checkbox = this.renderer.checkbox(checked);
49011
49012                           if (loose) {
49013                             if (item.tokens.length > 0 && item.tokens[0].type === 'text') {
49014                               item.tokens[0].text = checkbox + ' ' + item.tokens[0].text;
49015
49016                               if (item.tokens[0].tokens && item.tokens[0].tokens.length > 0 && item.tokens[0].tokens[0].type === 'text') {
49017                                 item.tokens[0].tokens[0].text = checkbox + ' ' + item.tokens[0].tokens[0].text;
49018                               }
49019                             } else {
49020                               item.tokens.unshift({
49021                                 type: 'text',
49022                                 text: checkbox
49023                               });
49024                             }
49025                           } else {
49026                             itemBody += checkbox;
49027                           }
49028                         }
49029
49030                         itemBody += this.parse(item.tokens, loose);
49031                         body += this.renderer.listitem(itemBody, task, checked);
49032                       }
49033
49034                       out += this.renderer.list(body, ordered, start);
49035                       continue;
49036                     }
49037
49038                   case 'html':
49039                     {
49040                       // TODO parse inline content if parameter markdown=1
49041                       out += this.renderer.html(token.text);
49042                       continue;
49043                     }
49044
49045                   case 'paragraph':
49046                     {
49047                       out += this.renderer.paragraph(this.parseInline(token.tokens));
49048                       continue;
49049                     }
49050
49051                   case 'text':
49052                     {
49053                       body = token.tokens ? this.parseInline(token.tokens) : token.text;
49054
49055                       while (i + 1 < l && tokens[i + 1].type === 'text') {
49056                         token = tokens[++i];
49057                         body += '\n' + (token.tokens ? this.parseInline(token.tokens) : token.text);
49058                       }
49059
49060                       out += top ? this.renderer.paragraph(body) : body;
49061                       continue;
49062                     }
49063
49064                   default:
49065                     {
49066                       var errMsg = 'Token with "' + token.type + '" type was not found.';
49067
49068                       if (this.options.silent) {
49069                         console.error(errMsg);
49070                         return;
49071                       } else {
49072                         throw new Error(errMsg);
49073                       }
49074                     }
49075                 }
49076               }
49077
49078               return out;
49079             }
49080             /**
49081              * Parse Inline Tokens
49082              */
49083
49084           }, {
49085             key: "parseInline",
49086             value: function parseInline(tokens, renderer) {
49087               renderer = renderer || this.renderer;
49088               var out = '',
49089                   i,
49090                   token;
49091               var l = tokens.length;
49092
49093               for (i = 0; i < l; i++) {
49094                 token = tokens[i];
49095
49096                 switch (token.type) {
49097                   case 'escape':
49098                     {
49099                       out += renderer.text(token.text);
49100                       break;
49101                     }
49102
49103                   case 'html':
49104                     {
49105                       out += renderer.html(token.text);
49106                       break;
49107                     }
49108
49109                   case 'link':
49110                     {
49111                       out += renderer.link(token.href, token.title, this.parseInline(token.tokens, renderer));
49112                       break;
49113                     }
49114
49115                   case 'image':
49116                     {
49117                       out += renderer.image(token.href, token.title, token.text);
49118                       break;
49119                     }
49120
49121                   case 'strong':
49122                     {
49123                       out += renderer.strong(this.parseInline(token.tokens, renderer));
49124                       break;
49125                     }
49126
49127                   case 'em':
49128                     {
49129                       out += renderer.em(this.parseInline(token.tokens, renderer));
49130                       break;
49131                     }
49132
49133                   case 'codespan':
49134                     {
49135                       out += renderer.codespan(token.text);
49136                       break;
49137                     }
49138
49139                   case 'br':
49140                     {
49141                       out += renderer.br();
49142                       break;
49143                     }
49144
49145                   case 'del':
49146                     {
49147                       out += renderer.del(this.parseInline(token.tokens, renderer));
49148                       break;
49149                     }
49150
49151                   case 'text':
49152                     {
49153                       out += renderer.text(token.text);
49154                       break;
49155                     }
49156
49157                   default:
49158                     {
49159                       var errMsg = 'Token with "' + token.type + '" type was not found.';
49160
49161                       if (this.options.silent) {
49162                         console.error(errMsg);
49163                         return;
49164                       } else {
49165                         throw new Error(errMsg);
49166                       }
49167                     }
49168                 }
49169               }
49170
49171               return out;
49172             }
49173           }], [{
49174             key: "parse",
49175             value: function parse(tokens, options) {
49176               var parser = new Parser(options);
49177               return parser.parse(tokens);
49178             }
49179             /**
49180              * Static Parse Inline Method
49181              */
49182
49183           }, {
49184             key: "parseInline",
49185             value: function parseInline(tokens, options) {
49186               var parser = new Parser(options);
49187               return parser.parseInline(tokens);
49188             }
49189           }]);
49190
49191           return Parser;
49192         }();
49193
49194         var merge = helpers.merge,
49195             checkSanitizeDeprecation = helpers.checkSanitizeDeprecation,
49196             escape$1 = helpers.escape;
49197         var getDefaults = defaults$5.getDefaults,
49198             changeDefaults = defaults$5.changeDefaults,
49199             defaults = defaults$5.defaults;
49200         /**
49201          * Marked
49202          */
49203
49204         function marked(src, opt, callback) {
49205           // throw error in case of non string input
49206           if (typeof src === 'undefined' || src === null) {
49207             throw new Error('marked(): input parameter is undefined or null');
49208           }
49209
49210           if (typeof src !== 'string') {
49211             throw new Error('marked(): input parameter is of type ' + Object.prototype.toString.call(src) + ', string expected');
49212           }
49213
49214           if (typeof opt === 'function') {
49215             callback = opt;
49216             opt = null;
49217           }
49218
49219           opt = merge({}, marked.defaults, opt || {});
49220           checkSanitizeDeprecation(opt);
49221
49222           if (callback) {
49223             var highlight = opt.highlight;
49224             var tokens;
49225
49226             try {
49227               tokens = Lexer_1.lex(src, opt);
49228             } catch (e) {
49229               return callback(e);
49230             }
49231
49232             var done = function done(err) {
49233               var out;
49234
49235               if (!err) {
49236                 try {
49237                   if (opt.walkTokens) {
49238                     marked.walkTokens(tokens, opt.walkTokens);
49239                   }
49240
49241                   out = Parser_1.parse(tokens, opt);
49242                 } catch (e) {
49243                   err = e;
49244                 }
49245               }
49246
49247               opt.highlight = highlight;
49248               return err ? callback(err) : callback(null, out);
49249             };
49250
49251             if (!highlight || highlight.length < 3) {
49252               return done();
49253             }
49254
49255             delete opt.highlight;
49256             if (!tokens.length) return done();
49257             var pending = 0;
49258             marked.walkTokens(tokens, function (token) {
49259               if (token.type === 'code') {
49260                 pending++;
49261                 setTimeout(function () {
49262                   highlight(token.text, token.lang, function (err, code) {
49263                     if (err) {
49264                       return done(err);
49265                     }
49266
49267                     if (code != null && code !== token.text) {
49268                       token.text = code;
49269                       token.escaped = true;
49270                     }
49271
49272                     pending--;
49273
49274                     if (pending === 0) {
49275                       done();
49276                     }
49277                   });
49278                 }, 0);
49279               }
49280             });
49281
49282             if (pending === 0) {
49283               done();
49284             }
49285
49286             return;
49287           }
49288
49289           try {
49290             var _tokens = Lexer_1.lex(src, opt);
49291
49292             if (opt.walkTokens) {
49293               marked.walkTokens(_tokens, opt.walkTokens);
49294             }
49295
49296             return Parser_1.parse(_tokens, opt);
49297           } catch (e) {
49298             e.message += '\nPlease report this to https://github.com/markedjs/marked.';
49299
49300             if (opt.silent) {
49301               return '<p>An error occurred:</p><pre>' + escape$1(e.message + '', true) + '</pre>';
49302             }
49303
49304             throw e;
49305           }
49306         }
49307         /**
49308          * Options
49309          */
49310
49311
49312         marked.options = marked.setOptions = function (opt) {
49313           merge(marked.defaults, opt);
49314           changeDefaults(marked.defaults);
49315           return marked;
49316         };
49317
49318         marked.getDefaults = getDefaults;
49319         marked.defaults = defaults;
49320         /**
49321          * Use Extension
49322          */
49323
49324         marked.use = function (extension) {
49325           var opts = merge({}, extension);
49326
49327           if (extension.renderer) {
49328             (function () {
49329               var renderer = marked.defaults.renderer || new Renderer_1();
49330
49331               var _loop = function _loop(prop) {
49332                 var prevRenderer = renderer[prop];
49333
49334                 renderer[prop] = function () {
49335                   for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
49336                     args[_key] = arguments[_key];
49337                   }
49338
49339                   var ret = extension.renderer[prop].apply(renderer, args);
49340
49341                   if (ret === false) {
49342                     ret = prevRenderer.apply(renderer, args);
49343                   }
49344
49345                   return ret;
49346                 };
49347               };
49348
49349               for (var prop in extension.renderer) {
49350                 _loop(prop);
49351               }
49352
49353               opts.renderer = renderer;
49354             })();
49355           }
49356
49357           if (extension.tokenizer) {
49358             (function () {
49359               var tokenizer = marked.defaults.tokenizer || new Tokenizer_1();
49360
49361               var _loop2 = function _loop2(prop) {
49362                 var prevTokenizer = tokenizer[prop];
49363
49364                 tokenizer[prop] = function () {
49365                   for (var _len2 = arguments.length, args = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
49366                     args[_key2] = arguments[_key2];
49367                   }
49368
49369                   var ret = extension.tokenizer[prop].apply(tokenizer, args);
49370
49371                   if (ret === false) {
49372                     ret = prevTokenizer.apply(tokenizer, args);
49373                   }
49374
49375                   return ret;
49376                 };
49377               };
49378
49379               for (var prop in extension.tokenizer) {
49380                 _loop2(prop);
49381               }
49382
49383               opts.tokenizer = tokenizer;
49384             })();
49385           }
49386
49387           if (extension.walkTokens) {
49388             var walkTokens = marked.defaults.walkTokens;
49389
49390             opts.walkTokens = function (token) {
49391               extension.walkTokens(token);
49392
49393               if (walkTokens) {
49394                 walkTokens(token);
49395               }
49396             };
49397           }
49398
49399           marked.setOptions(opts);
49400         };
49401         /**
49402          * Run callback for every token
49403          */
49404
49405
49406         marked.walkTokens = function (tokens, callback) {
49407           var _iterator = _createForOfIteratorHelper(tokens),
49408               _step;
49409
49410           try {
49411             for (_iterator.s(); !(_step = _iterator.n()).done;) {
49412               var token = _step.value;
49413               callback(token);
49414
49415               switch (token.type) {
49416                 case 'table':
49417                   {
49418                     var _iterator2 = _createForOfIteratorHelper(token.tokens.header),
49419                         _step2;
49420
49421                     try {
49422                       for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
49423                         var cell = _step2.value;
49424                         marked.walkTokens(cell, callback);
49425                       }
49426                     } catch (err) {
49427                       _iterator2.e(err);
49428                     } finally {
49429                       _iterator2.f();
49430                     }
49431
49432                     var _iterator3 = _createForOfIteratorHelper(token.tokens.cells),
49433                         _step3;
49434
49435                     try {
49436                       for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
49437                         var row = _step3.value;
49438
49439                         var _iterator4 = _createForOfIteratorHelper(row),
49440                             _step4;
49441
49442                         try {
49443                           for (_iterator4.s(); !(_step4 = _iterator4.n()).done;) {
49444                             var _cell = _step4.value;
49445                             marked.walkTokens(_cell, callback);
49446                           }
49447                         } catch (err) {
49448                           _iterator4.e(err);
49449                         } finally {
49450                           _iterator4.f();
49451                         }
49452                       }
49453                     } catch (err) {
49454                       _iterator3.e(err);
49455                     } finally {
49456                       _iterator3.f();
49457                     }
49458
49459                     break;
49460                   }
49461
49462                 case 'list':
49463                   {
49464                     marked.walkTokens(token.items, callback);
49465                     break;
49466                   }
49467
49468                 default:
49469                   {
49470                     if (token.tokens) {
49471                       marked.walkTokens(token.tokens, callback);
49472                     }
49473                   }
49474               }
49475             }
49476           } catch (err) {
49477             _iterator.e(err);
49478           } finally {
49479             _iterator.f();
49480           }
49481         };
49482         /**
49483          * Parse Inline
49484          */
49485
49486
49487         marked.parseInline = function (src, opt) {
49488           // throw error in case of non string input
49489           if (typeof src === 'undefined' || src === null) {
49490             throw new Error('marked.parseInline(): input parameter is undefined or null');
49491           }
49492
49493           if (typeof src !== 'string') {
49494             throw new Error('marked.parseInline(): input parameter is of type ' + Object.prototype.toString.call(src) + ', string expected');
49495           }
49496
49497           opt = merge({}, marked.defaults, opt || {});
49498           checkSanitizeDeprecation(opt);
49499
49500           try {
49501             var tokens = Lexer_1.lexInline(src, opt);
49502
49503             if (opt.walkTokens) {
49504               marked.walkTokens(tokens, opt.walkTokens);
49505             }
49506
49507             return Parser_1.parseInline(tokens, opt);
49508           } catch (e) {
49509             e.message += '\nPlease report this to https://github.com/markedjs/marked.';
49510
49511             if (opt.silent) {
49512               return '<p>An error occurred:</p><pre>' + escape$1(e.message + '', true) + '</pre>';
49513             }
49514
49515             throw e;
49516           }
49517         };
49518         /**
49519          * Expose
49520          */
49521
49522
49523         marked.Parser = Parser_1;
49524         marked.parser = Parser_1.parse;
49525         marked.Renderer = Renderer_1;
49526         marked.TextRenderer = TextRenderer_1;
49527         marked.Lexer = Lexer_1;
49528         marked.lexer = Lexer_1.lex;
49529         marked.Tokenizer = Tokenizer_1;
49530         marked.Slugger = Slugger_1;
49531         marked.parse = marked;
49532         var marked_1 = marked;
49533
49534         var tiler$4 = utilTiler();
49535         var dispatch$5 = dispatch$8('loaded');
49536         var _tileZoom$1 = 14;
49537         var _osmoseUrlRoot = 'https://osmose.openstreetmap.fr/api/0.3';
49538         var _osmoseData = {
49539           icons: {},
49540           items: []
49541         }; // This gets reassigned if reset
49542
49543         var _cache;
49544
49545         function abortRequest$4(controller) {
49546           if (controller) {
49547             controller.abort();
49548           }
49549         }
49550
49551         function abortUnwantedRequests$1(cache, tiles) {
49552           Object.keys(cache.inflightTile).forEach(function (k) {
49553             var wanted = tiles.find(function (tile) {
49554               return k === tile.id;
49555             });
49556
49557             if (!wanted) {
49558               abortRequest$4(cache.inflightTile[k]);
49559               delete cache.inflightTile[k];
49560             }
49561           });
49562         }
49563
49564         function encodeIssueRtree(d) {
49565           return {
49566             minX: d.loc[0],
49567             minY: d.loc[1],
49568             maxX: d.loc[0],
49569             maxY: d.loc[1],
49570             data: d
49571           };
49572         } // Replace or remove QAItem from rtree
49573
49574
49575         function updateRtree$1(item, replace) {
49576           _cache.rtree.remove(item, function (a, b) {
49577             return a.data.id === b.data.id;
49578           });
49579
49580           if (replace) {
49581             _cache.rtree.insert(item);
49582           }
49583         } // Issues shouldn't obscure each other
49584
49585
49586         function preventCoincident(loc) {
49587           var coincident = false;
49588
49589           do {
49590             // first time, move marker up. after that, move marker right.
49591             var delta = coincident ? [0.00001, 0] : [0, 0.00001];
49592             loc = geoVecAdd(loc, delta);
49593             var bbox = geoExtent(loc).bbox();
49594             coincident = _cache.rtree.search(bbox).length;
49595           } while (coincident);
49596
49597           return loc;
49598         }
49599
49600         var serviceOsmose = {
49601           title: 'osmose',
49602           init: function init() {
49603             _mainFileFetcher.get('qa_data').then(function (d) {
49604               _osmoseData = d.osmose;
49605               _osmoseData.items = Object.keys(d.osmose.icons).map(function (s) {
49606                 return s.split('-')[0];
49607               }).reduce(function (unique, item) {
49608                 return unique.indexOf(item) !== -1 ? unique : [].concat(_toConsumableArray(unique), [item]);
49609               }, []);
49610             });
49611
49612             if (!_cache) {
49613               this.reset();
49614             }
49615
49616             this.event = utilRebind(this, dispatch$5, 'on');
49617           },
49618           reset: function reset() {
49619             var _strings = {};
49620             var _colors = {};
49621
49622             if (_cache) {
49623               Object.values(_cache.inflightTile).forEach(abortRequest$4); // Strings and colors are static and should not be re-populated
49624
49625               _strings = _cache.strings;
49626               _colors = _cache.colors;
49627             }
49628
49629             _cache = {
49630               data: {},
49631               loadedTile: {},
49632               inflightTile: {},
49633               inflightPost: {},
49634               closed: {},
49635               rtree: new RBush(),
49636               strings: _strings,
49637               colors: _colors
49638             };
49639           },
49640           loadIssues: function loadIssues(projection) {
49641             var _this = this;
49642
49643             var params = {
49644               // Tiles return a maximum # of issues
49645               // So we want to filter our request for only types iD supports
49646               item: _osmoseData.items
49647             }; // determine the needed tiles to cover the view
49648
49649             var tiles = tiler$4.zoomExtent([_tileZoom$1, _tileZoom$1]).getTiles(projection); // abort inflight requests that are no longer needed
49650
49651             abortUnwantedRequests$1(_cache, tiles); // issue new requests..
49652
49653             tiles.forEach(function (tile) {
49654               if (_cache.loadedTile[tile.id] || _cache.inflightTile[tile.id]) return;
49655
49656               var _tile$xyz = _slicedToArray(tile.xyz, 3),
49657                   x = _tile$xyz[0],
49658                   y = _tile$xyz[1],
49659                   z = _tile$xyz[2];
49660
49661               var url = "".concat(_osmoseUrlRoot, "/issues/").concat(z, "/").concat(x, "/").concat(y, ".json?") + utilQsString(params);
49662               var controller = new AbortController();
49663               _cache.inflightTile[tile.id] = controller;
49664               d3_json(url, {
49665                 signal: controller.signal
49666               }).then(function (data) {
49667                 delete _cache.inflightTile[tile.id];
49668                 _cache.loadedTile[tile.id] = true;
49669
49670                 if (data.features) {
49671                   data.features.forEach(function (issue) {
49672                     var _issue$properties = issue.properties,
49673                         item = _issue$properties.item,
49674                         cl = _issue$properties["class"],
49675                         id = _issue$properties.uuid;
49676                     /* Osmose issues are uniquely identified by a unique
49677                       `item` and `class` combination (both integer values) */
49678
49679                     var itemType = "".concat(item, "-").concat(cl); // Filter out unsupported issue types (some are too specific or advanced)
49680
49681                     if (itemType in _osmoseData.icons) {
49682                       var loc = issue.geometry.coordinates; // lon, lat
49683
49684                       loc = preventCoincident(loc);
49685                       var d = new QAItem(loc, _this, itemType, id, {
49686                         item: item
49687                       }); // Setting elems here prevents UI detail requests
49688
49689                       if (item === 8300 || item === 8360) {
49690                         d.elems = [];
49691                       }
49692
49693                       _cache.data[d.id] = d;
49694
49695                       _cache.rtree.insert(encodeIssueRtree(d));
49696                     }
49697                   });
49698                 }
49699
49700                 dispatch$5.call('loaded');
49701               })["catch"](function () {
49702                 delete _cache.inflightTile[tile.id];
49703                 _cache.loadedTile[tile.id] = true;
49704               });
49705             });
49706           },
49707           loadIssueDetail: function loadIssueDetail(issue) {
49708             var _this2 = this;
49709
49710             // Issue details only need to be fetched once
49711             if (issue.elems !== undefined) {
49712               return Promise.resolve(issue);
49713             }
49714
49715             var url = "".concat(_osmoseUrlRoot, "/issue/").concat(issue.id, "?langs=").concat(_mainLocalizer.localeCode());
49716
49717             var cacheDetails = function cacheDetails(data) {
49718               // Associated elements used for highlighting
49719               // Assign directly for immediate use in the callback
49720               issue.elems = data.elems.map(function (e) {
49721                 return e.type.substring(0, 1) + e.id;
49722               }); // Some issues have instance specific detail in a subtitle
49723
49724               issue.detail = data.subtitle ? marked_1(data.subtitle.auto) : '';
49725
49726               _this2.replaceItem(issue);
49727             };
49728
49729             return d3_json(url).then(cacheDetails).then(function () {
49730               return issue;
49731             });
49732           },
49733           loadStrings: function loadStrings() {
49734             var locale = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : _mainLocalizer.localeCode();
49735             var items = Object.keys(_osmoseData.icons);
49736
49737             if (locale in _cache.strings && Object.keys(_cache.strings[locale]).length === items.length) {
49738               return Promise.resolve(_cache.strings[locale]);
49739             } // May be partially populated already if some requests were successful
49740
49741
49742             if (!(locale in _cache.strings)) {
49743               _cache.strings[locale] = {};
49744             } // Only need to cache strings for supported issue types
49745             // Using multiple individual item + class requests to reduce fetched data size
49746
49747
49748             var allRequests = items.map(function (itemType) {
49749               // No need to request data we already have
49750               if (itemType in _cache.strings[locale]) return null;
49751
49752               var cacheData = function cacheData(data) {
49753                 // Bunch of nested single value arrays of objects
49754                 var _data$categories = _slicedToArray(data.categories, 1),
49755                     _data$categories$ = _data$categories[0],
49756                     cat = _data$categories$ === void 0 ? {
49757                   items: []
49758                 } : _data$categories$;
49759
49760                 var _cat$items = _slicedToArray(cat.items, 1),
49761                     _cat$items$ = _cat$items[0],
49762                     item = _cat$items$ === void 0 ? {
49763                   "class": []
49764                 } : _cat$items$;
49765
49766                 var _item$class = _slicedToArray(item["class"], 1),
49767                     _item$class$ = _item$class[0],
49768                     cl = _item$class$ === void 0 ? null : _item$class$; // If null default value is reached, data wasn't as expected (or was empty)
49769
49770
49771                 if (!cl) {
49772                   /* eslint-disable no-console */
49773                   console.log("Osmose strings request (".concat(itemType, ") had unexpected data"));
49774                   /* eslint-enable no-console */
49775
49776                   return;
49777                 } // Cache served item colors to automatically style issue markers later
49778
49779
49780                 var itemInt = item.item,
49781                     color = item.color;
49782
49783                 if (/^#[A-Fa-f0-9]{6}|[A-Fa-f0-9]{3}/.test(color)) {
49784                   _cache.colors[itemInt] = color;
49785                 } // Value of root key will be null if no string exists
49786                 // If string exists, value is an object with key 'auto' for string
49787
49788
49789                 var title = cl.title,
49790                     detail = cl.detail,
49791                     fix = cl.fix,
49792                     trap = cl.trap; // Osmose titles shouldn't contain markdown
49793
49794                 var issueStrings = {};
49795                 if (title) issueStrings.title = title.auto;
49796                 if (detail) issueStrings.detail = marked_1(detail.auto);
49797                 if (trap) issueStrings.trap = marked_1(trap.auto);
49798                 if (fix) issueStrings.fix = marked_1(fix.auto);
49799                 _cache.strings[locale][itemType] = issueStrings;
49800               };
49801
49802               var _itemType$split = itemType.split('-'),
49803                   _itemType$split2 = _slicedToArray(_itemType$split, 2),
49804                   item = _itemType$split2[0],
49805                   cl = _itemType$split2[1]; // Osmose API falls back to English strings where untranslated or if locale doesn't exist
49806
49807
49808               var url = "".concat(_osmoseUrlRoot, "/items/").concat(item, "/class/").concat(cl, "?langs=").concat(locale);
49809               return d3_json(url).then(cacheData);
49810             }).filter(Boolean);
49811             return Promise.all(allRequests).then(function () {
49812               return _cache.strings[locale];
49813             });
49814           },
49815           getStrings: function getStrings(itemType) {
49816             var locale = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : _mainLocalizer.localeCode();
49817             // No need to fallback to English, Osmose API handles this for us
49818             return locale in _cache.strings ? _cache.strings[locale][itemType] : {};
49819           },
49820           getColor: function getColor(itemType) {
49821             return itemType in _cache.colors ? _cache.colors[itemType] : '#FFFFFF';
49822           },
49823           postUpdate: function postUpdate(issue, callback) {
49824             var _this3 = this;
49825
49826             if (_cache.inflightPost[issue.id]) {
49827               return callback({
49828                 message: 'Issue update already inflight',
49829                 status: -2
49830               }, issue);
49831             } // UI sets the status to either 'done' or 'false'
49832
49833
49834             var url = "".concat(_osmoseUrlRoot, "/issue/").concat(issue.id, "/").concat(issue.newStatus);
49835             var controller = new AbortController();
49836
49837             var after = function after() {
49838               delete _cache.inflightPost[issue.id];
49839
49840               _this3.removeItem(issue);
49841
49842               if (issue.newStatus === 'done') {
49843                 // Keep track of the number of issues closed per `item` to tag the changeset
49844                 if (!(issue.item in _cache.closed)) {
49845                   _cache.closed[issue.item] = 0;
49846                 }
49847
49848                 _cache.closed[issue.item] += 1;
49849               }
49850
49851               if (callback) callback(null, issue);
49852             };
49853
49854             _cache.inflightPost[issue.id] = controller;
49855             fetch(url, {
49856               signal: controller.signal
49857             }).then(after)["catch"](function (err) {
49858               delete _cache.inflightPost[issue.id];
49859               if (callback) callback(err.message);
49860             });
49861           },
49862           // Get all cached QAItems covering the viewport
49863           getItems: function getItems(projection) {
49864             var viewport = projection.clipExtent();
49865             var min = [viewport[0][0], viewport[1][1]];
49866             var max = [viewport[1][0], viewport[0][1]];
49867             var bbox = geoExtent(projection.invert(min), projection.invert(max)).bbox();
49868             return _cache.rtree.search(bbox).map(function (d) {
49869               return d.data;
49870             });
49871           },
49872           // Get a QAItem from cache
49873           // NOTE: Don't change method name until UI v3 is merged
49874           getError: function getError(id) {
49875             return _cache.data[id];
49876           },
49877           // get the name of the icon to display for this item
49878           getIcon: function getIcon(itemType) {
49879             return _osmoseData.icons[itemType];
49880           },
49881           // Replace a single QAItem in the cache
49882           replaceItem: function replaceItem(item) {
49883             if (!(item instanceof QAItem) || !item.id) return;
49884             _cache.data[item.id] = item;
49885             updateRtree$1(encodeIssueRtree(item), true); // true = replace
49886
49887             return item;
49888           },
49889           // Remove a single QAItem from the cache
49890           removeItem: function removeItem(item) {
49891             if (!(item instanceof QAItem) || !item.id) return;
49892             delete _cache.data[item.id];
49893             updateRtree$1(encodeIssueRtree(item), false); // false = remove
49894           },
49895           // Used to populate `closed:osmose:*` changeset tags
49896           getClosedCounts: function getClosedCounts() {
49897             return _cache.closed;
49898           },
49899           itemURL: function itemURL(item) {
49900             return "https://osmose.openstreetmap.fr/en/error/".concat(item.id);
49901           }
49902         };
49903
49904         /*! ieee754. BSD-3-Clause License. Feross Aboukhadijeh <https://feross.org/opensource> */
49905         var read$6 = function read(buffer, offset, isLE, mLen, nBytes) {
49906           var e, m;
49907           var eLen = nBytes * 8 - mLen - 1;
49908           var eMax = (1 << eLen) - 1;
49909           var eBias = eMax >> 1;
49910           var nBits = -7;
49911           var i = isLE ? nBytes - 1 : 0;
49912           var d = isLE ? -1 : 1;
49913           var s = buffer[offset + i];
49914           i += d;
49915           e = s & (1 << -nBits) - 1;
49916           s >>= -nBits;
49917           nBits += eLen;
49918
49919           for (; nBits > 0; e = e * 256 + buffer[offset + i], i += d, nBits -= 8) {}
49920
49921           m = e & (1 << -nBits) - 1;
49922           e >>= -nBits;
49923           nBits += mLen;
49924
49925           for (; nBits > 0; m = m * 256 + buffer[offset + i], i += d, nBits -= 8) {}
49926
49927           if (e === 0) {
49928             e = 1 - eBias;
49929           } else if (e === eMax) {
49930             return m ? NaN : (s ? -1 : 1) * Infinity;
49931           } else {
49932             m = m + Math.pow(2, mLen);
49933             e = e - eBias;
49934           }
49935
49936           return (s ? -1 : 1) * m * Math.pow(2, e - mLen);
49937         };
49938
49939         var write$6 = function write(buffer, value, offset, isLE, mLen, nBytes) {
49940           var e, m, c;
49941           var eLen = nBytes * 8 - mLen - 1;
49942           var eMax = (1 << eLen) - 1;
49943           var eBias = eMax >> 1;
49944           var rt = mLen === 23 ? Math.pow(2, -24) - Math.pow(2, -77) : 0;
49945           var i = isLE ? 0 : nBytes - 1;
49946           var d = isLE ? 1 : -1;
49947           var s = value < 0 || value === 0 && 1 / value < 0 ? 1 : 0;
49948           value = Math.abs(value);
49949
49950           if (isNaN(value) || value === Infinity) {
49951             m = isNaN(value) ? 1 : 0;
49952             e = eMax;
49953           } else {
49954             e = Math.floor(Math.log(value) / Math.LN2);
49955
49956             if (value * (c = Math.pow(2, -e)) < 1) {
49957               e--;
49958               c *= 2;
49959             }
49960
49961             if (e + eBias >= 1) {
49962               value += rt / c;
49963             } else {
49964               value += rt * Math.pow(2, 1 - eBias);
49965             }
49966
49967             if (value * c >= 2) {
49968               e++;
49969               c /= 2;
49970             }
49971
49972             if (e + eBias >= eMax) {
49973               m = 0;
49974               e = eMax;
49975             } else if (e + eBias >= 1) {
49976               m = (value * c - 1) * Math.pow(2, mLen);
49977               e = e + eBias;
49978             } else {
49979               m = value * Math.pow(2, eBias - 1) * Math.pow(2, mLen);
49980               e = 0;
49981             }
49982           }
49983
49984           for (; mLen >= 8; buffer[offset + i] = m & 0xff, i += d, m /= 256, mLen -= 8) {}
49985
49986           e = e << mLen | m;
49987           eLen += mLen;
49988
49989           for (; eLen > 0; buffer[offset + i] = e & 0xff, i += d, e /= 256, eLen -= 8) {}
49990
49991           buffer[offset + i - d] |= s * 128;
49992         };
49993
49994         var ieee754 = {
49995           read: read$6,
49996           write: write$6
49997         };
49998
49999         var pbf = Pbf;
50000
50001         function Pbf(buf) {
50002           this.buf = ArrayBuffer.isView && ArrayBuffer.isView(buf) ? buf : new Uint8Array(buf || 0);
50003           this.pos = 0;
50004           this.type = 0;
50005           this.length = this.buf.length;
50006         }
50007
50008         Pbf.Varint = 0; // varint: int32, int64, uint32, uint64, sint32, sint64, bool, enum
50009
50010         Pbf.Fixed64 = 1; // 64-bit: double, fixed64, sfixed64
50011
50012         Pbf.Bytes = 2; // length-delimited: string, bytes, embedded messages, packed repeated fields
50013
50014         Pbf.Fixed32 = 5; // 32-bit: float, fixed32, sfixed32
50015
50016         var SHIFT_LEFT_32 = (1 << 16) * (1 << 16),
50017             SHIFT_RIGHT_32 = 1 / SHIFT_LEFT_32; // Threshold chosen based on both benchmarking and knowledge about browser string
50018         // data structures (which currently switch structure types at 12 bytes or more)
50019
50020         var TEXT_DECODER_MIN_LENGTH = 12;
50021         var utf8TextDecoder = typeof TextDecoder === 'undefined' ? null : new TextDecoder('utf8');
50022         Pbf.prototype = {
50023           destroy: function destroy() {
50024             this.buf = null;
50025           },
50026           // === READING =================================================================
50027           readFields: function readFields(readField, result, end) {
50028             end = end || this.length;
50029
50030             while (this.pos < end) {
50031               var val = this.readVarint(),
50032                   tag = val >> 3,
50033                   startPos = this.pos;
50034               this.type = val & 0x7;
50035               readField(tag, result, this);
50036               if (this.pos === startPos) this.skip(val);
50037             }
50038
50039             return result;
50040           },
50041           readMessage: function readMessage(readField, result) {
50042             return this.readFields(readField, result, this.readVarint() + this.pos);
50043           },
50044           readFixed32: function readFixed32() {
50045             var val = readUInt32(this.buf, this.pos);
50046             this.pos += 4;
50047             return val;
50048           },
50049           readSFixed32: function readSFixed32() {
50050             var val = readInt32(this.buf, this.pos);
50051             this.pos += 4;
50052             return val;
50053           },
50054           // 64-bit int handling is based on github.com/dpw/node-buffer-more-ints (MIT-licensed)
50055           readFixed64: function readFixed64() {
50056             var val = readUInt32(this.buf, this.pos) + readUInt32(this.buf, this.pos + 4) * SHIFT_LEFT_32;
50057             this.pos += 8;
50058             return val;
50059           },
50060           readSFixed64: function readSFixed64() {
50061             var val = readUInt32(this.buf, this.pos) + readInt32(this.buf, this.pos + 4) * SHIFT_LEFT_32;
50062             this.pos += 8;
50063             return val;
50064           },
50065           readFloat: function readFloat() {
50066             var val = ieee754.read(this.buf, this.pos, true, 23, 4);
50067             this.pos += 4;
50068             return val;
50069           },
50070           readDouble: function readDouble() {
50071             var val = ieee754.read(this.buf, this.pos, true, 52, 8);
50072             this.pos += 8;
50073             return val;
50074           },
50075           readVarint: function readVarint(isSigned) {
50076             var buf = this.buf,
50077                 val,
50078                 b;
50079             b = buf[this.pos++];
50080             val = b & 0x7f;
50081             if (b < 0x80) return val;
50082             b = buf[this.pos++];
50083             val |= (b & 0x7f) << 7;
50084             if (b < 0x80) return val;
50085             b = buf[this.pos++];
50086             val |= (b & 0x7f) << 14;
50087             if (b < 0x80) return val;
50088             b = buf[this.pos++];
50089             val |= (b & 0x7f) << 21;
50090             if (b < 0x80) return val;
50091             b = buf[this.pos];
50092             val |= (b & 0x0f) << 28;
50093             return readVarintRemainder(val, isSigned, this);
50094           },
50095           readVarint64: function readVarint64() {
50096             // for compatibility with v2.0.1
50097             return this.readVarint(true);
50098           },
50099           readSVarint: function readSVarint() {
50100             var num = this.readVarint();
50101             return num % 2 === 1 ? (num + 1) / -2 : num / 2; // zigzag encoding
50102           },
50103           readBoolean: function readBoolean() {
50104             return Boolean(this.readVarint());
50105           },
50106           readString: function readString() {
50107             var end = this.readVarint() + this.pos;
50108             var pos = this.pos;
50109             this.pos = end;
50110
50111             if (end - pos >= TEXT_DECODER_MIN_LENGTH && utf8TextDecoder) {
50112               // longer strings are fast with the built-in browser TextDecoder API
50113               return readUtf8TextDecoder(this.buf, pos, end);
50114             } // short strings are fast with our custom implementation
50115
50116
50117             return readUtf8(this.buf, pos, end);
50118           },
50119           readBytes: function readBytes() {
50120             var end = this.readVarint() + this.pos,
50121                 buffer = this.buf.subarray(this.pos, end);
50122             this.pos = end;
50123             return buffer;
50124           },
50125           // verbose for performance reasons; doesn't affect gzipped size
50126           readPackedVarint: function readPackedVarint(arr, isSigned) {
50127             if (this.type !== Pbf.Bytes) return arr.push(this.readVarint(isSigned));
50128             var end = readPackedEnd(this);
50129             arr = arr || [];
50130
50131             while (this.pos < end) {
50132               arr.push(this.readVarint(isSigned));
50133             }
50134
50135             return arr;
50136           },
50137           readPackedSVarint: function readPackedSVarint(arr) {
50138             if (this.type !== Pbf.Bytes) return arr.push(this.readSVarint());
50139             var end = readPackedEnd(this);
50140             arr = arr || [];
50141
50142             while (this.pos < end) {
50143               arr.push(this.readSVarint());
50144             }
50145
50146             return arr;
50147           },
50148           readPackedBoolean: function readPackedBoolean(arr) {
50149             if (this.type !== Pbf.Bytes) return arr.push(this.readBoolean());
50150             var end = readPackedEnd(this);
50151             arr = arr || [];
50152
50153             while (this.pos < end) {
50154               arr.push(this.readBoolean());
50155             }
50156
50157             return arr;
50158           },
50159           readPackedFloat: function readPackedFloat(arr) {
50160             if (this.type !== Pbf.Bytes) return arr.push(this.readFloat());
50161             var end = readPackedEnd(this);
50162             arr = arr || [];
50163
50164             while (this.pos < end) {
50165               arr.push(this.readFloat());
50166             }
50167
50168             return arr;
50169           },
50170           readPackedDouble: function readPackedDouble(arr) {
50171             if (this.type !== Pbf.Bytes) return arr.push(this.readDouble());
50172             var end = readPackedEnd(this);
50173             arr = arr || [];
50174
50175             while (this.pos < end) {
50176               arr.push(this.readDouble());
50177             }
50178
50179             return arr;
50180           },
50181           readPackedFixed32: function readPackedFixed32(arr) {
50182             if (this.type !== Pbf.Bytes) return arr.push(this.readFixed32());
50183             var end = readPackedEnd(this);
50184             arr = arr || [];
50185
50186             while (this.pos < end) {
50187               arr.push(this.readFixed32());
50188             }
50189
50190             return arr;
50191           },
50192           readPackedSFixed32: function readPackedSFixed32(arr) {
50193             if (this.type !== Pbf.Bytes) return arr.push(this.readSFixed32());
50194             var end = readPackedEnd(this);
50195             arr = arr || [];
50196
50197             while (this.pos < end) {
50198               arr.push(this.readSFixed32());
50199             }
50200
50201             return arr;
50202           },
50203           readPackedFixed64: function readPackedFixed64(arr) {
50204             if (this.type !== Pbf.Bytes) return arr.push(this.readFixed64());
50205             var end = readPackedEnd(this);
50206             arr = arr || [];
50207
50208             while (this.pos < end) {
50209               arr.push(this.readFixed64());
50210             }
50211
50212             return arr;
50213           },
50214           readPackedSFixed64: function readPackedSFixed64(arr) {
50215             if (this.type !== Pbf.Bytes) return arr.push(this.readSFixed64());
50216             var end = readPackedEnd(this);
50217             arr = arr || [];
50218
50219             while (this.pos < end) {
50220               arr.push(this.readSFixed64());
50221             }
50222
50223             return arr;
50224           },
50225           skip: function skip(val) {
50226             var type = val & 0x7;
50227             if (type === Pbf.Varint) while (this.buf[this.pos++] > 0x7f) {} else if (type === Pbf.Bytes) this.pos = this.readVarint() + this.pos;else if (type === Pbf.Fixed32) this.pos += 4;else if (type === Pbf.Fixed64) this.pos += 8;else throw new Error('Unimplemented type: ' + type);
50228           },
50229           // === WRITING =================================================================
50230           writeTag: function writeTag(tag, type) {
50231             this.writeVarint(tag << 3 | type);
50232           },
50233           realloc: function realloc(min) {
50234             var length = this.length || 16;
50235
50236             while (length < this.pos + min) {
50237               length *= 2;
50238             }
50239
50240             if (length !== this.length) {
50241               var buf = new Uint8Array(length);
50242               buf.set(this.buf);
50243               this.buf = buf;
50244               this.length = length;
50245             }
50246           },
50247           finish: function finish() {
50248             this.length = this.pos;
50249             this.pos = 0;
50250             return this.buf.subarray(0, this.length);
50251           },
50252           writeFixed32: function writeFixed32(val) {
50253             this.realloc(4);
50254             writeInt32(this.buf, val, this.pos);
50255             this.pos += 4;
50256           },
50257           writeSFixed32: function writeSFixed32(val) {
50258             this.realloc(4);
50259             writeInt32(this.buf, val, this.pos);
50260             this.pos += 4;
50261           },
50262           writeFixed64: function writeFixed64(val) {
50263             this.realloc(8);
50264             writeInt32(this.buf, val & -1, this.pos);
50265             writeInt32(this.buf, Math.floor(val * SHIFT_RIGHT_32), this.pos + 4);
50266             this.pos += 8;
50267           },
50268           writeSFixed64: function writeSFixed64(val) {
50269             this.realloc(8);
50270             writeInt32(this.buf, val & -1, this.pos);
50271             writeInt32(this.buf, Math.floor(val * SHIFT_RIGHT_32), this.pos + 4);
50272             this.pos += 8;
50273           },
50274           writeVarint: function writeVarint(val) {
50275             val = +val || 0;
50276
50277             if (val > 0xfffffff || val < 0) {
50278               writeBigVarint(val, this);
50279               return;
50280             }
50281
50282             this.realloc(4);
50283             this.buf[this.pos++] = val & 0x7f | (val > 0x7f ? 0x80 : 0);
50284             if (val <= 0x7f) return;
50285             this.buf[this.pos++] = (val >>>= 7) & 0x7f | (val > 0x7f ? 0x80 : 0);
50286             if (val <= 0x7f) return;
50287             this.buf[this.pos++] = (val >>>= 7) & 0x7f | (val > 0x7f ? 0x80 : 0);
50288             if (val <= 0x7f) return;
50289             this.buf[this.pos++] = val >>> 7 & 0x7f;
50290           },
50291           writeSVarint: function writeSVarint(val) {
50292             this.writeVarint(val < 0 ? -val * 2 - 1 : val * 2);
50293           },
50294           writeBoolean: function writeBoolean(val) {
50295             this.writeVarint(Boolean(val));
50296           },
50297           writeString: function writeString(str) {
50298             str = String(str);
50299             this.realloc(str.length * 4);
50300             this.pos++; // reserve 1 byte for short string length
50301
50302             var startPos = this.pos; // write the string directly to the buffer and see how much was written
50303
50304             this.pos = writeUtf8(this.buf, str, this.pos);
50305             var len = this.pos - startPos;
50306             if (len >= 0x80) makeRoomForExtraLength(startPos, len, this); // finally, write the message length in the reserved place and restore the position
50307
50308             this.pos = startPos - 1;
50309             this.writeVarint(len);
50310             this.pos += len;
50311           },
50312           writeFloat: function writeFloat(val) {
50313             this.realloc(4);
50314             ieee754.write(this.buf, val, this.pos, true, 23, 4);
50315             this.pos += 4;
50316           },
50317           writeDouble: function writeDouble(val) {
50318             this.realloc(8);
50319             ieee754.write(this.buf, val, this.pos, true, 52, 8);
50320             this.pos += 8;
50321           },
50322           writeBytes: function writeBytes(buffer) {
50323             var len = buffer.length;
50324             this.writeVarint(len);
50325             this.realloc(len);
50326
50327             for (var i = 0; i < len; i++) {
50328               this.buf[this.pos++] = buffer[i];
50329             }
50330           },
50331           writeRawMessage: function writeRawMessage(fn, obj) {
50332             this.pos++; // reserve 1 byte for short message length
50333             // write the message directly to the buffer and see how much was written
50334
50335             var startPos = this.pos;
50336             fn(obj, this);
50337             var len = this.pos - startPos;
50338             if (len >= 0x80) makeRoomForExtraLength(startPos, len, this); // finally, write the message length in the reserved place and restore the position
50339
50340             this.pos = startPos - 1;
50341             this.writeVarint(len);
50342             this.pos += len;
50343           },
50344           writeMessage: function writeMessage(tag, fn, obj) {
50345             this.writeTag(tag, Pbf.Bytes);
50346             this.writeRawMessage(fn, obj);
50347           },
50348           writePackedVarint: function writePackedVarint(tag, arr) {
50349             if (arr.length) this.writeMessage(tag, _writePackedVarint, arr);
50350           },
50351           writePackedSVarint: function writePackedSVarint(tag, arr) {
50352             if (arr.length) this.writeMessage(tag, _writePackedSVarint, arr);
50353           },
50354           writePackedBoolean: function writePackedBoolean(tag, arr) {
50355             if (arr.length) this.writeMessage(tag, _writePackedBoolean, arr);
50356           },
50357           writePackedFloat: function writePackedFloat(tag, arr) {
50358             if (arr.length) this.writeMessage(tag, _writePackedFloat, arr);
50359           },
50360           writePackedDouble: function writePackedDouble(tag, arr) {
50361             if (arr.length) this.writeMessage(tag, _writePackedDouble, arr);
50362           },
50363           writePackedFixed32: function writePackedFixed32(tag, arr) {
50364             if (arr.length) this.writeMessage(tag, _writePackedFixed, arr);
50365           },
50366           writePackedSFixed32: function writePackedSFixed32(tag, arr) {
50367             if (arr.length) this.writeMessage(tag, _writePackedSFixed, arr);
50368           },
50369           writePackedFixed64: function writePackedFixed64(tag, arr) {
50370             if (arr.length) this.writeMessage(tag, _writePackedFixed2, arr);
50371           },
50372           writePackedSFixed64: function writePackedSFixed64(tag, arr) {
50373             if (arr.length) this.writeMessage(tag, _writePackedSFixed2, arr);
50374           },
50375           writeBytesField: function writeBytesField(tag, buffer) {
50376             this.writeTag(tag, Pbf.Bytes);
50377             this.writeBytes(buffer);
50378           },
50379           writeFixed32Field: function writeFixed32Field(tag, val) {
50380             this.writeTag(tag, Pbf.Fixed32);
50381             this.writeFixed32(val);
50382           },
50383           writeSFixed32Field: function writeSFixed32Field(tag, val) {
50384             this.writeTag(tag, Pbf.Fixed32);
50385             this.writeSFixed32(val);
50386           },
50387           writeFixed64Field: function writeFixed64Field(tag, val) {
50388             this.writeTag(tag, Pbf.Fixed64);
50389             this.writeFixed64(val);
50390           },
50391           writeSFixed64Field: function writeSFixed64Field(tag, val) {
50392             this.writeTag(tag, Pbf.Fixed64);
50393             this.writeSFixed64(val);
50394           },
50395           writeVarintField: function writeVarintField(tag, val) {
50396             this.writeTag(tag, Pbf.Varint);
50397             this.writeVarint(val);
50398           },
50399           writeSVarintField: function writeSVarintField(tag, val) {
50400             this.writeTag(tag, Pbf.Varint);
50401             this.writeSVarint(val);
50402           },
50403           writeStringField: function writeStringField(tag, str) {
50404             this.writeTag(tag, Pbf.Bytes);
50405             this.writeString(str);
50406           },
50407           writeFloatField: function writeFloatField(tag, val) {
50408             this.writeTag(tag, Pbf.Fixed32);
50409             this.writeFloat(val);
50410           },
50411           writeDoubleField: function writeDoubleField(tag, val) {
50412             this.writeTag(tag, Pbf.Fixed64);
50413             this.writeDouble(val);
50414           },
50415           writeBooleanField: function writeBooleanField(tag, val) {
50416             this.writeVarintField(tag, Boolean(val));
50417           }
50418         };
50419
50420         function readVarintRemainder(l, s, p) {
50421           var buf = p.buf,
50422               h,
50423               b;
50424           b = buf[p.pos++];
50425           h = (b & 0x70) >> 4;
50426           if (b < 0x80) return toNum(l, h, s);
50427           b = buf[p.pos++];
50428           h |= (b & 0x7f) << 3;
50429           if (b < 0x80) return toNum(l, h, s);
50430           b = buf[p.pos++];
50431           h |= (b & 0x7f) << 10;
50432           if (b < 0x80) return toNum(l, h, s);
50433           b = buf[p.pos++];
50434           h |= (b & 0x7f) << 17;
50435           if (b < 0x80) return toNum(l, h, s);
50436           b = buf[p.pos++];
50437           h |= (b & 0x7f) << 24;
50438           if (b < 0x80) return toNum(l, h, s);
50439           b = buf[p.pos++];
50440           h |= (b & 0x01) << 31;
50441           if (b < 0x80) return toNum(l, h, s);
50442           throw new Error('Expected varint not more than 10 bytes');
50443         }
50444
50445         function readPackedEnd(pbf) {
50446           return pbf.type === Pbf.Bytes ? pbf.readVarint() + pbf.pos : pbf.pos + 1;
50447         }
50448
50449         function toNum(low, high, isSigned) {
50450           if (isSigned) {
50451             return high * 0x100000000 + (low >>> 0);
50452           }
50453
50454           return (high >>> 0) * 0x100000000 + (low >>> 0);
50455         }
50456
50457         function writeBigVarint(val, pbf) {
50458           var low, high;
50459
50460           if (val >= 0) {
50461             low = val % 0x100000000 | 0;
50462             high = val / 0x100000000 | 0;
50463           } else {
50464             low = ~(-val % 0x100000000);
50465             high = ~(-val / 0x100000000);
50466
50467             if (low ^ 0xffffffff) {
50468               low = low + 1 | 0;
50469             } else {
50470               low = 0;
50471               high = high + 1 | 0;
50472             }
50473           }
50474
50475           if (val >= 0x10000000000000000 || val < -0x10000000000000000) {
50476             throw new Error('Given varint doesn\'t fit into 10 bytes');
50477           }
50478
50479           pbf.realloc(10);
50480           writeBigVarintLow(low, high, pbf);
50481           writeBigVarintHigh(high, pbf);
50482         }
50483
50484         function writeBigVarintLow(low, high, pbf) {
50485           pbf.buf[pbf.pos++] = low & 0x7f | 0x80;
50486           low >>>= 7;
50487           pbf.buf[pbf.pos++] = low & 0x7f | 0x80;
50488           low >>>= 7;
50489           pbf.buf[pbf.pos++] = low & 0x7f | 0x80;
50490           low >>>= 7;
50491           pbf.buf[pbf.pos++] = low & 0x7f | 0x80;
50492           low >>>= 7;
50493           pbf.buf[pbf.pos] = low & 0x7f;
50494         }
50495
50496         function writeBigVarintHigh(high, pbf) {
50497           var lsb = (high & 0x07) << 4;
50498           pbf.buf[pbf.pos++] |= lsb | ((high >>>= 3) ? 0x80 : 0);
50499           if (!high) return;
50500           pbf.buf[pbf.pos++] = high & 0x7f | ((high >>>= 7) ? 0x80 : 0);
50501           if (!high) return;
50502           pbf.buf[pbf.pos++] = high & 0x7f | ((high >>>= 7) ? 0x80 : 0);
50503           if (!high) return;
50504           pbf.buf[pbf.pos++] = high & 0x7f | ((high >>>= 7) ? 0x80 : 0);
50505           if (!high) return;
50506           pbf.buf[pbf.pos++] = high & 0x7f | ((high >>>= 7) ? 0x80 : 0);
50507           if (!high) return;
50508           pbf.buf[pbf.pos++] = high & 0x7f;
50509         }
50510
50511         function makeRoomForExtraLength(startPos, len, pbf) {
50512           var extraLen = len <= 0x3fff ? 1 : len <= 0x1fffff ? 2 : len <= 0xfffffff ? 3 : Math.floor(Math.log(len) / (Math.LN2 * 7)); // if 1 byte isn't enough for encoding message length, shift the data to the right
50513
50514           pbf.realloc(extraLen);
50515
50516           for (var i = pbf.pos - 1; i >= startPos; i--) {
50517             pbf.buf[i + extraLen] = pbf.buf[i];
50518           }
50519         }
50520
50521         function _writePackedVarint(arr, pbf) {
50522           for (var i = 0; i < arr.length; i++) {
50523             pbf.writeVarint(arr[i]);
50524           }
50525         }
50526
50527         function _writePackedSVarint(arr, pbf) {
50528           for (var i = 0; i < arr.length; i++) {
50529             pbf.writeSVarint(arr[i]);
50530           }
50531         }
50532
50533         function _writePackedFloat(arr, pbf) {
50534           for (var i = 0; i < arr.length; i++) {
50535             pbf.writeFloat(arr[i]);
50536           }
50537         }
50538
50539         function _writePackedDouble(arr, pbf) {
50540           for (var i = 0; i < arr.length; i++) {
50541             pbf.writeDouble(arr[i]);
50542           }
50543         }
50544
50545         function _writePackedBoolean(arr, pbf) {
50546           for (var i = 0; i < arr.length; i++) {
50547             pbf.writeBoolean(arr[i]);
50548           }
50549         }
50550
50551         function _writePackedFixed(arr, pbf) {
50552           for (var i = 0; i < arr.length; i++) {
50553             pbf.writeFixed32(arr[i]);
50554           }
50555         }
50556
50557         function _writePackedSFixed(arr, pbf) {
50558           for (var i = 0; i < arr.length; i++) {
50559             pbf.writeSFixed32(arr[i]);
50560           }
50561         }
50562
50563         function _writePackedFixed2(arr, pbf) {
50564           for (var i = 0; i < arr.length; i++) {
50565             pbf.writeFixed64(arr[i]);
50566           }
50567         }
50568
50569         function _writePackedSFixed2(arr, pbf) {
50570           for (var i = 0; i < arr.length; i++) {
50571             pbf.writeSFixed64(arr[i]);
50572           }
50573         } // Buffer code below from https://github.com/feross/buffer, MIT-licensed
50574
50575
50576         function readUInt32(buf, pos) {
50577           return (buf[pos] | buf[pos + 1] << 8 | buf[pos + 2] << 16) + buf[pos + 3] * 0x1000000;
50578         }
50579
50580         function writeInt32(buf, val, pos) {
50581           buf[pos] = val;
50582           buf[pos + 1] = val >>> 8;
50583           buf[pos + 2] = val >>> 16;
50584           buf[pos + 3] = val >>> 24;
50585         }
50586
50587         function readInt32(buf, pos) {
50588           return (buf[pos] | buf[pos + 1] << 8 | buf[pos + 2] << 16) + (buf[pos + 3] << 24);
50589         }
50590
50591         function readUtf8(buf, pos, end) {
50592           var str = '';
50593           var i = pos;
50594
50595           while (i < end) {
50596             var b0 = buf[i];
50597             var c = null; // codepoint
50598
50599             var bytesPerSequence = b0 > 0xEF ? 4 : b0 > 0xDF ? 3 : b0 > 0xBF ? 2 : 1;
50600             if (i + bytesPerSequence > end) break;
50601             var b1, b2, b3;
50602
50603             if (bytesPerSequence === 1) {
50604               if (b0 < 0x80) {
50605                 c = b0;
50606               }
50607             } else if (bytesPerSequence === 2) {
50608               b1 = buf[i + 1];
50609
50610               if ((b1 & 0xC0) === 0x80) {
50611                 c = (b0 & 0x1F) << 0x6 | b1 & 0x3F;
50612
50613                 if (c <= 0x7F) {
50614                   c = null;
50615                 }
50616               }
50617             } else if (bytesPerSequence === 3) {
50618               b1 = buf[i + 1];
50619               b2 = buf[i + 2];
50620
50621               if ((b1 & 0xC0) === 0x80 && (b2 & 0xC0) === 0x80) {
50622                 c = (b0 & 0xF) << 0xC | (b1 & 0x3F) << 0x6 | b2 & 0x3F;
50623
50624                 if (c <= 0x7FF || c >= 0xD800 && c <= 0xDFFF) {
50625                   c = null;
50626                 }
50627               }
50628             } else if (bytesPerSequence === 4) {
50629               b1 = buf[i + 1];
50630               b2 = buf[i + 2];
50631               b3 = buf[i + 3];
50632
50633               if ((b1 & 0xC0) === 0x80 && (b2 & 0xC0) === 0x80 && (b3 & 0xC0) === 0x80) {
50634                 c = (b0 & 0xF) << 0x12 | (b1 & 0x3F) << 0xC | (b2 & 0x3F) << 0x6 | b3 & 0x3F;
50635
50636                 if (c <= 0xFFFF || c >= 0x110000) {
50637                   c = null;
50638                 }
50639               }
50640             }
50641
50642             if (c === null) {
50643               c = 0xFFFD;
50644               bytesPerSequence = 1;
50645             } else if (c > 0xFFFF) {
50646               c -= 0x10000;
50647               str += String.fromCharCode(c >>> 10 & 0x3FF | 0xD800);
50648               c = 0xDC00 | c & 0x3FF;
50649             }
50650
50651             str += String.fromCharCode(c);
50652             i += bytesPerSequence;
50653           }
50654
50655           return str;
50656         }
50657
50658         function readUtf8TextDecoder(buf, pos, end) {
50659           return utf8TextDecoder.decode(buf.subarray(pos, end));
50660         }
50661
50662         function writeUtf8(buf, str, pos) {
50663           for (var i = 0, c, lead; i < str.length; i++) {
50664             c = str.charCodeAt(i); // code point
50665
50666             if (c > 0xD7FF && c < 0xE000) {
50667               if (lead) {
50668                 if (c < 0xDC00) {
50669                   buf[pos++] = 0xEF;
50670                   buf[pos++] = 0xBF;
50671                   buf[pos++] = 0xBD;
50672                   lead = c;
50673                   continue;
50674                 } else {
50675                   c = lead - 0xD800 << 10 | c - 0xDC00 | 0x10000;
50676                   lead = null;
50677                 }
50678               } else {
50679                 if (c > 0xDBFF || i + 1 === str.length) {
50680                   buf[pos++] = 0xEF;
50681                   buf[pos++] = 0xBF;
50682                   buf[pos++] = 0xBD;
50683                 } else {
50684                   lead = c;
50685                 }
50686
50687                 continue;
50688               }
50689             } else if (lead) {
50690               buf[pos++] = 0xEF;
50691               buf[pos++] = 0xBF;
50692               buf[pos++] = 0xBD;
50693               lead = null;
50694             }
50695
50696             if (c < 0x80) {
50697               buf[pos++] = c;
50698             } else {
50699               if (c < 0x800) {
50700                 buf[pos++] = c >> 0x6 | 0xC0;
50701               } else {
50702                 if (c < 0x10000) {
50703                   buf[pos++] = c >> 0xC | 0xE0;
50704                 } else {
50705                   buf[pos++] = c >> 0x12 | 0xF0;
50706                   buf[pos++] = c >> 0xC & 0x3F | 0x80;
50707                 }
50708
50709                 buf[pos++] = c >> 0x6 & 0x3F | 0x80;
50710               }
50711
50712               buf[pos++] = c & 0x3F | 0x80;
50713             }
50714           }
50715
50716           return pos;
50717         }
50718
50719         var pointGeometry = Point;
50720         /**
50721          * A standalone point geometry with useful accessor, comparison, and
50722          * modification methods.
50723          *
50724          * @class Point
50725          * @param {Number} x the x-coordinate. this could be longitude or screen
50726          * pixels, or any other sort of unit.
50727          * @param {Number} y the y-coordinate. this could be latitude or screen
50728          * pixels, or any other sort of unit.
50729          * @example
50730          * var point = new Point(-77, 38);
50731          */
50732
50733         function Point(x, y) {
50734           this.x = x;
50735           this.y = y;
50736         }
50737
50738         Point.prototype = {
50739           /**
50740            * Clone this point, returning a new point that can be modified
50741            * without affecting the old one.
50742            * @return {Point} the clone
50743            */
50744           clone: function clone() {
50745             return new Point(this.x, this.y);
50746           },
50747
50748           /**
50749            * Add this point's x & y coordinates to another point,
50750            * yielding a new point.
50751            * @param {Point} p the other point
50752            * @return {Point} output point
50753            */
50754           add: function add(p) {
50755             return this.clone()._add(p);
50756           },
50757
50758           /**
50759            * Subtract this point's x & y coordinates to from point,
50760            * yielding a new point.
50761            * @param {Point} p the other point
50762            * @return {Point} output point
50763            */
50764           sub: function sub(p) {
50765             return this.clone()._sub(p);
50766           },
50767
50768           /**
50769            * Multiply this point's x & y coordinates by point,
50770            * yielding a new point.
50771            * @param {Point} p the other point
50772            * @return {Point} output point
50773            */
50774           multByPoint: function multByPoint(p) {
50775             return this.clone()._multByPoint(p);
50776           },
50777
50778           /**
50779            * Divide this point's x & y coordinates by point,
50780            * yielding a new point.
50781            * @param {Point} p the other point
50782            * @return {Point} output point
50783            */
50784           divByPoint: function divByPoint(p) {
50785             return this.clone()._divByPoint(p);
50786           },
50787
50788           /**
50789            * Multiply this point's x & y coordinates by a factor,
50790            * yielding a new point.
50791            * @param {Point} k factor
50792            * @return {Point} output point
50793            */
50794           mult: function mult(k) {
50795             return this.clone()._mult(k);
50796           },
50797
50798           /**
50799            * Divide this point's x & y coordinates by a factor,
50800            * yielding a new point.
50801            * @param {Point} k factor
50802            * @return {Point} output point
50803            */
50804           div: function div(k) {
50805             return this.clone()._div(k);
50806           },
50807
50808           /**
50809            * Rotate this point around the 0, 0 origin by an angle a,
50810            * given in radians
50811            * @param {Number} a angle to rotate around, in radians
50812            * @return {Point} output point
50813            */
50814           rotate: function rotate(a) {
50815             return this.clone()._rotate(a);
50816           },
50817
50818           /**
50819            * Rotate this point around p point by an angle a,
50820            * given in radians
50821            * @param {Number} a angle to rotate around, in radians
50822            * @param {Point} p Point to rotate around
50823            * @return {Point} output point
50824            */
50825           rotateAround: function rotateAround(a, p) {
50826             return this.clone()._rotateAround(a, p);
50827           },
50828
50829           /**
50830            * Multiply this point by a 4x1 transformation matrix
50831            * @param {Array<Number>} m transformation matrix
50832            * @return {Point} output point
50833            */
50834           matMult: function matMult(m) {
50835             return this.clone()._matMult(m);
50836           },
50837
50838           /**
50839            * Calculate this point but as a unit vector from 0, 0, meaning
50840            * that the distance from the resulting point to the 0, 0
50841            * coordinate will be equal to 1 and the angle from the resulting
50842            * point to the 0, 0 coordinate will be the same as before.
50843            * @return {Point} unit vector point
50844            */
50845           unit: function unit() {
50846             return this.clone()._unit();
50847           },
50848
50849           /**
50850            * Compute a perpendicular point, where the new y coordinate
50851            * is the old x coordinate and the new x coordinate is the old y
50852            * coordinate multiplied by -1
50853            * @return {Point} perpendicular point
50854            */
50855           perp: function perp() {
50856             return this.clone()._perp();
50857           },
50858
50859           /**
50860            * Return a version of this point with the x & y coordinates
50861            * rounded to integers.
50862            * @return {Point} rounded point
50863            */
50864           round: function round() {
50865             return this.clone()._round();
50866           },
50867
50868           /**
50869            * Return the magitude of this point: this is the Euclidean
50870            * distance from the 0, 0 coordinate to this point's x and y
50871            * coordinates.
50872            * @return {Number} magnitude
50873            */
50874           mag: function mag() {
50875             return Math.sqrt(this.x * this.x + this.y * this.y);
50876           },
50877
50878           /**
50879            * Judge whether this point is equal to another point, returning
50880            * true or false.
50881            * @param {Point} other the other point
50882            * @return {boolean} whether the points are equal
50883            */
50884           equals: function equals(other) {
50885             return this.x === other.x && this.y === other.y;
50886           },
50887
50888           /**
50889            * Calculate the distance from this point to another point
50890            * @param {Point} p the other point
50891            * @return {Number} distance
50892            */
50893           dist: function dist(p) {
50894             return Math.sqrt(this.distSqr(p));
50895           },
50896
50897           /**
50898            * Calculate the distance from this point to another point,
50899            * without the square root step. Useful if you're comparing
50900            * relative distances.
50901            * @param {Point} p the other point
50902            * @return {Number} distance
50903            */
50904           distSqr: function distSqr(p) {
50905             var dx = p.x - this.x,
50906                 dy = p.y - this.y;
50907             return dx * dx + dy * dy;
50908           },
50909
50910           /**
50911            * Get the angle from the 0, 0 coordinate to this point, in radians
50912            * coordinates.
50913            * @return {Number} angle
50914            */
50915           angle: function angle() {
50916             return Math.atan2(this.y, this.x);
50917           },
50918
50919           /**
50920            * Get the angle from this point to another point, in radians
50921            * @param {Point} b the other point
50922            * @return {Number} angle
50923            */
50924           angleTo: function angleTo(b) {
50925             return Math.atan2(this.y - b.y, this.x - b.x);
50926           },
50927
50928           /**
50929            * Get the angle between this point and another point, in radians
50930            * @param {Point} b the other point
50931            * @return {Number} angle
50932            */
50933           angleWith: function angleWith(b) {
50934             return this.angleWithSep(b.x, b.y);
50935           },
50936
50937           /*
50938            * Find the angle of the two vectors, solving the formula for
50939            * the cross product a x b = |a||b|sin(θ) for θ.
50940            * @param {Number} x the x-coordinate
50941            * @param {Number} y the y-coordinate
50942            * @return {Number} the angle in radians
50943            */
50944           angleWithSep: function angleWithSep(x, y) {
50945             return Math.atan2(this.x * y - this.y * x, this.x * x + this.y * y);
50946           },
50947           _matMult: function _matMult(m) {
50948             var x = m[0] * this.x + m[1] * this.y,
50949                 y = m[2] * this.x + m[3] * this.y;
50950             this.x = x;
50951             this.y = y;
50952             return this;
50953           },
50954           _add: function _add(p) {
50955             this.x += p.x;
50956             this.y += p.y;
50957             return this;
50958           },
50959           _sub: function _sub(p) {
50960             this.x -= p.x;
50961             this.y -= p.y;
50962             return this;
50963           },
50964           _mult: function _mult(k) {
50965             this.x *= k;
50966             this.y *= k;
50967             return this;
50968           },
50969           _div: function _div(k) {
50970             this.x /= k;
50971             this.y /= k;
50972             return this;
50973           },
50974           _multByPoint: function _multByPoint(p) {
50975             this.x *= p.x;
50976             this.y *= p.y;
50977             return this;
50978           },
50979           _divByPoint: function _divByPoint(p) {
50980             this.x /= p.x;
50981             this.y /= p.y;
50982             return this;
50983           },
50984           _unit: function _unit() {
50985             this._div(this.mag());
50986
50987             return this;
50988           },
50989           _perp: function _perp() {
50990             var y = this.y;
50991             this.y = this.x;
50992             this.x = -y;
50993             return this;
50994           },
50995           _rotate: function _rotate(angle) {
50996             var cos = Math.cos(angle),
50997                 sin = Math.sin(angle),
50998                 x = cos * this.x - sin * this.y,
50999                 y = sin * this.x + cos * this.y;
51000             this.x = x;
51001             this.y = y;
51002             return this;
51003           },
51004           _rotateAround: function _rotateAround(angle, p) {
51005             var cos = Math.cos(angle),
51006                 sin = Math.sin(angle),
51007                 x = p.x + cos * (this.x - p.x) - sin * (this.y - p.y),
51008                 y = p.y + sin * (this.x - p.x) + cos * (this.y - p.y);
51009             this.x = x;
51010             this.y = y;
51011             return this;
51012           },
51013           _round: function _round() {
51014             this.x = Math.round(this.x);
51015             this.y = Math.round(this.y);
51016             return this;
51017           }
51018         };
51019         /**
51020          * Construct a point from an array if necessary, otherwise if the input
51021          * is already a Point, or an unknown type, return it unchanged
51022          * @param {Array<Number>|Point|*} a any kind of input value
51023          * @return {Point} constructed point, or passed-through value.
51024          * @example
51025          * // this
51026          * var point = Point.convert([0, 1]);
51027          * // is equivalent to
51028          * var point = new Point(0, 1);
51029          */
51030
51031         Point.convert = function (a) {
51032           if (a instanceof Point) {
51033             return a;
51034           }
51035
51036           if (Array.isArray(a)) {
51037             return new Point(a[0], a[1]);
51038           }
51039
51040           return a;
51041         };
51042
51043         var vectortilefeature = VectorTileFeature$1;
51044
51045         function VectorTileFeature$1(pbf, end, extent, keys, values) {
51046           // Public
51047           this.properties = {};
51048           this.extent = extent;
51049           this.type = 0; // Private
51050
51051           this._pbf = pbf;
51052           this._geometry = -1;
51053           this._keys = keys;
51054           this._values = values;
51055           pbf.readFields(readFeature, this, end);
51056         }
51057
51058         function readFeature(tag, feature, pbf) {
51059           if (tag == 1) feature.id = pbf.readVarint();else if (tag == 2) readTag(pbf, feature);else if (tag == 3) feature.type = pbf.readVarint();else if (tag == 4) feature._geometry = pbf.pos;
51060         }
51061
51062         function readTag(pbf, feature) {
51063           var end = pbf.readVarint() + pbf.pos;
51064
51065           while (pbf.pos < end) {
51066             var key = feature._keys[pbf.readVarint()],
51067                 value = feature._values[pbf.readVarint()];
51068
51069             feature.properties[key] = value;
51070           }
51071         }
51072
51073         VectorTileFeature$1.types = ['Unknown', 'Point', 'LineString', 'Polygon'];
51074
51075         VectorTileFeature$1.prototype.loadGeometry = function () {
51076           var pbf = this._pbf;
51077           pbf.pos = this._geometry;
51078           var end = pbf.readVarint() + pbf.pos,
51079               cmd = 1,
51080               length = 0,
51081               x = 0,
51082               y = 0,
51083               lines = [],
51084               line;
51085
51086           while (pbf.pos < end) {
51087             if (length <= 0) {
51088               var cmdLen = pbf.readVarint();
51089               cmd = cmdLen & 0x7;
51090               length = cmdLen >> 3;
51091             }
51092
51093             length--;
51094
51095             if (cmd === 1 || cmd === 2) {
51096               x += pbf.readSVarint();
51097               y += pbf.readSVarint();
51098
51099               if (cmd === 1) {
51100                 // moveTo
51101                 if (line) lines.push(line);
51102                 line = [];
51103               }
51104
51105               line.push(new pointGeometry(x, y));
51106             } else if (cmd === 7) {
51107               // Workaround for https://github.com/mapbox/mapnik-vector-tile/issues/90
51108               if (line) {
51109                 line.push(line[0].clone()); // closePolygon
51110               }
51111             } else {
51112               throw new Error('unknown command ' + cmd);
51113             }
51114           }
51115
51116           if (line) lines.push(line);
51117           return lines;
51118         };
51119
51120         VectorTileFeature$1.prototype.bbox = function () {
51121           var pbf = this._pbf;
51122           pbf.pos = this._geometry;
51123           var end = pbf.readVarint() + pbf.pos,
51124               cmd = 1,
51125               length = 0,
51126               x = 0,
51127               y = 0,
51128               x1 = Infinity,
51129               x2 = -Infinity,
51130               y1 = Infinity,
51131               y2 = -Infinity;
51132
51133           while (pbf.pos < end) {
51134             if (length <= 0) {
51135               var cmdLen = pbf.readVarint();
51136               cmd = cmdLen & 0x7;
51137               length = cmdLen >> 3;
51138             }
51139
51140             length--;
51141
51142             if (cmd === 1 || cmd === 2) {
51143               x += pbf.readSVarint();
51144               y += pbf.readSVarint();
51145               if (x < x1) x1 = x;
51146               if (x > x2) x2 = x;
51147               if (y < y1) y1 = y;
51148               if (y > y2) y2 = y;
51149             } else if (cmd !== 7) {
51150               throw new Error('unknown command ' + cmd);
51151             }
51152           }
51153
51154           return [x1, y1, x2, y2];
51155         };
51156
51157         VectorTileFeature$1.prototype.toGeoJSON = function (x, y, z) {
51158           var size = this.extent * Math.pow(2, z),
51159               x0 = this.extent * x,
51160               y0 = this.extent * y,
51161               coords = this.loadGeometry(),
51162               type = VectorTileFeature$1.types[this.type],
51163               i,
51164               j;
51165
51166           function project(line) {
51167             for (var j = 0; j < line.length; j++) {
51168               var p = line[j],
51169                   y2 = 180 - (p.y + y0) * 360 / size;
51170               line[j] = [(p.x + x0) * 360 / size - 180, 360 / Math.PI * Math.atan(Math.exp(y2 * Math.PI / 180)) - 90];
51171             }
51172           }
51173
51174           switch (this.type) {
51175             case 1:
51176               var points = [];
51177
51178               for (i = 0; i < coords.length; i++) {
51179                 points[i] = coords[i][0];
51180               }
51181
51182               coords = points;
51183               project(coords);
51184               break;
51185
51186             case 2:
51187               for (i = 0; i < coords.length; i++) {
51188                 project(coords[i]);
51189               }
51190
51191               break;
51192
51193             case 3:
51194               coords = classifyRings(coords);
51195
51196               for (i = 0; i < coords.length; i++) {
51197                 for (j = 0; j < coords[i].length; j++) {
51198                   project(coords[i][j]);
51199                 }
51200               }
51201
51202               break;
51203           }
51204
51205           if (coords.length === 1) {
51206             coords = coords[0];
51207           } else {
51208             type = 'Multi' + type;
51209           }
51210
51211           var result = {
51212             type: "Feature",
51213             geometry: {
51214               type: type,
51215               coordinates: coords
51216             },
51217             properties: this.properties
51218           };
51219
51220           if ('id' in this) {
51221             result.id = this.id;
51222           }
51223
51224           return result;
51225         }; // classifies an array of rings into polygons with outer rings and holes
51226
51227
51228         function classifyRings(rings) {
51229           var len = rings.length;
51230           if (len <= 1) return [rings];
51231           var polygons = [],
51232               polygon,
51233               ccw;
51234
51235           for (var i = 0; i < len; i++) {
51236             var area = signedArea(rings[i]);
51237             if (area === 0) continue;
51238             if (ccw === undefined) ccw = area < 0;
51239
51240             if (ccw === area < 0) {
51241               if (polygon) polygons.push(polygon);
51242               polygon = [rings[i]];
51243             } else {
51244               polygon.push(rings[i]);
51245             }
51246           }
51247
51248           if (polygon) polygons.push(polygon);
51249           return polygons;
51250         }
51251
51252         function signedArea(ring) {
51253           var sum = 0;
51254
51255           for (var i = 0, len = ring.length, j = len - 1, p1, p2; i < len; j = i++) {
51256             p1 = ring[i];
51257             p2 = ring[j];
51258             sum += (p2.x - p1.x) * (p1.y + p2.y);
51259           }
51260
51261           return sum;
51262         }
51263
51264         var vectortilelayer = VectorTileLayer$1;
51265
51266         function VectorTileLayer$1(pbf, end) {
51267           // Public
51268           this.version = 1;
51269           this.name = null;
51270           this.extent = 4096;
51271           this.length = 0; // Private
51272
51273           this._pbf = pbf;
51274           this._keys = [];
51275           this._values = [];
51276           this._features = [];
51277           pbf.readFields(readLayer, this, end);
51278           this.length = this._features.length;
51279         }
51280
51281         function readLayer(tag, layer, pbf) {
51282           if (tag === 15) layer.version = pbf.readVarint();else if (tag === 1) layer.name = pbf.readString();else if (tag === 5) layer.extent = pbf.readVarint();else if (tag === 2) layer._features.push(pbf.pos);else if (tag === 3) layer._keys.push(pbf.readString());else if (tag === 4) layer._values.push(readValueMessage(pbf));
51283         }
51284
51285         function readValueMessage(pbf) {
51286           var value = null,
51287               end = pbf.readVarint() + pbf.pos;
51288
51289           while (pbf.pos < end) {
51290             var tag = pbf.readVarint() >> 3;
51291             value = tag === 1 ? pbf.readString() : tag === 2 ? pbf.readFloat() : tag === 3 ? pbf.readDouble() : tag === 4 ? pbf.readVarint64() : tag === 5 ? pbf.readVarint() : tag === 6 ? pbf.readSVarint() : tag === 7 ? pbf.readBoolean() : null;
51292           }
51293
51294           return value;
51295         } // return feature `i` from this layer as a `VectorTileFeature`
51296
51297
51298         VectorTileLayer$1.prototype.feature = function (i) {
51299           if (i < 0 || i >= this._features.length) throw new Error('feature index out of bounds');
51300           this._pbf.pos = this._features[i];
51301
51302           var end = this._pbf.readVarint() + this._pbf.pos;
51303
51304           return new vectortilefeature(this._pbf, end, this.extent, this._keys, this._values);
51305         };
51306
51307         var vectortile = VectorTile$1;
51308
51309         function VectorTile$1(pbf, end) {
51310           this.layers = pbf.readFields(readTile, {}, end);
51311         }
51312
51313         function readTile(tag, layers, pbf) {
51314           if (tag === 3) {
51315             var layer = new vectortilelayer(pbf, pbf.readVarint() + pbf.pos);
51316             if (layer.length) layers[layer.name] = layer;
51317           }
51318         }
51319
51320         var VectorTile = vectortile;
51321         var VectorTileFeature = vectortilefeature;
51322         var VectorTileLayer = vectortilelayer;
51323         var vectorTile = {
51324           VectorTile: VectorTile,
51325           VectorTileFeature: VectorTileFeature,
51326           VectorTileLayer: VectorTileLayer
51327         };
51328
51329         var accessToken = 'MLY|4100327730013843|5bb78b81720791946a9a7b956c57b7cf';
51330         var apiUrl = 'https://graph.mapillary.com/';
51331         var baseTileUrl = 'https://tiles.mapillary.com/maps/vtp';
51332         var mapFeatureTileUrl = "".concat(baseTileUrl, "/mly_map_feature_point/2/{z}/{x}/{y}?access_token=").concat(accessToken);
51333         var tileUrl = "".concat(baseTileUrl, "/mly1_public/2/{z}/{x}/{y}?access_token=").concat(accessToken);
51334         var trafficSignTileUrl = "".concat(baseTileUrl, "/mly_map_feature_traffic_sign/2/{z}/{x}/{y}?access_token=").concat(accessToken);
51335         var viewercss = 'mapillary-js/mapillary.css';
51336         var viewerjs = 'mapillary-js/mapillary.js';
51337         var minZoom$1 = 14;
51338         var dispatch$4 = dispatch$8('change', 'loadedImages', 'loadedSigns', 'loadedMapFeatures', 'bearingChanged', 'imageChanged');
51339
51340         var _loadViewerPromise$2;
51341
51342         var _mlyActiveImage;
51343
51344         var _mlyCache;
51345
51346         var _mlyFallback = false;
51347
51348         var _mlyHighlightedDetection;
51349
51350         var _mlyShowFeatureDetections = false;
51351         var _mlyShowSignDetections = false;
51352
51353         var _mlyViewer;
51354
51355         var _mlyViewerFilter = ['all']; // Load all data for the specified type from Mapillary vector tiles
51356
51357         function loadTiles$2(which, url, maxZoom, projection) {
51358           var tiler = utilTiler().zoomExtent([minZoom$1, maxZoom]).skipNullIsland(true);
51359           var tiles = tiler.getTiles(projection);
51360           tiles.forEach(function (tile) {
51361             loadTile$1(which, url, tile);
51362           });
51363         } // Load all data for the specified type from one vector tile
51364
51365
51366         function loadTile$1(which, url, tile) {
51367           var cache = _mlyCache.requests;
51368           var tileId = "".concat(tile.id, "-").concat(which);
51369           if (cache.loaded[tileId] || cache.inflight[tileId]) return;
51370           var controller = new AbortController();
51371           cache.inflight[tileId] = controller;
51372           var requestUrl = url.replace('{x}', tile.xyz[0]).replace('{y}', tile.xyz[1]).replace('{z}', tile.xyz[2]);
51373           fetch(requestUrl, {
51374             signal: controller.signal
51375           }).then(function (response) {
51376             if (!response.ok) {
51377               throw new Error(response.status + ' ' + response.statusText);
51378             }
51379
51380             cache.loaded[tileId] = true;
51381             delete cache.inflight[tileId];
51382             return response.arrayBuffer();
51383           }).then(function (data) {
51384             if (!data) {
51385               throw new Error('No Data');
51386             }
51387
51388             loadTileDataToCache(data, tile, which);
51389
51390             if (which === 'images') {
51391               dispatch$4.call('loadedImages');
51392             } else if (which === 'signs') {
51393               dispatch$4.call('loadedSigns');
51394             } else if (which === 'points') {
51395               dispatch$4.call('loadedMapFeatures');
51396             }
51397           })["catch"](function () {
51398             cache.loaded[tileId] = true;
51399             delete cache.inflight[tileId];
51400           });
51401         } // Load the data from the vector tile into cache
51402
51403
51404         function loadTileDataToCache(data, tile, which) {
51405           var vectorTile = new VectorTile(new pbf(data));
51406           var features, cache, layer, i, feature, loc, d;
51407
51408           if (vectorTile.layers.hasOwnProperty('image')) {
51409             features = [];
51410             cache = _mlyCache.images;
51411             layer = vectorTile.layers.image;
51412
51413             for (i = 0; i < layer.length; i++) {
51414               feature = layer.feature(i).toGeoJSON(tile.xyz[0], tile.xyz[1], tile.xyz[2]);
51415               loc = feature.geometry.coordinates;
51416               d = {
51417                 loc: loc,
51418                 captured_at: feature.properties.captured_at,
51419                 ca: feature.properties.compass_angle,
51420                 id: feature.properties.id,
51421                 is_pano: feature.properties.is_pano,
51422                 sequence_id: feature.properties.sequence_id
51423               };
51424               cache.forImageId[d.id] = d;
51425               features.push({
51426                 minX: loc[0],
51427                 minY: loc[1],
51428                 maxX: loc[0],
51429                 maxY: loc[1],
51430                 data: d
51431               });
51432             }
51433
51434             if (cache.rtree) {
51435               cache.rtree.load(features);
51436             }
51437           }
51438
51439           if (vectorTile.layers.hasOwnProperty('sequence')) {
51440             features = [];
51441             cache = _mlyCache.sequences;
51442             layer = vectorTile.layers.sequence;
51443
51444             for (i = 0; i < layer.length; i++) {
51445               feature = layer.feature(i).toGeoJSON(tile.xyz[0], tile.xyz[1], tile.xyz[2]);
51446
51447               if (cache.lineString[feature.properties.id]) {
51448                 cache.lineString[feature.properties.id].push(feature);
51449               } else {
51450                 cache.lineString[feature.properties.id] = [feature];
51451               }
51452             }
51453           }
51454
51455           if (vectorTile.layers.hasOwnProperty('point')) {
51456             features = [];
51457             cache = _mlyCache[which];
51458             layer = vectorTile.layers.point;
51459
51460             for (i = 0; i < layer.length; i++) {
51461               feature = layer.feature(i).toGeoJSON(tile.xyz[0], tile.xyz[1], tile.xyz[2]);
51462               loc = feature.geometry.coordinates;
51463               d = {
51464                 loc: loc,
51465                 id: feature.properties.id,
51466                 first_seen_at: feature.properties.first_seen_at,
51467                 last_seen_at: feature.properties.last_seen_at,
51468                 value: feature.properties.value
51469               };
51470               features.push({
51471                 minX: loc[0],
51472                 minY: loc[1],
51473                 maxX: loc[0],
51474                 maxY: loc[1],
51475                 data: d
51476               });
51477             }
51478
51479             if (cache.rtree) {
51480               cache.rtree.load(features);
51481             }
51482           }
51483
51484           if (vectorTile.layers.hasOwnProperty('traffic_sign')) {
51485             features = [];
51486             cache = _mlyCache[which];
51487             layer = vectorTile.layers.traffic_sign;
51488
51489             for (i = 0; i < layer.length; i++) {
51490               feature = layer.feature(i).toGeoJSON(tile.xyz[0], tile.xyz[1], tile.xyz[2]);
51491               loc = feature.geometry.coordinates;
51492               d = {
51493                 loc: loc,
51494                 id: feature.properties.id,
51495                 first_seen_at: feature.properties.first_seen_at,
51496                 last_seen_at: feature.properties.last_seen_at,
51497                 value: feature.properties.value
51498               };
51499               features.push({
51500                 minX: loc[0],
51501                 minY: loc[1],
51502                 maxX: loc[0],
51503                 maxY: loc[1],
51504                 data: d
51505               });
51506             }
51507
51508             if (cache.rtree) {
51509               cache.rtree.load(features);
51510             }
51511           }
51512         } // Get data from the API
51513
51514
51515         function loadData(url) {
51516           return fetch(url).then(function (response) {
51517             if (!response.ok) {
51518               throw new Error(response.status + ' ' + response.statusText);
51519             }
51520
51521             return response.json();
51522           }).then(function (result) {
51523             if (!result) {
51524               return [];
51525             }
51526
51527             return result.data || [];
51528           });
51529         } // Partition viewport into higher zoom tiles
51530
51531
51532         function partitionViewport$2(projection) {
51533           var z = geoScaleToZoom(projection.scale());
51534           var z2 = Math.ceil(z * 2) / 2 + 2.5; // round to next 0.5 and add 2.5
51535
51536           var tiler = utilTiler().zoomExtent([z2, z2]);
51537           return tiler.getTiles(projection).map(function (tile) {
51538             return tile.extent;
51539           });
51540         } // Return no more than `limit` results per partition.
51541
51542
51543         function searchLimited$2(limit, projection, rtree) {
51544           limit = limit || 5;
51545           return partitionViewport$2(projection).reduce(function (result, extent) {
51546             var found = rtree.search(extent.bbox()).slice(0, limit).map(function (d) {
51547               return d.data;
51548             });
51549             return found.length ? result.concat(found) : result;
51550           }, []);
51551         }
51552
51553         var serviceMapillary = {
51554           // Initialize Mapillary
51555           init: function init() {
51556             if (!_mlyCache) {
51557               this.reset();
51558             }
51559
51560             this.event = utilRebind(this, dispatch$4, 'on');
51561           },
51562           // Reset cache and state
51563           reset: function reset() {
51564             if (_mlyCache) {
51565               Object.values(_mlyCache.requests.inflight).forEach(function (request) {
51566                 request.abort();
51567               });
51568             }
51569
51570             _mlyCache = {
51571               images: {
51572                 rtree: new RBush(),
51573                 forImageId: {}
51574               },
51575               image_detections: {
51576                 forImageId: {}
51577               },
51578               signs: {
51579                 rtree: new RBush()
51580               },
51581               points: {
51582                 rtree: new RBush()
51583               },
51584               sequences: {
51585                 rtree: new RBush(),
51586                 lineString: {}
51587               },
51588               requests: {
51589                 loaded: {},
51590                 inflight: {}
51591               }
51592             };
51593             _mlyActiveImage = null;
51594           },
51595           // Get visible images
51596           images: function images(projection) {
51597             var limit = 5;
51598             return searchLimited$2(limit, projection, _mlyCache.images.rtree);
51599           },
51600           // Get visible traffic signs
51601           signs: function signs(projection) {
51602             var limit = 5;
51603             return searchLimited$2(limit, projection, _mlyCache.signs.rtree);
51604           },
51605           // Get visible map (point) features
51606           mapFeatures: function mapFeatures(projection) {
51607             var limit = 5;
51608             return searchLimited$2(limit, projection, _mlyCache.points.rtree);
51609           },
51610           // Get cached image by id
51611           cachedImage: function cachedImage(imageId) {
51612             return _mlyCache.images.forImageId[imageId];
51613           },
51614           // Get visible sequences
51615           sequences: function sequences(projection) {
51616             var viewport = projection.clipExtent();
51617             var min = [viewport[0][0], viewport[1][1]];
51618             var max = [viewport[1][0], viewport[0][1]];
51619             var bbox = geoExtent(projection.invert(min), projection.invert(max)).bbox();
51620             var sequenceIds = {};
51621             var lineStrings = [];
51622
51623             _mlyCache.images.rtree.search(bbox).forEach(function (d) {
51624               if (d.data.sequence_id) {
51625                 sequenceIds[d.data.sequence_id] = true;
51626               }
51627             });
51628
51629             Object.keys(sequenceIds).forEach(function (sequenceId) {
51630               if (_mlyCache.sequences.lineString[sequenceId]) {
51631                 lineStrings = lineStrings.concat(_mlyCache.sequences.lineString[sequenceId]);
51632               }
51633             });
51634             return lineStrings;
51635           },
51636           // Load images in the visible area
51637           loadImages: function loadImages(projection) {
51638             loadTiles$2('images', tileUrl, 14, projection);
51639           },
51640           // Load traffic signs in the visible area
51641           loadSigns: function loadSigns(projection) {
51642             loadTiles$2('signs', trafficSignTileUrl, 14, projection);
51643           },
51644           // Load map (point) features in the visible area
51645           loadMapFeatures: function loadMapFeatures(projection) {
51646             loadTiles$2('points', mapFeatureTileUrl, 14, projection);
51647           },
51648           // Return a promise that resolves when the image viewer (Mapillary JS) library has finished loading
51649           ensureViewerLoaded: function ensureViewerLoaded(context) {
51650             if (_loadViewerPromise$2) return _loadViewerPromise$2; // add mly-wrapper
51651
51652             var wrap = context.container().select('.photoviewer').selectAll('.mly-wrapper').data([0]);
51653             wrap.enter().append('div').attr('id', 'ideditor-mly').attr('class', 'photo-wrapper mly-wrapper').classed('hide', true);
51654             var that = this;
51655             _loadViewerPromise$2 = new Promise(function (resolve, reject) {
51656               var loadedCount = 0;
51657
51658               function loaded() {
51659                 loadedCount += 1; // wait until both files are loaded
51660
51661                 if (loadedCount === 2) resolve();
51662               }
51663
51664               var head = select('head'); // load mapillary-viewercss
51665
51666               head.selectAll('#ideditor-mapillary-viewercss').data([0]).enter().append('link').attr('id', 'ideditor-mapillary-viewercss').attr('rel', 'stylesheet').attr('crossorigin', 'anonymous').attr('href', context.asset(viewercss)).on('load.serviceMapillary', loaded).on('error.serviceMapillary', function () {
51667                 reject();
51668               }); // load mapillary-viewerjs
51669
51670               head.selectAll('#ideditor-mapillary-viewerjs').data([0]).enter().append('script').attr('id', 'ideditor-mapillary-viewerjs').attr('crossorigin', 'anonymous').attr('src', context.asset(viewerjs)).on('load.serviceMapillary', loaded).on('error.serviceMapillary', function () {
51671                 reject();
51672               });
51673             })["catch"](function () {
51674               _loadViewerPromise$2 = null;
51675             }).then(function () {
51676               that.initViewer(context);
51677             });
51678             return _loadViewerPromise$2;
51679           },
51680           // Load traffic sign image sprites
51681           loadSignResources: function loadSignResources(context) {
51682             context.ui().svgDefs.addSprites(['mapillary-sprite'], false
51683             /* don't override colors */
51684             );
51685             return this;
51686           },
51687           // Load map (point) feature image sprites
51688           loadObjectResources: function loadObjectResources(context) {
51689             context.ui().svgDefs.addSprites(['mapillary-object-sprite'], false
51690             /* don't override colors */
51691             );
51692             return this;
51693           },
51694           // Remove previous detections in image viewer
51695           resetTags: function resetTags() {
51696             if (_mlyViewer && !_mlyFallback) {
51697               _mlyViewer.getComponent('tag').removeAll();
51698             }
51699           },
51700           // Show map feature detections in image viewer
51701           showFeatureDetections: function showFeatureDetections(value) {
51702             _mlyShowFeatureDetections = value;
51703
51704             if (!_mlyShowFeatureDetections && !_mlyShowSignDetections) {
51705               this.resetTags();
51706             }
51707           },
51708           // Show traffic sign detections in image viewer
51709           showSignDetections: function showSignDetections(value) {
51710             _mlyShowSignDetections = value;
51711
51712             if (!_mlyShowFeatureDetections && !_mlyShowSignDetections) {
51713               this.resetTags();
51714             }
51715           },
51716           // Apply filter to image viewer
51717           filterViewer: function filterViewer(context) {
51718             var showsPano = context.photos().showsPanoramic();
51719             var showsFlat = context.photos().showsFlat();
51720             var fromDate = context.photos().fromDate();
51721             var toDate = context.photos().toDate();
51722             var filter = ['all'];
51723             if (!showsPano) filter.push(['!=', 'cameraType', 'spherical']);
51724             if (!showsFlat && showsPano) filter.push(['==', 'pano', true]);
51725
51726             if (fromDate) {
51727               filter.push(['>=', 'capturedAt', new Date(fromDate).getTime()]);
51728             }
51729
51730             if (toDate) {
51731               filter.push(['>=', 'capturedAt', new Date(toDate).getTime()]);
51732             }
51733
51734             if (_mlyViewer) {
51735               _mlyViewer.setFilter(filter);
51736             }
51737
51738             _mlyViewerFilter = filter;
51739             return filter;
51740           },
51741           // Make the image viewer visible
51742           showViewer: function showViewer(context) {
51743             var wrap = context.container().select('.photoviewer').classed('hide', false);
51744             var isHidden = wrap.selectAll('.photo-wrapper.mly-wrapper.hide').size();
51745
51746             if (isHidden && _mlyViewer) {
51747               wrap.selectAll('.photo-wrapper:not(.mly-wrapper)').classed('hide', true);
51748               wrap.selectAll('.photo-wrapper.mly-wrapper').classed('hide', false);
51749
51750               _mlyViewer.resize();
51751             }
51752
51753             return this;
51754           },
51755           // Hide the image viewer and resets map markers
51756           hideViewer: function hideViewer(context) {
51757             _mlyActiveImage = null;
51758
51759             if (!_mlyFallback && _mlyViewer) {
51760               _mlyViewer.getComponent('sequence').stop();
51761             }
51762
51763             var viewer = context.container().select('.photoviewer');
51764             if (!viewer.empty()) viewer.datum(null);
51765             viewer.classed('hide', true).selectAll('.photo-wrapper').classed('hide', true);
51766             this.updateUrlImage(null);
51767             dispatch$4.call('imageChanged');
51768             dispatch$4.call('loadedMapFeatures');
51769             dispatch$4.call('loadedSigns');
51770             return this.setStyles(context, null);
51771           },
51772           // Update the URL with current image id
51773           updateUrlImage: function updateUrlImage(imageId) {
51774             if (!window.mocha) {
51775               var hash = utilStringQs(window.location.hash);
51776
51777               if (imageId) {
51778                 hash.photo = 'mapillary/' + imageId;
51779               } else {
51780                 delete hash.photo;
51781               }
51782
51783               window.location.replace('#' + utilQsString(hash, true));
51784             }
51785           },
51786           // Highlight the detection in the viewer that is related to the clicked map feature
51787           highlightDetection: function highlightDetection(detection) {
51788             if (detection) {
51789               _mlyHighlightedDetection = detection.id;
51790             }
51791
51792             return this;
51793           },
51794           // Initialize image viewer (Mapillar JS)
51795           initViewer: function initViewer(context) {
51796             var that = this;
51797             if (!window.mapillary) return;
51798             var opts = {
51799               accessToken: accessToken,
51800               component: {
51801                 cover: false,
51802                 keyboard: false,
51803                 tag: true
51804               },
51805               container: 'ideditor-mly'
51806             }; // Disable components requiring WebGL support
51807
51808             if (!mapillary.isSupported() && mapillary.isFallbackSupported()) {
51809               _mlyFallback = true;
51810               opts.component = {
51811                 cover: false,
51812                 direction: false,
51813                 imagePlane: false,
51814                 keyboard: false,
51815                 mouse: false,
51816                 sequence: false,
51817                 tag: false,
51818                 image: true,
51819                 // fallback
51820                 navigation: true // fallback
51821
51822               };
51823             }
51824
51825             _mlyViewer = new mapillary.Viewer(opts);
51826
51827             _mlyViewer.on('image', imageChanged);
51828
51829             _mlyViewer.on('bearing', bearingChanged);
51830
51831             if (_mlyViewerFilter) {
51832               _mlyViewer.setFilter(_mlyViewerFilter);
51833             } // Register viewer resize handler
51834
51835
51836             context.ui().photoviewer.on('resize.mapillary', function () {
51837               if (_mlyViewer) _mlyViewer.resize();
51838             }); // imageChanged: called after the viewer has changed images and is ready.
51839
51840             function imageChanged(node) {
51841               that.resetTags();
51842               var image = node.image;
51843               that.setActiveImage(image);
51844               that.setStyles(context, null);
51845               var loc = [image.originalLngLat.lng, image.originalLngLat.lat];
51846               context.map().centerEase(loc);
51847               that.updateUrlImage(image.id);
51848
51849               if (_mlyShowFeatureDetections || _mlyShowSignDetections) {
51850                 that.updateDetections(image.id, "".concat(apiUrl, "/").concat(image.id, "/detections?access_token=").concat(accessToken, "&fields=id,image,geometry,value"));
51851               }
51852
51853               dispatch$4.call('imageChanged');
51854             } // bearingChanged: called when the bearing changes in the image viewer.
51855
51856
51857             function bearingChanged(e) {
51858               dispatch$4.call('bearingChanged', undefined, e);
51859             }
51860           },
51861           // Move to an image
51862           selectImage: function selectImage(context, imageId) {
51863             if (_mlyViewer && imageId) {
51864               _mlyViewer.moveTo(imageId)["catch"](function (e) {
51865                 console.error('mly3', e); // eslint-disable-line no-console
51866               });
51867             }
51868
51869             return this;
51870           },
51871           // Return the currently displayed image
51872           getActiveImage: function getActiveImage() {
51873             return _mlyActiveImage;
51874           },
51875           // Return a list of detection objects for the given id
51876           getDetections: function getDetections(id) {
51877             return loadData("".concat(apiUrl, "/").concat(id, "/detections?access_token=").concat(accessToken, "&fields=id,value,image"));
51878           },
51879           // Set the currently visible image
51880           setActiveImage: function setActiveImage(image) {
51881             if (image) {
51882               _mlyActiveImage = {
51883                 ca: image.originalCompassAngle,
51884                 id: image.id,
51885                 loc: [image.originalLngLat.lng, image.originalLngLat.lat],
51886                 is_pano: image.cameraType === 'spherical',
51887                 sequence_id: image.sequenceId
51888               };
51889             } else {
51890               _mlyActiveImage = null;
51891             }
51892           },
51893           // Update the currently highlighted sequence and selected bubble.
51894           setStyles: function setStyles(context, hovered) {
51895             var hoveredImageId = hovered && hovered.id;
51896             var hoveredSequenceId = hovered && hovered.sequence_id;
51897             var selectedSequenceId = _mlyActiveImage && _mlyActiveImage.sequence_id;
51898             context.container().selectAll('.layer-mapillary .viewfield-group').classed('highlighted', function (d) {
51899               return d.sequence_id === selectedSequenceId || d.id === hoveredImageId;
51900             }).classed('hovered', function (d) {
51901               return d.id === hoveredImageId;
51902             });
51903             context.container().selectAll('.layer-mapillary .sequence').classed('highlighted', function (d) {
51904               return d.properties.id === hoveredSequenceId;
51905             }).classed('currentView', function (d) {
51906               return d.properties.id === selectedSequenceId;
51907             });
51908             return this;
51909           },
51910           // Get detections for the current image and shows them in the image viewer
51911           updateDetections: function updateDetections(imageId, url) {
51912             if (!_mlyViewer || _mlyFallback) return;
51913             if (!imageId) return;
51914             var cache = _mlyCache.image_detections;
51915
51916             if (cache.forImageId[imageId]) {
51917               showDetections(_mlyCache.image_detections.forImageId[imageId]);
51918             } else {
51919               loadData(url).then(function (detections) {
51920                 detections.forEach(function (detection) {
51921                   if (!cache.forImageId[imageId]) {
51922                     cache.forImageId[imageId] = [];
51923                   }
51924
51925                   cache.forImageId[imageId].push({
51926                     geometry: detection.geometry,
51927                     id: detection.id,
51928                     image_id: imageId,
51929                     value: detection.value
51930                   });
51931                 });
51932                 showDetections(_mlyCache.image_detections.forImageId[imageId] || []);
51933               });
51934             } // Create a tag for each detection and shows it in the image viewer
51935
51936
51937             function showDetections(detections) {
51938               var tagComponent = _mlyViewer.getComponent('tag');
51939
51940               detections.forEach(function (data) {
51941                 var tag = makeTag(data);
51942
51943                 if (tag) {
51944                   tagComponent.add([tag]);
51945                 }
51946               });
51947             } // Create a Mapillary JS tag object
51948
51949
51950             function makeTag(data) {
51951               var valueParts = data.value.split('--');
51952               if (!valueParts.length) return;
51953               var tag;
51954               var text;
51955               var color = 0xffffff;
51956
51957               if (_mlyHighlightedDetection === data.id) {
51958                 color = 0xffff00;
51959                 text = valueParts[1];
51960
51961                 if (text === 'flat' || text === 'discrete' || text === 'sign') {
51962                   text = valueParts[2];
51963                 }
51964
51965                 text = text.replace(/-/g, ' ');
51966                 text = text.charAt(0).toUpperCase() + text.slice(1);
51967                 _mlyHighlightedDetection = null;
51968               }
51969
51970               var decodedGeometry = window.atob(data.geometry);
51971               var uintArray = new Uint8Array(decodedGeometry.length);
51972
51973               for (var i = 0; i < decodedGeometry.length; i++) {
51974                 uintArray[i] = decodedGeometry.charCodeAt(i);
51975               }
51976
51977               var tile = new VectorTile(new pbf(uintArray.buffer));
51978               var layer = tile.layers['mpy-or'];
51979               var geometries = layer.feature(0).loadGeometry();
51980               var polygon = geometries.map(function (ring) {
51981                 return ring.map(function (point) {
51982                   return [point.x / layer.extent, point.y / layer.extent];
51983                 });
51984               });
51985               tag = new mapillary.OutlineTag(data.id, new mapillary.PolygonGeometry(polygon[0]), {
51986                 text: text,
51987                 textColor: color,
51988                 lineColor: color,
51989                 lineWidth: 2,
51990                 fillColor: color,
51991                 fillOpacity: 0.3
51992               });
51993               return tag;
51994             }
51995           },
51996           // Return the current cache
51997           cache: function cache() {
51998             return _mlyCache;
51999           }
52000         };
52001
52002         function validationIssue(attrs) {
52003           this.type = attrs.type; // required - name of rule that created the issue (e.g. 'missing_tag')
52004
52005           this.subtype = attrs.subtype; // optional - category of the issue within the type (e.g. 'relation_type' under 'missing_tag')
52006
52007           this.severity = attrs.severity; // required - 'warning' or 'error'
52008
52009           this.message = attrs.message; // required - function returning localized string
52010
52011           this.reference = attrs.reference; // optional - function(selection) to render reference information
52012
52013           this.entityIds = attrs.entityIds; // optional - array of IDs of entities involved in the issue
52014
52015           this.loc = attrs.loc; // optional - [lon, lat] to zoom in on to see the issue
52016
52017           this.data = attrs.data; // optional - object containing extra data for the fixes
52018
52019           this.dynamicFixes = attrs.dynamicFixes; // optional - function(context) returning fixes
52020
52021           this.hash = attrs.hash; // optional - string to further differentiate the issue
52022
52023           this.id = generateID.apply(this); // generated - see below
52024
52025           this.autoFix = null; // generated - if autofix exists, will be set below
52026           // A unique, deterministic string hash.
52027           // Issues with identical id values are considered identical.
52028
52029           function generateID() {
52030             var parts = [this.type];
52031
52032             if (this.hash) {
52033               // subclasses can pass in their own differentiator
52034               parts.push(this.hash);
52035             }
52036
52037             if (this.subtype) {
52038               parts.push(this.subtype);
52039             } // include the entities this issue is for
52040             // (sort them so the id is deterministic)
52041
52042
52043             if (this.entityIds) {
52044               var entityKeys = this.entityIds.slice().sort();
52045               parts.push.apply(parts, entityKeys);
52046             }
52047
52048             return parts.join(':');
52049           }
52050
52051           this.extent = function (resolver) {
52052             if (this.loc) {
52053               return geoExtent(this.loc);
52054             }
52055
52056             if (this.entityIds && this.entityIds.length) {
52057               return this.entityIds.reduce(function (extent, entityId) {
52058                 return extent.extend(resolver.entity(entityId).extent(resolver));
52059               }, geoExtent());
52060             }
52061
52062             return null;
52063           };
52064
52065           this.fixes = function (context) {
52066             var fixes = this.dynamicFixes ? this.dynamicFixes(context) : [];
52067             var issue = this;
52068
52069             if (issue.severity === 'warning') {
52070               // allow ignoring any issue that's not an error
52071               fixes.push(new validationIssueFix({
52072                 title: _t.html('issues.fix.ignore_issue.title'),
52073                 icon: 'iD-icon-close',
52074                 onClick: function onClick() {
52075                   context.validator().ignoreIssue(this.issue.id);
52076                 }
52077               }));
52078             }
52079
52080             fixes.forEach(function (fix) {
52081               // the id doesn't matter as long as it's unique to this issue/fix
52082               fix.id = fix.title; // add a reference to the issue for use in actions
52083
52084               fix.issue = issue;
52085
52086               if (fix.autoArgs) {
52087                 issue.autoFix = fix;
52088               }
52089             });
52090             return fixes;
52091           };
52092         }
52093         function validationIssueFix(attrs) {
52094           this.title = attrs.title; // Required
52095
52096           this.onClick = attrs.onClick; // Optional - the function to run to apply the fix
52097
52098           this.disabledReason = attrs.disabledReason; // Optional - a string explaining why the fix is unavailable, if any
52099
52100           this.icon = attrs.icon; // Optional - shows 'iD-icon-wrench' if not set
52101
52102           this.entityIds = attrs.entityIds || []; // Optional - used for hover-higlighting.
52103
52104           this.autoArgs = attrs.autoArgs; // Optional - pass [actions, annotation] arglist if this fix can automatically run
52105
52106           this.issue = null; // Generated link - added by validationIssue
52107         }
52108
52109         var buildRuleChecks = function buildRuleChecks() {
52110           return {
52111             equals: function equals(_equals) {
52112               return function (tags) {
52113                 return Object.keys(_equals).every(function (k) {
52114                   return _equals[k] === tags[k];
52115                 });
52116               };
52117             },
52118             notEquals: function notEquals(_notEquals) {
52119               return function (tags) {
52120                 return Object.keys(_notEquals).some(function (k) {
52121                   return _notEquals[k] !== tags[k];
52122                 });
52123               };
52124             },
52125             absence: function absence(_absence) {
52126               return function (tags) {
52127                 return Object.keys(tags).indexOf(_absence) === -1;
52128               };
52129             },
52130             presence: function presence(_presence) {
52131               return function (tags) {
52132                 return Object.keys(tags).indexOf(_presence) > -1;
52133               };
52134             },
52135             greaterThan: function greaterThan(_greaterThan) {
52136               var key = Object.keys(_greaterThan)[0];
52137               var value = _greaterThan[key];
52138               return function (tags) {
52139                 return tags[key] > value;
52140               };
52141             },
52142             greaterThanEqual: function greaterThanEqual(_greaterThanEqual) {
52143               var key = Object.keys(_greaterThanEqual)[0];
52144               var value = _greaterThanEqual[key];
52145               return function (tags) {
52146                 return tags[key] >= value;
52147               };
52148             },
52149             lessThan: function lessThan(_lessThan) {
52150               var key = Object.keys(_lessThan)[0];
52151               var value = _lessThan[key];
52152               return function (tags) {
52153                 return tags[key] < value;
52154               };
52155             },
52156             lessThanEqual: function lessThanEqual(_lessThanEqual) {
52157               var key = Object.keys(_lessThanEqual)[0];
52158               var value = _lessThanEqual[key];
52159               return function (tags) {
52160                 return tags[key] <= value;
52161               };
52162             },
52163             positiveRegex: function positiveRegex(_positiveRegex) {
52164               var tagKey = Object.keys(_positiveRegex)[0];
52165
52166               var expression = _positiveRegex[tagKey].join('|');
52167
52168               var regex = new RegExp(expression);
52169               return function (tags) {
52170                 return regex.test(tags[tagKey]);
52171               };
52172             },
52173             negativeRegex: function negativeRegex(_negativeRegex) {
52174               var tagKey = Object.keys(_negativeRegex)[0];
52175
52176               var expression = _negativeRegex[tagKey].join('|');
52177
52178               var regex = new RegExp(expression);
52179               return function (tags) {
52180                 return !regex.test(tags[tagKey]);
52181               };
52182             }
52183           };
52184         };
52185
52186         var buildLineKeys = function buildLineKeys() {
52187           return {
52188             highway: {
52189               rest_area: true,
52190               services: true
52191             },
52192             railway: {
52193               roundhouse: true,
52194               station: true,
52195               traverser: true,
52196               turntable: true,
52197               wash: true
52198             }
52199           };
52200         };
52201
52202         var serviceMapRules = {
52203           init: function init() {
52204             this._ruleChecks = buildRuleChecks();
52205             this._validationRules = [];
52206             this._areaKeys = osmAreaKeys;
52207             this._lineKeys = buildLineKeys();
52208           },
52209           // list of rules only relevant to tag checks...
52210           filterRuleChecks: function filterRuleChecks(selector) {
52211             var _ruleChecks = this._ruleChecks;
52212             return Object.keys(selector).reduce(function (rules, key) {
52213               if (['geometry', 'error', 'warning'].indexOf(key) === -1) {
52214                 rules.push(_ruleChecks[key](selector[key]));
52215               }
52216
52217               return rules;
52218             }, []);
52219           },
52220           // builds tagMap from mapcss-parse selector object...
52221           buildTagMap: function buildTagMap(selector) {
52222             var getRegexValues = function getRegexValues(regexes) {
52223               return regexes.map(function (regex) {
52224                 return regex.replace(/\$|\^/g, '');
52225               });
52226             };
52227
52228             var tagMap = Object.keys(selector).reduce(function (expectedTags, key) {
52229               var values;
52230               var isRegex = /regex/gi.test(key);
52231               var isEqual = /equals/gi.test(key);
52232
52233               if (isRegex || isEqual) {
52234                 Object.keys(selector[key]).forEach(function (selectorKey) {
52235                   values = isEqual ? [selector[key][selectorKey]] : getRegexValues(selector[key][selectorKey]);
52236
52237                   if (expectedTags.hasOwnProperty(selectorKey)) {
52238                     values = values.concat(expectedTags[selectorKey]);
52239                   }
52240
52241                   expectedTags[selectorKey] = values;
52242                 });
52243               } else if (/(greater|less)Than(Equal)?|presence/g.test(key)) {
52244                 var tagKey = /presence/.test(key) ? selector[key] : Object.keys(selector[key])[0];
52245                 values = [selector[key][tagKey]];
52246
52247                 if (expectedTags.hasOwnProperty(tagKey)) {
52248                   values = values.concat(expectedTags[tagKey]);
52249                 }
52250
52251                 expectedTags[tagKey] = values;
52252               }
52253
52254               return expectedTags;
52255             }, {});
52256             return tagMap;
52257           },
52258           // inspired by osmWay#isArea()
52259           inferGeometry: function inferGeometry(tagMap) {
52260             var _lineKeys = this._lineKeys;
52261             var _areaKeys = this._areaKeys;
52262
52263             var keyValueDoesNotImplyArea = function keyValueDoesNotImplyArea(key) {
52264               return utilArrayIntersection(tagMap[key], Object.keys(_areaKeys[key])).length > 0;
52265             };
52266
52267             var keyValueImpliesLine = function keyValueImpliesLine(key) {
52268               return utilArrayIntersection(tagMap[key], Object.keys(_lineKeys[key])).length > 0;
52269             };
52270
52271             if (tagMap.hasOwnProperty('area')) {
52272               if (tagMap.area.indexOf('yes') > -1) {
52273                 return 'area';
52274               }
52275
52276               if (tagMap.area.indexOf('no') > -1) {
52277                 return 'line';
52278               }
52279             }
52280
52281             for (var key in tagMap) {
52282               if (key in _areaKeys && !keyValueDoesNotImplyArea(key)) {
52283                 return 'area';
52284               }
52285
52286               if (key in _lineKeys && keyValueImpliesLine(key)) {
52287                 return 'area';
52288               }
52289             }
52290
52291             return 'line';
52292           },
52293           // adds from mapcss-parse selector check...
52294           addRule: function addRule(selector) {
52295             var rule = {
52296               // checks relevant to mapcss-selector
52297               checks: this.filterRuleChecks(selector),
52298               // true if all conditions for a tag error are true..
52299               matches: function matches(entity) {
52300                 return this.checks.every(function (check) {
52301                   return check(entity.tags);
52302                 });
52303               },
52304               // borrowed from Way#isArea()
52305               inferredGeometry: this.inferGeometry(this.buildTagMap(selector), this._areaKeys),
52306               geometryMatches: function geometryMatches(entity, graph) {
52307                 if (entity.type === 'node' || entity.type === 'relation') {
52308                   return selector.geometry === entity.type;
52309                 } else if (entity.type === 'way') {
52310                   return this.inferredGeometry === entity.geometry(graph);
52311                 }
52312               },
52313               // when geometries match and tag matches are present, return a warning...
52314               findIssues: function findIssues(entity, graph, issues) {
52315                 if (this.geometryMatches(entity, graph) && this.matches(entity)) {
52316                   var severity = Object.keys(selector).indexOf('error') > -1 ? 'error' : 'warning';
52317                   var _message = selector[severity];
52318                   issues.push(new validationIssue({
52319                     type: 'maprules',
52320                     severity: severity,
52321                     message: function message() {
52322                       return _message;
52323                     },
52324                     entityIds: [entity.id]
52325                   }));
52326                 }
52327               }
52328             };
52329
52330             this._validationRules.push(rule);
52331           },
52332           clearRules: function clearRules() {
52333             this._validationRules = [];
52334           },
52335           // returns validationRules...
52336           validationRules: function validationRules() {
52337             return this._validationRules;
52338           },
52339           // returns ruleChecks
52340           ruleChecks: function ruleChecks() {
52341             return this._ruleChecks;
52342           }
52343         };
52344
52345         var apibase$2 = 'https://nominatim.openstreetmap.org/';
52346         var _inflight$2 = {};
52347
52348         var _nominatimCache;
52349
52350         var serviceNominatim = {
52351           init: function init() {
52352             _inflight$2 = {};
52353             _nominatimCache = new RBush();
52354           },
52355           reset: function reset() {
52356             Object.values(_inflight$2).forEach(function (controller) {
52357               controller.abort();
52358             });
52359             _inflight$2 = {};
52360             _nominatimCache = new RBush();
52361           },
52362           countryCode: function countryCode(location, callback) {
52363             this.reverse(location, function (err, result) {
52364               if (err) {
52365                 return callback(err);
52366               } else if (result.address) {
52367                 return callback(null, result.address.country_code);
52368               } else {
52369                 return callback('Unable to geocode', null);
52370               }
52371             });
52372           },
52373           reverse: function reverse(loc, callback) {
52374             var cached = _nominatimCache.search({
52375               minX: loc[0],
52376               minY: loc[1],
52377               maxX: loc[0],
52378               maxY: loc[1]
52379             });
52380
52381             if (cached.length > 0) {
52382               if (callback) callback(null, cached[0].data);
52383               return;
52384             }
52385
52386             var params = {
52387               zoom: 13,
52388               format: 'json',
52389               addressdetails: 1,
52390               lat: loc[1],
52391               lon: loc[0]
52392             };
52393             var url = apibase$2 + 'reverse?' + utilQsString(params);
52394             if (_inflight$2[url]) return;
52395             var controller = new AbortController();
52396             _inflight$2[url] = controller;
52397             d3_json(url, {
52398               signal: controller.signal
52399             }).then(function (result) {
52400               delete _inflight$2[url];
52401
52402               if (result && result.error) {
52403                 throw new Error(result.error);
52404               }
52405
52406               var extent = geoExtent(loc).padByMeters(200);
52407
52408               _nominatimCache.insert(Object.assign(extent.bbox(), {
52409                 data: result
52410               }));
52411
52412               if (callback) callback(null, result);
52413             })["catch"](function (err) {
52414               delete _inflight$2[url];
52415               if (err.name === 'AbortError') return;
52416               if (callback) callback(err.message);
52417             });
52418           },
52419           search: function search(val, callback) {
52420             var searchVal = encodeURIComponent(val);
52421             var url = apibase$2 + 'search/' + searchVal + '?limit=10&format=json';
52422             if (_inflight$2[url]) return;
52423             var controller = new AbortController();
52424             _inflight$2[url] = controller;
52425             d3_json(url, {
52426               signal: controller.signal
52427             }).then(function (result) {
52428               delete _inflight$2[url];
52429
52430               if (result && result.error) {
52431                 throw new Error(result.error);
52432               }
52433
52434               if (callback) callback(null, result);
52435             })["catch"](function (err) {
52436               delete _inflight$2[url];
52437               if (err.name === 'AbortError') return;
52438               if (callback) callback(err.message);
52439             });
52440           }
52441         };
52442
52443         // for punction see https://stackoverflow.com/a/21224179
52444
52445         function simplify$1(str) {
52446           if (typeof str !== 'string') return '';
52447           return diacritics.remove(str.replace(/&/g, 'and').replace(/İ/ig, 'i') // for BİM, İşbank - #5017
52448           .replace(/[\s\-=_!"#%'*{},.\/:;?\(\)\[\]@\\$\^*+<>«»~`’\u00a1\u00a7\u00b6\u00b7\u00bf\u037e\u0387\u055a-\u055f\u0589\u05c0\u05c3\u05c6\u05f3\u05f4\u0609\u060a\u060c\u060d\u061b\u061e\u061f\u066a-\u066d\u06d4\u0700-\u070d\u07f7-\u07f9\u0830-\u083e\u085e\u0964\u0965\u0970\u0af0\u0df4\u0e4f\u0e5a\u0e5b\u0f04-\u0f12\u0f14\u0f85\u0fd0-\u0fd4\u0fd9\u0fda\u104a-\u104f\u10fb\u1360-\u1368\u166d\u166e\u16eb-\u16ed\u1735\u1736\u17d4-\u17d6\u17d8-\u17da\u1800-\u1805\u1807-\u180a\u1944\u1945\u1a1e\u1a1f\u1aa0-\u1aa6\u1aa8-\u1aad\u1b5a-\u1b60\u1bfc-\u1bff\u1c3b-\u1c3f\u1c7e\u1c7f\u1cc0-\u1cc7\u1cd3\u2000-\u206f\u2cf9-\u2cfc\u2cfe\u2cff\u2d70\u2e00-\u2e7f\u3001-\u3003\u303d\u30fb\ua4fe\ua4ff\ua60d-\ua60f\ua673\ua67e\ua6f2-\ua6f7\ua874-\ua877\ua8ce\ua8cf\ua8f8-\ua8fa\ua92e\ua92f\ua95f\ua9c1-\ua9cd\ua9de\ua9df\uaa5c-\uaa5f\uaade\uaadf\uaaf0\uaaf1\uabeb\ufe10-\ufe16\ufe19\ufe30\ufe45\ufe46\ufe49-\ufe4c\ufe50-\ufe52\ufe54-\ufe57\ufe5f-\ufe61\ufe68\ufe6a\ufe6b\ufeff\uff01-\uff03\uff05-\uff07\uff0a\uff0c\uff0e\uff0f\uff1a\uff1b\uff1f\uff20\uff3c\uff61\uff64\uff65]+/g, '').toLowerCase());
52449         }
52450
52451         var matchGroups$1 = {adult_gaming_centre:["amenity/casino","amenity/gambling","leisure/adult_gaming_centre"],beauty:["shop/beauty","shop/hairdresser_supply"],bed:["shop/bed","shop/furniture"],beverages:["shop/alcohol","shop/beer","shop/beverages","shop/wine"],camping:["leisure/park","tourism/camp_site","tourism/caravan_site"],car_parts:["shop/car_parts","shop/car_repair","shop/tires","shop/tyres"],clinic:["amenity/clinic","amenity/doctors","healthcare/clinic","healthcare/dialysis"],confectionery:["shop/candy","shop/chocolate","shop/confectionery"],convenience:["shop/beauty","shop/chemist","shop/convenience","shop/cosmetics","shop/grocery","shop/newsagent"],coworking:["amenity/coworking_space","office/coworking","office/coworking_space"],dentist:["amenity/dentist","amenity/doctors","healthcare/dentist"],electronics:["office/telecommunication","shop/computer","shop/electronics","shop/hifi","shop/mobile","shop/mobile_phone","shop/telecommunication"],fabric:["shop/fabric","shop/haberdashery","shop/sewing"],fashion:["shop/accessories","shop/bag","shop/botique","shop/clothes","shop/department_store","shop/fashion","shop/fashion_accessories","shop/sports","shop/shoes"],financial:["amenity/bank","office/accountant","office/financial","office/financial_advisor","office/tax_advisor","shop/tax"],fitness:["leisure/fitness_centre","leisure/fitness_center","leisure/sports_centre","leisure/sports_center"],food:["amenity/pub","amenity/bar","amenity/cafe","amenity/fast_food","amenity/ice_cream","amenity/restaurant","shop/bakery","shop/ice_cream","shop/pastry","shop/tea","shop/coffee"],fuel:["amenity/fuel","shop/gas","shop/convenience;gas","shop/gas;convenience"],gift:["shop/gift","shop/card","shop/cards","shop/stationery"],hardware:["shop/bathroom_furnishing","shop/carpet","shop/diy","shop/doityourself","shop/doors","shop/electrical","shop/flooring","shop/hardware","shop/hardware_store","shop/power_tools","shop/tool_hire","shop/tools","shop/trade"],health_food:["shop/health","shop/health_food","shop/herbalist","shop/nutrition_supplements"],hobby:["shop/electronics","shop/hobby","shop/books","shop/games","shop/collector","shop/toys","shop/model","shop/video_games","shop/anime"],hospital:["amenity/doctors","amenity/hospital","healthcare/hospital"],houseware:["shop/houseware","shop/interior_decoration"],lifeboat_station:["amenity/lifeboat_station","emergency/lifeboat_station","emergency/marine_rescue"],lodging:["tourism/hotel","tourism/motel"],money_transfer:["amenity/money_transfer","shop/money_transfer"],office_supplies:["shop/office_supplies","shop/stationary","shop/stationery"],outdoor:["shop/outdoor","shop/sports"],pharmacy:["amenity/doctors","amenity/pharmacy","healthcare/pharmacy"],playground:["amenity/theme_park","leisure/amusement_arcade","leisure/playground"],rental:["amenity/bicycle_rental","amenity/boat_rental","amenity/car_rental","amenity/truck_rental","amenity/vehicle_rental","shop/rental"],school:["amenity/childcare","amenity/college","amenity/kindergarten","amenity/language_school","amenity/prep_school","amenity/school","amenity/university"],supermarket:["shop/food","shop/frozen_food","shop/greengrocer","shop/grocery","shop/supermarket","shop/wholesale"],variety_store:["shop/variety_store","shop/discount","shop/convenience"],vending:["amenity/vending_machine","shop/vending_machine"],storage:["shop/storage_units","shop/storage_rental"],weight_loss:["amenity/doctors","amenity/weight_clinic","healthcare/counselling","leisure/fitness_centre","office/therapist","shop/beauty","shop/diet","shop/food","shop/health_food","shop/herbalist","shop/nutrition","shop/nutrition_supplements","shop/weight_loss"],wholesale:["shop/wholesale","shop/supermarket","shop/department_store"]};
52452         var matchGroupsJSON = {
52453         matchGroups: matchGroups$1
52454         };
52455
52456         var genericWords = ["^(barn|bazaa?r|bench|bou?tique|building|casa|church)$","^(baseball|basketball|football|soccer|softball|tennis(halle)?)\\s?(field|court)?$","^(club|green|out|ware)\\s?house$","^(driveway|el árbol|fountain|golf|government|graveyard)$","^(hofladen|librairie|magazine?|maison)$","^(mobile home|skate)?\\s?park$","^(n\\s?\\/?\\s?a|name|no\\s?name|none|null|temporary|test|unknown)$","^(obuwie|pond|pool|sale|shops?|sklep|stores?)$","^\\?+$","^tattoo( studio)?$","^windmill$","^церковная( лавка)?$"];
52457         var genericWordsJSON = {
52458         genericWords: genericWords
52459         };
52460
52461         var trees$1 = {brands:{emoji:"🍔",mainTag:"brand:wikidata",sourceTags:["brand","name"],nameTags:{primary:"^(name|name:\\w+)$",alternate:"^(brand|brand:\\w+|operator|operator:\\w+|\\w+_name|\\w+_name:\\w+)$"}},flags:{emoji:"🚩",mainTag:"flag:wikidata",nameTags:{primary:"^(flag:name|flag:name:\\w+)$",alternate:"^(country|country:\\w+|flag|flag:\\w+|subject|subject:\\w+)$"}},operators:{emoji:"💼",mainTag:"operator:wikidata",sourceTags:["operator"],nameTags:{primary:"^(name|name:\\w+|operator|operator:\\w+)$",alternate:"^(brand|brand:\\w+|\\w+_name|\\w+_name:\\w+)$"}},transit:{emoji:"🚇",mainTag:"network:wikidata",sourceTags:["network"],nameTags:{primary:"^network$",alternate:"^(operator|operator:\\w+|network:\\w+|\\w+_name|\\w+_name:\\w+)$"}}};
52462         var treesJSON = {
52463         trees: trees$1
52464         };
52465
52466         var matchGroups = matchGroupsJSON.matchGroups;
52467         var trees = treesJSON.trees;
52468         var Matcher = /*#__PURE__*/function () {
52469           //
52470           // `constructor`
52471           // initialize the genericWords regexes
52472           function Matcher() {
52473             var _this = this;
52474
52475             _classCallCheck$1(this, Matcher);
52476
52477             // The `matchIndex` is a specialized structure that allows us to quickly answer
52478             //   _"Given a [key/value tagpair, name, location], what canonical items (brands etc) can match it?"_
52479             //
52480             // The index contains all valid combinations of k/v tagpairs and names
52481             // matchIndex:
52482             // {
52483             //   'k/v': {
52484             //     'primary':         Map (String 'nsimple' -> Set (itemIDs…),   // matches for tags like `name`, `name:xx`, etc.
52485             //     'alternate':       Map (String 'nsimple' -> Set (itemIDs…),   // matches for tags like `alt_name`, `brand`, etc.
52486             //     'excludeNamed':    Map (String 'pattern' -> RegExp),
52487             //     'excludeGeneric':  Map (String 'pattern' -> RegExp)
52488             //   },
52489             // }
52490             //
52491             // {
52492             //   'amenity/bank': {
52493             //     'primary': {
52494             //       'firstbank':              Set ("firstbank-978cca", "firstbank-9794e6", "firstbank-f17495", …),
52495             //       …
52496             //     },
52497             //     'alternate': {
52498             //       '1stbank':                Set ("firstbank-f17495"),
52499             //       …
52500             //     }
52501             //   },
52502             //   'shop/supermarket': {
52503             //     'primary': {
52504             //       'coop':                   Set ("coop-76454b", "coop-ebf2d9", "coop-36e991", …),
52505             //       'coopfood':               Set ("coopfood-a8278b", …),
52506             //       …
52507             //     },
52508             //     'alternate': {
52509             //       'coop':                   Set ("coopfood-a8278b", …),
52510             //       'federatedcooperatives':  Set ("coop-76454b", …),
52511             //       'thecooperative':         Set ("coopfood-a8278b", …),
52512             //       …
52513             //     }
52514             //   }
52515             // }
52516             //
52517             this.matchIndex = undefined; // The `genericWords` structure matches the contents of genericWords.json to instantiated RegExp objects
52518             // Map (String 'pattern' -> RegExp),
52519
52520             this.genericWords = new Map();
52521             (genericWordsJSON.genericWords || []).forEach(function (s) {
52522               return _this.genericWords.set(s, new RegExp(s, 'i'));
52523             }); // The `itemLocation` structure maps itemIDs to locationSetIDs:
52524             // {
52525             //   'firstbank-f17495':  '+[first_bank_western_us.geojson]',
52526             //   'firstbank-978cca':  '+[first_bank_carolinas.geojson]',
52527             //   'coop-76454b':       '+[Q16]',
52528             //   'coopfood-a8278b':   '+[Q23666]',
52529             //   …
52530             // }
52531
52532             this.itemLocation = undefined; // The `locationSets` structure maps locationSetIDs to *resolved* locationSets:
52533             // {
52534             //   '+[first_bank_western_us.geojson]':  GeoJSON {…},
52535             //   '+[first_bank_carolinas.geojson]':   GeoJSON {…},
52536             //   '+[Q16]':                            GeoJSON {…},
52537             //   '+[Q23666]':                         GeoJSON {…},
52538             //   …
52539             // }
52540
52541             this.locationSets = undefined; // The `locationIndex` is an instance of which-polygon spatial index for the locationSets.
52542
52543             this.locationIndex = undefined; // Array of match conflict pairs (currently unused)
52544
52545             this.warnings = [];
52546           } //
52547           // `buildMatchIndex()`
52548           // Call this to prepare the matcher for use
52549           //
52550           // `data` needs to be an Object indexed on a 'tree/key/value' path.
52551           // (e.g. cache filled by `fileTree.read` or data found in `dist/nsi.json`)
52552           // {
52553           //    'brands/amenity/bank': { properties: {}, items: [ {}, {}, … ] },
52554           //    'brands/amenity/bar':  { properties: {}, items: [ {}, {}, … ] },
52555           //    …
52556           // }
52557           //
52558
52559
52560           _createClass$1(Matcher, [{
52561             key: "buildMatchIndex",
52562             value: function buildMatchIndex(data) {
52563               var that = this;
52564               if (that.matchIndex) return; // it was built already
52565
52566               that.matchIndex = new Map();
52567               Object.keys(data).forEach(function (tkv) {
52568                 var category = data[tkv];
52569                 var parts = tkv.split('/', 3); // tkv = "tree/key/value"
52570
52571                 var t = parts[0];
52572                 var k = parts[1];
52573                 var v = parts[2];
52574                 var thiskv = "".concat(k, "/").concat(v);
52575                 var tree = trees[t];
52576                 var branch = that.matchIndex.get(thiskv);
52577
52578                 if (!branch) {
52579                   branch = {
52580                     primary: new Map(),
52581                     alternate: new Map(),
52582                     excludeGeneric: new Map(),
52583                     excludeNamed: new Map()
52584                   };
52585                   that.matchIndex.set(thiskv, branch);
52586                 } // ADD EXCLUSIONS
52587
52588
52589                 var properties = category.properties || {};
52590                 var exclude = properties.exclude || {};
52591                 (exclude.generic || []).forEach(function (s) {
52592                   return branch.excludeGeneric.set(s, new RegExp(s, 'i'));
52593                 });
52594                 (exclude.named || []).forEach(function (s) {
52595                   return branch.excludeNamed.set(s, new RegExp(s, 'i'));
52596                 });
52597                 var excludeRegexes = [].concat(_toConsumableArray(branch.excludeGeneric.values()), _toConsumableArray(branch.excludeNamed.values())); // ADD ITEMS
52598
52599                 var items = category.items;
52600                 if (!Array.isArray(items) || !items.length) return; // Primary name patterns, match tags to take first
52601                 //  e.g. `name`, `name:ru`
52602
52603                 var primaryName = new RegExp(tree.nameTags.primary, 'i'); // Alternate name patterns, match tags to consider after primary
52604                 //  e.g. `alt_name`, `short_name`, `brand`, `brand:ru`, etc..
52605
52606                 var alternateName = new RegExp(tree.nameTags.alternate, 'i'); // There are a few exceptions to the name matching regexes.
52607                 // Usually a tag suffix contains a language code like `name:en`, `name:ru`
52608                 // but we want to exclude things like `operator:type`, `name:etymology`, etc..
52609
52610                 var notName = /:(colou?r|type|forward|backward|left|right|etymology|pronunciation|wikipedia)$/i; // For certain categories we do not want to match generic KV pairs like `building/yes` or `amenity/yes`
52611
52612                 var skipGenericKV = skipGenericKVMatches(t, k, v); // We will collect the generic KV pairs anyway (for the purpose of filtering them out of matchTags)
52613
52614                 var genericKV = new Set(["".concat(k, "/yes"), "building/yes"]); // Collect alternate tagpairs for this kv category from matchGroups.
52615                 // We might also pick up a few more generic KVs (like `shop/yes`)
52616
52617                 var matchGroupKV = new Set();
52618                 Object.values(matchGroups).forEach(function (matchGroup) {
52619                   var inGroup = matchGroup.some(function (otherkv) {
52620                     return otherkv === thiskv;
52621                   });
52622                   if (!inGroup) return;
52623                   matchGroup.forEach(function (otherkv) {
52624                     if (otherkv === thiskv) return; // skip self
52625
52626                     matchGroupKV.add(otherkv);
52627                     var otherk = otherkv.split('/', 2)[0]; // we might pick up a `shop/yes`
52628
52629                     genericKV.add("".concat(otherk, "/yes"));
52630                   });
52631                 }); // For each item, insert all [key, value, name] combinations into the match index
52632
52633                 items.forEach(function (item) {
52634                   if (!item.id) return; // Automatically remove redundant `matchTags` - #3417
52635                   // (i.e. This kv is already covered by matchGroups, so it doesn't need to be in `item.matchTags`)
52636
52637                   if (Array.isArray(item.matchTags) && item.matchTags.length) {
52638                     item.matchTags = item.matchTags.filter(function (matchTag) {
52639                       return !matchGroupKV.has(matchTag) && !genericKV.has(matchTag);
52640                     });
52641                     if (!item.matchTags.length) delete item.matchTags;
52642                   } // key/value tagpairs to insert into the match index..
52643
52644
52645                   var kvTags = ["".concat(thiskv)].concat(item.matchTags || []);
52646
52647                   if (!skipGenericKV) {
52648                     kvTags = kvTags.concat(Array.from(genericKV)); // #3454 - match some generic tags
52649                   } // Index all the namelike tag values
52650
52651
52652                   Object.keys(item.tags).forEach(function (osmkey) {
52653                     if (notName.test(osmkey)) return; // osmkey is not a namelike tag, skip
52654
52655                     var osmvalue = item.tags[osmkey];
52656                     if (!osmvalue || excludeRegexes.some(function (regex) {
52657                       return regex.test(osmvalue);
52658                     })) return; // osmvalue missing or excluded
52659
52660                     if (primaryName.test(osmkey)) {
52661                       kvTags.forEach(function (kv) {
52662                         return insertName('primary', kv, simplify$1(osmvalue), item.id);
52663                       });
52664                     } else if (alternateName.test(osmkey)) {
52665                       kvTags.forEach(function (kv) {
52666                         return insertName('alternate', kv, simplify$1(osmvalue), item.id);
52667                       });
52668                     }
52669                   }); // Index `matchNames` after indexing all other names..
52670
52671                   var keepMatchNames = new Set();
52672                   (item.matchNames || []).forEach(function (matchName) {
52673                     // If this matchname isn't already indexed, add it to the alternate index
52674                     var nsimple = simplify$1(matchName);
52675                     kvTags.forEach(function (kv) {
52676                       var branch = that.matchIndex.get(kv);
52677                       var primaryLeaf = branch && branch.primary.get(nsimple);
52678                       var alternateLeaf = branch && branch.alternate.get(nsimple);
52679                       var inPrimary = primaryLeaf && primaryLeaf.has(item.id);
52680                       var inAlternate = alternateLeaf && alternateLeaf.has(item.id);
52681
52682                       if (!inPrimary && !inAlternate) {
52683                         insertName('alternate', kv, nsimple, item.id);
52684                         keepMatchNames.add(matchName);
52685                       }
52686                     });
52687                   }); // Automatically remove redundant `matchNames` - #3417
52688                   // (i.e. This name got indexed some other way, so it doesn't need to be in `item.matchNames`)
52689
52690                   if (keepMatchNames.size) {
52691                     item.matchNames = Array.from(keepMatchNames);
52692                   } else {
52693                     delete item.matchNames;
52694                   }
52695                 }); // each item
52696               }); // each tkv
52697               // Insert this item into the matchIndex
52698
52699               function insertName(which, kv, nsimple, itemID) {
52700                 if (!nsimple) return;
52701                 var branch = that.matchIndex.get(kv);
52702
52703                 if (!branch) {
52704                   branch = {
52705                     primary: new Map(),
52706                     alternate: new Map(),
52707                     excludeGeneric: new Map(),
52708                     excludeNamed: new Map()
52709                   };
52710                   that.matchIndex.set(kv, branch);
52711                 }
52712
52713                 var leaf = branch[which].get(nsimple);
52714
52715                 if (!leaf) {
52716                   leaf = new Set();
52717                   branch[which].set(nsimple, leaf);
52718                 }
52719
52720                 leaf.add(itemID); // insert
52721               } // For certain categories we do not want to match generic KV pairs like `building/yes` or `amenity/yes`
52722
52723
52724               function skipGenericKVMatches(t, k, v) {
52725                 return t === 'flags' || t === 'transit' || k === 'landuse' || v === 'atm' || v === 'bicycle_parking' || v === 'car_sharing' || v === 'caravan_site' || v === 'charging_station' || v === 'dog_park' || v === 'parking' || v === 'phone' || v === 'playground' || v === 'post_box' || v === 'public_bookcase' || v === 'recycling' || v === 'vending_machine';
52726               }
52727             } //
52728             // `buildLocationIndex()`
52729             // Call this to prepare a which-polygon location index.
52730             // This *resolves* all the locationSets into GeoJSON, which takes some time.
52731             // You can skip this step if you don't care about matching within a location.
52732             //
52733             // `data` needs to be an Object indexed on a 'tree/key/value' path.
52734             // (e.g. cache filled by `fileTree.read` or data found in `dist/nsi.json`)
52735             // {
52736             //    'brands/amenity/bank': { properties: {}, items: [ {}, {}, … ] },
52737             //    'brands/amenity/bar':  { properties: {}, items: [ {}, {}, … ] },
52738             //    …
52739             // }
52740             //
52741
52742           }, {
52743             key: "buildLocationIndex",
52744             value: function buildLocationIndex(data, loco) {
52745               var that = this;
52746               if (that.locationIndex) return; // it was built already
52747
52748               that.itemLocation = new Map();
52749               that.locationSets = new Map();
52750               Object.keys(data).forEach(function (tkv) {
52751                 var items = data[tkv].items;
52752                 if (!Array.isArray(items) || !items.length) return;
52753                 items.forEach(function (item) {
52754                   if (that.itemLocation.has(item.id)) return; // we've seen item id already - shouldn't be possible?
52755
52756                   var resolved;
52757
52758                   try {
52759                     resolved = loco.resolveLocationSet(item.locationSet); // resolve a feature for this locationSet
52760                   } catch (err) {
52761                     console.warn("buildLocationIndex: ".concat(err.message)); // couldn't resolve
52762                   }
52763
52764                   if (!resolved || !resolved.id) return;
52765                   that.itemLocation.set(item.id, resolved.id); // link it to the item
52766
52767                   if (that.locationSets.has(resolved.id)) return; // we've seen this locationSet feature before..
52768                   // First time seeing this locationSet feature, make a copy and add to locationSet cache..
52769
52770                   var feature = _cloneDeep(resolved.feature);
52771
52772                   feature.id = resolved.id; // Important: always use the locationSet `id` (`+[Q30]`), not the feature `id` (`Q30`)
52773
52774                   feature.properties.id = resolved.id;
52775
52776                   if (!feature.geometry.coordinates.length || !feature.properties.area) {
52777                     console.warn("buildLocationIndex: locationSet ".concat(resolved.id, " for ").concat(item.id, " resolves to an empty feature:"));
52778                     console.warn(JSON.stringify(feature));
52779                     return;
52780                   }
52781
52782                   that.locationSets.set(resolved.id, feature);
52783                 });
52784               });
52785               that.locationIndex = whichPolygon_1({
52786                 type: 'FeatureCollection',
52787                 features: _toConsumableArray(that.locationSets.values())
52788               });
52789
52790               function _cloneDeep(obj) {
52791                 return JSON.parse(JSON.stringify(obj));
52792               }
52793             } //
52794             // `match()`
52795             // Pass parts and return an Array of matches.
52796             // `k` - key
52797             // `v` - value
52798             // `n` - namelike
52799             // `loc` - optional - [lon,lat] location to search
52800             //
52801             // 1. If the [k,v,n] tuple matches a canonical item…
52802             // Return an Array of match results.
52803             // Each result will include the area in km² that the item is valid.
52804             //
52805             // Order of results:
52806             // Primary ordering will be on the "match" column:
52807             //   "primary" - where the query matches the `name` tag, followed by
52808             //   "alternate" - where the query matches an alternate name tag (e.g. short_name, brand, operator, etc)
52809             // Secondary ordering will be on the "area" column:
52810             //   "area descending" if no location was provided, (worldwide before local)
52811             //   "area ascending" if location was provided (local before worldwide)
52812             //
52813             // [
52814             //   { match: 'primary',   itemID: String,  area: Number,  kv: String,  nsimple: String },
52815             //   { match: 'primary',   itemID: String,  area: Number,  kv: String,  nsimple: String },
52816             //   { match: 'alternate', itemID: String,  area: Number,  kv: String,  nsimple: String },
52817             //   { match: 'alternate', itemID: String,  area: Number,  kv: String,  nsimple: String },
52818             //   …
52819             // ]
52820             //
52821             // -or-
52822             //
52823             // 2. If the [k,v,n] tuple matches an exclude pattern…
52824             // Return an Array with a single exclude result, either
52825             //
52826             // [ { match: 'excludeGeneric', pattern: String,  kv: String } ]  // "generic" e.g. "Food Court"
52827             //   or
52828             // [ { match: 'excludeNamed', pattern: String,  kv: String } ]    // "named", e.g. "Kebabai"
52829             //
52830             // About results
52831             //   "generic" - a generic word that is probably not really a name.
52832             //     For these, iD should warn the user "Hey don't put 'food court' in the name tag".
52833             //   "named" - a real name like "Kebabai" that is just common, but not a brand.
52834             //     For these, iD should just let it be. We don't include these in NSI, but we don't want to nag users about it either.
52835             //
52836             // -or-
52837             //
52838             // 3. If the [k,v,n] tuple matches nothing of any kind, return `null`
52839             //
52840             //
52841
52842           }, {
52843             key: "match",
52844             value: function match(k, v, n, loc) {
52845               var that = this;
52846
52847               if (!that.matchIndex) {
52848                 throw new Error('match:  matchIndex not built.');
52849               } // If we were supplied a location, and a that.locationIndex has been set up,
52850               // get the locationSets that are valid there so we can filter results.
52851
52852
52853               var matchLocations;
52854
52855               if (Array.isArray(loc) && that.locationIndex) {
52856                 // which-polygon query returns an array of GeoJSON properties, pass true to return all results
52857                 matchLocations = that.locationIndex([loc[0], loc[1], loc[0], loc[1]], true);
52858               }
52859
52860               var nsimple = simplify$1(n);
52861               var seen = new Set();
52862               var results = [];
52863               gatherResults('primary');
52864               gatherResults('alternate');
52865               if (results.length) return results;
52866               gatherResults('exclude');
52867               return results.length ? results : null;
52868
52869               function gatherResults(which) {
52870                 // First try an exact match on k/v
52871                 var kv = "".concat(k, "/").concat(v);
52872                 var didMatch = tryMatch(which, kv);
52873                 if (didMatch) return; // If that didn't work, look in match groups for other pairs considered equivalent to k/v..
52874
52875                 for (var mg in matchGroups) {
52876                   var matchGroup = matchGroups[mg];
52877                   var inGroup = matchGroup.some(function (otherkv) {
52878                     return otherkv === kv;
52879                   });
52880                   if (!inGroup) continue;
52881
52882                   for (var i = 0; i < matchGroup.length; i++) {
52883                     var otherkv = matchGroup[i];
52884                     if (otherkv === kv) continue; // skip self
52885
52886                     didMatch = tryMatch(which, otherkv);
52887                     if (didMatch) return;
52888                   }
52889                 } // If finished 'exclude' pass and still haven't matched anything, try the global `genericWords.json` patterns
52890
52891
52892                 if (which === 'exclude') {
52893                   var regex = _toConsumableArray(that.genericWords.values()).find(function (regex) {
52894                     return regex.test(n);
52895                   });
52896
52897                   if (regex) {
52898                     results.push({
52899                       match: 'excludeGeneric',
52900                       pattern: String(regex)
52901                     }); // note no `branch`, no `kv`
52902
52903                     return;
52904                   }
52905                 }
52906               }
52907
52908               function tryMatch(which, kv) {
52909                 var branch = that.matchIndex.get(kv);
52910                 if (!branch) return;
52911
52912                 if (which === 'exclude') {
52913                   // Test name `n` against named and generic exclude patterns
52914                   var regex = _toConsumableArray(branch.excludeNamed.values()).find(function (regex) {
52915                     return regex.test(n);
52916                   });
52917
52918                   if (regex) {
52919                     results.push({
52920                       match: 'excludeNamed',
52921                       pattern: String(regex),
52922                       kv: kv
52923                     });
52924                     return;
52925                   }
52926
52927                   regex = _toConsumableArray(branch.excludeGeneric.values()).find(function (regex) {
52928                     return regex.test(n);
52929                   });
52930
52931                   if (regex) {
52932                     results.push({
52933                       match: 'excludeGeneric',
52934                       pattern: String(regex),
52935                       kv: kv
52936                     });
52937                     return;
52938                   }
52939
52940                   return;
52941                 }
52942
52943                 var leaf = branch[which].get(nsimple);
52944                 if (!leaf || !leaf.size) return; // If we get here, we matched something..
52945                 // Prepare the results, calculate areas (if location index was set up)
52946
52947                 var hits = Array.from(leaf).map(function (itemID) {
52948                   var area = Infinity;
52949
52950                   if (that.itemLocation && that.locationSets) {
52951                     var location = that.locationSets.get(that.itemLocation.get(itemID));
52952                     area = location && location.properties.area || Infinity;
52953                   }
52954
52955                   return {
52956                     match: which,
52957                     itemID: itemID,
52958                     area: area,
52959                     kv: kv,
52960                     nsimple: nsimple
52961                   };
52962                 });
52963                 var sortFn = byAreaDescending; // Filter the match to include only results valid in the requested `loc`..
52964
52965                 if (matchLocations) {
52966                   hits = hits.filter(isValidLocation);
52967                   sortFn = byAreaAscending;
52968                 }
52969
52970                 if (!hits.length) return; // push results
52971
52972                 hits.sort(sortFn).forEach(function (hit) {
52973                   if (seen.has(hit.itemID)) return;
52974                   seen.add(hit.itemID);
52975                   results.push(hit);
52976                 });
52977                 return true;
52978
52979                 function isValidLocation(hit) {
52980                   if (!that.itemLocation) return true;
52981                   return matchLocations.find(function (props) {
52982                     return props.id === that.itemLocation.get(hit.itemID);
52983                   });
52984                 } // Sort smaller (more local) locations first.
52985
52986
52987                 function byAreaAscending(hitA, hitB) {
52988                   return hitA.area - hitB.area;
52989                 } // Sort larger (more worldwide) locations first.
52990
52991
52992                 function byAreaDescending(hitA, hitB) {
52993                   return hitB.area - hitA.area;
52994                 }
52995               }
52996             } //
52997             // `getWarnings()`
52998             // Return any warnings discovered when buiding the index.
52999             // (currently this does nothing)
53000             //
53001
53002           }, {
53003             key: "getWarnings",
53004             value: function getWarnings() {
53005               return this.warnings;
53006             }
53007           }]);
53008
53009           return Matcher;
53010         }();
53011
53012         /**
53013          * Checks if `value` is the
53014          * [language type](http://www.ecma-international.org/ecma-262/7.0/#sec-ecmascript-language-types)
53015          * of `Object`. (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`)
53016          *
53017          * @static
53018          * @memberOf _
53019          * @since 0.1.0
53020          * @category Lang
53021          * @param {*} value The value to check.
53022          * @returns {boolean} Returns `true` if `value` is an object, else `false`.
53023          * @example
53024          *
53025          * _.isObject({});
53026          * // => true
53027          *
53028          * _.isObject([1, 2, 3]);
53029          * // => true
53030          *
53031          * _.isObject(_.noop);
53032          * // => true
53033          *
53034          * _.isObject(null);
53035          * // => false
53036          */
53037         function isObject$2(value) {
53038           var type = _typeof(value);
53039
53040           return value != null && (type == 'object' || type == 'function');
53041         }
53042
53043         /** Detect free variable `global` from Node.js. */
53044         var freeGlobal = (typeof global === "undefined" ? "undefined" : _typeof(global)) == 'object' && global && global.Object === Object && global;
53045
53046         /** Detect free variable `self`. */
53047
53048         var freeSelf = (typeof self === "undefined" ? "undefined" : _typeof(self)) == 'object' && self && self.Object === Object && self;
53049         /** Used as a reference to the global object. */
53050
53051         var root = freeGlobal || freeSelf || Function('return this')();
53052
53053         /**
53054          * Gets the timestamp of the number of milliseconds that have elapsed since
53055          * the Unix epoch (1 January 1970 00:00:00 UTC).
53056          *
53057          * @static
53058          * @memberOf _
53059          * @since 2.4.0
53060          * @category Date
53061          * @returns {number} Returns the timestamp.
53062          * @example
53063          *
53064          * _.defer(function(stamp) {
53065          *   console.log(_.now() - stamp);
53066          * }, _.now());
53067          * // => Logs the number of milliseconds it took for the deferred invocation.
53068          */
53069
53070         var now = function now() {
53071           return root.Date.now();
53072         };
53073
53074         /** Used to match a single whitespace character. */
53075         var reWhitespace = /\s/;
53076         /**
53077          * Used by `_.trim` and `_.trimEnd` to get the index of the last non-whitespace
53078          * character of `string`.
53079          *
53080          * @private
53081          * @param {string} string The string to inspect.
53082          * @returns {number} Returns the index of the last non-whitespace character.
53083          */
53084
53085         function trimmedEndIndex(string) {
53086           var index = string.length;
53087
53088           while (index-- && reWhitespace.test(string.charAt(index))) {}
53089
53090           return index;
53091         }
53092
53093         /** Used to match leading whitespace. */
53094
53095         var reTrimStart = /^\s+/;
53096         /**
53097          * The base implementation of `_.trim`.
53098          *
53099          * @private
53100          * @param {string} string The string to trim.
53101          * @returns {string} Returns the trimmed string.
53102          */
53103
53104         function baseTrim(string) {
53105           return string ? string.slice(0, trimmedEndIndex(string) + 1).replace(reTrimStart, '') : string;
53106         }
53107
53108         /** Built-in value references. */
53109
53110         var _Symbol = root.Symbol;
53111
53112         /** Used for built-in method references. */
53113
53114         var objectProto$1 = Object.prototype;
53115         /** Used to check objects for own properties. */
53116
53117         var hasOwnProperty$2 = objectProto$1.hasOwnProperty;
53118         /**
53119          * Used to resolve the
53120          * [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring)
53121          * of values.
53122          */
53123
53124         var nativeObjectToString$1 = objectProto$1.toString;
53125         /** Built-in value references. */
53126
53127         var symToStringTag$1 = _Symbol ? _Symbol.toStringTag : undefined;
53128         /**
53129          * A specialized version of `baseGetTag` which ignores `Symbol.toStringTag` values.
53130          *
53131          * @private
53132          * @param {*} value The value to query.
53133          * @returns {string} Returns the raw `toStringTag`.
53134          */
53135
53136         function getRawTag(value) {
53137           var isOwn = hasOwnProperty$2.call(value, symToStringTag$1),
53138               tag = value[symToStringTag$1];
53139
53140           try {
53141             value[symToStringTag$1] = undefined;
53142             var unmasked = true;
53143           } catch (e) {}
53144
53145           var result = nativeObjectToString$1.call(value);
53146
53147           if (unmasked) {
53148             if (isOwn) {
53149               value[symToStringTag$1] = tag;
53150             } else {
53151               delete value[symToStringTag$1];
53152             }
53153           }
53154
53155           return result;
53156         }
53157
53158         /** Used for built-in method references. */
53159         var objectProto = Object.prototype;
53160         /**
53161          * Used to resolve the
53162          * [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring)
53163          * of values.
53164          */
53165
53166         var nativeObjectToString = objectProto.toString;
53167         /**
53168          * Converts `value` to a string using `Object.prototype.toString`.
53169          *
53170          * @private
53171          * @param {*} value The value to convert.
53172          * @returns {string} Returns the converted string.
53173          */
53174
53175         function objectToString(value) {
53176           return nativeObjectToString.call(value);
53177         }
53178
53179         /** `Object#toString` result references. */
53180
53181         var nullTag = '[object Null]',
53182             undefinedTag = '[object Undefined]';
53183         /** Built-in value references. */
53184
53185         var symToStringTag = _Symbol ? _Symbol.toStringTag : undefined;
53186         /**
53187          * The base implementation of `getTag` without fallbacks for buggy environments.
53188          *
53189          * @private
53190          * @param {*} value The value to query.
53191          * @returns {string} Returns the `toStringTag`.
53192          */
53193
53194         function baseGetTag(value) {
53195           if (value == null) {
53196             return value === undefined ? undefinedTag : nullTag;
53197           }
53198
53199           return symToStringTag && symToStringTag in Object(value) ? getRawTag(value) : objectToString(value);
53200         }
53201
53202         /**
53203          * Checks if `value` is object-like. A value is object-like if it's not `null`
53204          * and has a `typeof` result of "object".
53205          *
53206          * @static
53207          * @memberOf _
53208          * @since 4.0.0
53209          * @category Lang
53210          * @param {*} value The value to check.
53211          * @returns {boolean} Returns `true` if `value` is object-like, else `false`.
53212          * @example
53213          *
53214          * _.isObjectLike({});
53215          * // => true
53216          *
53217          * _.isObjectLike([1, 2, 3]);
53218          * // => true
53219          *
53220          * _.isObjectLike(_.noop);
53221          * // => false
53222          *
53223          * _.isObjectLike(null);
53224          * // => false
53225          */
53226         function isObjectLike(value) {
53227           return value != null && _typeof(value) == 'object';
53228         }
53229
53230         /** `Object#toString` result references. */
53231
53232         var symbolTag = '[object Symbol]';
53233         /**
53234          * Checks if `value` is classified as a `Symbol` primitive or object.
53235          *
53236          * @static
53237          * @memberOf _
53238          * @since 4.0.0
53239          * @category Lang
53240          * @param {*} value The value to check.
53241          * @returns {boolean} Returns `true` if `value` is a symbol, else `false`.
53242          * @example
53243          *
53244          * _.isSymbol(Symbol.iterator);
53245          * // => true
53246          *
53247          * _.isSymbol('abc');
53248          * // => false
53249          */
53250
53251         function isSymbol(value) {
53252           return _typeof(value) == 'symbol' || isObjectLike(value) && baseGetTag(value) == symbolTag;
53253         }
53254
53255         /** Used as references for various `Number` constants. */
53256
53257         var NAN = 0 / 0;
53258         /** Used to detect bad signed hexadecimal string values. */
53259
53260         var reIsBadHex = /^[-+]0x[0-9a-f]+$/i;
53261         /** Used to detect binary string values. */
53262
53263         var reIsBinary = /^0b[01]+$/i;
53264         /** Used to detect octal string values. */
53265
53266         var reIsOctal = /^0o[0-7]+$/i;
53267         /** Built-in method references without a dependency on `root`. */
53268
53269         var freeParseInt = parseInt;
53270         /**
53271          * Converts `value` to a number.
53272          *
53273          * @static
53274          * @memberOf _
53275          * @since 4.0.0
53276          * @category Lang
53277          * @param {*} value The value to process.
53278          * @returns {number} Returns the number.
53279          * @example
53280          *
53281          * _.toNumber(3.2);
53282          * // => 3.2
53283          *
53284          * _.toNumber(Number.MIN_VALUE);
53285          * // => 5e-324
53286          *
53287          * _.toNumber(Infinity);
53288          * // => Infinity
53289          *
53290          * _.toNumber('3.2');
53291          * // => 3.2
53292          */
53293
53294         function toNumber(value) {
53295           if (typeof value == 'number') {
53296             return value;
53297           }
53298
53299           if (isSymbol(value)) {
53300             return NAN;
53301           }
53302
53303           if (isObject$2(value)) {
53304             var other = typeof value.valueOf == 'function' ? value.valueOf() : value;
53305             value = isObject$2(other) ? other + '' : other;
53306           }
53307
53308           if (typeof value != 'string') {
53309             return value === 0 ? value : +value;
53310           }
53311
53312           value = baseTrim(value);
53313           var isBinary = reIsBinary.test(value);
53314           return isBinary || reIsOctal.test(value) ? freeParseInt(value.slice(2), isBinary ? 2 : 8) : reIsBadHex.test(value) ? NAN : +value;
53315         }
53316
53317         /** Error message constants. */
53318
53319         var FUNC_ERROR_TEXT$1 = 'Expected a function';
53320         /* Built-in method references for those with the same name as other `lodash` methods. */
53321
53322         var nativeMax = Math.max,
53323             nativeMin = Math.min;
53324         /**
53325          * Creates a debounced function that delays invoking `func` until after `wait`
53326          * milliseconds have elapsed since the last time the debounced function was
53327          * invoked. The debounced function comes with a `cancel` method to cancel
53328          * delayed `func` invocations and a `flush` method to immediately invoke them.
53329          * Provide `options` to indicate whether `func` should be invoked on the
53330          * leading and/or trailing edge of the `wait` timeout. The `func` is invoked
53331          * with the last arguments provided to the debounced function. Subsequent
53332          * calls to the debounced function return the result of the last `func`
53333          * invocation.
53334          *
53335          * **Note:** If `leading` and `trailing` options are `true`, `func` is
53336          * invoked on the trailing edge of the timeout only if the debounced function
53337          * is invoked more than once during the `wait` timeout.
53338          *
53339          * If `wait` is `0` and `leading` is `false`, `func` invocation is deferred
53340          * until to the next tick, similar to `setTimeout` with a timeout of `0`.
53341          *
53342          * See [David Corbacho's article](https://css-tricks.com/debouncing-throttling-explained-examples/)
53343          * for details over the differences between `_.debounce` and `_.throttle`.
53344          *
53345          * @static
53346          * @memberOf _
53347          * @since 0.1.0
53348          * @category Function
53349          * @param {Function} func The function to debounce.
53350          * @param {number} [wait=0] The number of milliseconds to delay.
53351          * @param {Object} [options={}] The options object.
53352          * @param {boolean} [options.leading=false]
53353          *  Specify invoking on the leading edge of the timeout.
53354          * @param {number} [options.maxWait]
53355          *  The maximum time `func` is allowed to be delayed before it's invoked.
53356          * @param {boolean} [options.trailing=true]
53357          *  Specify invoking on the trailing edge of the timeout.
53358          * @returns {Function} Returns the new debounced function.
53359          * @example
53360          *
53361          * // Avoid costly calculations while the window size is in flux.
53362          * jQuery(window).on('resize', _.debounce(calculateLayout, 150));
53363          *
53364          * // Invoke `sendMail` when clicked, debouncing subsequent calls.
53365          * jQuery(element).on('click', _.debounce(sendMail, 300, {
53366          *   'leading': true,
53367          *   'trailing': false
53368          * }));
53369          *
53370          * // Ensure `batchLog` is invoked once after 1 second of debounced calls.
53371          * var debounced = _.debounce(batchLog, 250, { 'maxWait': 1000 });
53372          * var source = new EventSource('/stream');
53373          * jQuery(source).on('message', debounced);
53374          *
53375          * // Cancel the trailing debounced invocation.
53376          * jQuery(window).on('popstate', debounced.cancel);
53377          */
53378
53379         function debounce(func, wait, options) {
53380           var lastArgs,
53381               lastThis,
53382               maxWait,
53383               result,
53384               timerId,
53385               lastCallTime,
53386               lastInvokeTime = 0,
53387               leading = false,
53388               maxing = false,
53389               trailing = true;
53390
53391           if (typeof func != 'function') {
53392             throw new TypeError(FUNC_ERROR_TEXT$1);
53393           }
53394
53395           wait = toNumber(wait) || 0;
53396
53397           if (isObject$2(options)) {
53398             leading = !!options.leading;
53399             maxing = 'maxWait' in options;
53400             maxWait = maxing ? nativeMax(toNumber(options.maxWait) || 0, wait) : maxWait;
53401             trailing = 'trailing' in options ? !!options.trailing : trailing;
53402           }
53403
53404           function invokeFunc(time) {
53405             var args = lastArgs,
53406                 thisArg = lastThis;
53407             lastArgs = lastThis = undefined;
53408             lastInvokeTime = time;
53409             result = func.apply(thisArg, args);
53410             return result;
53411           }
53412
53413           function leadingEdge(time) {
53414             // Reset any `maxWait` timer.
53415             lastInvokeTime = time; // Start the timer for the trailing edge.
53416
53417             timerId = setTimeout(timerExpired, wait); // Invoke the leading edge.
53418
53419             return leading ? invokeFunc(time) : result;
53420           }
53421
53422           function remainingWait(time) {
53423             var timeSinceLastCall = time - lastCallTime,
53424                 timeSinceLastInvoke = time - lastInvokeTime,
53425                 timeWaiting = wait - timeSinceLastCall;
53426             return maxing ? nativeMin(timeWaiting, maxWait - timeSinceLastInvoke) : timeWaiting;
53427           }
53428
53429           function shouldInvoke(time) {
53430             var timeSinceLastCall = time - lastCallTime,
53431                 timeSinceLastInvoke = time - lastInvokeTime; // Either this is the first call, activity has stopped and we're at the
53432             // trailing edge, the system time has gone backwards and we're treating
53433             // it as the trailing edge, or we've hit the `maxWait` limit.
53434
53435             return lastCallTime === undefined || timeSinceLastCall >= wait || timeSinceLastCall < 0 || maxing && timeSinceLastInvoke >= maxWait;
53436           }
53437
53438           function timerExpired() {
53439             var time = now();
53440
53441             if (shouldInvoke(time)) {
53442               return trailingEdge(time);
53443             } // Restart the timer.
53444
53445
53446             timerId = setTimeout(timerExpired, remainingWait(time));
53447           }
53448
53449           function trailingEdge(time) {
53450             timerId = undefined; // Only invoke if we have `lastArgs` which means `func` has been
53451             // debounced at least once.
53452
53453             if (trailing && lastArgs) {
53454               return invokeFunc(time);
53455             }
53456
53457             lastArgs = lastThis = undefined;
53458             return result;
53459           }
53460
53461           function cancel() {
53462             if (timerId !== undefined) {
53463               clearTimeout(timerId);
53464             }
53465
53466             lastInvokeTime = 0;
53467             lastArgs = lastCallTime = lastThis = timerId = undefined;
53468           }
53469
53470           function flush() {
53471             return timerId === undefined ? result : trailingEdge(now());
53472           }
53473
53474           function debounced() {
53475             var time = now(),
53476                 isInvoking = shouldInvoke(time);
53477             lastArgs = arguments;
53478             lastThis = this;
53479             lastCallTime = time;
53480
53481             if (isInvoking) {
53482               if (timerId === undefined) {
53483                 return leadingEdge(lastCallTime);
53484               }
53485
53486               if (maxing) {
53487                 // Handle invocations in a tight loop.
53488                 clearTimeout(timerId);
53489                 timerId = setTimeout(timerExpired, wait);
53490                 return invokeFunc(lastCallTime);
53491               }
53492             }
53493
53494             if (timerId === undefined) {
53495               timerId = setTimeout(timerExpired, wait);
53496             }
53497
53498             return result;
53499           }
53500
53501           debounced.cancel = cancel;
53502           debounced.flush = flush;
53503           return debounced;
53504         }
53505
53506         /*
53507             iD.coreDifference represents the difference between two graphs.
53508             It knows how to calculate the set of entities that were
53509             created, modified, or deleted, and also contains the logic
53510             for recursively extending a difference to the complete set
53511             of entities that will require a redraw, taking into account
53512             child and parent relationships.
53513          */
53514
53515         function coreDifference(base, head) {
53516           var _changes = {};
53517           var _didChange = {}; // 'addition', 'deletion', 'geometry', 'properties'
53518
53519           var _diff = {};
53520
53521           function checkEntityID(id) {
53522             var h = head.entities[id];
53523             var b = base.entities[id];
53524             if (h === b) return;
53525             if (_changes[id]) return;
53526
53527             if (!h && b) {
53528               _changes[id] = {
53529                 base: b,
53530                 head: h
53531               };
53532               _didChange.deletion = true;
53533               return;
53534             }
53535
53536             if (h && !b) {
53537               _changes[id] = {
53538                 base: b,
53539                 head: h
53540               };
53541               _didChange.addition = true;
53542               return;
53543             }
53544
53545             if (h && b) {
53546               if (h.members && b.members && !fastDeepEqual(h.members, b.members)) {
53547                 _changes[id] = {
53548                   base: b,
53549                   head: h
53550                 };
53551                 _didChange.geometry = true;
53552                 _didChange.properties = true;
53553                 return;
53554               }
53555
53556               if (h.loc && b.loc && !geoVecEqual(h.loc, b.loc)) {
53557                 _changes[id] = {
53558                   base: b,
53559                   head: h
53560                 };
53561                 _didChange.geometry = true;
53562               }
53563
53564               if (h.nodes && b.nodes && !fastDeepEqual(h.nodes, b.nodes)) {
53565                 _changes[id] = {
53566                   base: b,
53567                   head: h
53568                 };
53569                 _didChange.geometry = true;
53570               }
53571
53572               if (h.tags && b.tags && !fastDeepEqual(h.tags, b.tags)) {
53573                 _changes[id] = {
53574                   base: b,
53575                   head: h
53576                 };
53577                 _didChange.properties = true;
53578               }
53579             }
53580           }
53581
53582           function load() {
53583             // HOT CODE: there can be many thousands of downloaded entities, so looping
53584             // through them all can become a performance bottleneck. Optimize by
53585             // resolving duplicates and using a basic `for` loop
53586             var ids = utilArrayUniq(Object.keys(head.entities).concat(Object.keys(base.entities)));
53587
53588             for (var i = 0; i < ids.length; i++) {
53589               checkEntityID(ids[i]);
53590             }
53591           }
53592
53593           load();
53594
53595           _diff.length = function length() {
53596             return Object.keys(_changes).length;
53597           };
53598
53599           _diff.changes = function changes() {
53600             return _changes;
53601           };
53602
53603           _diff.didChange = _didChange; // pass true to include affected relation members
53604
53605           _diff.extantIDs = function extantIDs(includeRelMembers) {
53606             var result = new Set();
53607             Object.keys(_changes).forEach(function (id) {
53608               if (_changes[id].head) {
53609                 result.add(id);
53610               }
53611
53612               var h = _changes[id].head;
53613               var b = _changes[id].base;
53614               var entity = h || b;
53615
53616               if (includeRelMembers && entity.type === 'relation') {
53617                 var mh = h ? h.members.map(function (m) {
53618                   return m.id;
53619                 }) : [];
53620                 var mb = b ? b.members.map(function (m) {
53621                   return m.id;
53622                 }) : [];
53623                 utilArrayUnion(mh, mb).forEach(function (memberID) {
53624                   if (head.hasEntity(memberID)) {
53625                     result.add(memberID);
53626                   }
53627                 });
53628               }
53629             });
53630             return Array.from(result);
53631           };
53632
53633           _diff.modified = function modified() {
53634             var result = [];
53635             Object.values(_changes).forEach(function (change) {
53636               if (change.base && change.head) {
53637                 result.push(change.head);
53638               }
53639             });
53640             return result;
53641           };
53642
53643           _diff.created = function created() {
53644             var result = [];
53645             Object.values(_changes).forEach(function (change) {
53646               if (!change.base && change.head) {
53647                 result.push(change.head);
53648               }
53649             });
53650             return result;
53651           };
53652
53653           _diff.deleted = function deleted() {
53654             var result = [];
53655             Object.values(_changes).forEach(function (change) {
53656               if (change.base && !change.head) {
53657                 result.push(change.base);
53658               }
53659             });
53660             return result;
53661           };
53662
53663           _diff.summary = function summary() {
53664             var relevant = {};
53665             var keys = Object.keys(_changes);
53666
53667             for (var i = 0; i < keys.length; i++) {
53668               var change = _changes[keys[i]];
53669
53670               if (change.head && change.head.geometry(head) !== 'vertex') {
53671                 addEntity(change.head, head, change.base ? 'modified' : 'created');
53672               } else if (change.base && change.base.geometry(base) !== 'vertex') {
53673                 addEntity(change.base, base, 'deleted');
53674               } else if (change.base && change.head) {
53675                 // modified vertex
53676                 var moved = !fastDeepEqual(change.base.loc, change.head.loc);
53677                 var retagged = !fastDeepEqual(change.base.tags, change.head.tags);
53678
53679                 if (moved) {
53680                   addParents(change.head);
53681                 }
53682
53683                 if (retagged || moved && change.head.hasInterestingTags()) {
53684                   addEntity(change.head, head, 'modified');
53685                 }
53686               } else if (change.head && change.head.hasInterestingTags()) {
53687                 // created vertex
53688                 addEntity(change.head, head, 'created');
53689               } else if (change.base && change.base.hasInterestingTags()) {
53690                 // deleted vertex
53691                 addEntity(change.base, base, 'deleted');
53692               }
53693             }
53694
53695             return Object.values(relevant);
53696
53697             function addEntity(entity, graph, changeType) {
53698               relevant[entity.id] = {
53699                 entity: entity,
53700                 graph: graph,
53701                 changeType: changeType
53702               };
53703             }
53704
53705             function addParents(entity) {
53706               var parents = head.parentWays(entity);
53707
53708               for (var j = parents.length - 1; j >= 0; j--) {
53709                 var parent = parents[j];
53710
53711                 if (!(parent.id in relevant)) {
53712                   addEntity(parent, head, 'modified');
53713                 }
53714               }
53715             }
53716           }; // returns complete set of entities that require a redraw
53717           //  (optionally within given `extent`)
53718
53719
53720           _diff.complete = function complete(extent) {
53721             var result = {};
53722             var id, change;
53723
53724             for (id in _changes) {
53725               change = _changes[id];
53726               var h = change.head;
53727               var b = change.base;
53728               var entity = h || b;
53729               var i;
53730
53731               if (extent && (!h || !h.intersects(extent, head)) && (!b || !b.intersects(extent, base))) {
53732                 continue;
53733               }
53734
53735               result[id] = h;
53736
53737               if (entity.type === 'way') {
53738                 var nh = h ? h.nodes : [];
53739                 var nb = b ? b.nodes : [];
53740                 var diff;
53741                 diff = utilArrayDifference(nh, nb);
53742
53743                 for (i = 0; i < diff.length; i++) {
53744                   result[diff[i]] = head.hasEntity(diff[i]);
53745                 }
53746
53747                 diff = utilArrayDifference(nb, nh);
53748
53749                 for (i = 0; i < diff.length; i++) {
53750                   result[diff[i]] = head.hasEntity(diff[i]);
53751                 }
53752               }
53753
53754               if (entity.type === 'relation' && entity.isMultipolygon()) {
53755                 var mh = h ? h.members.map(function (m) {
53756                   return m.id;
53757                 }) : [];
53758                 var mb = b ? b.members.map(function (m) {
53759                   return m.id;
53760                 }) : [];
53761                 var ids = utilArrayUnion(mh, mb);
53762
53763                 for (i = 0; i < ids.length; i++) {
53764                   var member = head.hasEntity(ids[i]);
53765                   if (!member) continue; // not downloaded
53766
53767                   if (extent && !member.intersects(extent, head)) continue; // not visible
53768
53769                   result[ids[i]] = member;
53770                 }
53771               }
53772
53773               addParents(head.parentWays(entity), result);
53774               addParents(head.parentRelations(entity), result);
53775             }
53776
53777             return result;
53778
53779             function addParents(parents, result) {
53780               for (var i = 0; i < parents.length; i++) {
53781                 var parent = parents[i];
53782                 if (parent.id in result) continue;
53783                 result[parent.id] = parent;
53784                 addParents(head.parentRelations(parent), result);
53785               }
53786             }
53787           };
53788
53789           return _diff;
53790         }
53791
53792         function coreTree(head) {
53793           // tree for entities
53794           var _rtree = new RBush();
53795
53796           var _bboxes = {}; // maintain a separate tree for granular way segments
53797
53798           var _segmentsRTree = new RBush();
53799
53800           var _segmentsBBoxes = {};
53801           var _segmentsByWayId = {};
53802           var tree = {};
53803
53804           function entityBBox(entity) {
53805             var bbox = entity.extent(head).bbox();
53806             bbox.id = entity.id;
53807             _bboxes[entity.id] = bbox;
53808             return bbox;
53809           }
53810
53811           function segmentBBox(segment) {
53812             var extent = segment.extent(head); // extent can be null if the node entities aren't in the graph for some reason
53813
53814             if (!extent) return null;
53815             var bbox = extent.bbox();
53816             bbox.segment = segment;
53817             _segmentsBBoxes[segment.id] = bbox;
53818             return bbox;
53819           }
53820
53821           function removeEntity(entity) {
53822             _rtree.remove(_bboxes[entity.id]);
53823
53824             delete _bboxes[entity.id];
53825
53826             if (_segmentsByWayId[entity.id]) {
53827               _segmentsByWayId[entity.id].forEach(function (segment) {
53828                 _segmentsRTree.remove(_segmentsBBoxes[segment.id]);
53829
53830                 delete _segmentsBBoxes[segment.id];
53831               });
53832
53833               delete _segmentsByWayId[entity.id];
53834             }
53835           }
53836
53837           function loadEntities(entities) {
53838             _rtree.load(entities.map(entityBBox));
53839
53840             var segments = [];
53841             entities.forEach(function (entity) {
53842               if (entity.segments) {
53843                 var entitySegments = entity.segments(head); // cache these to make them easy to remove later
53844
53845                 _segmentsByWayId[entity.id] = entitySegments;
53846                 segments = segments.concat(entitySegments);
53847               }
53848             });
53849             if (segments.length) _segmentsRTree.load(segments.map(segmentBBox).filter(Boolean));
53850           }
53851
53852           function updateParents(entity, insertions, memo) {
53853             head.parentWays(entity).forEach(function (way) {
53854               if (_bboxes[way.id]) {
53855                 removeEntity(way);
53856                 insertions[way.id] = way;
53857               }
53858
53859               updateParents(way, insertions, memo);
53860             });
53861             head.parentRelations(entity).forEach(function (relation) {
53862               if (memo[entity.id]) return;
53863               memo[entity.id] = true;
53864
53865               if (_bboxes[relation.id]) {
53866                 removeEntity(relation);
53867                 insertions[relation.id] = relation;
53868               }
53869
53870               updateParents(relation, insertions, memo);
53871             });
53872           }
53873
53874           tree.rebase = function (entities, force) {
53875             var insertions = {};
53876
53877             for (var i = 0; i < entities.length; i++) {
53878               var entity = entities[i];
53879               if (!entity.visible) continue;
53880
53881               if (head.entities.hasOwnProperty(entity.id) || _bboxes[entity.id]) {
53882                 if (!force) {
53883                   continue;
53884                 } else if (_bboxes[entity.id]) {
53885                   removeEntity(entity);
53886                 }
53887               }
53888
53889               insertions[entity.id] = entity;
53890               updateParents(entity, insertions, {});
53891             }
53892
53893             loadEntities(Object.values(insertions));
53894             return tree;
53895           };
53896
53897           function updateToGraph(graph) {
53898             if (graph === head) return;
53899             var diff = coreDifference(head, graph);
53900             head = graph;
53901             var changed = diff.didChange;
53902             if (!changed.addition && !changed.deletion && !changed.geometry) return;
53903             var insertions = {};
53904
53905             if (changed.deletion) {
53906               diff.deleted().forEach(function (entity) {
53907                 removeEntity(entity);
53908               });
53909             }
53910
53911             if (changed.geometry) {
53912               diff.modified().forEach(function (entity) {
53913                 removeEntity(entity);
53914                 insertions[entity.id] = entity;
53915                 updateParents(entity, insertions, {});
53916               });
53917             }
53918
53919             if (changed.addition) {
53920               diff.created().forEach(function (entity) {
53921                 insertions[entity.id] = entity;
53922               });
53923             }
53924
53925             loadEntities(Object.values(insertions));
53926           } // returns an array of entities with bounding boxes overlapping `extent` for the given `graph`
53927
53928
53929           tree.intersects = function (extent, graph) {
53930             updateToGraph(graph);
53931             return _rtree.search(extent.bbox()).map(function (bbox) {
53932               return graph.entity(bbox.id);
53933             });
53934           }; // returns an array of segment objects with bounding boxes overlapping `extent` for the given `graph`
53935
53936
53937           tree.waySegments = function (extent, graph) {
53938             updateToGraph(graph);
53939             return _segmentsRTree.search(extent.bbox()).map(function (bbox) {
53940               return bbox.segment;
53941             });
53942           };
53943
53944           return tree;
53945         }
53946
53947         function svgIcon(name, svgklass, useklass) {
53948           return function drawIcon(selection) {
53949             selection.selectAll('svg.icon' + (svgklass ? '.' + svgklass.split(' ')[0] : '')).data([0]).enter().append('svg').attr('class', 'icon ' + (svgklass || '')).append('use').attr('xlink:href', name).attr('class', useklass);
53950           };
53951         }
53952
53953         function uiModal(selection, blocking) {
53954           var _this = this;
53955
53956           var keybinding = utilKeybinding('modal');
53957           var previous = selection.select('div.modal');
53958           var animate = previous.empty();
53959           previous.transition().duration(200).style('opacity', 0).remove();
53960           var shaded = selection.append('div').attr('class', 'shaded').style('opacity', 0);
53961
53962           shaded.close = function () {
53963             shaded.transition().duration(200).style('opacity', 0).remove();
53964             modal.transition().duration(200).style('top', '0px');
53965             select(document).call(keybinding.unbind);
53966           };
53967
53968           var modal = shaded.append('div').attr('class', 'modal fillL');
53969           modal.append('input').attr('class', 'keytrap keytrap-first').on('focus.keytrap', moveFocusToLast);
53970
53971           if (!blocking) {
53972             shaded.on('click.remove-modal', function (d3_event) {
53973               if (d3_event.target === _this) {
53974                 shaded.close();
53975               }
53976             });
53977             modal.append('button').attr('class', 'close').on('click', shaded.close).call(svgIcon('#iD-icon-close'));
53978             keybinding.on('⌫', shaded.close).on('⎋', shaded.close);
53979             select(document).call(keybinding);
53980           }
53981
53982           modal.append('div').attr('class', 'content');
53983           modal.append('input').attr('class', 'keytrap keytrap-last').on('focus.keytrap', moveFocusToFirst);
53984
53985           if (animate) {
53986             shaded.transition().style('opacity', 1);
53987           } else {
53988             shaded.style('opacity', 1);
53989           }
53990
53991           return shaded;
53992
53993           function moveFocusToFirst() {
53994             var node = modal // there are additional rules about what's focusable, but this suits our purposes
53995             .select('a, button, input:not(.keytrap), select, textarea').node();
53996
53997             if (node) {
53998               node.focus();
53999             } else {
54000               select(this).node().blur();
54001             }
54002           }
54003
54004           function moveFocusToLast() {
54005             var nodes = modal.selectAll('a, button, input:not(.keytrap), select, textarea').nodes();
54006
54007             if (nodes.length) {
54008               nodes[nodes.length - 1].focus();
54009             } else {
54010               select(this).node().blur();
54011             }
54012           }
54013         }
54014
54015         function uiLoading(context) {
54016           var _modalSelection = select(null);
54017
54018           var _message = '';
54019           var _blocking = false;
54020
54021           var loading = function loading(selection) {
54022             _modalSelection = uiModal(selection, _blocking);
54023
54024             var loadertext = _modalSelection.select('.content').classed('loading-modal', true).append('div').attr('class', 'modal-section fillL');
54025
54026             loadertext.append('img').attr('class', 'loader').attr('src', context.imagePath('loader-white.gif'));
54027             loadertext.append('h3').html(_message);
54028
54029             _modalSelection.select('button.close').attr('class', 'hide');
54030
54031             return loading;
54032           };
54033
54034           loading.message = function (val) {
54035             if (!arguments.length) return _message;
54036             _message = val;
54037             return loading;
54038           };
54039
54040           loading.blocking = function (val) {
54041             if (!arguments.length) return _blocking;
54042             _blocking = val;
54043             return loading;
54044           };
54045
54046           loading.close = function () {
54047             _modalSelection.remove();
54048           };
54049
54050           loading.isShown = function () {
54051             return _modalSelection && !_modalSelection.empty() && _modalSelection.node().parentNode;
54052           };
54053
54054           return loading;
54055         }
54056
54057         function coreHistory(context) {
54058           var dispatch = dispatch$8('reset', 'change', 'merge', 'restore', 'undone', 'redone');
54059
54060           var _lock = utilSessionMutex('lock'); // restorable if iD not open in another window/tab and a saved history exists in localStorage
54061
54062
54063           var _hasUnresolvedRestorableChanges = _lock.lock() && !!corePreferences(getKey('saved_history'));
54064
54065           var duration = 150;
54066           var _imageryUsed = [];
54067           var _photoOverlaysUsed = [];
54068           var _checkpoints = {};
54069
54070           var _pausedGraph;
54071
54072           var _stack;
54073
54074           var _index;
54075
54076           var _tree; // internal _act, accepts list of actions and eased time
54077
54078
54079           function _act(actions, t) {
54080             actions = Array.prototype.slice.call(actions);
54081             var annotation;
54082
54083             if (typeof actions[actions.length - 1] !== 'function') {
54084               annotation = actions.pop();
54085             }
54086
54087             var graph = _stack[_index].graph;
54088
54089             for (var i = 0; i < actions.length; i++) {
54090               graph = actions[i](graph, t);
54091             }
54092
54093             return {
54094               graph: graph,
54095               annotation: annotation,
54096               imageryUsed: _imageryUsed,
54097               photoOverlaysUsed: _photoOverlaysUsed,
54098               transform: context.projection.transform(),
54099               selectedIDs: context.selectedIDs()
54100             };
54101           } // internal _perform with eased time
54102
54103
54104           function _perform(args, t) {
54105             var previous = _stack[_index].graph;
54106             _stack = _stack.slice(0, _index + 1);
54107
54108             var actionResult = _act(args, t);
54109
54110             _stack.push(actionResult);
54111
54112             _index++;
54113             return change(previous);
54114           } // internal _replace with eased time
54115
54116
54117           function _replace(args, t) {
54118             var previous = _stack[_index].graph; // assert(_index == _stack.length - 1)
54119
54120             var actionResult = _act(args, t);
54121
54122             _stack[_index] = actionResult;
54123             return change(previous);
54124           } // internal _overwrite with eased time
54125
54126
54127           function _overwrite(args, t) {
54128             var previous = _stack[_index].graph;
54129
54130             if (_index > 0) {
54131               _index--;
54132
54133               _stack.pop();
54134             }
54135
54136             _stack = _stack.slice(0, _index + 1);
54137
54138             var actionResult = _act(args, t);
54139
54140             _stack.push(actionResult);
54141
54142             _index++;
54143             return change(previous);
54144           } // determine difference and dispatch a change event
54145
54146
54147           function change(previous) {
54148             var difference = coreDifference(previous, history.graph());
54149
54150             if (!_pausedGraph) {
54151               dispatch.call('change', this, difference);
54152             }
54153
54154             return difference;
54155           } // iD uses namespaced keys so multiple installations do not conflict
54156
54157
54158           function getKey(n) {
54159             return 'iD_' + window.location.origin + '_' + n;
54160           }
54161
54162           var history = {
54163             graph: function graph() {
54164               return _stack[_index].graph;
54165             },
54166             tree: function tree() {
54167               return _tree;
54168             },
54169             base: function base() {
54170               return _stack[0].graph;
54171             },
54172             merge: function merge(entities
54173             /*, extent*/
54174             ) {
54175               var stack = _stack.map(function (state) {
54176                 return state.graph;
54177               });
54178
54179               _stack[0].graph.rebase(entities, stack, false);
54180
54181               _tree.rebase(entities, false);
54182
54183               dispatch.call('merge', this, entities);
54184             },
54185             perform: function perform() {
54186               // complete any transition already in progress
54187               select(document).interrupt('history.perform');
54188               var transitionable = false;
54189               var action0 = arguments[0];
54190
54191               if (arguments.length === 1 || arguments.length === 2 && typeof arguments[1] !== 'function') {
54192                 transitionable = !!action0.transitionable;
54193               }
54194
54195               if (transitionable) {
54196                 var origArguments = arguments;
54197                 select(document).transition('history.perform').duration(duration).ease(linear$1).tween('history.tween', function () {
54198                   return function (t) {
54199                     if (t < 1) _overwrite([action0], t);
54200                   };
54201                 }).on('start', function () {
54202                   _perform([action0], 0);
54203                 }).on('end interrupt', function () {
54204                   _overwrite(origArguments, 1);
54205                 });
54206               } else {
54207                 return _perform(arguments);
54208               }
54209             },
54210             replace: function replace() {
54211               select(document).interrupt('history.perform');
54212               return _replace(arguments, 1);
54213             },
54214             // Same as calling pop and then perform
54215             overwrite: function overwrite() {
54216               select(document).interrupt('history.perform');
54217               return _overwrite(arguments, 1);
54218             },
54219             pop: function pop(n) {
54220               select(document).interrupt('history.perform');
54221               var previous = _stack[_index].graph;
54222
54223               if (isNaN(+n) || +n < 0) {
54224                 n = 1;
54225               }
54226
54227               while (n-- > 0 && _index > 0) {
54228                 _index--;
54229
54230                 _stack.pop();
54231               }
54232
54233               return change(previous);
54234             },
54235             // Back to the previous annotated state or _index = 0.
54236             undo: function undo() {
54237               select(document).interrupt('history.perform');
54238               var previousStack = _stack[_index];
54239               var previous = previousStack.graph;
54240
54241               while (_index > 0) {
54242                 _index--;
54243                 if (_stack[_index].annotation) break;
54244               }
54245
54246               dispatch.call('undone', this, _stack[_index], previousStack);
54247               return change(previous);
54248             },
54249             // Forward to the next annotated state.
54250             redo: function redo() {
54251               select(document).interrupt('history.perform');
54252               var previousStack = _stack[_index];
54253               var previous = previousStack.graph;
54254               var tryIndex = _index;
54255
54256               while (tryIndex < _stack.length - 1) {
54257                 tryIndex++;
54258
54259                 if (_stack[tryIndex].annotation) {
54260                   _index = tryIndex;
54261                   dispatch.call('redone', this, _stack[_index], previousStack);
54262                   break;
54263                 }
54264               }
54265
54266               return change(previous);
54267             },
54268             pauseChangeDispatch: function pauseChangeDispatch() {
54269               if (!_pausedGraph) {
54270                 _pausedGraph = _stack[_index].graph;
54271               }
54272             },
54273             resumeChangeDispatch: function resumeChangeDispatch() {
54274               if (_pausedGraph) {
54275                 var previous = _pausedGraph;
54276                 _pausedGraph = null;
54277                 return change(previous);
54278               }
54279             },
54280             undoAnnotation: function undoAnnotation() {
54281               var i = _index;
54282
54283               while (i >= 0) {
54284                 if (_stack[i].annotation) return _stack[i].annotation;
54285                 i--;
54286               }
54287             },
54288             redoAnnotation: function redoAnnotation() {
54289               var i = _index + 1;
54290
54291               while (i <= _stack.length - 1) {
54292                 if (_stack[i].annotation) return _stack[i].annotation;
54293                 i++;
54294               }
54295             },
54296             // Returns the entities from the active graph with bounding boxes
54297             // overlapping the given `extent`.
54298             intersects: function intersects(extent) {
54299               return _tree.intersects(extent, _stack[_index].graph);
54300             },
54301             difference: function difference() {
54302               var base = _stack[0].graph;
54303               var head = _stack[_index].graph;
54304               return coreDifference(base, head);
54305             },
54306             changes: function changes(action) {
54307               var base = _stack[0].graph;
54308               var head = _stack[_index].graph;
54309
54310               if (action) {
54311                 head = action(head);
54312               }
54313
54314               var difference = coreDifference(base, head);
54315               return {
54316                 modified: difference.modified(),
54317                 created: difference.created(),
54318                 deleted: difference.deleted()
54319               };
54320             },
54321             hasChanges: function hasChanges() {
54322               return this.difference().length() > 0;
54323             },
54324             imageryUsed: function imageryUsed(sources) {
54325               if (sources) {
54326                 _imageryUsed = sources;
54327                 return history;
54328               } else {
54329                 var s = new Set();
54330
54331                 _stack.slice(1, _index + 1).forEach(function (state) {
54332                   state.imageryUsed.forEach(function (source) {
54333                     if (source !== 'Custom') {
54334                       s.add(source);
54335                     }
54336                   });
54337                 });
54338
54339                 return Array.from(s);
54340               }
54341             },
54342             photoOverlaysUsed: function photoOverlaysUsed(sources) {
54343               if (sources) {
54344                 _photoOverlaysUsed = sources;
54345                 return history;
54346               } else {
54347                 var s = new Set();
54348
54349                 _stack.slice(1, _index + 1).forEach(function (state) {
54350                   if (state.photoOverlaysUsed && Array.isArray(state.photoOverlaysUsed)) {
54351                     state.photoOverlaysUsed.forEach(function (photoOverlay) {
54352                       s.add(photoOverlay);
54353                     });
54354                   }
54355                 });
54356
54357                 return Array.from(s);
54358               }
54359             },
54360             // save the current history state
54361             checkpoint: function checkpoint(key) {
54362               _checkpoints[key] = {
54363                 stack: _stack,
54364                 index: _index
54365               };
54366               return history;
54367             },
54368             // restore history state to a given checkpoint or reset completely
54369             reset: function reset(key) {
54370               if (key !== undefined && _checkpoints.hasOwnProperty(key)) {
54371                 _stack = _checkpoints[key].stack;
54372                 _index = _checkpoints[key].index;
54373               } else {
54374                 _stack = [{
54375                   graph: coreGraph()
54376                 }];
54377                 _index = 0;
54378                 _tree = coreTree(_stack[0].graph);
54379                 _checkpoints = {};
54380               }
54381
54382               dispatch.call('reset');
54383               dispatch.call('change');
54384               return history;
54385             },
54386             // `toIntroGraph()` is used to export the intro graph used by the walkthrough.
54387             //
54388             // To use it:
54389             //  1. Start the walkthrough.
54390             //  2. Get to a "free editing" tutorial step
54391             //  3. Make your edits to the walkthrough map
54392             //  4. In your browser dev console run:
54393             //        `id.history().toIntroGraph()`
54394             //  5. This outputs stringified JSON to the browser console
54395             //  6. Copy it to `data/intro_graph.json` and prettify it in your code editor
54396             toIntroGraph: function toIntroGraph() {
54397               var nextID = {
54398                 n: 0,
54399                 r: 0,
54400                 w: 0
54401               };
54402               var permIDs = {};
54403               var graph = this.graph();
54404               var baseEntities = {}; // clone base entities..
54405
54406               Object.values(graph.base().entities).forEach(function (entity) {
54407                 var copy = copyIntroEntity(entity);
54408                 baseEntities[copy.id] = copy;
54409               }); // replace base entities with head entities..
54410
54411               Object.keys(graph.entities).forEach(function (id) {
54412                 var entity = graph.entities[id];
54413
54414                 if (entity) {
54415                   var copy = copyIntroEntity(entity);
54416                   baseEntities[copy.id] = copy;
54417                 } else {
54418                   delete baseEntities[id];
54419                 }
54420               }); // swap temporary for permanent ids..
54421
54422               Object.values(baseEntities).forEach(function (entity) {
54423                 if (Array.isArray(entity.nodes)) {
54424                   entity.nodes = entity.nodes.map(function (node) {
54425                     return permIDs[node] || node;
54426                   });
54427                 }
54428
54429                 if (Array.isArray(entity.members)) {
54430                   entity.members = entity.members.map(function (member) {
54431                     member.id = permIDs[member.id] || member.id;
54432                     return member;
54433                   });
54434                 }
54435               });
54436               return JSON.stringify({
54437                 dataIntroGraph: baseEntities
54438               });
54439
54440               function copyIntroEntity(source) {
54441                 var copy = utilObjectOmit(source, ['type', 'user', 'v', 'version', 'visible']); // Note: the copy is no longer an osmEntity, so it might not have `tags`
54442
54443                 if (copy.tags && !Object.keys(copy.tags)) {
54444                   delete copy.tags;
54445                 }
54446
54447                 if (Array.isArray(copy.loc)) {
54448                   copy.loc[0] = +copy.loc[0].toFixed(6);
54449                   copy.loc[1] = +copy.loc[1].toFixed(6);
54450                 }
54451
54452                 var match = source.id.match(/([nrw])-\d*/); // temporary id
54453
54454                 if (match !== null) {
54455                   var nrw = match[1];
54456                   var permID;
54457
54458                   do {
54459                     permID = nrw + ++nextID[nrw];
54460                   } while (baseEntities.hasOwnProperty(permID));
54461
54462                   copy.id = permIDs[source.id] = permID;
54463                 }
54464
54465                 return copy;
54466               }
54467             },
54468             toJSON: function toJSON() {
54469               if (!this.hasChanges()) return;
54470               var allEntities = {};
54471               var baseEntities = {};
54472               var base = _stack[0];
54473
54474               var s = _stack.map(function (i) {
54475                 var modified = [];
54476                 var deleted = [];
54477                 Object.keys(i.graph.entities).forEach(function (id) {
54478                   var entity = i.graph.entities[id];
54479
54480                   if (entity) {
54481                     var key = osmEntity.key(entity);
54482                     allEntities[key] = entity;
54483                     modified.push(key);
54484                   } else {
54485                     deleted.push(id);
54486                   } // make sure that the originals of changed or deleted entities get merged
54487                   // into the base of the _stack after restoring the data from JSON.
54488
54489
54490                   if (id in base.graph.entities) {
54491                     baseEntities[id] = base.graph.entities[id];
54492                   }
54493
54494                   if (entity && entity.nodes) {
54495                     // get originals of pre-existing child nodes
54496                     entity.nodes.forEach(function (nodeID) {
54497                       if (nodeID in base.graph.entities) {
54498                         baseEntities[nodeID] = base.graph.entities[nodeID];
54499                       }
54500                     });
54501                   } // get originals of parent entities too
54502
54503
54504                   var baseParents = base.graph._parentWays[id];
54505
54506                   if (baseParents) {
54507                     baseParents.forEach(function (parentID) {
54508                       if (parentID in base.graph.entities) {
54509                         baseEntities[parentID] = base.graph.entities[parentID];
54510                       }
54511                     });
54512                   }
54513                 });
54514                 var x = {};
54515                 if (modified.length) x.modified = modified;
54516                 if (deleted.length) x.deleted = deleted;
54517                 if (i.imageryUsed) x.imageryUsed = i.imageryUsed;
54518                 if (i.photoOverlaysUsed) x.photoOverlaysUsed = i.photoOverlaysUsed;
54519                 if (i.annotation) x.annotation = i.annotation;
54520                 if (i.transform) x.transform = i.transform;
54521                 if (i.selectedIDs) x.selectedIDs = i.selectedIDs;
54522                 return x;
54523               });
54524
54525               return JSON.stringify({
54526                 version: 3,
54527                 entities: Object.values(allEntities),
54528                 baseEntities: Object.values(baseEntities),
54529                 stack: s,
54530                 nextIDs: osmEntity.id.next,
54531                 index: _index,
54532                 // note the time the changes were saved
54533                 timestamp: new Date().getTime()
54534               });
54535             },
54536             fromJSON: function fromJSON(json, loadChildNodes) {
54537               var h = JSON.parse(json);
54538               var loadComplete = true;
54539               osmEntity.id.next = h.nextIDs;
54540               _index = h.index;
54541
54542               if (h.version === 2 || h.version === 3) {
54543                 var allEntities = {};
54544                 h.entities.forEach(function (entity) {
54545                   allEntities[osmEntity.key(entity)] = osmEntity(entity);
54546                 });
54547
54548                 if (h.version === 3) {
54549                   // This merges originals for changed entities into the base of
54550                   // the _stack even if the current _stack doesn't have them (for
54551                   // example when iD has been restarted in a different region)
54552                   var baseEntities = h.baseEntities.map(function (d) {
54553                     return osmEntity(d);
54554                   });
54555
54556                   var stack = _stack.map(function (state) {
54557                     return state.graph;
54558                   });
54559
54560                   _stack[0].graph.rebase(baseEntities, stack, true);
54561
54562                   _tree.rebase(baseEntities, true); // When we restore a modified way, we also need to fetch any missing
54563                   // childnodes that would normally have been downloaded with it.. #2142
54564
54565
54566                   if (loadChildNodes) {
54567                     var osm = context.connection();
54568                     var baseWays = baseEntities.filter(function (e) {
54569                       return e.type === 'way';
54570                     });
54571                     var nodeIDs = baseWays.reduce(function (acc, way) {
54572                       return utilArrayUnion(acc, way.nodes);
54573                     }, []);
54574                     var missing = nodeIDs.filter(function (n) {
54575                       return !_stack[0].graph.hasEntity(n);
54576                     });
54577
54578                     if (missing.length && osm) {
54579                       loadComplete = false;
54580                       context.map().redrawEnable(false);
54581                       var loading = uiLoading(context).blocking(true);
54582                       context.container().call(loading);
54583
54584                       var childNodesLoaded = function childNodesLoaded(err, result) {
54585                         if (!err) {
54586                           var visibleGroups = utilArrayGroupBy(result.data, 'visible');
54587                           var visibles = visibleGroups["true"] || []; // alive nodes
54588
54589                           var invisibles = visibleGroups["false"] || []; // deleted nodes
54590
54591                           if (visibles.length) {
54592                             var visibleIDs = visibles.map(function (entity) {
54593                               return entity.id;
54594                             });
54595
54596                             var stack = _stack.map(function (state) {
54597                               return state.graph;
54598                             });
54599
54600                             missing = utilArrayDifference(missing, visibleIDs);
54601
54602                             _stack[0].graph.rebase(visibles, stack, true);
54603
54604                             _tree.rebase(visibles, true);
54605                           } // fetch older versions of nodes that were deleted..
54606
54607
54608                           invisibles.forEach(function (entity) {
54609                             osm.loadEntityVersion(entity.id, +entity.version - 1, childNodesLoaded);
54610                           });
54611                         }
54612
54613                         if (err || !missing.length) {
54614                           loading.close();
54615                           context.map().redrawEnable(true);
54616                           dispatch.call('change');
54617                           dispatch.call('restore', this);
54618                         }
54619                       };
54620
54621                       osm.loadMultiple(missing, childNodesLoaded);
54622                     }
54623                   }
54624                 }
54625
54626                 _stack = h.stack.map(function (d) {
54627                   var entities = {},
54628                       entity;
54629
54630                   if (d.modified) {
54631                     d.modified.forEach(function (key) {
54632                       entity = allEntities[key];
54633                       entities[entity.id] = entity;
54634                     });
54635                   }
54636
54637                   if (d.deleted) {
54638                     d.deleted.forEach(function (id) {
54639                       entities[id] = undefined;
54640                     });
54641                   }
54642
54643                   return {
54644                     graph: coreGraph(_stack[0].graph).load(entities),
54645                     annotation: d.annotation,
54646                     imageryUsed: d.imageryUsed,
54647                     photoOverlaysUsed: d.photoOverlaysUsed,
54648                     transform: d.transform,
54649                     selectedIDs: d.selectedIDs
54650                   };
54651                 });
54652               } else {
54653                 // original version
54654                 _stack = h.stack.map(function (d) {
54655                   var entities = {};
54656
54657                   for (var i in d.entities) {
54658                     var entity = d.entities[i];
54659                     entities[i] = entity === 'undefined' ? undefined : osmEntity(entity);
54660                   }
54661
54662                   d.graph = coreGraph(_stack[0].graph).load(entities);
54663                   return d;
54664                 });
54665               }
54666
54667               var transform = _stack[_index].transform;
54668
54669               if (transform) {
54670                 context.map().transformEase(transform, 0); // 0 = immediate, no easing
54671               }
54672
54673               if (loadComplete) {
54674                 dispatch.call('change');
54675                 dispatch.call('restore', this);
54676               }
54677
54678               return history;
54679             },
54680             lock: function lock() {
54681               return _lock.lock();
54682             },
54683             unlock: function unlock() {
54684               _lock.unlock();
54685             },
54686             save: function save() {
54687               if (_lock.locked() && // don't overwrite existing, unresolved changes
54688               !_hasUnresolvedRestorableChanges) {
54689                 corePreferences(getKey('saved_history'), history.toJSON() || null);
54690               }
54691
54692               return history;
54693             },
54694             // delete the history version saved in localStorage
54695             clearSaved: function clearSaved() {
54696               context.debouncedSave.cancel();
54697
54698               if (_lock.locked()) {
54699                 _hasUnresolvedRestorableChanges = false;
54700                 corePreferences(getKey('saved_history'), null); // clear the changeset metadata associated with the saved history
54701
54702                 corePreferences('comment', null);
54703                 corePreferences('hashtags', null);
54704                 corePreferences('source', null);
54705               }
54706
54707               return history;
54708             },
54709             savedHistoryJSON: function savedHistoryJSON() {
54710               return corePreferences(getKey('saved_history'));
54711             },
54712             hasRestorableChanges: function hasRestorableChanges() {
54713               return _hasUnresolvedRestorableChanges;
54714             },
54715             // load history from a version stored in localStorage
54716             restore: function restore() {
54717               if (_lock.locked()) {
54718                 _hasUnresolvedRestorableChanges = false;
54719                 var json = this.savedHistoryJSON();
54720                 if (json) history.fromJSON(json, true);
54721               }
54722             },
54723             _getKey: getKey
54724           };
54725           history.reset();
54726           return utilRebind(history, dispatch, 'on');
54727         }
54728
54729         /**
54730          * Look for roads that can be connected to other roads with a short extension
54731          */
54732
54733         function validationAlmostJunction(context) {
54734           var type = 'almost_junction';
54735           var EXTEND_TH_METERS = 5;
54736           var WELD_TH_METERS = 0.75; // Comes from considering bounding case of parallel ways
54737
54738           var CLOSE_NODE_TH = EXTEND_TH_METERS - WELD_TH_METERS; // Comes from considering bounding case of perpendicular ways
54739
54740           var SIG_ANGLE_TH = Math.atan(WELD_TH_METERS / EXTEND_TH_METERS);
54741
54742           function isHighway(entity) {
54743             return entity.type === 'way' && osmRoutableHighwayTagValues[entity.tags.highway];
54744           }
54745
54746           function isTaggedAsNotContinuing(node) {
54747             return node.tags.noexit === 'yes' || node.tags.amenity === 'parking_entrance' || node.tags.entrance && node.tags.entrance !== 'no';
54748           }
54749
54750           var validation = function checkAlmostJunction(entity, graph) {
54751             if (!isHighway(entity)) return [];
54752             if (entity.isDegenerate()) return [];
54753             var tree = context.history().tree();
54754             var extendableNodeInfos = findConnectableEndNodesByExtension(entity);
54755             var issues = [];
54756             extendableNodeInfos.forEach(function (extendableNodeInfo) {
54757               issues.push(new validationIssue({
54758                 type: type,
54759                 subtype: 'highway-highway',
54760                 severity: 'warning',
54761                 message: function message(context) {
54762                   var entity1 = context.hasEntity(this.entityIds[0]);
54763
54764                   if (this.entityIds[0] === this.entityIds[2]) {
54765                     return entity1 ? _t.html('issues.almost_junction.self.message', {
54766                       feature: utilDisplayLabel(entity1, context.graph())
54767                     }) : '';
54768                   } else {
54769                     var entity2 = context.hasEntity(this.entityIds[2]);
54770                     return entity1 && entity2 ? _t.html('issues.almost_junction.message', {
54771                       feature: utilDisplayLabel(entity1, context.graph()),
54772                       feature2: utilDisplayLabel(entity2, context.graph())
54773                     }) : '';
54774                   }
54775                 },
54776                 reference: showReference,
54777                 entityIds: [entity.id, extendableNodeInfo.node.id, extendableNodeInfo.wid],
54778                 loc: extendableNodeInfo.node.loc,
54779                 hash: JSON.stringify(extendableNodeInfo.node.loc),
54780                 data: {
54781                   midId: extendableNodeInfo.mid.id,
54782                   edge: extendableNodeInfo.edge,
54783                   cross_loc: extendableNodeInfo.cross_loc
54784                 },
54785                 dynamicFixes: makeFixes
54786               }));
54787             });
54788             return issues;
54789
54790             function makeFixes(context) {
54791               var fixes = [new validationIssueFix({
54792                 icon: 'iD-icon-abutment',
54793                 title: _t.html('issues.fix.connect_features.title'),
54794                 onClick: function onClick(context) {
54795                   var annotation = _t('issues.fix.connect_almost_junction.annotation');
54796
54797                   var _this$issue$entityIds = _slicedToArray(this.issue.entityIds, 3),
54798                       endNodeId = _this$issue$entityIds[1],
54799                       crossWayId = _this$issue$entityIds[2];
54800
54801                   var midNode = context.entity(this.issue.data.midId);
54802                   var endNode = context.entity(endNodeId);
54803                   var crossWay = context.entity(crossWayId); // When endpoints are close, just join if resulting small change in angle (#7201)
54804
54805                   var nearEndNodes = findNearbyEndNodes(endNode, crossWay);
54806
54807                   if (nearEndNodes.length > 0) {
54808                     var collinear = findSmallJoinAngle(midNode, endNode, nearEndNodes);
54809
54810                     if (collinear) {
54811                       context.perform(actionMergeNodes([collinear.id, endNode.id], collinear.loc), annotation);
54812                       return;
54813                     }
54814                   }
54815
54816                   var targetEdge = this.issue.data.edge;
54817                   var crossLoc = this.issue.data.cross_loc;
54818                   var edgeNodes = [context.entity(targetEdge[0]), context.entity(targetEdge[1])];
54819                   var closestNodeInfo = geoSphericalClosestNode(edgeNodes, crossLoc); // already a point nearby, just connect to that
54820
54821                   if (closestNodeInfo.distance < WELD_TH_METERS) {
54822                     context.perform(actionMergeNodes([closestNodeInfo.node.id, endNode.id], closestNodeInfo.node.loc), annotation); // else add the end node to the edge way
54823                   } else {
54824                     context.perform(actionAddMidpoint({
54825                       loc: crossLoc,
54826                       edge: targetEdge
54827                     }, endNode), annotation);
54828                   }
54829                 }
54830               })];
54831               var node = context.hasEntity(this.entityIds[1]);
54832
54833               if (node && !node.hasInterestingTags()) {
54834                 // node has no descriptive tags, suggest noexit fix
54835                 fixes.push(new validationIssueFix({
54836                   icon: 'maki-barrier',
54837                   title: _t.html('issues.fix.tag_as_disconnected.title'),
54838                   onClick: function onClick(context) {
54839                     var nodeID = this.issue.entityIds[1];
54840                     var tags = Object.assign({}, context.entity(nodeID).tags);
54841                     tags.noexit = 'yes';
54842                     context.perform(actionChangeTags(nodeID, tags), _t('issues.fix.tag_as_disconnected.annotation'));
54843                   }
54844                 }));
54845               }
54846
54847               return fixes;
54848             }
54849
54850             function showReference(selection) {
54851               selection.selectAll('.issue-reference').data([0]).enter().append('div').attr('class', 'issue-reference').html(_t.html('issues.almost_junction.highway-highway.reference'));
54852             }
54853
54854             function isExtendableCandidate(node, way) {
54855               // can not accurately test vertices on tiles not downloaded from osm - #5938
54856               var osm = services.osm;
54857
54858               if (osm && !osm.isDataLoaded(node.loc)) {
54859                 return false;
54860               }
54861
54862               if (isTaggedAsNotContinuing(node) || graph.parentWays(node).length !== 1) {
54863                 return false;
54864               }
54865
54866               var occurrences = 0;
54867
54868               for (var index in way.nodes) {
54869                 if (way.nodes[index] === node.id) {
54870                   occurrences += 1;
54871
54872                   if (occurrences > 1) {
54873                     return false;
54874                   }
54875                 }
54876               }
54877
54878               return true;
54879             }
54880
54881             function findConnectableEndNodesByExtension(way) {
54882               var results = [];
54883               if (way.isClosed()) return results;
54884               var testNodes;
54885               var indices = [0, way.nodes.length - 1];
54886               indices.forEach(function (nodeIndex) {
54887                 var nodeID = way.nodes[nodeIndex];
54888                 var node = graph.entity(nodeID);
54889                 if (!isExtendableCandidate(node, way)) return;
54890                 var connectionInfo = canConnectByExtend(way, nodeIndex);
54891                 if (!connectionInfo) return;
54892                 testNodes = graph.childNodes(way).slice(); // shallow copy
54893
54894                 testNodes[nodeIndex] = testNodes[nodeIndex].move(connectionInfo.cross_loc); // don't flag issue if connecting the ways would cause self-intersection
54895
54896                 if (geoHasSelfIntersections(testNodes, nodeID)) return;
54897                 results.push(connectionInfo);
54898               });
54899               return results;
54900             }
54901
54902             function findNearbyEndNodes(node, way) {
54903               return [way.nodes[0], way.nodes[way.nodes.length - 1]].map(function (d) {
54904                 return graph.entity(d);
54905               }).filter(function (d) {
54906                 // Node cannot be near to itself, but other endnode of same way could be
54907                 return d.id !== node.id && geoSphericalDistance(node.loc, d.loc) <= CLOSE_NODE_TH;
54908               });
54909             }
54910
54911             function findSmallJoinAngle(midNode, tipNode, endNodes) {
54912               // Both nodes could be close, so want to join whichever is closest to collinear
54913               var joinTo;
54914               var minAngle = Infinity; // Checks midNode -> tipNode -> endNode for collinearity
54915
54916               endNodes.forEach(function (endNode) {
54917                 var a1 = geoAngle(midNode, tipNode, context.projection) + Math.PI;
54918                 var a2 = geoAngle(midNode, endNode, context.projection) + Math.PI;
54919                 var diff = Math.max(a1, a2) - Math.min(a1, a2);
54920
54921                 if (diff < minAngle) {
54922                   joinTo = endNode;
54923                   minAngle = diff;
54924                 }
54925               });
54926               /* Threshold set by considering right angle triangle
54927               based on node joining threshold and extension distance */
54928
54929               if (minAngle <= SIG_ANGLE_TH) return joinTo;
54930               return null;
54931             }
54932
54933             function hasTag(tags, key) {
54934               return tags[key] !== undefined && tags[key] !== 'no';
54935             }
54936
54937             function canConnectWays(way, way2) {
54938               // allow self-connections
54939               if (way.id === way2.id) return true; // if one is bridge or tunnel, both must be bridge or tunnel
54940
54941               if ((hasTag(way.tags, 'bridge') || hasTag(way2.tags, 'bridge')) && !(hasTag(way.tags, 'bridge') && hasTag(way2.tags, 'bridge'))) return false;
54942               if ((hasTag(way.tags, 'tunnel') || hasTag(way2.tags, 'tunnel')) && !(hasTag(way.tags, 'tunnel') && hasTag(way2.tags, 'tunnel'))) return false; // must have equivalent layers and levels
54943
54944               var layer1 = way.tags.layer || '0',
54945                   layer2 = way2.tags.layer || '0';
54946               if (layer1 !== layer2) return false;
54947               var level1 = way.tags.level || '0',
54948                   level2 = way2.tags.level || '0';
54949               if (level1 !== level2) return false;
54950               return true;
54951             }
54952
54953             function canConnectByExtend(way, endNodeIdx) {
54954               var tipNid = way.nodes[endNodeIdx]; // the 'tip' node for extension point
54955
54956               var midNid = endNodeIdx === 0 ? way.nodes[1] : way.nodes[way.nodes.length - 2]; // the other node of the edge
54957
54958               var tipNode = graph.entity(tipNid);
54959               var midNode = graph.entity(midNid);
54960               var lon = tipNode.loc[0];
54961               var lat = tipNode.loc[1];
54962               var lon_range = geoMetersToLon(EXTEND_TH_METERS, lat) / 2;
54963               var lat_range = geoMetersToLat(EXTEND_TH_METERS) / 2;
54964               var queryExtent = geoExtent([[lon - lon_range, lat - lat_range], [lon + lon_range, lat + lat_range]]); // first, extend the edge of [midNode -> tipNode] by EXTEND_TH_METERS and find the "extended tip" location
54965
54966               var edgeLen = geoSphericalDistance(midNode.loc, tipNode.loc);
54967               var t = EXTEND_TH_METERS / edgeLen + 1.0;
54968               var extTipLoc = geoVecInterp(midNode.loc, tipNode.loc, t); // then, check if the extension part [tipNode.loc -> extTipLoc] intersects any other ways
54969
54970               var segmentInfos = tree.waySegments(queryExtent, graph);
54971
54972               for (var i = 0; i < segmentInfos.length; i++) {
54973                 var segmentInfo = segmentInfos[i];
54974                 var way2 = graph.entity(segmentInfo.wayId);
54975                 if (!isHighway(way2)) continue;
54976                 if (!canConnectWays(way, way2)) continue;
54977                 var nAid = segmentInfo.nodes[0],
54978                     nBid = segmentInfo.nodes[1];
54979                 if (nAid === tipNid || nBid === tipNid) continue;
54980                 var nA = graph.entity(nAid),
54981                     nB = graph.entity(nBid);
54982                 var crossLoc = geoLineIntersection([tipNode.loc, extTipLoc], [nA.loc, nB.loc]);
54983
54984                 if (crossLoc) {
54985                   return {
54986                     mid: midNode,
54987                     node: tipNode,
54988                     wid: way2.id,
54989                     edge: [nA.id, nB.id],
54990                     cross_loc: crossLoc
54991                   };
54992                 }
54993               }
54994
54995               return null;
54996             }
54997           };
54998
54999           validation.type = type;
55000           return validation;
55001         }
55002
55003         function validationCloseNodes(context) {
55004           var type = 'close_nodes';
55005           var pointThresholdMeters = 0.2;
55006
55007           var validation = function validation(entity, graph) {
55008             if (entity.type === 'node') {
55009               return getIssuesForNode(entity);
55010             } else if (entity.type === 'way') {
55011               return getIssuesForWay(entity);
55012             }
55013
55014             return [];
55015
55016             function getIssuesForNode(node) {
55017               var parentWays = graph.parentWays(node);
55018
55019               if (parentWays.length) {
55020                 return getIssuesForVertex(node, parentWays);
55021               } else {
55022                 return getIssuesForDetachedPoint(node);
55023               }
55024             }
55025
55026             function wayTypeFor(way) {
55027               if (way.tags.boundary && way.tags.boundary !== 'no') return 'boundary';
55028               if (way.tags.indoor && way.tags.indoor !== 'no') return 'indoor';
55029               if (way.tags.building && way.tags.building !== 'no' || way.tags['building:part'] && way.tags['building:part'] !== 'no') return 'building';
55030               if (osmPathHighwayTagValues[way.tags.highway]) return 'path';
55031               var parentRelations = graph.parentRelations(way);
55032
55033               for (var i in parentRelations) {
55034                 var relation = parentRelations[i];
55035                 if (relation.tags.type === 'boundary') return 'boundary';
55036
55037                 if (relation.isMultipolygon()) {
55038                   if (relation.tags.indoor && relation.tags.indoor !== 'no') return 'indoor';
55039                   if (relation.tags.building && relation.tags.building !== 'no' || relation.tags['building:part'] && relation.tags['building:part'] !== 'no') return 'building';
55040                 }
55041               }
55042
55043               return 'other';
55044             }
55045
55046             function shouldCheckWay(way) {
55047               // don't flag issues where merging would create degenerate ways
55048               if (way.nodes.length <= 2 || way.isClosed() && way.nodes.length <= 4) return false;
55049               var bbox = way.extent(graph).bbox();
55050               var hypotenuseMeters = geoSphericalDistance([bbox.minX, bbox.minY], [bbox.maxX, bbox.maxY]); // don't flag close nodes in very small ways
55051
55052               if (hypotenuseMeters < 1.5) return false;
55053               return true;
55054             }
55055
55056             function getIssuesForWay(way) {
55057               if (!shouldCheckWay(way)) return [];
55058               var issues = [],
55059                   nodes = graph.childNodes(way);
55060
55061               for (var i = 0; i < nodes.length - 1; i++) {
55062                 var node1 = nodes[i];
55063                 var node2 = nodes[i + 1];
55064                 var issue = getWayIssueIfAny(node1, node2, way);
55065                 if (issue) issues.push(issue);
55066               }
55067
55068               return issues;
55069             }
55070
55071             function getIssuesForVertex(node, parentWays) {
55072               var issues = [];
55073
55074               function checkForCloseness(node1, node2, way) {
55075                 var issue = getWayIssueIfAny(node1, node2, way);
55076                 if (issue) issues.push(issue);
55077               }
55078
55079               for (var i = 0; i < parentWays.length; i++) {
55080                 var parentWay = parentWays[i];
55081                 if (!shouldCheckWay(parentWay)) continue;
55082                 var lastIndex = parentWay.nodes.length - 1;
55083
55084                 for (var j = 0; j < parentWay.nodes.length; j++) {
55085                   if (j !== 0) {
55086                     if (parentWay.nodes[j - 1] === node.id) {
55087                       checkForCloseness(node, graph.entity(parentWay.nodes[j]), parentWay);
55088                     }
55089                   }
55090
55091                   if (j !== lastIndex) {
55092                     if (parentWay.nodes[j + 1] === node.id) {
55093                       checkForCloseness(graph.entity(parentWay.nodes[j]), node, parentWay);
55094                     }
55095                   }
55096                 }
55097               }
55098
55099               return issues;
55100             }
55101
55102             function thresholdMetersForWay(way) {
55103               if (!shouldCheckWay(way)) return 0;
55104               var wayType = wayTypeFor(way); // don't flag boundaries since they might be highly detailed and can't be easily verified
55105
55106               if (wayType === 'boundary') return 0; // expect some features to be mapped with higher levels of detail
55107
55108               if (wayType === 'indoor') return 0.01;
55109               if (wayType === 'building') return 0.05;
55110               if (wayType === 'path') return 0.1;
55111               return 0.2;
55112             }
55113
55114             function getIssuesForDetachedPoint(node) {
55115               var issues = [];
55116               var lon = node.loc[0];
55117               var lat = node.loc[1];
55118               var lon_range = geoMetersToLon(pointThresholdMeters, lat) / 2;
55119               var lat_range = geoMetersToLat(pointThresholdMeters) / 2;
55120               var queryExtent = geoExtent([[lon - lon_range, lat - lat_range], [lon + lon_range, lat + lat_range]]);
55121               var intersected = context.history().tree().intersects(queryExtent, graph);
55122
55123               for (var j = 0; j < intersected.length; j++) {
55124                 var nearby = intersected[j];
55125                 if (nearby.id === node.id) continue;
55126                 if (nearby.type !== 'node' || nearby.geometry(graph) !== 'point') continue;
55127
55128                 if (nearby.loc === node.loc || geoSphericalDistance(node.loc, nearby.loc) < pointThresholdMeters) {
55129                   // allow very close points if tags indicate the z-axis might vary
55130                   var zAxisKeys = {
55131                     layer: true,
55132                     level: true,
55133                     'addr:housenumber': true,
55134                     'addr:unit': true
55135                   };
55136                   var zAxisDifferentiates = false;
55137
55138                   for (var key in zAxisKeys) {
55139                     var nodeValue = node.tags[key] || '0';
55140                     var nearbyValue = nearby.tags[key] || '0';
55141
55142                     if (nodeValue !== nearbyValue) {
55143                       zAxisDifferentiates = true;
55144                       break;
55145                     }
55146                   }
55147
55148                   if (zAxisDifferentiates) continue;
55149                   issues.push(new validationIssue({
55150                     type: type,
55151                     subtype: 'detached',
55152                     severity: 'warning',
55153                     message: function message(context) {
55154                       var entity = context.hasEntity(this.entityIds[0]),
55155                           entity2 = context.hasEntity(this.entityIds[1]);
55156                       return entity && entity2 ? _t.html('issues.close_nodes.detached.message', {
55157                         feature: utilDisplayLabel(entity, context.graph()),
55158                         feature2: utilDisplayLabel(entity2, context.graph())
55159                       }) : '';
55160                     },
55161                     reference: showReference,
55162                     entityIds: [node.id, nearby.id],
55163                     dynamicFixes: function dynamicFixes() {
55164                       return [new validationIssueFix({
55165                         icon: 'iD-operation-disconnect',
55166                         title: _t.html('issues.fix.move_points_apart.title')
55167                       }), new validationIssueFix({
55168                         icon: 'iD-icon-layers',
55169                         title: _t.html('issues.fix.use_different_layers_or_levels.title')
55170                       })];
55171                     }
55172                   }));
55173                 }
55174               }
55175
55176               return issues;
55177
55178               function showReference(selection) {
55179                 var referenceText = _t('issues.close_nodes.detached.reference');
55180                 selection.selectAll('.issue-reference').data([0]).enter().append('div').attr('class', 'issue-reference').html(referenceText);
55181               }
55182             }
55183
55184             function getWayIssueIfAny(node1, node2, way) {
55185               if (node1.id === node2.id || node1.hasInterestingTags() && node2.hasInterestingTags()) {
55186                 return null;
55187               }
55188
55189               if (node1.loc !== node2.loc) {
55190                 var parentWays1 = graph.parentWays(node1);
55191                 var parentWays2 = new Set(graph.parentWays(node2));
55192                 var sharedWays = parentWays1.filter(function (parentWay) {
55193                   return parentWays2.has(parentWay);
55194                 });
55195                 var thresholds = sharedWays.map(function (parentWay) {
55196                   return thresholdMetersForWay(parentWay);
55197                 });
55198                 var threshold = Math.min.apply(Math, _toConsumableArray(thresholds));
55199                 var distance = geoSphericalDistance(node1.loc, node2.loc);
55200                 if (distance > threshold) return null;
55201               }
55202
55203               return new validationIssue({
55204                 type: type,
55205                 subtype: 'vertices',
55206                 severity: 'warning',
55207                 message: function message(context) {
55208                   var entity = context.hasEntity(this.entityIds[0]);
55209                   return entity ? _t.html('issues.close_nodes.message', {
55210                     way: utilDisplayLabel(entity, context.graph())
55211                   }) : '';
55212                 },
55213                 reference: showReference,
55214                 entityIds: [way.id, node1.id, node2.id],
55215                 loc: node1.loc,
55216                 dynamicFixes: function dynamicFixes() {
55217                   return [new validationIssueFix({
55218                     icon: 'iD-icon-plus',
55219                     title: _t.html('issues.fix.merge_points.title'),
55220                     onClick: function onClick(context) {
55221                       var entityIds = this.issue.entityIds;
55222                       var action = actionMergeNodes([entityIds[1], entityIds[2]]);
55223                       context.perform(action, _t('issues.fix.merge_close_vertices.annotation'));
55224                     }
55225                   }), new validationIssueFix({
55226                     icon: 'iD-operation-disconnect',
55227                     title: _t.html('issues.fix.move_points_apart.title')
55228                   })];
55229                 }
55230               });
55231
55232               function showReference(selection) {
55233                 var referenceText = _t('issues.close_nodes.reference');
55234                 selection.selectAll('.issue-reference').data([0]).enter().append('div').attr('class', 'issue-reference').html(referenceText);
55235               }
55236             }
55237           };
55238
55239           validation.type = type;
55240           return validation;
55241         }
55242
55243         function validationCrossingWays(context) {
55244           var type = 'crossing_ways'; // returns the way or its parent relation, whichever has a useful feature type
55245
55246           function getFeatureWithFeatureTypeTagsForWay(way, graph) {
55247             if (getFeatureType(way, graph) === null) {
55248               // if the way doesn't match a feature type, check its parent relations
55249               var parentRels = graph.parentRelations(way);
55250
55251               for (var i = 0; i < parentRels.length; i++) {
55252                 var rel = parentRels[i];
55253
55254                 if (getFeatureType(rel, graph) !== null) {
55255                   return rel;
55256                 }
55257               }
55258             }
55259
55260             return way;
55261           }
55262
55263           function hasTag(tags, key) {
55264             return tags[key] !== undefined && tags[key] !== 'no';
55265           }
55266
55267           function taggedAsIndoor(tags) {
55268             return hasTag(tags, 'indoor') || hasTag(tags, 'level') || tags.highway === 'corridor';
55269           }
55270
55271           function allowsBridge(featureType) {
55272             return featureType === 'highway' || featureType === 'railway' || featureType === 'waterway';
55273           }
55274
55275           function allowsTunnel(featureType) {
55276             return featureType === 'highway' || featureType === 'railway' || featureType === 'waterway';
55277           } // discard
55278
55279
55280           var ignoredBuildings = {
55281             demolished: true,
55282             dismantled: true,
55283             proposed: true,
55284             razed: true
55285           };
55286
55287           function getFeatureType(entity, graph) {
55288             var geometry = entity.geometry(graph);
55289             if (geometry !== 'line' && geometry !== 'area') return null;
55290             var tags = entity.tags;
55291             if (hasTag(tags, 'building') && !ignoredBuildings[tags.building]) return 'building';
55292             if (hasTag(tags, 'highway') && osmRoutableHighwayTagValues[tags.highway]) return 'highway'; // don't check railway or waterway areas
55293
55294             if (geometry !== 'line') return null;
55295             if (hasTag(tags, 'railway') && osmRailwayTrackTagValues[tags.railway]) return 'railway';
55296             if (hasTag(tags, 'waterway') && osmFlowingWaterwayTagValues[tags.waterway]) return 'waterway';
55297             return null;
55298           }
55299
55300           function isLegitCrossing(tags1, featureType1, tags2, featureType2) {
55301             // assume 0 by default
55302             var level1 = tags1.level || '0';
55303             var level2 = tags2.level || '0';
55304
55305             if (taggedAsIndoor(tags1) && taggedAsIndoor(tags2) && level1 !== level2) {
55306               // assume features don't interact if they're indoor on different levels
55307               return true;
55308             } // assume 0 by default; don't use way.layer() since we account for structures here
55309
55310
55311             var layer1 = tags1.layer || '0';
55312             var layer2 = tags2.layer || '0';
55313
55314             if (allowsBridge(featureType1) && allowsBridge(featureType2)) {
55315               if (hasTag(tags1, 'bridge') && !hasTag(tags2, 'bridge')) return true;
55316               if (!hasTag(tags1, 'bridge') && hasTag(tags2, 'bridge')) return true; // crossing bridges must use different layers
55317
55318               if (hasTag(tags1, 'bridge') && hasTag(tags2, 'bridge') && layer1 !== layer2) return true;
55319             } else if (allowsBridge(featureType1) && hasTag(tags1, 'bridge')) return true;else if (allowsBridge(featureType2) && hasTag(tags2, 'bridge')) return true;
55320
55321             if (allowsTunnel(featureType1) && allowsTunnel(featureType2)) {
55322               if (hasTag(tags1, 'tunnel') && !hasTag(tags2, 'tunnel')) return true;
55323               if (!hasTag(tags1, 'tunnel') && hasTag(tags2, 'tunnel')) return true; // crossing tunnels must use different layers
55324
55325               if (hasTag(tags1, 'tunnel') && hasTag(tags2, 'tunnel') && layer1 !== layer2) return true;
55326             } else if (allowsTunnel(featureType1) && hasTag(tags1, 'tunnel')) return true;else if (allowsTunnel(featureType2) && hasTag(tags2, 'tunnel')) return true; // don't flag crossing waterways and pier/highways
55327
55328
55329             if (featureType1 === 'waterway' && featureType2 === 'highway' && tags2.man_made === 'pier') return true;
55330             if (featureType2 === 'waterway' && featureType1 === 'highway' && tags1.man_made === 'pier') return true;
55331
55332             if (featureType1 === 'building' || featureType2 === 'building') {
55333               // for building crossings, different layers are enough
55334               if (layer1 !== layer2) return true;
55335             }
55336
55337             return false;
55338           } // highway values for which we shouldn't recommend connecting to waterways
55339
55340
55341           var highwaysDisallowingFords = {
55342             motorway: true,
55343             motorway_link: true,
55344             trunk: true,
55345             trunk_link: true,
55346             primary: true,
55347             primary_link: true,
55348             secondary: true,
55349             secondary_link: true
55350           };
55351           var nonCrossingHighways = {
55352             track: true
55353           };
55354
55355           function tagsForConnectionNodeIfAllowed(entity1, entity2, graph) {
55356             var featureType1 = getFeatureType(entity1, graph);
55357             var featureType2 = getFeatureType(entity2, graph);
55358             var geometry1 = entity1.geometry(graph);
55359             var geometry2 = entity2.geometry(graph);
55360             var bothLines = geometry1 === 'line' && geometry2 === 'line';
55361
55362             if (featureType1 === featureType2) {
55363               if (featureType1 === 'highway') {
55364                 var entity1IsPath = osmPathHighwayTagValues[entity1.tags.highway];
55365                 var entity2IsPath = osmPathHighwayTagValues[entity2.tags.highway];
55366
55367                 if ((entity1IsPath || entity2IsPath) && entity1IsPath !== entity2IsPath) {
55368                   // one feature is a path but not both
55369                   var roadFeature = entity1IsPath ? entity2 : entity1;
55370
55371                   if (nonCrossingHighways[roadFeature.tags.highway]) {
55372                     // don't mark path connections with certain roads as crossings
55373                     return {};
55374                   }
55375
55376                   var pathFeature = entity1IsPath ? entity1 : entity2;
55377
55378                   if (['marked', 'unmarked'].indexOf(pathFeature.tags.crossing) !== -1) {
55379                     // if the path is a crossing, match the crossing type
55380                     return bothLines ? {
55381                       highway: 'crossing',
55382                       crossing: pathFeature.tags.crossing
55383                     } : {};
55384                   } // don't add a `crossing` subtag to ambiguous crossings
55385
55386
55387                   return bothLines ? {
55388                     highway: 'crossing'
55389                   } : {};
55390                 }
55391
55392                 return {};
55393               }
55394
55395               if (featureType1 === 'waterway') return {};
55396               if (featureType1 === 'railway') return {};
55397             } else {
55398               var featureTypes = [featureType1, featureType2];
55399
55400               if (featureTypes.indexOf('highway') !== -1) {
55401                 if (featureTypes.indexOf('railway') !== -1) {
55402                   if (!bothLines) return {};
55403                   var isTram = entity1.tags.railway === 'tram' || entity2.tags.railway === 'tram';
55404
55405                   if (osmPathHighwayTagValues[entity1.tags.highway] || osmPathHighwayTagValues[entity2.tags.highway]) {
55406                     // path-tram connections use this tag
55407                     if (isTram) return {
55408                       railway: 'tram_crossing'
55409                     }; // other path-rail connections use this tag
55410
55411                     return {
55412                       railway: 'crossing'
55413                     };
55414                   } else {
55415                     // path-tram connections use this tag
55416                     if (isTram) return {
55417                       railway: 'tram_level_crossing'
55418                     }; // other road-rail connections use this tag
55419
55420                     return {
55421                       railway: 'level_crossing'
55422                     };
55423                   }
55424                 }
55425
55426                 if (featureTypes.indexOf('waterway') !== -1) {
55427                   // do not allow fords on structures
55428                   if (hasTag(entity1.tags, 'tunnel') && hasTag(entity2.tags, 'tunnel')) return null;
55429                   if (hasTag(entity1.tags, 'bridge') && hasTag(entity2.tags, 'bridge')) return null;
55430
55431                   if (highwaysDisallowingFords[entity1.tags.highway] || highwaysDisallowingFords[entity2.tags.highway]) {
55432                     // do not allow fords on major highways
55433                     return null;
55434                   }
55435
55436                   return bothLines ? {
55437                     ford: 'yes'
55438                   } : {};
55439                 }
55440               }
55441             }
55442
55443             return null;
55444           }
55445
55446           function findCrossingsByWay(way1, graph, tree) {
55447             var edgeCrossInfos = [];
55448             if (way1.type !== 'way') return edgeCrossInfos;
55449             var taggedFeature1 = getFeatureWithFeatureTypeTagsForWay(way1, graph);
55450             var way1FeatureType = getFeatureType(taggedFeature1, graph);
55451             if (way1FeatureType === null) return edgeCrossInfos;
55452             var checkedSingleCrossingWays = {}; // declare vars ahead of time to reduce garbage collection
55453
55454             var i, j;
55455             var extent;
55456             var n1, n2, nA, nB, nAId, nBId;
55457             var segment1, segment2;
55458             var oneOnly;
55459             var segmentInfos, segment2Info, way2, taggedFeature2, way2FeatureType;
55460             var way1Nodes = graph.childNodes(way1);
55461             var comparedWays = {};
55462
55463             for (i = 0; i < way1Nodes.length - 1; i++) {
55464               n1 = way1Nodes[i];
55465               n2 = way1Nodes[i + 1];
55466               extent = geoExtent([[Math.min(n1.loc[0], n2.loc[0]), Math.min(n1.loc[1], n2.loc[1])], [Math.max(n1.loc[0], n2.loc[0]), Math.max(n1.loc[1], n2.loc[1])]]); // Optimize by only checking overlapping segments, not every segment
55467               // of overlapping ways
55468
55469               segmentInfos = tree.waySegments(extent, graph);
55470
55471               for (j = 0; j < segmentInfos.length; j++) {
55472                 segment2Info = segmentInfos[j]; // don't check for self-intersection in this validation
55473
55474                 if (segment2Info.wayId === way1.id) continue; // skip if this way was already checked and only one issue is needed
55475
55476                 if (checkedSingleCrossingWays[segment2Info.wayId]) continue; // mark this way as checked even if there are no crossings
55477
55478                 comparedWays[segment2Info.wayId] = true;
55479                 way2 = graph.hasEntity(segment2Info.wayId);
55480                 if (!way2) continue;
55481                 taggedFeature2 = getFeatureWithFeatureTypeTagsForWay(way2, graph); // only check crossing highway, waterway, building, and railway
55482
55483                 way2FeatureType = getFeatureType(taggedFeature2, graph);
55484
55485                 if (way2FeatureType === null || isLegitCrossing(taggedFeature1.tags, way1FeatureType, taggedFeature2.tags, way2FeatureType)) {
55486                   continue;
55487                 } // create only one issue for building crossings
55488
55489
55490                 oneOnly = way1FeatureType === 'building' || way2FeatureType === 'building';
55491                 nAId = segment2Info.nodes[0];
55492                 nBId = segment2Info.nodes[1];
55493
55494                 if (nAId === n1.id || nAId === n2.id || nBId === n1.id || nBId === n2.id) {
55495                   // n1 or n2 is a connection node; skip
55496                   continue;
55497                 }
55498
55499                 nA = graph.hasEntity(nAId);
55500                 if (!nA) continue;
55501                 nB = graph.hasEntity(nBId);
55502                 if (!nB) continue;
55503                 segment1 = [n1.loc, n2.loc];
55504                 segment2 = [nA.loc, nB.loc];
55505                 var point = geoLineIntersection(segment1, segment2);
55506
55507                 if (point) {
55508                   edgeCrossInfos.push({
55509                     wayInfos: [{
55510                       way: way1,
55511                       featureType: way1FeatureType,
55512                       edge: [n1.id, n2.id]
55513                     }, {
55514                       way: way2,
55515                       featureType: way2FeatureType,
55516                       edge: [nA.id, nB.id]
55517                     }],
55518                     crossPoint: point
55519                   });
55520
55521                   if (oneOnly) {
55522                     checkedSingleCrossingWays[way2.id] = true;
55523                     break;
55524                   }
55525                 }
55526               }
55527             }
55528
55529             return edgeCrossInfos;
55530           }
55531
55532           function waysToCheck(entity, graph) {
55533             var featureType = getFeatureType(entity, graph);
55534             if (!featureType) return [];
55535
55536             if (entity.type === 'way') {
55537               return [entity];
55538             } else if (entity.type === 'relation') {
55539               return entity.members.reduce(function (array, member) {
55540                 if (member.type === 'way' && ( // only look at geometry ways
55541                 !member.role || member.role === 'outer' || member.role === 'inner')) {
55542                   var entity = graph.hasEntity(member.id); // don't add duplicates
55543
55544                   if (entity && array.indexOf(entity) === -1) {
55545                     array.push(entity);
55546                   }
55547                 }
55548
55549                 return array;
55550               }, []);
55551             }
55552
55553             return [];
55554           }
55555
55556           var validation = function checkCrossingWays(entity, graph) {
55557             var tree = context.history().tree();
55558             var ways = waysToCheck(entity, graph);
55559             var issues = []; // declare these here to reduce garbage collection
55560
55561             var wayIndex, crossingIndex, crossings;
55562
55563             for (wayIndex in ways) {
55564               crossings = findCrossingsByWay(ways[wayIndex], graph, tree);
55565
55566               for (crossingIndex in crossings) {
55567                 issues.push(createIssue(crossings[crossingIndex], graph));
55568               }
55569             }
55570
55571             return issues;
55572           };
55573
55574           function createIssue(crossing, graph) {
55575             // use the entities with the tags that define the feature type
55576             crossing.wayInfos.sort(function (way1Info, way2Info) {
55577               var type1 = way1Info.featureType;
55578               var type2 = way2Info.featureType;
55579
55580               if (type1 === type2) {
55581                 return utilDisplayLabel(way1Info.way, graph) > utilDisplayLabel(way2Info.way, graph);
55582               } else if (type1 === 'waterway') {
55583                 return true;
55584               } else if (type2 === 'waterway') {
55585                 return false;
55586               }
55587
55588               return type1 < type2;
55589             });
55590             var entities = crossing.wayInfos.map(function (wayInfo) {
55591               return getFeatureWithFeatureTypeTagsForWay(wayInfo.way, graph);
55592             });
55593             var edges = [crossing.wayInfos[0].edge, crossing.wayInfos[1].edge];
55594             var featureTypes = [crossing.wayInfos[0].featureType, crossing.wayInfos[1].featureType];
55595             var connectionTags = tagsForConnectionNodeIfAllowed(entities[0], entities[1], graph);
55596             var featureType1 = crossing.wayInfos[0].featureType;
55597             var featureType2 = crossing.wayInfos[1].featureType;
55598             var isCrossingIndoors = taggedAsIndoor(entities[0].tags) && taggedAsIndoor(entities[1].tags);
55599             var isCrossingTunnels = allowsTunnel(featureType1) && hasTag(entities[0].tags, 'tunnel') && allowsTunnel(featureType2) && hasTag(entities[1].tags, 'tunnel');
55600             var isCrossingBridges = allowsBridge(featureType1) && hasTag(entities[0].tags, 'bridge') && allowsBridge(featureType2) && hasTag(entities[1].tags, 'bridge');
55601             var subtype = [featureType1, featureType2].sort().join('-');
55602             var crossingTypeID = subtype;
55603
55604             if (isCrossingIndoors) {
55605               crossingTypeID = 'indoor-indoor';
55606             } else if (isCrossingTunnels) {
55607               crossingTypeID = 'tunnel-tunnel';
55608             } else if (isCrossingBridges) {
55609               crossingTypeID = 'bridge-bridge';
55610             }
55611
55612             if (connectionTags && (isCrossingIndoors || isCrossingTunnels || isCrossingBridges)) {
55613               crossingTypeID += '_connectable';
55614             } // Differentiate based on the loc rounded to 4 digits, since two ways can cross multiple times.
55615
55616
55617             var uniqueID = '' + crossing.crossPoint[0].toFixed(4) + ',' + crossing.crossPoint[1].toFixed(4);
55618             return new validationIssue({
55619               type: type,
55620               subtype: subtype,
55621               severity: 'warning',
55622               message: function message(context) {
55623                 var graph = context.graph();
55624                 var entity1 = graph.hasEntity(this.entityIds[0]),
55625                     entity2 = graph.hasEntity(this.entityIds[1]);
55626                 return entity1 && entity2 ? _t.html('issues.crossing_ways.message', {
55627                   feature: utilDisplayLabel(entity1, graph),
55628                   feature2: utilDisplayLabel(entity2, graph)
55629                 }) : '';
55630               },
55631               reference: showReference,
55632               entityIds: entities.map(function (entity) {
55633                 return entity.id;
55634               }),
55635               data: {
55636                 edges: edges,
55637                 featureTypes: featureTypes,
55638                 connectionTags: connectionTags
55639               },
55640               hash: uniqueID,
55641               loc: crossing.crossPoint,
55642               dynamicFixes: function dynamicFixes(context) {
55643                 var mode = context.mode();
55644                 if (!mode || mode.id !== 'select' || mode.selectedIDs().length !== 1) return [];
55645                 var selectedIndex = this.entityIds[0] === mode.selectedIDs()[0] ? 0 : 1;
55646                 var selectedFeatureType = this.data.featureTypes[selectedIndex];
55647                 var otherFeatureType = this.data.featureTypes[selectedIndex === 0 ? 1 : 0];
55648                 var fixes = [];
55649
55650                 if (connectionTags) {
55651                   fixes.push(makeConnectWaysFix(this.data.connectionTags));
55652                 }
55653
55654                 if (isCrossingIndoors) {
55655                   fixes.push(new validationIssueFix({
55656                     icon: 'iD-icon-layers',
55657                     title: _t.html('issues.fix.use_different_levels.title')
55658                   }));
55659                 } else if (isCrossingTunnels || isCrossingBridges || featureType1 === 'building' || featureType2 === 'building') {
55660                   fixes.push(makeChangeLayerFix('higher'));
55661                   fixes.push(makeChangeLayerFix('lower')); // can only add bridge/tunnel if both features are lines
55662                 } else if (context.graph().geometry(this.entityIds[0]) === 'line' && context.graph().geometry(this.entityIds[1]) === 'line') {
55663                   // don't recommend adding bridges to waterways since they're uncommon
55664                   if (allowsBridge(selectedFeatureType) && selectedFeatureType !== 'waterway') {
55665                     fixes.push(makeAddBridgeOrTunnelFix('add_a_bridge', 'temaki-bridge', 'bridge'));
55666                   } // don't recommend adding tunnels under waterways since they're uncommon
55667
55668
55669                   var skipTunnelFix = otherFeatureType === 'waterway' && selectedFeatureType !== 'waterway';
55670
55671                   if (allowsTunnel(selectedFeatureType) && !skipTunnelFix) {
55672                     fixes.push(makeAddBridgeOrTunnelFix('add_a_tunnel', 'temaki-tunnel', 'tunnel'));
55673                   }
55674                 } // repositioning the features is always an option
55675
55676
55677                 fixes.push(new validationIssueFix({
55678                   icon: 'iD-operation-move',
55679                   title: _t.html('issues.fix.reposition_features.title')
55680                 }));
55681                 return fixes;
55682               }
55683             });
55684
55685             function showReference(selection) {
55686               selection.selectAll('.issue-reference').data([0]).enter().append('div').attr('class', 'issue-reference').html(_t.html('issues.crossing_ways.' + crossingTypeID + '.reference'));
55687             }
55688           }
55689
55690           function makeAddBridgeOrTunnelFix(fixTitleID, iconName, bridgeOrTunnel) {
55691             return new validationIssueFix({
55692               icon: iconName,
55693               title: _t.html('issues.fix.' + fixTitleID + '.title'),
55694               onClick: function onClick(context) {
55695                 var mode = context.mode();
55696                 if (!mode || mode.id !== 'select') return;
55697                 var selectedIDs = mode.selectedIDs();
55698                 if (selectedIDs.length !== 1) return;
55699                 var selectedWayID = selectedIDs[0];
55700                 if (!context.hasEntity(selectedWayID)) return;
55701                 var resultWayIDs = [selectedWayID];
55702                 var edge, crossedEdge, crossedWayID;
55703
55704                 if (this.issue.entityIds[0] === selectedWayID) {
55705                   edge = this.issue.data.edges[0];
55706                   crossedEdge = this.issue.data.edges[1];
55707                   crossedWayID = this.issue.entityIds[1];
55708                 } else {
55709                   edge = this.issue.data.edges[1];
55710                   crossedEdge = this.issue.data.edges[0];
55711                   crossedWayID = this.issue.entityIds[0];
55712                 }
55713
55714                 var crossingLoc = this.issue.loc;
55715                 var projection = context.projection;
55716
55717                 var action = function actionAddStructure(graph) {
55718                   var edgeNodes = [graph.entity(edge[0]), graph.entity(edge[1])];
55719                   var crossedWay = graph.hasEntity(crossedWayID); // use the explicit width of the crossed feature as the structure length, if available
55720
55721                   var structLengthMeters = crossedWay && crossedWay.tags.width && parseFloat(crossedWay.tags.width);
55722
55723                   if (!structLengthMeters) {
55724                     // if no explicit width is set, approximate the width based on the tags
55725                     structLengthMeters = crossedWay && crossedWay.impliedLineWidthMeters();
55726                   }
55727
55728                   if (structLengthMeters) {
55729                     if (getFeatureType(crossedWay, graph) === 'railway') {
55730                       // bridges over railways are generally much longer than the rail bed itself, compensate
55731                       structLengthMeters *= 2;
55732                     }
55733                   } else {
55734                     // should ideally never land here since all rail/water/road tags should have an implied width
55735                     structLengthMeters = 8;
55736                   }
55737
55738                   var a1 = geoAngle(edgeNodes[0], edgeNodes[1], projection) + Math.PI;
55739                   var a2 = geoAngle(graph.entity(crossedEdge[0]), graph.entity(crossedEdge[1]), projection) + Math.PI;
55740                   var crossingAngle = Math.max(a1, a2) - Math.min(a1, a2);
55741                   if (crossingAngle > Math.PI) crossingAngle -= Math.PI; // lengthen the structure to account for the angle of the crossing
55742
55743                   structLengthMeters = structLengthMeters / 2 / Math.sin(crossingAngle) * 2; // add padding since the structure must extend past the edges of the crossed feature
55744
55745                   structLengthMeters += 4; // clamp the length to a reasonable range
55746
55747                   structLengthMeters = Math.min(Math.max(structLengthMeters, 4), 50);
55748
55749                   function geomToProj(geoPoint) {
55750                     return [geoLonToMeters(geoPoint[0], geoPoint[1]), geoLatToMeters(geoPoint[1])];
55751                   }
55752
55753                   function projToGeom(projPoint) {
55754                     var lat = geoMetersToLat(projPoint[1]);
55755                     return [geoMetersToLon(projPoint[0], lat), lat];
55756                   }
55757
55758                   var projEdgeNode1 = geomToProj(edgeNodes[0].loc);
55759                   var projEdgeNode2 = geomToProj(edgeNodes[1].loc);
55760                   var projectedAngle = geoVecAngle(projEdgeNode1, projEdgeNode2);
55761                   var projectedCrossingLoc = geomToProj(crossingLoc);
55762                   var linearToSphericalMetersRatio = geoVecLength(projEdgeNode1, projEdgeNode2) / geoSphericalDistance(edgeNodes[0].loc, edgeNodes[1].loc);
55763
55764                   function locSphericalDistanceFromCrossingLoc(angle, distanceMeters) {
55765                     var lengthSphericalMeters = distanceMeters * linearToSphericalMetersRatio;
55766                     return projToGeom([projectedCrossingLoc[0] + Math.cos(angle) * lengthSphericalMeters, projectedCrossingLoc[1] + Math.sin(angle) * lengthSphericalMeters]);
55767                   }
55768
55769                   var endpointLocGetter1 = function endpointLocGetter1(lengthMeters) {
55770                     return locSphericalDistanceFromCrossingLoc(projectedAngle, lengthMeters);
55771                   };
55772
55773                   var endpointLocGetter2 = function endpointLocGetter2(lengthMeters) {
55774                     return locSphericalDistanceFromCrossingLoc(projectedAngle + Math.PI, lengthMeters);
55775                   }; // avoid creating very short edges from splitting too close to another node
55776
55777
55778                   var minEdgeLengthMeters = 0.55; // decide where to bound the structure along the way, splitting as necessary
55779
55780                   function determineEndpoint(edge, endNode, locGetter) {
55781                     var newNode;
55782                     var idealLengthMeters = structLengthMeters / 2; // distance between the crossing location and the end of the edge,
55783                     // the maximum length of this side of the structure
55784
55785                     var crossingToEdgeEndDistance = geoSphericalDistance(crossingLoc, endNode.loc);
55786
55787                     if (crossingToEdgeEndDistance - idealLengthMeters > minEdgeLengthMeters) {
55788                       // the edge is long enough to insert a new node
55789                       // the loc that would result in the full expected length
55790                       var idealNodeLoc = locGetter(idealLengthMeters);
55791                       newNode = osmNode();
55792                       graph = actionAddMidpoint({
55793                         loc: idealNodeLoc,
55794                         edge: edge
55795                       }, newNode)(graph);
55796                     } else {
55797                       var edgeCount = 0;
55798                       endNode.parentIntersectionWays(graph).forEach(function (way) {
55799                         way.nodes.forEach(function (nodeID) {
55800                           if (nodeID === endNode.id) {
55801                             if (endNode.id === way.first() && endNode.id !== way.last() || endNode.id === way.last() && endNode.id !== way.first()) {
55802                               edgeCount += 1;
55803                             } else {
55804                               edgeCount += 2;
55805                             }
55806                           }
55807                         });
55808                       });
55809
55810                       if (edgeCount >= 3) {
55811                         // the end node is a junction, try to leave a segment
55812                         // between it and the structure - #7202
55813                         var insetLength = crossingToEdgeEndDistance - minEdgeLengthMeters;
55814
55815                         if (insetLength > minEdgeLengthMeters) {
55816                           var insetNodeLoc = locGetter(insetLength);
55817                           newNode = osmNode();
55818                           graph = actionAddMidpoint({
55819                             loc: insetNodeLoc,
55820                             edge: edge
55821                           }, newNode)(graph);
55822                         }
55823                       }
55824                     } // if the edge is too short to subdivide as desired, then
55825                     // just bound the structure at the existing end node
55826
55827
55828                     if (!newNode) newNode = endNode;
55829                     var splitAction = actionSplit([newNode.id]).limitWays(resultWayIDs); // only split selected or created ways
55830                     // do the split
55831
55832                     graph = splitAction(graph);
55833
55834                     if (splitAction.getCreatedWayIDs().length) {
55835                       resultWayIDs.push(splitAction.getCreatedWayIDs()[0]);
55836                     }
55837
55838                     return newNode;
55839                   }
55840
55841                   var structEndNode1 = determineEndpoint(edge, edgeNodes[1], endpointLocGetter1);
55842                   var structEndNode2 = determineEndpoint([edgeNodes[0].id, structEndNode1.id], edgeNodes[0], endpointLocGetter2);
55843                   var structureWay = resultWayIDs.map(function (id) {
55844                     return graph.entity(id);
55845                   }).find(function (way) {
55846                     return way.nodes.indexOf(structEndNode1.id) !== -1 && way.nodes.indexOf(structEndNode2.id) !== -1;
55847                   });
55848                   var tags = Object.assign({}, structureWay.tags); // copy tags
55849
55850                   if (bridgeOrTunnel === 'bridge') {
55851                     tags.bridge = 'yes';
55852                     tags.layer = '1';
55853                   } else {
55854                     var tunnelValue = 'yes';
55855
55856                     if (getFeatureType(structureWay, graph) === 'waterway') {
55857                       // use `tunnel=culvert` for waterways by default
55858                       tunnelValue = 'culvert';
55859                     }
55860
55861                     tags.tunnel = tunnelValue;
55862                     tags.layer = '-1';
55863                   } // apply the structure tags to the way
55864
55865
55866                   graph = actionChangeTags(structureWay.id, tags)(graph);
55867                   return graph;
55868                 };
55869
55870                 context.perform(action, _t('issues.fix.' + fixTitleID + '.annotation'));
55871                 context.enter(modeSelect(context, resultWayIDs));
55872               }
55873             });
55874           }
55875
55876           function makeConnectWaysFix(connectionTags) {
55877             var fixTitleID = 'connect_features';
55878
55879             if (connectionTags.ford) {
55880               fixTitleID = 'connect_using_ford';
55881             }
55882
55883             return new validationIssueFix({
55884               icon: 'iD-icon-crossing',
55885               title: _t.html('issues.fix.' + fixTitleID + '.title'),
55886               onClick: function onClick(context) {
55887                 var loc = this.issue.loc;
55888                 var connectionTags = this.issue.data.connectionTags;
55889                 var edges = this.issue.data.edges;
55890                 context.perform(function actionConnectCrossingWays(graph) {
55891                   // create the new node for the points
55892                   var node = osmNode({
55893                     loc: loc,
55894                     tags: connectionTags
55895                   });
55896                   graph = graph.replace(node);
55897                   var nodesToMerge = [node.id];
55898                   var mergeThresholdInMeters = 0.75;
55899                   edges.forEach(function (edge) {
55900                     var edgeNodes = [graph.entity(edge[0]), graph.entity(edge[1])];
55901                     var nearby = geoSphericalClosestNode(edgeNodes, loc); // if there is already a suitable node nearby, use that
55902                     // use the node if node has no interesting tags or if it is a crossing node #8326
55903
55904                     if ((!nearby.node.hasInterestingTags() || nearby.node.isCrossing()) && nearby.distance < mergeThresholdInMeters) {
55905                       nodesToMerge.push(nearby.node.id); // else add the new node to the way
55906                     } else {
55907                       graph = actionAddMidpoint({
55908                         loc: loc,
55909                         edge: edge
55910                       }, node)(graph);
55911                     }
55912                   });
55913
55914                   if (nodesToMerge.length > 1) {
55915                     // if we're using nearby nodes, merge them with the new node
55916                     graph = actionMergeNodes(nodesToMerge, loc)(graph);
55917                   }
55918
55919                   return graph;
55920                 }, _t('issues.fix.connect_crossing_features.annotation'));
55921               }
55922             });
55923           }
55924
55925           function makeChangeLayerFix(higherOrLower) {
55926             return new validationIssueFix({
55927               icon: 'iD-icon-' + (higherOrLower === 'higher' ? 'up' : 'down'),
55928               title: _t.html('issues.fix.tag_this_as_' + higherOrLower + '.title'),
55929               onClick: function onClick(context) {
55930                 var mode = context.mode();
55931                 if (!mode || mode.id !== 'select') return;
55932                 var selectedIDs = mode.selectedIDs();
55933                 if (selectedIDs.length !== 1) return;
55934                 var selectedID = selectedIDs[0];
55935                 if (!this.issue.entityIds.some(function (entityId) {
55936                   return entityId === selectedID;
55937                 })) return;
55938                 var entity = context.hasEntity(selectedID);
55939                 if (!entity) return;
55940                 var tags = Object.assign({}, entity.tags); // shallow copy
55941
55942                 var layer = tags.layer && Number(tags.layer);
55943
55944                 if (layer && !isNaN(layer)) {
55945                   if (higherOrLower === 'higher') {
55946                     layer += 1;
55947                   } else {
55948                     layer -= 1;
55949                   }
55950                 } else {
55951                   if (higherOrLower === 'higher') {
55952                     layer = 1;
55953                   } else {
55954                     layer = -1;
55955                   }
55956                 }
55957
55958                 tags.layer = layer.toString();
55959                 context.perform(actionChangeTags(entity.id, tags), _t('operations.change_tags.annotation'));
55960               }
55961             });
55962           }
55963
55964           validation.type = type;
55965           return validation;
55966         }
55967
55968         function behaviorDrawWay(context, wayID, mode, startGraph) {
55969           var dispatch = dispatch$8('rejectedSelfIntersection');
55970           var behavior = behaviorDraw(context); // Must be set by `drawWay.nodeIndex` before each install of this behavior.
55971
55972           var _nodeIndex;
55973
55974           var _origWay;
55975
55976           var _wayGeometry;
55977
55978           var _headNodeID;
55979
55980           var _annotation;
55981
55982           var _pointerHasMoved = false; // The osmNode to be placed.
55983           // This is temporary and just follows the mouse cursor until an "add" event occurs.
55984
55985           var _drawNode;
55986
55987           var _didResolveTempEdit = false;
55988
55989           function createDrawNode(loc) {
55990             // don't make the draw node until we actually need it
55991             _drawNode = osmNode({
55992               loc: loc
55993             });
55994             context.pauseChangeDispatch();
55995             context.replace(function actionAddDrawNode(graph) {
55996               // add the draw node to the graph and insert it into the way
55997               var way = graph.entity(wayID);
55998               return graph.replace(_drawNode).replace(way.addNode(_drawNode.id, _nodeIndex));
55999             }, _annotation);
56000             context.resumeChangeDispatch();
56001             setActiveElements();
56002           }
56003
56004           function removeDrawNode() {
56005             context.pauseChangeDispatch();
56006             context.replace(function actionDeleteDrawNode(graph) {
56007               var way = graph.entity(wayID);
56008               return graph.replace(way.removeNode(_drawNode.id)).remove(_drawNode);
56009             }, _annotation);
56010             _drawNode = undefined;
56011             context.resumeChangeDispatch();
56012           }
56013
56014           function keydown(d3_event) {
56015             if (d3_event.keyCode === utilKeybinding.modifierCodes.alt) {
56016               if (context.surface().classed('nope')) {
56017                 context.surface().classed('nope-suppressed', true);
56018               }
56019
56020               context.surface().classed('nope', false).classed('nope-disabled', true);
56021             }
56022           }
56023
56024           function keyup(d3_event) {
56025             if (d3_event.keyCode === utilKeybinding.modifierCodes.alt) {
56026               if (context.surface().classed('nope-suppressed')) {
56027                 context.surface().classed('nope', true);
56028               }
56029
56030               context.surface().classed('nope-suppressed', false).classed('nope-disabled', false);
56031             }
56032           }
56033
56034           function allowsVertex(d) {
56035             return d.geometry(context.graph()) === 'vertex' || _mainPresetIndex.allowsVertex(d, context.graph());
56036           } // related code
56037           // - `mode/drag_node.js`     `doMove()`
56038           // - `behavior/draw.js`      `click()`
56039           // - `behavior/draw_way.js`  `move()`
56040
56041
56042           function move(d3_event, datum) {
56043             var loc = context.map().mouseCoordinates();
56044             if (!_drawNode) createDrawNode(loc);
56045             context.surface().classed('nope-disabled', d3_event.altKey);
56046             var targetLoc = datum && datum.properties && datum.properties.entity && allowsVertex(datum.properties.entity) && datum.properties.entity.loc;
56047             var targetNodes = datum && datum.properties && datum.properties.nodes;
56048
56049             if (targetLoc) {
56050               // snap to node/vertex - a point target with `.loc`
56051               loc = targetLoc;
56052             } else if (targetNodes) {
56053               // snap to way - a line target with `.nodes`
56054               var choice = geoChooseEdge(targetNodes, context.map().mouse(), context.projection, _drawNode.id);
56055
56056               if (choice) {
56057                 loc = choice.loc;
56058               }
56059             }
56060
56061             context.replace(actionMoveNode(_drawNode.id, loc), _annotation);
56062             _drawNode = context.entity(_drawNode.id);
56063             checkGeometry(true
56064             /* includeDrawNode */
56065             );
56066           } // Check whether this edit causes the geometry to break.
56067           // If so, class the surface with a nope cursor.
56068           // `includeDrawNode` - Only check the relevant line segments if finishing drawing
56069
56070
56071           function checkGeometry(includeDrawNode) {
56072             var nopeDisabled = context.surface().classed('nope-disabled');
56073             var isInvalid = isInvalidGeometry(includeDrawNode);
56074
56075             if (nopeDisabled) {
56076               context.surface().classed('nope', false).classed('nope-suppressed', isInvalid);
56077             } else {
56078               context.surface().classed('nope', isInvalid).classed('nope-suppressed', false);
56079             }
56080           }
56081
56082           function isInvalidGeometry(includeDrawNode) {
56083             var testNode = _drawNode; // we only need to test the single way we're drawing
56084
56085             var parentWay = context.graph().entity(wayID);
56086             var nodes = context.graph().childNodes(parentWay).slice(); // shallow copy
56087
56088             if (includeDrawNode) {
56089               if (parentWay.isClosed()) {
56090                 // don't test the last segment for closed ways - #4655
56091                 // (still test the first segment)
56092                 nodes.pop();
56093               }
56094             } else {
56095               // discount the draw node
56096               if (parentWay.isClosed()) {
56097                 if (nodes.length < 3) return false;
56098                 if (_drawNode) nodes.splice(-2, 1);
56099                 testNode = nodes[nodes.length - 2];
56100               } else {
56101                 // there's nothing we need to test if we ignore the draw node on open ways
56102                 return false;
56103               }
56104             }
56105
56106             return testNode && geoHasSelfIntersections(nodes, testNode.id);
56107           }
56108
56109           function undone() {
56110             // undoing removed the temp edit
56111             _didResolveTempEdit = true;
56112             context.pauseChangeDispatch();
56113             var nextMode;
56114
56115             if (context.graph() === startGraph) {
56116               // We've undone back to the initial state before we started drawing.
56117               // Just exit the draw mode without undoing whatever we did before
56118               // we entered the draw mode.
56119               nextMode = modeSelect(context, [wayID]);
56120             } else {
56121               // The `undo` only removed the temporary edit, so here we have to
56122               // manually undo to actually remove the last node we added. We can't
56123               // use the `undo` function since the initial "add" graph doesn't have
56124               // an annotation and so cannot be undone to.
56125               context.pop(1); // continue drawing
56126
56127               nextMode = mode;
56128             } // clear the redo stack by adding and removing a blank edit
56129
56130
56131             context.perform(actionNoop());
56132             context.pop(1);
56133             context.resumeChangeDispatch();
56134             context.enter(nextMode);
56135           }
56136
56137           function setActiveElements() {
56138             if (!_drawNode) return;
56139             context.surface().selectAll('.' + _drawNode.id).classed('active', true);
56140           }
56141
56142           function resetToStartGraph() {
56143             while (context.graph() !== startGraph) {
56144               context.pop();
56145             }
56146           }
56147
56148           var drawWay = function drawWay(surface) {
56149             _drawNode = undefined;
56150             _didResolveTempEdit = false;
56151             _origWay = context.entity(wayID);
56152
56153             if (typeof _nodeIndex === 'number') {
56154               _headNodeID = _origWay.nodes[_nodeIndex];
56155             } else if (_origWay.isClosed()) {
56156               _headNodeID = _origWay.nodes[_origWay.nodes.length - 2];
56157             } else {
56158               _headNodeID = _origWay.nodes[_origWay.nodes.length - 1];
56159             }
56160
56161             _wayGeometry = _origWay.geometry(context.graph());
56162             _annotation = _t((_origWay.nodes.length === (_origWay.isClosed() ? 2 : 1) ? 'operations.start.annotation.' : 'operations.continue.annotation.') + _wayGeometry);
56163             _pointerHasMoved = false; // Push an annotated state for undo to return back to.
56164             // We must make sure to replace or remove it later.
56165
56166             context.pauseChangeDispatch();
56167             context.perform(actionNoop(), _annotation);
56168             context.resumeChangeDispatch();
56169             behavior.hover().initialNodeID(_headNodeID);
56170             behavior.on('move', function () {
56171               _pointerHasMoved = true;
56172               move.apply(this, arguments);
56173             }).on('down', function () {
56174               move.apply(this, arguments);
56175             }).on('downcancel', function () {
56176               if (_drawNode) removeDrawNode();
56177             }).on('click', drawWay.add).on('clickWay', drawWay.addWay).on('clickNode', drawWay.addNode).on('undo', context.undo).on('cancel', drawWay.cancel).on('finish', drawWay.finish);
56178             select(window).on('keydown.drawWay', keydown).on('keyup.drawWay', keyup);
56179             context.map().dblclickZoomEnable(false).on('drawn.draw', setActiveElements);
56180             setActiveElements();
56181             surface.call(behavior);
56182             context.history().on('undone.draw', undone);
56183           };
56184
56185           drawWay.off = function (surface) {
56186             if (!_didResolveTempEdit) {
56187               // Drawing was interrupted unexpectedly.
56188               // This can happen if the user changes modes,
56189               // clicks geolocate button, a hashchange event occurs, etc.
56190               context.pauseChangeDispatch();
56191               resetToStartGraph();
56192               context.resumeChangeDispatch();
56193             }
56194
56195             _drawNode = undefined;
56196             _nodeIndex = undefined;
56197             context.map().on('drawn.draw', null);
56198             surface.call(behavior.off).selectAll('.active').classed('active', false);
56199             surface.classed('nope', false).classed('nope-suppressed', false).classed('nope-disabled', false);
56200             select(window).on('keydown.drawWay', null).on('keyup.drawWay', null);
56201             context.history().on('undone.draw', null);
56202           };
56203
56204           function attemptAdd(d, loc, doAdd) {
56205             if (_drawNode) {
56206               // move the node to the final loc in case move wasn't called
56207               // consistently (e.g. on touch devices)
56208               context.replace(actionMoveNode(_drawNode.id, loc), _annotation);
56209               _drawNode = context.entity(_drawNode.id);
56210             } else {
56211               createDrawNode(loc);
56212             }
56213
56214             checkGeometry(true
56215             /* includeDrawNode */
56216             );
56217
56218             if (d && d.properties && d.properties.nope || context.surface().classed('nope')) {
56219               if (!_pointerHasMoved) {
56220                 // prevent the temporary draw node from appearing on touch devices
56221                 removeDrawNode();
56222               }
56223
56224               dispatch.call('rejectedSelfIntersection', this);
56225               return; // can't click here
56226             }
56227
56228             context.pauseChangeDispatch();
56229             doAdd(); // we just replaced the temporary edit with the real one
56230
56231             _didResolveTempEdit = true;
56232             context.resumeChangeDispatch();
56233             context.enter(mode);
56234           } // Accept the current position of the drawing node
56235
56236
56237           drawWay.add = function (loc, d) {
56238             attemptAdd(d, loc, function () {// don't need to do anything extra
56239             });
56240           }; // Connect the way to an existing way
56241
56242
56243           drawWay.addWay = function (loc, edge, d) {
56244             attemptAdd(d, loc, function () {
56245               context.replace(actionAddMidpoint({
56246                 loc: loc,
56247                 edge: edge
56248               }, _drawNode), _annotation);
56249             });
56250           }; // Connect the way to an existing node
56251
56252
56253           drawWay.addNode = function (node, d) {
56254             // finish drawing if the mapper targets the prior node
56255             if (node.id === _headNodeID || // or the first node when drawing an area
56256             _origWay.isClosed() && node.id === _origWay.first()) {
56257               drawWay.finish();
56258               return;
56259             }
56260
56261             attemptAdd(d, node.loc, function () {
56262               context.replace(function actionReplaceDrawNode(graph) {
56263                 // remove the temporary draw node and insert the existing node
56264                 // at the same index
56265                 graph = graph.replace(graph.entity(wayID).removeNode(_drawNode.id)).remove(_drawNode);
56266                 return graph.replace(graph.entity(wayID).addNode(node.id, _nodeIndex));
56267               }, _annotation);
56268             });
56269           }; // Finish the draw operation, removing the temporary edit.
56270           // If the way has enough nodes to be valid, it's selected.
56271           // Otherwise, delete everything and return to browse mode.
56272
56273
56274           drawWay.finish = function () {
56275             checkGeometry(false
56276             /* includeDrawNode */
56277             );
56278
56279             if (context.surface().classed('nope')) {
56280               dispatch.call('rejectedSelfIntersection', this);
56281               return; // can't click here
56282             }
56283
56284             context.pauseChangeDispatch(); // remove the temporary edit
56285
56286             context.pop(1);
56287             _didResolveTempEdit = true;
56288             context.resumeChangeDispatch();
56289             var way = context.hasEntity(wayID);
56290
56291             if (!way || way.isDegenerate()) {
56292               drawWay.cancel();
56293               return;
56294             }
56295
56296             window.setTimeout(function () {
56297               context.map().dblclickZoomEnable(true);
56298             }, 1000);
56299             var isNewFeature = !mode.isContinuing;
56300             context.enter(modeSelect(context, [wayID]).newFeature(isNewFeature));
56301           }; // Cancel the draw operation, delete everything, and return to browse mode.
56302
56303
56304           drawWay.cancel = function () {
56305             context.pauseChangeDispatch();
56306             resetToStartGraph();
56307             context.resumeChangeDispatch();
56308             window.setTimeout(function () {
56309               context.map().dblclickZoomEnable(true);
56310             }, 1000);
56311             context.surface().classed('nope', false).classed('nope-disabled', false).classed('nope-suppressed', false);
56312             context.enter(modeBrowse(context));
56313           };
56314
56315           drawWay.nodeIndex = function (val) {
56316             if (!arguments.length) return _nodeIndex;
56317             _nodeIndex = val;
56318             return drawWay;
56319           };
56320
56321           drawWay.activeID = function () {
56322             if (!arguments.length) return _drawNode && _drawNode.id; // no assign
56323
56324             return drawWay;
56325           };
56326
56327           return utilRebind(drawWay, dispatch, 'on');
56328         }
56329
56330         function modeDrawLine(context, wayID, startGraph, button, affix, continuing) {
56331           var mode = {
56332             button: button,
56333             id: 'draw-line'
56334           };
56335           var behavior = behaviorDrawWay(context, wayID, mode, startGraph).on('rejectedSelfIntersection.modeDrawLine', function () {
56336             context.ui().flash.iconName('#iD-icon-no').label(_t('self_intersection.error.lines'))();
56337           });
56338           mode.wayID = wayID;
56339           mode.isContinuing = continuing;
56340
56341           mode.enter = function () {
56342             behavior.nodeIndex(affix === 'prefix' ? 0 : undefined);
56343             context.install(behavior);
56344           };
56345
56346           mode.exit = function () {
56347             context.uninstall(behavior);
56348           };
56349
56350           mode.selectedIDs = function () {
56351             return [wayID];
56352           };
56353
56354           mode.activeID = function () {
56355             return behavior && behavior.activeID() || [];
56356           };
56357
56358           return mode;
56359         }
56360
56361         function validationDisconnectedWay() {
56362           var type = 'disconnected_way';
56363
56364           function isTaggedAsHighway(entity) {
56365             return osmRoutableHighwayTagValues[entity.tags.highway];
56366           }
56367
56368           var validation = function checkDisconnectedWay(entity, graph) {
56369             var routingIslandWays = routingIslandForEntity(entity);
56370             if (!routingIslandWays) return [];
56371             return [new validationIssue({
56372               type: type,
56373               subtype: 'highway',
56374               severity: 'warning',
56375               message: function message(context) {
56376                 var entity = this.entityIds.length && context.hasEntity(this.entityIds[0]);
56377                 var label = entity && utilDisplayLabel(entity, context.graph());
56378                 return _t.html('issues.disconnected_way.routable.message', {
56379                   count: this.entityIds.length,
56380                   highway: label
56381                 });
56382               },
56383               reference: showReference,
56384               entityIds: Array.from(routingIslandWays).map(function (way) {
56385                 return way.id;
56386               }),
56387               dynamicFixes: makeFixes
56388             })];
56389
56390             function makeFixes(context) {
56391               var fixes = [];
56392               var singleEntity = this.entityIds.length === 1 && context.hasEntity(this.entityIds[0]);
56393
56394               if (singleEntity) {
56395                 if (singleEntity.type === 'way' && !singleEntity.isClosed()) {
56396                   var textDirection = _mainLocalizer.textDirection();
56397                   var startFix = makeContinueDrawingFixIfAllowed(textDirection, singleEntity.first(), 'start');
56398                   if (startFix) fixes.push(startFix);
56399                   var endFix = makeContinueDrawingFixIfAllowed(textDirection, singleEntity.last(), 'end');
56400                   if (endFix) fixes.push(endFix);
56401                 }
56402
56403                 if (!fixes.length) {
56404                   fixes.push(new validationIssueFix({
56405                     title: _t.html('issues.fix.connect_feature.title')
56406                   }));
56407                 }
56408
56409                 fixes.push(new validationIssueFix({
56410                   icon: 'iD-operation-delete',
56411                   title: _t.html('issues.fix.delete_feature.title'),
56412                   entityIds: [singleEntity.id],
56413                   onClick: function onClick(context) {
56414                     var id = this.issue.entityIds[0];
56415                     var operation = operationDelete(context, [id]);
56416
56417                     if (!operation.disabled()) {
56418                       operation();
56419                     }
56420                   }
56421                 }));
56422               } else {
56423                 fixes.push(new validationIssueFix({
56424                   title: _t.html('issues.fix.connect_features.title')
56425                 }));
56426               }
56427
56428               return fixes;
56429             }
56430
56431             function showReference(selection) {
56432               selection.selectAll('.issue-reference').data([0]).enter().append('div').attr('class', 'issue-reference').html(_t.html('issues.disconnected_way.routable.reference'));
56433             }
56434
56435             function routingIslandForEntity(entity) {
56436               var routingIsland = new Set(); // the interconnected routable features
56437
56438               var waysToCheck = []; // the queue of remaining routable ways to traverse
56439
56440               function queueParentWays(node) {
56441                 graph.parentWays(node).forEach(function (parentWay) {
56442                   if (!routingIsland.has(parentWay) && // only check each feature once
56443                   isRoutableWay(parentWay, false)) {
56444                     // only check routable features
56445                     routingIsland.add(parentWay);
56446                     waysToCheck.push(parentWay);
56447                   }
56448                 });
56449               }
56450
56451               if (entity.type === 'way' && isRoutableWay(entity, true)) {
56452                 routingIsland.add(entity);
56453                 waysToCheck.push(entity);
56454               } else if (entity.type === 'node' && isRoutableNode(entity)) {
56455                 routingIsland.add(entity);
56456                 queueParentWays(entity);
56457               } else {
56458                 // this feature isn't routable, cannot be a routing island
56459                 return null;
56460               }
56461
56462               while (waysToCheck.length) {
56463                 var wayToCheck = waysToCheck.pop();
56464                 var childNodes = graph.childNodes(wayToCheck);
56465
56466                 for (var i in childNodes) {
56467                   var vertex = childNodes[i];
56468
56469                   if (isConnectedVertex(vertex)) {
56470                     // found a link to the wider network, not a routing island
56471                     return null;
56472                   }
56473
56474                   if (isRoutableNode(vertex)) {
56475                     routingIsland.add(vertex);
56476                   }
56477
56478                   queueParentWays(vertex);
56479                 }
56480               } // no network link found, this is a routing island, return its members
56481
56482
56483               return routingIsland;
56484             }
56485
56486             function isConnectedVertex(vertex) {
56487               // assume ways overlapping unloaded tiles are connected to the wider road network  - #5938
56488               var osm = services.osm;
56489               if (osm && !osm.isDataLoaded(vertex.loc)) return true; // entrances are considered connected
56490
56491               if (vertex.tags.entrance && vertex.tags.entrance !== 'no') return true;
56492               if (vertex.tags.amenity === 'parking_entrance') return true;
56493               return false;
56494             }
56495
56496             function isRoutableNode(node) {
56497               // treat elevators as distinct features in the highway network
56498               if (node.tags.highway === 'elevator') return true;
56499               return false;
56500             }
56501
56502             function isRoutableWay(way, ignoreInnerWays) {
56503               if (isTaggedAsHighway(way) || way.tags.route === 'ferry') return true;
56504               return graph.parentRelations(way).some(function (parentRelation) {
56505                 if (parentRelation.tags.type === 'route' && parentRelation.tags.route === 'ferry') return true;
56506                 if (parentRelation.isMultipolygon() && isTaggedAsHighway(parentRelation) && (!ignoreInnerWays || parentRelation.memberById(way.id).role !== 'inner')) return true;
56507                 return false;
56508               });
56509             }
56510
56511             function makeContinueDrawingFixIfAllowed(textDirection, vertexID, whichEnd) {
56512               var vertex = graph.hasEntity(vertexID);
56513               if (!vertex || vertex.tags.noexit === 'yes') return null;
56514               var useLeftContinue = whichEnd === 'start' && textDirection === 'ltr' || whichEnd === 'end' && textDirection === 'rtl';
56515               return new validationIssueFix({
56516                 icon: 'iD-operation-continue' + (useLeftContinue ? '-left' : ''),
56517                 title: _t.html('issues.fix.continue_from_' + whichEnd + '.title'),
56518                 entityIds: [vertexID],
56519                 onClick: function onClick(context) {
56520                   var wayId = this.issue.entityIds[0];
56521                   var way = context.hasEntity(wayId);
56522                   var vertexId = this.entityIds[0];
56523                   var vertex = context.hasEntity(vertexId);
56524                   if (!way || !vertex) return; // make sure the vertex is actually visible and editable
56525
56526                   var map = context.map();
56527
56528                   if (!context.editable() || !map.trimmedExtent().contains(vertex.loc)) {
56529                     map.zoomToEase(vertex);
56530                   }
56531
56532                   context.enter(modeDrawLine(context, wayId, context.graph(), 'line', way.affix(vertexId), true));
56533                 }
56534               });
56535             }
56536           };
56537
56538           validation.type = type;
56539           return validation;
56540         }
56541
56542         function validationFormatting() {
56543           var type = 'invalid_format';
56544
56545           var validation = function validation(entity) {
56546             var issues = [];
56547
56548             function isValidEmail(email) {
56549               // Emails in OSM are going to be official so they should be pretty simple
56550               // Using negated lists to better support all possible unicode characters (#6494)
56551               var valid_email = /^[^\(\)\\,":;<>@\[\]]+@[^\(\)\\,":;<>@\[\]\.]+(?:\.[a-z0-9-]+)*$/i; // An empty value is also acceptable
56552
56553               return !email || valid_email.test(email);
56554             }
56555             /*
56556             function isSchemePresent(url) {
56557                 var valid_scheme = /^https?:\/\//i;
56558                 return (!url || valid_scheme.test(url));
56559             }
56560             */
56561
56562
56563             function showReferenceEmail(selection) {
56564               selection.selectAll('.issue-reference').data([0]).enter().append('div').attr('class', 'issue-reference').html(_t.html('issues.invalid_format.email.reference'));
56565             }
56566             /*
56567             function showReferenceWebsite(selection) {
56568                 selection.selectAll('.issue-reference')
56569                     .data([0])
56570                     .enter()
56571                     .append('div')
56572                     .attr('class', 'issue-reference')
56573                     .html(t.html('issues.invalid_format.website.reference'));
56574             }
56575              if (entity.tags.website) {
56576                 // Multiple websites are possible
56577                 // If ever we support ES6, arrow functions make this nicer
56578                 var websites = entity.tags.website
56579                     .split(';')
56580                     .map(function(s) { return s.trim(); })
56581                     .filter(function(x) { return !isSchemePresent(x); });
56582                  if (websites.length) {
56583                     issues.push(new validationIssue({
56584                         type: type,
56585                         subtype: 'website',
56586                         severity: 'warning',
56587                         message: function(context) {
56588                             var entity = context.hasEntity(this.entityIds[0]);
56589                             return entity ? t.html('issues.invalid_format.website.message' + this.data,
56590                                 { feature: utilDisplayLabel(entity, context.graph()), site: websites.join(', ') }) : '';
56591                         },
56592                         reference: showReferenceWebsite,
56593                         entityIds: [entity.id],
56594                         hash: websites.join(),
56595                         data: (websites.length > 1) ? '_multi' : ''
56596                     }));
56597                 }
56598             }
56599             */
56600
56601
56602             if (entity.tags.email) {
56603               // Multiple emails are possible
56604               var emails = entity.tags.email.split(';').map(function (s) {
56605                 return s.trim();
56606               }).filter(function (x) {
56607                 return !isValidEmail(x);
56608               });
56609
56610               if (emails.length) {
56611                 issues.push(new validationIssue({
56612                   type: type,
56613                   subtype: 'email',
56614                   severity: 'warning',
56615                   message: function message(context) {
56616                     var entity = context.hasEntity(this.entityIds[0]);
56617                     return entity ? _t.html('issues.invalid_format.email.message' + this.data, {
56618                       feature: utilDisplayLabel(entity, context.graph()),
56619                       email: emails.join(', ')
56620                     }) : '';
56621                   },
56622                   reference: showReferenceEmail,
56623                   entityIds: [entity.id],
56624                   hash: emails.join(),
56625                   data: emails.length > 1 ? '_multi' : ''
56626                 }));
56627               }
56628             }
56629
56630             return issues;
56631           };
56632
56633           validation.type = type;
56634           return validation;
56635         }
56636
56637         function validationHelpRequest(context) {
56638           var type = 'help_request';
56639
56640           var validation = function checkFixmeTag(entity) {
56641             if (!entity.tags.fixme) return []; // don't flag fixmes on features added by the user
56642
56643             if (entity.version === undefined) return [];
56644
56645             if (entity.v !== undefined) {
56646               var baseEntity = context.history().base().hasEntity(entity.id); // don't flag fixmes added by the user on existing features
56647
56648               if (!baseEntity || !baseEntity.tags.fixme) return [];
56649             }
56650
56651             return [new validationIssue({
56652               type: type,
56653               subtype: 'fixme_tag',
56654               severity: 'warning',
56655               message: function message(context) {
56656                 var entity = context.hasEntity(this.entityIds[0]);
56657                 return entity ? _t.html('issues.fixme_tag.message', {
56658                   feature: utilDisplayLabel(entity, context.graph(), true
56659                   /* verbose */
56660                   )
56661                 }) : '';
56662               },
56663               dynamicFixes: function dynamicFixes() {
56664                 return [new validationIssueFix({
56665                   title: _t.html('issues.fix.address_the_concern.title')
56666                 })];
56667               },
56668               reference: showReference,
56669               entityIds: [entity.id]
56670             })];
56671
56672             function showReference(selection) {
56673               selection.selectAll('.issue-reference').data([0]).enter().append('div').attr('class', 'issue-reference').html(_t.html('issues.fixme_tag.reference'));
56674             }
56675           };
56676
56677           validation.type = type;
56678           return validation;
56679         }
56680
56681         function validationImpossibleOneway() {
56682           var type = 'impossible_oneway';
56683
56684           var validation = function checkImpossibleOneway(entity, graph) {
56685             if (entity.type !== 'way' || entity.geometry(graph) !== 'line') return [];
56686             if (entity.isClosed()) return [];
56687             if (!typeForWay(entity)) return [];
56688             if (!isOneway(entity)) return [];
56689             var firstIssues = issuesForNode(entity, entity.first());
56690             var lastIssues = issuesForNode(entity, entity.last());
56691             return firstIssues.concat(lastIssues);
56692
56693             function typeForWay(way) {
56694               if (way.geometry(graph) !== 'line') return null;
56695               if (osmRoutableHighwayTagValues[way.tags.highway]) return 'highway';
56696               if (osmFlowingWaterwayTagValues[way.tags.waterway]) return 'waterway';
56697               return null;
56698             }
56699
56700             function isOneway(way) {
56701               if (way.tags.oneway === 'yes') return true;
56702               if (way.tags.oneway) return false;
56703
56704               for (var key in way.tags) {
56705                 if (osmOneWayTags[key] && osmOneWayTags[key][way.tags[key]]) {
56706                   return true;
56707                 }
56708               }
56709
56710               return false;
56711             }
56712
56713             function nodeOccursMoreThanOnce(way, nodeID) {
56714               var occurrences = 0;
56715
56716               for (var index in way.nodes) {
56717                 if (way.nodes[index] === nodeID) {
56718                   occurrences += 1;
56719                   if (occurrences > 1) return true;
56720                 }
56721               }
56722
56723               return false;
56724             }
56725
56726             function isConnectedViaOtherTypes(way, node) {
56727               var wayType = typeForWay(way);
56728
56729               if (wayType === 'highway') {
56730                 // entrances are considered connected
56731                 if (node.tags.entrance && node.tags.entrance !== 'no') return true;
56732                 if (node.tags.amenity === 'parking_entrance') return true;
56733               } else if (wayType === 'waterway') {
56734                 if (node.id === way.first()) {
56735                   // multiple waterways may start at the same spring
56736                   if (node.tags.natural === 'spring') return true;
56737                 } else {
56738                   // multiple waterways may end at the same drain
56739                   if (node.tags.manhole === 'drain') return true;
56740                 }
56741               }
56742
56743               return graph.parentWays(node).some(function (parentWay) {
56744                 if (parentWay.id === way.id) return false;
56745
56746                 if (wayType === 'highway') {
56747                   // allow connections to highway areas
56748                   if (parentWay.geometry(graph) === 'area' && osmRoutableHighwayTagValues[parentWay.tags.highway]) return true; // count connections to ferry routes as connected
56749
56750                   if (parentWay.tags.route === 'ferry') return true;
56751                   return graph.parentRelations(parentWay).some(function (parentRelation) {
56752                     if (parentRelation.tags.type === 'route' && parentRelation.tags.route === 'ferry') return true; // allow connections to highway multipolygons
56753
56754                     return parentRelation.isMultipolygon() && osmRoutableHighwayTagValues[parentRelation.tags.highway];
56755                   });
56756                 } else if (wayType === 'waterway') {
56757                   // multiple waterways may start or end at a water body at the same node
56758                   if (parentWay.tags.natural === 'water' || parentWay.tags.natural === 'coastline') return true;
56759                 }
56760
56761                 return false;
56762               });
56763             }
56764
56765             function issuesForNode(way, nodeID) {
56766               var isFirst = nodeID === way.first();
56767               var wayType = typeForWay(way); // ignore if this way is self-connected at this node
56768
56769               if (nodeOccursMoreThanOnce(way, nodeID)) return [];
56770               var osm = services.osm;
56771               if (!osm) return [];
56772               var node = graph.hasEntity(nodeID); // ignore if this node or its tile are unloaded
56773
56774               if (!node || !osm.isDataLoaded(node.loc)) return [];
56775               if (isConnectedViaOtherTypes(way, node)) return [];
56776               var attachedWaysOfSameType = graph.parentWays(node).filter(function (parentWay) {
56777                 if (parentWay.id === way.id) return false;
56778                 return typeForWay(parentWay) === wayType;
56779               }); // assume it's okay for waterways to start or end disconnected for now
56780
56781               if (wayType === 'waterway' && attachedWaysOfSameType.length === 0) return [];
56782               var attachedOneways = attachedWaysOfSameType.filter(function (attachedWay) {
56783                 return isOneway(attachedWay);
56784               }); // ignore if the way is connected to some non-oneway features
56785
56786               if (attachedOneways.length < attachedWaysOfSameType.length) return [];
56787
56788               if (attachedOneways.length) {
56789                 var connectedEndpointsOkay = attachedOneways.some(function (attachedOneway) {
56790                   if ((isFirst ? attachedOneway.first() : attachedOneway.last()) !== nodeID) return true;
56791                   if (nodeOccursMoreThanOnce(attachedOneway, nodeID)) return true;
56792                   return false;
56793                 });
56794                 if (connectedEndpointsOkay) return [];
56795               }
56796
56797               var placement = isFirst ? 'start' : 'end',
56798                   messageID = wayType + '.',
56799                   referenceID = wayType + '.';
56800
56801               if (wayType === 'waterway') {
56802                 messageID += 'connected.' + placement;
56803                 referenceID += 'connected';
56804               } else {
56805                 messageID += placement;
56806                 referenceID += placement;
56807               }
56808
56809               return [new validationIssue({
56810                 type: type,
56811                 subtype: wayType,
56812                 severity: 'warning',
56813                 message: function message(context) {
56814                   var entity = context.hasEntity(this.entityIds[0]);
56815                   return entity ? _t.html('issues.impossible_oneway.' + messageID + '.message', {
56816                     feature: utilDisplayLabel(entity, context.graph())
56817                   }) : '';
56818                 },
56819                 reference: getReference(referenceID),
56820                 entityIds: [way.id, node.id],
56821                 dynamicFixes: function dynamicFixes() {
56822                   var fixes = [];
56823
56824                   if (attachedOneways.length) {
56825                     fixes.push(new validationIssueFix({
56826                       icon: 'iD-operation-reverse',
56827                       title: _t.html('issues.fix.reverse_feature.title'),
56828                       entityIds: [way.id],
56829                       onClick: function onClick(context) {
56830                         var id = this.issue.entityIds[0];
56831                         context.perform(actionReverse(id), _t('operations.reverse.annotation.line', {
56832                           n: 1
56833                         }));
56834                       }
56835                     }));
56836                   }
56837
56838                   if (node.tags.noexit !== 'yes') {
56839                     var textDirection = _mainLocalizer.textDirection();
56840                     var useLeftContinue = isFirst && textDirection === 'ltr' || !isFirst && textDirection === 'rtl';
56841                     fixes.push(new validationIssueFix({
56842                       icon: 'iD-operation-continue' + (useLeftContinue ? '-left' : ''),
56843                       title: _t.html('issues.fix.continue_from_' + (isFirst ? 'start' : 'end') + '.title'),
56844                       onClick: function onClick(context) {
56845                         var entityID = this.issue.entityIds[0];
56846                         var vertexID = this.issue.entityIds[1];
56847                         var way = context.entity(entityID);
56848                         var vertex = context.entity(vertexID);
56849                         continueDrawing(way, vertex, context);
56850                       }
56851                     }));
56852                   }
56853
56854                   return fixes;
56855                 },
56856                 loc: node.loc
56857               })];
56858
56859               function getReference(referenceID) {
56860                 return function showReference(selection) {
56861                   selection.selectAll('.issue-reference').data([0]).enter().append('div').attr('class', 'issue-reference').html(_t.html('issues.impossible_oneway.' + referenceID + '.reference'));
56862                 };
56863               }
56864             }
56865           };
56866
56867           function continueDrawing(way, vertex, context) {
56868             // make sure the vertex is actually visible and editable
56869             var map = context.map();
56870
56871             if (!context.editable() || !map.trimmedExtent().contains(vertex.loc)) {
56872               map.zoomToEase(vertex);
56873             }
56874
56875             context.enter(modeDrawLine(context, way.id, context.graph(), 'line', way.affix(vertex.id), true));
56876           }
56877
56878           validation.type = type;
56879           return validation;
56880         }
56881
56882         function validationIncompatibleSource() {
56883           var type = 'incompatible_source';
56884           var invalidSources = [{
56885             id: 'google',
56886             regex: 'google',
56887             exceptRegex: 'books.google|Google Books|drive.google|googledrive|Google Drive'
56888           }];
56889
56890           var validation = function checkIncompatibleSource(entity) {
56891             var entitySources = entity.tags && entity.tags.source && entity.tags.source.split(';');
56892             if (!entitySources) return [];
56893             var issues = [];
56894             invalidSources.forEach(function (invalidSource) {
56895               var hasInvalidSource = entitySources.some(function (source) {
56896                 if (!source.match(new RegExp(invalidSource.regex, 'i'))) return false;
56897                 if (invalidSource.exceptRegex && source.match(new RegExp(invalidSource.exceptRegex, 'i'))) return false;
56898                 return true;
56899               });
56900               if (!hasInvalidSource) return;
56901               issues.push(new validationIssue({
56902                 type: type,
56903                 severity: 'warning',
56904                 message: function message(context) {
56905                   var entity = context.hasEntity(this.entityIds[0]);
56906                   return entity ? _t.html('issues.incompatible_source.' + invalidSource.id + '.feature.message', {
56907                     feature: utilDisplayLabel(entity, context.graph(), true
56908                     /* verbose */
56909                     )
56910                   }) : '';
56911                 },
56912                 reference: getReference(invalidSource.id),
56913                 entityIds: [entity.id],
56914                 dynamicFixes: function dynamicFixes() {
56915                   return [new validationIssueFix({
56916                     title: _t.html('issues.fix.remove_proprietary_data.title')
56917                   })];
56918                 }
56919               }));
56920             });
56921             return issues;
56922
56923             function getReference(id) {
56924               return function showReference(selection) {
56925                 selection.selectAll('.issue-reference').data([0]).enter().append('div').attr('class', 'issue-reference').html(_t.html('issues.incompatible_source.' + id + '.reference'));
56926               };
56927             }
56928           };
56929
56930           validation.type = type;
56931           return validation;
56932         }
56933
56934         function validationMaprules() {
56935           var type = 'maprules';
56936
56937           var validation = function checkMaprules(entity, graph) {
56938             if (!services.maprules) return [];
56939             var rules = services.maprules.validationRules();
56940             var issues = [];
56941
56942             for (var i = 0; i < rules.length; i++) {
56943               var rule = rules[i];
56944               rule.findIssues(entity, graph, issues);
56945             }
56946
56947             return issues;
56948           };
56949
56950           validation.type = type;
56951           return validation;
56952         }
56953
56954         function validationMismatchedGeometry() {
56955           var type = 'mismatched_geometry';
56956
56957           function tagSuggestingLineIsArea(entity) {
56958             if (entity.type !== 'way' || entity.isClosed()) return null;
56959             var tagSuggestingArea = entity.tagSuggestingArea();
56960
56961             if (!tagSuggestingArea) {
56962               return null;
56963             }
56964
56965             var asLine = _mainPresetIndex.matchTags(tagSuggestingArea, 'line');
56966             var asArea = _mainPresetIndex.matchTags(tagSuggestingArea, 'area');
56967
56968             if (asLine && asArea && asLine === asArea) {
56969               // these tags also allow lines and making this an area wouldn't matter
56970               return null;
56971             }
56972
56973             return tagSuggestingArea;
56974           }
56975
56976           function makeConnectEndpointsFixOnClick(way, graph) {
56977             // must have at least three nodes to close this automatically
56978             if (way.nodes.length < 3) return null;
56979             var nodes = graph.childNodes(way),
56980                 testNodes;
56981             var firstToLastDistanceMeters = geoSphericalDistance(nodes[0].loc, nodes[nodes.length - 1].loc); // if the distance is very small, attempt to merge the endpoints
56982
56983             if (firstToLastDistanceMeters < 0.75) {
56984               testNodes = nodes.slice(); // shallow copy
56985
56986               testNodes.pop();
56987               testNodes.push(testNodes[0]); // make sure this will not create a self-intersection
56988
56989               if (!geoHasSelfIntersections(testNodes, testNodes[0].id)) {
56990                 return function (context) {
56991                   var way = context.entity(this.issue.entityIds[0]);
56992                   context.perform(actionMergeNodes([way.nodes[0], way.nodes[way.nodes.length - 1]], nodes[0].loc), _t('issues.fix.connect_endpoints.annotation'));
56993                 };
56994               }
56995             } // if the points were not merged, attempt to close the way
56996
56997
56998             testNodes = nodes.slice(); // shallow copy
56999
57000             testNodes.push(testNodes[0]); // make sure this will not create a self-intersection
57001
57002             if (!geoHasSelfIntersections(testNodes, testNodes[0].id)) {
57003               return function (context) {
57004                 var wayId = this.issue.entityIds[0];
57005                 var way = context.entity(wayId);
57006                 var nodeId = way.nodes[0];
57007                 var index = way.nodes.length;
57008                 context.perform(actionAddVertex(wayId, nodeId, index), _t('issues.fix.connect_endpoints.annotation'));
57009               };
57010             }
57011           }
57012
57013           function lineTaggedAsAreaIssue(entity) {
57014             var tagSuggestingArea = tagSuggestingLineIsArea(entity);
57015             if (!tagSuggestingArea) return null;
57016             return new validationIssue({
57017               type: type,
57018               subtype: 'area_as_line',
57019               severity: 'warning',
57020               message: function message(context) {
57021                 var entity = context.hasEntity(this.entityIds[0]);
57022                 return entity ? _t.html('issues.tag_suggests_area.message', {
57023                   feature: utilDisplayLabel(entity, 'area', true
57024                   /* verbose */
57025                   ),
57026                   tag: utilTagText({
57027                     tags: tagSuggestingArea
57028                   })
57029                 }) : '';
57030               },
57031               reference: showReference,
57032               entityIds: [entity.id],
57033               hash: JSON.stringify(tagSuggestingArea),
57034               dynamicFixes: function dynamicFixes(context) {
57035                 var fixes = [];
57036                 var entity = context.entity(this.entityIds[0]);
57037                 var connectEndsOnClick = makeConnectEndpointsFixOnClick(entity, context.graph());
57038                 fixes.push(new validationIssueFix({
57039                   title: _t.html('issues.fix.connect_endpoints.title'),
57040                   onClick: connectEndsOnClick
57041                 }));
57042                 fixes.push(new validationIssueFix({
57043                   icon: 'iD-operation-delete',
57044                   title: _t.html('issues.fix.remove_tag.title'),
57045                   onClick: function onClick(context) {
57046                     var entityId = this.issue.entityIds[0];
57047                     var entity = context.entity(entityId);
57048                     var tags = Object.assign({}, entity.tags); // shallow copy
57049
57050                     for (var key in tagSuggestingArea) {
57051                       delete tags[key];
57052                     }
57053
57054                     context.perform(actionChangeTags(entityId, tags), _t('issues.fix.remove_tag.annotation'));
57055                   }
57056                 }));
57057                 return fixes;
57058               }
57059             });
57060
57061             function showReference(selection) {
57062               selection.selectAll('.issue-reference').data([0]).enter().append('div').attr('class', 'issue-reference').html(_t.html('issues.tag_suggests_area.reference'));
57063             }
57064           }
57065
57066           function vertexPointIssue(entity, graph) {
57067             // we only care about nodes
57068             if (entity.type !== 'node') return null; // ignore tagless points
57069
57070             if (Object.keys(entity.tags).length === 0) return null; // address lines are special so just ignore them
57071
57072             if (entity.isOnAddressLine(graph)) return null;
57073             var geometry = entity.geometry(graph);
57074             var allowedGeometries = osmNodeGeometriesForTags(entity.tags);
57075
57076             if (geometry === 'point' && !allowedGeometries.point && allowedGeometries.vertex) {
57077               return new validationIssue({
57078                 type: type,
57079                 subtype: 'vertex_as_point',
57080                 severity: 'warning',
57081                 message: function message(context) {
57082                   var entity = context.hasEntity(this.entityIds[0]);
57083                   return entity ? _t.html('issues.vertex_as_point.message', {
57084                     feature: utilDisplayLabel(entity, 'vertex', true
57085                     /* verbose */
57086                     )
57087                   }) : '';
57088                 },
57089                 reference: function showReference(selection) {
57090                   selection.selectAll('.issue-reference').data([0]).enter().append('div').attr('class', 'issue-reference').html(_t.html('issues.vertex_as_point.reference'));
57091                 },
57092                 entityIds: [entity.id]
57093               });
57094             } else if (geometry === 'vertex' && !allowedGeometries.vertex && allowedGeometries.point) {
57095               return new validationIssue({
57096                 type: type,
57097                 subtype: 'point_as_vertex',
57098                 severity: 'warning',
57099                 message: function message(context) {
57100                   var entity = context.hasEntity(this.entityIds[0]);
57101                   return entity ? _t.html('issues.point_as_vertex.message', {
57102                     feature: utilDisplayLabel(entity, 'point', true
57103                     /* verbose */
57104                     )
57105                   }) : '';
57106                 },
57107                 reference: function showReference(selection) {
57108                   selection.selectAll('.issue-reference').data([0]).enter().append('div').attr('class', 'issue-reference').html(_t.html('issues.point_as_vertex.reference'));
57109                 },
57110                 entityIds: [entity.id],
57111                 dynamicFixes: extractPointDynamicFixes
57112               });
57113             }
57114
57115             return null;
57116           }
57117
57118           function otherMismatchIssue(entity, graph) {
57119             // ignore boring features
57120             if (!entity.hasInterestingTags()) return null;
57121             if (entity.type !== 'node' && entity.type !== 'way') return null; // address lines are special so just ignore them
57122
57123             if (entity.type === 'node' && entity.isOnAddressLine(graph)) return null;
57124             var sourceGeom = entity.geometry(graph);
57125             var targetGeoms = entity.type === 'way' ? ['point', 'vertex'] : ['line', 'area'];
57126             if (sourceGeom === 'area') targetGeoms.unshift('line');
57127             var targetGeom = targetGeoms.find(function (nodeGeom) {
57128               var asSource = _mainPresetIndex.matchTags(entity.tags, sourceGeom);
57129               var asTarget = _mainPresetIndex.matchTags(entity.tags, nodeGeom);
57130               if (!asSource || !asTarget || asSource === asTarget || // sometimes there are two presets with the same tags for different geometries
57131               fastDeepEqual(asSource.tags, asTarget.tags)) return false;
57132               if (asTarget.isFallback()) return false;
57133               var primaryKey = Object.keys(asTarget.tags)[0]; // special case: buildings-as-points are discouraged by iD, but common in OSM, so ignore them
57134
57135               if (primaryKey === 'building') return false;
57136               if (asTarget.tags[primaryKey] === '*') return false;
57137               return asSource.isFallback() || asSource.tags[primaryKey] === '*';
57138             });
57139             if (!targetGeom) return null;
57140             var subtype = targetGeom + '_as_' + sourceGeom;
57141             if (targetGeom === 'vertex') targetGeom = 'point';
57142             if (sourceGeom === 'vertex') sourceGeom = 'point';
57143             var referenceId = targetGeom + '_as_' + sourceGeom;
57144             var dynamicFixes;
57145
57146             if (targetGeom === 'point') {
57147               dynamicFixes = extractPointDynamicFixes;
57148             } else if (sourceGeom === 'area' && targetGeom === 'line') {
57149               dynamicFixes = lineToAreaDynamicFixes;
57150             }
57151
57152             return new validationIssue({
57153               type: type,
57154               subtype: subtype,
57155               severity: 'warning',
57156               message: function message(context) {
57157                 var entity = context.hasEntity(this.entityIds[0]);
57158                 return entity ? _t.html('issues.' + referenceId + '.message', {
57159                   feature: utilDisplayLabel(entity, targetGeom, true
57160                   /* verbose */
57161                   )
57162                 }) : '';
57163               },
57164               reference: function showReference(selection) {
57165                 selection.selectAll('.issue-reference').data([0]).enter().append('div').attr('class', 'issue-reference').html(_t.html('issues.mismatched_geometry.reference'));
57166               },
57167               entityIds: [entity.id],
57168               dynamicFixes: dynamicFixes
57169             });
57170           }
57171
57172           function lineToAreaDynamicFixes(context) {
57173             var convertOnClick;
57174             var entityId = this.entityIds[0];
57175             var entity = context.entity(entityId);
57176             var tags = Object.assign({}, entity.tags); // shallow copy
57177
57178             delete tags.area;
57179
57180             if (!osmTagSuggestingArea(tags)) {
57181               // if removing the area tag would make this a line, offer that as a quick fix
57182               convertOnClick = function convertOnClick(context) {
57183                 var entityId = this.issue.entityIds[0];
57184                 var entity = context.entity(entityId);
57185                 var tags = Object.assign({}, entity.tags); // shallow copy
57186
57187                 if (tags.area) {
57188                   delete tags.area;
57189                 }
57190
57191                 context.perform(actionChangeTags(entityId, tags), _t('issues.fix.convert_to_line.annotation'));
57192               };
57193             }
57194
57195             return [new validationIssueFix({
57196               icon: 'iD-icon-line',
57197               title: _t.html('issues.fix.convert_to_line.title'),
57198               onClick: convertOnClick
57199             })];
57200           }
57201
57202           function extractPointDynamicFixes(context) {
57203             var entityId = this.entityIds[0];
57204             var extractOnClick = null;
57205
57206             if (!context.hasHiddenConnections(entityId)) {
57207               extractOnClick = function extractOnClick(context) {
57208                 var entityId = this.issue.entityIds[0];
57209                 var action = actionExtract(entityId, context.projection);
57210                 context.perform(action, _t('operations.extract.annotation', {
57211                   n: 1
57212                 })); // re-enter mode to trigger updates
57213
57214                 context.enter(modeSelect(context, [action.getExtractedNodeID()]));
57215               };
57216             }
57217
57218             return [new validationIssueFix({
57219               icon: 'iD-operation-extract',
57220               title: _t.html('issues.fix.extract_point.title'),
57221               onClick: extractOnClick
57222             })];
57223           }
57224
57225           function unclosedMultipolygonPartIssues(entity, graph) {
57226             if (entity.type !== 'relation' || !entity.isMultipolygon() || entity.isDegenerate() || // cannot determine issues for incompletely-downloaded relations
57227             !entity.isComplete(graph)) return [];
57228             var sequences = osmJoinWays(entity.members, graph);
57229             var issues = [];
57230
57231             for (var i in sequences) {
57232               var sequence = sequences[i];
57233               if (!sequence.nodes) continue;
57234               var firstNode = sequence.nodes[0];
57235               var lastNode = sequence.nodes[sequence.nodes.length - 1]; // part is closed if the first and last nodes are the same
57236
57237               if (firstNode === lastNode) continue;
57238               var issue = new validationIssue({
57239                 type: type,
57240                 subtype: 'unclosed_multipolygon_part',
57241                 severity: 'warning',
57242                 message: function message(context) {
57243                   var entity = context.hasEntity(this.entityIds[0]);
57244                   return entity ? _t.html('issues.unclosed_multipolygon_part.message', {
57245                     feature: utilDisplayLabel(entity, context.graph(), true
57246                     /* verbose */
57247                     )
57248                   }) : '';
57249                 },
57250                 reference: showReference,
57251                 loc: sequence.nodes[0].loc,
57252                 entityIds: [entity.id],
57253                 hash: sequence.map(function (way) {
57254                   return way.id;
57255                 }).join()
57256               });
57257               issues.push(issue);
57258             }
57259
57260             return issues;
57261
57262             function showReference(selection) {
57263               selection.selectAll('.issue-reference').data([0]).enter().append('div').attr('class', 'issue-reference').html(_t.html('issues.unclosed_multipolygon_part.reference'));
57264             }
57265           }
57266
57267           var validation = function checkMismatchedGeometry(entity, graph) {
57268             var vertexPoint = vertexPointIssue(entity, graph);
57269             if (vertexPoint) return [vertexPoint];
57270             var lineAsArea = lineTaggedAsAreaIssue(entity);
57271             if (lineAsArea) return [lineAsArea];
57272             var mismatch = otherMismatchIssue(entity, graph);
57273             if (mismatch) return [mismatch];
57274             return unclosedMultipolygonPartIssues(entity, graph);
57275           };
57276
57277           validation.type = type;
57278           return validation;
57279         }
57280
57281         function validationMissingRole() {
57282           var type = 'missing_role';
57283
57284           var validation = function checkMissingRole(entity, graph) {
57285             var issues = [];
57286
57287             if (entity.type === 'way') {
57288               graph.parentRelations(entity).forEach(function (relation) {
57289                 if (!relation.isMultipolygon()) return;
57290                 var member = relation.memberById(entity.id);
57291
57292                 if (member && isMissingRole(member)) {
57293                   issues.push(makeIssue(entity, relation, member));
57294                 }
57295               });
57296             } else if (entity.type === 'relation' && entity.isMultipolygon()) {
57297               entity.indexedMembers().forEach(function (member) {
57298                 var way = graph.hasEntity(member.id);
57299
57300                 if (way && isMissingRole(member)) {
57301                   issues.push(makeIssue(way, entity, member));
57302                 }
57303               });
57304             }
57305
57306             return issues;
57307           };
57308
57309           function isMissingRole(member) {
57310             return !member.role || !member.role.trim().length;
57311           }
57312
57313           function makeIssue(way, relation, member) {
57314             return new validationIssue({
57315               type: type,
57316               severity: 'warning',
57317               message: function message(context) {
57318                 var member = context.hasEntity(this.entityIds[1]),
57319                     relation = context.hasEntity(this.entityIds[0]);
57320                 return member && relation ? _t.html('issues.missing_role.message', {
57321                   member: utilDisplayLabel(member, context.graph()),
57322                   relation: utilDisplayLabel(relation, context.graph())
57323                 }) : '';
57324               },
57325               reference: showReference,
57326               entityIds: [relation.id, way.id],
57327               data: {
57328                 member: member
57329               },
57330               hash: member.index.toString(),
57331               dynamicFixes: function dynamicFixes() {
57332                 return [makeAddRoleFix('inner'), makeAddRoleFix('outer'), new validationIssueFix({
57333                   icon: 'iD-operation-delete',
57334                   title: _t.html('issues.fix.remove_from_relation.title'),
57335                   onClick: function onClick(context) {
57336                     context.perform(actionDeleteMember(this.issue.entityIds[0], this.issue.data.member.index), _t('operations.delete_member.annotation', {
57337                       n: 1
57338                     }));
57339                   }
57340                 })];
57341               }
57342             });
57343
57344             function showReference(selection) {
57345               selection.selectAll('.issue-reference').data([0]).enter().append('div').attr('class', 'issue-reference').html(_t.html('issues.missing_role.multipolygon.reference'));
57346             }
57347           }
57348
57349           function makeAddRoleFix(role) {
57350             return new validationIssueFix({
57351               title: _t.html('issues.fix.set_as_' + role + '.title'),
57352               onClick: function onClick(context) {
57353                 var oldMember = this.issue.data.member;
57354                 var member = {
57355                   id: this.issue.entityIds[1],
57356                   type: oldMember.type,
57357                   role: role
57358                 };
57359                 context.perform(actionChangeMember(this.issue.entityIds[0], member, oldMember.index), _t('operations.change_role.annotation', {
57360                   n: 1
57361                 }));
57362               }
57363             });
57364           }
57365
57366           validation.type = type;
57367           return validation;
57368         }
57369
57370         function validationMissingTag(context) {
57371           var type = 'missing_tag';
57372
57373           function hasDescriptiveTags(entity, graph) {
57374             var onlyAttributeKeys = ['description', 'name', 'note', 'start_date'];
57375             var entityDescriptiveKeys = Object.keys(entity.tags).filter(function (k) {
57376               if (k === 'area' || !osmIsInterestingTag(k)) return false;
57377               return !onlyAttributeKeys.some(function (attributeKey) {
57378                 return k === attributeKey || k.indexOf(attributeKey + ':') === 0;
57379               });
57380             });
57381
57382             if (entity.type === 'relation' && entityDescriptiveKeys.length === 1 && entity.tags.type === 'multipolygon') {
57383               // this relation's only interesting tag just says its a multipolygon,
57384               // which is not descriptive enough
57385               // It's okay for a simple multipolygon to have no descriptive tags
57386               // if its outer way has them (old model, see `outdated_tags.js`)
57387               return osmOldMultipolygonOuterMemberOfRelation(entity, graph);
57388             }
57389
57390             return entityDescriptiveKeys.length > 0;
57391           }
57392
57393           function isUnknownRoad(entity) {
57394             return entity.type === 'way' && entity.tags.highway === 'road';
57395           }
57396
57397           function isUntypedRelation(entity) {
57398             return entity.type === 'relation' && !entity.tags.type;
57399           }
57400
57401           var validation = function checkMissingTag(entity, graph) {
57402             var subtype;
57403             var osm = context.connection();
57404             var isUnloadedNode = entity.type === 'node' && osm && !osm.isDataLoaded(entity.loc); // we can't know if the node is a vertex if the tile is undownloaded
57405
57406             if (!isUnloadedNode && // allow untagged nodes that are part of ways
57407             entity.geometry(graph) !== 'vertex' && // allow untagged entities that are part of relations
57408             !entity.hasParentRelations(graph)) {
57409               if (Object.keys(entity.tags).length === 0) {
57410                 subtype = 'any';
57411               } else if (!hasDescriptiveTags(entity, graph)) {
57412                 subtype = 'descriptive';
57413               } else if (isUntypedRelation(entity)) {
57414                 subtype = 'relation_type';
57415               }
57416             } // flag an unknown road even if it's a member of a relation
57417
57418
57419             if (!subtype && isUnknownRoad(entity)) {
57420               subtype = 'highway_classification';
57421             }
57422
57423             if (!subtype) return [];
57424             var messageID = subtype === 'highway_classification' ? 'unknown_road' : 'missing_tag.' + subtype;
57425             var referenceID = subtype === 'highway_classification' ? 'unknown_road' : 'missing_tag'; // can always delete if the user created it in the first place..
57426
57427             var canDelete = entity.version === undefined || entity.v !== undefined;
57428             var severity = canDelete && subtype !== 'highway_classification' ? 'error' : 'warning';
57429             return [new validationIssue({
57430               type: type,
57431               subtype: subtype,
57432               severity: severity,
57433               message: function message(context) {
57434                 var entity = context.hasEntity(this.entityIds[0]);
57435                 return entity ? _t.html('issues.' + messageID + '.message', {
57436                   feature: utilDisplayLabel(entity, context.graph())
57437                 }) : '';
57438               },
57439               reference: showReference,
57440               entityIds: [entity.id],
57441               dynamicFixes: function dynamicFixes(context) {
57442                 var fixes = [];
57443                 var selectFixType = subtype === 'highway_classification' ? 'select_road_type' : 'select_preset';
57444                 fixes.push(new validationIssueFix({
57445                   icon: 'iD-icon-search',
57446                   title: _t.html('issues.fix.' + selectFixType + '.title'),
57447                   onClick: function onClick(context) {
57448                     context.ui().sidebar.showPresetList();
57449                   }
57450                 }));
57451                 var deleteOnClick;
57452                 var id = this.entityIds[0];
57453                 var operation = operationDelete(context, [id]);
57454                 var disabledReasonID = operation.disabled();
57455
57456                 if (!disabledReasonID) {
57457                   deleteOnClick = function deleteOnClick(context) {
57458                     var id = this.issue.entityIds[0];
57459                     var operation = operationDelete(context, [id]);
57460
57461                     if (!operation.disabled()) {
57462                       operation();
57463                     }
57464                   };
57465                 }
57466
57467                 fixes.push(new validationIssueFix({
57468                   icon: 'iD-operation-delete',
57469                   title: _t.html('issues.fix.delete_feature.title'),
57470                   disabledReason: disabledReasonID ? _t('operations.delete.' + disabledReasonID + '.single') : undefined,
57471                   onClick: deleteOnClick
57472                 }));
57473                 return fixes;
57474               }
57475             })];
57476
57477             function showReference(selection) {
57478               selection.selectAll('.issue-reference').data([0]).enter().append('div').attr('class', 'issue-reference').html(_t.html('issues.' + referenceID + '.reference'));
57479             }
57480           };
57481
57482           validation.type = type;
57483           return validation;
57484         }
57485
57486         function validationOutdatedTags() {
57487           var type = 'outdated_tags';
57488           var _waitingForDeprecated = true;
57489
57490           var _dataDeprecated; // fetch deprecated tags
57491
57492
57493           _mainFileFetcher.get('deprecated').then(function (d) {
57494             return _dataDeprecated = d;
57495           })["catch"](function () {
57496             /* ignore */
57497           })["finally"](function () {
57498             return _waitingForDeprecated = false;
57499           });
57500
57501           function oldTagIssues(entity, graph) {
57502             var oldTags = Object.assign({}, entity.tags); // shallow copy
57503
57504             var preset = _mainPresetIndex.match(entity, graph);
57505             var subtype = 'deprecated_tags';
57506             if (!preset) return [];
57507             if (!entity.hasInterestingTags()) return []; // Upgrade preset, if a replacement is available..
57508
57509             if (preset.replacement) {
57510               var newPreset = _mainPresetIndex.item(preset.replacement);
57511               graph = actionChangePreset(entity.id, preset, newPreset, true
57512               /* skip field defaults */
57513               )(graph);
57514               entity = graph.entity(entity.id);
57515               preset = newPreset;
57516             } // Upgrade deprecated tags..
57517
57518
57519             if (_dataDeprecated) {
57520               var deprecatedTags = entity.deprecatedTags(_dataDeprecated);
57521
57522               if (deprecatedTags.length) {
57523                 deprecatedTags.forEach(function (tag) {
57524                   graph = actionUpgradeTags(entity.id, tag.old, tag.replace)(graph);
57525                 });
57526                 entity = graph.entity(entity.id);
57527               }
57528             } // Add missing addTags from the detected preset
57529
57530
57531             var newTags = Object.assign({}, entity.tags); // shallow copy
57532
57533             if (preset.tags !== preset.addTags) {
57534               Object.keys(preset.addTags).forEach(function (k) {
57535                 if (!newTags[k]) {
57536                   if (preset.addTags[k] === '*') {
57537                     newTags[k] = 'yes';
57538                   } else {
57539                     newTags[k] = preset.addTags[k];
57540                   }
57541                 }
57542               });
57543             } // Attempt to match a canonical record in the name-suggestion-index.
57544
57545
57546             var nsi = services.nsi;
57547             var waitingForNsi = false;
57548
57549             if (nsi) {
57550               waitingForNsi = nsi.status() === 'loading';
57551
57552               if (!waitingForNsi) {
57553                 var loc = entity.extent(graph).center();
57554                 var result = nsi.upgradeTags(newTags, loc);
57555
57556                 if (result) {
57557                   newTags = result;
57558                   subtype = 'noncanonical_brand';
57559                 }
57560               }
57561             }
57562
57563             var issues = [];
57564             issues.provisional = _waitingForDeprecated || waitingForNsi; // determine diff
57565
57566             var tagDiff = utilTagDiff(oldTags, newTags);
57567             if (!tagDiff.length) return issues;
57568             var isOnlyAddingTags = tagDiff.every(function (d) {
57569               return d.type === '+';
57570             });
57571             var prefix = '';
57572
57573             if (subtype === 'noncanonical_brand') {
57574               prefix = 'noncanonical_brand.';
57575             } else if (subtype === 'deprecated_tags' && isOnlyAddingTags) {
57576               subtype = 'incomplete_tags';
57577               prefix = 'incomplete.';
57578             } // don't allow autofixing brand tags
57579
57580
57581             var autoArgs = subtype !== 'noncanonical_brand' ? [doUpgrade, _t('issues.fix.upgrade_tags.annotation')] : null;
57582             issues.push(new validationIssue({
57583               type: type,
57584               subtype: subtype,
57585               severity: 'warning',
57586               message: showMessage,
57587               reference: showReference,
57588               entityIds: [entity.id],
57589               hash: utilHashcode(JSON.stringify(tagDiff)),
57590               dynamicFixes: function dynamicFixes() {
57591                 return [new validationIssueFix({
57592                   autoArgs: autoArgs,
57593                   title: _t.html('issues.fix.upgrade_tags.title'),
57594                   onClick: function onClick(context) {
57595                     context.perform(doUpgrade, _t('issues.fix.upgrade_tags.annotation'));
57596                   }
57597                 })];
57598               }
57599             }));
57600             return issues;
57601
57602             function doUpgrade(graph) {
57603               var currEntity = graph.hasEntity(entity.id);
57604               if (!currEntity) return graph;
57605               var newTags = Object.assign({}, currEntity.tags); // shallow copy
57606
57607               tagDiff.forEach(function (diff) {
57608                 if (diff.type === '-') {
57609                   delete newTags[diff.key];
57610                 } else if (diff.type === '+') {
57611                   newTags[diff.key] = diff.newVal;
57612                 }
57613               });
57614               return actionChangeTags(currEntity.id, newTags)(graph);
57615             }
57616
57617             function showMessage(context) {
57618               var currEntity = context.hasEntity(entity.id);
57619               if (!currEntity) return '';
57620               var messageID = "issues.outdated_tags.".concat(prefix, "message");
57621
57622               if (subtype === 'noncanonical_brand' && isOnlyAddingTags) {
57623                 messageID += '_incomplete';
57624               }
57625
57626               return _t.html(messageID, {
57627                 feature: utilDisplayLabel(currEntity, context.graph(), true
57628                 /* verbose */
57629                 )
57630               });
57631             }
57632
57633             function showReference(selection) {
57634               var enter = selection.selectAll('.issue-reference').data([0]).enter();
57635               enter.append('div').attr('class', 'issue-reference').html(_t.html("issues.outdated_tags.".concat(prefix, "reference")));
57636               enter.append('strong').html(_t.html('issues.suggested'));
57637               enter.append('table').attr('class', 'tagDiff-table').selectAll('.tagDiff-row').data(tagDiff).enter().append('tr').attr('class', 'tagDiff-row').append('td').attr('class', function (d) {
57638                 var klass = d.type === '+' ? 'add' : 'remove';
57639                 return "tagDiff-cell tagDiff-cell-".concat(klass);
57640               }).html(function (d) {
57641                 return d.display;
57642               });
57643             }
57644           }
57645
57646           function oldMultipolygonIssues(entity, graph) {
57647             var multipolygon, outerWay;
57648
57649             if (entity.type === 'relation') {
57650               outerWay = osmOldMultipolygonOuterMemberOfRelation(entity, graph);
57651               multipolygon = entity;
57652             } else if (entity.type === 'way') {
57653               multipolygon = osmIsOldMultipolygonOuterMember(entity, graph);
57654               outerWay = entity;
57655             } else {
57656               return [];
57657             }
57658
57659             if (!multipolygon || !outerWay) return [];
57660             return [new validationIssue({
57661               type: type,
57662               subtype: 'old_multipolygon',
57663               severity: 'warning',
57664               message: showMessage,
57665               reference: showReference,
57666               entityIds: [outerWay.id, multipolygon.id],
57667               dynamicFixes: function dynamicFixes() {
57668                 return [new validationIssueFix({
57669                   autoArgs: [doUpgrade, _t('issues.fix.move_tags.annotation')],
57670                   title: _t.html('issues.fix.move_tags.title'),
57671                   onClick: function onClick(context) {
57672                     context.perform(doUpgrade, _t('issues.fix.move_tags.annotation'));
57673                   }
57674                 })];
57675               }
57676             })];
57677
57678             function doUpgrade(graph) {
57679               var currMultipolygon = graph.hasEntity(multipolygon.id);
57680               var currOuterWay = graph.hasEntity(outerWay.id);
57681               if (!currMultipolygon || !currOuterWay) return graph;
57682               currMultipolygon = currMultipolygon.mergeTags(currOuterWay.tags);
57683               graph = graph.replace(currMultipolygon);
57684               return actionChangeTags(currOuterWay.id, {})(graph);
57685             }
57686
57687             function showMessage(context) {
57688               var currMultipolygon = context.hasEntity(multipolygon.id);
57689               if (!currMultipolygon) return '';
57690               return _t.html('issues.old_multipolygon.message', {
57691                 multipolygon: utilDisplayLabel(currMultipolygon, context.graph(), true
57692                 /* verbose */
57693                 )
57694               });
57695             }
57696
57697             function showReference(selection) {
57698               selection.selectAll('.issue-reference').data([0]).enter().append('div').attr('class', 'issue-reference').html(_t.html('issues.old_multipolygon.reference'));
57699             }
57700           }
57701
57702           var validation = function checkOutdatedTags(entity, graph) {
57703             var issues = oldMultipolygonIssues(entity, graph);
57704             if (!issues.length) issues = oldTagIssues(entity, graph);
57705             return issues;
57706           };
57707
57708           validation.type = type;
57709           return validation;
57710         }
57711
57712         function validationPrivateData() {
57713           var type = 'private_data'; // assume that some buildings are private
57714
57715           var privateBuildingValues = {
57716             detached: true,
57717             farm: true,
57718             house: true,
57719             houseboat: true,
57720             residential: true,
57721             semidetached_house: true,
57722             static_caravan: true
57723           }; // but they might be public if they have one of these other tags
57724
57725           var publicKeys = {
57726             amenity: true,
57727             craft: true,
57728             historic: true,
57729             leisure: true,
57730             office: true,
57731             shop: true,
57732             tourism: true
57733           }; // these tags may contain personally identifying info
57734
57735           var personalTags = {
57736             'contact:email': true,
57737             'contact:fax': true,
57738             'contact:phone': true,
57739             email: true,
57740             fax: true,
57741             phone: true
57742           };
57743
57744           var validation = function checkPrivateData(entity) {
57745             var tags = entity.tags;
57746             if (!tags.building || !privateBuildingValues[tags.building]) return [];
57747             var keepTags = {};
57748
57749             for (var k in tags) {
57750               if (publicKeys[k]) return []; // probably a public feature
57751
57752               if (!personalTags[k]) {
57753                 keepTags[k] = tags[k];
57754               }
57755             }
57756
57757             var tagDiff = utilTagDiff(tags, keepTags);
57758             if (!tagDiff.length) return [];
57759             var fixID = tagDiff.length === 1 ? 'remove_tag' : 'remove_tags';
57760             return [new validationIssue({
57761               type: type,
57762               severity: 'warning',
57763               message: showMessage,
57764               reference: showReference,
57765               entityIds: [entity.id],
57766               dynamicFixes: function dynamicFixes() {
57767                 return [new validationIssueFix({
57768                   icon: 'iD-operation-delete',
57769                   title: _t.html('issues.fix.' + fixID + '.title'),
57770                   onClick: function onClick(context) {
57771                     context.perform(doUpgrade, _t('issues.fix.upgrade_tags.annotation'));
57772                   }
57773                 })];
57774               }
57775             })];
57776
57777             function doUpgrade(graph) {
57778               var currEntity = graph.hasEntity(entity.id);
57779               if (!currEntity) return graph;
57780               var newTags = Object.assign({}, currEntity.tags); // shallow copy
57781
57782               tagDiff.forEach(function (diff) {
57783                 if (diff.type === '-') {
57784                   delete newTags[diff.key];
57785                 } else if (diff.type === '+') {
57786                   newTags[diff.key] = diff.newVal;
57787                 }
57788               });
57789               return actionChangeTags(currEntity.id, newTags)(graph);
57790             }
57791
57792             function showMessage(context) {
57793               var currEntity = context.hasEntity(this.entityIds[0]);
57794               if (!currEntity) return '';
57795               return _t.html('issues.private_data.contact.message', {
57796                 feature: utilDisplayLabel(currEntity, context.graph())
57797               });
57798             }
57799
57800             function showReference(selection) {
57801               var enter = selection.selectAll('.issue-reference').data([0]).enter();
57802               enter.append('div').attr('class', 'issue-reference').html(_t.html('issues.private_data.reference'));
57803               enter.append('strong').html(_t.html('issues.suggested'));
57804               enter.append('table').attr('class', 'tagDiff-table').selectAll('.tagDiff-row').data(tagDiff).enter().append('tr').attr('class', 'tagDiff-row').append('td').attr('class', function (d) {
57805                 var klass = d.type === '+' ? 'add' : 'remove';
57806                 return 'tagDiff-cell tagDiff-cell-' + klass;
57807               }).html(function (d) {
57808                 return d.display;
57809               });
57810             }
57811           };
57812
57813           validation.type = type;
57814           return validation;
57815         }
57816
57817         function validationSuspiciousName() {
57818           var type = 'suspicious_name';
57819           var keysToTestForGenericValues = ['aerialway', 'aeroway', 'amenity', 'building', 'craft', 'highway', 'leisure', 'railway', 'man_made', 'office', 'shop', 'tourism', 'waterway'];
57820           var _waitingForNsi = false; // Attempt to match a generic record in the name-suggestion-index.
57821
57822           function isGenericMatchInNsi(tags) {
57823             var nsi = services.nsi;
57824
57825             if (nsi) {
57826               _waitingForNsi = nsi.status() === 'loading';
57827
57828               if (!_waitingForNsi) {
57829                 return nsi.isGenericName(tags);
57830               }
57831             }
57832
57833             return false;
57834           } // Test if the name is just the key or tag value (e.g. "park")
57835
57836
57837           function nameMatchesRawTag(lowercaseName, tags) {
57838             for (var i = 0; i < keysToTestForGenericValues.length; i++) {
57839               var key = keysToTestForGenericValues[i];
57840               var val = tags[key];
57841
57842               if (val) {
57843                 val = val.toLowerCase();
57844
57845                 if (key === lowercaseName || val === lowercaseName || key.replace(/\_/g, ' ') === lowercaseName || val.replace(/\_/g, ' ') === lowercaseName) {
57846                   return true;
57847                 }
57848               }
57849             }
57850
57851             return false;
57852           }
57853
57854           function isGenericName(name, tags) {
57855             name = name.toLowerCase();
57856             return nameMatchesRawTag(name, tags) || isGenericMatchInNsi(tags);
57857           }
57858
57859           function makeGenericNameIssue(entityId, nameKey, genericName, langCode) {
57860             return new validationIssue({
57861               type: type,
57862               subtype: 'generic_name',
57863               severity: 'warning',
57864               message: function message(context) {
57865                 var entity = context.hasEntity(this.entityIds[0]);
57866                 if (!entity) return '';
57867                 var preset = _mainPresetIndex.match(entity, context.graph());
57868                 var langName = langCode && _mainLocalizer.languageName(langCode);
57869                 return _t.html('issues.generic_name.message' + (langName ? '_language' : ''), {
57870                   feature: preset.name(),
57871                   name: genericName,
57872                   language: langName
57873                 });
57874               },
57875               reference: showReference,
57876               entityIds: [entityId],
57877               hash: "".concat(nameKey, "=").concat(genericName),
57878               dynamicFixes: function dynamicFixes() {
57879                 return [new validationIssueFix({
57880                   icon: 'iD-operation-delete',
57881                   title: _t.html('issues.fix.remove_the_name.title'),
57882                   onClick: function onClick(context) {
57883                     var entityId = this.issue.entityIds[0];
57884                     var entity = context.entity(entityId);
57885                     var tags = Object.assign({}, entity.tags); // shallow copy
57886
57887                     delete tags[nameKey];
57888                     context.perform(actionChangeTags(entityId, tags), _t('issues.fix.remove_generic_name.annotation'));
57889                   }
57890                 })];
57891               }
57892             });
57893
57894             function showReference(selection) {
57895               selection.selectAll('.issue-reference').data([0]).enter().append('div').attr('class', 'issue-reference').html(_t.html('issues.generic_name.reference'));
57896             }
57897           }
57898
57899           function makeIncorrectNameIssue(entityId, nameKey, incorrectName, langCode) {
57900             return new validationIssue({
57901               type: type,
57902               subtype: 'not_name',
57903               severity: 'warning',
57904               message: function message(context) {
57905                 var entity = context.hasEntity(this.entityIds[0]);
57906                 if (!entity) return '';
57907                 var preset = _mainPresetIndex.match(entity, context.graph());
57908                 var langName = langCode && _mainLocalizer.languageName(langCode);
57909                 return _t.html('issues.incorrect_name.message' + (langName ? '_language' : ''), {
57910                   feature: preset.name(),
57911                   name: incorrectName,
57912                   language: langName
57913                 });
57914               },
57915               reference: showReference,
57916               entityIds: [entityId],
57917               hash: "".concat(nameKey, "=").concat(incorrectName),
57918               dynamicFixes: function dynamicFixes() {
57919                 return [new validationIssueFix({
57920                   icon: 'iD-operation-delete',
57921                   title: _t.html('issues.fix.remove_the_name.title'),
57922                   onClick: function onClick(context) {
57923                     var entityId = this.issue.entityIds[0];
57924                     var entity = context.entity(entityId);
57925                     var tags = Object.assign({}, entity.tags); // shallow copy
57926
57927                     delete tags[nameKey];
57928                     context.perform(actionChangeTags(entityId, tags), _t('issues.fix.remove_mistaken_name.annotation'));
57929                   }
57930                 })];
57931               }
57932             });
57933
57934             function showReference(selection) {
57935               selection.selectAll('.issue-reference').data([0]).enter().append('div').attr('class', 'issue-reference').html(_t.html('issues.generic_name.reference'));
57936             }
57937           }
57938
57939           var validation = function checkGenericName(entity) {
57940             var tags = entity.tags; // a generic name is allowed if it's a known brand or entity
57941
57942             var hasWikidata = !!tags.wikidata || !!tags['brand:wikidata'] || !!tags['operator:wikidata'];
57943             if (hasWikidata) return [];
57944             var issues = [];
57945             var notNames = (tags['not:name'] || '').split(';');
57946
57947             for (var key in tags) {
57948               var m = key.match(/^name(?:(?::)([a-zA-Z_-]+))?$/);
57949               if (!m) continue;
57950               var langCode = m.length >= 2 ? m[1] : null;
57951               var value = tags[key];
57952
57953               if (notNames.length) {
57954                 for (var i in notNames) {
57955                   var notName = notNames[i];
57956
57957                   if (notName && value === notName) {
57958                     issues.push(makeIncorrectNameIssue(entity.id, key, value, langCode));
57959                     continue;
57960                   }
57961                 }
57962               }
57963
57964               if (isGenericName(value, tags)) {
57965                 issues.provisional = _waitingForNsi; // retry later if we are waiting on NSI to finish loading
57966
57967                 issues.push(makeGenericNameIssue(entity.id, key, value, langCode));
57968               }
57969             }
57970
57971             return issues;
57972           };
57973
57974           validation.type = type;
57975           return validation;
57976         }
57977
57978         function validationUnsquareWay(context) {
57979           var type = 'unsquare_way';
57980           var DEFAULT_DEG_THRESHOLD = 5; // see also issues.js
57981           // use looser epsilon for detection to reduce warnings of buildings that are essentially square already
57982
57983           var epsilon = 0.05;
57984           var nodeThreshold = 10;
57985
57986           function isBuilding(entity, graph) {
57987             if (entity.type !== 'way' || entity.geometry(graph) !== 'area') return false;
57988             return entity.tags.building && entity.tags.building !== 'no';
57989           }
57990
57991           var validation = function checkUnsquareWay(entity, graph) {
57992             if (!isBuilding(entity, graph)) return []; // don't flag ways marked as physically unsquare
57993
57994             if (entity.tags.nonsquare === 'yes') return [];
57995             var isClosed = entity.isClosed();
57996             if (!isClosed) return []; // this building has bigger problems
57997             // don't flag ways with lots of nodes since they are likely detail-mapped
57998
57999             var nodes = graph.childNodes(entity).slice(); // shallow copy
58000
58001             if (nodes.length > nodeThreshold + 1) return []; // +1 because closing node appears twice
58002             // ignore if not all nodes are fully downloaded
58003
58004             var osm = services.osm;
58005             if (!osm || nodes.some(function (node) {
58006               return !osm.isDataLoaded(node.loc);
58007             })) return []; // don't flag connected ways to avoid unresolvable unsquare loops
58008
58009             var hasConnectedSquarableWays = nodes.some(function (node) {
58010               return graph.parentWays(node).some(function (way) {
58011                 if (way.id === entity.id) return false;
58012                 if (isBuilding(way, graph)) return true;
58013                 return graph.parentRelations(way).some(function (parentRelation) {
58014                   return parentRelation.isMultipolygon() && parentRelation.tags.building && parentRelation.tags.building !== 'no';
58015                 });
58016               });
58017             });
58018             if (hasConnectedSquarableWays) return []; // user-configurable square threshold
58019
58020             var storedDegreeThreshold = corePreferences('validate-square-degrees');
58021             var degreeThreshold = isNaN(storedDegreeThreshold) ? DEFAULT_DEG_THRESHOLD : parseFloat(storedDegreeThreshold);
58022             var points = nodes.map(function (node) {
58023               return context.projection(node.loc);
58024             });
58025             if (!geoOrthoCanOrthogonalize(points, isClosed, epsilon, degreeThreshold, true)) return [];
58026             var autoArgs; // don't allow autosquaring features linked to wikidata
58027
58028             if (!entity.tags.wikidata) {
58029               // use same degree threshold as for detection
58030               var autoAction = actionOrthogonalize(entity.id, context.projection, undefined, degreeThreshold);
58031               autoAction.transitionable = false; // when autofixing, do it instantly
58032
58033               autoArgs = [autoAction, _t('operations.orthogonalize.annotation.feature', {
58034                 n: 1
58035               })];
58036             }
58037
58038             return [new validationIssue({
58039               type: type,
58040               subtype: 'building',
58041               severity: 'warning',
58042               message: function message(context) {
58043                 var entity = context.hasEntity(this.entityIds[0]);
58044                 return entity ? _t.html('issues.unsquare_way.message', {
58045                   feature: utilDisplayLabel(entity, context.graph())
58046                 }) : '';
58047               },
58048               reference: showReference,
58049               entityIds: [entity.id],
58050               hash: degreeThreshold,
58051               dynamicFixes: function dynamicFixes() {
58052                 return [new validationIssueFix({
58053                   icon: 'iD-operation-orthogonalize',
58054                   title: _t.html('issues.fix.square_feature.title'),
58055                   autoArgs: autoArgs,
58056                   onClick: function onClick(context, completionHandler) {
58057                     var entityId = this.issue.entityIds[0]; // use same degree threshold as for detection
58058
58059                     context.perform(actionOrthogonalize(entityId, context.projection, undefined, degreeThreshold), _t('operations.orthogonalize.annotation.feature', {
58060                       n: 1
58061                     })); // run after the squaring transition (currently 150ms)
58062
58063                     window.setTimeout(function () {
58064                       completionHandler();
58065                     }, 175);
58066                   }
58067                 })
58068                 /*
58069                 new validationIssueFix({
58070                     title: t.html('issues.fix.tag_as_unsquare.title'),
58071                     onClick: function(context) {
58072                         var entityId = this.issue.entityIds[0];
58073                         var entity = context.entity(entityId);
58074                         var tags = Object.assign({}, entity.tags);  // shallow copy
58075                         tags.nonsquare = 'yes';
58076                         context.perform(
58077                             actionChangeTags(entityId, tags),
58078                             t('issues.fix.tag_as_unsquare.annotation')
58079                         );
58080                     }
58081                 })
58082                 */
58083                 ];
58084               }
58085             })];
58086
58087             function showReference(selection) {
58088               selection.selectAll('.issue-reference').data([0]).enter().append('div').attr('class', 'issue-reference').html(_t.html('issues.unsquare_way.buildings.reference'));
58089             }
58090           };
58091
58092           validation.type = type;
58093           return validation;
58094         }
58095
58096         var Validations = /*#__PURE__*/Object.freeze({
58097                 __proto__: null,
58098                 validationAlmostJunction: validationAlmostJunction,
58099                 validationCloseNodes: validationCloseNodes,
58100                 validationCrossingWays: validationCrossingWays,
58101                 validationDisconnectedWay: validationDisconnectedWay,
58102                 validationFormatting: validationFormatting,
58103                 validationHelpRequest: validationHelpRequest,
58104                 validationImpossibleOneway: validationImpossibleOneway,
58105                 validationIncompatibleSource: validationIncompatibleSource,
58106                 validationMaprules: validationMaprules,
58107                 validationMismatchedGeometry: validationMismatchedGeometry,
58108                 validationMissingRole: validationMissingRole,
58109                 validationMissingTag: validationMissingTag,
58110                 validationOutdatedTags: validationOutdatedTags,
58111                 validationPrivateData: validationPrivateData,
58112                 validationSuspiciousName: validationSuspiciousName,
58113                 validationUnsquareWay: validationUnsquareWay
58114         });
58115
58116         function coreValidator(context) {
58117           var _this = this;
58118
58119           var dispatch = dispatch$8('validated', 'focusedIssue');
58120           var validator = utilRebind({}, dispatch, 'on');
58121           var _rules = {};
58122           var _disabledRules = {};
58123
58124           var _ignoredIssueIDs = new Set();
58125
58126           var _resolvedIssueIDs = new Set();
58127
58128           var _baseCache = validationCache('base'); // issues before any user edits
58129
58130
58131           var _headCache = validationCache('head'); // issues after all user edits
58132
58133
58134           var _completeDiff = {}; // complete diff base -> head of what the user changed
58135
58136           var _headIsCurrent = false;
58137
58138           var _deferredRIC = new Set(); // Set( RequestIdleCallback handles )
58139
58140
58141           var _deferredST = new Set(); // Set( SetTimeout handles )
58142
58143
58144           var _headPromise; // Promise fulfilled when validation is performed up to headGraph snapshot
58145
58146
58147           var RETRY = 5000; // wait 5sec before revalidating provisional entities
58148           // Allow validation severity to be overridden by url queryparams...
58149           // See: https://github.com/openstreetmap/iD/pull/8243
58150           //
58151           // Each param should contain a urlencoded comma separated list of
58152           // `type/subtype` rules.  `*` may be used as a wildcard..
58153           // Examples:
58154           //  `validationError=disconnected_way/*`
58155           //  `validationError=disconnected_way/highway`
58156           //  `validationError=crossing_ways/bridge*`
58157           //  `validationError=crossing_ways/bridge*,crossing_ways/tunnel*`
58158
58159           var _errorOverrides = parseHashParam(context.initialHashParams.validationError);
58160
58161           var _warningOverrides = parseHashParam(context.initialHashParams.validationWarning);
58162
58163           var _disableOverrides = parseHashParam(context.initialHashParams.validationDisable); // `parseHashParam()`   (private)
58164           // Checks hash parameters for severity overrides
58165           // Arguments
58166           //   `param` - a url hash parameter (`validationError`, `validationWarning`, or `validationDisable`)
58167           // Returns
58168           //   Array of Objects like { type: RegExp, subtype: RegExp }
58169           //
58170
58171
58172           function parseHashParam(param) {
58173             var result = [];
58174             var rules = (param || '').split(',');
58175             rules.forEach(function (rule) {
58176               rule = rule.trim();
58177               var parts = rule.split('/', 2); // "type/subtype"
58178
58179               var type = parts[0];
58180               var subtype = parts[1] || '*';
58181               if (!type || !subtype) return;
58182               result.push({
58183                 type: makeRegExp(type),
58184                 subtype: makeRegExp(subtype)
58185               });
58186             });
58187             return result;
58188
58189             function makeRegExp(str) {
58190               var escaped = str.replace(/[-\/\\^$+?.()|[\]{}]/g, '\\$&') // escape all reserved chars except for the '*'
58191               .replace(/\*/g, '.*'); // treat a '*' like '.*'
58192
58193               return new RegExp('^' + escaped + '$');
58194             }
58195           } // `init()`
58196           // Initialize the validator, called once on iD startup
58197           //
58198
58199
58200           validator.init = function () {
58201             Object.values(Validations).forEach(function (validation) {
58202               if (typeof validation !== 'function') return;
58203               var fn = validation(context);
58204               var key = fn.type;
58205               _rules[key] = fn;
58206             });
58207             var disabledRules = corePreferences('validate-disabledRules');
58208
58209             if (disabledRules) {
58210               disabledRules.split(',').forEach(function (k) {
58211                 return _disabledRules[k] = true;
58212               });
58213             }
58214           }; // `reset()`   (private)
58215           // Cancels deferred work and resets all caches
58216           //
58217           // Arguments
58218           //   `resetIgnored` - `true` to clear the list of user-ignored issues
58219           //
58220
58221
58222           function reset(resetIgnored) {
58223             // cancel deferred work
58224             _deferredRIC.forEach(window.cancelIdleCallback);
58225
58226             _deferredRIC.clear();
58227
58228             _deferredST.forEach(window.clearTimeout);
58229
58230             _deferredST.clear(); // empty queues and resolve any pending promise
58231
58232
58233             _baseCache.queue = [];
58234             _headCache.queue = [];
58235             processQueue(_headCache);
58236             processQueue(_baseCache); // clear caches
58237
58238             if (resetIgnored) _ignoredIssueIDs.clear();
58239
58240             _resolvedIssueIDs.clear();
58241
58242             _baseCache = validationCache('base');
58243             _headCache = validationCache('head');
58244             _completeDiff = {};
58245             _headIsCurrent = false;
58246           } // `reset()`
58247           // clear caches, called whenever iD resets after a save or switches sources
58248           // (clears out the _ignoredIssueIDs set also)
58249           //
58250
58251
58252           validator.reset = function () {
58253             reset(true);
58254           }; // `resetIgnoredIssues()`
58255           // clears out the _ignoredIssueIDs Set
58256           //
58257
58258
58259           validator.resetIgnoredIssues = function () {
58260             _ignoredIssueIDs.clear();
58261
58262             dispatch.call('validated'); // redraw UI
58263           }; // `revalidateUnsquare()`
58264           // Called whenever the user changes the unsquare threshold
58265           // It reruns just the "unsquare_way" validation on all buildings.
58266           //
58267
58268
58269           validator.revalidateUnsquare = function () {
58270             revalidateUnsquare(_headCache);
58271             revalidateUnsquare(_baseCache);
58272             dispatch.call('validated');
58273           };
58274
58275           function revalidateUnsquare(cache) {
58276             var checkUnsquareWay = _rules.unsquare_way;
58277             if (!cache.graph || typeof checkUnsquareWay !== 'function') return; // uncache existing
58278
58279             cache.uncacheIssuesOfType('unsquare_way');
58280             var buildings = context.history().tree().intersects(geoExtent([-180, -90], [180, 90]), cache.graph) // everywhere
58281             .filter(function (entity) {
58282               return entity.type === 'way' && entity.tags.building && entity.tags.building !== 'no';
58283             }); // rerun for all buildings
58284
58285             buildings.forEach(function (entity) {
58286               var detected = checkUnsquareWay(entity, cache.graph);
58287               if (!detected.length) return;
58288               cache.cacheIssues(detected);
58289             });
58290           } // `getIssues()`
58291           // Gets all issues that match the given options
58292           // This is called by many other places
58293           //
58294           // Arguments
58295           //   `options` Object like:
58296           //   {
58297           //     what: 'all',                  // 'all' or 'edited'
58298           //     where: 'all',                 // 'all' or 'visible'
58299           //     includeIgnored: false,        // true, false, or 'only'
58300           //     includeDisabledRules: false   // true, false, or 'only'
58301           //   }
58302           //
58303           // Returns
58304           //   An Array containing the issues
58305           //
58306
58307
58308           validator.getIssues = function (options) {
58309             var opts = Object.assign({
58310               what: 'all',
58311               where: 'all',
58312               includeIgnored: false,
58313               includeDisabledRules: false
58314             }, options);
58315             var view = context.map().extent();
58316             var seen = new Set();
58317             var results = []; // collect head issues - caused by user edits
58318
58319             if (_headCache.graph && _headCache.graph !== _baseCache.graph) {
58320               Object.values(_headCache.issuesByIssueID).forEach(function (issue) {
58321                 if (!filter(issue)) return;
58322                 seen.add(issue.id);
58323                 results.push(issue);
58324               });
58325             } // collect base issues - not caused by user edits
58326
58327
58328             if (opts.what === 'all') {
58329               Object.values(_baseCache.issuesByIssueID).forEach(function (issue) {
58330                 if (!filter(issue)) return;
58331                 seen.add(issue.id);
58332                 results.push(issue);
58333               });
58334             }
58335
58336             return results; // Filter the issue set to include only what the calling code wants to see.
58337             // Note that we use `context.graph()`/`context.hasEntity()` here, not `cache.graph`,
58338             // because that is the graph that the calling code will be using.
58339
58340             function filter(issue) {
58341               if (!issue) return false;
58342               if (seen.has(issue.id)) return false;
58343               if (_resolvedIssueIDs.has(issue.id)) return false;
58344               if (opts.includeDisabledRules === 'only' && !_disabledRules[issue.type]) return false;
58345               if (!opts.includeDisabledRules && _disabledRules[issue.type]) return false;
58346               if (opts.includeIgnored === 'only' && !_ignoredIssueIDs.has(issue.id)) return false;
58347               if (!opts.includeIgnored && _ignoredIssueIDs.has(issue.id)) return false; // This issue may involve an entity that doesn't exist in context.graph()
58348               // This can happen because validation is async and rendering the issue lists is async.
58349
58350               if ((issue.entityIds || []).some(function (id) {
58351                 return !context.hasEntity(id);
58352               })) return false;
58353
58354               if (opts.where === 'visible') {
58355                 var extent = issue.extent(context.graph());
58356                 if (!view.intersects(extent)) return false;
58357               }
58358
58359               return true;
58360             }
58361           }; // `getResolvedIssues()`
58362           // Gets the issues that have been fixed by the user.
58363           //
58364           // Resolved issues are tracked in the `_resolvedIssueIDs` Set,
58365           // and they should all be issues that exist in the _baseCache.
58366           //
58367           // Returns
58368           //   An Array containing the issues
58369           //
58370
58371
58372           validator.getResolvedIssues = function () {
58373             return Array.from(_resolvedIssueIDs).map(function (issueID) {
58374               return _baseCache.issuesByIssueID[issueID];
58375             }).filter(Boolean);
58376           }; // `focusIssue()`
58377           // Adjusts the map to focus on the given issue.
58378           // (requires the issue to have a reasonable extent defined)
58379           //
58380           // Arguments
58381           //   `issue` - the issue to focus on
58382           //
58383
58384
58385           validator.focusIssue = function (issue) {
58386             // Note that we use `context.graph()`/`context.hasEntity()` here, not `cache.graph`,
58387             // because that is the graph that the calling code will be using.
58388             var graph = context.graph();
58389             var selectID;
58390             var focusCenter; // Try to focus the map at the center of the issue..
58391
58392             var issueExtent = issue.extent(graph);
58393
58394             if (issueExtent) {
58395               focusCenter = issueExtent.center();
58396             } // Try to select the first entity in the issue..
58397
58398
58399             if (issue.entityIds && issue.entityIds.length) {
58400               selectID = issue.entityIds[0]; // If a relation, focus on one of its members instead.
58401               // Otherwise we might be focusing on a part of map where the relation is not visible.
58402
58403               if (selectID && selectID.charAt(0) === 'r') {
58404                 // relation
58405                 var ids = utilEntityAndDeepMemberIDs([selectID], graph);
58406                 var nodeID = ids.find(function (id) {
58407                   return id.charAt(0) === 'n' && graph.hasEntity(id);
58408                 });
58409
58410                 if (!nodeID) {
58411                   // relation has no downloaded nodes to focus on
58412                   var wayID = ids.find(function (id) {
58413                     return id.charAt(0) === 'w' && graph.hasEntity(id);
58414                   });
58415
58416                   if (wayID) {
58417                     nodeID = graph.entity(wayID).first(); // focus on the first node of this way
58418                   }
58419                 }
58420
58421                 if (nodeID) {
58422                   focusCenter = graph.entity(nodeID).loc;
58423                 }
58424               }
58425             }
58426
58427             if (focusCenter) {
58428               // Adjust the view
58429               var setZoom = Math.max(context.map().zoom(), 19);
58430               context.map().unobscuredCenterZoomEase(focusCenter, setZoom);
58431             }
58432
58433             if (selectID) {
58434               // Enter select mode
58435               window.setTimeout(function () {
58436                 context.enter(modeSelect(context, [selectID]));
58437                 dispatch.call('focusedIssue', _this, issue);
58438               }, 250); // after ease
58439             }
58440           }; // `getIssuesBySeverity()`
58441           // Gets the issues then groups them by error/warning
58442           // (This just calls getIssues, then puts issues in groups)
58443           //
58444           // Arguments
58445           //   `options` - (see `getIssues`)
58446           // Returns
58447           //   Object result like:
58448           //   {
58449           //     error:    Array of errors,
58450           //     warning:  Array of warnings
58451           //   }
58452           //
58453
58454
58455           validator.getIssuesBySeverity = function (options) {
58456             var groups = utilArrayGroupBy(validator.getIssues(options), 'severity');
58457             groups.error = groups.error || [];
58458             groups.warning = groups.warning || [];
58459             return groups;
58460           }; // `getEntityIssues()`
58461           // Gets the issues that the given entity IDs have in common, matching the given options
58462           // (This just calls getIssues, then filters for the given entity IDs)
58463           // The issues are sorted for relevance
58464           //
58465           // Arguments
58466           //   `entityIDs` - Array or Set of entityIDs to get issues for
58467           //   `options` - (see `getIssues`)
58468           // Returns
58469           //   An Array containing the issues
58470           //
58471
58472
58473           validator.getSharedEntityIssues = function (entityIDs, options) {
58474             var orderedIssueTypes = [// Show some issue types in a particular order:
58475             'missing_tag', 'missing_role', // - missing data first
58476             'outdated_tags', 'mismatched_geometry', // - identity issues
58477             'crossing_ways', 'almost_junction', // - geometry issues where fixing them might solve connectivity issues
58478             'disconnected_way', 'impossible_oneway' // - finally connectivity issues
58479             ];
58480             var allIssues = validator.getIssues(options);
58481             var forEntityIDs = new Set(entityIDs);
58482             return allIssues.filter(function (issue) {
58483               return (issue.entityIds || []).some(function (entityID) {
58484                 return forEntityIDs.has(entityID);
58485               });
58486             }).sort(function (issue1, issue2) {
58487               if (issue1.type === issue2.type) {
58488                 // issues of the same type, sort deterministically
58489                 return issue1.id < issue2.id ? -1 : 1;
58490               }
58491
58492               var index1 = orderedIssueTypes.indexOf(issue1.type);
58493               var index2 = orderedIssueTypes.indexOf(issue2.type);
58494
58495               if (index1 !== -1 && index2 !== -1) {
58496                 // both issue types have explicit sort orders
58497                 return index1 - index2;
58498               } else if (index1 === -1 && index2 === -1) {
58499                 // neither issue type has an explicit sort order, sort by type
58500                 return issue1.type < issue2.type ? -1 : 1;
58501               } else {
58502                 // order explicit types before everything else
58503                 return index1 !== -1 ? -1 : 1;
58504               }
58505             });
58506           }; // `getEntityIssues()`
58507           // Get an array of detected issues for the given entityID.
58508           // (This just calls getSharedEntityIssues for a single entity)
58509           //
58510           // Arguments
58511           //   `entityID` - the entity ID to get the issues for
58512           //   `options` - (see `getIssues`)
58513           // Returns
58514           //   An Array containing the issues
58515           //
58516
58517
58518           validator.getEntityIssues = function (entityID, options) {
58519             return validator.getSharedEntityIssues([entityID], options);
58520           }; // `getRuleKeys()`
58521           //
58522           // Returns
58523           //   An Array containing the rule keys
58524           //
58525
58526
58527           validator.getRuleKeys = function () {
58528             return Object.keys(_rules);
58529           }; // `isRuleEnabled()`
58530           //
58531           // Arguments
58532           //   `key` - the rule to check (e.g. 'crossing_ways')
58533           // Returns
58534           //   `true`/`false`
58535           //
58536
58537
58538           validator.isRuleEnabled = function (key) {
58539             return !_disabledRules[key];
58540           }; // `toggleRule()`
58541           // Toggles a single validation rule,
58542           // then reruns the validation so that the user sees something happen in the UI
58543           //
58544           // Arguments
58545           //   `key` - the rule to toggle (e.g. 'crossing_ways')
58546           //
58547
58548
58549           validator.toggleRule = function (key) {
58550             if (_disabledRules[key]) {
58551               delete _disabledRules[key];
58552             } else {
58553               _disabledRules[key] = true;
58554             }
58555
58556             corePreferences('validate-disabledRules', Object.keys(_disabledRules).join(','));
58557             validator.validate();
58558           }; // `disableRules()`
58559           // Disables given validation rules,
58560           // then reruns the validation so that the user sees something happen in the UI
58561           //
58562           // Arguments
58563           //   `keys` - Array or Set containing rule keys to disable
58564           //
58565
58566
58567           validator.disableRules = function (keys) {
58568             _disabledRules = {};
58569             keys.forEach(function (k) {
58570               return _disabledRules[k] = true;
58571             });
58572             corePreferences('validate-disabledRules', Object.keys(_disabledRules).join(','));
58573             validator.validate();
58574           }; // `ignoreIssue()`
58575           // Don't show the given issue in lists
58576           //
58577           // Arguments
58578           //   `issueID` - the issueID
58579           //
58580
58581
58582           validator.ignoreIssue = function (issueID) {
58583             _ignoredIssueIDs.add(issueID);
58584           }; // `validate()`
58585           // Validates anything that has changed in the head graph since the last time it was run.
58586           // (head graph contains user's edits)
58587           //
58588           // Returns
58589           //   A Promise fulfilled when the validation has completed and then dispatches a `validated` event.
58590           //   This may take time but happen in the background during browser idle time.
58591           //
58592
58593
58594           validator.validate = function () {
58595             // Make sure the caches have graphs assigned to them.
58596             // (we don't do this in `reset` because context is still resetting things and `history.base()` is unstable then)
58597             var baseGraph = context.history().base();
58598             if (!_headCache.graph) _headCache.graph = baseGraph;
58599             if (!_baseCache.graph) _baseCache.graph = baseGraph;
58600             var prevGraph = _headCache.graph;
58601             var currGraph = context.graph();
58602
58603             if (currGraph === prevGraph) {
58604               // _headCache.graph is current - we are caught up
58605               _headIsCurrent = true;
58606               dispatch.call('validated');
58607               return Promise.resolve();
58608             }
58609
58610             if (_headPromise) {
58611               // Validation already in process, but we aren't caught up to current
58612               _headIsCurrent = false; // We will need to catch up after the validation promise fulfills
58613
58614               return _headPromise;
58615             } // If we get here, its time to start validating stuff.
58616
58617
58618             _headCache.graph = currGraph; // take snapshot
58619
58620             _completeDiff = context.history().difference().complete();
58621             var incrementalDiff = coreDifference(prevGraph, currGraph);
58622             var entityIDs = Object.keys(incrementalDiff.complete()); // if (!entityIDs.size) {
58623
58624             if (!entityIDs.length) {
58625               dispatch.call('validated');
58626               return Promise.resolve();
58627             }
58628
58629             _headPromise = validateEntitiesAsync(entityIDs, _headCache).then(function () {
58630               return updateResolvedIssues(entityIDs);
58631             }).then(function () {
58632               return dispatch.call('validated');
58633             })["catch"](function () {
58634               /* ignore */
58635             }).then(function () {
58636               _headPromise = null;
58637
58638               if (!_headIsCurrent) {
58639                 validator.validate(); // run it again to catch up to current graph
58640               }
58641             });
58642             return _headPromise;
58643           }; // register event handlers:
58644           // WHEN TO RUN VALIDATION:
58645           // When history changes:
58646
58647
58648           context.history().on('restore.validator', validator.validate) // on restore saved history
58649           .on('undone.validator', validator.validate) // on undo
58650           .on('redone.validator', validator.validate) // on redo
58651           .on('reset.validator', function () {
58652             // on history reset - happens after save, or enter/exit walkthrough
58653             reset(false); // cached issues aren't valid any longer if the history has been reset
58654
58655             validator.validate();
58656           }); // but not on 'change' (e.g. while drawing)
58657           // When user changes editing modes (to catch recent changes e.g. drawing)
58658
58659           context.on('exit.validator', validator.validate); // When merging fetched data, validate base graph:
58660
58661           context.history().on('merge.validator', function (entities) {
58662             if (!entities) return; // Make sure the caches have graphs assigned to them.
58663             // (we don't do this in `reset` because context is still resetting things and `history.base()` is unstable then)
58664
58665             var baseGraph = context.history().base();
58666             if (!_headCache.graph) _headCache.graph = baseGraph;
58667             if (!_baseCache.graph) _baseCache.graph = baseGraph;
58668             var entityIDs = entities.map(function (entity) {
58669               return entity.id;
58670             }); // entityIDs = entityIDsToValidate(entityIDs, baseGraph);  // expand set
58671
58672             validateEntitiesAsync(entityIDs, _baseCache);
58673           }); // `validateEntity()`   (private)
58674           // Runs all validation rules on a single entity.
58675           // Some things to note:
58676           //  - Graph is passed in from whenever the validation was started.  Validators shouldn't use
58677           //   `context.graph()` because this all happens async, and the graph might have changed
58678           //   (for example, nodes getting deleted before the validation can run)
58679           //  - Validator functions may still be waiting on something and return a "provisional" result.
58680           //    In this situation, we will schedule to revalidate the entity sometime later.
58681           //
58682           // Arguments
58683           //   `entity` - The entity
58684           //   `graph` - graph containing the entity
58685           //
58686           // Returns
58687           //   Object result like:
58688           //   {
58689           //     issues:       Array of detected issues
58690           //     provisional:  `true` if provisional result, `false` if final result
58691           //   }
58692           //
58693
58694           function validateEntity(entity, graph) {
58695             var result = {
58696               issues: [],
58697               provisional: false
58698             };
58699             Object.keys(_rules).forEach(runValidation); // run all rules
58700
58701             return result; // runs validation and appends resulting issues
58702
58703             function runValidation(key) {
58704               var fn = _rules[key];
58705
58706               if (typeof fn !== 'function') {
58707                 console.error('no such validation rule = ' + key); // eslint-disable-line no-console
58708
58709                 return;
58710               }
58711
58712               var detected = fn(entity, graph);
58713
58714               if (detected.provisional) {
58715                 // this validation should be run again later
58716                 result.provisional = true;
58717               }
58718
58719               detected = detected.filter(applySeverityOverrides);
58720               result.issues = result.issues.concat(detected); // If there are any override rules that match the issue type/subtype,
58721               // adjust severity (or disable it) and keep/discard as quickly as possible.
58722
58723               function applySeverityOverrides(issue) {
58724                 var type = issue.type;
58725                 var subtype = issue.subtype || '';
58726                 var i;
58727
58728                 for (i = 0; i < _errorOverrides.length; i++) {
58729                   if (_errorOverrides[i].type.test(type) && _errorOverrides[i].subtype.test(subtype)) {
58730                     issue.severity = 'error';
58731                     return true;
58732                   }
58733                 }
58734
58735                 for (i = 0; i < _warningOverrides.length; i++) {
58736                   if (_warningOverrides[i].type.test(type) && _warningOverrides[i].subtype.test(subtype)) {
58737                     issue.severity = 'warning';
58738                     return true;
58739                   }
58740                 }
58741
58742                 for (i = 0; i < _disableOverrides.length; i++) {
58743                   if (_disableOverrides[i].type.test(type) && _disableOverrides[i].subtype.test(subtype)) {
58744                     return false;
58745                   }
58746                 }
58747
58748                 return true;
58749               }
58750             }
58751           } // `updateResolvedIssues()`   (private)
58752           // Determine if any issues were resolved for the given entities.
58753           // This is called by `validate()` after validation of the head graph
58754           //
58755           // Give the user credit for fixing an issue if:
58756           // - the issue is in the base cache
58757           // - the issue is not in the head cache
58758           // - the user did something to one of the entities involved in the issue
58759           //
58760           // Arguments
58761           //   `entityIDs` - Array containing entity IDs.
58762           //
58763
58764
58765           function updateResolvedIssues(entityIDs) {
58766             entityIDs.forEach(function (entityID) {
58767               var baseIssues = _baseCache.issuesByEntityID[entityID];
58768               if (!baseIssues) return;
58769               baseIssues.forEach(function (issueID) {
58770                 // Check if the user did something to one of the entities involved in this issue.
58771                 // (This issue could involve multiple entities, e.g. disconnected routable features)
58772                 var issue = _baseCache.issuesByIssueID[issueID];
58773                 var userModified = (issue.entityIds || []).some(function (id) {
58774                   return _completeDiff.hasOwnProperty(id);
58775                 });
58776
58777                 if (userModified && !_headCache.issuesByIssueID[issueID]) {
58778                   // issue seems fixed
58779                   _resolvedIssueIDs.add(issueID);
58780                 } else {
58781                   // issue still not resolved
58782                   _resolvedIssueIDs["delete"](issueID); // (did undo, or possibly fixed and then re-caused the issue)
58783
58784                 }
58785               });
58786             });
58787           } // `validateEntitiesAsync()`   (private)
58788           // Schedule validation for many entities.
58789           //
58790           // Arguments
58791           //   `entityIDs` - Array containing entity IDs.
58792           //   `graph` - the graph to validate that contains those entities
58793           //   `cache` - the cache to store results in (_headCache or _baseCache)
58794           //
58795           // Returns
58796           //   A Promise fulfilled when the validation has completed.
58797           //   This may take time but happen in the background during browser idle time.
58798           //
58799
58800
58801           function validateEntitiesAsync(entityIDs, cache) {
58802             // Enqueue the work
58803             var jobs = entityIDs.map(function (entityID) {
58804               if (cache.queuedEntityIDs.has(entityID)) return null; // queued already
58805
58806               cache.queuedEntityIDs.add(entityID);
58807               return function () {
58808                 // Clear caches for existing issues related to this entity
58809                 cache.uncacheEntityID(entityID);
58810                 cache.queuedEntityIDs["delete"](entityID);
58811                 var graph = cache.graph;
58812                 if (!graph) return; // was reset?
58813
58814                 var entity = graph.hasEntity(entityID); // Sanity check: don't validate deleted entities
58815
58816                 if (!entity) return; // In the head cache, only validate features that the user is responsible for - #8632
58817                 // For example, a user can undo some work and an issue will still present in the
58818                 // head graph, but we don't want to credit the user for causing that issue.
58819
58820                 if (cache.which === 'head' && !_completeDiff.hasOwnProperty(entityID)) return; // detect new issues and update caches
58821
58822                 var result = validateEntity(entity, graph);
58823
58824                 if (result.provisional) {
58825                   // provisional result
58826                   cache.provisionalEntityIDs.add(entityID); // we'll need to revalidate this entity again later
58827                 }
58828
58829                 cache.cacheIssues(result.issues); // update cache
58830               };
58831             }).filter(Boolean); // Perform the work in chunks.
58832             // Because this will happen during idle callbacks, we want to choose a chunk size
58833             // that won't make the browser stutter too badly.
58834
58835             cache.queue = cache.queue.concat(utilArrayChunk(jobs, 100)); // Perform the work
58836
58837             if (cache.queuePromise) return cache.queuePromise;
58838             cache.queuePromise = processQueue(cache).then(function () {
58839               return revalidateProvisionalEntities(cache);
58840             })["catch"](function () {
58841               /* ignore */
58842             })["finally"](function () {
58843               return cache.queuePromise = null;
58844             });
58845             return cache.queuePromise;
58846           } // `revalidateProvisionalEntities()`   (private)
58847           // Sometimes a validator will return a "provisional" result.
58848           // In this situation, we'll need to revalidate the entity later.
58849           // This function waits a delay, then places them back into the validation queue.
58850           //
58851           // Arguments
58852           //   `cache` - The cache (_headCache or _baseCache)
58853           //
58854
58855
58856           function revalidateProvisionalEntities(cache) {
58857             if (!cache.provisionalEntityIDs.size) return; // nothing to do
58858
58859             var handle = window.setTimeout(function () {
58860               _deferredST["delete"](handle);
58861
58862               if (!cache.provisionalEntityIDs.size) return; // nothing to do
58863
58864               validateEntitiesAsync(Array.from(cache.provisionalEntityIDs), cache);
58865             }, RETRY);
58866
58867             _deferredST.add(handle);
58868           } // `processQueue(queue)`   (private)
58869           // Process the next chunk of deferred validation work
58870           //
58871           // Arguments
58872           //   `cache` - The cache (_headCache or _baseCache)
58873           //
58874           // Returns
58875           //   A Promise fulfilled when the validation has completed.
58876           //   This may take time but happen in the background during browser idle time.
58877           //
58878
58879
58880           function processQueue(cache) {
58881             // console.log(`${cache.which} queue length ${cache.queue.length}`);
58882             if (!cache.queue.length) return Promise.resolve(); // we're done
58883
58884             var chunk = cache.queue.pop();
58885             return new Promise(function (resolvePromise) {
58886               var handle = window.requestIdleCallback(function () {
58887                 _deferredRIC["delete"](handle); // const t0 = performance.now();
58888
58889
58890                 chunk.forEach(function (job) {
58891                   return job();
58892                 }); // const t1 = performance.now();
58893                 // console.log('chunk processed in ' + (t1 - t0) + ' ms');
58894
58895                 resolvePromise();
58896               });
58897
58898               _deferredRIC.add(handle);
58899             }).then(function () {
58900               // dispatch an event sometimes to redraw various UI things
58901               if (cache.queue.length % 25 === 0) dispatch.call('validated');
58902             }).then(function () {
58903               return processQueue(cache);
58904             });
58905           }
58906
58907           return validator;
58908         } // `validationCache()`   (private)
58909         // Creates a cache to store validation state
58910         // We create 2 of these:
58911         //   `_baseCache` for validation on the base graph (unedited)
58912         //   `_headCache` for validation on the head graph (user edits applied)
58913         //
58914         // Arguments
58915         //   `which` - just a String 'base' or 'head' to keep track of it
58916         //
58917
58918         function validationCache(which) {
58919           var cache = {
58920             which: which,
58921             graph: null,
58922             queue: [],
58923             queuePromise: null,
58924             queuedEntityIDs: new Set(),
58925             provisionalEntityIDs: new Set(),
58926             issuesByIssueID: {},
58927             // issue.id -> issue
58928             issuesByEntityID: {} // entity.id -> Set(issue.id)
58929
58930           };
58931
58932           cache.cacheIssues = function (issues) {
58933             issues.forEach(function (issue) {
58934               var entityIDs = issue.entityIds || [];
58935               entityIDs.forEach(function (entityID) {
58936                 if (!cache.issuesByEntityID[entityID]) {
58937                   cache.issuesByEntityID[entityID] = new Set();
58938                 }
58939
58940                 cache.issuesByEntityID[entityID].add(issue.id);
58941               });
58942               cache.issuesByIssueID[issue.id] = issue;
58943             });
58944           };
58945
58946           cache.uncacheIssue = function (issue) {
58947             // When multiple entities are involved (e.g. crossing_ways),
58948             // remove this issue from the other entity caches too..
58949             var entityIDs = issue.entityIds || [];
58950             entityIDs.forEach(function (entityID) {
58951               if (cache.issuesByEntityID[entityID]) {
58952                 cache.issuesByEntityID[entityID]["delete"](issue.id);
58953               }
58954             });
58955             delete cache.issuesByIssueID[issue.id];
58956           };
58957
58958           cache.uncacheIssues = function (issues) {
58959             issues.forEach(cache.uncacheIssue);
58960           };
58961
58962           cache.uncacheIssuesOfType = function (type) {
58963             var issuesOfType = Object.values(cache.issuesByIssueID).filter(function (issue) {
58964               return issue.type === type;
58965             });
58966             cache.uncacheIssues(issuesOfType);
58967           }; // Remove a single entity and all its related issues from the caches
58968
58969
58970           cache.uncacheEntityID = function (entityID) {
58971             var issueIDs = cache.issuesByEntityID[entityID];
58972
58973             if (issueIDs) {
58974               issueIDs.forEach(function (issueID) {
58975                 var issue = cache.issuesByIssueID[issueID];
58976
58977                 if (issue) {
58978                   cache.uncacheIssue(issue);
58979                 } else {
58980                   delete cache.issuesByIssueID[issueID];
58981                 }
58982               });
58983             }
58984
58985             delete cache.issuesByEntityID[entityID];
58986             cache.provisionalEntityIDs["delete"](entityID);
58987           };
58988
58989           return cache;
58990         }
58991
58992         function coreUploader(context) {
58993           var dispatch = dispatch$8( // Start and end events are dispatched exactly once each per legitimate outside call to `save`
58994           'saveStarted', // dispatched as soon as a call to `save` has been deemed legitimate
58995           'saveEnded', // dispatched after the result event has been dispatched
58996           'willAttemptUpload', // dispatched before the actual upload call occurs, if it will
58997           'progressChanged', // Each save results in one of these outcomes:
58998           'resultNoChanges', // upload wasn't attempted since there were no edits
58999           'resultErrors', // upload failed due to errors
59000           'resultConflicts', // upload failed due to data conflicts
59001           'resultSuccess' // upload completed without errors
59002           );
59003           var _isSaving = false;
59004           var _conflicts = [];
59005           var _errors = [];
59006
59007           var _origChanges;
59008
59009           var _discardTags = {};
59010           _mainFileFetcher.get('discarded').then(function (d) {
59011             _discardTags = d;
59012           })["catch"](function () {
59013             /* ignore */
59014           });
59015           var uploader = utilRebind({}, dispatch, 'on');
59016
59017           uploader.isSaving = function () {
59018             return _isSaving;
59019           };
59020
59021           uploader.save = function (changeset, tryAgain, checkConflicts) {
59022             // Guard against accidentally entering save code twice - #4641
59023             if (_isSaving && !tryAgain) {
59024               return;
59025             }
59026
59027             var osm = context.connection();
59028             if (!osm) return; // If user somehow got logged out mid-save, try to reauthenticate..
59029             // This can happen if they were logged in from before, but the tokens are no longer valid.
59030
59031             if (!osm.authenticated()) {
59032               osm.authenticate(function (err) {
59033                 if (!err) {
59034                   uploader.save(changeset, tryAgain, checkConflicts); // continue where we left off..
59035                 }
59036               });
59037               return;
59038             }
59039
59040             if (!_isSaving) {
59041               _isSaving = true;
59042               dispatch.call('saveStarted', this);
59043             }
59044
59045             var history = context.history();
59046             _conflicts = [];
59047             _errors = []; // Store original changes, in case user wants to download them as an .osc file
59048
59049             _origChanges = history.changes(actionDiscardTags(history.difference(), _discardTags)); // First time, `history.perform` a no-op action.
59050             // Any conflict resolutions will be done as `history.replace`
59051             // Remember to pop this later if needed
59052
59053             if (!tryAgain) {
59054               history.perform(actionNoop());
59055             } // Attempt a fast upload.. If there are conflicts, re-enter with `checkConflicts = true`
59056
59057
59058             if (!checkConflicts) {
59059               upload(changeset); // Do the full (slow) conflict check..
59060             } else {
59061               performFullConflictCheck(changeset);
59062             }
59063           };
59064
59065           function performFullConflictCheck(changeset) {
59066             var osm = context.connection();
59067             if (!osm) return;
59068             var history = context.history();
59069             var localGraph = context.graph();
59070             var remoteGraph = coreGraph(history.base(), true);
59071             var summary = history.difference().summary();
59072             var _toCheck = [];
59073
59074             for (var i = 0; i < summary.length; i++) {
59075               var item = summary[i];
59076
59077               if (item.changeType === 'modified') {
59078                 _toCheck.push(item.entity.id);
59079               }
59080             }
59081
59082             var _toLoad = withChildNodes(_toCheck, localGraph);
59083
59084             var _loaded = {};
59085             var _toLoadCount = 0;
59086             var _toLoadTotal = _toLoad.length;
59087
59088             if (_toCheck.length) {
59089               dispatch.call('progressChanged', this, _toLoadCount, _toLoadTotal);
59090
59091               _toLoad.forEach(function (id) {
59092                 _loaded[id] = false;
59093               });
59094
59095               osm.loadMultiple(_toLoad, loaded);
59096             } else {
59097               upload(changeset);
59098             }
59099
59100             return;
59101
59102             function withChildNodes(ids, graph) {
59103               var s = new Set(ids);
59104               ids.forEach(function (id) {
59105                 var entity = graph.entity(id);
59106                 if (entity.type !== 'way') return;
59107                 graph.childNodes(entity).forEach(function (child) {
59108                   if (child.version !== undefined) {
59109                     s.add(child.id);
59110                   }
59111                 });
59112               });
59113               return Array.from(s);
59114             } // Reload modified entities into an alternate graph and check for conflicts..
59115
59116
59117             function loaded(err, result) {
59118               if (_errors.length) return;
59119
59120               if (err) {
59121                 _errors.push({
59122                   msg: err.message || err.responseText,
59123                   details: [_t('save.status_code', {
59124                     code: err.status
59125                   })]
59126                 });
59127
59128                 didResultInErrors();
59129               } else {
59130                 var loadMore = [];
59131                 result.data.forEach(function (entity) {
59132                   remoteGraph.replace(entity);
59133                   _loaded[entity.id] = true;
59134                   _toLoad = _toLoad.filter(function (val) {
59135                     return val !== entity.id;
59136                   });
59137                   if (!entity.visible) return; // Because loadMultiple doesn't download /full like loadEntity,
59138                   // need to also load children that aren't already being checked..
59139
59140                   var i, id;
59141
59142                   if (entity.type === 'way') {
59143                     for (i = 0; i < entity.nodes.length; i++) {
59144                       id = entity.nodes[i];
59145
59146                       if (_loaded[id] === undefined) {
59147                         _loaded[id] = false;
59148                         loadMore.push(id);
59149                       }
59150                     }
59151                   } else if (entity.type === 'relation' && entity.isMultipolygon()) {
59152                     for (i = 0; i < entity.members.length; i++) {
59153                       id = entity.members[i].id;
59154
59155                       if (_loaded[id] === undefined) {
59156                         _loaded[id] = false;
59157                         loadMore.push(id);
59158                       }
59159                     }
59160                   }
59161                 });
59162                 _toLoadCount += result.data.length;
59163                 _toLoadTotal += loadMore.length;
59164                 dispatch.call('progressChanged', this, _toLoadCount, _toLoadTotal);
59165
59166                 if (loadMore.length) {
59167                   _toLoad.push.apply(_toLoad, loadMore);
59168
59169                   osm.loadMultiple(loadMore, loaded);
59170                 }
59171
59172                 if (!_toLoad.length) {
59173                   detectConflicts();
59174                   upload(changeset);
59175                 }
59176               }
59177             }
59178
59179             function detectConflicts() {
59180               function choice(id, text, _action) {
59181                 return {
59182                   id: id,
59183                   text: text,
59184                   action: function action() {
59185                     history.replace(_action);
59186                   }
59187                 };
59188               }
59189
59190               function formatUser(d) {
59191                 return '<a href="' + osm.userURL(d) + '" target="_blank">' + d + '</a>';
59192               }
59193
59194               function entityName(entity) {
59195                 return utilDisplayName(entity) || utilDisplayType(entity.id) + ' ' + entity.id;
59196               }
59197
59198               function sameVersions(local, remote) {
59199                 if (local.version !== remote.version) return false;
59200
59201                 if (local.type === 'way') {
59202                   var children = utilArrayUnion(local.nodes, remote.nodes);
59203
59204                   for (var i = 0; i < children.length; i++) {
59205                     var a = localGraph.hasEntity(children[i]);
59206                     var b = remoteGraph.hasEntity(children[i]);
59207                     if (a && b && a.version !== b.version) return false;
59208                   }
59209                 }
59210
59211                 return true;
59212               }
59213
59214               _toCheck.forEach(function (id) {
59215                 var local = localGraph.entity(id);
59216                 var remote = remoteGraph.entity(id);
59217                 if (sameVersions(local, remote)) return;
59218                 var merge = actionMergeRemoteChanges(id, localGraph, remoteGraph, _discardTags, formatUser);
59219                 history.replace(merge);
59220                 var mergeConflicts = merge.conflicts();
59221                 if (!mergeConflicts.length) return; // merged safely
59222
59223                 var forceLocal = actionMergeRemoteChanges(id, localGraph, remoteGraph, _discardTags).withOption('force_local');
59224                 var forceRemote = actionMergeRemoteChanges(id, localGraph, remoteGraph, _discardTags).withOption('force_remote');
59225                 var keepMine = _t('save.conflict.' + (remote.visible ? 'keep_local' : 'restore'));
59226                 var keepTheirs = _t('save.conflict.' + (remote.visible ? 'keep_remote' : 'delete'));
59227
59228                 _conflicts.push({
59229                   id: id,
59230                   name: entityName(local),
59231                   details: mergeConflicts,
59232                   chosen: 1,
59233                   choices: [choice(id, keepMine, forceLocal), choice(id, keepTheirs, forceRemote)]
59234                 });
59235               });
59236             }
59237           }
59238
59239           function upload(changeset) {
59240             var osm = context.connection();
59241
59242             if (!osm) {
59243               _errors.push({
59244                 msg: 'No OSM Service'
59245               });
59246             }
59247
59248             if (_conflicts.length) {
59249               didResultInConflicts(changeset);
59250             } else if (_errors.length) {
59251               didResultInErrors();
59252             } else {
59253               var history = context.history();
59254               var changes = history.changes(actionDiscardTags(history.difference(), _discardTags));
59255
59256               if (changes.modified.length || changes.created.length || changes.deleted.length) {
59257                 dispatch.call('willAttemptUpload', this);
59258                 osm.putChangeset(changeset, changes, uploadCallback);
59259               } else {
59260                 // changes were insignificant or reverted by user
59261                 didResultInNoChanges();
59262               }
59263             }
59264           }
59265
59266           function uploadCallback(err, changeset) {
59267             if (err) {
59268               if (err.status === 409) {
59269                 // 409 Conflict
59270                 uploader.save(changeset, true, true); // tryAgain = true, checkConflicts = true
59271               } else {
59272                 _errors.push({
59273                   msg: err.message || err.responseText,
59274                   details: [_t('save.status_code', {
59275                     code: err.status
59276                   })]
59277                 });
59278
59279                 didResultInErrors();
59280               }
59281             } else {
59282               didResultInSuccess(changeset);
59283             }
59284           }
59285
59286           function didResultInNoChanges() {
59287             dispatch.call('resultNoChanges', this);
59288             endSave();
59289             context.flush(); // reset iD
59290           }
59291
59292           function didResultInErrors() {
59293             context.history().pop();
59294             dispatch.call('resultErrors', this, _errors);
59295             endSave();
59296           }
59297
59298           function didResultInConflicts(changeset) {
59299             _conflicts.sort(function (a, b) {
59300               return b.id.localeCompare(a.id);
59301             });
59302
59303             dispatch.call('resultConflicts', this, changeset, _conflicts, _origChanges);
59304             endSave();
59305           }
59306
59307           function didResultInSuccess(changeset) {
59308             // delete the edit stack cached to local storage
59309             context.history().clearSaved();
59310             dispatch.call('resultSuccess', this, changeset); // Add delay to allow for postgres replication #1646 #2678
59311
59312             window.setTimeout(function () {
59313               endSave();
59314               context.flush(); // reset iD
59315             }, 2500);
59316           }
59317
59318           function endSave() {
59319             _isSaving = false;
59320             dispatch.call('saveEnded', this);
59321           }
59322
59323           uploader.cancelConflictResolution = function () {
59324             context.history().pop();
59325           };
59326
59327           uploader.processResolvedConflicts = function (changeset) {
59328             var history = context.history();
59329
59330             for (var i = 0; i < _conflicts.length; i++) {
59331               if (_conflicts[i].chosen === 1) {
59332                 // user chose "use theirs"
59333                 var entity = context.hasEntity(_conflicts[i].id);
59334
59335                 if (entity && entity.type === 'way') {
59336                   var children = utilArrayUniq(entity.nodes);
59337
59338                   for (var j = 0; j < children.length; j++) {
59339                     history.replace(actionRevert(children[j]));
59340                   }
59341                 }
59342
59343                 history.replace(actionRevert(_conflicts[i].id));
59344               }
59345             }
59346
59347             uploader.save(changeset, true, false); // tryAgain = true, checkConflicts = false
59348           };
59349
59350           uploader.reset = function () {};
59351
59352           return uploader;
59353         }
59354
59355         var abs = Math.abs;
59356         var exp = Math.exp;
59357         var E = Math.E;
59358
59359         var FORCED = fails(function () {
59360           // eslint-disable-next-line es/no-math-sinh -- required for testing
59361           return Math.sinh(-2e-17) != -2e-17;
59362         });
59363
59364         // `Math.sinh` method
59365         // https://tc39.es/ecma262/#sec-math.sinh
59366         // V8 near Chromium 38 has a problem with very small numbers
59367         _export({ target: 'Math', stat: true, forced: FORCED }, {
59368           sinh: function sinh(x) {
59369             return abs(x = +x) < 1 ? (mathExpm1(x) - mathExpm1(-x)) / 2 : (exp(x - 1) - exp(-x - 1)) * (E / 2);
59370           }
59371         });
59372
59373         var isRetina = window.devicePixelRatio && window.devicePixelRatio >= 2; // listen for DPI change, e.g. when dragging a browser window from a retina to non-retina screen
59374
59375         window.matchMedia("\n        (-webkit-min-device-pixel-ratio: 2), /* Safari */\n        (min-resolution: 2dppx),             /* standard */\n        (min-resolution: 192dpi)             /* fallback */\n    ").addListener(function () {
59376           isRetina = window.devicePixelRatio && window.devicePixelRatio >= 2;
59377         });
59378
59379         function localeDateString(s) {
59380           if (!s) return null;
59381           var options = {
59382             day: 'numeric',
59383             month: 'short',
59384             year: 'numeric'
59385           };
59386           var d = new Date(s);
59387           if (isNaN(d.getTime())) return null;
59388           return d.toLocaleDateString(_mainLocalizer.localeCode(), options);
59389         }
59390
59391         function vintageRange(vintage) {
59392           var s;
59393
59394           if (vintage.start || vintage.end) {
59395             s = vintage.start || '?';
59396
59397             if (vintage.start !== vintage.end) {
59398               s += ' - ' + (vintage.end || '?');
59399             }
59400           }
59401
59402           return s;
59403         }
59404
59405         function rendererBackgroundSource(data) {
59406           var source = Object.assign({}, data); // shallow copy
59407
59408           var _offset = [0, 0];
59409           var _name = source.name;
59410           var _description = source.description;
59411
59412           var _best = !!source.best;
59413
59414           var _template = source.encrypted ? utilAesDecrypt(source.template) : source.template;
59415
59416           source.tileSize = data.tileSize || 256;
59417           source.zoomExtent = data.zoomExtent || [0, 22];
59418           source.overzoom = data.overzoom !== false;
59419
59420           source.offset = function (val) {
59421             if (!arguments.length) return _offset;
59422             _offset = val;
59423             return source;
59424           };
59425
59426           source.nudge = function (val, zoomlevel) {
59427             _offset[0] += val[0] / Math.pow(2, zoomlevel);
59428             _offset[1] += val[1] / Math.pow(2, zoomlevel);
59429             return source;
59430           };
59431
59432           source.name = function () {
59433             var id_safe = source.id.replace(/\./g, '<TX_DOT>');
59434             return _t('imagery.' + id_safe + '.name', {
59435               "default": _name
59436             });
59437           };
59438
59439           source.label = function () {
59440             var id_safe = source.id.replace(/\./g, '<TX_DOT>');
59441             return _t.html('imagery.' + id_safe + '.name', {
59442               "default": _name
59443             });
59444           };
59445
59446           source.description = function () {
59447             var id_safe = source.id.replace(/\./g, '<TX_DOT>');
59448             return _t.html('imagery.' + id_safe + '.description', {
59449               "default": _description
59450             });
59451           };
59452
59453           source.best = function () {
59454             return _best;
59455           };
59456
59457           source.area = function () {
59458             if (!data.polygon) return Number.MAX_VALUE; // worldwide
59459
59460             var area = d3_geoArea({
59461               type: 'MultiPolygon',
59462               coordinates: [data.polygon]
59463             });
59464             return isNaN(area) ? 0 : area;
59465           };
59466
59467           source.imageryUsed = function () {
59468             return _name || source.id;
59469           };
59470
59471           source.template = function (val) {
59472             if (!arguments.length) return _template;
59473
59474             if (source.id === 'custom' || source.id === 'Bing') {
59475               _template = val;
59476             }
59477
59478             return source;
59479           };
59480
59481           source.url = function (coord) {
59482             var result = _template;
59483             if (result === '') return result; // source 'none'
59484             // Guess a type based on the tokens present in the template
59485             // (This is for 'custom' source, where we don't know)
59486
59487             if (!source.type) {
59488               if (/SERVICE=WMS|\{(proj|wkid|bbox)\}/.test(_template)) {
59489                 source.type = 'wms';
59490                 source.projection = 'EPSG:3857'; // guess
59491               } else if (/\{(x|y)\}/.test(_template)) {
59492                 source.type = 'tms';
59493               } else if (/\{u\}/.test(_template)) {
59494                 source.type = 'bing';
59495               }
59496             }
59497
59498             if (source.type === 'wms') {
59499               var tileToProjectedCoords = function tileToProjectedCoords(x, y, z) {
59500                 //polyfill for IE11, PhantomJS
59501                 var sinh = Math.sinh || function (x) {
59502                   var y = Math.exp(x);
59503                   return (y - 1 / y) / 2;
59504                 };
59505
59506                 var zoomSize = Math.pow(2, z);
59507                 var lon = x / zoomSize * Math.PI * 2 - Math.PI;
59508                 var lat = Math.atan(sinh(Math.PI * (1 - 2 * y / zoomSize)));
59509
59510                 switch (source.projection) {
59511                   case 'EPSG:4326':
59512                     return {
59513                       x: lon * 180 / Math.PI,
59514                       y: lat * 180 / Math.PI
59515                     };
59516
59517                   default:
59518                     // EPSG:3857 and synonyms
59519                     var mercCoords = mercatorRaw(lon, lat);
59520                     return {
59521                       x: 20037508.34 / Math.PI * mercCoords[0],
59522                       y: 20037508.34 / Math.PI * mercCoords[1]
59523                     };
59524                 }
59525               };
59526
59527               var tileSize = source.tileSize;
59528               var projection = source.projection;
59529               var minXmaxY = tileToProjectedCoords(coord[0], coord[1], coord[2]);
59530               var maxXminY = tileToProjectedCoords(coord[0] + 1, coord[1] + 1, coord[2]);
59531               result = result.replace(/\{(\w+)\}/g, function (token, key) {
59532                 switch (key) {
59533                   case 'width':
59534                   case 'height':
59535                     return tileSize;
59536
59537                   case 'proj':
59538                     return projection;
59539
59540                   case 'wkid':
59541                     return projection.replace(/^EPSG:/, '');
59542
59543                   case 'bbox':
59544                     // WMS 1.3 flips x/y for some coordinate systems including EPSG:4326 - #7557
59545                     if (projection === 'EPSG:4326' && // The CRS parameter implies version 1.3 (prior versions use SRS)
59546                     /VERSION=1.3|CRS={proj}/.test(source.template().toUpperCase())) {
59547                       return maxXminY.y + ',' + minXmaxY.x + ',' + minXmaxY.y + ',' + maxXminY.x;
59548                     } else {
59549                       return minXmaxY.x + ',' + maxXminY.y + ',' + maxXminY.x + ',' + minXmaxY.y;
59550                     }
59551
59552                   case 'w':
59553                     return minXmaxY.x;
59554
59555                   case 's':
59556                     return maxXminY.y;
59557
59558                   case 'n':
59559                     return maxXminY.x;
59560
59561                   case 'e':
59562                     return minXmaxY.y;
59563
59564                   default:
59565                     return token;
59566                 }
59567               });
59568             } else if (source.type === 'tms') {
59569               result = result.replace('{x}', coord[0]).replace('{y}', coord[1]) // TMS-flipped y coordinate
59570               .replace(/\{[t-]y\}/, Math.pow(2, coord[2]) - coord[1] - 1).replace(/\{z(oom)?\}/, coord[2]) // only fetch retina tiles for retina screens
59571               .replace(/\{@2x\}|\{r\}/, isRetina ? '@2x' : '');
59572             } else if (source.type === 'bing') {
59573               result = result.replace('{u}', function () {
59574                 var u = '';
59575
59576                 for (var zoom = coord[2]; zoom > 0; zoom--) {
59577                   var b = 0;
59578                   var mask = 1 << zoom - 1;
59579                   if ((coord[0] & mask) !== 0) b++;
59580                   if ((coord[1] & mask) !== 0) b += 2;
59581                   u += b.toString();
59582                 }
59583
59584                 return u;
59585               });
59586             } // these apply to any type..
59587
59588
59589             result = result.replace(/\{switch:([^}]+)\}/, function (s, r) {
59590               var subdomains = r.split(',');
59591               return subdomains[(coord[0] + coord[1]) % subdomains.length];
59592             });
59593             return result;
59594           };
59595
59596           source.validZoom = function (z) {
59597             return source.zoomExtent[0] <= z && (source.overzoom || source.zoomExtent[1] > z);
59598           };
59599
59600           source.isLocatorOverlay = function () {
59601             return source.id === 'mapbox_locator_overlay';
59602           };
59603           /* hides a source from the list, but leaves it available for use */
59604
59605
59606           source.isHidden = function () {
59607             return source.id === 'DigitalGlobe-Premium-vintage' || source.id === 'DigitalGlobe-Standard-vintage';
59608           };
59609
59610           source.copyrightNotices = function () {};
59611
59612           source.getMetadata = function (center, tileCoord, callback) {
59613             var vintage = {
59614               start: localeDateString(source.startDate),
59615               end: localeDateString(source.endDate)
59616             };
59617             vintage.range = vintageRange(vintage);
59618             var metadata = {
59619               vintage: vintage
59620             };
59621             callback(null, metadata);
59622           };
59623
59624           return source;
59625         }
59626
59627         rendererBackgroundSource.Bing = function (data, dispatch) {
59628           // https://docs.microsoft.com/en-us/bingmaps/rest-services/imagery/get-imagery-metadata
59629           // https://docs.microsoft.com/en-us/bingmaps/rest-services/directly-accessing-the-bing-maps-tiles
59630           //fallback url template
59631           data.template = 'https://ecn.t{switch:0,1,2,3}.tiles.virtualearth.net/tiles/a{u}.jpeg?g=587&n=z';
59632           var bing = rendererBackgroundSource(data); //var key = 'Arzdiw4nlOJzRwOz__qailc8NiR31Tt51dN2D7cm57NrnceZnCpgOkmJhNpGoppU'; // P2, JOSM, etc
59633
59634           var key = 'Ak5oTE46TUbjRp08OFVcGpkARErDobfpuyNKa-W2mQ8wbt1K1KL8p1bIRwWwcF-Q'; // iD
59635
59636           /*
59637           missing tile image strictness param (n=)
59638           •   n=f -> (Fail) returns a 404
59639           •   n=z -> (Empty) returns a 200 with 0 bytes (no content)
59640           •   n=t -> (Transparent) returns a 200 with a transparent (png) tile
59641           */
59642
59643           var strictParam = 'n';
59644           var url = 'https://dev.virtualearth.net/REST/v1/Imagery/Metadata/Aerial?include=ImageryProviders&uriScheme=https&key=' + key;
59645           var cache = {};
59646           var inflight = {};
59647           var providers = [];
59648           d3_json(url).then(function (json) {
59649             var imageryResource = json.resourceSets[0].resources[0]; //retrieve and prepare up to date imagery template
59650
59651             var template = imageryResource.imageUrl; //https://ecn.{subdomain}.tiles.virtualearth.net/tiles/a{quadkey}.jpeg?g=10339
59652
59653             var subDomains = imageryResource.imageUrlSubdomains; //["t0, t1, t2, t3"]
59654
59655             var subDomainNumbers = subDomains.map(function (subDomain) {
59656               return subDomain.substring(1);
59657             }).join(',');
59658             template = template.replace('{subdomain}', "t{switch:".concat(subDomainNumbers, "}")).replace('{quadkey}', '{u}');
59659
59660             if (!new URLSearchParams(template).has(strictParam)) {
59661               template += "&".concat(strictParam, "=z");
59662             }
59663
59664             bing.template(template);
59665             providers = imageryResource.imageryProviders.map(function (provider) {
59666               return {
59667                 attribution: provider.attribution,
59668                 areas: provider.coverageAreas.map(function (area) {
59669                   return {
59670                     zoom: [area.zoomMin, area.zoomMax],
59671                     extent: geoExtent([area.bbox[1], area.bbox[0]], [area.bbox[3], area.bbox[2]])
59672                   };
59673                 })
59674               };
59675             });
59676             dispatch.call('change');
59677           })["catch"](function () {
59678             /* ignore */
59679           });
59680
59681           bing.copyrightNotices = function (zoom, extent) {
59682             zoom = Math.min(zoom, 21);
59683             return providers.filter(function (provider) {
59684               return provider.areas.some(function (area) {
59685                 return extent.intersects(area.extent) && area.zoom[0] <= zoom && area.zoom[1] >= zoom;
59686               });
59687             }).map(function (provider) {
59688               return provider.attribution;
59689             }).join(', ');
59690           };
59691
59692           bing.getMetadata = function (center, tileCoord, callback) {
59693             var tileID = tileCoord.slice(0, 3).join('/');
59694             var zoom = Math.min(tileCoord[2], 21);
59695             var centerPoint = center[1] + ',' + center[0]; // lat,lng
59696
59697             var url = 'https://dev.virtualearth.net/REST/v1/Imagery/Metadata/Aerial/' + centerPoint + '?zl=' + zoom + '&key=' + key;
59698             if (inflight[tileID]) return;
59699
59700             if (!cache[tileID]) {
59701               cache[tileID] = {};
59702             }
59703
59704             if (cache[tileID] && cache[tileID].metadata) {
59705               return callback(null, cache[tileID].metadata);
59706             }
59707
59708             inflight[tileID] = true;
59709             d3_json(url).then(function (result) {
59710               delete inflight[tileID];
59711
59712               if (!result) {
59713                 throw new Error('Unknown Error');
59714               }
59715
59716               var vintage = {
59717                 start: localeDateString(result.resourceSets[0].resources[0].vintageStart),
59718                 end: localeDateString(result.resourceSets[0].resources[0].vintageEnd)
59719               };
59720               vintage.range = vintageRange(vintage);
59721               var metadata = {
59722                 vintage: vintage
59723               };
59724               cache[tileID].metadata = metadata;
59725               if (callback) callback(null, metadata);
59726             })["catch"](function (err) {
59727               delete inflight[tileID];
59728               if (callback) callback(err.message);
59729             });
59730           };
59731
59732           bing.terms_url = 'https://blog.openstreetmap.org/2010/11/30/microsoft-imagery-details';
59733           return bing;
59734         };
59735
59736         rendererBackgroundSource.Esri = function (data) {
59737           // in addition to using the tilemap at zoom level 20, overzoom real tiles - #4327 (deprecated technique, but it works)
59738           if (data.template.match(/blankTile/) === null) {
59739             data.template = data.template + '?blankTile=false';
59740           }
59741
59742           var esri = rendererBackgroundSource(data);
59743           var cache = {};
59744           var inflight = {};
59745
59746           var _prevCenter; // use a tilemap service to set maximum zoom for esri tiles dynamically
59747           // https://developers.arcgis.com/documentation/tiled-elevation-service/
59748
59749
59750           esri.fetchTilemap = function (center) {
59751             // skip if we have already fetched a tilemap within 5km
59752             if (_prevCenter && geoSphericalDistance(center, _prevCenter) < 5000) return;
59753             _prevCenter = center; // tiles are available globally to zoom level 19, afterward they may or may not be present
59754
59755             var z = 20; // first generate a random url using the template
59756
59757             var dummyUrl = esri.url([1, 2, 3]); // calculate url z/y/x from the lat/long of the center of the map
59758
59759             var x = Math.floor((center[0] + 180) / 360 * Math.pow(2, z));
59760             var y = Math.floor((1 - Math.log(Math.tan(center[1] * Math.PI / 180) + 1 / Math.cos(center[1] * Math.PI / 180)) / Math.PI) / 2 * Math.pow(2, z)); // fetch an 8x8 grid to leverage cache
59761
59762             var tilemapUrl = dummyUrl.replace(/tile\/[0-9]+\/[0-9]+\/[0-9]+\?blankTile=false/, 'tilemap') + '/' + z + '/' + y + '/' + x + '/8/8'; // make the request and introspect the response from the tilemap server
59763
59764             d3_json(tilemapUrl).then(function (tilemap) {
59765               if (!tilemap) {
59766                 throw new Error('Unknown Error');
59767               }
59768
59769               var hasTiles = true;
59770
59771               for (var i = 0; i < tilemap.data.length; i++) {
59772                 // 0 means an individual tile in the grid doesn't exist
59773                 if (!tilemap.data[i]) {
59774                   hasTiles = false;
59775                   break;
59776                 }
59777               } // if any tiles are missing at level 20 we restrict maxZoom to 19
59778
59779
59780               esri.zoomExtent[1] = hasTiles ? 22 : 19;
59781             })["catch"](function () {
59782               /* ignore */
59783             });
59784           };
59785
59786           esri.getMetadata = function (center, tileCoord, callback) {
59787             var tileID = tileCoord.slice(0, 3).join('/');
59788             var zoom = Math.min(tileCoord[2], esri.zoomExtent[1]);
59789             var centerPoint = center[0] + ',' + center[1]; // long, lat (as it should be)
59790
59791             var unknown = _t('info_panels.background.unknown');
59792             var metadataLayer;
59793             var vintage = {};
59794             var metadata = {};
59795             if (inflight[tileID]) return;
59796
59797             switch (true) {
59798               case zoom >= 20 && esri.id === 'EsriWorldImageryClarity':
59799                 metadataLayer = 4;
59800                 break;
59801
59802               case zoom >= 19:
59803                 metadataLayer = 3;
59804                 break;
59805
59806               case zoom >= 17:
59807                 metadataLayer = 2;
59808                 break;
59809
59810               case zoom >= 13:
59811                 metadataLayer = 0;
59812                 break;
59813
59814               default:
59815                 metadataLayer = 99;
59816             }
59817
59818             var url; // build up query using the layer appropriate to the current zoom
59819
59820             if (esri.id === 'EsriWorldImagery') {
59821               url = 'https://services.arcgisonline.com/arcgis/rest/services/World_Imagery/MapServer/';
59822             } else if (esri.id === 'EsriWorldImageryClarity') {
59823               url = 'https://serviceslab.arcgisonline.com/arcgis/rest/services/Clarity_World_Imagery/MapServer/';
59824             }
59825
59826             url += metadataLayer + '/query?returnGeometry=false&geometry=' + centerPoint + '&inSR=4326&geometryType=esriGeometryPoint&outFields=*&f=json';
59827
59828             if (!cache[tileID]) {
59829               cache[tileID] = {};
59830             }
59831
59832             if (cache[tileID] && cache[tileID].metadata) {
59833               return callback(null, cache[tileID].metadata);
59834             } // accurate metadata is only available >= 13
59835
59836
59837             if (metadataLayer === 99) {
59838               vintage = {
59839                 start: null,
59840                 end: null,
59841                 range: null
59842               };
59843               metadata = {
59844                 vintage: null,
59845                 source: unknown,
59846                 description: unknown,
59847                 resolution: unknown,
59848                 accuracy: unknown
59849               };
59850               callback(null, metadata);
59851             } else {
59852               inflight[tileID] = true;
59853               d3_json(url).then(function (result) {
59854                 delete inflight[tileID];
59855
59856                 if (!result) {
59857                   throw new Error('Unknown Error');
59858                 } else if (result.features && result.features.length < 1) {
59859                   throw new Error('No Results');
59860                 } else if (result.error && result.error.message) {
59861                   throw new Error(result.error.message);
59862                 } // pass through the discrete capture date from metadata
59863
59864
59865                 var captureDate = localeDateString(result.features[0].attributes.SRC_DATE2);
59866                 vintage = {
59867                   start: captureDate,
59868                   end: captureDate,
59869                   range: captureDate
59870                 };
59871                 metadata = {
59872                   vintage: vintage,
59873                   source: clean(result.features[0].attributes.NICE_NAME),
59874                   description: clean(result.features[0].attributes.NICE_DESC),
59875                   resolution: clean(+parseFloat(result.features[0].attributes.SRC_RES).toFixed(4)),
59876                   accuracy: clean(+parseFloat(result.features[0].attributes.SRC_ACC).toFixed(4))
59877                 }; // append units - meters
59878
59879                 if (isFinite(metadata.resolution)) {
59880                   metadata.resolution += ' m';
59881                 }
59882
59883                 if (isFinite(metadata.accuracy)) {
59884                   metadata.accuracy += ' m';
59885                 }
59886
59887                 cache[tileID].metadata = metadata;
59888                 if (callback) callback(null, metadata);
59889               })["catch"](function (err) {
59890                 delete inflight[tileID];
59891                 if (callback) callback(err.message);
59892               });
59893             }
59894
59895             function clean(val) {
59896               return String(val).trim() || unknown;
59897             }
59898           };
59899
59900           return esri;
59901         };
59902
59903         rendererBackgroundSource.None = function () {
59904           var source = rendererBackgroundSource({
59905             id: 'none',
59906             template: ''
59907           });
59908
59909           source.name = function () {
59910             return _t('background.none');
59911           };
59912
59913           source.label = function () {
59914             return _t.html('background.none');
59915           };
59916
59917           source.imageryUsed = function () {
59918             return null;
59919           };
59920
59921           source.area = function () {
59922             return -1; // sources in background pane are sorted by area
59923           };
59924
59925           return source;
59926         };
59927
59928         rendererBackgroundSource.Custom = function (template) {
59929           var source = rendererBackgroundSource({
59930             id: 'custom',
59931             template: template
59932           });
59933
59934           source.name = function () {
59935             return _t('background.custom');
59936           };
59937
59938           source.label = function () {
59939             return _t.html('background.custom');
59940           };
59941
59942           source.imageryUsed = function () {
59943             // sanitize personal connection tokens - #6801
59944             var cleaned = source.template(); // from query string parameters
59945
59946             if (cleaned.indexOf('?') !== -1) {
59947               var parts = cleaned.split('?', 2);
59948               var qs = utilStringQs(parts[1]);
59949               ['access_token', 'connectId', 'token'].forEach(function (param) {
59950                 if (qs[param]) {
59951                   qs[param] = '{apikey}';
59952                 }
59953               });
59954               cleaned = parts[0] + '?' + utilQsString(qs, true); // true = soft encode
59955             } // from wms/wmts api path parameters
59956
59957
59958             cleaned = cleaned.replace(/token\/(\w+)/, 'token/{apikey}');
59959             return 'Custom (' + cleaned + ' )';
59960           };
59961
59962           source.area = function () {
59963             return -2; // sources in background pane are sorted by area
59964           };
59965
59966           return source;
59967         };
59968
59969         function rendererTileLayer(context) {
59970           var transformProp = utilPrefixCSSProperty('Transform');
59971           var tiler = utilTiler();
59972           var _tileSize = 256;
59973
59974           var _projection;
59975
59976           var _cache = {};
59977
59978           var _tileOrigin;
59979
59980           var _zoom;
59981
59982           var _source;
59983
59984           function tileSizeAtZoom(d, z) {
59985             var EPSILON = 0.002; // close seams
59986
59987             return _tileSize * Math.pow(2, z - d[2]) / _tileSize + EPSILON;
59988           }
59989
59990           function atZoom(t, distance) {
59991             var power = Math.pow(2, distance);
59992             return [Math.floor(t[0] * power), Math.floor(t[1] * power), t[2] + distance];
59993           }
59994
59995           function lookUp(d) {
59996             for (var up = -1; up > -d[2]; up--) {
59997               var tile = atZoom(d, up);
59998
59999               if (_cache[_source.url(tile)] !== false) {
60000                 return tile;
60001               }
60002             }
60003           }
60004
60005           function uniqueBy(a, n) {
60006             var o = [];
60007             var seen = {};
60008
60009             for (var i = 0; i < a.length; i++) {
60010               if (seen[a[i][n]] === undefined) {
60011                 o.push(a[i]);
60012                 seen[a[i][n]] = true;
60013               }
60014             }
60015
60016             return o;
60017           }
60018
60019           function addSource(d) {
60020             d.push(_source.url(d));
60021             return d;
60022           } // Update tiles based on current state of `projection`.
60023
60024
60025           function background(selection) {
60026             _zoom = geoScaleToZoom(_projection.scale(), _tileSize);
60027             var pixelOffset;
60028
60029             if (_source) {
60030               pixelOffset = [_source.offset()[0] * Math.pow(2, _zoom), _source.offset()[1] * Math.pow(2, _zoom)];
60031             } else {
60032               pixelOffset = [0, 0];
60033             }
60034
60035             var translate = [_projection.translate()[0] + pixelOffset[0], _projection.translate()[1] + pixelOffset[1]];
60036             tiler.scale(_projection.scale() * 2 * Math.PI).translate(translate);
60037             _tileOrigin = [_projection.scale() * Math.PI - translate[0], _projection.scale() * Math.PI - translate[1]];
60038             render(selection);
60039           } // Derive the tiles onscreen, remove those offscreen and position them.
60040           // Important that this part not depend on `_projection` because it's
60041           // rentered when tiles load/error (see #644).
60042
60043
60044           function render(selection) {
60045             if (!_source) return;
60046             var requests = [];
60047             var showDebug = context.getDebug('tile') && !_source.overlay;
60048
60049             if (_source.validZoom(_zoom)) {
60050               tiler.skipNullIsland(!!_source.overlay);
60051               tiler().forEach(function (d) {
60052                 addSource(d);
60053                 if (d[3] === '') return;
60054                 if (typeof d[3] !== 'string') return; // Workaround for #2295
60055
60056                 requests.push(d);
60057
60058                 if (_cache[d[3]] === false && lookUp(d)) {
60059                   requests.push(addSource(lookUp(d)));
60060                 }
60061               });
60062               requests = uniqueBy(requests, 3).filter(function (r) {
60063                 // don't re-request tiles which have failed in the past
60064                 return _cache[r[3]] !== false;
60065               });
60066             }
60067
60068             function load(d3_event, d) {
60069               _cache[d[3]] = true;
60070               select(this).on('error', null).on('load', null).classed('tile-loaded', true);
60071               render(selection);
60072             }
60073
60074             function error(d3_event, d) {
60075               _cache[d[3]] = false;
60076               select(this).on('error', null).on('load', null).remove();
60077               render(selection);
60078             }
60079
60080             function imageTransform(d) {
60081               var ts = _tileSize * Math.pow(2, _zoom - d[2]);
60082
60083               var scale = tileSizeAtZoom(d, _zoom);
60084               return 'translate(' + (d[0] * ts - _tileOrigin[0]) + 'px,' + (d[1] * ts - _tileOrigin[1]) + 'px) ' + 'scale(' + scale + ',' + scale + ')';
60085             }
60086
60087             function tileCenter(d) {
60088               var ts = _tileSize * Math.pow(2, _zoom - d[2]);
60089
60090               return [d[0] * ts - _tileOrigin[0] + ts / 2, d[1] * ts - _tileOrigin[1] + ts / 2];
60091             }
60092
60093             function debugTransform(d) {
60094               var coord = tileCenter(d);
60095               return 'translate(' + coord[0] + 'px,' + coord[1] + 'px)';
60096             } // Pick a representative tile near the center of the viewport
60097             // (This is useful for sampling the imagery vintage)
60098
60099
60100             var dims = tiler.size();
60101             var mapCenter = [dims[0] / 2, dims[1] / 2];
60102             var minDist = Math.max(dims[0], dims[1]);
60103             var nearCenter;
60104             requests.forEach(function (d) {
60105               var c = tileCenter(d);
60106               var dist = geoVecLength(c, mapCenter);
60107
60108               if (dist < minDist) {
60109                 minDist = dist;
60110                 nearCenter = d;
60111               }
60112             });
60113             var image = selection.selectAll('img').data(requests, function (d) {
60114               return d[3];
60115             });
60116             image.exit().style(transformProp, imageTransform).classed('tile-removing', true).classed('tile-center', false).each(function () {
60117               var tile = select(this);
60118               window.setTimeout(function () {
60119                 if (tile.classed('tile-removing')) {
60120                   tile.remove();
60121                 }
60122               }, 300);
60123             });
60124             image.enter().append('img').attr('class', 'tile').attr('draggable', 'false').style('width', _tileSize + 'px').style('height', _tileSize + 'px').attr('src', function (d) {
60125               return d[3];
60126             }).on('error', error).on('load', load).merge(image).style(transformProp, imageTransform).classed('tile-debug', showDebug).classed('tile-removing', false).classed('tile-center', function (d) {
60127               return d === nearCenter;
60128             });
60129             var debug = selection.selectAll('.tile-label-debug').data(showDebug ? requests : [], function (d) {
60130               return d[3];
60131             });
60132             debug.exit().remove();
60133
60134             if (showDebug) {
60135               var debugEnter = debug.enter().append('div').attr('class', 'tile-label-debug');
60136               debugEnter.append('div').attr('class', 'tile-label-debug-coord');
60137               debugEnter.append('div').attr('class', 'tile-label-debug-vintage');
60138               debug = debug.merge(debugEnter);
60139               debug.style(transformProp, debugTransform);
60140               debug.selectAll('.tile-label-debug-coord').html(function (d) {
60141                 return d[2] + ' / ' + d[0] + ' / ' + d[1];
60142               });
60143               debug.selectAll('.tile-label-debug-vintage').each(function (d) {
60144                 var span = select(this);
60145                 var center = context.projection.invert(tileCenter(d));
60146
60147                 _source.getMetadata(center, d, function (err, result) {
60148                   span.html(result && result.vintage && result.vintage.range || _t('info_panels.background.vintage') + ': ' + _t('info_panels.background.unknown'));
60149                 });
60150               });
60151             }
60152           }
60153
60154           background.projection = function (val) {
60155             if (!arguments.length) return _projection;
60156             _projection = val;
60157             return background;
60158           };
60159
60160           background.dimensions = function (val) {
60161             if (!arguments.length) return tiler.size();
60162             tiler.size(val);
60163             return background;
60164           };
60165
60166           background.source = function (val) {
60167             if (!arguments.length) return _source;
60168             _source = val;
60169             _tileSize = _source.tileSize;
60170             _cache = {};
60171             tiler.tileSize(_source.tileSize).zoomExtent(_source.zoomExtent);
60172             return background;
60173           };
60174
60175           return background;
60176         }
60177
60178         var _imageryIndex = null;
60179         function rendererBackground(context) {
60180           var dispatch = dispatch$8('change');
60181           var detected = utilDetect();
60182           var baseLayer = rendererTileLayer(context).projection(context.projection);
60183           var _isValid = true;
60184           var _overlayLayers = [];
60185           var _brightness = 1;
60186           var _contrast = 1;
60187           var _saturation = 1;
60188           var _sharpness = 1;
60189
60190           function ensureImageryIndex() {
60191             return _mainFileFetcher.get('imagery').then(function (sources) {
60192               if (_imageryIndex) return _imageryIndex;
60193               _imageryIndex = {
60194                 imagery: sources,
60195                 features: {}
60196               }; // use which-polygon to support efficient index and querying for imagery
60197
60198               var features = sources.map(function (source) {
60199                 if (!source.polygon) return null; // workaround for editor-layer-index weirdness..
60200                 // Add an extra array nest to each element in `source.polygon`
60201                 // so the rings are not treated as a bunch of holes:
60202                 // what we have: [ [[outer],[hole],[hole]] ]
60203                 // what we want: [ [[outer]],[[outer]],[[outer]] ]
60204
60205                 var rings = source.polygon.map(function (ring) {
60206                   return [ring];
60207                 });
60208                 var feature = {
60209                   type: 'Feature',
60210                   properties: {
60211                     id: source.id
60212                   },
60213                   geometry: {
60214                     type: 'MultiPolygon',
60215                     coordinates: rings
60216                   }
60217                 };
60218                 _imageryIndex.features[source.id] = feature;
60219                 return feature;
60220               }).filter(Boolean);
60221               _imageryIndex.query = whichPolygon_1({
60222                 type: 'FeatureCollection',
60223                 features: features
60224               }); // Instantiate `rendererBackgroundSource` objects for each source
60225
60226               _imageryIndex.backgrounds = sources.map(function (source) {
60227                 if (source.type === 'bing') {
60228                   return rendererBackgroundSource.Bing(source, dispatch);
60229                 } else if (/^EsriWorldImagery/.test(source.id)) {
60230                   return rendererBackgroundSource.Esri(source);
60231                 } else {
60232                   return rendererBackgroundSource(source);
60233                 }
60234               }); // Add 'None'
60235
60236               _imageryIndex.backgrounds.unshift(rendererBackgroundSource.None()); // Add 'Custom'
60237
60238
60239               var template = corePreferences('background-custom-template') || '';
60240               var custom = rendererBackgroundSource.Custom(template);
60241
60242               _imageryIndex.backgrounds.unshift(custom);
60243
60244               return _imageryIndex;
60245             });
60246           }
60247
60248           function background(selection) {
60249             var currSource = baseLayer.source(); // If we are displaying an Esri basemap at high zoom,
60250             // check its tilemap to see how high the zoom can go
60251
60252             if (context.map().zoom() > 18) {
60253               if (currSource && /^EsriWorldImagery/.test(currSource.id)) {
60254                 var center = context.map().center();
60255                 currSource.fetchTilemap(center);
60256               }
60257             } // Is the imagery valid here? - #4827
60258
60259
60260             var sources = background.sources(context.map().extent());
60261             var wasValid = _isValid;
60262             _isValid = !!sources.filter(function (d) {
60263               return d === currSource;
60264             }).length;
60265
60266             if (wasValid !== _isValid) {
60267               // change in valid status
60268               background.updateImagery();
60269             }
60270
60271             var baseFilter = '';
60272
60273             if (detected.cssfilters) {
60274               if (_brightness !== 1) {
60275                 baseFilter += " brightness(".concat(_brightness, ")");
60276               }
60277
60278               if (_contrast !== 1) {
60279                 baseFilter += " contrast(".concat(_contrast, ")");
60280               }
60281
60282               if (_saturation !== 1) {
60283                 baseFilter += " saturate(".concat(_saturation, ")");
60284               }
60285
60286               if (_sharpness < 1) {
60287                 // gaussian blur
60288                 var blur = d3_interpolateNumber(0.5, 5)(1 - _sharpness);
60289                 baseFilter += " blur(".concat(blur, "px)");
60290               }
60291             }
60292
60293             var base = selection.selectAll('.layer-background').data([0]);
60294             base = base.enter().insert('div', '.layer-data').attr('class', 'layer layer-background').merge(base);
60295
60296             if (detected.cssfilters) {
60297               base.style('filter', baseFilter || null);
60298             } else {
60299               base.style('opacity', _brightness);
60300             }
60301
60302             var imagery = base.selectAll('.layer-imagery').data([0]);
60303             imagery.enter().append('div').attr('class', 'layer layer-imagery').merge(imagery).call(baseLayer);
60304             var maskFilter = '';
60305             var mixBlendMode = '';
60306
60307             if (detected.cssfilters && _sharpness > 1) {
60308               // apply unsharp mask
60309               mixBlendMode = 'overlay';
60310               maskFilter = 'saturate(0) blur(3px) invert(1)';
60311               var contrast = _sharpness - 1;
60312               maskFilter += " contrast(".concat(contrast, ")");
60313               var brightness = d3_interpolateNumber(1, 0.85)(_sharpness - 1);
60314               maskFilter += " brightness(".concat(brightness, ")");
60315             }
60316
60317             var mask = base.selectAll('.layer-unsharp-mask').data(detected.cssfilters && _sharpness > 1 ? [0] : []);
60318             mask.exit().remove();
60319             mask.enter().append('div').attr('class', 'layer layer-mask layer-unsharp-mask').merge(mask).call(baseLayer).style('filter', maskFilter || null).style('mix-blend-mode', mixBlendMode || null);
60320             var overlays = selection.selectAll('.layer-overlay').data(_overlayLayers, function (d) {
60321               return d.source().name();
60322             });
60323             overlays.exit().remove();
60324             overlays.enter().insert('div', '.layer-data').attr('class', 'layer layer-overlay').merge(overlays).each(function (layer, i, nodes) {
60325               return select(nodes[i]).call(layer);
60326             });
60327           }
60328
60329           background.updateImagery = function () {
60330             var currSource = baseLayer.source();
60331             if (context.inIntro() || !currSource) return;
60332
60333             var o = _overlayLayers.filter(function (d) {
60334               return !d.source().isLocatorOverlay() && !d.source().isHidden();
60335             }).map(function (d) {
60336               return d.source().id;
60337             }).join(',');
60338
60339             var meters = geoOffsetToMeters(currSource.offset());
60340             var EPSILON = 0.01;
60341             var x = +meters[0].toFixed(2);
60342             var y = +meters[1].toFixed(2);
60343             var hash = utilStringQs(window.location.hash);
60344             var id = currSource.id;
60345
60346             if (id === 'custom') {
60347               id = "custom:".concat(currSource.template());
60348             }
60349
60350             if (id) {
60351               hash.background = id;
60352             } else {
60353               delete hash.background;
60354             }
60355
60356             if (o) {
60357               hash.overlays = o;
60358             } else {
60359               delete hash.overlays;
60360             }
60361
60362             if (Math.abs(x) > EPSILON || Math.abs(y) > EPSILON) {
60363               hash.offset = "".concat(x, ",").concat(y);
60364             } else {
60365               delete hash.offset;
60366             }
60367
60368             if (!window.mocha) {
60369               window.location.replace('#' + utilQsString(hash, true));
60370             }
60371
60372             var imageryUsed = [];
60373             var photoOverlaysUsed = [];
60374             var currUsed = currSource.imageryUsed();
60375
60376             if (currUsed && _isValid) {
60377               imageryUsed.push(currUsed);
60378             }
60379
60380             _overlayLayers.filter(function (d) {
60381               return !d.source().isLocatorOverlay() && !d.source().isHidden();
60382             }).forEach(function (d) {
60383               return imageryUsed.push(d.source().imageryUsed());
60384             });
60385
60386             var dataLayer = context.layers().layer('data');
60387
60388             if (dataLayer && dataLayer.enabled() && dataLayer.hasData()) {
60389               imageryUsed.push(dataLayer.getSrc());
60390             }
60391
60392             var photoOverlayLayers = {
60393               streetside: 'Bing Streetside',
60394               mapillary: 'Mapillary Images',
60395               'mapillary-map-features': 'Mapillary Map Features',
60396               'mapillary-signs': 'Mapillary Signs',
60397               openstreetcam: 'OpenStreetCam Images'
60398             };
60399
60400             for (var layerID in photoOverlayLayers) {
60401               var layer = context.layers().layer(layerID);
60402
60403               if (layer && layer.enabled()) {
60404                 photoOverlaysUsed.push(layerID);
60405                 imageryUsed.push(photoOverlayLayers[layerID]);
60406               }
60407             }
60408
60409             context.history().imageryUsed(imageryUsed);
60410             context.history().photoOverlaysUsed(photoOverlaysUsed);
60411           };
60412
60413           var _checkedBlocklists;
60414
60415           background.sources = function (extent, zoom, includeCurrent) {
60416             if (!_imageryIndex) return []; // called before init()?
60417
60418             var visible = {};
60419             (_imageryIndex.query.bbox(extent.rectangle(), true) || []).forEach(function (d) {
60420               return visible[d.id] = true;
60421             });
60422             var currSource = baseLayer.source();
60423             var osm = context.connection();
60424             var blocklists = osm && osm.imageryBlocklists();
60425
60426             if (blocklists && blocklists !== _checkedBlocklists) {
60427               _imageryIndex.backgrounds.forEach(function (source) {
60428                 source.isBlocked = blocklists.some(function (blocklist) {
60429                   return blocklist.test(source.template());
60430                 });
60431               });
60432
60433               _checkedBlocklists = blocklists;
60434             }
60435
60436             return _imageryIndex.backgrounds.filter(function (source) {
60437               if (includeCurrent && currSource === source) return true; // optionally always include the current imagery
60438
60439               if (source.isBlocked) return false; // even bundled sources may be blocked - #7905
60440
60441               if (!source.polygon) return true; // always include imagery with worldwide coverage
60442
60443               if (zoom && zoom < 6) return false; // optionally exclude local imagery at low zooms
60444
60445               return visible[source.id]; // include imagery visible in given extent
60446             });
60447           };
60448
60449           background.dimensions = function (val) {
60450             if (!val) return;
60451             baseLayer.dimensions(val);
60452
60453             _overlayLayers.forEach(function (layer) {
60454               return layer.dimensions(val);
60455             });
60456           };
60457
60458           background.baseLayerSource = function (d) {
60459             if (!arguments.length) return baseLayer.source(); // test source against OSM imagery blocklists..
60460
60461             var osm = context.connection();
60462             if (!osm) return background;
60463             var blocklists = osm.imageryBlocklists();
60464             var template = d.template();
60465             var fail = false;
60466             var tested = 0;
60467             var regex;
60468
60469             for (var i = 0; i < blocklists.length; i++) {
60470               regex = blocklists[i];
60471               fail = regex.test(template);
60472               tested++;
60473               if (fail) break;
60474             } // ensure at least one test was run.
60475
60476
60477             if (!tested) {
60478               regex = /.*\.google(apis)?\..*\/(vt|kh)[\?\/].*([xyz]=.*){3}.*/;
60479               fail = regex.test(template);
60480             }
60481
60482             baseLayer.source(!fail ? d : background.findSource('none'));
60483             dispatch.call('change');
60484             background.updateImagery();
60485             return background;
60486           };
60487
60488           background.findSource = function (id) {
60489             if (!id || !_imageryIndex) return null; // called before init()?
60490
60491             return _imageryIndex.backgrounds.find(function (d) {
60492               return d.id && d.id === id;
60493             });
60494           };
60495
60496           background.bing = function () {
60497             background.baseLayerSource(background.findSource('Bing'));
60498           };
60499
60500           background.showsLayer = function (d) {
60501             var currSource = baseLayer.source();
60502             if (!d || !currSource) return false;
60503             return d.id === currSource.id || _overlayLayers.some(function (layer) {
60504               return d.id === layer.source().id;
60505             });
60506           };
60507
60508           background.overlayLayerSources = function () {
60509             return _overlayLayers.map(function (layer) {
60510               return layer.source();
60511             });
60512           };
60513
60514           background.toggleOverlayLayer = function (d) {
60515             var layer;
60516
60517             for (var i = 0; i < _overlayLayers.length; i++) {
60518               layer = _overlayLayers[i];
60519
60520               if (layer.source() === d) {
60521                 _overlayLayers.splice(i, 1);
60522
60523                 dispatch.call('change');
60524                 background.updateImagery();
60525                 return;
60526               }
60527             }
60528
60529             layer = rendererTileLayer(context).source(d).projection(context.projection).dimensions(baseLayer.dimensions());
60530
60531             _overlayLayers.push(layer);
60532
60533             dispatch.call('change');
60534             background.updateImagery();
60535           };
60536
60537           background.nudge = function (d, zoom) {
60538             var currSource = baseLayer.source();
60539
60540             if (currSource) {
60541               currSource.nudge(d, zoom);
60542               dispatch.call('change');
60543               background.updateImagery();
60544             }
60545
60546             return background;
60547           };
60548
60549           background.offset = function (d) {
60550             var currSource = baseLayer.source();
60551
60552             if (!arguments.length) {
60553               return currSource && currSource.offset() || [0, 0];
60554             }
60555
60556             if (currSource) {
60557               currSource.offset(d);
60558               dispatch.call('change');
60559               background.updateImagery();
60560             }
60561
60562             return background;
60563           };
60564
60565           background.brightness = function (d) {
60566             if (!arguments.length) return _brightness;
60567             _brightness = d;
60568             if (context.mode()) dispatch.call('change');
60569             return background;
60570           };
60571
60572           background.contrast = function (d) {
60573             if (!arguments.length) return _contrast;
60574             _contrast = d;
60575             if (context.mode()) dispatch.call('change');
60576             return background;
60577           };
60578
60579           background.saturation = function (d) {
60580             if (!arguments.length) return _saturation;
60581             _saturation = d;
60582             if (context.mode()) dispatch.call('change');
60583             return background;
60584           };
60585
60586           background.sharpness = function (d) {
60587             if (!arguments.length) return _sharpness;
60588             _sharpness = d;
60589             if (context.mode()) dispatch.call('change');
60590             return background;
60591           };
60592
60593           var _loadPromise;
60594
60595           background.ensureLoaded = function () {
60596             if (_loadPromise) return _loadPromise;
60597
60598             function parseMapParams(qmap) {
60599               if (!qmap) return false;
60600               var params = qmap.split('/').map(Number);
60601               if (params.length < 3 || params.some(isNaN)) return false;
60602               return geoExtent([params[2], params[1]]); // lon,lat
60603             }
60604
60605             var hash = utilStringQs(window.location.hash);
60606             var requested = hash.background || hash.layer;
60607             var extent = parseMapParams(hash.map);
60608             return _loadPromise = ensureImageryIndex().then(function (imageryIndex) {
60609               var first = imageryIndex.backgrounds.length && imageryIndex.backgrounds[0];
60610               var best;
60611
60612               if (!requested && extent) {
60613                 best = background.sources(extent).find(function (s) {
60614                   return s.best();
60615                 });
60616               } // Decide which background layer to display
60617
60618
60619               if (requested && requested.indexOf('custom:') === 0) {
60620                 var template = requested.replace(/^custom:/, '');
60621                 var custom = background.findSource('custom');
60622                 background.baseLayerSource(custom.template(template));
60623                 corePreferences('background-custom-template', template);
60624               } else {
60625                 background.baseLayerSource(background.findSource(requested) || best || background.findSource(corePreferences('background-last-used')) || background.findSource('Bing') || first || background.findSource('none'));
60626               }
60627
60628               var locator = imageryIndex.backgrounds.find(function (d) {
60629                 return d.overlay && d["default"];
60630               });
60631
60632               if (locator) {
60633                 background.toggleOverlayLayer(locator);
60634               }
60635
60636               var overlays = (hash.overlays || '').split(',');
60637               overlays.forEach(function (overlay) {
60638                 overlay = background.findSource(overlay);
60639
60640                 if (overlay) {
60641                   background.toggleOverlayLayer(overlay);
60642                 }
60643               });
60644
60645               if (hash.gpx) {
60646                 var gpx = context.layers().layer('data');
60647
60648                 if (gpx) {
60649                   gpx.url(hash.gpx, '.gpx');
60650                 }
60651               }
60652
60653               if (hash.offset) {
60654                 var offset = hash.offset.replace(/;/g, ',').split(',').map(function (n) {
60655                   return !isNaN(n) && n;
60656                 });
60657
60658                 if (offset.length === 2) {
60659                   background.offset(geoMetersToOffset(offset));
60660                 }
60661               }
60662             })["catch"](function () {
60663               /* ignore */
60664             });
60665           };
60666
60667           return utilRebind(background, dispatch, 'on');
60668         }
60669
60670         function rendererFeatures(context) {
60671           var dispatch = dispatch$8('change', 'redraw');
60672           var features = utilRebind({}, dispatch, 'on');
60673
60674           var _deferred = new Set();
60675
60676           var traffic_roads = {
60677             'motorway': true,
60678             'motorway_link': true,
60679             'trunk': true,
60680             'trunk_link': true,
60681             'primary': true,
60682             'primary_link': true,
60683             'secondary': true,
60684             'secondary_link': true,
60685             'tertiary': true,
60686             'tertiary_link': true,
60687             'residential': true,
60688             'unclassified': true,
60689             'living_street': true
60690           };
60691           var service_roads = {
60692             'service': true,
60693             'road': true,
60694             'track': true
60695           };
60696           var paths = {
60697             'path': true,
60698             'footway': true,
60699             'cycleway': true,
60700             'bridleway': true,
60701             'steps': true,
60702             'pedestrian': true
60703           };
60704           var past_futures = {
60705             'proposed': true,
60706             'construction': true,
60707             'abandoned': true,
60708             'dismantled': true,
60709             'disused': true,
60710             'razed': true,
60711             'demolished': true,
60712             'obliterated': true
60713           };
60714           var _cullFactor = 1;
60715           var _cache = {};
60716           var _rules = {};
60717           var _stats = {};
60718           var _keys = [];
60719           var _hidden = [];
60720           var _forceVisible = {};
60721
60722           function update() {
60723             if (!window.mocha) {
60724               var hash = utilStringQs(window.location.hash);
60725               var disabled = features.disabled();
60726
60727               if (disabled.length) {
60728                 hash.disable_features = disabled.join(',');
60729               } else {
60730                 delete hash.disable_features;
60731               }
60732
60733               window.location.replace('#' + utilQsString(hash, true));
60734               corePreferences('disabled-features', disabled.join(','));
60735             }
60736
60737             _hidden = features.hidden();
60738             dispatch.call('change');
60739             dispatch.call('redraw');
60740           }
60741
60742           function defineRule(k, filter, max) {
60743             var isEnabled = true;
60744
60745             _keys.push(k);
60746
60747             _rules[k] = {
60748               filter: filter,
60749               enabled: isEnabled,
60750               // whether the user wants it enabled..
60751               count: 0,
60752               currentMax: max || Infinity,
60753               defaultMax: max || Infinity,
60754               enable: function enable() {
60755                 this.enabled = true;
60756                 this.currentMax = this.defaultMax;
60757               },
60758               disable: function disable() {
60759                 this.enabled = false;
60760                 this.currentMax = 0;
60761               },
60762               hidden: function hidden() {
60763                 return this.count === 0 && !this.enabled || this.count > this.currentMax * _cullFactor;
60764               },
60765               autoHidden: function autoHidden() {
60766                 return this.hidden() && this.currentMax > 0;
60767               }
60768             };
60769           }
60770
60771           defineRule('points', function isPoint(tags, geometry) {
60772             return geometry === 'point';
60773           }, 200);
60774           defineRule('traffic_roads', function isTrafficRoad(tags) {
60775             return traffic_roads[tags.highway];
60776           });
60777           defineRule('service_roads', function isServiceRoad(tags) {
60778             return service_roads[tags.highway];
60779           });
60780           defineRule('paths', function isPath(tags) {
60781             return paths[tags.highway];
60782           });
60783           defineRule('buildings', function isBuilding(tags) {
60784             return !!tags.building && tags.building !== 'no' || tags.parking === 'multi-storey' || tags.parking === 'sheds' || tags.parking === 'carports' || tags.parking === 'garage_boxes';
60785           }, 250);
60786           defineRule('building_parts', function isBuildingPart(tags) {
60787             return tags['building:part'];
60788           });
60789           defineRule('indoor', function isIndoor(tags) {
60790             return tags.indoor;
60791           });
60792           defineRule('landuse', function isLanduse(tags, geometry) {
60793             return geometry === 'area' && !_rules.buildings.filter(tags) && !_rules.building_parts.filter(tags) && !_rules.indoor.filter(tags) && !_rules.water.filter(tags) && !_rules.pistes.filter(tags);
60794           });
60795           defineRule('boundaries', function isBoundary(tags) {
60796             return !!tags.boundary && !(traffic_roads[tags.highway] || service_roads[tags.highway] || paths[tags.highway] || tags.waterway || tags.railway || tags.landuse || tags.natural || tags.building || tags.power);
60797           });
60798           defineRule('water', function isWater(tags) {
60799             return !!tags.waterway || tags.natural === 'water' || tags.natural === 'coastline' || tags.natural === 'bay' || tags.landuse === 'pond' || tags.landuse === 'basin' || tags.landuse === 'reservoir' || tags.landuse === 'salt_pond';
60800           });
60801           defineRule('rail', function isRail(tags) {
60802             return (!!tags.railway || tags.landuse === 'railway') && !(traffic_roads[tags.highway] || service_roads[tags.highway] || paths[tags.highway]);
60803           });
60804           defineRule('pistes', function isPiste(tags) {
60805             return tags['piste:type'];
60806           });
60807           defineRule('aerialways', function isPiste(tags) {
60808             return tags.aerialway && tags.aerialway !== 'yes' && tags.aerialway !== 'station';
60809           });
60810           defineRule('power', function isPower(tags) {
60811             return !!tags.power;
60812           }); // contains a past/future tag, but not in active use as a road/path/cycleway/etc..
60813
60814           defineRule('past_future', function isPastFuture(tags) {
60815             if (traffic_roads[tags.highway] || service_roads[tags.highway] || paths[tags.highway]) {
60816               return false;
60817             }
60818
60819             var strings = Object.keys(tags);
60820
60821             for (var i = 0; i < strings.length; i++) {
60822               var s = strings[i];
60823
60824               if (past_futures[s] || past_futures[tags[s]]) {
60825                 return true;
60826               }
60827             }
60828
60829             return false;
60830           }); // Lines or areas that don't match another feature filter.
60831           // IMPORTANT: The 'others' feature must be the last one defined,
60832           //   so that code in getMatches can skip this test if `hasMatch = true`
60833
60834           defineRule('others', function isOther(tags, geometry) {
60835             return geometry === 'line' || geometry === 'area';
60836           });
60837
60838           features.features = function () {
60839             return _rules;
60840           };
60841
60842           features.keys = function () {
60843             return _keys;
60844           };
60845
60846           features.enabled = function (k) {
60847             if (!arguments.length) {
60848               return _keys.filter(function (k) {
60849                 return _rules[k].enabled;
60850               });
60851             }
60852
60853             return _rules[k] && _rules[k].enabled;
60854           };
60855
60856           features.disabled = function (k) {
60857             if (!arguments.length) {
60858               return _keys.filter(function (k) {
60859                 return !_rules[k].enabled;
60860               });
60861             }
60862
60863             return _rules[k] && !_rules[k].enabled;
60864           };
60865
60866           features.hidden = function (k) {
60867             if (!arguments.length) {
60868               return _keys.filter(function (k) {
60869                 return _rules[k].hidden();
60870               });
60871             }
60872
60873             return _rules[k] && _rules[k].hidden();
60874           };
60875
60876           features.autoHidden = function (k) {
60877             if (!arguments.length) {
60878               return _keys.filter(function (k) {
60879                 return _rules[k].autoHidden();
60880               });
60881             }
60882
60883             return _rules[k] && _rules[k].autoHidden();
60884           };
60885
60886           features.enable = function (k) {
60887             if (_rules[k] && !_rules[k].enabled) {
60888               _rules[k].enable();
60889
60890               update();
60891             }
60892           };
60893
60894           features.enableAll = function () {
60895             var didEnable = false;
60896
60897             for (var k in _rules) {
60898               if (!_rules[k].enabled) {
60899                 didEnable = true;
60900
60901                 _rules[k].enable();
60902               }
60903             }
60904
60905             if (didEnable) update();
60906           };
60907
60908           features.disable = function (k) {
60909             if (_rules[k] && _rules[k].enabled) {
60910               _rules[k].disable();
60911
60912               update();
60913             }
60914           };
60915
60916           features.disableAll = function () {
60917             var didDisable = false;
60918
60919             for (var k in _rules) {
60920               if (_rules[k].enabled) {
60921                 didDisable = true;
60922
60923                 _rules[k].disable();
60924               }
60925             }
60926
60927             if (didDisable) update();
60928           };
60929
60930           features.toggle = function (k) {
60931             if (_rules[k]) {
60932               (function (f) {
60933                 return f.enabled ? f.disable() : f.enable();
60934               })(_rules[k]);
60935
60936               update();
60937             }
60938           };
60939
60940           features.resetStats = function () {
60941             for (var i = 0; i < _keys.length; i++) {
60942               _rules[_keys[i]].count = 0;
60943             }
60944
60945             dispatch.call('change');
60946           };
60947
60948           features.gatherStats = function (d, resolver, dimensions) {
60949             var needsRedraw = false;
60950             var types = utilArrayGroupBy(d, 'type');
60951             var entities = [].concat(types.relation || [], types.way || [], types.node || []);
60952             var currHidden, geometry, matches, i, j;
60953
60954             for (i = 0; i < _keys.length; i++) {
60955               _rules[_keys[i]].count = 0;
60956             } // adjust the threshold for point/building culling based on viewport size..
60957             // a _cullFactor of 1 corresponds to a 1000x1000px viewport..
60958
60959
60960             _cullFactor = dimensions[0] * dimensions[1] / 1000000;
60961
60962             for (i = 0; i < entities.length; i++) {
60963               geometry = entities[i].geometry(resolver);
60964               matches = Object.keys(features.getMatches(entities[i], resolver, geometry));
60965
60966               for (j = 0; j < matches.length; j++) {
60967                 _rules[matches[j]].count++;
60968               }
60969             }
60970
60971             currHidden = features.hidden();
60972
60973             if (currHidden !== _hidden) {
60974               _hidden = currHidden;
60975               needsRedraw = true;
60976               dispatch.call('change');
60977             }
60978
60979             return needsRedraw;
60980           };
60981
60982           features.stats = function () {
60983             for (var i = 0; i < _keys.length; i++) {
60984               _stats[_keys[i]] = _rules[_keys[i]].count;
60985             }
60986
60987             return _stats;
60988           };
60989
60990           features.clear = function (d) {
60991             for (var i = 0; i < d.length; i++) {
60992               features.clearEntity(d[i]);
60993             }
60994           };
60995
60996           features.clearEntity = function (entity) {
60997             delete _cache[osmEntity.key(entity)];
60998           };
60999
61000           features.reset = function () {
61001             Array.from(_deferred).forEach(function (handle) {
61002               window.cancelIdleCallback(handle);
61003
61004               _deferred["delete"](handle);
61005             });
61006             _cache = {};
61007           }; // only certain relations are worth checking
61008
61009
61010           function relationShouldBeChecked(relation) {
61011             // multipolygon features have `area` geometry and aren't checked here
61012             return relation.tags.type === 'boundary';
61013           }
61014
61015           features.getMatches = function (entity, resolver, geometry) {
61016             if (geometry === 'vertex' || geometry === 'relation' && !relationShouldBeChecked(entity)) return {};
61017             var ent = osmEntity.key(entity);
61018
61019             if (!_cache[ent]) {
61020               _cache[ent] = {};
61021             }
61022
61023             if (!_cache[ent].matches) {
61024               var matches = {};
61025               var hasMatch = false;
61026
61027               for (var i = 0; i < _keys.length; i++) {
61028                 if (_keys[i] === 'others') {
61029                   if (hasMatch) continue; // If an entity...
61030                   //   1. is a way that hasn't matched other 'interesting' feature rules,
61031
61032                   if (entity.type === 'way') {
61033                     var parents = features.getParents(entity, resolver, geometry); //   2a. belongs only to a single multipolygon relation
61034
61035                     if (parents.length === 1 && parents[0].isMultipolygon() || // 2b. or belongs only to boundary relations
61036                     parents.length > 0 && parents.every(function (parent) {
61037                       return parent.tags.type === 'boundary';
61038                     })) {
61039                       // ...then match whatever feature rules the parent relation has matched.
61040                       // see #2548, #2887
61041                       //
61042                       // IMPORTANT:
61043                       // For this to work, getMatches must be called on relations before ways.
61044                       //
61045                       var pkey = osmEntity.key(parents[0]);
61046
61047                       if (_cache[pkey] && _cache[pkey].matches) {
61048                         matches = Object.assign({}, _cache[pkey].matches); // shallow copy
61049
61050                         continue;
61051                       }
61052                     }
61053                   }
61054                 }
61055
61056                 if (_rules[_keys[i]].filter(entity.tags, geometry)) {
61057                   matches[_keys[i]] = hasMatch = true;
61058                 }
61059               }
61060
61061               _cache[ent].matches = matches;
61062             }
61063
61064             return _cache[ent].matches;
61065           };
61066
61067           features.getParents = function (entity, resolver, geometry) {
61068             if (geometry === 'point') return [];
61069             var ent = osmEntity.key(entity);
61070
61071             if (!_cache[ent]) {
61072               _cache[ent] = {};
61073             }
61074
61075             if (!_cache[ent].parents) {
61076               var parents = [];
61077
61078               if (geometry === 'vertex') {
61079                 parents = resolver.parentWays(entity);
61080               } else {
61081                 // 'line', 'area', 'relation'
61082                 parents = resolver.parentRelations(entity);
61083               }
61084
61085               _cache[ent].parents = parents;
61086             }
61087
61088             return _cache[ent].parents;
61089           };
61090
61091           features.isHiddenPreset = function (preset, geometry) {
61092             if (!_hidden.length) return false;
61093             if (!preset.tags) return false;
61094             var test = preset.setTags({}, geometry);
61095
61096             for (var key in _rules) {
61097               if (_rules[key].filter(test, geometry)) {
61098                 if (_hidden.indexOf(key) !== -1) {
61099                   return key;
61100                 }
61101
61102                 return false;
61103               }
61104             }
61105
61106             return false;
61107           };
61108
61109           features.isHiddenFeature = function (entity, resolver, geometry) {
61110             if (!_hidden.length) return false;
61111             if (!entity.version) return false;
61112             if (_forceVisible[entity.id]) return false;
61113             var matches = Object.keys(features.getMatches(entity, resolver, geometry));
61114             return matches.length && matches.every(function (k) {
61115               return features.hidden(k);
61116             });
61117           };
61118
61119           features.isHiddenChild = function (entity, resolver, geometry) {
61120             if (!_hidden.length) return false;
61121             if (!entity.version || geometry === 'point') return false;
61122             if (_forceVisible[entity.id]) return false;
61123             var parents = features.getParents(entity, resolver, geometry);
61124             if (!parents.length) return false;
61125
61126             for (var i = 0; i < parents.length; i++) {
61127               if (!features.isHidden(parents[i], resolver, parents[i].geometry(resolver))) {
61128                 return false;
61129               }
61130             }
61131
61132             return true;
61133           };
61134
61135           features.hasHiddenConnections = function (entity, resolver) {
61136             if (!_hidden.length) return false;
61137             var childNodes, connections;
61138
61139             if (entity.type === 'midpoint') {
61140               childNodes = [resolver.entity(entity.edge[0]), resolver.entity(entity.edge[1])];
61141               connections = [];
61142             } else {
61143               childNodes = entity.nodes ? resolver.childNodes(entity) : [];
61144               connections = features.getParents(entity, resolver, entity.geometry(resolver));
61145             } // gather ways connected to child nodes..
61146
61147
61148             connections = childNodes.reduce(function (result, e) {
61149               return resolver.isShared(e) ? utilArrayUnion(result, resolver.parentWays(e)) : result;
61150             }, connections);
61151             return connections.some(function (e) {
61152               return features.isHidden(e, resolver, e.geometry(resolver));
61153             });
61154           };
61155
61156           features.isHidden = function (entity, resolver, geometry) {
61157             if (!_hidden.length) return false;
61158             if (!entity.version) return false;
61159             var fn = geometry === 'vertex' ? features.isHiddenChild : features.isHiddenFeature;
61160             return fn(entity, resolver, geometry);
61161           };
61162
61163           features.filter = function (d, resolver) {
61164             if (!_hidden.length) return d;
61165             var result = [];
61166
61167             for (var i = 0; i < d.length; i++) {
61168               var entity = d[i];
61169
61170               if (!features.isHidden(entity, resolver, entity.geometry(resolver))) {
61171                 result.push(entity);
61172               }
61173             }
61174
61175             return result;
61176           };
61177
61178           features.forceVisible = function (entityIDs) {
61179             if (!arguments.length) return Object.keys(_forceVisible);
61180             _forceVisible = {};
61181
61182             for (var i = 0; i < entityIDs.length; i++) {
61183               _forceVisible[entityIDs[i]] = true;
61184               var entity = context.hasEntity(entityIDs[i]);
61185
61186               if (entity && entity.type === 'relation') {
61187                 // also show relation members (one level deep)
61188                 for (var j in entity.members) {
61189                   _forceVisible[entity.members[j].id] = true;
61190                 }
61191               }
61192             }
61193
61194             return features;
61195           };
61196
61197           features.init = function () {
61198             var storage = corePreferences('disabled-features');
61199
61200             if (storage) {
61201               var storageDisabled = storage.replace(/;/g, ',').split(',');
61202               storageDisabled.forEach(features.disable);
61203             }
61204
61205             var hash = utilStringQs(window.location.hash);
61206
61207             if (hash.disable_features) {
61208               var hashDisabled = hash.disable_features.replace(/;/g, ',').split(',');
61209               hashDisabled.forEach(features.disable);
61210             }
61211           }; // warm up the feature matching cache upon merging fetched data
61212
61213
61214           context.history().on('merge.features', function (newEntities) {
61215             if (!newEntities) return;
61216             var handle = window.requestIdleCallback(function () {
61217               var graph = context.graph();
61218               var types = utilArrayGroupBy(newEntities, 'type'); // ensure that getMatches is called on relations before ways
61219
61220               var entities = [].concat(types.relation || [], types.way || [], types.node || []);
61221
61222               for (var i = 0; i < entities.length; i++) {
61223                 var geometry = entities[i].geometry(graph);
61224                 features.getMatches(entities[i], graph, geometry);
61225               }
61226             });
61227
61228             _deferred.add(handle);
61229           });
61230           return features;
61231         }
61232
61233         /** Error message constants. */
61234
61235         var FUNC_ERROR_TEXT = 'Expected a function';
61236         /**
61237          * Creates a throttled function that only invokes `func` at most once per
61238          * every `wait` milliseconds. The throttled function comes with a `cancel`
61239          * method to cancel delayed `func` invocations and a `flush` method to
61240          * immediately invoke them. Provide `options` to indicate whether `func`
61241          * should be invoked on the leading and/or trailing edge of the `wait`
61242          * timeout. The `func` is invoked with the last arguments provided to the
61243          * throttled function. Subsequent calls to the throttled function return the
61244          * result of the last `func` invocation.
61245          *
61246          * **Note:** If `leading` and `trailing` options are `true`, `func` is
61247          * invoked on the trailing edge of the timeout only if the throttled function
61248          * is invoked more than once during the `wait` timeout.
61249          *
61250          * If `wait` is `0` and `leading` is `false`, `func` invocation is deferred
61251          * until to the next tick, similar to `setTimeout` with a timeout of `0`.
61252          *
61253          * See [David Corbacho's article](https://css-tricks.com/debouncing-throttling-explained-examples/)
61254          * for details over the differences between `_.throttle` and `_.debounce`.
61255          *
61256          * @static
61257          * @memberOf _
61258          * @since 0.1.0
61259          * @category Function
61260          * @param {Function} func The function to throttle.
61261          * @param {number} [wait=0] The number of milliseconds to throttle invocations to.
61262          * @param {Object} [options={}] The options object.
61263          * @param {boolean} [options.leading=true]
61264          *  Specify invoking on the leading edge of the timeout.
61265          * @param {boolean} [options.trailing=true]
61266          *  Specify invoking on the trailing edge of the timeout.
61267          * @returns {Function} Returns the new throttled function.
61268          * @example
61269          *
61270          * // Avoid excessively updating the position while scrolling.
61271          * jQuery(window).on('scroll', _.throttle(updatePosition, 100));
61272          *
61273          * // Invoke `renewToken` when the click event is fired, but not more than once every 5 minutes.
61274          * var throttled = _.throttle(renewToken, 300000, { 'trailing': false });
61275          * jQuery(element).on('click', throttled);
61276          *
61277          * // Cancel the trailing throttled invocation.
61278          * jQuery(window).on('popstate', throttled.cancel);
61279          */
61280
61281         function throttle(func, wait, options) {
61282           var leading = true,
61283               trailing = true;
61284
61285           if (typeof func != 'function') {
61286             throw new TypeError(FUNC_ERROR_TEXT);
61287           }
61288
61289           if (isObject$2(options)) {
61290             leading = 'leading' in options ? !!options.leading : leading;
61291             trailing = 'trailing' in options ? !!options.trailing : trailing;
61292           }
61293
61294           return debounce(func, wait, {
61295             'leading': leading,
61296             'maxWait': wait,
61297             'trailing': trailing
61298           });
61299         }
61300
61301         //
61302         // - the activeID - nope
61303         // - 1 away (adjacent) to the activeID - yes (vertices will be merged)
61304         // - 2 away from the activeID - nope (would create a self intersecting segment)
61305         // - all others on a linear way - yes
61306         // - all others on a closed way - nope (would create a self intersecting polygon)
61307         //
61308         // returns
61309         // 0 = active vertex - no touch/connect
61310         // 1 = passive vertex - yes touch/connect
61311         // 2 = adjacent vertex - yes but pay attention segmenting a line here
61312         //
61313
61314         function svgPassiveVertex(node, graph, activeID) {
61315           if (!activeID) return 1;
61316           if (activeID === node.id) return 0;
61317           var parents = graph.parentWays(node);
61318           var i, j, nodes, isClosed, ix1, ix2, ix3, ix4, max;
61319
61320           for (i = 0; i < parents.length; i++) {
61321             nodes = parents[i].nodes;
61322             isClosed = parents[i].isClosed();
61323
61324             for (j = 0; j < nodes.length; j++) {
61325               // find this vertex, look nearby
61326               if (nodes[j] === node.id) {
61327                 ix1 = j - 2;
61328                 ix2 = j - 1;
61329                 ix3 = j + 1;
61330                 ix4 = j + 2;
61331
61332                 if (isClosed) {
61333                   // wraparound if needed
61334                   max = nodes.length - 1;
61335                   if (ix1 < 0) ix1 = max + ix1;
61336                   if (ix2 < 0) ix2 = max + ix2;
61337                   if (ix3 > max) ix3 = ix3 - max;
61338                   if (ix4 > max) ix4 = ix4 - max;
61339                 }
61340
61341                 if (nodes[ix1] === activeID) return 0; // no - prevent self intersect
61342                 else if (nodes[ix2] === activeID) return 2; // ok - adjacent
61343                   else if (nodes[ix3] === activeID) return 2; // ok - adjacent
61344                     else if (nodes[ix4] === activeID) return 0; // no - prevent self intersect
61345                       else if (isClosed && nodes.indexOf(activeID) !== -1) return 0; // no - prevent self intersect
61346               }
61347             }
61348           }
61349
61350           return 1; // ok
61351         }
61352         function svgMarkerSegments(projection, graph, dt, shouldReverse, bothDirections) {
61353           return function (entity) {
61354             var i = 0;
61355             var offset = dt;
61356             var segments = [];
61357             var clip = d3_geoIdentity().clipExtent(projection.clipExtent()).stream;
61358             var coordinates = graph.childNodes(entity).map(function (n) {
61359               return n.loc;
61360             });
61361             var a, b;
61362
61363             if (shouldReverse(entity)) {
61364               coordinates.reverse();
61365             }
61366
61367             d3_geoStream({
61368               type: 'LineString',
61369               coordinates: coordinates
61370             }, projection.stream(clip({
61371               lineStart: function lineStart() {},
61372               lineEnd: function lineEnd() {
61373                 a = null;
61374               },
61375               point: function point(x, y) {
61376                 b = [x, y];
61377
61378                 if (a) {
61379                   var span = geoVecLength(a, b) - offset;
61380
61381                   if (span >= 0) {
61382                     var heading = geoVecAngle(a, b);
61383                     var dx = dt * Math.cos(heading);
61384                     var dy = dt * Math.sin(heading);
61385                     var p = [a[0] + offset * Math.cos(heading), a[1] + offset * Math.sin(heading)]; // gather coordinates
61386
61387                     var coord = [a, p];
61388
61389                     for (span -= dt; span >= 0; span -= dt) {
61390                       p = geoVecAdd(p, [dx, dy]);
61391                       coord.push(p);
61392                     }
61393
61394                     coord.push(b); // generate svg paths
61395
61396                     var segment = '';
61397                     var j;
61398
61399                     for (j = 0; j < coord.length; j++) {
61400                       segment += (j === 0 ? 'M' : 'L') + coord[j][0] + ',' + coord[j][1];
61401                     }
61402
61403                     segments.push({
61404                       id: entity.id,
61405                       index: i++,
61406                       d: segment
61407                     });
61408
61409                     if (bothDirections(entity)) {
61410                       segment = '';
61411
61412                       for (j = coord.length - 1; j >= 0; j--) {
61413                         segment += (j === coord.length - 1 ? 'M' : 'L') + coord[j][0] + ',' + coord[j][1];
61414                       }
61415
61416                       segments.push({
61417                         id: entity.id,
61418                         index: i++,
61419                         d: segment
61420                       });
61421                     }
61422                   }
61423
61424                   offset = -span;
61425                 }
61426
61427                 a = b;
61428               }
61429             })));
61430             return segments;
61431           };
61432         }
61433         function svgPath(projection, graph, isArea) {
61434           // Explanation of magic numbers:
61435           // "padding" here allows space for strokes to extend beyond the viewport,
61436           // so that the stroke isn't drawn along the edge of the viewport when
61437           // the shape is clipped.
61438           //
61439           // When drawing lines, pad viewport by 5px.
61440           // When drawing areas, pad viewport by 65px in each direction to allow
61441           // for 60px area fill stroke (see ".fill-partial path.fill" css rule)
61442           var cache = {};
61443           var padding = isArea ? 65 : 5;
61444           var viewport = projection.clipExtent();
61445           var paddedExtent = [[viewport[0][0] - padding, viewport[0][1] - padding], [viewport[1][0] + padding, viewport[1][1] + padding]];
61446           var clip = d3_geoIdentity().clipExtent(paddedExtent).stream;
61447           var project = projection.stream;
61448           var path = d3_geoPath().projection({
61449             stream: function stream(output) {
61450               return project(clip(output));
61451             }
61452           });
61453
61454           var svgpath = function svgpath(entity) {
61455             if (entity.id in cache) {
61456               return cache[entity.id];
61457             } else {
61458               return cache[entity.id] = path(entity.asGeoJSON(graph));
61459             }
61460           };
61461
61462           svgpath.geojson = function (d) {
61463             if (d.__featurehash__ !== undefined) {
61464               if (d.__featurehash__ in cache) {
61465                 return cache[d.__featurehash__];
61466               } else {
61467                 return cache[d.__featurehash__] = path(d);
61468               }
61469             } else {
61470               return path(d);
61471             }
61472           };
61473
61474           return svgpath;
61475         }
61476         function svgPointTransform(projection) {
61477           var svgpoint = function svgpoint(entity) {
61478             // http://jsperf.com/short-array-join
61479             var pt = projection(entity.loc);
61480             return 'translate(' + pt[0] + ',' + pt[1] + ')';
61481           };
61482
61483           svgpoint.geojson = function (d) {
61484             return svgpoint(d.properties.entity);
61485           };
61486
61487           return svgpoint;
61488         }
61489         function svgRelationMemberTags(graph) {
61490           return function (entity) {
61491             var tags = entity.tags;
61492             var shouldCopyMultipolygonTags = !entity.hasInterestingTags();
61493             graph.parentRelations(entity).forEach(function (relation) {
61494               var type = relation.tags.type;
61495
61496               if (type === 'multipolygon' && shouldCopyMultipolygonTags || type === 'boundary') {
61497                 tags = Object.assign({}, relation.tags, tags);
61498               }
61499             });
61500             return tags;
61501           };
61502         }
61503         function svgSegmentWay(way, graph, activeID) {
61504           // When there is no activeID, we can memoize this expensive computation
61505           if (activeID === undefined) {
61506             return graph["transient"](way, 'waySegments', getWaySegments);
61507           } else {
61508             return getWaySegments();
61509           }
61510
61511           function getWaySegments() {
61512             var isActiveWay = way.nodes.indexOf(activeID) !== -1;
61513             var features = {
61514               passive: [],
61515               active: []
61516             };
61517             var start = {};
61518             var end = {};
61519             var node, type;
61520
61521             for (var i = 0; i < way.nodes.length; i++) {
61522               node = graph.entity(way.nodes[i]);
61523               type = svgPassiveVertex(node, graph, activeID);
61524               end = {
61525                 node: node,
61526                 type: type
61527               };
61528
61529               if (start.type !== undefined) {
61530                 if (start.node.id === activeID || end.node.id === activeID) ; else if (isActiveWay && (start.type === 2 || end.type === 2)) {
61531                   // one adjacent vertex
61532                   pushActive(start, end, i);
61533                 } else if (start.type === 0 && end.type === 0) {
61534                   // both active vertices
61535                   pushActive(start, end, i);
61536                 } else {
61537                   pushPassive(start, end, i);
61538                 }
61539               }
61540
61541               start = end;
61542             }
61543
61544             return features;
61545
61546             function pushActive(start, end, index) {
61547               features.active.push({
61548                 type: 'Feature',
61549                 id: way.id + '-' + index + '-nope',
61550                 properties: {
61551                   nope: true,
61552                   target: true,
61553                   entity: way,
61554                   nodes: [start.node, end.node],
61555                   index: index
61556                 },
61557                 geometry: {
61558                   type: 'LineString',
61559                   coordinates: [start.node.loc, end.node.loc]
61560                 }
61561               });
61562             }
61563
61564             function pushPassive(start, end, index) {
61565               features.passive.push({
61566                 type: 'Feature',
61567                 id: way.id + '-' + index,
61568                 properties: {
61569                   target: true,
61570                   entity: way,
61571                   nodes: [start.node, end.node],
61572                   index: index
61573                 },
61574                 geometry: {
61575                   type: 'LineString',
61576                   coordinates: [start.node.loc, end.node.loc]
61577                 }
61578               });
61579             }
61580           }
61581         }
61582
61583         function svgTagClasses() {
61584           var primaries = ['building', 'highway', 'railway', 'waterway', 'aeroway', 'aerialway', 'piste:type', 'boundary', 'power', 'amenity', 'natural', 'landuse', 'leisure', 'military', 'place', 'man_made', 'route', 'attraction', 'building:part', 'indoor'];
61585           var statuses = [// nonexistent, might be built
61586           'proposed', 'planned', // under maintentance or between groundbreaking and opening
61587           'construction', // existent but not functional
61588           'disused', // dilapidated to nonexistent
61589           'abandoned', // nonexistent, still may appear in imagery
61590           'dismantled', 'razed', 'demolished', 'obliterated', // existent occasionally, e.g. stormwater drainage basin
61591           'intermittent'];
61592           var secondaries = ['oneway', 'bridge', 'tunnel', 'embankment', 'cutting', 'barrier', 'surface', 'tracktype', 'footway', 'crossing', 'service', 'sport', 'public_transport', 'location', 'parking', 'golf', 'type', 'leisure', 'man_made', 'indoor'];
61593
61594           var _tags = function _tags(entity) {
61595             return entity.tags;
61596           };
61597
61598           var tagClasses = function tagClasses(selection) {
61599             selection.each(function tagClassesEach(entity) {
61600               var value = this.className;
61601
61602               if (value.baseVal !== undefined) {
61603                 value = value.baseVal;
61604               }
61605
61606               var t = _tags(entity);
61607
61608               var computed = tagClasses.getClassesString(t, value);
61609
61610               if (computed !== value) {
61611                 select(this).attr('class', computed);
61612               }
61613             });
61614           };
61615
61616           tagClasses.getClassesString = function (t, value) {
61617             var primary, status;
61618             var i, j, k, v; // in some situations we want to render perimeter strokes a certain way
61619
61620             var overrideGeometry;
61621
61622             if (/\bstroke\b/.test(value)) {
61623               if (!!t.barrier && t.barrier !== 'no') {
61624                 overrideGeometry = 'line';
61625               }
61626             } // preserve base classes (nothing with `tag-`)
61627
61628
61629             var classes = value.trim().split(/\s+/).filter(function (klass) {
61630               return klass.length && !/^tag-/.test(klass);
61631             }).map(function (klass) {
61632               // special overrides for some perimeter strokes
61633               return klass === 'line' || klass === 'area' ? overrideGeometry || klass : klass;
61634             }); // pick at most one primary classification tag..
61635
61636             for (i = 0; i < primaries.length; i++) {
61637               k = primaries[i];
61638               v = t[k];
61639               if (!v || v === 'no') continue;
61640
61641               if (k === 'piste:type') {
61642                 // avoid a ':' in the class name
61643                 k = 'piste';
61644               } else if (k === 'building:part') {
61645                 // avoid a ':' in the class name
61646                 k = 'building_part';
61647               }
61648
61649               primary = k;
61650
61651               if (statuses.indexOf(v) !== -1) {
61652                 // e.g. `railway=abandoned`
61653                 status = v;
61654                 classes.push('tag-' + k);
61655               } else {
61656                 classes.push('tag-' + k);
61657                 classes.push('tag-' + k + '-' + v);
61658               }
61659
61660               break;
61661             }
61662
61663             if (!primary) {
61664               for (i = 0; i < statuses.length; i++) {
61665                 for (j = 0; j < primaries.length; j++) {
61666                   k = statuses[i] + ':' + primaries[j]; // e.g. `demolished:building=yes`
61667
61668                   v = t[k];
61669                   if (!v || v === 'no') continue;
61670                   status = statuses[i];
61671                   break;
61672                 }
61673               }
61674             } // add at most one status tag, only if relates to primary tag..
61675
61676
61677             if (!status) {
61678               for (i = 0; i < statuses.length; i++) {
61679                 k = statuses[i];
61680                 v = t[k];
61681                 if (!v || v === 'no') continue;
61682
61683                 if (v === 'yes') {
61684                   // e.g. `railway=rail + abandoned=yes`
61685                   status = k;
61686                 } else if (primary && primary === v) {
61687                   // e.g. `railway=rail + abandoned=railway`
61688                   status = k;
61689                 } else if (!primary && primaries.indexOf(v) !== -1) {
61690                   // e.g. `abandoned=railway`
61691                   status = k;
61692                   primary = v;
61693                   classes.push('tag-' + v);
61694                 } // else ignore e.g.  `highway=path + abandoned=railway`
61695
61696
61697                 if (status) break;
61698               }
61699             }
61700
61701             if (status) {
61702               classes.push('tag-status');
61703               classes.push('tag-status-' + status);
61704             } // add any secondary tags
61705
61706
61707             for (i = 0; i < secondaries.length; i++) {
61708               k = secondaries[i];
61709               v = t[k];
61710               if (!v || v === 'no' || k === primary) continue;
61711               classes.push('tag-' + k);
61712               classes.push('tag-' + k + '-' + v);
61713             } // For highways, look for surface tagging..
61714
61715
61716             if (primary === 'highway' && !osmPathHighwayTagValues[t.highway] || primary === 'aeroway') {
61717               var surface = t.highway === 'track' ? 'unpaved' : 'paved';
61718
61719               for (k in t) {
61720                 v = t[k];
61721
61722                 if (k in osmPavedTags) {
61723                   surface = osmPavedTags[k][v] ? 'paved' : 'unpaved';
61724                 }
61725
61726                 if (k in osmSemipavedTags && !!osmSemipavedTags[k][v]) {
61727                   surface = 'semipaved';
61728                 }
61729               }
61730
61731               classes.push('tag-' + surface);
61732             } // If this is a wikidata-tagged item, add a class for that..
61733
61734
61735             var qid = t.wikidata || t['flag:wikidata'] || t['brand:wikidata'] || t['network:wikidata'] || t['operator:wikidata'];
61736
61737             if (qid) {
61738               classes.push('tag-wikidata');
61739             }
61740
61741             return classes.join(' ').trim();
61742           };
61743
61744           tagClasses.tags = function (val) {
61745             if (!arguments.length) return _tags;
61746             _tags = val;
61747             return tagClasses;
61748           };
61749
61750           return tagClasses;
61751         }
61752
61753         // Patterns only work in Firefox when set directly on element.
61754         // (This is not a bug: https://bugzilla.mozilla.org/show_bug.cgi?id=750632)
61755         var patterns = {
61756           // tag - pattern name
61757           // -or-
61758           // tag - value - pattern name
61759           // -or-
61760           // tag - value - rules (optional tag-values, pattern name)
61761           // (matches earlier rules first, so fallback should be last entry)
61762           amenity: {
61763             grave_yard: 'cemetery',
61764             fountain: 'water_standing'
61765           },
61766           landuse: {
61767             cemetery: [{
61768               religion: 'christian',
61769               pattern: 'cemetery_christian'
61770             }, {
61771               religion: 'buddhist',
61772               pattern: 'cemetery_buddhist'
61773             }, {
61774               religion: 'muslim',
61775               pattern: 'cemetery_muslim'
61776             }, {
61777               religion: 'jewish',
61778               pattern: 'cemetery_jewish'
61779             }, {
61780               pattern: 'cemetery'
61781             }],
61782             construction: 'construction',
61783             farmland: 'farmland',
61784             farmyard: 'farmyard',
61785             forest: [{
61786               leaf_type: 'broadleaved',
61787               pattern: 'forest_broadleaved'
61788             }, {
61789               leaf_type: 'needleleaved',
61790               pattern: 'forest_needleleaved'
61791             }, {
61792               leaf_type: 'leafless',
61793               pattern: 'forest_leafless'
61794             }, {
61795               pattern: 'forest'
61796             } // same as 'leaf_type:mixed'
61797             ],
61798             grave_yard: 'cemetery',
61799             grass: [{
61800               golf: 'green',
61801               pattern: 'golf_green'
61802             }, {
61803               pattern: 'grass'
61804             }],
61805             landfill: 'landfill',
61806             meadow: 'meadow',
61807             military: 'construction',
61808             orchard: 'orchard',
61809             quarry: 'quarry',
61810             vineyard: 'vineyard'
61811           },
61812           natural: {
61813             beach: 'beach',
61814             grassland: 'grass',
61815             sand: 'beach',
61816             scrub: 'scrub',
61817             water: [{
61818               water: 'pond',
61819               pattern: 'pond'
61820             }, {
61821               water: 'reservoir',
61822               pattern: 'water_standing'
61823             }, {
61824               pattern: 'waves'
61825             }],
61826             wetland: [{
61827               wetland: 'marsh',
61828               pattern: 'wetland_marsh'
61829             }, {
61830               wetland: 'swamp',
61831               pattern: 'wetland_swamp'
61832             }, {
61833               wetland: 'bog',
61834               pattern: 'wetland_bog'
61835             }, {
61836               wetland: 'reedbed',
61837               pattern: 'wetland_reedbed'
61838             }, {
61839               pattern: 'wetland'
61840             }],
61841             wood: [{
61842               leaf_type: 'broadleaved',
61843               pattern: 'forest_broadleaved'
61844             }, {
61845               leaf_type: 'needleleaved',
61846               pattern: 'forest_needleleaved'
61847             }, {
61848               leaf_type: 'leafless',
61849               pattern: 'forest_leafless'
61850             }, {
61851               pattern: 'forest'
61852             } // same as 'leaf_type:mixed'
61853             ]
61854           },
61855           traffic_calming: {
61856             island: [{
61857               surface: 'grass',
61858               pattern: 'grass'
61859             }],
61860             chicane: [{
61861               surface: 'grass',
61862               pattern: 'grass'
61863             }],
61864             choker: [{
61865               surface: 'grass',
61866               pattern: 'grass'
61867             }]
61868           }
61869         };
61870         function svgTagPattern(tags) {
61871           // Skip pattern filling if this is a building (buildings don't get patterns applied)
61872           if (tags.building && tags.building !== 'no') {
61873             return null;
61874           }
61875
61876           for (var tag in patterns) {
61877             var entityValue = tags[tag];
61878             if (!entityValue) continue;
61879
61880             if (typeof patterns[tag] === 'string') {
61881               // extra short syntax (just tag) - pattern name
61882               return 'pattern-' + patterns[tag];
61883             } else {
61884               var values = patterns[tag];
61885
61886               for (var value in values) {
61887                 if (entityValue !== value) continue;
61888                 var rules = values[value];
61889
61890                 if (typeof rules === 'string') {
61891                   // short syntax - pattern name
61892                   return 'pattern-' + rules;
61893                 } // long syntax - rule array
61894
61895
61896                 for (var ruleKey in rules) {
61897                   var rule = rules[ruleKey];
61898                   var pass = true;
61899
61900                   for (var criterion in rule) {
61901                     if (criterion !== 'pattern') {
61902                       // reserved for pattern name
61903                       // The only rule is a required tag-value pair
61904                       var v = tags[criterion];
61905
61906                       if (!v || v !== rule[criterion]) {
61907                         pass = false;
61908                         break;
61909                       }
61910                     }
61911                   }
61912
61913                   if (pass) {
61914                     return 'pattern-' + rule.pattern;
61915                   }
61916                 }
61917               }
61918             }
61919           }
61920
61921           return null;
61922         }
61923
61924         function svgAreas(projection, context) {
61925           function getPatternStyle(tags) {
61926             var imageID = svgTagPattern(tags);
61927
61928             if (imageID) {
61929               return 'url("#ideditor-' + imageID + '")';
61930             }
61931
61932             return '';
61933           }
61934
61935           function drawTargets(selection, graph, entities, filter) {
61936             var targetClass = context.getDebug('target') ? 'pink ' : 'nocolor ';
61937             var nopeClass = context.getDebug('target') ? 'red ' : 'nocolor ';
61938             var getPath = svgPath(projection).geojson;
61939             var activeID = context.activeID();
61940             var base = context.history().base(); // The targets and nopes will be MultiLineString sub-segments of the ways
61941
61942             var data = {
61943               targets: [],
61944               nopes: []
61945             };
61946             entities.forEach(function (way) {
61947               var features = svgSegmentWay(way, graph, activeID);
61948               data.targets.push.apply(data.targets, features.passive);
61949               data.nopes.push.apply(data.nopes, features.active);
61950             }); // Targets allow hover and vertex snapping
61951
61952             var targetData = data.targets.filter(getPath);
61953             var targets = selection.selectAll('.area.target-allowed').filter(function (d) {
61954               return filter(d.properties.entity);
61955             }).data(targetData, function key(d) {
61956               return d.id;
61957             }); // exit
61958
61959             targets.exit().remove();
61960
61961             var segmentWasEdited = function segmentWasEdited(d) {
61962               var wayID = d.properties.entity.id; // if the whole line was edited, don't draw segment changes
61963
61964               if (!base.entities[wayID] || !fastDeepEqual(graph.entities[wayID].nodes, base.entities[wayID].nodes)) {
61965                 return false;
61966               }
61967
61968               return d.properties.nodes.some(function (n) {
61969                 return !base.entities[n.id] || !fastDeepEqual(graph.entities[n.id].loc, base.entities[n.id].loc);
61970               });
61971             }; // enter/update
61972
61973
61974             targets.enter().append('path').merge(targets).attr('d', getPath).attr('class', function (d) {
61975               return 'way area target target-allowed ' + targetClass + d.id;
61976             }).classed('segment-edited', segmentWasEdited); // NOPE
61977
61978             var nopeData = data.nopes.filter(getPath);
61979             var nopes = selection.selectAll('.area.target-nope').filter(function (d) {
61980               return filter(d.properties.entity);
61981             }).data(nopeData, function key(d) {
61982               return d.id;
61983             }); // exit
61984
61985             nopes.exit().remove(); // enter/update
61986
61987             nopes.enter().append('path').merge(nopes).attr('d', getPath).attr('class', function (d) {
61988               return 'way area target target-nope ' + nopeClass + d.id;
61989             }).classed('segment-edited', segmentWasEdited);
61990           }
61991
61992           function drawAreas(selection, graph, entities, filter) {
61993             var path = svgPath(projection, graph, true);
61994             var areas = {};
61995             var multipolygon;
61996             var base = context.history().base();
61997
61998             for (var i = 0; i < entities.length; i++) {
61999               var entity = entities[i];
62000               if (entity.geometry(graph) !== 'area') continue;
62001               multipolygon = osmIsOldMultipolygonOuterMember(entity, graph);
62002
62003               if (multipolygon) {
62004                 areas[multipolygon.id] = {
62005                   entity: multipolygon.mergeTags(entity.tags),
62006                   area: Math.abs(entity.area(graph))
62007                 };
62008               } else if (!areas[entity.id]) {
62009                 areas[entity.id] = {
62010                   entity: entity,
62011                   area: Math.abs(entity.area(graph))
62012                 };
62013               }
62014             }
62015
62016             var fills = Object.values(areas).filter(function hasPath(a) {
62017               return path(a.entity);
62018             });
62019             fills.sort(function areaSort(a, b) {
62020               return b.area - a.area;
62021             });
62022             fills = fills.map(function (a) {
62023               return a.entity;
62024             });
62025             var strokes = fills.filter(function (area) {
62026               return area.type === 'way';
62027             });
62028             var data = {
62029               clip: fills,
62030               shadow: strokes,
62031               stroke: strokes,
62032               fill: fills
62033             };
62034             var clipPaths = context.surface().selectAll('defs').selectAll('.clipPath-osm').filter(filter).data(data.clip, osmEntity.key);
62035             clipPaths.exit().remove();
62036             var clipPathsEnter = clipPaths.enter().append('clipPath').attr('class', 'clipPath-osm').attr('id', function (entity) {
62037               return 'ideditor-' + entity.id + '-clippath';
62038             });
62039             clipPathsEnter.append('path');
62040             clipPaths.merge(clipPathsEnter).selectAll('path').attr('d', path);
62041             var drawLayer = selection.selectAll('.layer-osm.areas');
62042             var touchLayer = selection.selectAll('.layer-touch.areas'); // Draw areas..
62043
62044             var areagroup = drawLayer.selectAll('g.areagroup').data(['fill', 'shadow', 'stroke']);
62045             areagroup = areagroup.enter().append('g').attr('class', function (d) {
62046               return 'areagroup area-' + d;
62047             }).merge(areagroup);
62048             var paths = areagroup.selectAll('path').filter(filter).data(function (layer) {
62049               return data[layer];
62050             }, osmEntity.key);
62051             paths.exit().remove();
62052             var fillpaths = selection.selectAll('.area-fill path.area').nodes();
62053             var bisect = d3_bisector(function (node) {
62054               return -node.__data__.area(graph);
62055             }).left;
62056
62057             function sortedByArea(entity) {
62058               if (this._parent.__data__ === 'fill') {
62059                 return fillpaths[bisect(fillpaths, -entity.area(graph))];
62060               }
62061             }
62062
62063             paths = paths.enter().insert('path', sortedByArea).merge(paths).each(function (entity) {
62064               var layer = this.parentNode.__data__;
62065               this.setAttribute('class', entity.type + ' area ' + layer + ' ' + entity.id);
62066
62067               if (layer === 'fill') {
62068                 this.setAttribute('clip-path', 'url(#ideditor-' + entity.id + '-clippath)');
62069                 this.style.fill = this.style.stroke = getPatternStyle(entity.tags);
62070               }
62071             }).classed('added', function (d) {
62072               return !base.entities[d.id];
62073             }).classed('geometry-edited', function (d) {
62074               return graph.entities[d.id] && base.entities[d.id] && !fastDeepEqual(graph.entities[d.id].nodes, base.entities[d.id].nodes);
62075             }).classed('retagged', function (d) {
62076               return graph.entities[d.id] && base.entities[d.id] && !fastDeepEqual(graph.entities[d.id].tags, base.entities[d.id].tags);
62077             }).call(svgTagClasses()).attr('d', path); // Draw touch targets..
62078
62079             touchLayer.call(drawTargets, graph, data.stroke, filter);
62080           }
62081
62082           return drawAreas;
62083         }
62084
62085         var fastJsonStableStringify = function fastJsonStableStringify(data, opts) {
62086           if (!opts) opts = {};
62087           if (typeof opts === 'function') opts = {
62088             cmp: opts
62089           };
62090           var cycles = typeof opts.cycles === 'boolean' ? opts.cycles : false;
62091
62092           var cmp = opts.cmp && function (f) {
62093             return function (node) {
62094               return function (a, b) {
62095                 var aobj = {
62096                   key: a,
62097                   value: node[a]
62098                 };
62099                 var bobj = {
62100                   key: b,
62101                   value: node[b]
62102                 };
62103                 return f(aobj, bobj);
62104               };
62105             };
62106           }(opts.cmp);
62107
62108           var seen = [];
62109           return function stringify(node) {
62110             if (node && node.toJSON && typeof node.toJSON === 'function') {
62111               node = node.toJSON();
62112             }
62113
62114             if (node === undefined) return;
62115             if (typeof node == 'number') return isFinite(node) ? '' + node : 'null';
62116             if (_typeof(node) !== 'object') return JSON.stringify(node);
62117             var i, out;
62118
62119             if (Array.isArray(node)) {
62120               out = '[';
62121
62122               for (i = 0; i < node.length; i++) {
62123                 if (i) out += ',';
62124                 out += stringify(node[i]) || 'null';
62125               }
62126
62127               return out + ']';
62128             }
62129
62130             if (node === null) return 'null';
62131
62132             if (seen.indexOf(node) !== -1) {
62133               if (cycles) return JSON.stringify('__cycle__');
62134               throw new TypeError('Converting circular structure to JSON');
62135             }
62136
62137             var seenIndex = seen.push(node) - 1;
62138             var keys = Object.keys(node).sort(cmp && cmp(node));
62139             out = '';
62140
62141             for (i = 0; i < keys.length; i++) {
62142               var key = keys[i];
62143               var value = stringify(node[key]);
62144               if (!value) continue;
62145               if (out) out += ',';
62146               out += JSON.stringify(key) + ':' + value;
62147             }
62148
62149             seen.splice(seenIndex, 1);
62150             return '{' + out + '}';
62151           }(data);
62152         };
62153
62154         var $entries = objectToArray.entries;
62155
62156         // `Object.entries` method
62157         // https://tc39.es/ecma262/#sec-object.entries
62158         _export({ target: 'Object', stat: true }, {
62159           entries: function entries(O) {
62160             return $entries(O);
62161           }
62162         });
62163
62164         var _marked = /*#__PURE__*/regeneratorRuntime.mark(gpxGen),
62165             _marked3 = /*#__PURE__*/regeneratorRuntime.mark(kmlGen);
62166
62167         // cast array x into numbers
62168         // get the content of a text node, if any
62169         function nodeVal(x) {
62170           if (x && x.normalize) {
62171             x.normalize();
62172           }
62173
62174           return x && x.textContent || "";
62175         } // one Y child of X, if any, otherwise null
62176
62177
62178         function get1(x, y) {
62179           var n = x.getElementsByTagName(y);
62180           return n.length ? n[0] : null;
62181         }
62182
62183         function getLineStyle(extensions) {
62184           var style = {};
62185
62186           if (extensions) {
62187             var lineStyle = get1(extensions, "line");
62188
62189             if (lineStyle) {
62190               var color = nodeVal(get1(lineStyle, "color")),
62191                   opacity = parseFloat(nodeVal(get1(lineStyle, "opacity"))),
62192                   width = parseFloat(nodeVal(get1(lineStyle, "width")));
62193               if (color) style.stroke = color;
62194               if (!isNaN(opacity)) style["stroke-opacity"] = opacity; // GPX width is in mm, convert to px with 96 px per inch
62195
62196               if (!isNaN(width)) style["stroke-width"] = width * 96 / 25.4;
62197             }
62198           }
62199
62200           return style;
62201         } // get the contents of multiple text nodes, if present
62202
62203
62204         function getMulti(x, ys) {
62205           var o = {};
62206           var n;
62207           var k;
62208
62209           for (k = 0; k < ys.length; k++) {
62210             n = get1(x, ys[k]);
62211             if (n) o[ys[k]] = nodeVal(n);
62212           }
62213
62214           return o;
62215         }
62216
62217         function getProperties$1(node) {
62218           var prop = getMulti(node, ["name", "cmt", "desc", "type", "time", "keywords"]); // Parse additional data from our Garmin extension(s)
62219
62220           var extensions = node.getElementsByTagNameNS("http://www.garmin.com/xmlschemas/GpxExtensions/v3", "*");
62221
62222           for (var i = 0; i < extensions.length; i++) {
62223             var extension = extensions[i]; // Ignore nested extensions, like those on routepoints or trackpoints
62224
62225             if (extension.parentNode.parentNode === node) {
62226               prop[extension.tagName.replace(":", "_")] = nodeVal(extension);
62227             }
62228           }
62229
62230           var links = node.getElementsByTagName("link");
62231           if (links.length) prop.links = [];
62232
62233           for (var _i = 0; _i < links.length; _i++) {
62234             prop.links.push(Object.assign({
62235               href: links[_i].getAttribute("href")
62236             }, getMulti(links[_i], ["text", "type"])));
62237           }
62238
62239           return prop;
62240         }
62241
62242         function coordPair$1(x) {
62243           var ll = [parseFloat(x.getAttribute("lon")), parseFloat(x.getAttribute("lat"))];
62244           var ele = get1(x, "ele"); // handle namespaced attribute in browser
62245
62246           var heart = get1(x, "gpxtpx:hr") || get1(x, "hr");
62247           var time = get1(x, "time");
62248           var e;
62249
62250           if (ele) {
62251             e = parseFloat(nodeVal(ele));
62252
62253             if (!isNaN(e)) {
62254               ll.push(e);
62255             }
62256           }
62257
62258           var result = {
62259             coordinates: ll,
62260             time: time ? nodeVal(time) : null,
62261             extendedValues: []
62262           };
62263
62264           if (heart) {
62265             result.extendedValues.push(["heart", parseFloat(nodeVal(heart))]);
62266           }
62267
62268           var extensions = get1(x, "extensions");
62269
62270           if (extensions !== null) {
62271             for (var _i2 = 0, _arr = ["speed", "course", "hAcc", "vAcc"]; _i2 < _arr.length; _i2++) {
62272               var name = _arr[_i2];
62273               var v = parseFloat(nodeVal(get1(extensions, name)));
62274
62275               if (!isNaN(v)) {
62276                 result.extendedValues.push([name, v]);
62277               }
62278             }
62279           }
62280
62281           return result;
62282         }
62283
62284         function getRoute(node) {
62285           var line = getPoints$1(node, "rtept");
62286           if (!line) return;
62287           return {
62288             type: "Feature",
62289             properties: Object.assign(getProperties$1(node), getLineStyle(get1(node, "extensions")), {
62290               _gpxType: "rte"
62291             }),
62292             geometry: {
62293               type: "LineString",
62294               coordinates: line.line
62295             }
62296           };
62297         }
62298
62299         function getPoints$1(node, pointname) {
62300           var pts = node.getElementsByTagName(pointname);
62301           if (pts.length < 2) return; // Invalid line in GeoJSON
62302
62303           var line = [];
62304           var times = [];
62305           var extendedValues = {};
62306
62307           for (var i = 0; i < pts.length; i++) {
62308             var c = coordPair$1(pts[i]);
62309             line.push(c.coordinates);
62310             if (c.time) times.push(c.time);
62311
62312             for (var j = 0; j < c.extendedValues.length; j++) {
62313               var _c$extendedValues$j = _slicedToArray(c.extendedValues[j], 2),
62314                   name = _c$extendedValues$j[0],
62315                   val = _c$extendedValues$j[1];
62316
62317               var plural = name === "heart" ? name : name + "s";
62318
62319               if (!extendedValues[plural]) {
62320                 extendedValues[plural] = Array(pts.length).fill(null);
62321               }
62322
62323               extendedValues[plural][i] = val;
62324             }
62325           }
62326
62327           return {
62328             line: line,
62329             times: times,
62330             extendedValues: extendedValues
62331           };
62332         }
62333
62334         function getTrack(node) {
62335           var segments = node.getElementsByTagName("trkseg");
62336           var track = [];
62337           var times = [];
62338           var extractedLines = [];
62339
62340           for (var i = 0; i < segments.length; i++) {
62341             var line = getPoints$1(segments[i], "trkpt");
62342
62343             if (line) {
62344               extractedLines.push(line);
62345               if (line.times && line.times.length) times.push(line.times);
62346             }
62347           }
62348
62349           if (extractedLines.length === 0) return;
62350           var multi = extractedLines.length > 1;
62351           var properties = Object.assign(getProperties$1(node), getLineStyle(get1(node, "extensions")), {
62352             _gpxType: "trk"
62353           }, times.length ? {
62354             coordinateProperties: {
62355               times: multi ? times : times[0]
62356             }
62357           } : {});
62358
62359           for (var _i3 = 0; _i3 < extractedLines.length; _i3++) {
62360             var _line = extractedLines[_i3];
62361             track.push(_line.line);
62362
62363             for (var _i4 = 0, _Object$entries = Object.entries(_line.extendedValues); _i4 < _Object$entries.length; _i4++) {
62364               var _Object$entries$_i = _slicedToArray(_Object$entries[_i4], 2),
62365                   name = _Object$entries$_i[0],
62366                   val = _Object$entries$_i[1];
62367
62368               var props = properties;
62369
62370               if (name === "heart") {
62371                 if (!properties.coordinateProperties) {
62372                   properties.coordinateProperties = {};
62373                 }
62374
62375                 props = properties.coordinateProperties;
62376               }
62377
62378               if (multi) {
62379                 if (!props[name]) props[name] = extractedLines.map(function (line) {
62380                   return new Array(line.line.length).fill(null);
62381                 });
62382                 props[name][_i3] = val;
62383               } else {
62384                 props[name] = val;
62385               }
62386             }
62387           }
62388
62389           return {
62390             type: "Feature",
62391             properties: properties,
62392             geometry: multi ? {
62393               type: "MultiLineString",
62394               coordinates: track
62395             } : {
62396               type: "LineString",
62397               coordinates: track[0]
62398             }
62399           };
62400         }
62401
62402         function getPoint(node) {
62403           return {
62404             type: "Feature",
62405             properties: Object.assign(getProperties$1(node), getMulti(node, ["sym"])),
62406             geometry: {
62407               type: "Point",
62408               coordinates: coordPair$1(node).coordinates
62409             }
62410           };
62411         }
62412
62413         function gpxGen(doc) {
62414           var tracks, routes, waypoints, i, feature, _i5, _feature, _i6;
62415
62416           return regeneratorRuntime.wrap(function gpxGen$(_context) {
62417             while (1) {
62418               switch (_context.prev = _context.next) {
62419                 case 0:
62420                   tracks = doc.getElementsByTagName("trk");
62421                   routes = doc.getElementsByTagName("rte");
62422                   waypoints = doc.getElementsByTagName("wpt");
62423                   i = 0;
62424
62425                 case 4:
62426                   if (!(i < tracks.length)) {
62427                     _context.next = 12;
62428                     break;
62429                   }
62430
62431                   feature = getTrack(tracks[i]);
62432
62433                   if (!feature) {
62434                     _context.next = 9;
62435                     break;
62436                   }
62437
62438                   _context.next = 9;
62439                   return feature;
62440
62441                 case 9:
62442                   i++;
62443                   _context.next = 4;
62444                   break;
62445
62446                 case 12:
62447                   _i5 = 0;
62448
62449                 case 13:
62450                   if (!(_i5 < routes.length)) {
62451                     _context.next = 21;
62452                     break;
62453                   }
62454
62455                   _feature = getRoute(routes[_i5]);
62456
62457                   if (!_feature) {
62458                     _context.next = 18;
62459                     break;
62460                   }
62461
62462                   _context.next = 18;
62463                   return _feature;
62464
62465                 case 18:
62466                   _i5++;
62467                   _context.next = 13;
62468                   break;
62469
62470                 case 21:
62471                   _i6 = 0;
62472
62473                 case 22:
62474                   if (!(_i6 < waypoints.length)) {
62475                     _context.next = 28;
62476                     break;
62477                   }
62478
62479                   _context.next = 25;
62480                   return getPoint(waypoints[_i6]);
62481
62482                 case 25:
62483                   _i6++;
62484                   _context.next = 22;
62485                   break;
62486
62487                 case 28:
62488                 case "end":
62489                   return _context.stop();
62490               }
62491             }
62492           }, _marked);
62493         }
62494
62495         function gpx(doc) {
62496           return {
62497             type: "FeatureCollection",
62498             features: Array.from(gpxGen(doc))
62499           };
62500         }
62501
62502         var removeSpace = /\s*/g;
62503         var trimSpace = /^\s*|\s*$/g;
62504         var splitSpace = /\s+/; // generate a short, numeric hash of a string
62505
62506         function okhash(x) {
62507           if (!x || !x.length) return 0;
62508           var h = 0;
62509
62510           for (var i = 0; i < x.length; i++) {
62511             h = (h << 5) - h + x.charCodeAt(i) | 0;
62512           }
62513
62514           return h;
62515         } // get one coordinate from a coordinate array, if any
62516
62517
62518         function coord1(v) {
62519           return v.replace(removeSpace, "").split(",").map(parseFloat);
62520         } // get all coordinates from a coordinate array as [[],[]]
62521
62522
62523         function coord(v) {
62524           return v.replace(trimSpace, "").split(splitSpace).map(coord1);
62525         }
62526
62527         function xml2str(node) {
62528           if (node.xml !== undefined) return node.xml;
62529
62530           if (node.tagName) {
62531             var output = node.tagName;
62532
62533             for (var i = 0; i < node.attributes.length; i++) {
62534               output += node.attributes[i].name + node.attributes[i].value;
62535             }
62536
62537             for (var _i9 = 0; _i9 < node.childNodes.length; _i9++) {
62538               output += xml2str(node.childNodes[_i9]);
62539             }
62540
62541             return output;
62542           }
62543
62544           if (node.nodeName === "#text") {
62545             return (node.nodeValue || node.value || "").trim();
62546           }
62547
62548           if (node.nodeName === "#cdata-section") {
62549             return node.nodeValue;
62550           }
62551
62552           return "";
62553         }
62554
62555         var geotypes = ["Polygon", "LineString", "Point", "Track", "gx:Track"];
62556
62557         function kmlColor(properties, elem, prefix) {
62558           var v = nodeVal(get1(elem, "color")) || "";
62559           var colorProp = prefix == "stroke" || prefix === "fill" ? prefix : prefix + "-color";
62560
62561           if (v.substr(0, 1) === "#") {
62562             v = v.substr(1);
62563           }
62564
62565           if (v.length === 6 || v.length === 3) {
62566             properties[colorProp] = v;
62567           } else if (v.length === 8) {
62568             properties[prefix + "-opacity"] = parseInt(v.substr(0, 2), 16) / 255;
62569             properties[colorProp] = "#" + v.substr(6, 2) + v.substr(4, 2) + v.substr(2, 2);
62570           }
62571         }
62572
62573         function numericProperty(properties, elem, source, target) {
62574           var val = parseFloat(nodeVal(get1(elem, source)));
62575           if (!isNaN(val)) properties[target] = val;
62576         }
62577
62578         function gxCoords(root) {
62579           var elems = root.getElementsByTagName("coord");
62580           var coords = [];
62581           var times = [];
62582           if (elems.length === 0) elems = root.getElementsByTagName("gx:coord");
62583
62584           for (var i = 0; i < elems.length; i++) {
62585             coords.push(nodeVal(elems[i]).split(" ").map(parseFloat));
62586           }
62587
62588           var timeElems = root.getElementsByTagName("when");
62589
62590           for (var j = 0; j < timeElems.length; j++) {
62591             times.push(nodeVal(timeElems[j]));
62592           }
62593
62594           return {
62595             coords: coords,
62596             times: times
62597           };
62598         }
62599
62600         function getGeometry(root) {
62601           var geomNode;
62602           var geomNodes;
62603           var i;
62604           var j;
62605           var k;
62606           var geoms = [];
62607           var coordTimes = [];
62608
62609           if (get1(root, "MultiGeometry")) {
62610             return getGeometry(get1(root, "MultiGeometry"));
62611           }
62612
62613           if (get1(root, "MultiTrack")) {
62614             return getGeometry(get1(root, "MultiTrack"));
62615           }
62616
62617           if (get1(root, "gx:MultiTrack")) {
62618             return getGeometry(get1(root, "gx:MultiTrack"));
62619           }
62620
62621           for (i = 0; i < geotypes.length; i++) {
62622             geomNodes = root.getElementsByTagName(geotypes[i]);
62623
62624             if (geomNodes) {
62625               for (j = 0; j < geomNodes.length; j++) {
62626                 geomNode = geomNodes[j];
62627
62628                 if (geotypes[i] === "Point") {
62629                   geoms.push({
62630                     type: "Point",
62631                     coordinates: coord1(nodeVal(get1(geomNode, "coordinates")))
62632                   });
62633                 } else if (geotypes[i] === "LineString") {
62634                   geoms.push({
62635                     type: "LineString",
62636                     coordinates: coord(nodeVal(get1(geomNode, "coordinates")))
62637                   });
62638                 } else if (geotypes[i] === "Polygon") {
62639                   var rings = geomNode.getElementsByTagName("LinearRing"),
62640                       coords = [];
62641
62642                   for (k = 0; k < rings.length; k++) {
62643                     coords.push(coord(nodeVal(get1(rings[k], "coordinates"))));
62644                   }
62645
62646                   geoms.push({
62647                     type: "Polygon",
62648                     coordinates: coords
62649                   });
62650                 } else if (geotypes[i] === "Track" || geotypes[i] === "gx:Track") {
62651                   var track = gxCoords(geomNode);
62652                   geoms.push({
62653                     type: "LineString",
62654                     coordinates: track.coords
62655                   });
62656                   if (track.times.length) coordTimes.push(track.times);
62657                 }
62658               }
62659             }
62660           }
62661
62662           return {
62663             geoms: geoms,
62664             coordTimes: coordTimes
62665           };
62666         }
62667
62668         function getPlacemark(root, styleIndex, styleMapIndex, styleByHash) {
62669           var geomsAndTimes = getGeometry(root);
62670           var i;
62671           var properties = {};
62672           var name = nodeVal(get1(root, "name"));
62673           var address = nodeVal(get1(root, "address"));
62674           var styleUrl = nodeVal(get1(root, "styleUrl"));
62675           var description = nodeVal(get1(root, "description"));
62676           var timeSpan = get1(root, "TimeSpan");
62677           var timeStamp = get1(root, "TimeStamp");
62678           var extendedData = get1(root, "ExtendedData");
62679           var iconStyle = get1(root, "IconStyle");
62680           var labelStyle = get1(root, "LabelStyle");
62681           var lineStyle = get1(root, "LineStyle");
62682           var polyStyle = get1(root, "PolyStyle");
62683           var visibility = get1(root, "visibility");
62684           if (name) properties.name = name;
62685           if (address) properties.address = address;
62686
62687           if (styleUrl) {
62688             if (styleUrl[0] !== "#") {
62689               styleUrl = "#" + styleUrl;
62690             }
62691
62692             properties.styleUrl = styleUrl;
62693
62694             if (styleIndex[styleUrl]) {
62695               properties.styleHash = styleIndex[styleUrl];
62696             }
62697
62698             if (styleMapIndex[styleUrl]) {
62699               properties.styleMapHash = styleMapIndex[styleUrl];
62700               properties.styleHash = styleIndex[styleMapIndex[styleUrl].normal];
62701             } // Try to populate the lineStyle or polyStyle since we got the style hash
62702
62703
62704             var style = styleByHash[properties.styleHash];
62705
62706             if (style) {
62707               if (!iconStyle) iconStyle = get1(style, "IconStyle");
62708               if (!labelStyle) labelStyle = get1(style, "LabelStyle");
62709               if (!lineStyle) lineStyle = get1(style, "LineStyle");
62710               if (!polyStyle) polyStyle = get1(style, "PolyStyle");
62711             }
62712           }
62713
62714           if (description) properties.description = description;
62715
62716           if (timeSpan) {
62717             var begin = nodeVal(get1(timeSpan, "begin"));
62718             var end = nodeVal(get1(timeSpan, "end"));
62719             properties.timespan = {
62720               begin: begin,
62721               end: end
62722             };
62723           }
62724
62725           if (timeStamp) {
62726             properties.timestamp = nodeVal(get1(timeStamp, "when"));
62727           }
62728
62729           if (iconStyle) {
62730             kmlColor(properties, iconStyle, "icon");
62731             numericProperty(properties, iconStyle, "scale", "icon-scale");
62732             numericProperty(properties, iconStyle, "heading", "icon-heading");
62733             var hotspot = get1(iconStyle, "hotSpot");
62734
62735             if (hotspot) {
62736               var left = parseFloat(hotspot.getAttribute("x"));
62737               var top = parseFloat(hotspot.getAttribute("y"));
62738               if (!isNaN(left) && !isNaN(top)) properties["icon-offset"] = [left, top];
62739             }
62740
62741             var icon = get1(iconStyle, "Icon");
62742
62743             if (icon) {
62744               var href = nodeVal(get1(icon, "href"));
62745               if (href) properties.icon = href;
62746             }
62747           }
62748
62749           if (labelStyle) {
62750             kmlColor(properties, labelStyle, "label");
62751             numericProperty(properties, labelStyle, "scale", "label-scale");
62752           }
62753
62754           if (lineStyle) {
62755             kmlColor(properties, lineStyle, "stroke");
62756             numericProperty(properties, lineStyle, "width", "stroke-width");
62757           }
62758
62759           if (polyStyle) {
62760             kmlColor(properties, polyStyle, "fill");
62761             var fill = nodeVal(get1(polyStyle, "fill"));
62762             var outline = nodeVal(get1(polyStyle, "outline"));
62763             if (fill) properties["fill-opacity"] = fill === "1" ? properties["fill-opacity"] || 1 : 0;
62764             if (outline) properties["stroke-opacity"] = outline === "1" ? properties["stroke-opacity"] || 1 : 0;
62765           }
62766
62767           if (extendedData) {
62768             var datas = extendedData.getElementsByTagName("Data"),
62769                 simpleDatas = extendedData.getElementsByTagName("SimpleData");
62770
62771             for (i = 0; i < datas.length; i++) {
62772               properties[datas[i].getAttribute("name")] = nodeVal(get1(datas[i], "value"));
62773             }
62774
62775             for (i = 0; i < simpleDatas.length; i++) {
62776               properties[simpleDatas[i].getAttribute("name")] = nodeVal(simpleDatas[i]);
62777             }
62778           }
62779
62780           if (visibility) {
62781             properties.visibility = nodeVal(visibility);
62782           }
62783
62784           if (geomsAndTimes.coordTimes.length) {
62785             properties.coordinateProperties = {
62786               times: geomsAndTimes.coordTimes.length === 1 ? geomsAndTimes.coordTimes[0] : geomsAndTimes.coordTimes
62787             };
62788           }
62789
62790           var feature = {
62791             type: "Feature",
62792             geometry: geomsAndTimes.geoms.length === 0 ? null : geomsAndTimes.geoms.length === 1 ? geomsAndTimes.geoms[0] : {
62793               type: "GeometryCollection",
62794               geometries: geomsAndTimes.geoms
62795             },
62796             properties: properties
62797           };
62798           if (root.getAttribute("id")) feature.id = root.getAttribute("id");
62799           return feature;
62800         }
62801
62802         function kmlGen(doc) {
62803           var styleIndex, styleByHash, styleMapIndex, placemarks, styles, styleMaps, k, hash, l, pairs, pairsMap, m, j, feature;
62804           return regeneratorRuntime.wrap(function kmlGen$(_context3) {
62805             while (1) {
62806               switch (_context3.prev = _context3.next) {
62807                 case 0:
62808                   // styleindex keeps track of hashed styles in order to match feature
62809                   styleIndex = {};
62810                   styleByHash = {}; // stylemapindex keeps track of style maps to expose in properties
62811
62812                   styleMapIndex = {}; // atomic geospatial types supported by KML - MultiGeometry is
62813                   // handled separately
62814                   // all root placemarks in the file
62815
62816                   placemarks = doc.getElementsByTagName("Placemark");
62817                   styles = doc.getElementsByTagName("Style");
62818                   styleMaps = doc.getElementsByTagName("StyleMap");
62819
62820                   for (k = 0; k < styles.length; k++) {
62821                     hash = okhash(xml2str(styles[k])).toString(16);
62822                     styleIndex["#" + styles[k].getAttribute("id")] = hash;
62823                     styleByHash[hash] = styles[k];
62824                   }
62825
62826                   for (l = 0; l < styleMaps.length; l++) {
62827                     styleIndex["#" + styleMaps[l].getAttribute("id")] = okhash(xml2str(styleMaps[l])).toString(16);
62828                     pairs = styleMaps[l].getElementsByTagName("Pair");
62829                     pairsMap = {};
62830
62831                     for (m = 0; m < pairs.length; m++) {
62832                       pairsMap[nodeVal(get1(pairs[m], "key"))] = nodeVal(get1(pairs[m], "styleUrl"));
62833                     }
62834
62835                     styleMapIndex["#" + styleMaps[l].getAttribute("id")] = pairsMap;
62836                   }
62837
62838                   j = 0;
62839
62840                 case 9:
62841                   if (!(j < placemarks.length)) {
62842                     _context3.next = 17;
62843                     break;
62844                   }
62845
62846                   feature = getPlacemark(placemarks[j], styleIndex, styleMapIndex, styleByHash);
62847
62848                   if (!feature) {
62849                     _context3.next = 14;
62850                     break;
62851                   }
62852
62853                   _context3.next = 14;
62854                   return feature;
62855
62856                 case 14:
62857                   j++;
62858                   _context3.next = 9;
62859                   break;
62860
62861                 case 17:
62862                 case "end":
62863                   return _context3.stop();
62864               }
62865             }
62866           }, _marked3);
62867         }
62868
62869         function kml(doc) {
62870           return {
62871             type: "FeatureCollection",
62872             features: Array.from(kmlGen(doc))
62873           };
62874         }
62875
62876         var _initialized = false;
62877         var _enabled = false;
62878
62879         var _geojson;
62880
62881         function svgData(projection, context, dispatch) {
62882           var throttledRedraw = throttle(function () {
62883             dispatch.call('change');
62884           }, 1000);
62885
62886           var _showLabels = true;
62887           var detected = utilDetect();
62888           var layer = select(null);
62889
62890           var _vtService;
62891
62892           var _fileList;
62893
62894           var _template;
62895
62896           var _src;
62897
62898           function init() {
62899             if (_initialized) return; // run once
62900
62901             _geojson = {};
62902             _enabled = true;
62903
62904             function over(d3_event) {
62905               d3_event.stopPropagation();
62906               d3_event.preventDefault();
62907               d3_event.dataTransfer.dropEffect = 'copy';
62908             }
62909
62910             context.container().attr('dropzone', 'copy').on('drop.svgData', function (d3_event) {
62911               d3_event.stopPropagation();
62912               d3_event.preventDefault();
62913               if (!detected.filedrop) return;
62914               drawData.fileList(d3_event.dataTransfer.files);
62915             }).on('dragenter.svgData', over).on('dragexit.svgData', over).on('dragover.svgData', over);
62916             _initialized = true;
62917           }
62918
62919           function getService() {
62920             if (services.vectorTile && !_vtService) {
62921               _vtService = services.vectorTile;
62922
62923               _vtService.event.on('loadedData', throttledRedraw);
62924             } else if (!services.vectorTile && _vtService) {
62925               _vtService = null;
62926             }
62927
62928             return _vtService;
62929           }
62930
62931           function showLayer() {
62932             layerOn();
62933             layer.style('opacity', 0).transition().duration(250).style('opacity', 1).on('end', function () {
62934               dispatch.call('change');
62935             });
62936           }
62937
62938           function hideLayer() {
62939             throttledRedraw.cancel();
62940             layer.transition().duration(250).style('opacity', 0).on('end', layerOff);
62941           }
62942
62943           function layerOn() {
62944             layer.style('display', 'block');
62945           }
62946
62947           function layerOff() {
62948             layer.selectAll('.viewfield-group').remove();
62949             layer.style('display', 'none');
62950           } // ensure that all geojson features in a collection have IDs
62951
62952
62953           function ensureIDs(gj) {
62954             if (!gj) return null;
62955
62956             if (gj.type === 'FeatureCollection') {
62957               for (var i = 0; i < gj.features.length; i++) {
62958                 ensureFeatureID(gj.features[i]);
62959               }
62960             } else {
62961               ensureFeatureID(gj);
62962             }
62963
62964             return gj;
62965           } // ensure that each single Feature object has a unique ID
62966
62967
62968           function ensureFeatureID(feature) {
62969             if (!feature) return;
62970             feature.__featurehash__ = utilHashcode(fastJsonStableStringify(feature));
62971             return feature;
62972           } // Prefer an array of Features instead of a FeatureCollection
62973
62974
62975           function getFeatures(gj) {
62976             if (!gj) return [];
62977
62978             if (gj.type === 'FeatureCollection') {
62979               return gj.features;
62980             } else {
62981               return [gj];
62982             }
62983           }
62984
62985           function featureKey(d) {
62986             return d.__featurehash__;
62987           }
62988
62989           function isPolygon(d) {
62990             return d.geometry.type === 'Polygon' || d.geometry.type === 'MultiPolygon';
62991           }
62992
62993           function clipPathID(d) {
62994             return 'ideditor-data-' + d.__featurehash__ + '-clippath';
62995           }
62996
62997           function featureClasses(d) {
62998             return ['data' + d.__featurehash__, d.geometry.type, isPolygon(d) ? 'area' : '', d.__layerID__ || ''].filter(Boolean).join(' ');
62999           }
63000
63001           function drawData(selection) {
63002             var vtService = getService();
63003             var getPath = svgPath(projection).geojson;
63004             var getAreaPath = svgPath(projection, null, true).geojson;
63005             var hasData = drawData.hasData();
63006             layer = selection.selectAll('.layer-mapdata').data(_enabled && hasData ? [0] : []);
63007             layer.exit().remove();
63008             layer = layer.enter().append('g').attr('class', 'layer-mapdata').merge(layer);
63009             var surface = context.surface();
63010             if (!surface || surface.empty()) return; // not ready to draw yet, starting up
63011             // Gather data
63012
63013             var geoData, polygonData;
63014
63015             if (_template && vtService) {
63016               // fetch data from vector tile service
63017               var sourceID = _template;
63018               vtService.loadTiles(sourceID, _template, projection);
63019               geoData = vtService.data(sourceID, projection);
63020             } else {
63021               geoData = getFeatures(_geojson);
63022             }
63023
63024             geoData = geoData.filter(getPath);
63025             polygonData = geoData.filter(isPolygon); // Draw clip paths for polygons
63026
63027             var clipPaths = surface.selectAll('defs').selectAll('.clipPath-data').data(polygonData, featureKey);
63028             clipPaths.exit().remove();
63029             var clipPathsEnter = clipPaths.enter().append('clipPath').attr('class', 'clipPath-data').attr('id', clipPathID);
63030             clipPathsEnter.append('path');
63031             clipPaths.merge(clipPathsEnter).selectAll('path').attr('d', getAreaPath); // Draw fill, shadow, stroke layers
63032
63033             var datagroups = layer.selectAll('g.datagroup').data(['fill', 'shadow', 'stroke']);
63034             datagroups = datagroups.enter().append('g').attr('class', function (d) {
63035               return 'datagroup datagroup-' + d;
63036             }).merge(datagroups); // Draw paths
63037
63038             var pathData = {
63039               fill: polygonData,
63040               shadow: geoData,
63041               stroke: geoData
63042             };
63043             var paths = datagroups.selectAll('path').data(function (layer) {
63044               return pathData[layer];
63045             }, featureKey); // exit
63046
63047             paths.exit().remove(); // enter/update
63048
63049             paths = paths.enter().append('path').attr('class', function (d) {
63050               var datagroup = this.parentNode.__data__;
63051               return 'pathdata ' + datagroup + ' ' + featureClasses(d);
63052             }).attr('clip-path', function (d) {
63053               var datagroup = this.parentNode.__data__;
63054               return datagroup === 'fill' ? 'url(#' + clipPathID(d) + ')' : null;
63055             }).merge(paths).attr('d', function (d) {
63056               var datagroup = this.parentNode.__data__;
63057               return datagroup === 'fill' ? getAreaPath(d) : getPath(d);
63058             }); // Draw labels
63059
63060             layer.call(drawLabels, 'label-halo', geoData).call(drawLabels, 'label', geoData);
63061
63062             function drawLabels(selection, textClass, data) {
63063               var labelPath = d3_geoPath(projection);
63064               var labelData = data.filter(function (d) {
63065                 return _showLabels && d.properties && (d.properties.desc || d.properties.name);
63066               });
63067               var labels = selection.selectAll('text.' + textClass).data(labelData, featureKey); // exit
63068
63069               labels.exit().remove(); // enter/update
63070
63071               labels = labels.enter().append('text').attr('class', function (d) {
63072                 return textClass + ' ' + featureClasses(d);
63073               }).merge(labels).text(function (d) {
63074                 return d.properties.desc || d.properties.name;
63075               }).attr('x', function (d) {
63076                 var centroid = labelPath.centroid(d);
63077                 return centroid[0] + 11;
63078               }).attr('y', function (d) {
63079                 var centroid = labelPath.centroid(d);
63080                 return centroid[1];
63081               });
63082             }
63083           }
63084
63085           function getExtension(fileName) {
63086             if (!fileName) return;
63087             var re = /\.(gpx|kml|(geo)?json)$/i;
63088             var match = fileName.toLowerCase().match(re);
63089             return match && match.length && match[0];
63090           }
63091
63092           function xmlToDom(textdata) {
63093             return new DOMParser().parseFromString(textdata, 'text/xml');
63094           }
63095
63096           drawData.setFile = function (extension, data) {
63097             _template = null;
63098             _fileList = null;
63099             _geojson = null;
63100             _src = null;
63101             var gj;
63102
63103             switch (extension) {
63104               case '.gpx':
63105                 gj = gpx(xmlToDom(data));
63106                 break;
63107
63108               case '.kml':
63109                 gj = kml(xmlToDom(data));
63110                 break;
63111
63112               case '.geojson':
63113               case '.json':
63114                 gj = JSON.parse(data);
63115                 break;
63116             }
63117
63118             gj = gj || {};
63119
63120             if (Object.keys(gj).length) {
63121               _geojson = ensureIDs(gj);
63122               _src = extension + ' data file';
63123               this.fitZoom();
63124             }
63125
63126             dispatch.call('change');
63127             return this;
63128           };
63129
63130           drawData.showLabels = function (val) {
63131             if (!arguments.length) return _showLabels;
63132             _showLabels = val;
63133             return this;
63134           };
63135
63136           drawData.enabled = function (val) {
63137             if (!arguments.length) return _enabled;
63138             _enabled = val;
63139
63140             if (_enabled) {
63141               showLayer();
63142             } else {
63143               hideLayer();
63144             }
63145
63146             dispatch.call('change');
63147             return this;
63148           };
63149
63150           drawData.hasData = function () {
63151             var gj = _geojson || {};
63152             return !!(_template || Object.keys(gj).length);
63153           };
63154
63155           drawData.template = function (val, src) {
63156             if (!arguments.length) return _template; // test source against OSM imagery blocklists..
63157
63158             var osm = context.connection();
63159
63160             if (osm) {
63161               var blocklists = osm.imageryBlocklists();
63162               var fail = false;
63163               var tested = 0;
63164               var regex;
63165
63166               for (var i = 0; i < blocklists.length; i++) {
63167                 regex = blocklists[i];
63168                 fail = regex.test(val);
63169                 tested++;
63170                 if (fail) break;
63171               } // ensure at least one test was run.
63172
63173
63174               if (!tested) {
63175                 regex = /.*\.google(apis)?\..*\/(vt|kh)[\?\/].*([xyz]=.*){3}.*/;
63176                 fail = regex.test(val);
63177               }
63178             }
63179
63180             _template = val;
63181             _fileList = null;
63182             _geojson = null; // strip off the querystring/hash from the template,
63183             // it often includes the access token
63184
63185             _src = src || 'vectortile:' + val.split(/[?#]/)[0];
63186             dispatch.call('change');
63187             return this;
63188           };
63189
63190           drawData.geojson = function (gj, src) {
63191             if (!arguments.length) return _geojson;
63192             _template = null;
63193             _fileList = null;
63194             _geojson = null;
63195             _src = null;
63196             gj = gj || {};
63197
63198             if (Object.keys(gj).length) {
63199               _geojson = ensureIDs(gj);
63200               _src = src || 'unknown.geojson';
63201             }
63202
63203             dispatch.call('change');
63204             return this;
63205           };
63206
63207           drawData.fileList = function (fileList) {
63208             if (!arguments.length) return _fileList;
63209             _template = null;
63210             _fileList = fileList;
63211             _geojson = null;
63212             _src = null;
63213             if (!fileList || !fileList.length) return this;
63214             var f = fileList[0];
63215             var extension = getExtension(f.name);
63216             var reader = new FileReader();
63217
63218             reader.onload = function () {
63219               return function (e) {
63220                 drawData.setFile(extension, e.target.result);
63221               };
63222             }();
63223
63224             reader.readAsText(f);
63225             return this;
63226           };
63227
63228           drawData.url = function (url, defaultExtension) {
63229             _template = null;
63230             _fileList = null;
63231             _geojson = null;
63232             _src = null; // strip off any querystring/hash from the url before checking extension
63233
63234             var testUrl = url.split(/[?#]/)[0];
63235             var extension = getExtension(testUrl) || defaultExtension;
63236
63237             if (extension) {
63238               _template = null;
63239               d3_text(url).then(function (data) {
63240                 drawData.setFile(extension, data);
63241               })["catch"](function () {
63242                 /* ignore */
63243               });
63244             } else {
63245               drawData.template(url);
63246             }
63247
63248             return this;
63249           };
63250
63251           drawData.getSrc = function () {
63252             return _src || '';
63253           };
63254
63255           drawData.fitZoom = function () {
63256             var features = getFeatures(_geojson);
63257             if (!features.length) return;
63258             var map = context.map();
63259             var viewport = map.trimmedExtent().polygon();
63260             var coords = features.reduce(function (coords, feature) {
63261               var geom = feature.geometry;
63262               if (!geom) return coords;
63263               var c = geom.coordinates;
63264               /* eslint-disable no-fallthrough */
63265
63266               switch (geom.type) {
63267                 case 'Point':
63268                   c = [c];
63269
63270                 case 'MultiPoint':
63271                 case 'LineString':
63272                   break;
63273
63274                 case 'MultiPolygon':
63275                   c = utilArrayFlatten(c);
63276
63277                 case 'Polygon':
63278                 case 'MultiLineString':
63279                   c = utilArrayFlatten(c);
63280                   break;
63281               }
63282               /* eslint-enable no-fallthrough */
63283
63284
63285               return utilArrayUnion(coords, c);
63286             }, []);
63287
63288             if (!geoPolygonIntersectsPolygon(viewport, coords, true)) {
63289               var extent = geoExtent(d3_geoBounds({
63290                 type: 'LineString',
63291                 coordinates: coords
63292               }));
63293               map.centerZoom(extent.center(), map.trimmedExtentZoom(extent));
63294             }
63295
63296             return this;
63297           };
63298
63299           init();
63300           return drawData;
63301         }
63302
63303         function svgDebug(projection, context) {
63304           function drawDebug(selection) {
63305             var showTile = context.getDebug('tile');
63306             var showCollision = context.getDebug('collision');
63307             var showImagery = context.getDebug('imagery');
63308             var showTouchTargets = context.getDebug('target');
63309             var showDownloaded = context.getDebug('downloaded');
63310             var debugData = [];
63311
63312             if (showTile) {
63313               debugData.push({
63314                 "class": 'red',
63315                 label: 'tile'
63316               });
63317             }
63318
63319             if (showCollision) {
63320               debugData.push({
63321                 "class": 'yellow',
63322                 label: 'collision'
63323               });
63324             }
63325
63326             if (showImagery) {
63327               debugData.push({
63328                 "class": 'orange',
63329                 label: 'imagery'
63330               });
63331             }
63332
63333             if (showTouchTargets) {
63334               debugData.push({
63335                 "class": 'pink',
63336                 label: 'touchTargets'
63337               });
63338             }
63339
63340             if (showDownloaded) {
63341               debugData.push({
63342                 "class": 'purple',
63343                 label: 'downloaded'
63344               });
63345             }
63346
63347             var legend = context.container().select('.main-content').selectAll('.debug-legend').data(debugData.length ? [0] : []);
63348             legend.exit().remove();
63349             legend = legend.enter().append('div').attr('class', 'fillD debug-legend').merge(legend);
63350             var legendItems = legend.selectAll('.debug-legend-item').data(debugData, function (d) {
63351               return d.label;
63352             });
63353             legendItems.exit().remove();
63354             legendItems.enter().append('span').attr('class', function (d) {
63355               return "debug-legend-item ".concat(d["class"]);
63356             }).text(function (d) {
63357               return d.label;
63358             });
63359             var layer = selection.selectAll('.layer-debug').data(showImagery || showDownloaded ? [0] : []);
63360             layer.exit().remove();
63361             layer = layer.enter().append('g').attr('class', 'layer-debug').merge(layer); // imagery
63362
63363             var extent = context.map().extent();
63364             _mainFileFetcher.get('imagery').then(function (d) {
63365               var hits = showImagery && d.query.bbox(extent.rectangle(), true) || [];
63366               var features = hits.map(function (d) {
63367                 return d.features[d.id];
63368               });
63369               var imagery = layer.selectAll('path.debug-imagery').data(features);
63370               imagery.exit().remove();
63371               imagery.enter().append('path').attr('class', 'debug-imagery debug orange');
63372             })["catch"](function () {
63373               /* ignore */
63374             }); // downloaded
63375
63376             var osm = context.connection();
63377             var dataDownloaded = [];
63378
63379             if (osm && showDownloaded) {
63380               var rtree = osm.caches('get').tile.rtree;
63381               dataDownloaded = rtree.all().map(function (bbox) {
63382                 return {
63383                   type: 'Feature',
63384                   properties: {
63385                     id: bbox.id
63386                   },
63387                   geometry: {
63388                     type: 'Polygon',
63389                     coordinates: [[[bbox.minX, bbox.minY], [bbox.minX, bbox.maxY], [bbox.maxX, bbox.maxY], [bbox.maxX, bbox.minY], [bbox.minX, bbox.minY]]]
63390                   }
63391                 };
63392               });
63393             }
63394
63395             var downloaded = layer.selectAll('path.debug-downloaded').data(showDownloaded ? dataDownloaded : []);
63396             downloaded.exit().remove();
63397             downloaded.enter().append('path').attr('class', 'debug-downloaded debug purple'); // update
63398
63399             layer.selectAll('path').attr('d', svgPath(projection).geojson);
63400           } // This looks strange because `enabled` methods on other layers are
63401           // chainable getter/setters, and this one is just a getter.
63402
63403
63404           drawDebug.enabled = function () {
63405             if (!arguments.length) {
63406               return context.getDebug('tile') || context.getDebug('collision') || context.getDebug('imagery') || context.getDebug('target') || context.getDebug('downloaded');
63407             } else {
63408               return this;
63409             }
63410           };
63411
63412           return drawDebug;
63413         }
63414
63415         /*
63416             A standalone SVG element that contains only a `defs` sub-element. To be
63417             used once globally, since defs IDs must be unique within a document.
63418         */
63419
63420         function svgDefs(context) {
63421           var _defsSelection = select(null);
63422
63423           var _spritesheetIds = ['iD-sprite', 'maki-sprite', 'temaki-sprite', 'fa-sprite', 'community-sprite'];
63424
63425           function drawDefs(selection) {
63426             _defsSelection = selection.append('defs'); // add markers
63427
63428             _defsSelection.append('marker').attr('id', 'ideditor-oneway-marker').attr('viewBox', '0 0 10 5').attr('refX', 2.5).attr('refY', 2.5).attr('markerWidth', 2).attr('markerHeight', 2).attr('markerUnits', 'strokeWidth').attr('orient', 'auto').append('path').attr('class', 'oneway-marker-path').attr('d', 'M 5,3 L 0,3 L 0,2 L 5,2 L 5,0 L 10,2.5 L 5,5 z').attr('stroke', 'none').attr('fill', '#000').attr('opacity', '0.75'); // SVG markers have to be given a colour where they're defined
63429             // (they can't inherit it from the line they're attached to),
63430             // so we need to manually define markers for each color of tag
63431             // (also, it's slightly nicer if we can control the
63432             // positioning for different tags)
63433
63434
63435             function addSidedMarker(name, color, offset) {
63436               _defsSelection.append('marker').attr('id', 'ideditor-sided-marker-' + name).attr('viewBox', '0 0 2 2').attr('refX', 1).attr('refY', -offset).attr('markerWidth', 1.5).attr('markerHeight', 1.5).attr('markerUnits', 'strokeWidth').attr('orient', 'auto').append('path').attr('class', 'sided-marker-path sided-marker-' + name + '-path').attr('d', 'M 0,0 L 1,1 L 2,0 z').attr('stroke', 'none').attr('fill', color);
63437             }
63438
63439             addSidedMarker('natural', 'rgb(170, 170, 170)', 0); // for a coastline, the arrows are (somewhat unintuitively) on
63440             // the water side, so let's color them blue (with a gap) to
63441             // give a stronger indication
63442
63443             addSidedMarker('coastline', '#77dede', 1);
63444             addSidedMarker('waterway', '#77dede', 1); // barriers have a dashed line, and separating the triangle
63445             // from the line visually suits that
63446
63447             addSidedMarker('barrier', '#ddd', 1);
63448             addSidedMarker('man_made', '#fff', 0);
63449
63450             _defsSelection.append('marker').attr('id', 'ideditor-viewfield-marker').attr('viewBox', '0 0 16 16').attr('refX', 8).attr('refY', 16).attr('markerWidth', 4).attr('markerHeight', 4).attr('markerUnits', 'strokeWidth').attr('orient', 'auto').append('path').attr('class', 'viewfield-marker-path').attr('d', 'M 6,14 C 8,13.4 8,13.4 10,14 L 16,3 C 12,0 4,0 0,3 z').attr('fill', '#333').attr('fill-opacity', '0.75').attr('stroke', '#fff').attr('stroke-width', '0.5px').attr('stroke-opacity', '0.75');
63451
63452             _defsSelection.append('marker').attr('id', 'ideditor-viewfield-marker-wireframe').attr('viewBox', '0 0 16 16').attr('refX', 8).attr('refY', 16).attr('markerWidth', 4).attr('markerHeight', 4).attr('markerUnits', 'strokeWidth').attr('orient', 'auto').append('path').attr('class', 'viewfield-marker-path').attr('d', 'M 6,14 C 8,13.4 8,13.4 10,14 L 16,3 C 12,0 4,0 0,3 z').attr('fill', 'none').attr('stroke', '#fff').attr('stroke-width', '0.5px').attr('stroke-opacity', '0.75'); // add patterns
63453
63454
63455             var patterns = _defsSelection.selectAll('pattern').data([// pattern name, pattern image name
63456             ['beach', 'dots'], ['construction', 'construction'], ['cemetery', 'cemetery'], ['cemetery_christian', 'cemetery_christian'], ['cemetery_buddhist', 'cemetery_buddhist'], ['cemetery_muslim', 'cemetery_muslim'], ['cemetery_jewish', 'cemetery_jewish'], ['farmland', 'farmland'], ['farmyard', 'farmyard'], ['forest', 'forest'], ['forest_broadleaved', 'forest_broadleaved'], ['forest_needleleaved', 'forest_needleleaved'], ['forest_leafless', 'forest_leafless'], ['golf_green', 'grass'], ['grass', 'grass'], ['landfill', 'landfill'], ['meadow', 'grass'], ['orchard', 'orchard'], ['pond', 'pond'], ['quarry', 'quarry'], ['scrub', 'bushes'], ['vineyard', 'vineyard'], ['water_standing', 'lines'], ['waves', 'waves'], ['wetland', 'wetland'], ['wetland_marsh', 'wetland_marsh'], ['wetland_swamp', 'wetland_swamp'], ['wetland_bog', 'wetland_bog'], ['wetland_reedbed', 'wetland_reedbed']]).enter().append('pattern').attr('id', function (d) {
63457               return 'ideditor-pattern-' + d[0];
63458             }).attr('width', 32).attr('height', 32).attr('patternUnits', 'userSpaceOnUse');
63459
63460             patterns.append('rect').attr('x', 0).attr('y', 0).attr('width', 32).attr('height', 32).attr('class', function (d) {
63461               return 'pattern-color-' + d[0];
63462             });
63463             patterns.append('image').attr('x', 0).attr('y', 0).attr('width', 32).attr('height', 32).attr('xlink:href', function (d) {
63464               return context.imagePath('pattern/' + d[1] + '.png');
63465             }); // add clip paths
63466
63467             _defsSelection.selectAll('clipPath').data([12, 18, 20, 32, 45]).enter().append('clipPath').attr('id', function (d) {
63468               return 'ideditor-clip-square-' + d;
63469             }).append('rect').attr('x', 0).attr('y', 0).attr('width', function (d) {
63470               return d;
63471             }).attr('height', function (d) {
63472               return d;
63473             }); // add symbol spritesheets
63474
63475
63476             addSprites(_spritesheetIds, true);
63477           }
63478
63479           function addSprites(ids, overrideColors) {
63480             _spritesheetIds = utilArrayUniq(_spritesheetIds.concat(ids));
63481
63482             var spritesheets = _defsSelection.selectAll('.spritesheet').data(_spritesheetIds);
63483
63484             spritesheets.enter().append('g').attr('class', function (d) {
63485               return 'spritesheet spritesheet-' + d;
63486             }).each(function (d) {
63487               var url = context.imagePath(d + '.svg');
63488               var node = select(this).node();
63489               svg(url).then(function (svg) {
63490                 node.appendChild(select(svg.documentElement).attr('id', 'ideditor-' + d).node());
63491
63492                 if (overrideColors && d !== 'iD-sprite') {
63493                   // allow icon colors to be overridden..
63494                   select(node).selectAll('path').attr('fill', 'currentColor');
63495                 }
63496               })["catch"](function () {
63497                 /* ignore */
63498               });
63499             });
63500             spritesheets.exit().remove();
63501           }
63502
63503           drawDefs.addSprites = addSprites;
63504           return drawDefs;
63505         }
63506
63507         var _layerEnabled$2 = false;
63508
63509         var _qaService$2;
63510
63511         function svgKeepRight(projection, context, dispatch) {
63512           var throttledRedraw = throttle(function () {
63513             return dispatch.call('change');
63514           }, 1000);
63515
63516           var minZoom = 12;
63517           var touchLayer = select(null);
63518           var drawLayer = select(null);
63519           var layerVisible = false;
63520
63521           function markerPath(selection, klass) {
63522             selection.attr('class', klass).attr('transform', 'translate(-4, -24)').attr('d', 'M11.6,6.2H7.1l1.4-5.1C8.6,0.6,8.1,0,7.5,0H2.2C1.7,0,1.3,0.3,1.3,0.8L0,10.2c-0.1,0.6,0.4,1.1,0.9,1.1h4.6l-1.8,7.6C3.6,19.4,4.1,20,4.7,20c0.3,0,0.6-0.2,0.8-0.5l6.9-11.9C12.7,7,12.3,6.2,11.6,6.2z');
63523           } // Loosely-coupled keepRight service for fetching issues.
63524
63525
63526           function getService() {
63527             if (services.keepRight && !_qaService$2) {
63528               _qaService$2 = services.keepRight;
63529
63530               _qaService$2.on('loaded', throttledRedraw);
63531             } else if (!services.keepRight && _qaService$2) {
63532               _qaService$2 = null;
63533             }
63534
63535             return _qaService$2;
63536           } // Show the markers
63537
63538
63539           function editOn() {
63540             if (!layerVisible) {
63541               layerVisible = true;
63542               drawLayer.style('display', 'block');
63543             }
63544           } // Immediately remove the markers and their touch targets
63545
63546
63547           function editOff() {
63548             if (layerVisible) {
63549               layerVisible = false;
63550               drawLayer.style('display', 'none');
63551               drawLayer.selectAll('.qaItem.keepRight').remove();
63552               touchLayer.selectAll('.qaItem.keepRight').remove();
63553             }
63554           } // Enable the layer.  This shows the markers and transitions them to visible.
63555
63556
63557           function layerOn() {
63558             editOn();
63559             drawLayer.style('opacity', 0).transition().duration(250).style('opacity', 1).on('end interrupt', function () {
63560               return dispatch.call('change');
63561             });
63562           } // Disable the layer.  This transitions the layer invisible and then hides the markers.
63563
63564
63565           function layerOff() {
63566             throttledRedraw.cancel();
63567             drawLayer.interrupt();
63568             touchLayer.selectAll('.qaItem.keepRight').remove();
63569             drawLayer.transition().duration(250).style('opacity', 0).on('end interrupt', function () {
63570               editOff();
63571               dispatch.call('change');
63572             });
63573           } // Update the issue markers
63574
63575
63576           function updateMarkers() {
63577             if (!layerVisible || !_layerEnabled$2) return;
63578             var service = getService();
63579             var selectedID = context.selectedErrorID();
63580             var data = service ? service.getItems(projection) : [];
63581             var getTransform = svgPointTransform(projection); // Draw markers..
63582
63583             var markers = drawLayer.selectAll('.qaItem.keepRight').data(data, function (d) {
63584               return d.id;
63585             }); // exit
63586
63587             markers.exit().remove(); // enter
63588
63589             var markersEnter = markers.enter().append('g').attr('class', function (d) {
63590               return "qaItem ".concat(d.service, " itemId-").concat(d.id, " itemType-").concat(d.parentIssueType);
63591             });
63592             markersEnter.append('ellipse').attr('cx', 0.5).attr('cy', 1).attr('rx', 6.5).attr('ry', 3).attr('class', 'stroke');
63593             markersEnter.append('path').call(markerPath, 'shadow');
63594             markersEnter.append('use').attr('class', 'qaItem-fill').attr('width', '20px').attr('height', '20px').attr('x', '-8px').attr('y', '-22px').attr('xlink:href', '#iD-icon-bolt'); // update
63595
63596             markers.merge(markersEnter).sort(sortY).classed('selected', function (d) {
63597               return d.id === selectedID;
63598             }).attr('transform', getTransform); // Draw targets..
63599
63600             if (touchLayer.empty()) return;
63601             var fillClass = context.getDebug('target') ? 'pink ' : 'nocolor ';
63602             var targets = touchLayer.selectAll('.qaItem.keepRight').data(data, function (d) {
63603               return d.id;
63604             }); // exit
63605
63606             targets.exit().remove(); // enter/update
63607
63608             targets.enter().append('rect').attr('width', '20px').attr('height', '20px').attr('x', '-8px').attr('y', '-22px').merge(targets).sort(sortY).attr('class', function (d) {
63609               return "qaItem ".concat(d.service, " target ").concat(fillClass, " itemId-").concat(d.id);
63610             }).attr('transform', getTransform);
63611
63612             function sortY(a, b) {
63613               return a.id === selectedID ? 1 : b.id === selectedID ? -1 : a.severity === 'error' && b.severity !== 'error' ? 1 : b.severity === 'error' && a.severity !== 'error' ? -1 : b.loc[1] - a.loc[1];
63614             }
63615           } // Draw the keepRight layer and schedule loading issues and updating markers.
63616
63617
63618           function drawKeepRight(selection) {
63619             var service = getService();
63620             var surface = context.surface();
63621
63622             if (surface && !surface.empty()) {
63623               touchLayer = surface.selectAll('.data-layer.touch .layer-touch.markers');
63624             }
63625
63626             drawLayer = selection.selectAll('.layer-keepRight').data(service ? [0] : []);
63627             drawLayer.exit().remove();
63628             drawLayer = drawLayer.enter().append('g').attr('class', 'layer-keepRight').style('display', _layerEnabled$2 ? 'block' : 'none').merge(drawLayer);
63629
63630             if (_layerEnabled$2) {
63631               if (service && ~~context.map().zoom() >= minZoom) {
63632                 editOn();
63633                 service.loadIssues(projection);
63634                 updateMarkers();
63635               } else {
63636                 editOff();
63637               }
63638             }
63639           } // Toggles the layer on and off
63640
63641
63642           drawKeepRight.enabled = function (val) {
63643             if (!arguments.length) return _layerEnabled$2;
63644             _layerEnabled$2 = val;
63645
63646             if (_layerEnabled$2) {
63647               layerOn();
63648             } else {
63649               layerOff();
63650
63651               if (context.selectedErrorID()) {
63652                 context.enter(modeBrowse(context));
63653               }
63654             }
63655
63656             dispatch.call('change');
63657             return this;
63658           };
63659
63660           drawKeepRight.supported = function () {
63661             return !!getService();
63662           };
63663
63664           return drawKeepRight;
63665         }
63666
63667         function svgGeolocate(projection) {
63668           var layer = select(null);
63669
63670           var _position;
63671
63672           function init() {
63673             if (svgGeolocate.initialized) return; // run once
63674
63675             svgGeolocate.enabled = false;
63676             svgGeolocate.initialized = true;
63677           }
63678
63679           function showLayer() {
63680             layer.style('display', 'block');
63681           }
63682
63683           function hideLayer() {
63684             layer.transition().duration(250).style('opacity', 0);
63685           }
63686
63687           function layerOn() {
63688             layer.style('opacity', 0).transition().duration(250).style('opacity', 1);
63689           }
63690
63691           function layerOff() {
63692             layer.style('display', 'none');
63693           }
63694
63695           function transform(d) {
63696             return svgPointTransform(projection)(d);
63697           }
63698
63699           function accuracy(accuracy, loc) {
63700             // converts accuracy to pixels...
63701             var degreesRadius = geoMetersToLat(accuracy),
63702                 tangentLoc = [loc[0], loc[1] + degreesRadius],
63703                 projectedTangent = projection(tangentLoc),
63704                 projectedLoc = projection([loc[0], loc[1]]); // southern most point will have higher pixel value...
63705
63706             return Math.round(projectedLoc[1] - projectedTangent[1]).toString();
63707           }
63708
63709           function update() {
63710             var geolocation = {
63711               loc: [_position.coords.longitude, _position.coords.latitude]
63712             };
63713             var groups = layer.selectAll('.geolocations').selectAll('.geolocation').data([geolocation]);
63714             groups.exit().remove();
63715             var pointsEnter = groups.enter().append('g').attr('class', 'geolocation');
63716             pointsEnter.append('circle').attr('class', 'geolocate-radius').attr('dx', '0').attr('dy', '0').attr('fill', 'rgb(15,128,225)').attr('fill-opacity', '0.3').attr('r', '0');
63717             pointsEnter.append('circle').attr('dx', '0').attr('dy', '0').attr('fill', 'rgb(15,128,225)').attr('stroke', 'white').attr('stroke-width', '1.5').attr('r', '6');
63718             groups.merge(pointsEnter).attr('transform', transform);
63719             layer.select('.geolocate-radius').attr('r', accuracy(_position.coords.accuracy, geolocation.loc));
63720           }
63721
63722           function drawLocation(selection) {
63723             var enabled = svgGeolocate.enabled;
63724             layer = selection.selectAll('.layer-geolocate').data([0]);
63725             layer.exit().remove();
63726             var layerEnter = layer.enter().append('g').attr('class', 'layer-geolocate').style('display', enabled ? 'block' : 'none');
63727             layerEnter.append('g').attr('class', 'geolocations');
63728             layer = layerEnter.merge(layer);
63729
63730             if (enabled) {
63731               update();
63732             } else {
63733               layerOff();
63734             }
63735           }
63736
63737           drawLocation.enabled = function (position, enabled) {
63738             if (!arguments.length) return svgGeolocate.enabled;
63739             _position = position;
63740             svgGeolocate.enabled = enabled;
63741
63742             if (svgGeolocate.enabled) {
63743               showLayer();
63744               layerOn();
63745             } else {
63746               hideLayer();
63747             }
63748
63749             return this;
63750           };
63751
63752           init();
63753           return drawLocation;
63754         }
63755
63756         function svgLabels(projection, context) {
63757           var path = d3_geoPath(projection);
63758           var detected = utilDetect();
63759           var baselineHack = detected.ie || detected.browser.toLowerCase() === 'edge' || detected.browser.toLowerCase() === 'firefox' && detected.version >= 70;
63760
63761           var _rdrawn = new RBush();
63762
63763           var _rskipped = new RBush();
63764
63765           var _textWidthCache = {};
63766           var _entitybboxes = {}; // Listed from highest to lowest priority
63767
63768           var labelStack = [['line', 'aeroway', '*', 12], ['line', 'highway', 'motorway', 12], ['line', 'highway', 'trunk', 12], ['line', 'highway', 'primary', 12], ['line', 'highway', 'secondary', 12], ['line', 'highway', 'tertiary', 12], ['line', 'highway', '*', 12], ['line', 'railway', '*', 12], ['line', 'waterway', '*', 12], ['area', 'aeroway', '*', 12], ['area', 'amenity', '*', 12], ['area', 'building', '*', 12], ['area', 'historic', '*', 12], ['area', 'leisure', '*', 12], ['area', 'man_made', '*', 12], ['area', 'natural', '*', 12], ['area', 'shop', '*', 12], ['area', 'tourism', '*', 12], ['area', 'camp_site', '*', 12], ['point', 'aeroway', '*', 10], ['point', 'amenity', '*', 10], ['point', 'building', '*', 10], ['point', 'historic', '*', 10], ['point', 'leisure', '*', 10], ['point', 'man_made', '*', 10], ['point', 'natural', '*', 10], ['point', 'shop', '*', 10], ['point', 'tourism', '*', 10], ['point', 'camp_site', '*', 10], ['line', 'name', '*', 12], ['area', 'name', '*', 12], ['point', 'name', '*', 10]];
63769
63770           function shouldSkipIcon(preset) {
63771             var noIcons = ['building', 'landuse', 'natural'];
63772             return noIcons.some(function (s) {
63773               return preset.id.indexOf(s) >= 0;
63774             });
63775           }
63776
63777           function get(array, prop) {
63778             return function (d, i) {
63779               return array[i][prop];
63780             };
63781           }
63782
63783           function textWidth(text, size, elem) {
63784             var c = _textWidthCache[size];
63785             if (!c) c = _textWidthCache[size] = {};
63786
63787             if (c[text]) {
63788               return c[text];
63789             } else if (elem) {
63790               c[text] = elem.getComputedTextLength();
63791               return c[text];
63792             } else {
63793               var str = encodeURIComponent(text).match(/%[CDEFcdef]/g);
63794
63795               if (str === null) {
63796                 return size / 3 * 2 * text.length;
63797               } else {
63798                 return size / 3 * (2 * text.length + str.length);
63799               }
63800             }
63801           }
63802
63803           function drawLinePaths(selection, entities, filter, classes, labels) {
63804             var paths = selection.selectAll('path').filter(filter).data(entities, osmEntity.key); // exit
63805
63806             paths.exit().remove(); // enter/update
63807
63808             paths.enter().append('path').style('stroke-width', get(labels, 'font-size')).attr('id', function (d) {
63809               return 'ideditor-labelpath-' + d.id;
63810             }).attr('class', classes).merge(paths).attr('d', get(labels, 'lineString'));
63811           }
63812
63813           function drawLineLabels(selection, entities, filter, classes, labels) {
63814             var texts = selection.selectAll('text.' + classes).filter(filter).data(entities, osmEntity.key); // exit
63815
63816             texts.exit().remove(); // enter
63817
63818             texts.enter().append('text').attr('class', function (d, i) {
63819               return classes + ' ' + labels[i].classes + ' ' + d.id;
63820             }).attr('dy', baselineHack ? '0.35em' : null).append('textPath').attr('class', 'textpath'); // update
63821
63822             selection.selectAll('text.' + classes).selectAll('.textpath').filter(filter).data(entities, osmEntity.key).attr('startOffset', '50%').attr('xlink:href', function (d) {
63823               return '#ideditor-labelpath-' + d.id;
63824             }).text(utilDisplayNameForPath);
63825           }
63826
63827           function drawPointLabels(selection, entities, filter, classes, labels) {
63828             var texts = selection.selectAll('text.' + classes).filter(filter).data(entities, osmEntity.key); // exit
63829
63830             texts.exit().remove(); // enter/update
63831
63832             texts.enter().append('text').attr('class', function (d, i) {
63833               return classes + ' ' + labels[i].classes + ' ' + d.id;
63834             }).merge(texts).attr('x', get(labels, 'x')).attr('y', get(labels, 'y')).style('text-anchor', get(labels, 'textAnchor')).text(utilDisplayName).each(function (d, i) {
63835               textWidth(utilDisplayName(d), labels[i].height, this);
63836             });
63837           }
63838
63839           function drawAreaLabels(selection, entities, filter, classes, labels) {
63840             entities = entities.filter(hasText);
63841             labels = labels.filter(hasText);
63842             drawPointLabels(selection, entities, filter, classes, labels);
63843
63844             function hasText(d, i) {
63845               return labels[i].hasOwnProperty('x') && labels[i].hasOwnProperty('y');
63846             }
63847           }
63848
63849           function drawAreaIcons(selection, entities, filter, classes, labels) {
63850             var icons = selection.selectAll('use.' + classes).filter(filter).data(entities, osmEntity.key); // exit
63851
63852             icons.exit().remove(); // enter/update
63853
63854             icons.enter().append('use').attr('class', 'icon ' + classes).attr('width', '17px').attr('height', '17px').merge(icons).attr('transform', get(labels, 'transform')).attr('xlink:href', function (d) {
63855               var preset = _mainPresetIndex.match(d, context.graph());
63856               var picon = preset && preset.icon;
63857
63858               if (!picon) {
63859                 return '';
63860               } else {
63861                 var isMaki = /^maki-/.test(picon);
63862                 return '#' + picon + (isMaki ? '-15' : '');
63863               }
63864             });
63865           }
63866
63867           function drawCollisionBoxes(selection, rtree, which) {
63868             var classes = 'debug ' + which + ' ' + (which === 'debug-skipped' ? 'orange' : 'yellow');
63869             var gj = [];
63870
63871             if (context.getDebug('collision')) {
63872               gj = rtree.all().map(function (d) {
63873                 return {
63874                   type: 'Polygon',
63875                   coordinates: [[[d.minX, d.minY], [d.maxX, d.minY], [d.maxX, d.maxY], [d.minX, d.maxY], [d.minX, d.minY]]]
63876                 };
63877               });
63878             }
63879
63880             var boxes = selection.selectAll('.' + which).data(gj); // exit
63881
63882             boxes.exit().remove(); // enter/update
63883
63884             boxes.enter().append('path').attr('class', classes).merge(boxes).attr('d', d3_geoPath());
63885           }
63886
63887           function drawLabels(selection, graph, entities, filter, dimensions, fullRedraw) {
63888             var wireframe = context.surface().classed('fill-wireframe');
63889             var zoom = geoScaleToZoom(projection.scale());
63890             var labelable = [];
63891             var renderNodeAs = {};
63892             var i, j, k, entity, geometry;
63893
63894             for (i = 0; i < labelStack.length; i++) {
63895               labelable.push([]);
63896             }
63897
63898             if (fullRedraw) {
63899               _rdrawn.clear();
63900
63901               _rskipped.clear();
63902
63903               _entitybboxes = {};
63904             } else {
63905               for (i = 0; i < entities.length; i++) {
63906                 entity = entities[i];
63907                 var toRemove = [].concat(_entitybboxes[entity.id] || []).concat(_entitybboxes[entity.id + 'I'] || []);
63908
63909                 for (j = 0; j < toRemove.length; j++) {
63910                   _rdrawn.remove(toRemove[j]);
63911
63912                   _rskipped.remove(toRemove[j]);
63913                 }
63914               }
63915             } // Loop through all the entities to do some preprocessing
63916
63917
63918             for (i = 0; i < entities.length; i++) {
63919               entity = entities[i];
63920               geometry = entity.geometry(graph); // Insert collision boxes around interesting points/vertices
63921
63922               if (geometry === 'point' || geometry === 'vertex' && isInterestingVertex(entity)) {
63923                 var hasDirections = entity.directions(graph, projection).length;
63924                 var markerPadding;
63925
63926                 if (!wireframe && geometry === 'point' && !(zoom >= 18 && hasDirections)) {
63927                   renderNodeAs[entity.id] = 'point';
63928                   markerPadding = 20; // extra y for marker height
63929                 } else {
63930                   renderNodeAs[entity.id] = 'vertex';
63931                   markerPadding = 0;
63932                 }
63933
63934                 var coord = projection(entity.loc);
63935                 var nodePadding = 10;
63936                 var bbox = {
63937                   minX: coord[0] - nodePadding,
63938                   minY: coord[1] - nodePadding - markerPadding,
63939                   maxX: coord[0] + nodePadding,
63940                   maxY: coord[1] + nodePadding
63941                 };
63942                 doInsert(bbox, entity.id + 'P');
63943               } // From here on, treat vertices like points
63944
63945
63946               if (geometry === 'vertex') {
63947                 geometry = 'point';
63948               } // Determine which entities are label-able
63949
63950
63951               var preset = geometry === 'area' && _mainPresetIndex.match(entity, graph);
63952               var icon = preset && !shouldSkipIcon(preset) && preset.icon;
63953               if (!icon && !utilDisplayName(entity)) continue;
63954
63955               for (k = 0; k < labelStack.length; k++) {
63956                 var matchGeom = labelStack[k][0];
63957                 var matchKey = labelStack[k][1];
63958                 var matchVal = labelStack[k][2];
63959                 var hasVal = entity.tags[matchKey];
63960
63961                 if (geometry === matchGeom && hasVal && (matchVal === '*' || matchVal === hasVal)) {
63962                   labelable[k].push(entity);
63963                   break;
63964                 }
63965               }
63966             }
63967
63968             var positions = {
63969               point: [],
63970               line: [],
63971               area: []
63972             };
63973             var labelled = {
63974               point: [],
63975               line: [],
63976               area: []
63977             }; // Try and find a valid label for labellable entities
63978
63979             for (k = 0; k < labelable.length; k++) {
63980               var fontSize = labelStack[k][3];
63981
63982               for (i = 0; i < labelable[k].length; i++) {
63983                 entity = labelable[k][i];
63984                 geometry = entity.geometry(graph);
63985                 var getName = geometry === 'line' ? utilDisplayNameForPath : utilDisplayName;
63986                 var name = getName(entity);
63987                 var width = name && textWidth(name, fontSize);
63988                 var p = null;
63989
63990                 if (geometry === 'point' || geometry === 'vertex') {
63991                   // no point or vertex labels in wireframe mode
63992                   // no vertex labels at low zooms (vertices have no icons)
63993                   if (wireframe) continue;
63994                   var renderAs = renderNodeAs[entity.id];
63995                   if (renderAs === 'vertex' && zoom < 17) continue;
63996                   p = getPointLabel(entity, width, fontSize, renderAs);
63997                 } else if (geometry === 'line') {
63998                   p = getLineLabel(entity, width, fontSize);
63999                 } else if (geometry === 'area') {
64000                   p = getAreaLabel(entity, width, fontSize);
64001                 }
64002
64003                 if (p) {
64004                   if (geometry === 'vertex') {
64005                     geometry = 'point';
64006                   } // treat vertex like point
64007
64008
64009                   p.classes = geometry + ' tag-' + labelStack[k][1];
64010                   positions[geometry].push(p);
64011                   labelled[geometry].push(entity);
64012                 }
64013               }
64014             }
64015
64016             function isInterestingVertex(entity) {
64017               var selectedIDs = context.selectedIDs();
64018               return entity.hasInterestingTags() || entity.isEndpoint(graph) || entity.isConnected(graph) || selectedIDs.indexOf(entity.id) !== -1 || graph.parentWays(entity).some(function (parent) {
64019                 return selectedIDs.indexOf(parent.id) !== -1;
64020               });
64021             }
64022
64023             function getPointLabel(entity, width, height, geometry) {
64024               var y = geometry === 'point' ? -12 : 0;
64025               var pointOffsets = {
64026                 ltr: [15, y, 'start'],
64027                 rtl: [-15, y, 'end']
64028               };
64029               var textDirection = _mainLocalizer.textDirection();
64030               var coord = projection(entity.loc);
64031               var textPadding = 2;
64032               var offset = pointOffsets[textDirection];
64033               var p = {
64034                 height: height,
64035                 width: width,
64036                 x: coord[0] + offset[0],
64037                 y: coord[1] + offset[1],
64038                 textAnchor: offset[2]
64039               }; // insert a collision box for the text label..
64040
64041               var bbox;
64042
64043               if (textDirection === 'rtl') {
64044                 bbox = {
64045                   minX: p.x - width - textPadding,
64046                   minY: p.y - height / 2 - textPadding,
64047                   maxX: p.x + textPadding,
64048                   maxY: p.y + height / 2 + textPadding
64049                 };
64050               } else {
64051                 bbox = {
64052                   minX: p.x - textPadding,
64053                   minY: p.y - height / 2 - textPadding,
64054                   maxX: p.x + width + textPadding,
64055                   maxY: p.y + height / 2 + textPadding
64056                 };
64057               }
64058
64059               if (tryInsert([bbox], entity.id, true)) {
64060                 return p;
64061               }
64062             }
64063
64064             function getLineLabel(entity, width, height) {
64065               var viewport = geoExtent(context.projection.clipExtent()).polygon();
64066               var points = graph.childNodes(entity).map(function (node) {
64067                 return projection(node.loc);
64068               });
64069               var length = geoPathLength(points);
64070               if (length < width + 20) return; // % along the line to attempt to place the label
64071
64072               var lineOffsets = [50, 45, 55, 40, 60, 35, 65, 30, 70, 25, 75, 20, 80, 15, 95, 10, 90, 5, 95];
64073               var padding = 3;
64074
64075               for (var i = 0; i < lineOffsets.length; i++) {
64076                 var offset = lineOffsets[i];
64077                 var middle = offset / 100 * length;
64078                 var start = middle - width / 2;
64079                 if (start < 0 || start + width > length) continue; // generate subpath and ignore paths that are invalid or don't cross viewport.
64080
64081                 var sub = subpath(points, start, start + width);
64082
64083                 if (!sub || !geoPolygonIntersectsPolygon(viewport, sub, true)) {
64084                   continue;
64085                 }
64086
64087                 var isReverse = reverse(sub);
64088
64089                 if (isReverse) {
64090                   sub = sub.reverse();
64091                 }
64092
64093                 var bboxes = [];
64094                 var boxsize = (height + 2) / 2;
64095
64096                 for (var j = 0; j < sub.length - 1; j++) {
64097                   var a = sub[j];
64098                   var b = sub[j + 1]; // split up the text into small collision boxes
64099
64100                   var num = Math.max(1, Math.floor(geoVecLength(a, b) / boxsize / 2));
64101
64102                   for (var box = 0; box < num; box++) {
64103                     var p = geoVecInterp(a, b, box / num);
64104                     var x0 = p[0] - boxsize - padding;
64105                     var y0 = p[1] - boxsize - padding;
64106                     var x1 = p[0] + boxsize + padding;
64107                     var y1 = p[1] + boxsize + padding;
64108                     bboxes.push({
64109                       minX: Math.min(x0, x1),
64110                       minY: Math.min(y0, y1),
64111                       maxX: Math.max(x0, x1),
64112                       maxY: Math.max(y0, y1)
64113                     });
64114                   }
64115                 }
64116
64117                 if (tryInsert(bboxes, entity.id, false)) {
64118                   // accept this one
64119                   return {
64120                     'font-size': height + 2,
64121                     lineString: lineString(sub),
64122                     startOffset: offset + '%'
64123                   };
64124                 }
64125               }
64126
64127               function reverse(p) {
64128                 var angle = Math.atan2(p[1][1] - p[0][1], p[1][0] - p[0][0]);
64129                 return !(p[0][0] < p[p.length - 1][0] && angle < Math.PI / 2 && angle > -Math.PI / 2);
64130               }
64131
64132               function lineString(points) {
64133                 return 'M' + points.join('L');
64134               }
64135
64136               function subpath(points, from, to) {
64137                 var sofar = 0;
64138                 var start, end, i0, i1;
64139
64140                 for (var i = 0; i < points.length - 1; i++) {
64141                   var a = points[i];
64142                   var b = points[i + 1];
64143                   var current = geoVecLength(a, b);
64144                   var portion;
64145
64146                   if (!start && sofar + current >= from) {
64147                     portion = (from - sofar) / current;
64148                     start = [a[0] + portion * (b[0] - a[0]), a[1] + portion * (b[1] - a[1])];
64149                     i0 = i + 1;
64150                   }
64151
64152                   if (!end && sofar + current >= to) {
64153                     portion = (to - sofar) / current;
64154                     end = [a[0] + portion * (b[0] - a[0]), a[1] + portion * (b[1] - a[1])];
64155                     i1 = i + 1;
64156                   }
64157
64158                   sofar += current;
64159                 }
64160
64161                 var result = points.slice(i0, i1);
64162                 result.unshift(start);
64163                 result.push(end);
64164                 return result;
64165               }
64166             }
64167
64168             function getAreaLabel(entity, width, height) {
64169               var centroid = path.centroid(entity.asGeoJSON(graph));
64170               var extent = entity.extent(graph);
64171               var areaWidth = projection(extent[1])[0] - projection(extent[0])[0];
64172               if (isNaN(centroid[0]) || areaWidth < 20) return;
64173               var preset = _mainPresetIndex.match(entity, context.graph());
64174               var picon = preset && preset.icon;
64175               var iconSize = 17;
64176               var padding = 2;
64177               var p = {};
64178
64179               if (picon) {
64180                 // icon and label..
64181                 if (addIcon()) {
64182                   addLabel(iconSize + padding);
64183                   return p;
64184                 }
64185               } else {
64186                 // label only..
64187                 if (addLabel(0)) {
64188                   return p;
64189                 }
64190               }
64191
64192               function addIcon() {
64193                 var iconX = centroid[0] - iconSize / 2;
64194                 var iconY = centroid[1] - iconSize / 2;
64195                 var bbox = {
64196                   minX: iconX,
64197                   minY: iconY,
64198                   maxX: iconX + iconSize,
64199                   maxY: iconY + iconSize
64200                 };
64201
64202                 if (tryInsert([bbox], entity.id + 'I', true)) {
64203                   p.transform = 'translate(' + iconX + ',' + iconY + ')';
64204                   return true;
64205                 }
64206
64207                 return false;
64208               }
64209
64210               function addLabel(yOffset) {
64211                 if (width && areaWidth >= width + 20) {
64212                   var labelX = centroid[0];
64213                   var labelY = centroid[1] + yOffset;
64214                   var bbox = {
64215                     minX: labelX - width / 2 - padding,
64216                     minY: labelY - height / 2 - padding,
64217                     maxX: labelX + width / 2 + padding,
64218                     maxY: labelY + height / 2 + padding
64219                   };
64220
64221                   if (tryInsert([bbox], entity.id, true)) {
64222                     p.x = labelX;
64223                     p.y = labelY;
64224                     p.textAnchor = 'middle';
64225                     p.height = height;
64226                     return true;
64227                   }
64228                 }
64229
64230                 return false;
64231               }
64232             } // force insert a singular bounding box
64233             // singular box only, no array, id better be unique
64234
64235
64236             function doInsert(bbox, id) {
64237               bbox.id = id;
64238               var oldbox = _entitybboxes[id];
64239
64240               if (oldbox) {
64241                 _rdrawn.remove(oldbox);
64242               }
64243
64244               _entitybboxes[id] = bbox;
64245
64246               _rdrawn.insert(bbox);
64247             }
64248
64249             function tryInsert(bboxes, id, saveSkipped) {
64250               var skipped = false;
64251
64252               for (var i = 0; i < bboxes.length; i++) {
64253                 var bbox = bboxes[i];
64254                 bbox.id = id; // Check that label is visible
64255
64256                 if (bbox.minX < 0 || bbox.minY < 0 || bbox.maxX > dimensions[0] || bbox.maxY > dimensions[1]) {
64257                   skipped = true;
64258                   break;
64259                 }
64260
64261                 if (_rdrawn.collides(bbox)) {
64262                   skipped = true;
64263                   break;
64264                 }
64265               }
64266
64267               _entitybboxes[id] = bboxes;
64268
64269               if (skipped) {
64270                 if (saveSkipped) {
64271                   _rskipped.load(bboxes);
64272                 }
64273               } else {
64274                 _rdrawn.load(bboxes);
64275               }
64276
64277               return !skipped;
64278             }
64279
64280             var layer = selection.selectAll('.layer-osm.labels');
64281             layer.selectAll('.labels-group').data(['halo', 'label', 'debug']).enter().append('g').attr('class', function (d) {
64282               return 'labels-group ' + d;
64283             });
64284             var halo = layer.selectAll('.labels-group.halo');
64285             var label = layer.selectAll('.labels-group.label');
64286             var debug = layer.selectAll('.labels-group.debug'); // points
64287
64288             drawPointLabels(label, labelled.point, filter, 'pointlabel', positions.point);
64289             drawPointLabels(halo, labelled.point, filter, 'pointlabel-halo', positions.point); // lines
64290
64291             drawLinePaths(layer, labelled.line, filter, '', positions.line);
64292             drawLineLabels(label, labelled.line, filter, 'linelabel', positions.line);
64293             drawLineLabels(halo, labelled.line, filter, 'linelabel-halo', positions.line); // areas
64294
64295             drawAreaLabels(label, labelled.area, filter, 'arealabel', positions.area);
64296             drawAreaLabels(halo, labelled.area, filter, 'arealabel-halo', positions.area);
64297             drawAreaIcons(label, labelled.area, filter, 'areaicon', positions.area);
64298             drawAreaIcons(halo, labelled.area, filter, 'areaicon-halo', positions.area); // debug
64299
64300             drawCollisionBoxes(debug, _rskipped, 'debug-skipped');
64301             drawCollisionBoxes(debug, _rdrawn, 'debug-drawn');
64302             layer.call(filterLabels);
64303           }
64304
64305           function filterLabels(selection) {
64306             var drawLayer = selection.selectAll('.layer-osm.labels');
64307             var layers = drawLayer.selectAll('.labels-group.halo, .labels-group.label');
64308             layers.selectAll('.nolabel').classed('nolabel', false);
64309             var mouse = context.map().mouse();
64310             var graph = context.graph();
64311             var selectedIDs = context.selectedIDs();
64312             var ids = [];
64313             var pad, bbox; // hide labels near the mouse
64314
64315             if (mouse) {
64316               pad = 20;
64317               bbox = {
64318                 minX: mouse[0] - pad,
64319                 minY: mouse[1] - pad,
64320                 maxX: mouse[0] + pad,
64321                 maxY: mouse[1] + pad
64322               };
64323
64324               var nearMouse = _rdrawn.search(bbox).map(function (entity) {
64325                 return entity.id;
64326               });
64327
64328               ids.push.apply(ids, nearMouse);
64329             } // hide labels on selected nodes (they look weird when dragging / haloed)
64330
64331
64332             for (var i = 0; i < selectedIDs.length; i++) {
64333               var entity = graph.hasEntity(selectedIDs[i]);
64334
64335               if (entity && entity.type === 'node') {
64336                 ids.push(selectedIDs[i]);
64337               }
64338             }
64339
64340             layers.selectAll(utilEntitySelector(ids)).classed('nolabel', true); // draw the mouse bbox if debugging is on..
64341
64342             var debug = selection.selectAll('.labels-group.debug');
64343             var gj = [];
64344
64345             if (context.getDebug('collision')) {
64346               gj = bbox ? [{
64347                 type: 'Polygon',
64348                 coordinates: [[[bbox.minX, bbox.minY], [bbox.maxX, bbox.minY], [bbox.maxX, bbox.maxY], [bbox.minX, bbox.maxY], [bbox.minX, bbox.minY]]]
64349               }] : [];
64350             }
64351
64352             var box = debug.selectAll('.debug-mouse').data(gj); // exit
64353
64354             box.exit().remove(); // enter/update
64355
64356             box.enter().append('path').attr('class', 'debug debug-mouse yellow').merge(box).attr('d', d3_geoPath());
64357           }
64358
64359           var throttleFilterLabels = throttle(filterLabels, 100);
64360
64361           drawLabels.observe = function (selection) {
64362             var listener = function listener() {
64363               throttleFilterLabels(selection);
64364             };
64365
64366             selection.on('mousemove.hidelabels', listener);
64367             context.on('enter.hidelabels', listener);
64368           };
64369
64370           drawLabels.off = function (selection) {
64371             throttleFilterLabels.cancel();
64372             selection.on('mousemove.hidelabels', null);
64373             context.on('enter.hidelabels', null);
64374           };
64375
64376           return drawLabels;
64377         }
64378
64379         var _layerEnabled$1 = false;
64380
64381         var _qaService$1;
64382
64383         function svgImproveOSM(projection, context, dispatch) {
64384           var throttledRedraw = throttle(function () {
64385             return dispatch.call('change');
64386           }, 1000);
64387
64388           var minZoom = 12;
64389           var touchLayer = select(null);
64390           var drawLayer = select(null);
64391           var layerVisible = false;
64392
64393           function markerPath(selection, klass) {
64394             selection.attr('class', klass).attr('transform', 'translate(-10, -28)').attr('points', '16,3 4,3 1,6 1,17 4,20 7,20 10,27 13,20 16,20 19,17.033 19,6');
64395           } // Loosely-coupled improveOSM service for fetching issues
64396
64397
64398           function getService() {
64399             if (services.improveOSM && !_qaService$1) {
64400               _qaService$1 = services.improveOSM;
64401
64402               _qaService$1.on('loaded', throttledRedraw);
64403             } else if (!services.improveOSM && _qaService$1) {
64404               _qaService$1 = null;
64405             }
64406
64407             return _qaService$1;
64408           } // Show the markers
64409
64410
64411           function editOn() {
64412             if (!layerVisible) {
64413               layerVisible = true;
64414               drawLayer.style('display', 'block');
64415             }
64416           } // Immediately remove the markers and their touch targets
64417
64418
64419           function editOff() {
64420             if (layerVisible) {
64421               layerVisible = false;
64422               drawLayer.style('display', 'none');
64423               drawLayer.selectAll('.qaItem.improveOSM').remove();
64424               touchLayer.selectAll('.qaItem.improveOSM').remove();
64425             }
64426           } // Enable the layer.  This shows the markers and transitions them to visible.
64427
64428
64429           function layerOn() {
64430             editOn();
64431             drawLayer.style('opacity', 0).transition().duration(250).style('opacity', 1).on('end interrupt', function () {
64432               return dispatch.call('change');
64433             });
64434           } // Disable the layer.  This transitions the layer invisible and then hides the markers.
64435
64436
64437           function layerOff() {
64438             throttledRedraw.cancel();
64439             drawLayer.interrupt();
64440             touchLayer.selectAll('.qaItem.improveOSM').remove();
64441             drawLayer.transition().duration(250).style('opacity', 0).on('end interrupt', function () {
64442               editOff();
64443               dispatch.call('change');
64444             });
64445           } // Update the issue markers
64446
64447
64448           function updateMarkers() {
64449             if (!layerVisible || !_layerEnabled$1) return;
64450             var service = getService();
64451             var selectedID = context.selectedErrorID();
64452             var data = service ? service.getItems(projection) : [];
64453             var getTransform = svgPointTransform(projection); // Draw markers..
64454
64455             var markers = drawLayer.selectAll('.qaItem.improveOSM').data(data, function (d) {
64456               return d.id;
64457             }); // exit
64458
64459             markers.exit().remove(); // enter
64460
64461             var markersEnter = markers.enter().append('g').attr('class', function (d) {
64462               return "qaItem ".concat(d.service, " itemId-").concat(d.id, " itemType-").concat(d.itemType);
64463             });
64464             markersEnter.append('polygon').call(markerPath, 'shadow');
64465             markersEnter.append('ellipse').attr('cx', 0).attr('cy', 0).attr('rx', 4.5).attr('ry', 2).attr('class', 'stroke');
64466             markersEnter.append('polygon').attr('fill', 'currentColor').call(markerPath, 'qaItem-fill');
64467             markersEnter.append('use').attr('transform', 'translate(-6.5, -23)').attr('class', 'icon-annotation').attr('width', '13px').attr('height', '13px').attr('xlink:href', function (d) {
64468               var picon = d.icon;
64469
64470               if (!picon) {
64471                 return '';
64472               } else {
64473                 var isMaki = /^maki-/.test(picon);
64474                 return "#".concat(picon).concat(isMaki ? '-11' : '');
64475               }
64476             }); // update
64477
64478             markers.merge(markersEnter).sort(sortY).classed('selected', function (d) {
64479               return d.id === selectedID;
64480             }).attr('transform', getTransform); // Draw targets..
64481
64482             if (touchLayer.empty()) return;
64483             var fillClass = context.getDebug('target') ? 'pink ' : 'nocolor ';
64484             var targets = touchLayer.selectAll('.qaItem.improveOSM').data(data, function (d) {
64485               return d.id;
64486             }); // exit
64487
64488             targets.exit().remove(); // enter/update
64489
64490             targets.enter().append('rect').attr('width', '20px').attr('height', '30px').attr('x', '-10px').attr('y', '-28px').merge(targets).sort(sortY).attr('class', function (d) {
64491               return "qaItem ".concat(d.service, " target ").concat(fillClass, " itemId-").concat(d.id);
64492             }).attr('transform', getTransform);
64493
64494             function sortY(a, b) {
64495               return a.id === selectedID ? 1 : b.id === selectedID ? -1 : b.loc[1] - a.loc[1];
64496             }
64497           } // Draw the ImproveOSM layer and schedule loading issues and updating markers.
64498
64499
64500           function drawImproveOSM(selection) {
64501             var service = getService();
64502             var surface = context.surface();
64503
64504             if (surface && !surface.empty()) {
64505               touchLayer = surface.selectAll('.data-layer.touch .layer-touch.markers');
64506             }
64507
64508             drawLayer = selection.selectAll('.layer-improveOSM').data(service ? [0] : []);
64509             drawLayer.exit().remove();
64510             drawLayer = drawLayer.enter().append('g').attr('class', 'layer-improveOSM').style('display', _layerEnabled$1 ? 'block' : 'none').merge(drawLayer);
64511
64512             if (_layerEnabled$1) {
64513               if (service && ~~context.map().zoom() >= minZoom) {
64514                 editOn();
64515                 service.loadIssues(projection);
64516                 updateMarkers();
64517               } else {
64518                 editOff();
64519               }
64520             }
64521           } // Toggles the layer on and off
64522
64523
64524           drawImproveOSM.enabled = function (val) {
64525             if (!arguments.length) return _layerEnabled$1;
64526             _layerEnabled$1 = val;
64527
64528             if (_layerEnabled$1) {
64529               layerOn();
64530             } else {
64531               layerOff();
64532
64533               if (context.selectedErrorID()) {
64534                 context.enter(modeBrowse(context));
64535               }
64536             }
64537
64538             dispatch.call('change');
64539             return this;
64540           };
64541
64542           drawImproveOSM.supported = function () {
64543             return !!getService();
64544           };
64545
64546           return drawImproveOSM;
64547         }
64548
64549         var _layerEnabled = false;
64550
64551         var _qaService;
64552
64553         function svgOsmose(projection, context, dispatch) {
64554           var throttledRedraw = throttle(function () {
64555             return dispatch.call('change');
64556           }, 1000);
64557
64558           var minZoom = 12;
64559           var touchLayer = select(null);
64560           var drawLayer = select(null);
64561           var layerVisible = false;
64562
64563           function markerPath(selection, klass) {
64564             selection.attr('class', klass).attr('transform', 'translate(-10, -28)').attr('points', '16,3 4,3 1,6 1,17 4,20 7,20 10,27 13,20 16,20 19,17.033 19,6');
64565           } // Loosely-coupled osmose service for fetching issues
64566
64567
64568           function getService() {
64569             if (services.osmose && !_qaService) {
64570               _qaService = services.osmose;
64571
64572               _qaService.on('loaded', throttledRedraw);
64573             } else if (!services.osmose && _qaService) {
64574               _qaService = null;
64575             }
64576
64577             return _qaService;
64578           } // Show the markers
64579
64580
64581           function editOn() {
64582             if (!layerVisible) {
64583               layerVisible = true;
64584               drawLayer.style('display', 'block');
64585             }
64586           } // Immediately remove the markers and their touch targets
64587
64588
64589           function editOff() {
64590             if (layerVisible) {
64591               layerVisible = false;
64592               drawLayer.style('display', 'none');
64593               drawLayer.selectAll('.qaItem.osmose').remove();
64594               touchLayer.selectAll('.qaItem.osmose').remove();
64595             }
64596           } // Enable the layer.  This shows the markers and transitions them to visible.
64597
64598
64599           function layerOn() {
64600             editOn();
64601             drawLayer.style('opacity', 0).transition().duration(250).style('opacity', 1).on('end interrupt', function () {
64602               return dispatch.call('change');
64603             });
64604           } // Disable the layer.  This transitions the layer invisible and then hides the markers.
64605
64606
64607           function layerOff() {
64608             throttledRedraw.cancel();
64609             drawLayer.interrupt();
64610             touchLayer.selectAll('.qaItem.osmose').remove();
64611             drawLayer.transition().duration(250).style('opacity', 0).on('end interrupt', function () {
64612               editOff();
64613               dispatch.call('change');
64614             });
64615           } // Update the issue markers
64616
64617
64618           function updateMarkers() {
64619             if (!layerVisible || !_layerEnabled) return;
64620             var service = getService();
64621             var selectedID = context.selectedErrorID();
64622             var data = service ? service.getItems(projection) : [];
64623             var getTransform = svgPointTransform(projection); // Draw markers..
64624
64625             var markers = drawLayer.selectAll('.qaItem.osmose').data(data, function (d) {
64626               return d.id;
64627             }); // exit
64628
64629             markers.exit().remove(); // enter
64630
64631             var markersEnter = markers.enter().append('g').attr('class', function (d) {
64632               return "qaItem ".concat(d.service, " itemId-").concat(d.id, " itemType-").concat(d.itemType);
64633             });
64634             markersEnter.append('polygon').call(markerPath, 'shadow');
64635             markersEnter.append('ellipse').attr('cx', 0).attr('cy', 0).attr('rx', 4.5).attr('ry', 2).attr('class', 'stroke');
64636             markersEnter.append('polygon').attr('fill', function (d) {
64637               return service.getColor(d.item);
64638             }).call(markerPath, 'qaItem-fill');
64639             markersEnter.append('use').attr('transform', 'translate(-6.5, -23)').attr('class', 'icon-annotation').attr('width', '13px').attr('height', '13px').attr('xlink:href', function (d) {
64640               var picon = d.icon;
64641
64642               if (!picon) {
64643                 return '';
64644               } else {
64645                 var isMaki = /^maki-/.test(picon);
64646                 return "#".concat(picon).concat(isMaki ? '-11' : '');
64647               }
64648             }); // update
64649
64650             markers.merge(markersEnter).sort(sortY).classed('selected', function (d) {
64651               return d.id === selectedID;
64652             }).attr('transform', getTransform); // Draw targets..
64653
64654             if (touchLayer.empty()) return;
64655             var fillClass = context.getDebug('target') ? 'pink' : 'nocolor';
64656             var targets = touchLayer.selectAll('.qaItem.osmose').data(data, function (d) {
64657               return d.id;
64658             }); // exit
64659
64660             targets.exit().remove(); // enter/update
64661
64662             targets.enter().append('rect').attr('width', '20px').attr('height', '30px').attr('x', '-10px').attr('y', '-28px').merge(targets).sort(sortY).attr('class', function (d) {
64663               return "qaItem ".concat(d.service, " target ").concat(fillClass, " itemId-").concat(d.id);
64664             }).attr('transform', getTransform);
64665
64666             function sortY(a, b) {
64667               return a.id === selectedID ? 1 : b.id === selectedID ? -1 : b.loc[1] - a.loc[1];
64668             }
64669           } // Draw the Osmose layer and schedule loading issues and updating markers.
64670
64671
64672           function drawOsmose(selection) {
64673             var service = getService();
64674             var surface = context.surface();
64675
64676             if (surface && !surface.empty()) {
64677               touchLayer = surface.selectAll('.data-layer.touch .layer-touch.markers');
64678             }
64679
64680             drawLayer = selection.selectAll('.layer-osmose').data(service ? [0] : []);
64681             drawLayer.exit().remove();
64682             drawLayer = drawLayer.enter().append('g').attr('class', 'layer-osmose').style('display', _layerEnabled ? 'block' : 'none').merge(drawLayer);
64683
64684             if (_layerEnabled) {
64685               if (service && ~~context.map().zoom() >= minZoom) {
64686                 editOn();
64687                 service.loadIssues(projection);
64688                 updateMarkers();
64689               } else {
64690                 editOff();
64691               }
64692             }
64693           } // Toggles the layer on and off
64694
64695
64696           drawOsmose.enabled = function (val) {
64697             if (!arguments.length) return _layerEnabled;
64698             _layerEnabled = val;
64699
64700             if (_layerEnabled) {
64701               // Strings supplied by Osmose fetched before showing layer for first time
64702               // NOTE: Currently no way to change locale in iD at runtime, would need to re-call this method if that's ever implemented
64703               // Also, If layer is toggled quickly multiple requests are sent
64704               getService().loadStrings().then(layerOn)["catch"](function (err) {
64705                 console.log(err); // eslint-disable-line no-console
64706               });
64707             } else {
64708               layerOff();
64709
64710               if (context.selectedErrorID()) {
64711                 context.enter(modeBrowse(context));
64712               }
64713             }
64714
64715             dispatch.call('change');
64716             return this;
64717           };
64718
64719           drawOsmose.supported = function () {
64720             return !!getService();
64721           };
64722
64723           return drawOsmose;
64724         }
64725
64726         function svgStreetside(projection, context, dispatch) {
64727           var throttledRedraw = throttle(function () {
64728             dispatch.call('change');
64729           }, 1000);
64730
64731           var minZoom = 14;
64732           var minMarkerZoom = 16;
64733           var minViewfieldZoom = 18;
64734           var layer = select(null);
64735           var _viewerYaw = 0;
64736           var _selectedSequence = null;
64737
64738           var _streetside;
64739           /**
64740            * init().
64741            */
64742
64743
64744           function init() {
64745             if (svgStreetside.initialized) return; // run once
64746
64747             svgStreetside.enabled = false;
64748             svgStreetside.initialized = true;
64749           }
64750           /**
64751            * getService().
64752            */
64753
64754
64755           function getService() {
64756             if (services.streetside && !_streetside) {
64757               _streetside = services.streetside;
64758
64759               _streetside.event.on('viewerChanged.svgStreetside', viewerChanged).on('loadedImages.svgStreetside', throttledRedraw);
64760             } else if (!services.streetside && _streetside) {
64761               _streetside = null;
64762             }
64763
64764             return _streetside;
64765           }
64766           /**
64767            * showLayer().
64768            */
64769
64770
64771           function showLayer() {
64772             var service = getService();
64773             if (!service) return;
64774             editOn();
64775             layer.style('opacity', 0).transition().duration(250).style('opacity', 1).on('end', function () {
64776               dispatch.call('change');
64777             });
64778           }
64779           /**
64780            * hideLayer().
64781            */
64782
64783
64784           function hideLayer() {
64785             throttledRedraw.cancel();
64786             layer.transition().duration(250).style('opacity', 0).on('end', editOff);
64787           }
64788           /**
64789            * editOn().
64790            */
64791
64792
64793           function editOn() {
64794             layer.style('display', 'block');
64795           }
64796           /**
64797            * editOff().
64798            */
64799
64800
64801           function editOff() {
64802             layer.selectAll('.viewfield-group').remove();
64803             layer.style('display', 'none');
64804           }
64805           /**
64806            * click() Handles 'bubble' point click event.
64807            */
64808
64809
64810           function click(d3_event, d) {
64811             var service = getService();
64812             if (!service) return; // try to preserve the viewer rotation when staying on the same sequence
64813
64814             if (d.sequenceKey !== _selectedSequence) {
64815               _viewerYaw = 0; // reset
64816             }
64817
64818             _selectedSequence = d.sequenceKey;
64819             service.ensureViewerLoaded(context).then(function () {
64820               service.selectImage(context, d.key).yaw(_viewerYaw).showViewer(context);
64821             });
64822             context.map().centerEase(d.loc);
64823           }
64824           /**
64825            * mouseover().
64826            */
64827
64828
64829           function mouseover(d3_event, d) {
64830             var service = getService();
64831             if (service) service.setStyles(context, d);
64832           }
64833           /**
64834            * mouseout().
64835            */
64836
64837
64838           function mouseout() {
64839             var service = getService();
64840             if (service) service.setStyles(context, null);
64841           }
64842           /**
64843            * transform().
64844            */
64845
64846
64847           function transform(d) {
64848             var t = svgPointTransform(projection)(d);
64849             var rot = d.ca + _viewerYaw;
64850
64851             if (rot) {
64852               t += ' rotate(' + Math.floor(rot) + ',0,0)';
64853             }
64854
64855             return t;
64856           }
64857
64858           function viewerChanged() {
64859             var service = getService();
64860             if (!service) return;
64861             var viewer = service.viewer();
64862             if (!viewer) return; // update viewfield rotation
64863
64864             _viewerYaw = viewer.getYaw(); // avoid updating if the map is currently transformed
64865             // e.g. during drags or easing.
64866
64867             if (context.map().isTransformed()) return;
64868             layer.selectAll('.viewfield-group.currentView').attr('transform', transform);
64869           }
64870
64871           function filterBubbles(bubbles) {
64872             var fromDate = context.photos().fromDate();
64873             var toDate = context.photos().toDate();
64874             var usernames = context.photos().usernames();
64875
64876             if (fromDate) {
64877               var fromTimestamp = new Date(fromDate).getTime();
64878               bubbles = bubbles.filter(function (bubble) {
64879                 return new Date(bubble.captured_at).getTime() >= fromTimestamp;
64880               });
64881             }
64882
64883             if (toDate) {
64884               var toTimestamp = new Date(toDate).getTime();
64885               bubbles = bubbles.filter(function (bubble) {
64886                 return new Date(bubble.captured_at).getTime() <= toTimestamp;
64887               });
64888             }
64889
64890             if (usernames) {
64891               bubbles = bubbles.filter(function (bubble) {
64892                 return usernames.indexOf(bubble.captured_by) !== -1;
64893               });
64894             }
64895
64896             return bubbles;
64897           }
64898
64899           function filterSequences(sequences) {
64900             var fromDate = context.photos().fromDate();
64901             var toDate = context.photos().toDate();
64902             var usernames = context.photos().usernames();
64903
64904             if (fromDate) {
64905               var fromTimestamp = new Date(fromDate).getTime();
64906               sequences = sequences.filter(function (sequences) {
64907                 return new Date(sequences.properties.captured_at).getTime() >= fromTimestamp;
64908               });
64909             }
64910
64911             if (toDate) {
64912               var toTimestamp = new Date(toDate).getTime();
64913               sequences = sequences.filter(function (sequences) {
64914                 return new Date(sequences.properties.captured_at).getTime() <= toTimestamp;
64915               });
64916             }
64917
64918             if (usernames) {
64919               sequences = sequences.filter(function (sequences) {
64920                 return usernames.indexOf(sequences.properties.captured_by) !== -1;
64921               });
64922             }
64923
64924             return sequences;
64925           }
64926           /**
64927            * update().
64928            */
64929
64930
64931           function update() {
64932             var viewer = context.container().select('.photoviewer');
64933             var selected = viewer.empty() ? undefined : viewer.datum();
64934             var z = ~~context.map().zoom();
64935             var showMarkers = z >= minMarkerZoom;
64936             var showViewfields = z >= minViewfieldZoom;
64937             var service = getService();
64938             var sequences = [];
64939             var bubbles = [];
64940
64941             if (context.photos().showsPanoramic()) {
64942               sequences = service ? service.sequences(projection) : [];
64943               bubbles = service && showMarkers ? service.bubbles(projection) : [];
64944               sequences = filterSequences(sequences);
64945               bubbles = filterBubbles(bubbles);
64946             }
64947
64948             var traces = layer.selectAll('.sequences').selectAll('.sequence').data(sequences, function (d) {
64949               return d.properties.key;
64950             }); // exit
64951
64952             traces.exit().remove(); // enter/update
64953
64954             traces = traces.enter().append('path').attr('class', 'sequence').merge(traces).attr('d', svgPath(projection).geojson);
64955             var groups = layer.selectAll('.markers').selectAll('.viewfield-group').data(bubbles, function (d) {
64956               // force reenter once bubbles are attached to a sequence
64957               return d.key + (d.sequenceKey ? 'v1' : 'v0');
64958             }); // exit
64959
64960             groups.exit().remove(); // enter
64961
64962             var groupsEnter = groups.enter().append('g').attr('class', 'viewfield-group').on('mouseenter', mouseover).on('mouseleave', mouseout).on('click', click);
64963             groupsEnter.append('g').attr('class', 'viewfield-scale'); // update
64964
64965             var markers = groups.merge(groupsEnter).sort(function (a, b) {
64966               return a === selected ? 1 : b === selected ? -1 : b.loc[1] - a.loc[1];
64967             }).attr('transform', transform).select('.viewfield-scale');
64968             markers.selectAll('circle').data([0]).enter().append('circle').attr('dx', '0').attr('dy', '0').attr('r', '6');
64969             var viewfields = markers.selectAll('.viewfield').data(showViewfields ? [0] : []);
64970             viewfields.exit().remove(); // viewfields may or may not be drawn...
64971             // but if they are, draw below the circles
64972
64973             viewfields.enter().insert('path', 'circle').attr('class', 'viewfield').attr('transform', 'scale(1.5,1.5),translate(-8, -13)').attr('d', viewfieldPath);
64974
64975             function viewfieldPath() {
64976               var d = this.parentNode.__data__;
64977
64978               if (d.pano) {
64979                 return 'M 8,13 m -10,0 a 10,10 0 1,0 20,0 a 10,10 0 1,0 -20,0';
64980               } else {
64981                 return 'M 6,9 C 8,8.4 8,8.4 10,9 L 16,-2 C 12,-5 4,-5 0,-2 z';
64982               }
64983             }
64984           }
64985           /**
64986            * drawImages()
64987            * drawImages is the method that is returned (and that runs) every time 'svgStreetside()' is called.
64988            * 'svgStreetside()' is called from index.js
64989            */
64990
64991
64992           function drawImages(selection) {
64993             var enabled = svgStreetside.enabled;
64994             var service = getService();
64995             layer = selection.selectAll('.layer-streetside-images').data(service ? [0] : []);
64996             layer.exit().remove();
64997             var layerEnter = layer.enter().append('g').attr('class', 'layer-streetside-images').style('display', enabled ? 'block' : 'none');
64998             layerEnter.append('g').attr('class', 'sequences');
64999             layerEnter.append('g').attr('class', 'markers');
65000             layer = layerEnter.merge(layer);
65001
65002             if (enabled) {
65003               if (service && ~~context.map().zoom() >= minZoom) {
65004                 editOn();
65005                 update();
65006                 service.loadBubbles(projection);
65007               } else {
65008                 editOff();
65009               }
65010             }
65011           }
65012           /**
65013            * drawImages.enabled().
65014            */
65015
65016
65017           drawImages.enabled = function (_) {
65018             if (!arguments.length) return svgStreetside.enabled;
65019             svgStreetside.enabled = _;
65020
65021             if (svgStreetside.enabled) {
65022               showLayer();
65023               context.photos().on('change.streetside', update);
65024             } else {
65025               hideLayer();
65026               context.photos().on('change.streetside', null);
65027             }
65028
65029             dispatch.call('change');
65030             return this;
65031           };
65032           /**
65033            * drawImages.supported().
65034            */
65035
65036
65037           drawImages.supported = function () {
65038             return !!getService();
65039           };
65040
65041           init();
65042           return drawImages;
65043         }
65044
65045         function svgMapillaryImages(projection, context, dispatch) {
65046           var throttledRedraw = throttle(function () {
65047             dispatch.call('change');
65048           }, 1000);
65049
65050           var minZoom = 12;
65051           var minMarkerZoom = 16;
65052           var minViewfieldZoom = 18;
65053           var layer = select(null);
65054
65055           var _mapillary;
65056
65057           function init() {
65058             if (svgMapillaryImages.initialized) return; // run once
65059
65060             svgMapillaryImages.enabled = false;
65061             svgMapillaryImages.initialized = true;
65062           }
65063
65064           function getService() {
65065             if (services.mapillary && !_mapillary) {
65066               _mapillary = services.mapillary;
65067
65068               _mapillary.event.on('loadedImages', throttledRedraw);
65069             } else if (!services.mapillary && _mapillary) {
65070               _mapillary = null;
65071             }
65072
65073             return _mapillary;
65074           }
65075
65076           function showLayer() {
65077             var service = getService();
65078             if (!service) return;
65079             editOn();
65080             layer.style('opacity', 0).transition().duration(250).style('opacity', 1).on('end', function () {
65081               dispatch.call('change');
65082             });
65083           }
65084
65085           function hideLayer() {
65086             throttledRedraw.cancel();
65087             layer.transition().duration(250).style('opacity', 0).on('end', editOff);
65088           }
65089
65090           function editOn() {
65091             layer.style('display', 'block');
65092           }
65093
65094           function editOff() {
65095             layer.selectAll('.viewfield-group').remove();
65096             layer.style('display', 'none');
65097           }
65098
65099           function click(d3_event, image) {
65100             var service = getService();
65101             if (!service) return;
65102             service.ensureViewerLoaded(context).then(function () {
65103               service.selectImage(context, image.id).showViewer(context);
65104             });
65105             context.map().centerEase(image.loc);
65106           }
65107
65108           function mouseover(d3_event, image) {
65109             var service = getService();
65110             if (service) service.setStyles(context, image);
65111           }
65112
65113           function mouseout() {
65114             var service = getService();
65115             if (service) service.setStyles(context, null);
65116           }
65117
65118           function transform(d) {
65119             var t = svgPointTransform(projection)(d);
65120
65121             if (d.ca) {
65122               t += ' rotate(' + Math.floor(d.ca) + ',0,0)';
65123             }
65124
65125             return t;
65126           }
65127
65128           function filterImages(images) {
65129             var showsPano = context.photos().showsPanoramic();
65130             var showsFlat = context.photos().showsFlat();
65131             var fromDate = context.photos().fromDate();
65132             var toDate = context.photos().toDate();
65133
65134             if (!showsPano || !showsFlat) {
65135               images = images.filter(function (image) {
65136                 if (image.is_pano) return showsPano;
65137                 return showsFlat;
65138               });
65139             }
65140
65141             if (fromDate) {
65142               images = images.filter(function (image) {
65143                 return new Date(image.captured_at).getTime() >= new Date(fromDate).getTime();
65144               });
65145             }
65146
65147             if (toDate) {
65148               images = images.filter(function (image) {
65149                 return new Date(image.captured_at).getTime() <= new Date(toDate).getTime();
65150               });
65151             }
65152
65153             return images;
65154           }
65155
65156           function filterSequences(sequences) {
65157             var showsPano = context.photos().showsPanoramic();
65158             var showsFlat = context.photos().showsFlat();
65159             var fromDate = context.photos().fromDate();
65160             var toDate = context.photos().toDate();
65161
65162             if (!showsPano || !showsFlat) {
65163               sequences = sequences.filter(function (sequence) {
65164                 if (sequence.properties.hasOwnProperty('is_pano')) {
65165                   if (sequence.properties.is_pano) return showsPano;
65166                   return showsFlat;
65167                 }
65168
65169                 return false;
65170               });
65171             }
65172
65173             if (fromDate) {
65174               sequences = sequences.filter(function (sequence) {
65175                 return new Date(sequence.properties.captured_at).getTime() >= new Date(fromDate).getTime().toString();
65176               });
65177             }
65178
65179             if (toDate) {
65180               sequences = sequences.filter(function (sequence) {
65181                 return new Date(sequence.properties.captured_at).getTime() <= new Date(toDate).getTime().toString();
65182               });
65183             }
65184
65185             return sequences;
65186           }
65187
65188           function update() {
65189             var z = ~~context.map().zoom();
65190             var showMarkers = z >= minMarkerZoom;
65191             var showViewfields = z >= minViewfieldZoom;
65192             var service = getService();
65193             var sequences = service ? service.sequences(projection) : [];
65194             var images = service && showMarkers ? service.images(projection) : [];
65195             images = filterImages(images);
65196             sequences = filterSequences(sequences);
65197             service.filterViewer(context);
65198             var traces = layer.selectAll('.sequences').selectAll('.sequence').data(sequences, function (d) {
65199               return d.properties.id;
65200             }); // exit
65201
65202             traces.exit().remove(); // enter/update
65203
65204             traces = traces.enter().append('path').attr('class', 'sequence').merge(traces).attr('d', svgPath(projection).geojson);
65205             var groups = layer.selectAll('.markers').selectAll('.viewfield-group').data(images, function (d) {
65206               return d.id;
65207             }); // exit
65208
65209             groups.exit().remove(); // enter
65210
65211             var groupsEnter = groups.enter().append('g').attr('class', 'viewfield-group').on('mouseenter', mouseover).on('mouseleave', mouseout).on('click', click);
65212             groupsEnter.append('g').attr('class', 'viewfield-scale'); // update
65213
65214             var markers = groups.merge(groupsEnter).sort(function (a, b) {
65215               return b.loc[1] - a.loc[1]; // sort Y
65216             }).attr('transform', transform).select('.viewfield-scale');
65217             markers.selectAll('circle').data([0]).enter().append('circle').attr('dx', '0').attr('dy', '0').attr('r', '6');
65218             var viewfields = markers.selectAll('.viewfield').data(showViewfields ? [0] : []);
65219             viewfields.exit().remove();
65220             viewfields.enter() // viewfields may or may not be drawn...
65221             .insert('path', 'circle') // but if they are, draw below the circles
65222             .attr('class', 'viewfield').classed('pano', function () {
65223               return this.parentNode.__data__.is_pano;
65224             }).attr('transform', 'scale(1.5,1.5),translate(-8, -13)').attr('d', viewfieldPath);
65225
65226             function viewfieldPath() {
65227               if (this.parentNode.__data__.is_pano) {
65228                 return 'M 8,13 m -10,0 a 10,10 0 1,0 20,0 a 10,10 0 1,0 -20,0';
65229               } else {
65230                 return 'M 6,9 C 8,8.4 8,8.4 10,9 L 16,-2 C 12,-5 4,-5 0,-2 z';
65231               }
65232             }
65233           }
65234
65235           function drawImages(selection) {
65236             var enabled = svgMapillaryImages.enabled;
65237             var service = getService();
65238             layer = selection.selectAll('.layer-mapillary').data(service ? [0] : []);
65239             layer.exit().remove();
65240             var layerEnter = layer.enter().append('g').attr('class', 'layer-mapillary').style('display', enabled ? 'block' : 'none');
65241             layerEnter.append('g').attr('class', 'sequences');
65242             layerEnter.append('g').attr('class', 'markers');
65243             layer = layerEnter.merge(layer);
65244
65245             if (enabled) {
65246               if (service && ~~context.map().zoom() >= minZoom) {
65247                 editOn();
65248                 update();
65249                 service.loadImages(projection);
65250               } else {
65251                 editOff();
65252               }
65253             }
65254           }
65255
65256           drawImages.enabled = function (_) {
65257             if (!arguments.length) return svgMapillaryImages.enabled;
65258             svgMapillaryImages.enabled = _;
65259
65260             if (svgMapillaryImages.enabled) {
65261               showLayer();
65262               context.photos().on('change.mapillary_images', update);
65263             } else {
65264               hideLayer();
65265               context.photos().on('change.mapillary_images', null);
65266             }
65267
65268             dispatch.call('change');
65269             return this;
65270           };
65271
65272           drawImages.supported = function () {
65273             return !!getService();
65274           };
65275
65276           init();
65277           return drawImages;
65278         }
65279
65280         function svgMapillaryPosition(projection, context) {
65281           var throttledRedraw = throttle(function () {
65282             update();
65283           }, 1000);
65284
65285           var minZoom = 12;
65286           var minViewfieldZoom = 18;
65287           var layer = select(null);
65288
65289           var _mapillary;
65290
65291           var viewerCompassAngle;
65292
65293           function init() {
65294             if (svgMapillaryPosition.initialized) return; // run once
65295
65296             svgMapillaryPosition.initialized = true;
65297           }
65298
65299           function getService() {
65300             if (services.mapillary && !_mapillary) {
65301               _mapillary = services.mapillary;
65302
65303               _mapillary.event.on('imageChanged', throttledRedraw);
65304
65305               _mapillary.event.on('bearingChanged', function (e) {
65306                 viewerCompassAngle = e.bearing;
65307                 if (context.map().isTransformed()) return;
65308                 layer.selectAll('.viewfield-group.currentView').filter(function (d) {
65309                   return d.is_pano;
65310                 }).attr('transform', transform);
65311               });
65312             } else if (!services.mapillary && _mapillary) {
65313               _mapillary = null;
65314             }
65315
65316             return _mapillary;
65317           }
65318
65319           function editOn() {
65320             layer.style('display', 'block');
65321           }
65322
65323           function editOff() {
65324             layer.selectAll('.viewfield-group').remove();
65325             layer.style('display', 'none');
65326           }
65327
65328           function transform(d) {
65329             var t = svgPointTransform(projection)(d);
65330
65331             if (d.is_pano && viewerCompassAngle !== null && isFinite(viewerCompassAngle)) {
65332               t += ' rotate(' + Math.floor(viewerCompassAngle) + ',0,0)';
65333             } else if (d.ca) {
65334               t += ' rotate(' + Math.floor(d.ca) + ',0,0)';
65335             }
65336
65337             return t;
65338           }
65339
65340           function update() {
65341             var z = ~~context.map().zoom();
65342             var showViewfields = z >= minViewfieldZoom;
65343             var service = getService();
65344             var image = service && service.getActiveImage();
65345             var groups = layer.selectAll('.markers').selectAll('.viewfield-group').data(image ? [image] : [], function (d) {
65346               return d.id;
65347             }); // exit
65348
65349             groups.exit().remove(); // enter
65350
65351             var groupsEnter = groups.enter().append('g').attr('class', 'viewfield-group currentView highlighted');
65352             groupsEnter.append('g').attr('class', 'viewfield-scale'); // update
65353
65354             var markers = groups.merge(groupsEnter).attr('transform', transform).select('.viewfield-scale');
65355             markers.selectAll('circle').data([0]).enter().append('circle').attr('dx', '0').attr('dy', '0').attr('r', '6');
65356             var viewfields = markers.selectAll('.viewfield').data(showViewfields ? [0] : []);
65357             viewfields.exit().remove();
65358             viewfields.enter().insert('path', 'circle').attr('class', 'viewfield').attr('transform', 'scale(1.5,1.5),translate(-8, -13)').attr('d', 'M 6,9 C 8,8.4 8,8.4 10,9 L 16,-2 C 12,-5 4,-5 0,-2 z');
65359           }
65360
65361           function drawImages(selection) {
65362             var service = getService();
65363             layer = selection.selectAll('.layer-mapillary-position').data(service ? [0] : []);
65364             layer.exit().remove();
65365             var layerEnter = layer.enter().append('g').attr('class', 'layer-mapillary-position');
65366             layerEnter.append('g').attr('class', 'markers');
65367             layer = layerEnter.merge(layer);
65368
65369             if (service && ~~context.map().zoom() >= minZoom) {
65370               editOn();
65371               update();
65372             } else {
65373               editOff();
65374             }
65375           }
65376
65377           drawImages.enabled = function () {
65378             update();
65379             return this;
65380           };
65381
65382           drawImages.supported = function () {
65383             return !!getService();
65384           };
65385
65386           init();
65387           return drawImages;
65388         }
65389
65390         function svgMapillarySigns(projection, context, dispatch) {
65391           var throttledRedraw = throttle(function () {
65392             dispatch.call('change');
65393           }, 1000);
65394
65395           var minZoom = 12;
65396           var layer = select(null);
65397
65398           var _mapillary;
65399
65400           function init() {
65401             if (svgMapillarySigns.initialized) return; // run once
65402
65403             svgMapillarySigns.enabled = false;
65404             svgMapillarySigns.initialized = true;
65405           }
65406
65407           function getService() {
65408             if (services.mapillary && !_mapillary) {
65409               _mapillary = services.mapillary;
65410
65411               _mapillary.event.on('loadedSigns', throttledRedraw);
65412             } else if (!services.mapillary && _mapillary) {
65413               _mapillary = null;
65414             }
65415
65416             return _mapillary;
65417           }
65418
65419           function showLayer() {
65420             var service = getService();
65421             if (!service) return;
65422             service.loadSignResources(context);
65423             editOn();
65424           }
65425
65426           function hideLayer() {
65427             throttledRedraw.cancel();
65428             editOff();
65429           }
65430
65431           function editOn() {
65432             layer.style('display', 'block');
65433           }
65434
65435           function editOff() {
65436             layer.selectAll('.icon-sign').remove();
65437             layer.style('display', 'none');
65438           }
65439
65440           function click(d3_event, d) {
65441             var service = getService();
65442             if (!service) return;
65443             context.map().centerEase(d.loc);
65444             var selectedImageId = service.getActiveImage() && service.getActiveImage().id;
65445             service.getDetections(d.id).then(function (detections) {
65446               if (detections.length) {
65447                 var imageId = detections[0].image.id;
65448
65449                 if (imageId === selectedImageId) {
65450                   service.highlightDetection(detections[0]).selectImage(context, imageId);
65451                 } else {
65452                   service.ensureViewerLoaded(context).then(function () {
65453                     service.highlightDetection(detections[0]).selectImage(context, imageId).showViewer(context);
65454                   });
65455                 }
65456               }
65457             });
65458           }
65459
65460           function filterData(detectedFeatures) {
65461             var fromDate = context.photos().fromDate();
65462             var toDate = context.photos().toDate();
65463
65464             if (fromDate) {
65465               var fromTimestamp = new Date(fromDate).getTime();
65466               detectedFeatures = detectedFeatures.filter(function (feature) {
65467                 return new Date(feature.last_seen_at).getTime() >= fromTimestamp;
65468               });
65469             }
65470
65471             if (toDate) {
65472               var toTimestamp = new Date(toDate).getTime();
65473               detectedFeatures = detectedFeatures.filter(function (feature) {
65474                 return new Date(feature.first_seen_at).getTime() <= toTimestamp;
65475               });
65476             }
65477
65478             return detectedFeatures;
65479           }
65480
65481           function update() {
65482             var service = getService();
65483             var data = service ? service.signs(projection) : [];
65484             data = filterData(data);
65485             var transform = svgPointTransform(projection);
65486             var signs = layer.selectAll('.icon-sign').data(data, function (d) {
65487               return d.id;
65488             }); // exit
65489
65490             signs.exit().remove(); // enter
65491
65492             var enter = signs.enter().append('g').attr('class', 'icon-sign icon-detected').on('click', click);
65493             enter.append('use').attr('width', '24px').attr('height', '24px').attr('x', '-12px').attr('y', '-12px').attr('xlink:href', function (d) {
65494               return '#' + d.value;
65495             });
65496             enter.append('rect').attr('width', '24px').attr('height', '24px').attr('x', '-12px').attr('y', '-12px'); // update
65497
65498             signs.merge(enter).attr('transform', transform);
65499           }
65500
65501           function drawSigns(selection) {
65502             var enabled = svgMapillarySigns.enabled;
65503             var service = getService();
65504             layer = selection.selectAll('.layer-mapillary-signs').data(service ? [0] : []);
65505             layer.exit().remove();
65506             layer = layer.enter().append('g').attr('class', 'layer-mapillary-signs layer-mapillary-detections').style('display', enabled ? 'block' : 'none').merge(layer);
65507
65508             if (enabled) {
65509               if (service && ~~context.map().zoom() >= minZoom) {
65510                 editOn();
65511                 update();
65512                 service.loadSigns(projection);
65513                 service.showSignDetections(true);
65514               } else {
65515                 editOff();
65516               }
65517             } else if (service) {
65518               service.showSignDetections(false);
65519             }
65520           }
65521
65522           drawSigns.enabled = function (_) {
65523             if (!arguments.length) return svgMapillarySigns.enabled;
65524             svgMapillarySigns.enabled = _;
65525
65526             if (svgMapillarySigns.enabled) {
65527               showLayer();
65528               context.photos().on('change.mapillary_signs', update);
65529             } else {
65530               hideLayer();
65531               context.photos().on('change.mapillary_signs', null);
65532             }
65533
65534             dispatch.call('change');
65535             return this;
65536           };
65537
65538           drawSigns.supported = function () {
65539             return !!getService();
65540           };
65541
65542           init();
65543           return drawSigns;
65544         }
65545
65546         function svgMapillaryMapFeatures(projection, context, dispatch) {
65547           var throttledRedraw = throttle(function () {
65548             dispatch.call('change');
65549           }, 1000);
65550
65551           var minZoom = 12;
65552           var layer = select(null);
65553
65554           var _mapillary;
65555
65556           function init() {
65557             if (svgMapillaryMapFeatures.initialized) return; // run once
65558
65559             svgMapillaryMapFeatures.enabled = false;
65560             svgMapillaryMapFeatures.initialized = true;
65561           }
65562
65563           function getService() {
65564             if (services.mapillary && !_mapillary) {
65565               _mapillary = services.mapillary;
65566
65567               _mapillary.event.on('loadedMapFeatures', throttledRedraw);
65568             } else if (!services.mapillary && _mapillary) {
65569               _mapillary = null;
65570             }
65571
65572             return _mapillary;
65573           }
65574
65575           function showLayer() {
65576             var service = getService();
65577             if (!service) return;
65578             service.loadObjectResources(context);
65579             editOn();
65580           }
65581
65582           function hideLayer() {
65583             throttledRedraw.cancel();
65584             editOff();
65585           }
65586
65587           function editOn() {
65588             layer.style('display', 'block');
65589           }
65590
65591           function editOff() {
65592             layer.selectAll('.icon-map-feature').remove();
65593             layer.style('display', 'none');
65594           }
65595
65596           function click(d3_event, d) {
65597             var service = getService();
65598             if (!service) return;
65599             context.map().centerEase(d.loc);
65600             var selectedImageId = service.getActiveImage() && service.getActiveImage().id;
65601             service.getDetections(d.id).then(function (detections) {
65602               if (detections.length) {
65603                 var imageId = detections[0].image.id;
65604
65605                 if (imageId === selectedImageId) {
65606                   service.highlightDetection(detections[0]).selectImage(context, imageId);
65607                 } else {
65608                   service.ensureViewerLoaded(context).then(function () {
65609                     service.highlightDetection(detections[0]).selectImage(context, imageId).showViewer(context);
65610                   });
65611                 }
65612               }
65613             });
65614           }
65615
65616           function filterData(detectedFeatures) {
65617             var fromDate = context.photos().fromDate();
65618             var toDate = context.photos().toDate();
65619
65620             if (fromDate) {
65621               detectedFeatures = detectedFeatures.filter(function (feature) {
65622                 return new Date(feature.last_seen_at).getTime() >= new Date(fromDate).getTime();
65623               });
65624             }
65625
65626             if (toDate) {
65627               detectedFeatures = detectedFeatures.filter(function (feature) {
65628                 return new Date(feature.first_seen_at).getTime() <= new Date(toDate).getTime();
65629               });
65630             }
65631
65632             return detectedFeatures;
65633           }
65634
65635           function update() {
65636             var service = getService();
65637             var data = service ? service.mapFeatures(projection) : [];
65638             data = filterData(data);
65639             var transform = svgPointTransform(projection);
65640             var mapFeatures = layer.selectAll('.icon-map-feature').data(data, function (d) {
65641               return d.id;
65642             }); // exit
65643
65644             mapFeatures.exit().remove(); // enter
65645
65646             var enter = mapFeatures.enter().append('g').attr('class', 'icon-map-feature icon-detected').on('click', click);
65647             enter.append('title').text(function (d) {
65648               var id = d.value.replace(/--/g, '.').replace(/-/g, '_');
65649               return _t('mapillary_map_features.' + id);
65650             });
65651             enter.append('use').attr('width', '24px').attr('height', '24px').attr('x', '-12px').attr('y', '-12px').attr('xlink:href', function (d) {
65652               if (d.value === 'object--billboard') {
65653                 // no billboard icon right now, so use the advertisement icon
65654                 return '#object--sign--advertisement';
65655               }
65656
65657               return '#' + d.value;
65658             });
65659             enter.append('rect').attr('width', '24px').attr('height', '24px').attr('x', '-12px').attr('y', '-12px'); // update
65660
65661             mapFeatures.merge(enter).attr('transform', transform);
65662           }
65663
65664           function drawMapFeatures(selection) {
65665             var enabled = svgMapillaryMapFeatures.enabled;
65666             var service = getService();
65667             layer = selection.selectAll('.layer-mapillary-map-features').data(service ? [0] : []);
65668             layer.exit().remove();
65669             layer = layer.enter().append('g').attr('class', 'layer-mapillary-map-features layer-mapillary-detections').style('display', enabled ? 'block' : 'none').merge(layer);
65670
65671             if (enabled) {
65672               if (service && ~~context.map().zoom() >= minZoom) {
65673                 editOn();
65674                 update();
65675                 service.loadMapFeatures(projection);
65676                 service.showFeatureDetections(true);
65677               } else {
65678                 editOff();
65679               }
65680             } else if (service) {
65681               service.showFeatureDetections(false);
65682             }
65683           }
65684
65685           drawMapFeatures.enabled = function (_) {
65686             if (!arguments.length) return svgMapillaryMapFeatures.enabled;
65687             svgMapillaryMapFeatures.enabled = _;
65688
65689             if (svgMapillaryMapFeatures.enabled) {
65690               showLayer();
65691               context.photos().on('change.mapillary_map_features', update);
65692             } else {
65693               hideLayer();
65694               context.photos().on('change.mapillary_map_features', null);
65695             }
65696
65697             dispatch.call('change');
65698             return this;
65699           };
65700
65701           drawMapFeatures.supported = function () {
65702             return !!getService();
65703           };
65704
65705           init();
65706           return drawMapFeatures;
65707         }
65708
65709         function svgOpenstreetcamImages(projection, context, dispatch) {
65710           var throttledRedraw = throttle(function () {
65711             dispatch.call('change');
65712           }, 1000);
65713
65714           var minZoom = 12;
65715           var minMarkerZoom = 16;
65716           var minViewfieldZoom = 18;
65717           var layer = select(null);
65718
65719           var _openstreetcam;
65720
65721           function init() {
65722             if (svgOpenstreetcamImages.initialized) return; // run once
65723
65724             svgOpenstreetcamImages.enabled = false;
65725             svgOpenstreetcamImages.initialized = true;
65726           }
65727
65728           function getService() {
65729             if (services.openstreetcam && !_openstreetcam) {
65730               _openstreetcam = services.openstreetcam;
65731
65732               _openstreetcam.event.on('loadedImages', throttledRedraw);
65733             } else if (!services.openstreetcam && _openstreetcam) {
65734               _openstreetcam = null;
65735             }
65736
65737             return _openstreetcam;
65738           }
65739
65740           function showLayer() {
65741             var service = getService();
65742             if (!service) return;
65743             editOn();
65744             layer.style('opacity', 0).transition().duration(250).style('opacity', 1).on('end', function () {
65745               dispatch.call('change');
65746             });
65747           }
65748
65749           function hideLayer() {
65750             throttledRedraw.cancel();
65751             layer.transition().duration(250).style('opacity', 0).on('end', editOff);
65752           }
65753
65754           function editOn() {
65755             layer.style('display', 'block');
65756           }
65757
65758           function editOff() {
65759             layer.selectAll('.viewfield-group').remove();
65760             layer.style('display', 'none');
65761           }
65762
65763           function click(d3_event, d) {
65764             var service = getService();
65765             if (!service) return;
65766             service.ensureViewerLoaded(context).then(function () {
65767               service.selectImage(context, d.key).showViewer(context);
65768             });
65769             context.map().centerEase(d.loc);
65770           }
65771
65772           function mouseover(d3_event, d) {
65773             var service = getService();
65774             if (service) service.setStyles(context, d);
65775           }
65776
65777           function mouseout() {
65778             var service = getService();
65779             if (service) service.setStyles(context, null);
65780           }
65781
65782           function transform(d) {
65783             var t = svgPointTransform(projection)(d);
65784
65785             if (d.ca) {
65786               t += ' rotate(' + Math.floor(d.ca) + ',0,0)';
65787             }
65788
65789             return t;
65790           }
65791
65792           function filterImages(images) {
65793             var fromDate = context.photos().fromDate();
65794             var toDate = context.photos().toDate();
65795             var usernames = context.photos().usernames();
65796
65797             if (fromDate) {
65798               var fromTimestamp = new Date(fromDate).getTime();
65799               images = images.filter(function (item) {
65800                 return new Date(item.captured_at).getTime() >= fromTimestamp;
65801               });
65802             }
65803
65804             if (toDate) {
65805               var toTimestamp = new Date(toDate).getTime();
65806               images = images.filter(function (item) {
65807                 return new Date(item.captured_at).getTime() <= toTimestamp;
65808               });
65809             }
65810
65811             if (usernames) {
65812               images = images.filter(function (item) {
65813                 return usernames.indexOf(item.captured_by) !== -1;
65814               });
65815             }
65816
65817             return images;
65818           }
65819
65820           function filterSequences(sequences) {
65821             var fromDate = context.photos().fromDate();
65822             var toDate = context.photos().toDate();
65823             var usernames = context.photos().usernames();
65824
65825             if (fromDate) {
65826               var fromTimestamp = new Date(fromDate).getTime();
65827               sequences = sequences.filter(function (image) {
65828                 return new Date(image.properties.captured_at).getTime() >= fromTimestamp;
65829               });
65830             }
65831
65832             if (toDate) {
65833               var toTimestamp = new Date(toDate).getTime();
65834               sequences = sequences.filter(function (image) {
65835                 return new Date(image.properties.captured_at).getTime() <= toTimestamp;
65836               });
65837             }
65838
65839             if (usernames) {
65840               sequences = sequences.filter(function (image) {
65841                 return usernames.indexOf(image.properties.captured_by) !== -1;
65842               });
65843             }
65844
65845             return sequences;
65846           }
65847
65848           function update() {
65849             var viewer = context.container().select('.photoviewer');
65850             var selected = viewer.empty() ? undefined : viewer.datum();
65851             var z = ~~context.map().zoom();
65852             var showMarkers = z >= minMarkerZoom;
65853             var showViewfields = z >= minViewfieldZoom;
65854             var service = getService();
65855             var sequences = [];
65856             var images = [];
65857
65858             if (context.photos().showsFlat()) {
65859               sequences = service ? service.sequences(projection) : [];
65860               images = service && showMarkers ? service.images(projection) : [];
65861               sequences = filterSequences(sequences);
65862               images = filterImages(images);
65863             }
65864
65865             var traces = layer.selectAll('.sequences').selectAll('.sequence').data(sequences, function (d) {
65866               return d.properties.key;
65867             }); // exit
65868
65869             traces.exit().remove(); // enter/update
65870
65871             traces = traces.enter().append('path').attr('class', 'sequence').merge(traces).attr('d', svgPath(projection).geojson);
65872             var groups = layer.selectAll('.markers').selectAll('.viewfield-group').data(images, function (d) {
65873               return d.key;
65874             }); // exit
65875
65876             groups.exit().remove(); // enter
65877
65878             var groupsEnter = groups.enter().append('g').attr('class', 'viewfield-group').on('mouseenter', mouseover).on('mouseleave', mouseout).on('click', click);
65879             groupsEnter.append('g').attr('class', 'viewfield-scale'); // update
65880
65881             var markers = groups.merge(groupsEnter).sort(function (a, b) {
65882               return a === selected ? 1 : b === selected ? -1 : b.loc[1] - a.loc[1]; // sort Y
65883             }).attr('transform', transform).select('.viewfield-scale');
65884             markers.selectAll('circle').data([0]).enter().append('circle').attr('dx', '0').attr('dy', '0').attr('r', '6');
65885             var viewfields = markers.selectAll('.viewfield').data(showViewfields ? [0] : []);
65886             viewfields.exit().remove();
65887             viewfields.enter() // viewfields may or may not be drawn...
65888             .insert('path', 'circle') // but if they are, draw below the circles
65889             .attr('class', 'viewfield').attr('transform', 'scale(1.5,1.5),translate(-8, -13)').attr('d', 'M 6,9 C 8,8.4 8,8.4 10,9 L 16,-2 C 12,-5 4,-5 0,-2 z');
65890           }
65891
65892           function drawImages(selection) {
65893             var enabled = svgOpenstreetcamImages.enabled,
65894                 service = getService();
65895             layer = selection.selectAll('.layer-openstreetcam').data(service ? [0] : []);
65896             layer.exit().remove();
65897             var layerEnter = layer.enter().append('g').attr('class', 'layer-openstreetcam').style('display', enabled ? 'block' : 'none');
65898             layerEnter.append('g').attr('class', 'sequences');
65899             layerEnter.append('g').attr('class', 'markers');
65900             layer = layerEnter.merge(layer);
65901
65902             if (enabled) {
65903               if (service && ~~context.map().zoom() >= minZoom) {
65904                 editOn();
65905                 update();
65906                 service.loadImages(projection);
65907               } else {
65908                 editOff();
65909               }
65910             }
65911           }
65912
65913           drawImages.enabled = function (_) {
65914             if (!arguments.length) return svgOpenstreetcamImages.enabled;
65915             svgOpenstreetcamImages.enabled = _;
65916
65917             if (svgOpenstreetcamImages.enabled) {
65918               showLayer();
65919               context.photos().on('change.openstreetcam_images', update);
65920             } else {
65921               hideLayer();
65922               context.photos().on('change.openstreetcam_images', null);
65923             }
65924
65925             dispatch.call('change');
65926             return this;
65927           };
65928
65929           drawImages.supported = function () {
65930             return !!getService();
65931           };
65932
65933           init();
65934           return drawImages;
65935         }
65936
65937         function svgOsm(projection, context, dispatch) {
65938           var enabled = true;
65939
65940           function drawOsm(selection) {
65941             selection.selectAll('.layer-osm').data(['covered', 'areas', 'lines', 'points', 'labels']).enter().append('g').attr('class', function (d) {
65942               return 'layer-osm ' + d;
65943             });
65944             selection.selectAll('.layer-osm.points').selectAll('.points-group').data(['points', 'midpoints', 'vertices', 'turns']).enter().append('g').attr('class', function (d) {
65945               return 'points-group ' + d;
65946             });
65947           }
65948
65949           function showLayer() {
65950             var layer = context.surface().selectAll('.data-layer.osm');
65951             layer.interrupt();
65952             layer.classed('disabled', false).style('opacity', 0).transition().duration(250).style('opacity', 1).on('end interrupt', function () {
65953               dispatch.call('change');
65954             });
65955           }
65956
65957           function hideLayer() {
65958             var layer = context.surface().selectAll('.data-layer.osm');
65959             layer.interrupt();
65960             layer.transition().duration(250).style('opacity', 0).on('end interrupt', function () {
65961               layer.classed('disabled', true);
65962               dispatch.call('change');
65963             });
65964           }
65965
65966           drawOsm.enabled = function (val) {
65967             if (!arguments.length) return enabled;
65968             enabled = val;
65969
65970             if (enabled) {
65971               showLayer();
65972             } else {
65973               hideLayer();
65974             }
65975
65976             dispatch.call('change');
65977             return this;
65978           };
65979
65980           return drawOsm;
65981         }
65982
65983         var _notesEnabled = false;
65984
65985         var _osmService;
65986
65987         function svgNotes(projection, context, dispatch) {
65988           if (!dispatch) {
65989             dispatch = dispatch$8('change');
65990           }
65991
65992           var throttledRedraw = throttle(function () {
65993             dispatch.call('change');
65994           }, 1000);
65995
65996           var minZoom = 12;
65997           var touchLayer = select(null);
65998           var drawLayer = select(null);
65999           var _notesVisible = false;
66000
66001           function markerPath(selection, klass) {
66002             selection.attr('class', klass).attr('transform', 'translate(-8, -22)').attr('d', 'm17.5,0l-15,0c-1.37,0 -2.5,1.12 -2.5,2.5l0,11.25c0,1.37 1.12,2.5 2.5,2.5l3.75,0l0,3.28c0,0.38 0.43,0.6 0.75,0.37l4.87,-3.65l5.62,0c1.37,0 2.5,-1.12 2.5,-2.5l0,-11.25c0,-1.37 -1.12,-2.5 -2.5,-2.5z');
66003           } // Loosely-coupled osm service for fetching notes.
66004
66005
66006           function getService() {
66007             if (services.osm && !_osmService) {
66008               _osmService = services.osm;
66009
66010               _osmService.on('loadedNotes', throttledRedraw);
66011             } else if (!services.osm && _osmService) {
66012               _osmService = null;
66013             }
66014
66015             return _osmService;
66016           } // Show the notes
66017
66018
66019           function editOn() {
66020             if (!_notesVisible) {
66021               _notesVisible = true;
66022               drawLayer.style('display', 'block');
66023             }
66024           } // Immediately remove the notes and their touch targets
66025
66026
66027           function editOff() {
66028             if (_notesVisible) {
66029               _notesVisible = false;
66030               drawLayer.style('display', 'none');
66031               drawLayer.selectAll('.note').remove();
66032               touchLayer.selectAll('.note').remove();
66033             }
66034           } // Enable the layer.  This shows the notes and transitions them to visible.
66035
66036
66037           function layerOn() {
66038             editOn();
66039             drawLayer.style('opacity', 0).transition().duration(250).style('opacity', 1).on('end interrupt', function () {
66040               dispatch.call('change');
66041             });
66042           } // Disable the layer.  This transitions the layer invisible and then hides the notes.
66043
66044
66045           function layerOff() {
66046             throttledRedraw.cancel();
66047             drawLayer.interrupt();
66048             touchLayer.selectAll('.note').remove();
66049             drawLayer.transition().duration(250).style('opacity', 0).on('end interrupt', function () {
66050               editOff();
66051               dispatch.call('change');
66052             });
66053           } // Update the note markers
66054
66055
66056           function updateMarkers() {
66057             if (!_notesVisible || !_notesEnabled) return;
66058             var service = getService();
66059             var selectedID = context.selectedNoteID();
66060             var data = service ? service.notes(projection) : [];
66061             var getTransform = svgPointTransform(projection); // Draw markers..
66062
66063             var notes = drawLayer.selectAll('.note').data(data, function (d) {
66064               return d.status + d.id;
66065             }); // exit
66066
66067             notes.exit().remove(); // enter
66068
66069             var notesEnter = notes.enter().append('g').attr('class', function (d) {
66070               return 'note note-' + d.id + ' ' + d.status;
66071             }).classed('new', function (d) {
66072               return d.id < 0;
66073             });
66074             notesEnter.append('ellipse').attr('cx', 0.5).attr('cy', 1).attr('rx', 6.5).attr('ry', 3).attr('class', 'stroke');
66075             notesEnter.append('path').call(markerPath, 'shadow');
66076             notesEnter.append('use').attr('class', 'note-fill').attr('width', '20px').attr('height', '20px').attr('x', '-8px').attr('y', '-22px').attr('xlink:href', '#iD-icon-note');
66077             notesEnter.selectAll('.icon-annotation').data(function (d) {
66078               return [d];
66079             }).enter().append('use').attr('class', 'icon-annotation').attr('width', '10px').attr('height', '10px').attr('x', '-3px').attr('y', '-19px').attr('xlink:href', function (d) {
66080               if (d.id < 0) return '#iD-icon-plus';
66081               if (d.status === 'open') return '#iD-icon-close';
66082               return '#iD-icon-apply';
66083             }); // update
66084
66085             notes.merge(notesEnter).sort(sortY).classed('selected', function (d) {
66086               var mode = context.mode();
66087               var isMoving = mode && mode.id === 'drag-note'; // no shadows when dragging
66088
66089               return !isMoving && d.id === selectedID;
66090             }).attr('transform', getTransform); // Draw targets..
66091
66092             if (touchLayer.empty()) return;
66093             var fillClass = context.getDebug('target') ? 'pink ' : 'nocolor ';
66094             var targets = touchLayer.selectAll('.note').data(data, function (d) {
66095               return d.id;
66096             }); // exit
66097
66098             targets.exit().remove(); // enter/update
66099
66100             targets.enter().append('rect').attr('width', '20px').attr('height', '20px').attr('x', '-8px').attr('y', '-22px').merge(targets).sort(sortY).attr('class', function (d) {
66101               var newClass = d.id < 0 ? 'new' : '';
66102               return 'note target note-' + d.id + ' ' + fillClass + newClass;
66103             }).attr('transform', getTransform);
66104
66105             function sortY(a, b) {
66106               if (a.id === selectedID) return 1;
66107               if (b.id === selectedID) return -1;
66108               return b.loc[1] - a.loc[1];
66109             }
66110           } // Draw the notes layer and schedule loading notes and updating markers.
66111
66112
66113           function drawNotes(selection) {
66114             var service = getService();
66115             var surface = context.surface();
66116
66117             if (surface && !surface.empty()) {
66118               touchLayer = surface.selectAll('.data-layer.touch .layer-touch.markers');
66119             }
66120
66121             drawLayer = selection.selectAll('.layer-notes').data(service ? [0] : []);
66122             drawLayer.exit().remove();
66123             drawLayer = drawLayer.enter().append('g').attr('class', 'layer-notes').style('display', _notesEnabled ? 'block' : 'none').merge(drawLayer);
66124
66125             if (_notesEnabled) {
66126               if (service && ~~context.map().zoom() >= minZoom) {
66127                 editOn();
66128                 service.loadNotes(projection);
66129                 updateMarkers();
66130               } else {
66131                 editOff();
66132               }
66133             }
66134           } // Toggles the layer on and off
66135
66136
66137           drawNotes.enabled = function (val) {
66138             if (!arguments.length) return _notesEnabled;
66139             _notesEnabled = val;
66140
66141             if (_notesEnabled) {
66142               layerOn();
66143             } else {
66144               layerOff();
66145
66146               if (context.selectedNoteID()) {
66147                 context.enter(modeBrowse(context));
66148               }
66149             }
66150
66151             dispatch.call('change');
66152             return this;
66153           };
66154
66155           return drawNotes;
66156         }
66157
66158         function svgTouch() {
66159           function drawTouch(selection) {
66160             selection.selectAll('.layer-touch').data(['areas', 'lines', 'points', 'turns', 'markers']).enter().append('g').attr('class', function (d) {
66161               return 'layer-touch ' + d;
66162             });
66163           }
66164
66165           return drawTouch;
66166         }
66167
66168         function refresh(selection, node) {
66169           var cr = node.getBoundingClientRect();
66170           var prop = [cr.width, cr.height];
66171           selection.property('__dimensions__', prop);
66172           return prop;
66173         }
66174
66175         function utilGetDimensions(selection, force) {
66176           if (!selection || selection.empty()) {
66177             return [0, 0];
66178           }
66179
66180           var node = selection.node(),
66181               cached = selection.property('__dimensions__');
66182           return !cached || force ? refresh(selection, node) : cached;
66183         }
66184         function utilSetDimensions(selection, dimensions) {
66185           if (!selection || selection.empty()) {
66186             return selection;
66187           }
66188
66189           var node = selection.node();
66190
66191           if (dimensions === null) {
66192             refresh(selection, node);
66193             return selection;
66194           }
66195
66196           return selection.property('__dimensions__', [dimensions[0], dimensions[1]]).attr('width', dimensions[0]).attr('height', dimensions[1]);
66197         }
66198
66199         function svgLayers(projection, context) {
66200           var dispatch = dispatch$8('change');
66201           var svg = select(null);
66202           var _layers = [{
66203             id: 'osm',
66204             layer: svgOsm(projection, context, dispatch)
66205           }, {
66206             id: 'notes',
66207             layer: svgNotes(projection, context, dispatch)
66208           }, {
66209             id: 'data',
66210             layer: svgData(projection, context, dispatch)
66211           }, {
66212             id: 'keepRight',
66213             layer: svgKeepRight(projection, context, dispatch)
66214           }, {
66215             id: 'improveOSM',
66216             layer: svgImproveOSM(projection, context, dispatch)
66217           }, {
66218             id: 'osmose',
66219             layer: svgOsmose(projection, context, dispatch)
66220           }, {
66221             id: 'streetside',
66222             layer: svgStreetside(projection, context, dispatch)
66223           }, {
66224             id: 'mapillary',
66225             layer: svgMapillaryImages(projection, context, dispatch)
66226           }, {
66227             id: 'mapillary-position',
66228             layer: svgMapillaryPosition(projection, context)
66229           }, {
66230             id: 'mapillary-map-features',
66231             layer: svgMapillaryMapFeatures(projection, context, dispatch)
66232           }, {
66233             id: 'mapillary-signs',
66234             layer: svgMapillarySigns(projection, context, dispatch)
66235           }, {
66236             id: 'openstreetcam',
66237             layer: svgOpenstreetcamImages(projection, context, dispatch)
66238           }, {
66239             id: 'debug',
66240             layer: svgDebug(projection, context)
66241           }, {
66242             id: 'geolocate',
66243             layer: svgGeolocate(projection)
66244           }, {
66245             id: 'touch',
66246             layer: svgTouch()
66247           }];
66248
66249           function drawLayers(selection) {
66250             svg = selection.selectAll('.surface').data([0]);
66251             svg = svg.enter().append('svg').attr('class', 'surface').merge(svg);
66252             var defs = svg.selectAll('.surface-defs').data([0]);
66253             defs.enter().append('defs').attr('class', 'surface-defs');
66254             var groups = svg.selectAll('.data-layer').data(_layers);
66255             groups.exit().remove();
66256             groups.enter().append('g').attr('class', function (d) {
66257               return 'data-layer ' + d.id;
66258             }).merge(groups).each(function (d) {
66259               select(this).call(d.layer);
66260             });
66261           }
66262
66263           drawLayers.all = function () {
66264             return _layers;
66265           };
66266
66267           drawLayers.layer = function (id) {
66268             var obj = _layers.find(function (o) {
66269               return o.id === id;
66270             });
66271
66272             return obj && obj.layer;
66273           };
66274
66275           drawLayers.only = function (what) {
66276             var arr = [].concat(what);
66277
66278             var all = _layers.map(function (layer) {
66279               return layer.id;
66280             });
66281
66282             return drawLayers.remove(utilArrayDifference(all, arr));
66283           };
66284
66285           drawLayers.remove = function (what) {
66286             var arr = [].concat(what);
66287             arr.forEach(function (id) {
66288               _layers = _layers.filter(function (o) {
66289                 return o.id !== id;
66290               });
66291             });
66292             dispatch.call('change');
66293             return this;
66294           };
66295
66296           drawLayers.add = function (what) {
66297             var arr = [].concat(what);
66298             arr.forEach(function (obj) {
66299               if ('id' in obj && 'layer' in obj) {
66300                 _layers.push(obj);
66301               }
66302             });
66303             dispatch.call('change');
66304             return this;
66305           };
66306
66307           drawLayers.dimensions = function (val) {
66308             if (!arguments.length) return utilGetDimensions(svg);
66309             utilSetDimensions(svg, val);
66310             return this;
66311           };
66312
66313           return utilRebind(drawLayers, dispatch, 'on');
66314         }
66315
66316         function svgLines(projection, context) {
66317           var detected = utilDetect();
66318           var highway_stack = {
66319             motorway: 0,
66320             motorway_link: 1,
66321             trunk: 2,
66322             trunk_link: 3,
66323             primary: 4,
66324             primary_link: 5,
66325             secondary: 6,
66326             tertiary: 7,
66327             unclassified: 8,
66328             residential: 9,
66329             service: 10,
66330             footway: 11
66331           };
66332
66333           function drawTargets(selection, graph, entities, filter) {
66334             var targetClass = context.getDebug('target') ? 'pink ' : 'nocolor ';
66335             var nopeClass = context.getDebug('target') ? 'red ' : 'nocolor ';
66336             var getPath = svgPath(projection).geojson;
66337             var activeID = context.activeID();
66338             var base = context.history().base(); // The targets and nopes will be MultiLineString sub-segments of the ways
66339
66340             var data = {
66341               targets: [],
66342               nopes: []
66343             };
66344             entities.forEach(function (way) {
66345               var features = svgSegmentWay(way, graph, activeID);
66346               data.targets.push.apply(data.targets, features.passive);
66347               data.nopes.push.apply(data.nopes, features.active);
66348             }); // Targets allow hover and vertex snapping
66349
66350             var targetData = data.targets.filter(getPath);
66351             var targets = selection.selectAll('.line.target-allowed').filter(function (d) {
66352               return filter(d.properties.entity);
66353             }).data(targetData, function key(d) {
66354               return d.id;
66355             }); // exit
66356
66357             targets.exit().remove();
66358
66359             var segmentWasEdited = function segmentWasEdited(d) {
66360               var wayID = d.properties.entity.id; // if the whole line was edited, don't draw segment changes
66361
66362               if (!base.entities[wayID] || !fastDeepEqual(graph.entities[wayID].nodes, base.entities[wayID].nodes)) {
66363                 return false;
66364               }
66365
66366               return d.properties.nodes.some(function (n) {
66367                 return !base.entities[n.id] || !fastDeepEqual(graph.entities[n.id].loc, base.entities[n.id].loc);
66368               });
66369             }; // enter/update
66370
66371
66372             targets.enter().append('path').merge(targets).attr('d', getPath).attr('class', function (d) {
66373               return 'way line target target-allowed ' + targetClass + d.id;
66374             }).classed('segment-edited', segmentWasEdited); // NOPE
66375
66376             var nopeData = data.nopes.filter(getPath);
66377             var nopes = selection.selectAll('.line.target-nope').filter(function (d) {
66378               return filter(d.properties.entity);
66379             }).data(nopeData, function key(d) {
66380               return d.id;
66381             }); // exit
66382
66383             nopes.exit().remove(); // enter/update
66384
66385             nopes.enter().append('path').merge(nopes).attr('d', getPath).attr('class', function (d) {
66386               return 'way line target target-nope ' + nopeClass + d.id;
66387             }).classed('segment-edited', segmentWasEdited);
66388           }
66389
66390           function drawLines(selection, graph, entities, filter) {
66391             var base = context.history().base();
66392
66393             function waystack(a, b) {
66394               var selected = context.selectedIDs();
66395               var scoreA = selected.indexOf(a.id) !== -1 ? 20 : 0;
66396               var scoreB = selected.indexOf(b.id) !== -1 ? 20 : 0;
66397
66398               if (a.tags.highway) {
66399                 scoreA -= highway_stack[a.tags.highway];
66400               }
66401
66402               if (b.tags.highway) {
66403                 scoreB -= highway_stack[b.tags.highway];
66404               }
66405
66406               return scoreA - scoreB;
66407             }
66408
66409             function drawLineGroup(selection, klass, isSelected) {
66410               // Note: Don't add `.selected` class in draw modes
66411               var mode = context.mode();
66412               var isDrawing = mode && /^draw/.test(mode.id);
66413               var selectedClass = !isDrawing && isSelected ? 'selected ' : '';
66414               var lines = selection.selectAll('path').filter(filter).data(getPathData(isSelected), osmEntity.key);
66415               lines.exit().remove(); // Optimization: Call expensive TagClasses only on enter selection. This
66416               // works because osmEntity.key is defined to include the entity v attribute.
66417
66418               lines.enter().append('path').attr('class', function (d) {
66419                 var prefix = 'way line'; // if this line isn't styled by its own tags
66420
66421                 if (!d.hasInterestingTags()) {
66422                   var parentRelations = graph.parentRelations(d);
66423                   var parentMultipolygons = parentRelations.filter(function (relation) {
66424                     return relation.isMultipolygon();
66425                   }); // and if it's a member of at least one multipolygon relation
66426
66427                   if (parentMultipolygons.length > 0 && // and only multipolygon relations
66428                   parentRelations.length === parentMultipolygons.length) {
66429                     // then fudge the classes to style this as an area edge
66430                     prefix = 'relation area';
66431                   }
66432                 }
66433
66434                 var oldMPClass = oldMultiPolygonOuters[d.id] ? 'old-multipolygon ' : '';
66435                 return prefix + ' ' + klass + ' ' + selectedClass + oldMPClass + d.id;
66436               }).classed('added', function (d) {
66437                 return !base.entities[d.id];
66438               }).classed('geometry-edited', function (d) {
66439                 return graph.entities[d.id] && base.entities[d.id] && !fastDeepEqual(graph.entities[d.id].nodes, base.entities[d.id].nodes);
66440               }).classed('retagged', function (d) {
66441                 return graph.entities[d.id] && base.entities[d.id] && !fastDeepEqual(graph.entities[d.id].tags, base.entities[d.id].tags);
66442               }).call(svgTagClasses()).merge(lines).sort(waystack).attr('d', getPath).call(svgTagClasses().tags(svgRelationMemberTags(graph)));
66443               return selection;
66444             }
66445
66446             function getPathData(isSelected) {
66447               return function () {
66448                 var layer = this.parentNode.__data__;
66449                 var data = pathdata[layer] || [];
66450                 return data.filter(function (d) {
66451                   if (isSelected) {
66452                     return context.selectedIDs().indexOf(d.id) !== -1;
66453                   } else {
66454                     return context.selectedIDs().indexOf(d.id) === -1;
66455                   }
66456                 });
66457               };
66458             }
66459
66460             function addMarkers(layergroup, pathclass, groupclass, groupdata, marker) {
66461               var markergroup = layergroup.selectAll('g.' + groupclass).data([pathclass]);
66462               markergroup = markergroup.enter().append('g').attr('class', groupclass).merge(markergroup);
66463               var markers = markergroup.selectAll('path').filter(filter).data(function data() {
66464                 return groupdata[this.parentNode.__data__] || [];
66465               }, function key(d) {
66466                 return [d.id, d.index];
66467               });
66468               markers.exit().remove();
66469               markers = markers.enter().append('path').attr('class', pathclass).merge(markers).attr('marker-mid', marker).attr('d', function (d) {
66470                 return d.d;
66471               });
66472
66473               if (detected.ie) {
66474                 markers.each(function () {
66475                   this.parentNode.insertBefore(this, this);
66476                 });
66477               }
66478             }
66479
66480             var getPath = svgPath(projection, graph);
66481             var ways = [];
66482             var onewaydata = {};
66483             var sideddata = {};
66484             var oldMultiPolygonOuters = {};
66485
66486             for (var i = 0; i < entities.length; i++) {
66487               var entity = entities[i];
66488               var outer = osmOldMultipolygonOuterMember(entity, graph);
66489
66490               if (outer) {
66491                 ways.push(entity.mergeTags(outer.tags));
66492                 oldMultiPolygonOuters[outer.id] = true;
66493               } else if (entity.geometry(graph) === 'line') {
66494                 ways.push(entity);
66495               }
66496             }
66497
66498             ways = ways.filter(getPath);
66499             var pathdata = utilArrayGroupBy(ways, function (way) {
66500               return way.layer();
66501             });
66502             Object.keys(pathdata).forEach(function (k) {
66503               var v = pathdata[k];
66504               var onewayArr = v.filter(function (d) {
66505                 return d.isOneWay();
66506               });
66507               var onewaySegments = svgMarkerSegments(projection, graph, 35, function shouldReverse(entity) {
66508                 return entity.tags.oneway === '-1';
66509               }, function bothDirections(entity) {
66510                 return entity.tags.oneway === 'reversible' || entity.tags.oneway === 'alternating';
66511               });
66512               onewaydata[k] = utilArrayFlatten(onewayArr.map(onewaySegments));
66513               var sidedArr = v.filter(function (d) {
66514                 return d.isSided();
66515               });
66516               var sidedSegments = svgMarkerSegments(projection, graph, 30, function shouldReverse() {
66517                 return false;
66518               }, function bothDirections() {
66519                 return false;
66520               });
66521               sideddata[k] = utilArrayFlatten(sidedArr.map(sidedSegments));
66522             });
66523             var covered = selection.selectAll('.layer-osm.covered'); // under areas
66524
66525             var uncovered = selection.selectAll('.layer-osm.lines'); // over areas
66526
66527             var touchLayer = selection.selectAll('.layer-touch.lines'); // Draw lines..
66528
66529             [covered, uncovered].forEach(function (selection) {
66530               var range = selection === covered ? range$1(-10, 0) : range$1(0, 11);
66531               var layergroup = selection.selectAll('g.layergroup').data(range);
66532               layergroup = layergroup.enter().append('g').attr('class', function (d) {
66533                 return 'layergroup layer' + String(d);
66534               }).merge(layergroup);
66535               layergroup.selectAll('g.linegroup').data(['shadow', 'casing', 'stroke', 'shadow-highlighted', 'casing-highlighted', 'stroke-highlighted']).enter().append('g').attr('class', function (d) {
66536                 return 'linegroup line-' + d;
66537               });
66538               layergroup.selectAll('g.line-shadow').call(drawLineGroup, 'shadow', false);
66539               layergroup.selectAll('g.line-casing').call(drawLineGroup, 'casing', false);
66540               layergroup.selectAll('g.line-stroke').call(drawLineGroup, 'stroke', false);
66541               layergroup.selectAll('g.line-shadow-highlighted').call(drawLineGroup, 'shadow', true);
66542               layergroup.selectAll('g.line-casing-highlighted').call(drawLineGroup, 'casing', true);
66543               layergroup.selectAll('g.line-stroke-highlighted').call(drawLineGroup, 'stroke', true);
66544               addMarkers(layergroup, 'oneway', 'onewaygroup', onewaydata, 'url(#ideditor-oneway-marker)');
66545               addMarkers(layergroup, 'sided', 'sidedgroup', sideddata, function marker(d) {
66546                 var category = graph.entity(d.id).sidednessIdentifier();
66547                 return 'url(#ideditor-sided-marker-' + category + ')';
66548               });
66549             }); // Draw touch targets..
66550
66551             touchLayer.call(drawTargets, graph, ways, filter);
66552           }
66553
66554           return drawLines;
66555         }
66556
66557         function svgMidpoints(projection, context) {
66558           var targetRadius = 8;
66559
66560           function drawTargets(selection, graph, entities, filter) {
66561             var fillClass = context.getDebug('target') ? 'pink ' : 'nocolor ';
66562             var getTransform = svgPointTransform(projection).geojson;
66563             var data = entities.map(function (midpoint) {
66564               return {
66565                 type: 'Feature',
66566                 id: midpoint.id,
66567                 properties: {
66568                   target: true,
66569                   entity: midpoint
66570                 },
66571                 geometry: {
66572                   type: 'Point',
66573                   coordinates: midpoint.loc
66574                 }
66575               };
66576             });
66577             var targets = selection.selectAll('.midpoint.target').filter(function (d) {
66578               return filter(d.properties.entity);
66579             }).data(data, function key(d) {
66580               return d.id;
66581             }); // exit
66582
66583             targets.exit().remove(); // enter/update
66584
66585             targets.enter().append('circle').attr('r', targetRadius).merge(targets).attr('class', function (d) {
66586               return 'node midpoint target ' + fillClass + d.id;
66587             }).attr('transform', getTransform);
66588           }
66589
66590           function drawMidpoints(selection, graph, entities, filter, extent) {
66591             var drawLayer = selection.selectAll('.layer-osm.points .points-group.midpoints');
66592             var touchLayer = selection.selectAll('.layer-touch.points');
66593             var mode = context.mode();
66594
66595             if (mode && mode.id !== 'select' || !context.map().withinEditableZoom()) {
66596               drawLayer.selectAll('.midpoint').remove();
66597               touchLayer.selectAll('.midpoint.target').remove();
66598               return;
66599             }
66600
66601             var poly = extent.polygon();
66602             var midpoints = {};
66603
66604             for (var i = 0; i < entities.length; i++) {
66605               var entity = entities[i];
66606               if (entity.type !== 'way') continue;
66607               if (!filter(entity)) continue;
66608               if (context.selectedIDs().indexOf(entity.id) < 0) continue;
66609               var nodes = graph.childNodes(entity);
66610
66611               for (var j = 0; j < nodes.length - 1; j++) {
66612                 var a = nodes[j];
66613                 var b = nodes[j + 1];
66614                 var id = [a.id, b.id].sort().join('-');
66615
66616                 if (midpoints[id]) {
66617                   midpoints[id].parents.push(entity);
66618                 } else if (geoVecLength(projection(a.loc), projection(b.loc)) > 40) {
66619                   var point = geoVecInterp(a.loc, b.loc, 0.5);
66620                   var loc = null;
66621
66622                   if (extent.intersects(point)) {
66623                     loc = point;
66624                   } else {
66625                     for (var k = 0; k < 4; k++) {
66626                       point = geoLineIntersection([a.loc, b.loc], [poly[k], poly[k + 1]]);
66627
66628                       if (point && geoVecLength(projection(a.loc), projection(point)) > 20 && geoVecLength(projection(b.loc), projection(point)) > 20) {
66629                         loc = point;
66630                         break;
66631                       }
66632                     }
66633                   }
66634
66635                   if (loc) {
66636                     midpoints[id] = {
66637                       type: 'midpoint',
66638                       id: id,
66639                       loc: loc,
66640                       edge: [a.id, b.id],
66641                       parents: [entity]
66642                     };
66643                   }
66644                 }
66645               }
66646             }
66647
66648             function midpointFilter(d) {
66649               if (midpoints[d.id]) return true;
66650
66651               for (var i = 0; i < d.parents.length; i++) {
66652                 if (filter(d.parents[i])) {
66653                   return true;
66654                 }
66655               }
66656
66657               return false;
66658             }
66659
66660             var groups = drawLayer.selectAll('.midpoint').filter(midpointFilter).data(Object.values(midpoints), function (d) {
66661               return d.id;
66662             });
66663             groups.exit().remove();
66664             var enter = groups.enter().insert('g', ':first-child').attr('class', 'midpoint');
66665             enter.append('polygon').attr('points', '-6,8 10,0 -6,-8').attr('class', 'shadow');
66666             enter.append('polygon').attr('points', '-3,4 5,0 -3,-4').attr('class', 'fill');
66667             groups = groups.merge(enter).attr('transform', function (d) {
66668               var translate = svgPointTransform(projection);
66669               var a = graph.entity(d.edge[0]);
66670               var b = graph.entity(d.edge[1]);
66671               var angle = geoAngle(a, b, projection) * (180 / Math.PI);
66672               return translate(d) + ' rotate(' + angle + ')';
66673             }).call(svgTagClasses().tags(function (d) {
66674               return d.parents[0].tags;
66675             })); // Propagate data bindings.
66676
66677             groups.select('polygon.shadow');
66678             groups.select('polygon.fill'); // Draw touch targets..
66679
66680             touchLayer.call(drawTargets, graph, Object.values(midpoints), midpointFilter);
66681           }
66682
66683           return drawMidpoints;
66684         }
66685
66686         function svgPoints(projection, context) {
66687           function markerPath(selection, klass) {
66688             selection.attr('class', klass).attr('transform', 'translate(-8, -23)').attr('d', 'M 17,8 C 17,13 11,21 8.5,23.5 C 6,21 0,13 0,8 C 0,4 4,-0.5 8.5,-0.5 C 13,-0.5 17,4 17,8 z');
66689           }
66690
66691           function sortY(a, b) {
66692             return b.loc[1] - a.loc[1];
66693           } // Avoid exit/enter if we're just moving stuff around.
66694           // The node will get a new version but we only need to run the update selection.
66695
66696
66697           function fastEntityKey(d) {
66698             var mode = context.mode();
66699             var isMoving = mode && /^(add|draw|drag|move|rotate)/.test(mode.id);
66700             return isMoving ? d.id : osmEntity.key(d);
66701           }
66702
66703           function drawTargets(selection, graph, entities, filter) {
66704             var fillClass = context.getDebug('target') ? 'pink ' : 'nocolor ';
66705             var getTransform = svgPointTransform(projection).geojson;
66706             var activeID = context.activeID();
66707             var data = [];
66708             entities.forEach(function (node) {
66709               if (activeID === node.id) return; // draw no target on the activeID
66710
66711               data.push({
66712                 type: 'Feature',
66713                 id: node.id,
66714                 properties: {
66715                   target: true,
66716                   entity: node
66717                 },
66718                 geometry: node.asGeoJSON()
66719               });
66720             });
66721             var targets = selection.selectAll('.point.target').filter(function (d) {
66722               return filter(d.properties.entity);
66723             }).data(data, function key(d) {
66724               return d.id;
66725             }); // exit
66726
66727             targets.exit().remove(); // enter/update
66728
66729             targets.enter().append('rect').attr('x', -10).attr('y', -26).attr('width', 20).attr('height', 30).merge(targets).attr('class', function (d) {
66730               return 'node point target ' + fillClass + d.id;
66731             }).attr('transform', getTransform);
66732           }
66733
66734           function drawPoints(selection, graph, entities, filter) {
66735             var wireframe = context.surface().classed('fill-wireframe');
66736             var zoom = geoScaleToZoom(projection.scale());
66737             var base = context.history().base(); // Points with a direction will render as vertices at higher zooms..
66738
66739             function renderAsPoint(entity) {
66740               return entity.geometry(graph) === 'point' && !(zoom >= 18 && entity.directions(graph, projection).length);
66741             } // All points will render as vertices in wireframe mode too..
66742
66743
66744             var points = wireframe ? [] : entities.filter(renderAsPoint);
66745             points.sort(sortY);
66746             var drawLayer = selection.selectAll('.layer-osm.points .points-group.points');
66747             var touchLayer = selection.selectAll('.layer-touch.points'); // Draw points..
66748
66749             var groups = drawLayer.selectAll('g.point').filter(filter).data(points, fastEntityKey);
66750             groups.exit().remove();
66751             var enter = groups.enter().append('g').attr('class', function (d) {
66752               return 'node point ' + d.id;
66753             }).order();
66754             enter.append('path').call(markerPath, 'shadow');
66755             enter.append('ellipse').attr('cx', 0.5).attr('cy', 1).attr('rx', 6.5).attr('ry', 3).attr('class', 'stroke');
66756             enter.append('path').call(markerPath, 'stroke');
66757             enter.append('use').attr('transform', 'translate(-5, -19)').attr('class', 'icon').attr('width', '11px').attr('height', '11px');
66758             groups = groups.merge(enter).attr('transform', svgPointTransform(projection)).classed('added', function (d) {
66759               return !base.entities[d.id]; // if it doesn't exist in the base graph, it's new
66760             }).classed('moved', function (d) {
66761               return base.entities[d.id] && !fastDeepEqual(graph.entities[d.id].loc, base.entities[d.id].loc);
66762             }).classed('retagged', function (d) {
66763               return base.entities[d.id] && !fastDeepEqual(graph.entities[d.id].tags, base.entities[d.id].tags);
66764             }).call(svgTagClasses());
66765             groups.select('.shadow'); // propagate bound data
66766
66767             groups.select('.stroke'); // propagate bound data
66768
66769             groups.select('.icon') // propagate bound data
66770             .attr('xlink:href', function (entity) {
66771               var preset = _mainPresetIndex.match(entity, graph);
66772               var picon = preset && preset.icon;
66773
66774               if (!picon) {
66775                 return '';
66776               } else {
66777                 var isMaki = /^maki-/.test(picon);
66778                 return '#' + picon + (isMaki ? '-11' : '');
66779               }
66780             }); // Draw touch targets..
66781
66782             touchLayer.call(drawTargets, graph, points, filter);
66783           }
66784
66785           return drawPoints;
66786         }
66787
66788         function svgTurns(projection, context) {
66789           function icon(turn) {
66790             var u = turn.u ? '-u' : '';
66791             if (turn.no) return '#iD-turn-no' + u;
66792             if (turn.only) return '#iD-turn-only' + u;
66793             return '#iD-turn-yes' + u;
66794           }
66795
66796           function drawTurns(selection, graph, turns) {
66797             function turnTransform(d) {
66798               var pxRadius = 50;
66799               var toWay = graph.entity(d.to.way);
66800               var toPoints = graph.childNodes(toWay).map(function (n) {
66801                 return n.loc;
66802               }).map(projection);
66803               var toLength = geoPathLength(toPoints);
66804               var mid = toLength / 2; // midpoint of destination way
66805
66806               var toNode = graph.entity(d.to.node);
66807               var toVertex = graph.entity(d.to.vertex);
66808               var a = geoAngle(toVertex, toNode, projection);
66809               var o = projection(toVertex.loc);
66810               var r = d.u ? 0 // u-turn: no radius
66811               : !toWay.__via ? pxRadius // leaf way: put marker at pxRadius
66812               : Math.min(mid, pxRadius); // via way: prefer pxRadius, fallback to mid for very short ways
66813
66814               return 'translate(' + (r * Math.cos(a) + o[0]) + ',' + (r * Math.sin(a) + o[1]) + ') ' + 'rotate(' + a * 180 / Math.PI + ')';
66815             }
66816
66817             var drawLayer = selection.selectAll('.layer-osm.points .points-group.turns');
66818             var touchLayer = selection.selectAll('.layer-touch.turns'); // Draw turns..
66819
66820             var groups = drawLayer.selectAll('g.turn').data(turns, function (d) {
66821               return d.key;
66822             }); // exit
66823
66824             groups.exit().remove(); // enter
66825
66826             var groupsEnter = groups.enter().append('g').attr('class', function (d) {
66827               return 'turn ' + d.key;
66828             });
66829             var turnsEnter = groupsEnter.filter(function (d) {
66830               return !d.u;
66831             });
66832             turnsEnter.append('rect').attr('transform', 'translate(-22, -12)').attr('width', '44').attr('height', '24');
66833             turnsEnter.append('use').attr('transform', 'translate(-22, -12)').attr('width', '44').attr('height', '24');
66834             var uEnter = groupsEnter.filter(function (d) {
66835               return d.u;
66836             });
66837             uEnter.append('circle').attr('r', '16');
66838             uEnter.append('use').attr('transform', 'translate(-16, -16)').attr('width', '32').attr('height', '32'); // update
66839
66840             groups = groups.merge(groupsEnter).attr('opacity', function (d) {
66841               return d.direct === false ? '0.7' : null;
66842             }).attr('transform', turnTransform);
66843             groups.select('use').attr('xlink:href', icon);
66844             groups.select('rect'); // propagate bound data
66845
66846             groups.select('circle'); // propagate bound data
66847             // Draw touch targets..
66848
66849             var fillClass = context.getDebug('target') ? 'pink ' : 'nocolor ';
66850             groups = touchLayer.selectAll('g.turn').data(turns, function (d) {
66851               return d.key;
66852             }); // exit
66853
66854             groups.exit().remove(); // enter
66855
66856             groupsEnter = groups.enter().append('g').attr('class', function (d) {
66857               return 'turn ' + d.key;
66858             });
66859             turnsEnter = groupsEnter.filter(function (d) {
66860               return !d.u;
66861             });
66862             turnsEnter.append('rect').attr('class', 'target ' + fillClass).attr('transform', 'translate(-22, -12)').attr('width', '44').attr('height', '24');
66863             uEnter = groupsEnter.filter(function (d) {
66864               return d.u;
66865             });
66866             uEnter.append('circle').attr('class', 'target ' + fillClass).attr('r', '16'); // update
66867
66868             groups = groups.merge(groupsEnter).attr('transform', turnTransform);
66869             groups.select('rect'); // propagate bound data
66870
66871             groups.select('circle'); // propagate bound data
66872
66873             return this;
66874           }
66875
66876           return drawTurns;
66877         }
66878
66879         function svgVertices(projection, context) {
66880           var radiuses = {
66881             //       z16-, z17,   z18+,  w/icon
66882             shadow: [6, 7.5, 7.5, 12],
66883             stroke: [2.5, 3.5, 3.5, 8],
66884             fill: [1, 1.5, 1.5, 1.5]
66885           };
66886
66887           var _currHoverTarget;
66888
66889           var _currPersistent = {};
66890           var _currHover = {};
66891           var _prevHover = {};
66892           var _currSelected = {};
66893           var _prevSelected = {};
66894           var _radii = {};
66895
66896           function sortY(a, b) {
66897             return b.loc[1] - a.loc[1];
66898           } // Avoid exit/enter if we're just moving stuff around.
66899           // The node will get a new version but we only need to run the update selection.
66900
66901
66902           function fastEntityKey(d) {
66903             var mode = context.mode();
66904             var isMoving = mode && /^(add|draw|drag|move|rotate)/.test(mode.id);
66905             return isMoving ? d.id : osmEntity.key(d);
66906           }
66907
66908           function draw(selection, graph, vertices, sets, filter) {
66909             sets = sets || {
66910               selected: {},
66911               important: {},
66912               hovered: {}
66913             };
66914             var icons = {};
66915             var directions = {};
66916             var wireframe = context.surface().classed('fill-wireframe');
66917             var zoom = geoScaleToZoom(projection.scale());
66918             var z = zoom < 17 ? 0 : zoom < 18 ? 1 : 2;
66919             var activeID = context.activeID();
66920             var base = context.history().base();
66921
66922             function getIcon(d) {
66923               // always check latest entity, as fastEntityKey avoids enter/exit now
66924               var entity = graph.entity(d.id);
66925               if (entity.id in icons) return icons[entity.id];
66926               icons[entity.id] = entity.hasInterestingTags() && _mainPresetIndex.match(entity, graph).icon;
66927               return icons[entity.id];
66928             } // memoize directions results, return false for empty arrays (for use in filter)
66929
66930
66931             function getDirections(entity) {
66932               if (entity.id in directions) return directions[entity.id];
66933               var angles = entity.directions(graph, projection);
66934               directions[entity.id] = angles.length ? angles : false;
66935               return angles;
66936             }
66937
66938             function updateAttributes(selection) {
66939               ['shadow', 'stroke', 'fill'].forEach(function (klass) {
66940                 var rads = radiuses[klass];
66941                 selection.selectAll('.' + klass).each(function (entity) {
66942                   var i = z && getIcon(entity);
66943                   var r = rads[i ? 3 : z]; // slightly increase the size of unconnected endpoints #3775
66944
66945                   if (entity.id !== activeID && entity.isEndpoint(graph) && !entity.isConnected(graph)) {
66946                     r += 1.5;
66947                   }
66948
66949                   if (klass === 'shadow') {
66950                     // remember this value, so we don't need to
66951                     _radii[entity.id] = r; // recompute it when we draw the touch targets
66952                   }
66953
66954                   select(this).attr('r', r).attr('visibility', i && klass === 'fill' ? 'hidden' : null);
66955                 });
66956               });
66957             }
66958
66959             vertices.sort(sortY);
66960             var groups = selection.selectAll('g.vertex').filter(filter).data(vertices, fastEntityKey); // exit
66961
66962             groups.exit().remove(); // enter
66963
66964             var enter = groups.enter().append('g').attr('class', function (d) {
66965               return 'node vertex ' + d.id;
66966             }).order();
66967             enter.append('circle').attr('class', 'shadow');
66968             enter.append('circle').attr('class', 'stroke'); // Vertices with tags get a fill.
66969
66970             enter.filter(function (d) {
66971               return d.hasInterestingTags();
66972             }).append('circle').attr('class', 'fill'); // update
66973
66974             groups = groups.merge(enter).attr('transform', svgPointTransform(projection)).classed('sibling', function (d) {
66975               return d.id in sets.selected;
66976             }).classed('shared', function (d) {
66977               return graph.isShared(d);
66978             }).classed('endpoint', function (d) {
66979               return d.isEndpoint(graph);
66980             }).classed('added', function (d) {
66981               return !base.entities[d.id]; // if it doesn't exist in the base graph, it's new
66982             }).classed('moved', function (d) {
66983               return base.entities[d.id] && !fastDeepEqual(graph.entities[d.id].loc, base.entities[d.id].loc);
66984             }).classed('retagged', function (d) {
66985               return base.entities[d.id] && !fastDeepEqual(graph.entities[d.id].tags, base.entities[d.id].tags);
66986             }).call(updateAttributes); // Vertices with icons get a `use`.
66987
66988             var iconUse = groups.selectAll('.icon').data(function data(d) {
66989               return zoom >= 17 && getIcon(d) ? [d] : [];
66990             }, fastEntityKey); // exit
66991
66992             iconUse.exit().remove(); // enter
66993
66994             iconUse.enter().append('use').attr('class', 'icon').attr('width', '11px').attr('height', '11px').attr('transform', 'translate(-5.5, -5.5)').attr('xlink:href', function (d) {
66995               var picon = getIcon(d);
66996               var isMaki = /^maki-/.test(picon);
66997               return '#' + picon + (isMaki ? '-11' : '');
66998             }); // Vertices with directions get viewfields
66999
67000             var dgroups = groups.selectAll('.viewfieldgroup').data(function data(d) {
67001               return zoom >= 18 && getDirections(d) ? [d] : [];
67002             }, fastEntityKey); // exit
67003
67004             dgroups.exit().remove(); // enter/update
67005
67006             dgroups = dgroups.enter().insert('g', '.shadow').attr('class', 'viewfieldgroup').merge(dgroups);
67007             var viewfields = dgroups.selectAll('.viewfield').data(getDirections, function key(d) {
67008               return osmEntity.key(d);
67009             }); // exit
67010
67011             viewfields.exit().remove(); // enter/update
67012
67013             viewfields.enter().append('path').attr('class', 'viewfield').attr('d', 'M0,0H0').merge(viewfields).attr('marker-start', 'url(#ideditor-viewfield-marker' + (wireframe ? '-wireframe' : '') + ')').attr('transform', function (d) {
67014               return 'rotate(' + d + ')';
67015             });
67016           }
67017
67018           function drawTargets(selection, graph, entities, filter) {
67019             var targetClass = context.getDebug('target') ? 'pink ' : 'nocolor ';
67020             var nopeClass = context.getDebug('target') ? 'red ' : 'nocolor ';
67021             var getTransform = svgPointTransform(projection).geojson;
67022             var activeID = context.activeID();
67023             var data = {
67024               targets: [],
67025               nopes: []
67026             };
67027             entities.forEach(function (node) {
67028               if (activeID === node.id) return; // draw no target on the activeID
67029
67030               var vertexType = svgPassiveVertex(node, graph, activeID);
67031
67032               if (vertexType !== 0) {
67033                 // passive or adjacent - allow to connect
67034                 data.targets.push({
67035                   type: 'Feature',
67036                   id: node.id,
67037                   properties: {
67038                     target: true,
67039                     entity: node
67040                   },
67041                   geometry: node.asGeoJSON()
67042                 });
67043               } else {
67044                 data.nopes.push({
67045                   type: 'Feature',
67046                   id: node.id + '-nope',
67047                   properties: {
67048                     nope: true,
67049                     target: true,
67050                     entity: node
67051                   },
67052                   geometry: node.asGeoJSON()
67053                 });
67054               }
67055             }); // Targets allow hover and vertex snapping
67056
67057             var targets = selection.selectAll('.vertex.target-allowed').filter(function (d) {
67058               return filter(d.properties.entity);
67059             }).data(data.targets, function key(d) {
67060               return d.id;
67061             }); // exit
67062
67063             targets.exit().remove(); // enter/update
67064
67065             targets.enter().append('circle').attr('r', function (d) {
67066               return _radii[d.id] || radiuses.shadow[3];
67067             }).merge(targets).attr('class', function (d) {
67068               return 'node vertex target target-allowed ' + targetClass + d.id;
67069             }).attr('transform', getTransform); // NOPE
67070
67071             var nopes = selection.selectAll('.vertex.target-nope').filter(function (d) {
67072               return filter(d.properties.entity);
67073             }).data(data.nopes, function key(d) {
67074               return d.id;
67075             }); // exit
67076
67077             nopes.exit().remove(); // enter/update
67078
67079             nopes.enter().append('circle').attr('r', function (d) {
67080               return _radii[d.properties.entity.id] || radiuses.shadow[3];
67081             }).merge(nopes).attr('class', function (d) {
67082               return 'node vertex target target-nope ' + nopeClass + d.id;
67083             }).attr('transform', getTransform);
67084           } // Points can also render as vertices:
67085           // 1. in wireframe mode or
67086           // 2. at higher zooms if they have a direction
67087
67088
67089           function renderAsVertex(entity, graph, wireframe, zoom) {
67090             var geometry = entity.geometry(graph);
67091             return geometry === 'vertex' || geometry === 'point' && (wireframe || zoom >= 18 && entity.directions(graph, projection).length);
67092           }
67093
67094           function isEditedNode(node, base, head) {
67095             var baseNode = base.entities[node.id];
67096             var headNode = head.entities[node.id];
67097             return !headNode || !baseNode || !fastDeepEqual(headNode.tags, baseNode.tags) || !fastDeepEqual(headNode.loc, baseNode.loc);
67098           }
67099
67100           function getSiblingAndChildVertices(ids, graph, wireframe, zoom) {
67101             var results = {};
67102             var seenIds = {};
67103
67104             function addChildVertices(entity) {
67105               // avoid redundant work and infinite recursion of circular relations
67106               if (seenIds[entity.id]) return;
67107               seenIds[entity.id] = true;
67108               var geometry = entity.geometry(graph);
67109
67110               if (!context.features().isHiddenFeature(entity, graph, geometry)) {
67111                 var i;
67112
67113                 if (entity.type === 'way') {
67114                   for (i = 0; i < entity.nodes.length; i++) {
67115                     var child = graph.hasEntity(entity.nodes[i]);
67116
67117                     if (child) {
67118                       addChildVertices(child);
67119                     }
67120                   }
67121                 } else if (entity.type === 'relation') {
67122                   for (i = 0; i < entity.members.length; i++) {
67123                     var member = graph.hasEntity(entity.members[i].id);
67124
67125                     if (member) {
67126                       addChildVertices(member);
67127                     }
67128                   }
67129                 } else if (renderAsVertex(entity, graph, wireframe, zoom)) {
67130                   results[entity.id] = entity;
67131                 }
67132               }
67133             }
67134
67135             ids.forEach(function (id) {
67136               var entity = graph.hasEntity(id);
67137               if (!entity) return;
67138
67139               if (entity.type === 'node') {
67140                 if (renderAsVertex(entity, graph, wireframe, zoom)) {
67141                   results[entity.id] = entity;
67142                   graph.parentWays(entity).forEach(function (entity) {
67143                     addChildVertices(entity);
67144                   });
67145                 }
67146               } else {
67147                 // way, relation
67148                 addChildVertices(entity);
67149               }
67150             });
67151             return results;
67152           }
67153
67154           function drawVertices(selection, graph, entities, filter, extent, fullRedraw) {
67155             var wireframe = context.surface().classed('fill-wireframe');
67156             var visualDiff = context.surface().classed('highlight-edited');
67157             var zoom = geoScaleToZoom(projection.scale());
67158             var mode = context.mode();
67159             var isMoving = mode && /^(add|draw|drag|move|rotate)/.test(mode.id);
67160             var base = context.history().base();
67161             var drawLayer = selection.selectAll('.layer-osm.points .points-group.vertices');
67162             var touchLayer = selection.selectAll('.layer-touch.points');
67163
67164             if (fullRedraw) {
67165               _currPersistent = {};
67166               _radii = {};
67167             } // Collect important vertices from the `entities` list..
67168             // (during a partial redraw, it will not contain everything)
67169
67170
67171             for (var i = 0; i < entities.length; i++) {
67172               var entity = entities[i];
67173               var geometry = entity.geometry(graph);
67174               var keep = false; // a point that looks like a vertex..
67175
67176               if (geometry === 'point' && renderAsVertex(entity, graph, wireframe, zoom)) {
67177                 _currPersistent[entity.id] = entity;
67178                 keep = true; // a vertex of some importance..
67179               } else if (geometry === 'vertex' && (entity.hasInterestingTags() || entity.isEndpoint(graph) || entity.isConnected(graph) || visualDiff && isEditedNode(entity, base, graph))) {
67180                 _currPersistent[entity.id] = entity;
67181                 keep = true;
67182               } // whatever this is, it's not a persistent vertex..
67183
67184
67185               if (!keep && !fullRedraw) {
67186                 delete _currPersistent[entity.id];
67187               }
67188             } // 3 sets of vertices to consider:
67189
67190
67191             var sets = {
67192               persistent: _currPersistent,
67193               // persistent = important vertices (render always)
67194               selected: _currSelected,
67195               // selected + siblings of selected (render always)
67196               hovered: _currHover // hovered + siblings of hovered (render only in draw modes)
67197
67198             };
67199             var all = Object.assign({}, isMoving ? _currHover : {}, _currSelected, _currPersistent); // Draw the vertices..
67200             // The filter function controls the scope of what objects d3 will touch (exit/enter/update)
67201             // Adjust the filter function to expand the scope beyond whatever entities were passed in.
67202
67203             var filterRendered = function filterRendered(d) {
67204               return d.id in _currPersistent || d.id in _currSelected || d.id in _currHover || filter(d);
67205             };
67206
67207             drawLayer.call(draw, graph, currentVisible(all), sets, filterRendered); // Draw touch targets..
67208             // When drawing, render all targets (not just those affected by a partial redraw)
67209
67210             var filterTouch = function filterTouch(d) {
67211               return isMoving ? true : filterRendered(d);
67212             };
67213
67214             touchLayer.call(drawTargets, graph, currentVisible(all), filterTouch);
67215
67216             function currentVisible(which) {
67217               return Object.keys(which).map(graph.hasEntity, graph) // the current version of this entity
67218               .filter(function (entity) {
67219                 return entity && entity.intersects(extent, graph);
67220               });
67221             }
67222           } // partial redraw - only update the selected items..
67223
67224
67225           drawVertices.drawSelected = function (selection, graph, extent) {
67226             var wireframe = context.surface().classed('fill-wireframe');
67227             var zoom = geoScaleToZoom(projection.scale());
67228             _prevSelected = _currSelected || {};
67229
67230             if (context.map().isInWideSelection()) {
67231               _currSelected = {};
67232               context.selectedIDs().forEach(function (id) {
67233                 var entity = graph.hasEntity(id);
67234                 if (!entity) return;
67235
67236                 if (entity.type === 'node') {
67237                   if (renderAsVertex(entity, graph, wireframe, zoom)) {
67238                     _currSelected[entity.id] = entity;
67239                   }
67240                 }
67241               });
67242             } else {
67243               _currSelected = getSiblingAndChildVertices(context.selectedIDs(), graph, wireframe, zoom);
67244             } // note that drawVertices will add `_currSelected` automatically if needed..
67245
67246
67247             var filter = function filter(d) {
67248               return d.id in _prevSelected;
67249             };
67250
67251             drawVertices(selection, graph, Object.values(_prevSelected), filter, extent, false);
67252           }; // partial redraw - only update the hovered items..
67253
67254
67255           drawVertices.drawHover = function (selection, graph, target, extent) {
67256             if (target === _currHoverTarget) return; // continue only if something changed
67257
67258             var wireframe = context.surface().classed('fill-wireframe');
67259             var zoom = geoScaleToZoom(projection.scale());
67260             _prevHover = _currHover || {};
67261             _currHoverTarget = target;
67262             var entity = target && target.properties && target.properties.entity;
67263
67264             if (entity) {
67265               _currHover = getSiblingAndChildVertices([entity.id], graph, wireframe, zoom);
67266             } else {
67267               _currHover = {};
67268             } // note that drawVertices will add `_currHover` automatically if needed..
67269
67270
67271             var filter = function filter(d) {
67272               return d.id in _prevHover;
67273             };
67274
67275             drawVertices(selection, graph, Object.values(_prevHover), filter, extent, false);
67276           };
67277
67278           return drawVertices;
67279         }
67280
67281         function utilBindOnce(target, type, listener, capture) {
67282           var typeOnce = type + '.once';
67283
67284           function one() {
67285             target.on(typeOnce, null);
67286             listener.apply(this, arguments);
67287           }
67288
67289           target.on(typeOnce, one, capture);
67290           return this;
67291         }
67292
67293         function defaultFilter(d3_event) {
67294           return !d3_event.ctrlKey && !d3_event.button;
67295         }
67296
67297         function defaultExtent() {
67298           var e = this;
67299
67300           if (e instanceof SVGElement) {
67301             e = e.ownerSVGElement || e;
67302
67303             if (e.hasAttribute('viewBox')) {
67304               e = e.viewBox.baseVal;
67305               return [[e.x, e.y], [e.x + e.width, e.y + e.height]];
67306             }
67307
67308             return [[0, 0], [e.width.baseVal.value, e.height.baseVal.value]];
67309           }
67310
67311           return [[0, 0], [e.clientWidth, e.clientHeight]];
67312         }
67313
67314         function defaultWheelDelta(d3_event) {
67315           return -d3_event.deltaY * (d3_event.deltaMode === 1 ? 0.05 : d3_event.deltaMode ? 1 : 0.002);
67316         }
67317
67318         function defaultConstrain(transform, extent, translateExtent) {
67319           var dx0 = transform.invertX(extent[0][0]) - translateExtent[0][0],
67320               dx1 = transform.invertX(extent[1][0]) - translateExtent[1][0],
67321               dy0 = transform.invertY(extent[0][1]) - translateExtent[0][1],
67322               dy1 = transform.invertY(extent[1][1]) - translateExtent[1][1];
67323           return transform.translate(dx1 > dx0 ? (dx0 + dx1) / 2 : Math.min(0, dx0) || Math.max(0, dx1), dy1 > dy0 ? (dy0 + dy1) / 2 : Math.min(0, dy0) || Math.max(0, dy1));
67324         }
67325
67326         function utilZoomPan() {
67327           var filter = defaultFilter,
67328               extent = defaultExtent,
67329               constrain = defaultConstrain,
67330               wheelDelta = defaultWheelDelta,
67331               scaleExtent = [0, Infinity],
67332               translateExtent = [[-Infinity, -Infinity], [Infinity, Infinity]],
67333               interpolate = interpolateZoom,
67334               dispatch = dispatch$8('start', 'zoom', 'end'),
67335               _wheelDelay = 150,
67336               _transform = identity$2,
67337               _activeGesture;
67338
67339           function zoom(selection) {
67340             selection.on('pointerdown.zoom', pointerdown).on('wheel.zoom', wheeled).style('touch-action', 'none').style('-webkit-tap-highlight-color', 'rgba(0,0,0,0)');
67341             select(window).on('pointermove.zoompan', pointermove).on('pointerup.zoompan pointercancel.zoompan', pointerup);
67342           }
67343
67344           zoom.transform = function (collection, transform, point) {
67345             var selection = collection.selection ? collection.selection() : collection;
67346
67347             if (collection !== selection) {
67348               schedule(collection, transform, point);
67349             } else {
67350               selection.interrupt().each(function () {
67351                 gesture(this, arguments).start(null).zoom(null, null, typeof transform === 'function' ? transform.apply(this, arguments) : transform).end(null);
67352               });
67353             }
67354           };
67355
67356           zoom.scaleBy = function (selection, k, p) {
67357             zoom.scaleTo(selection, function () {
67358               var k0 = _transform.k,
67359                   k1 = typeof k === 'function' ? k.apply(this, arguments) : k;
67360               return k0 * k1;
67361             }, p);
67362           };
67363
67364           zoom.scaleTo = function (selection, k, p) {
67365             zoom.transform(selection, function () {
67366               var e = extent.apply(this, arguments),
67367                   t0 = _transform,
67368                   p0 = !p ? centroid(e) : typeof p === 'function' ? p.apply(this, arguments) : p,
67369                   p1 = t0.invert(p0),
67370                   k1 = typeof k === 'function' ? k.apply(this, arguments) : k;
67371               return constrain(translate(scale(t0, k1), p0, p1), e, translateExtent);
67372             }, p);
67373           };
67374
67375           zoom.translateBy = function (selection, x, y) {
67376             zoom.transform(selection, function () {
67377               return constrain(_transform.translate(typeof x === 'function' ? x.apply(this, arguments) : x, typeof y === 'function' ? y.apply(this, arguments) : y), extent.apply(this, arguments), translateExtent);
67378             });
67379           };
67380
67381           zoom.translateTo = function (selection, x, y, p) {
67382             zoom.transform(selection, function () {
67383               var e = extent.apply(this, arguments),
67384                   t = _transform,
67385                   p0 = !p ? centroid(e) : typeof p === 'function' ? p.apply(this, arguments) : p;
67386               return constrain(identity$2.translate(p0[0], p0[1]).scale(t.k).translate(typeof x === 'function' ? -x.apply(this, arguments) : -x, typeof y === 'function' ? -y.apply(this, arguments) : -y), e, translateExtent);
67387             }, p);
67388           };
67389
67390           function scale(transform, k) {
67391             k = Math.max(scaleExtent[0], Math.min(scaleExtent[1], k));
67392             return k === transform.k ? transform : new Transform(k, transform.x, transform.y);
67393           }
67394
67395           function translate(transform, p0, p1) {
67396             var x = p0[0] - p1[0] * transform.k,
67397                 y = p0[1] - p1[1] * transform.k;
67398             return x === transform.x && y === transform.y ? transform : new Transform(transform.k, x, y);
67399           }
67400
67401           function centroid(extent) {
67402             return [(+extent[0][0] + +extent[1][0]) / 2, (+extent[0][1] + +extent[1][1]) / 2];
67403           }
67404
67405           function schedule(transition, transform, point) {
67406             transition.on('start.zoom', function () {
67407               gesture(this, arguments).start(null);
67408             }).on('interrupt.zoom end.zoom', function () {
67409               gesture(this, arguments).end(null);
67410             }).tween('zoom', function () {
67411               var that = this,
67412                   args = arguments,
67413                   g = gesture(that, args),
67414                   e = extent.apply(that, args),
67415                   p = !point ? centroid(e) : typeof point === 'function' ? point.apply(that, args) : point,
67416                   w = Math.max(e[1][0] - e[0][0], e[1][1] - e[0][1]),
67417                   a = _transform,
67418                   b = typeof transform === 'function' ? transform.apply(that, args) : transform,
67419                   i = interpolate(a.invert(p).concat(w / a.k), b.invert(p).concat(w / b.k));
67420               return function (t) {
67421                 if (t === 1) {
67422                   // Avoid rounding error on end.
67423                   t = b;
67424                 } else {
67425                   var l = i(t);
67426                   var k = w / l[2];
67427                   t = new Transform(k, p[0] - l[0] * k, p[1] - l[1] * k);
67428                 }
67429
67430                 g.zoom(null, null, t);
67431               };
67432             });
67433           }
67434
67435           function gesture(that, args, clean) {
67436             return !clean && _activeGesture || new Gesture(that, args);
67437           }
67438
67439           function Gesture(that, args) {
67440             this.that = that;
67441             this.args = args;
67442             this.active = 0;
67443             this.extent = extent.apply(that, args);
67444           }
67445
67446           Gesture.prototype = {
67447             start: function start(d3_event) {
67448               if (++this.active === 1) {
67449                 _activeGesture = this;
67450                 dispatch.call('start', this, d3_event);
67451               }
67452
67453               return this;
67454             },
67455             zoom: function zoom(d3_event, key, transform) {
67456               if (this.mouse && key !== 'mouse') this.mouse[1] = transform.invert(this.mouse[0]);
67457               if (this.pointer0 && key !== 'touch') this.pointer0[1] = transform.invert(this.pointer0[0]);
67458               if (this.pointer1 && key !== 'touch') this.pointer1[1] = transform.invert(this.pointer1[0]);
67459               _transform = transform;
67460               dispatch.call('zoom', this, d3_event, key, transform);
67461               return this;
67462             },
67463             end: function end(d3_event) {
67464               if (--this.active === 0) {
67465                 _activeGesture = null;
67466                 dispatch.call('end', this, d3_event);
67467               }
67468
67469               return this;
67470             }
67471           };
67472
67473           function wheeled(d3_event) {
67474             if (!filter.apply(this, arguments)) return;
67475             var g = gesture(this, arguments),
67476                 t = _transform,
67477                 k = Math.max(scaleExtent[0], Math.min(scaleExtent[1], t.k * Math.pow(2, wheelDelta.apply(this, arguments)))),
67478                 p = utilFastMouse(this)(d3_event); // If the mouse is in the same location as before, reuse it.
67479             // If there were recent wheel events, reset the wheel idle timeout.
67480
67481             if (g.wheel) {
67482               if (g.mouse[0][0] !== p[0] || g.mouse[0][1] !== p[1]) {
67483                 g.mouse[1] = t.invert(g.mouse[0] = p);
67484               }
67485
67486               clearTimeout(g.wheel); // Otherwise, capture the mouse point and location at the start.
67487             } else {
67488               g.mouse = [p, t.invert(p)];
67489               interrupt(this);
67490               g.start(d3_event);
67491             }
67492
67493             d3_event.preventDefault();
67494             d3_event.stopImmediatePropagation();
67495             g.wheel = setTimeout(wheelidled, _wheelDelay);
67496             g.zoom(d3_event, 'mouse', constrain(translate(scale(t, k), g.mouse[0], g.mouse[1]), g.extent, translateExtent));
67497
67498             function wheelidled() {
67499               g.wheel = null;
67500               g.end(d3_event);
67501             }
67502           }
67503
67504           var _downPointerIDs = new Set();
67505
67506           var _pointerLocGetter;
67507
67508           function pointerdown(d3_event) {
67509             _downPointerIDs.add(d3_event.pointerId);
67510
67511             if (!filter.apply(this, arguments)) return;
67512             var g = gesture(this, arguments, _downPointerIDs.size === 1);
67513             var started;
67514             d3_event.stopImmediatePropagation();
67515             _pointerLocGetter = utilFastMouse(this);
67516
67517             var loc = _pointerLocGetter(d3_event);
67518
67519             var p = [loc, _transform.invert(loc), d3_event.pointerId];
67520
67521             if (!g.pointer0) {
67522               g.pointer0 = p;
67523               started = true;
67524             } else if (!g.pointer1 && g.pointer0[2] !== p[2]) {
67525               g.pointer1 = p;
67526             }
67527
67528             if (started) {
67529               interrupt(this);
67530               g.start(d3_event);
67531             }
67532           }
67533
67534           function pointermove(d3_event) {
67535             if (!_downPointerIDs.has(d3_event.pointerId)) return;
67536             if (!_activeGesture || !_pointerLocGetter) return;
67537             var g = gesture(this, arguments);
67538             var isPointer0 = g.pointer0 && g.pointer0[2] === d3_event.pointerId;
67539             var isPointer1 = !isPointer0 && g.pointer1 && g.pointer1[2] === d3_event.pointerId;
67540
67541             if ((isPointer0 || isPointer1) && 'buttons' in d3_event && !d3_event.buttons) {
67542               // The pointer went up without ending the gesture somehow, e.g.
67543               // a down mouse was moved off the map and released. End it here.
67544               if (g.pointer0) _downPointerIDs["delete"](g.pointer0[2]);
67545               if (g.pointer1) _downPointerIDs["delete"](g.pointer1[2]);
67546               g.end(d3_event);
67547               return;
67548             }
67549
67550             d3_event.preventDefault();
67551             d3_event.stopImmediatePropagation();
67552
67553             var loc = _pointerLocGetter(d3_event);
67554
67555             var t, p, l;
67556             if (isPointer0) g.pointer0[0] = loc;else if (isPointer1) g.pointer1[0] = loc;
67557             t = _transform;
67558
67559             if (g.pointer1) {
67560               var p0 = g.pointer0[0],
67561                   l0 = g.pointer0[1],
67562                   p1 = g.pointer1[0],
67563                   l1 = g.pointer1[1],
67564                   dp = (dp = p1[0] - p0[0]) * dp + (dp = p1[1] - p0[1]) * dp,
67565                   dl = (dl = l1[0] - l0[0]) * dl + (dl = l1[1] - l0[1]) * dl;
67566               t = scale(t, Math.sqrt(dp / dl));
67567               p = [(p0[0] + p1[0]) / 2, (p0[1] + p1[1]) / 2];
67568               l = [(l0[0] + l1[0]) / 2, (l0[1] + l1[1]) / 2];
67569             } else if (g.pointer0) {
67570               p = g.pointer0[0];
67571               l = g.pointer0[1];
67572             } else {
67573               return;
67574             }
67575
67576             g.zoom(d3_event, 'touch', constrain(translate(t, p, l), g.extent, translateExtent));
67577           }
67578
67579           function pointerup(d3_event) {
67580             if (!_downPointerIDs.has(d3_event.pointerId)) return;
67581
67582             _downPointerIDs["delete"](d3_event.pointerId);
67583
67584             if (!_activeGesture) return;
67585             var g = gesture(this, arguments);
67586             d3_event.stopImmediatePropagation();
67587             if (g.pointer0 && g.pointer0[2] === d3_event.pointerId) delete g.pointer0;else if (g.pointer1 && g.pointer1[2] === d3_event.pointerId) delete g.pointer1;
67588
67589             if (g.pointer1 && !g.pointer0) {
67590               g.pointer0 = g.pointer1;
67591               delete g.pointer1;
67592             }
67593
67594             if (g.pointer0) {
67595               g.pointer0[1] = _transform.invert(g.pointer0[0]);
67596             } else {
67597               g.end(d3_event);
67598             }
67599           }
67600
67601           zoom.wheelDelta = function (_) {
67602             return arguments.length ? (wheelDelta = utilFunctor(+_), zoom) : wheelDelta;
67603           };
67604
67605           zoom.filter = function (_) {
67606             return arguments.length ? (filter = utilFunctor(!!_), zoom) : filter;
67607           };
67608
67609           zoom.extent = function (_) {
67610             return arguments.length ? (extent = utilFunctor([[+_[0][0], +_[0][1]], [+_[1][0], +_[1][1]]]), zoom) : extent;
67611           };
67612
67613           zoom.scaleExtent = function (_) {
67614             return arguments.length ? (scaleExtent[0] = +_[0], scaleExtent[1] = +_[1], zoom) : [scaleExtent[0], scaleExtent[1]];
67615           };
67616
67617           zoom.translateExtent = function (_) {
67618             return arguments.length ? (translateExtent[0][0] = +_[0][0], translateExtent[1][0] = +_[1][0], translateExtent[0][1] = +_[0][1], translateExtent[1][1] = +_[1][1], zoom) : [[translateExtent[0][0], translateExtent[0][1]], [translateExtent[1][0], translateExtent[1][1]]];
67619           };
67620
67621           zoom.constrain = function (_) {
67622             return arguments.length ? (constrain = _, zoom) : constrain;
67623           };
67624
67625           zoom.interpolate = function (_) {
67626             return arguments.length ? (interpolate = _, zoom) : interpolate;
67627           };
67628
67629           zoom._transform = function (_) {
67630             return arguments.length ? (_transform = _, zoom) : _transform;
67631           };
67632
67633           return utilRebind(zoom, dispatch, 'on');
67634         }
67635
67636         // if pointer events are supported. Falls back to default `dblclick` event.
67637
67638         function utilDoubleUp() {
67639           var dispatch = dispatch$8('doubleUp');
67640           var _maxTimespan = 500; // milliseconds
67641
67642           var _maxDistance = 20; // web pixels; be somewhat generous to account for touch devices
67643
67644           var _pointer; // object representing the pointer that could trigger double up
67645
67646
67647           function pointerIsValidFor(loc) {
67648             // second pointerup must occur within a small timeframe after the first pointerdown
67649             return new Date().getTime() - _pointer.startTime <= _maxTimespan && // all pointer events must occur within a small distance of the first pointerdown
67650             geoVecLength(_pointer.startLoc, loc) <= _maxDistance;
67651           }
67652
67653           function pointerdown(d3_event) {
67654             // ignore right-click
67655             if (d3_event.ctrlKey || d3_event.button === 2) return;
67656             var loc = [d3_event.clientX, d3_event.clientY]; // Don't rely on pointerId here since it can change between pointerdown
67657             // events on touch devices
67658
67659             if (_pointer && !pointerIsValidFor(loc)) {
67660               // if this pointer is no longer valid, clear it so another can be started
67661               _pointer = undefined;
67662             }
67663
67664             if (!_pointer) {
67665               _pointer = {
67666                 startLoc: loc,
67667                 startTime: new Date().getTime(),
67668                 upCount: 0,
67669                 pointerId: d3_event.pointerId
67670               };
67671             } else {
67672               // double down
67673               _pointer.pointerId = d3_event.pointerId;
67674             }
67675           }
67676
67677           function pointerup(d3_event) {
67678             // ignore right-click
67679             if (d3_event.ctrlKey || d3_event.button === 2) return;
67680             if (!_pointer || _pointer.pointerId !== d3_event.pointerId) return;
67681             _pointer.upCount += 1;
67682
67683             if (_pointer.upCount === 2) {
67684               // double up!
67685               var loc = [d3_event.clientX, d3_event.clientY];
67686
67687               if (pointerIsValidFor(loc)) {
67688                 var locInThis = utilFastMouse(this)(d3_event);
67689                 dispatch.call('doubleUp', this, d3_event, locInThis);
67690               } // clear the pointer info in any case
67691
67692
67693               _pointer = undefined;
67694             }
67695           }
67696
67697           function doubleUp(selection) {
67698             if ('PointerEvent' in window) {
67699               // dblclick isn't well supported on touch devices so manually use
67700               // pointer events if they're available
67701               selection.on('pointerdown.doubleUp', pointerdown).on('pointerup.doubleUp', pointerup);
67702             } else {
67703               // fallback to dblclick
67704               selection.on('dblclick.doubleUp', function (d3_event) {
67705                 dispatch.call('doubleUp', this, d3_event, utilFastMouse(this)(d3_event));
67706               });
67707             }
67708           }
67709
67710           doubleUp.off = function (selection) {
67711             selection.on('pointerdown.doubleUp', null).on('pointerup.doubleUp', null).on('dblclick.doubleUp', null);
67712           };
67713
67714           return utilRebind(doubleUp, dispatch, 'on');
67715         }
67716
67717         var TILESIZE = 256;
67718         var minZoom = 2;
67719         var maxZoom = 24;
67720         var kMin = geoZoomToScale(minZoom, TILESIZE);
67721         var kMax = geoZoomToScale(maxZoom, TILESIZE);
67722
67723         function clamp$1(num, min, max) {
67724           return Math.max(min, Math.min(num, max));
67725         }
67726
67727         function rendererMap(context) {
67728           var dispatch = dispatch$8('move', 'drawn', 'crossEditableZoom', 'hitMinZoom', 'changeHighlighting', 'changeAreaFill');
67729           var projection = context.projection;
67730           var curtainProjection = context.curtainProjection;
67731           var drawLayers;
67732           var drawPoints;
67733           var drawVertices;
67734           var drawLines;
67735           var drawAreas;
67736           var drawMidpoints;
67737           var drawLabels;
67738
67739           var _selection = select(null);
67740
67741           var supersurface = select(null);
67742           var wrapper = select(null);
67743           var surface = select(null);
67744           var _dimensions = [1, 1];
67745           var _dblClickZoomEnabled = true;
67746           var _redrawEnabled = true;
67747
67748           var _gestureTransformStart;
67749
67750           var _transformStart = projection.transform();
67751
67752           var _transformLast;
67753
67754           var _isTransformed = false;
67755           var _minzoom = 0;
67756
67757           var _getMouseCoords;
67758
67759           var _lastPointerEvent;
67760
67761           var _lastWithinEditableZoom; // whether a pointerdown event started the zoom
67762
67763
67764           var _pointerDown = false; // use pointer events on supported platforms; fallback to mouse events
67765
67766           var _pointerPrefix = 'PointerEvent' in window ? 'pointer' : 'mouse'; // use pointer event interaction if supported; fallback to touch/mouse events in d3-zoom
67767
67768
67769           var _zoomerPannerFunction = 'PointerEvent' in window ? utilZoomPan : d3_zoom;
67770
67771           var _zoomerPanner = _zoomerPannerFunction().scaleExtent([kMin, kMax]).interpolate(interpolate$1).filter(zoomEventFilter).on('zoom.map', zoomPan).on('start.map', function (d3_event) {
67772             _pointerDown = d3_event && (d3_event.type === 'pointerdown' || d3_event.sourceEvent && d3_event.sourceEvent.type === 'pointerdown');
67773           }).on('end.map', function () {
67774             _pointerDown = false;
67775           });
67776
67777           var _doubleUpHandler = utilDoubleUp();
67778
67779           var scheduleRedraw = throttle(redraw, 750); // var isRedrawScheduled = false;
67780           // var pendingRedrawCall;
67781           // function scheduleRedraw() {
67782           //     // Only schedule the redraw if one has not already been set.
67783           //     if (isRedrawScheduled) return;
67784           //     isRedrawScheduled = true;
67785           //     var that = this;
67786           //     var args = arguments;
67787           //     pendingRedrawCall = window.requestIdleCallback(function () {
67788           //         // Reset the boolean so future redraws can be set.
67789           //         isRedrawScheduled = false;
67790           //         redraw.apply(that, args);
67791           //     }, { timeout: 1400 });
67792           // }
67793
67794
67795           function cancelPendingRedraw() {
67796             scheduleRedraw.cancel(); // isRedrawScheduled = false;
67797             // window.cancelIdleCallback(pendingRedrawCall);
67798           }
67799
67800           function map(selection) {
67801             _selection = selection;
67802             context.on('change.map', immediateRedraw);
67803             var osm = context.connection();
67804
67805             if (osm) {
67806               osm.on('change.map', immediateRedraw);
67807             }
67808
67809             function didUndoOrRedo(targetTransform) {
67810               var mode = context.mode().id;
67811               if (mode !== 'browse' && mode !== 'select') return;
67812
67813               if (targetTransform) {
67814                 map.transformEase(targetTransform);
67815               }
67816             }
67817
67818             context.history().on('merge.map', function () {
67819               scheduleRedraw();
67820             }).on('change.map', immediateRedraw).on('undone.map', function (stack, fromStack) {
67821               didUndoOrRedo(fromStack.transform);
67822             }).on('redone.map', function (stack) {
67823               didUndoOrRedo(stack.transform);
67824             });
67825             context.background().on('change.map', immediateRedraw);
67826             context.features().on('redraw.map', immediateRedraw);
67827             drawLayers.on('change.map', function () {
67828               context.background().updateImagery();
67829               immediateRedraw();
67830             });
67831             selection.on('wheel.map mousewheel.map', function (d3_event) {
67832               // disable swipe-to-navigate browser pages on trackpad/magic mouse – #5552
67833               d3_event.preventDefault();
67834             }).call(_zoomerPanner).call(_zoomerPanner.transform, projection.transform()).on('dblclick.zoom', null); // override d3-zoom dblclick handling
67835
67836             map.supersurface = supersurface = selection.append('div').attr('class', 'supersurface').call(utilSetTransform, 0, 0); // Need a wrapper div because Opera can't cope with an absolutely positioned
67837             // SVG element: http://bl.ocks.org/jfirebaugh/6fbfbd922552bf776c16
67838
67839             wrapper = supersurface.append('div').attr('class', 'layer layer-data');
67840             map.surface = surface = wrapper.call(drawLayers).selectAll('.surface');
67841             surface.call(drawLabels.observe).call(_doubleUpHandler).on(_pointerPrefix + 'down.zoom', function (d3_event) {
67842               _lastPointerEvent = d3_event;
67843
67844               if (d3_event.button === 2) {
67845                 d3_event.stopPropagation();
67846               }
67847             }, true).on(_pointerPrefix + 'up.zoom', function (d3_event) {
67848               _lastPointerEvent = d3_event;
67849
67850               if (resetTransform()) {
67851                 immediateRedraw();
67852               }
67853             }).on(_pointerPrefix + 'move.map', function (d3_event) {
67854               _lastPointerEvent = d3_event;
67855             }).on(_pointerPrefix + 'over.vertices', function (d3_event) {
67856               if (map.editableDataEnabled() && !_isTransformed) {
67857                 var hover = d3_event.target.__data__;
67858                 surface.call(drawVertices.drawHover, context.graph(), hover, map.extent());
67859                 dispatch.call('drawn', this, {
67860                   full: false
67861                 });
67862               }
67863             }).on(_pointerPrefix + 'out.vertices', function (d3_event) {
67864               if (map.editableDataEnabled() && !_isTransformed) {
67865                 var hover = d3_event.relatedTarget && d3_event.relatedTarget.__data__;
67866                 surface.call(drawVertices.drawHover, context.graph(), hover, map.extent());
67867                 dispatch.call('drawn', this, {
67868                   full: false
67869                 });
67870               }
67871             });
67872             var detected = utilDetect(); // only WebKit supports gesture events
67873
67874             if ('GestureEvent' in window && // Listening for gesture events on iOS 13.4+ breaks double-tapping,
67875             // but we only need to do this on desktop Safari anyway. – #7694
67876             !detected.isMobileWebKit) {
67877               // Desktop Safari sends gesture events for multitouch trackpad pinches.
67878               // We can listen for these and translate them into map zooms.
67879               surface.on('gesturestart.surface', function (d3_event) {
67880                 d3_event.preventDefault();
67881                 _gestureTransformStart = projection.transform();
67882               }).on('gesturechange.surface', gestureChange);
67883             } // must call after surface init
67884
67885
67886             updateAreaFill();
67887
67888             _doubleUpHandler.on('doubleUp.map', function (d3_event, p0) {
67889               if (!_dblClickZoomEnabled) return; // don't zoom if targeting something other than the map itself
67890
67891               if (_typeof(d3_event.target.__data__) === 'object' && // or area fills
67892               !select(d3_event.target).classed('fill')) return;
67893               var zoomOut = d3_event.shiftKey;
67894               var t = projection.transform();
67895               var p1 = t.invert(p0);
67896               t = t.scale(zoomOut ? 0.5 : 2);
67897               t.x = p0[0] - p1[0] * t.k;
67898               t.y = p0[1] - p1[1] * t.k;
67899               map.transformEase(t);
67900             });
67901
67902             context.on('enter.map', function () {
67903               if (!map.editableDataEnabled(true
67904               /* skip zoom check */
67905               )) return;
67906               if (_isTransformed) return; // redraw immediately any objects affected by a change in selectedIDs.
67907
67908               var graph = context.graph();
67909               var selectedAndParents = {};
67910               context.selectedIDs().forEach(function (id) {
67911                 var entity = graph.hasEntity(id);
67912
67913                 if (entity) {
67914                   selectedAndParents[entity.id] = entity;
67915
67916                   if (entity.type === 'node') {
67917                     graph.parentWays(entity).forEach(function (parent) {
67918                       selectedAndParents[parent.id] = parent;
67919                     });
67920                   }
67921                 }
67922               });
67923               var data = Object.values(selectedAndParents);
67924
67925               var filter = function filter(d) {
67926                 return d.id in selectedAndParents;
67927               };
67928
67929               data = context.features().filter(data, graph);
67930               surface.call(drawVertices.drawSelected, graph, map.extent()).call(drawLines, graph, data, filter).call(drawAreas, graph, data, filter).call(drawMidpoints, graph, data, filter, map.trimmedExtent());
67931               dispatch.call('drawn', this, {
67932                 full: false
67933               }); // redraw everything else later
67934
67935               scheduleRedraw();
67936             });
67937             map.dimensions(utilGetDimensions(selection));
67938           }
67939
67940           function zoomEventFilter(d3_event) {
67941             // Fix for #2151, (see also d3/d3-zoom#60, d3/d3-brush#18)
67942             // Intercept `mousedown` and check if there is an orphaned zoom gesture.
67943             // This can happen if a previous `mousedown` occurred without a `mouseup`.
67944             // If we detect this, dispatch `mouseup` to complete the orphaned gesture,
67945             // so that d3-zoom won't stop propagation of new `mousedown` events.
67946             if (d3_event.type === 'mousedown') {
67947               var hasOrphan = false;
67948               var listeners = window.__on;
67949
67950               for (var i = 0; i < listeners.length; i++) {
67951                 var listener = listeners[i];
67952
67953                 if (listener.name === 'zoom' && listener.type === 'mouseup') {
67954                   hasOrphan = true;
67955                   break;
67956                 }
67957               }
67958
67959               if (hasOrphan) {
67960                 var event = window.CustomEvent;
67961
67962                 if (event) {
67963                   event = new event('mouseup');
67964                 } else {
67965                   event = window.document.createEvent('Event');
67966                   event.initEvent('mouseup', false, false);
67967                 } // Event needs to be dispatched with an event.view property.
67968
67969
67970                 event.view = window;
67971                 window.dispatchEvent(event);
67972               }
67973             }
67974
67975             return d3_event.button !== 2; // ignore right clicks
67976           }
67977
67978           function pxCenter() {
67979             return [_dimensions[0] / 2, _dimensions[1] / 2];
67980           }
67981
67982           function drawEditable(difference, extent) {
67983             var mode = context.mode();
67984             var graph = context.graph();
67985             var features = context.features();
67986             var all = context.history().intersects(map.extent());
67987             var fullRedraw = false;
67988             var data;
67989             var set;
67990             var filter;
67991             var applyFeatureLayerFilters = true;
67992
67993             if (map.isInWideSelection()) {
67994               data = [];
67995               utilEntityAndDeepMemberIDs(mode.selectedIDs(), context.graph()).forEach(function (id) {
67996                 var entity = context.hasEntity(id);
67997                 if (entity) data.push(entity);
67998               });
67999               fullRedraw = true;
68000               filter = utilFunctor(true); // selected features should always be visible, so we can skip filtering
68001
68002               applyFeatureLayerFilters = false;
68003             } else if (difference) {
68004               var complete = difference.complete(map.extent());
68005               data = Object.values(complete).filter(Boolean);
68006               set = new Set(Object.keys(complete));
68007
68008               filter = function filter(d) {
68009                 return set.has(d.id);
68010               };
68011
68012               features.clear(data);
68013             } else {
68014               // force a full redraw if gatherStats detects that a feature
68015               // should be auto-hidden (e.g. points or buildings)..
68016               if (features.gatherStats(all, graph, _dimensions)) {
68017                 extent = undefined;
68018               }
68019
68020               if (extent) {
68021                 data = context.history().intersects(map.extent().intersection(extent));
68022                 set = new Set(data.map(function (entity) {
68023                   return entity.id;
68024                 }));
68025
68026                 filter = function filter(d) {
68027                   return set.has(d.id);
68028                 };
68029               } else {
68030                 data = all;
68031                 fullRedraw = true;
68032                 filter = utilFunctor(true);
68033               }
68034             }
68035
68036             if (applyFeatureLayerFilters) {
68037               data = features.filter(data, graph);
68038             } else {
68039               context.features().resetStats();
68040             }
68041
68042             if (mode && mode.id === 'select') {
68043               // update selected vertices - the user might have just double-clicked a way,
68044               // creating a new vertex, triggering a partial redraw without a mode change
68045               surface.call(drawVertices.drawSelected, graph, map.extent());
68046             }
68047
68048             surface.call(drawVertices, graph, data, filter, map.extent(), fullRedraw).call(drawLines, graph, data, filter).call(drawAreas, graph, data, filter).call(drawMidpoints, graph, data, filter, map.trimmedExtent()).call(drawLabels, graph, data, filter, _dimensions, fullRedraw).call(drawPoints, graph, data, filter);
68049             dispatch.call('drawn', this, {
68050               full: true
68051             });
68052           }
68053
68054           map.init = function () {
68055             drawLayers = svgLayers(projection, context);
68056             drawPoints = svgPoints(projection, context);
68057             drawVertices = svgVertices(projection, context);
68058             drawLines = svgLines(projection, context);
68059             drawAreas = svgAreas(projection, context);
68060             drawMidpoints = svgMidpoints(projection, context);
68061             drawLabels = svgLabels(projection, context);
68062           };
68063
68064           function editOff() {
68065             context.features().resetStats();
68066             surface.selectAll('.layer-osm *').remove();
68067             surface.selectAll('.layer-touch:not(.markers) *').remove();
68068             var allowed = {
68069               'browse': true,
68070               'save': true,
68071               'select-note': true,
68072               'select-data': true,
68073               'select-error': true
68074             };
68075             var mode = context.mode();
68076
68077             if (mode && !allowed[mode.id]) {
68078               context.enter(modeBrowse(context));
68079             }
68080
68081             dispatch.call('drawn', this, {
68082               full: true
68083             });
68084           }
68085
68086           function gestureChange(d3_event) {
68087             // Remap Safari gesture events to wheel events - #5492
68088             // We want these disabled most places, but enabled for zoom/unzoom on map surface
68089             // https://developer.mozilla.org/en-US/docs/Web/API/GestureEvent
68090             var e = d3_event;
68091             e.preventDefault();
68092             var props = {
68093               deltaMode: 0,
68094               // dummy values to ignore in zoomPan
68095               deltaY: 1,
68096               // dummy values to ignore in zoomPan
68097               clientX: e.clientX,
68098               clientY: e.clientY,
68099               screenX: e.screenX,
68100               screenY: e.screenY,
68101               x: e.x,
68102               y: e.y
68103             };
68104             var e2 = new WheelEvent('wheel', props);
68105             e2._scale = e.scale; // preserve the original scale
68106
68107             e2._rotation = e.rotation; // preserve the original rotation
68108
68109             _selection.node().dispatchEvent(e2);
68110           }
68111
68112           function zoomPan(event, key, transform) {
68113             var source = event && event.sourceEvent || event;
68114             var eventTransform = transform || event && event.transform;
68115             var x = eventTransform.x;
68116             var y = eventTransform.y;
68117             var k = eventTransform.k; // Special handling of 'wheel' events:
68118             // They might be triggered by the user scrolling the mouse wheel,
68119             // or 2-finger pinch/zoom gestures, the transform may need adjustment.
68120
68121             if (source && source.type === 'wheel') {
68122               // assume that the gesture is already handled by pointer events
68123               if (_pointerDown) return;
68124               var detected = utilDetect();
68125               var dX = source.deltaX;
68126               var dY = source.deltaY;
68127               var x2 = x;
68128               var y2 = y;
68129               var k2 = k;
68130               var t0, p0, p1; // Normalize mousewheel scroll speed (Firefox) - #3029
68131               // If wheel delta is provided in LINE units, recalculate it in PIXEL units
68132               // We are essentially redoing the calculations that occur here:
68133               //   https://github.com/d3/d3-zoom/blob/78563a8348aa4133b07cac92e2595c2227ca7cd7/src/zoom.js#L203
68134               // See this for more info:
68135               //   https://github.com/basilfx/normalize-wheel/blob/master/src/normalizeWheel.js
68136
68137               if (source.deltaMode === 1
68138               /* LINE */
68139               ) {
68140                   // Convert from lines to pixels, more if the user is scrolling fast.
68141                   // (I made up the exp function to roughly match Firefox to what Chrome does)
68142                   // These numbers should be floats, because integers are treated as pan gesture below.
68143                   var lines = Math.abs(source.deltaY);
68144                   var sign = source.deltaY > 0 ? 1 : -1;
68145                   dY = sign * clamp$1(Math.exp((lines - 1) * 0.75) * 4.000244140625, 4.000244140625, // min
68146                   350.000244140625 // max
68147                   ); // On Firefox Windows and Linux we always get +/- the scroll line amount (default 3)
68148                   // There doesn't seem to be any scroll acceleration.
68149                   // This multiplier increases the speed a little bit - #5512
68150
68151                   if (detected.os !== 'mac') {
68152                     dY *= 5;
68153                   } // recalculate x2,y2,k2
68154
68155
68156                   t0 = _isTransformed ? _transformLast : _transformStart;
68157                   p0 = _getMouseCoords(source);
68158                   p1 = t0.invert(p0);
68159                   k2 = t0.k * Math.pow(2, -dY / 500);
68160                   k2 = clamp$1(k2, kMin, kMax);
68161                   x2 = p0[0] - p1[0] * k2;
68162                   y2 = p0[1] - p1[1] * k2; // 2 finger map pinch zooming (Safari) - #5492
68163                   // These are fake `wheel` events we made from Safari `gesturechange` events..
68164                 } else if (source._scale) {
68165                 // recalculate x2,y2,k2
68166                 t0 = _gestureTransformStart;
68167                 p0 = _getMouseCoords(source);
68168                 p1 = t0.invert(p0);
68169                 k2 = t0.k * source._scale;
68170                 k2 = clamp$1(k2, kMin, kMax);
68171                 x2 = p0[0] - p1[0] * k2;
68172                 y2 = p0[1] - p1[1] * k2; // 2 finger map pinch zooming (all browsers except Safari) - #5492
68173                 // Pinch zooming via the `wheel` event will always have:
68174                 // - `ctrlKey = true`
68175                 // - `deltaY` is not round integer pixels (ignore `deltaX`)
68176               } else if (source.ctrlKey && !isInteger(dY)) {
68177                 dY *= 6; // slightly scale up whatever the browser gave us
68178                 // recalculate x2,y2,k2
68179
68180                 t0 = _isTransformed ? _transformLast : _transformStart;
68181                 p0 = _getMouseCoords(source);
68182                 p1 = t0.invert(p0);
68183                 k2 = t0.k * Math.pow(2, -dY / 500);
68184                 k2 = clamp$1(k2, kMin, kMax);
68185                 x2 = p0[0] - p1[0] * k2;
68186                 y2 = p0[1] - p1[1] * k2; // Trackpad scroll zooming with shift or alt/option key down
68187               } else if ((source.altKey || source.shiftKey) && isInteger(dY)) {
68188                 // recalculate x2,y2,k2
68189                 t0 = _isTransformed ? _transformLast : _transformStart;
68190                 p0 = _getMouseCoords(source);
68191                 p1 = t0.invert(p0);
68192                 k2 = t0.k * Math.pow(2, -dY / 500);
68193                 k2 = clamp$1(k2, kMin, kMax);
68194                 x2 = p0[0] - p1[0] * k2;
68195                 y2 = p0[1] - p1[1] * k2; // 2 finger map panning (Mac only, all browsers except Firefox #8595) - #5492, #5512
68196                 // Panning via the `wheel` event will always have:
68197                 // - `ctrlKey = false`
68198                 // - `deltaX`,`deltaY` are round integer pixels
68199               } else if (detected.os === 'mac' && detected.browser !== 'Firefox' && !source.ctrlKey && isInteger(dX) && isInteger(dY)) {
68200                 p1 = projection.translate();
68201                 x2 = p1[0] - dX;
68202                 y2 = p1[1] - dY;
68203                 k2 = projection.scale();
68204                 k2 = clamp$1(k2, kMin, kMax);
68205               } // something changed - replace the event transform
68206
68207
68208               if (x2 !== x || y2 !== y || k2 !== k) {
68209                 x = x2;
68210                 y = y2;
68211                 k = k2;
68212                 eventTransform = identity$2.translate(x2, y2).scale(k2);
68213
68214                 if (_zoomerPanner._transform) {
68215                   // utilZoomPan interface
68216                   _zoomerPanner._transform(eventTransform);
68217                 } else {
68218                   // d3_zoom interface
68219                   _selection.node().__zoom = eventTransform;
68220                 }
68221               }
68222             }
68223
68224             if (_transformStart.x === x && _transformStart.y === y && _transformStart.k === k) {
68225               return; // no change
68226             }
68227
68228             if (geoScaleToZoom(k, TILESIZE) < _minzoom) {
68229               surface.interrupt();
68230               dispatch.call('hitMinZoom', this, map);
68231               setCenterZoom(map.center(), context.minEditableZoom(), 0, true);
68232               scheduleRedraw();
68233               dispatch.call('move', this, map);
68234               return;
68235             }
68236
68237             projection.transform(eventTransform);
68238             var withinEditableZoom = map.withinEditableZoom();
68239
68240             if (_lastWithinEditableZoom !== withinEditableZoom) {
68241               if (_lastWithinEditableZoom !== undefined) {
68242                 // notify that the map zoomed in or out over the editable zoom threshold
68243                 dispatch.call('crossEditableZoom', this, withinEditableZoom);
68244               }
68245
68246               _lastWithinEditableZoom = withinEditableZoom;
68247             }
68248
68249             var scale = k / _transformStart.k;
68250             var tX = (x / scale - _transformStart.x) * scale;
68251             var tY = (y / scale - _transformStart.y) * scale;
68252
68253             if (context.inIntro()) {
68254               curtainProjection.transform({
68255                 x: x - tX,
68256                 y: y - tY,
68257                 k: k
68258               });
68259             }
68260
68261             if (source) {
68262               _lastPointerEvent = event;
68263             }
68264
68265             _isTransformed = true;
68266             _transformLast = eventTransform;
68267             utilSetTransform(supersurface, tX, tY, scale);
68268             scheduleRedraw();
68269             dispatch.call('move', this, map);
68270
68271             function isInteger(val) {
68272               return typeof val === 'number' && isFinite(val) && Math.floor(val) === val;
68273             }
68274           }
68275
68276           function resetTransform() {
68277             if (!_isTransformed) return false;
68278             utilSetTransform(supersurface, 0, 0);
68279             _isTransformed = false;
68280
68281             if (context.inIntro()) {
68282               curtainProjection.transform(projection.transform());
68283             }
68284
68285             return true;
68286           }
68287
68288           function redraw(difference, extent) {
68289             if (surface.empty() || !_redrawEnabled) return; // If we are in the middle of a zoom/pan, we can't do differenced redraws.
68290             // It would result in artifacts where differenced entities are redrawn with
68291             // one transform and unchanged entities with another.
68292
68293             if (resetTransform()) {
68294               difference = extent = undefined;
68295             }
68296
68297             var zoom = map.zoom();
68298             var z = String(~~zoom);
68299
68300             if (surface.attr('data-zoom') !== z) {
68301               surface.attr('data-zoom', z);
68302             } // class surface as `lowzoom` around z17-z18.5 (based on latitude)
68303
68304
68305             var lat = map.center()[1];
68306             var lowzoom = linear().domain([-60, 0, 60]).range([17, 18.5, 17]).clamp(true);
68307             surface.classed('low-zoom', zoom <= lowzoom(lat));
68308
68309             if (!difference) {
68310               supersurface.call(context.background());
68311               wrapper.call(drawLayers);
68312             } // OSM
68313
68314
68315             if (map.editableDataEnabled() || map.isInWideSelection()) {
68316               context.loadTiles(projection);
68317               drawEditable(difference, extent);
68318             } else {
68319               editOff();
68320             }
68321
68322             _transformStart = projection.transform();
68323             return map;
68324           }
68325
68326           var immediateRedraw = function immediateRedraw(difference, extent) {
68327             if (!difference && !extent) cancelPendingRedraw();
68328             redraw(difference, extent);
68329           };
68330
68331           map.lastPointerEvent = function () {
68332             return _lastPointerEvent;
68333           };
68334
68335           map.mouse = function (d3_event) {
68336             var event = d3_event || _lastPointerEvent;
68337
68338             if (event) {
68339               var s;
68340
68341               while (s = event.sourceEvent) {
68342                 event = s;
68343               }
68344
68345               return _getMouseCoords(event);
68346             }
68347
68348             return null;
68349           }; // returns Lng/Lat
68350
68351
68352           map.mouseCoordinates = function () {
68353             var coord = map.mouse() || pxCenter();
68354             return projection.invert(coord);
68355           };
68356
68357           map.dblclickZoomEnable = function (val) {
68358             if (!arguments.length) return _dblClickZoomEnabled;
68359             _dblClickZoomEnabled = val;
68360             return map;
68361           };
68362
68363           map.redrawEnable = function (val) {
68364             if (!arguments.length) return _redrawEnabled;
68365             _redrawEnabled = val;
68366             return map;
68367           };
68368
68369           map.isTransformed = function () {
68370             return _isTransformed;
68371           };
68372
68373           function setTransform(t2, duration, force) {
68374             var t = projection.transform();
68375             if (!force && t2.k === t.k && t2.x === t.x && t2.y === t.y) return false;
68376
68377             if (duration) {
68378               _selection.transition().duration(duration).on('start', function () {
68379                 map.startEase();
68380               }).call(_zoomerPanner.transform, identity$2.translate(t2.x, t2.y).scale(t2.k));
68381             } else {
68382               projection.transform(t2);
68383               _transformStart = t2;
68384
68385               _selection.call(_zoomerPanner.transform, _transformStart);
68386             }
68387
68388             return true;
68389           }
68390
68391           function setCenterZoom(loc2, z2, duration, force) {
68392             var c = map.center();
68393             var z = map.zoom();
68394             if (loc2[0] === c[0] && loc2[1] === c[1] && z2 === z && !force) return false;
68395             var proj = geoRawMercator().transform(projection.transform()); // copy projection
68396
68397             var k2 = clamp$1(geoZoomToScale(z2, TILESIZE), kMin, kMax);
68398             proj.scale(k2);
68399             var t = proj.translate();
68400             var point = proj(loc2);
68401             var center = pxCenter();
68402             t[0] += center[0] - point[0];
68403             t[1] += center[1] - point[1];
68404             return setTransform(identity$2.translate(t[0], t[1]).scale(k2), duration, force);
68405           }
68406
68407           map.pan = function (delta, duration) {
68408             var t = projection.translate();
68409             var k = projection.scale();
68410             t[0] += delta[0];
68411             t[1] += delta[1];
68412
68413             if (duration) {
68414               _selection.transition().duration(duration).on('start', function () {
68415                 map.startEase();
68416               }).call(_zoomerPanner.transform, identity$2.translate(t[0], t[1]).scale(k));
68417             } else {
68418               projection.translate(t);
68419               _transformStart = projection.transform();
68420
68421               _selection.call(_zoomerPanner.transform, _transformStart);
68422
68423               dispatch.call('move', this, map);
68424               immediateRedraw();
68425             }
68426
68427             return map;
68428           };
68429
68430           map.dimensions = function (val) {
68431             if (!arguments.length) return _dimensions;
68432             _dimensions = val;
68433             drawLayers.dimensions(_dimensions);
68434             context.background().dimensions(_dimensions);
68435             projection.clipExtent([[0, 0], _dimensions]);
68436             _getMouseCoords = utilFastMouse(supersurface.node());
68437             scheduleRedraw();
68438             return map;
68439           };
68440
68441           function zoomIn(delta) {
68442             setCenterZoom(map.center(), ~~map.zoom() + delta, 250, true);
68443           }
68444
68445           function zoomOut(delta) {
68446             setCenterZoom(map.center(), ~~map.zoom() - delta, 250, true);
68447           }
68448
68449           map.zoomIn = function () {
68450             zoomIn(1);
68451           };
68452
68453           map.zoomInFurther = function () {
68454             zoomIn(4);
68455           };
68456
68457           map.canZoomIn = function () {
68458             return map.zoom() < maxZoom;
68459           };
68460
68461           map.zoomOut = function () {
68462             zoomOut(1);
68463           };
68464
68465           map.zoomOutFurther = function () {
68466             zoomOut(4);
68467           };
68468
68469           map.canZoomOut = function () {
68470             return map.zoom() > minZoom;
68471           };
68472
68473           map.center = function (loc2) {
68474             if (!arguments.length) {
68475               return projection.invert(pxCenter());
68476             }
68477
68478             if (setCenterZoom(loc2, map.zoom())) {
68479               dispatch.call('move', this, map);
68480             }
68481
68482             scheduleRedraw();
68483             return map;
68484           };
68485
68486           map.unobscuredCenterZoomEase = function (loc, zoom) {
68487             var offset = map.unobscuredOffsetPx();
68488             var proj = geoRawMercator().transform(projection.transform()); // copy projection
68489             // use the target zoom to calculate the offset center
68490
68491             proj.scale(geoZoomToScale(zoom, TILESIZE));
68492             var locPx = proj(loc);
68493             var offsetLocPx = [locPx[0] + offset[0], locPx[1] + offset[1]];
68494             var offsetLoc = proj.invert(offsetLocPx);
68495             map.centerZoomEase(offsetLoc, zoom);
68496           };
68497
68498           map.unobscuredOffsetPx = function () {
68499             var openPane = context.container().select('.map-panes .map-pane.shown');
68500
68501             if (!openPane.empty()) {
68502               return [openPane.node().offsetWidth / 2, 0];
68503             }
68504
68505             return [0, 0];
68506           };
68507
68508           map.zoom = function (z2) {
68509             if (!arguments.length) {
68510               return Math.max(geoScaleToZoom(projection.scale(), TILESIZE), 0);
68511             }
68512
68513             if (z2 < _minzoom) {
68514               surface.interrupt();
68515               dispatch.call('hitMinZoom', this, map);
68516               z2 = context.minEditableZoom();
68517             }
68518
68519             if (setCenterZoom(map.center(), z2)) {
68520               dispatch.call('move', this, map);
68521             }
68522
68523             scheduleRedraw();
68524             return map;
68525           };
68526
68527           map.centerZoom = function (loc2, z2) {
68528             if (setCenterZoom(loc2, z2)) {
68529               dispatch.call('move', this, map);
68530             }
68531
68532             scheduleRedraw();
68533             return map;
68534           };
68535
68536           map.zoomTo = function (entity) {
68537             var extent = entity.extent(context.graph());
68538             if (!isFinite(extent.area())) return map;
68539             var z2 = clamp$1(map.trimmedExtentZoom(extent), 0, 20);
68540             return map.centerZoom(extent.center(), z2);
68541           };
68542
68543           map.centerEase = function (loc2, duration) {
68544             duration = duration || 250;
68545             setCenterZoom(loc2, map.zoom(), duration);
68546             return map;
68547           };
68548
68549           map.zoomEase = function (z2, duration) {
68550             duration = duration || 250;
68551             setCenterZoom(map.center(), z2, duration, false);
68552             return map;
68553           };
68554
68555           map.centerZoomEase = function (loc2, z2, duration) {
68556             duration = duration || 250;
68557             setCenterZoom(loc2, z2, duration, false);
68558             return map;
68559           };
68560
68561           map.transformEase = function (t2, duration) {
68562             duration = duration || 250;
68563             setTransform(t2, duration, false
68564             /* don't force */
68565             );
68566             return map;
68567           };
68568
68569           map.zoomToEase = function (obj, duration) {
68570             var extent;
68571
68572             if (Array.isArray(obj)) {
68573               obj.forEach(function (entity) {
68574                 var entityExtent = entity.extent(context.graph());
68575
68576                 if (!extent) {
68577                   extent = entityExtent;
68578                 } else {
68579                   extent = extent.extend(entityExtent);
68580                 }
68581               });
68582             } else {
68583               extent = obj.extent(context.graph());
68584             }
68585
68586             if (!isFinite(extent.area())) return map;
68587             var z2 = clamp$1(map.trimmedExtentZoom(extent), 0, 20);
68588             return map.centerZoomEase(extent.center(), z2, duration);
68589           };
68590
68591           map.startEase = function () {
68592             utilBindOnce(surface, _pointerPrefix + 'down.ease', function () {
68593               map.cancelEase();
68594             });
68595             return map;
68596           };
68597
68598           map.cancelEase = function () {
68599             _selection.interrupt();
68600
68601             return map;
68602           };
68603
68604           map.extent = function (val) {
68605             if (!arguments.length) {
68606               return new geoExtent(projection.invert([0, _dimensions[1]]), projection.invert([_dimensions[0], 0]));
68607             } else {
68608               var extent = geoExtent(val);
68609               map.centerZoom(extent.center(), map.extentZoom(extent));
68610             }
68611           };
68612
68613           map.trimmedExtent = function (val) {
68614             if (!arguments.length) {
68615               var headerY = 71;
68616               var footerY = 30;
68617               var pad = 10;
68618               return new geoExtent(projection.invert([pad, _dimensions[1] - footerY - pad]), projection.invert([_dimensions[0] - pad, headerY + pad]));
68619             } else {
68620               var extent = geoExtent(val);
68621               map.centerZoom(extent.center(), map.trimmedExtentZoom(extent));
68622             }
68623           };
68624
68625           function calcExtentZoom(extent, dim) {
68626             var tl = projection([extent[0][0], extent[1][1]]);
68627             var br = projection([extent[1][0], extent[0][1]]); // Calculate maximum zoom that fits extent
68628
68629             var hFactor = (br[0] - tl[0]) / dim[0];
68630             var vFactor = (br[1] - tl[1]) / dim[1];
68631             var hZoomDiff = Math.log(Math.abs(hFactor)) / Math.LN2;
68632             var vZoomDiff = Math.log(Math.abs(vFactor)) / Math.LN2;
68633             var newZoom = map.zoom() - Math.max(hZoomDiff, vZoomDiff);
68634             return newZoom;
68635           }
68636
68637           map.extentZoom = function (val) {
68638             return calcExtentZoom(geoExtent(val), _dimensions);
68639           };
68640
68641           map.trimmedExtentZoom = function (val) {
68642             var trimY = 120;
68643             var trimX = 40;
68644             var trimmed = [_dimensions[0] - trimX, _dimensions[1] - trimY];
68645             return calcExtentZoom(geoExtent(val), trimmed);
68646           };
68647
68648           map.withinEditableZoom = function () {
68649             return map.zoom() >= context.minEditableZoom();
68650           };
68651
68652           map.isInWideSelection = function () {
68653             return !map.withinEditableZoom() && context.selectedIDs().length;
68654           };
68655
68656           map.editableDataEnabled = function (skipZoomCheck) {
68657             var layer = context.layers().layer('osm');
68658             if (!layer || !layer.enabled()) return false;
68659             return skipZoomCheck || map.withinEditableZoom();
68660           };
68661
68662           map.notesEditable = function () {
68663             var layer = context.layers().layer('notes');
68664             if (!layer || !layer.enabled()) return false;
68665             return map.withinEditableZoom();
68666           };
68667
68668           map.minzoom = function (val) {
68669             if (!arguments.length) return _minzoom;
68670             _minzoom = val;
68671             return map;
68672           };
68673
68674           map.toggleHighlightEdited = function () {
68675             surface.classed('highlight-edited', !surface.classed('highlight-edited'));
68676             map.pan([0, 0]); // trigger a redraw
68677
68678             dispatch.call('changeHighlighting', this);
68679           };
68680
68681           map.areaFillOptions = ['wireframe', 'partial', 'full'];
68682
68683           map.activeAreaFill = function (val) {
68684             if (!arguments.length) return corePreferences('area-fill') || 'partial';
68685             corePreferences('area-fill', val);
68686
68687             if (val !== 'wireframe') {
68688               corePreferences('area-fill-toggle', val);
68689             }
68690
68691             updateAreaFill();
68692             map.pan([0, 0]); // trigger a redraw
68693
68694             dispatch.call('changeAreaFill', this);
68695             return map;
68696           };
68697
68698           map.toggleWireframe = function () {
68699             var activeFill = map.activeAreaFill();
68700
68701             if (activeFill === 'wireframe') {
68702               activeFill = corePreferences('area-fill-toggle') || 'partial';
68703             } else {
68704               activeFill = 'wireframe';
68705             }
68706
68707             map.activeAreaFill(activeFill);
68708           };
68709
68710           function updateAreaFill() {
68711             var activeFill = map.activeAreaFill();
68712             map.areaFillOptions.forEach(function (opt) {
68713               surface.classed('fill-' + opt, Boolean(opt === activeFill));
68714             });
68715           }
68716
68717           map.layers = function () {
68718             return drawLayers;
68719           };
68720
68721           map.doubleUpHandler = function () {
68722             return _doubleUpHandler;
68723           };
68724
68725           return utilRebind(map, dispatch, 'on');
68726         }
68727
68728         function rendererPhotos(context) {
68729           var dispatch = dispatch$8('change');
68730           var _layerIDs = ['streetside', 'mapillary', 'mapillary-map-features', 'mapillary-signs', 'openstreetcam'];
68731           var _allPhotoTypes = ['flat', 'panoramic'];
68732
68733           var _shownPhotoTypes = _allPhotoTypes.slice(); // shallow copy
68734
68735
68736           var _dateFilters = ['fromDate', 'toDate'];
68737
68738           var _fromDate;
68739
68740           var _toDate;
68741
68742           var _usernames;
68743
68744           function photos() {}
68745
68746           function updateStorage() {
68747             if (window.mocha) return;
68748             var hash = utilStringQs(window.location.hash);
68749             var enabled = context.layers().all().filter(function (d) {
68750               return _layerIDs.indexOf(d.id) !== -1 && d.layer && d.layer.supported() && d.layer.enabled();
68751             }).map(function (d) {
68752               return d.id;
68753             });
68754
68755             if (enabled.length) {
68756               hash.photo_overlay = enabled.join(',');
68757             } else {
68758               delete hash.photo_overlay;
68759             }
68760
68761             window.location.replace('#' + utilQsString(hash, true));
68762           }
68763
68764           photos.overlayLayerIDs = function () {
68765             return _layerIDs;
68766           };
68767
68768           photos.allPhotoTypes = function () {
68769             return _allPhotoTypes;
68770           };
68771
68772           photos.dateFilters = function () {
68773             return _dateFilters;
68774           };
68775
68776           photos.dateFilterValue = function (val) {
68777             return val === _dateFilters[0] ? _fromDate : _toDate;
68778           };
68779
68780           photos.setDateFilter = function (type, val, updateUrl) {
68781             // validate the date
68782             var date = val && new Date(val);
68783
68784             if (date && !isNaN(date)) {
68785               val = date.toISOString().substr(0, 10);
68786             } else {
68787               val = null;
68788             }
68789
68790             if (type === _dateFilters[0]) {
68791               _fromDate = val;
68792
68793               if (_fromDate && _toDate && new Date(_toDate) < new Date(_fromDate)) {
68794                 _toDate = _fromDate;
68795               }
68796             }
68797
68798             if (type === _dateFilters[1]) {
68799               _toDate = val;
68800
68801               if (_fromDate && _toDate && new Date(_toDate) < new Date(_fromDate)) {
68802                 _fromDate = _toDate;
68803               }
68804             }
68805
68806             dispatch.call('change', this);
68807
68808             if (updateUrl) {
68809               var rangeString;
68810
68811               if (_fromDate || _toDate) {
68812                 rangeString = (_fromDate || '') + '_' + (_toDate || '');
68813               }
68814
68815               setUrlFilterValue('photo_dates', rangeString);
68816             }
68817           };
68818
68819           photos.setUsernameFilter = function (val, updateUrl) {
68820             if (val && typeof val === 'string') val = val.replace(/;/g, ',').split(',');
68821
68822             if (val) {
68823               val = val.map(function (d) {
68824                 return d.trim();
68825               }).filter(Boolean);
68826
68827               if (!val.length) {
68828                 val = null;
68829               }
68830             }
68831
68832             _usernames = val;
68833             dispatch.call('change', this);
68834
68835             if (updateUrl) {
68836               var hashString;
68837
68838               if (_usernames) {
68839                 hashString = _usernames.join(',');
68840               }
68841
68842               setUrlFilterValue('photo_username', hashString);
68843             }
68844           };
68845
68846           function setUrlFilterValue(property, val) {
68847             if (!window.mocha) {
68848               var hash = utilStringQs(window.location.hash);
68849
68850               if (val) {
68851                 if (hash[property] === val) return;
68852                 hash[property] = val;
68853               } else {
68854                 if (!(property in hash)) return;
68855                 delete hash[property];
68856               }
68857
68858               window.location.replace('#' + utilQsString(hash, true));
68859             }
68860           }
68861
68862           function showsLayer(id) {
68863             var layer = context.layers().layer(id);
68864             return layer && layer.supported() && layer.enabled();
68865           }
68866
68867           photos.shouldFilterByDate = function () {
68868             return showsLayer('mapillary') || showsLayer('openstreetcam') || showsLayer('streetside');
68869           };
68870
68871           photos.shouldFilterByPhotoType = function () {
68872             return showsLayer('mapillary') || showsLayer('streetside') && showsLayer('openstreetcam');
68873           };
68874
68875           photos.shouldFilterByUsername = function () {
68876             return !showsLayer('mapillary') && showsLayer('openstreetcam') && !showsLayer('streetside');
68877           };
68878
68879           photos.showsPhotoType = function (val) {
68880             if (!photos.shouldFilterByPhotoType()) return true;
68881             return _shownPhotoTypes.indexOf(val) !== -1;
68882           };
68883
68884           photos.showsFlat = function () {
68885             return photos.showsPhotoType('flat');
68886           };
68887
68888           photos.showsPanoramic = function () {
68889             return photos.showsPhotoType('panoramic');
68890           };
68891
68892           photos.fromDate = function () {
68893             return _fromDate;
68894           };
68895
68896           photos.toDate = function () {
68897             return _toDate;
68898           };
68899
68900           photos.togglePhotoType = function (val) {
68901             var index = _shownPhotoTypes.indexOf(val);
68902
68903             if (index !== -1) {
68904               _shownPhotoTypes.splice(index, 1);
68905             } else {
68906               _shownPhotoTypes.push(val);
68907             }
68908
68909             dispatch.call('change', this);
68910             return photos;
68911           };
68912
68913           photos.usernames = function () {
68914             return _usernames;
68915           };
68916
68917           photos.init = function () {
68918             var hash = utilStringQs(window.location.hash);
68919
68920             if (hash.photo_dates) {
68921               // expect format like `photo_dates=2019-01-01_2020-12-31`, but allow a couple different separators
68922               var parts = /^(.*)[–_](.*)$/g.exec(hash.photo_dates.trim());
68923               this.setDateFilter('fromDate', parts && parts.length >= 2 && parts[1], false);
68924               this.setDateFilter('toDate', parts && parts.length >= 3 && parts[2], false);
68925             }
68926
68927             if (hash.photo_username) {
68928               this.setUsernameFilter(hash.photo_username, false);
68929             }
68930
68931             if (hash.photo_overlay) {
68932               // support enabling photo layers by default via a URL parameter, e.g. `photo_overlay=openstreetcam;mapillary;streetside`
68933               var hashOverlayIDs = hash.photo_overlay.replace(/;/g, ',').split(',');
68934               hashOverlayIDs.forEach(function (id) {
68935                 var layer = _layerIDs.indexOf(id) !== -1 && context.layers().layer(id);
68936                 if (layer && !layer.enabled()) layer.enabled(true);
68937               });
68938             }
68939
68940             if (hash.photo) {
68941               // support opening a photo via a URL parameter, e.g. `photo=mapillary-fztgSDtLpa08ohPZFZjeRQ`
68942               var photoIds = hash.photo.replace(/;/g, ',').split(',');
68943               var photoId = photoIds.length && photoIds[0].trim();
68944               var results = /(.*)\/(.*)/g.exec(photoId);
68945
68946               if (results && results.length >= 3) {
68947                 var serviceId = results[1];
68948                 var photoKey = results[2];
68949                 var service = services[serviceId];
68950
68951                 if (service && service.ensureViewerLoaded) {
68952                   // if we're showing a photo then make sure its layer is enabled too
68953                   var layer = _layerIDs.indexOf(serviceId) !== -1 && context.layers().layer(serviceId);
68954                   if (layer && !layer.enabled()) layer.enabled(true);
68955                   var baselineTime = Date.now();
68956                   service.on('loadedImages.rendererPhotos', function () {
68957                     // don't open the viewer if too much time has elapsed
68958                     if (Date.now() - baselineTime > 45000) {
68959                       service.on('loadedImages.rendererPhotos', null);
68960                       return;
68961                     }
68962
68963                     if (!service.cachedImage(photoKey)) return;
68964                     service.on('loadedImages.rendererPhotos', null);
68965                     service.ensureViewerLoaded(context).then(function () {
68966                       service.selectImage(context, photoKey).showViewer(context);
68967                     });
68968                   });
68969                 }
68970               }
68971             }
68972
68973             context.layers().on('change.rendererPhotos', updateStorage);
68974           };
68975
68976           return utilRebind(photos, dispatch, 'on');
68977         }
68978
68979         function uiAccount(context) {
68980           var osm = context.connection();
68981
68982           function update(selection) {
68983             if (!osm) return;
68984
68985             if (!osm.authenticated()) {
68986               selection.selectAll('.userLink, .logoutLink').classed('hide', true);
68987               return;
68988             }
68989
68990             osm.userDetails(function (err, details) {
68991               var userLink = selection.select('.userLink'),
68992                   logoutLink = selection.select('.logoutLink');
68993               userLink.html('');
68994               logoutLink.html('');
68995               if (err || !details) return;
68996               selection.selectAll('.userLink, .logoutLink').classed('hide', false); // Link
68997
68998               var userLinkA = userLink.append('a').attr('href', osm.userURL(details.display_name)).attr('target', '_blank'); // Add thumbnail or dont
68999
69000               if (details.image_url) {
69001                 userLinkA.append('img').attr('class', 'icon pre-text user-icon').attr('src', details.image_url);
69002               } else {
69003                 userLinkA.call(svgIcon('#iD-icon-avatar', 'pre-text light'));
69004               } // Add user name
69005
69006
69007               userLinkA.append('span').attr('class', 'label').html(details.display_name);
69008               logoutLink.append('a').attr('class', 'logout').attr('href', '#').html(_t.html('logout')).on('click.logout', function (d3_event) {
69009                 d3_event.preventDefault();
69010                 osm.logout();
69011               });
69012             });
69013           }
69014
69015           return function (selection) {
69016             selection.append('li').attr('class', 'userLink').classed('hide', true);
69017             selection.append('li').attr('class', 'logoutLink').classed('hide', true);
69018
69019             if (osm) {
69020               osm.on('change.account', function () {
69021                 update(selection);
69022               });
69023               update(selection);
69024             }
69025           };
69026         }
69027
69028         function uiAttribution(context) {
69029           var _selection = select(null);
69030
69031           function render(selection, data, klass) {
69032             var div = selection.selectAll(".".concat(klass)).data([0]);
69033             div = div.enter().append('div').attr('class', klass).merge(div);
69034             var attributions = div.selectAll('.attribution').data(data, function (d) {
69035               return d.id;
69036             });
69037             attributions.exit().remove();
69038             attributions = attributions.enter().append('span').attr('class', 'attribution').each(function (d, i, nodes) {
69039               var attribution = select(nodes[i]);
69040
69041               if (d.terms_html) {
69042                 attribution.html(d.terms_html);
69043                 return;
69044               }
69045
69046               if (d.terms_url) {
69047                 attribution = attribution.append('a').attr('href', d.terms_url).attr('target', '_blank');
69048               }
69049
69050               var sourceID = d.id.replace(/\./g, '<TX_DOT>');
69051               var terms_text = _t("imagery.".concat(sourceID, ".attribution.text"), {
69052                 "default": d.terms_text || d.id || d.name()
69053               });
69054
69055               if (d.icon && !d.overlay) {
69056                 attribution.append('img').attr('class', 'source-image').attr('src', d.icon);
69057               }
69058
69059               attribution.append('span').attr('class', 'attribution-text').html(terms_text);
69060             }).merge(attributions);
69061             var copyright = attributions.selectAll('.copyright-notice').data(function (d) {
69062               var notice = d.copyrightNotices(context.map().zoom(), context.map().extent());
69063               return notice ? [notice] : [];
69064             });
69065             copyright.exit().remove();
69066             copyright = copyright.enter().append('span').attr('class', 'copyright-notice').merge(copyright);
69067             copyright.html(String);
69068           }
69069
69070           function update() {
69071             var baselayer = context.background().baseLayerSource();
69072
69073             _selection.call(render, baselayer ? [baselayer] : [], 'base-layer-attribution');
69074
69075             var z = context.map().zoom();
69076             var overlays = context.background().overlayLayerSources() || [];
69077
69078             _selection.call(render, overlays.filter(function (s) {
69079               return s.validZoom(z);
69080             }), 'overlay-layer-attribution');
69081           }
69082
69083           return function (selection) {
69084             _selection = selection;
69085             context.background().on('change.attribution', update);
69086             context.map().on('move.attribution', throttle(update, 400, {
69087               leading: false
69088             }));
69089             update();
69090           };
69091         }
69092
69093         function uiContributors(context) {
69094           var osm = context.connection(),
69095               debouncedUpdate = debounce(function () {
69096             update();
69097           }, 1000),
69098               limit = 4,
69099               hidden = false,
69100               wrap = select(null);
69101
69102           function update() {
69103             if (!osm) return;
69104             var users = {},
69105                 entities = context.history().intersects(context.map().extent());
69106             entities.forEach(function (entity) {
69107               if (entity && entity.user) users[entity.user] = true;
69108             });
69109             var u = Object.keys(users),
69110                 subset = u.slice(0, u.length > limit ? limit - 1 : limit);
69111             wrap.html('').call(svgIcon('#iD-icon-nearby', 'pre-text light'));
69112             var userList = select(document.createElement('span'));
69113             userList.selectAll().data(subset).enter().append('a').attr('class', 'user-link').attr('href', function (d) {
69114               return osm.userURL(d);
69115             }).attr('target', '_blank').html(String);
69116
69117             if (u.length > limit) {
69118               var count = select(document.createElement('span'));
69119               var othersNum = u.length - limit + 1;
69120               count.append('a').attr('target', '_blank').attr('href', function () {
69121                 return osm.changesetsURL(context.map().center(), context.map().zoom());
69122               }).html(othersNum);
69123               wrap.append('span').html(_t.html('contributors.truncated_list', {
69124                 n: othersNum,
69125                 users: userList.html(),
69126                 count: count.html()
69127               }));
69128             } else {
69129               wrap.append('span').html(_t.html('contributors.list', {
69130                 users: userList.html()
69131               }));
69132             }
69133
69134             if (!u.length) {
69135               hidden = true;
69136               wrap.transition().style('opacity', 0);
69137             } else if (hidden) {
69138               wrap.transition().style('opacity', 1);
69139             }
69140           }
69141
69142           return function (selection) {
69143             if (!osm) return;
69144             wrap = selection;
69145             update();
69146             osm.on('loaded.contributors', debouncedUpdate);
69147             context.map().on('move.contributors', debouncedUpdate);
69148           };
69149         }
69150
69151         var _popoverID = 0;
69152         function uiPopover(klass) {
69153           var _id = _popoverID++;
69154
69155           var _anchorSelection = select(null);
69156
69157           var popover = function popover(selection) {
69158             _anchorSelection = selection;
69159             selection.each(setup);
69160           };
69161
69162           var _animation = utilFunctor(false);
69163
69164           var _placement = utilFunctor('top'); // top, bottom, left, right
69165
69166
69167           var _alignment = utilFunctor('center'); // leading, center, trailing
69168
69169
69170           var _scrollContainer = utilFunctor(select(null));
69171
69172           var _content;
69173
69174           var _displayType = utilFunctor('');
69175
69176           var _hasArrow = utilFunctor(true); // use pointer events on supported platforms; fallback to mouse events
69177
69178
69179           var _pointerPrefix = 'PointerEvent' in window ? 'pointer' : 'mouse';
69180
69181           popover.displayType = function (val) {
69182             if (arguments.length) {
69183               _displayType = utilFunctor(val);
69184               return popover;
69185             } else {
69186               return _displayType;
69187             }
69188           };
69189
69190           popover.hasArrow = function (val) {
69191             if (arguments.length) {
69192               _hasArrow = utilFunctor(val);
69193               return popover;
69194             } else {
69195               return _hasArrow;
69196             }
69197           };
69198
69199           popover.placement = function (val) {
69200             if (arguments.length) {
69201               _placement = utilFunctor(val);
69202               return popover;
69203             } else {
69204               return _placement;
69205             }
69206           };
69207
69208           popover.alignment = function (val) {
69209             if (arguments.length) {
69210               _alignment = utilFunctor(val);
69211               return popover;
69212             } else {
69213               return _alignment;
69214             }
69215           };
69216
69217           popover.scrollContainer = function (val) {
69218             if (arguments.length) {
69219               _scrollContainer = utilFunctor(val);
69220               return popover;
69221             } else {
69222               return _scrollContainer;
69223             }
69224           };
69225
69226           popover.content = function (val) {
69227             if (arguments.length) {
69228               _content = val;
69229               return popover;
69230             } else {
69231               return _content;
69232             }
69233           };
69234
69235           popover.isShown = function () {
69236             var popoverSelection = _anchorSelection.select('.popover-' + _id);
69237
69238             return !popoverSelection.empty() && popoverSelection.classed('in');
69239           };
69240
69241           popover.show = function () {
69242             _anchorSelection.each(show);
69243           };
69244
69245           popover.updateContent = function () {
69246             _anchorSelection.each(updateContent);
69247           };
69248
69249           popover.hide = function () {
69250             _anchorSelection.each(hide);
69251           };
69252
69253           popover.toggle = function () {
69254             _anchorSelection.each(toggle);
69255           };
69256
69257           popover.destroy = function (selection, selector) {
69258             // by default, just destroy the current popover
69259             selector = selector || '.popover-' + _id;
69260             selection.on(_pointerPrefix + 'enter.popover', null).on(_pointerPrefix + 'leave.popover', null).on(_pointerPrefix + 'up.popover', null).on(_pointerPrefix + 'down.popover', null).on('click.popover', null).attr('title', function () {
69261               return this.getAttribute('data-original-title') || this.getAttribute('title');
69262             }).attr('data-original-title', null).selectAll(selector).remove();
69263           };
69264
69265           popover.destroyAny = function (selection) {
69266             selection.call(popover.destroy, '.popover');
69267           };
69268
69269           function setup() {
69270             var anchor = select(this);
69271
69272             var animate = _animation.apply(this, arguments);
69273
69274             var popoverSelection = anchor.selectAll('.popover-' + _id).data([0]);
69275             var enter = popoverSelection.enter().append('div').attr('class', 'popover popover-' + _id + ' ' + (klass ? klass : '')).classed('arrowed', _hasArrow.apply(this, arguments));
69276             enter.append('div').attr('class', 'popover-arrow');
69277             enter.append('div').attr('class', 'popover-inner');
69278             popoverSelection = enter.merge(popoverSelection);
69279
69280             if (animate) {
69281               popoverSelection.classed('fade', true);
69282             }
69283
69284             var display = _displayType.apply(this, arguments);
69285
69286             if (display === 'hover') {
69287               var _lastNonMouseEnterTime;
69288
69289               anchor.on(_pointerPrefix + 'enter.popover', function (d3_event) {
69290                 if (d3_event.pointerType) {
69291                   if (d3_event.pointerType !== 'mouse') {
69292                     _lastNonMouseEnterTime = d3_event.timeStamp; // only allow hover behavior for mouse input
69293
69294                     return;
69295                   } else if (_lastNonMouseEnterTime && d3_event.timeStamp - _lastNonMouseEnterTime < 1500) {
69296                     // HACK: iOS 13.4 sends an erroneous `mouse` type pointerenter
69297                     // event for non-mouse interactions right after sending
69298                     // the correct type pointerenter event. Workaround by discarding
69299                     // any mouse event that occurs immediately after a non-mouse event.
69300                     return;
69301                   }
69302                 } // don't show if buttons are pressed, e.g. during click and drag of map
69303
69304
69305                 if (d3_event.buttons !== 0) return;
69306                 show.apply(this, arguments);
69307               }).on(_pointerPrefix + 'leave.popover', function () {
69308                 hide.apply(this, arguments);
69309               }) // show on focus too for better keyboard navigation support
69310               .on('focus.popover', function () {
69311                 show.apply(this, arguments);
69312               }).on('blur.popover', function () {
69313                 hide.apply(this, arguments);
69314               });
69315             } else if (display === 'clickFocus') {
69316               anchor.on(_pointerPrefix + 'down.popover', function (d3_event) {
69317                 d3_event.preventDefault();
69318                 d3_event.stopPropagation();
69319               }).on(_pointerPrefix + 'up.popover', function (d3_event) {
69320                 d3_event.preventDefault();
69321                 d3_event.stopPropagation();
69322               }).on('click.popover', toggle);
69323               popoverSelection // This attribute lets the popover take focus
69324               .attr('tabindex', 0).on('blur.popover', function () {
69325                 anchor.each(function () {
69326                   hide.apply(this, arguments);
69327                 });
69328               });
69329             }
69330           }
69331
69332           function show() {
69333             var anchor = select(this);
69334             var popoverSelection = anchor.selectAll('.popover-' + _id);
69335
69336             if (popoverSelection.empty()) {
69337               // popover was removed somehow, put it back
69338               anchor.call(popover.destroy);
69339               anchor.each(setup);
69340               popoverSelection = anchor.selectAll('.popover-' + _id);
69341             }
69342
69343             popoverSelection.classed('in', true);
69344
69345             var displayType = _displayType.apply(this, arguments);
69346
69347             if (displayType === 'clickFocus') {
69348               anchor.classed('active', true);
69349               popoverSelection.node().focus();
69350             }
69351
69352             anchor.each(updateContent);
69353           }
69354
69355           function updateContent() {
69356             var anchor = select(this);
69357
69358             if (_content) {
69359               anchor.selectAll('.popover-' + _id + ' > .popover-inner').call(_content.apply(this, arguments));
69360             }
69361
69362             updatePosition.apply(this, arguments); // hack: update multiple times to fix instances where the absolute offset is
69363             // set before the dynamic popover size is calculated by the browser
69364
69365             updatePosition.apply(this, arguments);
69366             updatePosition.apply(this, arguments);
69367           }
69368
69369           function updatePosition() {
69370             var anchor = select(this);
69371             var popoverSelection = anchor.selectAll('.popover-' + _id);
69372
69373             var scrollContainer = _scrollContainer && _scrollContainer.apply(this, arguments);
69374
69375             var scrollNode = scrollContainer && !scrollContainer.empty() && scrollContainer.node();
69376             var scrollLeft = scrollNode ? scrollNode.scrollLeft : 0;
69377             var scrollTop = scrollNode ? scrollNode.scrollTop : 0;
69378
69379             var placement = _placement.apply(this, arguments);
69380
69381             popoverSelection.classed('left', false).classed('right', false).classed('top', false).classed('bottom', false).classed(placement, true);
69382
69383             var alignment = _alignment.apply(this, arguments);
69384
69385             var alignFactor = 0.5;
69386
69387             if (alignment === 'leading') {
69388               alignFactor = 0;
69389             } else if (alignment === 'trailing') {
69390               alignFactor = 1;
69391             }
69392
69393             var anchorFrame = getFrame(anchor.node());
69394             var popoverFrame = getFrame(popoverSelection.node());
69395             var position;
69396
69397             switch (placement) {
69398               case 'top':
69399                 position = {
69400                   x: anchorFrame.x + (anchorFrame.w - popoverFrame.w) * alignFactor,
69401                   y: anchorFrame.y - popoverFrame.h
69402                 };
69403                 break;
69404
69405               case 'bottom':
69406                 position = {
69407                   x: anchorFrame.x + (anchorFrame.w - popoverFrame.w) * alignFactor,
69408                   y: anchorFrame.y + anchorFrame.h
69409                 };
69410                 break;
69411
69412               case 'left':
69413                 position = {
69414                   x: anchorFrame.x - popoverFrame.w,
69415                   y: anchorFrame.y + (anchorFrame.h - popoverFrame.h) * alignFactor
69416                 };
69417                 break;
69418
69419               case 'right':
69420                 position = {
69421                   x: anchorFrame.x + anchorFrame.w,
69422                   y: anchorFrame.y + (anchorFrame.h - popoverFrame.h) * alignFactor
69423                 };
69424                 break;
69425             }
69426
69427             if (position) {
69428               if (scrollNode && (placement === 'top' || placement === 'bottom')) {
69429                 var initialPosX = position.x;
69430
69431                 if (position.x + popoverFrame.w > scrollNode.offsetWidth - 10) {
69432                   position.x = scrollNode.offsetWidth - 10 - popoverFrame.w;
69433                 } else if (position.x < 10) {
69434                   position.x = 10;
69435                 }
69436
69437                 var arrow = anchor.selectAll('.popover-' + _id + ' > .popover-arrow'); // keep the arrow centered on the button, or as close as possible
69438
69439                 var arrowPosX = Math.min(Math.max(popoverFrame.w / 2 - (position.x - initialPosX), 10), popoverFrame.w - 10);
69440                 arrow.style('left', ~~arrowPosX + 'px');
69441               }
69442
69443               popoverSelection.style('left', ~~position.x + 'px').style('top', ~~position.y + 'px');
69444             } else {
69445               popoverSelection.style('left', null).style('top', null);
69446             }
69447
69448             function getFrame(node) {
69449               var positionStyle = select(node).style('position');
69450
69451               if (positionStyle === 'absolute' || positionStyle === 'static') {
69452                 return {
69453                   x: node.offsetLeft - scrollLeft,
69454                   y: node.offsetTop - scrollTop,
69455                   w: node.offsetWidth,
69456                   h: node.offsetHeight
69457                 };
69458               } else {
69459                 return {
69460                   x: 0,
69461                   y: 0,
69462                   w: node.offsetWidth,
69463                   h: node.offsetHeight
69464                 };
69465               }
69466             }
69467           }
69468
69469           function hide() {
69470             var anchor = select(this);
69471
69472             if (_displayType.apply(this, arguments) === 'clickFocus') {
69473               anchor.classed('active', false);
69474             }
69475
69476             anchor.selectAll('.popover-' + _id).classed('in', false);
69477           }
69478
69479           function toggle() {
69480             if (select(this).select('.popover-' + _id).classed('in')) {
69481               hide.apply(this, arguments);
69482             } else {
69483               show.apply(this, arguments);
69484             }
69485           }
69486
69487           return popover;
69488         }
69489
69490         function uiTooltip(klass) {
69491           var tooltip = uiPopover((klass || '') + ' tooltip').displayType('hover');
69492
69493           var _title = function _title() {
69494             var title = this.getAttribute('data-original-title');
69495
69496             if (title) {
69497               return title;
69498             } else {
69499               title = this.getAttribute('title');
69500               this.removeAttribute('title');
69501               this.setAttribute('data-original-title', title);
69502             }
69503
69504             return title;
69505           };
69506
69507           var _heading = utilFunctor(null);
69508
69509           var _keys = utilFunctor(null);
69510
69511           tooltip.title = function (val) {
69512             if (!arguments.length) return _title;
69513             _title = utilFunctor(val);
69514             return tooltip;
69515           };
69516
69517           tooltip.heading = function (val) {
69518             if (!arguments.length) return _heading;
69519             _heading = utilFunctor(val);
69520             return tooltip;
69521           };
69522
69523           tooltip.keys = function (val) {
69524             if (!arguments.length) return _keys;
69525             _keys = utilFunctor(val);
69526             return tooltip;
69527           };
69528
69529           tooltip.content(function () {
69530             var heading = _heading.apply(this, arguments);
69531
69532             var text = _title.apply(this, arguments);
69533
69534             var keys = _keys.apply(this, arguments);
69535
69536             return function (selection) {
69537               var headingSelect = selection.selectAll('.tooltip-heading').data(heading ? [heading] : []);
69538               headingSelect.exit().remove();
69539               headingSelect.enter().append('div').attr('class', 'tooltip-heading').merge(headingSelect).html(heading);
69540               var textSelect = selection.selectAll('.tooltip-text').data(text ? [text] : []);
69541               textSelect.exit().remove();
69542               textSelect.enter().append('div').attr('class', 'tooltip-text').merge(textSelect).html(text);
69543               var keyhintWrap = selection.selectAll('.keyhint-wrap').data(keys && keys.length ? [0] : []);
69544               keyhintWrap.exit().remove();
69545               var keyhintWrapEnter = keyhintWrap.enter().append('div').attr('class', 'keyhint-wrap');
69546               keyhintWrapEnter.append('span').html(_t.html('tooltip_keyhint'));
69547               keyhintWrap = keyhintWrapEnter.merge(keyhintWrap);
69548               keyhintWrap.selectAll('kbd.shortcut').data(keys && keys.length ? keys : []).enter().append('kbd').attr('class', 'shortcut').html(function (d) {
69549                 return d;
69550               });
69551             };
69552           });
69553           return tooltip;
69554         }
69555
69556         function uiEditMenu(context) {
69557           var dispatch = dispatch$8('toggled');
69558
69559           var _menu = select(null);
69560
69561           var _operations = []; // the position the menu should be displayed relative to
69562
69563           var _anchorLoc = [0, 0];
69564           var _anchorLocLonLat = [0, 0]; // a string indicating how the menu was opened
69565
69566           var _triggerType = '';
69567           var _vpTopMargin = 85; // viewport top margin
69568
69569           var _vpBottomMargin = 45; // viewport bottom margin
69570
69571           var _vpSideMargin = 35; // viewport side margin
69572
69573           var _menuTop = false;
69574
69575           var _menuHeight;
69576
69577           var _menuWidth; // hardcode these values to make menu positioning easier
69578
69579
69580           var _verticalPadding = 4; // see also `.edit-menu .tooltip` CSS; include margin
69581
69582           var _tooltipWidth = 210; // offset the menu slightly from the target location
69583
69584           var _menuSideMargin = 10;
69585           var _tooltips = [];
69586
69587           var editMenu = function editMenu(selection) {
69588             var isTouchMenu = _triggerType.includes('touch') || _triggerType.includes('pen');
69589
69590             var ops = _operations.filter(function (op) {
69591               return !isTouchMenu || !op.mouseOnly;
69592             });
69593
69594             if (!ops.length) return;
69595             _tooltips = []; // Position the menu above the anchor for stylus and finger input
69596             // since the mapper's hand likely obscures the screen below the anchor
69597
69598             _menuTop = isTouchMenu; // Show labels for touch input since there aren't hover tooltips
69599
69600             var showLabels = isTouchMenu;
69601             var buttonHeight = showLabels ? 32 : 34;
69602
69603             if (showLabels) {
69604               // Get a general idea of the width based on the length of the label
69605               _menuWidth = 52 + Math.min(120, 6 * Math.max.apply(Math, ops.map(function (op) {
69606                 return op.title.length;
69607               })));
69608             } else {
69609               _menuWidth = 44;
69610             }
69611
69612             _menuHeight = _verticalPadding * 2 + ops.length * buttonHeight;
69613             _menu = selection.append('div').attr('class', 'edit-menu').classed('touch-menu', isTouchMenu).style('padding', _verticalPadding + 'px 0');
69614
69615             var buttons = _menu.selectAll('.edit-menu-item').data(ops); // enter
69616
69617
69618             var buttonsEnter = buttons.enter().append('button').attr('class', function (d) {
69619               return 'edit-menu-item edit-menu-item-' + d.id;
69620             }).style('height', buttonHeight + 'px').on('click', click) // don't listen for `mouseup` because we only care about non-mouse pointer types
69621             .on('pointerup', pointerup).on('pointerdown mousedown', function pointerdown(d3_event) {
69622               // don't let button presses also act as map input - #1869
69623               d3_event.stopPropagation();
69624             }).on('mouseenter.highlight', function (d3_event, d) {
69625               if (!d.relatedEntityIds || select(this).classed('disabled')) return;
69626               utilHighlightEntities(d.relatedEntityIds(), true, context);
69627             }).on('mouseleave.highlight', function (d3_event, d) {
69628               if (!d.relatedEntityIds) return;
69629               utilHighlightEntities(d.relatedEntityIds(), false, context);
69630             });
69631             buttonsEnter.each(function (d) {
69632               var tooltip = uiTooltip().heading(d.title).title(d.tooltip()).keys([d.keys[0]]);
69633
69634               _tooltips.push(tooltip);
69635
69636               select(this).call(tooltip).append('div').attr('class', 'icon-wrap').call(svgIcon('#iD-operation-' + d.id, 'operation'));
69637             });
69638
69639             if (showLabels) {
69640               buttonsEnter.append('span').attr('class', 'label').html(function (d) {
69641                 return d.title;
69642               });
69643             } // update
69644
69645
69646             buttonsEnter.merge(buttons).classed('disabled', function (d) {
69647               return d.disabled();
69648             });
69649             updatePosition();
69650             var initialScale = context.projection.scale();
69651             context.map().on('move.edit-menu', function () {
69652               if (initialScale !== context.projection.scale()) {
69653                 editMenu.close();
69654               }
69655             }).on('drawn.edit-menu', function (info) {
69656               if (info.full) updatePosition();
69657             });
69658             var lastPointerUpType; // `pointerup` is always called before `click`
69659
69660             function pointerup(d3_event) {
69661               lastPointerUpType = d3_event.pointerType;
69662             }
69663
69664             function click(d3_event, operation) {
69665               d3_event.stopPropagation();
69666
69667               if (operation.relatedEntityIds) {
69668                 utilHighlightEntities(operation.relatedEntityIds(), false, context);
69669               }
69670
69671               if (operation.disabled()) {
69672                 if (lastPointerUpType === 'touch' || lastPointerUpType === 'pen') {
69673                   // there are no tooltips for touch interactions so flash feedback instead
69674                   context.ui().flash.duration(4000).iconName('#iD-operation-' + operation.id).iconClass('operation disabled').label(operation.tooltip)();
69675                 }
69676               } else {
69677                 if (lastPointerUpType === 'touch' || lastPointerUpType === 'pen') {
69678                   context.ui().flash.duration(2000).iconName('#iD-operation-' + operation.id).iconClass('operation').label(operation.annotation() || operation.title)();
69679                 }
69680
69681                 operation();
69682                 editMenu.close();
69683               }
69684
69685               lastPointerUpType = null;
69686             }
69687
69688             dispatch.call('toggled', this, true);
69689           };
69690
69691           function updatePosition() {
69692             if (!_menu || _menu.empty()) return;
69693             var anchorLoc = context.projection(_anchorLocLonLat);
69694             var viewport = context.surfaceRect();
69695
69696             if (anchorLoc[0] < 0 || anchorLoc[0] > viewport.width || anchorLoc[1] < 0 || anchorLoc[1] > viewport.height) {
69697               // close the menu if it's gone offscreen
69698               editMenu.close();
69699               return;
69700             }
69701
69702             var menuLeft = displayOnLeft(viewport);
69703             var offset = [0, 0];
69704             offset[0] = menuLeft ? -1 * (_menuSideMargin + _menuWidth) : _menuSideMargin;
69705
69706             if (_menuTop) {
69707               if (anchorLoc[1] - _menuHeight < _vpTopMargin) {
69708                 // menu is near top viewport edge, shift downward
69709                 offset[1] = -anchorLoc[1] + _vpTopMargin;
69710               } else {
69711                 offset[1] = -_menuHeight;
69712               }
69713             } else {
69714               if (anchorLoc[1] + _menuHeight > viewport.height - _vpBottomMargin) {
69715                 // menu is near bottom viewport edge, shift upwards
69716                 offset[1] = -anchorLoc[1] - _menuHeight + viewport.height - _vpBottomMargin;
69717               } else {
69718                 offset[1] = 0;
69719               }
69720             }
69721
69722             var origin = geoVecAdd(anchorLoc, offset);
69723
69724             _menu.style('left', origin[0] + 'px').style('top', origin[1] + 'px');
69725
69726             var tooltipSide = tooltipPosition(viewport, menuLeft);
69727
69728             _tooltips.forEach(function (tooltip) {
69729               tooltip.placement(tooltipSide);
69730             });
69731
69732             function displayOnLeft(viewport) {
69733               if (_mainLocalizer.textDirection() === 'ltr') {
69734                 if (anchorLoc[0] + _menuSideMargin + _menuWidth > viewport.width - _vpSideMargin) {
69735                   // right menu would be too close to the right viewport edge, go left
69736                   return true;
69737                 } // prefer right menu
69738
69739
69740                 return false;
69741               } else {
69742                 // rtl
69743                 if (anchorLoc[0] - _menuSideMargin - _menuWidth < _vpSideMargin) {
69744                   // left menu would be too close to the left viewport edge, go right
69745                   return false;
69746                 } // prefer left menu
69747
69748
69749                 return true;
69750               }
69751             }
69752
69753             function tooltipPosition(viewport, menuLeft) {
69754               if (_mainLocalizer.textDirection() === 'ltr') {
69755                 if (menuLeft) {
69756                   // if there's not room for a right-side menu then there definitely
69757                   // isn't room for right-side tooltips
69758                   return 'left';
69759                 }
69760
69761                 if (anchorLoc[0] + _menuSideMargin + _menuWidth + _tooltipWidth > viewport.width - _vpSideMargin) {
69762                   // right tooltips would be too close to the right viewport edge, go left
69763                   return 'left';
69764                 } // prefer right tooltips
69765
69766
69767                 return 'right';
69768               } else {
69769                 // rtl
69770                 if (!menuLeft) {
69771                   return 'right';
69772                 }
69773
69774                 if (anchorLoc[0] - _menuSideMargin - _menuWidth - _tooltipWidth < _vpSideMargin) {
69775                   // left tooltips would be too close to the left viewport edge, go right
69776                   return 'right';
69777                 } // prefer left tooltips
69778
69779
69780                 return 'left';
69781               }
69782             }
69783           }
69784
69785           editMenu.close = function () {
69786             context.map().on('move.edit-menu', null).on('drawn.edit-menu', null);
69787
69788             _menu.remove();
69789
69790             _tooltips = [];
69791             dispatch.call('toggled', this, false);
69792           };
69793
69794           editMenu.anchorLoc = function (val) {
69795             if (!arguments.length) return _anchorLoc;
69796             _anchorLoc = val;
69797             _anchorLocLonLat = context.projection.invert(_anchorLoc);
69798             return editMenu;
69799           };
69800
69801           editMenu.triggerType = function (val) {
69802             if (!arguments.length) return _triggerType;
69803             _triggerType = val;
69804             return editMenu;
69805           };
69806
69807           editMenu.operations = function (val) {
69808             if (!arguments.length) return _operations;
69809             _operations = val;
69810             return editMenu;
69811           };
69812
69813           return utilRebind(editMenu, dispatch, 'on');
69814         }
69815
69816         function uiFeatureInfo(context) {
69817           function update(selection) {
69818             var features = context.features();
69819             var stats = features.stats();
69820             var count = 0;
69821             var hiddenList = features.hidden().map(function (k) {
69822               if (stats[k]) {
69823                 count += stats[k];
69824                 return _t('inspector.title_count', {
69825                   title: _t.html('feature.' + k + '.description'),
69826                   count: stats[k]
69827                 });
69828               }
69829
69830               return null;
69831             }).filter(Boolean);
69832             selection.html('');
69833
69834             if (hiddenList.length) {
69835               var tooltipBehavior = uiTooltip().placement('top').title(function () {
69836                 return hiddenList.join('<br/>');
69837               });
69838               selection.append('a').attr('class', 'chip').attr('href', '#').html(_t.html('feature_info.hidden_warning', {
69839                 count: count
69840               })).call(tooltipBehavior).on('click', function (d3_event) {
69841                 tooltipBehavior.hide();
69842                 d3_event.preventDefault(); // open the Map Data pane
69843
69844                 context.ui().togglePanes(context.container().select('.map-panes .map-data-pane'));
69845               });
69846             }
69847
69848             selection.classed('hide', !hiddenList.length);
69849           }
69850
69851           return function (selection) {
69852             update(selection);
69853             context.features().on('change.feature_info', function () {
69854               update(selection);
69855             });
69856           };
69857         }
69858
69859         function uiFlash(context) {
69860           var _flashTimer;
69861
69862           var _duration = 2000;
69863           var _iconName = '#iD-icon-no';
69864           var _iconClass = 'disabled';
69865           var _label = '';
69866
69867           function flash() {
69868             if (_flashTimer) {
69869               _flashTimer.stop();
69870             }
69871
69872             context.container().select('.main-footer-wrap').classed('footer-hide', true).classed('footer-show', false);
69873             context.container().select('.flash-wrap').classed('footer-hide', false).classed('footer-show', true);
69874             var content = context.container().select('.flash-wrap').selectAll('.flash-content').data([0]); // Enter
69875
69876             var contentEnter = content.enter().append('div').attr('class', 'flash-content');
69877             var iconEnter = contentEnter.append('svg').attr('class', 'flash-icon icon').append('g').attr('transform', 'translate(10,10)');
69878             iconEnter.append('circle').attr('r', 9);
69879             iconEnter.append('use').attr('transform', 'translate(-7,-7)').attr('width', '14').attr('height', '14');
69880             contentEnter.append('div').attr('class', 'flash-text'); // Update
69881
69882             content = content.merge(contentEnter);
69883             content.selectAll('.flash-icon').attr('class', 'icon flash-icon ' + (_iconClass || ''));
69884             content.selectAll('.flash-icon use').attr('xlink:href', _iconName);
69885             content.selectAll('.flash-text').attr('class', 'flash-text').html(_label);
69886             _flashTimer = d3_timeout(function () {
69887               _flashTimer = null;
69888               context.container().select('.main-footer-wrap').classed('footer-hide', false).classed('footer-show', true);
69889               context.container().select('.flash-wrap').classed('footer-hide', true).classed('footer-show', false);
69890             }, _duration);
69891             return content;
69892           }
69893
69894           flash.duration = function (_) {
69895             if (!arguments.length) return _duration;
69896             _duration = _;
69897             return flash;
69898           };
69899
69900           flash.label = function (_) {
69901             if (!arguments.length) return _label;
69902             _label = _;
69903             return flash;
69904           };
69905
69906           flash.iconName = function (_) {
69907             if (!arguments.length) return _iconName;
69908             _iconName = _;
69909             return flash;
69910           };
69911
69912           flash.iconClass = function (_) {
69913             if (!arguments.length) return _iconClass;
69914             _iconClass = _;
69915             return flash;
69916           };
69917
69918           return flash;
69919         }
69920
69921         function uiFullScreen(context) {
69922           var element = context.container().node(); // var button = d3_select(null);
69923
69924           function getFullScreenFn() {
69925             if (element.requestFullscreen) {
69926               return element.requestFullscreen;
69927             } else if (element.msRequestFullscreen) {
69928               return element.msRequestFullscreen;
69929             } else if (element.mozRequestFullScreen) {
69930               return element.mozRequestFullScreen;
69931             } else if (element.webkitRequestFullscreen) {
69932               return element.webkitRequestFullscreen;
69933             }
69934           }
69935
69936           function getExitFullScreenFn() {
69937             if (document.exitFullscreen) {
69938               return document.exitFullscreen;
69939             } else if (document.msExitFullscreen) {
69940               return document.msExitFullscreen;
69941             } else if (document.mozCancelFullScreen) {
69942               return document.mozCancelFullScreen;
69943             } else if (document.webkitExitFullscreen) {
69944               return document.webkitExitFullscreen;
69945             }
69946           }
69947
69948           function isFullScreen() {
69949             return document.fullscreenElement || document.mozFullScreenElement || document.webkitFullscreenElement || document.msFullscreenElement;
69950           }
69951
69952           function isSupported() {
69953             return !!getFullScreenFn();
69954           }
69955
69956           function fullScreen(d3_event) {
69957             d3_event.preventDefault();
69958
69959             if (!isFullScreen()) {
69960               // button.classed('active', true);
69961               getFullScreenFn().apply(element);
69962             } else {
69963               // button.classed('active', false);
69964               getExitFullScreenFn().apply(document);
69965             }
69966           }
69967
69968           return function () {
69969             // selection) {
69970             if (!isSupported()) return; // button = selection.append('button')
69971             //     .attr('title', t('full_screen'))
69972             //     .on('click', fullScreen)
69973             //     .call(tooltip);
69974             // button.append('span')
69975             //     .attr('class', 'icon full-screen');
69976
69977             var detected = utilDetect();
69978             var keys = detected.os === 'mac' ? [uiCmd('⌃⌘F'), 'f11'] : ['f11'];
69979             context.keybinding().on(keys, fullScreen);
69980           };
69981         }
69982
69983         function uiGeolocate(context) {
69984           var _geolocationOptions = {
69985             // prioritize speed and power usage over precision
69986             enableHighAccuracy: false,
69987             // don't hang indefinitely getting the location
69988             timeout: 6000 // 6sec
69989
69990           };
69991
69992           var _locating = uiLoading(context).message(_t.html('geolocate.locating')).blocking(true);
69993
69994           var _layer = context.layers().layer('geolocate');
69995
69996           var _position;
69997
69998           var _extent;
69999
70000           var _timeoutID;
70001
70002           var _button = select(null);
70003
70004           function click() {
70005             if (context.inIntro()) return;
70006
70007             if (!_layer.enabled() && !_locating.isShown()) {
70008               // This timeout ensures that we still call finish() even if
70009               // the user declines to share their location in Firefox
70010               _timeoutID = setTimeout(error, 10000
70011               /* 10sec */
70012               );
70013               context.container().call(_locating); // get the latest position even if we already have one
70014
70015               navigator.geolocation.getCurrentPosition(success, error, _geolocationOptions);
70016             } else {
70017               _locating.close();
70018
70019               _layer.enabled(null, false);
70020
70021               updateButtonState();
70022             }
70023           }
70024
70025           function zoomTo() {
70026             context.enter(modeBrowse(context));
70027             var map = context.map();
70028
70029             _layer.enabled(_position, true);
70030
70031             updateButtonState();
70032             map.centerZoomEase(_extent.center(), Math.min(20, map.extentZoom(_extent)));
70033           }
70034
70035           function success(geolocation) {
70036             _position = geolocation;
70037             var coords = _position.coords;
70038             _extent = geoExtent([coords.longitude, coords.latitude]).padByMeters(coords.accuracy);
70039             zoomTo();
70040             finish();
70041           }
70042
70043           function error() {
70044             if (_position) {
70045               // use the position from a previous call if we have one
70046               zoomTo();
70047             } else {
70048               context.ui().flash.label(_t.html('geolocate.location_unavailable')).iconName('#iD-icon-geolocate')();
70049             }
70050
70051             finish();
70052           }
70053
70054           function finish() {
70055             _locating.close(); // unblock ui
70056
70057
70058             if (_timeoutID) {
70059               clearTimeout(_timeoutID);
70060             }
70061
70062             _timeoutID = undefined;
70063           }
70064
70065           function updateButtonState() {
70066             _button.classed('active', _layer.enabled());
70067           }
70068
70069           return function (selection) {
70070             if (!navigator.geolocation || !navigator.geolocation.getCurrentPosition) return;
70071             _button = selection.append('button').on('click', click).call(svgIcon('#iD-icon-geolocate', 'light')).call(uiTooltip().placement(_mainLocalizer.textDirection() === 'rtl' ? 'right' : 'left').title(_t.html('geolocate.title')).keys([_t('geolocate.key')]));
70072             context.keybinding().on(_t('geolocate.key'), click);
70073           };
70074         }
70075
70076         function uiPanelBackground(context) {
70077           var background = context.background();
70078           var _currSourceName = null;
70079           var _metadata = {};
70080           var _metadataKeys = ['zoom', 'vintage', 'source', 'description', 'resolution', 'accuracy'];
70081
70082           var debouncedRedraw = debounce(redraw, 250);
70083
70084           function redraw(selection) {
70085             var source = background.baseLayerSource();
70086             if (!source) return;
70087             var isDG = source.id.match(/^DigitalGlobe/i) !== null;
70088             var sourceLabel = source.label();
70089
70090             if (_currSourceName !== sourceLabel) {
70091               _currSourceName = sourceLabel;
70092               _metadata = {};
70093             }
70094
70095             selection.html('');
70096             var list = selection.append('ul').attr('class', 'background-info');
70097             list.append('li').html(_currSourceName);
70098
70099             _metadataKeys.forEach(function (k) {
70100               // DigitalGlobe vintage is available in raster layers for now.
70101               if (isDG && k === 'vintage') return;
70102               list.append('li').attr('class', 'background-info-list-' + k).classed('hide', !_metadata[k]).html(_t.html('info_panels.background.' + k) + ':').append('span').attr('class', 'background-info-span-' + k).html(_metadata[k]);
70103             });
70104
70105             debouncedGetMetadata(selection);
70106             var toggleTiles = context.getDebug('tile') ? 'hide_tiles' : 'show_tiles';
70107             selection.append('a').html(_t.html('info_panels.background.' + toggleTiles)).attr('href', '#').attr('class', 'button button-toggle-tiles').on('click', function (d3_event) {
70108               d3_event.preventDefault();
70109               context.setDebug('tile', !context.getDebug('tile'));
70110               selection.call(redraw);
70111             });
70112
70113             if (isDG) {
70114               var key = source.id + '-vintage';
70115               var sourceVintage = context.background().findSource(key);
70116               var showsVintage = context.background().showsLayer(sourceVintage);
70117               var toggleVintage = showsVintage ? 'hide_vintage' : 'show_vintage';
70118               selection.append('a').html(_t.html('info_panels.background.' + toggleVintage)).attr('href', '#').attr('class', 'button button-toggle-vintage').on('click', function (d3_event) {
70119                 d3_event.preventDefault();
70120                 context.background().toggleOverlayLayer(sourceVintage);
70121                 selection.call(redraw);
70122               });
70123             } // disable if necessary
70124
70125
70126             ['DigitalGlobe-Premium', 'DigitalGlobe-Standard'].forEach(function (layerId) {
70127               if (source.id !== layerId) {
70128                 var key = layerId + '-vintage';
70129                 var sourceVintage = context.background().findSource(key);
70130
70131                 if (context.background().showsLayer(sourceVintage)) {
70132                   context.background().toggleOverlayLayer(sourceVintage);
70133                 }
70134               }
70135             });
70136           }
70137
70138           var debouncedGetMetadata = debounce(getMetadata, 250);
70139
70140           function getMetadata(selection) {
70141             var tile = context.container().select('.layer-background img.tile-center'); // tile near viewport center
70142
70143             if (tile.empty()) return;
70144             var sourceName = _currSourceName;
70145             var d = tile.datum();
70146             var zoom = d && d.length >= 3 && d[2] || Math.floor(context.map().zoom());
70147             var center = context.map().center(); // update zoom
70148
70149             _metadata.zoom = String(zoom);
70150             selection.selectAll('.background-info-list-zoom').classed('hide', false).selectAll('.background-info-span-zoom').html(_metadata.zoom);
70151             if (!d || !d.length >= 3) return;
70152             background.baseLayerSource().getMetadata(center, d, function (err, result) {
70153               if (err || _currSourceName !== sourceName) return; // update vintage
70154
70155               var vintage = result.vintage;
70156               _metadata.vintage = vintage && vintage.range || _t('info_panels.background.unknown');
70157               selection.selectAll('.background-info-list-vintage').classed('hide', false).selectAll('.background-info-span-vintage').html(_metadata.vintage); // update other _metadata
70158
70159               _metadataKeys.forEach(function (k) {
70160                 if (k === 'zoom' || k === 'vintage') return; // done already
70161
70162                 var val = result[k];
70163                 _metadata[k] = val;
70164                 selection.selectAll('.background-info-list-' + k).classed('hide', !val).selectAll('.background-info-span-' + k).html(val);
70165               });
70166             });
70167           }
70168
70169           var panel = function panel(selection) {
70170             selection.call(redraw);
70171             context.map().on('drawn.info-background', function () {
70172               selection.call(debouncedRedraw);
70173             }).on('move.info-background', function () {
70174               selection.call(debouncedGetMetadata);
70175             });
70176           };
70177
70178           panel.off = function () {
70179             context.map().on('drawn.info-background', null).on('move.info-background', null);
70180           };
70181
70182           panel.id = 'background';
70183           panel.label = _t.html('info_panels.background.title');
70184           panel.key = _t('info_panels.background.key');
70185           return panel;
70186         }
70187
70188         function uiPanelHistory(context) {
70189           var osm;
70190
70191           function displayTimestamp(timestamp) {
70192             if (!timestamp) return _t('info_panels.history.unknown');
70193             var options = {
70194               day: 'numeric',
70195               month: 'short',
70196               year: 'numeric',
70197               hour: 'numeric',
70198               minute: 'numeric',
70199               second: 'numeric'
70200             };
70201             var d = new Date(timestamp);
70202             if (isNaN(d.getTime())) return _t('info_panels.history.unknown');
70203             return d.toLocaleString(_mainLocalizer.localeCode(), options);
70204           }
70205
70206           function displayUser(selection, userName) {
70207             if (!userName) {
70208               selection.append('span').html(_t.html('info_panels.history.unknown'));
70209               return;
70210             }
70211
70212             selection.append('span').attr('class', 'user-name').html(userName);
70213             var links = selection.append('div').attr('class', 'links');
70214
70215             if (osm) {
70216               links.append('a').attr('class', 'user-osm-link').attr('href', osm.userURL(userName)).attr('target', '_blank').html('OSM');
70217             }
70218
70219             links.append('a').attr('class', 'user-hdyc-link').attr('href', 'https://hdyc.neis-one.org/?' + userName).attr('target', '_blank').attr('tabindex', -1).html('HDYC');
70220           }
70221
70222           function displayChangeset(selection, changeset) {
70223             if (!changeset) {
70224               selection.append('span').html(_t.html('info_panels.history.unknown'));
70225               return;
70226             }
70227
70228             selection.append('span').attr('class', 'changeset-id').html(changeset);
70229             var links = selection.append('div').attr('class', 'links');
70230
70231             if (osm) {
70232               links.append('a').attr('class', 'changeset-osm-link').attr('href', osm.changesetURL(changeset)).attr('target', '_blank').html('OSM');
70233             }
70234
70235             links.append('a').attr('class', 'changeset-osmcha-link').attr('href', 'https://osmcha.org/changesets/' + changeset).attr('target', '_blank').html('OSMCha');
70236             links.append('a').attr('class', 'changeset-achavi-link').attr('href', 'https://overpass-api.de/achavi/?changeset=' + changeset).attr('target', '_blank').html('Achavi');
70237           }
70238
70239           function redraw(selection) {
70240             var selectedNoteID = context.selectedNoteID();
70241             osm = context.connection();
70242             var selected, note, entity;
70243
70244             if (selectedNoteID && osm) {
70245               // selected 1 note
70246               selected = [_t('note.note') + ' ' + selectedNoteID];
70247               note = osm.getNote(selectedNoteID);
70248             } else {
70249               // selected 1..n entities
70250               selected = context.selectedIDs().filter(function (e) {
70251                 return context.hasEntity(e);
70252               });
70253
70254               if (selected.length) {
70255                 entity = context.entity(selected[0]);
70256               }
70257             }
70258
70259             var singular = selected.length === 1 ? selected[0] : null;
70260             selection.html('');
70261             selection.append('h4').attr('class', 'history-heading').html(singular || _t.html('info_panels.selected', {
70262               n: selected.length
70263             }));
70264             if (!singular) return;
70265
70266             if (entity) {
70267               selection.call(redrawEntity, entity);
70268             } else if (note) {
70269               selection.call(redrawNote, note);
70270             }
70271           }
70272
70273           function redrawNote(selection, note) {
70274             if (!note || note.isNew()) {
70275               selection.append('div').html(_t.html('info_panels.history.note_no_history'));
70276               return;
70277             }
70278
70279             var list = selection.append('ul');
70280             list.append('li').html(_t.html('info_panels.history.note_comments') + ':').append('span').html(note.comments.length);
70281
70282             if (note.comments.length) {
70283               list.append('li').html(_t.html('info_panels.history.note_created_date') + ':').append('span').html(displayTimestamp(note.comments[0].date));
70284               list.append('li').html(_t.html('info_panels.history.note_created_user') + ':').call(displayUser, note.comments[0].user);
70285             }
70286
70287             if (osm) {
70288               selection.append('a').attr('class', 'view-history-on-osm').attr('target', '_blank').attr('href', osm.noteURL(note)).call(svgIcon('#iD-icon-out-link', 'inline')).append('span').html(_t.html('info_panels.history.note_link_text'));
70289             }
70290           }
70291
70292           function redrawEntity(selection, entity) {
70293             if (!entity || entity.isNew()) {
70294               selection.append('div').html(_t.html('info_panels.history.no_history'));
70295               return;
70296             }
70297
70298             var links = selection.append('div').attr('class', 'links');
70299
70300             if (osm) {
70301               links.append('a').attr('class', 'view-history-on-osm').attr('href', osm.historyURL(entity)).attr('target', '_blank').attr('title', _t('info_panels.history.link_text')).html('OSM');
70302             }
70303
70304             links.append('a').attr('class', 'pewu-history-viewer-link').attr('href', 'https://pewu.github.io/osm-history/#/' + entity.type + '/' + entity.osmId()).attr('target', '_blank').attr('tabindex', -1).html('PeWu');
70305             var list = selection.append('ul');
70306             list.append('li').html(_t.html('info_panels.history.version') + ':').append('span').html(entity.version);
70307             list.append('li').html(_t.html('info_panels.history.last_edit') + ':').append('span').html(displayTimestamp(entity.timestamp));
70308             list.append('li').html(_t.html('info_panels.history.edited_by') + ':').call(displayUser, entity.user);
70309             list.append('li').html(_t.html('info_panels.history.changeset') + ':').call(displayChangeset, entity.changeset);
70310           }
70311
70312           var panel = function panel(selection) {
70313             selection.call(redraw);
70314             context.map().on('drawn.info-history', function () {
70315               selection.call(redraw);
70316             });
70317             context.on('enter.info-history', function () {
70318               selection.call(redraw);
70319             });
70320           };
70321
70322           panel.off = function () {
70323             context.map().on('drawn.info-history', null);
70324             context.on('enter.info-history', null);
70325           };
70326
70327           panel.id = 'history';
70328           panel.label = _t.html('info_panels.history.title');
70329           panel.key = _t('info_panels.history.key');
70330           return panel;
70331         }
70332
70333         var OSM_PRECISION = 7;
70334         /**
70335          * Returns a localized representation of the given length measurement.
70336          *
70337          * @param {Number} m area in meters
70338          * @param {Boolean} isImperial true for U.S. customary units; false for metric
70339          */
70340
70341         function displayLength(m, isImperial) {
70342           var d = m * (isImperial ? 3.28084 : 1);
70343           var unit;
70344
70345           if (isImperial) {
70346             if (d >= 5280) {
70347               d /= 5280;
70348               unit = 'miles';
70349             } else {
70350               unit = 'feet';
70351             }
70352           } else {
70353             if (d >= 1000) {
70354               d /= 1000;
70355               unit = 'kilometers';
70356             } else {
70357               unit = 'meters';
70358             }
70359           }
70360
70361           return _t('units.' + unit, {
70362             quantity: d.toLocaleString(_mainLocalizer.localeCode(), {
70363               maximumSignificantDigits: 4
70364             })
70365           });
70366         }
70367         /**
70368          * Returns a localized representation of the given area measurement.
70369          *
70370          * @param {Number} m2 area in square meters
70371          * @param {Boolean} isImperial true for U.S. customary units; false for metric
70372          */
70373
70374         function displayArea(m2, isImperial) {
70375           var locale = _mainLocalizer.localeCode();
70376           var d = m2 * (isImperial ? 10.7639111056 : 1);
70377           var d1, d2, area;
70378           var unit1 = '';
70379           var unit2 = '';
70380
70381           if (isImperial) {
70382             if (d >= 6969600) {
70383               // > 0.25mi² show mi²
70384               d1 = d / 27878400;
70385               unit1 = 'square_miles';
70386             } else {
70387               d1 = d;
70388               unit1 = 'square_feet';
70389             }
70390
70391             if (d > 4356 && d < 43560000) {
70392               // 0.1 - 1000 acres
70393               d2 = d / 43560;
70394               unit2 = 'acres';
70395             }
70396           } else {
70397             if (d >= 250000) {
70398               // > 0.25km² show km²
70399               d1 = d / 1000000;
70400               unit1 = 'square_kilometers';
70401             } else {
70402               d1 = d;
70403               unit1 = 'square_meters';
70404             }
70405
70406             if (d > 1000 && d < 10000000) {
70407               // 0.1 - 1000 hectares
70408               d2 = d / 10000;
70409               unit2 = 'hectares';
70410             }
70411           }
70412
70413           area = _t('units.' + unit1, {
70414             quantity: d1.toLocaleString(locale, {
70415               maximumSignificantDigits: 4
70416             })
70417           });
70418
70419           if (d2) {
70420             return _t('units.area_pair', {
70421               area1: area,
70422               area2: _t('units.' + unit2, {
70423                 quantity: d2.toLocaleString(locale, {
70424                   maximumSignificantDigits: 2
70425                 })
70426               })
70427             });
70428           } else {
70429             return area;
70430           }
70431         }
70432
70433         function wrap(x, min, max) {
70434           var d = max - min;
70435           return ((x - min) % d + d) % d + min;
70436         }
70437
70438         function clamp(x, min, max) {
70439           return Math.max(min, Math.min(x, max));
70440         }
70441
70442         function displayCoordinate(deg, pos, neg) {
70443           var locale = _mainLocalizer.localeCode();
70444           var min = (Math.abs(deg) - Math.floor(Math.abs(deg))) * 60;
70445           var sec = (min - Math.floor(min)) * 60;
70446           var displayDegrees = _t('units.arcdegrees', {
70447             quantity: Math.floor(Math.abs(deg)).toLocaleString(locale)
70448           });
70449           var displayCoordinate;
70450
70451           if (Math.floor(sec) > 0) {
70452             displayCoordinate = displayDegrees + _t('units.arcminutes', {
70453               quantity: Math.floor(min).toLocaleString(locale)
70454             }) + _t('units.arcseconds', {
70455               quantity: Math.round(sec).toLocaleString(locale)
70456             });
70457           } else if (Math.floor(min) > 0) {
70458             displayCoordinate = displayDegrees + _t('units.arcminutes', {
70459               quantity: Math.round(min).toLocaleString(locale)
70460             });
70461           } else {
70462             displayCoordinate = _t('units.arcdegrees', {
70463               quantity: Math.round(Math.abs(deg)).toLocaleString(locale)
70464             });
70465           }
70466
70467           if (deg === 0) {
70468             return displayCoordinate;
70469           } else {
70470             return _t('units.coordinate', {
70471               coordinate: displayCoordinate,
70472               direction: _t('units.' + (deg > 0 ? pos : neg))
70473             });
70474           }
70475         }
70476         /**
70477          * Returns given coordinate pair in degree-minute-second format.
70478          *
70479          * @param {Array<Number>} coord longitude and latitude
70480          */
70481
70482
70483         function dmsCoordinatePair(coord) {
70484           return _t('units.coordinate_pair', {
70485             latitude: displayCoordinate(clamp(coord[1], -90, 90), 'north', 'south'),
70486             longitude: displayCoordinate(wrap(coord[0], -180, 180), 'east', 'west')
70487           });
70488         }
70489         /**
70490          * Returns the given coordinate pair in decimal format.
70491          * note: unlocalized to avoid comma ambiguity - see #4765
70492          *
70493          * @param {Array<Number>} coord longitude and latitude
70494          */
70495
70496         function decimalCoordinatePair(coord) {
70497           return _t('units.coordinate_pair', {
70498             latitude: clamp(coord[1], -90, 90).toFixed(OSM_PRECISION),
70499             longitude: wrap(coord[0], -180, 180).toFixed(OSM_PRECISION)
70500           });
70501         }
70502
70503         function uiPanelLocation(context) {
70504           var currLocation = '';
70505
70506           function redraw(selection) {
70507             selection.html('');
70508             var list = selection.append('ul'); // Mouse coordinates
70509
70510             var coord = context.map().mouseCoordinates();
70511
70512             if (coord.some(isNaN)) {
70513               coord = context.map().center();
70514             }
70515
70516             list.append('li').html(dmsCoordinatePair(coord)).append('li').html(decimalCoordinatePair(coord)); // Location Info
70517
70518             selection.append('div').attr('class', 'location-info').html(currLocation || ' ');
70519             debouncedGetLocation(selection, coord);
70520           }
70521
70522           var debouncedGetLocation = debounce(getLocation, 250);
70523
70524           function getLocation(selection, coord) {
70525             if (!services.geocoder) {
70526               currLocation = _t('info_panels.location.unknown_location');
70527               selection.selectAll('.location-info').html(currLocation);
70528             } else {
70529               services.geocoder.reverse(coord, function (err, result) {
70530                 currLocation = result ? result.display_name : _t('info_panels.location.unknown_location');
70531                 selection.selectAll('.location-info').html(currLocation);
70532               });
70533             }
70534           }
70535
70536           var panel = function panel(selection) {
70537             selection.call(redraw);
70538             context.surface().on(('PointerEvent' in window ? 'pointer' : 'mouse') + 'move.info-location', function () {
70539               selection.call(redraw);
70540             });
70541           };
70542
70543           panel.off = function () {
70544             context.surface().on('.info-location', null);
70545           };
70546
70547           panel.id = 'location';
70548           panel.label = _t.html('info_panels.location.title');
70549           panel.key = _t('info_panels.location.key');
70550           return panel;
70551         }
70552
70553         function uiPanelMeasurement(context) {
70554           function radiansToMeters(r) {
70555             // using WGS84 authalic radius (6371007.1809 m)
70556             return r * 6371007.1809;
70557           }
70558
70559           function steradiansToSqmeters(r) {
70560             // http://gis.stackexchange.com/a/124857/40446
70561             return r / (4 * Math.PI) * 510065621724000;
70562           }
70563
70564           function toLineString(feature) {
70565             if (feature.type === 'LineString') return feature;
70566             var result = {
70567               type: 'LineString',
70568               coordinates: []
70569             };
70570
70571             if (feature.type === 'Polygon') {
70572               result.coordinates = feature.coordinates[0];
70573             } else if (feature.type === 'MultiPolygon') {
70574               result.coordinates = feature.coordinates[0][0];
70575             }
70576
70577             return result;
70578           }
70579
70580           var _isImperial = !_mainLocalizer.usesMetric();
70581
70582           function redraw(selection) {
70583             var graph = context.graph();
70584             var selectedNoteID = context.selectedNoteID();
70585             var osm = services.osm;
70586             var localeCode = _mainLocalizer.localeCode();
70587             var heading;
70588             var center, location, centroid;
70589             var closed, geometry;
70590             var totalNodeCount,
70591                 length = 0,
70592                 area = 0,
70593                 distance;
70594
70595             if (selectedNoteID && osm) {
70596               // selected 1 note
70597               var note = osm.getNote(selectedNoteID);
70598               heading = _t('note.note') + ' ' + selectedNoteID;
70599               location = note.loc;
70600               geometry = 'note';
70601             } else {
70602               // selected 1..n entities
70603               var selectedIDs = context.selectedIDs().filter(function (id) {
70604                 return context.hasEntity(id);
70605               });
70606               var selected = selectedIDs.map(function (id) {
70607                 return context.entity(id);
70608               });
70609               heading = selected.length === 1 ? selected[0].id : _t('info_panels.selected', {
70610                 n: selected.length
70611               });
70612
70613               if (selected.length) {
70614                 var extent = geoExtent();
70615
70616                 for (var i in selected) {
70617                   var entity = selected[i];
70618
70619                   extent._extend(entity.extent(graph));
70620
70621                   geometry = entity.geometry(graph);
70622
70623                   if (geometry === 'line' || geometry === 'area') {
70624                     closed = entity.type === 'relation' || entity.isClosed() && !entity.isDegenerate();
70625                     var feature = entity.asGeoJSON(graph);
70626                     length += radiansToMeters(d3_geoLength(toLineString(feature)));
70627                     centroid = d3_geoPath(context.projection).centroid(entity.asGeoJSON(graph));
70628                     centroid = centroid && context.projection.invert(centroid);
70629
70630                     if (!centroid || !isFinite(centroid[0]) || !isFinite(centroid[1])) {
70631                       centroid = entity.extent(graph).center();
70632                     }
70633
70634                     if (closed) {
70635                       area += steradiansToSqmeters(entity.area(graph));
70636                     }
70637                   }
70638                 }
70639
70640                 if (selected.length > 1) {
70641                   geometry = null;
70642                   closed = null;
70643                   centroid = null;
70644                 }
70645
70646                 if (selected.length === 2 && selected[0].type === 'node' && selected[1].type === 'node') {
70647                   distance = geoSphericalDistance(selected[0].loc, selected[1].loc);
70648                 }
70649
70650                 if (selected.length === 1 && selected[0].type === 'node') {
70651                   location = selected[0].loc;
70652                 } else {
70653                   totalNodeCount = utilGetAllNodes(selectedIDs, context.graph()).length;
70654                 }
70655
70656                 if (!location && !centroid) {
70657                   center = extent.center();
70658                 }
70659               }
70660             }
70661
70662             selection.html('');
70663
70664             if (heading) {
70665               selection.append('h4').attr('class', 'measurement-heading').html(heading);
70666             }
70667
70668             var list = selection.append('ul');
70669             var coordItem;
70670
70671             if (geometry) {
70672               list.append('li').html(_t.html('info_panels.measurement.geometry') + ':').append('span').html(closed ? _t('info_panels.measurement.closed_' + geometry) : _t('geometry.' + geometry));
70673             }
70674
70675             if (totalNodeCount) {
70676               list.append('li').html(_t.html('info_panels.measurement.node_count') + ':').append('span').html(totalNodeCount.toLocaleString(localeCode));
70677             }
70678
70679             if (area) {
70680               list.append('li').html(_t.html('info_panels.measurement.area') + ':').append('span').html(displayArea(area, _isImperial));
70681             }
70682
70683             if (length) {
70684               list.append('li').html(_t.html('info_panels.measurement.' + (closed ? 'perimeter' : 'length')) + ':').append('span').html(displayLength(length, _isImperial));
70685             }
70686
70687             if (typeof distance === 'number') {
70688               list.append('li').html(_t.html('info_panels.measurement.distance') + ':').append('span').html(displayLength(distance, _isImperial));
70689             }
70690
70691             if (location) {
70692               coordItem = list.append('li').html(_t.html('info_panels.measurement.location') + ':');
70693               coordItem.append('span').html(dmsCoordinatePair(location));
70694               coordItem.append('span').html(decimalCoordinatePair(location));
70695             }
70696
70697             if (centroid) {
70698               coordItem = list.append('li').html(_t.html('info_panels.measurement.centroid') + ':');
70699               coordItem.append('span').html(dmsCoordinatePair(centroid));
70700               coordItem.append('span').html(decimalCoordinatePair(centroid));
70701             }
70702
70703             if (center) {
70704               coordItem = list.append('li').html(_t.html('info_panels.measurement.center') + ':');
70705               coordItem.append('span').html(dmsCoordinatePair(center));
70706               coordItem.append('span').html(decimalCoordinatePair(center));
70707             }
70708
70709             if (length || area || typeof distance === 'number') {
70710               var toggle = _isImperial ? 'imperial' : 'metric';
70711               selection.append('a').html(_t.html('info_panels.measurement.' + toggle)).attr('href', '#').attr('class', 'button button-toggle-units').on('click', function (d3_event) {
70712                 d3_event.preventDefault();
70713                 _isImperial = !_isImperial;
70714                 selection.call(redraw);
70715               });
70716             }
70717           }
70718
70719           var panel = function panel(selection) {
70720             selection.call(redraw);
70721             context.map().on('drawn.info-measurement', function () {
70722               selection.call(redraw);
70723             });
70724             context.on('enter.info-measurement', function () {
70725               selection.call(redraw);
70726             });
70727           };
70728
70729           panel.off = function () {
70730             context.map().on('drawn.info-measurement', null);
70731             context.on('enter.info-measurement', null);
70732           };
70733
70734           panel.id = 'measurement';
70735           panel.label = _t.html('info_panels.measurement.title');
70736           panel.key = _t('info_panels.measurement.key');
70737           return panel;
70738         }
70739
70740         var uiInfoPanels = {
70741           background: uiPanelBackground,
70742           history: uiPanelHistory,
70743           location: uiPanelLocation,
70744           measurement: uiPanelMeasurement
70745         };
70746
70747         function uiInfo(context) {
70748           var ids = Object.keys(uiInfoPanels);
70749           var wasActive = ['measurement'];
70750           var panels = {};
70751           var active = {}; // create panels
70752
70753           ids.forEach(function (k) {
70754             if (!panels[k]) {
70755               panels[k] = uiInfoPanels[k](context);
70756               active[k] = false;
70757             }
70758           });
70759
70760           function info(selection) {
70761             function redraw() {
70762               var activeids = ids.filter(function (k) {
70763                 return active[k];
70764               }).sort();
70765               var containers = infoPanels.selectAll('.panel-container').data(activeids, function (k) {
70766                 return k;
70767               });
70768               containers.exit().style('opacity', 1).transition().duration(200).style('opacity', 0).on('end', function (d) {
70769                 select(this).call(panels[d].off).remove();
70770               });
70771               var enter = containers.enter().append('div').attr('class', function (d) {
70772                 return 'fillD2 panel-container panel-container-' + d;
70773               });
70774               enter.style('opacity', 0).transition().duration(200).style('opacity', 1);
70775               var title = enter.append('div').attr('class', 'panel-title fillD2');
70776               title.append('h3').html(function (d) {
70777                 return panels[d].label;
70778               });
70779               title.append('button').attr('class', 'close').on('click', function (d3_event, d) {
70780                 d3_event.stopImmediatePropagation();
70781                 d3_event.preventDefault();
70782                 info.toggle(d);
70783               }).call(svgIcon('#iD-icon-close'));
70784               enter.append('div').attr('class', function (d) {
70785                 return 'panel-content panel-content-' + d;
70786               }); // redraw the panels
70787
70788               infoPanels.selectAll('.panel-content').each(function (d) {
70789                 select(this).call(panels[d]);
70790               });
70791             }
70792
70793             info.toggle = function (which) {
70794               var activeids = ids.filter(function (k) {
70795                 return active[k];
70796               });
70797
70798               if (which) {
70799                 // toggle one
70800                 active[which] = !active[which];
70801
70802                 if (activeids.length === 1 && activeids[0] === which) {
70803                   // none active anymore
70804                   wasActive = [which];
70805                 }
70806
70807                 context.container().select('.' + which + '-panel-toggle-item').classed('active', active[which]).select('input').property('checked', active[which]);
70808               } else {
70809                 // toggle all
70810                 if (activeids.length) {
70811                   wasActive = activeids;
70812                   activeids.forEach(function (k) {
70813                     active[k] = false;
70814                   });
70815                 } else {
70816                   wasActive.forEach(function (k) {
70817                     active[k] = true;
70818                   });
70819                 }
70820               }
70821
70822               redraw();
70823             };
70824
70825             var infoPanels = selection.selectAll('.info-panels').data([0]);
70826             infoPanels = infoPanels.enter().append('div').attr('class', 'info-panels').merge(infoPanels);
70827             redraw();
70828             context.keybinding().on(uiCmd('⌘' + _t('info_panels.key')), function (d3_event) {
70829               d3_event.stopImmediatePropagation();
70830               d3_event.preventDefault();
70831               info.toggle();
70832             });
70833             ids.forEach(function (k) {
70834               var key = _t('info_panels.' + k + '.key', {
70835                 "default": null
70836               });
70837               if (!key) return;
70838               context.keybinding().on(uiCmd('⌘⇧' + key), function (d3_event) {
70839                 d3_event.stopImmediatePropagation();
70840                 d3_event.preventDefault();
70841                 info.toggle(k);
70842               });
70843             });
70844           }
70845
70846           return info;
70847         }
70848
70849         function pointBox(loc, context) {
70850           var rect = context.surfaceRect();
70851           var point = context.curtainProjection(loc);
70852           return {
70853             left: point[0] + rect.left - 40,
70854             top: point[1] + rect.top - 60,
70855             width: 80,
70856             height: 90
70857           };
70858         }
70859         function pad(locOrBox, padding, context) {
70860           var box;
70861
70862           if (locOrBox instanceof Array) {
70863             var rect = context.surfaceRect();
70864             var point = context.curtainProjection(locOrBox);
70865             box = {
70866               left: point[0] + rect.left,
70867               top: point[1] + rect.top
70868             };
70869           } else {
70870             box = locOrBox;
70871           }
70872
70873           return {
70874             left: box.left - padding,
70875             top: box.top - padding,
70876             width: (box.width || 0) + 2 * padding,
70877             height: (box.width || 0) + 2 * padding
70878           };
70879         }
70880         function icon(name, svgklass, useklass) {
70881           return '<svg class="icon ' + (svgklass || '') + '">' + '<use xlink:href="' + name + '"' + (useklass ? ' class="' + useklass + '"' : '') + '></use></svg>';
70882         }
70883         var helpStringReplacements; // Returns the localized HTML element for `id` with a standardized set of icon, key, and
70884         // label replacements suitable for tutorials and documentation. Optionally supplemented
70885         // with custom `replacements`
70886
70887         function helpHtml(id, replacements) {
70888           // only load these the first time
70889           if (!helpStringReplacements) {
70890             helpStringReplacements = {
70891               // insert icons corresponding to various UI elements
70892               point_icon: icon('#iD-icon-point', 'inline'),
70893               line_icon: icon('#iD-icon-line', 'inline'),
70894               area_icon: icon('#iD-icon-area', 'inline'),
70895               note_icon: icon('#iD-icon-note', 'inline add-note'),
70896               plus: icon('#iD-icon-plus', 'inline'),
70897               minus: icon('#iD-icon-minus', 'inline'),
70898               layers_icon: icon('#iD-icon-layers', 'inline'),
70899               data_icon: icon('#iD-icon-data', 'inline'),
70900               inspect: icon('#iD-icon-inspect', 'inline'),
70901               help_icon: icon('#iD-icon-help', 'inline'),
70902               undo_icon: icon(_mainLocalizer.textDirection() === 'rtl' ? '#iD-icon-redo' : '#iD-icon-undo', 'inline'),
70903               redo_icon: icon(_mainLocalizer.textDirection() === 'rtl' ? '#iD-icon-undo' : '#iD-icon-redo', 'inline'),
70904               save_icon: icon('#iD-icon-save', 'inline'),
70905               // operation icons
70906               circularize_icon: icon('#iD-operation-circularize', 'inline operation'),
70907               continue_icon: icon('#iD-operation-continue', 'inline operation'),
70908               copy_icon: icon('#iD-operation-copy', 'inline operation'),
70909               delete_icon: icon('#iD-operation-delete', 'inline operation'),
70910               disconnect_icon: icon('#iD-operation-disconnect', 'inline operation'),
70911               downgrade_icon: icon('#iD-operation-downgrade', 'inline operation'),
70912               extract_icon: icon('#iD-operation-extract', 'inline operation'),
70913               merge_icon: icon('#iD-operation-merge', 'inline operation'),
70914               move_icon: icon('#iD-operation-move', 'inline operation'),
70915               orthogonalize_icon: icon('#iD-operation-orthogonalize', 'inline operation'),
70916               paste_icon: icon('#iD-operation-paste', 'inline operation'),
70917               reflect_long_icon: icon('#iD-operation-reflect-long', 'inline operation'),
70918               reflect_short_icon: icon('#iD-operation-reflect-short', 'inline operation'),
70919               reverse_icon: icon('#iD-operation-reverse', 'inline operation'),
70920               rotate_icon: icon('#iD-operation-rotate', 'inline operation'),
70921               split_icon: icon('#iD-operation-split', 'inline operation'),
70922               straighten_icon: icon('#iD-operation-straighten', 'inline operation'),
70923               // interaction icons
70924               leftclick: icon('#iD-walkthrough-mouse-left', 'inline operation'),
70925               rightclick: icon('#iD-walkthrough-mouse-right', 'inline operation'),
70926               mousewheel_icon: icon('#iD-walkthrough-mousewheel', 'inline operation'),
70927               tap_icon: icon('#iD-walkthrough-tap', 'inline operation'),
70928               doubletap_icon: icon('#iD-walkthrough-doubletap', 'inline operation'),
70929               longpress_icon: icon('#iD-walkthrough-longpress', 'inline operation'),
70930               touchdrag_icon: icon('#iD-walkthrough-touchdrag', 'inline operation'),
70931               pinch_icon: icon('#iD-walkthrough-pinch-apart', 'inline operation'),
70932               // insert keys; may be localized and platform-dependent
70933               shift: uiCmd.display('⇧'),
70934               alt: uiCmd.display('⌥'),
70935               "return": uiCmd.display('↵'),
70936               esc: _t.html('shortcuts.key.esc'),
70937               space: _t.html('shortcuts.key.space'),
70938               add_note_key: _t.html('modes.add_note.key'),
70939               help_key: _t.html('help.key'),
70940               shortcuts_key: _t.html('shortcuts.toggle.key'),
70941               // reference localized UI labels directly so that they'll always match
70942               save: _t.html('save.title'),
70943               undo: _t.html('undo.title'),
70944               redo: _t.html('redo.title'),
70945               upload: _t.html('commit.save'),
70946               point: _t.html('modes.add_point.title'),
70947               line: _t.html('modes.add_line.title'),
70948               area: _t.html('modes.add_area.title'),
70949               note: _t.html('modes.add_note.label'),
70950               circularize: _t.html('operations.circularize.title'),
70951               "continue": _t.html('operations.continue.title'),
70952               copy: _t.html('operations.copy.title'),
70953               "delete": _t.html('operations.delete.title'),
70954               disconnect: _t.html('operations.disconnect.title'),
70955               downgrade: _t.html('operations.downgrade.title'),
70956               extract: _t.html('operations.extract.title'),
70957               merge: _t.html('operations.merge.title'),
70958               move: _t.html('operations.move.title'),
70959               orthogonalize: _t.html('operations.orthogonalize.title'),
70960               paste: _t.html('operations.paste.title'),
70961               reflect_long: _t.html('operations.reflect.title.long'),
70962               reflect_short: _t.html('operations.reflect.title.short'),
70963               reverse: _t.html('operations.reverse.title'),
70964               rotate: _t.html('operations.rotate.title'),
70965               split: _t.html('operations.split.title'),
70966               straighten: _t.html('operations.straighten.title'),
70967               map_data: _t.html('map_data.title'),
70968               osm_notes: _t.html('map_data.layers.notes.title'),
70969               fields: _t.html('inspector.fields'),
70970               tags: _t.html('inspector.tags'),
70971               relations: _t.html('inspector.relations'),
70972               new_relation: _t.html('inspector.new_relation'),
70973               turn_restrictions: _t.html('_tagging.presets.fields.restrictions.label'),
70974               background_settings: _t.html('background.description'),
70975               imagery_offset: _t.html('background.fix_misalignment'),
70976               start_the_walkthrough: _t.html('splash.walkthrough'),
70977               help: _t.html('help.title'),
70978               ok: _t.html('intro.ok')
70979             };
70980           }
70981
70982           var reps;
70983
70984           if (replacements) {
70985             reps = Object.assign(replacements, helpStringReplacements);
70986           } else {
70987             reps = helpStringReplacements;
70988           }
70989
70990           return _t.html(id, reps) // use keyboard key styling for shortcuts
70991           .replace(/\`(.*?)\`/g, '<kbd>$1</kbd>');
70992         }
70993
70994         function slugify(text) {
70995           return text.toString().toLowerCase().replace(/\s+/g, '-') // Replace spaces with -
70996           .replace(/[^\w\-]+/g, '') // Remove all non-word chars
70997           .replace(/\-\-+/g, '-') // Replace multiple - with single -
70998           .replace(/^-+/, '') // Trim - from start of text
70999           .replace(/-+$/, ''); // Trim - from end of text
71000         } // console warning for missing walkthrough names
71001
71002
71003         var missingStrings = {};
71004
71005         function checkKey(key, text) {
71006           if (_t(key, {
71007             "default": undefined
71008           }) === undefined) {
71009             if (missingStrings.hasOwnProperty(key)) return; // warn once
71010
71011             missingStrings[key] = text;
71012             var missing = key + ': ' + text;
71013             if (typeof console !== 'undefined') console.log(missing); // eslint-disable-line
71014           }
71015         }
71016
71017         function localize(obj) {
71018           var key; // Assign name if entity has one..
71019
71020           var name = obj.tags && obj.tags.name;
71021
71022           if (name) {
71023             key = 'intro.graph.name.' + slugify(name);
71024             obj.tags.name = _t(key, {
71025               "default": name
71026             });
71027             checkKey(key, name);
71028           } // Assign street name if entity has one..
71029
71030
71031           var street = obj.tags && obj.tags['addr:street'];
71032
71033           if (street) {
71034             key = 'intro.graph.name.' + slugify(street);
71035             obj.tags['addr:street'] = _t(key, {
71036               "default": street
71037             });
71038             checkKey(key, street); // Add address details common across walkthrough..
71039
71040             var addrTags = ['block_number', 'city', 'county', 'district', 'hamlet', 'neighbourhood', 'postcode', 'province', 'quarter', 'state', 'subdistrict', 'suburb'];
71041             addrTags.forEach(function (k) {
71042               var key = 'intro.graph.' + k;
71043               var tag = 'addr:' + k;
71044               var val = obj.tags && obj.tags[tag];
71045               var str = _t(key, {
71046                 "default": val
71047               });
71048
71049               if (str) {
71050                 if (str.match(/^<.*>$/) !== null) {
71051                   delete obj.tags[tag];
71052                 } else {
71053                   obj.tags[tag] = str;
71054                 }
71055               }
71056             });
71057           }
71058
71059           return obj;
71060         } // Used to detect squareness.. some duplicataion of code from actionOrthogonalize.
71061
71062         function isMostlySquare(points) {
71063           // note: uses 15 here instead of the 12 from actionOrthogonalize because
71064           // actionOrthogonalize can actually straighten some larger angles as it iterates
71065           var threshold = 15; // degrees within right or straight
71066
71067           var lowerBound = Math.cos((90 - threshold) * Math.PI / 180); // near right
71068
71069           var upperBound = Math.cos(threshold * Math.PI / 180); // near straight
71070
71071           for (var i = 0; i < points.length; i++) {
71072             var a = points[(i - 1 + points.length) % points.length];
71073             var origin = points[i];
71074             var b = points[(i + 1) % points.length];
71075             var dotp = geoVecNormalizedDot(a, b, origin);
71076             var mag = Math.abs(dotp);
71077
71078             if (mag > lowerBound && mag < upperBound) {
71079               return false;
71080             }
71081           }
71082
71083           return true;
71084         }
71085         function selectMenuItem(context, operation) {
71086           return context.container().select('.edit-menu .edit-menu-item-' + operation);
71087         }
71088         function transitionTime(point1, point2) {
71089           var distance = geoSphericalDistance(point1, point2);
71090
71091           if (distance === 0) {
71092             return 0;
71093           } else if (distance < 80) {
71094             return 500;
71095           } else {
71096             return 1000;
71097           }
71098         }
71099
71100         // hide class, which sets display=none, and a d3 transition for opacity.
71101         // this will cause blinking when called repeatedly, so check that the
71102         // value actually changes between calls.
71103
71104         function uiToggle(show, callback) {
71105           return function (selection) {
71106             selection.style('opacity', show ? 0 : 1).classed('hide', false).transition().style('opacity', show ? 1 : 0).on('end', function () {
71107               select(this).classed('hide', !show).style('opacity', null);
71108               if (callback) callback.apply(this);
71109             });
71110           };
71111         }
71112
71113         function uiCurtain(containerNode) {
71114           var surface = select(null),
71115               tooltip = select(null),
71116               darkness = select(null);
71117
71118           function curtain(selection) {
71119             surface = selection.append('svg').attr('class', 'curtain').style('top', 0).style('left', 0);
71120             darkness = surface.append('path').attr('x', 0).attr('y', 0).attr('class', 'curtain-darkness');
71121             select(window).on('resize.curtain', resize);
71122             tooltip = selection.append('div').attr('class', 'tooltip');
71123             tooltip.append('div').attr('class', 'popover-arrow');
71124             tooltip.append('div').attr('class', 'popover-inner');
71125             resize();
71126
71127             function resize() {
71128               surface.attr('width', containerNode.clientWidth).attr('height', containerNode.clientHeight);
71129               curtain.cut(darkness.datum());
71130             }
71131           }
71132           /**
71133            * Reveal cuts the curtain to highlight the given box,
71134            * and shows a tooltip with instructions next to the box.
71135            *
71136            * @param  {String|ClientRect} [box]   box used to cut the curtain
71137            * @param  {String}    [text]          text for a tooltip
71138            * @param  {Object}    [options]
71139            * @param  {string}    [options.tooltipClass]    optional class to add to the tooltip
71140            * @param  {integer}   [options.duration]        transition time in milliseconds
71141            * @param  {string}    [options.buttonText]      if set, create a button with this text label
71142            * @param  {function}  [options.buttonCallback]  if set, the callback for the button
71143            * @param  {function}  [options.padding]         extra margin in px to put around bbox
71144            * @param  {String|ClientRect} [options.tooltipBox]  box for tooltip position, if different from box for the curtain
71145            */
71146
71147
71148           curtain.reveal = function (box, html, options) {
71149             options = options || {};
71150
71151             if (typeof box === 'string') {
71152               box = select(box).node();
71153             }
71154
71155             if (box && box.getBoundingClientRect) {
71156               box = copyBox(box.getBoundingClientRect());
71157               var containerRect = containerNode.getBoundingClientRect();
71158               box.top -= containerRect.top;
71159               box.left -= containerRect.left;
71160             }
71161
71162             if (box && options.padding) {
71163               box.top -= options.padding;
71164               box.left -= options.padding;
71165               box.bottom += options.padding;
71166               box.right += options.padding;
71167               box.height += options.padding * 2;
71168               box.width += options.padding * 2;
71169             }
71170
71171             var tooltipBox;
71172
71173             if (options.tooltipBox) {
71174               tooltipBox = options.tooltipBox;
71175
71176               if (typeof tooltipBox === 'string') {
71177                 tooltipBox = select(tooltipBox).node();
71178               }
71179
71180               if (tooltipBox && tooltipBox.getBoundingClientRect) {
71181                 tooltipBox = copyBox(tooltipBox.getBoundingClientRect());
71182               }
71183             } else {
71184               tooltipBox = box;
71185             }
71186
71187             if (tooltipBox && html) {
71188               if (html.indexOf('**') !== -1) {
71189                 if (html.indexOf('<span') === 0) {
71190                   html = html.replace(/^(<span.*?>)(.+?)(\*\*)/, '$1<span>$2</span>$3');
71191                 } else {
71192                   html = html.replace(/^(.+?)(\*\*)/, '<span>$1</span>$2');
71193                 } // pseudo markdown bold text for the instruction section..
71194
71195
71196                 html = html.replace(/\*\*(.*?)\*\*/g, '<span class="instruction">$1</span>');
71197               }
71198
71199               html = html.replace(/\*(.*?)\*/g, '<em>$1</em>'); // emphasis
71200
71201               html = html.replace(/\{br\}/g, '<br/><br/>'); // linebreak
71202
71203               if (options.buttonText && options.buttonCallback) {
71204                 html += '<div class="button-section">' + '<button href="#" class="button action">' + options.buttonText + '</button></div>';
71205               }
71206
71207               var classes = 'curtain-tooltip popover tooltip arrowed in ' + (options.tooltipClass || '');
71208               tooltip.classed(classes, true).selectAll('.popover-inner').html(html);
71209
71210               if (options.buttonText && options.buttonCallback) {
71211                 var button = tooltip.selectAll('.button-section .button.action');
71212                 button.on('click', function (d3_event) {
71213                   d3_event.preventDefault();
71214                   options.buttonCallback();
71215                 });
71216               }
71217
71218               var tip = copyBox(tooltip.node().getBoundingClientRect()),
71219                   w = containerNode.clientWidth,
71220                   h = containerNode.clientHeight,
71221                   tooltipWidth = 200,
71222                   tooltipArrow = 5,
71223                   side,
71224                   pos; // hack: this will have bottom placement,
71225               // so need to reserve extra space for the tooltip illustration.
71226
71227               if (options.tooltipClass === 'intro-mouse') {
71228                 tip.height += 80;
71229               } // trim box dimensions to just the portion that fits in the container..
71230
71231
71232               if (tooltipBox.top + tooltipBox.height > h) {
71233                 tooltipBox.height -= tooltipBox.top + tooltipBox.height - h;
71234               }
71235
71236               if (tooltipBox.left + tooltipBox.width > w) {
71237                 tooltipBox.width -= tooltipBox.left + tooltipBox.width - w;
71238               } // determine tooltip placement..
71239
71240
71241               if (tooltipBox.top + tooltipBox.height < 100) {
71242                 // tooltip below box..
71243                 side = 'bottom';
71244                 pos = [tooltipBox.left + tooltipBox.width / 2 - tip.width / 2, tooltipBox.top + tooltipBox.height];
71245               } else if (tooltipBox.top > h - 140) {
71246                 // tooltip above box..
71247                 side = 'top';
71248                 pos = [tooltipBox.left + tooltipBox.width / 2 - tip.width / 2, tooltipBox.top - tip.height];
71249               } else {
71250                 // tooltip to the side of the tooltipBox..
71251                 var tipY = tooltipBox.top + tooltipBox.height / 2 - tip.height / 2;
71252
71253                 if (_mainLocalizer.textDirection() === 'rtl') {
71254                   if (tooltipBox.left - tooltipWidth - tooltipArrow < 70) {
71255                     side = 'right';
71256                     pos = [tooltipBox.left + tooltipBox.width + tooltipArrow, tipY];
71257                   } else {
71258                     side = 'left';
71259                     pos = [tooltipBox.left - tooltipWidth - tooltipArrow, tipY];
71260                   }
71261                 } else {
71262                   if (tooltipBox.left + tooltipBox.width + tooltipArrow + tooltipWidth > w - 70) {
71263                     side = 'left';
71264                     pos = [tooltipBox.left - tooltipWidth - tooltipArrow, tipY];
71265                   } else {
71266                     side = 'right';
71267                     pos = [tooltipBox.left + tooltipBox.width + tooltipArrow, tipY];
71268                   }
71269                 }
71270               }
71271
71272               if (options.duration !== 0 || !tooltip.classed(side)) {
71273                 tooltip.call(uiToggle(true));
71274               }
71275
71276               tooltip.style('top', pos[1] + 'px').style('left', pos[0] + 'px').attr('class', classes + ' ' + side); // shift popover-inner if it is very close to the top or bottom edge
71277               // (doesn't affect the placement of the popover-arrow)
71278
71279               var shiftY = 0;
71280
71281               if (side === 'left' || side === 'right') {
71282                 if (pos[1] < 60) {
71283                   shiftY = 60 - pos[1];
71284                 } else if (pos[1] + tip.height > h - 100) {
71285                   shiftY = h - pos[1] - tip.height - 100;
71286                 }
71287               }
71288
71289               tooltip.selectAll('.popover-inner').style('top', shiftY + 'px');
71290             } else {
71291               tooltip.classed('in', false).call(uiToggle(false));
71292             }
71293
71294             curtain.cut(box, options.duration);
71295             return tooltip;
71296           };
71297
71298           curtain.cut = function (datum, duration) {
71299             darkness.datum(datum).interrupt();
71300             var selection;
71301
71302             if (duration === 0) {
71303               selection = darkness;
71304             } else {
71305               selection = darkness.transition().duration(duration || 600).ease(linear$1);
71306             }
71307
71308             selection.attr('d', function (d) {
71309               var containerWidth = containerNode.clientWidth;
71310               var containerHeight = containerNode.clientHeight;
71311               var string = 'M 0,0 L 0,' + containerHeight + ' L ' + containerWidth + ',' + containerHeight + 'L' + containerWidth + ',0 Z';
71312               if (!d) return string;
71313               return string + 'M' + d.left + ',' + d.top + 'L' + d.left + ',' + (d.top + d.height) + 'L' + (d.left + d.width) + ',' + (d.top + d.height) + 'L' + (d.left + d.width) + ',' + d.top + 'Z';
71314             });
71315           };
71316
71317           curtain.remove = function () {
71318             surface.remove();
71319             tooltip.remove();
71320             select(window).on('resize.curtain', null);
71321           }; // ClientRects are immutable, so copy them to an object,
71322           // in case we need to trim the height/width.
71323
71324
71325           function copyBox(src) {
71326             return {
71327               top: src.top,
71328               right: src.right,
71329               bottom: src.bottom,
71330               left: src.left,
71331               width: src.width,
71332               height: src.height
71333             };
71334           }
71335
71336           return curtain;
71337         }
71338
71339         function uiIntroWelcome(context, reveal) {
71340           var dispatch = dispatch$8('done');
71341           var chapter = {
71342             title: 'intro.welcome.title'
71343           };
71344
71345           function welcome() {
71346             context.map().centerZoom([-85.63591, 41.94285], 19);
71347             reveal('.intro-nav-wrap .chapter-welcome', helpHtml('intro.welcome.welcome'), {
71348               buttonText: _t.html('intro.ok'),
71349               buttonCallback: practice
71350             });
71351           }
71352
71353           function practice() {
71354             reveal('.intro-nav-wrap .chapter-welcome', helpHtml('intro.welcome.practice'), {
71355               buttonText: _t.html('intro.ok'),
71356               buttonCallback: words
71357             });
71358           }
71359
71360           function words() {
71361             reveal('.intro-nav-wrap .chapter-welcome', helpHtml('intro.welcome.words'), {
71362               buttonText: _t.html('intro.ok'),
71363               buttonCallback: chapters
71364             });
71365           }
71366
71367           function chapters() {
71368             dispatch.call('done');
71369             reveal('.intro-nav-wrap .chapter-navigation', helpHtml('intro.welcome.chapters', {
71370               next: _t('intro.navigation.title')
71371             }));
71372           }
71373
71374           chapter.enter = function () {
71375             welcome();
71376           };
71377
71378           chapter.exit = function () {
71379             context.container().select('.curtain-tooltip.intro-mouse').selectAll('.counter').remove();
71380           };
71381
71382           chapter.restart = function () {
71383             chapter.exit();
71384             chapter.enter();
71385           };
71386
71387           return utilRebind(chapter, dispatch, 'on');
71388         }
71389
71390         function uiIntroNavigation(context, reveal) {
71391           var dispatch = dispatch$8('done');
71392           var timeouts = [];
71393           var hallId = 'n2061';
71394           var townHall = [-85.63591, 41.94285];
71395           var springStreetId = 'w397';
71396           var springStreetEndId = 'n1834';
71397           var springStreet = [-85.63582, 41.94255];
71398           var onewayField = _mainPresetIndex.field('oneway');
71399           var maxspeedField = _mainPresetIndex.field('maxspeed');
71400           var chapter = {
71401             title: 'intro.navigation.title'
71402           };
71403
71404           function timeout(f, t) {
71405             timeouts.push(window.setTimeout(f, t));
71406           }
71407
71408           function eventCancel(d3_event) {
71409             d3_event.stopPropagation();
71410             d3_event.preventDefault();
71411           }
71412
71413           function isTownHallSelected() {
71414             var ids = context.selectedIDs();
71415             return ids.length === 1 && ids[0] === hallId;
71416           }
71417
71418           function dragMap() {
71419             context.enter(modeBrowse(context));
71420             context.history().reset('initial');
71421             var msec = transitionTime(townHall, context.map().center());
71422
71423             if (msec) {
71424               reveal(null, null, {
71425                 duration: 0
71426               });
71427             }
71428
71429             context.map().centerZoomEase(townHall, 19, msec);
71430             timeout(function () {
71431               var centerStart = context.map().center();
71432               var textId = context.lastPointerType() === 'mouse' ? 'drag' : 'drag_touch';
71433               var dragString = helpHtml('intro.navigation.map_info') + '{br}' + helpHtml('intro.navigation.' + textId);
71434               reveal('.surface', dragString);
71435               context.map().on('drawn.intro', function () {
71436                 reveal('.surface', dragString, {
71437                   duration: 0
71438                 });
71439               });
71440               context.map().on('move.intro', function () {
71441                 var centerNow = context.map().center();
71442
71443                 if (centerStart[0] !== centerNow[0] || centerStart[1] !== centerNow[1]) {
71444                   context.map().on('move.intro', null);
71445                   timeout(function () {
71446                     continueTo(zoomMap);
71447                   }, 3000);
71448                 }
71449               });
71450             }, msec + 100);
71451
71452             function continueTo(nextStep) {
71453               context.map().on('move.intro drawn.intro', null);
71454               nextStep();
71455             }
71456           }
71457
71458           function zoomMap() {
71459             var zoomStart = context.map().zoom();
71460             var textId = context.lastPointerType() === 'mouse' ? 'zoom' : 'zoom_touch';
71461             var zoomString = helpHtml('intro.navigation.' + textId);
71462             reveal('.surface', zoomString);
71463             context.map().on('drawn.intro', function () {
71464               reveal('.surface', zoomString, {
71465                 duration: 0
71466               });
71467             });
71468             context.map().on('move.intro', function () {
71469               if (context.map().zoom() !== zoomStart) {
71470                 context.map().on('move.intro', null);
71471                 timeout(function () {
71472                   continueTo(features);
71473                 }, 3000);
71474               }
71475             });
71476
71477             function continueTo(nextStep) {
71478               context.map().on('move.intro drawn.intro', null);
71479               nextStep();
71480             }
71481           }
71482
71483           function features() {
71484             var onClick = function onClick() {
71485               continueTo(pointsLinesAreas);
71486             };
71487
71488             reveal('.surface', helpHtml('intro.navigation.features'), {
71489               buttonText: _t.html('intro.ok'),
71490               buttonCallback: onClick
71491             });
71492             context.map().on('drawn.intro', function () {
71493               reveal('.surface', helpHtml('intro.navigation.features'), {
71494                 duration: 0,
71495                 buttonText: _t.html('intro.ok'),
71496                 buttonCallback: onClick
71497               });
71498             });
71499
71500             function continueTo(nextStep) {
71501               context.map().on('drawn.intro', null);
71502               nextStep();
71503             }
71504           }
71505
71506           function pointsLinesAreas() {
71507             var onClick = function onClick() {
71508               continueTo(nodesWays);
71509             };
71510
71511             reveal('.surface', helpHtml('intro.navigation.points_lines_areas'), {
71512               buttonText: _t.html('intro.ok'),
71513               buttonCallback: onClick
71514             });
71515             context.map().on('drawn.intro', function () {
71516               reveal('.surface', helpHtml('intro.navigation.points_lines_areas'), {
71517                 duration: 0,
71518                 buttonText: _t.html('intro.ok'),
71519                 buttonCallback: onClick
71520               });
71521             });
71522
71523             function continueTo(nextStep) {
71524               context.map().on('drawn.intro', null);
71525               nextStep();
71526             }
71527           }
71528
71529           function nodesWays() {
71530             var onClick = function onClick() {
71531               continueTo(clickTownHall);
71532             };
71533
71534             reveal('.surface', helpHtml('intro.navigation.nodes_ways'), {
71535               buttonText: _t.html('intro.ok'),
71536               buttonCallback: onClick
71537             });
71538             context.map().on('drawn.intro', function () {
71539               reveal('.surface', helpHtml('intro.navigation.nodes_ways'), {
71540                 duration: 0,
71541                 buttonText: _t.html('intro.ok'),
71542                 buttonCallback: onClick
71543               });
71544             });
71545
71546             function continueTo(nextStep) {
71547               context.map().on('drawn.intro', null);
71548               nextStep();
71549             }
71550           }
71551
71552           function clickTownHall() {
71553             context.enter(modeBrowse(context));
71554             context.history().reset('initial');
71555             var entity = context.hasEntity(hallId);
71556             if (!entity) return;
71557             reveal(null, null, {
71558               duration: 0
71559             });
71560             context.map().centerZoomEase(entity.loc, 19, 500);
71561             timeout(function () {
71562               var entity = context.hasEntity(hallId);
71563               if (!entity) return;
71564               var box = pointBox(entity.loc, context);
71565               var textId = context.lastPointerType() === 'mouse' ? 'click_townhall' : 'tap_townhall';
71566               reveal(box, helpHtml('intro.navigation.' + textId));
71567               context.map().on('move.intro drawn.intro', function () {
71568                 var entity = context.hasEntity(hallId);
71569                 if (!entity) return;
71570                 var box = pointBox(entity.loc, context);
71571                 reveal(box, helpHtml('intro.navigation.' + textId), {
71572                   duration: 0
71573                 });
71574               });
71575               context.on('enter.intro', function () {
71576                 if (isTownHallSelected()) continueTo(selectedTownHall);
71577               });
71578             }, 550); // after centerZoomEase
71579
71580             context.history().on('change.intro', function () {
71581               if (!context.hasEntity(hallId)) {
71582                 continueTo(clickTownHall);
71583               }
71584             });
71585
71586             function continueTo(nextStep) {
71587               context.on('enter.intro', null);
71588               context.map().on('move.intro drawn.intro', null);
71589               context.history().on('change.intro', null);
71590               nextStep();
71591             }
71592           }
71593
71594           function selectedTownHall() {
71595             if (!isTownHallSelected()) return clickTownHall();
71596             var entity = context.hasEntity(hallId);
71597             if (!entity) return clickTownHall();
71598             var box = pointBox(entity.loc, context);
71599
71600             var onClick = function onClick() {
71601               continueTo(editorTownHall);
71602             };
71603
71604             reveal(box, helpHtml('intro.navigation.selected_townhall'), {
71605               buttonText: _t.html('intro.ok'),
71606               buttonCallback: onClick
71607             });
71608             context.map().on('move.intro drawn.intro', function () {
71609               var entity = context.hasEntity(hallId);
71610               if (!entity) return;
71611               var box = pointBox(entity.loc, context);
71612               reveal(box, helpHtml('intro.navigation.selected_townhall'), {
71613                 duration: 0,
71614                 buttonText: _t.html('intro.ok'),
71615                 buttonCallback: onClick
71616               });
71617             });
71618             context.history().on('change.intro', function () {
71619               if (!context.hasEntity(hallId)) {
71620                 continueTo(clickTownHall);
71621               }
71622             });
71623
71624             function continueTo(nextStep) {
71625               context.map().on('move.intro drawn.intro', null);
71626               context.history().on('change.intro', null);
71627               nextStep();
71628             }
71629           }
71630
71631           function editorTownHall() {
71632             if (!isTownHallSelected()) return clickTownHall(); // disallow scrolling
71633
71634             context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
71635
71636             var onClick = function onClick() {
71637               continueTo(presetTownHall);
71638             };
71639
71640             reveal('.entity-editor-pane', helpHtml('intro.navigation.editor_townhall'), {
71641               buttonText: _t.html('intro.ok'),
71642               buttonCallback: onClick
71643             });
71644             context.on('exit.intro', function () {
71645               continueTo(clickTownHall);
71646             });
71647             context.history().on('change.intro', function () {
71648               if (!context.hasEntity(hallId)) {
71649                 continueTo(clickTownHall);
71650               }
71651             });
71652
71653             function continueTo(nextStep) {
71654               context.on('exit.intro', null);
71655               context.history().on('change.intro', null);
71656               context.container().select('.inspector-wrap').on('wheel.intro', null);
71657               nextStep();
71658             }
71659           }
71660
71661           function presetTownHall() {
71662             if (!isTownHallSelected()) return clickTownHall(); // reset pane, in case user happened to change it..
71663
71664             context.container().select('.inspector-wrap .panewrap').style('right', '0%'); // disallow scrolling
71665
71666             context.container().select('.inspector-wrap').on('wheel.intro', eventCancel); // preset match, in case the user happened to change it.
71667
71668             var entity = context.entity(context.selectedIDs()[0]);
71669             var preset = _mainPresetIndex.match(entity, context.graph());
71670
71671             var onClick = function onClick() {
71672               continueTo(fieldsTownHall);
71673             };
71674
71675             reveal('.entity-editor-pane .section-feature-type', helpHtml('intro.navigation.preset_townhall', {
71676               preset: preset.name()
71677             }), {
71678               buttonText: _t.html('intro.ok'),
71679               buttonCallback: onClick
71680             });
71681             context.on('exit.intro', function () {
71682               continueTo(clickTownHall);
71683             });
71684             context.history().on('change.intro', function () {
71685               if (!context.hasEntity(hallId)) {
71686                 continueTo(clickTownHall);
71687               }
71688             });
71689
71690             function continueTo(nextStep) {
71691               context.on('exit.intro', null);
71692               context.history().on('change.intro', null);
71693               context.container().select('.inspector-wrap').on('wheel.intro', null);
71694               nextStep();
71695             }
71696           }
71697
71698           function fieldsTownHall() {
71699             if (!isTownHallSelected()) return clickTownHall(); // reset pane, in case user happened to change it..
71700
71701             context.container().select('.inspector-wrap .panewrap').style('right', '0%'); // disallow scrolling
71702
71703             context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
71704
71705             var onClick = function onClick() {
71706               continueTo(closeTownHall);
71707             };
71708
71709             reveal('.entity-editor-pane .section-preset-fields', helpHtml('intro.navigation.fields_townhall'), {
71710               buttonText: _t.html('intro.ok'),
71711               buttonCallback: onClick
71712             });
71713             context.on('exit.intro', function () {
71714               continueTo(clickTownHall);
71715             });
71716             context.history().on('change.intro', function () {
71717               if (!context.hasEntity(hallId)) {
71718                 continueTo(clickTownHall);
71719               }
71720             });
71721
71722             function continueTo(nextStep) {
71723               context.on('exit.intro', null);
71724               context.history().on('change.intro', null);
71725               context.container().select('.inspector-wrap').on('wheel.intro', null);
71726               nextStep();
71727             }
71728           }
71729
71730           function closeTownHall() {
71731             if (!isTownHallSelected()) return clickTownHall();
71732             var selector = '.entity-editor-pane button.close svg use';
71733             var href = select(selector).attr('href') || '#iD-icon-close';
71734             reveal('.entity-editor-pane', helpHtml('intro.navigation.close_townhall', {
71735               button: icon(href, 'inline')
71736             }));
71737             context.on('exit.intro', function () {
71738               continueTo(searchStreet);
71739             });
71740             context.history().on('change.intro', function () {
71741               // update the close icon in the tooltip if the user edits something.
71742               var selector = '.entity-editor-pane button.close svg use';
71743               var href = select(selector).attr('href') || '#iD-icon-close';
71744               reveal('.entity-editor-pane', helpHtml('intro.navigation.close_townhall', {
71745                 button: icon(href, 'inline')
71746               }), {
71747                 duration: 0
71748               });
71749             });
71750
71751             function continueTo(nextStep) {
71752               context.on('exit.intro', null);
71753               context.history().on('change.intro', null);
71754               nextStep();
71755             }
71756           }
71757
71758           function searchStreet() {
71759             context.enter(modeBrowse(context));
71760             context.history().reset('initial'); // ensure spring street exists
71761
71762             var msec = transitionTime(springStreet, context.map().center());
71763
71764             if (msec) {
71765               reveal(null, null, {
71766                 duration: 0
71767               });
71768             }
71769
71770             context.map().centerZoomEase(springStreet, 19, msec); // ..and user can see it
71771
71772             timeout(function () {
71773               reveal('.search-header input', helpHtml('intro.navigation.search_street', {
71774                 name: _t('intro.graph.name.spring-street')
71775               }));
71776               context.container().select('.search-header input').on('keyup.intro', checkSearchResult);
71777             }, msec + 100);
71778           }
71779
71780           function checkSearchResult() {
71781             var first = context.container().select('.feature-list-item:nth-child(0n+2)'); // skip "No Results" item
71782
71783             var firstName = first.select('.entity-name');
71784             var name = _t('intro.graph.name.spring-street');
71785
71786             if (!firstName.empty() && firstName.html() === name) {
71787               reveal(first.node(), helpHtml('intro.navigation.choose_street', {
71788                 name: name
71789               }), {
71790                 duration: 300
71791               });
71792               context.on('exit.intro', function () {
71793                 continueTo(selectedStreet);
71794               });
71795               context.container().select('.search-header input').on('keydown.intro', eventCancel, true).on('keyup.intro', null);
71796             }
71797
71798             function continueTo(nextStep) {
71799               context.on('exit.intro', null);
71800               context.container().select('.search-header input').on('keydown.intro', null).on('keyup.intro', null);
71801               nextStep();
71802             }
71803           }
71804
71805           function selectedStreet() {
71806             if (!context.hasEntity(springStreetEndId) || !context.hasEntity(springStreetId)) {
71807               return searchStreet();
71808             }
71809
71810             var onClick = function onClick() {
71811               continueTo(editorStreet);
71812             };
71813
71814             var entity = context.entity(springStreetEndId);
71815             var box = pointBox(entity.loc, context);
71816             box.height = 500;
71817             reveal(box, helpHtml('intro.navigation.selected_street', {
71818               name: _t('intro.graph.name.spring-street')
71819             }), {
71820               duration: 600,
71821               buttonText: _t.html('intro.ok'),
71822               buttonCallback: onClick
71823             });
71824             timeout(function () {
71825               context.map().on('move.intro drawn.intro', function () {
71826                 var entity = context.hasEntity(springStreetEndId);
71827                 if (!entity) return;
71828                 var box = pointBox(entity.loc, context);
71829                 box.height = 500;
71830                 reveal(box, helpHtml('intro.navigation.selected_street', {
71831                   name: _t('intro.graph.name.spring-street')
71832                 }), {
71833                   duration: 0,
71834                   buttonText: _t.html('intro.ok'),
71835                   buttonCallback: onClick
71836                 });
71837               });
71838             }, 600); // after reveal.
71839
71840             context.on('enter.intro', function (mode) {
71841               if (!context.hasEntity(springStreetId)) {
71842                 return continueTo(searchStreet);
71843               }
71844
71845               var ids = context.selectedIDs();
71846
71847               if (mode.id !== 'select' || !ids.length || ids[0] !== springStreetId) {
71848                 // keep Spring Street selected..
71849                 context.enter(modeSelect(context, [springStreetId]));
71850               }
71851             });
71852             context.history().on('change.intro', function () {
71853               if (!context.hasEntity(springStreetEndId) || !context.hasEntity(springStreetId)) {
71854                 timeout(function () {
71855                   continueTo(searchStreet);
71856                 }, 300); // after any transition (e.g. if user deleted intersection)
71857               }
71858             });
71859
71860             function continueTo(nextStep) {
71861               context.map().on('move.intro drawn.intro', null);
71862               context.on('enter.intro', null);
71863               context.history().on('change.intro', null);
71864               nextStep();
71865             }
71866           }
71867
71868           function editorStreet() {
71869             var selector = '.entity-editor-pane button.close svg use';
71870             var href = select(selector).attr('href') || '#iD-icon-close';
71871             reveal('.entity-editor-pane', helpHtml('intro.navigation.street_different_fields') + '{br}' + helpHtml('intro.navigation.editor_street', {
71872               button: icon(href, 'inline'),
71873               field1: onewayField.label(),
71874               field2: maxspeedField.label()
71875             }));
71876             context.on('exit.intro', function () {
71877               continueTo(play);
71878             });
71879             context.history().on('change.intro', function () {
71880               // update the close icon in the tooltip if the user edits something.
71881               var selector = '.entity-editor-pane button.close svg use';
71882               var href = select(selector).attr('href') || '#iD-icon-close';
71883               reveal('.entity-editor-pane', helpHtml('intro.navigation.street_different_fields') + '{br}' + helpHtml('intro.navigation.editor_street', {
71884                 button: icon(href, 'inline'),
71885                 field1: onewayField.label(),
71886                 field2: maxspeedField.label()
71887               }), {
71888                 duration: 0
71889               });
71890             });
71891
71892             function continueTo(nextStep) {
71893               context.on('exit.intro', null);
71894               context.history().on('change.intro', null);
71895               nextStep();
71896             }
71897           }
71898
71899           function play() {
71900             dispatch.call('done');
71901             reveal('.ideditor', helpHtml('intro.navigation.play', {
71902               next: _t('intro.points.title')
71903             }), {
71904               tooltipBox: '.intro-nav-wrap .chapter-point',
71905               buttonText: _t.html('intro.ok'),
71906               buttonCallback: function buttonCallback() {
71907                 reveal('.ideditor');
71908               }
71909             });
71910           }
71911
71912           chapter.enter = function () {
71913             dragMap();
71914           };
71915
71916           chapter.exit = function () {
71917             timeouts.forEach(window.clearTimeout);
71918             context.on('enter.intro exit.intro', null);
71919             context.map().on('move.intro drawn.intro', null);
71920             context.history().on('change.intro', null);
71921             context.container().select('.inspector-wrap').on('wheel.intro', null);
71922             context.container().select('.search-header input').on('keydown.intro keyup.intro', null);
71923           };
71924
71925           chapter.restart = function () {
71926             chapter.exit();
71927             chapter.enter();
71928           };
71929
71930           return utilRebind(chapter, dispatch, 'on');
71931         }
71932
71933         function uiIntroPoint(context, reveal) {
71934           var dispatch = dispatch$8('done');
71935           var timeouts = [];
71936           var intersection = [-85.63279, 41.94394];
71937           var building = [-85.632422, 41.944045];
71938           var cafePreset = _mainPresetIndex.item('amenity/cafe');
71939           var _pointID = null;
71940           var chapter = {
71941             title: 'intro.points.title'
71942           };
71943
71944           function timeout(f, t) {
71945             timeouts.push(window.setTimeout(f, t));
71946           }
71947
71948           function eventCancel(d3_event) {
71949             d3_event.stopPropagation();
71950             d3_event.preventDefault();
71951           }
71952
71953           function addPoint() {
71954             context.enter(modeBrowse(context));
71955             context.history().reset('initial');
71956             var msec = transitionTime(intersection, context.map().center());
71957
71958             if (msec) {
71959               reveal(null, null, {
71960                 duration: 0
71961               });
71962             }
71963
71964             context.map().centerZoomEase(intersection, 19, msec);
71965             timeout(function () {
71966               var tooltip = reveal('button.add-point', helpHtml('intro.points.points_info') + '{br}' + helpHtml('intro.points.add_point'));
71967               _pointID = null;
71968               tooltip.selectAll('.popover-inner').insert('svg', 'span').attr('class', 'tooltip-illustration').append('use').attr('xlink:href', '#iD-graphic-points');
71969               context.on('enter.intro', function (mode) {
71970                 if (mode.id !== 'add-point') return;
71971                 continueTo(placePoint);
71972               });
71973             }, msec + 100);
71974
71975             function continueTo(nextStep) {
71976               context.on('enter.intro', null);
71977               nextStep();
71978             }
71979           }
71980
71981           function placePoint() {
71982             if (context.mode().id !== 'add-point') {
71983               return chapter.restart();
71984             }
71985
71986             var pointBox = pad(building, 150, context);
71987             var textId = context.lastPointerType() === 'mouse' ? 'place_point' : 'place_point_touch';
71988             reveal(pointBox, helpHtml('intro.points.' + textId));
71989             context.map().on('move.intro drawn.intro', function () {
71990               pointBox = pad(building, 150, context);
71991               reveal(pointBox, helpHtml('intro.points.' + textId), {
71992                 duration: 0
71993               });
71994             });
71995             context.on('enter.intro', function (mode) {
71996               if (mode.id !== 'select') return chapter.restart();
71997               _pointID = context.mode().selectedIDs()[0];
71998               continueTo(searchPreset);
71999             });
72000
72001             function continueTo(nextStep) {
72002               context.map().on('move.intro drawn.intro', null);
72003               context.on('enter.intro', null);
72004               nextStep();
72005             }
72006           }
72007
72008           function searchPreset() {
72009             if (context.mode().id !== 'select' || !_pointID || !context.hasEntity(_pointID)) {
72010               return addPoint();
72011             } // disallow scrolling
72012
72013
72014             context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
72015             context.container().select('.preset-search-input').on('keydown.intro', null).on('keyup.intro', checkPresetSearch);
72016             reveal('.preset-search-input', helpHtml('intro.points.search_cafe', {
72017               preset: cafePreset.name()
72018             }));
72019             context.on('enter.intro', function (mode) {
72020               if (!_pointID || !context.hasEntity(_pointID)) {
72021                 return continueTo(addPoint);
72022               }
72023
72024               var ids = context.selectedIDs();
72025
72026               if (mode.id !== 'select' || !ids.length || ids[0] !== _pointID) {
72027                 // keep the user's point selected..
72028                 context.enter(modeSelect(context, [_pointID])); // disallow scrolling
72029
72030                 context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
72031                 context.container().select('.preset-search-input').on('keydown.intro', null).on('keyup.intro', checkPresetSearch);
72032                 reveal('.preset-search-input', helpHtml('intro.points.search_cafe', {
72033                   preset: cafePreset.name()
72034                 }));
72035                 context.history().on('change.intro', null);
72036               }
72037             });
72038
72039             function checkPresetSearch() {
72040               var first = context.container().select('.preset-list-item:first-child');
72041
72042               if (first.classed('preset-amenity-cafe')) {
72043                 context.container().select('.preset-search-input').on('keydown.intro', eventCancel, true).on('keyup.intro', null);
72044                 reveal(first.select('.preset-list-button').node(), helpHtml('intro.points.choose_cafe', {
72045                   preset: cafePreset.name()
72046                 }), {
72047                   duration: 300
72048                 });
72049                 context.history().on('change.intro', function () {
72050                   continueTo(aboutFeatureEditor);
72051                 });
72052               }
72053             }
72054
72055             function continueTo(nextStep) {
72056               context.on('enter.intro', null);
72057               context.history().on('change.intro', null);
72058               context.container().select('.inspector-wrap').on('wheel.intro', null);
72059               context.container().select('.preset-search-input').on('keydown.intro keyup.intro', null);
72060               nextStep();
72061             }
72062           }
72063
72064           function aboutFeatureEditor() {
72065             if (context.mode().id !== 'select' || !_pointID || !context.hasEntity(_pointID)) {
72066               return addPoint();
72067             }
72068
72069             timeout(function () {
72070               reveal('.entity-editor-pane', helpHtml('intro.points.feature_editor'), {
72071                 tooltipClass: 'intro-points-describe',
72072                 buttonText: _t.html('intro.ok'),
72073                 buttonCallback: function buttonCallback() {
72074                   continueTo(addName);
72075                 }
72076               });
72077             }, 400);
72078             context.on('exit.intro', function () {
72079               // if user leaves select mode here, just continue with the tutorial.
72080               continueTo(reselectPoint);
72081             });
72082
72083             function continueTo(nextStep) {
72084               context.on('exit.intro', null);
72085               nextStep();
72086             }
72087           }
72088
72089           function addName() {
72090             if (context.mode().id !== 'select' || !_pointID || !context.hasEntity(_pointID)) {
72091               return addPoint();
72092             } // reset pane, in case user happened to change it..
72093
72094
72095             context.container().select('.inspector-wrap .panewrap').style('right', '0%');
72096             var addNameString = helpHtml('intro.points.fields_info') + '{br}' + helpHtml('intro.points.add_name');
72097             timeout(function () {
72098               // It's possible for the user to add a name in a previous step..
72099               // If so, don't tell them to add the name in this step.
72100               // Give them an OK button instead.
72101               var entity = context.entity(_pointID);
72102
72103               if (entity.tags.name) {
72104                 var tooltip = reveal('.entity-editor-pane', addNameString, {
72105                   tooltipClass: 'intro-points-describe',
72106                   buttonText: _t.html('intro.ok'),
72107                   buttonCallback: function buttonCallback() {
72108                     continueTo(addCloseEditor);
72109                   }
72110                 });
72111                 tooltip.select('.instruction').style('display', 'none');
72112               } else {
72113                 reveal('.entity-editor-pane', addNameString, {
72114                   tooltipClass: 'intro-points-describe'
72115                 });
72116               }
72117             }, 400);
72118             context.history().on('change.intro', function () {
72119               continueTo(addCloseEditor);
72120             });
72121             context.on('exit.intro', function () {
72122               // if user leaves select mode here, just continue with the tutorial.
72123               continueTo(reselectPoint);
72124             });
72125
72126             function continueTo(nextStep) {
72127               context.on('exit.intro', null);
72128               context.history().on('change.intro', null);
72129               nextStep();
72130             }
72131           }
72132
72133           function addCloseEditor() {
72134             // reset pane, in case user happened to change it..
72135             context.container().select('.inspector-wrap .panewrap').style('right', '0%');
72136             var selector = '.entity-editor-pane button.close svg use';
72137             var href = select(selector).attr('href') || '#iD-icon-close';
72138             context.on('exit.intro', function () {
72139               continueTo(reselectPoint);
72140             });
72141             reveal('.entity-editor-pane', helpHtml('intro.points.add_close', {
72142               button: icon(href, 'inline')
72143             }));
72144
72145             function continueTo(nextStep) {
72146               context.on('exit.intro', null);
72147               nextStep();
72148             }
72149           }
72150
72151           function reselectPoint() {
72152             if (!_pointID) return chapter.restart();
72153             var entity = context.hasEntity(_pointID);
72154             if (!entity) return chapter.restart(); // make sure it's still a cafe, in case user somehow changed it..
72155
72156             var oldPreset = _mainPresetIndex.match(entity, context.graph());
72157             context.replace(actionChangePreset(_pointID, oldPreset, cafePreset));
72158             context.enter(modeBrowse(context));
72159             var msec = transitionTime(entity.loc, context.map().center());
72160
72161             if (msec) {
72162               reveal(null, null, {
72163                 duration: 0
72164               });
72165             }
72166
72167             context.map().centerEase(entity.loc, msec);
72168             timeout(function () {
72169               var box = pointBox(entity.loc, context);
72170               reveal(box, helpHtml('intro.points.reselect'), {
72171                 duration: 600
72172               });
72173               timeout(function () {
72174                 context.map().on('move.intro drawn.intro', function () {
72175                   var entity = context.hasEntity(_pointID);
72176                   if (!entity) return chapter.restart();
72177                   var box = pointBox(entity.loc, context);
72178                   reveal(box, helpHtml('intro.points.reselect'), {
72179                     duration: 0
72180                   });
72181                 });
72182               }, 600); // after reveal..
72183
72184               context.on('enter.intro', function (mode) {
72185                 if (mode.id !== 'select') return;
72186                 continueTo(updatePoint);
72187               });
72188             }, msec + 100);
72189
72190             function continueTo(nextStep) {
72191               context.map().on('move.intro drawn.intro', null);
72192               context.on('enter.intro', null);
72193               nextStep();
72194             }
72195           }
72196
72197           function updatePoint() {
72198             if (context.mode().id !== 'select' || !_pointID || !context.hasEntity(_pointID)) {
72199               return continueTo(reselectPoint);
72200             } // reset pane, in case user happened to untag the point..
72201
72202
72203             context.container().select('.inspector-wrap .panewrap').style('right', '0%');
72204             context.on('exit.intro', function () {
72205               continueTo(reselectPoint);
72206             });
72207             context.history().on('change.intro', function () {
72208               continueTo(updateCloseEditor);
72209             });
72210             timeout(function () {
72211               reveal('.entity-editor-pane', helpHtml('intro.points.update'), {
72212                 tooltipClass: 'intro-points-describe'
72213               });
72214             }, 400);
72215
72216             function continueTo(nextStep) {
72217               context.on('exit.intro', null);
72218               context.history().on('change.intro', null);
72219               nextStep();
72220             }
72221           }
72222
72223           function updateCloseEditor() {
72224             if (context.mode().id !== 'select' || !_pointID || !context.hasEntity(_pointID)) {
72225               return continueTo(reselectPoint);
72226             } // reset pane, in case user happened to change it..
72227
72228
72229             context.container().select('.inspector-wrap .panewrap').style('right', '0%');
72230             context.on('exit.intro', function () {
72231               continueTo(rightClickPoint);
72232             });
72233             timeout(function () {
72234               reveal('.entity-editor-pane', helpHtml('intro.points.update_close', {
72235                 button: icon('#iD-icon-close', 'inline')
72236               }));
72237             }, 500);
72238
72239             function continueTo(nextStep) {
72240               context.on('exit.intro', null);
72241               nextStep();
72242             }
72243           }
72244
72245           function rightClickPoint() {
72246             if (!_pointID) return chapter.restart();
72247             var entity = context.hasEntity(_pointID);
72248             if (!entity) return chapter.restart();
72249             context.enter(modeBrowse(context));
72250             var box = pointBox(entity.loc, context);
72251             var textId = context.lastPointerType() === 'mouse' ? 'rightclick' : 'edit_menu_touch';
72252             reveal(box, helpHtml('intro.points.' + textId), {
72253               duration: 600
72254             });
72255             timeout(function () {
72256               context.map().on('move.intro', function () {
72257                 var entity = context.hasEntity(_pointID);
72258                 if (!entity) return chapter.restart();
72259                 var box = pointBox(entity.loc, context);
72260                 reveal(box, helpHtml('intro.points.' + textId), {
72261                   duration: 0
72262                 });
72263               });
72264             }, 600); // after reveal
72265
72266             context.on('enter.intro', function (mode) {
72267               if (mode.id !== 'select') return;
72268               var ids = context.selectedIDs();
72269               if (ids.length !== 1 || ids[0] !== _pointID) return;
72270               timeout(function () {
72271                 var node = selectMenuItem(context, 'delete').node();
72272                 if (!node) return;
72273                 continueTo(enterDelete);
72274               }, 50); // after menu visible
72275             });
72276
72277             function continueTo(nextStep) {
72278               context.on('enter.intro', null);
72279               context.map().on('move.intro', null);
72280               nextStep();
72281             }
72282           }
72283
72284           function enterDelete() {
72285             if (!_pointID) return chapter.restart();
72286             var entity = context.hasEntity(_pointID);
72287             if (!entity) return chapter.restart();
72288             var node = selectMenuItem(context, 'delete').node();
72289
72290             if (!node) {
72291               return continueTo(rightClickPoint);
72292             }
72293
72294             reveal('.edit-menu', helpHtml('intro.points.delete'), {
72295               padding: 50
72296             });
72297             timeout(function () {
72298               context.map().on('move.intro', function () {
72299                 reveal('.edit-menu', helpHtml('intro.points.delete'), {
72300                   duration: 0,
72301                   padding: 50
72302                 });
72303               });
72304             }, 300); // after menu visible
72305
72306             context.on('exit.intro', function () {
72307               if (!_pointID) return chapter.restart();
72308               var entity = context.hasEntity(_pointID);
72309               if (entity) return continueTo(rightClickPoint); // point still exists
72310             });
72311             context.history().on('change.intro', function (changed) {
72312               if (changed.deleted().length) {
72313                 continueTo(undo);
72314               }
72315             });
72316
72317             function continueTo(nextStep) {
72318               context.map().on('move.intro', null);
72319               context.history().on('change.intro', null);
72320               context.on('exit.intro', null);
72321               nextStep();
72322             }
72323           }
72324
72325           function undo() {
72326             context.history().on('change.intro', function () {
72327               continueTo(play);
72328             });
72329             reveal('.top-toolbar button.undo-button', helpHtml('intro.points.undo'));
72330
72331             function continueTo(nextStep) {
72332               context.history().on('change.intro', null);
72333               nextStep();
72334             }
72335           }
72336
72337           function play() {
72338             dispatch.call('done');
72339             reveal('.ideditor', helpHtml('intro.points.play', {
72340               next: _t('intro.areas.title')
72341             }), {
72342               tooltipBox: '.intro-nav-wrap .chapter-area',
72343               buttonText: _t.html('intro.ok'),
72344               buttonCallback: function buttonCallback() {
72345                 reveal('.ideditor');
72346               }
72347             });
72348           }
72349
72350           chapter.enter = function () {
72351             addPoint();
72352           };
72353
72354           chapter.exit = function () {
72355             timeouts.forEach(window.clearTimeout);
72356             context.on('enter.intro exit.intro', null);
72357             context.map().on('move.intro drawn.intro', null);
72358             context.history().on('change.intro', null);
72359             context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
72360             context.container().select('.preset-search-input').on('keydown.intro keyup.intro', null);
72361           };
72362
72363           chapter.restart = function () {
72364             chapter.exit();
72365             chapter.enter();
72366           };
72367
72368           return utilRebind(chapter, dispatch, 'on');
72369         }
72370
72371         function uiIntroArea(context, reveal) {
72372           var dispatch = dispatch$8('done');
72373           var playground = [-85.63552, 41.94159];
72374           var playgroundPreset = _mainPresetIndex.item('leisure/playground');
72375           var nameField = _mainPresetIndex.field('name');
72376           var descriptionField = _mainPresetIndex.field('description');
72377           var timeouts = [];
72378
72379           var _areaID;
72380
72381           var chapter = {
72382             title: 'intro.areas.title'
72383           };
72384
72385           function timeout(f, t) {
72386             timeouts.push(window.setTimeout(f, t));
72387           }
72388
72389           function eventCancel(d3_event) {
72390             d3_event.stopPropagation();
72391             d3_event.preventDefault();
72392           }
72393
72394           function revealPlayground(center, text, options) {
72395             var padding = 180 * Math.pow(2, context.map().zoom() - 19.5);
72396             var box = pad(center, padding, context);
72397             reveal(box, text, options);
72398           }
72399
72400           function addArea() {
72401             context.enter(modeBrowse(context));
72402             context.history().reset('initial');
72403             _areaID = null;
72404             var msec = transitionTime(playground, context.map().center());
72405
72406             if (msec) {
72407               reveal(null, null, {
72408                 duration: 0
72409               });
72410             }
72411
72412             context.map().centerZoomEase(playground, 19, msec);
72413             timeout(function () {
72414               var tooltip = reveal('button.add-area', helpHtml('intro.areas.add_playground'));
72415               tooltip.selectAll('.popover-inner').insert('svg', 'span').attr('class', 'tooltip-illustration').append('use').attr('xlink:href', '#iD-graphic-areas');
72416               context.on('enter.intro', function (mode) {
72417                 if (mode.id !== 'add-area') return;
72418                 continueTo(startPlayground);
72419               });
72420             }, msec + 100);
72421
72422             function continueTo(nextStep) {
72423               context.on('enter.intro', null);
72424               nextStep();
72425             }
72426           }
72427
72428           function startPlayground() {
72429             if (context.mode().id !== 'add-area') {
72430               return chapter.restart();
72431             }
72432
72433             _areaID = null;
72434             context.map().zoomEase(19.5, 500);
72435             timeout(function () {
72436               var textId = context.lastPointerType() === 'mouse' ? 'starting_node_click' : 'starting_node_tap';
72437               var startDrawString = helpHtml('intro.areas.start_playground') + helpHtml('intro.areas.' + textId);
72438               revealPlayground(playground, startDrawString, {
72439                 duration: 250
72440               });
72441               timeout(function () {
72442                 context.map().on('move.intro drawn.intro', function () {
72443                   revealPlayground(playground, startDrawString, {
72444                     duration: 0
72445                   });
72446                 });
72447                 context.on('enter.intro', function (mode) {
72448                   if (mode.id !== 'draw-area') return chapter.restart();
72449                   continueTo(continuePlayground);
72450                 });
72451               }, 250); // after reveal
72452             }, 550); // after easing
72453
72454             function continueTo(nextStep) {
72455               context.map().on('move.intro drawn.intro', null);
72456               context.on('enter.intro', null);
72457               nextStep();
72458             }
72459           }
72460
72461           function continuePlayground() {
72462             if (context.mode().id !== 'draw-area') {
72463               return chapter.restart();
72464             }
72465
72466             _areaID = null;
72467             revealPlayground(playground, helpHtml('intro.areas.continue_playground'), {
72468               duration: 250
72469             });
72470             timeout(function () {
72471               context.map().on('move.intro drawn.intro', function () {
72472                 revealPlayground(playground, helpHtml('intro.areas.continue_playground'), {
72473                   duration: 0
72474                 });
72475               });
72476             }, 250); // after reveal
72477
72478             context.on('enter.intro', function (mode) {
72479               if (mode.id === 'draw-area') {
72480                 var entity = context.hasEntity(context.selectedIDs()[0]);
72481
72482                 if (entity && entity.nodes.length >= 6) {
72483                   return continueTo(finishPlayground);
72484                 } else {
72485                   return;
72486                 }
72487               } else if (mode.id === 'select') {
72488                 _areaID = context.selectedIDs()[0];
72489                 return continueTo(searchPresets);
72490               } else {
72491                 return chapter.restart();
72492               }
72493             });
72494
72495             function continueTo(nextStep) {
72496               context.map().on('move.intro drawn.intro', null);
72497               context.on('enter.intro', null);
72498               nextStep();
72499             }
72500           }
72501
72502           function finishPlayground() {
72503             if (context.mode().id !== 'draw-area') {
72504               return chapter.restart();
72505             }
72506
72507             _areaID = null;
72508             var finishString = helpHtml('intro.areas.finish_area_' + (context.lastPointerType() === 'mouse' ? 'click' : 'tap')) + helpHtml('intro.areas.finish_playground');
72509             revealPlayground(playground, finishString, {
72510               duration: 250
72511             });
72512             timeout(function () {
72513               context.map().on('move.intro drawn.intro', function () {
72514                 revealPlayground(playground, finishString, {
72515                   duration: 0
72516                 });
72517               });
72518             }, 250); // after reveal
72519
72520             context.on('enter.intro', function (mode) {
72521               if (mode.id === 'draw-area') {
72522                 return;
72523               } else if (mode.id === 'select') {
72524                 _areaID = context.selectedIDs()[0];
72525                 return continueTo(searchPresets);
72526               } else {
72527                 return chapter.restart();
72528               }
72529             });
72530
72531             function continueTo(nextStep) {
72532               context.map().on('move.intro drawn.intro', null);
72533               context.on('enter.intro', null);
72534               nextStep();
72535             }
72536           }
72537
72538           function searchPresets() {
72539             if (!_areaID || !context.hasEntity(_areaID)) {
72540               return addArea();
72541             }
72542
72543             var ids = context.selectedIDs();
72544
72545             if (context.mode().id !== 'select' || !ids.length || ids[0] !== _areaID) {
72546               context.enter(modeSelect(context, [_areaID]));
72547             } // disallow scrolling
72548
72549
72550             context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
72551             timeout(function () {
72552               // reset pane, in case user somehow happened to change it..
72553               context.container().select('.inspector-wrap .panewrap').style('right', '-100%');
72554               context.container().select('.preset-search-input').on('keydown.intro', null).on('keyup.intro', checkPresetSearch);
72555               reveal('.preset-search-input', helpHtml('intro.areas.search_playground', {
72556                 preset: playgroundPreset.name()
72557               }));
72558             }, 400); // after preset list pane visible..
72559
72560             context.on('enter.intro', function (mode) {
72561               if (!_areaID || !context.hasEntity(_areaID)) {
72562                 return continueTo(addArea);
72563               }
72564
72565               var ids = context.selectedIDs();
72566
72567               if (mode.id !== 'select' || !ids.length || ids[0] !== _areaID) {
72568                 // keep the user's area selected..
72569                 context.enter(modeSelect(context, [_areaID])); // reset pane, in case user somehow happened to change it..
72570
72571                 context.container().select('.inspector-wrap .panewrap').style('right', '-100%'); // disallow scrolling
72572
72573                 context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
72574                 context.container().select('.preset-search-input').on('keydown.intro', null).on('keyup.intro', checkPresetSearch);
72575                 reveal('.preset-search-input', helpHtml('intro.areas.search_playground', {
72576                   preset: playgroundPreset.name()
72577                 }));
72578                 context.history().on('change.intro', null);
72579               }
72580             });
72581
72582             function checkPresetSearch() {
72583               var first = context.container().select('.preset-list-item:first-child');
72584
72585               if (first.classed('preset-leisure-playground')) {
72586                 reveal(first.select('.preset-list-button').node(), helpHtml('intro.areas.choose_playground', {
72587                   preset: playgroundPreset.name()
72588                 }), {
72589                   duration: 300
72590                 });
72591                 context.container().select('.preset-search-input').on('keydown.intro', eventCancel, true).on('keyup.intro', null);
72592                 context.history().on('change.intro', function () {
72593                   continueTo(clickAddField);
72594                 });
72595               }
72596             }
72597
72598             function continueTo(nextStep) {
72599               context.container().select('.inspector-wrap').on('wheel.intro', null);
72600               context.on('enter.intro', null);
72601               context.history().on('change.intro', null);
72602               context.container().select('.preset-search-input').on('keydown.intro keyup.intro', null);
72603               nextStep();
72604             }
72605           }
72606
72607           function clickAddField() {
72608             if (!_areaID || !context.hasEntity(_areaID)) {
72609               return addArea();
72610             }
72611
72612             var ids = context.selectedIDs();
72613
72614             if (context.mode().id !== 'select' || !ids.length || ids[0] !== _areaID) {
72615               return searchPresets();
72616             }
72617
72618             if (!context.container().select('.form-field-description').empty()) {
72619               return continueTo(describePlayground);
72620             } // disallow scrolling
72621
72622
72623             context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
72624             timeout(function () {
72625               // reset pane, in case user somehow happened to change it..
72626               context.container().select('.inspector-wrap .panewrap').style('right', '0%'); // It's possible for the user to add a description in a previous step..
72627               // If they did this already, just continue to next step.
72628
72629               var entity = context.entity(_areaID);
72630
72631               if (entity.tags.description) {
72632                 return continueTo(play);
72633               } // scroll "Add field" into view
72634
72635
72636               var box = context.container().select('.more-fields').node().getBoundingClientRect();
72637
72638               if (box.top > 300) {
72639                 var pane = context.container().select('.entity-editor-pane .inspector-body');
72640                 var start = pane.node().scrollTop;
72641                 var end = start + (box.top - 300);
72642                 pane.transition().duration(250).tween('scroll.inspector', function () {
72643                   var node = this;
72644                   var i = d3_interpolateNumber(start, end);
72645                   return function (t) {
72646                     node.scrollTop = i(t);
72647                   };
72648                 });
72649               }
72650
72651               timeout(function () {
72652                 reveal('.more-fields .combobox-input', helpHtml('intro.areas.add_field', {
72653                   name: nameField.label(),
72654                   description: descriptionField.label()
72655                 }), {
72656                   duration: 300
72657                 });
72658                 context.container().select('.more-fields .combobox-input').on('click.intro', function () {
72659                   // Watch for the combobox to appear...
72660                   var watcher;
72661                   watcher = window.setInterval(function () {
72662                     if (!context.container().select('div.combobox').empty()) {
72663                       window.clearInterval(watcher);
72664                       continueTo(chooseDescriptionField);
72665                     }
72666                   }, 300);
72667                 });
72668               }, 300); // after "Add Field" visible
72669             }, 400); // after editor pane visible
72670
72671             context.on('exit.intro', function () {
72672               return continueTo(searchPresets);
72673             });
72674
72675             function continueTo(nextStep) {
72676               context.container().select('.inspector-wrap').on('wheel.intro', null);
72677               context.container().select('.more-fields .combobox-input').on('click.intro', null);
72678               context.on('exit.intro', null);
72679               nextStep();
72680             }
72681           }
72682
72683           function chooseDescriptionField() {
72684             if (!_areaID || !context.hasEntity(_areaID)) {
72685               return addArea();
72686             }
72687
72688             var ids = context.selectedIDs();
72689
72690             if (context.mode().id !== 'select' || !ids.length || ids[0] !== _areaID) {
72691               return searchPresets();
72692             }
72693
72694             if (!context.container().select('.form-field-description').empty()) {
72695               return continueTo(describePlayground);
72696             } // Make sure combobox is ready..
72697
72698
72699             if (context.container().select('div.combobox').empty()) {
72700               return continueTo(clickAddField);
72701             } // Watch for the combobox to go away..
72702
72703
72704             var watcher;
72705             watcher = window.setInterval(function () {
72706               if (context.container().select('div.combobox').empty()) {
72707                 window.clearInterval(watcher);
72708                 timeout(function () {
72709                   if (context.container().select('.form-field-description').empty()) {
72710                     continueTo(retryChooseDescription);
72711                   } else {
72712                     continueTo(describePlayground);
72713                   }
72714                 }, 300); // after description field added.
72715               }
72716             }, 300);
72717             reveal('div.combobox', helpHtml('intro.areas.choose_field', {
72718               field: descriptionField.label()
72719             }), {
72720               duration: 300
72721             });
72722             context.on('exit.intro', function () {
72723               return continueTo(searchPresets);
72724             });
72725
72726             function continueTo(nextStep) {
72727               if (watcher) window.clearInterval(watcher);
72728               context.on('exit.intro', null);
72729               nextStep();
72730             }
72731           }
72732
72733           function describePlayground() {
72734             if (!_areaID || !context.hasEntity(_areaID)) {
72735               return addArea();
72736             }
72737
72738             var ids = context.selectedIDs();
72739
72740             if (context.mode().id !== 'select' || !ids.length || ids[0] !== _areaID) {
72741               return searchPresets();
72742             } // reset pane, in case user happened to change it..
72743
72744
72745             context.container().select('.inspector-wrap .panewrap').style('right', '0%');
72746
72747             if (context.container().select('.form-field-description').empty()) {
72748               return continueTo(retryChooseDescription);
72749             }
72750
72751             context.on('exit.intro', function () {
72752               continueTo(play);
72753             });
72754             reveal('.entity-editor-pane', helpHtml('intro.areas.describe_playground', {
72755               button: icon('#iD-icon-close', 'inline')
72756             }), {
72757               duration: 300
72758             });
72759
72760             function continueTo(nextStep) {
72761               context.on('exit.intro', null);
72762               nextStep();
72763             }
72764           }
72765
72766           function retryChooseDescription() {
72767             if (!_areaID || !context.hasEntity(_areaID)) {
72768               return addArea();
72769             }
72770
72771             var ids = context.selectedIDs();
72772
72773             if (context.mode().id !== 'select' || !ids.length || ids[0] !== _areaID) {
72774               return searchPresets();
72775             } // reset pane, in case user happened to change it..
72776
72777
72778             context.container().select('.inspector-wrap .panewrap').style('right', '0%');
72779             reveal('.entity-editor-pane', helpHtml('intro.areas.retry_add_field', {
72780               field: descriptionField.label()
72781             }), {
72782               buttonText: _t.html('intro.ok'),
72783               buttonCallback: function buttonCallback() {
72784                 continueTo(clickAddField);
72785               }
72786             });
72787             context.on('exit.intro', function () {
72788               return continueTo(searchPresets);
72789             });
72790
72791             function continueTo(nextStep) {
72792               context.on('exit.intro', null);
72793               nextStep();
72794             }
72795           }
72796
72797           function play() {
72798             dispatch.call('done');
72799             reveal('.ideditor', helpHtml('intro.areas.play', {
72800               next: _t('intro.lines.title')
72801             }), {
72802               tooltipBox: '.intro-nav-wrap .chapter-line',
72803               buttonText: _t.html('intro.ok'),
72804               buttonCallback: function buttonCallback() {
72805                 reveal('.ideditor');
72806               }
72807             });
72808           }
72809
72810           chapter.enter = function () {
72811             addArea();
72812           };
72813
72814           chapter.exit = function () {
72815             timeouts.forEach(window.clearTimeout);
72816             context.on('enter.intro exit.intro', null);
72817             context.map().on('move.intro drawn.intro', null);
72818             context.history().on('change.intro', null);
72819             context.container().select('.inspector-wrap').on('wheel.intro', null);
72820             context.container().select('.preset-search-input').on('keydown.intro keyup.intro', null);
72821             context.container().select('.more-fields .combobox-input').on('click.intro', null);
72822           };
72823
72824           chapter.restart = function () {
72825             chapter.exit();
72826             chapter.enter();
72827           };
72828
72829           return utilRebind(chapter, dispatch, 'on');
72830         }
72831
72832         function uiIntroLine(context, reveal) {
72833           var dispatch = dispatch$8('done');
72834           var timeouts = [];
72835           var _tulipRoadID = null;
72836           var flowerRoadID = 'w646';
72837           var tulipRoadStart = [-85.6297754121684, 41.95805253325314];
72838           var tulipRoadMidpoint = [-85.62975395449628, 41.95787501510204];
72839           var tulipRoadIntersection = [-85.62974496187628, 41.95742515554585];
72840           var roadCategory = _mainPresetIndex.item('category-road_minor');
72841           var residentialPreset = _mainPresetIndex.item('highway/residential');
72842           var woodRoadID = 'w525';
72843           var woodRoadEndID = 'n2862';
72844           var woodRoadAddNode = [-85.62390110349587, 41.95397111462291];
72845           var woodRoadDragEndpoint = [-85.623867390213, 41.95466987786487];
72846           var woodRoadDragMidpoint = [-85.62386254803509, 41.95430395953872];
72847           var washingtonStreetID = 'w522';
72848           var twelfthAvenueID = 'w1';
72849           var eleventhAvenueEndID = 'n3550';
72850           var twelfthAvenueEndID = 'n5';
72851           var _washingtonSegmentID = null;
72852           var eleventhAvenueEnd = context.entity(eleventhAvenueEndID).loc;
72853           var twelfthAvenueEnd = context.entity(twelfthAvenueEndID).loc;
72854           var deleteLinesLoc = [-85.6219395542764, 41.95228033922477];
72855           var twelfthAvenue = [-85.62219310052491, 41.952505413152956];
72856           var chapter = {
72857             title: 'intro.lines.title'
72858           };
72859
72860           function timeout(f, t) {
72861             timeouts.push(window.setTimeout(f, t));
72862           }
72863
72864           function eventCancel(d3_event) {
72865             d3_event.stopPropagation();
72866             d3_event.preventDefault();
72867           }
72868
72869           function addLine() {
72870             context.enter(modeBrowse(context));
72871             context.history().reset('initial');
72872             var msec = transitionTime(tulipRoadStart, context.map().center());
72873
72874             if (msec) {
72875               reveal(null, null, {
72876                 duration: 0
72877               });
72878             }
72879
72880             context.map().centerZoomEase(tulipRoadStart, 18.5, msec);
72881             timeout(function () {
72882               var tooltip = reveal('button.add-line', helpHtml('intro.lines.add_line'));
72883               tooltip.selectAll('.popover-inner').insert('svg', 'span').attr('class', 'tooltip-illustration').append('use').attr('xlink:href', '#iD-graphic-lines');
72884               context.on('enter.intro', function (mode) {
72885                 if (mode.id !== 'add-line') return;
72886                 continueTo(startLine);
72887               });
72888             }, msec + 100);
72889
72890             function continueTo(nextStep) {
72891               context.on('enter.intro', null);
72892               nextStep();
72893             }
72894           }
72895
72896           function startLine() {
72897             if (context.mode().id !== 'add-line') return chapter.restart();
72898             _tulipRoadID = null;
72899             var padding = 70 * Math.pow(2, context.map().zoom() - 18);
72900             var box = pad(tulipRoadStart, padding, context);
72901             box.height = box.height + 100;
72902             var textId = context.lastPointerType() === 'mouse' ? 'start_line' : 'start_line_tap';
72903             var startLineString = helpHtml('intro.lines.missing_road') + '{br}' + helpHtml('intro.lines.line_draw_info') + helpHtml('intro.lines.' + textId);
72904             reveal(box, startLineString);
72905             context.map().on('move.intro drawn.intro', function () {
72906               padding = 70 * Math.pow(2, context.map().zoom() - 18);
72907               box = pad(tulipRoadStart, padding, context);
72908               box.height = box.height + 100;
72909               reveal(box, startLineString, {
72910                 duration: 0
72911               });
72912             });
72913             context.on('enter.intro', function (mode) {
72914               if (mode.id !== 'draw-line') return chapter.restart();
72915               continueTo(drawLine);
72916             });
72917
72918             function continueTo(nextStep) {
72919               context.map().on('move.intro drawn.intro', null);
72920               context.on('enter.intro', null);
72921               nextStep();
72922             }
72923           }
72924
72925           function drawLine() {
72926             if (context.mode().id !== 'draw-line') return chapter.restart();
72927             _tulipRoadID = context.mode().selectedIDs()[0];
72928             context.map().centerEase(tulipRoadMidpoint, 500);
72929             timeout(function () {
72930               var padding = 200 * Math.pow(2, context.map().zoom() - 18.5);
72931               var box = pad(tulipRoadMidpoint, padding, context);
72932               box.height = box.height * 2;
72933               reveal(box, helpHtml('intro.lines.intersect', {
72934                 name: _t('intro.graph.name.flower-street')
72935               }));
72936               context.map().on('move.intro drawn.intro', function () {
72937                 padding = 200 * Math.pow(2, context.map().zoom() - 18.5);
72938                 box = pad(tulipRoadMidpoint, padding, context);
72939                 box.height = box.height * 2;
72940                 reveal(box, helpHtml('intro.lines.intersect', {
72941                   name: _t('intro.graph.name.flower-street')
72942                 }), {
72943                   duration: 0
72944                 });
72945               });
72946             }, 550); // after easing..
72947
72948             context.history().on('change.intro', function () {
72949               if (isLineConnected()) {
72950                 continueTo(continueLine);
72951               }
72952             });
72953             context.on('enter.intro', function (mode) {
72954               if (mode.id === 'draw-line') {
72955                 return;
72956               } else if (mode.id === 'select') {
72957                 continueTo(retryIntersect);
72958                 return;
72959               } else {
72960                 return chapter.restart();
72961               }
72962             });
72963
72964             function continueTo(nextStep) {
72965               context.map().on('move.intro drawn.intro', null);
72966               context.history().on('change.intro', null);
72967               context.on('enter.intro', null);
72968               nextStep();
72969             }
72970           }
72971
72972           function isLineConnected() {
72973             var entity = _tulipRoadID && context.hasEntity(_tulipRoadID);
72974
72975             if (!entity) return false;
72976             var drawNodes = context.graph().childNodes(entity);
72977             return drawNodes.some(function (node) {
72978               return context.graph().parentWays(node).some(function (parent) {
72979                 return parent.id === flowerRoadID;
72980               });
72981             });
72982           }
72983
72984           function retryIntersect() {
72985             select(window).on('pointerdown.intro mousedown.intro', eventCancel, true);
72986             var box = pad(tulipRoadIntersection, 80, context);
72987             reveal(box, helpHtml('intro.lines.retry_intersect', {
72988               name: _t('intro.graph.name.flower-street')
72989             }));
72990             timeout(chapter.restart, 3000);
72991           }
72992
72993           function continueLine() {
72994             if (context.mode().id !== 'draw-line') return chapter.restart();
72995
72996             var entity = _tulipRoadID && context.hasEntity(_tulipRoadID);
72997
72998             if (!entity) return chapter.restart();
72999             context.map().centerEase(tulipRoadIntersection, 500);
73000             var continueLineText = helpHtml('intro.lines.continue_line') + '{br}' + helpHtml('intro.lines.finish_line_' + (context.lastPointerType() === 'mouse' ? 'click' : 'tap')) + helpHtml('intro.lines.finish_road');
73001             reveal('.surface', continueLineText);
73002             context.on('enter.intro', function (mode) {
73003               if (mode.id === 'draw-line') {
73004                 return;
73005               } else if (mode.id === 'select') {
73006                 return continueTo(chooseCategoryRoad);
73007               } else {
73008                 return chapter.restart();
73009               }
73010             });
73011
73012             function continueTo(nextStep) {
73013               context.on('enter.intro', null);
73014               nextStep();
73015             }
73016           }
73017
73018           function chooseCategoryRoad() {
73019             if (context.mode().id !== 'select') return chapter.restart();
73020             context.on('exit.intro', function () {
73021               return chapter.restart();
73022             });
73023             var button = context.container().select('.preset-category-road_minor .preset-list-button');
73024             if (button.empty()) return chapter.restart(); // disallow scrolling
73025
73026             context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
73027             timeout(function () {
73028               // reset pane, in case user somehow happened to change it..
73029               context.container().select('.inspector-wrap .panewrap').style('right', '-100%');
73030               reveal(button.node(), helpHtml('intro.lines.choose_category_road', {
73031                 category: roadCategory.name()
73032               }));
73033               button.on('click.intro', function () {
73034                 continueTo(choosePresetResidential);
73035               });
73036             }, 400); // after editor pane visible
73037
73038             function continueTo(nextStep) {
73039               context.container().select('.inspector-wrap').on('wheel.intro', null);
73040               context.container().select('.preset-list-button').on('click.intro', null);
73041               context.on('exit.intro', null);
73042               nextStep();
73043             }
73044           }
73045
73046           function choosePresetResidential() {
73047             if (context.mode().id !== 'select') return chapter.restart();
73048             context.on('exit.intro', function () {
73049               return chapter.restart();
73050             });
73051             var subgrid = context.container().select('.preset-category-road_minor .subgrid');
73052             if (subgrid.empty()) return chapter.restart();
73053             subgrid.selectAll(':not(.preset-highway-residential) .preset-list-button').on('click.intro', function () {
73054               continueTo(retryPresetResidential);
73055             });
73056             subgrid.selectAll('.preset-highway-residential .preset-list-button').on('click.intro', function () {
73057               continueTo(nameRoad);
73058             });
73059             timeout(function () {
73060               reveal(subgrid.node(), helpHtml('intro.lines.choose_preset_residential', {
73061                 preset: residentialPreset.name()
73062               }), {
73063                 tooltipBox: '.preset-highway-residential .preset-list-button',
73064                 duration: 300
73065               });
73066             }, 300);
73067
73068             function continueTo(nextStep) {
73069               context.container().select('.preset-list-button').on('click.intro', null);
73070               context.on('exit.intro', null);
73071               nextStep();
73072             }
73073           } // selected wrong road type
73074
73075
73076           function retryPresetResidential() {
73077             if (context.mode().id !== 'select') return chapter.restart();
73078             context.on('exit.intro', function () {
73079               return chapter.restart();
73080             }); // disallow scrolling
73081
73082             context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
73083             timeout(function () {
73084               var button = context.container().select('.entity-editor-pane .preset-list-button');
73085               reveal(button.node(), helpHtml('intro.lines.retry_preset_residential', {
73086                 preset: residentialPreset.name()
73087               }));
73088               button.on('click.intro', function () {
73089                 continueTo(chooseCategoryRoad);
73090               });
73091             }, 500);
73092
73093             function continueTo(nextStep) {
73094               context.container().select('.inspector-wrap').on('wheel.intro', null);
73095               context.container().select('.preset-list-button').on('click.intro', null);
73096               context.on('exit.intro', null);
73097               nextStep();
73098             }
73099           }
73100
73101           function nameRoad() {
73102             context.on('exit.intro', function () {
73103               continueTo(didNameRoad);
73104             });
73105             timeout(function () {
73106               reveal('.entity-editor-pane', helpHtml('intro.lines.name_road', {
73107                 button: icon('#iD-icon-close', 'inline')
73108               }), {
73109                 tooltipClass: 'intro-lines-name_road'
73110               });
73111             }, 500);
73112
73113             function continueTo(nextStep) {
73114               context.on('exit.intro', null);
73115               nextStep();
73116             }
73117           }
73118
73119           function didNameRoad() {
73120             context.history().checkpoint('doneAddLine');
73121             timeout(function () {
73122               reveal('.surface', helpHtml('intro.lines.did_name_road'), {
73123                 buttonText: _t.html('intro.ok'),
73124                 buttonCallback: function buttonCallback() {
73125                   continueTo(updateLine);
73126                 }
73127               });
73128             }, 500);
73129
73130             function continueTo(nextStep) {
73131               nextStep();
73132             }
73133           }
73134
73135           function updateLine() {
73136             context.history().reset('doneAddLine');
73137
73138             if (!context.hasEntity(woodRoadID) || !context.hasEntity(woodRoadEndID)) {
73139               return chapter.restart();
73140             }
73141
73142             var msec = transitionTime(woodRoadDragMidpoint, context.map().center());
73143
73144             if (msec) {
73145               reveal(null, null, {
73146                 duration: 0
73147               });
73148             }
73149
73150             context.map().centerZoomEase(woodRoadDragMidpoint, 19, msec);
73151             timeout(function () {
73152               var padding = 250 * Math.pow(2, context.map().zoom() - 19);
73153               var box = pad(woodRoadDragMidpoint, padding, context);
73154
73155               var advance = function advance() {
73156                 continueTo(addNode);
73157               };
73158
73159               reveal(box, helpHtml('intro.lines.update_line'), {
73160                 buttonText: _t.html('intro.ok'),
73161                 buttonCallback: advance
73162               });
73163               context.map().on('move.intro drawn.intro', function () {
73164                 var padding = 250 * Math.pow(2, context.map().zoom() - 19);
73165                 var box = pad(woodRoadDragMidpoint, padding, context);
73166                 reveal(box, helpHtml('intro.lines.update_line'), {
73167                   duration: 0,
73168                   buttonText: _t.html('intro.ok'),
73169                   buttonCallback: advance
73170                 });
73171               });
73172             }, msec + 100);
73173
73174             function continueTo(nextStep) {
73175               context.map().on('move.intro drawn.intro', null);
73176               nextStep();
73177             }
73178           }
73179
73180           function addNode() {
73181             context.history().reset('doneAddLine');
73182
73183             if (!context.hasEntity(woodRoadID) || !context.hasEntity(woodRoadEndID)) {
73184               return chapter.restart();
73185             }
73186
73187             var padding = 40 * Math.pow(2, context.map().zoom() - 19);
73188             var box = pad(woodRoadAddNode, padding, context);
73189             var addNodeString = helpHtml('intro.lines.add_node' + (context.lastPointerType() === 'mouse' ? '' : '_touch'));
73190             reveal(box, addNodeString);
73191             context.map().on('move.intro drawn.intro', function () {
73192               var padding = 40 * Math.pow(2, context.map().zoom() - 19);
73193               var box = pad(woodRoadAddNode, padding, context);
73194               reveal(box, addNodeString, {
73195                 duration: 0
73196               });
73197             });
73198             context.history().on('change.intro', function (changed) {
73199               if (!context.hasEntity(woodRoadID) || !context.hasEntity(woodRoadEndID)) {
73200                 return continueTo(updateLine);
73201               }
73202
73203               if (changed.created().length === 1) {
73204                 timeout(function () {
73205                   continueTo(startDragEndpoint);
73206                 }, 500);
73207               }
73208             });
73209             context.on('enter.intro', function (mode) {
73210               if (mode.id !== 'select') {
73211                 continueTo(updateLine);
73212               }
73213             });
73214
73215             function continueTo(nextStep) {
73216               context.map().on('move.intro drawn.intro', null);
73217               context.history().on('change.intro', null);
73218               context.on('enter.intro', null);
73219               nextStep();
73220             }
73221           }
73222
73223           function startDragEndpoint() {
73224             if (!context.hasEntity(woodRoadID) || !context.hasEntity(woodRoadEndID)) {
73225               return continueTo(updateLine);
73226             }
73227
73228             var padding = 100 * Math.pow(2, context.map().zoom() - 19);
73229             var box = pad(woodRoadDragEndpoint, padding, context);
73230             var startDragString = helpHtml('intro.lines.start_drag_endpoint' + (context.lastPointerType() === 'mouse' ? '' : '_touch')) + helpHtml('intro.lines.drag_to_intersection');
73231             reveal(box, startDragString);
73232             context.map().on('move.intro drawn.intro', function () {
73233               if (!context.hasEntity(woodRoadID) || !context.hasEntity(woodRoadEndID)) {
73234                 return continueTo(updateLine);
73235               }
73236
73237               var padding = 100 * Math.pow(2, context.map().zoom() - 19);
73238               var box = pad(woodRoadDragEndpoint, padding, context);
73239               reveal(box, startDragString, {
73240                 duration: 0
73241               });
73242               var entity = context.entity(woodRoadEndID);
73243
73244               if (geoSphericalDistance(entity.loc, woodRoadDragEndpoint) <= 4) {
73245                 continueTo(finishDragEndpoint);
73246               }
73247             });
73248
73249             function continueTo(nextStep) {
73250               context.map().on('move.intro drawn.intro', null);
73251               nextStep();
73252             }
73253           }
73254
73255           function finishDragEndpoint() {
73256             if (!context.hasEntity(woodRoadID) || !context.hasEntity(woodRoadEndID)) {
73257               return continueTo(updateLine);
73258             }
73259
73260             var padding = 100 * Math.pow(2, context.map().zoom() - 19);
73261             var box = pad(woodRoadDragEndpoint, padding, context);
73262             var finishDragString = helpHtml('intro.lines.spot_looks_good') + helpHtml('intro.lines.finish_drag_endpoint' + (context.lastPointerType() === 'mouse' ? '' : '_touch'));
73263             reveal(box, finishDragString);
73264             context.map().on('move.intro drawn.intro', function () {
73265               if (!context.hasEntity(woodRoadID) || !context.hasEntity(woodRoadEndID)) {
73266                 return continueTo(updateLine);
73267               }
73268
73269               var padding = 100 * Math.pow(2, context.map().zoom() - 19);
73270               var box = pad(woodRoadDragEndpoint, padding, context);
73271               reveal(box, finishDragString, {
73272                 duration: 0
73273               });
73274               var entity = context.entity(woodRoadEndID);
73275
73276               if (geoSphericalDistance(entity.loc, woodRoadDragEndpoint) > 4) {
73277                 continueTo(startDragEndpoint);
73278               }
73279             });
73280             context.on('enter.intro', function () {
73281               continueTo(startDragMidpoint);
73282             });
73283
73284             function continueTo(nextStep) {
73285               context.map().on('move.intro drawn.intro', null);
73286               context.on('enter.intro', null);
73287               nextStep();
73288             }
73289           }
73290
73291           function startDragMidpoint() {
73292             if (!context.hasEntity(woodRoadID) || !context.hasEntity(woodRoadEndID)) {
73293               return continueTo(updateLine);
73294             }
73295
73296             if (context.selectedIDs().indexOf(woodRoadID) === -1) {
73297               context.enter(modeSelect(context, [woodRoadID]));
73298             }
73299
73300             var padding = 80 * Math.pow(2, context.map().zoom() - 19);
73301             var box = pad(woodRoadDragMidpoint, padding, context);
73302             reveal(box, helpHtml('intro.lines.start_drag_midpoint'));
73303             context.map().on('move.intro drawn.intro', function () {
73304               if (!context.hasEntity(woodRoadID) || !context.hasEntity(woodRoadEndID)) {
73305                 return continueTo(updateLine);
73306               }
73307
73308               var padding = 80 * Math.pow(2, context.map().zoom() - 19);
73309               var box = pad(woodRoadDragMidpoint, padding, context);
73310               reveal(box, helpHtml('intro.lines.start_drag_midpoint'), {
73311                 duration: 0
73312               });
73313             });
73314             context.history().on('change.intro', function (changed) {
73315               if (changed.created().length === 1) {
73316                 continueTo(continueDragMidpoint);
73317               }
73318             });
73319             context.on('enter.intro', function (mode) {
73320               if (mode.id !== 'select') {
73321                 // keep Wood Road selected so midpoint triangles are drawn..
73322                 context.enter(modeSelect(context, [woodRoadID]));
73323               }
73324             });
73325
73326             function continueTo(nextStep) {
73327               context.map().on('move.intro drawn.intro', null);
73328               context.history().on('change.intro', null);
73329               context.on('enter.intro', null);
73330               nextStep();
73331             }
73332           }
73333
73334           function continueDragMidpoint() {
73335             if (!context.hasEntity(woodRoadID) || !context.hasEntity(woodRoadEndID)) {
73336               return continueTo(updateLine);
73337             }
73338
73339             var padding = 100 * Math.pow(2, context.map().zoom() - 19);
73340             var box = pad(woodRoadDragEndpoint, padding, context);
73341             box.height += 400;
73342
73343             var advance = function advance() {
73344               context.history().checkpoint('doneUpdateLine');
73345               continueTo(deleteLines);
73346             };
73347
73348             reveal(box, helpHtml('intro.lines.continue_drag_midpoint'), {
73349               buttonText: _t.html('intro.ok'),
73350               buttonCallback: advance
73351             });
73352             context.map().on('move.intro drawn.intro', function () {
73353               if (!context.hasEntity(woodRoadID) || !context.hasEntity(woodRoadEndID)) {
73354                 return continueTo(updateLine);
73355               }
73356
73357               var padding = 100 * Math.pow(2, context.map().zoom() - 19);
73358               var box = pad(woodRoadDragEndpoint, padding, context);
73359               box.height += 400;
73360               reveal(box, helpHtml('intro.lines.continue_drag_midpoint'), {
73361                 duration: 0,
73362                 buttonText: _t.html('intro.ok'),
73363                 buttonCallback: advance
73364               });
73365             });
73366
73367             function continueTo(nextStep) {
73368               context.map().on('move.intro drawn.intro', null);
73369               nextStep();
73370             }
73371           }
73372
73373           function deleteLines() {
73374             context.history().reset('doneUpdateLine');
73375             context.enter(modeBrowse(context));
73376
73377             if (!context.hasEntity(washingtonStreetID) || !context.hasEntity(twelfthAvenueID) || !context.hasEntity(eleventhAvenueEndID)) {
73378               return chapter.restart();
73379             }
73380
73381             var msec = transitionTime(deleteLinesLoc, context.map().center());
73382
73383             if (msec) {
73384               reveal(null, null, {
73385                 duration: 0
73386               });
73387             }
73388
73389             context.map().centerZoomEase(deleteLinesLoc, 18, msec);
73390             timeout(function () {
73391               var padding = 200 * Math.pow(2, context.map().zoom() - 18);
73392               var box = pad(deleteLinesLoc, padding, context);
73393               box.top -= 200;
73394               box.height += 400;
73395
73396               var advance = function advance() {
73397                 continueTo(rightClickIntersection);
73398               };
73399
73400               reveal(box, helpHtml('intro.lines.delete_lines', {
73401                 street: _t('intro.graph.name.12th-avenue')
73402               }), {
73403                 buttonText: _t.html('intro.ok'),
73404                 buttonCallback: advance
73405               });
73406               context.map().on('move.intro drawn.intro', function () {
73407                 var padding = 200 * Math.pow(2, context.map().zoom() - 18);
73408                 var box = pad(deleteLinesLoc, padding, context);
73409                 box.top -= 200;
73410                 box.height += 400;
73411                 reveal(box, helpHtml('intro.lines.delete_lines', {
73412                   street: _t('intro.graph.name.12th-avenue')
73413                 }), {
73414                   duration: 0,
73415                   buttonText: _t.html('intro.ok'),
73416                   buttonCallback: advance
73417                 });
73418               });
73419               context.history().on('change.intro', function () {
73420                 timeout(function () {
73421                   continueTo(deleteLines);
73422                 }, 500); // after any transition (e.g. if user deleted intersection)
73423               });
73424             }, msec + 100);
73425
73426             function continueTo(nextStep) {
73427               context.map().on('move.intro drawn.intro', null);
73428               context.history().on('change.intro', null);
73429               nextStep();
73430             }
73431           }
73432
73433           function rightClickIntersection() {
73434             context.history().reset('doneUpdateLine');
73435             context.enter(modeBrowse(context));
73436             context.map().centerZoomEase(eleventhAvenueEnd, 18, 500);
73437             var rightClickString = helpHtml('intro.lines.split_street', {
73438               street1: _t('intro.graph.name.11th-avenue'),
73439               street2: _t('intro.graph.name.washington-street')
73440             }) + helpHtml('intro.lines.' + (context.lastPointerType() === 'mouse' ? 'rightclick_intersection' : 'edit_menu_intersection_touch'));
73441             timeout(function () {
73442               var padding = 60 * Math.pow(2, context.map().zoom() - 18);
73443               var box = pad(eleventhAvenueEnd, padding, context);
73444               reveal(box, rightClickString);
73445               context.map().on('move.intro drawn.intro', function () {
73446                 var padding = 60 * Math.pow(2, context.map().zoom() - 18);
73447                 var box = pad(eleventhAvenueEnd, padding, context);
73448                 reveal(box, rightClickString, {
73449                   duration: 0
73450                 });
73451               });
73452               context.on('enter.intro', function (mode) {
73453                 if (mode.id !== 'select') return;
73454                 var ids = context.selectedIDs();
73455                 if (ids.length !== 1 || ids[0] !== eleventhAvenueEndID) return;
73456                 timeout(function () {
73457                   var node = selectMenuItem(context, 'split').node();
73458                   if (!node) return;
73459                   continueTo(splitIntersection);
73460                 }, 50); // after menu visible
73461               });
73462               context.history().on('change.intro', function () {
73463                 timeout(function () {
73464                   continueTo(deleteLines);
73465                 }, 300); // after any transition (e.g. if user deleted intersection)
73466               });
73467             }, 600);
73468
73469             function continueTo(nextStep) {
73470               context.map().on('move.intro drawn.intro', null);
73471               context.on('enter.intro', null);
73472               context.history().on('change.intro', null);
73473               nextStep();
73474             }
73475           }
73476
73477           function splitIntersection() {
73478             if (!context.hasEntity(washingtonStreetID) || !context.hasEntity(twelfthAvenueID) || !context.hasEntity(eleventhAvenueEndID)) {
73479               return continueTo(deleteLines);
73480             }
73481
73482             var node = selectMenuItem(context, 'split').node();
73483
73484             if (!node) {
73485               return continueTo(rightClickIntersection);
73486             }
73487
73488             var wasChanged = false;
73489             _washingtonSegmentID = null;
73490             reveal('.edit-menu', helpHtml('intro.lines.split_intersection', {
73491               street: _t('intro.graph.name.washington-street')
73492             }), {
73493               padding: 50
73494             });
73495             context.map().on('move.intro drawn.intro', function () {
73496               var node = selectMenuItem(context, 'split').node();
73497
73498               if (!wasChanged && !node) {
73499                 return continueTo(rightClickIntersection);
73500               }
73501
73502               reveal('.edit-menu', helpHtml('intro.lines.split_intersection', {
73503                 street: _t('intro.graph.name.washington-street')
73504               }), {
73505                 duration: 0,
73506                 padding: 50
73507               });
73508             });
73509             context.history().on('change.intro', function (changed) {
73510               wasChanged = true;
73511               timeout(function () {
73512                 if (context.history().undoAnnotation() === _t('operations.split.annotation.line', {
73513                   n: 1
73514                 })) {
73515                   _washingtonSegmentID = changed.created()[0].id;
73516                   continueTo(didSplit);
73517                 } else {
73518                   _washingtonSegmentID = null;
73519                   continueTo(retrySplit);
73520                 }
73521               }, 300); // after any transition (e.g. if user deleted intersection)
73522             });
73523
73524             function continueTo(nextStep) {
73525               context.map().on('move.intro drawn.intro', null);
73526               context.history().on('change.intro', null);
73527               nextStep();
73528             }
73529           }
73530
73531           function retrySplit() {
73532             context.enter(modeBrowse(context));
73533             context.map().centerZoomEase(eleventhAvenueEnd, 18, 500);
73534
73535             var advance = function advance() {
73536               continueTo(rightClickIntersection);
73537             };
73538
73539             var padding = 60 * Math.pow(2, context.map().zoom() - 18);
73540             var box = pad(eleventhAvenueEnd, padding, context);
73541             reveal(box, helpHtml('intro.lines.retry_split'), {
73542               buttonText: _t.html('intro.ok'),
73543               buttonCallback: advance
73544             });
73545             context.map().on('move.intro drawn.intro', function () {
73546               var padding = 60 * Math.pow(2, context.map().zoom() - 18);
73547               var box = pad(eleventhAvenueEnd, padding, context);
73548               reveal(box, helpHtml('intro.lines.retry_split'), {
73549                 duration: 0,
73550                 buttonText: _t.html('intro.ok'),
73551                 buttonCallback: advance
73552               });
73553             });
73554
73555             function continueTo(nextStep) {
73556               context.map().on('move.intro drawn.intro', null);
73557               nextStep();
73558             }
73559           }
73560
73561           function didSplit() {
73562             if (!_washingtonSegmentID || !context.hasEntity(_washingtonSegmentID) || !context.hasEntity(washingtonStreetID) || !context.hasEntity(twelfthAvenueID) || !context.hasEntity(eleventhAvenueEndID)) {
73563               return continueTo(rightClickIntersection);
73564             }
73565
73566             var ids = context.selectedIDs();
73567             var string = 'intro.lines.did_split_' + (ids.length > 1 ? 'multi' : 'single');
73568             var street = _t('intro.graph.name.washington-street');
73569             var padding = 200 * Math.pow(2, context.map().zoom() - 18);
73570             var box = pad(twelfthAvenue, padding, context);
73571             box.width = box.width / 2;
73572             reveal(box, helpHtml(string, {
73573               street1: street,
73574               street2: street
73575             }), {
73576               duration: 500
73577             });
73578             timeout(function () {
73579               context.map().centerZoomEase(twelfthAvenue, 18, 500);
73580               context.map().on('move.intro drawn.intro', function () {
73581                 var padding = 200 * Math.pow(2, context.map().zoom() - 18);
73582                 var box = pad(twelfthAvenue, padding, context);
73583                 box.width = box.width / 2;
73584                 reveal(box, helpHtml(string, {
73585                   street1: street,
73586                   street2: street
73587                 }), {
73588                   duration: 0
73589                 });
73590               });
73591             }, 600); // after initial reveal and curtain cut
73592
73593             context.on('enter.intro', function () {
73594               var ids = context.selectedIDs();
73595
73596               if (ids.length === 1 && ids[0] === _washingtonSegmentID) {
73597                 continueTo(multiSelect);
73598               }
73599             });
73600             context.history().on('change.intro', function () {
73601               if (!_washingtonSegmentID || !context.hasEntity(_washingtonSegmentID) || !context.hasEntity(washingtonStreetID) || !context.hasEntity(twelfthAvenueID) || !context.hasEntity(eleventhAvenueEndID)) {
73602                 return continueTo(rightClickIntersection);
73603               }
73604             });
73605
73606             function continueTo(nextStep) {
73607               context.map().on('move.intro drawn.intro', null);
73608               context.on('enter.intro', null);
73609               context.history().on('change.intro', null);
73610               nextStep();
73611             }
73612           }
73613
73614           function multiSelect() {
73615             if (!_washingtonSegmentID || !context.hasEntity(_washingtonSegmentID) || !context.hasEntity(washingtonStreetID) || !context.hasEntity(twelfthAvenueID) || !context.hasEntity(eleventhAvenueEndID)) {
73616               return continueTo(rightClickIntersection);
73617             }
73618
73619             var ids = context.selectedIDs();
73620             var hasWashington = ids.indexOf(_washingtonSegmentID) !== -1;
73621             var hasTwelfth = ids.indexOf(twelfthAvenueID) !== -1;
73622
73623             if (hasWashington && hasTwelfth) {
73624               return continueTo(multiRightClick);
73625             } else if (!hasWashington && !hasTwelfth) {
73626               return continueTo(didSplit);
73627             }
73628
73629             context.map().centerZoomEase(twelfthAvenue, 18, 500);
73630             timeout(function () {
73631               var selected, other, padding, box;
73632
73633               if (hasWashington) {
73634                 selected = _t('intro.graph.name.washington-street');
73635                 other = _t('intro.graph.name.12th-avenue');
73636                 padding = 60 * Math.pow(2, context.map().zoom() - 18);
73637                 box = pad(twelfthAvenueEnd, padding, context);
73638                 box.width *= 3;
73639               } else {
73640                 selected = _t('intro.graph.name.12th-avenue');
73641                 other = _t('intro.graph.name.washington-street');
73642                 padding = 200 * Math.pow(2, context.map().zoom() - 18);
73643                 box = pad(twelfthAvenue, padding, context);
73644                 box.width /= 2;
73645               }
73646
73647               reveal(box, helpHtml('intro.lines.multi_select', {
73648                 selected: selected,
73649                 other1: other
73650               }) + ' ' + helpHtml('intro.lines.add_to_selection_' + (context.lastPointerType() === 'mouse' ? 'click' : 'touch'), {
73651                 selected: selected,
73652                 other2: other
73653               }));
73654               context.map().on('move.intro drawn.intro', function () {
73655                 if (hasWashington) {
73656                   selected = _t('intro.graph.name.washington-street');
73657                   other = _t('intro.graph.name.12th-avenue');
73658                   padding = 60 * Math.pow(2, context.map().zoom() - 18);
73659                   box = pad(twelfthAvenueEnd, padding, context);
73660                   box.width *= 3;
73661                 } else {
73662                   selected = _t('intro.graph.name.12th-avenue');
73663                   other = _t('intro.graph.name.washington-street');
73664                   padding = 200 * Math.pow(2, context.map().zoom() - 18);
73665                   box = pad(twelfthAvenue, padding, context);
73666                   box.width /= 2;
73667                 }
73668
73669                 reveal(box, helpHtml('intro.lines.multi_select', {
73670                   selected: selected,
73671                   other1: other
73672                 }) + ' ' + helpHtml('intro.lines.add_to_selection_' + (context.lastPointerType() === 'mouse' ? 'click' : 'touch'), {
73673                   selected: selected,
73674                   other2: other
73675                 }), {
73676                   duration: 0
73677                 });
73678               });
73679               context.on('enter.intro', function () {
73680                 continueTo(multiSelect);
73681               });
73682               context.history().on('change.intro', function () {
73683                 if (!_washingtonSegmentID || !context.hasEntity(_washingtonSegmentID) || !context.hasEntity(washingtonStreetID) || !context.hasEntity(twelfthAvenueID) || !context.hasEntity(eleventhAvenueEndID)) {
73684                   return continueTo(rightClickIntersection);
73685                 }
73686               });
73687             }, 600);
73688
73689             function continueTo(nextStep) {
73690               context.map().on('move.intro drawn.intro', null);
73691               context.on('enter.intro', null);
73692               context.history().on('change.intro', null);
73693               nextStep();
73694             }
73695           }
73696
73697           function multiRightClick() {
73698             if (!_washingtonSegmentID || !context.hasEntity(_washingtonSegmentID) || !context.hasEntity(washingtonStreetID) || !context.hasEntity(twelfthAvenueID) || !context.hasEntity(eleventhAvenueEndID)) {
73699               return continueTo(rightClickIntersection);
73700             }
73701
73702             var padding = 200 * Math.pow(2, context.map().zoom() - 18);
73703             var box = pad(twelfthAvenue, padding, context);
73704             var rightClickString = helpHtml('intro.lines.multi_select_success') + helpHtml('intro.lines.multi_' + (context.lastPointerType() === 'mouse' ? 'rightclick' : 'edit_menu_touch'));
73705             reveal(box, rightClickString);
73706             context.map().on('move.intro drawn.intro', function () {
73707               var padding = 200 * Math.pow(2, context.map().zoom() - 18);
73708               var box = pad(twelfthAvenue, padding, context);
73709               reveal(box, rightClickString, {
73710                 duration: 0
73711               });
73712             });
73713             context.ui().editMenu().on('toggled.intro', function (open) {
73714               if (!open) return;
73715               timeout(function () {
73716                 var ids = context.selectedIDs();
73717
73718                 if (ids.length === 2 && ids.indexOf(twelfthAvenueID) !== -1 && ids.indexOf(_washingtonSegmentID) !== -1) {
73719                   var node = selectMenuItem(context, 'delete').node();
73720                   if (!node) return;
73721                   continueTo(multiDelete);
73722                 } else if (ids.length === 1 && ids.indexOf(_washingtonSegmentID) !== -1) {
73723                   return continueTo(multiSelect);
73724                 } else {
73725                   return continueTo(didSplit);
73726                 }
73727               }, 300); // after edit menu visible
73728             });
73729             context.history().on('change.intro', function () {
73730               if (!_washingtonSegmentID || !context.hasEntity(_washingtonSegmentID) || !context.hasEntity(washingtonStreetID) || !context.hasEntity(twelfthAvenueID) || !context.hasEntity(eleventhAvenueEndID)) {
73731                 return continueTo(rightClickIntersection);
73732               }
73733             });
73734
73735             function continueTo(nextStep) {
73736               context.map().on('move.intro drawn.intro', null);
73737               context.ui().editMenu().on('toggled.intro', null);
73738               context.history().on('change.intro', null);
73739               nextStep();
73740             }
73741           }
73742
73743           function multiDelete() {
73744             if (!_washingtonSegmentID || !context.hasEntity(_washingtonSegmentID) || !context.hasEntity(washingtonStreetID) || !context.hasEntity(twelfthAvenueID) || !context.hasEntity(eleventhAvenueEndID)) {
73745               return continueTo(rightClickIntersection);
73746             }
73747
73748             var node = selectMenuItem(context, 'delete').node();
73749             if (!node) return continueTo(multiRightClick);
73750             reveal('.edit-menu', helpHtml('intro.lines.multi_delete'), {
73751               padding: 50
73752             });
73753             context.map().on('move.intro drawn.intro', function () {
73754               reveal('.edit-menu', helpHtml('intro.lines.multi_delete'), {
73755                 duration: 0,
73756                 padding: 50
73757               });
73758             });
73759             context.on('exit.intro', function () {
73760               if (context.hasEntity(_washingtonSegmentID) || context.hasEntity(twelfthAvenueID)) {
73761                 return continueTo(multiSelect); // left select mode but roads still exist
73762               }
73763             });
73764             context.history().on('change.intro', function () {
73765               if (context.hasEntity(_washingtonSegmentID) || context.hasEntity(twelfthAvenueID)) {
73766                 continueTo(retryDelete); // changed something but roads still exist
73767               } else {
73768                 continueTo(play);
73769               }
73770             });
73771
73772             function continueTo(nextStep) {
73773               context.map().on('move.intro drawn.intro', null);
73774               context.on('exit.intro', null);
73775               context.history().on('change.intro', null);
73776               nextStep();
73777             }
73778           }
73779
73780           function retryDelete() {
73781             context.enter(modeBrowse(context));
73782             var padding = 200 * Math.pow(2, context.map().zoom() - 18);
73783             var box = pad(twelfthAvenue, padding, context);
73784             reveal(box, helpHtml('intro.lines.retry_delete'), {
73785               buttonText: _t.html('intro.ok'),
73786               buttonCallback: function buttonCallback() {
73787                 continueTo(multiSelect);
73788               }
73789             });
73790
73791             function continueTo(nextStep) {
73792               nextStep();
73793             }
73794           }
73795
73796           function play() {
73797             dispatch.call('done');
73798             reveal('.ideditor', helpHtml('intro.lines.play', {
73799               next: _t('intro.buildings.title')
73800             }), {
73801               tooltipBox: '.intro-nav-wrap .chapter-building',
73802               buttonText: _t.html('intro.ok'),
73803               buttonCallback: function buttonCallback() {
73804                 reveal('.ideditor');
73805               }
73806             });
73807           }
73808
73809           chapter.enter = function () {
73810             addLine();
73811           };
73812
73813           chapter.exit = function () {
73814             timeouts.forEach(window.clearTimeout);
73815             select(window).on('pointerdown.intro mousedown.intro', null, true);
73816             context.on('enter.intro exit.intro', null);
73817             context.map().on('move.intro drawn.intro', null);
73818             context.history().on('change.intro', null);
73819             context.container().select('.inspector-wrap').on('wheel.intro', null);
73820             context.container().select('.preset-list-button').on('click.intro', null);
73821           };
73822
73823           chapter.restart = function () {
73824             chapter.exit();
73825             chapter.enter();
73826           };
73827
73828           return utilRebind(chapter, dispatch, 'on');
73829         }
73830
73831         function uiIntroBuilding(context, reveal) {
73832           var dispatch = dispatch$8('done');
73833           var house = [-85.62815, 41.95638];
73834           var tank = [-85.62732, 41.95347];
73835           var buildingCatetory = _mainPresetIndex.item('category-building');
73836           var housePreset = _mainPresetIndex.item('building/house');
73837           var tankPreset = _mainPresetIndex.item('man_made/storage_tank');
73838           var timeouts = [];
73839           var _houseID = null;
73840           var _tankID = null;
73841           var chapter = {
73842             title: 'intro.buildings.title'
73843           };
73844
73845           function timeout(f, t) {
73846             timeouts.push(window.setTimeout(f, t));
73847           }
73848
73849           function eventCancel(d3_event) {
73850             d3_event.stopPropagation();
73851             d3_event.preventDefault();
73852           }
73853
73854           function revealHouse(center, text, options) {
73855             var padding = 160 * Math.pow(2, context.map().zoom() - 20);
73856             var box = pad(center, padding, context);
73857             reveal(box, text, options);
73858           }
73859
73860           function revealTank(center, text, options) {
73861             var padding = 190 * Math.pow(2, context.map().zoom() - 19.5);
73862             var box = pad(center, padding, context);
73863             reveal(box, text, options);
73864           }
73865
73866           function addHouse() {
73867             context.enter(modeBrowse(context));
73868             context.history().reset('initial');
73869             _houseID = null;
73870             var msec = transitionTime(house, context.map().center());
73871
73872             if (msec) {
73873               reveal(null, null, {
73874                 duration: 0
73875               });
73876             }
73877
73878             context.map().centerZoomEase(house, 19, msec);
73879             timeout(function () {
73880               var tooltip = reveal('button.add-area', helpHtml('intro.buildings.add_building'));
73881               tooltip.selectAll('.popover-inner').insert('svg', 'span').attr('class', 'tooltip-illustration').append('use').attr('xlink:href', '#iD-graphic-buildings');
73882               context.on('enter.intro', function (mode) {
73883                 if (mode.id !== 'add-area') return;
73884                 continueTo(startHouse);
73885               });
73886             }, msec + 100);
73887
73888             function continueTo(nextStep) {
73889               context.on('enter.intro', null);
73890               nextStep();
73891             }
73892           }
73893
73894           function startHouse() {
73895             if (context.mode().id !== 'add-area') {
73896               return continueTo(addHouse);
73897             }
73898
73899             _houseID = null;
73900             context.map().zoomEase(20, 500);
73901             timeout(function () {
73902               var startString = helpHtml('intro.buildings.start_building') + helpHtml('intro.buildings.building_corner_' + (context.lastPointerType() === 'mouse' ? 'click' : 'tap'));
73903               revealHouse(house, startString);
73904               context.map().on('move.intro drawn.intro', function () {
73905                 revealHouse(house, startString, {
73906                   duration: 0
73907                 });
73908               });
73909               context.on('enter.intro', function (mode) {
73910                 if (mode.id !== 'draw-area') return chapter.restart();
73911                 continueTo(continueHouse);
73912               });
73913             }, 550); // after easing
73914
73915             function continueTo(nextStep) {
73916               context.map().on('move.intro drawn.intro', null);
73917               context.on('enter.intro', null);
73918               nextStep();
73919             }
73920           }
73921
73922           function continueHouse() {
73923             if (context.mode().id !== 'draw-area') {
73924               return continueTo(addHouse);
73925             }
73926
73927             _houseID = null;
73928             var continueString = helpHtml('intro.buildings.continue_building') + '{br}' + helpHtml('intro.areas.finish_area_' + (context.lastPointerType() === 'mouse' ? 'click' : 'tap')) + helpHtml('intro.buildings.finish_building');
73929             revealHouse(house, continueString);
73930             context.map().on('move.intro drawn.intro', function () {
73931               revealHouse(house, continueString, {
73932                 duration: 0
73933               });
73934             });
73935             context.on('enter.intro', function (mode) {
73936               if (mode.id === 'draw-area') {
73937                 return;
73938               } else if (mode.id === 'select') {
73939                 var graph = context.graph();
73940                 var way = context.entity(context.selectedIDs()[0]);
73941                 var nodes = graph.childNodes(way);
73942                 var points = utilArrayUniq(nodes).map(function (n) {
73943                   return context.projection(n.loc);
73944                 });
73945
73946                 if (isMostlySquare(points)) {
73947                   _houseID = way.id;
73948                   return continueTo(chooseCategoryBuilding);
73949                 } else {
73950                   return continueTo(retryHouse);
73951                 }
73952               } else {
73953                 return chapter.restart();
73954               }
73955             });
73956
73957             function continueTo(nextStep) {
73958               context.map().on('move.intro drawn.intro', null);
73959               context.on('enter.intro', null);
73960               nextStep();
73961             }
73962           }
73963
73964           function retryHouse() {
73965             var onClick = function onClick() {
73966               continueTo(addHouse);
73967             };
73968
73969             revealHouse(house, helpHtml('intro.buildings.retry_building'), {
73970               buttonText: _t.html('intro.ok'),
73971               buttonCallback: onClick
73972             });
73973             context.map().on('move.intro drawn.intro', function () {
73974               revealHouse(house, helpHtml('intro.buildings.retry_building'), {
73975                 duration: 0,
73976                 buttonText: _t.html('intro.ok'),
73977                 buttonCallback: onClick
73978               });
73979             });
73980
73981             function continueTo(nextStep) {
73982               context.map().on('move.intro drawn.intro', null);
73983               nextStep();
73984             }
73985           }
73986
73987           function chooseCategoryBuilding() {
73988             if (!_houseID || !context.hasEntity(_houseID)) {
73989               return addHouse();
73990             }
73991
73992             var ids = context.selectedIDs();
73993
73994             if (context.mode().id !== 'select' || !ids.length || ids[0] !== _houseID) {
73995               context.enter(modeSelect(context, [_houseID]));
73996             } // disallow scrolling
73997
73998
73999             context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
74000             timeout(function () {
74001               // reset pane, in case user somehow happened to change it..
74002               context.container().select('.inspector-wrap .panewrap').style('right', '-100%');
74003               var button = context.container().select('.preset-category-building .preset-list-button');
74004               reveal(button.node(), helpHtml('intro.buildings.choose_category_building', {
74005                 category: buildingCatetory.name()
74006               }));
74007               button.on('click.intro', function () {
74008                 button.on('click.intro', null);
74009                 continueTo(choosePresetHouse);
74010               });
74011             }, 400); // after preset list pane visible..
74012
74013             context.on('enter.intro', function (mode) {
74014               if (!_houseID || !context.hasEntity(_houseID)) {
74015                 return continueTo(addHouse);
74016               }
74017
74018               var ids = context.selectedIDs();
74019
74020               if (mode.id !== 'select' || !ids.length || ids[0] !== _houseID) {
74021                 return continueTo(chooseCategoryBuilding);
74022               }
74023             });
74024
74025             function continueTo(nextStep) {
74026               context.container().select('.inspector-wrap').on('wheel.intro', null);
74027               context.container().select('.preset-list-button').on('click.intro', null);
74028               context.on('enter.intro', null);
74029               nextStep();
74030             }
74031           }
74032
74033           function choosePresetHouse() {
74034             if (!_houseID || !context.hasEntity(_houseID)) {
74035               return addHouse();
74036             }
74037
74038             var ids = context.selectedIDs();
74039
74040             if (context.mode().id !== 'select' || !ids.length || ids[0] !== _houseID) {
74041               context.enter(modeSelect(context, [_houseID]));
74042             } // disallow scrolling
74043
74044
74045             context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
74046             timeout(function () {
74047               // reset pane, in case user somehow happened to change it..
74048               context.container().select('.inspector-wrap .panewrap').style('right', '-100%');
74049               var button = context.container().select('.preset-building-house .preset-list-button');
74050               reveal(button.node(), helpHtml('intro.buildings.choose_preset_house', {
74051                 preset: housePreset.name()
74052               }), {
74053                 duration: 300
74054               });
74055               button.on('click.intro', function () {
74056                 button.on('click.intro', null);
74057                 continueTo(closeEditorHouse);
74058               });
74059             }, 400); // after preset list pane visible..
74060
74061             context.on('enter.intro', function (mode) {
74062               if (!_houseID || !context.hasEntity(_houseID)) {
74063                 return continueTo(addHouse);
74064               }
74065
74066               var ids = context.selectedIDs();
74067
74068               if (mode.id !== 'select' || !ids.length || ids[0] !== _houseID) {
74069                 return continueTo(chooseCategoryBuilding);
74070               }
74071             });
74072
74073             function continueTo(nextStep) {
74074               context.container().select('.inspector-wrap').on('wheel.intro', null);
74075               context.container().select('.preset-list-button').on('click.intro', null);
74076               context.on('enter.intro', null);
74077               nextStep();
74078             }
74079           }
74080
74081           function closeEditorHouse() {
74082             if (!_houseID || !context.hasEntity(_houseID)) {
74083               return addHouse();
74084             }
74085
74086             var ids = context.selectedIDs();
74087
74088             if (context.mode().id !== 'select' || !ids.length || ids[0] !== _houseID) {
74089               context.enter(modeSelect(context, [_houseID]));
74090             }
74091
74092             context.history().checkpoint('hasHouse');
74093             context.on('exit.intro', function () {
74094               continueTo(rightClickHouse);
74095             });
74096             timeout(function () {
74097               reveal('.entity-editor-pane', helpHtml('intro.buildings.close', {
74098                 button: icon('#iD-icon-close', 'inline')
74099               }));
74100             }, 500);
74101
74102             function continueTo(nextStep) {
74103               context.on('exit.intro', null);
74104               nextStep();
74105             }
74106           }
74107
74108           function rightClickHouse() {
74109             if (!_houseID) return chapter.restart();
74110             context.enter(modeBrowse(context));
74111             context.history().reset('hasHouse');
74112             var zoom = context.map().zoom();
74113
74114             if (zoom < 20) {
74115               zoom = 20;
74116             }
74117
74118             context.map().centerZoomEase(house, zoom, 500);
74119             context.on('enter.intro', function (mode) {
74120               if (mode.id !== 'select') return;
74121               var ids = context.selectedIDs();
74122               if (ids.length !== 1 || ids[0] !== _houseID) return;
74123               timeout(function () {
74124                 var node = selectMenuItem(context, 'orthogonalize').node();
74125                 if (!node) return;
74126                 continueTo(clickSquare);
74127               }, 50); // after menu visible
74128             });
74129             context.map().on('move.intro drawn.intro', function () {
74130               var rightclickString = helpHtml('intro.buildings.' + (context.lastPointerType() === 'mouse' ? 'rightclick_building' : 'edit_menu_building_touch'));
74131               revealHouse(house, rightclickString, {
74132                 duration: 0
74133               });
74134             });
74135             context.history().on('change.intro', function () {
74136               continueTo(rightClickHouse);
74137             });
74138
74139             function continueTo(nextStep) {
74140               context.on('enter.intro', null);
74141               context.map().on('move.intro drawn.intro', null);
74142               context.history().on('change.intro', null);
74143               nextStep();
74144             }
74145           }
74146
74147           function clickSquare() {
74148             if (!_houseID) return chapter.restart();
74149             var entity = context.hasEntity(_houseID);
74150             if (!entity) return continueTo(rightClickHouse);
74151             var node = selectMenuItem(context, 'orthogonalize').node();
74152
74153             if (!node) {
74154               return continueTo(rightClickHouse);
74155             }
74156
74157             var wasChanged = false;
74158             reveal('.edit-menu', helpHtml('intro.buildings.square_building'), {
74159               padding: 50
74160             });
74161             context.on('enter.intro', function (mode) {
74162               if (mode.id === 'browse') {
74163                 continueTo(rightClickHouse);
74164               } else if (mode.id === 'move' || mode.id === 'rotate') {
74165                 continueTo(retryClickSquare);
74166               }
74167             });
74168             context.map().on('move.intro', function () {
74169               var node = selectMenuItem(context, 'orthogonalize').node();
74170
74171               if (!wasChanged && !node) {
74172                 return continueTo(rightClickHouse);
74173               }
74174
74175               reveal('.edit-menu', helpHtml('intro.buildings.square_building'), {
74176                 duration: 0,
74177                 padding: 50
74178               });
74179             });
74180             context.history().on('change.intro', function () {
74181               wasChanged = true;
74182               context.history().on('change.intro', null); // Something changed.  Wait for transition to complete and check undo annotation.
74183
74184               timeout(function () {
74185                 if (context.history().undoAnnotation() === _t('operations.orthogonalize.annotation.feature', {
74186                   n: 1
74187                 })) {
74188                   continueTo(doneSquare);
74189                 } else {
74190                   continueTo(retryClickSquare);
74191                 }
74192               }, 500); // after transitioned actions
74193             });
74194
74195             function continueTo(nextStep) {
74196               context.on('enter.intro', null);
74197               context.map().on('move.intro', null);
74198               context.history().on('change.intro', null);
74199               nextStep();
74200             }
74201           }
74202
74203           function retryClickSquare() {
74204             context.enter(modeBrowse(context));
74205             revealHouse(house, helpHtml('intro.buildings.retry_square'), {
74206               buttonText: _t.html('intro.ok'),
74207               buttonCallback: function buttonCallback() {
74208                 continueTo(rightClickHouse);
74209               }
74210             });
74211
74212             function continueTo(nextStep) {
74213               nextStep();
74214             }
74215           }
74216
74217           function doneSquare() {
74218             context.history().checkpoint('doneSquare');
74219             revealHouse(house, helpHtml('intro.buildings.done_square'), {
74220               buttonText: _t.html('intro.ok'),
74221               buttonCallback: function buttonCallback() {
74222                 continueTo(addTank);
74223               }
74224             });
74225
74226             function continueTo(nextStep) {
74227               nextStep();
74228             }
74229           }
74230
74231           function addTank() {
74232             context.enter(modeBrowse(context));
74233             context.history().reset('doneSquare');
74234             _tankID = null;
74235             var msec = transitionTime(tank, context.map().center());
74236
74237             if (msec) {
74238               reveal(null, null, {
74239                 duration: 0
74240               });
74241             }
74242
74243             context.map().centerZoomEase(tank, 19.5, msec);
74244             timeout(function () {
74245               reveal('button.add-area', helpHtml('intro.buildings.add_tank'));
74246               context.on('enter.intro', function (mode) {
74247                 if (mode.id !== 'add-area') return;
74248                 continueTo(startTank);
74249               });
74250             }, msec + 100);
74251
74252             function continueTo(nextStep) {
74253               context.on('enter.intro', null);
74254               nextStep();
74255             }
74256           }
74257
74258           function startTank() {
74259             if (context.mode().id !== 'add-area') {
74260               return continueTo(addTank);
74261             }
74262
74263             _tankID = null;
74264             timeout(function () {
74265               var startString = helpHtml('intro.buildings.start_tank') + helpHtml('intro.buildings.tank_edge_' + (context.lastPointerType() === 'mouse' ? 'click' : 'tap'));
74266               revealTank(tank, startString);
74267               context.map().on('move.intro drawn.intro', function () {
74268                 revealTank(tank, startString, {
74269                   duration: 0
74270                 });
74271               });
74272               context.on('enter.intro', function (mode) {
74273                 if (mode.id !== 'draw-area') return chapter.restart();
74274                 continueTo(continueTank);
74275               });
74276             }, 550); // after easing
74277
74278             function continueTo(nextStep) {
74279               context.map().on('move.intro drawn.intro', null);
74280               context.on('enter.intro', null);
74281               nextStep();
74282             }
74283           }
74284
74285           function continueTank() {
74286             if (context.mode().id !== 'draw-area') {
74287               return continueTo(addTank);
74288             }
74289
74290             _tankID = null;
74291             var continueString = helpHtml('intro.buildings.continue_tank') + '{br}' + helpHtml('intro.areas.finish_area_' + (context.lastPointerType() === 'mouse' ? 'click' : 'tap')) + helpHtml('intro.buildings.finish_tank');
74292             revealTank(tank, continueString);
74293             context.map().on('move.intro drawn.intro', function () {
74294               revealTank(tank, continueString, {
74295                 duration: 0
74296               });
74297             });
74298             context.on('enter.intro', function (mode) {
74299               if (mode.id === 'draw-area') {
74300                 return;
74301               } else if (mode.id === 'select') {
74302                 _tankID = context.selectedIDs()[0];
74303                 return continueTo(searchPresetTank);
74304               } else {
74305                 return continueTo(addTank);
74306               }
74307             });
74308
74309             function continueTo(nextStep) {
74310               context.map().on('move.intro drawn.intro', null);
74311               context.on('enter.intro', null);
74312               nextStep();
74313             }
74314           }
74315
74316           function searchPresetTank() {
74317             if (!_tankID || !context.hasEntity(_tankID)) {
74318               return addTank();
74319             }
74320
74321             var ids = context.selectedIDs();
74322
74323             if (context.mode().id !== 'select' || !ids.length || ids[0] !== _tankID) {
74324               context.enter(modeSelect(context, [_tankID]));
74325             } // disallow scrolling
74326
74327
74328             context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
74329             timeout(function () {
74330               // reset pane, in case user somehow happened to change it..
74331               context.container().select('.inspector-wrap .panewrap').style('right', '-100%');
74332               context.container().select('.preset-search-input').on('keydown.intro', null).on('keyup.intro', checkPresetSearch);
74333               reveal('.preset-search-input', helpHtml('intro.buildings.search_tank', {
74334                 preset: tankPreset.name()
74335               }));
74336             }, 400); // after preset list pane visible..
74337
74338             context.on('enter.intro', function (mode) {
74339               if (!_tankID || !context.hasEntity(_tankID)) {
74340                 return continueTo(addTank);
74341               }
74342
74343               var ids = context.selectedIDs();
74344
74345               if (mode.id !== 'select' || !ids.length || ids[0] !== _tankID) {
74346                 // keep the user's area selected..
74347                 context.enter(modeSelect(context, [_tankID])); // reset pane, in case user somehow happened to change it..
74348
74349                 context.container().select('.inspector-wrap .panewrap').style('right', '-100%'); // disallow scrolling
74350
74351                 context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
74352                 context.container().select('.preset-search-input').on('keydown.intro', null).on('keyup.intro', checkPresetSearch);
74353                 reveal('.preset-search-input', helpHtml('intro.buildings.search_tank', {
74354                   preset: tankPreset.name()
74355                 }));
74356                 context.history().on('change.intro', null);
74357               }
74358             });
74359
74360             function checkPresetSearch() {
74361               var first = context.container().select('.preset-list-item:first-child');
74362
74363               if (first.classed('preset-man_made-storage_tank')) {
74364                 reveal(first.select('.preset-list-button').node(), helpHtml('intro.buildings.choose_tank', {
74365                   preset: tankPreset.name()
74366                 }), {
74367                   duration: 300
74368                 });
74369                 context.container().select('.preset-search-input').on('keydown.intro', eventCancel, true).on('keyup.intro', null);
74370                 context.history().on('change.intro', function () {
74371                   continueTo(closeEditorTank);
74372                 });
74373               }
74374             }
74375
74376             function continueTo(nextStep) {
74377               context.container().select('.inspector-wrap').on('wheel.intro', null);
74378               context.on('enter.intro', null);
74379               context.history().on('change.intro', null);
74380               context.container().select('.preset-search-input').on('keydown.intro keyup.intro', null);
74381               nextStep();
74382             }
74383           }
74384
74385           function closeEditorTank() {
74386             if (!_tankID || !context.hasEntity(_tankID)) {
74387               return addTank();
74388             }
74389
74390             var ids = context.selectedIDs();
74391
74392             if (context.mode().id !== 'select' || !ids.length || ids[0] !== _tankID) {
74393               context.enter(modeSelect(context, [_tankID]));
74394             }
74395
74396             context.history().checkpoint('hasTank');
74397             context.on('exit.intro', function () {
74398               continueTo(rightClickTank);
74399             });
74400             timeout(function () {
74401               reveal('.entity-editor-pane', helpHtml('intro.buildings.close', {
74402                 button: icon('#iD-icon-close', 'inline')
74403               }));
74404             }, 500);
74405
74406             function continueTo(nextStep) {
74407               context.on('exit.intro', null);
74408               nextStep();
74409             }
74410           }
74411
74412           function rightClickTank() {
74413             if (!_tankID) return continueTo(addTank);
74414             context.enter(modeBrowse(context));
74415             context.history().reset('hasTank');
74416             context.map().centerEase(tank, 500);
74417             timeout(function () {
74418               context.on('enter.intro', function (mode) {
74419                 if (mode.id !== 'select') return;
74420                 var ids = context.selectedIDs();
74421                 if (ids.length !== 1 || ids[0] !== _tankID) return;
74422                 timeout(function () {
74423                   var node = selectMenuItem(context, 'circularize').node();
74424                   if (!node) return;
74425                   continueTo(clickCircle);
74426                 }, 50); // after menu visible
74427               });
74428               var rightclickString = helpHtml('intro.buildings.' + (context.lastPointerType() === 'mouse' ? 'rightclick_tank' : 'edit_menu_tank_touch'));
74429               revealTank(tank, rightclickString);
74430               context.map().on('move.intro drawn.intro', function () {
74431                 revealTank(tank, rightclickString, {
74432                   duration: 0
74433                 });
74434               });
74435               context.history().on('change.intro', function () {
74436                 continueTo(rightClickTank);
74437               });
74438             }, 600);
74439
74440             function continueTo(nextStep) {
74441               context.on('enter.intro', null);
74442               context.map().on('move.intro drawn.intro', null);
74443               context.history().on('change.intro', null);
74444               nextStep();
74445             }
74446           }
74447
74448           function clickCircle() {
74449             if (!_tankID) return chapter.restart();
74450             var entity = context.hasEntity(_tankID);
74451             if (!entity) return continueTo(rightClickTank);
74452             var node = selectMenuItem(context, 'circularize').node();
74453
74454             if (!node) {
74455               return continueTo(rightClickTank);
74456             }
74457
74458             var wasChanged = false;
74459             reveal('.edit-menu', helpHtml('intro.buildings.circle_tank'), {
74460               padding: 50
74461             });
74462             context.on('enter.intro', function (mode) {
74463               if (mode.id === 'browse') {
74464                 continueTo(rightClickTank);
74465               } else if (mode.id === 'move' || mode.id === 'rotate') {
74466                 continueTo(retryClickCircle);
74467               }
74468             });
74469             context.map().on('move.intro', function () {
74470               var node = selectMenuItem(context, 'circularize').node();
74471
74472               if (!wasChanged && !node) {
74473                 return continueTo(rightClickTank);
74474               }
74475
74476               reveal('.edit-menu', helpHtml('intro.buildings.circle_tank'), {
74477                 duration: 0,
74478                 padding: 50
74479               });
74480             });
74481             context.history().on('change.intro', function () {
74482               wasChanged = true;
74483               context.history().on('change.intro', null); // Something changed.  Wait for transition to complete and check undo annotation.
74484
74485               timeout(function () {
74486                 if (context.history().undoAnnotation() === _t('operations.circularize.annotation.feature', {
74487                   n: 1
74488                 })) {
74489                   continueTo(play);
74490                 } else {
74491                   continueTo(retryClickCircle);
74492                 }
74493               }, 500); // after transitioned actions
74494             });
74495
74496             function continueTo(nextStep) {
74497               context.on('enter.intro', null);
74498               context.map().on('move.intro', null);
74499               context.history().on('change.intro', null);
74500               nextStep();
74501             }
74502           }
74503
74504           function retryClickCircle() {
74505             context.enter(modeBrowse(context));
74506             revealTank(tank, helpHtml('intro.buildings.retry_circle'), {
74507               buttonText: _t.html('intro.ok'),
74508               buttonCallback: function buttonCallback() {
74509                 continueTo(rightClickTank);
74510               }
74511             });
74512
74513             function continueTo(nextStep) {
74514               nextStep();
74515             }
74516           }
74517
74518           function play() {
74519             dispatch.call('done');
74520             reveal('.ideditor', helpHtml('intro.buildings.play', {
74521               next: _t('intro.startediting.title')
74522             }), {
74523               tooltipBox: '.intro-nav-wrap .chapter-startEditing',
74524               buttonText: _t.html('intro.ok'),
74525               buttonCallback: function buttonCallback() {
74526                 reveal('.ideditor');
74527               }
74528             });
74529           }
74530
74531           chapter.enter = function () {
74532             addHouse();
74533           };
74534
74535           chapter.exit = function () {
74536             timeouts.forEach(window.clearTimeout);
74537             context.on('enter.intro exit.intro', null);
74538             context.map().on('move.intro drawn.intro', null);
74539             context.history().on('change.intro', null);
74540             context.container().select('.inspector-wrap').on('wheel.intro', null);
74541             context.container().select('.preset-search-input').on('keydown.intro keyup.intro', null);
74542             context.container().select('.more-fields .combobox-input').on('click.intro', null);
74543           };
74544
74545           chapter.restart = function () {
74546             chapter.exit();
74547             chapter.enter();
74548           };
74549
74550           return utilRebind(chapter, dispatch, 'on');
74551         }
74552
74553         function uiIntroStartEditing(context, reveal) {
74554           var dispatch = dispatch$8('done', 'startEditing');
74555           var modalSelection = select(null);
74556           var chapter = {
74557             title: 'intro.startediting.title'
74558           };
74559
74560           function showHelp() {
74561             reveal('.map-control.help-control', helpHtml('intro.startediting.help'), {
74562               buttonText: _t.html('intro.ok'),
74563               buttonCallback: function buttonCallback() {
74564                 shortcuts();
74565               }
74566             });
74567           }
74568
74569           function shortcuts() {
74570             reveal('.map-control.help-control', helpHtml('intro.startediting.shortcuts'), {
74571               buttonText: _t.html('intro.ok'),
74572               buttonCallback: function buttonCallback() {
74573                 showSave();
74574               }
74575             });
74576           }
74577
74578           function showSave() {
74579             context.container().selectAll('.shaded').remove(); // in case user opened keyboard shortcuts
74580
74581             reveal('.top-toolbar button.save', helpHtml('intro.startediting.save'), {
74582               buttonText: _t.html('intro.ok'),
74583               buttonCallback: function buttonCallback() {
74584                 showStart();
74585               }
74586             });
74587           }
74588
74589           function showStart() {
74590             context.container().selectAll('.shaded').remove(); // in case user opened keyboard shortcuts
74591
74592             modalSelection = uiModal(context.container());
74593             modalSelection.select('.modal').attr('class', 'modal-splash modal');
74594             modalSelection.selectAll('.close').remove();
74595             var startbutton = modalSelection.select('.content').attr('class', 'fillL').append('button').attr('class', 'modal-section huge-modal-button').on('click', function () {
74596               modalSelection.remove();
74597             });
74598             startbutton.append('svg').attr('class', 'illustration').append('use').attr('xlink:href', '#iD-logo-walkthrough');
74599             startbutton.append('h2').html(_t.html('intro.startediting.start'));
74600             dispatch.call('startEditing');
74601           }
74602
74603           chapter.enter = function () {
74604             showHelp();
74605           };
74606
74607           chapter.exit = function () {
74608             modalSelection.remove();
74609             context.container().selectAll('.shaded').remove(); // in case user opened keyboard shortcuts
74610           };
74611
74612           return utilRebind(chapter, dispatch, 'on');
74613         }
74614
74615         var chapterUi = {
74616           welcome: uiIntroWelcome,
74617           navigation: uiIntroNavigation,
74618           point: uiIntroPoint,
74619           area: uiIntroArea,
74620           line: uiIntroLine,
74621           building: uiIntroBuilding,
74622           startEditing: uiIntroStartEditing
74623         };
74624         var chapterFlow = ['welcome', 'navigation', 'point', 'area', 'line', 'building', 'startEditing'];
74625         function uiIntro(context) {
74626           var INTRO_IMAGERY = 'EsriWorldImageryClarity';
74627           var _introGraph = {};
74628
74629           var _currChapter;
74630
74631           function intro(selection) {
74632             _mainFileFetcher.get('intro_graph').then(function (dataIntroGraph) {
74633               // create entities for intro graph and localize names
74634               for (var id in dataIntroGraph) {
74635                 if (!_introGraph[id]) {
74636                   _introGraph[id] = osmEntity(localize(dataIntroGraph[id]));
74637                 }
74638               }
74639
74640               selection.call(startIntro);
74641             })["catch"](function () {
74642               /* ignore */
74643             });
74644           }
74645
74646           function startIntro(selection) {
74647             context.enter(modeBrowse(context)); // Save current map state
74648
74649             var osm = context.connection();
74650             var history = context.history().toJSON();
74651             var hash = window.location.hash;
74652             var center = context.map().center();
74653             var zoom = context.map().zoom();
74654             var background = context.background().baseLayerSource();
74655             var overlays = context.background().overlayLayerSources();
74656             var opacity = context.container().selectAll('.main-map .layer-background').style('opacity');
74657             var caches = osm && osm.caches();
74658             var baseEntities = context.history().graph().base().entities; // Show sidebar and disable the sidebar resizing button
74659             // (this needs to be before `context.inIntro(true)`)
74660
74661             context.ui().sidebar.expand();
74662             context.container().selectAll('button.sidebar-toggle').classed('disabled', true); // Block saving
74663
74664             context.inIntro(true); // Load semi-real data used in intro
74665
74666             if (osm) {
74667               osm.toggle(false).reset();
74668             }
74669
74670             context.history().reset();
74671             context.history().merge(Object.values(coreGraph().load(_introGraph).entities));
74672             context.history().checkpoint('initial'); // Setup imagery
74673
74674             var imagery = context.background().findSource(INTRO_IMAGERY);
74675
74676             if (imagery) {
74677               context.background().baseLayerSource(imagery);
74678             } else {
74679               context.background().bing();
74680             }
74681
74682             overlays.forEach(function (d) {
74683               return context.background().toggleOverlayLayer(d);
74684             }); // Setup data layers (only OSM)
74685
74686             var layers = context.layers();
74687             layers.all().forEach(function (item) {
74688               // if the layer has the function `enabled`
74689               if (typeof item.layer.enabled === 'function') {
74690                 item.layer.enabled(item.id === 'osm');
74691               }
74692             });
74693             context.container().selectAll('.main-map .layer-background').style('opacity', 1);
74694             var curtain = uiCurtain(context.container().node());
74695             selection.call(curtain); // Store that the user started the walkthrough..
74696
74697             corePreferences('walkthrough_started', 'yes'); // Restore previous walkthrough progress..
74698
74699             var storedProgress = corePreferences('walkthrough_progress') || '';
74700             var progress = storedProgress.split(';').filter(Boolean);
74701             var chapters = chapterFlow.map(function (chapter, i) {
74702               var s = chapterUi[chapter](context, curtain.reveal).on('done', function () {
74703                 buttons.filter(function (d) {
74704                   return d.title === s.title;
74705                 }).classed('finished', true);
74706
74707                 if (i < chapterFlow.length - 1) {
74708                   var next = chapterFlow[i + 1];
74709                   context.container().select("button.chapter-".concat(next)).classed('next', true);
74710                 } // Store walkthrough progress..
74711
74712
74713                 progress.push(chapter);
74714                 corePreferences('walkthrough_progress', utilArrayUniq(progress).join(';'));
74715               });
74716               return s;
74717             });
74718             chapters[chapters.length - 1].on('startEditing', function () {
74719               // Store walkthrough progress..
74720               progress.push('startEditing');
74721               corePreferences('walkthrough_progress', utilArrayUniq(progress).join(';')); // Store if walkthrough is completed..
74722
74723               var incomplete = utilArrayDifference(chapterFlow, progress);
74724
74725               if (!incomplete.length) {
74726                 corePreferences('walkthrough_completed', 'yes');
74727               }
74728
74729               curtain.remove();
74730               navwrap.remove();
74731               context.container().selectAll('.main-map .layer-background').style('opacity', opacity);
74732               context.container().selectAll('button.sidebar-toggle').classed('disabled', false);
74733
74734               if (osm) {
74735                 osm.toggle(true).reset().caches(caches);
74736               }
74737
74738               context.history().reset().merge(Object.values(baseEntities));
74739               context.background().baseLayerSource(background);
74740               overlays.forEach(function (d) {
74741                 return context.background().toggleOverlayLayer(d);
74742               });
74743
74744               if (history) {
74745                 context.history().fromJSON(history, false);
74746               }
74747
74748               context.map().centerZoom(center, zoom);
74749               window.location.replace(hash);
74750               context.inIntro(false);
74751             });
74752             var navwrap = selection.append('div').attr('class', 'intro-nav-wrap fillD');
74753             navwrap.append('svg').attr('class', 'intro-nav-wrap-logo').append('use').attr('xlink:href', '#iD-logo-walkthrough');
74754             var buttonwrap = navwrap.append('div').attr('class', 'joined').selectAll('button.chapter');
74755             var buttons = buttonwrap.data(chapters).enter().append('button').attr('class', function (d, i) {
74756               return "chapter chapter-".concat(chapterFlow[i]);
74757             }).on('click', enterChapter);
74758             buttons.append('span').html(function (d) {
74759               return _t.html(d.title);
74760             });
74761             buttons.append('span').attr('class', 'status').call(svgIcon(_mainLocalizer.textDirection() === 'rtl' ? '#iD-icon-backward' : '#iD-icon-forward', 'inline'));
74762             enterChapter(null, chapters[0]);
74763
74764             function enterChapter(d3_event, newChapter) {
74765               if (_currChapter) {
74766                 _currChapter.exit();
74767               }
74768
74769               context.enter(modeBrowse(context));
74770               _currChapter = newChapter;
74771
74772               _currChapter.enter();
74773
74774               buttons.classed('next', false).classed('active', function (d) {
74775                 return d.title === _currChapter.title;
74776               });
74777             }
74778           }
74779
74780           return intro;
74781         }
74782
74783         function uiIssuesInfo(context) {
74784           var warningsItem = {
74785             id: 'warnings',
74786             count: 0,
74787             iconID: 'iD-icon-alert',
74788             descriptionID: 'issues.warnings_and_errors'
74789           };
74790           var resolvedItem = {
74791             id: 'resolved',
74792             count: 0,
74793             iconID: 'iD-icon-apply',
74794             descriptionID: 'issues.user_resolved_issues'
74795           };
74796
74797           function update(selection) {
74798             var shownItems = [];
74799             var liveIssues = context.validator().getIssues({
74800               what: corePreferences('validate-what') || 'edited',
74801               where: corePreferences('validate-where') || 'all'
74802             });
74803
74804             if (liveIssues.length) {
74805               warningsItem.count = liveIssues.length;
74806               shownItems.push(warningsItem);
74807             }
74808
74809             if (corePreferences('validate-what') === 'all') {
74810               var resolvedIssues = context.validator().getResolvedIssues();
74811
74812               if (resolvedIssues.length) {
74813                 resolvedItem.count = resolvedIssues.length;
74814                 shownItems.push(resolvedItem);
74815               }
74816             }
74817
74818             var chips = selection.selectAll('.chip').data(shownItems, function (d) {
74819               return d.id;
74820             });
74821             chips.exit().remove();
74822             var enter = chips.enter().append('a').attr('class', function (d) {
74823               return 'chip ' + d.id + '-count';
74824             }).attr('href', '#').each(function (d) {
74825               var chipSelection = select(this);
74826               var tooltipBehavior = uiTooltip().placement('top').title(_t.html(d.descriptionID));
74827               chipSelection.call(tooltipBehavior).on('click', function (d3_event) {
74828                 d3_event.preventDefault();
74829                 tooltipBehavior.hide(select(this)); // open the Issues pane
74830
74831                 context.ui().togglePanes(context.container().select('.map-panes .issues-pane'));
74832               });
74833               chipSelection.call(svgIcon('#' + d.iconID));
74834             });
74835             enter.append('span').attr('class', 'count');
74836             enter.merge(chips).selectAll('span.count').html(function (d) {
74837               return d.count.toString();
74838             });
74839           }
74840
74841           return function (selection) {
74842             update(selection);
74843             context.validator().on('validated.infobox', function () {
74844               update(selection);
74845             });
74846           };
74847         }
74848
74849         function uiMapInMap(context) {
74850           function mapInMap(selection) {
74851             var backgroundLayer = rendererTileLayer(context);
74852             var overlayLayers = {};
74853             var projection = geoRawMercator();
74854             var dataLayer = svgData(projection, context).showLabels(false);
74855             var debugLayer = svgDebug(projection, context);
74856             var zoom = d3_zoom().scaleExtent([geoZoomToScale(0.5), geoZoomToScale(24)]).on('start', zoomStarted).on('zoom', zoomed).on('end', zoomEnded);
74857             var wrap = select(null);
74858             var tiles = select(null);
74859             var viewport = select(null);
74860             var _isTransformed = false;
74861             var _isHidden = true;
74862             var _skipEvents = false;
74863             var _gesture = null;
74864             var _zDiff = 6; // by default, minimap renders at (main zoom - 6)
74865
74866             var _dMini; // dimensions of minimap
74867
74868
74869             var _cMini; // center pixel of minimap
74870
74871
74872             var _tStart; // transform at start of gesture
74873
74874
74875             var _tCurr; // transform at most recent event
74876
74877
74878             var _timeoutID;
74879
74880             function zoomStarted() {
74881               if (_skipEvents) return;
74882               _tStart = _tCurr = projection.transform();
74883               _gesture = null;
74884             }
74885
74886             function zoomed(d3_event) {
74887               if (_skipEvents) return;
74888               var x = d3_event.transform.x;
74889               var y = d3_event.transform.y;
74890               var k = d3_event.transform.k;
74891               var isZooming = k !== _tStart.k;
74892               var isPanning = x !== _tStart.x || y !== _tStart.y;
74893
74894               if (!isZooming && !isPanning) {
74895                 return; // no change
74896               } // lock in either zooming or panning, don't allow both in minimap.
74897
74898
74899               if (!_gesture) {
74900                 _gesture = isZooming ? 'zoom' : 'pan';
74901               }
74902
74903               var tMini = projection.transform();
74904               var tX, tY, scale;
74905
74906               if (_gesture === 'zoom') {
74907                 scale = k / tMini.k;
74908                 tX = (_cMini[0] / scale - _cMini[0]) * scale;
74909                 tY = (_cMini[1] / scale - _cMini[1]) * scale;
74910               } else {
74911                 k = tMini.k;
74912                 scale = 1;
74913                 tX = x - tMini.x;
74914                 tY = y - tMini.y;
74915               }
74916
74917               utilSetTransform(tiles, tX, tY, scale);
74918               utilSetTransform(viewport, 0, 0, scale);
74919               _isTransformed = true;
74920               _tCurr = identity$2.translate(x, y).scale(k);
74921               var zMain = geoScaleToZoom(context.projection.scale());
74922               var zMini = geoScaleToZoom(k);
74923               _zDiff = zMain - zMini;
74924               queueRedraw();
74925             }
74926
74927             function zoomEnded() {
74928               if (_skipEvents) return;
74929               if (_gesture !== 'pan') return;
74930               updateProjection();
74931               _gesture = null;
74932               context.map().center(projection.invert(_cMini)); // recenter main map..
74933             }
74934
74935             function updateProjection() {
74936               var loc = context.map().center();
74937               var tMain = context.projection.transform();
74938               var zMain = geoScaleToZoom(tMain.k);
74939               var zMini = Math.max(zMain - _zDiff, 0.5);
74940               var kMini = geoZoomToScale(zMini);
74941               projection.translate([tMain.x, tMain.y]).scale(kMini);
74942               var point = projection(loc);
74943               var mouse = _gesture === 'pan' ? geoVecSubtract([_tCurr.x, _tCurr.y], [_tStart.x, _tStart.y]) : [0, 0];
74944               var xMini = _cMini[0] - point[0] + tMain.x + mouse[0];
74945               var yMini = _cMini[1] - point[1] + tMain.y + mouse[1];
74946               projection.translate([xMini, yMini]).clipExtent([[0, 0], _dMini]);
74947               _tCurr = projection.transform();
74948
74949               if (_isTransformed) {
74950                 utilSetTransform(tiles, 0, 0);
74951                 utilSetTransform(viewport, 0, 0);
74952                 _isTransformed = false;
74953               }
74954
74955               zoom.scaleExtent([geoZoomToScale(0.5), geoZoomToScale(zMain - 3)]);
74956               _skipEvents = true;
74957               wrap.call(zoom.transform, _tCurr);
74958               _skipEvents = false;
74959             }
74960
74961             function redraw() {
74962               clearTimeout(_timeoutID);
74963               if (_isHidden) return;
74964               updateProjection();
74965               var zMini = geoScaleToZoom(projection.scale()); // setup tile container
74966
74967               tiles = wrap.selectAll('.map-in-map-tiles').data([0]);
74968               tiles = tiles.enter().append('div').attr('class', 'map-in-map-tiles').merge(tiles); // redraw background
74969
74970               backgroundLayer.source(context.background().baseLayerSource()).projection(projection).dimensions(_dMini);
74971               var background = tiles.selectAll('.map-in-map-background').data([0]);
74972               background.enter().append('div').attr('class', 'map-in-map-background').merge(background).call(backgroundLayer); // redraw overlay
74973
74974               var overlaySources = context.background().overlayLayerSources();
74975               var activeOverlayLayers = [];
74976
74977               for (var i = 0; i < overlaySources.length; i++) {
74978                 if (overlaySources[i].validZoom(zMini)) {
74979                   if (!overlayLayers[i]) overlayLayers[i] = rendererTileLayer(context);
74980                   activeOverlayLayers.push(overlayLayers[i].source(overlaySources[i]).projection(projection).dimensions(_dMini));
74981                 }
74982               }
74983
74984               var overlay = tiles.selectAll('.map-in-map-overlay').data([0]);
74985               overlay = overlay.enter().append('div').attr('class', 'map-in-map-overlay').merge(overlay);
74986               var overlays = overlay.selectAll('div').data(activeOverlayLayers, function (d) {
74987                 return d.source().name();
74988               });
74989               overlays.exit().remove();
74990               overlays = overlays.enter().append('div').merge(overlays).each(function (layer) {
74991                 select(this).call(layer);
74992               });
74993               var dataLayers = tiles.selectAll('.map-in-map-data').data([0]);
74994               dataLayers.exit().remove();
74995               dataLayers = dataLayers.enter().append('svg').attr('class', 'map-in-map-data').merge(dataLayers).call(dataLayer).call(debugLayer); // redraw viewport bounding box
74996
74997               if (_gesture !== 'pan') {
74998                 var getPath = d3_geoPath(projection);
74999                 var bbox = {
75000                   type: 'Polygon',
75001                   coordinates: [context.map().extent().polygon()]
75002                 };
75003                 viewport = wrap.selectAll('.map-in-map-viewport').data([0]);
75004                 viewport = viewport.enter().append('svg').attr('class', 'map-in-map-viewport').merge(viewport);
75005                 var path = viewport.selectAll('.map-in-map-bbox').data([bbox]);
75006                 path.enter().append('path').attr('class', 'map-in-map-bbox').merge(path).attr('d', getPath).classed('thick', function (d) {
75007                   return getPath.area(d) < 30;
75008                 });
75009               }
75010             }
75011
75012             function queueRedraw() {
75013               clearTimeout(_timeoutID);
75014               _timeoutID = setTimeout(function () {
75015                 redraw();
75016               }, 750);
75017             }
75018
75019             function toggle(d3_event) {
75020               if (d3_event) d3_event.preventDefault();
75021               _isHidden = !_isHidden;
75022               context.container().select('.minimap-toggle-item').classed('active', !_isHidden).select('input').property('checked', !_isHidden);
75023
75024               if (_isHidden) {
75025                 wrap.style('display', 'block').style('opacity', '1').transition().duration(200).style('opacity', '0').on('end', function () {
75026                   selection.selectAll('.map-in-map').style('display', 'none');
75027                 });
75028               } else {
75029                 wrap.style('display', 'block').style('opacity', '0').transition().duration(200).style('opacity', '1').on('end', function () {
75030                   redraw();
75031                 });
75032               }
75033             }
75034
75035             uiMapInMap.toggle = toggle;
75036             wrap = selection.selectAll('.map-in-map').data([0]);
75037             wrap = wrap.enter().append('div').attr('class', 'map-in-map').style('display', _isHidden ? 'none' : 'block').call(zoom).on('dblclick.zoom', null).merge(wrap); // reflow warning: Hardcode dimensions - currently can't resize it anyway..
75038
75039             _dMini = [200, 150]; //utilGetDimensions(wrap);
75040
75041             _cMini = geoVecScale(_dMini, 0.5);
75042             context.map().on('drawn.map-in-map', function (drawn) {
75043               if (drawn.full === true) {
75044                 redraw();
75045               }
75046             });
75047             redraw();
75048             context.keybinding().on(_t('background.minimap.key'), toggle);
75049           }
75050
75051           return mapInMap;
75052         }
75053
75054         function uiNotice(context) {
75055           return function (selection) {
75056             var div = selection.append('div').attr('class', 'notice');
75057             var button = div.append('button').attr('class', 'zoom-to notice fillD').on('click', function () {
75058               context.map().zoomEase(context.minEditableZoom());
75059             }).on('wheel', function (d3_event) {
75060               // let wheel events pass through #4482
75061               var e2 = new WheelEvent(d3_event.type, d3_event);
75062               context.surface().node().dispatchEvent(e2);
75063             });
75064             button.call(svgIcon('#iD-icon-plus', 'pre-text')).append('span').attr('class', 'label').html(_t.html('zoom_in_edit'));
75065
75066             function disableTooHigh() {
75067               var canEdit = context.map().zoom() >= context.minEditableZoom();
75068               div.style('display', canEdit ? 'none' : 'block');
75069             }
75070
75071             context.map().on('move.notice', debounce(disableTooHigh, 500));
75072             disableTooHigh();
75073           };
75074         }
75075
75076         function uiPhotoviewer(context) {
75077           var dispatch = dispatch$8('resize');
75078
75079           var _pointerPrefix = 'PointerEvent' in window ? 'pointer' : 'mouse';
75080
75081           function photoviewer(selection) {
75082             selection.append('button').attr('class', 'thumb-hide').on('click', function () {
75083               if (services.streetside) {
75084                 services.streetside.hideViewer(context);
75085               }
75086
75087               if (services.mapillary) {
75088                 services.mapillary.hideViewer(context);
75089               }
75090
75091               if (services.openstreetcam) {
75092                 services.openstreetcam.hideViewer(context);
75093               }
75094             }).append('div').call(svgIcon('#iD-icon-close'));
75095
75096             function preventDefault(d3_event) {
75097               d3_event.preventDefault();
75098             }
75099
75100             selection.append('button').attr('class', 'resize-handle-xy').on('touchstart touchdown touchend', preventDefault).on(_pointerPrefix + 'down', buildResizeListener(selection, 'resize', dispatch, {
75101               resizeOnX: true,
75102               resizeOnY: true
75103             }));
75104             selection.append('button').attr('class', 'resize-handle-x').on('touchstart touchdown touchend', preventDefault).on(_pointerPrefix + 'down', buildResizeListener(selection, 'resize', dispatch, {
75105               resizeOnX: true
75106             }));
75107             selection.append('button').attr('class', 'resize-handle-y').on('touchstart touchdown touchend', preventDefault).on(_pointerPrefix + 'down', buildResizeListener(selection, 'resize', dispatch, {
75108               resizeOnY: true
75109             }));
75110
75111             function buildResizeListener(target, eventName, dispatch, options) {
75112               var resizeOnX = !!options.resizeOnX;
75113               var resizeOnY = !!options.resizeOnY;
75114               var minHeight = options.minHeight || 240;
75115               var minWidth = options.minWidth || 320;
75116               var pointerId;
75117               var startX;
75118               var startY;
75119               var startWidth;
75120               var startHeight;
75121
75122               function startResize(d3_event) {
75123                 if (pointerId !== (d3_event.pointerId || 'mouse')) return;
75124                 d3_event.preventDefault();
75125                 d3_event.stopPropagation();
75126                 var mapSize = context.map().dimensions();
75127
75128                 if (resizeOnX) {
75129                   var maxWidth = mapSize[0];
75130                   var newWidth = clamp(startWidth + d3_event.clientX - startX, minWidth, maxWidth);
75131                   target.style('width', newWidth + 'px');
75132                 }
75133
75134                 if (resizeOnY) {
75135                   var maxHeight = mapSize[1] - 90; // preserve space at top/bottom of map
75136
75137                   var newHeight = clamp(startHeight + startY - d3_event.clientY, minHeight, maxHeight);
75138                   target.style('height', newHeight + 'px');
75139                 }
75140
75141                 dispatch.call(eventName, target, utilGetDimensions(target, true));
75142               }
75143
75144               function clamp(num, min, max) {
75145                 return Math.max(min, Math.min(num, max));
75146               }
75147
75148               function stopResize(d3_event) {
75149                 if (pointerId !== (d3_event.pointerId || 'mouse')) return;
75150                 d3_event.preventDefault();
75151                 d3_event.stopPropagation(); // remove all the listeners we added
75152
75153                 select(window).on('.' + eventName, null);
75154               }
75155
75156               return function initResize(d3_event) {
75157                 d3_event.preventDefault();
75158                 d3_event.stopPropagation();
75159                 pointerId = d3_event.pointerId || 'mouse';
75160                 startX = d3_event.clientX;
75161                 startY = d3_event.clientY;
75162                 var targetRect = target.node().getBoundingClientRect();
75163                 startWidth = targetRect.width;
75164                 startHeight = targetRect.height;
75165                 select(window).on(_pointerPrefix + 'move.' + eventName, startResize, false).on(_pointerPrefix + 'up.' + eventName, stopResize, false);
75166
75167                 if (_pointerPrefix === 'pointer') {
75168                   select(window).on('pointercancel.' + eventName, stopResize, false);
75169                 }
75170               };
75171             }
75172           }
75173
75174           photoviewer.onMapResize = function () {
75175             var photoviewer = context.container().select('.photoviewer');
75176             var content = context.container().select('.main-content');
75177             var mapDimensions = utilGetDimensions(content, true); // shrink photo viewer if it is too big
75178             // (-90 preserves space at top and bottom of map used by menus)
75179
75180             var photoDimensions = utilGetDimensions(photoviewer, true);
75181
75182             if (photoDimensions[0] > mapDimensions[0] || photoDimensions[1] > mapDimensions[1] - 90) {
75183               var setPhotoDimensions = [Math.min(photoDimensions[0], mapDimensions[0]), Math.min(photoDimensions[1], mapDimensions[1] - 90)];
75184               photoviewer.style('width', setPhotoDimensions[0] + 'px').style('height', setPhotoDimensions[1] + 'px');
75185               dispatch.call('resize', photoviewer, setPhotoDimensions);
75186             }
75187           };
75188
75189           return utilRebind(photoviewer, dispatch, 'on');
75190         }
75191
75192         function uiRestore(context) {
75193           return function (selection) {
75194             if (!context.history().hasRestorableChanges()) return;
75195             var modalSelection = uiModal(selection, true);
75196             modalSelection.select('.modal').attr('class', 'modal fillL');
75197             var introModal = modalSelection.select('.content');
75198             introModal.append('div').attr('class', 'modal-section').append('h3').html(_t.html('restore.heading'));
75199             introModal.append('div').attr('class', 'modal-section').append('p').html(_t.html('restore.description'));
75200             var buttonWrap = introModal.append('div').attr('class', 'modal-actions');
75201             var restore = buttonWrap.append('button').attr('class', 'restore').on('click', function () {
75202               context.history().restore();
75203               modalSelection.remove();
75204             });
75205             restore.append('svg').attr('class', 'logo logo-restore').append('use').attr('xlink:href', '#iD-logo-restore');
75206             restore.append('div').html(_t.html('restore.restore'));
75207             var reset = buttonWrap.append('button').attr('class', 'reset').on('click', function () {
75208               context.history().clearSaved();
75209               modalSelection.remove();
75210             });
75211             reset.append('svg').attr('class', 'logo logo-reset').append('use').attr('xlink:href', '#iD-logo-reset');
75212             reset.append('div').html(_t.html('restore.reset'));
75213             restore.node().focus();
75214           };
75215         }
75216
75217         function uiScale(context) {
75218           var projection = context.projection,
75219               isImperial = !_mainLocalizer.usesMetric(),
75220               maxLength = 180,
75221               tickHeight = 8;
75222
75223           function scaleDefs(loc1, loc2) {
75224             var lat = (loc2[1] + loc1[1]) / 2,
75225                 conversion = isImperial ? 3.28084 : 1,
75226                 dist = geoLonToMeters(loc2[0] - loc1[0], lat) * conversion,
75227                 scale = {
75228               dist: 0,
75229               px: 0,
75230               text: ''
75231             },
75232                 buckets,
75233                 i,
75234                 val,
75235                 dLon;
75236
75237             if (isImperial) {
75238               buckets = [5280000, 528000, 52800, 5280, 500, 50, 5, 1];
75239             } else {
75240               buckets = [5000000, 500000, 50000, 5000, 500, 50, 5, 1];
75241             } // determine a user-friendly endpoint for the scale
75242
75243
75244             for (i = 0; i < buckets.length; i++) {
75245               val = buckets[i];
75246
75247               if (dist >= val) {
75248                 scale.dist = Math.floor(dist / val) * val;
75249                 break;
75250               } else {
75251                 scale.dist = +dist.toFixed(2);
75252               }
75253             }
75254
75255             dLon = geoMetersToLon(scale.dist / conversion, lat);
75256             scale.px = Math.round(projection([loc1[0] + dLon, loc1[1]])[0]);
75257             scale.text = displayLength(scale.dist / conversion, isImperial);
75258             return scale;
75259           }
75260
75261           function update(selection) {
75262             // choose loc1, loc2 along bottom of viewport (near where the scale will be drawn)
75263             var dims = context.map().dimensions(),
75264                 loc1 = projection.invert([0, dims[1]]),
75265                 loc2 = projection.invert([maxLength, dims[1]]),
75266                 scale = scaleDefs(loc1, loc2);
75267             selection.select('.scale-path').attr('d', 'M0.5,0.5v' + tickHeight + 'h' + scale.px + 'v-' + tickHeight);
75268             selection.select('.scale-text').style(_mainLocalizer.textDirection() === 'ltr' ? 'left' : 'right', scale.px + 16 + 'px').html(scale.text);
75269           }
75270
75271           return function (selection) {
75272             function switchUnits() {
75273               isImperial = !isImperial;
75274               selection.call(update);
75275             }
75276
75277             var scalegroup = selection.append('svg').attr('class', 'scale').on('click', switchUnits).append('g').attr('transform', 'translate(10,11)');
75278             scalegroup.append('path').attr('class', 'scale-path');
75279             selection.append('div').attr('class', 'scale-text');
75280             selection.call(update);
75281             context.map().on('move.scale', function () {
75282               update(selection);
75283             });
75284           };
75285         }
75286
75287         function uiShortcuts(context) {
75288           var detected = utilDetect();
75289           var _activeTab = 0;
75290
75291           var _modalSelection;
75292
75293           var _selection = select(null);
75294
75295           var _dataShortcuts;
75296
75297           function shortcutsModal(_modalSelection) {
75298             _modalSelection.select('.modal').classed('modal-shortcuts', true);
75299
75300             var content = _modalSelection.select('.content');
75301
75302             content.append('div').attr('class', 'modal-section').append('h3').html(_t.html('shortcuts.title'));
75303             _mainFileFetcher.get('shortcuts').then(function (data) {
75304               _dataShortcuts = data;
75305               content.call(render);
75306             })["catch"](function () {
75307               /* ignore */
75308             });
75309           }
75310
75311           function render(selection) {
75312             if (!_dataShortcuts) return;
75313             var wrapper = selection.selectAll('.wrapper').data([0]);
75314             var wrapperEnter = wrapper.enter().append('div').attr('class', 'wrapper modal-section');
75315             var tabsBar = wrapperEnter.append('div').attr('class', 'tabs-bar');
75316             var shortcutsList = wrapperEnter.append('div').attr('class', 'shortcuts-list');
75317             wrapper = wrapper.merge(wrapperEnter);
75318             var tabs = tabsBar.selectAll('.tab').data(_dataShortcuts);
75319             var tabsEnter = tabs.enter().append('a').attr('class', 'tab').attr('href', '#').on('click', function (d3_event, d) {
75320               d3_event.preventDefault();
75321
75322               var i = _dataShortcuts.indexOf(d);
75323
75324               _activeTab = i;
75325               render(selection);
75326             });
75327             tabsEnter.append('span').html(function (d) {
75328               return _t.html(d.text);
75329             }); // Update
75330
75331             wrapper.selectAll('.tab').classed('active', function (d, i) {
75332               return i === _activeTab;
75333             });
75334             var shortcuts = shortcutsList.selectAll('.shortcut-tab').data(_dataShortcuts);
75335             var shortcutsEnter = shortcuts.enter().append('div').attr('class', function (d) {
75336               return 'shortcut-tab shortcut-tab-' + d.tab;
75337             });
75338             var columnsEnter = shortcutsEnter.selectAll('.shortcut-column').data(function (d) {
75339               return d.columns;
75340             }).enter().append('table').attr('class', 'shortcut-column');
75341             var rowsEnter = columnsEnter.selectAll('.shortcut-row').data(function (d) {
75342               return d.rows;
75343             }).enter().append('tr').attr('class', 'shortcut-row');
75344             var sectionRows = rowsEnter.filter(function (d) {
75345               return !d.shortcuts;
75346             });
75347             sectionRows.append('td');
75348             sectionRows.append('td').attr('class', 'shortcut-section').append('h3').html(function (d) {
75349               return _t.html(d.text);
75350             });
75351             var shortcutRows = rowsEnter.filter(function (d) {
75352               return d.shortcuts;
75353             });
75354             var shortcutKeys = shortcutRows.append('td').attr('class', 'shortcut-keys');
75355             var modifierKeys = shortcutKeys.filter(function (d) {
75356               return d.modifiers;
75357             });
75358             modifierKeys.selectAll('kbd.modifier').data(function (d) {
75359               if (detected.os === 'win' && d.text === 'shortcuts.editing.commands.redo') {
75360                 return ['⌘'];
75361               } else if (detected.os !== 'mac' && d.text === 'shortcuts.browsing.display_options.fullscreen') {
75362                 return [];
75363               } else {
75364                 return d.modifiers;
75365               }
75366             }).enter().each(function () {
75367               var selection = select(this);
75368               selection.append('kbd').attr('class', 'modifier').html(function (d) {
75369                 return uiCmd.display(d);
75370               });
75371               selection.append('span').html('+');
75372             });
75373             shortcutKeys.selectAll('kbd.shortcut').data(function (d) {
75374               var arr = d.shortcuts;
75375
75376               if (detected.os === 'win' && d.text === 'shortcuts.editing.commands.redo') {
75377                 arr = ['Y'];
75378               } else if (detected.os !== 'mac' && d.text === 'shortcuts.browsing.display_options.fullscreen') {
75379                 arr = ['F11'];
75380               } // replace translations
75381
75382
75383               arr = arr.map(function (s) {
75384                 return uiCmd.display(s.indexOf('.') !== -1 ? _t(s) : s);
75385               });
75386               return utilArrayUniq(arr).map(function (s) {
75387                 return {
75388                   shortcut: s,
75389                   separator: d.separator,
75390                   suffix: d.suffix
75391                 };
75392               });
75393             }).enter().each(function (d, i, nodes) {
75394               var selection = select(this);
75395               var click = d.shortcut.toLowerCase().match(/(.*).click/);
75396
75397               if (click && click[1]) {
75398                 // replace "left_click", "right_click" with mouse icon
75399                 selection.call(svgIcon('#iD-walkthrough-mouse-' + click[1], 'operation'));
75400               } else if (d.shortcut.toLowerCase() === 'long-press') {
75401                 selection.call(svgIcon('#iD-walkthrough-longpress', 'longpress operation'));
75402               } else if (d.shortcut.toLowerCase() === 'tap') {
75403                 selection.call(svgIcon('#iD-walkthrough-tap', 'tap operation'));
75404               } else {
75405                 selection.append('kbd').attr('class', 'shortcut').html(function (d) {
75406                   return d.shortcut;
75407                 });
75408               }
75409
75410               if (i < nodes.length - 1) {
75411                 selection.append('span').html(d.separator || "\xA0" + _t.html('shortcuts.or') + "\xA0");
75412               } else if (i === nodes.length - 1 && d.suffix) {
75413                 selection.append('span').html(d.suffix);
75414               }
75415             });
75416             shortcutKeys.filter(function (d) {
75417               return d.gesture;
75418             }).each(function () {
75419               var selection = select(this);
75420               selection.append('span').html('+');
75421               selection.append('span').attr('class', 'gesture').html(function (d) {
75422                 return _t.html(d.gesture);
75423               });
75424             });
75425             shortcutRows.append('td').attr('class', 'shortcut-desc').html(function (d) {
75426               return d.text ? _t.html(d.text) : "\xA0";
75427             }); // Update
75428
75429             wrapper.selectAll('.shortcut-tab').style('display', function (d, i) {
75430               return i === _activeTab ? 'flex' : 'none';
75431             });
75432           }
75433
75434           return function (selection, show) {
75435             _selection = selection;
75436
75437             if (show) {
75438               _modalSelection = uiModal(selection);
75439
75440               _modalSelection.call(shortcutsModal);
75441             } else {
75442               context.keybinding().on([_t('shortcuts.toggle.key'), '?'], function () {
75443                 if (context.container().selectAll('.modal-shortcuts').size()) {
75444                   // already showing
75445                   if (_modalSelection) {
75446                     _modalSelection.close();
75447
75448                     _modalSelection = null;
75449                   }
75450                 } else {
75451                   _modalSelection = uiModal(_selection);
75452
75453                   _modalSelection.call(shortcutsModal);
75454                 }
75455               });
75456             }
75457           };
75458         }
75459
75460         function uiDataHeader() {
75461           var _datum;
75462
75463           function dataHeader(selection) {
75464             var header = selection.selectAll('.data-header').data(_datum ? [_datum] : [], function (d) {
75465               return d.__featurehash__;
75466             });
75467             header.exit().remove();
75468             var headerEnter = header.enter().append('div').attr('class', 'data-header');
75469             var iconEnter = headerEnter.append('div').attr('class', 'data-header-icon');
75470             iconEnter.append('div').attr('class', 'preset-icon-28').call(svgIcon('#iD-icon-data', 'note-fill'));
75471             headerEnter.append('div').attr('class', 'data-header-label').html(_t.html('map_data.layers.custom.title'));
75472           }
75473
75474           dataHeader.datum = function (val) {
75475             if (!arguments.length) return _datum;
75476             _datum = val;
75477             return this;
75478           };
75479
75480           return dataHeader;
75481         }
75482
75483         // It is keyed on the `value` of the entry. Data should be an array of objects like:
75484         //   [{
75485         //       value:   'string value',  // required
75486         //       display: 'label html'     // optional
75487         //       title:   'hover text'     // optional
75488         //       terms:   ['search terms'] // optional
75489         //   }, ...]
75490
75491         var _comboHideTimerID;
75492
75493         function uiCombobox(context, klass) {
75494           var dispatch = dispatch$8('accept', 'cancel');
75495           var container = context.container();
75496           var _suggestions = [];
75497           var _data = [];
75498           var _fetched = {};
75499           var _selected = null;
75500           var _canAutocomplete = true;
75501           var _caseSensitive = false;
75502           var _cancelFetch = false;
75503           var _minItems = 2;
75504           var _tDown = 0;
75505
75506           var _mouseEnterHandler, _mouseLeaveHandler;
75507
75508           var _fetcher = function _fetcher(val, cb) {
75509             cb(_data.filter(function (d) {
75510               var terms = d.terms || [];
75511               terms.push(d.value);
75512               return terms.some(function (term) {
75513                 return term.toString().toLowerCase().indexOf(val.toLowerCase()) !== -1;
75514               });
75515             }));
75516           };
75517
75518           var combobox = function combobox(input, attachTo) {
75519             if (!input || input.empty()) return;
75520             input.classed('combobox-input', true).on('focus.combo-input', focus).on('blur.combo-input', blur).on('keydown.combo-input', keydown).on('keyup.combo-input', keyup).on('input.combo-input', change).on('mousedown.combo-input', mousedown).each(function () {
75521               var parent = this.parentNode;
75522               var sibling = this.nextSibling;
75523               select(parent).selectAll('.combobox-caret').filter(function (d) {
75524                 return d === input.node();
75525               }).data([input.node()]).enter().insert('div', function () {
75526                 return sibling;
75527               }).attr('class', 'combobox-caret').on('mousedown.combo-caret', function (d3_event) {
75528                 d3_event.preventDefault(); // don't steal focus from input
75529
75530                 input.node().focus(); // focus the input as if it was clicked
75531
75532                 mousedown(d3_event);
75533               }).on('mouseup.combo-caret', function (d3_event) {
75534                 d3_event.preventDefault(); // don't steal focus from input
75535
75536                 mouseup(d3_event);
75537               });
75538             });
75539
75540             function mousedown(d3_event) {
75541               if (d3_event.button !== 0) return; // left click only
75542
75543               _tDown = +new Date(); // clear selection
75544
75545               var start = input.property('selectionStart');
75546               var end = input.property('selectionEnd');
75547
75548               if (start !== end) {
75549                 var val = utilGetSetValue(input);
75550                 input.node().setSelectionRange(val.length, val.length);
75551                 return;
75552               }
75553
75554               input.on('mouseup.combo-input', mouseup);
75555             }
75556
75557             function mouseup(d3_event) {
75558               input.on('mouseup.combo-input', null);
75559               if (d3_event.button !== 0) return; // left click only
75560
75561               if (input.node() !== document.activeElement) return; // exit if this input is not focused
75562
75563               var start = input.property('selectionStart');
75564               var end = input.property('selectionEnd');
75565               if (start !== end) return; // exit if user is selecting
75566               // not showing or showing for a different field - try to show it.
75567
75568               var combo = container.selectAll('.combobox');
75569
75570               if (combo.empty() || combo.datum() !== input.node()) {
75571                 var tOrig = _tDown;
75572                 window.setTimeout(function () {
75573                   if (tOrig !== _tDown) return; // exit if user double clicked
75574
75575                   fetchComboData('', function () {
75576                     show();
75577                     render();
75578                   });
75579                 }, 250);
75580               } else {
75581                 hide();
75582               }
75583             }
75584
75585             function focus() {
75586               fetchComboData(''); // prefetch values (may warm taginfo cache)
75587             }
75588
75589             function blur() {
75590               _comboHideTimerID = window.setTimeout(hide, 75);
75591             }
75592
75593             function show() {
75594               hide(); // remove any existing
75595
75596               container.insert('div', ':first-child').datum(input.node()).attr('class', 'combobox' + (klass ? ' combobox-' + klass : '')).style('position', 'absolute').style('display', 'block').style('left', '0px').on('mousedown.combo-container', function (d3_event) {
75597                 // prevent moving focus out of the input field
75598                 d3_event.preventDefault();
75599               });
75600               container.on('scroll.combo-scroll', render, true);
75601             }
75602
75603             function hide() {
75604               if (_comboHideTimerID) {
75605                 window.clearTimeout(_comboHideTimerID);
75606                 _comboHideTimerID = undefined;
75607               }
75608
75609               container.selectAll('.combobox').remove();
75610               container.on('scroll.combo-scroll', null);
75611             }
75612
75613             function keydown(d3_event) {
75614               var shown = !container.selectAll('.combobox').empty();
75615               var tagName = input.node() ? input.node().tagName.toLowerCase() : '';
75616
75617               switch (d3_event.keyCode) {
75618                 case 8: // ⌫ Backspace
75619
75620                 case 46:
75621                   // ⌦ Delete
75622                   d3_event.stopPropagation();
75623                   _selected = null;
75624                   render();
75625                   input.on('input.combo-input', function () {
75626                     var start = input.property('selectionStart');
75627                     input.node().setSelectionRange(start, start);
75628                     input.on('input.combo-input', change);
75629                   });
75630                   break;
75631
75632                 case 9:
75633                   // ⇥ Tab
75634                   accept();
75635                   break;
75636
75637                 case 13:
75638                   // ↩ Return
75639                   d3_event.preventDefault();
75640                   d3_event.stopPropagation();
75641                   break;
75642
75643                 case 38:
75644                   // ↑ Up arrow
75645                   if (tagName === 'textarea' && !shown) return;
75646                   d3_event.preventDefault();
75647
75648                   if (tagName === 'input' && !shown) {
75649                     show();
75650                   }
75651
75652                   nav(-1);
75653                   break;
75654
75655                 case 40:
75656                   // ↓ Down arrow
75657                   if (tagName === 'textarea' && !shown) return;
75658                   d3_event.preventDefault();
75659
75660                   if (tagName === 'input' && !shown) {
75661                     show();
75662                   }
75663
75664                   nav(+1);
75665                   break;
75666               }
75667             }
75668
75669             function keyup(d3_event) {
75670               switch (d3_event.keyCode) {
75671                 case 27:
75672                   // ⎋ Escape
75673                   cancel();
75674                   break;
75675
75676                 case 13:
75677                   // ↩ Return
75678                   accept();
75679                   break;
75680               }
75681             } // Called whenever the input value is changed (e.g. on typing)
75682
75683
75684             function change() {
75685               fetchComboData(value(), function () {
75686                 _selected = null;
75687                 var val = input.property('value');
75688
75689                 if (_suggestions.length) {
75690                   if (input.property('selectionEnd') === val.length) {
75691                     _selected = tryAutocomplete();
75692                   }
75693
75694                   if (!_selected) {
75695                     _selected = val;
75696                   }
75697                 }
75698
75699                 if (val.length) {
75700                   var combo = container.selectAll('.combobox');
75701
75702                   if (combo.empty()) {
75703                     show();
75704                   }
75705                 } else {
75706                   hide();
75707                 }
75708
75709                 render();
75710               });
75711             } // Called when the user presses up/down arrows to navigate the list
75712
75713
75714             function nav(dir) {
75715               if (_suggestions.length) {
75716                 // try to determine previously selected index..
75717                 var index = -1;
75718
75719                 for (var i = 0; i < _suggestions.length; i++) {
75720                   if (_selected && _suggestions[i].value === _selected) {
75721                     index = i;
75722                     break;
75723                   }
75724                 } // pick new _selected
75725
75726
75727                 index = Math.max(Math.min(index + dir, _suggestions.length - 1), 0);
75728                 _selected = _suggestions[index].value;
75729                 input.property('value', _selected);
75730               }
75731
75732               render();
75733               ensureVisible();
75734             }
75735
75736             function ensureVisible() {
75737               var combo = container.selectAll('.combobox');
75738               if (combo.empty()) return;
75739               var containerRect = container.node().getBoundingClientRect();
75740               var comboRect = combo.node().getBoundingClientRect();
75741
75742               if (comboRect.bottom > containerRect.bottom) {
75743                 var node = attachTo ? attachTo.node() : input.node();
75744                 node.scrollIntoView({
75745                   behavior: 'instant',
75746                   block: 'center'
75747                 });
75748                 render();
75749               } // https://stackoverflow.com/questions/11039885/scrollintoview-causing-the-whole-page-to-move
75750
75751
75752               var selected = combo.selectAll('.combobox-option.selected').node();
75753
75754               if (selected) {
75755                 selected.scrollIntoView({
75756                   behavior: 'smooth',
75757                   block: 'nearest'
75758                 });
75759               }
75760             }
75761
75762             function value() {
75763               var value = input.property('value');
75764               var start = input.property('selectionStart');
75765               var end = input.property('selectionEnd');
75766
75767               if (start && end) {
75768                 value = value.substring(0, start);
75769               }
75770
75771               return value;
75772             }
75773
75774             function fetchComboData(v, cb) {
75775               _cancelFetch = false;
75776
75777               _fetcher.call(input, v, function (results) {
75778                 // already chose a value, don't overwrite or autocomplete it
75779                 if (_cancelFetch) return;
75780                 _suggestions = results;
75781                 results.forEach(function (d) {
75782                   _fetched[d.value] = d;
75783                 });
75784
75785                 if (cb) {
75786                   cb();
75787                 }
75788               });
75789             }
75790
75791             function tryAutocomplete() {
75792               if (!_canAutocomplete) return;
75793               var val = _caseSensitive ? value() : value().toLowerCase();
75794               if (!val) return; // Don't autocomplete if user is typing a number - #4935
75795
75796               if (!isNaN(parseFloat(val)) && isFinite(val)) return;
75797               var bestIndex = -1;
75798
75799               for (var i = 0; i < _suggestions.length; i++) {
75800                 var suggestion = _suggestions[i].value;
75801                 var compare = _caseSensitive ? suggestion : suggestion.toLowerCase(); // if search string matches suggestion exactly, pick it..
75802
75803                 if (compare === val) {
75804                   bestIndex = i;
75805                   break; // otherwise lock in the first result that starts with the search string..
75806                 } else if (bestIndex === -1 && compare.indexOf(val) === 0) {
75807                   bestIndex = i;
75808                 }
75809               }
75810
75811               if (bestIndex !== -1) {
75812                 var bestVal = _suggestions[bestIndex].value;
75813                 input.property('value', bestVal);
75814                 input.node().setSelectionRange(val.length, bestVal.length);
75815                 return bestVal;
75816               }
75817             }
75818
75819             function render() {
75820               if (_suggestions.length < _minItems || document.activeElement !== input.node()) {
75821                 hide();
75822                 return;
75823               }
75824
75825               var shown = !container.selectAll('.combobox').empty();
75826               if (!shown) return;
75827               var combo = container.selectAll('.combobox');
75828               var options = combo.selectAll('.combobox-option').data(_suggestions, function (d) {
75829                 return d.value;
75830               });
75831               options.exit().remove(); // enter/update
75832
75833               options.enter().append('a').attr('class', function (d) {
75834                 return 'combobox-option ' + (d.klass || '');
75835               }).attr('title', function (d) {
75836                 return d.title;
75837               }).html(function (d) {
75838                 return d.display || d.value;
75839               }).on('mouseenter', _mouseEnterHandler).on('mouseleave', _mouseLeaveHandler).merge(options).classed('selected', function (d) {
75840                 return d.value === _selected;
75841               }).on('click.combo-option', accept).order();
75842               var node = attachTo ? attachTo.node() : input.node();
75843               var containerRect = container.node().getBoundingClientRect();
75844               var rect = node.getBoundingClientRect();
75845               combo.style('left', rect.left + 5 - containerRect.left + 'px').style('width', rect.width - 10 + 'px').style('top', rect.height + rect.top - containerRect.top + 'px');
75846             } // Dispatches an 'accept' event
75847             // Then hides the combobox.
75848
75849
75850             function accept(d3_event, d) {
75851               _cancelFetch = true;
75852               var thiz = input.node();
75853
75854               if (d) {
75855                 // user clicked on a suggestion
75856                 utilGetSetValue(input, d.value); // replace field contents
75857
75858                 utilTriggerEvent(input, 'change');
75859               } // clear (and keep) selection
75860
75861
75862               var val = utilGetSetValue(input);
75863               thiz.setSelectionRange(val.length, val.length);
75864               d = _fetched[val];
75865               dispatch.call('accept', thiz, d, val);
75866               hide();
75867             } // Dispatches an 'cancel' event
75868             // Then hides the combobox.
75869
75870
75871             function cancel() {
75872               _cancelFetch = true;
75873               var thiz = input.node(); // clear (and remove) selection, and replace field contents
75874
75875               var val = utilGetSetValue(input);
75876               var start = input.property('selectionStart');
75877               var end = input.property('selectionEnd');
75878               val = val.slice(0, start) + val.slice(end);
75879               utilGetSetValue(input, val);
75880               thiz.setSelectionRange(val.length, val.length);
75881               dispatch.call('cancel', thiz);
75882               hide();
75883             }
75884           };
75885
75886           combobox.canAutocomplete = function (val) {
75887             if (!arguments.length) return _canAutocomplete;
75888             _canAutocomplete = val;
75889             return combobox;
75890           };
75891
75892           combobox.caseSensitive = function (val) {
75893             if (!arguments.length) return _caseSensitive;
75894             _caseSensitive = val;
75895             return combobox;
75896           };
75897
75898           combobox.data = function (val) {
75899             if (!arguments.length) return _data;
75900             _data = val;
75901             return combobox;
75902           };
75903
75904           combobox.fetcher = function (val) {
75905             if (!arguments.length) return _fetcher;
75906             _fetcher = val;
75907             return combobox;
75908           };
75909
75910           combobox.minItems = function (val) {
75911             if (!arguments.length) return _minItems;
75912             _minItems = val;
75913             return combobox;
75914           };
75915
75916           combobox.itemsMouseEnter = function (val) {
75917             if (!arguments.length) return _mouseEnterHandler;
75918             _mouseEnterHandler = val;
75919             return combobox;
75920           };
75921
75922           combobox.itemsMouseLeave = function (val) {
75923             if (!arguments.length) return _mouseLeaveHandler;
75924             _mouseLeaveHandler = val;
75925             return combobox;
75926           };
75927
75928           return utilRebind(combobox, dispatch, 'on');
75929         }
75930
75931         uiCombobox.off = function (input, context) {
75932           input.on('focus.combo-input', null).on('blur.combo-input', null).on('keydown.combo-input', null).on('keyup.combo-input', null).on('input.combo-input', null).on('mousedown.combo-input', null).on('mouseup.combo-input', null);
75933           context.container().on('scroll.combo-scroll', null);
75934         };
75935
75936         function uiDisclosure(context, key, expandedDefault) {
75937           var dispatch = dispatch$8('toggled');
75938
75939           var _expanded;
75940
75941           var _label = utilFunctor('');
75942
75943           var _updatePreference = true;
75944
75945           var _content = function _content() {};
75946
75947           var disclosure = function disclosure(selection) {
75948             if (_expanded === undefined || _expanded === null) {
75949               // loading _expanded here allows it to be reset by calling `disclosure.expanded(null)`
75950               var preference = corePreferences('disclosure.' + key + '.expanded');
75951               _expanded = preference === null ? !!expandedDefault : preference === 'true';
75952             }
75953
75954             var hideToggle = selection.selectAll('.hide-toggle-' + key).data([0]); // enter
75955
75956             var hideToggleEnter = hideToggle.enter().append('a').attr('href', '#').attr('class', 'hide-toggle hide-toggle-' + key).call(svgIcon('', 'pre-text', 'hide-toggle-icon'));
75957             hideToggleEnter.append('span').attr('class', 'hide-toggle-text'); // update
75958
75959             hideToggle = hideToggleEnter.merge(hideToggle);
75960             hideToggle.on('click', toggle).classed('expanded', _expanded);
75961             hideToggle.selectAll('.hide-toggle-text').html(_label());
75962             hideToggle.selectAll('.hide-toggle-icon').attr('xlink:href', _expanded ? '#iD-icon-down' : _mainLocalizer.textDirection() === 'rtl' ? '#iD-icon-backward' : '#iD-icon-forward');
75963             var wrap = selection.selectAll('.disclosure-wrap').data([0]); // enter/update
75964
75965             wrap = wrap.enter().append('div').attr('class', 'disclosure-wrap disclosure-wrap-' + key).merge(wrap).classed('hide', !_expanded);
75966
75967             if (_expanded) {
75968               wrap.call(_content);
75969             }
75970
75971             function toggle(d3_event) {
75972               d3_event.preventDefault();
75973               _expanded = !_expanded;
75974
75975               if (_updatePreference) {
75976                 corePreferences('disclosure.' + key + '.expanded', _expanded);
75977               }
75978
75979               hideToggle.classed('expanded', _expanded);
75980               hideToggle.selectAll('.hide-toggle-icon').attr('xlink:href', _expanded ? '#iD-icon-down' : _mainLocalizer.textDirection() === 'rtl' ? '#iD-icon-backward' : '#iD-icon-forward');
75981               wrap.call(uiToggle(_expanded));
75982
75983               if (_expanded) {
75984                 wrap.call(_content);
75985               }
75986
75987               dispatch.call('toggled', this, _expanded);
75988             }
75989           };
75990
75991           disclosure.label = function (val) {
75992             if (!arguments.length) return _label;
75993             _label = utilFunctor(val);
75994             return disclosure;
75995           };
75996
75997           disclosure.expanded = function (val) {
75998             if (!arguments.length) return _expanded;
75999             _expanded = val;
76000             return disclosure;
76001           };
76002
76003           disclosure.updatePreference = function (val) {
76004             if (!arguments.length) return _updatePreference;
76005             _updatePreference = val;
76006             return disclosure;
76007           };
76008
76009           disclosure.content = function (val) {
76010             if (!arguments.length) return _content;
76011             _content = val;
76012             return disclosure;
76013           };
76014
76015           return utilRebind(disclosure, dispatch, 'on');
76016         }
76017
76018         // Can be labeled and collapsible.
76019
76020         function uiSection(id, context) {
76021           var _classes = utilFunctor('');
76022
76023           var _shouldDisplay;
76024
76025           var _content;
76026
76027           var _disclosure;
76028
76029           var _label;
76030
76031           var _expandedByDefault = utilFunctor(true);
76032
76033           var _disclosureContent;
76034
76035           var _disclosureExpanded;
76036
76037           var _containerSelection = select(null);
76038
76039           var section = {
76040             id: id
76041           };
76042
76043           section.classes = function (val) {
76044             if (!arguments.length) return _classes;
76045             _classes = utilFunctor(val);
76046             return section;
76047           };
76048
76049           section.label = function (val) {
76050             if (!arguments.length) return _label;
76051             _label = utilFunctor(val);
76052             return section;
76053           };
76054
76055           section.expandedByDefault = function (val) {
76056             if (!arguments.length) return _expandedByDefault;
76057             _expandedByDefault = utilFunctor(val);
76058             return section;
76059           };
76060
76061           section.shouldDisplay = function (val) {
76062             if (!arguments.length) return _shouldDisplay;
76063             _shouldDisplay = utilFunctor(val);
76064             return section;
76065           };
76066
76067           section.content = function (val) {
76068             if (!arguments.length) return _content;
76069             _content = val;
76070             return section;
76071           };
76072
76073           section.disclosureContent = function (val) {
76074             if (!arguments.length) return _disclosureContent;
76075             _disclosureContent = val;
76076             return section;
76077           };
76078
76079           section.disclosureExpanded = function (val) {
76080             if (!arguments.length) return _disclosureExpanded;
76081             _disclosureExpanded = val;
76082             return section;
76083           }; // may be called multiple times
76084
76085
76086           section.render = function (selection) {
76087             _containerSelection = selection.selectAll('.section-' + id).data([0]);
76088
76089             var sectionEnter = _containerSelection.enter().append('div').attr('class', 'section section-' + id + ' ' + (_classes && _classes() || ''));
76090
76091             _containerSelection = sectionEnter.merge(_containerSelection);
76092
76093             _containerSelection.call(renderContent);
76094           };
76095
76096           section.reRender = function () {
76097             _containerSelection.call(renderContent);
76098           };
76099
76100           section.selection = function () {
76101             return _containerSelection;
76102           };
76103
76104           section.disclosure = function () {
76105             return _disclosure;
76106           }; // may be called multiple times
76107
76108
76109           function renderContent(selection) {
76110             if (_shouldDisplay) {
76111               var shouldDisplay = _shouldDisplay();
76112
76113               selection.classed('hide', !shouldDisplay);
76114
76115               if (!shouldDisplay) {
76116                 selection.html('');
76117                 return;
76118               }
76119             }
76120
76121             if (_disclosureContent) {
76122               if (!_disclosure) {
76123                 _disclosure = uiDisclosure(context, id.replace(/-/g, '_'), _expandedByDefault()).label(_label || '')
76124                 /*.on('toggled', function(expanded) {
76125                     if (expanded) { selection.node().parentNode.scrollTop += 200; }
76126                 })*/
76127                 .content(_disclosureContent);
76128               }
76129
76130               if (_disclosureExpanded !== undefined) {
76131                 _disclosure.expanded(_disclosureExpanded);
76132
76133                 _disclosureExpanded = undefined;
76134               }
76135
76136               selection.call(_disclosure);
76137               return;
76138             }
76139
76140             if (_content) {
76141               selection.call(_content);
76142             }
76143           }
76144
76145           return section;
76146         }
76147
76148         // {
76149         //   key: 'string',     // required
76150         //   value: 'string'    // optional
76151         // }
76152         //   -or-
76153         // {
76154         //   qid: 'string'      // brand wikidata  (e.g. 'Q37158')
76155         // }
76156         //
76157
76158         function uiTagReference(what) {
76159           var wikibase = what.qid ? services.wikidata : services.osmWikibase;
76160           var tagReference = {};
76161
76162           var _button = select(null);
76163
76164           var _body = select(null);
76165
76166           var _loaded;
76167
76168           var _showing;
76169
76170           function load() {
76171             if (!wikibase) return;
76172
76173             _button.classed('tag-reference-loading', true);
76174
76175             wikibase.getDocs(what, gotDocs);
76176           }
76177
76178           function gotDocs(err, docs) {
76179             _body.html('');
76180
76181             if (!docs || !docs.title) {
76182               _body.append('p').attr('class', 'tag-reference-description').html(_t.html('inspector.no_documentation_key'));
76183
76184               done();
76185               return;
76186             }
76187
76188             if (docs.imageURL) {
76189               _body.append('img').attr('class', 'tag-reference-wiki-image').attr('src', docs.imageURL).on('load', function () {
76190                 done();
76191               }).on('error', function () {
76192                 select(this).remove();
76193                 done();
76194               });
76195             } else {
76196               done();
76197             }
76198
76199             _body.append('p').attr('class', 'tag-reference-description').html(docs.description ? _mainLocalizer.htmlForLocalizedText(docs.description, docs.descriptionLocaleCode) : _t.html('inspector.no_documentation_key')).append('a').attr('class', 'tag-reference-edit').attr('target', '_blank').attr('title', _t('inspector.edit_reference')).attr('href', docs.editURL).call(svgIcon('#iD-icon-edit', 'inline'));
76200
76201             if (docs.wiki) {
76202               _body.append('a').attr('class', 'tag-reference-link').attr('target', '_blank').attr('href', docs.wiki.url).call(svgIcon('#iD-icon-out-link', 'inline')).append('span').html(_t.html(docs.wiki.text));
76203             } // Add link to info about "good changeset comments" - #2923
76204
76205
76206             if (what.key === 'comment') {
76207               _body.append('a').attr('class', 'tag-reference-comment-link').attr('target', '_blank').call(svgIcon('#iD-icon-out-link', 'inline')).attr('href', _t('commit.about_changeset_comments_link')).append('span').html(_t.html('commit.about_changeset_comments'));
76208             }
76209           }
76210
76211           function done() {
76212             _loaded = true;
76213
76214             _button.classed('tag-reference-loading', false);
76215
76216             _body.classed('expanded', true).transition().duration(200).style('max-height', '200px').style('opacity', '1');
76217
76218             _showing = true;
76219
76220             _button.selectAll('svg.icon use').each(function () {
76221               var iconUse = select(this);
76222
76223               if (iconUse.attr('href') === '#iD-icon-info') {
76224                 iconUse.attr('href', '#iD-icon-info-filled');
76225               }
76226             });
76227           }
76228
76229           function hide() {
76230             _body.transition().duration(200).style('max-height', '0px').style('opacity', '0').on('end', function () {
76231               _body.classed('expanded', false);
76232             });
76233
76234             _showing = false;
76235
76236             _button.selectAll('svg.icon use').each(function () {
76237               var iconUse = select(this);
76238
76239               if (iconUse.attr('href') === '#iD-icon-info-filled') {
76240                 iconUse.attr('href', '#iD-icon-info');
76241               }
76242             });
76243           }
76244
76245           tagReference.button = function (selection, klass, iconName) {
76246             _button = selection.selectAll('.tag-reference-button').data([0]);
76247             _button = _button.enter().append('button').attr('class', 'tag-reference-button ' + (klass || '')).attr('title', _t('icons.information')).call(svgIcon('#iD-icon-' + (iconName || 'inspect'))).merge(_button);
76248
76249             _button.on('click', function (d3_event) {
76250               d3_event.stopPropagation();
76251               d3_event.preventDefault();
76252               this.blur(); // avoid keeping focus on the button - #4641
76253
76254               if (_showing) {
76255                 hide();
76256               } else if (_loaded) {
76257                 done();
76258               } else {
76259                 load();
76260               }
76261             });
76262           };
76263
76264           tagReference.body = function (selection) {
76265             var itemID = what.qid || what.key + '-' + (what.value || '');
76266             _body = selection.selectAll('.tag-reference-body').data([itemID], function (d) {
76267               return d;
76268             });
76269
76270             _body.exit().remove();
76271
76272             _body = _body.enter().append('div').attr('class', 'tag-reference-body').style('max-height', '0').style('opacity', '0').merge(_body);
76273
76274             if (_showing === false) {
76275               hide();
76276             }
76277           };
76278
76279           tagReference.showing = function (val) {
76280             if (!arguments.length) return _showing;
76281             _showing = val;
76282             return tagReference;
76283           };
76284
76285           return tagReference;
76286         }
76287
76288         function uiSectionRawTagEditor(id, context) {
76289           var section = uiSection(id, context).classes('raw-tag-editor').label(function () {
76290             var count = Object.keys(_tags).filter(function (d) {
76291               return d;
76292             }).length;
76293             return _t('inspector.title_count', {
76294               title: _t.html('inspector.tags'),
76295               count: count
76296             });
76297           }).expandedByDefault(false).disclosureContent(renderDisclosureContent);
76298           var taginfo = services.taginfo;
76299           var dispatch = dispatch$8('change');
76300           var availableViews = [{
76301             id: 'list',
76302             icon: '#fas-th-list'
76303           }, {
76304             id: 'text',
76305             icon: '#fas-i-cursor'
76306           }];
76307
76308           var _tagView = corePreferences('raw-tag-editor-view') || 'list'; // 'list, 'text'
76309
76310
76311           var _readOnlyTags = []; // the keys in the order we want them to display
76312
76313           var _orderedKeys = [];
76314           var _showBlank = false;
76315           var _pendingChange = null;
76316
76317           var _state;
76318
76319           var _presets;
76320
76321           var _tags;
76322
76323           var _entityIDs;
76324
76325           var _didInteract = false;
76326
76327           function interacted() {
76328             _didInteract = true;
76329           }
76330
76331           function renderDisclosureContent(wrap) {
76332             // remove deleted keys
76333             _orderedKeys = _orderedKeys.filter(function (key) {
76334               return _tags[key] !== undefined;
76335             }); // When switching to a different entity or changing the state (hover/select)
76336             // reorder the keys alphabetically.
76337             // We trigger this by emptying the `_orderedKeys` array, then it will be rebuilt here.
76338             // Otherwise leave their order alone - #5857, #5927
76339
76340             var all = Object.keys(_tags).sort();
76341             var missingKeys = utilArrayDifference(all, _orderedKeys);
76342
76343             for (var i in missingKeys) {
76344               _orderedKeys.push(missingKeys[i]);
76345             } // assemble row data
76346
76347
76348             var rowData = _orderedKeys.map(function (key, i) {
76349               return {
76350                 index: i,
76351                 key: key,
76352                 value: _tags[key]
76353               };
76354             }); // append blank row last, if necessary
76355
76356
76357             if (!rowData.length || _showBlank) {
76358               _showBlank = false;
76359               rowData.push({
76360                 index: rowData.length,
76361                 key: '',
76362                 value: ''
76363               });
76364             } // View Options
76365
76366
76367             var options = wrap.selectAll('.raw-tag-options').data([0]);
76368             options.exit().remove();
76369             var optionsEnter = options.enter().insert('div', ':first-child').attr('class', 'raw-tag-options');
76370             var optionEnter = optionsEnter.selectAll('.raw-tag-option').data(availableViews, function (d) {
76371               return d.id;
76372             }).enter();
76373             optionEnter.append('button').attr('class', function (d) {
76374               return 'raw-tag-option raw-tag-option-' + d.id + (_tagView === d.id ? ' selected' : '');
76375             }).attr('title', function (d) {
76376               return _t('icons.' + d.id);
76377             }).on('click', function (d3_event, d) {
76378               _tagView = d.id;
76379               corePreferences('raw-tag-editor-view', d.id);
76380               wrap.selectAll('.raw-tag-option').classed('selected', function (datum) {
76381                 return datum === d;
76382               });
76383               wrap.selectAll('.tag-text').classed('hide', d.id !== 'text').each(setTextareaHeight);
76384               wrap.selectAll('.tag-list, .add-row').classed('hide', d.id !== 'list');
76385             }).each(function (d) {
76386               select(this).call(svgIcon(d.icon));
76387             }); // View as Text
76388
76389             var textData = rowsToText(rowData);
76390             var textarea = wrap.selectAll('.tag-text').data([0]);
76391             textarea = textarea.enter().append('textarea').attr('class', 'tag-text' + (_tagView !== 'text' ? ' hide' : '')).call(utilNoAuto).attr('placeholder', _t('inspector.key_value')).attr('spellcheck', 'false').merge(textarea);
76392             textarea.call(utilGetSetValue, textData).each(setTextareaHeight).on('input', setTextareaHeight).on('focus', interacted).on('blur', textChanged).on('change', textChanged); // View as List
76393
76394             var list = wrap.selectAll('.tag-list').data([0]);
76395             list = list.enter().append('ul').attr('class', 'tag-list' + (_tagView !== 'list' ? ' hide' : '')).merge(list); // Container for the Add button
76396
76397             var addRowEnter = wrap.selectAll('.add-row').data([0]).enter().append('div').attr('class', 'add-row' + (_tagView !== 'list' ? ' hide' : ''));
76398             addRowEnter.append('button').attr('class', 'add-tag').call(svgIcon('#iD-icon-plus', 'light')).on('click', addTag);
76399             addRowEnter.append('div').attr('class', 'space-value'); // preserve space
76400
76401             addRowEnter.append('div').attr('class', 'space-buttons'); // preserve space
76402             // Tag list items
76403
76404             var items = list.selectAll('.tag-row').data(rowData, function (d) {
76405               return d.key;
76406             });
76407             items.exit().each(unbind).remove(); // Enter
76408
76409             var itemsEnter = items.enter().append('li').attr('class', 'tag-row').classed('readonly', isReadOnly);
76410             var innerWrap = itemsEnter.append('div').attr('class', 'inner-wrap');
76411             innerWrap.append('div').attr('class', 'key-wrap').append('input').property('type', 'text').attr('class', 'key').call(utilNoAuto).on('focus', interacted).on('blur', keyChange).on('change', keyChange);
76412             innerWrap.append('div').attr('class', 'value-wrap').append('input').property('type', 'text').attr('class', 'value').call(utilNoAuto).on('focus', interacted).on('blur', valueChange).on('change', valueChange).on('keydown.push-more', pushMore);
76413             innerWrap.append('button').attr('class', 'form-field-button remove').attr('title', _t('icons.remove')).call(svgIcon('#iD-operation-delete')); // Update
76414
76415             items = items.merge(itemsEnter).sort(function (a, b) {
76416               return a.index - b.index;
76417             });
76418             items.each(function (d) {
76419               var row = select(this);
76420               var key = row.select('input.key'); // propagate bound data
76421
76422               var value = row.select('input.value'); // propagate bound data
76423
76424               if (_entityIDs && taginfo && _state !== 'hover') {
76425                 bindTypeahead(key, value);
76426               }
76427
76428               var referenceOptions = {
76429                 key: d.key
76430               };
76431
76432               if (typeof d.value === 'string') {
76433                 referenceOptions.value = d.value;
76434               }
76435
76436               var reference = uiTagReference(referenceOptions);
76437
76438               if (_state === 'hover') {
76439                 reference.showing(false);
76440               }
76441
76442               row.select('.inner-wrap') // propagate bound data
76443               .call(reference.button);
76444               row.call(reference.body);
76445               row.select('button.remove'); // propagate bound data
76446             });
76447             items.selectAll('input.key').attr('title', function (d) {
76448               return d.key;
76449             }).call(utilGetSetValue, function (d) {
76450               return d.key;
76451             }).attr('readonly', function (d) {
76452               return isReadOnly(d) || typeof d.value !== 'string' || null;
76453             });
76454             items.selectAll('input.value').attr('title', function (d) {
76455               return Array.isArray(d.value) ? d.value.filter(Boolean).join('\n') : d.value;
76456             }).classed('mixed', function (d) {
76457               return Array.isArray(d.value);
76458             }).attr('placeholder', function (d) {
76459               return typeof d.value === 'string' ? null : _t('inspector.multiple_values');
76460             }).call(utilGetSetValue, function (d) {
76461               return typeof d.value === 'string' ? d.value : '';
76462             }).attr('readonly', function (d) {
76463               return isReadOnly(d) || null;
76464             });
76465             items.selectAll('button.remove').on(('PointerEvent' in window ? 'pointer' : 'mouse') + 'down', removeTag); // 'click' fires too late - #5878
76466           }
76467
76468           function isReadOnly(d) {
76469             for (var i = 0; i < _readOnlyTags.length; i++) {
76470               if (d.key.match(_readOnlyTags[i]) !== null) {
76471                 return true;
76472               }
76473             }
76474
76475             return false;
76476           }
76477
76478           function setTextareaHeight() {
76479             if (_tagView !== 'text') return;
76480             var selection = select(this);
76481             var matches = selection.node().value.match(/\n/g);
76482             var lineCount = 2 + Number(matches && matches.length);
76483             var lineHeight = 20;
76484             selection.style('height', lineCount * lineHeight + 'px');
76485           }
76486
76487           function stringify(s) {
76488             return JSON.stringify(s).slice(1, -1); // without leading/trailing "
76489           }
76490
76491           function unstringify(s) {
76492             var leading = '';
76493             var trailing = '';
76494
76495             if (s.length < 1 || s.charAt(0) !== '"') {
76496               leading = '"';
76497             }
76498
76499             if (s.length < 2 || s.charAt(s.length - 1) !== '"' || s.charAt(s.length - 1) === '"' && s.charAt(s.length - 2) === '\\') {
76500               trailing = '"';
76501             }
76502
76503             return JSON.parse(leading + s + trailing);
76504           }
76505
76506           function rowsToText(rows) {
76507             var str = rows.filter(function (row) {
76508               return row.key && row.key.trim() !== '';
76509             }).map(function (row) {
76510               var rawVal = row.value;
76511               if (typeof rawVal !== 'string') rawVal = '*';
76512               var val = rawVal ? stringify(rawVal) : '';
76513               return stringify(row.key) + '=' + val;
76514             }).join('\n');
76515
76516             if (_state !== 'hover' && str.length) {
76517               return str + '\n';
76518             }
76519
76520             return str;
76521           }
76522
76523           function textChanged() {
76524             var newText = this.value.trim();
76525             var newTags = {};
76526             newText.split('\n').forEach(function (row) {
76527               var m = row.match(/^\s*([^=]+)=(.*)$/);
76528
76529               if (m !== null) {
76530                 var k = context.cleanTagKey(unstringify(m[1].trim()));
76531                 var v = context.cleanTagValue(unstringify(m[2].trim()));
76532                 newTags[k] = v;
76533               }
76534             });
76535             var tagDiff = utilTagDiff(_tags, newTags);
76536             if (!tagDiff.length) return;
76537             _pendingChange = _pendingChange || {};
76538             tagDiff.forEach(function (change) {
76539               if (isReadOnly({
76540                 key: change.key
76541               })) return; // skip unchanged multiselection placeholders
76542
76543               if (change.newVal === '*' && typeof change.oldVal !== 'string') return;
76544
76545               if (change.type === '-') {
76546                 _pendingChange[change.key] = undefined;
76547               } else if (change.type === '+') {
76548                 _pendingChange[change.key] = change.newVal || '';
76549               }
76550             });
76551
76552             if (Object.keys(_pendingChange).length === 0) {
76553               _pendingChange = null;
76554               return;
76555             }
76556
76557             scheduleChange();
76558           }
76559
76560           function pushMore(d3_event) {
76561             // if pressing Tab on the last value field with content, add a blank row
76562             if (d3_event.keyCode === 9 && !d3_event.shiftKey && section.selection().selectAll('.tag-list li:last-child input.value').node() === this && utilGetSetValue(select(this))) {
76563               addTag();
76564             }
76565           }
76566
76567           function bindTypeahead(key, value) {
76568             if (isReadOnly(key.datum())) return;
76569
76570             if (Array.isArray(value.datum().value)) {
76571               value.call(uiCombobox(context, 'tag-value').minItems(1).fetcher(function (value, callback) {
76572                 var keyString = utilGetSetValue(key);
76573                 if (!_tags[keyString]) return;
76574
76575                 var data = _tags[keyString].filter(Boolean).map(function (tagValue) {
76576                   return {
76577                     value: tagValue,
76578                     title: tagValue
76579                   };
76580                 });
76581
76582                 callback(data);
76583               }));
76584               return;
76585             }
76586
76587             var geometry = context.graph().geometry(_entityIDs[0]);
76588             key.call(uiCombobox(context, 'tag-key').fetcher(function (value, callback) {
76589               taginfo.keys({
76590                 debounce: true,
76591                 geometry: geometry,
76592                 query: value
76593               }, function (err, data) {
76594                 if (!err) {
76595                   var filtered = data.filter(function (d) {
76596                     return _tags[d.value] === undefined;
76597                   });
76598                   callback(sort(value, filtered));
76599                 }
76600               });
76601             }));
76602             value.call(uiCombobox(context, 'tag-value').fetcher(function (value, callback) {
76603               taginfo.values({
76604                 debounce: true,
76605                 key: utilGetSetValue(key),
76606                 geometry: geometry,
76607                 query: value
76608               }, function (err, data) {
76609                 if (!err) callback(sort(value, data));
76610               });
76611             }));
76612
76613             function sort(value, data) {
76614               var sameletter = [];
76615               var other = [];
76616
76617               for (var i = 0; i < data.length; i++) {
76618                 if (data[i].value.substring(0, value.length) === value) {
76619                   sameletter.push(data[i]);
76620                 } else {
76621                   other.push(data[i]);
76622                 }
76623               }
76624
76625               return sameletter.concat(other);
76626             }
76627           }
76628
76629           function unbind() {
76630             var row = select(this);
76631             row.selectAll('input.key').call(uiCombobox.off, context);
76632             row.selectAll('input.value').call(uiCombobox.off, context);
76633           }
76634
76635           function keyChange(d3_event, d) {
76636             if (select(this).attr('readonly')) return;
76637             var kOld = d.key; // exit if we are currently about to delete this row anyway - #6366
76638
76639             if (_pendingChange && _pendingChange.hasOwnProperty(kOld) && _pendingChange[kOld] === undefined) return;
76640             var kNew = context.cleanTagKey(this.value.trim()); // allow no change if the key should be readonly
76641
76642             if (isReadOnly({
76643               key: kNew
76644             })) {
76645               this.value = kOld;
76646               return;
76647             }
76648
76649             if (kNew && kNew !== kOld && _tags[kNew] !== undefined) {
76650               // new key is already in use, switch focus to the existing row
76651               this.value = kOld; // reset the key
76652
76653               section.selection().selectAll('.tag-list input.value').each(function (d) {
76654                 if (d.key === kNew) {
76655                   // send focus to that other value combo instead
76656                   var input = select(this).node();
76657                   input.focus();
76658                   input.select();
76659                 }
76660               });
76661               return;
76662             }
76663
76664             var row = this.parentNode.parentNode;
76665             var inputVal = select(row).selectAll('input.value');
76666             var vNew = context.cleanTagValue(utilGetSetValue(inputVal));
76667             _pendingChange = _pendingChange || {};
76668
76669             if (kOld) {
76670               _pendingChange[kOld] = undefined;
76671             }
76672
76673             _pendingChange[kNew] = vNew; // update the ordered key index so this row doesn't change position
76674
76675             var existingKeyIndex = _orderedKeys.indexOf(kOld);
76676
76677             if (existingKeyIndex !== -1) _orderedKeys[existingKeyIndex] = kNew;
76678             d.key = kNew; // update datum to avoid exit/enter on tag update
76679
76680             d.value = vNew;
76681             this.value = kNew;
76682             utilGetSetValue(inputVal, vNew);
76683             scheduleChange();
76684           }
76685
76686           function valueChange(d3_event, d) {
76687             if (isReadOnly(d)) return; // exit if this is a multiselection and no value was entered
76688
76689             if (typeof d.value !== 'string' && !this.value) return; // exit if we are currently about to delete this row anyway - #6366
76690
76691             if (_pendingChange && _pendingChange.hasOwnProperty(d.key) && _pendingChange[d.key] === undefined) return;
76692             _pendingChange = _pendingChange || {};
76693             _pendingChange[d.key] = context.cleanTagValue(this.value);
76694             scheduleChange();
76695           }
76696
76697           function removeTag(d3_event, d) {
76698             if (isReadOnly(d)) return;
76699
76700             if (d.key === '') {
76701               // removing the blank row
76702               _showBlank = false;
76703               section.reRender();
76704             } else {
76705               // remove the key from the ordered key index
76706               _orderedKeys = _orderedKeys.filter(function (key) {
76707                 return key !== d.key;
76708               });
76709               _pendingChange = _pendingChange || {};
76710               _pendingChange[d.key] = undefined;
76711               scheduleChange();
76712             }
76713           }
76714
76715           function addTag() {
76716             // Delay render in case this click is blurring an edited combo.
76717             // Without the setTimeout, the `content` render would wipe out the pending tag change.
76718             window.setTimeout(function () {
76719               _showBlank = true;
76720               section.reRender();
76721               section.selection().selectAll('.tag-list li:last-child input.key').node().focus();
76722             }, 20);
76723           }
76724
76725           function scheduleChange() {
76726             // Cache IDs in case the editor is reloaded before the change event is called. - #6028
76727             var entityIDs = _entityIDs; // Delay change in case this change is blurring an edited combo. - #5878
76728
76729             window.setTimeout(function () {
76730               if (!_pendingChange) return;
76731               dispatch.call('change', this, entityIDs, _pendingChange);
76732               _pendingChange = null;
76733             }, 10);
76734           }
76735
76736           section.state = function (val) {
76737             if (!arguments.length) return _state;
76738
76739             if (_state !== val) {
76740               _orderedKeys = [];
76741               _state = val;
76742             }
76743
76744             return section;
76745           };
76746
76747           section.presets = function (val) {
76748             if (!arguments.length) return _presets;
76749             _presets = val;
76750
76751             if (_presets && _presets.length && _presets[0].isFallback()) {
76752               section.disclosureExpanded(true); // don't collapse the disclosure if the mapper used the raw tag editor - #1881
76753             } else if (!_didInteract) {
76754               section.disclosureExpanded(null);
76755             }
76756
76757             return section;
76758           };
76759
76760           section.tags = function (val) {
76761             if (!arguments.length) return _tags;
76762             _tags = val;
76763             return section;
76764           };
76765
76766           section.entityIDs = function (val) {
76767             if (!arguments.length) return _entityIDs;
76768
76769             if (!_entityIDs || !val || !utilArrayIdentical(_entityIDs, val)) {
76770               _entityIDs = val;
76771               _orderedKeys = [];
76772             }
76773
76774             return section;
76775           }; // pass an array of regular expressions to test against the tag key
76776
76777
76778           section.readOnlyTags = function (val) {
76779             if (!arguments.length) return _readOnlyTags;
76780             _readOnlyTags = val;
76781             return section;
76782           };
76783
76784           return utilRebind(section, dispatch, 'on');
76785         }
76786
76787         function uiDataEditor(context) {
76788           var dataHeader = uiDataHeader();
76789           var rawTagEditor = uiSectionRawTagEditor('custom-data-tag-editor', context).expandedByDefault(true).readOnlyTags([/./]);
76790
76791           var _datum;
76792
76793           function dataEditor(selection) {
76794             var header = selection.selectAll('.header').data([0]);
76795             var headerEnter = header.enter().append('div').attr('class', 'header fillL');
76796             headerEnter.append('button').attr('class', 'close').on('click', function () {
76797               context.enter(modeBrowse(context));
76798             }).call(svgIcon('#iD-icon-close'));
76799             headerEnter.append('h3').html(_t.html('map_data.title'));
76800             var body = selection.selectAll('.body').data([0]);
76801             body = body.enter().append('div').attr('class', 'body').merge(body);
76802             var editor = body.selectAll('.data-editor').data([0]); // enter/update
76803
76804             editor.enter().append('div').attr('class', 'modal-section data-editor').merge(editor).call(dataHeader.datum(_datum));
76805             var rte = body.selectAll('.raw-tag-editor').data([0]); // enter/update
76806
76807             rte.enter().append('div').attr('class', 'raw-tag-editor data-editor').merge(rte).call(rawTagEditor.tags(_datum && _datum.properties || {}).state('hover').render).selectAll('textarea.tag-text').attr('readonly', true).classed('readonly', true);
76808           }
76809
76810           dataEditor.datum = function (val) {
76811             if (!arguments.length) return _datum;
76812             _datum = val;
76813             return this;
76814           };
76815
76816           return dataEditor;
76817         }
76818
76819         var pair_1 = pair;
76820
76821         function search(input, dims) {
76822           if (!dims) dims = 'NSEW';
76823           if (typeof input !== 'string') return null;
76824           input = input.toUpperCase();
76825           var regex = /^[\s\,]*([NSEW])?\s*([\-|\—|\―]?[0-9.]+)[°º˚]?\s*(?:([0-9.]+)['’′‘]\s*)?(?:([0-9.]+)(?:''|"|”|″)\s*)?([NSEW])?/;
76826           var m = input.match(regex);
76827           if (!m) return null; // no match
76828
76829           var matched = m[0]; // extract dimension.. m[1] = leading, m[5] = trailing
76830
76831           var dim;
76832
76833           if (m[1] && m[5]) {
76834             // if matched both..
76835             dim = m[1]; // keep leading
76836
76837             matched = matched.slice(0, -1); // remove trailing dimension from match
76838           } else {
76839             dim = m[1] || m[5];
76840           } // if unrecognized dimension
76841
76842
76843           if (dim && dims.indexOf(dim) === -1) return null; // extract DMS
76844
76845           var deg = m[2] ? parseFloat(m[2]) : 0;
76846           var min = m[3] ? parseFloat(m[3]) / 60 : 0;
76847           var sec = m[4] ? parseFloat(m[4]) / 3600 : 0;
76848           var sign = deg < 0 ? -1 : 1;
76849           if (dim === 'S' || dim === 'W') sign *= -1;
76850           return {
76851             val: (Math.abs(deg) + min + sec) * sign,
76852             dim: dim,
76853             matched: matched,
76854             remain: input.slice(matched.length)
76855           };
76856         }
76857
76858         function pair(input, dims) {
76859           input = input.trim();
76860           var one = search(input, dims);
76861           if (!one) return null;
76862           input = one.remain.trim();
76863           var two = search(input, dims);
76864           if (!two || two.remain) return null;
76865
76866           if (one.dim) {
76867             return swapdim(one.val, two.val, one.dim);
76868           } else {
76869             return [one.val, two.val];
76870           }
76871         }
76872
76873         function swapdim(a, b, dim) {
76874           if (dim === 'N' || dim === 'S') return [a, b];
76875           if (dim === 'W' || dim === 'E') return [b, a];
76876         }
76877
76878         function uiFeatureList(context) {
76879           var _geocodeResults;
76880
76881           function featureList(selection) {
76882             var header = selection.append('div').attr('class', 'header fillL');
76883             header.append('h3').html(_t.html('inspector.feature_list'));
76884             var searchWrap = selection.append('div').attr('class', 'search-header');
76885             searchWrap.call(svgIcon('#iD-icon-search', 'pre-text'));
76886             var search = searchWrap.append('input').attr('placeholder', _t('inspector.search')).attr('type', 'search').call(utilNoAuto).on('keypress', keypress).on('keydown', keydown).on('input', inputevent);
76887             var listWrap = selection.append('div').attr('class', 'inspector-body');
76888             var list = listWrap.append('div').attr('class', 'feature-list');
76889             context.on('exit.feature-list', clearSearch);
76890             context.map().on('drawn.feature-list', mapDrawn);
76891             context.keybinding().on(uiCmd('⌘F'), focusSearch);
76892
76893             function focusSearch(d3_event) {
76894               var mode = context.mode() && context.mode().id;
76895               if (mode !== 'browse') return;
76896               d3_event.preventDefault();
76897               search.node().focus();
76898             }
76899
76900             function keydown(d3_event) {
76901               if (d3_event.keyCode === 27) {
76902                 // escape
76903                 search.node().blur();
76904               }
76905             }
76906
76907             function keypress(d3_event) {
76908               var q = search.property('value'),
76909                   items = list.selectAll('.feature-list-item');
76910
76911               if (d3_event.keyCode === 13 && // ↩ Return
76912               q.length && items.size()) {
76913                 click(d3_event, items.datum());
76914               }
76915             }
76916
76917             function inputevent() {
76918               _geocodeResults = undefined;
76919               drawList();
76920             }
76921
76922             function clearSearch() {
76923               search.property('value', '');
76924               drawList();
76925             }
76926
76927             function mapDrawn(e) {
76928               if (e.full) {
76929                 drawList();
76930               }
76931             }
76932
76933             function features() {
76934               var result = [];
76935               var graph = context.graph();
76936               var visibleCenter = context.map().extent().center();
76937               var q = search.property('value').toLowerCase();
76938               if (!q) return result;
76939               var locationMatch = pair_1(q.toUpperCase()) || q.match(/^(-?\d+\.?\d*)\s+(-?\d+\.?\d*)$/);
76940
76941               if (locationMatch) {
76942                 var loc = [parseFloat(locationMatch[0]), parseFloat(locationMatch[1])];
76943                 result.push({
76944                   id: -1,
76945                   geometry: 'point',
76946                   type: _t('inspector.location'),
76947                   name: dmsCoordinatePair([loc[1], loc[0]]),
76948                   location: loc
76949                 });
76950               } // A location search takes priority over an ID search
76951
76952
76953               var idMatch = !locationMatch && q.match(/(?:^|\W)(node|way|relation|[nwr])\W?0*([1-9]\d*)(?:\W|$)/i);
76954
76955               if (idMatch) {
76956                 var elemType = idMatch[1].charAt(0);
76957                 var elemId = idMatch[2];
76958                 result.push({
76959                   id: elemType + elemId,
76960                   geometry: elemType === 'n' ? 'point' : elemType === 'w' ? 'line' : 'relation',
76961                   type: elemType === 'n' ? _t('inspector.node') : elemType === 'w' ? _t('inspector.way') : _t('inspector.relation'),
76962                   name: elemId
76963                 });
76964               }
76965
76966               var allEntities = graph.entities;
76967               var localResults = [];
76968
76969               for (var id in allEntities) {
76970                 var entity = allEntities[id];
76971                 if (!entity) continue;
76972                 var name = utilDisplayName(entity) || '';
76973                 if (name.toLowerCase().indexOf(q) < 0) continue;
76974                 var matched = _mainPresetIndex.match(entity, graph);
76975                 var type = matched && matched.name() || utilDisplayType(entity.id);
76976                 var extent = entity.extent(graph);
76977                 var distance = extent ? geoSphericalDistance(visibleCenter, extent.center()) : 0;
76978                 localResults.push({
76979                   id: entity.id,
76980                   entity: entity,
76981                   geometry: entity.geometry(graph),
76982                   type: type,
76983                   name: name,
76984                   distance: distance
76985                 });
76986                 if (localResults.length > 100) break;
76987               }
76988
76989               localResults = localResults.sort(function byDistance(a, b) {
76990                 return a.distance - b.distance;
76991               });
76992               result = result.concat(localResults);
76993
76994               (_geocodeResults || []).forEach(function (d) {
76995                 if (d.osm_type && d.osm_id) {
76996                   // some results may be missing these - #1890
76997                   // Make a temporary osmEntity so we can preset match
76998                   // and better localize the search result - #4725
76999                   var id = osmEntity.id.fromOSM(d.osm_type, d.osm_id);
77000                   var tags = {};
77001                   tags[d["class"]] = d.type;
77002                   var attrs = {
77003                     id: id,
77004                     type: d.osm_type,
77005                     tags: tags
77006                   };
77007
77008                   if (d.osm_type === 'way') {
77009                     // for ways, add some fake closed nodes
77010                     attrs.nodes = ['a', 'a']; // so that geometry area is possible
77011                   }
77012
77013                   var tempEntity = osmEntity(attrs);
77014                   var tempGraph = coreGraph([tempEntity]);
77015                   var matched = _mainPresetIndex.match(tempEntity, tempGraph);
77016                   var type = matched && matched.name() || utilDisplayType(id);
77017                   result.push({
77018                     id: tempEntity.id,
77019                     geometry: tempEntity.geometry(tempGraph),
77020                     type: type,
77021                     name: d.display_name,
77022                     extent: new geoExtent([parseFloat(d.boundingbox[3]), parseFloat(d.boundingbox[0])], [parseFloat(d.boundingbox[2]), parseFloat(d.boundingbox[1])])
77023                   });
77024                 }
77025               });
77026
77027               if (q.match(/^[0-9]+$/)) {
77028                 // if query is just a number, possibly an OSM ID without a prefix
77029                 result.push({
77030                   id: 'n' + q,
77031                   geometry: 'point',
77032                   type: _t('inspector.node'),
77033                   name: q
77034                 });
77035                 result.push({
77036                   id: 'w' + q,
77037                   geometry: 'line',
77038                   type: _t('inspector.way'),
77039                   name: q
77040                 });
77041                 result.push({
77042                   id: 'r' + q,
77043                   geometry: 'relation',
77044                   type: _t('inspector.relation'),
77045                   name: q
77046                 });
77047               }
77048
77049               return result;
77050             }
77051
77052             function drawList() {
77053               var value = search.property('value');
77054               var results = features();
77055               list.classed('filtered', value.length);
77056               var resultsIndicator = list.selectAll('.no-results-item').data([0]).enter().append('button').property('disabled', true).attr('class', 'no-results-item').call(svgIcon('#iD-icon-alert', 'pre-text'));
77057               resultsIndicator.append('span').attr('class', 'entity-name');
77058               list.selectAll('.no-results-item .entity-name').html(_t.html('geocoder.no_results_worldwide'));
77059
77060               if (services.geocoder) {
77061                 list.selectAll('.geocode-item').data([0]).enter().append('button').attr('class', 'geocode-item secondary-action').on('click', geocoderSearch).append('div').attr('class', 'label').append('span').attr('class', 'entity-name').html(_t.html('geocoder.search'));
77062               }
77063
77064               list.selectAll('.no-results-item').style('display', value.length && !results.length ? 'block' : 'none');
77065               list.selectAll('.geocode-item').style('display', value && _geocodeResults === undefined ? 'block' : 'none');
77066               list.selectAll('.feature-list-item').data([-1]).remove();
77067               var items = list.selectAll('.feature-list-item').data(results, function (d) {
77068                 return d.id;
77069               });
77070               var enter = items.enter().insert('button', '.geocode-item').attr('class', 'feature-list-item').on('mouseover', mouseover).on('mouseout', mouseout).on('click', click);
77071               var label = enter.append('div').attr('class', 'label');
77072               label.each(function (d) {
77073                 select(this).call(svgIcon('#iD-icon-' + d.geometry, 'pre-text'));
77074               });
77075               label.append('span').attr('class', 'entity-type').html(function (d) {
77076                 return d.type;
77077               });
77078               label.append('span').attr('class', 'entity-name').html(function (d) {
77079                 return d.name;
77080               });
77081               enter.style('opacity', 0).transition().style('opacity', 1);
77082               items.order();
77083               items.exit().remove();
77084             }
77085
77086             function mouseover(d3_event, d) {
77087               if (d.id === -1) return;
77088               utilHighlightEntities([d.id], true, context);
77089             }
77090
77091             function mouseout(d3_event, d) {
77092               if (d.id === -1) return;
77093               utilHighlightEntities([d.id], false, context);
77094             }
77095
77096             function click(d3_event, d) {
77097               d3_event.preventDefault();
77098
77099               if (d.location) {
77100                 context.map().centerZoomEase([d.location[1], d.location[0]], 19);
77101               } else if (d.entity) {
77102                 utilHighlightEntities([d.id], false, context);
77103                 context.enter(modeSelect(context, [d.entity.id]));
77104                 context.map().zoomToEase(d.entity);
77105               } else {
77106                 // download, zoom to, and select the entity with the given ID
77107                 context.zoomToEntity(d.id);
77108               }
77109             }
77110
77111             function geocoderSearch() {
77112               services.geocoder.search(search.property('value'), function (err, resp) {
77113                 _geocodeResults = resp || [];
77114                 drawList();
77115               });
77116             }
77117           }
77118
77119           return featureList;
77120         }
77121
77122         var getOwnPropertyDescriptor$1 = objectGetOwnPropertyDescriptor.f;
77123
77124
77125
77126
77127
77128
77129         // eslint-disable-next-line es/no-string-prototype-startswith -- safe
77130         var $startsWith = ''.startsWith;
77131         var min$1 = Math.min;
77132
77133         var CORRECT_IS_REGEXP_LOGIC$1 = correctIsRegexpLogic('startsWith');
77134         // https://github.com/zloirock/core-js/pull/702
77135         var MDN_POLYFILL_BUG$1 = !CORRECT_IS_REGEXP_LOGIC$1 && !!function () {
77136           var descriptor = getOwnPropertyDescriptor$1(String.prototype, 'startsWith');
77137           return descriptor && !descriptor.writable;
77138         }();
77139
77140         // `String.prototype.startsWith` method
77141         // https://tc39.es/ecma262/#sec-string.prototype.startswith
77142         _export({ target: 'String', proto: true, forced: !MDN_POLYFILL_BUG$1 && !CORRECT_IS_REGEXP_LOGIC$1 }, {
77143           startsWith: function startsWith(searchString /* , position = 0 */) {
77144             var that = String(requireObjectCoercible(this));
77145             notARegexp(searchString);
77146             var index = toLength(min$1(arguments.length > 1 ? arguments[1] : undefined, that.length));
77147             var search = String(searchString);
77148             return $startsWith
77149               ? $startsWith.call(that, search, index)
77150               : that.slice(index, index + search.length) === search;
77151           }
77152         });
77153
77154         function uiSectionEntityIssues(context) {
77155           // Does the user prefer to expand the active issue?  Useful for viewing tag diff.
77156           // Expand by default so first timers see it - #6408, #8143
77157           var preference = corePreferences('entity-issues.reference.expanded');
77158
77159           var _expanded = preference === null ? true : preference === 'true';
77160
77161           var _entityIDs = [];
77162           var _issues = [];
77163
77164           var _activeIssueID;
77165
77166           var section = uiSection('entity-issues', context).shouldDisplay(function () {
77167             return _issues.length > 0;
77168           }).label(function () {
77169             return _t('inspector.title_count', {
77170               title: _t.html('issues.list_title'),
77171               count: _issues.length
77172             });
77173           }).disclosureContent(renderDisclosureContent);
77174           context.validator().on('validated.entity_issues', function () {
77175             // Refresh on validated events
77176             reloadIssues();
77177             section.reRender();
77178           }).on('focusedIssue.entity_issues', function (issue) {
77179             makeActiveIssue(issue.id);
77180           });
77181
77182           function reloadIssues() {
77183             _issues = context.validator().getSharedEntityIssues(_entityIDs, {
77184               includeDisabledRules: true
77185             });
77186           }
77187
77188           function makeActiveIssue(issueID) {
77189             _activeIssueID = issueID;
77190             section.selection().selectAll('.issue-container').classed('active', function (d) {
77191               return d.id === _activeIssueID;
77192             });
77193           }
77194
77195           function renderDisclosureContent(selection) {
77196             selection.classed('grouped-items-area', true);
77197             _activeIssueID = _issues.length > 0 ? _issues[0].id : null;
77198             var containers = selection.selectAll('.issue-container').data(_issues, function (d) {
77199               return d.id;
77200             }); // Exit
77201
77202             containers.exit().remove(); // Enter
77203
77204             var containersEnter = containers.enter().append('div').attr('class', 'issue-container');
77205             var itemsEnter = containersEnter.append('div').attr('class', function (d) {
77206               return 'issue severity-' + d.severity;
77207             }).on('mouseover.highlight', function (d3_event, d) {
77208               // don't hover-highlight the selected entity
77209               var ids = d.entityIds.filter(function (e) {
77210                 return _entityIDs.indexOf(e) === -1;
77211               });
77212               utilHighlightEntities(ids, true, context);
77213             }).on('mouseout.highlight', function (d3_event, d) {
77214               var ids = d.entityIds.filter(function (e) {
77215                 return _entityIDs.indexOf(e) === -1;
77216               });
77217               utilHighlightEntities(ids, false, context);
77218             });
77219             var labelsEnter = itemsEnter.append('div').attr('class', 'issue-label');
77220             var textEnter = labelsEnter.append('button').attr('class', 'issue-text').on('click', function (d3_event, d) {
77221               makeActiveIssue(d.id); // expand only the clicked item
77222
77223               var extent = d.extent(context.graph());
77224
77225               if (extent) {
77226                 var setZoom = Math.max(context.map().zoom(), 19);
77227                 context.map().unobscuredCenterZoomEase(extent.center(), setZoom);
77228               }
77229             });
77230             textEnter.each(function (d) {
77231               var iconName = '#iD-icon-' + (d.severity === 'warning' ? 'alert' : 'error');
77232               select(this).call(svgIcon(iconName, 'issue-icon'));
77233             });
77234             textEnter.append('span').attr('class', 'issue-message');
77235             var infoButton = labelsEnter.append('button').attr('class', 'issue-info-button').attr('title', _t('icons.information')).call(svgIcon('#iD-icon-inspect'));
77236             infoButton.on('click', function (d3_event) {
77237               d3_event.stopPropagation();
77238               d3_event.preventDefault();
77239               this.blur(); // avoid keeping focus on the button - #4641
77240
77241               var container = select(this.parentNode.parentNode.parentNode);
77242               var info = container.selectAll('.issue-info');
77243               var isExpanded = info.classed('expanded');
77244               _expanded = !isExpanded;
77245               corePreferences('entity-issues.reference.expanded', _expanded); // update preference
77246
77247               if (isExpanded) {
77248                 info.transition().duration(200).style('max-height', '0px').style('opacity', '0').on('end', function () {
77249                   info.classed('expanded', false);
77250                 });
77251               } else {
77252                 info.classed('expanded', true).transition().duration(200).style('max-height', '200px').style('opacity', '1').on('end', function () {
77253                   info.style('max-height', null);
77254                 });
77255               }
77256             });
77257             itemsEnter.append('ul').attr('class', 'issue-fix-list');
77258             containersEnter.append('div').attr('class', 'issue-info' + (_expanded ? ' expanded' : '')).style('max-height', _expanded ? null : '0').style('opacity', _expanded ? '1' : '0').each(function (d) {
77259               if (typeof d.reference === 'function') {
77260                 select(this).call(d.reference);
77261               } else {
77262                 select(this).html(_t.html('inspector.no_documentation_key'));
77263               }
77264             }); // Update
77265
77266             containers = containers.merge(containersEnter).classed('active', function (d) {
77267               return d.id === _activeIssueID;
77268             });
77269             containers.selectAll('.issue-message').html(function (d) {
77270               return d.message(context);
77271             }); // fixes
77272
77273             var fixLists = containers.selectAll('.issue-fix-list');
77274             var fixes = fixLists.selectAll('.issue-fix-item').data(function (d) {
77275               return d.fixes ? d.fixes(context) : [];
77276             }, function (fix) {
77277               return fix.id;
77278             });
77279             fixes.exit().remove();
77280             var fixesEnter = fixes.enter().append('li').attr('class', 'issue-fix-item');
77281             var buttons = fixesEnter.append('button').on('click', function (d3_event, d) {
77282               // not all fixes are actionable
77283               if (select(this).attr('disabled') || !d.onClick) return; // Don't run another fix for this issue within a second of running one
77284               // (Necessary for "Select a feature type" fix. Most fixes should only ever run once)
77285
77286               if (d.issue.dateLastRanFix && new Date() - d.issue.dateLastRanFix < 1000) return;
77287               d.issue.dateLastRanFix = new Date(); // remove hover-highlighting
77288
77289               utilHighlightEntities(d.issue.entityIds.concat(d.entityIds), false, context);
77290               new Promise(function (resolve, reject) {
77291                 d.onClick(context, resolve, reject);
77292
77293                 if (d.onClick.length <= 1) {
77294                   // if the fix doesn't take any completion parameters then consider it resolved
77295                   resolve();
77296                 }
77297               }).then(function () {
77298                 // revalidate whenever the fix has finished running successfully
77299                 context.validator().validate();
77300               });
77301             }).on('mouseover.highlight', function (d3_event, d) {
77302               utilHighlightEntities(d.entityIds, true, context);
77303             }).on('mouseout.highlight', function (d3_event, d) {
77304               utilHighlightEntities(d.entityIds, false, context);
77305             });
77306             buttons.each(function (d) {
77307               var iconName = d.icon || 'iD-icon-wrench';
77308
77309               if (iconName.startsWith('maki')) {
77310                 iconName += '-15';
77311               }
77312
77313               select(this).call(svgIcon('#' + iconName, 'fix-icon'));
77314             });
77315             buttons.append('span').attr('class', 'fix-message').html(function (d) {
77316               return d.title;
77317             });
77318             fixesEnter.merge(fixes).selectAll('button').classed('actionable', function (d) {
77319               return d.onClick;
77320             }).attr('disabled', function (d) {
77321               return d.onClick ? null : 'true';
77322             }).attr('title', function (d) {
77323               if (d.disabledReason) {
77324                 return d.disabledReason;
77325               }
77326
77327               return null;
77328             });
77329           }
77330
77331           section.entityIDs = function (val) {
77332             if (!arguments.length) return _entityIDs;
77333
77334             if (!_entityIDs || !val || !utilArrayIdentical(_entityIDs, val)) {
77335               _entityIDs = val;
77336               _activeIssueID = null;
77337               reloadIssues();
77338             }
77339
77340             return section;
77341           };
77342
77343           return section;
77344         }
77345
77346         function uiPresetIcon() {
77347           var _preset;
77348
77349           var _geometry;
77350
77351           var _sizeClass = 'medium';
77352
77353           function isSmall() {
77354             return _sizeClass === 'small';
77355           }
77356
77357           function presetIcon(selection) {
77358             selection.each(render);
77359           }
77360
77361           function getIcon(p, geom) {
77362             if (isSmall() && p.isFallback && p.isFallback()) return 'iD-icon-' + p.id;
77363             if (p.icon) return p.icon;
77364             if (geom === 'line') return 'iD-other-line';
77365             if (geom === 'vertex') return p.isFallback() ? '' : 'temaki-vertex';
77366             if (isSmall() && geom === 'point') return '';
77367             return 'maki-marker-stroked';
77368           }
77369
77370           function renderPointBorder(container, drawPoint) {
77371             var pointBorder = container.selectAll('.preset-icon-point-border').data(drawPoint ? [0] : []);
77372             pointBorder.exit().remove();
77373             var pointBorderEnter = pointBorder.enter();
77374             var w = 40;
77375             var h = 40;
77376             pointBorderEnter.append('svg').attr('class', 'preset-icon-fill preset-icon-point-border').attr('width', w).attr('height', h).attr('viewBox', "0 0 ".concat(w, " ").concat(h)).append('path').attr('transform', 'translate(11.5, 8)').attr('d', 'M 17,8 C 17,13 11,21 8.5,23.5 C 6,21 0,13 0,8 C 0,4 4,-0.5 8.5,-0.5 C 13,-0.5 17,4 17,8 z');
77377             pointBorder = pointBorderEnter.merge(pointBorder);
77378           }
77379
77380           function renderCategoryBorder(container, category) {
77381             var categoryBorder = container.selectAll('.preset-icon-category-border').data(category ? [0] : []);
77382             categoryBorder.exit().remove();
77383             var categoryBorderEnter = categoryBorder.enter();
77384             var d = 60;
77385             var svgEnter = categoryBorderEnter.append('svg').attr('class', 'preset-icon-fill preset-icon-category-border').attr('width', d).attr('height', d).attr('viewBox', "0 0 ".concat(d, " ").concat(d));
77386             ['fill', 'stroke'].forEach(function (klass) {
77387               svgEnter.append('path').attr('class', "area ".concat(klass)).attr('d', 'M9.5,7.5 L25.5,7.5 L28.5,12.5 L49.5,12.5 C51.709139,12.5 53.5,14.290861 53.5,16.5 L53.5,43.5 C53.5,45.709139 51.709139,47.5 49.5,47.5 L10.5,47.5 C8.290861,47.5 6.5,45.709139 6.5,43.5 L6.5,12.5 L9.5,7.5 Z');
77388             });
77389             categoryBorder = categoryBorderEnter.merge(categoryBorder);
77390
77391             if (category) {
77392               var tagClasses = svgTagClasses().getClassesString(category.members.collection[0].addTags, '');
77393               categoryBorder.selectAll('path.stroke').attr('class', "area stroke ".concat(tagClasses));
77394               categoryBorder.selectAll('path.fill').attr('class', "area fill ".concat(tagClasses));
77395             }
77396           }
77397
77398           function renderCircleFill(container, drawVertex) {
77399             var vertexFill = container.selectAll('.preset-icon-fill-vertex').data(drawVertex ? [0] : []);
77400             vertexFill.exit().remove();
77401             var vertexFillEnter = vertexFill.enter();
77402             var w = 60;
77403             var h = 60;
77404             var d = 40;
77405             vertexFillEnter.append('svg').attr('class', 'preset-icon-fill preset-icon-fill-vertex').attr('width', w).attr('height', h).attr('viewBox', "0 0 ".concat(w, " ").concat(h)).append('circle').attr('cx', w / 2).attr('cy', h / 2).attr('r', d / 2);
77406             vertexFill = vertexFillEnter.merge(vertexFill);
77407           }
77408
77409           function renderSquareFill(container, drawArea, tagClasses) {
77410             var fill = container.selectAll('.preset-icon-fill-area').data(drawArea ? [0] : []);
77411             fill.exit().remove();
77412             var fillEnter = fill.enter();
77413             var d = isSmall() ? 40 : 60;
77414             var w = d;
77415             var h = d;
77416             var l = d * 2 / 3;
77417             var c1 = (w - l) / 2;
77418             var c2 = c1 + l;
77419             fillEnter = fillEnter.append('svg').attr('class', 'preset-icon-fill preset-icon-fill-area').attr('width', w).attr('height', h).attr('viewBox', "0 0 ".concat(w, " ").concat(h));
77420             ['fill', 'stroke'].forEach(function (klass) {
77421               fillEnter.append('path').attr('d', "M".concat(c1, " ").concat(c1, " L").concat(c1, " ").concat(c2, " L").concat(c2, " ").concat(c2, " L").concat(c2, " ").concat(c1, " Z")).attr('class', "area ".concat(klass));
77422             });
77423             var rVertex = 2.5;
77424             [[c1, c1], [c1, c2], [c2, c2], [c2, c1]].forEach(function (point) {
77425               fillEnter.append('circle').attr('class', 'vertex').attr('cx', point[0]).attr('cy', point[1]).attr('r', rVertex);
77426             });
77427
77428             if (!isSmall()) {
77429               var rMidpoint = 1.25;
77430               [[c1, w / 2], [c2, w / 2], [h / 2, c1], [h / 2, c2]].forEach(function (point) {
77431                 fillEnter.append('circle').attr('class', 'midpoint').attr('cx', point[0]).attr('cy', point[1]).attr('r', rMidpoint);
77432               });
77433             }
77434
77435             fill = fillEnter.merge(fill);
77436             fill.selectAll('path.stroke').attr('class', "area stroke ".concat(tagClasses));
77437             fill.selectAll('path.fill').attr('class', "area fill ".concat(tagClasses));
77438           }
77439
77440           function renderLine(container, drawLine, tagClasses) {
77441             var line = container.selectAll('.preset-icon-line').data(drawLine ? [0] : []);
77442             line.exit().remove();
77443             var lineEnter = line.enter();
77444             var d = isSmall() ? 40 : 60; // draw the line parametrically
77445
77446             var w = d;
77447             var h = d;
77448             var y = Math.round(d * 0.72);
77449             var l = Math.round(d * 0.6);
77450             var r = 2.5;
77451             var x1 = (w - l) / 2;
77452             var x2 = x1 + l;
77453             lineEnter = lineEnter.append('svg').attr('class', 'preset-icon-line').attr('width', w).attr('height', h).attr('viewBox', "0 0 ".concat(w, " ").concat(h));
77454             ['casing', 'stroke'].forEach(function (klass) {
77455               lineEnter.append('path').attr('d', "M".concat(x1, " ").concat(y, " L").concat(x2, " ").concat(y)).attr('class', "line ".concat(klass));
77456             });
77457             [[x1 - 1, y], [x2 + 1, y]].forEach(function (point) {
77458               lineEnter.append('circle').attr('class', 'vertex').attr('cx', point[0]).attr('cy', point[1]).attr('r', r);
77459             });
77460             line = lineEnter.merge(line);
77461             line.selectAll('path.stroke').attr('class', "line stroke ".concat(tagClasses));
77462             line.selectAll('path.casing').attr('class', "line casing ".concat(tagClasses));
77463           }
77464
77465           function renderRoute(container, drawRoute, p) {
77466             var route = container.selectAll('.preset-icon-route').data(drawRoute ? [0] : []);
77467             route.exit().remove();
77468             var routeEnter = route.enter();
77469             var d = isSmall() ? 40 : 60; // draw the route parametrically
77470
77471             var w = d;
77472             var h = d;
77473             var y1 = Math.round(d * 0.80);
77474             var y2 = Math.round(d * 0.68);
77475             var l = Math.round(d * 0.6);
77476             var r = 2;
77477             var x1 = (w - l) / 2;
77478             var x2 = x1 + l / 3;
77479             var x3 = x2 + l / 3;
77480             var x4 = x3 + l / 3;
77481             routeEnter = routeEnter.append('svg').attr('class', 'preset-icon-route').attr('width', w).attr('height', h).attr('viewBox', "0 0 ".concat(w, " ").concat(h));
77482             ['casing', 'stroke'].forEach(function (klass) {
77483               routeEnter.append('path').attr('d', "M".concat(x1, " ").concat(y1, " L").concat(x2, " ").concat(y2)).attr('class', "segment0 line ".concat(klass));
77484               routeEnter.append('path').attr('d', "M".concat(x2, " ").concat(y2, " L").concat(x3, " ").concat(y1)).attr('class', "segment1 line ".concat(klass));
77485               routeEnter.append('path').attr('d', "M".concat(x3, " ").concat(y1, " L").concat(x4, " ").concat(y2)).attr('class', "segment2 line ".concat(klass));
77486             });
77487             [[x1, y1], [x2, y2], [x3, y1], [x4, y2]].forEach(function (point) {
77488               routeEnter.append('circle').attr('class', 'vertex').attr('cx', point[0]).attr('cy', point[1]).attr('r', r);
77489             });
77490             route = routeEnter.merge(route);
77491
77492             if (drawRoute) {
77493               var routeType = p.tags.type === 'waterway' ? 'waterway' : p.tags.route;
77494               var segmentPresetIDs = routeSegments[routeType];
77495
77496               for (var i in segmentPresetIDs) {
77497                 var segmentPreset = _mainPresetIndex.item(segmentPresetIDs[i]);
77498                 var segmentTagClasses = svgTagClasses().getClassesString(segmentPreset.tags, '');
77499                 route.selectAll("path.stroke.segment".concat(i)).attr('class', "segment".concat(i, " line stroke ").concat(segmentTagClasses));
77500                 route.selectAll("path.casing.segment".concat(i)).attr('class', "segment".concat(i, " line casing ").concat(segmentTagClasses));
77501               }
77502             }
77503           }
77504
77505           function renderSvgIcon(container, picon, geom, isFramed, category, tagClasses) {
77506             var isMaki = picon && /^maki-/.test(picon);
77507             var isTemaki = picon && /^temaki-/.test(picon);
77508             var isFa = picon && /^fa[srb]-/.test(picon);
77509             var isiDIcon = picon && !(isMaki || isTemaki || isFa);
77510             var icon = container.selectAll('.preset-icon').data(picon ? [0] : []);
77511             icon.exit().remove();
77512             icon = icon.enter().append('div').attr('class', 'preset-icon').call(svgIcon('')).merge(icon);
77513             icon.attr('class', 'preset-icon ' + (geom ? geom + '-geom' : '')).classed('category', category).classed('framed', isFramed).classed('preset-icon-iD', isiDIcon);
77514             icon.selectAll('svg').attr('class', 'icon ' + picon + ' ' + (!isiDIcon && geom !== 'line' ? '' : tagClasses));
77515             var suffix = '';
77516
77517             if (isMaki) {
77518               suffix = isSmall() && geom === 'point' ? '-11' : '-15';
77519             }
77520
77521             icon.selectAll('use').attr('href', '#' + picon + suffix);
77522           }
77523
77524           function renderImageIcon(container, imageURL) {
77525             var imageIcon = container.selectAll('img.image-icon').data(imageURL ? [0] : []);
77526             imageIcon.exit().remove();
77527             imageIcon = imageIcon.enter().append('img').attr('class', 'image-icon').on('load', function () {
77528               return container.classed('showing-img', true);
77529             }).on('error', function () {
77530               return container.classed('showing-img', false);
77531             }).merge(imageIcon);
77532             imageIcon.attr('src', imageURL);
77533           } // Route icons are drawn with a zigzag annotation underneath:
77534           //     o   o
77535           //    / \ /
77536           //   o   o
77537           // This dataset defines the styles that are used to draw the zigzag segments.
77538
77539
77540           var routeSegments = {
77541             bicycle: ['highway/cycleway', 'highway/cycleway', 'highway/cycleway'],
77542             bus: ['highway/unclassified', 'highway/secondary', 'highway/primary'],
77543             trolleybus: ['highway/unclassified', 'highway/secondary', 'highway/primary'],
77544             detour: ['highway/tertiary', 'highway/residential', 'highway/unclassified'],
77545             ferry: ['route/ferry', 'route/ferry', 'route/ferry'],
77546             foot: ['highway/footway', 'highway/footway', 'highway/footway'],
77547             hiking: ['highway/path', 'highway/path', 'highway/path'],
77548             horse: ['highway/bridleway', 'highway/bridleway', 'highway/bridleway'],
77549             light_rail: ['railway/light_rail', 'railway/light_rail', 'railway/light_rail'],
77550             monorail: ['railway/monorail', 'railway/monorail', 'railway/monorail'],
77551             mtb: ['highway/path', 'highway/track', 'highway/bridleway'],
77552             pipeline: ['man_made/pipeline', 'man_made/pipeline', 'man_made/pipeline'],
77553             piste: ['piste/downhill', 'piste/hike', 'piste/nordic'],
77554             power: ['power/line', 'power/line', 'power/line'],
77555             road: ['highway/secondary', 'highway/primary', 'highway/trunk'],
77556             subway: ['railway/subway', 'railway/subway', 'railway/subway'],
77557             train: ['railway/rail', 'railway/rail', 'railway/rail'],
77558             tram: ['railway/tram', 'railway/tram', 'railway/tram'],
77559             waterway: ['waterway/stream', 'waterway/stream', 'waterway/stream']
77560           };
77561
77562           function render() {
77563             var p = _preset.apply(this, arguments);
77564
77565             var geom = _geometry ? _geometry.apply(this, arguments) : null;
77566
77567             if (geom === 'relation' && p.tags && (p.tags.type === 'route' && p.tags.route && routeSegments[p.tags.route] || p.tags.type === 'waterway')) {
77568               geom = 'route';
77569             }
77570
77571             var showThirdPartyIcons = corePreferences('preferences.privacy.thirdpartyicons') || 'true';
77572             var isFallback = isSmall() && p.isFallback && p.isFallback();
77573             var imageURL = showThirdPartyIcons === 'true' && p.imageURL;
77574             var picon = getIcon(p, geom);
77575             var isCategory = !p.setTags;
77576             var drawPoint = picon && geom === 'point' && isSmall() && !isFallback;
77577             var drawVertex = picon !== null && geom === 'vertex' && (!isSmall() || !isFallback);
77578             var drawLine = picon && geom === 'line' && !isFallback && !isCategory;
77579             var drawArea = picon && geom === 'area' && !isFallback && !isCategory;
77580             var drawRoute = picon && geom === 'route';
77581             var isFramed = drawVertex || drawArea || drawLine || drawRoute || isCategory;
77582             var tags = !isCategory ? p.setTags({}, geom) : {};
77583
77584             for (var k in tags) {
77585               if (tags[k] === '*') {
77586                 tags[k] = 'yes';
77587               }
77588             }
77589
77590             var tagClasses = svgTagClasses().getClassesString(tags, '');
77591             var selection = select(this);
77592             var container = selection.selectAll('.preset-icon-container').data([0]);
77593             container = container.enter().append('div').attr('class', "preset-icon-container ".concat(_sizeClass)).merge(container);
77594             container.classed('showing-img', !!imageURL).classed('fallback', isFallback);
77595             renderCategoryBorder(container, isCategory && p);
77596             renderPointBorder(container, drawPoint);
77597             renderCircleFill(container, drawVertex);
77598             renderSquareFill(container, drawArea, tagClasses);
77599             renderLine(container, drawLine, tagClasses);
77600             renderRoute(container, drawRoute, p);
77601             renderSvgIcon(container, picon, geom, isFramed, isCategory, tagClasses);
77602             renderImageIcon(container, imageURL);
77603           }
77604
77605           presetIcon.preset = function (val) {
77606             if (!arguments.length) return _preset;
77607             _preset = utilFunctor(val);
77608             return presetIcon;
77609           };
77610
77611           presetIcon.geometry = function (val) {
77612             if (!arguments.length) return _geometry;
77613             _geometry = utilFunctor(val);
77614             return presetIcon;
77615           };
77616
77617           presetIcon.sizeClass = function (val) {
77618             if (!arguments.length) return _sizeClass;
77619             _sizeClass = val;
77620             return presetIcon;
77621           };
77622
77623           return presetIcon;
77624         }
77625
77626         function uiSectionFeatureType(context) {
77627           var dispatch = dispatch$8('choose');
77628           var _entityIDs = [];
77629           var _presets = [];
77630
77631           var _tagReference;
77632
77633           var section = uiSection('feature-type', context).label(_t.html('inspector.feature_type')).disclosureContent(renderDisclosureContent);
77634
77635           function renderDisclosureContent(selection) {
77636             selection.classed('preset-list-item', true);
77637             selection.classed('mixed-types', _presets.length > 1);
77638             var presetButtonWrap = selection.selectAll('.preset-list-button-wrap').data([0]).enter().append('div').attr('class', 'preset-list-button-wrap');
77639             var presetButton = presetButtonWrap.append('button').attr('class', 'preset-list-button preset-reset').call(uiTooltip().title(_t.html('inspector.back_tooltip')).placement('bottom'));
77640             presetButton.append('div').attr('class', 'preset-icon-container');
77641             presetButton.append('div').attr('class', 'label').append('div').attr('class', 'label-inner');
77642             presetButtonWrap.append('div').attr('class', 'accessory-buttons');
77643             var tagReferenceBodyWrap = selection.selectAll('.tag-reference-body-wrap').data([0]);
77644             tagReferenceBodyWrap = tagReferenceBodyWrap.enter().append('div').attr('class', 'tag-reference-body-wrap').merge(tagReferenceBodyWrap); // update header
77645
77646             if (_tagReference) {
77647               selection.selectAll('.preset-list-button-wrap .accessory-buttons').style('display', _presets.length === 1 ? null : 'none').call(_tagReference.button);
77648               tagReferenceBodyWrap.style('display', _presets.length === 1 ? null : 'none').call(_tagReference.body);
77649             }
77650
77651             selection.selectAll('.preset-reset').on('click', function () {
77652               dispatch.call('choose', this, _presets);
77653             }).on('pointerdown pointerup mousedown mouseup', function (d3_event) {
77654               d3_event.preventDefault();
77655               d3_event.stopPropagation();
77656             });
77657             var geometries = entityGeometries();
77658             selection.select('.preset-list-item button').call(uiPresetIcon().geometry(_presets.length === 1 ? geometries.length === 1 && geometries[0] : null).preset(_presets.length === 1 ? _presets[0] : _mainPresetIndex.item('point')));
77659             var names = _presets.length === 1 ? [_presets[0].nameLabel(), _presets[0].subtitleLabel()].filter(Boolean) : [_t('inspector.multiple_types')];
77660             var label = selection.select('.label-inner');
77661             var nameparts = label.selectAll('.namepart').data(names, function (d) {
77662               return d;
77663             });
77664             nameparts.exit().remove();
77665             nameparts.enter().append('div').attr('class', 'namepart').html(function (d) {
77666               return d;
77667             });
77668           }
77669
77670           section.entityIDs = function (val) {
77671             if (!arguments.length) return _entityIDs;
77672             _entityIDs = val;
77673             return section;
77674           };
77675
77676           section.presets = function (val) {
77677             if (!arguments.length) return _presets; // don't reload the same preset
77678
77679             if (!utilArrayIdentical(val, _presets)) {
77680               _presets = val;
77681
77682               if (_presets.length === 1) {
77683                 _tagReference = uiTagReference(_presets[0].reference()).showing(false);
77684               }
77685             }
77686
77687             return section;
77688           };
77689
77690           function entityGeometries() {
77691             var counts = {};
77692
77693             for (var i in _entityIDs) {
77694               var geometry = context.graph().geometry(_entityIDs[i]);
77695               if (!counts[geometry]) counts[geometry] = 0;
77696               counts[geometry] += 1;
77697             }
77698
77699             return Object.keys(counts).sort(function (geom1, geom2) {
77700               return counts[geom2] - counts[geom1];
77701             });
77702           }
77703
77704           return utilRebind(section, dispatch, 'on');
77705         }
77706
77707         // It borrows some code from uiHelp
77708
77709         function uiFieldHelp(context, fieldName) {
77710           var fieldHelp = {};
77711
77712           var _inspector = select(null);
77713
77714           var _wrap = select(null);
77715
77716           var _body = select(null);
77717
77718           var fieldHelpKeys = {
77719             restrictions: [['about', ['about', 'from_via_to', 'maxdist', 'maxvia']], ['inspecting', ['about', 'from_shadow', 'allow_shadow', 'restrict_shadow', 'only_shadow', 'restricted', 'only']], ['modifying', ['about', 'indicators', 'allow_turn', 'restrict_turn', 'only_turn']], ['tips', ['simple', 'simple_example', 'indirect', 'indirect_example', 'indirect_noedit']]]
77720           };
77721           var fieldHelpHeadings = {};
77722           var replacements = {
77723             distField: _t.html('restriction.controls.distance'),
77724             viaField: _t.html('restriction.controls.via'),
77725             fromShadow: icon('#iD-turn-shadow', 'inline shadow from'),
77726             allowShadow: icon('#iD-turn-shadow', 'inline shadow allow'),
77727             restrictShadow: icon('#iD-turn-shadow', 'inline shadow restrict'),
77728             onlyShadow: icon('#iD-turn-shadow', 'inline shadow only'),
77729             allowTurn: icon('#iD-turn-yes', 'inline turn'),
77730             restrictTurn: icon('#iD-turn-no', 'inline turn'),
77731             onlyTurn: icon('#iD-turn-only', 'inline turn')
77732           }; // For each section, squash all the texts into a single markdown document
77733
77734           var docs = fieldHelpKeys[fieldName].map(function (key) {
77735             var helpkey = 'help.field.' + fieldName + '.' + key[0];
77736             var text = key[1].reduce(function (all, part) {
77737               var subkey = helpkey + '.' + part;
77738               var depth = fieldHelpHeadings[subkey]; // is this subkey a heading?
77739
77740               var hhh = depth ? Array(depth + 1).join('#') + ' ' : ''; // if so, prepend with some ##'s
77741
77742               return all + hhh + _t.html(subkey, replacements) + '\n\n';
77743             }, '');
77744             return {
77745               key: helpkey,
77746               title: _t.html(helpkey + '.title'),
77747               html: marked_1(text.trim())
77748             };
77749           });
77750
77751           function show() {
77752             updatePosition();
77753
77754             _body.classed('hide', false).style('opacity', '0').transition().duration(200).style('opacity', '1');
77755           }
77756
77757           function hide() {
77758             _body.classed('hide', true).transition().duration(200).style('opacity', '0').on('end', function () {
77759               _body.classed('hide', true);
77760             });
77761           }
77762
77763           function clickHelp(index) {
77764             var d = docs[index];
77765             var tkeys = fieldHelpKeys[fieldName][index][1];
77766
77767             _body.selectAll('.field-help-nav-item').classed('active', function (d, i) {
77768               return i === index;
77769             });
77770
77771             var content = _body.selectAll('.field-help-content').html(d.html); // class the paragraphs so we can find and style them
77772
77773
77774             content.selectAll('p').attr('class', function (d, i) {
77775               return tkeys[i];
77776             }); // insert special content for certain help sections
77777
77778             if (d.key === 'help.field.restrictions.inspecting') {
77779               content.insert('img', 'p.from_shadow').attr('class', 'field-help-image cf').attr('src', context.imagePath('tr_inspect.gif'));
77780             } else if (d.key === 'help.field.restrictions.modifying') {
77781               content.insert('img', 'p.allow_turn').attr('class', 'field-help-image cf').attr('src', context.imagePath('tr_modify.gif'));
77782             }
77783           }
77784
77785           fieldHelp.button = function (selection) {
77786             if (_body.empty()) return;
77787             var button = selection.selectAll('.field-help-button').data([0]); // enter/update
77788
77789             button.enter().append('button').attr('class', 'field-help-button').call(svgIcon('#iD-icon-help')).merge(button).on('click', function (d3_event) {
77790               d3_event.stopPropagation();
77791               d3_event.preventDefault();
77792
77793               if (_body.classed('hide')) {
77794                 show();
77795               } else {
77796                 hide();
77797               }
77798             });
77799           };
77800
77801           function updatePosition() {
77802             var wrap = _wrap.node();
77803
77804             var inspector = _inspector.node();
77805
77806             var wRect = wrap.getBoundingClientRect();
77807             var iRect = inspector.getBoundingClientRect();
77808
77809             _body.style('top', wRect.top + inspector.scrollTop - iRect.top + 'px');
77810           }
77811
77812           fieldHelp.body = function (selection) {
77813             // This control expects the field to have a form-field-input-wrap div
77814             _wrap = selection.selectAll('.form-field-input-wrap');
77815             if (_wrap.empty()) return; // absolute position relative to the inspector, so it "floats" above the fields
77816
77817             _inspector = context.container().select('.sidebar .entity-editor-pane .inspector-body');
77818             if (_inspector.empty()) return;
77819             _body = _inspector.selectAll('.field-help-body').data([0]);
77820
77821             var enter = _body.enter().append('div').attr('class', 'field-help-body hide'); // initially hidden
77822
77823
77824             var titleEnter = enter.append('div').attr('class', 'field-help-title cf');
77825             titleEnter.append('h2').attr('class', _mainLocalizer.textDirection() === 'rtl' ? 'fr' : 'fl').html(_t.html('help.field.' + fieldName + '.title'));
77826             titleEnter.append('button').attr('class', 'fr close').on('click', function (d3_event) {
77827               d3_event.stopPropagation();
77828               d3_event.preventDefault();
77829               hide();
77830             }).call(svgIcon('#iD-icon-close'));
77831             var navEnter = enter.append('div').attr('class', 'field-help-nav cf');
77832             var titles = docs.map(function (d) {
77833               return d.title;
77834             });
77835             navEnter.selectAll('.field-help-nav-item').data(titles).enter().append('div').attr('class', 'field-help-nav-item').html(function (d) {
77836               return d;
77837             }).on('click', function (d3_event, d) {
77838               d3_event.stopPropagation();
77839               d3_event.preventDefault();
77840               clickHelp(titles.indexOf(d));
77841             });
77842             enter.append('div').attr('class', 'field-help-content');
77843             _body = _body.merge(enter);
77844             clickHelp(0);
77845           };
77846
77847           return fieldHelp;
77848         }
77849
77850         function uiFieldCheck(field, context) {
77851           var dispatch = dispatch$8('change');
77852           var options = field.options;
77853           var values = [];
77854           var texts = [];
77855
77856           var _tags;
77857
77858           var input = select(null);
77859           var text = select(null);
77860           var label = select(null);
77861           var reverser = select(null);
77862
77863           var _impliedYes;
77864
77865           var _entityIDs = [];
77866
77867           var _value;
77868
77869           if (options) {
77870             for (var i in options) {
77871               var v = options[i];
77872               values.push(v === 'undefined' ? undefined : v);
77873               texts.push(field.t.html('options.' + v, {
77874                 'default': v
77875               }));
77876             }
77877           } else {
77878             values = [undefined, 'yes'];
77879             texts = [_t.html('inspector.unknown'), _t.html('inspector.check.yes')];
77880
77881             if (field.type !== 'defaultCheck') {
77882               values.push('no');
77883               texts.push(_t.html('inspector.check.no'));
77884             }
77885           } // Checks tags to see whether an undefined value is "Assumed to be Yes"
77886
77887
77888           function checkImpliedYes() {
77889             _impliedYes = field.id === 'oneway_yes'; // hack: pretend `oneway` field is a `oneway_yes` field
77890             // where implied oneway tag exists (e.g. `junction=roundabout`) #2220, #1841
77891
77892             if (field.id === 'oneway') {
77893               var entity = context.entity(_entityIDs[0]);
77894
77895               for (var key in entity.tags) {
77896                 if (key in osmOneWayTags && entity.tags[key] in osmOneWayTags[key]) {
77897                   _impliedYes = true;
77898                   texts[0] = _t.html('_tagging.presets.fields.oneway_yes.options.undefined');
77899                   break;
77900                 }
77901               }
77902             }
77903           }
77904
77905           function reverserHidden() {
77906             if (!context.container().select('div.inspector-hover').empty()) return true;
77907             return !(_value === 'yes' || _impliedYes && !_value);
77908           }
77909
77910           function reverserSetText(selection) {
77911             var entity = _entityIDs.length && context.hasEntity(_entityIDs[0]);
77912             if (reverserHidden() || !entity) return selection;
77913             var first = entity.first();
77914             var last = entity.isClosed() ? entity.nodes[entity.nodes.length - 2] : entity.last();
77915             var pseudoDirection = first < last;
77916             var icon = pseudoDirection ? '#iD-icon-forward' : '#iD-icon-backward';
77917             selection.selectAll('.reverser-span').html(_t.html('inspector.check.reverser')).call(svgIcon(icon, 'inline'));
77918             return selection;
77919           }
77920
77921           var check = function check(selection) {
77922             checkImpliedYes();
77923             label = selection.selectAll('.form-field-input-wrap').data([0]);
77924             var enter = label.enter().append('label').attr('class', 'form-field-input-wrap form-field-input-check');
77925             enter.append('input').property('indeterminate', field.type !== 'defaultCheck').attr('type', 'checkbox').attr('id', field.domId);
77926             enter.append('span').html(texts[0]).attr('class', 'value');
77927
77928             if (field.type === 'onewayCheck') {
77929               enter.append('button').attr('class', 'reverser' + (reverserHidden() ? ' hide' : '')).append('span').attr('class', 'reverser-span');
77930             }
77931
77932             label = label.merge(enter);
77933             input = label.selectAll('input');
77934             text = label.selectAll('span.value');
77935             input.on('click', function (d3_event) {
77936               d3_event.stopPropagation();
77937               var t = {};
77938
77939               if (Array.isArray(_tags[field.key])) {
77940                 if (values.indexOf('yes') !== -1) {
77941                   t[field.key] = 'yes';
77942                 } else {
77943                   t[field.key] = values[0];
77944                 }
77945               } else {
77946                 t[field.key] = values[(values.indexOf(_value) + 1) % values.length];
77947               } // Don't cycle through `alternating` or `reversible` states - #4970
77948               // (They are supported as translated strings, but should not toggle with clicks)
77949
77950
77951               if (t[field.key] === 'reversible' || t[field.key] === 'alternating') {
77952                 t[field.key] = values[0];
77953               }
77954
77955               dispatch.call('change', this, t);
77956             });
77957
77958             if (field.type === 'onewayCheck') {
77959               reverser = label.selectAll('.reverser');
77960               reverser.call(reverserSetText).on('click', function (d3_event) {
77961                 d3_event.preventDefault();
77962                 d3_event.stopPropagation();
77963                 context.perform(function (graph) {
77964                   for (var i in _entityIDs) {
77965                     graph = actionReverse(_entityIDs[i])(graph);
77966                   }
77967
77968                   return graph;
77969                 }, _t('operations.reverse.annotation.line', {
77970                   n: 1
77971                 })); // must manually revalidate since no 'change' event was called
77972
77973                 context.validator().validate();
77974                 select(this).call(reverserSetText);
77975               });
77976             }
77977           };
77978
77979           check.entityIDs = function (val) {
77980             if (!arguments.length) return _entityIDs;
77981             _entityIDs = val;
77982             return check;
77983           };
77984
77985           check.tags = function (tags) {
77986             _tags = tags;
77987
77988             function isChecked(val) {
77989               return val !== 'no' && val !== '' && val !== undefined && val !== null;
77990             }
77991
77992             function textFor(val) {
77993               if (val === '') val = undefined;
77994               var index = values.indexOf(val);
77995               return index !== -1 ? texts[index] : '"' + val + '"';
77996             }
77997
77998             checkImpliedYes();
77999             var isMixed = Array.isArray(tags[field.key]);
78000             _value = !isMixed && tags[field.key] && tags[field.key].toLowerCase();
78001
78002             if (field.type === 'onewayCheck' && (_value === '1' || _value === '-1')) {
78003               _value = 'yes';
78004             }
78005
78006             input.property('indeterminate', isMixed || field.type !== 'defaultCheck' && !_value).property('checked', isChecked(_value));
78007             text.html(isMixed ? _t.html('inspector.multiple_values') : textFor(_value)).classed('mixed', isMixed);
78008             label.classed('set', !!_value);
78009
78010             if (field.type === 'onewayCheck') {
78011               reverser.classed('hide', reverserHidden()).call(reverserSetText);
78012             }
78013           };
78014
78015           check.focus = function () {
78016             input.node().focus();
78017           };
78018
78019           return utilRebind(check, dispatch, 'on');
78020         }
78021
78022         function uiFieldCombo(field, context) {
78023           var dispatch = dispatch$8('change');
78024
78025           var _isMulti = field.type === 'multiCombo' || field.type === 'manyCombo';
78026
78027           var _isNetwork = field.type === 'networkCombo';
78028
78029           var _isSemi = field.type === 'semiCombo';
78030
78031           var _optarray = field.options;
78032
78033           var _showTagInfoSuggestions = field.type !== 'manyCombo' && field.autoSuggestions !== false;
78034
78035           var _allowCustomValues = field.type !== 'manyCombo' && field.customValues !== false;
78036
78037           var _snake_case = field.snake_case || field.snake_case === undefined;
78038
78039           var _combobox = uiCombobox(context, 'combo-' + field.safeid).caseSensitive(field.caseSensitive).minItems(_isMulti || _isSemi ? 1 : 2);
78040
78041           var _container = select(null);
78042
78043           var _inputWrap = select(null);
78044
78045           var _input = select(null);
78046
78047           var _comboData = [];
78048           var _multiData = [];
78049           var _entityIDs = [];
78050
78051           var _tags;
78052
78053           var _countryCode;
78054
78055           var _staticPlaceholder; // initialize deprecated tags array
78056
78057
78058           var _dataDeprecated = [];
78059           _mainFileFetcher.get('deprecated').then(function (d) {
78060             _dataDeprecated = d;
78061           })["catch"](function () {
78062             /* ignore */
78063           }); // ensure multiCombo field.key ends with a ':'
78064
78065           if (_isMulti && field.key && /[^:]$/.test(field.key)) {
78066             field.key += ':';
78067           }
78068
78069           function snake(s) {
78070             return s.replace(/\s+/g, '_').toLowerCase();
78071           }
78072
78073           function clean(s) {
78074             return s.split(';').map(function (s) {
78075               return s.trim();
78076             }).join(';');
78077           } // returns the tag value for a display value
78078           // (for multiCombo, dval should be the key suffix, not the entire key)
78079
78080
78081           function tagValue(dval) {
78082             dval = clean(dval || '');
78083
78084             var found = _comboData.find(function (o) {
78085               return o.key && clean(o.value) === dval;
78086             });
78087
78088             if (found) return found.key;
78089
78090             if (field.type === 'typeCombo' && !dval) {
78091               return 'yes';
78092             }
78093
78094             return (_snake_case ? snake(dval) : dval) || undefined;
78095           } // returns the display value for a tag value
78096           // (for multiCombo, tval should be the key suffix, not the entire key)
78097
78098
78099           function displayValue(tval) {
78100             tval = tval || '';
78101
78102             if (field.hasTextForStringId('options.' + tval)) {
78103               return field.t('options.' + tval, {
78104                 "default": tval
78105               });
78106             }
78107
78108             if (field.type === 'typeCombo' && tval.toLowerCase() === 'yes') {
78109               return '';
78110             }
78111
78112             return tval;
78113           } // Compute the difference between arrays of objects by `value` property
78114           //
78115           // objectDifference([{value:1}, {value:2}, {value:3}], [{value:2}])
78116           // > [{value:1}, {value:3}]
78117           //
78118
78119
78120           function objectDifference(a, b) {
78121             return a.filter(function (d1) {
78122               return !b.some(function (d2) {
78123                 return !d2.isMixed && d1.value === d2.value;
78124               });
78125             });
78126           }
78127
78128           function initCombo(selection, attachTo) {
78129             if (!_allowCustomValues) {
78130               selection.attr('readonly', 'readonly');
78131             }
78132
78133             if (_showTagInfoSuggestions && services.taginfo) {
78134               selection.call(_combobox.fetcher(setTaginfoValues), attachTo);
78135               setTaginfoValues('', setPlaceholder);
78136             } else {
78137               selection.call(_combobox, attachTo);
78138               setStaticValues(setPlaceholder);
78139             }
78140           }
78141
78142           function setStaticValues(callback) {
78143             if (!_optarray) return;
78144             _comboData = _optarray.map(function (v) {
78145               return {
78146                 key: v,
78147                 value: field.t('options.' + v, {
78148                   "default": v
78149                 }),
78150                 title: v,
78151                 display: field.t.html('options.' + v, {
78152                   "default": v
78153                 }),
78154                 klass: field.hasTextForStringId('options.' + v) ? '' : 'raw-option'
78155               };
78156             });
78157
78158             _combobox.data(objectDifference(_comboData, _multiData));
78159
78160             if (callback) callback(_comboData);
78161           }
78162
78163           function setTaginfoValues(q, callback) {
78164             var fn = _isMulti ? 'multikeys' : 'values';
78165             var query = (_isMulti ? field.key : '') + q;
78166             var hasCountryPrefix = _isNetwork && _countryCode && _countryCode.indexOf(q.toLowerCase()) === 0;
78167
78168             if (hasCountryPrefix) {
78169               query = _countryCode + ':';
78170             }
78171
78172             var params = {
78173               debounce: q !== '',
78174               key: field.key,
78175               query: query
78176             };
78177
78178             if (_entityIDs.length) {
78179               params.geometry = context.graph().geometry(_entityIDs[0]);
78180             }
78181
78182             services.taginfo[fn](params, function (err, data) {
78183               if (err) return;
78184               data = data.filter(function (d) {
78185                 if (field.type === 'typeCombo' && d.value === 'yes') {
78186                   // don't show the fallback value
78187                   return false;
78188                 } // don't show values with very low usage
78189
78190
78191                 return !d.count || d.count > 10;
78192               });
78193               var deprecatedValues = osmEntity.deprecatedTagValuesByKey(_dataDeprecated)[field.key];
78194
78195               if (deprecatedValues) {
78196                 // don't suggest deprecated tag values
78197                 data = data.filter(function (d) {
78198                   return deprecatedValues.indexOf(d.value) === -1;
78199                 });
78200               }
78201
78202               if (hasCountryPrefix) {
78203                 data = data.filter(function (d) {
78204                   return d.value.toLowerCase().indexOf(_countryCode + ':') === 0;
78205                 });
78206               } // hide the caret if there are no suggestions
78207
78208
78209               _container.classed('empty-combobox', data.length === 0);
78210
78211               _comboData = data.map(function (d) {
78212                 var k = d.value;
78213                 if (_isMulti) k = k.replace(field.key, '');
78214                 var label = field.t('options.' + k, {
78215                   "default": k
78216                 });
78217                 return {
78218                   key: k,
78219                   value: label,
78220                   display: field.t.html('options.' + k, {
78221                     "default": k
78222                   }),
78223                   title: d.title || label,
78224                   klass: field.hasTextForStringId('options.' + k) ? '' : 'raw-option'
78225                 };
78226               });
78227               _comboData = objectDifference(_comboData, _multiData);
78228               if (callback) callback(_comboData);
78229             });
78230           }
78231
78232           function setPlaceholder(values) {
78233             if (_isMulti || _isSemi) {
78234               _staticPlaceholder = field.placeholder() || _t('inspector.add');
78235             } else {
78236               var vals = values.map(function (d) {
78237                 return d.value;
78238               }).filter(function (s) {
78239                 return s.length < 20;
78240               });
78241               var placeholders = vals.length > 1 ? vals : values.map(function (d) {
78242                 return d.key;
78243               });
78244               _staticPlaceholder = field.placeholder() || placeholders.slice(0, 3).join(', ');
78245             }
78246
78247             if (!/(…|\.\.\.)$/.test(_staticPlaceholder)) {
78248               _staticPlaceholder += '…';
78249             }
78250
78251             var ph;
78252
78253             if (!_isMulti && !_isSemi && _tags && Array.isArray(_tags[field.key])) {
78254               ph = _t('inspector.multiple_values');
78255             } else {
78256               ph = _staticPlaceholder;
78257             }
78258
78259             _container.selectAll('input').attr('placeholder', ph);
78260           }
78261
78262           function change() {
78263             var t = {};
78264             var val;
78265
78266             if (_isMulti || _isSemi) {
78267               val = tagValue(utilGetSetValue(_input).replace(/,/g, ';')) || '';
78268
78269               _container.classed('active', false);
78270
78271               utilGetSetValue(_input, '');
78272               var vals = val.split(';').filter(Boolean);
78273               if (!vals.length) return;
78274
78275               if (_isMulti) {
78276                 utilArrayUniq(vals).forEach(function (v) {
78277                   var key = (field.key || '') + v;
78278
78279                   if (_tags) {
78280                     // don't set a multicombo value to 'yes' if it already has a non-'no' value
78281                     // e.g. `language:de=main`
78282                     var old = _tags[key];
78283                     if (typeof old === 'string' && old.toLowerCase() !== 'no') return;
78284                   }
78285
78286                   key = context.cleanTagKey(key);
78287                   field.keys.push(key);
78288                   t[key] = 'yes';
78289                 });
78290               } else if (_isSemi) {
78291                 var arr = _multiData.map(function (d) {
78292                   return d.key;
78293                 });
78294
78295                 arr = arr.concat(vals);
78296                 t[field.key] = context.cleanTagValue(utilArrayUniq(arr).filter(Boolean).join(';'));
78297               }
78298
78299               window.setTimeout(function () {
78300                 _input.node().focus();
78301               }, 10);
78302             } else {
78303               var rawValue = utilGetSetValue(_input); // don't override multiple values with blank string
78304
78305               if (!rawValue && Array.isArray(_tags[field.key])) return;
78306               val = context.cleanTagValue(tagValue(rawValue));
78307               t[field.key] = val || undefined;
78308             }
78309
78310             dispatch.call('change', this, t);
78311           }
78312
78313           function removeMultikey(d3_event, d) {
78314             d3_event.preventDefault();
78315             d3_event.stopPropagation();
78316             var t = {};
78317
78318             if (_isMulti) {
78319               t[d.key] = undefined;
78320             } else if (_isSemi) {
78321               var arr = _multiData.map(function (md) {
78322                 return md.key === d.key ? null : md.key;
78323               }).filter(Boolean);
78324
78325               arr = utilArrayUniq(arr);
78326               t[field.key] = arr.length ? arr.join(';') : undefined;
78327             }
78328
78329             dispatch.call('change', this, t);
78330           }
78331
78332           function combo(selection) {
78333             _container = selection.selectAll('.form-field-input-wrap').data([0]);
78334             var type = _isMulti || _isSemi ? 'multicombo' : 'combo';
78335             _container = _container.enter().append('div').attr('class', 'form-field-input-wrap form-field-input-' + type).merge(_container);
78336
78337             if (_isMulti || _isSemi) {
78338               _container = _container.selectAll('.chiplist').data([0]);
78339               var listClass = 'chiplist'; // Use a separate line for each value in the Destinations and Via fields
78340               // to mimic highway exit signs
78341
78342               if (field.key === 'destination' || field.key === 'via') {
78343                 listClass += ' full-line-chips';
78344               }
78345
78346               _container = _container.enter().append('ul').attr('class', listClass).on('click', function () {
78347                 window.setTimeout(function () {
78348                   _input.node().focus();
78349                 }, 10);
78350               }).merge(_container);
78351               _inputWrap = _container.selectAll('.input-wrap').data([0]);
78352               _inputWrap = _inputWrap.enter().append('li').attr('class', 'input-wrap').merge(_inputWrap);
78353               _input = _inputWrap.selectAll('input').data([0]);
78354             } else {
78355               _input = _container.selectAll('input').data([0]);
78356             }
78357
78358             _input = _input.enter().append('input').attr('type', 'text').attr('id', field.domId).call(utilNoAuto).call(initCombo, selection).merge(_input);
78359
78360             if (_isNetwork) {
78361               var extent = combinedEntityExtent();
78362               var countryCode = extent && iso1A2Code(extent.center());
78363               _countryCode = countryCode && countryCode.toLowerCase();
78364             }
78365
78366             _input.on('change', change).on('blur', change);
78367
78368             _input.on('keydown.field', function (d3_event) {
78369               switch (d3_event.keyCode) {
78370                 case 13:
78371                   // ↩ Return
78372                   _input.node().blur(); // blurring also enters the value
78373
78374
78375                   d3_event.stopPropagation();
78376                   break;
78377               }
78378             });
78379
78380             if (_isMulti || _isSemi) {
78381               _combobox.on('accept', function () {
78382                 _input.node().blur();
78383
78384                 _input.node().focus();
78385               });
78386
78387               _input.on('focus', function () {
78388                 _container.classed('active', true);
78389               });
78390             }
78391           }
78392
78393           combo.tags = function (tags) {
78394             _tags = tags;
78395
78396             if (_isMulti || _isSemi) {
78397               _multiData = [];
78398               var maxLength;
78399
78400               if (_isMulti) {
78401                 // Build _multiData array containing keys already set..
78402                 for (var k in tags) {
78403                   if (field.key && k.indexOf(field.key) !== 0) continue;
78404                   if (!field.key && field.keys.indexOf(k) === -1) continue;
78405                   var v = tags[k];
78406                   if (!v || typeof v === 'string' && v.toLowerCase() === 'no') continue;
78407                   var suffix = field.key ? k.substr(field.key.length) : k;
78408
78409                   _multiData.push({
78410                     key: k,
78411                     value: displayValue(suffix),
78412                     isMixed: Array.isArray(v)
78413                   });
78414                 }
78415
78416                 if (field.key) {
78417                   // Set keys for form-field modified (needed for undo and reset buttons)..
78418                   field.keys = _multiData.map(function (d) {
78419                     return d.key;
78420                   }); // limit the input length so it fits after prepending the key prefix
78421
78422                   maxLength = context.maxCharsForTagKey() - utilUnicodeCharsCount(field.key);
78423                 } else {
78424                   maxLength = context.maxCharsForTagKey();
78425                 }
78426               } else if (_isSemi) {
78427                 var allValues = [];
78428                 var commonValues;
78429
78430                 if (Array.isArray(tags[field.key])) {
78431                   tags[field.key].forEach(function (tagVal) {
78432                     var thisVals = utilArrayUniq((tagVal || '').split(';')).filter(Boolean);
78433                     allValues = allValues.concat(thisVals);
78434
78435                     if (!commonValues) {
78436                       commonValues = thisVals;
78437                     } else {
78438                       commonValues = commonValues.filter(function (value) {
78439                         return thisVals.includes(value);
78440                       });
78441                     }
78442                   });
78443                   allValues = utilArrayUniq(allValues).filter(Boolean);
78444                 } else {
78445                   allValues = utilArrayUniq((tags[field.key] || '').split(';')).filter(Boolean);
78446                   commonValues = allValues;
78447                 }
78448
78449                 _multiData = allValues.map(function (v) {
78450                   return {
78451                     key: v,
78452                     value: displayValue(v),
78453                     isMixed: !commonValues.includes(v)
78454                   };
78455                 });
78456                 var currLength = utilUnicodeCharsCount(commonValues.join(';')); // limit the input length to the remaining available characters
78457
78458                 maxLength = context.maxCharsForTagValue() - currLength;
78459
78460                 if (currLength > 0) {
78461                   // account for the separator if a new value will be appended to existing
78462                   maxLength -= 1;
78463                 }
78464               } // a negative maxlength doesn't make sense
78465
78466
78467               maxLength = Math.max(0, maxLength);
78468               var allowDragAndDrop = _isSemi // only semiCombo values are ordered
78469               && !Array.isArray(tags[field.key]); // Exclude existing multikeys from combo options..
78470
78471               var available = objectDifference(_comboData, _multiData);
78472
78473               _combobox.data(available); // Hide 'Add' button if this field uses fixed set of
78474               // options and they're all currently used,
78475               // or if the field is already at its character limit
78476
78477
78478               var hideAdd = !_allowCustomValues && !available.length || maxLength <= 0;
78479
78480               _container.selectAll('.chiplist .input-wrap').style('display', hideAdd ? 'none' : null); // Render chips
78481
78482
78483               var chips = _container.selectAll('.chip').data(_multiData);
78484
78485               chips.exit().remove();
78486               var enter = chips.enter().insert('li', '.input-wrap').attr('class', 'chip');
78487               enter.append('span');
78488               enter.append('a');
78489               chips = chips.merge(enter).order().classed('raw-value', function (d) {
78490                 var k = d.key;
78491                 if (_isMulti) k = k.replace(field.key, '');
78492                 return !field.hasTextForStringId('options.' + k);
78493               }).classed('draggable', allowDragAndDrop).classed('mixed', function (d) {
78494                 return d.isMixed;
78495               }).attr('title', function (d) {
78496                 return d.isMixed ? _t('inspector.unshared_value_tooltip') : null;
78497               });
78498
78499               if (allowDragAndDrop) {
78500                 registerDragAndDrop(chips);
78501               }
78502
78503               chips.select('span').html(function (d) {
78504                 return d.value;
78505               });
78506               chips.select('a').attr('href', '#').on('click', removeMultikey).attr('class', 'remove').html('×');
78507             } else {
78508               var isMixed = Array.isArray(tags[field.key]);
78509               var mixedValues = isMixed && tags[field.key].map(function (val) {
78510                 return displayValue(val);
78511               }).filter(Boolean);
78512               var showsValue = !isMixed && tags[field.key] && !(field.type === 'typeCombo' && tags[field.key] === 'yes');
78513               var isRawValue = showsValue && !field.hasTextForStringId('options.' + tags[field.key]);
78514               var isKnownValue = showsValue && !isRawValue;
78515               var isReadOnly = !_allowCustomValues || isKnownValue;
78516               utilGetSetValue(_input, !isMixed ? displayValue(tags[field.key]) : '').classed('raw-value', isRawValue).classed('known-value', isKnownValue).attr('readonly', isReadOnly ? 'readonly' : undefined).attr('title', isMixed ? mixedValues.join('\n') : undefined).attr('placeholder', isMixed ? _t('inspector.multiple_values') : _staticPlaceholder || '').classed('mixed', isMixed).on('keydown.deleteCapture', function (d3_event) {
78517                 if (isReadOnly && isKnownValue && (d3_event.keyCode === utilKeybinding.keyCodes['⌫'] || d3_event.keyCode === utilKeybinding.keyCodes['⌦'])) {
78518                   d3_event.preventDefault();
78519                   d3_event.stopPropagation();
78520                   var t = {};
78521                   t[field.key] = undefined;
78522                   dispatch.call('change', this, t);
78523                 }
78524               });
78525             }
78526           };
78527
78528           function registerDragAndDrop(selection) {
78529             // allow drag and drop re-ordering of chips
78530             var dragOrigin, targetIndex;
78531             selection.call(d3_drag().on('start', function (d3_event) {
78532               dragOrigin = {
78533                 x: d3_event.x,
78534                 y: d3_event.y
78535               };
78536               targetIndex = null;
78537             }).on('drag', function (d3_event) {
78538               var x = d3_event.x - dragOrigin.x,
78539                   y = d3_event.y - dragOrigin.y;
78540               if (!select(this).classed('dragging') && // don't display drag until dragging beyond a distance threshold
78541               Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2)) <= 5) return;
78542               var index = selection.nodes().indexOf(this);
78543               select(this).classed('dragging', true);
78544               targetIndex = null;
78545               var targetIndexOffsetTop = null;
78546               var draggedTagWidth = select(this).node().offsetWidth;
78547
78548               if (field.key === 'destination' || field.key === 'via') {
78549                 // meaning tags are full width
78550                 _container.selectAll('.chip').style('transform', function (d2, index2) {
78551                   var node = select(this).node();
78552
78553                   if (index === index2) {
78554                     return 'translate(' + x + 'px, ' + y + 'px)'; // move the dragged tag up the order
78555                   } else if (index2 > index && d3_event.y > node.offsetTop) {
78556                     if (targetIndex === null || index2 > targetIndex) {
78557                       targetIndex = index2;
78558                     }
78559
78560                     return 'translateY(-100%)'; // move the dragged tag down the order
78561                   } else if (index2 < index && d3_event.y < node.offsetTop + node.offsetHeight) {
78562                     if (targetIndex === null || index2 < targetIndex) {
78563                       targetIndex = index2;
78564                     }
78565
78566                     return 'translateY(100%)';
78567                   }
78568
78569                   return null;
78570                 });
78571               } else {
78572                 _container.selectAll('.chip').each(function (d2, index2) {
78573                   var node = select(this).node(); // check the cursor is in the bounding box
78574
78575                   if (index !== index2 && d3_event.x < node.offsetLeft + node.offsetWidth + 5 && d3_event.x > node.offsetLeft && d3_event.y < node.offsetTop + node.offsetHeight && d3_event.y > node.offsetTop) {
78576                     targetIndex = index2;
78577                     targetIndexOffsetTop = node.offsetTop;
78578                   }
78579                 }).style('transform', function (d2, index2) {
78580                   var node = select(this).node();
78581
78582                   if (index === index2) {
78583                     return 'translate(' + x + 'px, ' + y + 'px)';
78584                   } // only translate tags in the same row
78585
78586
78587                   if (node.offsetTop === targetIndexOffsetTop) {
78588                     if (index2 < index && index2 >= targetIndex) {
78589                       return 'translateX(' + draggedTagWidth + 'px)';
78590                     } else if (index2 > index && index2 <= targetIndex) {
78591                       return 'translateX(-' + draggedTagWidth + 'px)';
78592                     }
78593                   }
78594
78595                   return null;
78596                 });
78597               }
78598             }).on('end', function () {
78599               if (!select(this).classed('dragging')) {
78600                 return;
78601               }
78602
78603               var index = selection.nodes().indexOf(this);
78604               select(this).classed('dragging', false);
78605
78606               _container.selectAll('.chip').style('transform', null);
78607
78608               if (typeof targetIndex === 'number') {
78609                 var element = _multiData[index];
78610
78611                 _multiData.splice(index, 1);
78612
78613                 _multiData.splice(targetIndex, 0, element);
78614
78615                 var t = {};
78616
78617                 if (_multiData.length) {
78618                   t[field.key] = _multiData.map(function (element) {
78619                     return element.key;
78620                   }).join(';');
78621                 } else {
78622                   t[field.key] = undefined;
78623                 }
78624
78625                 dispatch.call('change', this, t);
78626               }
78627
78628               dragOrigin = undefined;
78629               targetIndex = undefined;
78630             }));
78631           }
78632
78633           combo.focus = function () {
78634             _input.node().focus();
78635           };
78636
78637           combo.entityIDs = function (val) {
78638             if (!arguments.length) return _entityIDs;
78639             _entityIDs = val;
78640             return combo;
78641           };
78642
78643           function combinedEntityExtent() {
78644             return _entityIDs && _entityIDs.length && utilTotalExtent(_entityIDs, context.graph());
78645           }
78646
78647           return utilRebind(combo, dispatch, 'on');
78648         }
78649
78650         function uiFieldText(field, context) {
78651           var dispatch = dispatch$8('change');
78652           var input = select(null);
78653           var outlinkButton = select(null);
78654           var _entityIDs = [];
78655
78656           var _tags;
78657
78658           var _phoneFormats = {};
78659
78660           if (field.type === 'tel') {
78661             _mainFileFetcher.get('phone_formats').then(function (d) {
78662               _phoneFormats = d;
78663               updatePhonePlaceholder();
78664             })["catch"](function () {
78665               /* ignore */
78666             });
78667           }
78668
78669           function calcLocked() {
78670             // Protect certain fields that have a companion `*:wikidata` value
78671             var isLocked = (field.id === 'brand' || field.id === 'network' || field.id === 'operator' || field.id === 'flag') && _entityIDs.length && _entityIDs.some(function (entityID) {
78672               var entity = context.graph().hasEntity(entityID);
78673               if (!entity) return false; // Features linked to Wikidata are likely important and should be protected
78674
78675               if (entity.tags.wikidata) return true;
78676               var preset = _mainPresetIndex.match(entity, context.graph());
78677               var isSuggestion = preset && preset.suggestion; // Lock the field if there is a value and a companion `*:wikidata` value
78678
78679               var which = field.id; // 'brand', 'network', 'operator', 'flag'
78680
78681               return isSuggestion && !!entity.tags[which] && !!entity.tags[which + ':wikidata'];
78682             });
78683
78684             field.locked(isLocked);
78685           }
78686
78687           function i(selection) {
78688             calcLocked();
78689             var isLocked = field.locked();
78690             var wrap = selection.selectAll('.form-field-input-wrap').data([0]);
78691             wrap = wrap.enter().append('div').attr('class', 'form-field-input-wrap form-field-input-' + field.type).merge(wrap);
78692             input = wrap.selectAll('input').data([0]);
78693             input = input.enter().append('input').attr('type', field.type === 'identifier' || field.type === 'roadheight' ? 'text' : field.type).attr('id', field.domId).classed(field.type, true).call(utilNoAuto).merge(input);
78694             input.classed('disabled', !!isLocked).attr('readonly', isLocked || null).on('input', change(true)).on('blur', change()).on('change', change());
78695
78696             if (field.type === 'tel') {
78697               updatePhonePlaceholder();
78698             } else if (field.type === 'number') {
78699               var rtl = _mainLocalizer.textDirection() === 'rtl';
78700               input.attr('type', 'text');
78701               var inc = field.increment;
78702               var buttons = wrap.selectAll('.increment, .decrement').data(rtl ? [inc, -inc] : [-inc, inc]);
78703               buttons.enter().append('button').attr('class', function (d) {
78704                 var which = d > 0 ? 'increment' : 'decrement';
78705                 return 'form-field-button ' + which;
78706               }).merge(buttons).on('click', function (d3_event, d) {
78707                 d3_event.preventDefault();
78708                 var raw_vals = input.node().value || '0';
78709                 var vals = raw_vals.split(';');
78710                 vals = vals.map(function (v) {
78711                   var num = parseFloat(v.trim(), 10);
78712                   return isFinite(num) ? clamped(num + d) : v.trim();
78713                 });
78714                 input.node().value = vals.join(';');
78715                 change()();
78716               });
78717             } else if (field.type === 'identifier' && field.urlFormat && field.pattern) {
78718               input.attr('type', 'text');
78719               outlinkButton = wrap.selectAll('.foreign-id-permalink').data([0]);
78720               outlinkButton.enter().append('button').call(svgIcon('#iD-icon-out-link')).attr('class', 'form-field-button foreign-id-permalink').attr('title', function () {
78721                 var domainResults = /^https?:\/\/(.{1,}?)\//.exec(field.urlFormat);
78722
78723                 if (domainResults.length >= 2 && domainResults[1]) {
78724                   var domain = domainResults[1];
78725                   return _t('icons.view_on', {
78726                     domain: domain
78727                   });
78728                 }
78729
78730                 return '';
78731               }).on('click', function (d3_event) {
78732                 d3_event.preventDefault();
78733                 var value = validIdentifierValueForLink();
78734
78735                 if (value) {
78736                   var url = field.urlFormat.replace(/{value}/, encodeURIComponent(value));
78737                   window.open(url, '_blank');
78738                 }
78739               }).merge(outlinkButton);
78740             }
78741           }
78742
78743           function updatePhonePlaceholder() {
78744             if (input.empty() || !Object.keys(_phoneFormats).length) return;
78745             var extent = combinedEntityExtent();
78746             var countryCode = extent && iso1A2Code(extent.center());
78747
78748             var format = countryCode && _phoneFormats[countryCode.toLowerCase()];
78749
78750             if (format) input.attr('placeholder', format);
78751           }
78752
78753           function validIdentifierValueForLink() {
78754             if (field.type === 'identifier' && field.pattern) {
78755               var value = utilGetSetValue(input).trim().split(';')[0];
78756               return value && value.match(new RegExp(field.pattern));
78757             }
78758
78759             return null;
78760           } // clamp number to min/max
78761
78762
78763           function clamped(num) {
78764             if (field.minValue !== undefined) {
78765               num = Math.max(num, field.minValue);
78766             }
78767
78768             if (field.maxValue !== undefined) {
78769               num = Math.min(num, field.maxValue);
78770             }
78771
78772             return num;
78773           }
78774
78775           function change(onInput) {
78776             return function () {
78777               var t = {};
78778               var val = utilGetSetValue(input);
78779               if (!onInput) val = context.cleanTagValue(val); // don't override multiple values with blank string
78780
78781               if (!val && Array.isArray(_tags[field.key])) return;
78782
78783               if (!onInput) {
78784                 if (field.type === 'number' && val) {
78785                   var vals = val.split(';');
78786                   vals = vals.map(function (v) {
78787                     var num = parseFloat(v.trim(), 10);
78788                     return isFinite(num) ? clamped(num) : v.trim();
78789                   });
78790                   val = vals.join(';');
78791                 }
78792
78793                 utilGetSetValue(input, val);
78794               }
78795
78796               t[field.key] = val || undefined;
78797               dispatch.call('change', this, t, onInput);
78798             };
78799           }
78800
78801           i.entityIDs = function (val) {
78802             if (!arguments.length) return _entityIDs;
78803             _entityIDs = val;
78804             return i;
78805           };
78806
78807           i.tags = function (tags) {
78808             _tags = tags;
78809             var isMixed = Array.isArray(tags[field.key]);
78810             utilGetSetValue(input, !isMixed && tags[field.key] ? tags[field.key] : '').attr('title', isMixed ? tags[field.key].filter(Boolean).join('\n') : undefined).attr('placeholder', isMixed ? _t('inspector.multiple_values') : field.placeholder() || _t('inspector.unknown')).classed('mixed', isMixed);
78811
78812             if (outlinkButton && !outlinkButton.empty()) {
78813               var disabled = !validIdentifierValueForLink();
78814               outlinkButton.classed('disabled', disabled);
78815             }
78816           };
78817
78818           i.focus = function () {
78819             var node = input.node();
78820             if (node) node.focus();
78821           };
78822
78823           function combinedEntityExtent() {
78824             return _entityIDs && _entityIDs.length && utilTotalExtent(_entityIDs, context.graph());
78825           }
78826
78827           return utilRebind(i, dispatch, 'on');
78828         }
78829
78830         function uiFieldAccess(field, context) {
78831           var dispatch = dispatch$8('change');
78832           var items = select(null);
78833
78834           var _tags;
78835
78836           function access(selection) {
78837             var wrap = selection.selectAll('.form-field-input-wrap').data([0]);
78838             wrap = wrap.enter().append('div').attr('class', 'form-field-input-wrap form-field-input-' + field.type).merge(wrap);
78839             var list = wrap.selectAll('ul').data([0]);
78840             list = list.enter().append('ul').attr('class', 'rows').merge(list);
78841             items = list.selectAll('li').data(field.keys); // Enter
78842
78843             var enter = items.enter().append('li').attr('class', function (d) {
78844               return 'labeled-input preset-access-' + d;
78845             });
78846             enter.append('span').attr('class', 'label preset-label-access').attr('for', function (d) {
78847               return 'preset-input-access-' + d;
78848             }).html(function (d) {
78849               return field.t.html('types.' + d);
78850             });
78851             enter.append('div').attr('class', 'preset-input-access-wrap').append('input').attr('type', 'text').attr('class', function (d) {
78852               return 'preset-input-access preset-input-access-' + d;
78853             }).call(utilNoAuto).each(function (d) {
78854               select(this).call(uiCombobox(context, 'access-' + d).data(access.options(d)));
78855             }); // Update
78856
78857             items = items.merge(enter);
78858             wrap.selectAll('.preset-input-access').on('change', change).on('blur', change);
78859           }
78860
78861           function change(d3_event, d) {
78862             var tag = {};
78863             var value = context.cleanTagValue(utilGetSetValue(select(this))); // don't override multiple values with blank string
78864
78865             if (!value && typeof _tags[d] !== 'string') return;
78866             tag[d] = value || undefined;
78867             dispatch.call('change', this, tag);
78868           }
78869
78870           access.options = function (type) {
78871             var options = ['no', 'permissive', 'private', 'permit', 'destination'];
78872
78873             if (type !== 'access') {
78874               options.unshift('yes');
78875               options.push('designated');
78876
78877               if (type === 'bicycle') {
78878                 options.push('dismount');
78879               }
78880             }
78881
78882             return options.map(function (option) {
78883               return {
78884                 title: field.t('options.' + option + '.description'),
78885                 value: option
78886               };
78887             });
78888           };
78889
78890           var placeholdersByHighway = {
78891             footway: {
78892               foot: 'designated',
78893               motor_vehicle: 'no'
78894             },
78895             steps: {
78896               foot: 'yes',
78897               motor_vehicle: 'no',
78898               bicycle: 'no',
78899               horse: 'no'
78900             },
78901             pedestrian: {
78902               foot: 'yes',
78903               motor_vehicle: 'no'
78904             },
78905             cycleway: {
78906               motor_vehicle: 'no',
78907               bicycle: 'designated'
78908             },
78909             bridleway: {
78910               motor_vehicle: 'no',
78911               horse: 'designated'
78912             },
78913             path: {
78914               foot: 'yes',
78915               motor_vehicle: 'no',
78916               bicycle: 'yes',
78917               horse: 'yes'
78918             },
78919             motorway: {
78920               foot: 'no',
78921               motor_vehicle: 'yes',
78922               bicycle: 'no',
78923               horse: 'no'
78924             },
78925             trunk: {
78926               motor_vehicle: 'yes'
78927             },
78928             primary: {
78929               foot: 'yes',
78930               motor_vehicle: 'yes',
78931               bicycle: 'yes',
78932               horse: 'yes'
78933             },
78934             secondary: {
78935               foot: 'yes',
78936               motor_vehicle: 'yes',
78937               bicycle: 'yes',
78938               horse: 'yes'
78939             },
78940             tertiary: {
78941               foot: 'yes',
78942               motor_vehicle: 'yes',
78943               bicycle: 'yes',
78944               horse: 'yes'
78945             },
78946             residential: {
78947               foot: 'yes',
78948               motor_vehicle: 'yes',
78949               bicycle: 'yes',
78950               horse: 'yes'
78951             },
78952             unclassified: {
78953               foot: 'yes',
78954               motor_vehicle: 'yes',
78955               bicycle: 'yes',
78956               horse: 'yes'
78957             },
78958             service: {
78959               foot: 'yes',
78960               motor_vehicle: 'yes',
78961               bicycle: 'yes',
78962               horse: 'yes'
78963             },
78964             motorway_link: {
78965               foot: 'no',
78966               motor_vehicle: 'yes',
78967               bicycle: 'no',
78968               horse: 'no'
78969             },
78970             trunk_link: {
78971               motor_vehicle: 'yes'
78972             },
78973             primary_link: {
78974               foot: 'yes',
78975               motor_vehicle: 'yes',
78976               bicycle: 'yes',
78977               horse: 'yes'
78978             },
78979             secondary_link: {
78980               foot: 'yes',
78981               motor_vehicle: 'yes',
78982               bicycle: 'yes',
78983               horse: 'yes'
78984             },
78985             tertiary_link: {
78986               foot: 'yes',
78987               motor_vehicle: 'yes',
78988               bicycle: 'yes',
78989               horse: 'yes'
78990             }
78991           };
78992
78993           access.tags = function (tags) {
78994             _tags = tags;
78995             utilGetSetValue(items.selectAll('.preset-input-access'), function (d) {
78996               return typeof tags[d] === 'string' ? tags[d] : '';
78997             }).classed('mixed', function (d) {
78998               return tags[d] && Array.isArray(tags[d]);
78999             }).attr('title', function (d) {
79000               return tags[d] && Array.isArray(tags[d]) && tags[d].filter(Boolean).join('\n');
79001             }).attr('placeholder', function (d) {
79002               if (tags[d] && Array.isArray(tags[d])) {
79003                 return _t('inspector.multiple_values');
79004               }
79005
79006               if (d === 'access') {
79007                 return 'yes';
79008               }
79009
79010               if (tags.access && typeof tags.access === 'string') {
79011                 return tags.access;
79012               }
79013
79014               if (tags.highway) {
79015                 if (typeof tags.highway === 'string') {
79016                   if (placeholdersByHighway[tags.highway] && placeholdersByHighway[tags.highway][d]) {
79017                     return placeholdersByHighway[tags.highway][d];
79018                   }
79019                 } else {
79020                   var impliedAccesses = tags.highway.filter(Boolean).map(function (highwayVal) {
79021                     return placeholdersByHighway[highwayVal] && placeholdersByHighway[highwayVal][d];
79022                   }).filter(Boolean);
79023
79024                   if (impliedAccesses.length === tags.highway.length && new Set(impliedAccesses).size === 1) {
79025                     // if all the highway values have the same implied access for this type then use that
79026                     return impliedAccesses[0];
79027                   }
79028                 }
79029               }
79030
79031               return field.placeholder();
79032             });
79033           };
79034
79035           access.focus = function () {
79036             items.selectAll('.preset-input-access').node().focus();
79037           };
79038
79039           return utilRebind(access, dispatch, 'on');
79040         }
79041
79042         function uiFieldAddress(field, context) {
79043           var dispatch = dispatch$8('change');
79044
79045           var _selection = select(null);
79046
79047           var _wrap = select(null);
79048
79049           var addrField = _mainPresetIndex.field('address'); // needed for placeholder strings
79050
79051           var _entityIDs = [];
79052
79053           var _tags;
79054
79055           var _countryCode;
79056
79057           var _addressFormats = [{
79058             format: [['housenumber', 'street'], ['city', 'postcode']]
79059           }];
79060           _mainFileFetcher.get('address_formats').then(function (d) {
79061             _addressFormats = d;
79062
79063             if (!_selection.empty()) {
79064               _selection.call(address);
79065             }
79066           })["catch"](function () {
79067             /* ignore */
79068           });
79069
79070           function getNearStreets() {
79071             var extent = combinedEntityExtent();
79072             var l = extent.center();
79073             var box = geoExtent(l).padByMeters(200);
79074             var streets = context.history().intersects(box).filter(isAddressable).map(function (d) {
79075               var loc = context.projection([(extent[0][0] + extent[1][0]) / 2, (extent[0][1] + extent[1][1]) / 2]);
79076               var choice = geoChooseEdge(context.graph().childNodes(d), loc, context.projection);
79077               return {
79078                 title: d.tags.name,
79079                 value: d.tags.name,
79080                 dist: choice.distance
79081               };
79082             }).sort(function (a, b) {
79083               return a.dist - b.dist;
79084             });
79085             return utilArrayUniqBy(streets, 'value');
79086
79087             function isAddressable(d) {
79088               return d.tags.highway && d.tags.name && d.type === 'way';
79089             }
79090           }
79091
79092           function getNearCities() {
79093             var extent = combinedEntityExtent();
79094             var l = extent.center();
79095             var box = geoExtent(l).padByMeters(200);
79096             var cities = context.history().intersects(box).filter(isAddressable).map(function (d) {
79097               return {
79098                 title: d.tags['addr:city'] || d.tags.name,
79099                 value: d.tags['addr:city'] || d.tags.name,
79100                 dist: geoSphericalDistance(d.extent(context.graph()).center(), l)
79101               };
79102             }).sort(function (a, b) {
79103               return a.dist - b.dist;
79104             });
79105             return utilArrayUniqBy(cities, 'value');
79106
79107             function isAddressable(d) {
79108               if (d.tags.name) {
79109                 if (d.tags.admin_level === '8' && d.tags.boundary === 'administrative') return true;
79110                 if (d.tags.border_type === 'city') return true;
79111                 if (d.tags.place === 'city' || d.tags.place === 'town' || d.tags.place === 'village') return true;
79112               }
79113
79114               if (d.tags['addr:city']) return true;
79115               return false;
79116             }
79117           }
79118
79119           function getNearValues(key) {
79120             var extent = combinedEntityExtent();
79121             var l = extent.center();
79122             var box = geoExtent(l).padByMeters(200);
79123             var results = context.history().intersects(box).filter(function hasTag(d) {
79124               return _entityIDs.indexOf(d.id) === -1 && d.tags[key];
79125             }).map(function (d) {
79126               return {
79127                 title: d.tags[key],
79128                 value: d.tags[key],
79129                 dist: geoSphericalDistance(d.extent(context.graph()).center(), l)
79130               };
79131             }).sort(function (a, b) {
79132               return a.dist - b.dist;
79133             });
79134             return utilArrayUniqBy(results, 'value');
79135           }
79136
79137           function updateForCountryCode() {
79138             if (!_countryCode) return;
79139             var addressFormat;
79140
79141             for (var i = 0; i < _addressFormats.length; i++) {
79142               var format = _addressFormats[i];
79143
79144               if (!format.countryCodes) {
79145                 addressFormat = format; // choose the default format, keep going
79146               } else if (format.countryCodes.indexOf(_countryCode) !== -1) {
79147                 addressFormat = format; // choose the country format, stop here
79148
79149                 break;
79150               }
79151             }
79152
79153             var dropdowns = addressFormat.dropdowns || ['city', 'county', 'country', 'district', 'hamlet', 'neighbourhood', 'place', 'postcode', 'province', 'quarter', 'state', 'street', 'subdistrict', 'suburb'];
79154             var widths = addressFormat.widths || {
79155               housenumber: 1 / 3,
79156               street: 2 / 3,
79157               city: 2 / 3,
79158               state: 1 / 4,
79159               postcode: 1 / 3
79160             };
79161
79162             function row(r) {
79163               // Normalize widths.
79164               var total = r.reduce(function (sum, key) {
79165                 return sum + (widths[key] || 0.5);
79166               }, 0);
79167               return r.map(function (key) {
79168                 return {
79169                   id: key,
79170                   width: (widths[key] || 0.5) / total
79171                 };
79172               });
79173             }
79174
79175             var rows = _wrap.selectAll('.addr-row').data(addressFormat.format, function (d) {
79176               return d.toString();
79177             });
79178
79179             rows.exit().remove();
79180             rows.enter().append('div').attr('class', 'addr-row').selectAll('input').data(row).enter().append('input').property('type', 'text').call(updatePlaceholder).attr('class', function (d) {
79181               return 'addr-' + d.id;
79182             }).call(utilNoAuto).each(addDropdown).style('width', function (d) {
79183               return d.width * 100 + '%';
79184             });
79185
79186             function addDropdown(d) {
79187               if (dropdowns.indexOf(d.id) === -1) return; // not a dropdown
79188
79189               var nearValues = d.id === 'street' ? getNearStreets : d.id === 'city' ? getNearCities : getNearValues;
79190               select(this).call(uiCombobox(context, 'address-' + d.id).minItems(1).caseSensitive(true).fetcher(function (value, callback) {
79191                 callback(nearValues('addr:' + d.id));
79192               }));
79193             }
79194
79195             _wrap.selectAll('input').on('blur', change()).on('change', change());
79196
79197             _wrap.selectAll('input:not(.combobox-input)').on('input', change(true));
79198
79199             if (_tags) updateTags(_tags);
79200           }
79201
79202           function address(selection) {
79203             _selection = selection;
79204             _wrap = selection.selectAll('.form-field-input-wrap').data([0]);
79205             _wrap = _wrap.enter().append('div').attr('class', 'form-field-input-wrap form-field-input-' + field.type).merge(_wrap);
79206             var extent = combinedEntityExtent();
79207
79208             if (extent) {
79209               var countryCode;
79210
79211               if (context.inIntro()) {
79212                 // localize the address format for the walkthrough
79213                 countryCode = _t('intro.graph.countrycode');
79214               } else {
79215                 var center = extent.center();
79216                 countryCode = iso1A2Code(center);
79217               }
79218
79219               if (countryCode) {
79220                 _countryCode = countryCode.toLowerCase();
79221                 updateForCountryCode();
79222               }
79223             }
79224           }
79225
79226           function change(onInput) {
79227             return function () {
79228               var tags = {};
79229
79230               _wrap.selectAll('input').each(function (subfield) {
79231                 var key = field.key + ':' + subfield.id;
79232                 var value = this.value;
79233                 if (!onInput) value = context.cleanTagValue(value); // don't override multiple values with blank string
79234
79235                 if (Array.isArray(_tags[key]) && !value) return;
79236                 tags[key] = value || undefined;
79237               });
79238
79239               dispatch.call('change', this, tags, onInput);
79240             };
79241           }
79242
79243           function updatePlaceholder(inputSelection) {
79244             return inputSelection.attr('placeholder', function (subfield) {
79245               if (_tags && Array.isArray(_tags[field.key + ':' + subfield.id])) {
79246                 return _t('inspector.multiple_values');
79247               }
79248
79249               if (_countryCode) {
79250                 var localkey = subfield.id + '!' + _countryCode;
79251                 var tkey = addrField.hasTextForStringId('placeholders.' + localkey) ? localkey : subfield.id;
79252                 return addrField.t('placeholders.' + tkey);
79253               }
79254             });
79255           }
79256
79257           function updateTags(tags) {
79258             utilGetSetValue(_wrap.selectAll('input'), function (subfield) {
79259               var val = tags[field.key + ':' + subfield.id];
79260               return typeof val === 'string' ? val : '';
79261             }).attr('title', function (subfield) {
79262               var val = tags[field.key + ':' + subfield.id];
79263               return val && Array.isArray(val) && val.filter(Boolean).join('\n');
79264             }).classed('mixed', function (subfield) {
79265               return Array.isArray(tags[field.key + ':' + subfield.id]);
79266             }).call(updatePlaceholder);
79267           }
79268
79269           function combinedEntityExtent() {
79270             return _entityIDs && _entityIDs.length && utilTotalExtent(_entityIDs, context.graph());
79271           }
79272
79273           address.entityIDs = function (val) {
79274             if (!arguments.length) return _entityIDs;
79275             _entityIDs = val;
79276             return address;
79277           };
79278
79279           address.tags = function (tags) {
79280             _tags = tags;
79281             updateTags(tags);
79282           };
79283
79284           address.focus = function () {
79285             var node = _wrap.selectAll('input').node();
79286
79287             if (node) node.focus();
79288           };
79289
79290           return utilRebind(address, dispatch, 'on');
79291         }
79292
79293         function uiFieldCycleway(field, context) {
79294           var dispatch = dispatch$8('change');
79295           var items = select(null);
79296           var wrap = select(null);
79297
79298           var _tags;
79299
79300           function cycleway(selection) {
79301             function stripcolon(s) {
79302               return s.replace(':', '');
79303             }
79304
79305             wrap = selection.selectAll('.form-field-input-wrap').data([0]);
79306             wrap = wrap.enter().append('div').attr('class', 'form-field-input-wrap form-field-input-' + field.type).merge(wrap);
79307             var div = wrap.selectAll('ul').data([0]);
79308             div = div.enter().append('ul').attr('class', 'rows').merge(div);
79309             var keys = ['cycleway:left', 'cycleway:right'];
79310             items = div.selectAll('li').data(keys);
79311             var enter = items.enter().append('li').attr('class', function (d) {
79312               return 'labeled-input preset-cycleway-' + stripcolon(d);
79313             });
79314             enter.append('span').attr('class', 'label preset-label-cycleway').attr('for', function (d) {
79315               return 'preset-input-cycleway-' + stripcolon(d);
79316             }).html(function (d) {
79317               return field.t.html('types.' + d);
79318             });
79319             enter.append('div').attr('class', 'preset-input-cycleway-wrap').append('input').attr('type', 'text').attr('class', function (d) {
79320               return 'preset-input-cycleway preset-input-' + stripcolon(d);
79321             }).call(utilNoAuto).each(function (d) {
79322               select(this).call(uiCombobox(context, 'cycleway-' + stripcolon(d)).data(cycleway.options(d)));
79323             });
79324             items = items.merge(enter); // Update
79325
79326             wrap.selectAll('.preset-input-cycleway').on('change', change).on('blur', change);
79327           }
79328
79329           function change(d3_event, key) {
79330             var newValue = context.cleanTagValue(utilGetSetValue(select(this))); // don't override multiple values with blank string
79331
79332             if (!newValue && (Array.isArray(_tags.cycleway) || Array.isArray(_tags[key]))) return;
79333
79334             if (newValue === 'none' || newValue === '') {
79335               newValue = undefined;
79336             }
79337
79338             var otherKey = key === 'cycleway:left' ? 'cycleway:right' : 'cycleway:left';
79339             var otherValue = typeof _tags.cycleway === 'string' ? _tags.cycleway : _tags[otherKey];
79340
79341             if (otherValue && Array.isArray(otherValue)) {
79342               // we must always have an explicit value for comparison
79343               otherValue = otherValue[0];
79344             }
79345
79346             if (otherValue === 'none' || otherValue === '') {
79347               otherValue = undefined;
79348             }
79349
79350             var tag = {}; // If the left and right tags match, use the cycleway tag to tag both
79351             // sides the same way
79352
79353             if (newValue === otherValue) {
79354               tag = {
79355                 cycleway: newValue,
79356                 'cycleway:left': undefined,
79357                 'cycleway:right': undefined
79358               };
79359             } else {
79360               // Always set both left and right as changing one can affect the other
79361               tag = {
79362                 cycleway: undefined
79363               };
79364               tag[key] = newValue;
79365               tag[otherKey] = otherValue;
79366             }
79367
79368             dispatch.call('change', this, tag);
79369           }
79370
79371           cycleway.options = function () {
79372             return field.options.map(function (option) {
79373               return {
79374                 title: field.t('options.' + option + '.description'),
79375                 value: option
79376               };
79377             });
79378           };
79379
79380           cycleway.tags = function (tags) {
79381             _tags = tags; // If cycleway is set, use that instead of individual values
79382
79383             var commonValue = typeof tags.cycleway === 'string' && tags.cycleway;
79384             utilGetSetValue(items.selectAll('.preset-input-cycleway'), function (d) {
79385               if (commonValue) return commonValue;
79386               return !tags.cycleway && typeof tags[d] === 'string' ? tags[d] : '';
79387             }).attr('title', function (d) {
79388               if (Array.isArray(tags.cycleway) || Array.isArray(tags[d])) {
79389                 var vals = [];
79390
79391                 if (Array.isArray(tags.cycleway)) {
79392                   vals = vals.concat(tags.cycleway);
79393                 }
79394
79395                 if (Array.isArray(tags[d])) {
79396                   vals = vals.concat(tags[d]);
79397                 }
79398
79399                 return vals.filter(Boolean).join('\n');
79400               }
79401
79402               return null;
79403             }).attr('placeholder', function (d) {
79404               if (Array.isArray(tags.cycleway) || Array.isArray(tags[d])) {
79405                 return _t('inspector.multiple_values');
79406               }
79407
79408               return field.placeholder();
79409             }).classed('mixed', function (d) {
79410               return Array.isArray(tags.cycleway) || Array.isArray(tags[d]);
79411             });
79412           };
79413
79414           cycleway.focus = function () {
79415             var node = wrap.selectAll('input').node();
79416             if (node) node.focus();
79417           };
79418
79419           return utilRebind(cycleway, dispatch, 'on');
79420         }
79421
79422         function uiFieldLanes(field, context) {
79423           var dispatch = dispatch$8('change');
79424           var LANE_WIDTH = 40;
79425           var LANE_HEIGHT = 200;
79426           var _entityIDs = [];
79427
79428           function lanes(selection) {
79429             var lanesData = context.entity(_entityIDs[0]).lanes();
79430
79431             if (!context.container().select('.inspector-wrap.inspector-hidden').empty() || !selection.node().parentNode) {
79432               selection.call(lanes.off);
79433               return;
79434             }
79435
79436             var wrap = selection.selectAll('.form-field-input-wrap').data([0]);
79437             wrap = wrap.enter().append('div').attr('class', 'form-field-input-wrap form-field-input-' + field.type).merge(wrap);
79438             var surface = wrap.selectAll('.surface').data([0]);
79439             var d = utilGetDimensions(wrap);
79440             var freeSpace = d[0] - lanesData.lanes.length * LANE_WIDTH * 1.5 + LANE_WIDTH * 0.5;
79441             surface = surface.enter().append('svg').attr('width', d[0]).attr('height', 300).attr('class', 'surface').merge(surface);
79442             var lanesSelection = surface.selectAll('.lanes').data([0]);
79443             lanesSelection = lanesSelection.enter().append('g').attr('class', 'lanes').merge(lanesSelection);
79444             lanesSelection.attr('transform', function () {
79445               return 'translate(' + freeSpace / 2 + ', 0)';
79446             });
79447             var lane = lanesSelection.selectAll('.lane').data(lanesData.lanes);
79448             lane.exit().remove();
79449             var enter = lane.enter().append('g').attr('class', 'lane');
79450             enter.append('g').append('rect').attr('y', 50).attr('width', LANE_WIDTH).attr('height', LANE_HEIGHT);
79451             enter.append('g').attr('class', 'forward').append('text').attr('y', 40).attr('x', 14).html('▲');
79452             enter.append('g').attr('class', 'bothways').append('text').attr('y', 40).attr('x', 14).html('▲▼');
79453             enter.append('g').attr('class', 'backward').append('text').attr('y', 40).attr('x', 14).html('▼');
79454             lane = lane.merge(enter);
79455             lane.attr('transform', function (d) {
79456               return 'translate(' + LANE_WIDTH * d.index * 1.5 + ', 0)';
79457             });
79458             lane.select('.forward').style('visibility', function (d) {
79459               return d.direction === 'forward' ? 'visible' : 'hidden';
79460             });
79461             lane.select('.bothways').style('visibility', function (d) {
79462               return d.direction === 'bothways' ? 'visible' : 'hidden';
79463             });
79464             lane.select('.backward').style('visibility', function (d) {
79465               return d.direction === 'backward' ? 'visible' : 'hidden';
79466             });
79467           }
79468
79469           lanes.entityIDs = function (val) {
79470             _entityIDs = val;
79471           };
79472
79473           lanes.tags = function () {};
79474
79475           lanes.focus = function () {};
79476
79477           lanes.off = function () {};
79478
79479           return utilRebind(lanes, dispatch, 'on');
79480         }
79481         uiFieldLanes.supportsMultiselection = false;
79482
79483         var _languagesArray = [];
79484         function uiFieldLocalized(field, context) {
79485           var dispatch = dispatch$8('change', 'input');
79486           var wikipedia = services.wikipedia;
79487           var input = select(null);
79488           var localizedInputs = select(null);
79489
79490           var _countryCode;
79491
79492           var _tags; // A concern here in switching to async data means that _languagesArray will not
79493           // be available the first time through, so things like the fetchers and
79494           // the language() function will not work immediately.
79495
79496
79497           _mainFileFetcher.get('languages').then(loadLanguagesArray)["catch"](function () {
79498             /* ignore */
79499           });
79500           var _territoryLanguages = {};
79501           _mainFileFetcher.get('territory_languages').then(function (d) {
79502             _territoryLanguages = d;
79503           })["catch"](function () {
79504             /* ignore */
79505           }); // reuse these combos
79506
79507           var langCombo = uiCombobox(context, 'localized-lang').fetcher(fetchLanguages).minItems(0);
79508
79509           var _selection = select(null);
79510
79511           var _multilingual = [];
79512
79513           var _buttonTip = uiTooltip().title(_t.html('translate.translate')).placement('left');
79514
79515           var _wikiTitles;
79516
79517           var _entityIDs = [];
79518
79519           function loadLanguagesArray(dataLanguages) {
79520             if (_languagesArray.length !== 0) return; // some conversion is needed to ensure correct OSM tags are used
79521
79522             var replacements = {
79523               sr: 'sr-Cyrl',
79524               // in OSM, `sr` implies Cyrillic
79525               'sr-Cyrl': false // `sr-Cyrl` isn't used in OSM
79526
79527             };
79528
79529             for (var code in dataLanguages) {
79530               if (replacements[code] === false) continue;
79531               var metaCode = code;
79532               if (replacements[code]) metaCode = replacements[code];
79533
79534               _languagesArray.push({
79535                 localName: _mainLocalizer.languageName(metaCode, {
79536                   localOnly: true
79537                 }),
79538                 nativeName: dataLanguages[metaCode].nativeName,
79539                 code: code,
79540                 label: _mainLocalizer.languageName(metaCode)
79541               });
79542             }
79543           }
79544
79545           function calcLocked() {
79546             // Protect name field for suggestion presets that don't display a brand/operator field
79547             var isLocked = field.id === 'name' && _entityIDs.length && _entityIDs.some(function (entityID) {
79548               var entity = context.graph().hasEntity(entityID);
79549               if (!entity) return false; // Features linked to Wikidata are likely important and should be protected
79550
79551               if (entity.tags.wikidata) return true; // Assume the name has already been confirmed if its source has been researched
79552
79553               if (entity.tags['name:etymology:wikidata']) return true; // Lock the `name` if this is a suggestion preset that assigns the name,
79554               // and the preset does not display a `brand` or `operator` field.
79555               // (For presets like hotels, car dealerships, post offices, the `name` should remain editable)
79556               // see also similar logic in `outdated_tags.js`
79557
79558               var preset = _mainPresetIndex.match(entity, context.graph());
79559
79560               if (preset) {
79561                 var isSuggestion = preset.suggestion;
79562                 var fields = preset.fields();
79563                 var showsBrandField = fields.some(function (d) {
79564                   return d.id === 'brand';
79565                 });
79566                 var showsOperatorField = fields.some(function (d) {
79567                   return d.id === 'operator';
79568                 });
79569                 var setsName = preset.addTags.name;
79570                 var setsBrandWikidata = preset.addTags['brand:wikidata'];
79571                 var setsOperatorWikidata = preset.addTags['operator:wikidata'];
79572                 return isSuggestion && setsName && (setsBrandWikidata && !showsBrandField || setsOperatorWikidata && !showsOperatorField);
79573               }
79574
79575               return false;
79576             });
79577
79578             field.locked(isLocked);
79579           } // update _multilingual, maintaining the existing order
79580
79581
79582           function calcMultilingual(tags) {
79583             var existingLangsOrdered = _multilingual.map(function (item) {
79584               return item.lang;
79585             });
79586
79587             var existingLangs = new Set(existingLangsOrdered.filter(Boolean));
79588
79589             for (var k in tags) {
79590               var m = k.match(/^(.*):(.*)$/);
79591
79592               if (m && m[1] === field.key && m[2]) {
79593                 var item = {
79594                   lang: m[2],
79595                   value: tags[k]
79596                 };
79597
79598                 if (existingLangs.has(item.lang)) {
79599                   // update the value
79600                   _multilingual[existingLangsOrdered.indexOf(item.lang)].value = item.value;
79601                   existingLangs["delete"](item.lang);
79602                 } else {
79603                   _multilingual.push(item);
79604                 }
79605               }
79606             } // Don't remove items based on deleted tags, since this makes the UI
79607             // disappear unexpectedly when clearing values - #8164
79608
79609
79610             _multilingual.forEach(function (item) {
79611               if (item.lang && existingLangs.has(item.lang)) {
79612                 item.value = '';
79613               }
79614             });
79615           }
79616
79617           function localized(selection) {
79618             _selection = selection;
79619             calcLocked();
79620             var isLocked = field.locked();
79621             var wrap = selection.selectAll('.form-field-input-wrap').data([0]); // enter/update
79622
79623             wrap = wrap.enter().append('div').attr('class', 'form-field-input-wrap form-field-input-' + field.type).merge(wrap);
79624             input = wrap.selectAll('.localized-main').data([0]); // enter/update
79625
79626             input = input.enter().append('input').attr('type', 'text').attr('id', field.domId).attr('class', 'localized-main').call(utilNoAuto).merge(input);
79627             input.classed('disabled', !!isLocked).attr('readonly', isLocked || null).on('input', change(true)).on('blur', change()).on('change', change());
79628             var translateButton = wrap.selectAll('.localized-add').data([0]);
79629             translateButton = translateButton.enter().append('button').attr('class', 'localized-add form-field-button').call(svgIcon('#iD-icon-plus')).merge(translateButton);
79630             translateButton.classed('disabled', !!isLocked).call(isLocked ? _buttonTip.destroy : _buttonTip).on('click', addNew);
79631
79632             if (_tags && !_multilingual.length) {
79633               calcMultilingual(_tags);
79634             }
79635
79636             localizedInputs = selection.selectAll('.localized-multilingual').data([0]);
79637             localizedInputs = localizedInputs.enter().append('div').attr('class', 'localized-multilingual').merge(localizedInputs);
79638             localizedInputs.call(renderMultilingual);
79639             localizedInputs.selectAll('button, input').classed('disabled', !!isLocked).attr('readonly', isLocked || null);
79640
79641             function addNew(d3_event) {
79642               d3_event.preventDefault();
79643               if (field.locked()) return;
79644               var defaultLang = _mainLocalizer.languageCode().toLowerCase();
79645
79646               var langExists = _multilingual.find(function (datum) {
79647                 return datum.lang === defaultLang;
79648               });
79649
79650               var isLangEn = defaultLang.indexOf('en') > -1;
79651
79652               if (isLangEn || langExists) {
79653                 defaultLang = '';
79654                 langExists = _multilingual.find(function (datum) {
79655                   return datum.lang === defaultLang;
79656                 });
79657               }
79658
79659               if (!langExists) {
79660                 // prepend the value so it appears at the top
79661                 _multilingual.unshift({
79662                   lang: defaultLang,
79663                   value: ''
79664                 });
79665
79666                 localizedInputs.call(renderMultilingual);
79667               }
79668             }
79669
79670             function change(onInput) {
79671               return function (d3_event) {
79672                 if (field.locked()) {
79673                   d3_event.preventDefault();
79674                   return;
79675                 }
79676
79677                 var val = utilGetSetValue(select(this));
79678                 if (!onInput) val = context.cleanTagValue(val); // don't override multiple values with blank string
79679
79680                 if (!val && Array.isArray(_tags[field.key])) return;
79681                 var t = {};
79682                 t[field.key] = val || undefined;
79683                 dispatch.call('change', this, t, onInput);
79684               };
79685             }
79686           }
79687
79688           function key(lang) {
79689             return field.key + ':' + lang;
79690           }
79691
79692           function changeLang(d3_event, d) {
79693             var tags = {}; // make sure unrecognized suffixes are lowercase - #7156
79694
79695             var lang = utilGetSetValue(select(this)).toLowerCase();
79696
79697             var language = _languagesArray.find(function (d) {
79698               return d.label.toLowerCase() === lang || d.localName && d.localName.toLowerCase() === lang || d.nativeName && d.nativeName.toLowerCase() === lang;
79699             });
79700
79701             if (language) lang = language.code;
79702
79703             if (d.lang && d.lang !== lang) {
79704               tags[key(d.lang)] = undefined;
79705             }
79706
79707             var newKey = lang && context.cleanTagKey(key(lang));
79708             var value = utilGetSetValue(select(this.parentNode).selectAll('.localized-value'));
79709
79710             if (newKey && value) {
79711               tags[newKey] = value;
79712             } else if (newKey && _wikiTitles && _wikiTitles[d.lang]) {
79713               tags[newKey] = _wikiTitles[d.lang];
79714             }
79715
79716             d.lang = lang;
79717             dispatch.call('change', this, tags);
79718           }
79719
79720           function changeValue(d3_event, d) {
79721             if (!d.lang) return;
79722             var value = context.cleanTagValue(utilGetSetValue(select(this))) || undefined; // don't override multiple values with blank string
79723
79724             if (!value && Array.isArray(d.value)) return;
79725             var t = {};
79726             t[key(d.lang)] = value;
79727             d.value = value;
79728             dispatch.call('change', this, t);
79729           }
79730
79731           function fetchLanguages(value, cb) {
79732             var v = value.toLowerCase(); // show the user's language first
79733
79734             var langCodes = [_mainLocalizer.localeCode(), _mainLocalizer.languageCode()];
79735
79736             if (_countryCode && _territoryLanguages[_countryCode]) {
79737               langCodes = langCodes.concat(_territoryLanguages[_countryCode]);
79738             }
79739
79740             var langItems = [];
79741             langCodes.forEach(function (code) {
79742               var langItem = _languagesArray.find(function (item) {
79743                 return item.code === code;
79744               });
79745
79746               if (langItem) langItems.push(langItem);
79747             });
79748             langItems = utilArrayUniq(langItems.concat(_languagesArray));
79749             cb(langItems.filter(function (d) {
79750               return d.label.toLowerCase().indexOf(v) >= 0 || d.localName && d.localName.toLowerCase().indexOf(v) >= 0 || d.nativeName && d.nativeName.toLowerCase().indexOf(v) >= 0 || d.code.toLowerCase().indexOf(v) >= 0;
79751             }).map(function (d) {
79752               return {
79753                 value: d.label
79754               };
79755             }));
79756           }
79757
79758           function renderMultilingual(selection) {
79759             var entries = selection.selectAll('div.entry').data(_multilingual, function (d) {
79760               return d.lang;
79761             });
79762             entries.exit().style('top', '0').style('max-height', '240px').transition().duration(200).style('opacity', '0').style('max-height', '0px').remove();
79763             var entriesEnter = entries.enter().append('div').attr('class', 'entry').each(function (_, index) {
79764               var wrap = select(this);
79765               var domId = utilUniqueDomId(index);
79766               var label = wrap.append('label').attr('class', 'field-label').attr('for', domId);
79767               var text = label.append('span').attr('class', 'label-text');
79768               text.append('span').attr('class', 'label-textvalue').html(_t.html('translate.localized_translation_label'));
79769               text.append('span').attr('class', 'label-textannotation');
79770               label.append('button').attr('class', 'remove-icon-multilingual').on('click', function (d3_event, d) {
79771                 if (field.locked()) return;
79772                 d3_event.preventDefault(); // remove the UI item manually
79773
79774                 _multilingual.splice(_multilingual.indexOf(d), 1);
79775
79776                 var langKey = d.lang && key(d.lang);
79777
79778                 if (langKey && langKey in _tags) {
79779                   delete _tags[langKey]; // remove from entity tags
79780
79781                   var t = {};
79782                   t[langKey] = undefined;
79783                   dispatch.call('change', this, t);
79784                   return;
79785                 }
79786
79787                 renderMultilingual(selection);
79788               }).call(svgIcon('#iD-operation-delete'));
79789               wrap.append('input').attr('class', 'localized-lang').attr('id', domId).attr('type', 'text').attr('placeholder', _t('translate.localized_translation_language')).on('blur', changeLang).on('change', changeLang).call(langCombo);
79790               wrap.append('input').attr('type', 'text').attr('class', 'localized-value').on('blur', changeValue).on('change', changeValue);
79791             });
79792             entriesEnter.style('margin-top', '0px').style('max-height', '0px').style('opacity', '0').transition().duration(200).style('margin-top', '10px').style('max-height', '240px').style('opacity', '1').on('end', function () {
79793               select(this).style('max-height', '').style('overflow', 'visible');
79794             });
79795             entries = entries.merge(entriesEnter);
79796             entries.order(); // allow removing the entry UIs even if there isn't a tag to remove
79797
79798             entries.classed('present', true);
79799             utilGetSetValue(entries.select('.localized-lang'), function (d) {
79800               var langItem = _languagesArray.find(function (item) {
79801                 return item.code === d.lang;
79802               });
79803
79804               if (langItem) return langItem.label;
79805               return d.lang;
79806             });
79807             utilGetSetValue(entries.select('.localized-value'), function (d) {
79808               return typeof d.value === 'string' ? d.value : '';
79809             }).attr('title', function (d) {
79810               return Array.isArray(d.value) ? d.value.filter(Boolean).join('\n') : null;
79811             }).attr('placeholder', function (d) {
79812               return Array.isArray(d.value) ? _t('inspector.multiple_values') : _t('translate.localized_translation_name');
79813             }).classed('mixed', function (d) {
79814               return Array.isArray(d.value);
79815             });
79816           }
79817
79818           localized.tags = function (tags) {
79819             _tags = tags; // Fetch translations from wikipedia
79820
79821             if (typeof tags.wikipedia === 'string' && !_wikiTitles) {
79822               _wikiTitles = {};
79823               var wm = tags.wikipedia.match(/([^:]+):(.+)/);
79824
79825               if (wm && wm[0] && wm[1]) {
79826                 wikipedia.translations(wm[1], wm[2], function (err, d) {
79827                   if (err || !d) return;
79828                   _wikiTitles = d;
79829                 });
79830               }
79831             }
79832
79833             var isMixed = Array.isArray(tags[field.key]);
79834             utilGetSetValue(input, typeof tags[field.key] === 'string' ? tags[field.key] : '').attr('title', isMixed ? tags[field.key].filter(Boolean).join('\n') : undefined).attr('placeholder', isMixed ? _t('inspector.multiple_values') : field.placeholder()).classed('mixed', isMixed);
79835             calcMultilingual(tags);
79836
79837             _selection.call(localized);
79838           };
79839
79840           localized.focus = function () {
79841             input.node().focus();
79842           };
79843
79844           localized.entityIDs = function (val) {
79845             if (!arguments.length) return _entityIDs;
79846             _entityIDs = val;
79847             _multilingual = [];
79848             loadCountryCode();
79849             return localized;
79850           };
79851
79852           function loadCountryCode() {
79853             var extent = combinedEntityExtent();
79854             var countryCode = extent && iso1A2Code(extent.center());
79855             _countryCode = countryCode && countryCode.toLowerCase();
79856           }
79857
79858           function combinedEntityExtent() {
79859             return _entityIDs && _entityIDs.length && utilTotalExtent(_entityIDs, context.graph());
79860           }
79861
79862           return utilRebind(localized, dispatch, 'on');
79863         }
79864
79865         function uiFieldRoadspeed(field, context) {
79866           var dispatch = dispatch$8('change');
79867           var unitInput = select(null);
79868           var input = select(null);
79869           var _entityIDs = [];
79870
79871           var _tags;
79872
79873           var _isImperial;
79874
79875           var speedCombo = uiCombobox(context, 'roadspeed');
79876           var unitCombo = uiCombobox(context, 'roadspeed-unit').data(['km/h', 'mph'].map(comboValues));
79877           var metricValues = [20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120];
79878           var imperialValues = [5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75, 80];
79879
79880           function roadspeed(selection) {
79881             var wrap = selection.selectAll('.form-field-input-wrap').data([0]);
79882             wrap = wrap.enter().append('div').attr('class', 'form-field-input-wrap form-field-input-' + field.type).merge(wrap);
79883             input = wrap.selectAll('input.roadspeed-number').data([0]);
79884             input = input.enter().append('input').attr('type', 'text').attr('class', 'roadspeed-number').attr('id', field.domId).call(utilNoAuto).call(speedCombo).merge(input);
79885             input.on('change', change).on('blur', change);
79886             var loc = combinedEntityExtent().center();
79887             _isImperial = roadSpeedUnit(loc) === 'mph';
79888             unitInput = wrap.selectAll('input.roadspeed-unit').data([0]);
79889             unitInput = unitInput.enter().append('input').attr('type', 'text').attr('class', 'roadspeed-unit').call(unitCombo).merge(unitInput);
79890             unitInput.on('blur', changeUnits).on('change', changeUnits);
79891
79892             function changeUnits() {
79893               _isImperial = utilGetSetValue(unitInput) === 'mph';
79894               utilGetSetValue(unitInput, _isImperial ? 'mph' : 'km/h');
79895               setUnitSuggestions();
79896               change();
79897             }
79898           }
79899
79900           function setUnitSuggestions() {
79901             speedCombo.data((_isImperial ? imperialValues : metricValues).map(comboValues));
79902             utilGetSetValue(unitInput, _isImperial ? 'mph' : 'km/h');
79903           }
79904
79905           function comboValues(d) {
79906             return {
79907               value: d.toString(),
79908               title: d.toString()
79909             };
79910           }
79911
79912           function change() {
79913             var tag = {};
79914             var value = utilGetSetValue(input).trim(); // don't override multiple values with blank string
79915
79916             if (!value && Array.isArray(_tags[field.key])) return;
79917
79918             if (!value) {
79919               tag[field.key] = undefined;
79920             } else if (isNaN(value) || !_isImperial) {
79921               tag[field.key] = context.cleanTagValue(value);
79922             } else {
79923               tag[field.key] = context.cleanTagValue(value + ' mph');
79924             }
79925
79926             dispatch.call('change', this, tag);
79927           }
79928
79929           roadspeed.tags = function (tags) {
79930             _tags = tags;
79931             var value = tags[field.key];
79932             var isMixed = Array.isArray(value);
79933
79934             if (!isMixed) {
79935               if (value && value.indexOf('mph') >= 0) {
79936                 value = parseInt(value, 10).toString();
79937                 _isImperial = true;
79938               } else if (value) {
79939                 _isImperial = false;
79940               }
79941             }
79942
79943             setUnitSuggestions();
79944             utilGetSetValue(input, typeof value === 'string' ? value : '').attr('title', isMixed ? value.filter(Boolean).join('\n') : null).attr('placeholder', isMixed ? _t('inspector.multiple_values') : field.placeholder()).classed('mixed', isMixed);
79945           };
79946
79947           roadspeed.focus = function () {
79948             input.node().focus();
79949           };
79950
79951           roadspeed.entityIDs = function (val) {
79952             _entityIDs = val;
79953           };
79954
79955           function combinedEntityExtent() {
79956             return _entityIDs && _entityIDs.length && utilTotalExtent(_entityIDs, context.graph());
79957           }
79958
79959           return utilRebind(roadspeed, dispatch, 'on');
79960         }
79961
79962         function uiFieldRadio(field, context) {
79963           var dispatch = dispatch$8('change');
79964           var placeholder = select(null);
79965           var wrap = select(null);
79966           var labels = select(null);
79967           var radios = select(null);
79968           var radioData = (field.options || field.keys).slice(); // shallow copy
79969
79970           var typeField;
79971           var layerField;
79972           var _oldType = {};
79973           var _entityIDs = [];
79974
79975           function selectedKey() {
79976             var node = wrap.selectAll('.form-field-input-radio label.active input');
79977             return !node.empty() && node.datum();
79978           }
79979
79980           function radio(selection) {
79981             selection.classed('preset-radio', true);
79982             wrap = selection.selectAll('.form-field-input-wrap').data([0]);
79983             var enter = wrap.enter().append('div').attr('class', 'form-field-input-wrap form-field-input-radio');
79984             enter.append('span').attr('class', 'placeholder');
79985             wrap = wrap.merge(enter);
79986             placeholder = wrap.selectAll('.placeholder');
79987             labels = wrap.selectAll('label').data(radioData);
79988             enter = labels.enter().append('label');
79989             enter.append('input').attr('type', 'radio').attr('name', field.id).attr('value', function (d) {
79990               return field.t('options.' + d, {
79991                 'default': d
79992               });
79993             }).attr('checked', false);
79994             enter.append('span').html(function (d) {
79995               return field.t.html('options.' + d, {
79996                 'default': d
79997               });
79998             });
79999             labels = labels.merge(enter);
80000             radios = labels.selectAll('input').on('change', changeRadio);
80001           }
80002
80003           function structureExtras(selection, tags) {
80004             var selected = selectedKey() || tags.layer !== undefined;
80005             var type = _mainPresetIndex.field(selected);
80006             var layer = _mainPresetIndex.field('layer');
80007             var showLayer = selected === 'bridge' || selected === 'tunnel' || tags.layer !== undefined;
80008             var extrasWrap = selection.selectAll('.structure-extras-wrap').data(selected ? [0] : []);
80009             extrasWrap.exit().remove();
80010             extrasWrap = extrasWrap.enter().append('div').attr('class', 'structure-extras-wrap').merge(extrasWrap);
80011             var list = extrasWrap.selectAll('ul').data([0]);
80012             list = list.enter().append('ul').attr('class', 'rows').merge(list); // Type
80013
80014             if (type) {
80015               if (!typeField || typeField.id !== selected) {
80016                 typeField = uiField(context, type, _entityIDs, {
80017                   wrap: false
80018                 }).on('change', changeType);
80019               }
80020
80021               typeField.tags(tags);
80022             } else {
80023               typeField = null;
80024             }
80025
80026             var typeItem = list.selectAll('.structure-type-item').data(typeField ? [typeField] : [], function (d) {
80027               return d.id;
80028             }); // Exit
80029
80030             typeItem.exit().remove(); // Enter
80031
80032             var typeEnter = typeItem.enter().insert('li', ':first-child').attr('class', 'labeled-input structure-type-item');
80033             typeEnter.append('span').attr('class', 'label structure-label-type').attr('for', 'preset-input-' + selected).html(_t.html('inspector.radio.structure.type'));
80034             typeEnter.append('div').attr('class', 'structure-input-type-wrap'); // Update
80035
80036             typeItem = typeItem.merge(typeEnter);
80037
80038             if (typeField) {
80039               typeItem.selectAll('.structure-input-type-wrap').call(typeField.render);
80040             } // Layer
80041
80042
80043             if (layer && showLayer) {
80044               if (!layerField) {
80045                 layerField = uiField(context, layer, _entityIDs, {
80046                   wrap: false
80047                 }).on('change', changeLayer);
80048               }
80049
80050               layerField.tags(tags);
80051               field.keys = utilArrayUnion(field.keys, ['layer']);
80052             } else {
80053               layerField = null;
80054               field.keys = field.keys.filter(function (k) {
80055                 return k !== 'layer';
80056               });
80057             }
80058
80059             var layerItem = list.selectAll('.structure-layer-item').data(layerField ? [layerField] : []); // Exit
80060
80061             layerItem.exit().remove(); // Enter
80062
80063             var layerEnter = layerItem.enter().append('li').attr('class', 'labeled-input structure-layer-item');
80064             layerEnter.append('span').attr('class', 'label structure-label-layer').attr('for', 'preset-input-layer').html(_t.html('inspector.radio.structure.layer'));
80065             layerEnter.append('div').attr('class', 'structure-input-layer-wrap'); // Update
80066
80067             layerItem = layerItem.merge(layerEnter);
80068
80069             if (layerField) {
80070               layerItem.selectAll('.structure-input-layer-wrap').call(layerField.render);
80071             }
80072           }
80073
80074           function changeType(t, onInput) {
80075             var key = selectedKey();
80076             if (!key) return;
80077             var val = t[key];
80078
80079             if (val !== 'no') {
80080               _oldType[key] = val;
80081             }
80082
80083             if (field.type === 'structureRadio') {
80084               // remove layer if it should not be set
80085               if (val === 'no' || key !== 'bridge' && key !== 'tunnel' || key === 'tunnel' && val === 'building_passage') {
80086                 t.layer = undefined;
80087               } // add layer if it should be set
80088
80089
80090               if (t.layer === undefined) {
80091                 if (key === 'bridge' && val !== 'no') {
80092                   t.layer = '1';
80093                 }
80094
80095                 if (key === 'tunnel' && val !== 'no' && val !== 'building_passage') {
80096                   t.layer = '-1';
80097                 }
80098               }
80099             }
80100
80101             dispatch.call('change', this, t, onInput);
80102           }
80103
80104           function changeLayer(t, onInput) {
80105             if (t.layer === '0') {
80106               t.layer = undefined;
80107             }
80108
80109             dispatch.call('change', this, t, onInput);
80110           }
80111
80112           function changeRadio() {
80113             var t = {};
80114             var activeKey;
80115
80116             if (field.key) {
80117               t[field.key] = undefined;
80118             }
80119
80120             radios.each(function (d) {
80121               var active = select(this).property('checked');
80122               if (active) activeKey = d;
80123
80124               if (field.key) {
80125                 if (active) t[field.key] = d;
80126               } else {
80127                 var val = _oldType[activeKey] || 'yes';
80128                 t[d] = active ? val : undefined;
80129               }
80130             });
80131
80132             if (field.type === 'structureRadio') {
80133               if (activeKey === 'bridge') {
80134                 t.layer = '1';
80135               } else if (activeKey === 'tunnel' && t.tunnel !== 'building_passage') {
80136                 t.layer = '-1';
80137               } else {
80138                 t.layer = undefined;
80139               }
80140             }
80141
80142             dispatch.call('change', this, t);
80143           }
80144
80145           radio.tags = function (tags) {
80146             radios.property('checked', function (d) {
80147               if (field.key) {
80148                 return tags[field.key] === d;
80149               }
80150
80151               return !!(typeof tags[d] === 'string' && tags[d].toLowerCase() !== 'no');
80152             });
80153
80154             function isMixed(d) {
80155               if (field.key) {
80156                 return Array.isArray(tags[field.key]) && tags[field.key].includes(d);
80157               }
80158
80159               return Array.isArray(tags[d]);
80160             }
80161
80162             labels.classed('active', function (d) {
80163               if (field.key) {
80164                 return Array.isArray(tags[field.key]) && tags[field.key].includes(d) || tags[field.key] === d;
80165               }
80166
80167               return Array.isArray(tags[d]) || !!(tags[d] && tags[d].toLowerCase() !== 'no');
80168             }).classed('mixed', isMixed).attr('title', function (d) {
80169               return isMixed(d) ? _t('inspector.unshared_value_tooltip') : null;
80170             });
80171             var selection = radios.filter(function () {
80172               return this.checked;
80173             });
80174
80175             if (selection.empty()) {
80176               placeholder.html(_t.html('inspector.none'));
80177             } else {
80178               placeholder.html(selection.attr('value'));
80179               _oldType[selection.datum()] = tags[selection.datum()];
80180             }
80181
80182             if (field.type === 'structureRadio') {
80183               // For waterways without a tunnel tag, set 'culvert' as
80184               // the _oldType to default to if the user picks 'tunnel'
80185               if (!!tags.waterway && !_oldType.tunnel) {
80186                 _oldType.tunnel = 'culvert';
80187               }
80188
80189               wrap.call(structureExtras, tags);
80190             }
80191           };
80192
80193           radio.focus = function () {
80194             radios.node().focus();
80195           };
80196
80197           radio.entityIDs = function (val) {
80198             if (!arguments.length) return _entityIDs;
80199             _entityIDs = val;
80200             _oldType = {};
80201             return radio;
80202           };
80203
80204           radio.isAllowed = function () {
80205             return _entityIDs.length === 1;
80206           };
80207
80208           return utilRebind(radio, dispatch, 'on');
80209         }
80210
80211         function uiFieldRestrictions(field, context) {
80212           var dispatch = dispatch$8('change');
80213           var breathe = behaviorBreathe();
80214           corePreferences('turn-restriction-via-way', null); // remove old key
80215
80216           var storedViaWay = corePreferences('turn-restriction-via-way0'); // use new key #6922
80217
80218           var storedDistance = corePreferences('turn-restriction-distance');
80219
80220           var _maxViaWay = storedViaWay !== null ? +storedViaWay : 0;
80221
80222           var _maxDistance = storedDistance ? +storedDistance : 30;
80223
80224           var _initialized = false;
80225
80226           var _parent = select(null); // the entire field
80227
80228
80229           var _container = select(null); // just the map
80230
80231
80232           var _oldTurns;
80233
80234           var _graph;
80235
80236           var _vertexID;
80237
80238           var _intersection;
80239
80240           var _fromWayID;
80241
80242           var _lastXPos;
80243
80244           function restrictions(selection) {
80245             _parent = selection; // try to reuse the intersection, but always rebuild it if the graph has changed
80246
80247             if (_vertexID && (context.graph() !== _graph || !_intersection)) {
80248               _graph = context.graph();
80249               _intersection = osmIntersection(_graph, _vertexID, _maxDistance);
80250             } // It's possible for there to be no actual intersection here.
80251             // for example, a vertex of two `highway=path`
80252             // In this case, hide the field.
80253
80254
80255             var isOK = _intersection && _intersection.vertices.length && // has vertices
80256             _intersection.vertices // has the vertex that the user selected
80257             .filter(function (vertex) {
80258               return vertex.id === _vertexID;
80259             }).length && _intersection.ways.length > 2 && // has more than 2 ways
80260             _intersection.ways // has more than 1 TO way
80261             .filter(function (way) {
80262               return way.__to;
80263             }).length > 1; // Also hide in the case where
80264
80265             select(selection.node().parentNode).classed('hide', !isOK); // if form field is hidden or has detached from dom, clean up.
80266
80267             if (!isOK || !context.container().select('.inspector-wrap.inspector-hidden').empty() || !selection.node().parentNode || !selection.node().parentNode.parentNode) {
80268               selection.call(restrictions.off);
80269               return;
80270             }
80271
80272             var wrap = selection.selectAll('.form-field-input-wrap').data([0]);
80273             wrap = wrap.enter().append('div').attr('class', 'form-field-input-wrap form-field-input-' + field.type).merge(wrap);
80274             var container = wrap.selectAll('.restriction-container').data([0]); // enter
80275
80276             var containerEnter = container.enter().append('div').attr('class', 'restriction-container');
80277             containerEnter.append('div').attr('class', 'restriction-help'); // update
80278
80279             _container = containerEnter.merge(container).call(renderViewer);
80280             var controls = wrap.selectAll('.restriction-controls').data([0]); // enter/update
80281
80282             controls.enter().append('div').attr('class', 'restriction-controls-container').append('div').attr('class', 'restriction-controls').merge(controls).call(renderControls);
80283           }
80284
80285           function renderControls(selection) {
80286             var distControl = selection.selectAll('.restriction-distance').data([0]);
80287             distControl.exit().remove();
80288             var distControlEnter = distControl.enter().append('div').attr('class', 'restriction-control restriction-distance');
80289             distControlEnter.append('span').attr('class', 'restriction-control-label restriction-distance-label').html(_t.html('restriction.controls.distance') + ':');
80290             distControlEnter.append('input').attr('class', 'restriction-distance-input').attr('type', 'range').attr('min', '20').attr('max', '50').attr('step', '5');
80291             distControlEnter.append('span').attr('class', 'restriction-distance-text'); // update
80292
80293             selection.selectAll('.restriction-distance-input').property('value', _maxDistance).on('input', function () {
80294               var val = select(this).property('value');
80295               _maxDistance = +val;
80296               _intersection = null;
80297
80298               _container.selectAll('.layer-osm .layer-turns *').remove();
80299
80300               corePreferences('turn-restriction-distance', _maxDistance);
80301
80302               _parent.call(restrictions);
80303             });
80304             selection.selectAll('.restriction-distance-text').html(displayMaxDistance(_maxDistance));
80305             var viaControl = selection.selectAll('.restriction-via-way').data([0]);
80306             viaControl.exit().remove();
80307             var viaControlEnter = viaControl.enter().append('div').attr('class', 'restriction-control restriction-via-way');
80308             viaControlEnter.append('span').attr('class', 'restriction-control-label restriction-via-way-label').html(_t.html('restriction.controls.via') + ':');
80309             viaControlEnter.append('input').attr('class', 'restriction-via-way-input').attr('type', 'range').attr('min', '0').attr('max', '2').attr('step', '1');
80310             viaControlEnter.append('span').attr('class', 'restriction-via-way-text'); // update
80311
80312             selection.selectAll('.restriction-via-way-input').property('value', _maxViaWay).on('input', function () {
80313               var val = select(this).property('value');
80314               _maxViaWay = +val;
80315
80316               _container.selectAll('.layer-osm .layer-turns *').remove();
80317
80318               corePreferences('turn-restriction-via-way0', _maxViaWay);
80319
80320               _parent.call(restrictions);
80321             });
80322             selection.selectAll('.restriction-via-way-text').html(displayMaxVia(_maxViaWay));
80323           }
80324
80325           function renderViewer(selection) {
80326             if (!_intersection) return;
80327             var vgraph = _intersection.graph;
80328             var filter = utilFunctor(true);
80329             var projection = geoRawMercator(); // Reflow warning: `utilGetDimensions` calls `getBoundingClientRect`
80330             // Instead of asking the restriction-container for its dimensions,
80331             //  we can ask the .sidebar, which can have its dimensions cached.
80332             // width: calc as sidebar - padding
80333             // height: hardcoded (from `80_app.css`)
80334             // var d = utilGetDimensions(selection);
80335
80336             var sdims = utilGetDimensions(context.container().select('.sidebar'));
80337             var d = [sdims[0] - 50, 370];
80338             var c = geoVecScale(d, 0.5);
80339             var z = 22;
80340             projection.scale(geoZoomToScale(z)); // Calculate extent of all key vertices
80341
80342             var extent = geoExtent();
80343
80344             for (var i = 0; i < _intersection.vertices.length; i++) {
80345               extent._extend(_intersection.vertices[i].extent());
80346             } // If this is a large intersection, adjust zoom to fit extent
80347
80348
80349             if (_intersection.vertices.length > 1) {
80350               var padding = 180; // in z22 pixels
80351
80352               var tl = projection([extent[0][0], extent[1][1]]);
80353               var br = projection([extent[1][0], extent[0][1]]);
80354               var hFactor = (br[0] - tl[0]) / (d[0] - padding);
80355               var vFactor = (br[1] - tl[1]) / (d[1] - padding);
80356               var hZoomDiff = Math.log(Math.abs(hFactor)) / Math.LN2;
80357               var vZoomDiff = Math.log(Math.abs(vFactor)) / Math.LN2;
80358               z = z - Math.max(hZoomDiff, vZoomDiff);
80359               projection.scale(geoZoomToScale(z));
80360             }
80361
80362             var padTop = 35; // reserve top space for hint text
80363
80364             var extentCenter = projection(extent.center());
80365             extentCenter[1] = extentCenter[1] - padTop;
80366             projection.translate(geoVecSubtract(c, extentCenter)).clipExtent([[0, 0], d]);
80367             var drawLayers = svgLayers(projection, context).only(['osm', 'touch']).dimensions(d);
80368             var drawVertices = svgVertices(projection, context);
80369             var drawLines = svgLines(projection, context);
80370             var drawTurns = svgTurns(projection, context);
80371             var firstTime = selection.selectAll('.surface').empty();
80372             selection.call(drawLayers);
80373             var surface = selection.selectAll('.surface').classed('tr', true);
80374
80375             if (firstTime) {
80376               _initialized = true;
80377               surface.call(breathe);
80378             } // This can happen if we've lowered the detail while a FROM way
80379             // is selected, and that way is no longer part of the intersection.
80380
80381
80382             if (_fromWayID && !vgraph.hasEntity(_fromWayID)) {
80383               _fromWayID = null;
80384               _oldTurns = null;
80385             }
80386
80387             surface.call(utilSetDimensions, d).call(drawVertices, vgraph, _intersection.vertices, filter, extent, z).call(drawLines, vgraph, _intersection.ways, filter).call(drawTurns, vgraph, _intersection.turns(_fromWayID, _maxViaWay));
80388             surface.on('click.restrictions', click).on('mouseover.restrictions', mouseover);
80389             surface.selectAll('.selected').classed('selected', false);
80390             surface.selectAll('.related').classed('related', false);
80391             var way;
80392
80393             if (_fromWayID) {
80394               way = vgraph.entity(_fromWayID);
80395               surface.selectAll('.' + _fromWayID).classed('selected', true).classed('related', true);
80396             }
80397
80398             document.addEventListener('resizeWindow', function () {
80399               utilSetDimensions(_container, null);
80400               redraw(1);
80401             }, false);
80402             updateHints(null);
80403
80404             function click(d3_event) {
80405               surface.call(breathe.off).call(breathe);
80406               var datum = d3_event.target.__data__;
80407               var entity = datum && datum.properties && datum.properties.entity;
80408
80409               if (entity) {
80410                 datum = entity;
80411               }
80412
80413               if (datum instanceof osmWay && (datum.__from || datum.__via)) {
80414                 _fromWayID = datum.id;
80415                 _oldTurns = null;
80416                 redraw();
80417               } else if (datum instanceof osmTurn) {
80418                 var actions, extraActions, turns, i;
80419                 var restrictionType = osmInferRestriction(vgraph, datum, projection);
80420
80421                 if (datum.restrictionID && !datum.direct) {
80422                   return;
80423                 } else if (datum.restrictionID && !datum.only) {
80424                   // NO -> ONLY
80425                   var seen = {};
80426                   var datumOnly = JSON.parse(JSON.stringify(datum)); // deep clone the datum
80427
80428                   datumOnly.only = true; // but change this property
80429
80430                   restrictionType = restrictionType.replace(/^no/, 'only'); // Adding an ONLY restriction should destroy all other direct restrictions from the FROM towards the VIA.
80431                   // We will remember them in _oldTurns, and restore them if the user clicks again.
80432
80433                   turns = _intersection.turns(_fromWayID, 2);
80434                   extraActions = [];
80435                   _oldTurns = [];
80436
80437                   for (i = 0; i < turns.length; i++) {
80438                     var turn = turns[i];
80439                     if (seen[turn.restrictionID]) continue; // avoid deleting the turn twice (#4968, #4928)
80440
80441                     if (turn.direct && turn.path[1] === datum.path[1]) {
80442                       seen[turns[i].restrictionID] = true;
80443                       turn.restrictionType = osmInferRestriction(vgraph, turn, projection);
80444
80445                       _oldTurns.push(turn);
80446
80447                       extraActions.push(actionUnrestrictTurn(turn));
80448                     }
80449                   }
80450
80451                   actions = _intersection.actions.concat(extraActions, [actionRestrictTurn(datumOnly, restrictionType), _t('operations.restriction.annotation.create')]);
80452                 } else if (datum.restrictionID) {
80453                   // ONLY -> Allowed
80454                   // Restore whatever restrictions we might have destroyed by cycling thru the ONLY state.
80455                   // This relies on the assumption that the intersection was already split up when we
80456                   // performed the previous action (NO -> ONLY), so the IDs in _oldTurns shouldn't have changed.
80457                   turns = _oldTurns || [];
80458                   extraActions = [];
80459
80460                   for (i = 0; i < turns.length; i++) {
80461                     if (turns[i].key !== datum.key) {
80462                       extraActions.push(actionRestrictTurn(turns[i], turns[i].restrictionType));
80463                     }
80464                   }
80465
80466                   _oldTurns = null;
80467                   actions = _intersection.actions.concat(extraActions, [actionUnrestrictTurn(datum), _t('operations.restriction.annotation.delete')]);
80468                 } else {
80469                   // Allowed -> NO
80470                   actions = _intersection.actions.concat([actionRestrictTurn(datum, restrictionType), _t('operations.restriction.annotation.create')]);
80471                 }
80472
80473                 context.perform.apply(context, actions); // At this point the datum will be changed, but will have same key..
80474                 // Refresh it and update the help..
80475
80476                 var s = surface.selectAll('.' + datum.key);
80477                 datum = s.empty() ? null : s.datum();
80478                 updateHints(datum);
80479               } else {
80480                 _fromWayID = null;
80481                 _oldTurns = null;
80482                 redraw();
80483               }
80484             }
80485
80486             function mouseover(d3_event) {
80487               var datum = d3_event.target.__data__;
80488               updateHints(datum);
80489             }
80490
80491             _lastXPos = _lastXPos || sdims[0];
80492
80493             function redraw(minChange) {
80494               var xPos = -1;
80495
80496               if (minChange) {
80497                 xPos = utilGetDimensions(context.container().select('.sidebar'))[0];
80498               }
80499
80500               if (!minChange || minChange && Math.abs(xPos - _lastXPos) >= minChange) {
80501                 if (context.hasEntity(_vertexID)) {
80502                   _lastXPos = xPos;
80503
80504                   _container.call(renderViewer);
80505                 }
80506               }
80507             }
80508
80509             function highlightPathsFrom(wayID) {
80510               surface.selectAll('.related').classed('related', false).classed('allow', false).classed('restrict', false).classed('only', false);
80511               surface.selectAll('.' + wayID).classed('related', true);
80512
80513               if (wayID) {
80514                 var turns = _intersection.turns(wayID, _maxViaWay);
80515
80516                 for (var i = 0; i < turns.length; i++) {
80517                   var turn = turns[i];
80518                   var ids = [turn.to.way];
80519                   var klass = turn.no ? 'restrict' : turn.only ? 'only' : 'allow';
80520
80521                   if (turn.only || turns.length === 1) {
80522                     if (turn.via.ways) {
80523                       ids = ids.concat(turn.via.ways);
80524                     }
80525                   } else if (turn.to.way === wayID) {
80526                     continue;
80527                   }
80528
80529                   surface.selectAll(utilEntitySelector(ids)).classed('related', true).classed('allow', klass === 'allow').classed('restrict', klass === 'restrict').classed('only', klass === 'only');
80530                 }
80531               }
80532             }
80533
80534             function updateHints(datum) {
80535               var help = _container.selectAll('.restriction-help').html('');
80536
80537               var placeholders = {};
80538               ['from', 'via', 'to'].forEach(function (k) {
80539                 placeholders[k] = '<span class="qualifier">' + _t('restriction.help.' + k) + '</span>';
80540               });
80541               var entity = datum && datum.properties && datum.properties.entity;
80542
80543               if (entity) {
80544                 datum = entity;
80545               }
80546
80547               if (_fromWayID) {
80548                 way = vgraph.entity(_fromWayID);
80549                 surface.selectAll('.' + _fromWayID).classed('selected', true).classed('related', true);
80550               } // Hovering a way
80551
80552
80553               if (datum instanceof osmWay && datum.__from) {
80554                 way = datum;
80555                 highlightPathsFrom(_fromWayID ? null : way.id);
80556                 surface.selectAll('.' + way.id).classed('related', true);
80557                 var clickSelect = !_fromWayID || _fromWayID !== way.id;
80558                 help.append('div') // "Click to select FROM {fromName}." / "FROM {fromName}"
80559                 .html(_t.html('restriction.help.' + (clickSelect ? 'select_from_name' : 'from_name'), {
80560                   from: placeholders.from,
80561                   fromName: displayName(way.id, vgraph)
80562                 })); // Hovering a turn arrow
80563               } else if (datum instanceof osmTurn) {
80564                 var restrictionType = osmInferRestriction(vgraph, datum, projection);
80565                 var turnType = restrictionType.replace(/^(only|no)\_/, '');
80566                 var indirect = datum.direct === false ? _t.html('restriction.help.indirect') : '';
80567                 var klass, turnText, nextText;
80568
80569                 if (datum.no) {
80570                   klass = 'restrict';
80571                   turnText = _t.html('restriction.help.turn.no_' + turnType, {
80572                     indirect: indirect
80573                   });
80574                   nextText = _t.html('restriction.help.turn.only_' + turnType, {
80575                     indirect: ''
80576                   });
80577                 } else if (datum.only) {
80578                   klass = 'only';
80579                   turnText = _t.html('restriction.help.turn.only_' + turnType, {
80580                     indirect: indirect
80581                   });
80582                   nextText = _t.html('restriction.help.turn.allowed_' + turnType, {
80583                     indirect: ''
80584                   });
80585                 } else {
80586                   klass = 'allow';
80587                   turnText = _t.html('restriction.help.turn.allowed_' + turnType, {
80588                     indirect: indirect
80589                   });
80590                   nextText = _t.html('restriction.help.turn.no_' + turnType, {
80591                     indirect: ''
80592                   });
80593                 }
80594
80595                 help.append('div') // "NO Right Turn (indirect)"
80596                 .attr('class', 'qualifier ' + klass).html(turnText);
80597                 help.append('div') // "FROM {fromName} TO {toName}"
80598                 .html(_t.html('restriction.help.from_name_to_name', {
80599                   from: placeholders.from,
80600                   fromName: displayName(datum.from.way, vgraph),
80601                   to: placeholders.to,
80602                   toName: displayName(datum.to.way, vgraph)
80603                 }));
80604
80605                 if (datum.via.ways && datum.via.ways.length) {
80606                   var names = [];
80607
80608                   for (var i = 0; i < datum.via.ways.length; i++) {
80609                     var prev = names[names.length - 1];
80610                     var curr = displayName(datum.via.ways[i], vgraph);
80611
80612                     if (!prev || curr !== prev) {
80613                       // collapse identical names
80614                       names.push(curr);
80615                     }
80616                   }
80617
80618                   help.append('div') // "VIA {viaNames}"
80619                   .html(_t.html('restriction.help.via_names', {
80620                     via: placeholders.via,
80621                     viaNames: names.join(', ')
80622                   }));
80623                 }
80624
80625                 if (!indirect) {
80626                   help.append('div') // Click for "No Right Turn"
80627                   .html(_t.html('restriction.help.toggle', {
80628                     turn: nextText.trim()
80629                   }));
80630                 }
80631
80632                 highlightPathsFrom(null);
80633                 var alongIDs = datum.path.slice();
80634                 surface.selectAll(utilEntitySelector(alongIDs)).classed('related', true).classed('allow', klass === 'allow').classed('restrict', klass === 'restrict').classed('only', klass === 'only'); // Hovering empty surface
80635               } else {
80636                 highlightPathsFrom(null);
80637
80638                 if (_fromWayID) {
80639                   help.append('div') // "FROM {fromName}"
80640                   .html(_t.html('restriction.help.from_name', {
80641                     from: placeholders.from,
80642                     fromName: displayName(_fromWayID, vgraph)
80643                   }));
80644                 } else {
80645                   help.append('div') // "Click to select a FROM segment."
80646                   .html(_t.html('restriction.help.select_from', {
80647                     from: placeholders.from
80648                   }));
80649                 }
80650               }
80651             }
80652           }
80653
80654           function displayMaxDistance(maxDist) {
80655             var isImperial = !_mainLocalizer.usesMetric();
80656             var opts;
80657
80658             if (isImperial) {
80659               var distToFeet = {
80660                 // imprecise conversion for prettier display
80661                 20: 70,
80662                 25: 85,
80663                 30: 100,
80664                 35: 115,
80665                 40: 130,
80666                 45: 145,
80667                 50: 160
80668               }[maxDist];
80669               opts = {
80670                 distance: _t('units.feet', {
80671                   quantity: distToFeet
80672                 })
80673               };
80674             } else {
80675               opts = {
80676                 distance: _t('units.meters', {
80677                   quantity: maxDist
80678                 })
80679               };
80680             }
80681
80682             return _t.html('restriction.controls.distance_up_to', opts);
80683           }
80684
80685           function displayMaxVia(maxVia) {
80686             return maxVia === 0 ? _t.html('restriction.controls.via_node_only') : maxVia === 1 ? _t.html('restriction.controls.via_up_to_one') : _t.html('restriction.controls.via_up_to_two');
80687           }
80688
80689           function displayName(entityID, graph) {
80690             var entity = graph.entity(entityID);
80691             var name = utilDisplayName(entity) || '';
80692             var matched = _mainPresetIndex.match(entity, graph);
80693             var type = matched && matched.name() || utilDisplayType(entity.id);
80694             return name || type;
80695           }
80696
80697           restrictions.entityIDs = function (val) {
80698             _intersection = null;
80699             _fromWayID = null;
80700             _oldTurns = null;
80701             _vertexID = val[0];
80702           };
80703
80704           restrictions.tags = function () {};
80705
80706           restrictions.focus = function () {};
80707
80708           restrictions.off = function (selection) {
80709             if (!_initialized) return;
80710             selection.selectAll('.surface').call(breathe.off).on('click.restrictions', null).on('mouseover.restrictions', null);
80711             select(window).on('resize.restrictions', null);
80712           };
80713
80714           return utilRebind(restrictions, dispatch, 'on');
80715         }
80716         uiFieldRestrictions.supportsMultiselection = false;
80717
80718         function uiFieldTextarea(field, context) {
80719           var dispatch = dispatch$8('change');
80720           var input = select(null);
80721
80722           var _tags;
80723
80724           function textarea(selection) {
80725             var wrap = selection.selectAll('.form-field-input-wrap').data([0]);
80726             wrap = wrap.enter().append('div').attr('class', 'form-field-input-wrap form-field-input-' + field.type).merge(wrap);
80727             input = wrap.selectAll('textarea').data([0]);
80728             input = input.enter().append('textarea').attr('id', field.domId).call(utilNoAuto).on('input', change(true)).on('blur', change()).on('change', change()).merge(input);
80729           }
80730
80731           function change(onInput) {
80732             return function () {
80733               var val = utilGetSetValue(input);
80734               if (!onInput) val = context.cleanTagValue(val); // don't override multiple values with blank string
80735
80736               if (!val && Array.isArray(_tags[field.key])) return;
80737               var t = {};
80738               t[field.key] = val || undefined;
80739               dispatch.call('change', this, t, onInput);
80740             };
80741           }
80742
80743           textarea.tags = function (tags) {
80744             _tags = tags;
80745             var isMixed = Array.isArray(tags[field.key]);
80746             utilGetSetValue(input, !isMixed && tags[field.key] ? tags[field.key] : '').attr('title', isMixed ? tags[field.key].filter(Boolean).join('\n') : undefined).attr('placeholder', isMixed ? _t('inspector.multiple_values') : field.placeholder() || _t('inspector.unknown')).classed('mixed', isMixed);
80747           };
80748
80749           textarea.focus = function () {
80750             input.node().focus();
80751           };
80752
80753           return utilRebind(textarea, dispatch, 'on');
80754         }
80755
80756         var getOwnPropertyDescriptor = objectGetOwnPropertyDescriptor.f;
80757
80758
80759
80760
80761
80762
80763         // eslint-disable-next-line es/no-string-prototype-endswith -- safe
80764         var $endsWith = ''.endsWith;
80765         var min = Math.min;
80766
80767         var CORRECT_IS_REGEXP_LOGIC = correctIsRegexpLogic('endsWith');
80768         // https://github.com/zloirock/core-js/pull/702
80769         var MDN_POLYFILL_BUG = !CORRECT_IS_REGEXP_LOGIC && !!function () {
80770           var descriptor = getOwnPropertyDescriptor(String.prototype, 'endsWith');
80771           return descriptor && !descriptor.writable;
80772         }();
80773
80774         // `String.prototype.endsWith` method
80775         // https://tc39.es/ecma262/#sec-string.prototype.endswith
80776         _export({ target: 'String', proto: true, forced: !MDN_POLYFILL_BUG && !CORRECT_IS_REGEXP_LOGIC }, {
80777           endsWith: function endsWith(searchString /* , endPosition = @length */) {
80778             var that = String(requireObjectCoercible(this));
80779             notARegexp(searchString);
80780             var endPosition = arguments.length > 1 ? arguments[1] : undefined;
80781             var len = toLength(that.length);
80782             var end = endPosition === undefined ? len : min(toLength(endPosition), len);
80783             var search = String(searchString);
80784             return $endsWith
80785               ? $endsWith.call(that, search, end)
80786               : that.slice(end - search.length, end) === search;
80787           }
80788         });
80789
80790         function uiFieldWikidata(field, context) {
80791           var wikidata = services.wikidata;
80792           var dispatch = dispatch$8('change');
80793
80794           var _selection = select(null);
80795
80796           var _searchInput = select(null);
80797
80798           var _qid = null;
80799           var _wikidataEntity = null;
80800           var _wikiURL = '';
80801           var _entityIDs = [];
80802
80803           var _wikipediaKey = field.keys && field.keys.find(function (key) {
80804             return key.includes('wikipedia');
80805           }),
80806               _hintKey = field.key === 'wikidata' ? 'name' : field.key.split(':')[0];
80807
80808           var combobox = uiCombobox(context, 'combo-' + field.safeid).caseSensitive(true).minItems(1);
80809
80810           function wiki(selection) {
80811             _selection = selection;
80812             var wrap = selection.selectAll('.form-field-input-wrap').data([0]);
80813             wrap = wrap.enter().append('div').attr('class', 'form-field-input-wrap form-field-input-' + field.type).merge(wrap);
80814             var list = wrap.selectAll('ul').data([0]);
80815             list = list.enter().append('ul').attr('class', 'rows').merge(list);
80816             var searchRow = list.selectAll('li.wikidata-search').data([0]);
80817             var searchRowEnter = searchRow.enter().append('li').attr('class', 'wikidata-search');
80818             searchRowEnter.append('input').attr('type', 'text').attr('id', field.domId).style('flex', '1').call(utilNoAuto).on('focus', function () {
80819               var node = select(this).node();
80820               node.setSelectionRange(0, node.value.length);
80821             }).on('blur', function () {
80822               setLabelForEntity();
80823             }).call(combobox.fetcher(fetchWikidataItems));
80824             combobox.on('accept', function (d) {
80825               if (d) {
80826                 _qid = d.id;
80827                 change();
80828               }
80829             }).on('cancel', function () {
80830               setLabelForEntity();
80831             });
80832             searchRowEnter.append('button').attr('class', 'form-field-button wiki-link').attr('title', _t('icons.view_on', {
80833               domain: 'wikidata.org'
80834             })).call(svgIcon('#iD-icon-out-link')).on('click', function (d3_event) {
80835               d3_event.preventDefault();
80836               if (_wikiURL) window.open(_wikiURL, '_blank');
80837             });
80838             searchRow = searchRow.merge(searchRowEnter);
80839             _searchInput = searchRow.select('input');
80840             var wikidataProperties = ['description', 'identifier'];
80841             var items = list.selectAll('li.labeled-input').data(wikidataProperties); // Enter
80842
80843             var enter = items.enter().append('li').attr('class', function (d) {
80844               return 'labeled-input preset-wikidata-' + d;
80845             });
80846             enter.append('span').attr('class', 'label').html(function (d) {
80847               return _t.html('wikidata.' + d);
80848             });
80849             enter.append('input').attr('type', 'text').call(utilNoAuto).classed('disabled', 'true').attr('readonly', 'true');
80850             enter.append('button').attr('class', 'form-field-button').attr('title', _t('icons.copy')).call(svgIcon('#iD-operation-copy')).on('click', function (d3_event) {
80851               d3_event.preventDefault();
80852               select(this.parentNode).select('input').node().select();
80853               document.execCommand('copy');
80854             });
80855           }
80856
80857           function fetchWikidataItems(q, callback) {
80858             if (!q && _hintKey) {
80859               // other tags may be good search terms
80860               for (var i in _entityIDs) {
80861                 var entity = context.hasEntity(_entityIDs[i]);
80862
80863                 if (entity.tags[_hintKey]) {
80864                   q = entity.tags[_hintKey];
80865                   break;
80866                 }
80867               }
80868             }
80869
80870             wikidata.itemsForSearchQuery(q, function (err, data) {
80871               if (err) return;
80872
80873               for (var i in data) {
80874                 data[i].value = data[i].label + ' (' + data[i].id + ')';
80875                 data[i].title = data[i].description;
80876               }
80877
80878               if (callback) callback(data);
80879             });
80880           }
80881
80882           function change() {
80883             var syncTags = {};
80884             syncTags[field.key] = _qid;
80885             dispatch.call('change', this, syncTags); // attempt asynchronous update of wikidata tag..
80886
80887             var initGraph = context.graph();
80888             var initEntityIDs = _entityIDs;
80889             wikidata.entityByQID(_qid, function (err, entity) {
80890               if (err) return; // If graph has changed, we can't apply this update.
80891
80892               if (context.graph() !== initGraph) return;
80893               if (!entity.sitelinks) return;
80894               var langs = wikidata.languagesToQuery(); // use the label and description languages as fallbacks
80895
80896               ['labels', 'descriptions'].forEach(function (key) {
80897                 if (!entity[key]) return;
80898                 var valueLangs = Object.keys(entity[key]);
80899                 if (valueLangs.length === 0) return;
80900                 var valueLang = valueLangs[0];
80901
80902                 if (langs.indexOf(valueLang) === -1) {
80903                   langs.push(valueLang);
80904                 }
80905               });
80906               var newWikipediaValue;
80907
80908               if (_wikipediaKey) {
80909                 var foundPreferred;
80910
80911                 for (var i in langs) {
80912                   var lang = langs[i];
80913                   var siteID = lang.replace('-', '_') + 'wiki';
80914
80915                   if (entity.sitelinks[siteID]) {
80916                     foundPreferred = true;
80917                     newWikipediaValue = lang + ':' + entity.sitelinks[siteID].title; // use the first match
80918
80919                     break;
80920                   }
80921                 }
80922
80923                 if (!foundPreferred) {
80924                   // No wikipedia sites available in the user's language or the fallback languages,
80925                   // default to any wikipedia sitelink
80926                   var wikiSiteKeys = Object.keys(entity.sitelinks).filter(function (site) {
80927                     return site.endsWith('wiki');
80928                   });
80929
80930                   if (wikiSiteKeys.length === 0) {
80931                     // if no wikipedia pages are linked to this wikidata entity, delete that tag
80932                     newWikipediaValue = null;
80933                   } else {
80934                     var wikiLang = wikiSiteKeys[0].slice(0, -4).replace('_', '-');
80935                     var wikiTitle = entity.sitelinks[wikiSiteKeys[0]].title;
80936                     newWikipediaValue = wikiLang + ':' + wikiTitle;
80937                   }
80938                 }
80939               }
80940
80941               if (newWikipediaValue) {
80942                 newWikipediaValue = context.cleanTagValue(newWikipediaValue);
80943               }
80944
80945               if (typeof newWikipediaValue === 'undefined') return;
80946               var actions = initEntityIDs.map(function (entityID) {
80947                 var entity = context.hasEntity(entityID);
80948                 if (!entity) return null;
80949                 var currTags = Object.assign({}, entity.tags); // shallow copy
80950
80951                 if (newWikipediaValue === null) {
80952                   if (!currTags[_wikipediaKey]) return null;
80953                   delete currTags[_wikipediaKey];
80954                 } else {
80955                   currTags[_wikipediaKey] = newWikipediaValue;
80956                 }
80957
80958                 return actionChangeTags(entityID, currTags);
80959               }).filter(Boolean);
80960               if (!actions.length) return; // Coalesce the update of wikidata tag into the previous tag change
80961
80962               context.overwrite(function actionUpdateWikipediaTags(graph) {
80963                 actions.forEach(function (action) {
80964                   graph = action(graph);
80965                 });
80966                 return graph;
80967               }, context.history().undoAnnotation()); // do not dispatch.call('change') here, because entity_editor
80968               // changeTags() is not intended to be called asynchronously
80969             });
80970           }
80971
80972           function setLabelForEntity() {
80973             var label = '';
80974
80975             if (_wikidataEntity) {
80976               label = entityPropertyForDisplay(_wikidataEntity, 'labels');
80977
80978               if (label.length === 0) {
80979                 label = _wikidataEntity.id.toString();
80980               }
80981             }
80982
80983             utilGetSetValue(_searchInput, label);
80984           }
80985
80986           wiki.tags = function (tags) {
80987             var isMixed = Array.isArray(tags[field.key]);
80988
80989             _searchInput.attr('title', isMixed ? tags[field.key].filter(Boolean).join('\n') : null).attr('placeholder', isMixed ? _t('inspector.multiple_values') : '').classed('mixed', isMixed);
80990
80991             _qid = typeof tags[field.key] === 'string' && tags[field.key] || '';
80992
80993             if (!/^Q[0-9]*$/.test(_qid)) {
80994               // not a proper QID
80995               unrecognized();
80996               return;
80997             } // QID value in correct format
80998
80999
81000             _wikiURL = 'https://wikidata.org/wiki/' + _qid;
81001             wikidata.entityByQID(_qid, function (err, entity) {
81002               if (err) {
81003                 unrecognized();
81004                 return;
81005               }
81006
81007               _wikidataEntity = entity;
81008               setLabelForEntity();
81009               var description = entityPropertyForDisplay(entity, 'descriptions');
81010
81011               _selection.select('button.wiki-link').classed('disabled', false);
81012
81013               _selection.select('.preset-wikidata-description').style('display', function () {
81014                 return description.length > 0 ? 'flex' : 'none';
81015               }).select('input').attr('value', description);
81016
81017               _selection.select('.preset-wikidata-identifier').style('display', function () {
81018                 return entity.id ? 'flex' : 'none';
81019               }).select('input').attr('value', entity.id);
81020             }); // not a proper QID
81021
81022             function unrecognized() {
81023               _wikidataEntity = null;
81024               setLabelForEntity();
81025
81026               _selection.select('.preset-wikidata-description').style('display', 'none');
81027
81028               _selection.select('.preset-wikidata-identifier').style('display', 'none');
81029
81030               _selection.select('button.wiki-link').classed('disabled', true);
81031
81032               if (_qid && _qid !== '') {
81033                 _wikiURL = 'https://wikidata.org/wiki/Special:Search?search=' + _qid;
81034               } else {
81035                 _wikiURL = '';
81036               }
81037             }
81038           };
81039
81040           function entityPropertyForDisplay(wikidataEntity, propKey) {
81041             if (!wikidataEntity[propKey]) return '';
81042             var propObj = wikidataEntity[propKey];
81043             var langKeys = Object.keys(propObj);
81044             if (langKeys.length === 0) return ''; // sorted by priority, since we want to show the user's language first if possible
81045
81046             var langs = wikidata.languagesToQuery();
81047
81048             for (var i in langs) {
81049               var lang = langs[i];
81050               var valueObj = propObj[lang];
81051               if (valueObj && valueObj.value && valueObj.value.length > 0) return valueObj.value;
81052             } // default to any available value
81053
81054
81055             return propObj[langKeys[0]].value;
81056           }
81057
81058           wiki.entityIDs = function (val) {
81059             if (!arguments.length) return _entityIDs;
81060             _entityIDs = val;
81061             return wiki;
81062           };
81063
81064           wiki.focus = function () {
81065             _searchInput.node().focus();
81066           };
81067
81068           return utilRebind(wiki, dispatch, 'on');
81069         }
81070
81071         function uiFieldWikipedia(field, context) {
81072           var _arguments = arguments;
81073           var dispatch = dispatch$8('change');
81074           var wikipedia = services.wikipedia;
81075           var wikidata = services.wikidata;
81076
81077           var _langInput = select(null);
81078
81079           var _titleInput = select(null);
81080
81081           var _wikiURL = '';
81082
81083           var _entityIDs;
81084
81085           var _tags;
81086
81087           var _dataWikipedia = [];
81088           _mainFileFetcher.get('wmf_sitematrix').then(function (d) {
81089             _dataWikipedia = d;
81090             if (_tags) updateForTags(_tags);
81091           })["catch"](function () {
81092             /* ignore */
81093           });
81094           var langCombo = uiCombobox(context, 'wikipedia-lang').fetcher(function (value, callback) {
81095             var v = value.toLowerCase();
81096             callback(_dataWikipedia.filter(function (d) {
81097               return d[0].toLowerCase().indexOf(v) >= 0 || d[1].toLowerCase().indexOf(v) >= 0 || d[2].toLowerCase().indexOf(v) >= 0;
81098             }).map(function (d) {
81099               return {
81100                 value: d[1]
81101               };
81102             }));
81103           });
81104           var titleCombo = uiCombobox(context, 'wikipedia-title').fetcher(function (value, callback) {
81105             if (!value) {
81106               value = '';
81107
81108               for (var i in _entityIDs) {
81109                 var entity = context.hasEntity(_entityIDs[i]);
81110
81111                 if (entity.tags.name) {
81112                   value = entity.tags.name;
81113                   break;
81114                 }
81115               }
81116             }
81117
81118             var searchfn = value.length > 7 ? wikipedia.search : wikipedia.suggestions;
81119             searchfn(language()[2], value, function (query, data) {
81120               callback(data.map(function (d) {
81121                 return {
81122                   value: d
81123                 };
81124               }));
81125             });
81126           });
81127
81128           function wiki(selection) {
81129             var wrap = selection.selectAll('.form-field-input-wrap').data([0]);
81130             wrap = wrap.enter().append('div').attr('class', "form-field-input-wrap form-field-input-".concat(field.type)).merge(wrap);
81131             var langContainer = wrap.selectAll('.wiki-lang-container').data([0]);
81132             langContainer = langContainer.enter().append('div').attr('class', 'wiki-lang-container').merge(langContainer);
81133             _langInput = langContainer.selectAll('input.wiki-lang').data([0]);
81134             _langInput = _langInput.enter().append('input').attr('type', 'text').attr('class', 'wiki-lang').attr('placeholder', _t('translate.localized_translation_language')).call(utilNoAuto).call(langCombo).merge(_langInput);
81135
81136             _langInput.on('blur', changeLang).on('change', changeLang);
81137
81138             var titleContainer = wrap.selectAll('.wiki-title-container').data([0]);
81139             titleContainer = titleContainer.enter().append('div').attr('class', 'wiki-title-container').merge(titleContainer);
81140             _titleInput = titleContainer.selectAll('input.wiki-title').data([0]);
81141             _titleInput = _titleInput.enter().append('input').attr('type', 'text').attr('class', 'wiki-title').attr('id', field.domId).call(utilNoAuto).call(titleCombo).merge(_titleInput);
81142
81143             _titleInput.on('blur', function () {
81144               change(true);
81145             }).on('change', function () {
81146               change(false);
81147             });
81148
81149             var link = titleContainer.selectAll('.wiki-link').data([0]);
81150             link = link.enter().append('button').attr('class', 'form-field-button wiki-link').attr('title', _t('icons.view_on', {
81151               domain: 'wikipedia.org'
81152             })).call(svgIcon('#iD-icon-out-link')).merge(link);
81153             link.on('click', function (d3_event) {
81154               d3_event.preventDefault();
81155               if (_wikiURL) window.open(_wikiURL, '_blank');
81156             });
81157           }
81158
81159           function defaultLanguageInfo(skipEnglishFallback) {
81160             var langCode = _mainLocalizer.languageCode().toLowerCase();
81161
81162             for (var i in _dataWikipedia) {
81163               var d = _dataWikipedia[i]; // default to the language of iD's current locale
81164
81165               if (d[2] === langCode) return d;
81166             } // fallback to English
81167
81168
81169             return skipEnglishFallback ? ['', '', ''] : ['English', 'English', 'en'];
81170           }
81171
81172           function language(skipEnglishFallback) {
81173             var value = utilGetSetValue(_langInput).toLowerCase();
81174
81175             for (var i in _dataWikipedia) {
81176               var d = _dataWikipedia[i]; // return the language already set in the UI, if supported
81177
81178               if (d[0].toLowerCase() === value || d[1].toLowerCase() === value || d[2] === value) return d;
81179             } // fallback to English
81180
81181
81182             return defaultLanguageInfo(skipEnglishFallback);
81183           }
81184
81185           function changeLang() {
81186             utilGetSetValue(_langInput, language()[1]);
81187             change(true);
81188           }
81189
81190           function change(skipWikidata) {
81191             var value = utilGetSetValue(_titleInput);
81192             var m = value.match(/https?:\/\/([-a-z]+)\.wikipedia\.org\/(?:wiki|\1-[-a-z]+)\/([^#]+)(?:#(.+))?/);
81193
81194             var langInfo = m && _dataWikipedia.find(function (d) {
81195               return m[1] === d[2];
81196             });
81197
81198             var syncTags = {};
81199
81200             if (langInfo) {
81201               var nativeLangName = langInfo[1]; // Normalize title http://www.mediawiki.org/wiki/API:Query#Title_normalization
81202
81203               value = decodeURIComponent(m[2]).replace(/_/g, ' ');
81204
81205               if (m[3]) {
81206                 var anchor; // try {
81207                 // leave this out for now - #6232
81208                 // Best-effort `anchordecode:` implementation
81209                 // anchor = decodeURIComponent(m[3].replace(/\.([0-9A-F]{2})/g, '%$1'));
81210                 // } catch (e) {
81211
81212                 anchor = decodeURIComponent(m[3]); // }
81213
81214                 value += '#' + anchor.replace(/_/g, ' ');
81215               }
81216
81217               value = value.slice(0, 1).toUpperCase() + value.slice(1);
81218               utilGetSetValue(_langInput, nativeLangName);
81219               utilGetSetValue(_titleInput, value);
81220             }
81221
81222             if (value) {
81223               syncTags.wikipedia = context.cleanTagValue(language()[2] + ':' + value);
81224             } else {
81225               syncTags.wikipedia = undefined;
81226             }
81227
81228             dispatch.call('change', this, syncTags);
81229             if (skipWikidata || !value || !language()[2]) return; // attempt asynchronous update of wikidata tag..
81230
81231             var initGraph = context.graph();
81232             var initEntityIDs = _entityIDs;
81233             wikidata.itemsByTitle(language()[2], value, function (err, data) {
81234               if (err || !data || !Object.keys(data).length) return; // If graph has changed, we can't apply this update.
81235
81236               if (context.graph() !== initGraph) return;
81237               var qids = Object.keys(data);
81238               var value = qids && qids.find(function (id) {
81239                 return id.match(/^Q\d+$/);
81240               });
81241               var actions = initEntityIDs.map(function (entityID) {
81242                 var entity = context.entity(entityID).tags;
81243                 var currTags = Object.assign({}, entity); // shallow copy
81244
81245                 if (currTags.wikidata !== value) {
81246                   currTags.wikidata = value;
81247                   return actionChangeTags(entityID, currTags);
81248                 }
81249
81250                 return null;
81251               }).filter(Boolean);
81252               if (!actions.length) return; // Coalesce the update of wikidata tag into the previous tag change
81253
81254               context.overwrite(function actionUpdateWikidataTags(graph) {
81255                 actions.forEach(function (action) {
81256                   graph = action(graph);
81257                 });
81258                 return graph;
81259               }, context.history().undoAnnotation()); // do not dispatch.call('change') here, because entity_editor
81260               // changeTags() is not intended to be called asynchronously
81261             });
81262           }
81263
81264           wiki.tags = function (tags) {
81265             _tags = tags;
81266             updateForTags(tags);
81267           };
81268
81269           function updateForTags(tags) {
81270             var value = typeof tags[field.key] === 'string' ? tags[field.key] : ''; // Expect tag format of `tagLang:tagArticleTitle`, e.g. `fr:Paris`, with
81271             // optional suffix of `#anchor`
81272
81273             var m = value.match(/([^:]+):([^#]+)(?:#(.+))?/);
81274             var tagLang = m && m[1];
81275             var tagArticleTitle = m && m[2];
81276             var anchor = m && m[3];
81277
81278             var tagLangInfo = tagLang && _dataWikipedia.find(function (d) {
81279               return tagLang === d[2];
81280             }); // value in correct format
81281
81282
81283             if (tagLangInfo) {
81284               var nativeLangName = tagLangInfo[1];
81285               utilGetSetValue(_langInput, nativeLangName);
81286               utilGetSetValue(_titleInput, tagArticleTitle + (anchor ? '#' + anchor : ''));
81287
81288               if (anchor) {
81289                 try {
81290                   // Best-effort `anchorencode:` implementation
81291                   anchor = encodeURIComponent(anchor.replace(/ /g, '_')).replace(/%/g, '.');
81292                 } catch (e) {
81293                   anchor = anchor.replace(/ /g, '_');
81294                 }
81295               }
81296
81297               _wikiURL = 'https://' + tagLang + '.wikipedia.org/wiki/' + tagArticleTitle.replace(/ /g, '_') + (anchor ? '#' + anchor : ''); // unrecognized value format
81298             } else {
81299               utilGetSetValue(_titleInput, value);
81300
81301               if (value && value !== '') {
81302                 utilGetSetValue(_langInput, '');
81303                 var defaultLangInfo = defaultLanguageInfo();
81304                 _wikiURL = "https://".concat(defaultLangInfo[2], ".wikipedia.org/w/index.php?fulltext=1&search=").concat(value);
81305               } else {
81306                 var shownOrDefaultLangInfo = language(true
81307                 /* skipEnglishFallback */
81308                 );
81309                 utilGetSetValue(_langInput, shownOrDefaultLangInfo[1]);
81310                 _wikiURL = '';
81311               }
81312             }
81313           }
81314
81315           wiki.entityIDs = function (val) {
81316             if (!_arguments.length) return _entityIDs;
81317             _entityIDs = val;
81318             return wiki;
81319           };
81320
81321           wiki.focus = function () {
81322             _titleInput.node().focus();
81323           };
81324
81325           return utilRebind(wiki, dispatch, 'on');
81326         }
81327         uiFieldWikipedia.supportsMultiselection = false;
81328
81329         var uiFields = {
81330           access: uiFieldAccess,
81331           address: uiFieldAddress,
81332           check: uiFieldCheck,
81333           combo: uiFieldCombo,
81334           cycleway: uiFieldCycleway,
81335           defaultCheck: uiFieldCheck,
81336           email: uiFieldText,
81337           identifier: uiFieldText,
81338           lanes: uiFieldLanes,
81339           localized: uiFieldLocalized,
81340           roadspeed: uiFieldRoadspeed,
81341           roadheight: uiFieldText,
81342           manyCombo: uiFieldCombo,
81343           multiCombo: uiFieldCombo,
81344           networkCombo: uiFieldCombo,
81345           number: uiFieldText,
81346           onewayCheck: uiFieldCheck,
81347           radio: uiFieldRadio,
81348           restrictions: uiFieldRestrictions,
81349           semiCombo: uiFieldCombo,
81350           structureRadio: uiFieldRadio,
81351           tel: uiFieldText,
81352           text: uiFieldText,
81353           textarea: uiFieldTextarea,
81354           typeCombo: uiFieldCombo,
81355           url: uiFieldText,
81356           wikidata: uiFieldWikidata,
81357           wikipedia: uiFieldWikipedia
81358         };
81359
81360         function uiField(context, presetField, entityIDs, options) {
81361           options = Object.assign({
81362             show: true,
81363             wrap: true,
81364             remove: true,
81365             revert: true,
81366             info: true
81367           }, options);
81368           var dispatch = dispatch$8('change', 'revert');
81369           var field = Object.assign({}, presetField); // shallow copy
81370
81371           field.domId = utilUniqueDomId('form-field-' + field.safeid);
81372           var _show = options.show;
81373           var _state = '';
81374           var _tags = {};
81375
81376           var _entityExtent;
81377
81378           if (entityIDs && entityIDs.length) {
81379             _entityExtent = entityIDs.reduce(function (extent, entityID) {
81380               var entity = context.graph().entity(entityID);
81381               return extent.extend(entity.extent(context.graph()));
81382             }, geoExtent());
81383           }
81384
81385           var _locked = false;
81386
81387           var _lockedTip = uiTooltip().title(_t.html('inspector.lock.suggestion', {
81388             label: field.label
81389           })).placement('bottom');
81390
81391           field.keys = field.keys || [field.key]; // only create the fields that are actually being shown
81392
81393           if (_show && !field.impl) {
81394             createField();
81395           } // Creates the field.. This is done lazily,
81396           // once we know that the field will be shown.
81397
81398
81399           function createField() {
81400             field.impl = uiFields[field.type](field, context).on('change', function (t, onInput) {
81401               dispatch.call('change', field, t, onInput);
81402             });
81403
81404             if (entityIDs) {
81405               field.entityIDs = entityIDs; // if this field cares about the entities, pass them along
81406
81407               if (field.impl.entityIDs) {
81408                 field.impl.entityIDs(entityIDs);
81409               }
81410             }
81411           }
81412
81413           function isModified() {
81414             if (!entityIDs || !entityIDs.length) return false;
81415             return entityIDs.some(function (entityID) {
81416               var original = context.graph().base().entities[entityID];
81417               var latest = context.graph().entity(entityID);
81418               return field.keys.some(function (key) {
81419                 return original ? latest.tags[key] !== original.tags[key] : latest.tags[key];
81420               });
81421             });
81422           }
81423
81424           function tagsContainFieldKey() {
81425             return field.keys.some(function (key) {
81426               if (field.type === 'multiCombo') {
81427                 for (var tagKey in _tags) {
81428                   if (tagKey.indexOf(key) === 0) {
81429                     return true;
81430                   }
81431                 }
81432
81433                 return false;
81434               }
81435
81436               return _tags[key] !== undefined;
81437             });
81438           }
81439
81440           function revert(d3_event, d) {
81441             d3_event.stopPropagation();
81442             d3_event.preventDefault();
81443             if (!entityIDs || _locked) return;
81444             dispatch.call('revert', d, d.keys);
81445           }
81446
81447           function remove(d3_event, d) {
81448             d3_event.stopPropagation();
81449             d3_event.preventDefault();
81450             if (_locked) return;
81451             var t = {};
81452             d.keys.forEach(function (key) {
81453               t[key] = undefined;
81454             });
81455             dispatch.call('change', d, t);
81456           }
81457
81458           field.render = function (selection) {
81459             var container = selection.selectAll('.form-field').data([field]); // Enter
81460
81461             var enter = container.enter().append('div').attr('class', function (d) {
81462               return 'form-field form-field-' + d.safeid;
81463             }).classed('nowrap', !options.wrap);
81464
81465             if (options.wrap) {
81466               var labelEnter = enter.append('label').attr('class', 'field-label').attr('for', function (d) {
81467                 return d.domId;
81468               });
81469               var textEnter = labelEnter.append('span').attr('class', 'label-text');
81470               textEnter.append('span').attr('class', 'label-textvalue').html(function (d) {
81471                 return d.label();
81472               });
81473               textEnter.append('span').attr('class', 'label-textannotation');
81474
81475               if (options.remove) {
81476                 labelEnter.append('button').attr('class', 'remove-icon').attr('title', _t('icons.remove')).call(svgIcon('#iD-operation-delete'));
81477               }
81478
81479               if (options.revert) {
81480                 labelEnter.append('button').attr('class', 'modified-icon').attr('title', _t('icons.undo')).call(svgIcon(_mainLocalizer.textDirection() === 'rtl' ? '#iD-icon-redo' : '#iD-icon-undo'));
81481               }
81482             } // Update
81483
81484
81485             container = container.merge(enter);
81486             container.select('.field-label > .remove-icon') // propagate bound data
81487             .on('click', remove);
81488             container.select('.field-label > .modified-icon') // propagate bound data
81489             .on('click', revert);
81490             container.each(function (d) {
81491               var selection = select(this);
81492
81493               if (!d.impl) {
81494                 createField();
81495               }
81496
81497               var reference, help; // instantiate field help
81498
81499               if (options.wrap && field.type === 'restrictions') {
81500                 help = uiFieldHelp(context, 'restrictions');
81501               } // instantiate tag reference
81502
81503
81504               if (options.wrap && options.info) {
81505                 var referenceKey = d.key || '';
81506
81507                 if (d.type === 'multiCombo') {
81508                   // lookup key without the trailing ':'
81509                   referenceKey = referenceKey.replace(/:$/, '');
81510                 }
81511
81512                 reference = uiTagReference(d.reference || {
81513                   key: referenceKey
81514                 });
81515
81516                 if (_state === 'hover') {
81517                   reference.showing(false);
81518                 }
81519               }
81520
81521               selection.call(d.impl); // add field help components
81522
81523               if (help) {
81524                 selection.call(help.body).select('.field-label').call(help.button);
81525               } // add tag reference components
81526
81527
81528               if (reference) {
81529                 selection.call(reference.body).select('.field-label').call(reference.button);
81530               }
81531
81532               d.impl.tags(_tags);
81533             });
81534             container.classed('locked', _locked).classed('modified', isModified()).classed('present', tagsContainFieldKey()); // show a tip and lock icon if the field is locked
81535
81536             var annotation = container.selectAll('.field-label .label-textannotation');
81537             var icon = annotation.selectAll('.icon').data(_locked ? [0] : []);
81538             icon.exit().remove();
81539             icon.enter().append('svg').attr('class', 'icon').append('use').attr('xlink:href', '#fas-lock');
81540             container.call(_locked ? _lockedTip : _lockedTip.destroy);
81541           };
81542
81543           field.state = function (val) {
81544             if (!arguments.length) return _state;
81545             _state = val;
81546             return field;
81547           };
81548
81549           field.tags = function (val) {
81550             if (!arguments.length) return _tags;
81551             _tags = val;
81552
81553             if (tagsContainFieldKey() && !_show) {
81554               // always show a field if it has a value to display
81555               _show = true;
81556
81557               if (!field.impl) {
81558                 createField();
81559               }
81560             }
81561
81562             return field;
81563           };
81564
81565           field.locked = function (val) {
81566             if (!arguments.length) return _locked;
81567             _locked = val;
81568             return field;
81569           };
81570
81571           field.show = function () {
81572             _show = true;
81573
81574             if (!field.impl) {
81575               createField();
81576             }
81577
81578             if (field["default"] && field.key && _tags[field.key] !== field["default"]) {
81579               var t = {};
81580               t[field.key] = field["default"];
81581               dispatch.call('change', this, t);
81582             }
81583           }; // A shown field has a visible UI, a non-shown field is in the 'Add field' dropdown
81584
81585
81586           field.isShown = function () {
81587             return _show;
81588           }; // An allowed field can appear in the UI or in the 'Add field' dropdown.
81589           // A non-allowed field is hidden from the user altogether
81590
81591
81592           field.isAllowed = function () {
81593             if (entityIDs && entityIDs.length > 1 && uiFields[field.type].supportsMultiselection === false) return false;
81594             if (field.geometry && !entityIDs.every(function (entityID) {
81595               return field.matchGeometry(context.graph().geometry(entityID));
81596             })) return false;
81597
81598             if (entityIDs && _entityExtent && field.locationSetID) {
81599               // is field allowed in this location?
81600               var validLocations = _mainLocations.locationsAt(_entityExtent.center());
81601               if (!validLocations[field.locationSetID]) return false;
81602             }
81603
81604             var prerequisiteTag = field.prerequisiteTag;
81605
81606             if (entityIDs && !tagsContainFieldKey() && // ignore tagging prerequisites if a value is already present
81607             prerequisiteTag) {
81608               if (!entityIDs.every(function (entityID) {
81609                 var entity = context.graph().entity(entityID);
81610
81611                 if (prerequisiteTag.key) {
81612                   var value = entity.tags[prerequisiteTag.key];
81613                   if (!value) return false;
81614
81615                   if (prerequisiteTag.valueNot) {
81616                     return prerequisiteTag.valueNot !== value;
81617                   }
81618
81619                   if (prerequisiteTag.value) {
81620                     return prerequisiteTag.value === value;
81621                   }
81622                 } else if (prerequisiteTag.keyNot) {
81623                   if (entity.tags[prerequisiteTag.keyNot]) return false;
81624                 }
81625
81626                 return true;
81627               })) return false;
81628             }
81629
81630             return true;
81631           };
81632
81633           field.focus = function () {
81634             if (field.impl) {
81635               field.impl.focus();
81636             }
81637           };
81638
81639           return utilRebind(field, dispatch, 'on');
81640         }
81641
81642         function uiFormFields(context) {
81643           var moreCombo = uiCombobox(context, 'more-fields').minItems(1);
81644           var _fieldsArr = [];
81645           var _lastPlaceholder = '';
81646           var _state = '';
81647           var _klass = '';
81648
81649           function formFields(selection) {
81650             var allowedFields = _fieldsArr.filter(function (field) {
81651               return field.isAllowed();
81652             });
81653
81654             var shown = allowedFields.filter(function (field) {
81655               return field.isShown();
81656             });
81657             var notShown = allowedFields.filter(function (field) {
81658               return !field.isShown();
81659             });
81660             var container = selection.selectAll('.form-fields-container').data([0]);
81661             container = container.enter().append('div').attr('class', 'form-fields-container ' + (_klass || '')).merge(container);
81662             var fields = container.selectAll('.wrap-form-field').data(shown, function (d) {
81663               return d.id + (d.entityIDs ? d.entityIDs.join() : '');
81664             });
81665             fields.exit().remove(); // Enter
81666
81667             var enter = fields.enter().append('div').attr('class', function (d) {
81668               return 'wrap-form-field wrap-form-field-' + d.safeid;
81669             }); // Update
81670
81671             fields = fields.merge(enter);
81672             fields.order().each(function (d) {
81673               select(this).call(d.render);
81674             });
81675             var titles = [];
81676             var moreFields = notShown.map(function (field) {
81677               var title = field.title();
81678               titles.push(title);
81679               var terms = field.terms();
81680               if (field.key) terms.push(field.key);
81681               if (field.keys) terms = terms.concat(field.keys);
81682               return {
81683                 display: field.label(),
81684                 value: title,
81685                 title: title,
81686                 field: field,
81687                 terms: terms
81688               };
81689             });
81690             var placeholder = titles.slice(0, 3).join(', ') + (titles.length > 3 ? '…' : '');
81691             var more = selection.selectAll('.more-fields').data(_state === 'hover' || moreFields.length === 0 ? [] : [0]);
81692             more.exit().remove();
81693             var moreEnter = more.enter().append('div').attr('class', 'more-fields').append('label');
81694             moreEnter.append('span').html(_t.html('inspector.add_fields'));
81695             more = moreEnter.merge(more);
81696             var input = more.selectAll('.value').data([0]);
81697             input.exit().remove();
81698             input = input.enter().append('input').attr('class', 'value').attr('type', 'text').attr('placeholder', placeholder).call(utilNoAuto).merge(input);
81699             input.call(utilGetSetValue, '').call(moreCombo.data(moreFields).on('accept', function (d) {
81700               if (!d) return; // user entered something that was not matched
81701
81702               var field = d.field;
81703               field.show();
81704               selection.call(formFields); // rerender
81705
81706               field.focus();
81707             })); // avoid updating placeholder excessively (triggers style recalc)
81708
81709             if (_lastPlaceholder !== placeholder) {
81710               input.attr('placeholder', placeholder);
81711               _lastPlaceholder = placeholder;
81712             }
81713           }
81714
81715           formFields.fieldsArr = function (val) {
81716             if (!arguments.length) return _fieldsArr;
81717             _fieldsArr = val || [];
81718             return formFields;
81719           };
81720
81721           formFields.state = function (val) {
81722             if (!arguments.length) return _state;
81723             _state = val;
81724             return formFields;
81725           };
81726
81727           formFields.klass = function (val) {
81728             if (!arguments.length) return _klass;
81729             _klass = val;
81730             return formFields;
81731           };
81732
81733           return formFields;
81734         }
81735
81736         function uiSectionPresetFields(context) {
81737           var section = uiSection('preset-fields', context).label(_t.html('inspector.fields')).disclosureContent(renderDisclosureContent);
81738           var dispatch = dispatch$8('change', 'revert');
81739           var formFields = uiFormFields(context);
81740
81741           var _state;
81742
81743           var _fieldsArr;
81744
81745           var _presets = [];
81746
81747           var _tags;
81748
81749           var _entityIDs;
81750
81751           function renderDisclosureContent(selection) {
81752             if (!_fieldsArr) {
81753               var graph = context.graph();
81754               var geometries = Object.keys(_entityIDs.reduce(function (geoms, entityID) {
81755                 geoms[graph.entity(entityID).geometry(graph)] = true;
81756                 return geoms;
81757               }, {}));
81758               var presetsManager = _mainPresetIndex;
81759               var allFields = [];
81760               var allMoreFields = [];
81761               var sharedTotalFields;
81762
81763               _presets.forEach(function (preset) {
81764                 var fields = preset.fields();
81765                 var moreFields = preset.moreFields();
81766                 allFields = utilArrayUnion(allFields, fields);
81767                 allMoreFields = utilArrayUnion(allMoreFields, moreFields);
81768
81769                 if (!sharedTotalFields) {
81770                   sharedTotalFields = utilArrayUnion(fields, moreFields);
81771                 } else {
81772                   sharedTotalFields = sharedTotalFields.filter(function (field) {
81773                     return fields.indexOf(field) !== -1 || moreFields.indexOf(field) !== -1;
81774                   });
81775                 }
81776               });
81777
81778               var sharedFields = allFields.filter(function (field) {
81779                 return sharedTotalFields.indexOf(field) !== -1;
81780               });
81781               var sharedMoreFields = allMoreFields.filter(function (field) {
81782                 return sharedTotalFields.indexOf(field) !== -1;
81783               });
81784               _fieldsArr = [];
81785               sharedFields.forEach(function (field) {
81786                 if (field.matchAllGeometry(geometries)) {
81787                   _fieldsArr.push(uiField(context, field, _entityIDs));
81788                 }
81789               });
81790               var singularEntity = _entityIDs.length === 1 && graph.hasEntity(_entityIDs[0]);
81791
81792               if (singularEntity && singularEntity.isHighwayIntersection(graph) && presetsManager.field('restrictions')) {
81793                 _fieldsArr.push(uiField(context, presetsManager.field('restrictions'), _entityIDs));
81794               }
81795
81796               var additionalFields = utilArrayUnion(sharedMoreFields, presetsManager.universal());
81797               additionalFields.sort(function (field1, field2) {
81798                 return field1.label().localeCompare(field2.label(), _mainLocalizer.localeCode());
81799               });
81800               additionalFields.forEach(function (field) {
81801                 if (sharedFields.indexOf(field) === -1 && field.matchAllGeometry(geometries)) {
81802                   _fieldsArr.push(uiField(context, field, _entityIDs, {
81803                     show: false
81804                   }));
81805                 }
81806               });
81807
81808               _fieldsArr.forEach(function (field) {
81809                 field.on('change', function (t, onInput) {
81810                   dispatch.call('change', field, _entityIDs, t, onInput);
81811                 }).on('revert', function (keys) {
81812                   dispatch.call('revert', field, keys);
81813                 });
81814               });
81815             }
81816
81817             _fieldsArr.forEach(function (field) {
81818               field.state(_state).tags(_tags);
81819             });
81820
81821             selection.call(formFields.fieldsArr(_fieldsArr).state(_state).klass('grouped-items-area'));
81822             selection.selectAll('.wrap-form-field input').on('keydown', function (d3_event) {
81823               // if user presses enter, and combobox is not active, accept edits..
81824               if (d3_event.keyCode === 13 && // ↩ Return
81825               context.container().select('.combobox').empty()) {
81826                 context.enter(modeBrowse(context));
81827               }
81828             });
81829           }
81830
81831           section.presets = function (val) {
81832             if (!arguments.length) return _presets;
81833
81834             if (!_presets || !val || !utilArrayIdentical(_presets, val)) {
81835               _presets = val;
81836               _fieldsArr = null;
81837             }
81838
81839             return section;
81840           };
81841
81842           section.state = function (val) {
81843             if (!arguments.length) return _state;
81844             _state = val;
81845             return section;
81846           };
81847
81848           section.tags = function (val) {
81849             if (!arguments.length) return _tags;
81850             _tags = val; // Don't reset _fieldsArr here.
81851
81852             return section;
81853           };
81854
81855           section.entityIDs = function (val) {
81856             if (!arguments.length) return _entityIDs;
81857
81858             if (!val || !_entityIDs || !utilArrayIdentical(_entityIDs, val)) {
81859               _entityIDs = val;
81860               _fieldsArr = null;
81861             }
81862
81863             return section;
81864           };
81865
81866           return utilRebind(section, dispatch, 'on');
81867         }
81868
81869         function uiSectionRawMemberEditor(context) {
81870           var section = uiSection('raw-member-editor', context).shouldDisplay(function () {
81871             if (!_entityIDs || _entityIDs.length !== 1) return false;
81872             var entity = context.hasEntity(_entityIDs[0]);
81873             return entity && entity.type === 'relation';
81874           }).label(function () {
81875             var entity = context.hasEntity(_entityIDs[0]);
81876             if (!entity) return '';
81877             var gt = entity.members.length > _maxMembers ? '>' : '';
81878             var count = gt + entity.members.slice(0, _maxMembers).length;
81879             return _t('inspector.title_count', {
81880               title: _t.html('inspector.members'),
81881               count: count
81882             });
81883           }).disclosureContent(renderDisclosureContent);
81884           var taginfo = services.taginfo;
81885
81886           var _entityIDs;
81887
81888           var _maxMembers = 1000;
81889
81890           function downloadMember(d3_event, d) {
81891             d3_event.preventDefault(); // display the loading indicator
81892
81893             select(this.parentNode).classed('tag-reference-loading', true);
81894             context.loadEntity(d.id, function () {
81895               section.reRender();
81896             });
81897           }
81898
81899           function zoomToMember(d3_event, d) {
81900             d3_event.preventDefault();
81901             var entity = context.entity(d.id);
81902             context.map().zoomToEase(entity); // highlight the feature in case it wasn't previously on-screen
81903
81904             utilHighlightEntities([d.id], true, context);
81905           }
81906
81907           function selectMember(d3_event, d) {
81908             d3_event.preventDefault(); // remove the hover-highlight styling
81909
81910             utilHighlightEntities([d.id], false, context);
81911             var entity = context.entity(d.id);
81912             var mapExtent = context.map().extent();
81913
81914             if (!entity.intersects(mapExtent, context.graph())) {
81915               // zoom to the entity if its extent is not visible now
81916               context.map().zoomToEase(entity);
81917             }
81918
81919             context.enter(modeSelect(context, [d.id]));
81920           }
81921
81922           function changeRole(d3_event, d) {
81923             var oldRole = d.role;
81924             var newRole = context.cleanRelationRole(select(this).property('value'));
81925
81926             if (oldRole !== newRole) {
81927               var member = {
81928                 id: d.id,
81929                 type: d.type,
81930                 role: newRole
81931               };
81932               context.perform(actionChangeMember(d.relation.id, member, d.index), _t('operations.change_role.annotation', {
81933                 n: 1
81934               }));
81935               context.validator().validate();
81936             }
81937           }
81938
81939           function deleteMember(d3_event, d) {
81940             // remove the hover-highlight styling
81941             utilHighlightEntities([d.id], false, context);
81942             context.perform(actionDeleteMember(d.relation.id, d.index), _t('operations.delete_member.annotation', {
81943               n: 1
81944             }));
81945
81946             if (!context.hasEntity(d.relation.id)) {
81947               // Removing the last member will also delete the relation.
81948               // If this happens we need to exit the selection mode
81949               context.enter(modeBrowse(context));
81950             } else {
81951               // Changing the mode also runs `validate`, but otherwise we need to
81952               // rerun it manually
81953               context.validator().validate();
81954             }
81955           }
81956
81957           function renderDisclosureContent(selection) {
81958             var entityID = _entityIDs[0];
81959             var memberships = [];
81960             var entity = context.entity(entityID);
81961             entity.members.slice(0, _maxMembers).forEach(function (member, index) {
81962               memberships.push({
81963                 index: index,
81964                 id: member.id,
81965                 type: member.type,
81966                 role: member.role,
81967                 relation: entity,
81968                 member: context.hasEntity(member.id),
81969                 domId: utilUniqueDomId(entityID + '-member-' + index)
81970               });
81971             });
81972             var list = selection.selectAll('.member-list').data([0]);
81973             list = list.enter().append('ul').attr('class', 'member-list').merge(list);
81974             var items = list.selectAll('li').data(memberships, function (d) {
81975               return osmEntity.key(d.relation) + ',' + d.index + ',' + (d.member ? osmEntity.key(d.member) : 'incomplete');
81976             });
81977             items.exit().each(unbind).remove();
81978             var itemsEnter = items.enter().append('li').attr('class', 'member-row form-field').classed('member-incomplete', function (d) {
81979               return !d.member;
81980             });
81981             itemsEnter.each(function (d) {
81982               var item = select(this);
81983               var label = item.append('label').attr('class', 'field-label').attr('for', d.domId);
81984
81985               if (d.member) {
81986                 // highlight the member feature in the map while hovering on the list item
81987                 item.on('mouseover', function () {
81988                   utilHighlightEntities([d.id], true, context);
81989                 }).on('mouseout', function () {
81990                   utilHighlightEntities([d.id], false, context);
81991                 });
81992                 var labelLink = label.append('span').attr('class', 'label-text').append('a').attr('href', '#').on('click', selectMember);
81993                 labelLink.append('span').attr('class', 'member-entity-type').html(function (d) {
81994                   var matched = _mainPresetIndex.match(d.member, context.graph());
81995                   return matched && matched.name() || utilDisplayType(d.member.id);
81996                 });
81997                 labelLink.append('span').attr('class', 'member-entity-name').html(function (d) {
81998                   return utilDisplayName(d.member);
81999                 });
82000                 label.append('button').attr('title', _t('icons.remove')).attr('class', 'remove member-delete').call(svgIcon('#iD-operation-delete'));
82001                 label.append('button').attr('class', 'member-zoom').attr('title', _t('icons.zoom_to')).call(svgIcon('#iD-icon-framed-dot', 'monochrome')).on('click', zoomToMember);
82002               } else {
82003                 var labelText = label.append('span').attr('class', 'label-text');
82004                 labelText.append('span').attr('class', 'member-entity-type').html(_t.html('inspector.' + d.type, {
82005                   id: d.id
82006                 }));
82007                 labelText.append('span').attr('class', 'member-entity-name').html(_t.html('inspector.incomplete', {
82008                   id: d.id
82009                 }));
82010                 label.append('button').attr('class', 'member-download').attr('title', _t('icons.download')).call(svgIcon('#iD-icon-load')).on('click', downloadMember);
82011               }
82012             });
82013             var wrapEnter = itemsEnter.append('div').attr('class', 'form-field-input-wrap form-field-input-member');
82014             wrapEnter.append('input').attr('class', 'member-role').attr('id', function (d) {
82015               return d.domId;
82016             }).property('type', 'text').attr('placeholder', _t('inspector.role')).call(utilNoAuto);
82017
82018             if (taginfo) {
82019               wrapEnter.each(bindTypeahead);
82020             } // update
82021
82022
82023             items = items.merge(itemsEnter).order();
82024             items.select('input.member-role').property('value', function (d) {
82025               return d.role;
82026             }).on('blur', changeRole).on('change', changeRole);
82027             items.select('button.member-delete').on('click', deleteMember);
82028             var dragOrigin, targetIndex;
82029             items.call(d3_drag().on('start', function (d3_event) {
82030               dragOrigin = {
82031                 x: d3_event.x,
82032                 y: d3_event.y
82033               };
82034               targetIndex = null;
82035             }).on('drag', function (d3_event) {
82036               var x = d3_event.x - dragOrigin.x,
82037                   y = d3_event.y - dragOrigin.y;
82038               if (!select(this).classed('dragging') && // don't display drag until dragging beyond a distance threshold
82039               Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2)) <= 5) return;
82040               var index = items.nodes().indexOf(this);
82041               select(this).classed('dragging', true);
82042               targetIndex = null;
82043               selection.selectAll('li.member-row').style('transform', function (d2, index2) {
82044                 var node = select(this).node();
82045
82046                 if (index === index2) {
82047                   return 'translate(' + x + 'px, ' + y + 'px)';
82048                 } else if (index2 > index && d3_event.y > node.offsetTop) {
82049                   if (targetIndex === null || index2 > targetIndex) {
82050                     targetIndex = index2;
82051                   }
82052
82053                   return 'translateY(-100%)';
82054                 } else if (index2 < index && d3_event.y < node.offsetTop + node.offsetHeight) {
82055                   if (targetIndex === null || index2 < targetIndex) {
82056                     targetIndex = index2;
82057                   }
82058
82059                   return 'translateY(100%)';
82060                 }
82061
82062                 return null;
82063               });
82064             }).on('end', function (d3_event, d) {
82065               if (!select(this).classed('dragging')) return;
82066               var index = items.nodes().indexOf(this);
82067               select(this).classed('dragging', false);
82068               selection.selectAll('li.member-row').style('transform', null);
82069
82070               if (targetIndex !== null) {
82071                 // dragged to a new position, reorder
82072                 context.perform(actionMoveMember(d.relation.id, index, targetIndex), _t('operations.reorder_members.annotation'));
82073                 context.validator().validate();
82074               }
82075             }));
82076
82077             function bindTypeahead(d) {
82078               var row = select(this);
82079               var role = row.selectAll('input.member-role');
82080               var origValue = role.property('value');
82081
82082               function sort(value, data) {
82083                 var sameletter = [];
82084                 var other = [];
82085
82086                 for (var i = 0; i < data.length; i++) {
82087                   if (data[i].value.substring(0, value.length) === value) {
82088                     sameletter.push(data[i]);
82089                   } else {
82090                     other.push(data[i]);
82091                   }
82092                 }
82093
82094                 return sameletter.concat(other);
82095               }
82096
82097               role.call(uiCombobox(context, 'member-role').fetcher(function (role, callback) {
82098                 // The `geometry` param is used in the `taginfo.js` interface for
82099                 // filtering results, as a key into the `tag_members_fractions`
82100                 // object.  If we don't know the geometry because the member is
82101                 // not yet downloaded, it's ok to guess based on type.
82102                 var geometry;
82103
82104                 if (d.member) {
82105                   geometry = context.graph().geometry(d.member.id);
82106                 } else if (d.type === 'relation') {
82107                   geometry = 'relation';
82108                 } else if (d.type === 'way') {
82109                   geometry = 'line';
82110                 } else {
82111                   geometry = 'point';
82112                 }
82113
82114                 var rtype = entity.tags.type;
82115                 taginfo.roles({
82116                   debounce: true,
82117                   rtype: rtype || '',
82118                   geometry: geometry,
82119                   query: role
82120                 }, function (err, data) {
82121                   if (!err) callback(sort(role, data));
82122                 });
82123               }).on('cancel', function () {
82124                 role.property('value', origValue);
82125               }));
82126             }
82127
82128             function unbind() {
82129               var row = select(this);
82130               row.selectAll('input.member-role').call(uiCombobox.off, context);
82131             }
82132           }
82133
82134           section.entityIDs = function (val) {
82135             if (!arguments.length) return _entityIDs;
82136             _entityIDs = val;
82137             return section;
82138           };
82139
82140           return section;
82141         }
82142
82143         function actionDeleteMembers(relationId, memberIndexes) {
82144           return function (graph) {
82145             // Remove the members in descending order so removals won't shift what members
82146             // are at the remaining indexes
82147             memberIndexes.sort(function (a, b) {
82148               return b - a;
82149             });
82150
82151             for (var i in memberIndexes) {
82152               graph = actionDeleteMember(relationId, memberIndexes[i])(graph);
82153             }
82154
82155             return graph;
82156           };
82157         }
82158
82159         function uiSectionRawMembershipEditor(context) {
82160           var section = uiSection('raw-membership-editor', context).shouldDisplay(function () {
82161             return _entityIDs && _entityIDs.length;
82162           }).label(function () {
82163             var parents = getSharedParentRelations();
82164             var gt = parents.length > _maxMemberships ? '>' : '';
82165             var count = gt + parents.slice(0, _maxMemberships).length;
82166             return _t('inspector.title_count', {
82167               title: _t.html('inspector.relations'),
82168               count: count
82169             });
82170           }).disclosureContent(renderDisclosureContent);
82171           var taginfo = services.taginfo;
82172           var nearbyCombo = uiCombobox(context, 'parent-relation').minItems(1).fetcher(fetchNearbyRelations).itemsMouseEnter(function (d3_event, d) {
82173             if (d.relation) utilHighlightEntities([d.relation.id], true, context);
82174           }).itemsMouseLeave(function (d3_event, d) {
82175             if (d.relation) utilHighlightEntities([d.relation.id], false, context);
82176           });
82177           var _inChange = false;
82178           var _entityIDs = [];
82179
82180           var _showBlank;
82181
82182           var _maxMemberships = 1000;
82183
82184           function getSharedParentRelations() {
82185             var parents = [];
82186
82187             for (var i = 0; i < _entityIDs.length; i++) {
82188               var entity = context.graph().hasEntity(_entityIDs[i]);
82189               if (!entity) continue;
82190
82191               if (i === 0) {
82192                 parents = context.graph().parentRelations(entity);
82193               } else {
82194                 parents = utilArrayIntersection(parents, context.graph().parentRelations(entity));
82195               }
82196
82197               if (!parents.length) break;
82198             }
82199
82200             return parents;
82201           }
82202
82203           function getMemberships() {
82204             var memberships = [];
82205             var relations = getSharedParentRelations().slice(0, _maxMemberships);
82206             var isMultiselect = _entityIDs.length > 1;
82207             var i, relation, membership, index, member, indexedMember;
82208
82209             for (i = 0; i < relations.length; i++) {
82210               relation = relations[i];
82211               membership = {
82212                 relation: relation,
82213                 members: [],
82214                 hash: osmEntity.key(relation)
82215               };
82216
82217               for (index = 0; index < relation.members.length; index++) {
82218                 member = relation.members[index];
82219
82220                 if (_entityIDs.indexOf(member.id) !== -1) {
82221                   indexedMember = Object.assign({}, member, {
82222                     index: index
82223                   });
82224                   membership.members.push(indexedMember);
82225                   membership.hash += ',' + index.toString();
82226
82227                   if (!isMultiselect) {
82228                     // For single selections, list one entry per membership per relation.
82229                     // For multiselections, list one entry per relation.
82230                     memberships.push(membership);
82231                     membership = {
82232                       relation: relation,
82233                       members: [],
82234                       hash: osmEntity.key(relation)
82235                     };
82236                   }
82237                 }
82238               }
82239
82240               if (membership.members.length) memberships.push(membership);
82241             }
82242
82243             memberships.forEach(function (membership) {
82244               membership.domId = utilUniqueDomId('membership-' + membership.relation.id);
82245               var roles = [];
82246               membership.members.forEach(function (member) {
82247                 if (roles.indexOf(member.role) === -1) roles.push(member.role);
82248               });
82249               membership.role = roles.length === 1 ? roles[0] : roles;
82250             });
82251             return memberships;
82252           }
82253
82254           function selectRelation(d3_event, d) {
82255             d3_event.preventDefault(); // remove the hover-highlight styling
82256
82257             utilHighlightEntities([d.relation.id], false, context);
82258             context.enter(modeSelect(context, [d.relation.id]));
82259           }
82260
82261           function zoomToRelation(d3_event, d) {
82262             d3_event.preventDefault();
82263             var entity = context.entity(d.relation.id);
82264             context.map().zoomToEase(entity); // highlight the relation in case it wasn't previously on-screen
82265
82266             utilHighlightEntities([d.relation.id], true, context);
82267           }
82268
82269           function changeRole(d3_event, d) {
82270             if (d === 0) return; // called on newrow (shouldn't happen)
82271
82272             if (_inChange) return; // avoid accidental recursive call #5731
82273
82274             var newRole = context.cleanRelationRole(select(this).property('value'));
82275             if (!newRole.trim() && typeof d.role !== 'string') return;
82276             var membersToUpdate = d.members.filter(function (member) {
82277               return member.role !== newRole;
82278             });
82279
82280             if (membersToUpdate.length) {
82281               _inChange = true;
82282               context.perform(function actionChangeMemberRoles(graph) {
82283                 membersToUpdate.forEach(function (member) {
82284                   var newMember = Object.assign({}, member, {
82285                     role: newRole
82286                   });
82287                   delete newMember.index;
82288                   graph = actionChangeMember(d.relation.id, newMember, member.index)(graph);
82289                 });
82290                 return graph;
82291               }, _t('operations.change_role.annotation', {
82292                 n: membersToUpdate.length
82293               }));
82294               context.validator().validate();
82295             }
82296
82297             _inChange = false;
82298           }
82299
82300           function addMembership(d, role) {
82301             this.blur(); // avoid keeping focus on the button
82302
82303             _showBlank = false;
82304
82305             function actionAddMembers(relationId, ids, role) {
82306               return function (graph) {
82307                 for (var i in ids) {
82308                   var member = {
82309                     id: ids[i],
82310                     type: graph.entity(ids[i]).type,
82311                     role: role
82312                   };
82313                   graph = actionAddMember(relationId, member)(graph);
82314                 }
82315
82316                 return graph;
82317               };
82318             }
82319
82320             if (d.relation) {
82321               context.perform(actionAddMembers(d.relation.id, _entityIDs, role), _t('operations.add_member.annotation', {
82322                 n: _entityIDs.length
82323               }));
82324               context.validator().validate();
82325             } else {
82326               var relation = osmRelation();
82327               context.perform(actionAddEntity(relation), actionAddMembers(relation.id, _entityIDs, role), _t('operations.add.annotation.relation')); // changing the mode also runs `validate`
82328
82329               context.enter(modeSelect(context, [relation.id]).newFeature(true));
82330             }
82331           }
82332
82333           function deleteMembership(d3_event, d) {
82334             this.blur(); // avoid keeping focus on the button
82335
82336             if (d === 0) return; // called on newrow (shouldn't happen)
82337             // remove the hover-highlight styling
82338
82339             utilHighlightEntities([d.relation.id], false, context);
82340             var indexes = d.members.map(function (member) {
82341               return member.index;
82342             });
82343             context.perform(actionDeleteMembers(d.relation.id, indexes), _t('operations.delete_member.annotation', {
82344               n: _entityIDs.length
82345             }));
82346             context.validator().validate();
82347           }
82348
82349           function fetchNearbyRelations(q, callback) {
82350             var newRelation = {
82351               relation: null,
82352               value: _t('inspector.new_relation'),
82353               display: _t.html('inspector.new_relation')
82354             };
82355             var entityID = _entityIDs[0];
82356             var result = [];
82357             var graph = context.graph();
82358
82359             function baseDisplayLabel(entity) {
82360               var matched = _mainPresetIndex.match(entity, graph);
82361               var presetName = matched && matched.name() || _t('inspector.relation');
82362               var entityName = utilDisplayName(entity) || '';
82363               return presetName + ' ' + entityName;
82364             }
82365
82366             var explicitRelation = q && context.hasEntity(q.toLowerCase());
82367
82368             if (explicitRelation && explicitRelation.type === 'relation' && explicitRelation.id !== entityID) {
82369               // loaded relation is specified explicitly, only show that
82370               result.push({
82371                 relation: explicitRelation,
82372                 value: baseDisplayLabel(explicitRelation) + ' ' + explicitRelation.id
82373               });
82374             } else {
82375               context.history().intersects(context.map().extent()).forEach(function (entity) {
82376                 if (entity.type !== 'relation' || entity.id === entityID) return;
82377                 var value = baseDisplayLabel(entity);
82378                 if (q && (value + ' ' + entity.id).toLowerCase().indexOf(q.toLowerCase()) === -1) return;
82379                 result.push({
82380                   relation: entity,
82381                   value: value
82382                 });
82383               });
82384               result.sort(function (a, b) {
82385                 return osmRelation.creationOrder(a.relation, b.relation);
82386               }); // Dedupe identical names by appending relation id - see #2891
82387
82388               var dupeGroups = Object.values(utilArrayGroupBy(result, 'value')).filter(function (v) {
82389                 return v.length > 1;
82390               });
82391               dupeGroups.forEach(function (group) {
82392                 group.forEach(function (obj) {
82393                   obj.value += ' ' + obj.relation.id;
82394                 });
82395               });
82396             }
82397
82398             result.forEach(function (obj) {
82399               obj.title = obj.value;
82400             });
82401             result.unshift(newRelation);
82402             callback(result);
82403           }
82404
82405           function renderDisclosureContent(selection) {
82406             var memberships = getMemberships();
82407             var list = selection.selectAll('.member-list').data([0]);
82408             list = list.enter().append('ul').attr('class', 'member-list').merge(list);
82409             var items = list.selectAll('li.member-row-normal').data(memberships, function (d) {
82410               return d.hash;
82411             });
82412             items.exit().each(unbind).remove(); // Enter
82413
82414             var itemsEnter = items.enter().append('li').attr('class', 'member-row member-row-normal form-field'); // highlight the relation in the map while hovering on the list item
82415
82416             itemsEnter.on('mouseover', function (d3_event, d) {
82417               utilHighlightEntities([d.relation.id], true, context);
82418             }).on('mouseout', function (d3_event, d) {
82419               utilHighlightEntities([d.relation.id], false, context);
82420             });
82421             var labelEnter = itemsEnter.append('label').attr('class', 'field-label').attr('for', function (d) {
82422               return d.domId;
82423             });
82424             var labelLink = labelEnter.append('span').attr('class', 'label-text').append('a').attr('href', '#').on('click', selectRelation);
82425             labelLink.append('span').attr('class', 'member-entity-type').html(function (d) {
82426               var matched = _mainPresetIndex.match(d.relation, context.graph());
82427               return matched && matched.name() || _t('inspector.relation');
82428             });
82429             labelLink.append('span').attr('class', 'member-entity-name').html(function (d) {
82430               return utilDisplayName(d.relation);
82431             });
82432             labelEnter.append('button').attr('class', 'remove member-delete').call(svgIcon('#iD-operation-delete')).on('click', deleteMembership);
82433             labelEnter.append('button').attr('class', 'member-zoom').attr('title', _t('icons.zoom_to')).call(svgIcon('#iD-icon-framed-dot', 'monochrome')).on('click', zoomToRelation);
82434             var wrapEnter = itemsEnter.append('div').attr('class', 'form-field-input-wrap form-field-input-member');
82435             wrapEnter.append('input').attr('class', 'member-role').attr('id', function (d) {
82436               return d.domId;
82437             }).property('type', 'text').property('value', function (d) {
82438               return typeof d.role === 'string' ? d.role : '';
82439             }).attr('title', function (d) {
82440               return Array.isArray(d.role) ? d.role.filter(Boolean).join('\n') : d.role;
82441             }).attr('placeholder', function (d) {
82442               return Array.isArray(d.role) ? _t('inspector.multiple_roles') : _t('inspector.role');
82443             }).classed('mixed', function (d) {
82444               return Array.isArray(d.role);
82445             }).call(utilNoAuto).on('blur', changeRole).on('change', changeRole);
82446
82447             if (taginfo) {
82448               wrapEnter.each(bindTypeahead);
82449             }
82450
82451             var newMembership = list.selectAll('.member-row-new').data(_showBlank ? [0] : []); // Exit
82452
82453             newMembership.exit().remove(); // Enter
82454
82455             var newMembershipEnter = newMembership.enter().append('li').attr('class', 'member-row member-row-new form-field');
82456             var newLabelEnter = newMembershipEnter.append('label').attr('class', 'field-label');
82457             newLabelEnter.append('input').attr('placeholder', _t('inspector.choose_relation')).attr('type', 'text').attr('class', 'member-entity-input').call(utilNoAuto);
82458             newLabelEnter.append('button').attr('class', 'remove member-delete').call(svgIcon('#iD-operation-delete')).on('click', function () {
82459               list.selectAll('.member-row-new').remove();
82460             });
82461             var newWrapEnter = newMembershipEnter.append('div').attr('class', 'form-field-input-wrap form-field-input-member');
82462             newWrapEnter.append('input').attr('class', 'member-role').property('type', 'text').attr('placeholder', _t('inspector.role')).call(utilNoAuto); // Update
82463
82464             newMembership = newMembership.merge(newMembershipEnter);
82465             newMembership.selectAll('.member-entity-input').on('blur', cancelEntity) // if it wasn't accepted normally, cancel it
82466             .call(nearbyCombo.on('accept', acceptEntity).on('cancel', cancelEntity)); // Container for the Add button
82467
82468             var addRow = selection.selectAll('.add-row').data([0]); // enter
82469
82470             var addRowEnter = addRow.enter().append('div').attr('class', 'add-row');
82471             var addRelationButton = addRowEnter.append('button').attr('class', 'add-relation');
82472             addRelationButton.call(svgIcon('#iD-icon-plus', 'light'));
82473             addRelationButton.call(uiTooltip().title(_t.html('inspector.add_to_relation')).placement(_mainLocalizer.textDirection() === 'ltr' ? 'right' : 'left'));
82474             addRowEnter.append('div').attr('class', 'space-value'); // preserve space
82475
82476             addRowEnter.append('div').attr('class', 'space-buttons'); // preserve space
82477             // update
82478
82479             addRow = addRow.merge(addRowEnter);
82480             addRow.select('.add-relation').on('click', function () {
82481               _showBlank = true;
82482               section.reRender();
82483               list.selectAll('.member-entity-input').node().focus();
82484             });
82485
82486             function acceptEntity(d) {
82487               if (!d) {
82488                 cancelEntity();
82489                 return;
82490               } // remove hover-higlighting
82491
82492
82493               if (d.relation) utilHighlightEntities([d.relation.id], false, context);
82494               var role = context.cleanRelationRole(list.selectAll('.member-row-new .member-role').property('value'));
82495               addMembership(d, role);
82496             }
82497
82498             function cancelEntity() {
82499               var input = newMembership.selectAll('.member-entity-input');
82500               input.property('value', ''); // remove hover-higlighting
82501
82502               context.surface().selectAll('.highlighted').classed('highlighted', false);
82503             }
82504
82505             function bindTypeahead(d) {
82506               var row = select(this);
82507               var role = row.selectAll('input.member-role');
82508               var origValue = role.property('value');
82509
82510               function sort(value, data) {
82511                 var sameletter = [];
82512                 var other = [];
82513
82514                 for (var i = 0; i < data.length; i++) {
82515                   if (data[i].value.substring(0, value.length) === value) {
82516                     sameletter.push(data[i]);
82517                   } else {
82518                     other.push(data[i]);
82519                   }
82520                 }
82521
82522                 return sameletter.concat(other);
82523               }
82524
82525               role.call(uiCombobox(context, 'member-role').fetcher(function (role, callback) {
82526                 var rtype = d.relation.tags.type;
82527                 taginfo.roles({
82528                   debounce: true,
82529                   rtype: rtype || '',
82530                   geometry: context.graph().geometry(_entityIDs[0]),
82531                   query: role
82532                 }, function (err, data) {
82533                   if (!err) callback(sort(role, data));
82534                 });
82535               }).on('cancel', function () {
82536                 role.property('value', origValue);
82537               }));
82538             }
82539
82540             function unbind() {
82541               var row = select(this);
82542               row.selectAll('input.member-role').call(uiCombobox.off, context);
82543             }
82544           }
82545
82546           section.entityIDs = function (val) {
82547             if (!arguments.length) return _entityIDs;
82548             _entityIDs = val;
82549             _showBlank = false;
82550             return section;
82551           };
82552
82553           return section;
82554         }
82555
82556         function uiSectionSelectionList(context) {
82557           var _selectedIDs = [];
82558           var section = uiSection('selected-features', context).shouldDisplay(function () {
82559             return _selectedIDs.length > 1;
82560           }).label(function () {
82561             return _t('inspector.title_count', {
82562               title: _t.html('inspector.features'),
82563               count: _selectedIDs.length
82564             });
82565           }).disclosureContent(renderDisclosureContent);
82566           context.history().on('change.selectionList', function (difference) {
82567             if (difference) {
82568               section.reRender();
82569             }
82570           });
82571
82572           section.entityIDs = function (val) {
82573             if (!arguments.length) return _selectedIDs;
82574             _selectedIDs = val;
82575             return section;
82576           };
82577
82578           function selectEntity(d3_event, entity) {
82579             context.enter(modeSelect(context, [entity.id]));
82580           }
82581
82582           function deselectEntity(d3_event, entity) {
82583             var selectedIDs = _selectedIDs.slice();
82584
82585             var index = selectedIDs.indexOf(entity.id);
82586
82587             if (index > -1) {
82588               selectedIDs.splice(index, 1);
82589               context.enter(modeSelect(context, selectedIDs));
82590             }
82591           }
82592
82593           function renderDisclosureContent(selection) {
82594             var list = selection.selectAll('.feature-list').data([0]);
82595             list = list.enter().append('ul').attr('class', 'feature-list').merge(list);
82596
82597             var entities = _selectedIDs.map(function (id) {
82598               return context.hasEntity(id);
82599             }).filter(Boolean);
82600
82601             var items = list.selectAll('.feature-list-item').data(entities, osmEntity.key);
82602             items.exit().remove(); // Enter
82603
82604             var enter = items.enter().append('li').attr('class', 'feature-list-item').each(function (d) {
82605               select(this).on('mouseover', function () {
82606                 utilHighlightEntities([d.id], true, context);
82607               }).on('mouseout', function () {
82608                 utilHighlightEntities([d.id], false, context);
82609               });
82610             });
82611             var label = enter.append('button').attr('class', 'label').on('click', selectEntity);
82612             label.append('span').attr('class', 'entity-geom-icon').call(svgIcon('', 'pre-text'));
82613             label.append('span').attr('class', 'entity-type');
82614             label.append('span').attr('class', 'entity-name');
82615             enter.append('button').attr('class', 'close').attr('title', _t('icons.deselect')).on('click', deselectEntity).call(svgIcon('#iD-icon-close')); // Update
82616
82617             items = items.merge(enter);
82618             items.selectAll('.entity-geom-icon use').attr('href', function () {
82619               var entity = this.parentNode.parentNode.__data__;
82620               return '#iD-icon-' + entity.geometry(context.graph());
82621             });
82622             items.selectAll('.entity-type').html(function (entity) {
82623               return _mainPresetIndex.match(entity, context.graph()).name();
82624             });
82625             items.selectAll('.entity-name').html(function (d) {
82626               // fetch latest entity
82627               var entity = context.entity(d.id);
82628               return utilDisplayName(entity);
82629             });
82630           }
82631
82632           return section;
82633         }
82634
82635         function uiEntityEditor(context) {
82636           var dispatch = dispatch$8('choose');
82637           var _state = 'select';
82638           var _coalesceChanges = false;
82639           var _modified = false;
82640
82641           var _base;
82642
82643           var _entityIDs;
82644
82645           var _activePresets = [];
82646
82647           var _newFeature;
82648
82649           var _sections;
82650
82651           function entityEditor(selection) {
82652             var combinedTags = utilCombinedTags(_entityIDs, context.graph()); // Header
82653
82654             var header = selection.selectAll('.header').data([0]); // Enter
82655
82656             var headerEnter = header.enter().append('div').attr('class', 'header fillL');
82657             headerEnter.append('button').attr('class', 'preset-reset preset-choose').call(svgIcon(_mainLocalizer.textDirection() === 'rtl' ? '#iD-icon-forward' : '#iD-icon-backward'));
82658             headerEnter.append('button').attr('class', 'close').on('click', function () {
82659               context.enter(modeBrowse(context));
82660             }).call(svgIcon(_modified ? '#iD-icon-apply' : '#iD-icon-close'));
82661             headerEnter.append('h3'); // Update
82662
82663             header = header.merge(headerEnter);
82664             header.selectAll('h3').html(_entityIDs.length === 1 ? _t.html('inspector.edit') : _t.html('inspector.edit_features'));
82665             header.selectAll('.preset-reset').on('click', function () {
82666               dispatch.call('choose', this, _activePresets);
82667             }); // Body
82668
82669             var body = selection.selectAll('.inspector-body').data([0]); // Enter
82670
82671             var bodyEnter = body.enter().append('div').attr('class', 'entity-editor inspector-body sep-top'); // Update
82672
82673             body = body.merge(bodyEnter);
82674
82675             if (!_sections) {
82676               _sections = [uiSectionSelectionList(context), uiSectionFeatureType(context).on('choose', function (presets) {
82677                 dispatch.call('choose', this, presets);
82678               }), uiSectionEntityIssues(context), uiSectionPresetFields(context).on('change', changeTags).on('revert', revertTags), uiSectionRawTagEditor('raw-tag-editor', context).on('change', changeTags), uiSectionRawMemberEditor(context), uiSectionRawMembershipEditor(context)];
82679             }
82680
82681             _sections.forEach(function (section) {
82682               if (section.entityIDs) {
82683                 section.entityIDs(_entityIDs);
82684               }
82685
82686               if (section.presets) {
82687                 section.presets(_activePresets);
82688               }
82689
82690               if (section.tags) {
82691                 section.tags(combinedTags);
82692               }
82693
82694               if (section.state) {
82695                 section.state(_state);
82696               }
82697
82698               body.call(section.render);
82699             });
82700
82701             context.history().on('change.entity-editor', historyChanged);
82702
82703             function historyChanged(difference) {
82704               if (selection.selectAll('.entity-editor').empty()) return;
82705               if (_state === 'hide') return;
82706               var significant = !difference || difference.didChange.properties || difference.didChange.addition || difference.didChange.deletion;
82707               if (!significant) return;
82708               _entityIDs = _entityIDs.filter(context.hasEntity);
82709               if (!_entityIDs.length) return;
82710               var priorActivePreset = _activePresets.length === 1 && _activePresets[0];
82711               loadActivePresets();
82712               var graph = context.graph();
82713               entityEditor.modified(_base !== graph);
82714               entityEditor(selection);
82715
82716               if (priorActivePreset && _activePresets.length === 1 && priorActivePreset !== _activePresets[0]) {
82717                 // flash the button to indicate the preset changed
82718                 context.container().selectAll('.entity-editor button.preset-reset .label').style('background-color', '#fff').transition().duration(750).style('background-color', null);
82719               }
82720             }
82721           } // Tag changes that fire on input can all get coalesced into a single
82722           // history operation when the user leaves the field.  #2342
82723           // Use explicit entityIDs in case the selection changes before the event is fired.
82724
82725
82726           function changeTags(entityIDs, changed, onInput) {
82727             var actions = [];
82728
82729             for (var i in entityIDs) {
82730               var entityID = entityIDs[i];
82731               var entity = context.entity(entityID);
82732               var tags = Object.assign({}, entity.tags); // shallow copy
82733
82734               for (var k in changed) {
82735                 if (!k) continue;
82736                 var v = changed[k];
82737
82738                 if (v !== undefined || tags.hasOwnProperty(k)) {
82739                   tags[k] = v;
82740                 }
82741               }
82742
82743               if (!onInput) {
82744                 tags = utilCleanTags(tags);
82745               }
82746
82747               if (!fastDeepEqual(entity.tags, tags)) {
82748                 actions.push(actionChangeTags(entityID, tags));
82749               }
82750             }
82751
82752             if (actions.length) {
82753               var combinedAction = function combinedAction(graph) {
82754                 actions.forEach(function (action) {
82755                   graph = action(graph);
82756                 });
82757                 return graph;
82758               };
82759
82760               var annotation = _t('operations.change_tags.annotation');
82761
82762               if (_coalesceChanges) {
82763                 context.overwrite(combinedAction, annotation);
82764               } else {
82765                 context.perform(combinedAction, annotation);
82766                 _coalesceChanges = !!onInput;
82767               }
82768             } // if leaving field (blur event), rerun validation
82769
82770
82771             if (!onInput) {
82772               context.validator().validate();
82773             }
82774           }
82775
82776           function revertTags(keys) {
82777             var actions = [];
82778
82779             for (var i in _entityIDs) {
82780               var entityID = _entityIDs[i];
82781               var original = context.graph().base().entities[entityID];
82782               var changed = {};
82783
82784               for (var j in keys) {
82785                 var key = keys[j];
82786                 changed[key] = original ? original.tags[key] : undefined;
82787               }
82788
82789               var entity = context.entity(entityID);
82790               var tags = Object.assign({}, entity.tags); // shallow copy
82791
82792               for (var k in changed) {
82793                 if (!k) continue;
82794                 var v = changed[k];
82795
82796                 if (v !== undefined || tags.hasOwnProperty(k)) {
82797                   tags[k] = v;
82798                 }
82799               }
82800
82801               tags = utilCleanTags(tags);
82802
82803               if (!fastDeepEqual(entity.tags, tags)) {
82804                 actions.push(actionChangeTags(entityID, tags));
82805               }
82806             }
82807
82808             if (actions.length) {
82809               var combinedAction = function combinedAction(graph) {
82810                 actions.forEach(function (action) {
82811                   graph = action(graph);
82812                 });
82813                 return graph;
82814               };
82815
82816               var annotation = _t('operations.change_tags.annotation');
82817
82818               if (_coalesceChanges) {
82819                 context.overwrite(combinedAction, annotation);
82820               } else {
82821                 context.perform(combinedAction, annotation);
82822                 _coalesceChanges = false;
82823               }
82824             }
82825
82826             context.validator().validate();
82827           }
82828
82829           entityEditor.modified = function (val) {
82830             if (!arguments.length) return _modified;
82831             _modified = val;
82832             return entityEditor;
82833           };
82834
82835           entityEditor.state = function (val) {
82836             if (!arguments.length) return _state;
82837             _state = val;
82838             return entityEditor;
82839           };
82840
82841           entityEditor.entityIDs = function (val) {
82842             if (!arguments.length) return _entityIDs; // always reload these even if the entityIDs are unchanged, since we
82843             // could be reselecting after something like dragging a node
82844
82845             _base = context.graph();
82846             _coalesceChanges = false;
82847             if (val && _entityIDs && utilArrayIdentical(_entityIDs, val)) return entityEditor; // exit early if no change
82848
82849             _entityIDs = val;
82850             loadActivePresets(true);
82851             return entityEditor.modified(false);
82852           };
82853
82854           entityEditor.newFeature = function (val) {
82855             if (!arguments.length) return _newFeature;
82856             _newFeature = val;
82857             return entityEditor;
82858           };
82859
82860           function loadActivePresets(isForNewSelection) {
82861             var graph = context.graph();
82862             var counts = {};
82863
82864             for (var i in _entityIDs) {
82865               var entity = graph.hasEntity(_entityIDs[i]);
82866               if (!entity) return;
82867               var match = _mainPresetIndex.match(entity, graph);
82868               if (!counts[match.id]) counts[match.id] = 0;
82869               counts[match.id] += 1;
82870             }
82871
82872             var matches = Object.keys(counts).sort(function (p1, p2) {
82873               return counts[p2] - counts[p1];
82874             }).map(function (pID) {
82875               return _mainPresetIndex.item(pID);
82876             });
82877
82878             if (!isForNewSelection) {
82879               // A "weak" preset doesn't set any tags. (e.g. "Address")
82880               var weakPreset = _activePresets.length === 1 && !_activePresets[0].isFallback() && Object.keys(_activePresets[0].addTags || {}).length === 0; // Don't replace a weak preset with a fallback preset (e.g. "Point")
82881
82882               if (weakPreset && matches.length === 1 && matches[0].isFallback()) return;
82883             }
82884
82885             entityEditor.presets(matches);
82886           }
82887
82888           entityEditor.presets = function (val) {
82889             if (!arguments.length) return _activePresets; // don't reload the same preset
82890
82891             if (!utilArrayIdentical(val, _activePresets)) {
82892               _activePresets = val;
82893             }
82894
82895             return entityEditor;
82896           };
82897
82898           return utilRebind(entityEditor, dispatch, 'on');
82899         }
82900
82901         function uiPresetList(context) {
82902           var dispatch = dispatch$8('cancel', 'choose');
82903
82904           var _entityIDs;
82905
82906           var _currLoc;
82907
82908           var _currentPresets;
82909
82910           var _autofocus = false;
82911
82912           function presetList(selection) {
82913             if (!_entityIDs) return;
82914             var presets = _mainPresetIndex.matchAllGeometry(entityGeometries());
82915             selection.html('');
82916             var messagewrap = selection.append('div').attr('class', 'header fillL');
82917             var message = messagewrap.append('h3').html(_t.html('inspector.choose'));
82918             messagewrap.append('button').attr('class', 'preset-choose').on('click', function () {
82919               dispatch.call('cancel', this);
82920             }).call(svgIcon(_mainLocalizer.textDirection() === 'rtl' ? '#iD-icon-backward' : '#iD-icon-forward'));
82921
82922             function initialKeydown(d3_event) {
82923               // hack to let delete shortcut work when search is autofocused
82924               if (search.property('value').length === 0 && (d3_event.keyCode === utilKeybinding.keyCodes['⌫'] || d3_event.keyCode === utilKeybinding.keyCodes['⌦'])) {
82925                 d3_event.preventDefault();
82926                 d3_event.stopPropagation();
82927                 operationDelete(context, _entityIDs)(); // hack to let undo work when search is autofocused
82928               } else if (search.property('value').length === 0 && (d3_event.ctrlKey || d3_event.metaKey) && d3_event.keyCode === utilKeybinding.keyCodes.z) {
82929                 d3_event.preventDefault();
82930                 d3_event.stopPropagation();
82931                 context.undo();
82932               } else if (!d3_event.ctrlKey && !d3_event.metaKey) {
82933                 // don't check for delete/undo hack on future keydown events
82934                 select(this).on('keydown', keydown);
82935                 keydown.call(this, d3_event);
82936               }
82937             }
82938
82939             function keydown(d3_event) {
82940               // down arrow
82941               if (d3_event.keyCode === utilKeybinding.keyCodes['↓'] && // if insertion point is at the end of the string
82942               search.node().selectionStart === search.property('value').length) {
82943                 d3_event.preventDefault();
82944                 d3_event.stopPropagation(); // move focus to the first item in the preset list
82945
82946                 var buttons = list.selectAll('.preset-list-button');
82947                 if (!buttons.empty()) buttons.nodes()[0].focus();
82948               }
82949             }
82950
82951             function keypress(d3_event) {
82952               // enter
82953               var value = search.property('value');
82954
82955               if (d3_event.keyCode === 13 && // ↩ Return
82956               value.length) {
82957                 list.selectAll('.preset-list-item:first-child').each(function (d) {
82958                   d.choose.call(this);
82959                 });
82960               }
82961             }
82962
82963             function inputevent() {
82964               var value = search.property('value');
82965               list.classed('filtered', value.length);
82966               var results, messageText;
82967
82968               if (value.length) {
82969                 results = presets.search(value, entityGeometries()[0], _currLoc);
82970                 messageText = _t('inspector.results', {
82971                   n: results.collection.length,
82972                   search: value
82973                 });
82974               } else {
82975                 results = _mainPresetIndex.defaults(entityGeometries()[0], 36, !context.inIntro(), _currLoc);
82976                 messageText = _t('inspector.choose');
82977               }
82978
82979               list.call(drawList, results);
82980               message.html(messageText);
82981             }
82982
82983             var searchWrap = selection.append('div').attr('class', 'search-header');
82984             searchWrap.call(svgIcon('#iD-icon-search', 'pre-text'));
82985             var search = searchWrap.append('input').attr('class', 'preset-search-input').attr('placeholder', _t('inspector.search')).attr('type', 'search').call(utilNoAuto).on('keydown', initialKeydown).on('keypress', keypress).on('input', inputevent);
82986
82987             if (_autofocus) {
82988               search.node().focus(); // Safari 14 doesn't always like to focus immediately,
82989               // so try again on the next pass
82990
82991               setTimeout(function () {
82992                 search.node().focus();
82993               }, 0);
82994             }
82995
82996             var listWrap = selection.append('div').attr('class', 'inspector-body');
82997             var list = listWrap.append('div').attr('class', 'preset-list').call(drawList, _mainPresetIndex.defaults(entityGeometries()[0], 36, !context.inIntro(), _currLoc));
82998             context.features().on('change.preset-list', updateForFeatureHiddenState);
82999           }
83000
83001           function drawList(list, presets) {
83002             presets = presets.matchAllGeometry(entityGeometries());
83003             var collection = presets.collection.reduce(function (collection, preset) {
83004               if (!preset) return collection;
83005
83006               if (preset.members) {
83007                 if (preset.members.collection.filter(function (preset) {
83008                   return preset.addable();
83009                 }).length > 1) {
83010                   collection.push(CategoryItem(preset));
83011                 }
83012               } else if (preset.addable()) {
83013                 collection.push(PresetItem(preset));
83014               }
83015
83016               return collection;
83017             }, []);
83018             var items = list.selectAll('.preset-list-item').data(collection, function (d) {
83019               return d.preset.id;
83020             });
83021             items.order();
83022             items.exit().remove();
83023             items.enter().append('div').attr('class', function (item) {
83024               return 'preset-list-item preset-' + item.preset.id.replace('/', '-');
83025             }).classed('current', function (item) {
83026               return _currentPresets.indexOf(item.preset) !== -1;
83027             }).each(function (item) {
83028               select(this).call(item);
83029             }).style('opacity', 0).transition().style('opacity', 1);
83030             updateForFeatureHiddenState();
83031           }
83032
83033           function itemKeydown(d3_event) {
83034             // the actively focused item
83035             var item = select(this.closest('.preset-list-item'));
83036             var parentItem = select(item.node().parentNode.closest('.preset-list-item')); // arrow down, move focus to the next, lower item
83037
83038             if (d3_event.keyCode === utilKeybinding.keyCodes['↓']) {
83039               d3_event.preventDefault();
83040               d3_event.stopPropagation(); // the next item in the list at the same level
83041
83042               var nextItem = select(item.node().nextElementSibling); // if there is no next item in this list
83043
83044               if (nextItem.empty()) {
83045                 // if there is a parent item
83046                 if (!parentItem.empty()) {
83047                   // the item is the last item of a sublist,
83048                   // select the next item at the parent level
83049                   nextItem = select(parentItem.node().nextElementSibling);
83050                 } // if the focused item is expanded
83051
83052               } else if (select(this).classed('expanded')) {
83053                 // select the first subitem instead
83054                 nextItem = item.select('.subgrid .preset-list-item:first-child');
83055               }
83056
83057               if (!nextItem.empty()) {
83058                 // focus on the next item
83059                 nextItem.select('.preset-list-button').node().focus();
83060               } // arrow up, move focus to the previous, higher item
83061
83062             } else if (d3_event.keyCode === utilKeybinding.keyCodes['↑']) {
83063               d3_event.preventDefault();
83064               d3_event.stopPropagation(); // the previous item in the list at the same level
83065
83066               var previousItem = select(item.node().previousElementSibling); // if there is no previous item in this list
83067
83068               if (previousItem.empty()) {
83069                 // if there is a parent item
83070                 if (!parentItem.empty()) {
83071                   // the item is the first subitem of a sublist select the parent item
83072                   previousItem = parentItem;
83073                 } // if the previous item is expanded
83074
83075               } else if (previousItem.select('.preset-list-button').classed('expanded')) {
83076                 // select the last subitem of the sublist of the previous item
83077                 previousItem = previousItem.select('.subgrid .preset-list-item:last-child');
83078               }
83079
83080               if (!previousItem.empty()) {
83081                 // focus on the previous item
83082                 previousItem.select('.preset-list-button').node().focus();
83083               } else {
83084                 // the focus is at the top of the list, move focus back to the search field
83085                 var search = select(this.closest('.preset-list-pane')).select('.preset-search-input');
83086                 search.node().focus();
83087               } // arrow left, move focus to the parent item if there is one
83088
83089             } else if (d3_event.keyCode === utilKeybinding.keyCodes[_mainLocalizer.textDirection() === 'rtl' ? '→' : '←']) {
83090               d3_event.preventDefault();
83091               d3_event.stopPropagation(); // if there is a parent item, focus on the parent item
83092
83093               if (!parentItem.empty()) {
83094                 parentItem.select('.preset-list-button').node().focus();
83095               } // arrow right, choose this item
83096
83097             } else if (d3_event.keyCode === utilKeybinding.keyCodes[_mainLocalizer.textDirection() === 'rtl' ? '←' : '→']) {
83098               d3_event.preventDefault();
83099               d3_event.stopPropagation();
83100               item.datum().choose.call(select(this).node());
83101             }
83102           }
83103
83104           function CategoryItem(preset) {
83105             var box,
83106                 sublist,
83107                 shown = false;
83108
83109             function item(selection) {
83110               var wrap = selection.append('div').attr('class', 'preset-list-button-wrap category');
83111
83112               function click() {
83113                 var isExpanded = select(this).classed('expanded');
83114                 var iconName = isExpanded ? _mainLocalizer.textDirection() === 'rtl' ? '#iD-icon-backward' : '#iD-icon-forward' : '#iD-icon-down';
83115                 select(this).classed('expanded', !isExpanded);
83116                 select(this).selectAll('div.label-inner svg.icon use').attr('href', iconName);
83117                 item.choose();
83118               }
83119
83120               var geometries = entityGeometries();
83121               var button = wrap.append('button').attr('class', 'preset-list-button').classed('expanded', false).call(uiPresetIcon().geometry(geometries.length === 1 && geometries[0]).preset(preset)).on('click', click).on('keydown', function (d3_event) {
83122                 // right arrow, expand the focused item
83123                 if (d3_event.keyCode === utilKeybinding.keyCodes[_mainLocalizer.textDirection() === 'rtl' ? '←' : '→']) {
83124                   d3_event.preventDefault();
83125                   d3_event.stopPropagation(); // if the item isn't expanded
83126
83127                   if (!select(this).classed('expanded')) {
83128                     // toggle expansion (expand the item)
83129                     click.call(this, d3_event);
83130                   } // left arrow, collapse the focused item
83131
83132                 } else if (d3_event.keyCode === utilKeybinding.keyCodes[_mainLocalizer.textDirection() === 'rtl' ? '→' : '←']) {
83133                   d3_event.preventDefault();
83134                   d3_event.stopPropagation(); // if the item is expanded
83135
83136                   if (select(this).classed('expanded')) {
83137                     // toggle expansion (collapse the item)
83138                     click.call(this, d3_event);
83139                   }
83140                 } else {
83141                   itemKeydown.call(this, d3_event);
83142                 }
83143               });
83144               var label = button.append('div').attr('class', 'label').append('div').attr('class', 'label-inner');
83145               label.append('div').attr('class', 'namepart').call(svgIcon(_mainLocalizer.textDirection() === 'rtl' ? '#iD-icon-backward' : '#iD-icon-forward', 'inline')).append('span').html(function () {
83146                 return preset.nameLabel() + '&hellip;';
83147               });
83148               box = selection.append('div').attr('class', 'subgrid').style('max-height', '0px').style('opacity', 0);
83149               box.append('div').attr('class', 'arrow');
83150               sublist = box.append('div').attr('class', 'preset-list fillL3');
83151             }
83152
83153             item.choose = function () {
83154               if (!box || !sublist) return;
83155
83156               if (shown) {
83157                 shown = false;
83158                 box.transition().duration(200).style('opacity', '0').style('max-height', '0px').style('padding-bottom', '0px');
83159               } else {
83160                 shown = true;
83161                 var members = preset.members.matchAllGeometry(entityGeometries());
83162                 sublist.call(drawList, members);
83163                 box.transition().duration(200).style('opacity', '1').style('max-height', 200 + members.collection.length * 190 + 'px').style('padding-bottom', '10px');
83164               }
83165             };
83166
83167             item.preset = preset;
83168             return item;
83169           }
83170
83171           function PresetItem(preset) {
83172             function item(selection) {
83173               var wrap = selection.append('div').attr('class', 'preset-list-button-wrap');
83174               var geometries = entityGeometries();
83175               var button = wrap.append('button').attr('class', 'preset-list-button').call(uiPresetIcon().geometry(geometries.length === 1 && geometries[0]).preset(preset)).on('click', item.choose).on('keydown', itemKeydown);
83176               var label = button.append('div').attr('class', 'label').append('div').attr('class', 'label-inner');
83177               var nameparts = [preset.nameLabel(), preset.subtitleLabel()].filter(Boolean);
83178               label.selectAll('.namepart').data(nameparts).enter().append('div').attr('class', 'namepart').html(function (d) {
83179                 return d;
83180               });
83181               wrap.call(item.reference.button);
83182               selection.call(item.reference.body);
83183             }
83184
83185             item.choose = function () {
83186               if (select(this).classed('disabled')) return;
83187
83188               if (!context.inIntro()) {
83189                 _mainPresetIndex.setMostRecent(preset, entityGeometries()[0]);
83190               }
83191
83192               context.perform(function (graph) {
83193                 for (var i in _entityIDs) {
83194                   var entityID = _entityIDs[i];
83195                   var oldPreset = _mainPresetIndex.match(graph.entity(entityID), graph);
83196                   graph = actionChangePreset(entityID, oldPreset, preset)(graph);
83197                 }
83198
83199                 return graph;
83200               }, _t('operations.change_tags.annotation'));
83201               context.validator().validate(); // rerun validation
83202
83203               dispatch.call('choose', this, preset);
83204             };
83205
83206             item.help = function (d3_event) {
83207               d3_event.stopPropagation();
83208               item.reference.toggle();
83209             };
83210
83211             item.preset = preset;
83212             item.reference = uiTagReference(preset.reference());
83213             return item;
83214           }
83215
83216           function updateForFeatureHiddenState() {
83217             if (!_entityIDs.every(context.hasEntity)) return;
83218             var geometries = entityGeometries();
83219             var button = context.container().selectAll('.preset-list .preset-list-button'); // remove existing tooltips
83220
83221             button.call(uiTooltip().destroyAny);
83222             button.each(function (item, index) {
83223               var hiddenPresetFeaturesId;
83224
83225               for (var i in geometries) {
83226                 hiddenPresetFeaturesId = context.features().isHiddenPreset(item.preset, geometries[i]);
83227                 if (hiddenPresetFeaturesId) break;
83228               }
83229
83230               var isHiddenPreset = !context.inIntro() && !!hiddenPresetFeaturesId && (_currentPresets.length !== 1 || item.preset !== _currentPresets[0]);
83231               select(this).classed('disabled', isHiddenPreset);
83232
83233               if (isHiddenPreset) {
83234                 var isAutoHidden = context.features().autoHidden(hiddenPresetFeaturesId);
83235                 select(this).call(uiTooltip().title(_t.html('inspector.hidden_preset.' + (isAutoHidden ? 'zoom' : 'manual'), {
83236                   features: _t.html('feature.' + hiddenPresetFeaturesId + '.description')
83237                 })).placement(index < 2 ? 'bottom' : 'top'));
83238               }
83239             });
83240           }
83241
83242           presetList.autofocus = function (val) {
83243             if (!arguments.length) return _autofocus;
83244             _autofocus = val;
83245             return presetList;
83246           };
83247
83248           presetList.entityIDs = function (val) {
83249             if (!arguments.length) return _entityIDs;
83250             _entityIDs = val;
83251             _currLoc = null;
83252
83253             if (_entityIDs && _entityIDs.length) {
83254               // calculate current location
83255               var extent = _entityIDs.reduce(function (extent, entityID) {
83256                 var entity = context.graph().entity(entityID);
83257                 return extent.extend(entity.extent(context.graph()));
83258               }, geoExtent());
83259
83260               _currLoc = extent.center(); // match presets
83261
83262               var presets = _entityIDs.map(function (entityID) {
83263                 return _mainPresetIndex.match(context.entity(entityID), context.graph());
83264               });
83265
83266               presetList.presets(presets);
83267             }
83268
83269             return presetList;
83270           };
83271
83272           presetList.presets = function (val) {
83273             if (!arguments.length) return _currentPresets;
83274             _currentPresets = val;
83275             return presetList;
83276           };
83277
83278           function entityGeometries() {
83279             var counts = {};
83280
83281             for (var i in _entityIDs) {
83282               var entityID = _entityIDs[i];
83283               var entity = context.entity(entityID);
83284               var geometry = entity.geometry(context.graph()); // Treat entities on addr:interpolation lines as points, not vertices (#3241)
83285
83286               if (geometry === 'vertex' && entity.isOnAddressLine(context.graph())) {
83287                 geometry = 'point';
83288               }
83289
83290               if (!counts[geometry]) counts[geometry] = 0;
83291               counts[geometry] += 1;
83292             }
83293
83294             return Object.keys(counts).sort(function (geom1, geom2) {
83295               return counts[geom2] - counts[geom1];
83296             });
83297           }
83298
83299           return utilRebind(presetList, dispatch, 'on');
83300         }
83301
83302         function uiViewOnOSM(context) {
83303           var _what; // an osmEntity or osmNote
83304
83305
83306           function viewOnOSM(selection) {
83307             var url;
83308
83309             if (_what instanceof osmEntity) {
83310               url = context.connection().entityURL(_what);
83311             } else if (_what instanceof osmNote) {
83312               url = context.connection().noteURL(_what);
83313             }
83314
83315             var data = !_what || _what.isNew() ? [] : [_what];
83316             var link = selection.selectAll('.view-on-osm').data(data, function (d) {
83317               return d.id;
83318             }); // exit
83319
83320             link.exit().remove(); // enter
83321
83322             var linkEnter = link.enter().append('a').attr('class', 'view-on-osm').attr('target', '_blank').attr('href', url).call(svgIcon('#iD-icon-out-link', 'inline'));
83323             linkEnter.append('span').html(_t.html('inspector.view_on_osm'));
83324           }
83325
83326           viewOnOSM.what = function (_) {
83327             if (!arguments.length) return _what;
83328             _what = _;
83329             return viewOnOSM;
83330           };
83331
83332           return viewOnOSM;
83333         }
83334
83335         function uiInspector(context) {
83336           var presetList = uiPresetList(context);
83337           var entityEditor = uiEntityEditor(context);
83338           var wrap = select(null),
83339               presetPane = select(null),
83340               editorPane = select(null);
83341           var _state = 'select';
83342
83343           var _entityIDs;
83344
83345           var _newFeature = false;
83346
83347           function inspector(selection) {
83348             presetList.entityIDs(_entityIDs).autofocus(_newFeature).on('choose', inspector.setPreset).on('cancel', function () {
83349               inspector.setPreset();
83350             });
83351             entityEditor.state(_state).entityIDs(_entityIDs).on('choose', inspector.showList);
83352             wrap = selection.selectAll('.panewrap').data([0]);
83353             var enter = wrap.enter().append('div').attr('class', 'panewrap');
83354             enter.append('div').attr('class', 'preset-list-pane pane');
83355             enter.append('div').attr('class', 'entity-editor-pane pane');
83356             wrap = wrap.merge(enter);
83357             presetPane = wrap.selectAll('.preset-list-pane');
83358             editorPane = wrap.selectAll('.entity-editor-pane');
83359
83360             function shouldDefaultToPresetList() {
83361               // always show the inspector on hover
83362               if (_state !== 'select') return false; // can only change preset on single selection
83363
83364               if (_entityIDs.length !== 1) return false;
83365               var entityID = _entityIDs[0];
83366               var entity = context.hasEntity(entityID);
83367               if (!entity) return false; // default to inspector if there are already tags
83368
83369               if (entity.hasNonGeometryTags()) return false; // prompt to select preset if feature is new and untagged
83370
83371               if (_newFeature) return true; // all existing features except vertices should default to inspector
83372
83373               if (entity.geometry(context.graph()) !== 'vertex') return false; // show vertex relations if any
83374
83375               if (context.graph().parentRelations(entity).length) return false; // show vertex issues if there are any
83376
83377               if (context.validator().getEntityIssues(entityID).length) return false; // show turn retriction editor for junction vertices
83378
83379               if (entity.isHighwayIntersection(context.graph())) return false; // otherwise show preset list for uninteresting vertices
83380
83381               return true;
83382             }
83383
83384             if (shouldDefaultToPresetList()) {
83385               wrap.style('right', '-100%');
83386               editorPane.classed('hide', true);
83387               presetPane.classed('hide', false).call(presetList);
83388             } else {
83389               wrap.style('right', '0%');
83390               presetPane.classed('hide', true);
83391               editorPane.classed('hide', false).call(entityEditor);
83392             }
83393
83394             var footer = selection.selectAll('.footer').data([0]);
83395             footer = footer.enter().append('div').attr('class', 'footer').merge(footer);
83396             footer.call(uiViewOnOSM(context).what(context.hasEntity(_entityIDs.length === 1 && _entityIDs[0])));
83397           }
83398
83399           inspector.showList = function (presets) {
83400             presetPane.classed('hide', false);
83401             wrap.transition().styleTween('right', function () {
83402               return interpolate$1('0%', '-100%');
83403             }).on('end', function () {
83404               editorPane.classed('hide', true);
83405             });
83406
83407             if (presets) {
83408               presetList.presets(presets);
83409             }
83410
83411             presetPane.call(presetList.autofocus(true));
83412           };
83413
83414           inspector.setPreset = function (preset) {
83415             // upon setting multipolygon, go to the area preset list instead of the editor
83416             if (preset && preset.id === 'type/multipolygon') {
83417               presetPane.call(presetList.autofocus(true));
83418             } else {
83419               editorPane.classed('hide', false);
83420               wrap.transition().styleTween('right', function () {
83421                 return interpolate$1('-100%', '0%');
83422               }).on('end', function () {
83423                 presetPane.classed('hide', true);
83424               });
83425
83426               if (preset) {
83427                 entityEditor.presets([preset]);
83428               }
83429
83430               editorPane.call(entityEditor);
83431             }
83432           };
83433
83434           inspector.state = function (val) {
83435             if (!arguments.length) return _state;
83436             _state = val;
83437             entityEditor.state(_state); // remove any old field help overlay that might have gotten attached to the inspector
83438
83439             context.container().selectAll('.field-help-body').remove();
83440             return inspector;
83441           };
83442
83443           inspector.entityIDs = function (val) {
83444             if (!arguments.length) return _entityIDs;
83445             _entityIDs = val;
83446             return inspector;
83447           };
83448
83449           inspector.newFeature = function (val) {
83450             if (!arguments.length) return _newFeature;
83451             _newFeature = val;
83452             return inspector;
83453           };
83454
83455           return inspector;
83456         }
83457
83458         function uiImproveOsmComments() {
83459           var _qaItem;
83460
83461           function issueComments(selection) {
83462             // make the div immediately so it appears above the buttons
83463             var comments = selection.selectAll('.comments-container').data([0]);
83464             comments = comments.enter().append('div').attr('class', 'comments-container').merge(comments); // must retrieve comments from API before they can be displayed
83465
83466             services.improveOSM.getComments(_qaItem).then(function (d) {
83467               if (!d.comments) return; // nothing to do here
83468
83469               var commentEnter = comments.selectAll('.comment').data(d.comments).enter().append('div').attr('class', 'comment');
83470               commentEnter.append('div').attr('class', 'comment-avatar').call(svgIcon('#iD-icon-avatar', 'comment-avatar-icon'));
83471               var mainEnter = commentEnter.append('div').attr('class', 'comment-main');
83472               var metadataEnter = mainEnter.append('div').attr('class', 'comment-metadata');
83473               metadataEnter.append('div').attr('class', 'comment-author').each(function (d) {
83474                 var osm = services.osm;
83475                 var selection = select(this);
83476
83477                 if (osm && d.username) {
83478                   selection = selection.append('a').attr('class', 'comment-author-link').attr('href', osm.userURL(d.username)).attr('target', '_blank');
83479                 }
83480
83481                 selection.html(function (d) {
83482                   return d.username;
83483                 });
83484               });
83485               metadataEnter.append('div').attr('class', 'comment-date').html(function (d) {
83486                 return _t.html('note.status.commented', {
83487                   when: localeDateString(d.timestamp)
83488                 });
83489               });
83490               mainEnter.append('div').attr('class', 'comment-text').append('p').html(function (d) {
83491                 return d.text;
83492               });
83493             })["catch"](function (err) {
83494               console.log(err); // eslint-disable-line no-console
83495             });
83496           }
83497
83498           function localeDateString(s) {
83499             if (!s) return null;
83500             var options = {
83501               day: 'numeric',
83502               month: 'short',
83503               year: 'numeric'
83504             };
83505             var d = new Date(s * 1000); // timestamp is served in seconds, date takes ms
83506
83507             if (isNaN(d.getTime())) return null;
83508             return d.toLocaleDateString(_mainLocalizer.localeCode(), options);
83509           }
83510
83511           issueComments.issue = function (val) {
83512             if (!arguments.length) return _qaItem;
83513             _qaItem = val;
83514             return issueComments;
83515           };
83516
83517           return issueComments;
83518         }
83519
83520         function uiImproveOsmDetails(context) {
83521           var _qaItem;
83522
83523           function issueDetail(d) {
83524             if (d.desc) return d.desc;
83525             var issueKey = d.issueKey;
83526             d.replacements = d.replacements || {};
83527             d.replacements["default"] = _t.html('inspector.unknown'); // special key `default` works as a fallback string
83528
83529             return _t.html("QA.improveOSM.error_types.".concat(issueKey, ".description"), d.replacements);
83530           }
83531
83532           function improveOsmDetails(selection) {
83533             var details = selection.selectAll('.error-details').data(_qaItem ? [_qaItem] : [], function (d) {
83534               return "".concat(d.id, "-").concat(d.status || 0);
83535             });
83536             details.exit().remove();
83537             var detailsEnter = details.enter().append('div').attr('class', 'error-details qa-details-container'); // description
83538
83539             var descriptionEnter = detailsEnter.append('div').attr('class', 'qa-details-subsection');
83540             descriptionEnter.append('h4').html(_t.html('QA.keepRight.detail_description'));
83541             descriptionEnter.append('div').attr('class', 'qa-details-description-text').html(issueDetail); // If there are entity links in the error message..
83542
83543             var relatedEntities = [];
83544             descriptionEnter.selectAll('.error_entity_link, .error_object_link').attr('href', '#').each(function () {
83545               var link = select(this);
83546               var isObjectLink = link.classed('error_object_link');
83547               var entityID = isObjectLink ? utilEntityRoot(_qaItem.objectType) + _qaItem.objectId : this.textContent;
83548               var entity = context.hasEntity(entityID);
83549               relatedEntities.push(entityID); // Add click handler
83550
83551               link.on('mouseenter', function () {
83552                 utilHighlightEntities([entityID], true, context);
83553               }).on('mouseleave', function () {
83554                 utilHighlightEntities([entityID], false, context);
83555               }).on('click', function (d3_event) {
83556                 d3_event.preventDefault();
83557                 utilHighlightEntities([entityID], false, context);
83558                 var osmlayer = context.layers().layer('osm');
83559
83560                 if (!osmlayer.enabled()) {
83561                   osmlayer.enabled(true);
83562                 }
83563
83564                 context.map().centerZoom(_qaItem.loc, 20);
83565
83566                 if (entity) {
83567                   context.enter(modeSelect(context, [entityID]));
83568                 } else {
83569                   context.loadEntity(entityID, function (err, result) {
83570                     if (err) return;
83571                     var entity = result.data.find(function (e) {
83572                       return e.id === entityID;
83573                     });
83574                     if (entity) context.enter(modeSelect(context, [entityID]));
83575                   });
83576                 }
83577               }); // Replace with friendly name if possible
83578               // (The entity may not yet be loaded into the graph)
83579
83580               if (entity) {
83581                 var name = utilDisplayName(entity); // try to use common name
83582
83583                 if (!name && !isObjectLink) {
83584                   var preset = _mainPresetIndex.match(entity, context.graph());
83585                   name = preset && !preset.isFallback() && preset.name(); // fallback to preset name
83586                 }
83587
83588                 if (name) {
83589                   this.innerText = name;
83590                 }
83591               }
83592             }); // Don't hide entities related to this error - #5880
83593
83594             context.features().forceVisible(relatedEntities);
83595             context.map().pan([0, 0]); // trigger a redraw
83596           }
83597
83598           improveOsmDetails.issue = function (val) {
83599             if (!arguments.length) return _qaItem;
83600             _qaItem = val;
83601             return improveOsmDetails;
83602           };
83603
83604           return improveOsmDetails;
83605         }
83606
83607         function uiImproveOsmHeader() {
83608           var _qaItem;
83609
83610           function issueTitle(d) {
83611             var issueKey = d.issueKey;
83612             d.replacements = d.replacements || {};
83613             d.replacements["default"] = _t.html('inspector.unknown'); // special key `default` works as a fallback string
83614
83615             return _t.html("QA.improveOSM.error_types.".concat(issueKey, ".title"), d.replacements);
83616           }
83617
83618           function improveOsmHeader(selection) {
83619             var header = selection.selectAll('.qa-header').data(_qaItem ? [_qaItem] : [], function (d) {
83620               return "".concat(d.id, "-").concat(d.status || 0);
83621             });
83622             header.exit().remove();
83623             var headerEnter = header.enter().append('div').attr('class', 'qa-header');
83624             var svgEnter = headerEnter.append('div').attr('class', 'qa-header-icon').classed('new', function (d) {
83625               return d.id < 0;
83626             }).append('svg').attr('width', '20px').attr('height', '30px').attr('viewbox', '0 0 20 30').attr('class', function (d) {
83627               return "preset-icon-28 qaItem ".concat(d.service, " itemId-").concat(d.id, " itemType-").concat(d.itemType);
83628             });
83629             svgEnter.append('polygon').attr('fill', 'currentColor').attr('class', 'qaItem-fill').attr('points', '16,3 4,3 1,6 1,17 4,20 7,20 10,27 13,20 16,20 19,17.033 19,6');
83630             svgEnter.append('use').attr('class', 'icon-annotation').attr('width', '13px').attr('height', '13px').attr('transform', 'translate(3.5, 5)').attr('xlink:href', function (d) {
83631               var picon = d.icon;
83632
83633               if (!picon) {
83634                 return '';
83635               } else {
83636                 var isMaki = /^maki-/.test(picon);
83637                 return "#".concat(picon).concat(isMaki ? '-11' : '');
83638               }
83639             });
83640             headerEnter.append('div').attr('class', 'qa-header-label').html(issueTitle);
83641           }
83642
83643           improveOsmHeader.issue = function (val) {
83644             if (!arguments.length) return _qaItem;
83645             _qaItem = val;
83646             return improveOsmHeader;
83647           };
83648
83649           return improveOsmHeader;
83650         }
83651
83652         function uiImproveOsmEditor(context) {
83653           var dispatch = dispatch$8('change');
83654           var qaDetails = uiImproveOsmDetails(context);
83655           var qaComments = uiImproveOsmComments();
83656           var qaHeader = uiImproveOsmHeader();
83657
83658           var _qaItem;
83659
83660           function improveOsmEditor(selection) {
83661             var headerEnter = selection.selectAll('.header').data([0]).enter().append('div').attr('class', 'header fillL');
83662             headerEnter.append('button').attr('class', 'close').on('click', function () {
83663               return context.enter(modeBrowse(context));
83664             }).call(svgIcon('#iD-icon-close'));
83665             headerEnter.append('h3').html(_t.html('QA.improveOSM.title'));
83666             var body = selection.selectAll('.body').data([0]);
83667             body = body.enter().append('div').attr('class', 'body').merge(body);
83668             var editor = body.selectAll('.qa-editor').data([0]);
83669             editor.enter().append('div').attr('class', 'modal-section qa-editor').merge(editor).call(qaHeader.issue(_qaItem)).call(qaDetails.issue(_qaItem)).call(qaComments.issue(_qaItem)).call(improveOsmSaveSection);
83670           }
83671
83672           function improveOsmSaveSection(selection) {
83673             var isSelected = _qaItem && _qaItem.id === context.selectedErrorID();
83674
83675             var isShown = _qaItem && (isSelected || _qaItem.newComment || _qaItem.comment);
83676             var saveSection = selection.selectAll('.qa-save').data(isShown ? [_qaItem] : [], function (d) {
83677               return "".concat(d.id, "-").concat(d.status || 0);
83678             }); // exit
83679
83680             saveSection.exit().remove(); // enter
83681
83682             var saveSectionEnter = saveSection.enter().append('div').attr('class', 'qa-save save-section cf');
83683             saveSectionEnter.append('h4').attr('class', '.qa-save-header').html(_t.html('note.newComment'));
83684             saveSectionEnter.append('textarea').attr('class', 'new-comment-input').attr('placeholder', _t('QA.keepRight.comment_placeholder')).attr('maxlength', 1000).property('value', function (d) {
83685               return d.newComment;
83686             }).call(utilNoAuto).on('input', changeInput).on('blur', changeInput); // update
83687
83688             saveSection = saveSectionEnter.merge(saveSection).call(qaSaveButtons);
83689
83690             function changeInput() {
83691               var input = select(this);
83692               var val = input.property('value').trim();
83693
83694               if (val === '') {
83695                 val = undefined;
83696               } // store the unsaved comment with the issue itself
83697
83698
83699               _qaItem = _qaItem.update({
83700                 newComment: val
83701               });
83702               var qaService = services.improveOSM;
83703
83704               if (qaService) {
83705                 qaService.replaceItem(_qaItem);
83706               }
83707
83708               saveSection.call(qaSaveButtons);
83709             }
83710           }
83711
83712           function qaSaveButtons(selection) {
83713             var isSelected = _qaItem && _qaItem.id === context.selectedErrorID();
83714
83715             var buttonSection = selection.selectAll('.buttons').data(isSelected ? [_qaItem] : [], function (d) {
83716               return d.status + d.id;
83717             }); // exit
83718
83719             buttonSection.exit().remove(); // enter
83720
83721             var buttonEnter = buttonSection.enter().append('div').attr('class', 'buttons');
83722             buttonEnter.append('button').attr('class', 'button comment-button action').html(_t.html('QA.keepRight.save_comment'));
83723             buttonEnter.append('button').attr('class', 'button close-button action');
83724             buttonEnter.append('button').attr('class', 'button ignore-button action'); // update
83725
83726             buttonSection = buttonSection.merge(buttonEnter);
83727             buttonSection.select('.comment-button').attr('disabled', function (d) {
83728               return d.newComment ? null : true;
83729             }).on('click.comment', function (d3_event, d) {
83730               this.blur(); // avoid keeping focus on the button - #4641
83731
83732               var qaService = services.improveOSM;
83733
83734               if (qaService) {
83735                 qaService.postUpdate(d, function (err, item) {
83736                   return dispatch.call('change', item);
83737                 });
83738               }
83739             });
83740             buttonSection.select('.close-button').html(function (d) {
83741               var andComment = d.newComment ? '_comment' : '';
83742               return _t.html("QA.keepRight.close".concat(andComment));
83743             }).on('click.close', function (d3_event, d) {
83744               this.blur(); // avoid keeping focus on the button - #4641
83745
83746               var qaService = services.improveOSM;
83747
83748               if (qaService) {
83749                 d.newStatus = 'SOLVED';
83750                 qaService.postUpdate(d, function (err, item) {
83751                   return dispatch.call('change', item);
83752                 });
83753               }
83754             });
83755             buttonSection.select('.ignore-button').html(function (d) {
83756               var andComment = d.newComment ? '_comment' : '';
83757               return _t.html("QA.keepRight.ignore".concat(andComment));
83758             }).on('click.ignore', function (d3_event, d) {
83759               this.blur(); // avoid keeping focus on the button - #4641
83760
83761               var qaService = services.improveOSM;
83762
83763               if (qaService) {
83764                 d.newStatus = 'INVALID';
83765                 qaService.postUpdate(d, function (err, item) {
83766                   return dispatch.call('change', item);
83767                 });
83768               }
83769             });
83770           } // NOTE: Don't change method name until UI v3 is merged
83771
83772
83773           improveOsmEditor.error = function (val) {
83774             if (!arguments.length) return _qaItem;
83775             _qaItem = val;
83776             return improveOsmEditor;
83777           };
83778
83779           return utilRebind(improveOsmEditor, dispatch, 'on');
83780         }
83781
83782         function uiKeepRightDetails(context) {
83783           var _qaItem;
83784
83785           function issueDetail(d) {
83786             var itemType = d.itemType,
83787                 parentIssueType = d.parentIssueType;
83788             var unknown = _t.html('inspector.unknown');
83789             var replacements = d.replacements || {};
83790             replacements["default"] = unknown; // special key `default` works as a fallback string
83791
83792             var detail = _t.html("QA.keepRight.errorTypes.".concat(itemType, ".description"), replacements);
83793
83794             if (detail === unknown) {
83795               detail = _t.html("QA.keepRight.errorTypes.".concat(parentIssueType, ".description"), replacements);
83796             }
83797
83798             return detail;
83799           }
83800
83801           function keepRightDetails(selection) {
83802             var details = selection.selectAll('.error-details').data(_qaItem ? [_qaItem] : [], function (d) {
83803               return "".concat(d.id, "-").concat(d.status || 0);
83804             });
83805             details.exit().remove();
83806             var detailsEnter = details.enter().append('div').attr('class', 'error-details qa-details-container'); // description
83807
83808             var descriptionEnter = detailsEnter.append('div').attr('class', 'qa-details-subsection');
83809             descriptionEnter.append('h4').html(_t.html('QA.keepRight.detail_description'));
83810             descriptionEnter.append('div').attr('class', 'qa-details-description-text').html(issueDetail); // If there are entity links in the error message..
83811
83812             var relatedEntities = [];
83813             descriptionEnter.selectAll('.error_entity_link, .error_object_link').attr('href', '#').each(function () {
83814               var link = select(this);
83815               var isObjectLink = link.classed('error_object_link');
83816               var entityID = isObjectLink ? utilEntityRoot(_qaItem.objectType) + _qaItem.objectId : this.textContent;
83817               var entity = context.hasEntity(entityID);
83818               relatedEntities.push(entityID); // Add click handler
83819
83820               link.on('mouseenter', function () {
83821                 utilHighlightEntities([entityID], true, context);
83822               }).on('mouseleave', function () {
83823                 utilHighlightEntities([entityID], false, context);
83824               }).on('click', function (d3_event) {
83825                 d3_event.preventDefault();
83826                 utilHighlightEntities([entityID], false, context);
83827                 var osmlayer = context.layers().layer('osm');
83828
83829                 if (!osmlayer.enabled()) {
83830                   osmlayer.enabled(true);
83831                 }
83832
83833                 context.map().centerZoomEase(_qaItem.loc, 20);
83834
83835                 if (entity) {
83836                   context.enter(modeSelect(context, [entityID]));
83837                 } else {
83838                   context.loadEntity(entityID, function (err, result) {
83839                     if (err) return;
83840                     var entity = result.data.find(function (e) {
83841                       return e.id === entityID;
83842                     });
83843                     if (entity) context.enter(modeSelect(context, [entityID]));
83844                   });
83845                 }
83846               }); // Replace with friendly name if possible
83847               // (The entity may not yet be loaded into the graph)
83848
83849               if (entity) {
83850                 var name = utilDisplayName(entity); // try to use common name
83851
83852                 if (!name && !isObjectLink) {
83853                   var preset = _mainPresetIndex.match(entity, context.graph());
83854                   name = preset && !preset.isFallback() && preset.name(); // fallback to preset name
83855                 }
83856
83857                 if (name) {
83858                   this.innerText = name;
83859                 }
83860               }
83861             }); // Don't hide entities related to this issue - #5880
83862
83863             context.features().forceVisible(relatedEntities);
83864             context.map().pan([0, 0]); // trigger a redraw
83865           }
83866
83867           keepRightDetails.issue = function (val) {
83868             if (!arguments.length) return _qaItem;
83869             _qaItem = val;
83870             return keepRightDetails;
83871           };
83872
83873           return keepRightDetails;
83874         }
83875
83876         function uiKeepRightHeader() {
83877           var _qaItem;
83878
83879           function issueTitle(d) {
83880             var itemType = d.itemType,
83881                 parentIssueType = d.parentIssueType;
83882             var unknown = _t.html('inspector.unknown');
83883             var replacements = d.replacements || {};
83884             replacements["default"] = unknown; // special key `default` works as a fallback string
83885
83886             var title = _t.html("QA.keepRight.errorTypes.".concat(itemType, ".title"), replacements);
83887
83888             if (title === unknown) {
83889               title = _t.html("QA.keepRight.errorTypes.".concat(parentIssueType, ".title"), replacements);
83890             }
83891
83892             return title;
83893           }
83894
83895           function keepRightHeader(selection) {
83896             var header = selection.selectAll('.qa-header').data(_qaItem ? [_qaItem] : [], function (d) {
83897               return "".concat(d.id, "-").concat(d.status || 0);
83898             });
83899             header.exit().remove();
83900             var headerEnter = header.enter().append('div').attr('class', 'qa-header');
83901             var iconEnter = headerEnter.append('div').attr('class', 'qa-header-icon').classed('new', function (d) {
83902               return d.id < 0;
83903             });
83904             iconEnter.append('div').attr('class', function (d) {
83905               return "preset-icon-28 qaItem ".concat(d.service, " itemId-").concat(d.id, " itemType-").concat(d.parentIssueType);
83906             }).call(svgIcon('#iD-icon-bolt', 'qaItem-fill'));
83907             headerEnter.append('div').attr('class', 'qa-header-label').html(issueTitle);
83908           }
83909
83910           keepRightHeader.issue = function (val) {
83911             if (!arguments.length) return _qaItem;
83912             _qaItem = val;
83913             return keepRightHeader;
83914           };
83915
83916           return keepRightHeader;
83917         }
83918
83919         function uiViewOnKeepRight() {
83920           var _qaItem;
83921
83922           function viewOnKeepRight(selection) {
83923             var url;
83924
83925             if (services.keepRight && _qaItem instanceof QAItem) {
83926               url = services.keepRight.issueURL(_qaItem);
83927             }
83928
83929             var link = selection.selectAll('.view-on-keepRight').data(url ? [url] : []); // exit
83930
83931             link.exit().remove(); // enter
83932
83933             var linkEnter = link.enter().append('a').attr('class', 'view-on-keepRight').attr('target', '_blank').attr('rel', 'noopener') // security measure
83934             .attr('href', function (d) {
83935               return d;
83936             }).call(svgIcon('#iD-icon-out-link', 'inline'));
83937             linkEnter.append('span').html(_t.html('inspector.view_on_keepRight'));
83938           }
83939
83940           viewOnKeepRight.what = function (val) {
83941             if (!arguments.length) return _qaItem;
83942             _qaItem = val;
83943             return viewOnKeepRight;
83944           };
83945
83946           return viewOnKeepRight;
83947         }
83948
83949         function uiKeepRightEditor(context) {
83950           var dispatch = dispatch$8('change');
83951           var qaDetails = uiKeepRightDetails(context);
83952           var qaHeader = uiKeepRightHeader();
83953
83954           var _qaItem;
83955
83956           function keepRightEditor(selection) {
83957             var headerEnter = selection.selectAll('.header').data([0]).enter().append('div').attr('class', 'header fillL');
83958             headerEnter.append('button').attr('class', 'close').on('click', function () {
83959               return context.enter(modeBrowse(context));
83960             }).call(svgIcon('#iD-icon-close'));
83961             headerEnter.append('h3').html(_t.html('QA.keepRight.title'));
83962             var body = selection.selectAll('.body').data([0]);
83963             body = body.enter().append('div').attr('class', 'body').merge(body);
83964             var editor = body.selectAll('.qa-editor').data([0]);
83965             editor.enter().append('div').attr('class', 'modal-section qa-editor').merge(editor).call(qaHeader.issue(_qaItem)).call(qaDetails.issue(_qaItem)).call(keepRightSaveSection);
83966             var footer = selection.selectAll('.footer').data([0]);
83967             footer.enter().append('div').attr('class', 'footer').merge(footer).call(uiViewOnKeepRight().what(_qaItem));
83968           }
83969
83970           function keepRightSaveSection(selection) {
83971             var isSelected = _qaItem && _qaItem.id === context.selectedErrorID();
83972
83973             var isShown = _qaItem && (isSelected || _qaItem.newComment || _qaItem.comment);
83974             var saveSection = selection.selectAll('.qa-save').data(isShown ? [_qaItem] : [], function (d) {
83975               return "".concat(d.id, "-").concat(d.status || 0);
83976             }); // exit
83977
83978             saveSection.exit().remove(); // enter
83979
83980             var saveSectionEnter = saveSection.enter().append('div').attr('class', 'qa-save save-section cf');
83981             saveSectionEnter.append('h4').attr('class', '.qa-save-header').html(_t.html('QA.keepRight.comment'));
83982             saveSectionEnter.append('textarea').attr('class', 'new-comment-input').attr('placeholder', _t('QA.keepRight.comment_placeholder')).attr('maxlength', 1000).property('value', function (d) {
83983               return d.newComment || d.comment;
83984             }).call(utilNoAuto).on('input', changeInput).on('blur', changeInput); // update
83985
83986             saveSection = saveSectionEnter.merge(saveSection).call(qaSaveButtons);
83987
83988             function changeInput() {
83989               var input = select(this);
83990               var val = input.property('value').trim();
83991
83992               if (val === _qaItem.comment) {
83993                 val = undefined;
83994               } // store the unsaved comment with the issue itself
83995
83996
83997               _qaItem = _qaItem.update({
83998                 newComment: val
83999               });
84000               var qaService = services.keepRight;
84001
84002               if (qaService) {
84003                 qaService.replaceItem(_qaItem); // update keepright cache
84004               }
84005
84006               saveSection.call(qaSaveButtons);
84007             }
84008           }
84009
84010           function qaSaveButtons(selection) {
84011             var isSelected = _qaItem && _qaItem.id === context.selectedErrorID();
84012
84013             var buttonSection = selection.selectAll('.buttons').data(isSelected ? [_qaItem] : [], function (d) {
84014               return d.status + d.id;
84015             }); // exit
84016
84017             buttonSection.exit().remove(); // enter
84018
84019             var buttonEnter = buttonSection.enter().append('div').attr('class', 'buttons');
84020             buttonEnter.append('button').attr('class', 'button comment-button action').html(_t.html('QA.keepRight.save_comment'));
84021             buttonEnter.append('button').attr('class', 'button close-button action');
84022             buttonEnter.append('button').attr('class', 'button ignore-button action'); // update
84023
84024             buttonSection = buttonSection.merge(buttonEnter);
84025             buttonSection.select('.comment-button') // select and propagate data
84026             .attr('disabled', function (d) {
84027               return d.newComment ? null : true;
84028             }).on('click.comment', function (d3_event, d) {
84029               this.blur(); // avoid keeping focus on the button - #4641
84030
84031               var qaService = services.keepRight;
84032
84033               if (qaService) {
84034                 qaService.postUpdate(d, function (err, item) {
84035                   return dispatch.call('change', item);
84036                 });
84037               }
84038             });
84039             buttonSection.select('.close-button') // select and propagate data
84040             .html(function (d) {
84041               var andComment = d.newComment ? '_comment' : '';
84042               return _t.html("QA.keepRight.close".concat(andComment));
84043             }).on('click.close', function (d3_event, d) {
84044               this.blur(); // avoid keeping focus on the button - #4641
84045
84046               var qaService = services.keepRight;
84047
84048               if (qaService) {
84049                 d.newStatus = 'ignore_t'; // ignore temporarily (item fixed)
84050
84051                 qaService.postUpdate(d, function (err, item) {
84052                   return dispatch.call('change', item);
84053                 });
84054               }
84055             });
84056             buttonSection.select('.ignore-button') // select and propagate data
84057             .html(function (d) {
84058               var andComment = d.newComment ? '_comment' : '';
84059               return _t.html("QA.keepRight.ignore".concat(andComment));
84060             }).on('click.ignore', function (d3_event, d) {
84061               this.blur(); // avoid keeping focus on the button - #4641
84062
84063               var qaService = services.keepRight;
84064
84065               if (qaService) {
84066                 d.newStatus = 'ignore'; // ignore permanently (false positive)
84067
84068                 qaService.postUpdate(d, function (err, item) {
84069                   return dispatch.call('change', item);
84070                 });
84071               }
84072             });
84073           } // NOTE: Don't change method name until UI v3 is merged
84074
84075
84076           keepRightEditor.error = function (val) {
84077             if (!arguments.length) return _qaItem;
84078             _qaItem = val;
84079             return keepRightEditor;
84080           };
84081
84082           return utilRebind(keepRightEditor, dispatch, 'on');
84083         }
84084
84085         function uiOsmoseDetails(context) {
84086           var _qaItem;
84087
84088           function issueString(d, type) {
84089             if (!d) return ''; // Issue strings are cached from Osmose API
84090
84091             var s = services.osmose.getStrings(d.itemType);
84092             return type in s ? s[type] : '';
84093           }
84094
84095           function osmoseDetails(selection) {
84096             var details = selection.selectAll('.error-details').data(_qaItem ? [_qaItem] : [], function (d) {
84097               return "".concat(d.id, "-").concat(d.status || 0);
84098             });
84099             details.exit().remove();
84100             var detailsEnter = details.enter().append('div').attr('class', 'error-details qa-details-container'); // Description
84101
84102             if (issueString(_qaItem, 'detail')) {
84103               var div = detailsEnter.append('div').attr('class', 'qa-details-subsection');
84104               div.append('h4').html(_t.html('QA.keepRight.detail_description'));
84105               div.append('p').attr('class', 'qa-details-description-text').html(function (d) {
84106                 return issueString(d, 'detail');
84107               }).selectAll('a').attr('rel', 'noopener').attr('target', '_blank');
84108             } // Elements (populated later as data is requested)
84109
84110
84111             var detailsDiv = detailsEnter.append('div').attr('class', 'qa-details-subsection');
84112             var elemsDiv = detailsEnter.append('div').attr('class', 'qa-details-subsection'); // Suggested Fix (mustn't exist for every issue type)
84113
84114             if (issueString(_qaItem, 'fix')) {
84115               var _div = detailsEnter.append('div').attr('class', 'qa-details-subsection');
84116
84117               _div.append('h4').html(_t.html('QA.osmose.fix_title'));
84118
84119               _div.append('p').html(function (d) {
84120                 return issueString(d, 'fix');
84121               }).selectAll('a').attr('rel', 'noopener').attr('target', '_blank');
84122             } // Common Pitfalls (mustn't exist for every issue type)
84123
84124
84125             if (issueString(_qaItem, 'trap')) {
84126               var _div2 = detailsEnter.append('div').attr('class', 'qa-details-subsection');
84127
84128               _div2.append('h4').html(_t.html('QA.osmose.trap_title'));
84129
84130               _div2.append('p').html(function (d) {
84131                 return issueString(d, 'trap');
84132               }).selectAll('a').attr('rel', 'noopener').attr('target', '_blank');
84133             } // Save current item to check if UI changed by time request resolves
84134
84135
84136             var thisItem = _qaItem;
84137             services.osmose.loadIssueDetail(_qaItem).then(function (d) {
84138               // No details to add if there are no associated issue elements
84139               if (!d.elems || d.elems.length === 0) return; // Do nothing if UI has moved on by the time this resolves
84140
84141               if (context.selectedErrorID() !== thisItem.id && context.container().selectAll(".qaItem.osmose.hover.itemId-".concat(thisItem.id)).empty()) return; // Things like keys and values are dynamically added to a subtitle string
84142
84143               if (d.detail) {
84144                 detailsDiv.append('h4').html(_t.html('QA.osmose.detail_title'));
84145                 detailsDiv.append('p').html(function (d) {
84146                   return d.detail;
84147                 }).selectAll('a').attr('rel', 'noopener').attr('target', '_blank');
84148               } // Create list of linked issue elements
84149
84150
84151               elemsDiv.append('h4').html(_t.html('QA.osmose.elems_title'));
84152               elemsDiv.append('ul').selectAll('li').data(d.elems).enter().append('li').append('a').attr('href', '#').attr('class', 'error_entity_link').html(function (d) {
84153                 return d;
84154               }).each(function () {
84155                 var link = select(this);
84156                 var entityID = this.textContent;
84157                 var entity = context.hasEntity(entityID); // Add click handler
84158
84159                 link.on('mouseenter', function () {
84160                   utilHighlightEntities([entityID], true, context);
84161                 }).on('mouseleave', function () {
84162                   utilHighlightEntities([entityID], false, context);
84163                 }).on('click', function (d3_event) {
84164                   d3_event.preventDefault();
84165                   utilHighlightEntities([entityID], false, context);
84166                   var osmlayer = context.layers().layer('osm');
84167
84168                   if (!osmlayer.enabled()) {
84169                     osmlayer.enabled(true);
84170                   }
84171
84172                   context.map().centerZoom(d.loc, 20);
84173
84174                   if (entity) {
84175                     context.enter(modeSelect(context, [entityID]));
84176                   } else {
84177                     context.loadEntity(entityID, function (err, result) {
84178                       if (err) return;
84179                       var entity = result.data.find(function (e) {
84180                         return e.id === entityID;
84181                       });
84182                       if (entity) context.enter(modeSelect(context, [entityID]));
84183                     });
84184                   }
84185                 }); // Replace with friendly name if possible
84186                 // (The entity may not yet be loaded into the graph)
84187
84188                 if (entity) {
84189                   var name = utilDisplayName(entity); // try to use common name
84190
84191                   if (!name) {
84192                     var preset = _mainPresetIndex.match(entity, context.graph());
84193                     name = preset && !preset.isFallback() && preset.name(); // fallback to preset name
84194                   }
84195
84196                   if (name) {
84197                     this.innerText = name;
84198                   }
84199                 }
84200               }); // Don't hide entities related to this issue - #5880
84201
84202               context.features().forceVisible(d.elems);
84203               context.map().pan([0, 0]); // trigger a redraw
84204             })["catch"](function (err) {
84205               console.log(err); // eslint-disable-line no-console
84206             });
84207           }
84208
84209           osmoseDetails.issue = function (val) {
84210             if (!arguments.length) return _qaItem;
84211             _qaItem = val;
84212             return osmoseDetails;
84213           };
84214
84215           return osmoseDetails;
84216         }
84217
84218         function uiOsmoseHeader() {
84219           var _qaItem;
84220
84221           function issueTitle(d) {
84222             var unknown = _t('inspector.unknown');
84223             if (!d) return unknown; // Issue titles supplied by Osmose
84224
84225             var s = services.osmose.getStrings(d.itemType);
84226             return 'title' in s ? s.title : unknown;
84227           }
84228
84229           function osmoseHeader(selection) {
84230             var header = selection.selectAll('.qa-header').data(_qaItem ? [_qaItem] : [], function (d) {
84231               return "".concat(d.id, "-").concat(d.status || 0);
84232             });
84233             header.exit().remove();
84234             var headerEnter = header.enter().append('div').attr('class', 'qa-header');
84235             var svgEnter = headerEnter.append('div').attr('class', 'qa-header-icon').classed('new', function (d) {
84236               return d.id < 0;
84237             }).append('svg').attr('width', '20px').attr('height', '30px').attr('viewbox', '0 0 20 30').attr('class', function (d) {
84238               return "preset-icon-28 qaItem ".concat(d.service, " itemId-").concat(d.id, " itemType-").concat(d.itemType);
84239             });
84240             svgEnter.append('polygon').attr('fill', function (d) {
84241               return services.osmose.getColor(d.item);
84242             }).attr('class', 'qaItem-fill').attr('points', '16,3 4,3 1,6 1,17 4,20 7,20 10,27 13,20 16,20 19,17.033 19,6');
84243             svgEnter.append('use').attr('class', 'icon-annotation').attr('width', '13px').attr('height', '13px').attr('transform', 'translate(3.5, 5)').attr('xlink:href', function (d) {
84244               var picon = d.icon;
84245
84246               if (!picon) {
84247                 return '';
84248               } else {
84249                 var isMaki = /^maki-/.test(picon);
84250                 return "#".concat(picon).concat(isMaki ? '-11' : '');
84251               }
84252             });
84253             headerEnter.append('div').attr('class', 'qa-header-label').html(issueTitle);
84254           }
84255
84256           osmoseHeader.issue = function (val) {
84257             if (!arguments.length) return _qaItem;
84258             _qaItem = val;
84259             return osmoseHeader;
84260           };
84261
84262           return osmoseHeader;
84263         }
84264
84265         function uiViewOnOsmose() {
84266           var _qaItem;
84267
84268           function viewOnOsmose(selection) {
84269             var url;
84270
84271             if (services.osmose && _qaItem instanceof QAItem) {
84272               url = services.osmose.itemURL(_qaItem);
84273             }
84274
84275             var link = selection.selectAll('.view-on-osmose').data(url ? [url] : []); // exit
84276
84277             link.exit().remove(); // enter
84278
84279             var linkEnter = link.enter().append('a').attr('class', 'view-on-osmose').attr('target', '_blank').attr('rel', 'noopener') // security measure
84280             .attr('href', function (d) {
84281               return d;
84282             }).call(svgIcon('#iD-icon-out-link', 'inline'));
84283             linkEnter.append('span').html(_t.html('inspector.view_on_osmose'));
84284           }
84285
84286           viewOnOsmose.what = function (val) {
84287             if (!arguments.length) return _qaItem;
84288             _qaItem = val;
84289             return viewOnOsmose;
84290           };
84291
84292           return viewOnOsmose;
84293         }
84294
84295         function uiOsmoseEditor(context) {
84296           var dispatch = dispatch$8('change');
84297           var qaDetails = uiOsmoseDetails(context);
84298           var qaHeader = uiOsmoseHeader();
84299
84300           var _qaItem;
84301
84302           function osmoseEditor(selection) {
84303             var header = selection.selectAll('.header').data([0]);
84304             var headerEnter = header.enter().append('div').attr('class', 'header fillL');
84305             headerEnter.append('button').attr('class', 'close').on('click', function () {
84306               return context.enter(modeBrowse(context));
84307             }).call(svgIcon('#iD-icon-close'));
84308             headerEnter.append('h3').html(_t.html('QA.osmose.title'));
84309             var body = selection.selectAll('.body').data([0]);
84310             body = body.enter().append('div').attr('class', 'body').merge(body);
84311             var editor = body.selectAll('.qa-editor').data([0]);
84312             editor.enter().append('div').attr('class', 'modal-section qa-editor').merge(editor).call(qaHeader.issue(_qaItem)).call(qaDetails.issue(_qaItem)).call(osmoseSaveSection);
84313             var footer = selection.selectAll('.footer').data([0]);
84314             footer.enter().append('div').attr('class', 'footer').merge(footer).call(uiViewOnOsmose().what(_qaItem));
84315           }
84316
84317           function osmoseSaveSection(selection) {
84318             var isSelected = _qaItem && _qaItem.id === context.selectedErrorID();
84319
84320             var isShown = _qaItem && isSelected;
84321             var saveSection = selection.selectAll('.qa-save').data(isShown ? [_qaItem] : [], function (d) {
84322               return "".concat(d.id, "-").concat(d.status || 0);
84323             }); // exit
84324
84325             saveSection.exit().remove(); // enter
84326
84327             var saveSectionEnter = saveSection.enter().append('div').attr('class', 'qa-save save-section cf'); // update
84328
84329             saveSection = saveSectionEnter.merge(saveSection).call(qaSaveButtons);
84330           }
84331
84332           function qaSaveButtons(selection) {
84333             var isSelected = _qaItem && _qaItem.id === context.selectedErrorID();
84334
84335             var buttonSection = selection.selectAll('.buttons').data(isSelected ? [_qaItem] : [], function (d) {
84336               return d.status + d.id;
84337             }); // exit
84338
84339             buttonSection.exit().remove(); // enter
84340
84341             var buttonEnter = buttonSection.enter().append('div').attr('class', 'buttons');
84342             buttonEnter.append('button').attr('class', 'button close-button action');
84343             buttonEnter.append('button').attr('class', 'button ignore-button action'); // update
84344
84345             buttonSection = buttonSection.merge(buttonEnter);
84346             buttonSection.select('.close-button').html(_t.html('QA.keepRight.close')).on('click.close', function (d3_event, d) {
84347               this.blur(); // avoid keeping focus on the button - #4641
84348
84349               var qaService = services.osmose;
84350
84351               if (qaService) {
84352                 d.newStatus = 'done';
84353                 qaService.postUpdate(d, function (err, item) {
84354                   return dispatch.call('change', item);
84355                 });
84356               }
84357             });
84358             buttonSection.select('.ignore-button').html(_t.html('QA.keepRight.ignore')).on('click.ignore', function (d3_event, d) {
84359               this.blur(); // avoid keeping focus on the button - #4641
84360
84361               var qaService = services.osmose;
84362
84363               if (qaService) {
84364                 d.newStatus = 'false';
84365                 qaService.postUpdate(d, function (err, item) {
84366                   return dispatch.call('change', item);
84367                 });
84368               }
84369             });
84370           } // NOTE: Don't change method name until UI v3 is merged
84371
84372
84373           osmoseEditor.error = function (val) {
84374             if (!arguments.length) return _qaItem;
84375             _qaItem = val;
84376             return osmoseEditor;
84377           };
84378
84379           return utilRebind(osmoseEditor, dispatch, 'on');
84380         }
84381
84382         function uiNoteComments() {
84383           var _note;
84384
84385           function noteComments(selection) {
84386             if (_note.isNew()) return; // don't draw .comments-container
84387
84388             var comments = selection.selectAll('.comments-container').data([0]);
84389             comments = comments.enter().append('div').attr('class', 'comments-container').merge(comments);
84390             var commentEnter = comments.selectAll('.comment').data(_note.comments).enter().append('div').attr('class', 'comment');
84391             commentEnter.append('div').attr('class', function (d) {
84392               return 'comment-avatar user-' + d.uid;
84393             }).call(svgIcon('#iD-icon-avatar', 'comment-avatar-icon'));
84394             var mainEnter = commentEnter.append('div').attr('class', 'comment-main');
84395             var metadataEnter = mainEnter.append('div').attr('class', 'comment-metadata');
84396             metadataEnter.append('div').attr('class', 'comment-author').each(function (d) {
84397               var selection = select(this);
84398               var osm = services.osm;
84399
84400               if (osm && d.user) {
84401                 selection = selection.append('a').attr('class', 'comment-author-link').attr('href', osm.userURL(d.user)).attr('target', '_blank');
84402               }
84403
84404               selection.html(function (d) {
84405                 return d.user || _t.html('note.anonymous');
84406               });
84407             });
84408             metadataEnter.append('div').attr('class', 'comment-date').html(function (d) {
84409               return _t('note.status.' + d.action, {
84410                 when: localeDateString(d.date)
84411               });
84412             });
84413             mainEnter.append('div').attr('class', 'comment-text').html(function (d) {
84414               return d.html;
84415             }).selectAll('a').attr('rel', 'noopener nofollow').attr('target', '_blank');
84416             comments.call(replaceAvatars);
84417           }
84418
84419           function replaceAvatars(selection) {
84420             var showThirdPartyIcons = corePreferences('preferences.privacy.thirdpartyicons') || 'true';
84421             var osm = services.osm;
84422             if (showThirdPartyIcons !== 'true' || !osm) return;
84423             var uids = {}; // gather uids in the comment thread
84424
84425             _note.comments.forEach(function (d) {
84426               if (d.uid) uids[d.uid] = true;
84427             });
84428
84429             Object.keys(uids).forEach(function (uid) {
84430               osm.loadUser(uid, function (err, user) {
84431                 if (!user || !user.image_url) return;
84432                 selection.selectAll('.comment-avatar.user-' + uid).html('').append('img').attr('class', 'icon comment-avatar-icon').attr('src', user.image_url).attr('alt', user.display_name);
84433               });
84434             });
84435           }
84436
84437           function localeDateString(s) {
84438             if (!s) return null;
84439             var options = {
84440               day: 'numeric',
84441               month: 'short',
84442               year: 'numeric'
84443             };
84444             s = s.replace(/-/g, '/'); // fix browser-specific Date() issues
84445
84446             var d = new Date(s);
84447             if (isNaN(d.getTime())) return null;
84448             return d.toLocaleDateString(_mainLocalizer.localeCode(), options);
84449           }
84450
84451           noteComments.note = function (val) {
84452             if (!arguments.length) return _note;
84453             _note = val;
84454             return noteComments;
84455           };
84456
84457           return noteComments;
84458         }
84459
84460         function uiNoteHeader() {
84461           var _note;
84462
84463           function noteHeader(selection) {
84464             var header = selection.selectAll('.note-header').data(_note ? [_note] : [], function (d) {
84465               return d.status + d.id;
84466             });
84467             header.exit().remove();
84468             var headerEnter = header.enter().append('div').attr('class', 'note-header');
84469             var iconEnter = headerEnter.append('div').attr('class', function (d) {
84470               return 'note-header-icon ' + d.status;
84471             }).classed('new', function (d) {
84472               return d.id < 0;
84473             });
84474             iconEnter.append('div').attr('class', 'preset-icon-28').call(svgIcon('#iD-icon-note', 'note-fill'));
84475             iconEnter.each(function (d) {
84476               var statusIcon;
84477
84478               if (d.id < 0) {
84479                 statusIcon = '#iD-icon-plus';
84480               } else if (d.status === 'open') {
84481                 statusIcon = '#iD-icon-close';
84482               } else {
84483                 statusIcon = '#iD-icon-apply';
84484               }
84485
84486               iconEnter.append('div').attr('class', 'note-icon-annotation').call(svgIcon(statusIcon, 'icon-annotation'));
84487             });
84488             headerEnter.append('div').attr('class', 'note-header-label').html(function (d) {
84489               if (_note.isNew()) {
84490                 return _t('note.new');
84491               }
84492
84493               return _t('note.note') + ' ' + d.id + ' ' + (d.status === 'closed' ? _t('note.closed') : '');
84494             });
84495           }
84496
84497           noteHeader.note = function (val) {
84498             if (!arguments.length) return _note;
84499             _note = val;
84500             return noteHeader;
84501           };
84502
84503           return noteHeader;
84504         }
84505
84506         function uiNoteReport() {
84507           var _note;
84508
84509           function noteReport(selection) {
84510             var url;
84511
84512             if (services.osm && _note instanceof osmNote && !_note.isNew()) {
84513               url = services.osm.noteReportURL(_note);
84514             }
84515
84516             var link = selection.selectAll('.note-report').data(url ? [url] : []); // exit
84517
84518             link.exit().remove(); // enter
84519
84520             var linkEnter = link.enter().append('a').attr('class', 'note-report').attr('target', '_blank').attr('href', function (d) {
84521               return d;
84522             }).call(svgIcon('#iD-icon-out-link', 'inline'));
84523             linkEnter.append('span').html(_t.html('note.report'));
84524           }
84525
84526           noteReport.note = function (val) {
84527             if (!arguments.length) return _note;
84528             _note = val;
84529             return noteReport;
84530           };
84531
84532           return noteReport;
84533         }
84534
84535         function uiNoteEditor(context) {
84536           var dispatch = dispatch$8('change');
84537           var noteComments = uiNoteComments();
84538           var noteHeader = uiNoteHeader(); // var formFields = uiFormFields(context);
84539
84540           var _note;
84541
84542           var _newNote; // var _fieldsArr;
84543
84544
84545           function noteEditor(selection) {
84546             var header = selection.selectAll('.header').data([0]);
84547             var headerEnter = header.enter().append('div').attr('class', 'header fillL');
84548             headerEnter.append('button').attr('class', 'close').on('click', function () {
84549               context.enter(modeBrowse(context));
84550             }).call(svgIcon('#iD-icon-close'));
84551             headerEnter.append('h3').html(_t.html('note.title'));
84552             var body = selection.selectAll('.body').data([0]);
84553             body = body.enter().append('div').attr('class', 'body').merge(body);
84554             var editor = body.selectAll('.note-editor').data([0]);
84555             editor.enter().append('div').attr('class', 'modal-section note-editor').merge(editor).call(noteHeader.note(_note)).call(noteComments.note(_note)).call(noteSaveSection);
84556             var footer = selection.selectAll('.footer').data([0]);
84557             footer.enter().append('div').attr('class', 'footer').merge(footer).call(uiViewOnOSM(context).what(_note)).call(uiNoteReport().note(_note)); // rerender the note editor on any auth change
84558
84559             var osm = services.osm;
84560
84561             if (osm) {
84562               osm.on('change.note-save', function () {
84563                 selection.call(noteEditor);
84564               });
84565             }
84566           }
84567
84568           function noteSaveSection(selection) {
84569             var isSelected = _note && _note.id === context.selectedNoteID();
84570
84571             var noteSave = selection.selectAll('.note-save').data(isSelected ? [_note] : [], function (d) {
84572               return d.status + d.id;
84573             }); // exit
84574
84575             noteSave.exit().remove(); // enter
84576
84577             var noteSaveEnter = noteSave.enter().append('div').attr('class', 'note-save save-section cf'); // // if new note, show categories to pick from
84578             // if (_note.isNew()) {
84579             //     var presets = presetManager;
84580             //     // NOTE: this key isn't a age and therefore there is no documentation (yet)
84581             //     _fieldsArr = [
84582             //         uiField(context, presets.field('category'), null, { show: true, revert: false }),
84583             //     ];
84584             //     _fieldsArr.forEach(function(field) {
84585             //         field
84586             //             .on('change', changeCategory);
84587             //     });
84588             //     noteSaveEnter
84589             //         .append('div')
84590             //         .attr('class', 'note-category')
84591             //         .call(formFields.fieldsArr(_fieldsArr));
84592             // }
84593             // function changeCategory() {
84594             //     // NOTE: perhaps there is a better way to get value
84595             //     var val = context.container().select('input[name=\'category\']:checked').property('__data__') || undefined;
84596             //     // store the unsaved category with the note itself
84597             //     _note = _note.update({ newCategory: val });
84598             //     var osm = services.osm;
84599             //     if (osm) {
84600             //         osm.replaceNote(_note);  // update note cache
84601             //     }
84602             //     noteSave
84603             //         .call(noteSaveButtons);
84604             // }
84605
84606             noteSaveEnter.append('h4').attr('class', '.note-save-header').html(function () {
84607               return _note.isNew() ? _t('note.newDescription') : _t('note.newComment');
84608             });
84609             var commentTextarea = noteSaveEnter.append('textarea').attr('class', 'new-comment-input').attr('placeholder', _t('note.inputPlaceholder')).attr('maxlength', 1000).property('value', function (d) {
84610               return d.newComment;
84611             }).call(utilNoAuto).on('keydown.note-input', keydown).on('input.note-input', changeInput).on('blur.note-input', changeInput);
84612
84613             if (!commentTextarea.empty() && _newNote) {
84614               // autofocus the comment field for new notes
84615               commentTextarea.node().focus();
84616             } // update
84617
84618
84619             noteSave = noteSaveEnter.merge(noteSave).call(userDetails).call(noteSaveButtons); // fast submit if user presses cmd+enter
84620
84621             function keydown(d3_event) {
84622               if (!(d3_event.keyCode === 13 && // ↩ Return
84623               d3_event.metaKey)) return;
84624               var osm = services.osm;
84625               if (!osm) return;
84626               var hasAuth = osm.authenticated();
84627               if (!hasAuth) return;
84628               if (!_note.newComment) return;
84629               d3_event.preventDefault();
84630               select(this).on('keydown.note-input', null); // focus on button and submit
84631
84632               window.setTimeout(function () {
84633                 if (_note.isNew()) {
84634                   noteSave.selectAll('.save-button').node().focus();
84635                   clickSave();
84636                 } else {
84637                   noteSave.selectAll('.comment-button').node().focus();
84638                   clickComment();
84639                 }
84640               }, 10);
84641             }
84642
84643             function changeInput() {
84644               var input = select(this);
84645               var val = input.property('value').trim() || undefined; // store the unsaved comment with the note itself
84646
84647               _note = _note.update({
84648                 newComment: val
84649               });
84650               var osm = services.osm;
84651
84652               if (osm) {
84653                 osm.replaceNote(_note); // update note cache
84654               }
84655
84656               noteSave.call(noteSaveButtons);
84657             }
84658           }
84659
84660           function userDetails(selection) {
84661             var detailSection = selection.selectAll('.detail-section').data([0]);
84662             detailSection = detailSection.enter().append('div').attr('class', 'detail-section').merge(detailSection);
84663             var osm = services.osm;
84664             if (!osm) return; // Add warning if user is not logged in
84665
84666             var hasAuth = osm.authenticated();
84667             var authWarning = detailSection.selectAll('.auth-warning').data(hasAuth ? [] : [0]);
84668             authWarning.exit().transition().duration(200).style('opacity', 0).remove();
84669             var authEnter = authWarning.enter().insert('div', '.tag-reference-body').attr('class', 'field-warning auth-warning').style('opacity', 0);
84670             authEnter.call(svgIcon('#iD-icon-alert', 'inline'));
84671             authEnter.append('span').html(_t.html('note.login'));
84672             authEnter.append('a').attr('target', '_blank').call(svgIcon('#iD-icon-out-link', 'inline')).append('span').html(_t.html('login')).on('click.note-login', function (d3_event) {
84673               d3_event.preventDefault();
84674               osm.authenticate();
84675             });
84676             authEnter.transition().duration(200).style('opacity', 1);
84677             var prose = detailSection.selectAll('.note-save-prose').data(hasAuth ? [0] : []);
84678             prose.exit().remove();
84679             prose = prose.enter().append('p').attr('class', 'note-save-prose').html(_t.html('note.upload_explanation')).merge(prose);
84680             osm.userDetails(function (err, user) {
84681               if (err) return;
84682               var userLink = select(document.createElement('div'));
84683
84684               if (user.image_url) {
84685                 userLink.append('img').attr('src', user.image_url).attr('class', 'icon pre-text user-icon');
84686               }
84687
84688               userLink.append('a').attr('class', 'user-info').html(user.display_name).attr('href', osm.userURL(user.display_name)).attr('target', '_blank');
84689               prose.html(_t.html('note.upload_explanation_with_user', {
84690                 user: userLink.html()
84691               }));
84692             });
84693           }
84694
84695           function noteSaveButtons(selection) {
84696             var osm = services.osm;
84697             var hasAuth = osm && osm.authenticated();
84698
84699             var isSelected = _note && _note.id === context.selectedNoteID();
84700
84701             var buttonSection = selection.selectAll('.buttons').data(isSelected ? [_note] : [], function (d) {
84702               return d.status + d.id;
84703             }); // exit
84704
84705             buttonSection.exit().remove(); // enter
84706
84707             var buttonEnter = buttonSection.enter().append('div').attr('class', 'buttons');
84708
84709             if (_note.isNew()) {
84710               buttonEnter.append('button').attr('class', 'button cancel-button secondary-action').html(_t.html('confirm.cancel'));
84711               buttonEnter.append('button').attr('class', 'button save-button action').html(_t.html('note.save'));
84712             } else {
84713               buttonEnter.append('button').attr('class', 'button status-button action');
84714               buttonEnter.append('button').attr('class', 'button comment-button action').html(_t.html('note.comment'));
84715             } // update
84716
84717
84718             buttonSection = buttonSection.merge(buttonEnter);
84719             buttonSection.select('.cancel-button') // select and propagate data
84720             .on('click.cancel', clickCancel);
84721             buttonSection.select('.save-button') // select and propagate data
84722             .attr('disabled', isSaveDisabled).on('click.save', clickSave);
84723             buttonSection.select('.status-button') // select and propagate data
84724             .attr('disabled', hasAuth ? null : true).html(function (d) {
84725               var action = d.status === 'open' ? 'close' : 'open';
84726               var andComment = d.newComment ? '_comment' : '';
84727               return _t('note.' + action + andComment);
84728             }).on('click.status', clickStatus);
84729             buttonSection.select('.comment-button') // select and propagate data
84730             .attr('disabled', isSaveDisabled).on('click.comment', clickComment);
84731
84732             function isSaveDisabled(d) {
84733               return hasAuth && d.status === 'open' && d.newComment ? null : true;
84734             }
84735           }
84736
84737           function clickCancel(d3_event, d) {
84738             this.blur(); // avoid keeping focus on the button - #4641
84739
84740             var osm = services.osm;
84741
84742             if (osm) {
84743               osm.removeNote(d);
84744             }
84745
84746             context.enter(modeBrowse(context));
84747             dispatch.call('change');
84748           }
84749
84750           function clickSave(d3_event, d) {
84751             this.blur(); // avoid keeping focus on the button - #4641
84752
84753             var osm = services.osm;
84754
84755             if (osm) {
84756               osm.postNoteCreate(d, function (err, note) {
84757                 dispatch.call('change', note);
84758               });
84759             }
84760           }
84761
84762           function clickStatus(d3_event, d) {
84763             this.blur(); // avoid keeping focus on the button - #4641
84764
84765             var osm = services.osm;
84766
84767             if (osm) {
84768               var setStatus = d.status === 'open' ? 'closed' : 'open';
84769               osm.postNoteUpdate(d, setStatus, function (err, note) {
84770                 dispatch.call('change', note);
84771               });
84772             }
84773           }
84774
84775           function clickComment(d3_event, d) {
84776             this.blur(); // avoid keeping focus on the button - #4641
84777
84778             var osm = services.osm;
84779
84780             if (osm) {
84781               osm.postNoteUpdate(d, d.status, function (err, note) {
84782                 dispatch.call('change', note);
84783               });
84784             }
84785           }
84786
84787           noteEditor.note = function (val) {
84788             if (!arguments.length) return _note;
84789             _note = val;
84790             return noteEditor;
84791           };
84792
84793           noteEditor.newNote = function (val) {
84794             if (!arguments.length) return _newNote;
84795             _newNote = val;
84796             return noteEditor;
84797           };
84798
84799           return utilRebind(noteEditor, dispatch, 'on');
84800         }
84801
84802         function uiSidebar(context) {
84803           var inspector = uiInspector(context);
84804           var dataEditor = uiDataEditor(context);
84805           var noteEditor = uiNoteEditor(context);
84806           var improveOsmEditor = uiImproveOsmEditor(context);
84807           var keepRightEditor = uiKeepRightEditor(context);
84808           var osmoseEditor = uiOsmoseEditor(context);
84809
84810           var _current;
84811
84812           var _wasData = false;
84813           var _wasNote = false;
84814           var _wasQaItem = false; // use pointer events on supported platforms; fallback to mouse events
84815
84816           var _pointerPrefix = 'PointerEvent' in window ? 'pointer' : 'mouse';
84817
84818           function sidebar(selection) {
84819             var container = context.container();
84820             var minWidth = 240;
84821             var sidebarWidth;
84822             var containerWidth;
84823             var dragOffset; // Set the initial width constraints
84824
84825             selection.style('min-width', minWidth + 'px').style('max-width', '400px').style('width', '33.3333%');
84826             var resizer = selection.append('div').attr('class', 'sidebar-resizer').on(_pointerPrefix + 'down.sidebar-resizer', pointerdown);
84827             var downPointerId, lastClientX, containerLocGetter;
84828
84829             function pointerdown(d3_event) {
84830               if (downPointerId) return;
84831               if ('button' in d3_event && d3_event.button !== 0) return;
84832               downPointerId = d3_event.pointerId || 'mouse';
84833               lastClientX = d3_event.clientX;
84834               containerLocGetter = utilFastMouse(container.node()); // offset from edge of sidebar-resizer
84835
84836               dragOffset = utilFastMouse(resizer.node())(d3_event)[0] - 1;
84837               sidebarWidth = selection.node().getBoundingClientRect().width;
84838               containerWidth = container.node().getBoundingClientRect().width;
84839               var widthPct = sidebarWidth / containerWidth * 100;
84840               selection.style('width', widthPct + '%') // lock in current width
84841               .style('max-width', '85%'); // but allow larger widths
84842
84843               resizer.classed('dragging', true);
84844               select(window).on('touchmove.sidebar-resizer', function (d3_event) {
84845                 // disable page scrolling while resizing on touch input
84846                 d3_event.preventDefault();
84847               }, {
84848                 passive: false
84849               }).on(_pointerPrefix + 'move.sidebar-resizer', pointermove).on(_pointerPrefix + 'up.sidebar-resizer pointercancel.sidebar-resizer', pointerup);
84850             }
84851
84852             function pointermove(d3_event) {
84853               if (downPointerId !== (d3_event.pointerId || 'mouse')) return;
84854               d3_event.preventDefault();
84855               var dx = d3_event.clientX - lastClientX;
84856               lastClientX = d3_event.clientX;
84857               var isRTL = _mainLocalizer.textDirection() === 'rtl';
84858               var scaleX = isRTL ? 0 : 1;
84859               var xMarginProperty = isRTL ? 'margin-right' : 'margin-left';
84860               var x = containerLocGetter(d3_event)[0] - dragOffset;
84861               sidebarWidth = isRTL ? containerWidth - x : x;
84862               var isCollapsed = selection.classed('collapsed');
84863               var shouldCollapse = sidebarWidth < minWidth;
84864               selection.classed('collapsed', shouldCollapse);
84865
84866               if (shouldCollapse) {
84867                 if (!isCollapsed) {
84868                   selection.style(xMarginProperty, '-400px').style('width', '400px');
84869                   context.ui().onResize([(sidebarWidth - dx) * scaleX, 0]);
84870                 }
84871               } else {
84872                 var widthPct = sidebarWidth / containerWidth * 100;
84873                 selection.style(xMarginProperty, null).style('width', widthPct + '%');
84874
84875                 if (isCollapsed) {
84876                   context.ui().onResize([-sidebarWidth * scaleX, 0]);
84877                 } else {
84878                   context.ui().onResize([-dx * scaleX, 0]);
84879                 }
84880               }
84881             }
84882
84883             function pointerup(d3_event) {
84884               if (downPointerId !== (d3_event.pointerId || 'mouse')) return;
84885               downPointerId = null;
84886               resizer.classed('dragging', false);
84887               select(window).on('touchmove.sidebar-resizer', null).on(_pointerPrefix + 'move.sidebar-resizer', null).on(_pointerPrefix + 'up.sidebar-resizer pointercancel.sidebar-resizer', null);
84888             }
84889
84890             var featureListWrap = selection.append('div').attr('class', 'feature-list-pane').call(uiFeatureList(context));
84891             var inspectorWrap = selection.append('div').attr('class', 'inspector-hidden inspector-wrap');
84892
84893             var hoverModeSelect = function hoverModeSelect(targets) {
84894               context.container().selectAll('.feature-list-item button').classed('hover', false);
84895
84896               if (context.selectedIDs().length > 1 && targets && targets.length) {
84897                 var elements = context.container().selectAll('.feature-list-item button').filter(function (node) {
84898                   return targets.indexOf(node) !== -1;
84899                 });
84900
84901                 if (!elements.empty()) {
84902                   elements.classed('hover', true);
84903                 }
84904               }
84905             };
84906
84907             sidebar.hoverModeSelect = throttle(hoverModeSelect, 200);
84908
84909             function hover(targets) {
84910               var datum = targets && targets.length && targets[0];
84911
84912               if (datum && datum.__featurehash__) {
84913                 // hovering on data
84914                 _wasData = true;
84915                 sidebar.show(dataEditor.datum(datum));
84916                 selection.selectAll('.sidebar-component').classed('inspector-hover', true);
84917               } else if (datum instanceof osmNote) {
84918                 if (context.mode().id === 'drag-note') return;
84919                 _wasNote = true;
84920                 var osm = services.osm;
84921
84922                 if (osm) {
84923                   datum = osm.getNote(datum.id); // marker may contain stale data - get latest
84924                 }
84925
84926                 sidebar.show(noteEditor.note(datum));
84927                 selection.selectAll('.sidebar-component').classed('inspector-hover', true);
84928               } else if (datum instanceof QAItem) {
84929                 _wasQaItem = true;
84930                 var errService = services[datum.service];
84931
84932                 if (errService) {
84933                   // marker may contain stale data - get latest
84934                   datum = errService.getError(datum.id);
84935                 } // Currently only three possible services
84936
84937
84938                 var errEditor;
84939
84940                 if (datum.service === 'keepRight') {
84941                   errEditor = keepRightEditor;
84942                 } else if (datum.service === 'osmose') {
84943                   errEditor = osmoseEditor;
84944                 } else {
84945                   errEditor = improveOsmEditor;
84946                 }
84947
84948                 context.container().selectAll('.qaItem.' + datum.service).classed('hover', function (d) {
84949                   return d.id === datum.id;
84950                 });
84951                 sidebar.show(errEditor.error(datum));
84952                 selection.selectAll('.sidebar-component').classed('inspector-hover', true);
84953               } else if (!_current && datum instanceof osmEntity) {
84954                 featureListWrap.classed('inspector-hidden', true);
84955                 inspectorWrap.classed('inspector-hidden', false).classed('inspector-hover', true);
84956
84957                 if (!inspector.entityIDs() || !utilArrayIdentical(inspector.entityIDs(), [datum.id]) || inspector.state() !== 'hover') {
84958                   inspector.state('hover').entityIDs([datum.id]).newFeature(false);
84959                   inspectorWrap.call(inspector);
84960                 }
84961               } else if (!_current) {
84962                 featureListWrap.classed('inspector-hidden', false);
84963                 inspectorWrap.classed('inspector-hidden', true);
84964                 inspector.state('hide');
84965               } else if (_wasData || _wasNote || _wasQaItem) {
84966                 _wasNote = false;
84967                 _wasData = false;
84968                 _wasQaItem = false;
84969                 context.container().selectAll('.note').classed('hover', false);
84970                 context.container().selectAll('.qaItem').classed('hover', false);
84971                 sidebar.hide();
84972               }
84973             }
84974
84975             sidebar.hover = throttle(hover, 200);
84976
84977             sidebar.intersects = function (extent) {
84978               var rect = selection.node().getBoundingClientRect();
84979               return extent.intersects([context.projection.invert([0, rect.height]), context.projection.invert([rect.width, 0])]);
84980             };
84981
84982             sidebar.select = function (ids, newFeature) {
84983               sidebar.hide();
84984
84985               if (ids && ids.length) {
84986                 var entity = ids.length === 1 && context.entity(ids[0]);
84987
84988                 if (entity && newFeature && selection.classed('collapsed')) {
84989                   // uncollapse the sidebar
84990                   var extent = entity.extent(context.graph());
84991                   sidebar.expand(sidebar.intersects(extent));
84992                 }
84993
84994                 featureListWrap.classed('inspector-hidden', true);
84995                 inspectorWrap.classed('inspector-hidden', false).classed('inspector-hover', false); // reload the UI even if the ids are the same since the entities
84996                 // themselves may have changed
84997
84998                 inspector.state('select').entityIDs(ids).newFeature(newFeature);
84999                 inspectorWrap.call(inspector);
85000               } else {
85001                 inspector.state('hide');
85002               }
85003             };
85004
85005             sidebar.showPresetList = function () {
85006               inspector.showList();
85007             };
85008
85009             sidebar.show = function (component, element) {
85010               featureListWrap.classed('inspector-hidden', true);
85011               inspectorWrap.classed('inspector-hidden', true);
85012               if (_current) _current.remove();
85013               _current = selection.append('div').attr('class', 'sidebar-component').call(component, element);
85014             };
85015
85016             sidebar.hide = function () {
85017               featureListWrap.classed('inspector-hidden', false);
85018               inspectorWrap.classed('inspector-hidden', true);
85019               if (_current) _current.remove();
85020               _current = null;
85021             };
85022
85023             sidebar.expand = function (moveMap) {
85024               if (selection.classed('collapsed')) {
85025                 sidebar.toggle(moveMap);
85026               }
85027             };
85028
85029             sidebar.collapse = function (moveMap) {
85030               if (!selection.classed('collapsed')) {
85031                 sidebar.toggle(moveMap);
85032               }
85033             };
85034
85035             sidebar.toggle = function (moveMap) {
85036               // Don't allow sidebar to toggle when the user is in the walkthrough.
85037               if (context.inIntro()) return;
85038               var isCollapsed = selection.classed('collapsed');
85039               var isCollapsing = !isCollapsed;
85040               var isRTL = _mainLocalizer.textDirection() === 'rtl';
85041               var scaleX = isRTL ? 0 : 1;
85042               var xMarginProperty = isRTL ? 'margin-right' : 'margin-left';
85043               sidebarWidth = selection.node().getBoundingClientRect().width; // switch from % to px
85044
85045               selection.style('width', sidebarWidth + 'px');
85046               var startMargin, endMargin, lastMargin;
85047
85048               if (isCollapsing) {
85049                 startMargin = lastMargin = 0;
85050                 endMargin = -sidebarWidth;
85051               } else {
85052                 startMargin = lastMargin = -sidebarWidth;
85053                 endMargin = 0;
85054               }
85055
85056               if (!isCollapsing) {
85057                 // unhide the sidebar's content before it transitions onscreen
85058                 selection.classed('collapsed', isCollapsing);
85059               }
85060
85061               selection.transition().style(xMarginProperty, endMargin + 'px').tween('panner', function () {
85062                 var i = d3_interpolateNumber(startMargin, endMargin);
85063                 return function (t) {
85064                   var dx = lastMargin - Math.round(i(t));
85065                   lastMargin = lastMargin - dx;
85066                   context.ui().onResize(moveMap ? undefined : [dx * scaleX, 0]);
85067                 };
85068               }).on('end', function () {
85069                 if (isCollapsing) {
85070                   // hide the sidebar's content after it transitions offscreen
85071                   selection.classed('collapsed', isCollapsing);
85072                 } // switch back from px to %
85073
85074
85075                 if (!isCollapsing) {
85076                   var containerWidth = container.node().getBoundingClientRect().width;
85077                   var widthPct = sidebarWidth / containerWidth * 100;
85078                   selection.style(xMarginProperty, null).style('width', widthPct + '%');
85079                 }
85080               });
85081             }; // toggle the sidebar collapse when double-clicking the resizer
85082
85083
85084             resizer.on('dblclick', function (d3_event) {
85085               d3_event.preventDefault();
85086
85087               if (d3_event.sourceEvent) {
85088                 d3_event.sourceEvent.preventDefault();
85089               }
85090
85091               sidebar.toggle();
85092             }); // ensure hover sidebar is closed when zooming out beyond editable zoom
85093
85094             context.map().on('crossEditableZoom.sidebar', function (within) {
85095               if (!within && !selection.select('.inspector-hover').empty()) {
85096                 hover([]);
85097               }
85098             });
85099           }
85100
85101           sidebar.showPresetList = function () {};
85102
85103           sidebar.hover = function () {};
85104
85105           sidebar.hover.cancel = function () {};
85106
85107           sidebar.intersects = function () {};
85108
85109           sidebar.select = function () {};
85110
85111           sidebar.show = function () {};
85112
85113           sidebar.hide = function () {};
85114
85115           sidebar.expand = function () {};
85116
85117           sidebar.collapse = function () {};
85118
85119           sidebar.toggle = function () {};
85120
85121           return sidebar;
85122         }
85123
85124         function uiSourceSwitch(context) {
85125           var keys;
85126
85127           function click(d3_event) {
85128             d3_event.preventDefault();
85129             var osm = context.connection();
85130             if (!osm) return;
85131             if (context.inIntro()) return;
85132             if (context.history().hasChanges() && !window.confirm(_t('source_switch.lose_changes'))) return;
85133             var isLive = select(this).classed('live');
85134             isLive = !isLive;
85135             context.enter(modeBrowse(context));
85136             context.history().clearSaved(); // remove saved history
85137
85138             context.flush(); // remove stored data
85139
85140             select(this).html(isLive ? _t.html('source_switch.live') : _t.html('source_switch.dev')).classed('live', isLive).classed('chip', isLive);
85141             osm["switch"](isLive ? keys[0] : keys[1]); // switch connection (warning: dispatches 'change' event)
85142           }
85143
85144           var sourceSwitch = function sourceSwitch(selection) {
85145             selection.append('a').attr('href', '#').html(_t.html('source_switch.live')).attr('class', 'live chip').on('click', click);
85146           };
85147
85148           sourceSwitch.keys = function (_) {
85149             if (!arguments.length) return keys;
85150             keys = _;
85151             return sourceSwitch;
85152           };
85153
85154           return sourceSwitch;
85155         }
85156
85157         function uiSpinner(context) {
85158           var osm = context.connection();
85159           return function (selection) {
85160             var img = selection.append('img').attr('src', context.imagePath('loader-black.gif')).style('opacity', 0);
85161
85162             if (osm) {
85163               osm.on('loading.spinner', function () {
85164                 img.transition().style('opacity', 1);
85165               }).on('loaded.spinner', function () {
85166                 img.transition().style('opacity', 0);
85167               });
85168             }
85169           };
85170         }
85171
85172         function uiSplash(context) {
85173           return function (selection) {
85174             // Exception - if there are restorable changes, skip this splash screen.
85175             // This is because we currently only support one `uiModal` at a time
85176             //  and we need to show them `uiRestore`` instead of this one.
85177             if (context.history().hasRestorableChanges()) return; // If user has not seen this version of the privacy policy, show the splash again.
85178
85179             var updateMessage = '';
85180             var sawPrivacyVersion = corePreferences('sawPrivacyVersion');
85181             var showSplash = !corePreferences('sawSplash');
85182
85183             if (sawPrivacyVersion !== context.privacyVersion) {
85184               updateMessage = _t('splash.privacy_update');
85185               showSplash = true;
85186             }
85187
85188             if (!showSplash) return;
85189             corePreferences('sawSplash', true);
85190             corePreferences('sawPrivacyVersion', context.privacyVersion); // fetch intro graph data now, while user is looking at the splash screen
85191
85192             _mainFileFetcher.get('intro_graph');
85193             var modalSelection = uiModal(selection);
85194             modalSelection.select('.modal').attr('class', 'modal-splash modal');
85195             var introModal = modalSelection.select('.content').append('div').attr('class', 'fillL');
85196             introModal.append('div').attr('class', 'modal-section').append('h3').html(_t.html('splash.welcome'));
85197             var modalSection = introModal.append('div').attr('class', 'modal-section');
85198             modalSection.append('p').html(_t.html('splash.text', {
85199               version: context.version,
85200               website: '<a target="_blank" href="https://github.com/openstreetmap/iD/blob/develop/CHANGELOG.md#whats-new">changelog</a>',
85201               github: '<a target="_blank" href="https://github.com/openstreetmap/iD/issues">github.com</a>'
85202             }));
85203             modalSection.append('p').html(_t.html('splash.privacy', {
85204               updateMessage: updateMessage,
85205               privacyLink: '<a target="_blank" href="https://github.com/openstreetmap/iD/blob/release/PRIVACY.md">' + _t('splash.privacy_policy') + '</a>'
85206             }));
85207             var buttonWrap = introModal.append('div').attr('class', 'modal-actions');
85208             var walkthrough = buttonWrap.append('button').attr('class', 'walkthrough').on('click', function () {
85209               context.container().call(uiIntro(context));
85210               modalSelection.close();
85211             });
85212             walkthrough.append('svg').attr('class', 'logo logo-walkthrough').append('use').attr('xlink:href', '#iD-logo-walkthrough');
85213             walkthrough.append('div').html(_t.html('splash.walkthrough'));
85214             var startEditing = buttonWrap.append('button').attr('class', 'start-editing').on('click', modalSelection.close);
85215             startEditing.append('svg').attr('class', 'logo logo-features').append('use').attr('xlink:href', '#iD-logo-features');
85216             startEditing.append('div').html(_t.html('splash.start'));
85217             modalSelection.select('button.close').attr('class', 'hide');
85218           };
85219         }
85220
85221         function uiStatus(context) {
85222           var osm = context.connection();
85223           return function (selection) {
85224             if (!osm) return;
85225
85226             function update(err, apiStatus) {
85227               selection.html('');
85228
85229               if (err) {
85230                 if (apiStatus === 'connectionSwitched') {
85231                   // if the connection was just switched, we can't rely on
85232                   // the status (we're getting the status of the previous api)
85233                   return;
85234                 } else if (apiStatus === 'rateLimited') {
85235                   selection.html(_t.html('osm_api_status.message.rateLimit')).append('a').attr('href', '#').attr('class', 'api-status-login').attr('target', '_blank').call(svgIcon('#iD-icon-out-link', 'inline')).append('span').html(_t.html('login')).on('click.login', function (d3_event) {
85236                     d3_event.preventDefault();
85237                     osm.authenticate();
85238                   });
85239                 } else {
85240                   // don't allow retrying too rapidly
85241                   var throttledRetry = throttle(function () {
85242                     // try loading the visible tiles
85243                     context.loadTiles(context.projection); // manually reload the status too in case all visible tiles were already loaded
85244
85245                     osm.reloadApiStatus();
85246                   }, 2000); // eslint-disable-next-line no-warning-comments
85247                   // TODO: nice messages for different error types
85248
85249
85250                   selection.html(_t.html('osm_api_status.message.error') + ' ').append('a').attr('href', '#') // let the user manually retry their connection directly
85251                   .html(_t.html('osm_api_status.retry')).on('click.retry', function (d3_event) {
85252                     d3_event.preventDefault();
85253                     throttledRetry();
85254                   });
85255                 }
85256               } else if (apiStatus === 'readonly') {
85257                 selection.html(_t.html('osm_api_status.message.readonly'));
85258               } else if (apiStatus === 'offline') {
85259                 selection.html(_t.html('osm_api_status.message.offline'));
85260               }
85261
85262               selection.attr('class', 'api-status ' + (err ? 'error' : apiStatus));
85263             }
85264
85265             osm.on('apiStatusChange.uiStatus', update); // reload the status periodically regardless of other factors
85266
85267             window.setInterval(function () {
85268               osm.reloadApiStatus();
85269             }, 90000); // load the initial status in case no OSM data was loaded yet
85270
85271             osm.reloadApiStatus();
85272           };
85273         }
85274
85275         function modeDrawArea(context, wayID, startGraph, button) {
85276           var mode = {
85277             button: button,
85278             id: 'draw-area'
85279           };
85280           var behavior = behaviorDrawWay(context, wayID, mode, startGraph).on('rejectedSelfIntersection.modeDrawArea', function () {
85281             context.ui().flash.iconName('#iD-icon-no').label(_t('self_intersection.error.areas'))();
85282           });
85283           mode.wayID = wayID;
85284
85285           mode.enter = function () {
85286             context.install(behavior);
85287           };
85288
85289           mode.exit = function () {
85290             context.uninstall(behavior);
85291           };
85292
85293           mode.selectedIDs = function () {
85294             return [wayID];
85295           };
85296
85297           mode.activeID = function () {
85298             return behavior && behavior.activeID() || [];
85299           };
85300
85301           return mode;
85302         }
85303
85304         function modeAddArea(context, mode) {
85305           mode.id = 'add-area';
85306           var behavior = behaviorAddWay(context).on('start', start).on('startFromWay', startFromWay).on('startFromNode', startFromNode);
85307           var defaultTags = {
85308             area: 'yes'
85309           };
85310           if (mode.preset) defaultTags = mode.preset.setTags(defaultTags, 'area');
85311
85312           function actionClose(wayId) {
85313             return function (graph) {
85314               return graph.replace(graph.entity(wayId).close());
85315             };
85316           }
85317
85318           function start(loc) {
85319             var startGraph = context.graph();
85320             var node = osmNode({
85321               loc: loc
85322             });
85323             var way = osmWay({
85324               tags: defaultTags
85325             });
85326             context.perform(actionAddEntity(node), actionAddEntity(way), actionAddVertex(way.id, node.id), actionClose(way.id));
85327             context.enter(modeDrawArea(context, way.id, startGraph, mode.button));
85328           }
85329
85330           function startFromWay(loc, edge) {
85331             var startGraph = context.graph();
85332             var node = osmNode({
85333               loc: loc
85334             });
85335             var way = osmWay({
85336               tags: defaultTags
85337             });
85338             context.perform(actionAddEntity(node), actionAddEntity(way), actionAddVertex(way.id, node.id), actionClose(way.id), actionAddMidpoint({
85339               loc: loc,
85340               edge: edge
85341             }, node));
85342             context.enter(modeDrawArea(context, way.id, startGraph, mode.button));
85343           }
85344
85345           function startFromNode(node) {
85346             var startGraph = context.graph();
85347             var way = osmWay({
85348               tags: defaultTags
85349             });
85350             context.perform(actionAddEntity(way), actionAddVertex(way.id, node.id), actionClose(way.id));
85351             context.enter(modeDrawArea(context, way.id, startGraph, mode.button));
85352           }
85353
85354           mode.enter = function () {
85355             context.install(behavior);
85356           };
85357
85358           mode.exit = function () {
85359             context.uninstall(behavior);
85360           };
85361
85362           return mode;
85363         }
85364
85365         function modeAddLine(context, mode) {
85366           mode.id = 'add-line';
85367           var behavior = behaviorAddWay(context).on('start', start).on('startFromWay', startFromWay).on('startFromNode', startFromNode);
85368           var defaultTags = {};
85369           if (mode.preset) defaultTags = mode.preset.setTags(defaultTags, 'line');
85370
85371           function start(loc) {
85372             var startGraph = context.graph();
85373             var node = osmNode({
85374               loc: loc
85375             });
85376             var way = osmWay({
85377               tags: defaultTags
85378             });
85379             context.perform(actionAddEntity(node), actionAddEntity(way), actionAddVertex(way.id, node.id));
85380             context.enter(modeDrawLine(context, way.id, startGraph, mode.button));
85381           }
85382
85383           function startFromWay(loc, edge) {
85384             var startGraph = context.graph();
85385             var node = osmNode({
85386               loc: loc
85387             });
85388             var way = osmWay({
85389               tags: defaultTags
85390             });
85391             context.perform(actionAddEntity(node), actionAddEntity(way), actionAddVertex(way.id, node.id), actionAddMidpoint({
85392               loc: loc,
85393               edge: edge
85394             }, node));
85395             context.enter(modeDrawLine(context, way.id, startGraph, mode.button));
85396           }
85397
85398           function startFromNode(node) {
85399             var startGraph = context.graph();
85400             var way = osmWay({
85401               tags: defaultTags
85402             });
85403             context.perform(actionAddEntity(way), actionAddVertex(way.id, node.id));
85404             context.enter(modeDrawLine(context, way.id, startGraph, mode.button));
85405           }
85406
85407           mode.enter = function () {
85408             context.install(behavior);
85409           };
85410
85411           mode.exit = function () {
85412             context.uninstall(behavior);
85413           };
85414
85415           return mode;
85416         }
85417
85418         function modeAddPoint(context, mode) {
85419           mode.id = 'add-point';
85420           var behavior = behaviorDraw(context).on('click', add).on('clickWay', addWay).on('clickNode', addNode).on('cancel', cancel).on('finish', cancel);
85421           var defaultTags = {};
85422           if (mode.preset) defaultTags = mode.preset.setTags(defaultTags, 'point');
85423
85424           function add(loc) {
85425             var node = osmNode({
85426               loc: loc,
85427               tags: defaultTags
85428             });
85429             context.perform(actionAddEntity(node), _t('operations.add.annotation.point'));
85430             enterSelectMode(node);
85431           }
85432
85433           function addWay(loc, edge) {
85434             var node = osmNode({
85435               tags: defaultTags
85436             });
85437             context.perform(actionAddMidpoint({
85438               loc: loc,
85439               edge: edge
85440             }, node), _t('operations.add.annotation.vertex'));
85441             enterSelectMode(node);
85442           }
85443
85444           function enterSelectMode(node) {
85445             context.enter(modeSelect(context, [node.id]).newFeature(true));
85446           }
85447
85448           function addNode(node) {
85449             if (Object.keys(defaultTags).length === 0) {
85450               enterSelectMode(node);
85451               return;
85452             }
85453
85454             var tags = Object.assign({}, node.tags); // shallow copy
85455
85456             for (var key in defaultTags) {
85457               tags[key] = defaultTags[key];
85458             }
85459
85460             context.perform(actionChangeTags(node.id, tags), _t('operations.add.annotation.point'));
85461             enterSelectMode(node);
85462           }
85463
85464           function cancel() {
85465             context.enter(modeBrowse(context));
85466           }
85467
85468           mode.enter = function () {
85469             context.install(behavior);
85470           };
85471
85472           mode.exit = function () {
85473             context.uninstall(behavior);
85474           };
85475
85476           return mode;
85477         }
85478
85479         function modeSelectNote(context, selectedNoteID) {
85480           var mode = {
85481             id: 'select-note',
85482             button: 'browse'
85483           };
85484
85485           var _keybinding = utilKeybinding('select-note');
85486
85487           var _noteEditor = uiNoteEditor(context).on('change', function () {
85488             context.map().pan([0, 0]); // trigger a redraw
85489
85490             var note = checkSelectedID();
85491             if (!note) return;
85492             context.ui().sidebar.show(_noteEditor.note(note));
85493           });
85494
85495           var _behaviors = [behaviorBreathe(), behaviorHover(context), behaviorSelect(context), behaviorLasso(context), modeDragNode(context).behavior, modeDragNote(context).behavior];
85496           var _newFeature = false;
85497
85498           function checkSelectedID() {
85499             if (!services.osm) return;
85500             var note = services.osm.getNote(selectedNoteID);
85501
85502             if (!note) {
85503               context.enter(modeBrowse(context));
85504             }
85505
85506             return note;
85507           } // class the note as selected, or return to browse mode if the note is gone
85508
85509
85510           function selectNote(d3_event, drawn) {
85511             if (!checkSelectedID()) return;
85512             var selection = context.surface().selectAll('.layer-notes .note-' + selectedNoteID);
85513
85514             if (selection.empty()) {
85515               // Return to browse mode if selected DOM elements have
85516               // disappeared because the user moved them out of view..
85517               var source = d3_event && d3_event.type === 'zoom' && d3_event.sourceEvent;
85518
85519               if (drawn && source && (source.type === 'pointermove' || source.type === 'mousemove' || source.type === 'touchmove')) {
85520                 context.enter(modeBrowse(context));
85521               }
85522             } else {
85523               selection.classed('selected', true);
85524               context.selectedNoteID(selectedNoteID);
85525             }
85526           }
85527
85528           function esc() {
85529             if (context.container().select('.combobox').size()) return;
85530             context.enter(modeBrowse(context));
85531           }
85532
85533           mode.zoomToSelected = function () {
85534             if (!services.osm) return;
85535             var note = services.osm.getNote(selectedNoteID);
85536
85537             if (note) {
85538               context.map().centerZoomEase(note.loc, 20);
85539             }
85540           };
85541
85542           mode.newFeature = function (val) {
85543             if (!arguments.length) return _newFeature;
85544             _newFeature = val;
85545             return mode;
85546           };
85547
85548           mode.enter = function () {
85549             var note = checkSelectedID();
85550             if (!note) return;
85551
85552             _behaviors.forEach(context.install);
85553
85554             _keybinding.on(_t('inspector.zoom_to.key'), mode.zoomToSelected).on('⎋', esc, true);
85555
85556             select(document).call(_keybinding);
85557             selectNote();
85558             var sidebar = context.ui().sidebar;
85559             sidebar.show(_noteEditor.note(note).newNote(_newFeature)); // expand the sidebar, avoid obscuring the note if needed
85560
85561             sidebar.expand(sidebar.intersects(note.extent()));
85562             context.map().on('drawn.select', selectNote);
85563           };
85564
85565           mode.exit = function () {
85566             _behaviors.forEach(context.uninstall);
85567
85568             select(document).call(_keybinding.unbind);
85569             context.surface().selectAll('.layer-notes .selected').classed('selected hover', false);
85570             context.map().on('drawn.select', null);
85571             context.ui().sidebar.hide();
85572             context.selectedNoteID(null);
85573           };
85574
85575           return mode;
85576         }
85577
85578         function modeAddNote(context) {
85579           var mode = {
85580             id: 'add-note',
85581             button: 'note',
85582             description: _t.html('modes.add_note.description'),
85583             key: _t('modes.add_note.key')
85584           };
85585           var behavior = behaviorDraw(context).on('click', add).on('cancel', cancel).on('finish', cancel);
85586
85587           function add(loc) {
85588             var osm = services.osm;
85589             if (!osm) return;
85590             var note = osmNote({
85591               loc: loc,
85592               status: 'open',
85593               comments: []
85594             });
85595             osm.replaceNote(note); // force a reraw (there is no history change that would otherwise do this)
85596
85597             context.map().pan([0, 0]);
85598             context.selectedNoteID(note.id).enter(modeSelectNote(context, note.id).newFeature(true));
85599           }
85600
85601           function cancel() {
85602             context.enter(modeBrowse(context));
85603           }
85604
85605           mode.enter = function () {
85606             context.install(behavior);
85607           };
85608
85609           mode.exit = function () {
85610             context.uninstall(behavior);
85611           };
85612
85613           return mode;
85614         }
85615
85616         var JXON = new function () {
85617           var sValueProp = 'keyValue',
85618               sAttributesProp = 'keyAttributes',
85619               sAttrPref = '@',
85620
85621           /* you can customize these values */
85622           aCache = [],
85623               rIsNull = /^\s*$/,
85624               rIsBool = /^(?:true|false)$/i;
85625
85626           function parseText(sValue) {
85627             if (rIsNull.test(sValue)) {
85628               return null;
85629             }
85630
85631             if (rIsBool.test(sValue)) {
85632               return sValue.toLowerCase() === 'true';
85633             }
85634
85635             if (isFinite(sValue)) {
85636               return parseFloat(sValue);
85637             }
85638
85639             if (isFinite(Date.parse(sValue))) {
85640               return new Date(sValue);
85641             }
85642
85643             return sValue;
85644           }
85645
85646           function EmptyTree() {}
85647
85648           EmptyTree.prototype.toString = function () {
85649             return 'null';
85650           };
85651
85652           EmptyTree.prototype.valueOf = function () {
85653             return null;
85654           };
85655
85656           function objectify(vValue) {
85657             return vValue === null ? new EmptyTree() : vValue instanceof Object ? vValue : new vValue.constructor(vValue);
85658           }
85659
85660           function createObjTree(oParentNode, nVerb, bFreeze, bNesteAttr) {
85661             var nLevelStart = aCache.length,
85662                 bChildren = oParentNode.hasChildNodes(),
85663                 bAttributes = oParentNode.hasAttributes(),
85664                 bHighVerb = Boolean(nVerb & 2);
85665             var sProp,
85666                 vContent,
85667                 nLength = 0,
85668                 sCollectedTxt = '',
85669                 vResult = bHighVerb ? {} :
85670             /* put here the default value for empty nodes: */
85671             true;
85672
85673             if (bChildren) {
85674               for (var oNode, nItem = 0; nItem < oParentNode.childNodes.length; nItem++) {
85675                 oNode = oParentNode.childNodes.item(nItem);
85676
85677                 if (oNode.nodeType === 4) {
85678                   /* nodeType is 'CDATASection' (4) */
85679                   sCollectedTxt += oNode.nodeValue;
85680                 } else if (oNode.nodeType === 3) {
85681                   /* nodeType is 'Text' (3) */
85682                   sCollectedTxt += oNode.nodeValue.trim();
85683                 } else if (oNode.nodeType === 1 && !oNode.prefix) {
85684                   /* nodeType is 'Element' (1) */
85685                   aCache.push(oNode);
85686                 }
85687               }
85688             }
85689
85690             var nLevelEnd = aCache.length,
85691                 vBuiltVal = parseText(sCollectedTxt);
85692
85693             if (!bHighVerb && (bChildren || bAttributes)) {
85694               vResult = nVerb === 0 ? objectify(vBuiltVal) : {};
85695             }
85696
85697             for (var nElId = nLevelStart; nElId < nLevelEnd; nElId++) {
85698               sProp = aCache[nElId].nodeName.toLowerCase();
85699               vContent = createObjTree(aCache[nElId], nVerb, bFreeze, bNesteAttr);
85700
85701               if (vResult.hasOwnProperty(sProp)) {
85702                 if (vResult[sProp].constructor !== Array) {
85703                   vResult[sProp] = [vResult[sProp]];
85704                 }
85705
85706                 vResult[sProp].push(vContent);
85707               } else {
85708                 vResult[sProp] = vContent;
85709                 nLength++;
85710               }
85711             }
85712
85713             if (bAttributes) {
85714               var nAttrLen = oParentNode.attributes.length,
85715                   sAPrefix = bNesteAttr ? '' : sAttrPref,
85716                   oAttrParent = bNesteAttr ? {} : vResult;
85717
85718               for (var oAttrib, nAttrib = 0; nAttrib < nAttrLen; nLength++, nAttrib++) {
85719                 oAttrib = oParentNode.attributes.item(nAttrib);
85720                 oAttrParent[sAPrefix + oAttrib.name.toLowerCase()] = parseText(oAttrib.value.trim());
85721               }
85722
85723               if (bNesteAttr) {
85724                 if (bFreeze) {
85725                   Object.freeze(oAttrParent);
85726                 }
85727
85728                 vResult[sAttributesProp] = oAttrParent;
85729                 nLength -= nAttrLen - 1;
85730               }
85731             }
85732
85733             if (nVerb === 3 || (nVerb === 2 || nVerb === 1 && nLength > 0) && sCollectedTxt) {
85734               vResult[sValueProp] = vBuiltVal;
85735             } else if (!bHighVerb && nLength === 0 && sCollectedTxt) {
85736               vResult = vBuiltVal;
85737             }
85738
85739             if (bFreeze && (bHighVerb || nLength > 0)) {
85740               Object.freeze(vResult);
85741             }
85742
85743             aCache.length = nLevelStart;
85744             return vResult;
85745           }
85746
85747           function loadObjTree(oXMLDoc, oParentEl, oParentObj) {
85748             var vValue, oChild;
85749
85750             if (oParentObj instanceof String || oParentObj instanceof Number || oParentObj instanceof Boolean) {
85751               oParentEl.appendChild(oXMLDoc.createTextNode(oParentObj.toString()));
85752               /* verbosity level is 0 */
85753             } else if (oParentObj.constructor === Date) {
85754               oParentEl.appendChild(oXMLDoc.createTextNode(oParentObj.toGMTString()));
85755             }
85756
85757             for (var sName in oParentObj) {
85758               vValue = oParentObj[sName];
85759
85760               if (isFinite(sName) || vValue instanceof Function) {
85761                 continue;
85762               }
85763               /* verbosity level is 0 */
85764
85765
85766               if (sName === sValueProp) {
85767                 if (vValue !== null && vValue !== true) {
85768                   oParentEl.appendChild(oXMLDoc.createTextNode(vValue.constructor === Date ? vValue.toGMTString() : String(vValue)));
85769                 }
85770               } else if (sName === sAttributesProp) {
85771                 /* verbosity level is 3 */
85772                 for (var sAttrib in vValue) {
85773                   oParentEl.setAttribute(sAttrib, vValue[sAttrib]);
85774                 }
85775               } else if (sName.charAt(0) === sAttrPref) {
85776                 oParentEl.setAttribute(sName.slice(1), vValue);
85777               } else if (vValue.constructor === Array) {
85778                 for (var nItem = 0; nItem < vValue.length; nItem++) {
85779                   oChild = oXMLDoc.createElement(sName);
85780                   loadObjTree(oXMLDoc, oChild, vValue[nItem]);
85781                   oParentEl.appendChild(oChild);
85782                 }
85783               } else {
85784                 oChild = oXMLDoc.createElement(sName);
85785
85786                 if (vValue instanceof Object) {
85787                   loadObjTree(oXMLDoc, oChild, vValue);
85788                 } else if (vValue !== null && vValue !== true) {
85789                   oChild.appendChild(oXMLDoc.createTextNode(vValue.toString()));
85790                 }
85791
85792                 oParentEl.appendChild(oChild);
85793               }
85794             }
85795           }
85796
85797           this.build = function (oXMLParent, nVerbosity
85798           /* optional */
85799           , bFreeze
85800           /* optional */
85801           , bNesteAttributes
85802           /* optional */
85803           ) {
85804             var _nVerb = arguments.length > 1 && typeof nVerbosity === 'number' ? nVerbosity & 3 :
85805             /* put here the default verbosity level: */
85806             1;
85807
85808             return createObjTree(oXMLParent, _nVerb, bFreeze || false, arguments.length > 3 ? bNesteAttributes : _nVerb === 3);
85809           };
85810
85811           this.unbuild = function (oObjTree) {
85812             var oNewDoc = document.implementation.createDocument('', '', null);
85813             loadObjTree(oNewDoc, oNewDoc, oObjTree);
85814             return oNewDoc;
85815           };
85816
85817           this.stringify = function (oObjTree) {
85818             return new XMLSerializer().serializeToString(JXON.unbuild(oObjTree));
85819           };
85820         }(); // var myObject = JXON.build(doc);
85821         // we got our javascript object! try: alert(JSON.stringify(myObject));
85822         // var newDoc = JXON.unbuild(myObject);
85823         // we got our Document instance! try: alert((new XMLSerializer()).serializeToString(newDoc));
85824
85825         function uiConflicts(context) {
85826           var dispatch = dispatch$8('cancel', 'save');
85827           var keybinding = utilKeybinding('conflicts');
85828
85829           var _origChanges;
85830
85831           var _conflictList;
85832
85833           var _shownConflictIndex;
85834
85835           function keybindingOn() {
85836             select(document).call(keybinding.on('⎋', cancel, true));
85837           }
85838
85839           function keybindingOff() {
85840             select(document).call(keybinding.unbind);
85841           }
85842
85843           function tryAgain() {
85844             keybindingOff();
85845             dispatch.call('save');
85846           }
85847
85848           function cancel() {
85849             keybindingOff();
85850             dispatch.call('cancel');
85851           }
85852
85853           function conflicts(selection) {
85854             keybindingOn();
85855             var headerEnter = selection.selectAll('.header').data([0]).enter().append('div').attr('class', 'header fillL');
85856             headerEnter.append('button').attr('class', 'fr').on('click', cancel).call(svgIcon('#iD-icon-close'));
85857             headerEnter.append('h3').html(_t.html('save.conflict.header'));
85858             var bodyEnter = selection.selectAll('.body').data([0]).enter().append('div').attr('class', 'body fillL');
85859             var conflictsHelpEnter = bodyEnter.append('div').attr('class', 'conflicts-help').html(_t.html('save.conflict.help')); // Download changes link
85860
85861             var detected = utilDetect();
85862             var changeset = new osmChangeset();
85863             delete changeset.id; // Export without changeset_id
85864
85865             var data = JXON.stringify(changeset.osmChangeJXON(_origChanges));
85866             var blob = new Blob([data], {
85867               type: 'text/xml;charset=utf-8;'
85868             });
85869             var fileName = 'changes.osc';
85870             var linkEnter = conflictsHelpEnter.selectAll('.download-changes').append('a').attr('class', 'download-changes');
85871
85872             if (detected.download) {
85873               // All except IE11 and Edge
85874               linkEnter // download the data as a file
85875               .attr('href', window.URL.createObjectURL(blob)).attr('download', fileName);
85876             } else {
85877               // IE11 and Edge
85878               linkEnter // open data uri in a new tab
85879               .attr('target', '_blank').on('click.download', function () {
85880                 navigator.msSaveBlob(blob, fileName);
85881               });
85882             }
85883
85884             linkEnter.call(svgIcon('#iD-icon-load', 'inline')).append('span').html(_t.html('save.conflict.download_changes'));
85885             bodyEnter.append('div').attr('class', 'conflict-container fillL3').call(showConflict, 0);
85886             bodyEnter.append('div').attr('class', 'conflicts-done').attr('opacity', 0).style('display', 'none').html(_t.html('save.conflict.done'));
85887             var buttonsEnter = bodyEnter.append('div').attr('class', 'buttons col12 joined conflicts-buttons');
85888             buttonsEnter.append('button').attr('disabled', _conflictList.length > 1).attr('class', 'action conflicts-button col6').html(_t.html('save.title')).on('click.try_again', tryAgain);
85889             buttonsEnter.append('button').attr('class', 'secondary-action conflicts-button col6').html(_t.html('confirm.cancel')).on('click.cancel', cancel);
85890           }
85891
85892           function showConflict(selection, index) {
85893             index = utilWrap(index, _conflictList.length);
85894             _shownConflictIndex = index;
85895             var parent = select(selection.node().parentNode); // enable save button if this is the last conflict being reviewed..
85896
85897             if (index === _conflictList.length - 1) {
85898               window.setTimeout(function () {
85899                 parent.select('.conflicts-button').attr('disabled', null);
85900                 parent.select('.conflicts-done').transition().attr('opacity', 1).style('display', 'block');
85901               }, 250);
85902             }
85903
85904             var conflict = selection.selectAll('.conflict').data([_conflictList[index]]);
85905             conflict.exit().remove();
85906             var conflictEnter = conflict.enter().append('div').attr('class', 'conflict');
85907             conflictEnter.append('h4').attr('class', 'conflict-count').html(_t.html('save.conflict.count', {
85908               num: index + 1,
85909               total: _conflictList.length
85910             }));
85911             conflictEnter.append('a').attr('class', 'conflict-description').attr('href', '#').html(function (d) {
85912               return d.name;
85913             }).on('click', function (d3_event, d) {
85914               d3_event.preventDefault();
85915               zoomToEntity(d.id);
85916             });
85917             var details = conflictEnter.append('div').attr('class', 'conflict-detail-container');
85918             details.append('ul').attr('class', 'conflict-detail-list').selectAll('li').data(function (d) {
85919               return d.details || [];
85920             }).enter().append('li').attr('class', 'conflict-detail-item').html(function (d) {
85921               return d;
85922             });
85923             details.append('div').attr('class', 'conflict-choices').call(addChoices);
85924             details.append('div').attr('class', 'conflict-nav-buttons joined cf').selectAll('button').data(['previous', 'next']).enter().append('button').html(function (d) {
85925               return _t.html('save.conflict.' + d);
85926             }).attr('class', 'conflict-nav-button action col6').attr('disabled', function (d, i) {
85927               return i === 0 && index === 0 || i === 1 && index === _conflictList.length - 1 || null;
85928             }).on('click', function (d3_event, d) {
85929               d3_event.preventDefault();
85930               var container = parent.selectAll('.conflict-container');
85931               var sign = d === 'previous' ? -1 : 1;
85932               container.selectAll('.conflict').remove();
85933               container.call(showConflict, index + sign);
85934             });
85935           }
85936
85937           function addChoices(selection) {
85938             var choices = selection.append('ul').attr('class', 'layer-list').selectAll('li').data(function (d) {
85939               return d.choices || [];
85940             }); // enter
85941
85942             var choicesEnter = choices.enter().append('li').attr('class', 'layer');
85943             var labelEnter = choicesEnter.append('label');
85944             labelEnter.append('input').attr('type', 'radio').attr('name', function (d) {
85945               return d.id;
85946             }).on('change', function (d3_event, d) {
85947               var ul = this.parentNode.parentNode.parentNode;
85948               ul.__data__.chosen = d.id;
85949               choose(d3_event, ul, d);
85950             });
85951             labelEnter.append('span').html(function (d) {
85952               return d.text;
85953             }); // update
85954
85955             choicesEnter.merge(choices).each(function (d) {
85956               var ul = this.parentNode;
85957
85958               if (ul.__data__.chosen === d.id) {
85959                 choose(null, ul, d);
85960               }
85961             });
85962           }
85963
85964           function choose(d3_event, ul, datum) {
85965             if (d3_event) d3_event.preventDefault();
85966             select(ul).selectAll('li').classed('active', function (d) {
85967               return d === datum;
85968             }).selectAll('input').property('checked', function (d) {
85969               return d === datum;
85970             });
85971             var extent = geoExtent();
85972             var entity;
85973             entity = context.graph().hasEntity(datum.id);
85974             if (entity) extent._extend(entity.extent(context.graph()));
85975             datum.action();
85976             entity = context.graph().hasEntity(datum.id);
85977             if (entity) extent._extend(entity.extent(context.graph()));
85978             zoomToEntity(datum.id, extent);
85979           }
85980
85981           function zoomToEntity(id, extent) {
85982             context.surface().selectAll('.hover').classed('hover', false);
85983             var entity = context.graph().hasEntity(id);
85984
85985             if (entity) {
85986               if (extent) {
85987                 context.map().trimmedExtent(extent);
85988               } else {
85989                 context.map().zoomToEase(entity);
85990               }
85991
85992               context.surface().selectAll(utilEntityOrMemberSelector([entity.id], context.graph())).classed('hover', true);
85993             }
85994           } // The conflict list should be an array of objects like:
85995           // {
85996           //     id: id,
85997           //     name: entityName(local),
85998           //     details: merge.conflicts(),
85999           //     chosen: 1,
86000           //     choices: [
86001           //         choice(id, keepMine, forceLocal),
86002           //         choice(id, keepTheirs, forceRemote)
86003           //     ]
86004           // }
86005
86006
86007           conflicts.conflictList = function (_) {
86008             if (!arguments.length) return _conflictList;
86009             _conflictList = _;
86010             return conflicts;
86011           };
86012
86013           conflicts.origChanges = function (_) {
86014             if (!arguments.length) return _origChanges;
86015             _origChanges = _;
86016             return conflicts;
86017           };
86018
86019           conflicts.shownEntityIds = function () {
86020             if (_conflictList && typeof _shownConflictIndex === 'number') {
86021               return [_conflictList[_shownConflictIndex].id];
86022             }
86023
86024             return [];
86025           };
86026
86027           return utilRebind(conflicts, dispatch, 'on');
86028         }
86029
86030         function uiConfirm(selection) {
86031           var modalSelection = uiModal(selection);
86032           modalSelection.select('.modal').classed('modal-alert', true);
86033           var section = modalSelection.select('.content');
86034           section.append('div').attr('class', 'modal-section header');
86035           section.append('div').attr('class', 'modal-section message-text');
86036           var buttons = section.append('div').attr('class', 'modal-section buttons cf');
86037
86038           modalSelection.okButton = function () {
86039             buttons.append('button').attr('class', 'button ok-button action').on('click.confirm', function () {
86040               modalSelection.remove();
86041             }).html(_t.html('confirm.okay')).node().focus();
86042             return modalSelection;
86043           };
86044
86045           return modalSelection;
86046         }
86047
86048         function uiChangesetEditor(context) {
86049           var dispatch = dispatch$8('change');
86050           var formFields = uiFormFields(context);
86051           var commentCombo = uiCombobox(context, 'comment').caseSensitive(true);
86052
86053           var _fieldsArr;
86054
86055           var _tags;
86056
86057           var _changesetID;
86058
86059           function changesetEditor(selection) {
86060             render(selection);
86061           }
86062
86063           function render(selection) {
86064             var initial = false;
86065
86066             if (!_fieldsArr) {
86067               initial = true;
86068               var presets = _mainPresetIndex;
86069               _fieldsArr = [uiField(context, presets.field('comment'), null, {
86070                 show: true,
86071                 revert: false
86072               }), uiField(context, presets.field('source'), null, {
86073                 show: false,
86074                 revert: false
86075               }), uiField(context, presets.field('hashtags'), null, {
86076                 show: false,
86077                 revert: false
86078               })];
86079
86080               _fieldsArr.forEach(function (field) {
86081                 field.on('change', function (t, onInput) {
86082                   dispatch.call('change', field, undefined, t, onInput);
86083                 });
86084               });
86085             }
86086
86087             _fieldsArr.forEach(function (field) {
86088               field.tags(_tags);
86089             });
86090
86091             selection.call(formFields.fieldsArr(_fieldsArr));
86092
86093             if (initial) {
86094               var commentField = selection.select('.form-field-comment textarea');
86095               var commentNode = commentField.node();
86096
86097               if (commentNode) {
86098                 commentNode.focus();
86099                 commentNode.select();
86100               } // trigger a 'blur' event so that comment field can be cleaned
86101               // and checked for hashtags, even if retrieved from localstorage
86102
86103
86104               utilTriggerEvent(commentField, 'blur');
86105               var osm = context.connection();
86106
86107               if (osm) {
86108                 osm.userChangesets(function (err, changesets) {
86109                   if (err) return;
86110                   var comments = changesets.map(function (changeset) {
86111                     var comment = changeset.tags.comment;
86112                     return comment ? {
86113                       title: comment,
86114                       value: comment
86115                     } : null;
86116                   }).filter(Boolean);
86117                   commentField.call(commentCombo.data(utilArrayUniqBy(comments, 'title')));
86118                 });
86119               }
86120             } // Add warning if comment mentions Google
86121
86122
86123             var hasGoogle = _tags.comment.match(/google/i);
86124
86125             var commentWarning = selection.select('.form-field-comment').selectAll('.comment-warning').data(hasGoogle ? [0] : []);
86126             commentWarning.exit().transition().duration(200).style('opacity', 0).remove();
86127             var commentEnter = commentWarning.enter().insert('div', '.tag-reference-body').attr('class', 'field-warning comment-warning').style('opacity', 0);
86128             commentEnter.append('a').attr('target', '_blank').call(svgIcon('#iD-icon-alert', 'inline')).attr('href', _t('commit.google_warning_link')).append('span').html(_t.html('commit.google_warning'));
86129             commentEnter.transition().duration(200).style('opacity', 1);
86130           }
86131
86132           changesetEditor.tags = function (_) {
86133             if (!arguments.length) return _tags;
86134             _tags = _; // Don't reset _fieldsArr here.
86135
86136             return changesetEditor;
86137           };
86138
86139           changesetEditor.changesetID = function (_) {
86140             if (!arguments.length) return _changesetID;
86141             if (_changesetID === _) return changesetEditor;
86142             _changesetID = _;
86143             _fieldsArr = null;
86144             return changesetEditor;
86145           };
86146
86147           return utilRebind(changesetEditor, dispatch, 'on');
86148         }
86149
86150         function uiSectionChanges(context) {
86151           var detected = utilDetect();
86152           var _discardTags = {};
86153           _mainFileFetcher.get('discarded').then(function (d) {
86154             _discardTags = d;
86155           })["catch"](function () {
86156             /* ignore */
86157           });
86158           var section = uiSection('changes-list', context).label(function () {
86159             var history = context.history();
86160             var summary = history.difference().summary();
86161             return _t('inspector.title_count', {
86162               title: _t.html('commit.changes'),
86163               count: summary.length
86164             });
86165           }).disclosureContent(renderDisclosureContent);
86166
86167           function renderDisclosureContent(selection) {
86168             var history = context.history();
86169             var summary = history.difference().summary();
86170             var container = selection.selectAll('.commit-section').data([0]);
86171             var containerEnter = container.enter().append('div').attr('class', 'commit-section');
86172             containerEnter.append('ul').attr('class', 'changeset-list');
86173             container = containerEnter.merge(container);
86174             var items = container.select('ul').selectAll('li').data(summary);
86175             var itemsEnter = items.enter().append('li').attr('class', 'change-item');
86176             var buttons = itemsEnter.append('button').on('mouseover', mouseover).on('mouseout', mouseout).on('click', click);
86177             buttons.each(function (d) {
86178               select(this).call(svgIcon('#iD-icon-' + d.entity.geometry(d.graph), 'pre-text ' + d.changeType));
86179             });
86180             buttons.append('span').attr('class', 'change-type').html(function (d) {
86181               return _t.html('commit.' + d.changeType) + ' ';
86182             });
86183             buttons.append('strong').attr('class', 'entity-type').html(function (d) {
86184               var matched = _mainPresetIndex.match(d.entity, d.graph);
86185               return matched && matched.name() || utilDisplayType(d.entity.id);
86186             });
86187             buttons.append('span').attr('class', 'entity-name').html(function (d) {
86188               var name = utilDisplayName(d.entity) || '',
86189                   string = '';
86190
86191               if (name !== '') {
86192                 string += ':';
86193               }
86194
86195               return string += ' ' + name;
86196             });
86197             items = itemsEnter.merge(items); // Download changeset link
86198
86199             var changeset = new osmChangeset().update({
86200               id: undefined
86201             });
86202             var changes = history.changes(actionDiscardTags(history.difference(), _discardTags));
86203             delete changeset.id; // Export without chnageset_id
86204
86205             var data = JXON.stringify(changeset.osmChangeJXON(changes));
86206             var blob = new Blob([data], {
86207               type: 'text/xml;charset=utf-8;'
86208             });
86209             var fileName = 'changes.osc';
86210             var linkEnter = container.selectAll('.download-changes').data([0]).enter().append('a').attr('class', 'download-changes');
86211
86212             if (detected.download) {
86213               // All except IE11 and Edge
86214               linkEnter // download the data as a file
86215               .attr('href', window.URL.createObjectURL(blob)).attr('download', fileName);
86216             } else {
86217               // IE11 and Edge
86218               linkEnter // open data uri in a new tab
86219               .attr('target', '_blank').on('click.download', function () {
86220                 navigator.msSaveBlob(blob, fileName);
86221               });
86222             }
86223
86224             linkEnter.call(svgIcon('#iD-icon-load', 'inline')).append('span').html(_t.html('commit.download_changes'));
86225
86226             function mouseover(d) {
86227               if (d.entity) {
86228                 context.surface().selectAll(utilEntityOrMemberSelector([d.entity.id], context.graph())).classed('hover', true);
86229               }
86230             }
86231
86232             function mouseout() {
86233               context.surface().selectAll('.hover').classed('hover', false);
86234             }
86235
86236             function click(d3_event, change) {
86237               if (change.changeType !== 'deleted') {
86238                 var entity = change.entity;
86239                 context.map().zoomToEase(entity);
86240                 context.surface().selectAll(utilEntityOrMemberSelector([entity.id], context.graph())).classed('hover', true);
86241               }
86242             }
86243           }
86244
86245           return section;
86246         }
86247
86248         function uiCommitWarnings(context) {
86249           function commitWarnings(selection) {
86250             var issuesBySeverity = context.validator().getIssuesBySeverity({
86251               what: 'edited',
86252               where: 'all',
86253               includeDisabledRules: true
86254             });
86255
86256             for (var severity in issuesBySeverity) {
86257               var issues = issuesBySeverity[severity];
86258
86259               if (severity !== 'error') {
86260                 // exclude 'fixme' and similar - #8603
86261                 issues = issues.filter(function (issue) {
86262                   return issue.type !== 'help_request';
86263                 });
86264               }
86265
86266               var section = severity + '-section';
86267               var issueItem = severity + '-item';
86268               var container = selection.selectAll('.' + section).data(issues.length ? [0] : []);
86269               container.exit().remove();
86270               var containerEnter = container.enter().append('div').attr('class', 'modal-section ' + section + ' fillL2');
86271               containerEnter.append('h3').html(severity === 'warning' ? _t.html('commit.warnings') : _t.html('commit.errors'));
86272               containerEnter.append('ul').attr('class', 'changeset-list');
86273               container = containerEnter.merge(container);
86274               var items = container.select('ul').selectAll('li').data(issues, function (d) {
86275                 return d.id;
86276               });
86277               items.exit().remove();
86278               var itemsEnter = items.enter().append('li').attr('class', issueItem);
86279               var buttons = itemsEnter.append('button').on('mouseover', function (d3_event, d) {
86280                 if (d.entityIds) {
86281                   context.surface().selectAll(utilEntityOrMemberSelector(d.entityIds, context.graph())).classed('hover', true);
86282                 }
86283               }).on('mouseout', function () {
86284                 context.surface().selectAll('.hover').classed('hover', false);
86285               }).on('click', function (d3_event, d) {
86286                 context.validator().focusIssue(d);
86287               });
86288               buttons.call(svgIcon('#iD-icon-alert', 'pre-text'));
86289               buttons.append('strong').attr('class', 'issue-message');
86290               buttons.filter(function (d) {
86291                 return d.tooltip;
86292               }).call(uiTooltip().title(function (d) {
86293                 return d.tooltip;
86294               }).placement('top'));
86295               items = itemsEnter.merge(items);
86296               items.selectAll('.issue-message').html(function (d) {
86297                 return d.message(context);
86298               });
86299             }
86300           }
86301
86302           return commitWarnings;
86303         }
86304
86305         var readOnlyTags = [/^changesets_count$/, /^created_by$/, /^ideditor:/, /^imagery_used$/, /^host$/, /^locale$/, /^warnings:/, /^resolved:/, /^closed:note$/, /^closed:keepright$/, /^closed:improveosm:/, /^closed:osmose:/]; // treat most punctuation (except -, _, +, &) as hashtag delimiters - #4398
86306         // from https://stackoverflow.com/a/25575009
86307
86308         var hashtagRegex = /(#[^\u2000-\u206F\u2E00-\u2E7F\s\\'!"#$%()*,.\/:;<=>?@\[\]^`{|}~]+)/g;
86309         function uiCommit(context) {
86310           var dispatch = dispatch$8('cancel');
86311
86312           var _userDetails;
86313
86314           var _selection;
86315
86316           var changesetEditor = uiChangesetEditor(context).on('change', changeTags);
86317           var rawTagEditor = uiSectionRawTagEditor('changeset-tag-editor', context).on('change', changeTags).readOnlyTags(readOnlyTags);
86318           var commitChanges = uiSectionChanges(context);
86319           var commitWarnings = uiCommitWarnings(context);
86320
86321           function commit(selection) {
86322             _selection = selection; // Initialize changeset if one does not exist yet.
86323
86324             if (!context.changeset) initChangeset();
86325             loadDerivedChangesetTags();
86326             selection.call(render);
86327           }
86328
86329           function initChangeset() {
86330             // expire stored comment, hashtags, source after cutoff datetime - #3947 #4899
86331             var commentDate = +corePreferences('commentDate') || 0;
86332             var currDate = Date.now();
86333             var cutoff = 2 * 86400 * 1000; // 2 days
86334
86335             if (commentDate > currDate || currDate - commentDate > cutoff) {
86336               corePreferences('comment', null);
86337               corePreferences('hashtags', null);
86338               corePreferences('source', null);
86339             } // load in explicitly-set values, if any
86340
86341
86342             if (context.defaultChangesetComment()) {
86343               corePreferences('comment', context.defaultChangesetComment());
86344               corePreferences('commentDate', Date.now());
86345             }
86346
86347             if (context.defaultChangesetSource()) {
86348               corePreferences('source', context.defaultChangesetSource());
86349               corePreferences('commentDate', Date.now());
86350             }
86351
86352             if (context.defaultChangesetHashtags()) {
86353               corePreferences('hashtags', context.defaultChangesetHashtags());
86354               corePreferences('commentDate', Date.now());
86355             }
86356
86357             var detected = utilDetect();
86358             var tags = {
86359               comment: corePreferences('comment') || '',
86360               created_by: context.cleanTagValue('iD ' + context.version),
86361               host: context.cleanTagValue(detected.host),
86362               locale: context.cleanTagValue(_mainLocalizer.localeCode())
86363             }; // call findHashtags initially - this will remove stored
86364             // hashtags if any hashtags are found in the comment - #4304
86365
86366             findHashtags(tags, true);
86367             var hashtags = corePreferences('hashtags');
86368
86369             if (hashtags) {
86370               tags.hashtags = hashtags;
86371             }
86372
86373             var source = corePreferences('source');
86374
86375             if (source) {
86376               tags.source = source;
86377             }
86378
86379             var photoOverlaysUsed = context.history().photoOverlaysUsed();
86380
86381             if (photoOverlaysUsed.length) {
86382               var sources = (tags.source || '').split(';'); // include this tag for any photo layer
86383
86384               if (sources.indexOf('streetlevel imagery') === -1) {
86385                 sources.push('streetlevel imagery');
86386               } // add the photo overlays used during editing as sources
86387
86388
86389               photoOverlaysUsed.forEach(function (photoOverlay) {
86390                 if (sources.indexOf(photoOverlay) === -1) {
86391                   sources.push(photoOverlay);
86392                 }
86393               });
86394               tags.source = context.cleanTagValue(sources.join(';'));
86395             }
86396
86397             context.changeset = new osmChangeset({
86398               tags: tags
86399             });
86400           } // Calculates read-only metadata tags based on the user's editing session and applies
86401           // them to the changeset.
86402
86403
86404           function loadDerivedChangesetTags() {
86405             var osm = context.connection();
86406             if (!osm) return;
86407             var tags = Object.assign({}, context.changeset.tags); // shallow copy
86408             // assign tags for imagery used
86409
86410             var imageryUsed = context.cleanTagValue(context.history().imageryUsed().join(';'));
86411             tags.imagery_used = imageryUsed || 'None'; // assign tags for closed issues and notes
86412
86413             var osmClosed = osm.getClosedIDs();
86414             var itemType;
86415
86416             if (osmClosed.length) {
86417               tags['closed:note'] = context.cleanTagValue(osmClosed.join(';'));
86418             }
86419
86420             if (services.keepRight) {
86421               var krClosed = services.keepRight.getClosedIDs();
86422
86423               if (krClosed.length) {
86424                 tags['closed:keepright'] = context.cleanTagValue(krClosed.join(';'));
86425               }
86426             }
86427
86428             if (services.improveOSM) {
86429               var iOsmClosed = services.improveOSM.getClosedCounts();
86430
86431               for (itemType in iOsmClosed) {
86432                 tags['closed:improveosm:' + itemType] = context.cleanTagValue(iOsmClosed[itemType].toString());
86433               }
86434             }
86435
86436             if (services.osmose) {
86437               var osmoseClosed = services.osmose.getClosedCounts();
86438
86439               for (itemType in osmoseClosed) {
86440                 tags['closed:osmose:' + itemType] = context.cleanTagValue(osmoseClosed[itemType].toString());
86441               }
86442             } // remove existing issue counts
86443
86444
86445             for (var key in tags) {
86446               if (key.match(/(^warnings:)|(^resolved:)/)) {
86447                 delete tags[key];
86448               }
86449             }
86450
86451             function addIssueCounts(issues, prefix) {
86452               var issuesByType = utilArrayGroupBy(issues, 'type');
86453
86454               for (var issueType in issuesByType) {
86455                 var issuesOfType = issuesByType[issueType];
86456
86457                 if (issuesOfType[0].subtype) {
86458                   var issuesBySubtype = utilArrayGroupBy(issuesOfType, 'subtype');
86459
86460                   for (var issueSubtype in issuesBySubtype) {
86461                     var issuesOfSubtype = issuesBySubtype[issueSubtype];
86462                     tags[prefix + ':' + issueType + ':' + issueSubtype] = context.cleanTagValue(issuesOfSubtype.length.toString());
86463                   }
86464                 } else {
86465                   tags[prefix + ':' + issueType] = context.cleanTagValue(issuesOfType.length.toString());
86466                 }
86467               }
86468             } // add counts of warnings generated by the user's edits
86469
86470
86471             var warnings = context.validator().getIssuesBySeverity({
86472               what: 'edited',
86473               where: 'all',
86474               includeIgnored: true,
86475               includeDisabledRules: true
86476             }).warning.filter(function (issue) {
86477               return issue.type !== 'help_request';
86478             }); // exclude 'fixme' and similar - #8603
86479
86480             addIssueCounts(warnings, 'warnings'); // add counts of issues resolved by the user's edits
86481
86482             var resolvedIssues = context.validator().getResolvedIssues();
86483             addIssueCounts(resolvedIssues, 'resolved');
86484             context.changeset = context.changeset.update({
86485               tags: tags
86486             });
86487           }
86488
86489           function render(selection) {
86490             var osm = context.connection();
86491             if (!osm) return;
86492             var header = selection.selectAll('.header').data([0]);
86493             var headerTitle = header.enter().append('div').attr('class', 'header fillL');
86494             headerTitle.append('div').append('h3').html(_t.html('commit.title'));
86495             headerTitle.append('button').attr('class', 'close').on('click', function () {
86496               dispatch.call('cancel', this);
86497             }).call(svgIcon('#iD-icon-close'));
86498             var body = selection.selectAll('.body').data([0]);
86499             body = body.enter().append('div').attr('class', 'body').merge(body); // Changeset Section
86500
86501             var changesetSection = body.selectAll('.changeset-editor').data([0]);
86502             changesetSection = changesetSection.enter().append('div').attr('class', 'modal-section changeset-editor').merge(changesetSection);
86503             changesetSection.call(changesetEditor.changesetID(context.changeset.id).tags(context.changeset.tags)); // Warnings
86504
86505             body.call(commitWarnings); // Upload Explanation
86506
86507             var saveSection = body.selectAll('.save-section').data([0]);
86508             saveSection = saveSection.enter().append('div').attr('class', 'modal-section save-section fillL').merge(saveSection);
86509             var prose = saveSection.selectAll('.commit-info').data([0]);
86510
86511             if (prose.enter().size()) {
86512               // first time, make sure to update user details in prose
86513               _userDetails = null;
86514             }
86515
86516             prose = prose.enter().append('p').attr('class', 'commit-info').html(_t.html('commit.upload_explanation')).merge(prose); // always check if this has changed, but only update prose.html()
86517             // if needed, because it can trigger a style recalculation
86518
86519             osm.userDetails(function (err, user) {
86520               if (err) return;
86521               if (_userDetails === user) return; // no change
86522
86523               _userDetails = user;
86524               var userLink = select(document.createElement('div'));
86525
86526               if (user.image_url) {
86527                 userLink.append('img').attr('src', user.image_url).attr('class', 'icon pre-text user-icon');
86528               }
86529
86530               userLink.append('a').attr('class', 'user-info').html(user.display_name).attr('href', osm.userURL(user.display_name)).attr('target', '_blank');
86531               prose.html(_t.html('commit.upload_explanation_with_user', {
86532                 user: userLink.html()
86533               }));
86534             }); // Request Review
86535
86536             var requestReview = saveSection.selectAll('.request-review').data([0]); // Enter
86537
86538             var requestReviewEnter = requestReview.enter().append('div').attr('class', 'request-review');
86539             var requestReviewDomId = utilUniqueDomId('commit-input-request-review');
86540             var labelEnter = requestReviewEnter.append('label').attr('for', requestReviewDomId);
86541
86542             if (!labelEnter.empty()) {
86543               labelEnter.call(uiTooltip().title(_t.html('commit.request_review_info')).placement('top'));
86544             }
86545
86546             labelEnter.append('input').attr('type', 'checkbox').attr('id', requestReviewDomId);
86547             labelEnter.append('span').html(_t.html('commit.request_review')); // Update
86548
86549             requestReview = requestReview.merge(requestReviewEnter);
86550             var requestReviewInput = requestReview.selectAll('input').property('checked', isReviewRequested(context.changeset.tags)).on('change', toggleRequestReview); // Buttons
86551
86552             var buttonSection = saveSection.selectAll('.buttons').data([0]); // enter
86553
86554             var buttonEnter = buttonSection.enter().append('div').attr('class', 'buttons fillL');
86555             buttonEnter.append('button').attr('class', 'secondary-action button cancel-button').append('span').attr('class', 'label').html(_t.html('commit.cancel'));
86556             var uploadButton = buttonEnter.append('button').attr('class', 'action button save-button');
86557             uploadButton.append('span').attr('class', 'label').html(_t.html('commit.save'));
86558             var uploadBlockerTooltipText = getUploadBlockerMessage(); // update
86559
86560             buttonSection = buttonSection.merge(buttonEnter);
86561             buttonSection.selectAll('.cancel-button').on('click.cancel', function () {
86562               dispatch.call('cancel', this);
86563             });
86564             buttonSection.selectAll('.save-button').classed('disabled', uploadBlockerTooltipText !== null).on('click.save', function () {
86565               if (!select(this).classed('disabled')) {
86566                 this.blur(); // avoid keeping focus on the button - #4641
86567
86568                 for (var key in context.changeset.tags) {
86569                   // remove any empty keys before upload
86570                   if (!key) delete context.changeset.tags[key];
86571                 }
86572
86573                 context.uploader().save(context.changeset);
86574               }
86575             }); // remove any existing tooltip
86576
86577             uiTooltip().destroyAny(buttonSection.selectAll('.save-button'));
86578
86579             if (uploadBlockerTooltipText) {
86580               buttonSection.selectAll('.save-button').call(uiTooltip().title(uploadBlockerTooltipText).placement('top'));
86581             } // Raw Tag Editor
86582
86583
86584             var tagSection = body.selectAll('.tag-section.raw-tag-editor').data([0]);
86585             tagSection = tagSection.enter().append('div').attr('class', 'modal-section tag-section raw-tag-editor').merge(tagSection);
86586             tagSection.call(rawTagEditor.tags(Object.assign({}, context.changeset.tags)) // shallow copy
86587             .render);
86588             var changesSection = body.selectAll('.commit-changes-section').data([0]);
86589             changesSection = changesSection.enter().append('div').attr('class', 'modal-section commit-changes-section').merge(changesSection); // Change summary
86590
86591             changesSection.call(commitChanges.render);
86592
86593             function toggleRequestReview() {
86594               var rr = requestReviewInput.property('checked');
86595               updateChangeset({
86596                 review_requested: rr ? 'yes' : undefined
86597               });
86598               tagSection.call(rawTagEditor.tags(Object.assign({}, context.changeset.tags)) // shallow copy
86599               .render);
86600             }
86601           }
86602
86603           function getUploadBlockerMessage() {
86604             var errors = context.validator().getIssuesBySeverity({
86605               what: 'edited',
86606               where: 'all'
86607             }).error;
86608
86609             if (errors.length) {
86610               return _t('commit.outstanding_errors_message', {
86611                 count: errors.length
86612               });
86613             } else {
86614               var hasChangesetComment = context.changeset && context.changeset.tags.comment && context.changeset.tags.comment.trim().length;
86615
86616               if (!hasChangesetComment) {
86617                 return _t('commit.comment_needed_message');
86618               }
86619             }
86620
86621             return null;
86622           }
86623
86624           function changeTags(_, changed, onInput) {
86625             if (changed.hasOwnProperty('comment')) {
86626               if (changed.comment === undefined) {
86627                 changed.comment = '';
86628               }
86629
86630               if (!onInput) {
86631                 corePreferences('comment', changed.comment);
86632                 corePreferences('commentDate', Date.now());
86633               }
86634             }
86635
86636             if (changed.hasOwnProperty('source')) {
86637               if (changed.source === undefined) {
86638                 corePreferences('source', null);
86639               } else if (!onInput) {
86640                 corePreferences('source', changed.source);
86641                 corePreferences('commentDate', Date.now());
86642               }
86643             } // no need to update `prefs` for `hashtags` here since it's done in `updateChangeset`
86644
86645
86646             updateChangeset(changed, onInput);
86647
86648             if (_selection) {
86649               _selection.call(render);
86650             }
86651           }
86652
86653           function findHashtags(tags, commentOnly) {
86654             var detectedHashtags = commentHashtags();
86655
86656             if (detectedHashtags.length) {
86657               // always remove stored hashtags if there are hashtags in the comment - #4304
86658               corePreferences('hashtags', null);
86659             }
86660
86661             if (!detectedHashtags.length || !commentOnly) {
86662               detectedHashtags = detectedHashtags.concat(hashtagHashtags());
86663             }
86664
86665             var allLowerCase = new Set();
86666             return detectedHashtags.filter(function (hashtag) {
86667               // Compare tags as lowercase strings, but keep original case tags
86668               var lowerCase = hashtag.toLowerCase();
86669
86670               if (!allLowerCase.has(lowerCase)) {
86671                 allLowerCase.add(lowerCase);
86672                 return true;
86673               }
86674
86675               return false;
86676             }); // Extract hashtags from `comment`
86677
86678             function commentHashtags() {
86679               var matches = (tags.comment || '').replace(/http\S*/g, '') // drop anything that looks like a URL - #4289
86680               .match(hashtagRegex);
86681               return matches || [];
86682             } // Extract and clean hashtags from `hashtags`
86683
86684
86685             function hashtagHashtags() {
86686               var matches = (tags.hashtags || '').split(/[,;\s]+/).map(function (s) {
86687                 if (s[0] !== '#') {
86688                   s = '#' + s;
86689                 } // prepend '#'
86690
86691
86692                 var matched = s.match(hashtagRegex);
86693                 return matched && matched[0];
86694               }).filter(Boolean); // exclude falsy
86695
86696               return matches || [];
86697             }
86698           }
86699
86700           function isReviewRequested(tags) {
86701             var rr = tags.review_requested;
86702             if (rr === undefined) return false;
86703             rr = rr.trim().toLowerCase();
86704             return !(rr === '' || rr === 'no');
86705           }
86706
86707           function updateChangeset(changed, onInput) {
86708             var tags = Object.assign({}, context.changeset.tags); // shallow copy
86709
86710             Object.keys(changed).forEach(function (k) {
86711               var v = changed[k];
86712               k = context.cleanTagKey(k);
86713               if (readOnlyTags.indexOf(k) !== -1) return;
86714
86715               if (v === undefined) {
86716                 delete tags[k];
86717               } else if (onInput) {
86718                 tags[k] = v;
86719               } else {
86720                 tags[k] = context.cleanTagValue(v);
86721               }
86722             });
86723
86724             if (!onInput) {
86725               // when changing the comment, override hashtags with any found in comment.
86726               var commentOnly = changed.hasOwnProperty('comment') && changed.comment !== '';
86727               var arr = findHashtags(tags, commentOnly);
86728
86729               if (arr.length) {
86730                 tags.hashtags = context.cleanTagValue(arr.join(';'));
86731                 corePreferences('hashtags', tags.hashtags);
86732               } else {
86733                 delete tags.hashtags;
86734                 corePreferences('hashtags', null);
86735               }
86736             } // always update userdetails, just in case user reauthenticates as someone else
86737
86738
86739             if (_userDetails && _userDetails.changesets_count !== undefined) {
86740               var changesetsCount = parseInt(_userDetails.changesets_count, 10) + 1; // #4283
86741
86742               tags.changesets_count = String(changesetsCount); // first 100 edits - new user
86743
86744               if (changesetsCount <= 100) {
86745                 var s;
86746                 s = corePreferences('walkthrough_completed');
86747
86748                 if (s) {
86749                   tags['ideditor:walkthrough_completed'] = s;
86750                 }
86751
86752                 s = corePreferences('walkthrough_progress');
86753
86754                 if (s) {
86755                   tags['ideditor:walkthrough_progress'] = s;
86756                 }
86757
86758                 s = corePreferences('walkthrough_started');
86759
86760                 if (s) {
86761                   tags['ideditor:walkthrough_started'] = s;
86762                 }
86763               }
86764             } else {
86765               delete tags.changesets_count;
86766             }
86767
86768             if (!fastDeepEqual(context.changeset.tags, tags)) {
86769               context.changeset = context.changeset.update({
86770                 tags: tags
86771               });
86772             }
86773           }
86774
86775           commit.reset = function () {
86776             context.changeset = null;
86777           };
86778
86779           return utilRebind(commit, dispatch, 'on');
86780         }
86781
86782         // for punction see https://stackoverflow.com/a/21224179
86783
86784         function simplify(str) {
86785           if (typeof str !== 'string') return '';
86786           return diacritics.remove(str.replace(/&/g, 'and').replace(/İ/ig, 'i').replace(/[\s\-=_!"#%'*{},.\/:;?\(\)\[\]@\\$\^*+<>«»~`’\u00a1\u00a7\u00b6\u00b7\u00bf\u037e\u0387\u055a-\u055f\u0589\u05c0\u05c3\u05c6\u05f3\u05f4\u0609\u060a\u060c\u060d\u061b\u061e\u061f\u066a-\u066d\u06d4\u0700-\u070d\u07f7-\u07f9\u0830-\u083e\u085e\u0964\u0965\u0970\u0af0\u0df4\u0e4f\u0e5a\u0e5b\u0f04-\u0f12\u0f14\u0f85\u0fd0-\u0fd4\u0fd9\u0fda\u104a-\u104f\u10fb\u1360-\u1368\u166d\u166e\u16eb-\u16ed\u1735\u1736\u17d4-\u17d6\u17d8-\u17da\u1800-\u1805\u1807-\u180a\u1944\u1945\u1a1e\u1a1f\u1aa0-\u1aa6\u1aa8-\u1aad\u1b5a-\u1b60\u1bfc-\u1bff\u1c3b-\u1c3f\u1c7e\u1c7f\u1cc0-\u1cc7\u1cd3\u200b-\u200f\u2016\u2017\u2020-\u2027\u2030-\u2038\u203b-\u203e\u2041-\u2043\u2047-\u2051\u2053\u2055-\u205e\u2cf9-\u2cfc\u2cfe\u2cff\u2d70\u2e00\u2e01\u2e06-\u2e08\u2e0b\u2e0e-\u2e16\u2e18\u2e19\u2e1b\u2e1e\u2e1f\u2e2a-\u2e2e\u2e30-\u2e39\u3001-\u3003\u303d\u30fb\ua4fe\ua4ff\ua60d-\ua60f\ua673\ua67e\ua6f2-\ua6f7\ua874-\ua877\ua8ce\ua8cf\ua8f8-\ua8fa\ua92e\ua92f\ua95f\ua9c1-\ua9cd\ua9de\ua9df\uaa5c-\uaa5f\uaade\uaadf\uaaf0\uaaf1\uabeb\ufe10-\ufe16\ufe19\ufe30\ufe45\ufe46\ufe49-\ufe4c\ufe50-\ufe52\ufe54-\ufe57\ufe5f-\ufe61\ufe68\ufe6a\ufe6b\ufeff\uff01-\uff03\uff05-\uff07\uff0a\uff0c\uff0e\uff0f\uff1a\uff1b\uff1f\uff20\uff3c\uff61\uff64\uff65]+/g, '').toLowerCase());
86787         }
86788
86789         // `resolveStrings`
86790         // Resolves the text strings for a given community index item
86791         //
86792         // Arguments
86793         //   `item`:  Object containing the community index item
86794         //   `defaults`: Object containing the community index default strings
86795         //   `localizerFn?`: optional function we will call to do the localization.
86796         //      This function should be like the iD `t()` function that
86797         //      accepts a `stringID` and returns a localized string
86798         //
86799         // Returns
86800         //   An Object containing all the resolved strings:
86801         //   {
86802         //     name:                     'talk-ru Mailing List',
86803         //     url:                      'https://lists.openstreetmap.org/listinfo/talk-ru',
86804         //     signupUrl:                'https://example.url/signup',
86805         //     description:              'A one line description',
86806         //     extendedDescription:      'Extended description',
86807         //     nameHTML:                 '<a href="the url">the name</a>',
86808         //     urlHTML:                  '<a href="the url">the url</a>',
86809         //     signupUrlHTML:            '<a href="the signupUrl">the signupUrl</a>',
86810         //     descriptionHTML:          the description, with urls and signupUrls linkified,
86811         //     extendedDescriptionHTML:  the extendedDescription with urls and signupUrls linkified
86812         //   }
86813         //
86814
86815         function resolveStrings(item, defaults, localizerFn) {
86816           var itemStrings = Object.assign({}, item.strings); // shallow clone
86817
86818           var defaultStrings = Object.assign({}, defaults[item.type]); // shallow clone
86819
86820           var anyToken = new RegExp(/(\{\w+\})/, 'gi'); // Pre-localize the item and default strings
86821
86822           if (localizerFn) {
86823             if (itemStrings.community) {
86824               var communityID = simplify(itemStrings.community);
86825               itemStrings.community = localizerFn("_communities.".concat(communityID));
86826             }
86827
86828             ['name', 'description', 'extendedDescription'].forEach(function (prop) {
86829               if (defaultStrings[prop]) defaultStrings[prop] = localizerFn("_defaults.".concat(item.type, ".").concat(prop));
86830               if (itemStrings[prop]) itemStrings[prop] = localizerFn("".concat(item.id, ".").concat(prop));
86831             });
86832           }
86833
86834           var replacements = {
86835             account: item.account,
86836             community: itemStrings.community,
86837             signupUrl: itemStrings.signupUrl,
86838             url: itemStrings.url
86839           }; // Resolve URLs first (which may refer to {account})
86840
86841           if (!replacements.signupUrl) {
86842             replacements.signupUrl = resolve(itemStrings.signupUrl || defaultStrings.signupUrl);
86843           }
86844
86845           if (!replacements.url) {
86846             replacements.url = resolve(itemStrings.url || defaultStrings.url);
86847           }
86848
86849           var resolved = {
86850             name: resolve(itemStrings.name || defaultStrings.name),
86851             url: resolve(itemStrings.url || defaultStrings.url),
86852             signupUrl: resolve(itemStrings.signupUrl || defaultStrings.signupUrl),
86853             description: resolve(itemStrings.description || defaultStrings.description),
86854             extendedDescription: resolve(itemStrings.extendedDescription || defaultStrings.extendedDescription)
86855           }; // Generate linkified strings
86856
86857           resolved.nameHTML = linkify(resolved.url, resolved.name);
86858           resolved.urlHTML = linkify(resolved.url);
86859           resolved.signupUrlHTML = linkify(resolved.signupUrl);
86860           resolved.descriptionHTML = resolve(itemStrings.description || defaultStrings.description, true);
86861           resolved.extendedDescriptionHTML = resolve(itemStrings.extendedDescription || defaultStrings.extendedDescription, true);
86862           return resolved;
86863
86864           function resolve(s, addLinks) {
86865             if (!s) return undefined;
86866             var result = s;
86867
86868             for (var key in replacements) {
86869               var token = "{".concat(key, "}");
86870               var regex = new RegExp(token, 'g');
86871
86872               if (regex.test(result)) {
86873                 var replacement = replacements[key];
86874
86875                 if (!replacement) {
86876                   throw new Error("Cannot resolve token: ".concat(token));
86877                 } else {
86878                   if (addLinks && (key === 'signupUrl' || key === 'url')) {
86879                     replacement = linkify(replacement);
86880                   }
86881
86882                   result = result.replace(regex, replacement);
86883                 }
86884               }
86885             } // There shouldn't be any leftover tokens in a resolved string
86886
86887
86888             var leftovers = result.match(anyToken);
86889
86890             if (leftovers) {
86891               throw new Error("Cannot resolve tokens: ".concat(leftovers));
86892             } // Linkify subreddits like `/r/openstreetmap`
86893             // https://github.com/osmlab/osm-community-index/issues/82
86894             // https://github.com/openstreetmap/iD/issues/4997
86895
86896
86897             if (addLinks && item.type === 'reddit') {
86898               result = result.replace(/(\/r\/\w+\/*)/i, function (match) {
86899                 return linkify(resolved.url, match);
86900               });
86901             }
86902
86903             return result;
86904           }
86905
86906           function linkify(url, text) {
86907             if (!url) return undefined;
86908             text = text || url;
86909             return "<a target=\"_blank\" href=\"".concat(url, "\">").concat(text, "</a>");
86910           }
86911         }
86912
86913         var _oci = null;
86914         function uiSuccess(context) {
86915           var MAXEVENTS = 2;
86916           var dispatch = dispatch$8('cancel');
86917
86918           var _changeset;
86919
86920           var _location;
86921
86922           ensureOSMCommunityIndex(); // start fetching the data
86923
86924           function ensureOSMCommunityIndex() {
86925             var data = _mainFileFetcher;
86926             return Promise.all([data.get('oci_features'), data.get('oci_resources'), data.get('oci_defaults')]).then(function (vals) {
86927               if (_oci) return _oci; // Merge Custom Features
86928
86929               if (vals[0] && Array.isArray(vals[0].features)) {
86930                 _mainLocations.mergeCustomGeoJSON(vals[0]);
86931               }
86932
86933               var ociResources = Object.values(vals[1].resources);
86934
86935               if (ociResources.length) {
86936                 // Resolve all locationSet features.
86937                 return _mainLocations.mergeLocationSets(ociResources).then(function () {
86938                   _oci = {
86939                     resources: ociResources,
86940                     defaults: vals[2].defaults
86941                   };
86942                   return _oci;
86943                 });
86944               } else {
86945                 _oci = {
86946                   resources: [],
86947                   // no resources?
86948                   defaults: vals[2].defaults
86949                 };
86950                 return _oci;
86951               }
86952             });
86953           } // string-to-date parsing in JavaScript is weird
86954
86955
86956           function parseEventDate(when) {
86957             if (!when) return;
86958             var raw = when.trim();
86959             if (!raw) return;
86960
86961             if (!/Z$/.test(raw)) {
86962               // if no trailing 'Z', add one
86963               raw += 'Z'; // this forces date to be parsed as a UTC date
86964             }
86965
86966             var parsed = new Date(raw);
86967             return new Date(parsed.toUTCString().substr(0, 25)); // convert to local timezone
86968           }
86969
86970           function success(selection) {
86971             var header = selection.append('div').attr('class', 'header fillL');
86972             header.append('h3').html(_t.html('success.just_edited'));
86973             header.append('button').attr('class', 'close').on('click', function () {
86974               return dispatch.call('cancel');
86975             }).call(svgIcon('#iD-icon-close'));
86976             var body = selection.append('div').attr('class', 'body save-success fillL');
86977             var summary = body.append('div').attr('class', 'save-summary');
86978             summary.append('h3').html(_t.html('success.thank_you' + (_location ? '_location' : ''), {
86979               where: _location
86980             }));
86981             summary.append('p').html(_t.html('success.help_html')).append('a').attr('class', 'link-out').attr('target', '_blank').attr('href', _t('success.help_link_url')).call(svgIcon('#iD-icon-out-link', 'inline')).append('span').html(_t.html('success.help_link_text'));
86982             var osm = context.connection();
86983             if (!osm) return;
86984             var changesetURL = osm.changesetURL(_changeset.id);
86985             var table = summary.append('table').attr('class', 'summary-table');
86986             var row = table.append('tr').attr('class', 'summary-row');
86987             row.append('td').attr('class', 'cell-icon summary-icon').append('a').attr('target', '_blank').attr('href', changesetURL).append('svg').attr('class', 'logo-small').append('use').attr('xlink:href', '#iD-logo-osm');
86988             var summaryDetail = row.append('td').attr('class', 'cell-detail summary-detail');
86989             summaryDetail.append('a').attr('class', 'cell-detail summary-view-on-osm').attr('target', '_blank').attr('href', changesetURL).html(_t.html('success.view_on_osm'));
86990             summaryDetail.append('div').html(_t.html('success.changeset_id', {
86991               changeset_id: "<a href=\"".concat(changesetURL, "\" target=\"_blank\">").concat(_changeset.id, "</a>")
86992             })); // Get OSM community index features intersecting the map..
86993
86994             ensureOSMCommunityIndex().then(function (oci) {
86995               var loc = context.map().center();
86996               var validLocations = _mainLocations.locationsAt(loc); // Gather the communities
86997
86998               var communities = [];
86999               oci.resources.forEach(function (resource) {
87000                 var area = validLocations[resource.locationSetID];
87001                 if (!area) return; // Resolve strings
87002
87003                 var localizer = function localizer(stringID) {
87004                   return _t.html("community.".concat(stringID));
87005                 };
87006
87007                 resource.resolved = resolveStrings(resource, oci.defaults, localizer);
87008                 communities.push({
87009                   area: area,
87010                   order: resource.order || 0,
87011                   resource: resource
87012                 });
87013               }); // sort communities by feature area ascending, community order descending
87014
87015               communities.sort(function (a, b) {
87016                 return a.area - b.area || b.order - a.order;
87017               });
87018               body.call(showCommunityLinks, communities.map(function (c) {
87019                 return c.resource;
87020               }));
87021             });
87022           }
87023
87024           function showCommunityLinks(selection, resources) {
87025             var communityLinks = selection.append('div').attr('class', 'save-communityLinks');
87026             communityLinks.append('h3').html(_t.html('success.like_osm'));
87027             var table = communityLinks.append('table').attr('class', 'community-table');
87028             var row = table.selectAll('.community-row').data(resources);
87029             var rowEnter = row.enter().append('tr').attr('class', 'community-row');
87030             rowEnter.append('td').attr('class', 'cell-icon community-icon').append('a').attr('target', '_blank').attr('href', function (d) {
87031               return d.resolved.url;
87032             }).append('svg').attr('class', 'logo-small').append('use').attr('xlink:href', function (d) {
87033               return "#community-".concat(d.type);
87034             });
87035             var communityDetail = rowEnter.append('td').attr('class', 'cell-detail community-detail');
87036             communityDetail.each(showCommunityDetails);
87037             communityLinks.append('div').attr('class', 'community-missing').html(_t.html('success.missing')).append('a').attr('class', 'link-out').attr('target', '_blank').call(svgIcon('#iD-icon-out-link', 'inline')).attr('href', 'https://github.com/osmlab/osm-community-index/issues').append('span').html(_t.html('success.tell_us'));
87038           }
87039
87040           function showCommunityDetails(d) {
87041             var selection = select(this);
87042             var communityID = d.id;
87043             selection.append('div').attr('class', 'community-name').html(d.resolved.nameHTML);
87044             selection.append('div').attr('class', 'community-description').html(d.resolved.descriptionHTML); // Create an expanding section if any of these are present..
87045
87046             if (d.resolved.extendedDescriptionHTML || d.languageCodes && d.languageCodes.length) {
87047               selection.append('div').call(uiDisclosure(context, "community-more-".concat(d.id), false).expanded(false).updatePreference(false).label(_t.html('success.more')).content(showMore));
87048             }
87049
87050             var nextEvents = (d.events || []).map(function (event) {
87051               event.date = parseEventDate(event.when);
87052               return event;
87053             }).filter(function (event) {
87054               // date is valid and future (or today)
87055               var t = event.date.getTime();
87056               var now = new Date().setHours(0, 0, 0, 0);
87057               return !isNaN(t) && t >= now;
87058             }).sort(function (a, b) {
87059               // sort by date ascending
87060               return a.date < b.date ? -1 : a.date > b.date ? 1 : 0;
87061             }).slice(0, MAXEVENTS); // limit number of events shown
87062
87063             if (nextEvents.length) {
87064               selection.append('div').call(uiDisclosure(context, "community-events-".concat(d.id), false).expanded(false).updatePreference(false).label(_t.html('success.events')).content(showNextEvents)).select('.hide-toggle').append('span').attr('class', 'badge-text').html(nextEvents.length);
87065             }
87066
87067             function showMore(selection) {
87068               var more = selection.selectAll('.community-more').data([0]);
87069               var moreEnter = more.enter().append('div').attr('class', 'community-more');
87070
87071               if (d.resolved.extendedDescriptionHTML) {
87072                 moreEnter.append('div').attr('class', 'community-extended-description').html(d.resolved.extendedDescriptionHTML);
87073               }
87074
87075               if (d.languageCodes && d.languageCodes.length) {
87076                 var languageList = d.languageCodes.map(function (code) {
87077                   return _mainLocalizer.languageName(code);
87078                 }).join(', ');
87079                 moreEnter.append('div').attr('class', 'community-languages').html(_t.html('success.languages', {
87080                   languages: languageList
87081                 }));
87082               }
87083             }
87084
87085             function showNextEvents(selection) {
87086               var events = selection.append('div').attr('class', 'community-events');
87087               var item = events.selectAll('.community-event').data(nextEvents);
87088               var itemEnter = item.enter().append('div').attr('class', 'community-event');
87089               itemEnter.append('div').attr('class', 'community-event-name').append('a').attr('target', '_blank').attr('href', function (d) {
87090                 return d.url;
87091               }).html(function (d) {
87092                 var name = d.name;
87093
87094                 if (d.i18n && d.id) {
87095                   name = _t("community.".concat(communityID, ".events.").concat(d.id, ".name"), {
87096                     "default": name
87097                   });
87098                 }
87099
87100                 return name;
87101               });
87102               itemEnter.append('div').attr('class', 'community-event-when').html(function (d) {
87103                 var options = {
87104                   weekday: 'short',
87105                   day: 'numeric',
87106                   month: 'short',
87107                   year: 'numeric'
87108                 };
87109
87110                 if (d.date.getHours() || d.date.getMinutes()) {
87111                   // include time if it has one
87112                   options.hour = 'numeric';
87113                   options.minute = 'numeric';
87114                 }
87115
87116                 return d.date.toLocaleString(_mainLocalizer.localeCode(), options);
87117               });
87118               itemEnter.append('div').attr('class', 'community-event-where').html(function (d) {
87119                 var where = d.where;
87120
87121                 if (d.i18n && d.id) {
87122                   where = _t("community.".concat(communityID, ".events.").concat(d.id, ".where"), {
87123                     "default": where
87124                   });
87125                 }
87126
87127                 return where;
87128               });
87129               itemEnter.append('div').attr('class', 'community-event-description').html(function (d) {
87130                 var description = d.description;
87131
87132                 if (d.i18n && d.id) {
87133                   description = _t("community.".concat(communityID, ".events.").concat(d.id, ".description"), {
87134                     "default": description
87135                   });
87136                 }
87137
87138                 return description;
87139               });
87140             }
87141           }
87142
87143           success.changeset = function (val) {
87144             if (!arguments.length) return _changeset;
87145             _changeset = val;
87146             return success;
87147           };
87148
87149           success.location = function (val) {
87150             if (!arguments.length) return _location;
87151             _location = val;
87152             return success;
87153           };
87154
87155           return utilRebind(success, dispatch, 'on');
87156         }
87157
87158         function modeSave(context) {
87159           var mode = {
87160             id: 'save'
87161           };
87162           var keybinding = utilKeybinding('modeSave');
87163           var commit = uiCommit(context).on('cancel', cancel);
87164
87165           var _conflictsUi; // uiConflicts
87166
87167
87168           var _location;
87169
87170           var _success;
87171
87172           var uploader = context.uploader().on('saveStarted.modeSave', function () {
87173             keybindingOff();
87174           }) // fire off some async work that we want to be ready later
87175           .on('willAttemptUpload.modeSave', prepareForSuccess).on('progressChanged.modeSave', showProgress).on('resultNoChanges.modeSave', function () {
87176             cancel();
87177           }).on('resultErrors.modeSave', showErrors).on('resultConflicts.modeSave', showConflicts).on('resultSuccess.modeSave', showSuccess);
87178
87179           function cancel() {
87180             context.enter(modeBrowse(context));
87181           }
87182
87183           function showProgress(num, total) {
87184             var modal = context.container().select('.loading-modal .modal-section');
87185             var progress = modal.selectAll('.progress').data([0]); // enter/update
87186
87187             progress.enter().append('div').attr('class', 'progress').merge(progress).text(_t('save.conflict_progress', {
87188               num: num,
87189               total: total
87190             }));
87191           }
87192
87193           function showConflicts(changeset, conflicts, origChanges) {
87194             var selection = context.container().select('.sidebar').append('div').attr('class', 'sidebar-component');
87195             context.container().selectAll('.main-content').classed('active', true).classed('inactive', false);
87196             _conflictsUi = uiConflicts(context).conflictList(conflicts).origChanges(origChanges).on('cancel', function () {
87197               context.container().selectAll('.main-content').classed('active', false).classed('inactive', true);
87198               selection.remove();
87199               keybindingOn();
87200               uploader.cancelConflictResolution();
87201             }).on('save', function () {
87202               context.container().selectAll('.main-content').classed('active', false).classed('inactive', true);
87203               selection.remove();
87204               uploader.processResolvedConflicts(changeset);
87205             });
87206             selection.call(_conflictsUi);
87207           }
87208
87209           function showErrors(errors) {
87210             keybindingOn();
87211             var selection = uiConfirm(context.container());
87212             selection.select('.modal-section.header').append('h3').text(_t('save.error'));
87213             addErrors(selection, errors);
87214             selection.okButton();
87215           }
87216
87217           function addErrors(selection, data) {
87218             var message = selection.select('.modal-section.message-text');
87219             var items = message.selectAll('.error-container').data(data);
87220             var enter = items.enter().append('div').attr('class', 'error-container');
87221             enter.append('a').attr('class', 'error-description').attr('href', '#').classed('hide-toggle', true).text(function (d) {
87222               return d.msg || _t('save.unknown_error_details');
87223             }).on('click', function (d3_event) {
87224               d3_event.preventDefault();
87225               var error = select(this);
87226               var detail = select(this.nextElementSibling);
87227               var exp = error.classed('expanded');
87228               detail.style('display', exp ? 'none' : 'block');
87229               error.classed('expanded', !exp);
87230             });
87231             var details = enter.append('div').attr('class', 'error-detail-container').style('display', 'none');
87232             details.append('ul').attr('class', 'error-detail-list').selectAll('li').data(function (d) {
87233               return d.details || [];
87234             }).enter().append('li').attr('class', 'error-detail-item').text(function (d) {
87235               return d;
87236             });
87237             items.exit().remove();
87238           }
87239
87240           function showSuccess(changeset) {
87241             commit.reset();
87242
87243             var ui = _success.changeset(changeset).location(_location).on('cancel', function () {
87244               context.ui().sidebar.hide();
87245             });
87246
87247             context.enter(modeBrowse(context).sidebar(ui));
87248           }
87249
87250           function keybindingOn() {
87251             select(document).call(keybinding.on('⎋', cancel, true));
87252           }
87253
87254           function keybindingOff() {
87255             select(document).call(keybinding.unbind);
87256           } // Reverse geocode current map location so we can display a message on
87257           // the success screen like "Thank you for editing around place, region."
87258
87259
87260           function prepareForSuccess() {
87261             _success = uiSuccess(context);
87262             _location = null;
87263             if (!services.geocoder) return;
87264             services.geocoder.reverse(context.map().center(), function (err, result) {
87265               if (err || !result || !result.address) return;
87266               var addr = result.address;
87267               var place = addr && (addr.town || addr.city || addr.county) || '';
87268               var region = addr && (addr.state || addr.country) || '';
87269               var separator = place && region ? _t('success.thank_you_where.separator') : '';
87270               _location = _t('success.thank_you_where.format', {
87271                 place: place,
87272                 separator: separator,
87273                 region: region
87274               });
87275             });
87276           }
87277
87278           mode.selectedIDs = function () {
87279             return _conflictsUi ? _conflictsUi.shownEntityIds() : [];
87280           };
87281
87282           mode.enter = function () {
87283             // Show sidebar
87284             context.ui().sidebar.expand();
87285
87286             function done() {
87287               context.ui().sidebar.show(commit);
87288             }
87289
87290             keybindingOn();
87291             context.container().selectAll('.main-content').classed('active', false).classed('inactive', true);
87292             var osm = context.connection();
87293
87294             if (!osm) {
87295               cancel();
87296               return;
87297             }
87298
87299             if (osm.authenticated()) {
87300               done();
87301             } else {
87302               osm.authenticate(function (err) {
87303                 if (err) {
87304                   cancel();
87305                 } else {
87306                   done();
87307                 }
87308               });
87309             }
87310           };
87311
87312           mode.exit = function () {
87313             keybindingOff();
87314             context.container().selectAll('.main-content').classed('active', true).classed('inactive', false);
87315             context.ui().sidebar.hide();
87316           };
87317
87318           return mode;
87319         }
87320
87321         function modeSelectError(context, selectedErrorID, selectedErrorService) {
87322           var mode = {
87323             id: 'select-error',
87324             button: 'browse'
87325           };
87326           var keybinding = utilKeybinding('select-error');
87327           var errorService = services[selectedErrorService];
87328           var errorEditor;
87329
87330           switch (selectedErrorService) {
87331             case 'improveOSM':
87332               errorEditor = uiImproveOsmEditor(context).on('change', function () {
87333                 context.map().pan([0, 0]); // trigger a redraw
87334
87335                 var error = checkSelectedID();
87336                 if (!error) return;
87337                 context.ui().sidebar.show(errorEditor.error(error));
87338               });
87339               break;
87340
87341             case 'keepRight':
87342               errorEditor = uiKeepRightEditor(context).on('change', function () {
87343                 context.map().pan([0, 0]); // trigger a redraw
87344
87345                 var error = checkSelectedID();
87346                 if (!error) return;
87347                 context.ui().sidebar.show(errorEditor.error(error));
87348               });
87349               break;
87350
87351             case 'osmose':
87352               errorEditor = uiOsmoseEditor(context).on('change', function () {
87353                 context.map().pan([0, 0]); // trigger a redraw
87354
87355                 var error = checkSelectedID();
87356                 if (!error) return;
87357                 context.ui().sidebar.show(errorEditor.error(error));
87358               });
87359               break;
87360           }
87361
87362           var behaviors = [behaviorBreathe(), behaviorHover(context), behaviorSelect(context), behaviorLasso(context), modeDragNode(context).behavior, modeDragNote(context).behavior];
87363
87364           function checkSelectedID() {
87365             if (!errorService) return;
87366             var error = errorService.getError(selectedErrorID);
87367
87368             if (!error) {
87369               context.enter(modeBrowse(context));
87370             }
87371
87372             return error;
87373           }
87374
87375           mode.zoomToSelected = function () {
87376             if (!errorService) return;
87377             var error = errorService.getError(selectedErrorID);
87378
87379             if (error) {
87380               context.map().centerZoomEase(error.loc, 20);
87381             }
87382           };
87383
87384           mode.enter = function () {
87385             var error = checkSelectedID();
87386             if (!error) return;
87387             behaviors.forEach(context.install);
87388             keybinding.on(_t('inspector.zoom_to.key'), mode.zoomToSelected).on('⎋', esc, true);
87389             select(document).call(keybinding);
87390             selectError();
87391             var sidebar = context.ui().sidebar;
87392             sidebar.show(errorEditor.error(error));
87393             context.map().on('drawn.select-error', selectError); // class the error as selected, or return to browse mode if the error is gone
87394
87395             function selectError(d3_event, drawn) {
87396               if (!checkSelectedID()) return;
87397               var selection = context.surface().selectAll('.itemId-' + selectedErrorID + '.' + selectedErrorService);
87398
87399               if (selection.empty()) {
87400                 // Return to browse mode if selected DOM elements have
87401                 // disappeared because the user moved them out of view..
87402                 var source = d3_event && d3_event.type === 'zoom' && d3_event.sourceEvent;
87403
87404                 if (drawn && source && (source.type === 'pointermove' || source.type === 'mousemove' || source.type === 'touchmove')) {
87405                   context.enter(modeBrowse(context));
87406                 }
87407               } else {
87408                 selection.classed('selected', true);
87409                 context.selectedErrorID(selectedErrorID);
87410               }
87411             }
87412
87413             function esc() {
87414               if (context.container().select('.combobox').size()) return;
87415               context.enter(modeBrowse(context));
87416             }
87417           };
87418
87419           mode.exit = function () {
87420             behaviors.forEach(context.uninstall);
87421             select(document).call(keybinding.unbind);
87422             context.surface().selectAll('.qaItem.selected').classed('selected hover', false);
87423             context.map().on('drawn.select-error', null);
87424             context.ui().sidebar.hide();
87425             context.selectedErrorID(null);
87426             context.features().forceVisible([]);
87427           };
87428
87429           return mode;
87430         }
87431
87432         function uiToolOldDrawModes(context) {
87433           var tool = {
87434             id: 'old_modes',
87435             label: _t.html('toolbar.add_feature')
87436           };
87437           var modes = [modeAddPoint(context, {
87438             title: _t.html('modes.add_point.title'),
87439             button: 'point',
87440             description: _t.html('modes.add_point.description'),
87441             preset: _mainPresetIndex.item('point'),
87442             key: '1'
87443           }), modeAddLine(context, {
87444             title: _t.html('modes.add_line.title'),
87445             button: 'line',
87446             description: _t.html('modes.add_line.description'),
87447             preset: _mainPresetIndex.item('line'),
87448             key: '2'
87449           }), modeAddArea(context, {
87450             title: _t.html('modes.add_area.title'),
87451             button: 'area',
87452             description: _t.html('modes.add_area.description'),
87453             preset: _mainPresetIndex.item('area'),
87454             key: '3'
87455           })];
87456
87457           function enabled() {
87458             return osmEditable();
87459           }
87460
87461           function osmEditable() {
87462             return context.editable();
87463           }
87464
87465           modes.forEach(function (mode) {
87466             context.keybinding().on(mode.key, function () {
87467               if (!enabled()) return;
87468
87469               if (mode.id === context.mode().id) {
87470                 context.enter(modeBrowse(context));
87471               } else {
87472                 context.enter(mode);
87473               }
87474             });
87475           });
87476
87477           tool.render = function (selection) {
87478             var wrap = selection.append('div').attr('class', 'joined').style('display', 'flex');
87479
87480             var debouncedUpdate = debounce(update, 500, {
87481               leading: true,
87482               trailing: true
87483             });
87484
87485             context.map().on('move.modes', debouncedUpdate).on('drawn.modes', debouncedUpdate);
87486             context.on('enter.modes', update);
87487             update();
87488
87489             function update() {
87490               var buttons = wrap.selectAll('button.add-button').data(modes, function (d) {
87491                 return d.id;
87492               }); // exit
87493
87494               buttons.exit().remove(); // enter
87495
87496               var buttonsEnter = buttons.enter().append('button').attr('class', function (d) {
87497                 return d.id + ' add-button bar-button';
87498               }).on('click.mode-buttons', function (d3_event, d) {
87499                 if (!enabled()) return; // When drawing, ignore accidental clicks on mode buttons - #4042
87500
87501                 var currMode = context.mode().id;
87502                 if (/^draw/.test(currMode)) return;
87503
87504                 if (d.id === currMode) {
87505                   context.enter(modeBrowse(context));
87506                 } else {
87507                   context.enter(d);
87508                 }
87509               }).call(uiTooltip().placement('bottom').title(function (d) {
87510                 return d.description;
87511               }).keys(function (d) {
87512                 return [d.key];
87513               }).scrollContainer(context.container().select('.top-toolbar')));
87514               buttonsEnter.each(function (d) {
87515                 select(this).call(svgIcon('#iD-icon-' + d.button));
87516               });
87517               buttonsEnter.append('span').attr('class', 'label').html(function (mode) {
87518                 return mode.title;
87519               }); // if we are adding/removing the buttons, check if toolbar has overflowed
87520
87521               if (buttons.enter().size() || buttons.exit().size()) {
87522                 context.ui().checkOverflow('.top-toolbar', true);
87523               } // update
87524
87525
87526               buttons = buttons.merge(buttonsEnter).classed('disabled', function (d) {
87527                 return !enabled();
87528               }).classed('active', function (d) {
87529                 return context.mode() && context.mode().button === d.button;
87530               });
87531             }
87532           };
87533
87534           return tool;
87535         }
87536
87537         function uiToolNotes(context) {
87538           var tool = {
87539             id: 'notes',
87540             label: _t.html('modes.add_note.label')
87541           };
87542           var mode = modeAddNote(context);
87543
87544           function enabled() {
87545             return notesEnabled() && notesEditable();
87546           }
87547
87548           function notesEnabled() {
87549             var noteLayer = context.layers().layer('notes');
87550             return noteLayer && noteLayer.enabled();
87551           }
87552
87553           function notesEditable() {
87554             var mode = context.mode();
87555             return context.map().notesEditable() && mode && mode.id !== 'save';
87556           }
87557
87558           context.keybinding().on(mode.key, function () {
87559             if (!enabled()) return;
87560
87561             if (mode.id === context.mode().id) {
87562               context.enter(modeBrowse(context));
87563             } else {
87564               context.enter(mode);
87565             }
87566           });
87567
87568           tool.render = function (selection) {
87569             var debouncedUpdate = debounce(update, 500, {
87570               leading: true,
87571               trailing: true
87572             });
87573
87574             context.map().on('move.notes', debouncedUpdate).on('drawn.notes', debouncedUpdate);
87575             context.on('enter.notes', update);
87576             update();
87577
87578             function update() {
87579               var showNotes = notesEnabled();
87580               var data = showNotes ? [mode] : [];
87581               var buttons = selection.selectAll('button.add-button').data(data, function (d) {
87582                 return d.id;
87583               }); // exit
87584
87585               buttons.exit().remove(); // enter
87586
87587               var buttonsEnter = buttons.enter().append('button').attr('class', function (d) {
87588                 return d.id + ' add-button bar-button';
87589               }).on('click.notes', function (d3_event, d) {
87590                 if (!enabled()) return; // When drawing, ignore accidental clicks on mode buttons - #4042
87591
87592                 var currMode = context.mode().id;
87593                 if (/^draw/.test(currMode)) return;
87594
87595                 if (d.id === currMode) {
87596                   context.enter(modeBrowse(context));
87597                 } else {
87598                   context.enter(d);
87599                 }
87600               }).call(uiTooltip().placement('bottom').title(function (d) {
87601                 return d.description;
87602               }).keys(function (d) {
87603                 return [d.key];
87604               }).scrollContainer(context.container().select('.top-toolbar')));
87605               buttonsEnter.each(function (d) {
87606                 select(this).call(svgIcon(d.icon || '#iD-icon-' + d.button));
87607               }); // if we are adding/removing the buttons, check if toolbar has overflowed
87608
87609               if (buttons.enter().size() || buttons.exit().size()) {
87610                 context.ui().checkOverflow('.top-toolbar', true);
87611               } // update
87612
87613
87614               buttons = buttons.merge(buttonsEnter).classed('disabled', function (d) {
87615                 return !enabled();
87616               }).classed('active', function (d) {
87617                 return context.mode() && context.mode().button === d.button;
87618               });
87619             }
87620           };
87621
87622           tool.uninstall = function () {
87623             context.on('enter.editor.notes', null).on('exit.editor.notes', null).on('enter.notes', null);
87624             context.map().on('move.notes', null).on('drawn.notes', null);
87625           };
87626
87627           return tool;
87628         }
87629
87630         function uiToolSave(context) {
87631           var tool = {
87632             id: 'save',
87633             label: _t.html('save.title')
87634           };
87635           var button = null;
87636           var tooltipBehavior = null;
87637           var history = context.history();
87638           var key = uiCmd('⌘S');
87639           var _numChanges = 0;
87640
87641           function isSaving() {
87642             var mode = context.mode();
87643             return mode && mode.id === 'save';
87644           }
87645
87646           function isDisabled() {
87647             return _numChanges === 0 || isSaving();
87648           }
87649
87650           function save(d3_event) {
87651             d3_event.preventDefault();
87652
87653             if (!context.inIntro() && !isSaving() && history.hasChanges()) {
87654               context.enter(modeSave(context));
87655             }
87656           }
87657
87658           function bgColor() {
87659             var step;
87660
87661             if (_numChanges === 0) {
87662               return null;
87663             } else if (_numChanges <= 50) {
87664               step = _numChanges / 50;
87665               return d3_interpolateRgb('#fff', '#ff8')(step); // white -> yellow
87666             } else {
87667               step = Math.min((_numChanges - 50) / 50, 1.0);
87668               return d3_interpolateRgb('#ff8', '#f88')(step); // yellow -> red
87669             }
87670           }
87671
87672           function updateCount() {
87673             var val = history.difference().summary().length;
87674             if (val === _numChanges) return;
87675             _numChanges = val;
87676
87677             if (tooltipBehavior) {
87678               tooltipBehavior.title(_t.html(_numChanges > 0 ? 'save.help' : 'save.no_changes')).keys([key]);
87679             }
87680
87681             if (button) {
87682               button.classed('disabled', isDisabled()).style('background', bgColor());
87683               button.select('span.count').html(_numChanges);
87684             }
87685           }
87686
87687           tool.render = function (selection) {
87688             tooltipBehavior = uiTooltip().placement('bottom').title(_t.html('save.no_changes')).keys([key]).scrollContainer(context.container().select('.top-toolbar'));
87689             var lastPointerUpType;
87690             button = selection.append('button').attr('class', 'save disabled bar-button').on('pointerup', function (d3_event) {
87691               lastPointerUpType = d3_event.pointerType;
87692             }).on('click', function (d3_event) {
87693               save(d3_event);
87694
87695               if (_numChanges === 0 && (lastPointerUpType === 'touch' || lastPointerUpType === 'pen')) {
87696                 // there are no tooltips for touch interactions so flash feedback instead
87697                 context.ui().flash.duration(2000).iconName('#iD-icon-save').iconClass('disabled').label(_t.html('save.no_changes'))();
87698               }
87699
87700               lastPointerUpType = null;
87701             }).call(tooltipBehavior);
87702             button.call(svgIcon('#iD-icon-save'));
87703             button.append('span').attr('class', 'count').attr('aria-hidden', 'true').html('0');
87704             updateCount();
87705             context.keybinding().on(key, save, true);
87706             context.history().on('change.save', updateCount);
87707             context.on('enter.save', function () {
87708               if (button) {
87709                 button.classed('disabled', isDisabled());
87710
87711                 if (isSaving()) {
87712                   button.call(tooltipBehavior.hide);
87713                 }
87714               }
87715             });
87716           };
87717
87718           tool.uninstall = function () {
87719             context.keybinding().off(key, true);
87720             context.history().on('change.save', null);
87721             context.on('enter.save', null);
87722             button = null;
87723             tooltipBehavior = null;
87724           };
87725
87726           return tool;
87727         }
87728
87729         function uiToolSidebarToggle(context) {
87730           var tool = {
87731             id: 'sidebar_toggle',
87732             label: _t.html('toolbar.inspect')
87733           };
87734
87735           tool.render = function (selection) {
87736             selection.append('button').attr('class', 'bar-button').on('click', function () {
87737               context.ui().sidebar.toggle();
87738             }).call(uiTooltip().placement('bottom').title(_t.html('sidebar.tooltip')).keys([_t('sidebar.key')]).scrollContainer(context.container().select('.top-toolbar'))).call(svgIcon('#iD-icon-sidebar-' + (_mainLocalizer.textDirection() === 'rtl' ? 'right' : 'left')));
87739           };
87740
87741           return tool;
87742         }
87743
87744         function uiToolUndoRedo(context) {
87745           var tool = {
87746             id: 'undo_redo',
87747             label: _t.html('toolbar.undo_redo')
87748           };
87749           var commands = [{
87750             id: 'undo',
87751             cmd: uiCmd('⌘Z'),
87752             action: function action() {
87753               context.undo();
87754             },
87755             annotation: function annotation() {
87756               return context.history().undoAnnotation();
87757             },
87758             icon: 'iD-icon-' + (_mainLocalizer.textDirection() === 'rtl' ? 'redo' : 'undo')
87759           }, {
87760             id: 'redo',
87761             cmd: uiCmd('⌘⇧Z'),
87762             action: function action() {
87763               context.redo();
87764             },
87765             annotation: function annotation() {
87766               return context.history().redoAnnotation();
87767             },
87768             icon: 'iD-icon-' + (_mainLocalizer.textDirection() === 'rtl' ? 'undo' : 'redo')
87769           }];
87770
87771           function editable() {
87772             return context.mode() && context.mode().id !== 'save' && context.map().editableDataEnabled(true
87773             /* ignore min zoom */
87774             );
87775           }
87776
87777           tool.render = function (selection) {
87778             var tooltipBehavior = uiTooltip().placement('bottom').title(function (d) {
87779               return d.annotation() ? _t.html(d.id + '.tooltip', {
87780                 action: d.annotation()
87781               }) : _t.html(d.id + '.nothing');
87782             }).keys(function (d) {
87783               return [d.cmd];
87784             }).scrollContainer(context.container().select('.top-toolbar'));
87785             var lastPointerUpType;
87786             var buttons = selection.selectAll('button').data(commands).enter().append('button').attr('class', function (d) {
87787               return 'disabled ' + d.id + '-button bar-button';
87788             }).on('pointerup', function (d3_event) {
87789               // `pointerup` is always called before `click`
87790               lastPointerUpType = d3_event.pointerType;
87791             }).on('click', function (d3_event, d) {
87792               d3_event.preventDefault();
87793               var annotation = d.annotation();
87794
87795               if (editable() && annotation) {
87796                 d.action();
87797               }
87798
87799               if (editable() && (lastPointerUpType === 'touch' || lastPointerUpType === 'pen')) {
87800                 // there are no tooltips for touch interactions so flash feedback instead
87801                 var text = annotation ? _t(d.id + '.tooltip', {
87802                   action: annotation
87803                 }) : _t(d.id + '.nothing');
87804                 context.ui().flash.duration(2000).iconName('#' + d.icon).iconClass(annotation ? '' : 'disabled').label(text)();
87805               }
87806
87807               lastPointerUpType = null;
87808             }).call(tooltipBehavior);
87809             buttons.each(function (d) {
87810               select(this).call(svgIcon('#' + d.icon));
87811             });
87812             context.keybinding().on(commands[0].cmd, function (d3_event) {
87813               d3_event.preventDefault();
87814               if (editable()) commands[0].action();
87815             }).on(commands[1].cmd, function (d3_event) {
87816               d3_event.preventDefault();
87817               if (editable()) commands[1].action();
87818             });
87819
87820             var debouncedUpdate = debounce(update, 500, {
87821               leading: true,
87822               trailing: true
87823             });
87824
87825             context.map().on('move.undo_redo', debouncedUpdate).on('drawn.undo_redo', debouncedUpdate);
87826             context.history().on('change.undo_redo', function (difference) {
87827               if (difference) update();
87828             });
87829             context.on('enter.undo_redo', update);
87830
87831             function update() {
87832               buttons.classed('disabled', function (d) {
87833                 return !editable() || !d.annotation();
87834               }).each(function () {
87835                 var selection = select(this);
87836
87837                 if (!selection.select('.tooltip.in').empty()) {
87838                   selection.call(tooltipBehavior.updateContent);
87839                 }
87840               });
87841             }
87842           };
87843
87844           tool.uninstall = function () {
87845             context.keybinding().off(commands[0].cmd).off(commands[1].cmd);
87846             context.map().on('move.undo_redo', null).on('drawn.undo_redo', null);
87847             context.history().on('change.undo_redo', null);
87848             context.on('enter.undo_redo', null);
87849           };
87850
87851           return tool;
87852         }
87853
87854         function uiTopToolbar(context) {
87855           var sidebarToggle = uiToolSidebarToggle(context),
87856               modes = uiToolOldDrawModes(context),
87857               notes = uiToolNotes(context),
87858               undoRedo = uiToolUndoRedo(context),
87859               save = uiToolSave(context);
87860
87861           function notesEnabled() {
87862             var noteLayer = context.layers().layer('notes');
87863             return noteLayer && noteLayer.enabled();
87864           }
87865
87866           function topToolbar(bar) {
87867             bar.on('wheel.topToolbar', function (d3_event) {
87868               if (!d3_event.deltaX) {
87869                 // translate vertical scrolling into horizontal scrolling in case
87870                 // the user doesn't have an input device that can scroll horizontally
87871                 bar.node().scrollLeft += d3_event.deltaY;
87872               }
87873             });
87874
87875             var debouncedUpdate = debounce(update, 500, {
87876               leading: true,
87877               trailing: true
87878             });
87879
87880             context.layers().on('change.topToolbar', debouncedUpdate);
87881             update();
87882
87883             function update() {
87884               var tools = [sidebarToggle, 'spacer', modes];
87885               tools.push('spacer');
87886
87887               if (notesEnabled()) {
87888                 tools = tools.concat([notes, 'spacer']);
87889               }
87890
87891               tools = tools.concat([undoRedo, save]);
87892               var toolbarItems = bar.selectAll('.toolbar-item').data(tools, function (d) {
87893                 return d.id || d;
87894               });
87895               toolbarItems.exit().each(function (d) {
87896                 if (d.uninstall) {
87897                   d.uninstall();
87898                 }
87899               }).remove();
87900               var itemsEnter = toolbarItems.enter().append('div').attr('class', function (d) {
87901                 var classes = 'toolbar-item ' + (d.id || d).replace('_', '-');
87902                 if (d.klass) classes += ' ' + d.klass;
87903                 return classes;
87904               });
87905               var actionableItems = itemsEnter.filter(function (d) {
87906                 return d !== 'spacer';
87907               });
87908               actionableItems.append('div').attr('class', 'item-content').each(function (d) {
87909                 select(this).call(d.render, bar);
87910               });
87911               actionableItems.append('div').attr('class', 'item-label').html(function (d) {
87912                 return d.label;
87913               });
87914             }
87915           }
87916
87917           return topToolbar;
87918         }
87919
87920         var sawVersion = null;
87921         var isNewVersion = false;
87922         var isNewUser = false;
87923         function uiVersion(context) {
87924           var currVersion = context.version;
87925           var matchedVersion = currVersion.match(/\d+\.\d+\.\d+.*/);
87926
87927           if (sawVersion === null && matchedVersion !== null) {
87928             if (corePreferences('sawVersion')) {
87929               isNewUser = false;
87930               isNewVersion = corePreferences('sawVersion') !== currVersion && currVersion.indexOf('-') === -1;
87931             } else {
87932               isNewUser = true;
87933               isNewVersion = true;
87934             }
87935
87936             corePreferences('sawVersion', currVersion);
87937             sawVersion = currVersion;
87938           }
87939
87940           return function (selection) {
87941             selection.append('a').attr('target', '_blank').attr('href', 'https://github.com/openstreetmap/iD').html(currVersion); // only show new version indicator to users that have used iD before
87942
87943             if (isNewVersion && !isNewUser) {
87944               selection.append('a').attr('class', 'badge').attr('target', '_blank').attr('href', 'https://github.com/openstreetmap/iD/blob/release/CHANGELOG.md#whats-new').call(svgIcon('#maki-gift-11')).call(uiTooltip().title(_t.html('version.whats_new', {
87945                 version: currVersion
87946               })).placement('top').scrollContainer(context.container().select('.main-footer-wrap')));
87947             }
87948           };
87949         }
87950
87951         function uiZoom(context) {
87952           var zooms = [{
87953             id: 'zoom-in',
87954             icon: 'iD-icon-plus',
87955             title: _t.html('zoom.in'),
87956             action: zoomIn,
87957             disabled: function disabled() {
87958               return !context.map().canZoomIn();
87959             },
87960             disabledTitle: _t.html('zoom.disabled.in'),
87961             key: '+'
87962           }, {
87963             id: 'zoom-out',
87964             icon: 'iD-icon-minus',
87965             title: _t.html('zoom.out'),
87966             action: zoomOut,
87967             disabled: function disabled() {
87968               return !context.map().canZoomOut();
87969             },
87970             disabledTitle: _t.html('zoom.disabled.out'),
87971             key: '-'
87972           }];
87973
87974           function zoomIn(d3_event) {
87975             if (d3_event.shiftKey) return;
87976             d3_event.preventDefault();
87977             context.map().zoomIn();
87978           }
87979
87980           function zoomOut(d3_event) {
87981             if (d3_event.shiftKey) return;
87982             d3_event.preventDefault();
87983             context.map().zoomOut();
87984           }
87985
87986           function zoomInFurther(d3_event) {
87987             if (d3_event.shiftKey) return;
87988             d3_event.preventDefault();
87989             context.map().zoomInFurther();
87990           }
87991
87992           function zoomOutFurther(d3_event) {
87993             if (d3_event.shiftKey) return;
87994             d3_event.preventDefault();
87995             context.map().zoomOutFurther();
87996           }
87997
87998           return function (selection) {
87999             var tooltipBehavior = uiTooltip().placement(_mainLocalizer.textDirection() === 'rtl' ? 'right' : 'left').title(function (d) {
88000               if (d.disabled()) {
88001                 return d.disabledTitle;
88002               }
88003
88004               return d.title;
88005             }).keys(function (d) {
88006               return [d.key];
88007             });
88008             var lastPointerUpType;
88009             var buttons = selection.selectAll('button').data(zooms).enter().append('button').attr('class', function (d) {
88010               return d.id;
88011             }).on('pointerup.editor', function (d3_event) {
88012               lastPointerUpType = d3_event.pointerType;
88013             }).on('click.editor', function (d3_event, d) {
88014               if (!d.disabled()) {
88015                 d.action(d3_event);
88016               } else if (lastPointerUpType === 'touch' || lastPointerUpType === 'pen') {
88017                 context.ui().flash.duration(2000).iconName('#' + d.icon).iconClass('disabled').label(d.disabledTitle)();
88018               }
88019
88020               lastPointerUpType = null;
88021             }).call(tooltipBehavior);
88022             buttons.each(function (d) {
88023               select(this).call(svgIcon('#' + d.icon, 'light'));
88024             });
88025             utilKeybinding.plusKeys.forEach(function (key) {
88026               context.keybinding().on([key], zoomIn);
88027               context.keybinding().on([uiCmd('⌥' + key)], zoomInFurther);
88028             });
88029             utilKeybinding.minusKeys.forEach(function (key) {
88030               context.keybinding().on([key], zoomOut);
88031               context.keybinding().on([uiCmd('⌥' + key)], zoomOutFurther);
88032             });
88033
88034             function updateButtonStates() {
88035               buttons.classed('disabled', function (d) {
88036                 return d.disabled();
88037               }).each(function () {
88038                 var selection = select(this);
88039
88040                 if (!selection.select('.tooltip.in').empty()) {
88041                   selection.call(tooltipBehavior.updateContent);
88042                 }
88043               });
88044             }
88045
88046             updateButtonStates();
88047             context.map().on('move.uiZoom', updateButtonStates);
88048           };
88049         }
88050
88051         function uiZoomToSelection(context) {
88052           function isDisabled() {
88053             var mode = context.mode();
88054             return !mode || !mode.zoomToSelected;
88055           }
88056
88057           var _lastPointerUpType;
88058
88059           function pointerup(d3_event) {
88060             _lastPointerUpType = d3_event.pointerType;
88061           }
88062
88063           function click(d3_event) {
88064             d3_event.preventDefault();
88065
88066             if (isDisabled()) {
88067               if (_lastPointerUpType === 'touch' || _lastPointerUpType === 'pen') {
88068                 context.ui().flash.duration(2000).iconName('#iD-icon-framed-dot').iconClass('disabled').label(_t.html('inspector.zoom_to.no_selection'))();
88069               }
88070             } else {
88071               var mode = context.mode();
88072
88073               if (mode && mode.zoomToSelected) {
88074                 mode.zoomToSelected();
88075               }
88076             }
88077
88078             _lastPointerUpType = null;
88079           }
88080
88081           return function (selection) {
88082             var tooltipBehavior = uiTooltip().placement(_mainLocalizer.textDirection() === 'rtl' ? 'right' : 'left').title(function () {
88083               if (isDisabled()) {
88084                 return _t.html('inspector.zoom_to.no_selection');
88085               }
88086
88087               return _t.html('inspector.zoom_to.title');
88088             }).keys([_t('inspector.zoom_to.key')]);
88089             var button = selection.append('button').on('pointerup', pointerup).on('click', click).call(svgIcon('#iD-icon-framed-dot', 'light')).call(tooltipBehavior);
88090
88091             function setEnabledState() {
88092               button.classed('disabled', isDisabled());
88093
88094               if (!button.select('.tooltip.in').empty()) {
88095                 button.call(tooltipBehavior.updateContent);
88096               }
88097             }
88098
88099             context.on('enter.uiZoomToSelection', setEnabledState);
88100             setEnabledState();
88101           };
88102         }
88103
88104         function uiPane(id, context) {
88105           var _key;
88106
88107           var _label = '';
88108           var _description = '';
88109           var _iconName = '';
88110
88111           var _sections; // array of uiSection objects
88112
88113
88114           var _paneSelection = select(null);
88115
88116           var _paneTooltip;
88117
88118           var pane = {
88119             id: id
88120           };
88121
88122           pane.label = function (val) {
88123             if (!arguments.length) return _label;
88124             _label = val;
88125             return pane;
88126           };
88127
88128           pane.key = function (val) {
88129             if (!arguments.length) return _key;
88130             _key = val;
88131             return pane;
88132           };
88133
88134           pane.description = function (val) {
88135             if (!arguments.length) return _description;
88136             _description = val;
88137             return pane;
88138           };
88139
88140           pane.iconName = function (val) {
88141             if (!arguments.length) return _iconName;
88142             _iconName = val;
88143             return pane;
88144           };
88145
88146           pane.sections = function (val) {
88147             if (!arguments.length) return _sections;
88148             _sections = val;
88149             return pane;
88150           };
88151
88152           pane.selection = function () {
88153             return _paneSelection;
88154           };
88155
88156           function hidePane() {
88157             context.ui().togglePanes();
88158           }
88159
88160           pane.togglePane = function (d3_event) {
88161             if (d3_event) d3_event.preventDefault();
88162
88163             _paneTooltip.hide();
88164
88165             context.ui().togglePanes(!_paneSelection.classed('shown') ? _paneSelection : undefined);
88166           };
88167
88168           pane.renderToggleButton = function (selection) {
88169             if (!_paneTooltip) {
88170               _paneTooltip = uiTooltip().placement(_mainLocalizer.textDirection() === 'rtl' ? 'right' : 'left').title(_description).keys([_key]);
88171             }
88172
88173             selection.append('button').on('click', pane.togglePane).call(svgIcon('#' + _iconName, 'light')).call(_paneTooltip);
88174           };
88175
88176           pane.renderContent = function (selection) {
88177             // override to fully customize content
88178             if (_sections) {
88179               _sections.forEach(function (section) {
88180                 selection.call(section.render);
88181               });
88182             }
88183           };
88184
88185           pane.renderPane = function (selection) {
88186             _paneSelection = selection.append('div').attr('class', 'fillL map-pane hide ' + id + '-pane').attr('pane', id);
88187
88188             var heading = _paneSelection.append('div').attr('class', 'pane-heading');
88189
88190             heading.append('h2').html(_label);
88191             heading.append('button').on('click', hidePane).call(svgIcon('#iD-icon-close'));
88192
88193             _paneSelection.append('div').attr('class', 'pane-content').call(pane.renderContent);
88194
88195             if (_key) {
88196               context.keybinding().on(_key, pane.togglePane);
88197             }
88198           };
88199
88200           return pane;
88201         }
88202
88203         function uiSectionBackgroundDisplayOptions(context) {
88204           var section = uiSection('background-display-options', context).label(_t.html('background.display_options')).disclosureContent(renderDisclosureContent);
88205
88206           var _detected = utilDetect();
88207
88208           var _storedOpacity = corePreferences('background-opacity');
88209
88210           var _minVal = 0;
88211
88212           var _maxVal = _detected.cssfilters ? 3 : 1;
88213
88214           var _sliders = _detected.cssfilters ? ['brightness', 'contrast', 'saturation', 'sharpness'] : ['brightness'];
88215
88216           var _options = {
88217             brightness: _storedOpacity !== null ? +_storedOpacity : 1,
88218             contrast: 1,
88219             saturation: 1,
88220             sharpness: 1
88221           };
88222
88223           function clamp(x, min, max) {
88224             return Math.max(min, Math.min(x, max));
88225           }
88226
88227           function updateValue(d, val) {
88228             val = clamp(val, _minVal, _maxVal);
88229             _options[d] = val;
88230             context.background()[d](val);
88231
88232             if (d === 'brightness') {
88233               corePreferences('background-opacity', val);
88234             }
88235
88236             section.reRender();
88237           }
88238
88239           function renderDisclosureContent(selection) {
88240             var container = selection.selectAll('.display-options-container').data([0]);
88241             var containerEnter = container.enter().append('div').attr('class', 'display-options-container controls-list'); // add slider controls
88242
88243             var slidersEnter = containerEnter.selectAll('.display-control').data(_sliders).enter().append('div').attr('class', function (d) {
88244               return 'display-control display-control-' + d;
88245             });
88246             slidersEnter.append('h5').html(function (d) {
88247               return _t.html('background.' + d);
88248             }).append('span').attr('class', function (d) {
88249               return 'display-option-value display-option-value-' + d;
88250             });
88251             var sildersControlEnter = slidersEnter.append('div').attr('class', 'control-wrap');
88252             sildersControlEnter.append('input').attr('class', function (d) {
88253               return 'display-option-input display-option-input-' + d;
88254             }).attr('type', 'range').attr('min', _minVal).attr('max', _maxVal).attr('step', '0.05').on('input', function (d3_event, d) {
88255               var val = select(this).property('value');
88256
88257               if (!val && d3_event && d3_event.target) {
88258                 val = d3_event.target.value;
88259               }
88260
88261               updateValue(d, val);
88262             });
88263             sildersControlEnter.append('button').attr('title', _t('background.reset')).attr('class', function (d) {
88264               return 'display-option-reset display-option-reset-' + d;
88265             }).on('click', function (d3_event, d) {
88266               if (d3_event.button !== 0) return;
88267               updateValue(d, 1);
88268             }).call(svgIcon('#iD-icon-' + (_mainLocalizer.textDirection() === 'rtl' ? 'redo' : 'undo'))); // reset all button
88269
88270             containerEnter.append('a').attr('class', 'display-option-resetlink').attr('href', '#').html(_t.html('background.reset_all')).on('click', function (d3_event) {
88271               d3_event.preventDefault();
88272
88273               for (var i = 0; i < _sliders.length; i++) {
88274                 updateValue(_sliders[i], 1);
88275               }
88276             }); // update
88277
88278             container = containerEnter.merge(container);
88279             container.selectAll('.display-option-input').property('value', function (d) {
88280               return _options[d];
88281             });
88282             container.selectAll('.display-option-value').html(function (d) {
88283               return Math.floor(_options[d] * 100) + '%';
88284             });
88285             container.selectAll('.display-option-reset').classed('disabled', function (d) {
88286               return _options[d] === 1;
88287             }); // first time only, set brightness if needed
88288
88289             if (containerEnter.size() && _options.brightness !== 1) {
88290               context.background().brightness(_options.brightness);
88291             }
88292           }
88293
88294           return section;
88295         }
88296
88297         function uiSettingsCustomBackground() {
88298           var dispatch = dispatch$8('change');
88299
88300           function render(selection) {
88301             // keep separate copies of original and current settings
88302             var _origSettings = {
88303               template: corePreferences('background-custom-template')
88304             };
88305             var _currSettings = {
88306               template: corePreferences('background-custom-template')
88307             };
88308             var example = 'https://{switch:a,b,c}.tile.openstreetmap.org/{zoom}/{x}/{y}.png';
88309             var modal = uiConfirm(selection).okButton();
88310             modal.classed('settings-modal settings-custom-background', true);
88311             modal.select('.modal-section.header').append('h3').html(_t.html('settings.custom_background.header'));
88312             var textSection = modal.select('.modal-section.message-text');
88313             var instructions = "".concat(_t.html('settings.custom_background.instructions.info'), "\n") + '\n' + "#### ".concat(_t.html('settings.custom_background.instructions.wms.tokens_label'), "\n") + "* ".concat(_t.html('settings.custom_background.instructions.wms.tokens.proj'), "\n") + "* ".concat(_t.html('settings.custom_background.instructions.wms.tokens.wkid'), "\n") + "* ".concat(_t.html('settings.custom_background.instructions.wms.tokens.dimensions'), "\n") + "* ".concat(_t.html('settings.custom_background.instructions.wms.tokens.bbox'), "\n") + '\n' + "#### ".concat(_t.html('settings.custom_background.instructions.tms.tokens_label'), "\n") + "* ".concat(_t.html('settings.custom_background.instructions.tms.tokens.xyz'), "\n") + "* ".concat(_t.html('settings.custom_background.instructions.tms.tokens.flipped_y'), "\n") + "* ".concat(_t.html('settings.custom_background.instructions.tms.tokens.switch'), "\n") + "* ".concat(_t.html('settings.custom_background.instructions.tms.tokens.quadtile'), "\n") + "* ".concat(_t.html('settings.custom_background.instructions.tms.tokens.scale_factor'), "\n") + '\n' + "#### ".concat(_t.html('settings.custom_background.instructions.example'), "\n") + "`".concat(example, "`");
88314             textSection.append('div').attr('class', 'instructions-template').html(marked_1(instructions));
88315             textSection.append('textarea').attr('class', 'field-template').attr('placeholder', _t('settings.custom_background.template.placeholder')).call(utilNoAuto).property('value', _currSettings.template); // insert a cancel button
88316
88317             var buttonSection = modal.select('.modal-section.buttons');
88318             buttonSection.insert('button', '.ok-button').attr('class', 'button cancel-button secondary-action').html(_t.html('confirm.cancel'));
88319             buttonSection.select('.cancel-button').on('click.cancel', clickCancel);
88320             buttonSection.select('.ok-button').attr('disabled', isSaveDisabled).on('click.save', clickSave);
88321
88322             function isSaveDisabled() {
88323               return null;
88324             } // restore the original template
88325
88326
88327             function clickCancel() {
88328               textSection.select('.field-template').property('value', _origSettings.template);
88329               corePreferences('background-custom-template', _origSettings.template);
88330               this.blur();
88331               modal.close();
88332             } // accept the current template
88333
88334
88335             function clickSave() {
88336               _currSettings.template = textSection.select('.field-template').property('value');
88337               corePreferences('background-custom-template', _currSettings.template);
88338               this.blur();
88339               modal.close();
88340               dispatch.call('change', this, _currSettings);
88341             }
88342           }
88343
88344           return utilRebind(render, dispatch, 'on');
88345         }
88346
88347         function uiSectionBackgroundList(context) {
88348           var _backgroundList = select(null);
88349
88350           var _customSource = context.background().findSource('custom');
88351
88352           var _settingsCustomBackground = uiSettingsCustomBackground().on('change', customChanged);
88353
88354           var section = uiSection('background-list', context).label(_t.html('background.backgrounds')).disclosureContent(renderDisclosureContent);
88355
88356           function previousBackgroundID() {
88357             return corePreferences('background-last-used-toggle');
88358           }
88359
88360           function renderDisclosureContent(selection) {
88361             // the background list
88362             var container = selection.selectAll('.layer-background-list').data([0]);
88363             _backgroundList = container.enter().append('ul').attr('class', 'layer-list layer-background-list').attr('dir', 'auto').merge(container); // add minimap toggle below list
88364
88365             var bgExtrasListEnter = selection.selectAll('.bg-extras-list').data([0]).enter().append('ul').attr('class', 'layer-list bg-extras-list');
88366             var minimapLabelEnter = bgExtrasListEnter.append('li').attr('class', 'minimap-toggle-item').append('label').call(uiTooltip().title(_t.html('background.minimap.tooltip')).keys([_t('background.minimap.key')]).placement('top'));
88367             minimapLabelEnter.append('input').attr('type', 'checkbox').on('change', function (d3_event) {
88368               d3_event.preventDefault();
88369               uiMapInMap.toggle();
88370             });
88371             minimapLabelEnter.append('span').html(_t.html('background.minimap.description'));
88372             var panelLabelEnter = bgExtrasListEnter.append('li').attr('class', 'background-panel-toggle-item').append('label').call(uiTooltip().title(_t.html('background.panel.tooltip')).keys([uiCmd('⌘⇧' + _t('info_panels.background.key'))]).placement('top'));
88373             panelLabelEnter.append('input').attr('type', 'checkbox').on('change', function (d3_event) {
88374               d3_event.preventDefault();
88375               context.ui().info.toggle('background');
88376             });
88377             panelLabelEnter.append('span').html(_t.html('background.panel.description'));
88378             var locPanelLabelEnter = bgExtrasListEnter.append('li').attr('class', 'location-panel-toggle-item').append('label').call(uiTooltip().title(_t.html('background.location_panel.tooltip')).keys([uiCmd('⌘⇧' + _t('info_panels.location.key'))]).placement('top'));
88379             locPanelLabelEnter.append('input').attr('type', 'checkbox').on('change', function (d3_event) {
88380               d3_event.preventDefault();
88381               context.ui().info.toggle('location');
88382             });
88383             locPanelLabelEnter.append('span').html(_t.html('background.location_panel.description')); // "Info / Report a Problem" link
88384
88385             selection.selectAll('.imagery-faq').data([0]).enter().append('div').attr('class', 'imagery-faq').append('a').attr('target', '_blank').call(svgIcon('#iD-icon-out-link', 'inline')).attr('href', 'https://github.com/openstreetmap/iD/blob/develop/FAQ.md#how-can-i-report-an-issue-with-background-imagery').append('span').html(_t.html('background.imagery_problem_faq'));
88386
88387             _backgroundList.call(drawListItems, 'radio', function (d3_event, d) {
88388               chooseBackground(d);
88389             }, function (d) {
88390               return !d.isHidden() && !d.overlay;
88391             });
88392           }
88393
88394           function setTooltips(selection) {
88395             selection.each(function (d, i, nodes) {
88396               var item = select(this).select('label');
88397               var span = item.select('span');
88398               var placement = i < nodes.length / 2 ? 'bottom' : 'top';
88399               var description = d.description();
88400               var isOverflowing = span.property('clientWidth') !== span.property('scrollWidth');
88401               item.call(uiTooltip().destroyAny);
88402
88403               if (d.id === previousBackgroundID()) {
88404                 item.call(uiTooltip().placement(placement).title('<div>' + _t.html('background.switch') + '</div>').keys([uiCmd('⌘' + _t('background.key'))]));
88405               } else if (description || isOverflowing) {
88406                 item.call(uiTooltip().placement(placement).title(description || d.label()));
88407               }
88408             });
88409           }
88410
88411           function drawListItems(layerList, type, change, filter) {
88412             var sources = context.background().sources(context.map().extent(), context.map().zoom(), true).filter(filter).sort(function (a, b) {
88413               return a.best() && !b.best() ? -1 : b.best() && !a.best() ? 1 : d3_descending(a.area(), b.area()) || d3_ascending(a.name(), b.name()) || 0;
88414             });
88415             var layerLinks = layerList.selectAll('li') // We have to be a bit inefficient about reordering the list since
88416             // arrow key navigation of radio values likes to work in the order
88417             // they were added, not the display document order.
88418             .data(sources, function (d, i) {
88419               return d.id + '---' + i;
88420             });
88421             layerLinks.exit().remove();
88422             var enter = layerLinks.enter().append('li').classed('layer-custom', function (d) {
88423               return d.id === 'custom';
88424             }).classed('best', function (d) {
88425               return d.best();
88426             });
88427             var label = enter.append('label');
88428             label.append('input').attr('type', type).attr('name', 'background-layer').attr('value', function (d) {
88429               return d.id;
88430             }).on('change', change);
88431             label.append('span').html(function (d) {
88432               return d.label();
88433             });
88434             enter.filter(function (d) {
88435               return d.id === 'custom';
88436             }).append('button').attr('class', 'layer-browse').call(uiTooltip().title(_t.html('settings.custom_background.tooltip')).placement(_mainLocalizer.textDirection() === 'rtl' ? 'right' : 'left')).on('click', function (d3_event) {
88437               d3_event.preventDefault();
88438               editCustom();
88439             }).call(svgIcon('#iD-icon-more'));
88440             enter.filter(function (d) {
88441               return d.best();
88442             }).append('div').attr('class', 'best').call(uiTooltip().title(_t.html('background.best_imagery')).placement(_mainLocalizer.textDirection() === 'rtl' ? 'right' : 'left')).append('span').html('&#9733;');
88443             layerList.call(updateLayerSelections);
88444           }
88445
88446           function updateLayerSelections(selection) {
88447             function active(d) {
88448               return context.background().showsLayer(d);
88449             }
88450
88451             selection.selectAll('li').classed('active', active).classed('switch', function (d) {
88452               return d.id === previousBackgroundID();
88453             }).call(setTooltips).selectAll('input').property('checked', active);
88454           }
88455
88456           function chooseBackground(d) {
88457             if (d.id === 'custom' && !d.template()) {
88458               return editCustom();
88459             }
88460
88461             var previousBackground = context.background().baseLayerSource();
88462             corePreferences('background-last-used-toggle', previousBackground.id);
88463             corePreferences('background-last-used', d.id);
88464             context.background().baseLayerSource(d);
88465           }
88466
88467           function customChanged(d) {
88468             if (d && d.template) {
88469               _customSource.template(d.template);
88470
88471               chooseBackground(_customSource);
88472             } else {
88473               _customSource.template('');
88474
88475               chooseBackground(context.background().findSource('none'));
88476             }
88477           }
88478
88479           function editCustom() {
88480             context.container().call(_settingsCustomBackground);
88481           }
88482
88483           context.background().on('change.background_list', function () {
88484             _backgroundList.call(updateLayerSelections);
88485           });
88486           context.map().on('move.background_list', debounce(function () {
88487             // layers in-view may have changed due to map move
88488             window.requestIdleCallback(section.reRender);
88489           }, 1000));
88490           return section;
88491         }
88492
88493         function uiSectionBackgroundOffset(context) {
88494           var section = uiSection('background-offset', context).label(_t.html('background.fix_misalignment')).disclosureContent(renderDisclosureContent).expandedByDefault(false);
88495
88496           var _pointerPrefix = 'PointerEvent' in window ? 'pointer' : 'mouse';
88497
88498           var _directions = [['top', [0, -0.5]], ['left', [-0.5, 0]], ['right', [0.5, 0]], ['bottom', [0, 0.5]]];
88499
88500           function updateValue() {
88501             var meters = geoOffsetToMeters(context.background().offset());
88502             var x = +meters[0].toFixed(2);
88503             var y = +meters[1].toFixed(2);
88504             context.container().selectAll('.nudge-inner-rect').select('input').classed('error', false).property('value', x + ', ' + y);
88505             context.container().selectAll('.nudge-reset').classed('disabled', function () {
88506               return x === 0 && y === 0;
88507             });
88508           }
88509
88510           function resetOffset() {
88511             context.background().offset([0, 0]);
88512             updateValue();
88513           }
88514
88515           function nudge(d) {
88516             context.background().nudge(d, context.map().zoom());
88517             updateValue();
88518           }
88519
88520           function inputOffset() {
88521             var input = select(this);
88522             var d = input.node().value;
88523             if (d === '') return resetOffset();
88524             d = d.replace(/;/g, ',').split(',').map(function (n) {
88525               // if n is NaN, it will always get mapped to false.
88526               return !isNaN(n) && n;
88527             });
88528
88529             if (d.length !== 2 || !d[0] || !d[1]) {
88530               input.classed('error', true);
88531               return;
88532             }
88533
88534             context.background().offset(geoMetersToOffset(d));
88535             updateValue();
88536           }
88537
88538           function dragOffset(d3_event) {
88539             if (d3_event.button !== 0) return;
88540             var origin = [d3_event.clientX, d3_event.clientY];
88541             var pointerId = d3_event.pointerId || 'mouse';
88542             context.container().append('div').attr('class', 'nudge-surface');
88543             select(window).on(_pointerPrefix + 'move.drag-bg-offset', pointermove).on(_pointerPrefix + 'up.drag-bg-offset', pointerup);
88544
88545             if (_pointerPrefix === 'pointer') {
88546               select(window).on('pointercancel.drag-bg-offset', pointerup);
88547             }
88548
88549             function pointermove(d3_event) {
88550               if (pointerId !== (d3_event.pointerId || 'mouse')) return;
88551               var latest = [d3_event.clientX, d3_event.clientY];
88552               var d = [-(origin[0] - latest[0]) / 4, -(origin[1] - latest[1]) / 4];
88553               origin = latest;
88554               nudge(d);
88555             }
88556
88557             function pointerup(d3_event) {
88558               if (pointerId !== (d3_event.pointerId || 'mouse')) return;
88559               if (d3_event.button !== 0) return;
88560               context.container().selectAll('.nudge-surface').remove();
88561               select(window).on('.drag-bg-offset', null);
88562             }
88563           }
88564
88565           function renderDisclosureContent(selection) {
88566             var container = selection.selectAll('.nudge-container').data([0]);
88567             var containerEnter = container.enter().append('div').attr('class', 'nudge-container');
88568             containerEnter.append('div').attr('class', 'nudge-instructions').html(_t.html('background.offset'));
88569             var nudgeWrapEnter = containerEnter.append('div').attr('class', 'nudge-controls-wrap');
88570             var nudgeEnter = nudgeWrapEnter.append('div').attr('class', 'nudge-outer-rect').on(_pointerPrefix + 'down', dragOffset);
88571             nudgeEnter.append('div').attr('class', 'nudge-inner-rect').append('input').attr('type', 'text').on('change', inputOffset);
88572             nudgeWrapEnter.append('div').selectAll('button').data(_directions).enter().append('button').attr('class', function (d) {
88573               return d[0] + ' nudge';
88574             }).on('click', function (d3_event, d) {
88575               nudge(d[1]);
88576             });
88577             nudgeWrapEnter.append('button').attr('title', _t('background.reset')).attr('class', 'nudge-reset disabled').on('click', function (d3_event) {
88578               d3_event.preventDefault();
88579               resetOffset();
88580             }).call(svgIcon('#iD-icon-' + (_mainLocalizer.textDirection() === 'rtl' ? 'redo' : 'undo')));
88581             updateValue();
88582           }
88583
88584           context.background().on('change.backgroundOffset-update', updateValue);
88585           return section;
88586         }
88587
88588         function uiSectionOverlayList(context) {
88589           var section = uiSection('overlay-list', context).label(_t.html('background.overlays')).disclosureContent(renderDisclosureContent);
88590
88591           var _overlayList = select(null);
88592
88593           function setTooltips(selection) {
88594             selection.each(function (d, i, nodes) {
88595               var item = select(this).select('label');
88596               var span = item.select('span');
88597               var placement = i < nodes.length / 2 ? 'bottom' : 'top';
88598               var description = d.description();
88599               var isOverflowing = span.property('clientWidth') !== span.property('scrollWidth');
88600               item.call(uiTooltip().destroyAny);
88601
88602               if (description || isOverflowing) {
88603                 item.call(uiTooltip().placement(placement).title(description || d.name()));
88604               }
88605             });
88606           }
88607
88608           function updateLayerSelections(selection) {
88609             function active(d) {
88610               return context.background().showsLayer(d);
88611             }
88612
88613             selection.selectAll('li').classed('active', active).call(setTooltips).selectAll('input').property('checked', active);
88614           }
88615
88616           function chooseOverlay(d3_event, d) {
88617             d3_event.preventDefault();
88618             context.background().toggleOverlayLayer(d);
88619
88620             _overlayList.call(updateLayerSelections);
88621
88622             document.activeElement.blur();
88623           }
88624
88625           function drawListItems(layerList, type, change, filter) {
88626             var sources = context.background().sources(context.map().extent(), context.map().zoom(), true).filter(filter);
88627             var layerLinks = layerList.selectAll('li').data(sources, function (d) {
88628               return d.name();
88629             });
88630             layerLinks.exit().remove();
88631             var enter = layerLinks.enter().append('li');
88632             var label = enter.append('label');
88633             label.append('input').attr('type', type).attr('name', 'layers').on('change', change);
88634             label.append('span').html(function (d) {
88635               return d.label();
88636             });
88637             layerList.selectAll('li').sort(sortSources);
88638             layerList.call(updateLayerSelections);
88639
88640             function sortSources(a, b) {
88641               return a.best() && !b.best() ? -1 : b.best() && !a.best() ? 1 : d3_descending(a.area(), b.area()) || d3_ascending(a.name(), b.name()) || 0;
88642             }
88643           }
88644
88645           function renderDisclosureContent(selection) {
88646             var container = selection.selectAll('.layer-overlay-list').data([0]);
88647             _overlayList = container.enter().append('ul').attr('class', 'layer-list layer-overlay-list').attr('dir', 'auto').merge(container);
88648
88649             _overlayList.call(drawListItems, 'checkbox', chooseOverlay, function (d) {
88650               return !d.isHidden() && d.overlay;
88651             });
88652           }
88653
88654           context.map().on('move.overlay_list', debounce(function () {
88655             // layers in-view may have changed due to map move
88656             window.requestIdleCallback(section.reRender);
88657           }, 1000));
88658           return section;
88659         }
88660
88661         function uiPaneBackground(context) {
88662           var backgroundPane = uiPane('background', context).key(_t('background.key')).label(_t.html('background.title')).description(_t.html('background.description')).iconName('iD-icon-layers').sections([uiSectionBackgroundList(context), uiSectionOverlayList(context), uiSectionBackgroundDisplayOptions(context), uiSectionBackgroundOffset(context)]);
88663           return backgroundPane;
88664         }
88665
88666         function uiPaneHelp(context) {
88667           var docKeys = [['help', ['welcome', 'open_data_h', 'open_data', 'before_start_h', 'before_start', 'open_source_h', 'open_source', 'open_source_help']], ['overview', ['navigation_h', 'navigation_drag', 'navigation_zoom', 'features_h', 'features', 'nodes_ways']], ['editing', ['select_h', 'select_left_click', 'select_right_click', 'select_space', 'multiselect_h', 'multiselect', 'multiselect_shift_click', 'multiselect_lasso', 'undo_redo_h', 'undo_redo', 'save_h', 'save', 'save_validation', 'upload_h', 'upload', 'backups_h', 'backups', 'keyboard_h', 'keyboard']], ['feature_editor', ['intro', 'definitions', 'type_h', 'type', 'type_picker', 'fields_h', 'fields_all_fields', 'fields_example', 'fields_add_field', 'tags_h', 'tags_all_tags', 'tags_resources']], ['points', ['intro', 'add_point_h', 'add_point', 'add_point_finish', 'move_point_h', 'move_point', 'delete_point_h', 'delete_point', 'delete_point_command']], ['lines', ['intro', 'add_line_h', 'add_line', 'add_line_draw', 'add_line_continue', 'add_line_finish', 'modify_line_h', 'modify_line_dragnode', 'modify_line_addnode', 'connect_line_h', 'connect_line', 'connect_line_display', 'connect_line_drag', 'connect_line_tag', 'disconnect_line_h', 'disconnect_line_command', 'move_line_h', 'move_line_command', 'move_line_connected', 'delete_line_h', 'delete_line', 'delete_line_command']], ['areas', ['intro', 'point_or_area_h', 'point_or_area', 'add_area_h', 'add_area_command', 'add_area_draw', 'add_area_continue', 'add_area_finish', 'square_area_h', 'square_area_command', 'modify_area_h', 'modify_area_dragnode', 'modify_area_addnode', 'delete_area_h', 'delete_area', 'delete_area_command']], ['relations', ['intro', 'edit_relation_h', 'edit_relation', 'edit_relation_add', 'edit_relation_delete', 'maintain_relation_h', 'maintain_relation', 'relation_types_h', 'multipolygon_h', 'multipolygon', 'multipolygon_create', 'multipolygon_merge', 'turn_restriction_h', 'turn_restriction', 'turn_restriction_field', 'turn_restriction_editing', 'route_h', 'route', 'route_add', 'boundary_h', 'boundary', 'boundary_add']], ['operations', ['intro', 'intro_2', 'straighten', 'orthogonalize', 'circularize', 'move', 'rotate', 'reflect', 'continue', 'reverse', 'disconnect', 'split', 'extract', 'merge', 'delete', 'downgrade', 'copy_paste']], ['notes', ['intro', 'add_note_h', 'add_note', 'place_note', 'move_note', 'update_note_h', 'update_note', 'save_note_h', 'save_note']], ['imagery', ['intro', 'sources_h', 'choosing', 'sources', 'offsets_h', 'offset', 'offset_change']], ['streetlevel', ['intro', 'using_h', 'using', 'photos', 'viewer']], ['gps', ['intro', 'survey', 'using_h', 'using', 'tracing', 'upload']], ['qa', ['intro', 'tools_h', 'tools', 'issues_h', 'issues']]];
88668           var headings = {
88669             'help.help.open_data_h': 3,
88670             'help.help.before_start_h': 3,
88671             'help.help.open_source_h': 3,
88672             'help.overview.navigation_h': 3,
88673             'help.overview.features_h': 3,
88674             'help.editing.select_h': 3,
88675             'help.editing.multiselect_h': 3,
88676             'help.editing.undo_redo_h': 3,
88677             'help.editing.save_h': 3,
88678             'help.editing.upload_h': 3,
88679             'help.editing.backups_h': 3,
88680             'help.editing.keyboard_h': 3,
88681             'help.feature_editor.type_h': 3,
88682             'help.feature_editor.fields_h': 3,
88683             'help.feature_editor.tags_h': 3,
88684             'help.points.add_point_h': 3,
88685             'help.points.move_point_h': 3,
88686             'help.points.delete_point_h': 3,
88687             'help.lines.add_line_h': 3,
88688             'help.lines.modify_line_h': 3,
88689             'help.lines.connect_line_h': 3,
88690             'help.lines.disconnect_line_h': 3,
88691             'help.lines.move_line_h': 3,
88692             'help.lines.delete_line_h': 3,
88693             'help.areas.point_or_area_h': 3,
88694             'help.areas.add_area_h': 3,
88695             'help.areas.square_area_h': 3,
88696             'help.areas.modify_area_h': 3,
88697             'help.areas.delete_area_h': 3,
88698             'help.relations.edit_relation_h': 3,
88699             'help.relations.maintain_relation_h': 3,
88700             'help.relations.relation_types_h': 2,
88701             'help.relations.multipolygon_h': 3,
88702             'help.relations.turn_restriction_h': 3,
88703             'help.relations.route_h': 3,
88704             'help.relations.boundary_h': 3,
88705             'help.notes.add_note_h': 3,
88706             'help.notes.update_note_h': 3,
88707             'help.notes.save_note_h': 3,
88708             'help.imagery.sources_h': 3,
88709             'help.imagery.offsets_h': 3,
88710             'help.streetlevel.using_h': 3,
88711             'help.gps.using_h': 3,
88712             'help.qa.tools_h': 3,
88713             'help.qa.issues_h': 3
88714           }; // For each section, squash all the texts into a single markdown document
88715
88716           var docs = docKeys.map(function (key) {
88717             var helpkey = 'help.' + key[0];
88718             var helpPaneReplacements = {
88719               version: context.version
88720             };
88721             var text = key[1].reduce(function (all, part) {
88722               var subkey = helpkey + '.' + part;
88723               var depth = headings[subkey]; // is this subkey a heading?
88724
88725               var hhh = depth ? Array(depth + 1).join('#') + ' ' : ''; // if so, prepend with some ##'s
88726
88727               return all + hhh + helpHtml(subkey, helpPaneReplacements) + '\n\n';
88728             }, '');
88729             return {
88730               title: _t.html(helpkey + '.title'),
88731               content: marked_1(text.trim()) // use keyboard key styling for shortcuts
88732               .replace(/<code>/g, '<kbd>').replace(/<\/code>/g, '<\/kbd>')
88733             };
88734           });
88735           var helpPane = uiPane('help', context).key(_t('help.key')).label(_t.html('help.title')).description(_t.html('help.title')).iconName('iD-icon-help');
88736
88737           helpPane.renderContent = function (content) {
88738             function clickHelp(d, i) {
88739               var rtl = _mainLocalizer.textDirection() === 'rtl';
88740               content.property('scrollTop', 0);
88741               helpPane.selection().select('.pane-heading h2').html(d.title);
88742               body.html(d.content);
88743               body.selectAll('a').attr('target', '_blank');
88744               menuItems.classed('selected', function (m) {
88745                 return m.title === d.title;
88746               });
88747               nav.html('');
88748
88749               if (rtl) {
88750                 nav.call(drawNext).call(drawPrevious);
88751               } else {
88752                 nav.call(drawPrevious).call(drawNext);
88753               }
88754
88755               function drawNext(selection) {
88756                 if (i < docs.length - 1) {
88757                   var nextLink = selection.append('a').attr('href', '#').attr('class', 'next').on('click', function (d3_event) {
88758                     d3_event.preventDefault();
88759                     clickHelp(docs[i + 1], i + 1);
88760                   });
88761                   nextLink.append('span').html(docs[i + 1].title).call(svgIcon(rtl ? '#iD-icon-backward' : '#iD-icon-forward', 'inline'));
88762                 }
88763               }
88764
88765               function drawPrevious(selection) {
88766                 if (i > 0) {
88767                   var prevLink = selection.append('a').attr('href', '#').attr('class', 'previous').on('click', function (d3_event) {
88768                     d3_event.preventDefault();
88769                     clickHelp(docs[i - 1], i - 1);
88770                   });
88771                   prevLink.call(svgIcon(rtl ? '#iD-icon-forward' : '#iD-icon-backward', 'inline')).append('span').html(docs[i - 1].title);
88772                 }
88773               }
88774             }
88775
88776             function clickWalkthrough(d3_event) {
88777               d3_event.preventDefault();
88778               if (context.inIntro()) return;
88779               context.container().call(uiIntro(context));
88780               context.ui().togglePanes();
88781             }
88782
88783             function clickShortcuts(d3_event) {
88784               d3_event.preventDefault();
88785               context.container().call(context.ui().shortcuts, true);
88786             }
88787
88788             var toc = content.append('ul').attr('class', 'toc');
88789             var menuItems = toc.selectAll('li').data(docs).enter().append('li').append('a').attr('href', '#').html(function (d) {
88790               return d.title;
88791             }).on('click', function (d3_event, d) {
88792               d3_event.preventDefault();
88793               clickHelp(d, docs.indexOf(d));
88794             });
88795             var shortcuts = toc.append('li').attr('class', 'shortcuts').call(uiTooltip().title(_t.html('shortcuts.tooltip')).keys(['?']).placement('top')).append('a').attr('href', '#').on('click', clickShortcuts);
88796             shortcuts.append('div').html(_t.html('shortcuts.title'));
88797             var walkthrough = toc.append('li').attr('class', 'walkthrough').append('a').attr('href', '#').on('click', clickWalkthrough);
88798             walkthrough.append('svg').attr('class', 'logo logo-walkthrough').append('use').attr('xlink:href', '#iD-logo-walkthrough');
88799             walkthrough.append('div').html(_t.html('splash.walkthrough'));
88800             var helpContent = content.append('div').attr('class', 'left-content');
88801             var body = helpContent.append('div').attr('class', 'body');
88802             var nav = helpContent.append('div').attr('class', 'nav');
88803             clickHelp(docs[0], 0);
88804           };
88805
88806           return helpPane;
88807         }
88808
88809         function uiSectionValidationIssues(id, severity, context) {
88810           var _issues = [];
88811           var section = uiSection(id, context).label(function () {
88812             if (!_issues) return '';
88813             var issueCountText = _issues.length > 1000 ? '1000+' : String(_issues.length);
88814             return _t('inspector.title_count', {
88815               title: _t.html('issues.' + severity + 's.list_title'),
88816               count: issueCountText
88817             });
88818           }).disclosureContent(renderDisclosureContent).shouldDisplay(function () {
88819             return _issues && _issues.length;
88820           });
88821
88822           function getOptions() {
88823             return {
88824               what: corePreferences('validate-what') || 'edited',
88825               where: corePreferences('validate-where') || 'all'
88826             };
88827           } // get and cache the issues to display, unordered
88828
88829
88830           function reloadIssues() {
88831             _issues = context.validator().getIssuesBySeverity(getOptions())[severity];
88832           }
88833
88834           function renderDisclosureContent(selection) {
88835             var center = context.map().center();
88836             var graph = context.graph(); // sort issues by distance away from the center of the map
88837
88838             var issues = _issues.map(function withDistance(issue) {
88839               var extent = issue.extent(graph);
88840               var dist = extent ? geoSphericalDistance(center, extent.center()) : 0;
88841               return Object.assign(issue, {
88842                 dist: dist
88843               });
88844             }).sort(function byDistance(a, b) {
88845               return a.dist - b.dist;
88846             }); // cut off at 1000
88847
88848
88849             issues = issues.slice(0, 1000); //renderIgnoredIssuesReset(_warningsSelection);
88850
88851             selection.call(drawIssuesList, issues);
88852           }
88853
88854           function drawIssuesList(selection, issues) {
88855             var list = selection.selectAll('.issues-list').data([0]);
88856             list = list.enter().append('ul').attr('class', 'layer-list issues-list ' + severity + 's-list').merge(list);
88857             var items = list.selectAll('li').data(issues, function (d) {
88858               return d.id;
88859             }); // Exit
88860
88861             items.exit().remove(); // Enter
88862
88863             var itemsEnter = items.enter().append('li').attr('class', function (d) {
88864               return 'issue severity-' + d.severity;
88865             });
88866             var labelsEnter = itemsEnter.append('button').attr('class', 'issue-label').on('click', function (d3_event, d) {
88867               context.validator().focusIssue(d);
88868             }).on('mouseover', function (d3_event, d) {
88869               utilHighlightEntities(d.entityIds, true, context);
88870             }).on('mouseout', function (d3_event, d) {
88871               utilHighlightEntities(d.entityIds, false, context);
88872             });
88873             var textEnter = labelsEnter.append('span').attr('class', 'issue-text');
88874             textEnter.append('span').attr('class', 'issue-icon').each(function (d) {
88875               var iconName = '#iD-icon-' + (d.severity === 'warning' ? 'alert' : 'error');
88876               select(this).call(svgIcon(iconName));
88877             });
88878             textEnter.append('span').attr('class', 'issue-message');
88879             /*
88880             labelsEnter
88881                 .append('span')
88882                 .attr('class', 'issue-autofix')
88883                 .each(function(d) {
88884                     if (!d.autoFix) return;
88885                      d3_select(this)
88886                         .append('button')
88887                         .attr('title', t('issues.fix_one.title'))
88888                         .datum(d.autoFix)  // set button datum to the autofix
88889                         .attr('class', 'autofix action')
88890                         .on('click', function(d3_event, d) {
88891                             d3_event.preventDefault();
88892                             d3_event.stopPropagation();
88893                              var issuesEntityIDs = d.issue.entityIds;
88894                             utilHighlightEntities(issuesEntityIDs.concat(d.entityIds), false, context);
88895                              context.perform.apply(context, d.autoArgs);
88896                             context.validator().validate();
88897                         })
88898                         .call(svgIcon('#iD-icon-wrench'));
88899                 });
88900             */
88901             // Update
88902
88903             items = items.merge(itemsEnter).order();
88904             items.selectAll('.issue-message').html(function (d) {
88905               return d.message(context);
88906             });
88907             /*
88908             // autofix
88909             var canAutoFix = issues.filter(function(issue) { return issue.autoFix; });
88910              var autoFixAll = selection.selectAll('.autofix-all')
88911                 .data(canAutoFix.length ? [0] : []);
88912              // exit
88913             autoFixAll.exit()
88914                 .remove();
88915              // enter
88916             var autoFixAllEnter = autoFixAll.enter()
88917                 .insert('div', '.issues-list')
88918                 .attr('class', 'autofix-all');
88919              var linkEnter = autoFixAllEnter
88920                 .append('a')
88921                 .attr('class', 'autofix-all-link')
88922                 .attr('href', '#');
88923              linkEnter
88924                 .append('span')
88925                 .attr('class', 'autofix-all-link-text')
88926                 .html(t.html('issues.fix_all.title'));
88927              linkEnter
88928                 .append('span')
88929                 .attr('class', 'autofix-all-link-icon')
88930                 .call(svgIcon('#iD-icon-wrench'));
88931              if (severity === 'warning') {
88932                 renderIgnoredIssuesReset(selection);
88933             }
88934              // update
88935             autoFixAll = autoFixAll
88936                 .merge(autoFixAllEnter);
88937              autoFixAll.selectAll('.autofix-all-link')
88938                 .on('click', function() {
88939                     context.pauseChangeDispatch();
88940                     context.perform(actionNoop());
88941                     canAutoFix.forEach(function(issue) {
88942                         var args = issue.autoFix.autoArgs.slice();  // copy
88943                         if (typeof args[args.length - 1] !== 'function') {
88944                             args.pop();
88945                         }
88946                         args.push(t('issues.fix_all.annotation'));
88947                         context.replace.apply(context, args);
88948                     });
88949                     context.resumeChangeDispatch();
88950                     context.validator().validate();
88951                 });
88952             */
88953           }
88954
88955           context.validator().on('validated.uiSectionValidationIssues' + id, function () {
88956             window.requestIdleCallback(function () {
88957               reloadIssues();
88958               section.reRender();
88959             });
88960           });
88961           context.map().on('move.uiSectionValidationIssues' + id, debounce(function () {
88962             window.requestIdleCallback(function () {
88963               if (getOptions().where === 'visible') {
88964                 // must refetch issues if they are viewport-dependent
88965                 reloadIssues();
88966               } // always reload list to re-sort-by-distance
88967
88968
88969               section.reRender();
88970             });
88971           }, 1000));
88972           return section;
88973         }
88974
88975         function uiSectionValidationOptions(context) {
88976           var section = uiSection('issues-options', context).content(renderContent);
88977
88978           function renderContent(selection) {
88979             var container = selection.selectAll('.issues-options-container').data([0]);
88980             container = container.enter().append('div').attr('class', 'issues-options-container').merge(container);
88981             var data = [{
88982               key: 'what',
88983               values: ['edited', 'all']
88984             }, {
88985               key: 'where',
88986               values: ['visible', 'all']
88987             }];
88988             var options = container.selectAll('.issues-option').data(data, function (d) {
88989               return d.key;
88990             });
88991             var optionsEnter = options.enter().append('div').attr('class', function (d) {
88992               return 'issues-option issues-option-' + d.key;
88993             });
88994             optionsEnter.append('div').attr('class', 'issues-option-title').html(function (d) {
88995               return _t.html('issues.options.' + d.key + '.title');
88996             });
88997             var valuesEnter = optionsEnter.selectAll('label').data(function (d) {
88998               return d.values.map(function (val) {
88999                 return {
89000                   value: val,
89001                   key: d.key
89002                 };
89003               });
89004             }).enter().append('label');
89005             valuesEnter.append('input').attr('type', 'radio').attr('name', function (d) {
89006               return 'issues-option-' + d.key;
89007             }).attr('value', function (d) {
89008               return d.value;
89009             }).property('checked', function (d) {
89010               return getOptions()[d.key] === d.value;
89011             }).on('change', function (d3_event, d) {
89012               updateOptionValue(d3_event, d.key, d.value);
89013             });
89014             valuesEnter.append('span').html(function (d) {
89015               return _t.html('issues.options.' + d.key + '.' + d.value);
89016             });
89017           }
89018
89019           function getOptions() {
89020             return {
89021               what: corePreferences('validate-what') || 'edited',
89022               // 'all', 'edited'
89023               where: corePreferences('validate-where') || 'all' // 'all', 'visible'
89024
89025             };
89026           }
89027
89028           function updateOptionValue(d3_event, d, val) {
89029             if (!val && d3_event && d3_event.target) {
89030               val = d3_event.target.value;
89031             }
89032
89033             corePreferences('validate-' + d, val);
89034             context.validator().validate();
89035           }
89036
89037           return section;
89038         }
89039
89040         function uiSectionValidationRules(context) {
89041           var MINSQUARE = 0;
89042           var MAXSQUARE = 20;
89043           var DEFAULTSQUARE = 5; // see also unsquare_way.js
89044
89045           var section = uiSection('issues-rules', context).disclosureContent(renderDisclosureContent).label(_t.html('issues.rules.title'));
89046
89047           var _ruleKeys = context.validator().getRuleKeys().filter(function (key) {
89048             return key !== 'maprules';
89049           }).sort(function (key1, key2) {
89050             // alphabetize by localized title
89051             return _t('issues.' + key1 + '.title') < _t('issues.' + key2 + '.title') ? -1 : 1;
89052           });
89053
89054           function renderDisclosureContent(selection) {
89055             var container = selection.selectAll('.issues-rulelist-container').data([0]);
89056             var containerEnter = container.enter().append('div').attr('class', 'issues-rulelist-container');
89057             containerEnter.append('ul').attr('class', 'layer-list issue-rules-list');
89058             var ruleLinks = containerEnter.append('div').attr('class', 'issue-rules-links section-footer');
89059             ruleLinks.append('a').attr('class', 'issue-rules-link').attr('href', '#').html(_t.html('issues.disable_all')).on('click', function (d3_event) {
89060               d3_event.preventDefault();
89061               context.validator().disableRules(_ruleKeys);
89062             });
89063             ruleLinks.append('a').attr('class', 'issue-rules-link').attr('href', '#').html(_t.html('issues.enable_all')).on('click', function (d3_event) {
89064               d3_event.preventDefault();
89065               context.validator().disableRules([]);
89066             }); // Update
89067
89068             container = container.merge(containerEnter);
89069             container.selectAll('.issue-rules-list').call(drawListItems, _ruleKeys, 'checkbox', 'rule', toggleRule, isRuleEnabled);
89070           }
89071
89072           function drawListItems(selection, data, type, name, change, active) {
89073             var items = selection.selectAll('li').data(data); // Exit
89074
89075             items.exit().remove(); // Enter
89076
89077             var enter = items.enter().append('li');
89078
89079             if (name === 'rule') {
89080               enter.call(uiTooltip().title(function (d) {
89081                 return _t.html('issues.' + d + '.tip');
89082               }).placement('top'));
89083             }
89084
89085             var label = enter.append('label');
89086             label.append('input').attr('type', type).attr('name', name).on('change', change);
89087             label.append('span').html(function (d) {
89088               var params = {};
89089
89090               if (d === 'unsquare_way') {
89091                 params.val = '<span class="square-degrees"></span>';
89092               }
89093
89094               return _t.html('issues.' + d + '.title', params);
89095             }); // Update
89096
89097             items = items.merge(enter);
89098             items.classed('active', active).selectAll('input').property('checked', active).property('indeterminate', false); // user-configurable square threshold
89099
89100             var degStr = corePreferences('validate-square-degrees');
89101
89102             if (degStr === null) {
89103               degStr = DEFAULTSQUARE.toString();
89104             }
89105
89106             var span = items.selectAll('.square-degrees');
89107             var input = span.selectAll('.square-degrees-input').data([0]); // enter / update
89108
89109             input.enter().append('input').attr('type', 'number').attr('min', MINSQUARE.toString()).attr('max', MAXSQUARE.toString()).attr('step', '0.5').attr('class', 'square-degrees-input').call(utilNoAuto).on('click', function (d3_event) {
89110               d3_event.preventDefault();
89111               d3_event.stopPropagation();
89112               this.select();
89113             }).on('keyup', function (d3_event) {
89114               if (d3_event.keyCode === 13) {
89115                 // ↩ Return
89116                 this.blur();
89117                 this.select();
89118               }
89119             }).on('blur', changeSquare).merge(input).property('value', degStr);
89120           }
89121
89122           function changeSquare() {
89123             var input = select(this);
89124             var degStr = utilGetSetValue(input).trim();
89125             var degNum = parseFloat(degStr, 10);
89126
89127             if (!isFinite(degNum)) {
89128               degNum = DEFAULTSQUARE;
89129             } else if (degNum > MAXSQUARE) {
89130               degNum = MAXSQUARE;
89131             } else if (degNum < MINSQUARE) {
89132               degNum = MINSQUARE;
89133             }
89134
89135             degNum = Math.round(degNum * 10) / 10; // round to 1 decimal
89136
89137             degStr = degNum.toString();
89138             input.property('value', degStr);
89139             corePreferences('validate-square-degrees', degStr);
89140             context.validator().revalidateUnsquare();
89141           }
89142
89143           function isRuleEnabled(d) {
89144             return context.validator().isRuleEnabled(d);
89145           }
89146
89147           function toggleRule(d3_event, d) {
89148             context.validator().toggleRule(d);
89149           }
89150
89151           context.validator().on('validated.uiSectionValidationRules', function () {
89152             window.requestIdleCallback(section.reRender);
89153           });
89154           return section;
89155         }
89156
89157         function uiSectionValidationStatus(context) {
89158           var section = uiSection('issues-status', context).content(renderContent).shouldDisplay(function () {
89159             var issues = context.validator().getIssues(getOptions());
89160             return issues.length === 0;
89161           });
89162
89163           function getOptions() {
89164             return {
89165               what: corePreferences('validate-what') || 'edited',
89166               where: corePreferences('validate-where') || 'all'
89167             };
89168           }
89169
89170           function renderContent(selection) {
89171             var box = selection.selectAll('.box').data([0]);
89172             var boxEnter = box.enter().append('div').attr('class', 'box');
89173             boxEnter.append('div').call(svgIcon('#iD-icon-apply', 'pre-text'));
89174             var noIssuesMessage = boxEnter.append('span');
89175             noIssuesMessage.append('strong').attr('class', 'message');
89176             noIssuesMessage.append('br');
89177             noIssuesMessage.append('span').attr('class', 'details');
89178             renderIgnoredIssuesReset(selection);
89179             setNoIssuesText(selection);
89180           }
89181
89182           function renderIgnoredIssuesReset(selection) {
89183             var ignoredIssues = context.validator().getIssues({
89184               what: 'all',
89185               where: 'all',
89186               includeDisabledRules: true,
89187               includeIgnored: 'only'
89188             });
89189             var resetIgnored = selection.selectAll('.reset-ignored').data(ignoredIssues.length ? [0] : []); // exit
89190
89191             resetIgnored.exit().remove(); // enter
89192
89193             var resetIgnoredEnter = resetIgnored.enter().append('div').attr('class', 'reset-ignored section-footer');
89194             resetIgnoredEnter.append('a').attr('href', '#'); // update
89195
89196             resetIgnored = resetIgnored.merge(resetIgnoredEnter);
89197             resetIgnored.select('a').html(_t('inspector.title_count', {
89198               title: _t.html('issues.reset_ignored'),
89199               count: ignoredIssues.length
89200             }));
89201             resetIgnored.on('click', function (d3_event) {
89202               d3_event.preventDefault();
89203               context.validator().resetIgnoredIssues();
89204             });
89205           }
89206
89207           function setNoIssuesText(selection) {
89208             var opts = getOptions();
89209
89210             function checkForHiddenIssues(cases) {
89211               for (var type in cases) {
89212                 var hiddenOpts = cases[type];
89213                 var hiddenIssues = context.validator().getIssues(hiddenOpts);
89214
89215                 if (hiddenIssues.length) {
89216                   selection.select('.box .details').html(_t.html('issues.no_issues.hidden_issues.' + type, {
89217                     count: hiddenIssues.length.toString()
89218                   }));
89219                   return;
89220                 }
89221               }
89222
89223               selection.select('.box .details').html(_t.html('issues.no_issues.hidden_issues.none'));
89224             }
89225
89226             var messageType;
89227
89228             if (opts.what === 'edited' && opts.where === 'visible') {
89229               messageType = 'edits_in_view';
89230               checkForHiddenIssues({
89231                 elsewhere: {
89232                   what: 'edited',
89233                   where: 'all'
89234                 },
89235                 everything_else: {
89236                   what: 'all',
89237                   where: 'visible'
89238                 },
89239                 disabled_rules: {
89240                   what: 'edited',
89241                   where: 'visible',
89242                   includeDisabledRules: 'only'
89243                 },
89244                 everything_else_elsewhere: {
89245                   what: 'all',
89246                   where: 'all'
89247                 },
89248                 disabled_rules_elsewhere: {
89249                   what: 'edited',
89250                   where: 'all',
89251                   includeDisabledRules: 'only'
89252                 },
89253                 ignored_issues: {
89254                   what: 'edited',
89255                   where: 'visible',
89256                   includeIgnored: 'only'
89257                 },
89258                 ignored_issues_elsewhere: {
89259                   what: 'edited',
89260                   where: 'all',
89261                   includeIgnored: 'only'
89262                 }
89263               });
89264             } else if (opts.what === 'edited' && opts.where === 'all') {
89265               messageType = 'edits';
89266               checkForHiddenIssues({
89267                 everything_else: {
89268                   what: 'all',
89269                   where: 'all'
89270                 },
89271                 disabled_rules: {
89272                   what: 'edited',
89273                   where: 'all',
89274                   includeDisabledRules: 'only'
89275                 },
89276                 ignored_issues: {
89277                   what: 'edited',
89278                   where: 'all',
89279                   includeIgnored: 'only'
89280                 }
89281               });
89282             } else if (opts.what === 'all' && opts.where === 'visible') {
89283               messageType = 'everything_in_view';
89284               checkForHiddenIssues({
89285                 elsewhere: {
89286                   what: 'all',
89287                   where: 'all'
89288                 },
89289                 disabled_rules: {
89290                   what: 'all',
89291                   where: 'visible',
89292                   includeDisabledRules: 'only'
89293                 },
89294                 disabled_rules_elsewhere: {
89295                   what: 'all',
89296                   where: 'all',
89297                   includeDisabledRules: 'only'
89298                 },
89299                 ignored_issues: {
89300                   what: 'all',
89301                   where: 'visible',
89302                   includeIgnored: 'only'
89303                 },
89304                 ignored_issues_elsewhere: {
89305                   what: 'all',
89306                   where: 'all',
89307                   includeIgnored: 'only'
89308                 }
89309               });
89310             } else if (opts.what === 'all' && opts.where === 'all') {
89311               messageType = 'everything';
89312               checkForHiddenIssues({
89313                 disabled_rules: {
89314                   what: 'all',
89315                   where: 'all',
89316                   includeDisabledRules: 'only'
89317                 },
89318                 ignored_issues: {
89319                   what: 'all',
89320                   where: 'all',
89321                   includeIgnored: 'only'
89322                 }
89323               });
89324             }
89325
89326             if (opts.what === 'edited' && context.history().difference().summary().length === 0) {
89327               messageType = 'no_edits';
89328             }
89329
89330             selection.select('.box .message').html(_t.html('issues.no_issues.message.' + messageType));
89331           }
89332
89333           context.validator().on('validated.uiSectionValidationStatus', function () {
89334             window.requestIdleCallback(section.reRender);
89335           });
89336           context.map().on('move.uiSectionValidationStatus', debounce(function () {
89337             window.requestIdleCallback(section.reRender);
89338           }, 1000));
89339           return section;
89340         }
89341
89342         function uiPaneIssues(context) {
89343           var issuesPane = uiPane('issues', context).key(_t('issues.key')).label(_t.html('issues.title')).description(_t.html('issues.title')).iconName('iD-icon-alert').sections([uiSectionValidationOptions(context), uiSectionValidationStatus(context), uiSectionValidationIssues('issues-errors', 'error', context), uiSectionValidationIssues('issues-warnings', 'warning', context), uiSectionValidationRules(context)]);
89344           return issuesPane;
89345         }
89346
89347         function uiSettingsCustomData(context) {
89348           var dispatch = dispatch$8('change');
89349
89350           function render(selection) {
89351             var dataLayer = context.layers().layer('data'); // keep separate copies of original and current settings
89352
89353             var _origSettings = {
89354               fileList: dataLayer && dataLayer.fileList() || null,
89355               url: corePreferences('settings-custom-data-url')
89356             };
89357             var _currSettings = {
89358               fileList: dataLayer && dataLayer.fileList() || null,
89359               url: corePreferences('settings-custom-data-url')
89360             }; // var example = 'https://{switch:a,b,c}.tile.openstreetmap.org/{zoom}/{x}/{y}.png';
89361
89362             var modal = uiConfirm(selection).okButton();
89363             modal.classed('settings-modal settings-custom-data', true);
89364             modal.select('.modal-section.header').append('h3').html(_t.html('settings.custom_data.header'));
89365             var textSection = modal.select('.modal-section.message-text');
89366             textSection.append('pre').attr('class', 'instructions-file').html(_t.html('settings.custom_data.file.instructions'));
89367             textSection.append('input').attr('class', 'field-file').attr('type', 'file').property('files', _currSettings.fileList) // works for all except IE11
89368             .on('change', function (d3_event) {
89369               var files = d3_event.target.files;
89370
89371               if (files && files.length) {
89372                 _currSettings.url = '';
89373                 textSection.select('.field-url').property('value', '');
89374                 _currSettings.fileList = files;
89375               } else {
89376                 _currSettings.fileList = null;
89377               }
89378             });
89379             textSection.append('h4').html(_t.html('settings.custom_data.or'));
89380             textSection.append('pre').attr('class', 'instructions-url').html(_t.html('settings.custom_data.url.instructions'));
89381             textSection.append('textarea').attr('class', 'field-url').attr('placeholder', _t('settings.custom_data.url.placeholder')).call(utilNoAuto).property('value', _currSettings.url); // insert a cancel button
89382
89383             var buttonSection = modal.select('.modal-section.buttons');
89384             buttonSection.insert('button', '.ok-button').attr('class', 'button cancel-button secondary-action').html(_t.html('confirm.cancel'));
89385             buttonSection.select('.cancel-button').on('click.cancel', clickCancel);
89386             buttonSection.select('.ok-button').attr('disabled', isSaveDisabled).on('click.save', clickSave);
89387
89388             function isSaveDisabled() {
89389               return null;
89390             } // restore the original url
89391
89392
89393             function clickCancel() {
89394               textSection.select('.field-url').property('value', _origSettings.url);
89395               corePreferences('settings-custom-data-url', _origSettings.url);
89396               this.blur();
89397               modal.close();
89398             } // accept the current url
89399
89400
89401             function clickSave() {
89402               _currSettings.url = textSection.select('.field-url').property('value').trim(); // one or the other but not both
89403
89404               if (_currSettings.url) {
89405                 _currSettings.fileList = null;
89406               }
89407
89408               if (_currSettings.fileList) {
89409                 _currSettings.url = '';
89410               }
89411
89412               corePreferences('settings-custom-data-url', _currSettings.url);
89413               this.blur();
89414               modal.close();
89415               dispatch.call('change', this, _currSettings);
89416             }
89417           }
89418
89419           return utilRebind(render, dispatch, 'on');
89420         }
89421
89422         function uiSectionDataLayers(context) {
89423           var settingsCustomData = uiSettingsCustomData(context).on('change', customChanged);
89424           var layers = context.layers();
89425           var section = uiSection('data-layers', context).label(_t.html('map_data.data_layers')).disclosureContent(renderDisclosureContent);
89426
89427           function renderDisclosureContent(selection) {
89428             var container = selection.selectAll('.data-layer-container').data([0]);
89429             container.enter().append('div').attr('class', 'data-layer-container').merge(container).call(drawOsmItems).call(drawQAItems).call(drawCustomDataItems).call(drawVectorItems) // Beta - Detroit mapping challenge
89430             .call(drawPanelItems);
89431           }
89432
89433           function showsLayer(which) {
89434             var layer = layers.layer(which);
89435
89436             if (layer) {
89437               return layer.enabled();
89438             }
89439
89440             return false;
89441           }
89442
89443           function setLayer(which, enabled) {
89444             // Don't allow layer changes while drawing - #6584
89445             var mode = context.mode();
89446             if (mode && /^draw/.test(mode.id)) return;
89447             var layer = layers.layer(which);
89448
89449             if (layer) {
89450               layer.enabled(enabled);
89451
89452               if (!enabled && (which === 'osm' || which === 'notes')) {
89453                 context.enter(modeBrowse(context));
89454               }
89455             }
89456           }
89457
89458           function toggleLayer(which) {
89459             setLayer(which, !showsLayer(which));
89460           }
89461
89462           function drawOsmItems(selection) {
89463             var osmKeys = ['osm', 'notes'];
89464             var osmLayers = layers.all().filter(function (obj) {
89465               return osmKeys.indexOf(obj.id) !== -1;
89466             });
89467             var ul = selection.selectAll('.layer-list-osm').data([0]);
89468             ul = ul.enter().append('ul').attr('class', 'layer-list layer-list-osm').merge(ul);
89469             var li = ul.selectAll('.list-item').data(osmLayers);
89470             li.exit().remove();
89471             var liEnter = li.enter().append('li').attr('class', function (d) {
89472               return 'list-item list-item-' + d.id;
89473             });
89474             var labelEnter = liEnter.append('label').each(function (d) {
89475               if (d.id === 'osm') {
89476                 select(this).call(uiTooltip().title(_t.html('map_data.layers.' + d.id + '.tooltip')).keys([uiCmd('⌥' + _t('area_fill.wireframe.key'))]).placement('bottom'));
89477               } else {
89478                 select(this).call(uiTooltip().title(_t.html('map_data.layers.' + d.id + '.tooltip')).placement('bottom'));
89479               }
89480             });
89481             labelEnter.append('input').attr('type', 'checkbox').on('change', function (d3_event, d) {
89482               toggleLayer(d.id);
89483             });
89484             labelEnter.append('span').html(function (d) {
89485               return _t.html('map_data.layers.' + d.id + '.title');
89486             }); // Update
89487
89488             li.merge(liEnter).classed('active', function (d) {
89489               return d.layer.enabled();
89490             }).selectAll('input').property('checked', function (d) {
89491               return d.layer.enabled();
89492             });
89493           }
89494
89495           function drawQAItems(selection) {
89496             var qaKeys = ['keepRight', 'improveOSM', 'osmose'];
89497             var qaLayers = layers.all().filter(function (obj) {
89498               return qaKeys.indexOf(obj.id) !== -1;
89499             });
89500             var ul = selection.selectAll('.layer-list-qa').data([0]);
89501             ul = ul.enter().append('ul').attr('class', 'layer-list layer-list-qa').merge(ul);
89502             var li = ul.selectAll('.list-item').data(qaLayers);
89503             li.exit().remove();
89504             var liEnter = li.enter().append('li').attr('class', function (d) {
89505               return 'list-item list-item-' + d.id;
89506             });
89507             var labelEnter = liEnter.append('label').each(function (d) {
89508               select(this).call(uiTooltip().title(_t.html('map_data.layers.' + d.id + '.tooltip')).placement('bottom'));
89509             });
89510             labelEnter.append('input').attr('type', 'checkbox').on('change', function (d3_event, d) {
89511               toggleLayer(d.id);
89512             });
89513             labelEnter.append('span').html(function (d) {
89514               return _t.html('map_data.layers.' + d.id + '.title');
89515             }); // Update
89516
89517             li.merge(liEnter).classed('active', function (d) {
89518               return d.layer.enabled();
89519             }).selectAll('input').property('checked', function (d) {
89520               return d.layer.enabled();
89521             });
89522           } // Beta feature - sample vector layers to support Detroit Mapping Challenge
89523           // https://github.com/osmus/detroit-mapping-challenge
89524
89525
89526           function drawVectorItems(selection) {
89527             var dataLayer = layers.layer('data');
89528             var vtData = [{
89529               name: 'Detroit Neighborhoods/Parks',
89530               src: 'neighborhoods-parks',
89531               tooltip: 'Neighborhood boundaries and parks as compiled by City of Detroit in concert with community groups.',
89532               template: 'https://{switch:a,b,c,d}.tiles.mapbox.com/v4/jonahadkins.cjksmur6x34562qp9iv1u3ksf-54hev,jonahadkins.cjksmqxdx33jj2wp90xd9x2md-4e5y2/{z}/{x}/{y}.vector.pbf?access_token=pk.eyJ1Ijoiam9uYWhhZGtpbnMiLCJhIjoiRlVVVkx3VSJ9.9sdVEK_B_VkEXPjssU5MqA'
89533             }, {
89534               name: 'Detroit Composite POIs',
89535               src: 'composite-poi',
89536               tooltip: 'Fire Inspections, Business Licenses, and other public location data collated from the City of Detroit.',
89537               template: 'https://{switch:a,b,c,d}.tiles.mapbox.com/v4/jonahadkins.cjksmm6a02sli31myxhsr7zf3-2sw8h/{z}/{x}/{y}.vector.pbf?access_token=pk.eyJ1Ijoiam9uYWhhZGtpbnMiLCJhIjoiRlVVVkx3VSJ9.9sdVEK_B_VkEXPjssU5MqA'
89538             }, {
89539               name: 'Detroit All-The-Places POIs',
89540               src: 'alltheplaces-poi',
89541               tooltip: 'Public domain business location data created by web scrapers.',
89542               template: 'https://{switch:a,b,c,d}.tiles.mapbox.com/v4/jonahadkins.cjksmswgk340g2vo06p1w9w0j-8fjjc/{z}/{x}/{y}.vector.pbf?access_token=pk.eyJ1Ijoiam9uYWhhZGtpbnMiLCJhIjoiRlVVVkx3VSJ9.9sdVEK_B_VkEXPjssU5MqA'
89543             }]; // Only show this if the map is around Detroit..
89544
89545             var detroit = geoExtent([-83.5, 42.1], [-82.8, 42.5]);
89546             var showVectorItems = context.map().zoom() > 9 && detroit.contains(context.map().center());
89547             var container = selection.selectAll('.vectortile-container').data(showVectorItems ? [0] : []);
89548             container.exit().remove();
89549             var containerEnter = container.enter().append('div').attr('class', 'vectortile-container');
89550             containerEnter.append('h4').attr('class', 'vectortile-header').html('Detroit Vector Tiles (Beta)');
89551             containerEnter.append('ul').attr('class', 'layer-list layer-list-vectortile');
89552             containerEnter.append('div').attr('class', 'vectortile-footer').append('a').attr('target', '_blank').call(svgIcon('#iD-icon-out-link', 'inline')).attr('href', 'https://github.com/osmus/detroit-mapping-challenge').append('span').html('About these layers');
89553             container = container.merge(containerEnter);
89554             var ul = container.selectAll('.layer-list-vectortile');
89555             var li = ul.selectAll('.list-item').data(vtData);
89556             li.exit().remove();
89557             var liEnter = li.enter().append('li').attr('class', function (d) {
89558               return 'list-item list-item-' + d.src;
89559             });
89560             var labelEnter = liEnter.append('label').each(function (d) {
89561               select(this).call(uiTooltip().title(d.tooltip).placement('top'));
89562             });
89563             labelEnter.append('input').attr('type', 'radio').attr('name', 'vectortile').on('change', selectVTLayer);
89564             labelEnter.append('span').html(function (d) {
89565               return d.name;
89566             }); // Update
89567
89568             li.merge(liEnter).classed('active', isVTLayerSelected).selectAll('input').property('checked', isVTLayerSelected);
89569
89570             function isVTLayerSelected(d) {
89571               return dataLayer && dataLayer.template() === d.template;
89572             }
89573
89574             function selectVTLayer(d3_event, d) {
89575               corePreferences('settings-custom-data-url', d.template);
89576
89577               if (dataLayer) {
89578                 dataLayer.template(d.template, d.src);
89579                 dataLayer.enabled(true);
89580               }
89581             }
89582           }
89583
89584           function drawCustomDataItems(selection) {
89585             var dataLayer = layers.layer('data');
89586             var hasData = dataLayer && dataLayer.hasData();
89587             var showsData = hasData && dataLayer.enabled();
89588             var ul = selection.selectAll('.layer-list-data').data(dataLayer ? [0] : []); // Exit
89589
89590             ul.exit().remove(); // Enter
89591
89592             var ulEnter = ul.enter().append('ul').attr('class', 'layer-list layer-list-data');
89593             var liEnter = ulEnter.append('li').attr('class', 'list-item-data');
89594             var labelEnter = liEnter.append('label').call(uiTooltip().title(_t.html('map_data.layers.custom.tooltip')).placement('top'));
89595             labelEnter.append('input').attr('type', 'checkbox').on('change', function () {
89596               toggleLayer('data');
89597             });
89598             labelEnter.append('span').html(_t.html('map_data.layers.custom.title'));
89599             liEnter.append('button').attr('class', 'open-data-options').call(uiTooltip().title(_t.html('settings.custom_data.tooltip')).placement(_mainLocalizer.textDirection() === 'rtl' ? 'right' : 'left')).on('click', function (d3_event) {
89600               d3_event.preventDefault();
89601               editCustom();
89602             }).call(svgIcon('#iD-icon-more'));
89603             liEnter.append('button').attr('class', 'zoom-to-data').call(uiTooltip().title(_t.html('map_data.layers.custom.zoom')).placement(_mainLocalizer.textDirection() === 'rtl' ? 'right' : 'left')).on('click', function (d3_event) {
89604               if (select(this).classed('disabled')) return;
89605               d3_event.preventDefault();
89606               d3_event.stopPropagation();
89607               dataLayer.fitZoom();
89608             }).call(svgIcon('#iD-icon-framed-dot', 'monochrome')); // Update
89609
89610             ul = ul.merge(ulEnter);
89611             ul.selectAll('.list-item-data').classed('active', showsData).selectAll('label').classed('deemphasize', !hasData).selectAll('input').property('disabled', !hasData).property('checked', showsData);
89612             ul.selectAll('button.zoom-to-data').classed('disabled', !hasData);
89613           }
89614
89615           function editCustom() {
89616             context.container().call(settingsCustomData);
89617           }
89618
89619           function customChanged(d) {
89620             var dataLayer = layers.layer('data');
89621
89622             if (d && d.url) {
89623               dataLayer.url(d.url);
89624             } else if (d && d.fileList) {
89625               dataLayer.fileList(d.fileList);
89626             }
89627           }
89628
89629           function drawPanelItems(selection) {
89630             var panelsListEnter = selection.selectAll('.md-extras-list').data([0]).enter().append('ul').attr('class', 'layer-list md-extras-list');
89631             var historyPanelLabelEnter = panelsListEnter.append('li').attr('class', 'history-panel-toggle-item').append('label').call(uiTooltip().title(_t.html('map_data.history_panel.tooltip')).keys([uiCmd('⌘⇧' + _t('info_panels.history.key'))]).placement('top'));
89632             historyPanelLabelEnter.append('input').attr('type', 'checkbox').on('change', function (d3_event) {
89633               d3_event.preventDefault();
89634               context.ui().info.toggle('history');
89635             });
89636             historyPanelLabelEnter.append('span').html(_t.html('map_data.history_panel.title'));
89637             var measurementPanelLabelEnter = panelsListEnter.append('li').attr('class', 'measurement-panel-toggle-item').append('label').call(uiTooltip().title(_t.html('map_data.measurement_panel.tooltip')).keys([uiCmd('⌘⇧' + _t('info_panels.measurement.key'))]).placement('top'));
89638             measurementPanelLabelEnter.append('input').attr('type', 'checkbox').on('change', function (d3_event) {
89639               d3_event.preventDefault();
89640               context.ui().info.toggle('measurement');
89641             });
89642             measurementPanelLabelEnter.append('span').html(_t.html('map_data.measurement_panel.title'));
89643           }
89644
89645           context.layers().on('change.uiSectionDataLayers', section.reRender);
89646           context.map().on('move.uiSectionDataLayers', debounce(function () {
89647             // Detroit layers may have moved in or out of view
89648             window.requestIdleCallback(section.reRender);
89649           }, 1000));
89650           return section;
89651         }
89652
89653         function uiSectionMapFeatures(context) {
89654           var _features = context.features().keys();
89655
89656           var section = uiSection('map-features', context).label(_t.html('map_data.map_features')).disclosureContent(renderDisclosureContent).expandedByDefault(false);
89657
89658           function renderDisclosureContent(selection) {
89659             var container = selection.selectAll('.layer-feature-list-container').data([0]);
89660             var containerEnter = container.enter().append('div').attr('class', 'layer-feature-list-container');
89661             containerEnter.append('ul').attr('class', 'layer-list layer-feature-list');
89662             var footer = containerEnter.append('div').attr('class', 'feature-list-links section-footer');
89663             footer.append('a').attr('class', 'feature-list-link').attr('href', '#').html(_t.html('issues.disable_all')).on('click', function (d3_event) {
89664               d3_event.preventDefault();
89665               context.features().disableAll();
89666             });
89667             footer.append('a').attr('class', 'feature-list-link').attr('href', '#').html(_t.html('issues.enable_all')).on('click', function (d3_event) {
89668               d3_event.preventDefault();
89669               context.features().enableAll();
89670             }); // Update
89671
89672             container = container.merge(containerEnter);
89673             container.selectAll('.layer-feature-list').call(drawListItems, _features, 'checkbox', 'feature', clickFeature, showsFeature);
89674           }
89675
89676           function drawListItems(selection, data, type, name, change, active) {
89677             var items = selection.selectAll('li').data(data); // Exit
89678
89679             items.exit().remove(); // Enter
89680
89681             var enter = items.enter().append('li').call(uiTooltip().title(function (d) {
89682               var tip = _t.html(name + '.' + d + '.tooltip');
89683
89684               if (autoHiddenFeature(d)) {
89685                 var msg = showsLayer('osm') ? _t.html('map_data.autohidden') : _t.html('map_data.osmhidden');
89686                 tip += '<div>' + msg + '</div>';
89687               }
89688
89689               return tip;
89690             }).placement('top'));
89691             var label = enter.append('label');
89692             label.append('input').attr('type', type).attr('name', name).on('change', change);
89693             label.append('span').html(function (d) {
89694               return _t.html(name + '.' + d + '.description');
89695             }); // Update
89696
89697             items = items.merge(enter);
89698             items.classed('active', active).selectAll('input').property('checked', active).property('indeterminate', autoHiddenFeature);
89699           }
89700
89701           function autoHiddenFeature(d) {
89702             return context.features().autoHidden(d);
89703           }
89704
89705           function showsFeature(d) {
89706             return context.features().enabled(d);
89707           }
89708
89709           function clickFeature(d3_event, d) {
89710             context.features().toggle(d);
89711           }
89712
89713           function showsLayer(id) {
89714             var layer = context.layers().layer(id);
89715             return layer && layer.enabled();
89716           } // add listeners
89717
89718
89719           context.features().on('change.map_features', section.reRender);
89720           return section;
89721         }
89722
89723         function uiSectionMapStyleOptions(context) {
89724           var section = uiSection('fill-area', context).label(_t.html('map_data.style_options')).disclosureContent(renderDisclosureContent).expandedByDefault(false);
89725
89726           function renderDisclosureContent(selection) {
89727             var container = selection.selectAll('.layer-fill-list').data([0]);
89728             container.enter().append('ul').attr('class', 'layer-list layer-fill-list').merge(container).call(drawListItems, context.map().areaFillOptions, 'radio', 'area_fill', setFill, isActiveFill);
89729             var container2 = selection.selectAll('.layer-visual-diff-list').data([0]);
89730             container2.enter().append('ul').attr('class', 'layer-list layer-visual-diff-list').merge(container2).call(drawListItems, ['highlight_edits'], 'checkbox', 'visual_diff', toggleHighlightEdited, function () {
89731               return context.surface().classed('highlight-edited');
89732             });
89733           }
89734
89735           function drawListItems(selection, data, type, name, change, active) {
89736             var items = selection.selectAll('li').data(data); // Exit
89737
89738             items.exit().remove(); // Enter
89739
89740             var enter = items.enter().append('li').call(uiTooltip().title(function (d) {
89741               return _t.html(name + '.' + d + '.tooltip');
89742             }).keys(function (d) {
89743               var key = d === 'wireframe' ? _t('area_fill.wireframe.key') : null;
89744               if (d === 'highlight_edits') key = _t('map_data.highlight_edits.key');
89745               return key ? [key] : null;
89746             }).placement('top'));
89747             var label = enter.append('label');
89748             label.append('input').attr('type', type).attr('name', name).on('change', change);
89749             label.append('span').html(function (d) {
89750               return _t.html(name + '.' + d + '.description');
89751             }); // Update
89752
89753             items = items.merge(enter);
89754             items.classed('active', active).selectAll('input').property('checked', active).property('indeterminate', false);
89755           }
89756
89757           function isActiveFill(d) {
89758             return context.map().activeAreaFill() === d;
89759           }
89760
89761           function toggleHighlightEdited(d3_event) {
89762             d3_event.preventDefault();
89763             context.map().toggleHighlightEdited();
89764           }
89765
89766           function setFill(d3_event, d) {
89767             context.map().activeAreaFill(d);
89768           }
89769
89770           context.map().on('changeHighlighting.ui_style, changeAreaFill.ui_style', section.reRender);
89771           return section;
89772         }
89773
89774         function uiSectionPhotoOverlays(context) {
89775           var layers = context.layers();
89776           var section = uiSection('photo-overlays', context).label(_t.html('photo_overlays.title')).disclosureContent(renderDisclosureContent).expandedByDefault(false);
89777
89778           function renderDisclosureContent(selection) {
89779             var container = selection.selectAll('.photo-overlay-container').data([0]);
89780             container.enter().append('div').attr('class', 'photo-overlay-container').merge(container).call(drawPhotoItems).call(drawPhotoTypeItems).call(drawDateFilter).call(drawUsernameFilter);
89781           }
89782
89783           function drawPhotoItems(selection) {
89784             var photoKeys = context.photos().overlayLayerIDs();
89785             var photoLayers = layers.all().filter(function (obj) {
89786               return photoKeys.indexOf(obj.id) !== -1;
89787             });
89788             var data = photoLayers.filter(function (obj) {
89789               return obj.layer.supported();
89790             });
89791
89792             function layerSupported(d) {
89793               return d.layer && d.layer.supported();
89794             }
89795
89796             function layerEnabled(d) {
89797               return layerSupported(d) && d.layer.enabled();
89798             }
89799
89800             var ul = selection.selectAll('.layer-list-photos').data([0]);
89801             ul = ul.enter().append('ul').attr('class', 'layer-list layer-list-photos').merge(ul);
89802             var li = ul.selectAll('.list-item-photos').data(data);
89803             li.exit().remove();
89804             var liEnter = li.enter().append('li').attr('class', function (d) {
89805               var classes = 'list-item-photos list-item-' + d.id;
89806
89807               if (d.id === 'mapillary-signs' || d.id === 'mapillary-map-features') {
89808                 classes += ' indented';
89809               }
89810
89811               return classes;
89812             });
89813             var labelEnter = liEnter.append('label').each(function (d) {
89814               var titleID;
89815               if (d.id === 'mapillary-signs') titleID = 'mapillary.signs.tooltip';else if (d.id === 'mapillary') titleID = 'mapillary_images.tooltip';else if (d.id === 'openstreetcam') titleID = 'openstreetcam_images.tooltip';else titleID = d.id.replace(/-/g, '_') + '.tooltip';
89816               select(this).call(uiTooltip().title(_t.html(titleID)).placement('top'));
89817             });
89818             labelEnter.append('input').attr('type', 'checkbox').on('change', function (d3_event, d) {
89819               toggleLayer(d.id);
89820             });
89821             labelEnter.append('span').html(function (d) {
89822               var id = d.id;
89823               if (id === 'mapillary-signs') id = 'photo_overlays.traffic_signs';
89824               return _t.html(id.replace(/-/g, '_') + '.title');
89825             }); // Update
89826
89827             li.merge(liEnter).classed('active', layerEnabled).selectAll('input').property('checked', layerEnabled);
89828           }
89829
89830           function drawPhotoTypeItems(selection) {
89831             var data = context.photos().allPhotoTypes();
89832
89833             function typeEnabled(d) {
89834               return context.photos().showsPhotoType(d);
89835             }
89836
89837             var ul = selection.selectAll('.layer-list-photo-types').data([0]);
89838             ul.exit().remove();
89839             ul = ul.enter().append('ul').attr('class', 'layer-list layer-list-photo-types').merge(ul);
89840             var li = ul.selectAll('.list-item-photo-types').data(context.photos().shouldFilterByPhotoType() ? data : []);
89841             li.exit().remove();
89842             var liEnter = li.enter().append('li').attr('class', function (d) {
89843               return 'list-item-photo-types list-item-' + d;
89844             });
89845             var labelEnter = liEnter.append('label').each(function (d) {
89846               select(this).call(uiTooltip().title(_t.html('photo_overlays.photo_type.' + d + '.tooltip')).placement('top'));
89847             });
89848             labelEnter.append('input').attr('type', 'checkbox').on('change', function (d3_event, d) {
89849               context.photos().togglePhotoType(d);
89850             });
89851             labelEnter.append('span').html(function (d) {
89852               return _t.html('photo_overlays.photo_type.' + d + '.title');
89853             }); // Update
89854
89855             li.merge(liEnter).classed('active', typeEnabled).selectAll('input').property('checked', typeEnabled);
89856           }
89857
89858           function drawDateFilter(selection) {
89859             var data = context.photos().dateFilters();
89860
89861             function filterEnabled(d) {
89862               return context.photos().dateFilterValue(d);
89863             }
89864
89865             var ul = selection.selectAll('.layer-list-date-filter').data([0]);
89866             ul.exit().remove();
89867             ul = ul.enter().append('ul').attr('class', 'layer-list layer-list-date-filter').merge(ul);
89868             var li = ul.selectAll('.list-item-date-filter').data(context.photos().shouldFilterByDate() ? data : []);
89869             li.exit().remove();
89870             var liEnter = li.enter().append('li').attr('class', 'list-item-date-filter');
89871             var labelEnter = liEnter.append('label').each(function (d) {
89872               select(this).call(uiTooltip().title(_t.html('photo_overlays.date_filter.' + d + '.tooltip')).placement('top'));
89873             });
89874             labelEnter.append('span').html(function (d) {
89875               return _t.html('photo_overlays.date_filter.' + d + '.title');
89876             });
89877             labelEnter.append('input').attr('type', 'date').attr('class', 'list-item-input').attr('placeholder', _t('units.year_month_day')).call(utilNoAuto).each(function (d) {
89878               utilGetSetValue(select(this), context.photos().dateFilterValue(d) || '');
89879             }).on('change', function (d3_event, d) {
89880               var value = utilGetSetValue(select(this)).trim();
89881               context.photos().setDateFilter(d, value, true); // reload the displayed dates
89882
89883               li.selectAll('input').each(function (d) {
89884                 utilGetSetValue(select(this), context.photos().dateFilterValue(d) || '');
89885               });
89886             });
89887             li = li.merge(liEnter).classed('active', filterEnabled);
89888           }
89889
89890           function drawUsernameFilter(selection) {
89891             function filterEnabled() {
89892               return context.photos().usernames();
89893             }
89894
89895             var ul = selection.selectAll('.layer-list-username-filter').data([0]);
89896             ul.exit().remove();
89897             ul = ul.enter().append('ul').attr('class', 'layer-list layer-list-username-filter').merge(ul);
89898             var li = ul.selectAll('.list-item-username-filter').data(context.photos().shouldFilterByUsername() ? ['username-filter'] : []);
89899             li.exit().remove();
89900             var liEnter = li.enter().append('li').attr('class', 'list-item-username-filter');
89901             var labelEnter = liEnter.append('label').each(function () {
89902               select(this).call(uiTooltip().title(_t.html('photo_overlays.username_filter.tooltip')).placement('top'));
89903             });
89904             labelEnter.append('span').html(_t.html('photo_overlays.username_filter.title'));
89905             labelEnter.append('input').attr('type', 'text').attr('class', 'list-item-input').call(utilNoAuto).property('value', usernameValue).on('change', function () {
89906               var value = select(this).property('value');
89907               context.photos().setUsernameFilter(value, true);
89908               select(this).property('value', usernameValue);
89909             });
89910             li.merge(liEnter).classed('active', filterEnabled);
89911
89912             function usernameValue() {
89913               var usernames = context.photos().usernames();
89914               if (usernames) return usernames.join('; ');
89915               return usernames;
89916             }
89917           }
89918
89919           function toggleLayer(which) {
89920             setLayer(which, !showsLayer(which));
89921           }
89922
89923           function showsLayer(which) {
89924             var layer = layers.layer(which);
89925
89926             if (layer) {
89927               return layer.enabled();
89928             }
89929
89930             return false;
89931           }
89932
89933           function setLayer(which, enabled) {
89934             var layer = layers.layer(which);
89935
89936             if (layer) {
89937               layer.enabled(enabled);
89938             }
89939           }
89940
89941           context.layers().on('change.uiSectionPhotoOverlays', section.reRender);
89942           context.photos().on('change.uiSectionPhotoOverlays', section.reRender);
89943           return section;
89944         }
89945
89946         function uiPaneMapData(context) {
89947           var mapDataPane = uiPane('map-data', context).key(_t('map_data.key')).label(_t.html('map_data.title')).description(_t.html('map_data.description')).iconName('iD-icon-data').sections([uiSectionDataLayers(context), uiSectionPhotoOverlays(context), uiSectionMapStyleOptions(context), uiSectionMapFeatures(context)]);
89948           return mapDataPane;
89949         }
89950
89951         function uiSectionPrivacy(context) {
89952           var section = uiSection('preferences-third-party', context).label(_t.html('preferences.privacy.title')).disclosureContent(renderDisclosureContent);
89953
89954           var _showThirdPartyIcons = corePreferences('preferences.privacy.thirdpartyicons') || 'true';
89955
89956           function renderDisclosureContent(selection) {
89957             // enter
89958             var privacyOptionsListEnter = selection.selectAll('.privacy-options-list').data([0]).enter().append('ul').attr('class', 'layer-list privacy-options-list');
89959             var thirdPartyIconsEnter = privacyOptionsListEnter.append('li').attr('class', 'privacy-third-party-icons-item').append('label').call(uiTooltip().title(_t.html('preferences.privacy.third_party_icons.tooltip')).placement('bottom'));
89960             thirdPartyIconsEnter.append('input').attr('type', 'checkbox').on('change', function (d3_event) {
89961               d3_event.preventDefault();
89962               _showThirdPartyIcons = _showThirdPartyIcons === 'true' ? 'false' : 'true';
89963               corePreferences('preferences.privacy.thirdpartyicons', _showThirdPartyIcons);
89964               update();
89965             });
89966             thirdPartyIconsEnter.append('span').html(_t.html('preferences.privacy.third_party_icons.description')); // Privacy Policy link
89967
89968             selection.selectAll('.privacy-link').data([0]).enter().append('div').attr('class', 'privacy-link').append('a').attr('target', '_blank').call(svgIcon('#iD-icon-out-link', 'inline')).attr('href', 'https://github.com/openstreetmap/iD/blob/release/PRIVACY.md').append('span').html(_t.html('preferences.privacy.privacy_link'));
89969             update();
89970
89971             function update() {
89972               selection.selectAll('.privacy-third-party-icons-item').classed('active', _showThirdPartyIcons === 'true').select('input').property('checked', _showThirdPartyIcons === 'true');
89973             }
89974           }
89975
89976           return section;
89977         }
89978
89979         function uiPanePreferences(context) {
89980           var preferencesPane = uiPane('preferences', context).key(_t('preferences.key')).label(_t.html('preferences.title')).description(_t.html('preferences.description')).iconName('fas-user-cog').sections([uiSectionPrivacy(context)]);
89981           return preferencesPane;
89982         }
89983
89984         function uiInit(context) {
89985           var _initCounter = 0;
89986           var _needWidth = {};
89987
89988           var _lastPointerType;
89989
89990           function render(container) {
89991             container.on('click.ui', function (d3_event) {
89992               // we're only concerned with the primary mouse button
89993               if (d3_event.button !== 0) return;
89994               if (!d3_event.composedPath) return; // some targets have default click events we don't want to override
89995
89996               var isOkayTarget = d3_event.composedPath().some(function (node) {
89997                 // we only care about element nodes
89998                 return node.nodeType === 1 && ( // clicking <input> focuses it and/or changes a value
89999                 node.nodeName === 'INPUT' || // clicking <label> affects its <input> by default
90000                 node.nodeName === 'LABEL' || // clicking <a> opens a hyperlink by default
90001                 node.nodeName === 'A');
90002               });
90003               if (isOkayTarget) return; // disable double-tap-to-zoom on touchscreens
90004
90005               d3_event.preventDefault();
90006             });
90007             var detected = utilDetect(); // only WebKit supports gesture events
90008
90009             if ('GestureEvent' in window && // Listening for gesture events on iOS 13.4+ breaks double-tapping,
90010             // but we only need to do this on desktop Safari anyway. – #7694
90011             !detected.isMobileWebKit) {
90012               // On iOS we disable pinch-to-zoom of the UI via the `touch-action`
90013               // CSS property, but on desktop Safari we need to manually cancel the
90014               // default gesture events.
90015               container.on('gesturestart.ui gesturechange.ui gestureend.ui', function (d3_event) {
90016                 // disable pinch-to-zoom of the UI via multitouch trackpads on macOS Safari
90017                 d3_event.preventDefault();
90018               });
90019             }
90020
90021             if ('PointerEvent' in window) {
90022               select(window).on('pointerdown.ui pointerup.ui', function (d3_event) {
90023                 var pointerType = d3_event.pointerType || 'mouse';
90024
90025                 if (_lastPointerType !== pointerType) {
90026                   _lastPointerType = pointerType;
90027                   container.attr('pointer', pointerType);
90028                 }
90029               }, true);
90030             } else {
90031               _lastPointerType = 'mouse';
90032               container.attr('pointer', 'mouse');
90033             }
90034
90035             container.attr('lang', _mainLocalizer.localeCode()).attr('dir', _mainLocalizer.textDirection()); // setup fullscreen keybindings (no button shown at this time)
90036
90037             container.call(uiFullScreen(context));
90038             var map = context.map();
90039             map.redrawEnable(false); // don't draw until we've set zoom/lat/long
90040
90041             map.on('hitMinZoom.ui', function () {
90042               ui.flash.iconName('#iD-icon-no').label(_t.html('cannot_zoom'))();
90043             });
90044             container.append('svg').attr('id', 'ideditor-defs').call(ui.svgDefs);
90045             container.append('div').attr('class', 'sidebar').call(ui.sidebar);
90046             var content = container.append('div').attr('class', 'main-content active'); // Top toolbar
90047
90048             content.append('div').attr('class', 'top-toolbar-wrap').append('div').attr('class', 'top-toolbar fillD').call(uiTopToolbar(context));
90049             content.append('div').attr('class', 'main-map').attr('dir', 'ltr').call(map);
90050             var overMap = content.append('div').attr('class', 'over-map'); // HACK: Mobile Safari 14 likes to select anything selectable when long-
90051             // pressing, even if it's not targeted. This conflicts with long-pressing
90052             // to show the edit menu. We add a selectable offscreen element as the first
90053             // child to trick Safari into not showing the selection UI.
90054
90055             overMap.append('div').attr('class', 'select-trap').text('t');
90056             overMap.call(uiMapInMap(context)).call(uiNotice(context));
90057             overMap.append('div').attr('class', 'spinner').call(uiSpinner(context)); // Map controls
90058
90059             var controls = overMap.append('div').attr('class', 'map-controls');
90060             controls.append('div').attr('class', 'map-control zoombuttons').call(uiZoom(context));
90061             controls.append('div').attr('class', 'map-control zoom-to-selection-control').call(uiZoomToSelection(context));
90062             controls.append('div').attr('class', 'map-control geolocate-control').call(uiGeolocate(context)); // Add panes
90063             // This should happen after map is initialized, as some require surface()
90064
90065             var panes = overMap.append('div').attr('class', 'map-panes');
90066             var uiPanes = [uiPaneBackground(context), uiPaneMapData(context), uiPaneIssues(context), uiPanePreferences(context), uiPaneHelp(context)];
90067             uiPanes.forEach(function (pane) {
90068               controls.append('div').attr('class', 'map-control map-pane-control ' + pane.id + '-control').call(pane.renderToggleButton);
90069               panes.call(pane.renderPane);
90070             });
90071             ui.info = uiInfo(context);
90072             overMap.call(ui.info);
90073             overMap.append('div').attr('class', 'photoviewer').classed('al', true) // 'al'=left,  'ar'=right
90074             .classed('hide', true).call(ui.photoviewer);
90075             overMap.append('div').attr('class', 'attribution-wrap').attr('dir', 'ltr').call(uiAttribution(context)); // Add footer
90076
90077             var about = content.append('div').attr('class', 'map-footer');
90078             about.append('div').attr('class', 'api-status').call(uiStatus(context));
90079             var footer = about.append('div').attr('class', 'map-footer-bar fillD');
90080             footer.append('div').attr('class', 'flash-wrap footer-hide');
90081             var footerWrap = footer.append('div').attr('class', 'main-footer-wrap footer-show');
90082             footerWrap.append('div').attr('class', 'scale-block').call(uiScale(context));
90083             var aboutList = footerWrap.append('div').attr('class', 'info-block').append('ul').attr('class', 'map-footer-list');
90084             aboutList.append('li').attr('class', 'user-list').call(uiContributors(context));
90085             var apiConnections = context.apiConnections();
90086
90087             if (apiConnections && apiConnections.length > 1) {
90088               aboutList.append('li').attr('class', 'source-switch').call(uiSourceSwitch(context).keys(apiConnections));
90089             }
90090
90091             aboutList.append('li').attr('class', 'issues-info').call(uiIssuesInfo(context));
90092             aboutList.append('li').attr('class', 'feature-warning').call(uiFeatureInfo(context));
90093             var issueLinks = aboutList.append('li');
90094             issueLinks.append('a').attr('target', '_blank').attr('href', 'https://github.com/openstreetmap/iD/issues').call(svgIcon('#iD-icon-bug', 'light')).call(uiTooltip().title(_t.html('report_a_bug')).placement('top'));
90095             issueLinks.append('a').attr('target', '_blank').attr('href', 'https://github.com/openstreetmap/iD/blob/develop/CONTRIBUTING.md#translating').call(svgIcon('#iD-icon-translate', 'light')).call(uiTooltip().title(_t.html('help_translate')).placement('top'));
90096             aboutList.append('li').attr('class', 'version').call(uiVersion(context));
90097
90098             if (!context.embed()) {
90099               aboutList.call(uiAccount(context));
90100             } // Setup map dimensions and move map to initial center/zoom.
90101             // This should happen after .main-content and toolbars exist.
90102
90103
90104             ui.onResize();
90105             map.redrawEnable(true);
90106             ui.hash = behaviorHash(context);
90107             ui.hash();
90108
90109             if (!ui.hash.hadHash) {
90110               map.centerZoom([0, 0], 2);
90111             } // Bind events
90112
90113
90114             window.onbeforeunload = function () {
90115               return context.save();
90116             };
90117
90118             window.onunload = function () {
90119               context.history().unlock();
90120             };
90121
90122             select(window).on('resize.editor', function () {
90123               ui.onResize();
90124             });
90125             var panPixels = 80;
90126             context.keybinding().on('⌫', function (d3_event) {
90127               d3_event.preventDefault();
90128             }).on([_t('sidebar.key'), '`', '²', '@'], ui.sidebar.toggle) // #5663, #6864 - common QWERTY, AZERTY
90129             .on('←', pan([panPixels, 0])).on('↑', pan([0, panPixels])).on('→', pan([-panPixels, 0])).on('↓', pan([0, -panPixels])).on(uiCmd('⌥←'), pan([map.dimensions()[0], 0])).on(uiCmd('⌥↑'), pan([0, map.dimensions()[1]])).on(uiCmd('⌥→'), pan([-map.dimensions()[0], 0])).on(uiCmd('⌥↓'), pan([0, -map.dimensions()[1]])).on(uiCmd('⌘' + _t('background.key')), function quickSwitch(d3_event) {
90130               if (d3_event) {
90131                 d3_event.stopImmediatePropagation();
90132                 d3_event.preventDefault();
90133               }
90134
90135               var previousBackground = context.background().findSource(corePreferences('background-last-used-toggle'));
90136
90137               if (previousBackground) {
90138                 var currentBackground = context.background().baseLayerSource();
90139                 corePreferences('background-last-used-toggle', currentBackground.id);
90140                 corePreferences('background-last-used', previousBackground.id);
90141                 context.background().baseLayerSource(previousBackground);
90142               }
90143             }).on(_t('area_fill.wireframe.key'), function toggleWireframe(d3_event) {
90144               d3_event.preventDefault();
90145               d3_event.stopPropagation();
90146               context.map().toggleWireframe();
90147             }).on(uiCmd('⌥' + _t('area_fill.wireframe.key')), function toggleOsmData(d3_event) {
90148               d3_event.preventDefault();
90149               d3_event.stopPropagation(); // Don't allow layer changes while drawing - #6584
90150
90151               var mode = context.mode();
90152               if (mode && /^draw/.test(mode.id)) return;
90153               var layer = context.layers().layer('osm');
90154
90155               if (layer) {
90156                 layer.enabled(!layer.enabled());
90157
90158                 if (!layer.enabled()) {
90159                   context.enter(modeBrowse(context));
90160                 }
90161               }
90162             }).on(_t('map_data.highlight_edits.key'), function toggleHighlightEdited(d3_event) {
90163               d3_event.preventDefault();
90164               context.map().toggleHighlightEdited();
90165             });
90166             context.on('enter.editor', function (entered) {
90167               container.classed('mode-' + entered.id, true);
90168             }).on('exit.editor', function (exited) {
90169               container.classed('mode-' + exited.id, false);
90170             });
90171             context.enter(modeBrowse(context));
90172
90173             if (!_initCounter++) {
90174               if (!ui.hash.startWalkthrough) {
90175                 context.container().call(uiSplash(context)).call(uiRestore(context));
90176               }
90177
90178               context.container().call(ui.shortcuts);
90179             }
90180
90181             var osm = context.connection();
90182             var auth = uiLoading(context).message(_t.html('loading_auth')).blocking(true);
90183
90184             if (osm && auth) {
90185               osm.on('authLoading.ui', function () {
90186                 context.container().call(auth);
90187               }).on('authDone.ui', function () {
90188                 auth.close();
90189               });
90190             }
90191
90192             _initCounter++;
90193
90194             if (ui.hash.startWalkthrough) {
90195               ui.hash.startWalkthrough = false;
90196               context.container().call(uiIntro(context));
90197             }
90198
90199             function pan(d) {
90200               return function (d3_event) {
90201                 if (d3_event.shiftKey) return;
90202                 if (context.container().select('.combobox').size()) return;
90203                 d3_event.preventDefault();
90204                 context.map().pan(d, 100);
90205               };
90206             }
90207           }
90208
90209           var ui = {};
90210
90211           var _loadPromise; // renders the iD interface into the container node
90212
90213
90214           ui.ensureLoaded = function () {
90215             if (_loadPromise) return _loadPromise;
90216             return _loadPromise = Promise.all([// must have strings and presets before loading the UI
90217             _mainLocalizer.ensureLoaded(), _mainPresetIndex.ensureLoaded()]).then(function () {
90218               if (!context.container().empty()) render(context.container());
90219             })["catch"](function (err) {
90220               return console.error(err);
90221             }); // eslint-disable-line
90222           }; // `ui.restart()` will destroy and rebuild the entire iD interface,
90223           // for example to switch the locale while iD is running.
90224
90225
90226           ui.restart = function () {
90227             context.keybinding().clear();
90228             _loadPromise = null;
90229             context.container().selectAll('*').remove();
90230             ui.ensureLoaded();
90231           };
90232
90233           ui.lastPointerType = function () {
90234             return _lastPointerType;
90235           };
90236
90237           ui.svgDefs = svgDefs(context);
90238           ui.flash = uiFlash(context);
90239           ui.sidebar = uiSidebar(context);
90240           ui.photoviewer = uiPhotoviewer(context);
90241           ui.shortcuts = uiShortcuts(context);
90242
90243           ui.onResize = function (withPan) {
90244             var map = context.map(); // Recalc dimensions of map and sidebar.. (`true` = force recalc)
90245             // This will call `getBoundingClientRect` and trigger reflow,
90246             //  but the values will be cached for later use.
90247
90248             var mapDimensions = utilGetDimensions(context.container().select('.main-content'), true);
90249             utilGetDimensions(context.container().select('.sidebar'), true);
90250
90251             if (withPan !== undefined) {
90252               map.redrawEnable(false);
90253               map.pan(withPan);
90254               map.redrawEnable(true);
90255             }
90256
90257             map.dimensions(mapDimensions);
90258             ui.photoviewer.onMapResize(); // check if header or footer have overflowed
90259
90260             ui.checkOverflow('.top-toolbar');
90261             ui.checkOverflow('.map-footer-bar'); // Use outdated code so it works on Explorer
90262
90263             var resizeWindowEvent = document.createEvent('Event');
90264             resizeWindowEvent.initEvent('resizeWindow', true, true);
90265             document.dispatchEvent(resizeWindowEvent);
90266           }; // Call checkOverflow when resizing or whenever the contents change.
90267
90268
90269           ui.checkOverflow = function (selector, reset) {
90270             if (reset) {
90271               delete _needWidth[selector];
90272             }
90273
90274             var selection = context.container().select(selector);
90275             if (selection.empty()) return;
90276             var scrollWidth = selection.property('scrollWidth');
90277             var clientWidth = selection.property('clientWidth');
90278             var needed = _needWidth[selector] || scrollWidth;
90279
90280             if (scrollWidth > clientWidth) {
90281               // overflow happening
90282               selection.classed('narrow', true);
90283
90284               if (!_needWidth[selector]) {
90285                 _needWidth[selector] = scrollWidth;
90286               }
90287             } else if (scrollWidth >= needed) {
90288               selection.classed('narrow', false);
90289             }
90290           };
90291
90292           ui.togglePanes = function (showPane) {
90293             var hidePanes = context.container().selectAll('.map-pane.shown');
90294             var side = _mainLocalizer.textDirection() === 'ltr' ? 'right' : 'left';
90295             hidePanes.classed('shown', false).classed('hide', true);
90296             context.container().selectAll('.map-pane-control button').classed('active', false);
90297
90298             if (showPane) {
90299               hidePanes.classed('shown', false).classed('hide', true).style(side, '-500px');
90300               context.container().selectAll('.' + showPane.attr('pane') + '-control button').classed('active', true);
90301               showPane.classed('shown', true).classed('hide', false);
90302
90303               if (hidePanes.empty()) {
90304                 showPane.style(side, '-500px').transition().duration(200).style(side, '0px');
90305               } else {
90306                 showPane.style(side, '0px');
90307               }
90308             } else {
90309               hidePanes.classed('shown', true).classed('hide', false).style(side, '0px').transition().duration(200).style(side, '-500px').on('end', function () {
90310                 select(this).classed('shown', false).classed('hide', true);
90311               });
90312             }
90313           };
90314
90315           var _editMenu = uiEditMenu(context);
90316
90317           ui.editMenu = function () {
90318             return _editMenu;
90319           };
90320
90321           ui.showEditMenu = function (anchorPoint, triggerType, operations) {
90322             // remove any displayed menu
90323             ui.closeEditMenu();
90324             if (!operations && context.mode().operations) operations = context.mode().operations();
90325             if (!operations || !operations.length) return; // disable menu if in wide selection, for example
90326
90327             if (!context.map().editableDataEnabled()) return;
90328             var surfaceNode = context.surface().node();
90329
90330             if (surfaceNode.focus) {
90331               // FF doesn't support it
90332               // focus the surface or else clicking off the menu may not trigger modeBrowse
90333               surfaceNode.focus();
90334             }
90335
90336             operations.forEach(function (operation) {
90337               if (operation.point) operation.point(anchorPoint);
90338             });
90339
90340             _editMenu.anchorLoc(anchorPoint).triggerType(triggerType).operations(operations); // render the menu
90341
90342
90343             context.map().supersurface.call(_editMenu);
90344           };
90345
90346           ui.closeEditMenu = function () {
90347             // remove any existing menu no matter how it was added
90348             context.map().supersurface.select('.edit-menu').remove();
90349           };
90350
90351           var _saveLoading = select(null);
90352
90353           context.uploader().on('saveStarted.ui', function () {
90354             _saveLoading = uiLoading(context).message(_t.html('save.uploading')).blocking(true);
90355             context.container().call(_saveLoading); // block input during upload
90356           }).on('saveEnded.ui', function () {
90357             _saveLoading.close();
90358
90359             _saveLoading = select(null);
90360           });
90361           return ui;
90362         }
90363
90364         function coreContext() {
90365           var _this = this;
90366
90367           var dispatch = dispatch$8('enter', 'exit', 'change');
90368           var context = utilRebind({}, dispatch, 'on');
90369
90370           var _deferred = new Set();
90371
90372           context.version = '2.20.1';
90373           context.privacyVersion = '20201202'; // iD will alter the hash so cache the parameters intended to setup the session
90374
90375           context.initialHashParams = window.location.hash ? utilStringQs(window.location.hash) : {};
90376           context.isFirstSession = !corePreferences('sawSplash') && !corePreferences('sawPrivacyVersion');
90377           /* Changeset */
90378           // An osmChangeset object. Not loaded until needed.
90379
90380           context.changeset = null;
90381           var _defaultChangesetComment = context.initialHashParams.comment;
90382           var _defaultChangesetSource = context.initialHashParams.source;
90383           var _defaultChangesetHashtags = context.initialHashParams.hashtags;
90384
90385           context.defaultChangesetComment = function (val) {
90386             if (!arguments.length) return _defaultChangesetComment;
90387             _defaultChangesetComment = val;
90388             return context;
90389           };
90390
90391           context.defaultChangesetSource = function (val) {
90392             if (!arguments.length) return _defaultChangesetSource;
90393             _defaultChangesetSource = val;
90394             return context;
90395           };
90396
90397           context.defaultChangesetHashtags = function (val) {
90398             if (!arguments.length) return _defaultChangesetHashtags;
90399             _defaultChangesetHashtags = val;
90400             return context;
90401           };
90402           /* Document title */
90403
90404           /* (typically shown as the label for the browser window/tab) */
90405           // If true, iD will update the title based on what the user is doing
90406
90407
90408           var _setsDocumentTitle = true;
90409
90410           context.setsDocumentTitle = function (val) {
90411             if (!arguments.length) return _setsDocumentTitle;
90412             _setsDocumentTitle = val;
90413             return context;
90414           }; // The part of the title that is always the same
90415
90416
90417           var _documentTitleBase = document.title;
90418
90419           context.documentTitleBase = function (val) {
90420             if (!arguments.length) return _documentTitleBase;
90421             _documentTitleBase = val;
90422             return context;
90423           };
90424           /* User interface and keybinding */
90425
90426
90427           var _ui;
90428
90429           context.ui = function () {
90430             return _ui;
90431           };
90432
90433           context.lastPointerType = function () {
90434             return _ui.lastPointerType();
90435           };
90436
90437           var _keybinding = utilKeybinding('context');
90438
90439           context.keybinding = function () {
90440             return _keybinding;
90441           };
90442
90443           select(document).call(_keybinding);
90444           /* Straight accessors. Avoid using these if you can. */
90445           // Instantiate the connection here because it doesn't require passing in
90446           // `context` and it's needed for pre-init calls like `preauth`
90447
90448           var _connection = services.osm;
90449
90450           var _history;
90451
90452           var _validator;
90453
90454           var _uploader;
90455
90456           context.connection = function () {
90457             return _connection;
90458           };
90459
90460           context.history = function () {
90461             return _history;
90462           };
90463
90464           context.validator = function () {
90465             return _validator;
90466           };
90467
90468           context.uploader = function () {
90469             return _uploader;
90470           };
90471           /* Connection */
90472
90473
90474           context.preauth = function (options) {
90475             if (_connection) {
90476               _connection["switch"](options);
90477             }
90478
90479             return context;
90480           };
90481           /* connection options for source switcher (optional) */
90482
90483
90484           var _apiConnections;
90485
90486           context.apiConnections = function (val) {
90487             if (!arguments.length) return _apiConnections;
90488             _apiConnections = val;
90489             return context;
90490           }; // A string or array or locale codes to prefer over the browser's settings
90491
90492
90493           context.locale = function (locale) {
90494             if (!arguments.length) return _mainLocalizer.localeCode();
90495             _mainLocalizer.preferredLocaleCodes(locale);
90496             return context;
90497           };
90498
90499           function afterLoad(cid, callback) {
90500             return function (err, result) {
90501               if (err) {
90502                 // 400 Bad Request, 401 Unauthorized, 403 Forbidden..
90503                 if (err.status === 400 || err.status === 401 || err.status === 403) {
90504                   if (_connection) {
90505                     _connection.logout();
90506                   }
90507                 }
90508
90509                 if (typeof callback === 'function') {
90510                   callback(err);
90511                 }
90512
90513                 return;
90514               } else if (_connection && _connection.getConnectionId() !== cid) {
90515                 if (typeof callback === 'function') {
90516                   callback({
90517                     message: 'Connection Switched',
90518                     status: -1
90519                   });
90520                 }
90521
90522                 return;
90523               } else {
90524                 _history.merge(result.data, result.extent);
90525
90526                 if (typeof callback === 'function') {
90527                   callback(err, result);
90528                 }
90529
90530                 return;
90531               }
90532             };
90533           }
90534
90535           context.loadTiles = function (projection, callback) {
90536             var handle = window.requestIdleCallback(function () {
90537               _deferred["delete"](handle);
90538
90539               if (_connection && context.editableDataEnabled()) {
90540                 var cid = _connection.getConnectionId();
90541
90542                 _connection.loadTiles(projection, afterLoad(cid, callback));
90543               }
90544             });
90545
90546             _deferred.add(handle);
90547           };
90548
90549           context.loadTileAtLoc = function (loc, callback) {
90550             var handle = window.requestIdleCallback(function () {
90551               _deferred["delete"](handle);
90552
90553               if (_connection && context.editableDataEnabled()) {
90554                 var cid = _connection.getConnectionId();
90555
90556                 _connection.loadTileAtLoc(loc, afterLoad(cid, callback));
90557               }
90558             });
90559
90560             _deferred.add(handle);
90561           }; // Download the full entity and its parent relations. The callback may be called multiple times.
90562
90563
90564           context.loadEntity = function (entityID, callback) {
90565             if (_connection) {
90566               var cid = _connection.getConnectionId();
90567
90568               _connection.loadEntity(entityID, afterLoad(cid, callback)); // We need to fetch the parent relations separately.
90569
90570
90571               _connection.loadEntityRelations(entityID, afterLoad(cid, callback));
90572             }
90573           };
90574
90575           context.zoomToEntity = function (entityID, zoomTo) {
90576             // be sure to load the entity even if we're not going to zoom to it
90577             context.loadEntity(entityID, function (err, result) {
90578               if (err) return;
90579
90580               if (zoomTo !== false) {
90581                 var entity = result.data.find(function (e) {
90582                   return e.id === entityID;
90583                 });
90584
90585                 if (entity) {
90586                   _map.zoomTo(entity);
90587                 }
90588               }
90589             });
90590
90591             _map.on('drawn.zoomToEntity', function () {
90592               if (!context.hasEntity(entityID)) return;
90593
90594               _map.on('drawn.zoomToEntity', null);
90595
90596               context.on('enter.zoomToEntity', null);
90597               context.enter(modeSelect(context, [entityID]));
90598             });
90599
90600             context.on('enter.zoomToEntity', function () {
90601               if (_mode.id !== 'browse') {
90602                 _map.on('drawn.zoomToEntity', null);
90603
90604                 context.on('enter.zoomToEntity', null);
90605               }
90606             });
90607           };
90608
90609           var _minEditableZoom = 16;
90610
90611           context.minEditableZoom = function (val) {
90612             if (!arguments.length) return _minEditableZoom;
90613             _minEditableZoom = val;
90614
90615             if (_connection) {
90616               _connection.tileZoom(val);
90617             }
90618
90619             return context;
90620           }; // String length limits in Unicode characters, not JavaScript UTF-16 code units
90621
90622
90623           context.maxCharsForTagKey = function () {
90624             return 255;
90625           };
90626
90627           context.maxCharsForTagValue = function () {
90628             return 255;
90629           };
90630
90631           context.maxCharsForRelationRole = function () {
90632             return 255;
90633           };
90634
90635           function cleanOsmString(val, maxChars) {
90636             // be lenient with input
90637             if (val === undefined || val === null) {
90638               val = '';
90639             } else {
90640               val = val.toString();
90641             } // remove whitespace
90642
90643
90644             val = val.trim(); // use the canonical form of the string
90645
90646             if (val.normalize) val = val.normalize('NFC'); // trim to the number of allowed characters
90647
90648             return utilUnicodeCharsTruncated(val, maxChars);
90649           }
90650
90651           context.cleanTagKey = function (val) {
90652             return cleanOsmString(val, context.maxCharsForTagKey());
90653           };
90654
90655           context.cleanTagValue = function (val) {
90656             return cleanOsmString(val, context.maxCharsForTagValue());
90657           };
90658
90659           context.cleanRelationRole = function (val) {
90660             return cleanOsmString(val, context.maxCharsForRelationRole());
90661           };
90662           /* History */
90663
90664
90665           var _inIntro = false;
90666
90667           context.inIntro = function (val) {
90668             if (!arguments.length) return _inIntro;
90669             _inIntro = val;
90670             return context;
90671           }; // Immediately save the user's history to localstorage, if possible
90672           // This is called someteimes, but also on the `window.onbeforeunload` handler
90673
90674
90675           context.save = function () {
90676             // no history save, no message onbeforeunload
90677             if (_inIntro || context.container().select('.modal').size()) return;
90678             var canSave;
90679
90680             if (_mode && _mode.id === 'save') {
90681               canSave = false; // Attempt to prevent user from creating duplicate changes - see #5200
90682
90683               if (services.osm && services.osm.isChangesetInflight()) {
90684                 _history.clearSaved();
90685
90686                 return;
90687               }
90688             } else {
90689               canSave = context.selectedIDs().every(function (id) {
90690                 var entity = context.hasEntity(id);
90691                 return entity && !entity.isDegenerate();
90692               });
90693             }
90694
90695             if (canSave) {
90696               _history.save();
90697             }
90698
90699             if (_history.hasChanges()) {
90700               return _t('save.unsaved_changes');
90701             }
90702           }; // Debounce save, since it's a synchronous localStorage write,
90703           // and history changes can happen frequently (e.g. when dragging).
90704
90705
90706           context.debouncedSave = debounce(context.save, 350);
90707
90708           function withDebouncedSave(fn) {
90709             return function () {
90710               var result = fn.apply(_history, arguments);
90711               context.debouncedSave();
90712               return result;
90713             };
90714           }
90715           /* Graph */
90716
90717
90718           context.hasEntity = function (id) {
90719             return _history.graph().hasEntity(id);
90720           };
90721
90722           context.entity = function (id) {
90723             return _history.graph().entity(id);
90724           };
90725           /* Modes */
90726
90727
90728           var _mode;
90729
90730           context.mode = function () {
90731             return _mode;
90732           };
90733
90734           context.enter = function (newMode) {
90735             if (_mode) {
90736               _mode.exit();
90737
90738               dispatch.call('exit', _this, _mode);
90739             }
90740
90741             _mode = newMode;
90742
90743             _mode.enter();
90744
90745             dispatch.call('enter', _this, _mode);
90746           };
90747
90748           context.selectedIDs = function () {
90749             return _mode && _mode.selectedIDs && _mode.selectedIDs() || [];
90750           };
90751
90752           context.activeID = function () {
90753             return _mode && _mode.activeID && _mode.activeID();
90754           };
90755
90756           var _selectedNoteID;
90757
90758           context.selectedNoteID = function (noteID) {
90759             if (!arguments.length) return _selectedNoteID;
90760             _selectedNoteID = noteID;
90761             return context;
90762           }; // NOTE: Don't change the name of this until UI v3 is merged
90763
90764
90765           var _selectedErrorID;
90766
90767           context.selectedErrorID = function (errorID) {
90768             if (!arguments.length) return _selectedErrorID;
90769             _selectedErrorID = errorID;
90770             return context;
90771           };
90772           /* Behaviors */
90773
90774
90775           context.install = function (behavior) {
90776             return context.surface().call(behavior);
90777           };
90778
90779           context.uninstall = function (behavior) {
90780             return context.surface().call(behavior.off);
90781           };
90782           /* Copy/Paste */
90783
90784
90785           var _copyGraph;
90786
90787           context.copyGraph = function () {
90788             return _copyGraph;
90789           };
90790
90791           var _copyIDs = [];
90792
90793           context.copyIDs = function (val) {
90794             if (!arguments.length) return _copyIDs;
90795             _copyIDs = val;
90796             _copyGraph = _history.graph();
90797             return context;
90798           };
90799
90800           var _copyLonLat;
90801
90802           context.copyLonLat = function (val) {
90803             if (!arguments.length) return _copyLonLat;
90804             _copyLonLat = val;
90805             return context;
90806           };
90807           /* Background */
90808
90809
90810           var _background;
90811
90812           context.background = function () {
90813             return _background;
90814           };
90815           /* Features */
90816
90817
90818           var _features;
90819
90820           context.features = function () {
90821             return _features;
90822           };
90823
90824           context.hasHiddenConnections = function (id) {
90825             var graph = _history.graph();
90826
90827             var entity = graph.entity(id);
90828             return _features.hasHiddenConnections(entity, graph);
90829           };
90830           /* Photos */
90831
90832
90833           var _photos;
90834
90835           context.photos = function () {
90836             return _photos;
90837           };
90838           /* Map */
90839
90840
90841           var _map;
90842
90843           context.map = function () {
90844             return _map;
90845           };
90846
90847           context.layers = function () {
90848             return _map.layers();
90849           };
90850
90851           context.surface = function () {
90852             return _map.surface;
90853           };
90854
90855           context.editableDataEnabled = function () {
90856             return _map.editableDataEnabled();
90857           };
90858
90859           context.surfaceRect = function () {
90860             return _map.surface.node().getBoundingClientRect();
90861           };
90862
90863           context.editable = function () {
90864             // don't allow editing during save
90865             var mode = context.mode();
90866             if (!mode || mode.id === 'save') return false;
90867             return _map.editableDataEnabled();
90868           };
90869           /* Debug */
90870
90871
90872           var _debugFlags = {
90873             tile: false,
90874             // tile boundaries
90875             collision: false,
90876             // label collision bounding boxes
90877             imagery: false,
90878             // imagery bounding polygons
90879             target: false,
90880             // touch targets
90881             downloaded: false // downloaded data from osm
90882
90883           };
90884
90885           context.debugFlags = function () {
90886             return _debugFlags;
90887           };
90888
90889           context.getDebug = function (flag) {
90890             return flag && _debugFlags[flag];
90891           };
90892
90893           context.setDebug = function (flag, val) {
90894             if (arguments.length === 1) val = true;
90895             _debugFlags[flag] = val;
90896             dispatch.call('change');
90897             return context;
90898           };
90899           /* Container */
90900
90901
90902           var _container = select(null);
90903
90904           context.container = function (val) {
90905             if (!arguments.length) return _container;
90906             _container = val;
90907
90908             _container.classed('ideditor', true);
90909
90910             return context;
90911           };
90912
90913           context.containerNode = function (val) {
90914             if (!arguments.length) return context.container().node();
90915             context.container(select(val));
90916             return context;
90917           };
90918
90919           var _embed;
90920
90921           context.embed = function (val) {
90922             if (!arguments.length) return _embed;
90923             _embed = val;
90924             return context;
90925           };
90926           /* Assets */
90927
90928
90929           var _assetPath = '';
90930
90931           context.assetPath = function (val) {
90932             if (!arguments.length) return _assetPath;
90933             _assetPath = val;
90934             _mainFileFetcher.assetPath(val);
90935             return context;
90936           };
90937
90938           var _assetMap = {};
90939
90940           context.assetMap = function (val) {
90941             if (!arguments.length) return _assetMap;
90942             _assetMap = val;
90943             _mainFileFetcher.assetMap(val);
90944             return context;
90945           };
90946
90947           context.asset = function (val) {
90948             if (/^http(s)?:\/\//i.test(val)) return val;
90949             var filename = _assetPath + val;
90950             return _assetMap[filename] || filename;
90951           };
90952
90953           context.imagePath = function (val) {
90954             return context.asset("img/".concat(val));
90955           };
90956           /* reset (aka flush) */
90957
90958
90959           context.reset = context.flush = function () {
90960             context.debouncedSave.cancel();
90961             Array.from(_deferred).forEach(function (handle) {
90962               window.cancelIdleCallback(handle);
90963
90964               _deferred["delete"](handle);
90965             });
90966             Object.values(services).forEach(function (service) {
90967               if (service && typeof service.reset === 'function') {
90968                 service.reset(context);
90969               }
90970             });
90971             context.changeset = null;
90972
90973             _validator.reset();
90974
90975             _features.reset();
90976
90977             _history.reset();
90978
90979             _uploader.reset(); // don't leave stale state in the inspector
90980
90981
90982             context.container().select('.inspector-wrap *').remove();
90983             return context;
90984           };
90985           /* Projections */
90986
90987
90988           context.projection = geoRawMercator();
90989           context.curtainProjection = geoRawMercator();
90990           /* Init */
90991
90992           context.init = function () {
90993             instantiateInternal();
90994             initializeDependents();
90995             return context; // Load variables and properties. No property of `context` should be accessed
90996             // until this is complete since load statuses are indeterminate. The order
90997             // of instantiation shouldn't matter.
90998
90999             function instantiateInternal() {
91000               _history = coreHistory(context);
91001               context.graph = _history.graph;
91002               context.pauseChangeDispatch = _history.pauseChangeDispatch;
91003               context.resumeChangeDispatch = _history.resumeChangeDispatch;
91004               context.perform = withDebouncedSave(_history.perform);
91005               context.replace = withDebouncedSave(_history.replace);
91006               context.pop = withDebouncedSave(_history.pop);
91007               context.overwrite = withDebouncedSave(_history.overwrite);
91008               context.undo = withDebouncedSave(_history.undo);
91009               context.redo = withDebouncedSave(_history.redo);
91010               _validator = coreValidator(context);
91011               _uploader = coreUploader(context);
91012               _background = rendererBackground(context);
91013               _features = rendererFeatures(context);
91014               _map = rendererMap(context);
91015               _photos = rendererPhotos(context);
91016               _ui = uiInit(context);
91017             } // Set up objects that might need to access properties of `context`. The order
91018             // might matter if dependents make calls to each other. Be wary of async calls.
91019
91020
91021             function initializeDependents() {
91022               if (context.initialHashParams.presets) {
91023                 _mainPresetIndex.addablePresetIDs(new Set(context.initialHashParams.presets.split(',')));
91024               }
91025
91026               if (context.initialHashParams.locale) {
91027                 _mainLocalizer.preferredLocaleCodes(context.initialHashParams.locale);
91028               } // kick off some async work
91029
91030
91031               _mainLocalizer.ensureLoaded();
91032
91033               _background.ensureLoaded();
91034
91035               _mainPresetIndex.ensureLoaded();
91036               Object.values(services).forEach(function (service) {
91037                 if (service && typeof service.init === 'function') {
91038                   service.init();
91039                 }
91040               });
91041
91042               _map.init();
91043
91044               _validator.init();
91045
91046               _features.init();
91047
91048               if (services.maprules && context.initialHashParams.maprules) {
91049                 d3_json(context.initialHashParams.maprules).then(function (mapcss) {
91050                   services.maprules.init();
91051                   mapcss.forEach(function (mapcssSelector) {
91052                     return services.maprules.addRule(mapcssSelector);
91053                   });
91054                 })["catch"](function () {
91055                   /* ignore */
91056                 });
91057               } // if the container isn't available, e.g. when testing, don't load the UI
91058
91059
91060               if (!context.container().empty()) {
91061                 _ui.ensureLoaded().then(function () {
91062                   _photos.init();
91063                 });
91064               }
91065             }
91066           };
91067
91068           return context;
91069         }
91070
91071         // NSI contains the most correct tagging for many commonly mapped features.
91072         // See https://github.com/osmlab/name-suggestion-index  and  https://nsi.guide
91073         // DATA
91074
91075         var _nsiStatus = 'loading'; // 'loading', 'ok', 'failed'
91076
91077         var _nsi = {}; // Sometimes we can upgrade a feature tagged like `building=yes` to a better tag.
91078
91079         var buildingPreset = {
91080           'building/commercial': true,
91081           'building/government': true,
91082           'building/hotel': true,
91083           'building/retail': true,
91084           'building/office': true,
91085           'building/supermarket': true,
91086           'building/yes': true
91087         }; // Exceptions to the namelike regexes.
91088         // Usually a tag suffix contains a language code like `name:en`, `name:ru`
91089         // but we want to exclude things like `operator:type`, `name:etymology`, etc..
91090
91091         var notNames = /:(colou?r|type|forward|backward|left|right|etymology|pronunciation|wikipedia)$/i; // Exceptions to the branchlike regexes
91092
91093         var notBranches = /(coop|express|wireless|factory|outlet)/i; // PRIVATE FUNCTIONS
91094         // `setNsiSources()`
91095         // Adds the sources to iD's filemap so we can start downloading data.
91096         //
91097
91098         function setNsiSources() {
91099           var nsiVersion = packageJSON.devDependencies['name-suggestion-index'];
91100           var v = vparse(nsiVersion);
91101           var vMinor = "".concat(v.major, ".").concat(v.minor);
91102           var sources = {
91103             'nsi_data': "https://cdn.jsdelivr.net/npm/name-suggestion-index@".concat(vMinor, "/dist/nsi.min.json"),
91104             'nsi_dissolved': "https://cdn.jsdelivr.net/npm/name-suggestion-index@".concat(vMinor, "/dist/dissolved.min.json"),
91105             'nsi_features': "https://cdn.jsdelivr.net/npm/name-suggestion-index@".concat(vMinor, "/dist/featureCollection.min.json"),
91106             'nsi_generics': "https://cdn.jsdelivr.net/npm/name-suggestion-index@".concat(vMinor, "/dist/genericWords.min.json"),
91107             'nsi_presets': "https://cdn.jsdelivr.net/npm/name-suggestion-index@".concat(vMinor, "/dist/presets/nsi-id-presets.min.json"),
91108             'nsi_replacements': "https://cdn.jsdelivr.net/npm/name-suggestion-index@".concat(vMinor, "/dist/replacements.min.json"),
91109             'nsi_trees': "https://cdn.jsdelivr.net/npm/name-suggestion-index@".concat(vMinor, "/dist/trees.min.json")
91110           };
91111           var fileMap = _mainFileFetcher.fileMap();
91112
91113           for (var k in sources) {
91114             fileMap[k] = sources[k];
91115           }
91116         } // `loadNsiPresets()`
91117         //  Returns a Promise fulfilled when the presets have been downloaded and merged into iD.
91118         //
91119
91120
91121         function loadNsiPresets() {
91122           return Promise.all([_mainFileFetcher.get('nsi_presets'), _mainFileFetcher.get('nsi_features')]).then(function (vals) {
91123             // Add `suggestion=true` to all the nsi presets
91124             // The preset json schema doesn't include it, but the iD code still uses it
91125             Object.values(vals[0].presets).forEach(function (preset) {
91126               return preset.suggestion = true;
91127             });
91128             _mainPresetIndex.merge({
91129               presets: vals[0].presets,
91130               featureCollection: vals[1]
91131             });
91132           });
91133         } // `loadNsiData()`
91134         //  Returns a Promise fulfilled when the other data have been downloaded and processed
91135         //
91136
91137
91138         function loadNsiData() {
91139           return Promise.all([_mainFileFetcher.get('nsi_data'), _mainFileFetcher.get('nsi_dissolved'), _mainFileFetcher.get('nsi_replacements'), _mainFileFetcher.get('nsi_trees')]).then(function (vals) {
91140             _nsi = {
91141               data: vals[0].nsi,
91142               // the raw name-suggestion-index data
91143               dissolved: vals[1].dissolved,
91144               // list of dissolved items
91145               replacements: vals[2].replacements,
91146               // trivial old->new qid replacements
91147               trees: vals[3].trees,
91148               // metadata about trees, main tags
91149               kvt: new Map(),
91150               // Map (k -> Map (v -> t) )
91151               qids: new Map(),
91152               // Map (wd/wp tag values -> qids)
91153               ids: new Map() // Map (id -> NSI item)
91154
91155             };
91156             _nsi.matcher = new Matcher();
91157
91158             _nsi.matcher.buildMatchIndex(_nsi.data);
91159
91160             _nsi.matcher.buildLocationIndex(_nsi.data, _mainLocations.loco());
91161
91162             Object.keys(_nsi.data).forEach(function (tkv) {
91163               var category = _nsi.data[tkv];
91164               var parts = tkv.split('/', 3); // tkv = "tree/key/value"
91165
91166               var t = parts[0];
91167               var k = parts[1];
91168               var v = parts[2]; // Build a reverse index of keys -> values -> trees present in the name-suggestion-index
91169               // Collect primary keys  (e.g. "amenity", "craft", "shop", "man_made", "route", etc)
91170               // "amenity": {
91171               //   "restaurant": "brands"
91172               // }
91173
91174               var vmap = _nsi.kvt.get(k);
91175
91176               if (!vmap) {
91177                 vmap = new Map();
91178
91179                 _nsi.kvt.set(k, vmap);
91180               }
91181
91182               vmap.set(v, t);
91183               var tree = _nsi.trees[t]; // e.g. "brands", "operators"
91184
91185               var mainTag = tree.mainTag; // e.g. "brand:wikidata", "operator:wikidata", etc
91186
91187               var items = category.items || [];
91188               items.forEach(function (item) {
91189                 // Remember some useful things for later, cache NSI id -> item
91190                 item.tkv = tkv;
91191                 item.mainTag = mainTag;
91192
91193                 _nsi.ids.set(item.id, item); // Cache Wikidata/Wikipedia values -> qid, for #6416
91194
91195
91196                 var wd = item.tags[mainTag];
91197                 var wp = item.tags[mainTag.replace('wikidata', 'wikipedia')];
91198                 if (wd) _nsi.qids.set(wd, wd);
91199                 if (wp && wd) _nsi.qids.set(wp, wd);
91200               });
91201             });
91202           });
91203         } // `gatherKVs()`
91204         // Gather all the k/v pairs that we will run through the NSI matcher.
91205         // An OSM tags object can contain anything, but only a few tags will be interesting to NSI.
91206         //
91207         // This function will return the interesting tag pairs like:
91208         //   "amenity/restaurant", "man_made/flagpole"
91209         // and fallbacks like
91210         //   "amenity/yes"
91211         // excluding things like
91212         //   "tiger:reviewed", "surface", "ref", etc.
91213         //
91214         // Arguments
91215         //   `tags`: `Object` containing the feature's OSM tags
91216         // Returns
91217         //   `Object` containing kv pairs to test:
91218         //   {
91219         //     'primary': Set(),
91220         //     'alternate': Set()
91221         //   }
91222         //
91223
91224
91225         function gatherKVs(tags) {
91226           var primary = new Set();
91227           var alternate = new Set();
91228           Object.keys(tags).forEach(function (osmkey) {
91229             var osmvalue = tags[osmkey];
91230             if (!osmvalue) return; // Match a 'route_master' as if it were a 'route' - name-suggestion-index#5184
91231
91232             if (osmkey === 'route_master') osmkey = 'route';
91233
91234             var vmap = _nsi.kvt.get(osmkey);
91235
91236             if (!vmap) return; // not an interesting key
91237
91238             if (vmap.get(osmvalue)) {
91239               // Matched a category in NSI
91240               primary.add("".concat(osmkey, "/").concat(osmvalue)); // interesting key/value
91241             } else if (osmvalue === 'yes') {
91242               alternate.add("".concat(osmkey, "/").concat(osmvalue)); // fallback key/yes
91243             }
91244           }); // Can we try a generic building fallback match? - See #6122, #7197
91245           // Only try this if we do a preset match and find nothing else remarkable about that building.
91246           // For example, a way with `building=yes` + `name=Westfield` may be a Westfield department store.
91247           // But a way with `building=yes` + `name=Westfield` + `public_transport=station` is a train station for a town named "Westfield"
91248
91249           var preset = _mainPresetIndex.matchTags(tags, 'area');
91250
91251           if (buildingPreset[preset.id]) {
91252             alternate.add('building/yes');
91253           }
91254
91255           return {
91256             primary: primary,
91257             alternate: alternate
91258           };
91259         } // `identifyTree()`
91260         // NSI has a concept of trees: "brands", "operators", "flags", "transit".
91261         // The tree determines things like which tags are namelike, and which tags hold important wikidata.
91262         // This takes an Object of tags and tries to identify what tree to use.
91263         //
91264         // Arguments
91265         //   `tags`: `Object` containing the feature's OSM tags
91266         // Returns
91267         //   `string` the name of the tree if known
91268         //   or 'unknown' if it could match several trees (e.g. amenity/yes)
91269         //   or null if no match
91270         //
91271
91272
91273         function identifyTree(tags) {
91274           var unknown;
91275           var t; // Check all tags
91276
91277           Object.keys(tags).forEach(function (osmkey) {
91278             if (t) return; // found already
91279
91280             var osmvalue = tags[osmkey];
91281             if (!osmvalue) return; // Match a 'route_master' as if it were a 'route' - name-suggestion-index#5184
91282
91283             if (osmkey === 'route_master') osmkey = 'route';
91284
91285             var vmap = _nsi.kvt.get(osmkey);
91286
91287             if (!vmap) return; // this key is not in nsi
91288
91289             if (osmvalue === 'yes') {
91290               unknown = 'unknown';
91291             } else {
91292               t = vmap.get(osmvalue);
91293             }
91294           });
91295           return t || unknown || null;
91296         } // `gatherNames()`
91297         // Gather all the namelike values that we will run through the NSI matcher.
91298         // It will gather values primarily from tags `name`, `name:ru`, `flag:name`
91299         //  and fallback to alternate tags like `brand`, `brand:ru`, `alt_name`
91300         //
91301         // Arguments
91302         //   `tags`: `Object` containing the feature's OSM tags
91303         // Returns
91304         //   `Object` containing namelike values to test:
91305         //   {
91306         //     'primary': Set(),
91307         //     'fallbacks': Set()
91308         //   }
91309         //
91310
91311
91312         function gatherNames(tags) {
91313           var empty = {
91314             primary: new Set(),
91315             alternate: new Set()
91316           };
91317           var primary = new Set();
91318           var alternate = new Set();
91319           var foundSemi = false;
91320           var testNameFragments = false;
91321           var patterns; // Patterns for matching OSM keys that might contain namelike values.
91322           // These roughly correspond to the "trees" concept in name-suggestion-index,
91323
91324           var t = identifyTree(tags);
91325           if (!t) return empty;
91326
91327           if (t === 'transit') {
91328             patterns = {
91329               primary: /^network$/i,
91330               alternate: /^(operator|operator:\w+|network:\w+|\w+_name|\w+_name:\w+)$/i
91331             };
91332           } else if (t === 'flags') {
91333             patterns = {
91334               primary: /^(flag:name|flag:name:\w+)$/i,
91335               alternate: /^(flag|flag:\w+|subject|subject:\w+)$/i // note: no `country`, we special-case it below
91336
91337             };
91338           } else if (t === 'brands') {
91339             testNameFragments = true;
91340             patterns = {
91341               primary: /^(name|name:\w+)$/i,
91342               alternate: /^(brand|brand:\w+|operator|operator:\w+|\w+_name|\w+_name:\w+)/i
91343             };
91344           } else if (t === 'operators') {
91345             testNameFragments = true;
91346             patterns = {
91347               primary: /^(name|name:\w+|operator|operator:\w+)$/i,
91348               alternate: /^(brand|brand:\w+|\w+_name|\w+_name:\w+)/i
91349             };
91350           } else {
91351             // unknown/multiple
91352             testNameFragments = true;
91353             patterns = {
91354               primary: /^(name|name:\w+)$/i,
91355               alternate: /^(brand|brand:\w+|network|network:\w+|operator|operator:\w+|\w+_name|\w+_name:\w+)/i
91356             };
91357           } // Test `name` fragments, longest to shortest, to fit them into a "Name Branch" pattern.
91358           // e.g. "TUI ReiseCenter - Neuss Innenstadt" -> ["TUI", "ReiseCenter", "Neuss", "Innenstadt"]
91359
91360
91361           if (tags.name && testNameFragments) {
91362             var nameParts = tags.name.split(/[\s\-\/,.]/);
91363
91364             for (var split = nameParts.length; split > 0; split--) {
91365               var name = nameParts.slice(0, split).join(' '); // e.g. "TUI ReiseCenter"
91366
91367               primary.add(name);
91368             }
91369           } // Check all tags
91370
91371
91372           Object.keys(tags).forEach(function (osmkey) {
91373             var osmvalue = tags[osmkey];
91374             if (!osmvalue) return;
91375
91376             if (isNamelike(osmkey, 'primary')) {
91377               if (/;/.test(osmvalue)) {
91378                 foundSemi = true;
91379               } else {
91380                 primary.add(osmvalue);
91381                 alternate["delete"](osmvalue);
91382               }
91383             } else if (!primary.has(osmvalue) && isNamelike(osmkey, 'alternate')) {
91384               if (/;/.test(osmvalue)) {
91385                 foundSemi = true;
91386               } else {
91387                 alternate.add(osmvalue);
91388               }
91389             }
91390           }); // For flags only, fallback to `country` tag only if no other namelike values were found.
91391           // See https://github.com/openstreetmap/iD/pull/8305#issuecomment-769174070
91392
91393           if (tags.man_made === 'flagpole' && !primary.size && !alternate.size && !!tags.country) {
91394             var osmvalue = tags.country;
91395
91396             if (/;/.test(osmvalue)) {
91397               foundSemi = true;
91398             } else {
91399               alternate.add(osmvalue);
91400             }
91401           } // If any namelike value contained a semicolon, return empty set and don't try matching anything.
91402
91403
91404           if (foundSemi) {
91405             return empty;
91406           } else {
91407             return {
91408               primary: primary,
91409               alternate: alternate
91410             };
91411           }
91412
91413           function isNamelike(osmkey, which) {
91414             if (osmkey === 'old_name') return false;
91415             return patterns[which].test(osmkey) && !notNames.test(osmkey);
91416           }
91417         } // `gatherTuples()`
91418         // Generate all combinations of [key,value,name] that we want to test.
91419         // This prioritizes them so that the primary name and k/v pairs go first
91420         //
91421         // Arguments
91422         //   `tryKVs`: `Object` containing primary and alternate k/v pairs to test
91423         //   `tryNames`: `Object` containing primary and alternate names to test
91424         // Returns
91425         //   `Array`: tuple objects ordered by priority
91426         //
91427
91428
91429         function gatherTuples(tryKVs, tryNames) {
91430           var tuples = [];
91431           ['primary', 'alternate'].forEach(function (whichName) {
91432             // test names longest to shortest
91433             var arr = Array.from(tryNames[whichName]).sort(function (a, b) {
91434               return b.length - a.length;
91435             });
91436             arr.forEach(function (n) {
91437               ['primary', 'alternate'].forEach(function (whichKV) {
91438                 tryKVs[whichKV].forEach(function (kv) {
91439                   var parts = kv.split('/', 2);
91440                   var k = parts[0];
91441                   var v = parts[1];
91442                   tuples.push({
91443                     k: k,
91444                     v: v,
91445                     n: n
91446                   });
91447                 });
91448               });
91449             });
91450           });
91451           return tuples;
91452         } // `_upgradeTags()`
91453         // Try to match a feature to a canonical record in name-suggestion-index
91454         // and upgrade the tags to match.
91455         //
91456         // Arguments
91457         //   `tags`: `Object` containing the feature's OSM tags
91458         //   `loc`: Location where this feature exists, as a [lon, lat]
91459         // Returns
91460         //   `Object`: The tags the the feature should have, or `null` if no changes needed
91461         //
91462
91463
91464         function _upgradeTags(tags, loc) {
91465           var newTags = Object.assign({}, tags); // shallow copy
91466
91467           var changed = false; // Before anything, perform trivial Wikipedia/Wikidata replacements
91468
91469           Object.keys(newTags).forEach(function (osmkey) {
91470             var matchTag = osmkey.match(/^(\w+:)?wikidata$/);
91471
91472             if (matchTag) {
91473               // Look at '*:wikidata' tags
91474               var prefix = matchTag[1] || '';
91475               var wd = newTags[osmkey];
91476               var replace = _nsi.replacements[wd]; // If it matches a QID in the replacement list...
91477
91478               if (replace && replace.wikidata !== undefined) {
91479                 // replace or delete `*:wikidata` tag
91480                 changed = true;
91481
91482                 if (replace.wikidata) {
91483                   newTags[osmkey] = replace.wikidata;
91484                 } else {
91485                   delete newTags[osmkey];
91486                 }
91487               }
91488
91489               if (replace && replace.wikipedia !== undefined) {
91490                 // replace or delete `*:wikipedia` tag
91491                 changed = true;
91492                 var wpkey = "".concat(prefix, "wikipedia");
91493
91494                 if (replace.wikipedia) {
91495                   newTags[wpkey] = replace.wikipedia;
91496                 } else {
91497                   delete newTags[wpkey];
91498                 }
91499               }
91500             }
91501           }); // Match a 'route_master' as if it were a 'route' - name-suggestion-index#5184
91502
91503           var isRouteMaster = tags.type === 'route_master'; // Gather key/value tag pairs to try to match
91504
91505           var tryKVs = gatherKVs(tags);
91506           if (!tryKVs.primary.size && !tryKVs.alternate.size) return changed ? newTags : null; // Gather namelike tag values to try to match
91507
91508           var tryNames = gatherNames(tags); // Do `wikidata=*` or `wikipedia=*` tags identify this entity as a chain? - See #6416
91509           // If so, these tags can be swapped to e.g. `brand:wikidata`/`brand:wikipedia`.
91510
91511           var foundQID = _nsi.qids.get(tags.wikidata) || _nsi.qids.get(tags.wikipedia);
91512
91513           if (foundQID) tryNames.primary.add(foundQID); // matcher will recognize the Wikidata QID as name too
91514
91515           if (!tryNames.primary.size && !tryNames.alternate.size) return changed ? newTags : null; // Order the [key,value,name] tuples - test primary before alternate
91516
91517           var tuples = gatherTuples(tryKVs, tryNames);
91518
91519           var _loop = function _loop(i) {
91520             var tuple = tuples[i];
91521
91522             var hits = _nsi.matcher.match(tuple.k, tuple.v, tuple.n, loc); // Attempt to match an item in NSI
91523
91524
91525             if (!hits || !hits.length) return "continue"; // no match, try next tuple
91526
91527             if (hits[0].match !== 'primary' && hits[0].match !== 'alternate') return "break"; // a generic match, stop looking
91528             // A match may contain multiple results, the first one is likely the best one for this location
91529             // e.g. `['pfk-a54c14', 'kfc-1ff19c', 'kfc-658eea']`
91530
91531             var itemID = void 0,
91532                 item = void 0;
91533
91534             for (var j = 0; j < hits.length; j++) {
91535               var hit = hits[j];
91536               itemID = hit.itemID;
91537               if (_nsi.dissolved[itemID]) continue; // Don't upgrade to a dissolved item
91538
91539               item = _nsi.ids.get(itemID);
91540               if (!item) continue;
91541               var mainTag = item.mainTag; // e.g. `brand:wikidata`
91542
91543               var itemQID = item.tags[mainTag]; // e.g. `brand:wikidata` qid
91544
91545               var notQID = newTags["not:".concat(mainTag)]; // e.g. `not:brand:wikidata` qid
91546
91547               if ( // Exceptions, skip this hit
91548               !itemQID || itemQID === notQID || // No `*:wikidata` or matched a `not:*:wikidata`
91549               newTags.office && !item.tags.office // feature may be a corporate office for a brand? - #6416
91550               ) {
91551                   item = null;
91552                   continue; // continue looking
91553                 } else {
91554                   break; // use `item`
91555                 }
91556             } // Can't use any of these hits, try next tuple..
91557
91558
91559             if (!item) return "continue"; // At this point we have matched a canonical item and can suggest tag upgrades..
91560
91561             var tkv = item.tkv;
91562             var parts = tkv.split('/', 3); // tkv = "tree/key/value"
91563
91564             var k = parts[1];
91565             var v = parts[2];
91566             var category = _nsi.data[tkv];
91567             var properties = category.properties || {}; // Preserve some tags that we specifically don't want NSI to overwrite. ('^name', sometimes)
91568
91569             var preserveTags = item.preserveTags || properties.preserveTags || []; // These tags can be toplevel tags -or- attributes - so we generally want to preserve existing values - #8615
91570             // We'll only _replace_ the tag value if this tag is the toplevel/defining tag for the matched item (`k`)
91571
91572             ['building', 'emergency', 'internet_access', 'takeaway'].forEach(function (osmkey) {
91573               if (k !== osmkey) preserveTags.push("^".concat(osmkey, "$"));
91574             });
91575             var regexes = preserveTags.map(function (s) {
91576               return new RegExp(s, 'i');
91577             });
91578             var keepTags = {};
91579             Object.keys(newTags).forEach(function (osmkey) {
91580               if (regexes.some(function (regex) {
91581                 return regex.test(osmkey);
91582               })) {
91583                 keepTags[osmkey] = newTags[osmkey];
91584               }
91585             }); // Remove any primary tags ("amenity", "craft", "shop", "man_made", "route", etc) that have a
91586             // value like `amenity=yes` or `shop=yes` (exceptions have already been added to `keepTags` above)
91587
91588             _nsi.kvt.forEach(function (vmap, k) {
91589               if (newTags[k] === 'yes') delete newTags[k];
91590             }); // Replace mistagged `wikidata`/`wikipedia` with e.g. `brand:wikidata`/`brand:wikipedia`
91591
91592
91593             if (foundQID) {
91594               delete newTags.wikipedia;
91595               delete newTags.wikidata;
91596             } // Do the tag upgrade
91597
91598
91599             Object.assign(newTags, item.tags, keepTags); // Swap `route` back to `route_master` - name-suggestion-index#5184
91600
91601             if (isRouteMaster) {
91602               newTags.route_master = newTags.route;
91603               delete newTags.route;
91604             } // Special `branch` splitting rules - IF..
91605             // - NSI is suggesting to replace `name`, AND
91606             // - `branch` doesn't already contain something, AND
91607             // - original name has not moved to an alternate name (e.g. "Dunkin' Donuts" -> "Dunkin'"), AND
91608             // - original name is "some name" + "some stuff", THEN
91609             // consider splitting `name` into `name`/`branch`..
91610
91611
91612             var origName = tags.name;
91613             var newName = newTags.name;
91614
91615             if (newName && origName && newName !== origName && !newTags.branch) {
91616               var newNames = gatherNames(newTags);
91617               var newSet = new Set([].concat(_toConsumableArray(newNames.primary), _toConsumableArray(newNames.alternate)));
91618               var isMoved = newSet.has(origName); // another tag holds the original name now
91619
91620               if (!isMoved) {
91621                 // Test name fragments, longest to shortest, to fit them into a "Name Branch" pattern.
91622                 // e.g. "TUI ReiseCenter - Neuss Innenstadt" -> ["TUI", "ReiseCenter", "Neuss", "Innenstadt"]
91623                 var nameParts = origName.split(/[\s\-\/,.]/);
91624
91625                 for (var split = nameParts.length; split > 0; split--) {
91626                   var name = nameParts.slice(0, split).join(' '); // e.g. "TUI ReiseCenter"
91627
91628                   var branch = nameParts.slice(split).join(' '); // e.g. "Neuss Innenstadt"
91629
91630                   var nameHits = _nsi.matcher.match(k, v, name, loc);
91631
91632                   if (!nameHits || !nameHits.length) continue; // no match, try next name fragment
91633
91634                   if (nameHits.some(function (hit) {
91635                     return hit.itemID === itemID;
91636                   })) {
91637                     // matched the name fragment to the same itemID above
91638                     if (branch) {
91639                       if (notBranches.test(branch)) {
91640                         // "branch" was detected but is noise ("factory outlet", etc)
91641                         newTags.name = origName; // Leave `name` alone, this part of the name may be significant..
91642                       } else {
91643                         var branchHits = _nsi.matcher.match(k, v, branch, loc);
91644
91645                         if (branchHits && branchHits.length) {
91646                           // if "branch" matched something else in NSI..
91647                           if (branchHits[0].match === 'primary' || branchHits[0].match === 'alternate') {
91648                             // if another brand! (e.g. "KFC - Taco Bell"?)
91649                             return {
91650                               v: null
91651                             }; //   bail out - can't suggest tags in this case
91652                           } // else a generic (e.g. "gas", "cafe") - ignore
91653
91654                         } else {
91655                           // "branch" is not noise and not something in NSI
91656                           newTags.branch = branch; // Stick it in the `branch` tag..
91657                         }
91658                       }
91659                     }
91660
91661                     break;
91662                   }
91663                 }
91664               }
91665             }
91666
91667             return {
91668               v: newTags
91669             };
91670           };
91671
91672           for (var i = 0; i < tuples.length; i++) {
91673             var _ret = _loop(i);
91674
91675             if (_ret === "continue") continue;
91676             if (_ret === "break") break;
91677             if (_typeof(_ret) === "object") return _ret.v;
91678           }
91679
91680           return changed ? newTags : null;
91681         } // `_isGenericName()`
91682         // Is the `name` tag generic?
91683         //
91684         // Arguments
91685         //   `tags`: `Object` containing the feature's OSM tags
91686         // Returns
91687         //   `true` if it is generic, `false` if not
91688         //
91689
91690
91691         function _isGenericName(tags) {
91692           var n = tags.name;
91693           if (!n) return false; // tryNames just contains the `name` tag value and nothing else
91694
91695           var tryNames = {
91696             primary: new Set([n]),
91697             alternate: new Set()
91698           }; // Gather key/value tag pairs to try to match
91699
91700           var tryKVs = gatherKVs(tags);
91701           if (!tryKVs.primary.size && !tryKVs.alternate.size) return false; // Order the [key,value,name] tuples - test primary before alternate
91702
91703           var tuples = gatherTuples(tryKVs, tryNames);
91704
91705           for (var i = 0; i < tuples.length; i++) {
91706             var tuple = tuples[i];
91707
91708             var hits = _nsi.matcher.match(tuple.k, tuple.v, tuple.n); // Attempt to match an item in NSI
91709             // If we get a `excludeGeneric` hit, this is a generic name.
91710
91711
91712             if (hits && hits.length && hits[0].match === 'excludeGeneric') return true;
91713           }
91714
91715           return false;
91716         } // PUBLIC INTERFACE
91717
91718
91719         var serviceNsi = {
91720           // `init()`
91721           // On init, start preparing the name-suggestion-index
91722           //
91723           init: function init() {
91724             // Note: service.init is called immediately after the presetManager has started loading its data.
91725             // We expect to chain onto an unfulfilled promise here.
91726             setNsiSources();
91727             _mainPresetIndex.ensureLoaded().then(function () {
91728               return loadNsiPresets();
91729             }).then(function () {
91730               return delay(100);
91731             }) // wait briefly for locationSets to enter the locationManager queue
91732             .then(function () {
91733               return _mainLocations.mergeLocationSets([]);
91734             }) // wait for locationSets to resolve
91735             .then(function () {
91736               return loadNsiData();
91737             }).then(function () {
91738               return _nsiStatus = 'ok';
91739             })["catch"](function () {
91740               return _nsiStatus = 'failed';
91741             });
91742
91743             function delay(msec) {
91744               return new Promise(function (resolve) {
91745                 window.setTimeout(resolve, msec);
91746               });
91747             }
91748           },
91749           // `reset()`
91750           // Reset is called when user saves data to OSM (does nothing here)
91751           //
91752           reset: function reset() {},
91753           // `status()`
91754           // To let other code know how it's going...
91755           //
91756           // Returns
91757           //   `String`: 'loading', 'ok', 'failed'
91758           //
91759           status: function status() {
91760             return _nsiStatus;
91761           },
91762           // `isGenericName()`
91763           // Is the `name` tag generic?
91764           //
91765           // Arguments
91766           //   `tags`: `Object` containing the feature's OSM tags
91767           // Returns
91768           //   `true` if it is generic, `false` if not
91769           //
91770           isGenericName: function isGenericName(tags) {
91771             return _isGenericName(tags);
91772           },
91773           // `upgradeTags()`
91774           // Suggest tag upgrades.
91775           // This function will not modify the input tags, it makes a copy.
91776           //
91777           // Arguments
91778           //   `tags`: `Object` containing the feature's OSM tags
91779           //   `loc`: Location where this feature exists, as a [lon, lat]
91780           // Returns
91781           //   `Object`: The tags the the feature should have, or `null` if no change
91782           //
91783           upgradeTags: function upgradeTags(tags, loc) {
91784             return _upgradeTags(tags, loc);
91785           },
91786           // `cache()`
91787           // Direct access to the NSI cache, useful for testing or breaking things
91788           //
91789           // Returns
91790           //   `Object`: the internal NSI cache
91791           //
91792           cache: function cache() {
91793             return _nsi;
91794           }
91795         };
91796
91797         var apibase$1 = 'https://openstreetcam.org';
91798         var maxResults$1 = 1000;
91799         var tileZoom$1 = 14;
91800         var tiler$3 = utilTiler().zoomExtent([tileZoom$1, tileZoom$1]).skipNullIsland(true);
91801         var dispatch$3 = dispatch$8('loadedImages');
91802         var imgZoom = d3_zoom().extent([[0, 0], [320, 240]]).translateExtent([[0, 0], [320, 240]]).scaleExtent([1, 15]);
91803
91804         var _oscCache;
91805
91806         var _oscSelectedImage;
91807
91808         var _loadViewerPromise$1;
91809
91810         function abortRequest$3(controller) {
91811           controller.abort();
91812         }
91813
91814         function maxPageAtZoom(z) {
91815           if (z < 15) return 2;
91816           if (z === 15) return 5;
91817           if (z === 16) return 10;
91818           if (z === 17) return 20;
91819           if (z === 18) return 40;
91820           if (z > 18) return 80;
91821         }
91822
91823         function loadTiles$1(which, url, projection) {
91824           var currZoom = Math.floor(geoScaleToZoom(projection.scale()));
91825           var tiles = tiler$3.getTiles(projection); // abort inflight requests that are no longer needed
91826
91827           var cache = _oscCache[which];
91828           Object.keys(cache.inflight).forEach(function (k) {
91829             var wanted = tiles.find(function (tile) {
91830               return k.indexOf(tile.id + ',') === 0;
91831             });
91832
91833             if (!wanted) {
91834               abortRequest$3(cache.inflight[k]);
91835               delete cache.inflight[k];
91836             }
91837           });
91838           tiles.forEach(function (tile) {
91839             loadNextTilePage$1(which, currZoom, url, tile);
91840           });
91841         }
91842
91843         function loadNextTilePage$1(which, currZoom, url, tile) {
91844           var cache = _oscCache[which];
91845           var bbox = tile.extent.bbox();
91846           var maxPages = maxPageAtZoom(currZoom);
91847           var nextPage = cache.nextPage[tile.id] || 1;
91848           var params = utilQsString({
91849             ipp: maxResults$1,
91850             page: nextPage,
91851             // client_id: clientId,
91852             bbTopLeft: [bbox.maxY, bbox.minX].join(','),
91853             bbBottomRight: [bbox.minY, bbox.maxX].join(',')
91854           }, true);
91855           if (nextPage > maxPages) return;
91856           var id = tile.id + ',' + String(nextPage);
91857           if (cache.loaded[id] || cache.inflight[id]) return;
91858           var controller = new AbortController();
91859           cache.inflight[id] = controller;
91860           var options = {
91861             method: 'POST',
91862             signal: controller.signal,
91863             body: params,
91864             headers: {
91865               'Content-Type': 'application/x-www-form-urlencoded'
91866             }
91867           };
91868           d3_json(url, options).then(function (data) {
91869             cache.loaded[id] = true;
91870             delete cache.inflight[id];
91871
91872             if (!data || !data.currentPageItems || !data.currentPageItems.length) {
91873               throw new Error('No Data');
91874             }
91875
91876             var features = data.currentPageItems.map(function (item) {
91877               var loc = [+item.lng, +item.lat];
91878               var d;
91879
91880               if (which === 'images') {
91881                 d = {
91882                   loc: loc,
91883                   key: item.id,
91884                   ca: +item.heading,
91885                   captured_at: item.shot_date || item.date_added,
91886                   captured_by: item.username,
91887                   imagePath: item.lth_name,
91888                   sequence_id: item.sequence_id,
91889                   sequence_index: +item.sequence_index
91890                 }; // cache sequence info
91891
91892                 var seq = _oscCache.sequences[d.sequence_id];
91893
91894                 if (!seq) {
91895                   seq = {
91896                     rotation: 0,
91897                     images: []
91898                   };
91899                   _oscCache.sequences[d.sequence_id] = seq;
91900                 }
91901
91902                 seq.images[d.sequence_index] = d;
91903                 _oscCache.images.forImageKey[d.key] = d; // cache imageKey -> image
91904               }
91905
91906               return {
91907                 minX: loc[0],
91908                 minY: loc[1],
91909                 maxX: loc[0],
91910                 maxY: loc[1],
91911                 data: d
91912               };
91913             });
91914             cache.rtree.load(features);
91915
91916             if (data.currentPageItems.length === maxResults$1) {
91917               // more pages to load
91918               cache.nextPage[tile.id] = nextPage + 1;
91919               loadNextTilePage$1(which, currZoom, url, tile);
91920             } else {
91921               cache.nextPage[tile.id] = Infinity; // no more pages to load
91922             }
91923
91924             if (which === 'images') {
91925               dispatch$3.call('loadedImages');
91926             }
91927           })["catch"](function () {
91928             cache.loaded[id] = true;
91929             delete cache.inflight[id];
91930           });
91931         } // partition viewport into higher zoom tiles
91932
91933
91934         function partitionViewport$1(projection) {
91935           var z = geoScaleToZoom(projection.scale());
91936           var z2 = Math.ceil(z * 2) / 2 + 2.5; // round to next 0.5 and add 2.5
91937
91938           var tiler = utilTiler().zoomExtent([z2, z2]);
91939           return tiler.getTiles(projection).map(function (tile) {
91940             return tile.extent;
91941           });
91942         } // no more than `limit` results per partition.
91943
91944
91945         function searchLimited$1(limit, projection, rtree) {
91946           limit = limit || 5;
91947           return partitionViewport$1(projection).reduce(function (result, extent) {
91948             var found = rtree.search(extent.bbox()).slice(0, limit).map(function (d) {
91949               return d.data;
91950             });
91951             return found.length ? result.concat(found) : result;
91952           }, []);
91953         }
91954
91955         var serviceOpenstreetcam = {
91956           init: function init() {
91957             if (!_oscCache) {
91958               this.reset();
91959             }
91960
91961             this.event = utilRebind(this, dispatch$3, 'on');
91962           },
91963           reset: function reset() {
91964             if (_oscCache) {
91965               Object.values(_oscCache.images.inflight).forEach(abortRequest$3);
91966             }
91967
91968             _oscCache = {
91969               images: {
91970                 inflight: {},
91971                 loaded: {},
91972                 nextPage: {},
91973                 rtree: new RBush(),
91974                 forImageKey: {}
91975               },
91976               sequences: {}
91977             };
91978             _oscSelectedImage = null;
91979           },
91980           images: function images(projection) {
91981             var limit = 5;
91982             return searchLimited$1(limit, projection, _oscCache.images.rtree);
91983           },
91984           sequences: function sequences(projection) {
91985             var viewport = projection.clipExtent();
91986             var min = [viewport[0][0], viewport[1][1]];
91987             var max = [viewport[1][0], viewport[0][1]];
91988             var bbox = geoExtent(projection.invert(min), projection.invert(max)).bbox();
91989             var sequenceKeys = {}; // all sequences for images in viewport
91990
91991             _oscCache.images.rtree.search(bbox).forEach(function (d) {
91992               sequenceKeys[d.data.sequence_id] = true;
91993             }); // make linestrings from those sequences
91994
91995
91996             var lineStrings = [];
91997             Object.keys(sequenceKeys).forEach(function (sequenceKey) {
91998               var seq = _oscCache.sequences[sequenceKey];
91999               var images = seq && seq.images;
92000
92001               if (images) {
92002                 lineStrings.push({
92003                   type: 'LineString',
92004                   coordinates: images.map(function (d) {
92005                     return d.loc;
92006                   }).filter(Boolean),
92007                   properties: {
92008                     captured_at: images[0] ? images[0].captured_at : null,
92009                     captured_by: images[0] ? images[0].captured_by : null,
92010                     key: sequenceKey
92011                   }
92012                 });
92013               }
92014             });
92015             return lineStrings;
92016           },
92017           cachedImage: function cachedImage(imageKey) {
92018             return _oscCache.images.forImageKey[imageKey];
92019           },
92020           loadImages: function loadImages(projection) {
92021             var url = apibase$1 + '/1.0/list/nearby-photos/';
92022             loadTiles$1('images', url, projection);
92023           },
92024           ensureViewerLoaded: function ensureViewerLoaded(context) {
92025             if (_loadViewerPromise$1) return _loadViewerPromise$1; // add osc-wrapper
92026
92027             var wrap = context.container().select('.photoviewer').selectAll('.osc-wrapper').data([0]);
92028             var that = this;
92029             var wrapEnter = wrap.enter().append('div').attr('class', 'photo-wrapper osc-wrapper').classed('hide', true).call(imgZoom.on('zoom', zoomPan)).on('dblclick.zoom', null);
92030             wrapEnter.append('div').attr('class', 'photo-attribution fillD');
92031             var controlsEnter = wrapEnter.append('div').attr('class', 'photo-controls-wrap').append('div').attr('class', 'photo-controls');
92032             controlsEnter.append('button').on('click.back', step(-1)).html('◄');
92033             controlsEnter.append('button').on('click.rotate-ccw', rotate(-90)).html('⤿');
92034             controlsEnter.append('button').on('click.rotate-cw', rotate(90)).html('⤾');
92035             controlsEnter.append('button').on('click.forward', step(1)).html('►');
92036             wrapEnter.append('div').attr('class', 'osc-image-wrap'); // Register viewer resize handler
92037
92038             context.ui().photoviewer.on('resize.openstreetcam', function (dimensions) {
92039               imgZoom = d3_zoom().extent([[0, 0], dimensions]).translateExtent([[0, 0], dimensions]).scaleExtent([1, 15]).on('zoom', zoomPan);
92040             });
92041
92042             function zoomPan(d3_event) {
92043               var t = d3_event.transform;
92044               context.container().select('.photoviewer .osc-image-wrap').call(utilSetTransform, t.x, t.y, t.k);
92045             }
92046
92047             function rotate(deg) {
92048               return function () {
92049                 if (!_oscSelectedImage) return;
92050                 var sequenceKey = _oscSelectedImage.sequence_id;
92051                 var sequence = _oscCache.sequences[sequenceKey];
92052                 if (!sequence) return;
92053                 var r = sequence.rotation || 0;
92054                 r += deg;
92055                 if (r > 180) r -= 360;
92056                 if (r < -180) r += 360;
92057                 sequence.rotation = r;
92058                 var wrap = context.container().select('.photoviewer .osc-wrapper');
92059                 wrap.transition().duration(100).call(imgZoom.transform, identity$2);
92060                 wrap.selectAll('.osc-image').transition().duration(100).style('transform', 'rotate(' + r + 'deg)');
92061               };
92062             }
92063
92064             function step(stepBy) {
92065               return function () {
92066                 if (!_oscSelectedImage) return;
92067                 var sequenceKey = _oscSelectedImage.sequence_id;
92068                 var sequence = _oscCache.sequences[sequenceKey];
92069                 if (!sequence) return;
92070                 var nextIndex = _oscSelectedImage.sequence_index + stepBy;
92071                 var nextImage = sequence.images[nextIndex];
92072                 if (!nextImage) return;
92073                 context.map().centerEase(nextImage.loc);
92074                 that.selectImage(context, nextImage.key);
92075               };
92076             } // don't need any async loading so resolve immediately
92077
92078
92079             _loadViewerPromise$1 = Promise.resolve();
92080             return _loadViewerPromise$1;
92081           },
92082           showViewer: function showViewer(context) {
92083             var viewer = context.container().select('.photoviewer').classed('hide', false);
92084             var isHidden = viewer.selectAll('.photo-wrapper.osc-wrapper.hide').size();
92085
92086             if (isHidden) {
92087               viewer.selectAll('.photo-wrapper:not(.osc-wrapper)').classed('hide', true);
92088               viewer.selectAll('.photo-wrapper.osc-wrapper').classed('hide', false);
92089             }
92090
92091             return this;
92092           },
92093           hideViewer: function hideViewer(context) {
92094             _oscSelectedImage = null;
92095             this.updateUrlImage(null);
92096             var viewer = context.container().select('.photoviewer');
92097             if (!viewer.empty()) viewer.datum(null);
92098             viewer.classed('hide', true).selectAll('.photo-wrapper').classed('hide', true);
92099             context.container().selectAll('.viewfield-group, .sequence, .icon-sign').classed('currentView', false);
92100             return this.setStyles(context, null, true);
92101           },
92102           selectImage: function selectImage(context, imageKey) {
92103             var d = this.cachedImage(imageKey);
92104             _oscSelectedImage = d;
92105             this.updateUrlImage(imageKey);
92106             var viewer = context.container().select('.photoviewer');
92107             if (!viewer.empty()) viewer.datum(d);
92108             this.setStyles(context, null, true);
92109             context.container().selectAll('.icon-sign').classed('currentView', false);
92110             if (!d) return this;
92111             var wrap = context.container().select('.photoviewer .osc-wrapper');
92112             var imageWrap = wrap.selectAll('.osc-image-wrap');
92113             var attribution = wrap.selectAll('.photo-attribution').html('');
92114             wrap.transition().duration(100).call(imgZoom.transform, identity$2);
92115             imageWrap.selectAll('.osc-image').remove();
92116
92117             if (d) {
92118               var sequence = _oscCache.sequences[d.sequence_id];
92119               var r = sequence && sequence.rotation || 0;
92120               imageWrap.append('img').attr('class', 'osc-image').attr('src', apibase$1 + '/' + d.imagePath).style('transform', 'rotate(' + r + 'deg)');
92121
92122               if (d.captured_by) {
92123                 attribution.append('a').attr('class', 'captured_by').attr('target', '_blank').attr('href', 'https://openstreetcam.org/user/' + encodeURIComponent(d.captured_by)).html('@' + d.captured_by);
92124                 attribution.append('span').html('|');
92125               }
92126
92127               if (d.captured_at) {
92128                 attribution.append('span').attr('class', 'captured_at').html(localeDateString(d.captured_at));
92129                 attribution.append('span').html('|');
92130               }
92131
92132               attribution.append('a').attr('class', 'image-link').attr('target', '_blank').attr('href', 'https://openstreetcam.org/details/' + d.sequence_id + '/' + d.sequence_index).html('openstreetcam.org');
92133             }
92134
92135             return this;
92136
92137             function localeDateString(s) {
92138               if (!s) return null;
92139               var options = {
92140                 day: 'numeric',
92141                 month: 'short',
92142                 year: 'numeric'
92143               };
92144               var d = new Date(s);
92145               if (isNaN(d.getTime())) return null;
92146               return d.toLocaleDateString(_mainLocalizer.localeCode(), options);
92147             }
92148           },
92149           getSelectedImage: function getSelectedImage() {
92150             return _oscSelectedImage;
92151           },
92152           getSequenceKeyForImage: function getSequenceKeyForImage(d) {
92153             return d && d.sequence_id;
92154           },
92155           // Updates the currently highlighted sequence and selected bubble.
92156           // Reset is only necessary when interacting with the viewport because
92157           // this implicitly changes the currently selected bubble/sequence
92158           setStyles: function setStyles(context, hovered, reset) {
92159             if (reset) {
92160               // reset all layers
92161               context.container().selectAll('.viewfield-group').classed('highlighted', false).classed('hovered', false).classed('currentView', false);
92162               context.container().selectAll('.sequence').classed('highlighted', false).classed('currentView', false);
92163             }
92164
92165             var hoveredImageKey = hovered && hovered.key;
92166             var hoveredSequenceKey = this.getSequenceKeyForImage(hovered);
92167             var hoveredSequence = hoveredSequenceKey && _oscCache.sequences[hoveredSequenceKey];
92168             var hoveredImageKeys = hoveredSequence && hoveredSequence.images.map(function (d) {
92169               return d.key;
92170             }) || [];
92171             var viewer = context.container().select('.photoviewer');
92172             var selected = viewer.empty() ? undefined : viewer.datum();
92173             var selectedImageKey = selected && selected.key;
92174             var selectedSequenceKey = this.getSequenceKeyForImage(selected);
92175             var selectedSequence = selectedSequenceKey && _oscCache.sequences[selectedSequenceKey];
92176             var selectedImageKeys = selectedSequence && selectedSequence.images.map(function (d) {
92177               return d.key;
92178             }) || []; // highlight sibling viewfields on either the selected or the hovered sequences
92179
92180             var highlightedImageKeys = utilArrayUnion(hoveredImageKeys, selectedImageKeys);
92181             context.container().selectAll('.layer-openstreetcam .viewfield-group').classed('highlighted', function (d) {
92182               return highlightedImageKeys.indexOf(d.key) !== -1;
92183             }).classed('hovered', function (d) {
92184               return d.key === hoveredImageKey;
92185             }).classed('currentView', function (d) {
92186               return d.key === selectedImageKey;
92187             });
92188             context.container().selectAll('.layer-openstreetcam .sequence').classed('highlighted', function (d) {
92189               return d.properties.key === hoveredSequenceKey;
92190             }).classed('currentView', function (d) {
92191               return d.properties.key === selectedSequenceKey;
92192             }); // update viewfields if needed
92193
92194             context.container().selectAll('.layer-openstreetcam .viewfield-group .viewfield').attr('d', viewfieldPath);
92195
92196             function viewfieldPath() {
92197               var d = this.parentNode.__data__;
92198
92199               if (d.pano && d.key !== selectedImageKey) {
92200                 return 'M 8,13 m -10,0 a 10,10 0 1,0 20,0 a 10,10 0 1,0 -20,0';
92201               } else {
92202                 return 'M 6,9 C 8,8.4 8,8.4 10,9 L 16,-2 C 12,-5 4,-5 0,-2 z';
92203               }
92204             }
92205
92206             return this;
92207           },
92208           updateUrlImage: function updateUrlImage(imageKey) {
92209             if (!window.mocha) {
92210               var hash = utilStringQs(window.location.hash);
92211
92212               if (imageKey) {
92213                 hash.photo = 'openstreetcam/' + imageKey;
92214               } else {
92215                 delete hash.photo;
92216               }
92217
92218               window.location.replace('#' + utilQsString(hash, true));
92219             }
92220           },
92221           cache: function cache() {
92222             return _oscCache;
92223           }
92224         };
92225
92226         var hashes = createCommonjsModule(function (module, exports) {
92227           (function () {
92228             var Hashes;
92229
92230             function utf8Encode(str) {
92231               var x,
92232                   y,
92233                   output = '',
92234                   i = -1,
92235                   l;
92236
92237               if (str && str.length) {
92238                 l = str.length;
92239
92240                 while ((i += 1) < l) {
92241                   /* Decode utf-16 surrogate pairs */
92242                   x = str.charCodeAt(i);
92243                   y = i + 1 < l ? str.charCodeAt(i + 1) : 0;
92244
92245                   if (0xD800 <= x && x <= 0xDBFF && 0xDC00 <= y && y <= 0xDFFF) {
92246                     x = 0x10000 + ((x & 0x03FF) << 10) + (y & 0x03FF);
92247                     i += 1;
92248                   }
92249                   /* Encode output as utf-8 */
92250
92251
92252                   if (x <= 0x7F) {
92253                     output += String.fromCharCode(x);
92254                   } else if (x <= 0x7FF) {
92255                     output += String.fromCharCode(0xC0 | x >>> 6 & 0x1F, 0x80 | x & 0x3F);
92256                   } else if (x <= 0xFFFF) {
92257                     output += String.fromCharCode(0xE0 | x >>> 12 & 0x0F, 0x80 | x >>> 6 & 0x3F, 0x80 | x & 0x3F);
92258                   } else if (x <= 0x1FFFFF) {
92259                     output += String.fromCharCode(0xF0 | x >>> 18 & 0x07, 0x80 | x >>> 12 & 0x3F, 0x80 | x >>> 6 & 0x3F, 0x80 | x & 0x3F);
92260                   }
92261                 }
92262               }
92263
92264               return output;
92265             }
92266
92267             function utf8Decode(str) {
92268               var i,
92269                   ac,
92270                   c1,
92271                   c2,
92272                   c3,
92273                   arr = [],
92274                   l;
92275               i = ac = c1 = c2 = c3 = 0;
92276
92277               if (str && str.length) {
92278                 l = str.length;
92279                 str += '';
92280
92281                 while (i < l) {
92282                   c1 = str.charCodeAt(i);
92283                   ac += 1;
92284
92285                   if (c1 < 128) {
92286                     arr[ac] = String.fromCharCode(c1);
92287                     i += 1;
92288                   } else if (c1 > 191 && c1 < 224) {
92289                     c2 = str.charCodeAt(i + 1);
92290                     arr[ac] = String.fromCharCode((c1 & 31) << 6 | c2 & 63);
92291                     i += 2;
92292                   } else {
92293                     c2 = str.charCodeAt(i + 1);
92294                     c3 = str.charCodeAt(i + 2);
92295                     arr[ac] = String.fromCharCode((c1 & 15) << 12 | (c2 & 63) << 6 | c3 & 63);
92296                     i += 3;
92297                   }
92298                 }
92299               }
92300
92301               return arr.join('');
92302             }
92303             /**
92304              * Add integers, wrapping at 2^32. This uses 16-bit operations internally
92305              * to work around bugs in some JS interpreters.
92306              */
92307
92308
92309             function safe_add(x, y) {
92310               var lsw = (x & 0xFFFF) + (y & 0xFFFF),
92311                   msw = (x >> 16) + (y >> 16) + (lsw >> 16);
92312               return msw << 16 | lsw & 0xFFFF;
92313             }
92314             /**
92315              * Bitwise rotate a 32-bit number to the left.
92316              */
92317
92318
92319             function bit_rol(num, cnt) {
92320               return num << cnt | num >>> 32 - cnt;
92321             }
92322             /**
92323              * Convert a raw string to a hex string
92324              */
92325
92326
92327             function rstr2hex(input, hexcase) {
92328               var hex_tab = hexcase ? '0123456789ABCDEF' : '0123456789abcdef',
92329                   output = '',
92330                   x,
92331                   i = 0,
92332                   l = input.length;
92333
92334               for (; i < l; i += 1) {
92335                 x = input.charCodeAt(i);
92336                 output += hex_tab.charAt(x >>> 4 & 0x0F) + hex_tab.charAt(x & 0x0F);
92337               }
92338
92339               return output;
92340             }
92341             /**
92342              * Convert an array of big-endian words to a string
92343              */
92344
92345
92346             function binb2rstr(input) {
92347               var i,
92348                   l = input.length * 32,
92349                   output = '';
92350
92351               for (i = 0; i < l; i += 8) {
92352                 output += String.fromCharCode(input[i >> 5] >>> 24 - i % 32 & 0xFF);
92353               }
92354
92355               return output;
92356             }
92357             /**
92358              * Convert an array of little-endian words to a string
92359              */
92360
92361
92362             function binl2rstr(input) {
92363               var i,
92364                   l = input.length * 32,
92365                   output = '';
92366
92367               for (i = 0; i < l; i += 8) {
92368                 output += String.fromCharCode(input[i >> 5] >>> i % 32 & 0xFF);
92369               }
92370
92371               return output;
92372             }
92373             /**
92374              * Convert a raw string to an array of little-endian words
92375              * Characters >255 have their high-byte silently ignored.
92376              */
92377
92378
92379             function rstr2binl(input) {
92380               var i,
92381                   l = input.length * 8,
92382                   output = Array(input.length >> 2),
92383                   lo = output.length;
92384
92385               for (i = 0; i < lo; i += 1) {
92386                 output[i] = 0;
92387               }
92388
92389               for (i = 0; i < l; i += 8) {
92390                 output[i >> 5] |= (input.charCodeAt(i / 8) & 0xFF) << i % 32;
92391               }
92392
92393               return output;
92394             }
92395             /**
92396              * Convert a raw string to an array of big-endian words
92397              * Characters >255 have their high-byte silently ignored.
92398              */
92399
92400
92401             function rstr2binb(input) {
92402               var i,
92403                   l = input.length * 8,
92404                   output = Array(input.length >> 2),
92405                   lo = output.length;
92406
92407               for (i = 0; i < lo; i += 1) {
92408                 output[i] = 0;
92409               }
92410
92411               for (i = 0; i < l; i += 8) {
92412                 output[i >> 5] |= (input.charCodeAt(i / 8) & 0xFF) << 24 - i % 32;
92413               }
92414
92415               return output;
92416             }
92417             /**
92418              * Convert a raw string to an arbitrary string encoding
92419              */
92420
92421
92422             function rstr2any(input, encoding) {
92423               var divisor = encoding.length,
92424                   remainders = Array(),
92425                   i,
92426                   q,
92427                   x,
92428                   ld,
92429                   quotient,
92430                   dividend,
92431                   output,
92432                   full_length;
92433               /* Convert to an array of 16-bit big-endian values, forming the dividend */
92434
92435               dividend = Array(Math.ceil(input.length / 2));
92436               ld = dividend.length;
92437
92438               for (i = 0; i < ld; i += 1) {
92439                 dividend[i] = input.charCodeAt(i * 2) << 8 | input.charCodeAt(i * 2 + 1);
92440               }
92441               /**
92442                * Repeatedly perform a long division. The binary array forms the dividend,
92443                * the length of the encoding is the divisor. Once computed, the quotient
92444                * forms the dividend for the next step. We stop when the dividend is zerHashes.
92445                * All remainders are stored for later use.
92446                */
92447
92448
92449               while (dividend.length > 0) {
92450                 quotient = Array();
92451                 x = 0;
92452
92453                 for (i = 0; i < dividend.length; i += 1) {
92454                   x = (x << 16) + dividend[i];
92455                   q = Math.floor(x / divisor);
92456                   x -= q * divisor;
92457
92458                   if (quotient.length > 0 || q > 0) {
92459                     quotient[quotient.length] = q;
92460                   }
92461                 }
92462
92463                 remainders[remainders.length] = x;
92464                 dividend = quotient;
92465               }
92466               /* Convert the remainders to the output string */
92467
92468
92469               output = '';
92470
92471               for (i = remainders.length - 1; i >= 0; i--) {
92472                 output += encoding.charAt(remainders[i]);
92473               }
92474               /* Append leading zero equivalents */
92475
92476
92477               full_length = Math.ceil(input.length * 8 / (Math.log(encoding.length) / Math.log(2)));
92478
92479               for (i = output.length; i < full_length; i += 1) {
92480                 output = encoding[0] + output;
92481               }
92482
92483               return output;
92484             }
92485             /**
92486              * Convert a raw string to a base-64 string
92487              */
92488
92489
92490             function rstr2b64(input, b64pad) {
92491               var tab = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/',
92492                   output = '',
92493                   len = input.length,
92494                   i,
92495                   j,
92496                   triplet;
92497               b64pad = b64pad || '=';
92498
92499               for (i = 0; i < len; i += 3) {
92500                 triplet = input.charCodeAt(i) << 16 | (i + 1 < len ? input.charCodeAt(i + 1) << 8 : 0) | (i + 2 < len ? input.charCodeAt(i + 2) : 0);
92501
92502                 for (j = 0; j < 4; j += 1) {
92503                   if (i * 8 + j * 6 > input.length * 8) {
92504                     output += b64pad;
92505                   } else {
92506                     output += tab.charAt(triplet >>> 6 * (3 - j) & 0x3F);
92507                   }
92508                 }
92509               }
92510
92511               return output;
92512             }
92513
92514             Hashes = {
92515               /**
92516                * @property {String} version
92517                * @readonly
92518                */
92519               VERSION: '1.0.6',
92520
92521               /**
92522                * @member Hashes
92523                * @class Base64
92524                * @constructor
92525                */
92526               Base64: function Base64() {
92527                 // private properties
92528                 var tab = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/',
92529                     pad = '=',
92530                     // URL encoding support @todo
92531                 utf8 = true; // by default enable UTF-8 support encoding
92532                 // public method for encoding
92533
92534                 this.encode = function (input) {
92535                   var i,
92536                       j,
92537                       triplet,
92538                       output = '',
92539                       len = input.length;
92540                   pad = pad || '=';
92541                   input = utf8 ? utf8Encode(input) : input;
92542
92543                   for (i = 0; i < len; i += 3) {
92544                     triplet = input.charCodeAt(i) << 16 | (i + 1 < len ? input.charCodeAt(i + 1) << 8 : 0) | (i + 2 < len ? input.charCodeAt(i + 2) : 0);
92545
92546                     for (j = 0; j < 4; j += 1) {
92547                       if (i * 8 + j * 6 > len * 8) {
92548                         output += pad;
92549                       } else {
92550                         output += tab.charAt(triplet >>> 6 * (3 - j) & 0x3F);
92551                       }
92552                     }
92553                   }
92554
92555                   return output;
92556                 }; // public method for decoding
92557
92558
92559                 this.decode = function (input) {
92560                   // var b64 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
92561                   var i,
92562                       o1,
92563                       o2,
92564                       o3,
92565                       h1,
92566                       h2,
92567                       h3,
92568                       h4,
92569                       bits,
92570                       ac,
92571                       dec = '',
92572                       arr = [];
92573
92574                   if (!input) {
92575                     return input;
92576                   }
92577
92578                   i = ac = 0;
92579                   input = input.replace(new RegExp('\\' + pad, 'gi'), ''); // use '='
92580                   //input += '';
92581
92582                   do {
92583                     // unpack four hexets into three octets using index points in b64
92584                     h1 = tab.indexOf(input.charAt(i += 1));
92585                     h2 = tab.indexOf(input.charAt(i += 1));
92586                     h3 = tab.indexOf(input.charAt(i += 1));
92587                     h4 = tab.indexOf(input.charAt(i += 1));
92588                     bits = h1 << 18 | h2 << 12 | h3 << 6 | h4;
92589                     o1 = bits >> 16 & 0xff;
92590                     o2 = bits >> 8 & 0xff;
92591                     o3 = bits & 0xff;
92592                     ac += 1;
92593
92594                     if (h3 === 64) {
92595                       arr[ac] = String.fromCharCode(o1);
92596                     } else if (h4 === 64) {
92597                       arr[ac] = String.fromCharCode(o1, o2);
92598                     } else {
92599                       arr[ac] = String.fromCharCode(o1, o2, o3);
92600                     }
92601                   } while (i < input.length);
92602
92603                   dec = arr.join('');
92604                   dec = utf8 ? utf8Decode(dec) : dec;
92605                   return dec;
92606                 }; // set custom pad string
92607
92608
92609                 this.setPad = function (str) {
92610                   pad = str || pad;
92611                   return this;
92612                 }; // set custom tab string characters
92613
92614
92615                 this.setTab = function (str) {
92616                   tab = str || tab;
92617                   return this;
92618                 };
92619
92620                 this.setUTF8 = function (bool) {
92621                   if (typeof bool === 'boolean') {
92622                     utf8 = bool;
92623                   }
92624
92625                   return this;
92626                 };
92627               },
92628
92629               /**
92630                * CRC-32 calculation
92631                * @member Hashes
92632                * @method CRC32
92633                * @static
92634                * @param {String} str Input String
92635                * @return {String}
92636                */
92637               CRC32: function CRC32(str) {
92638                 var crc = 0,
92639                     x = 0,
92640                     y = 0,
92641                     table,
92642                     i,
92643                     iTop;
92644                 str = utf8Encode(str);
92645                 table = ['00000000 77073096 EE0E612C 990951BA 076DC419 706AF48F E963A535 9E6495A3 0EDB8832 ', '79DCB8A4 E0D5E91E 97D2D988 09B64C2B 7EB17CBD E7B82D07 90BF1D91 1DB71064 6AB020F2 F3B97148 ', '84BE41DE 1ADAD47D 6DDDE4EB F4D4B551 83D385C7 136C9856 646BA8C0 FD62F97A 8A65C9EC 14015C4F ', '63066CD9 FA0F3D63 8D080DF5 3B6E20C8 4C69105E D56041E4 A2677172 3C03E4D1 4B04D447 D20D85FD ', 'A50AB56B 35B5A8FA 42B2986C DBBBC9D6 ACBCF940 32D86CE3 45DF5C75 DCD60DCF ABD13D59 26D930AC ', '51DE003A C8D75180 BFD06116 21B4F4B5 56B3C423 CFBA9599 B8BDA50F 2802B89E 5F058808 C60CD9B2 ', 'B10BE924 2F6F7C87 58684C11 C1611DAB B6662D3D 76DC4190 01DB7106 98D220BC EFD5102A 71B18589 ', '06B6B51F 9FBFE4A5 E8B8D433 7807C9A2 0F00F934 9609A88E E10E9818 7F6A0DBB 086D3D2D 91646C97 ', 'E6635C01 6B6B51F4 1C6C6162 856530D8 F262004E 6C0695ED 1B01A57B 8208F4C1 F50FC457 65B0D9C6 ', '12B7E950 8BBEB8EA FCB9887C 62DD1DDF 15DA2D49 8CD37CF3 FBD44C65 4DB26158 3AB551CE A3BC0074 ', 'D4BB30E2 4ADFA541 3DD895D7 A4D1C46D D3D6F4FB 4369E96A 346ED9FC AD678846 DA60B8D0 44042D73 ', '33031DE5 AA0A4C5F DD0D7CC9 5005713C 270241AA BE0B1010 C90C2086 5768B525 206F85B3 B966D409 ', 'CE61E49F 5EDEF90E 29D9C998 B0D09822 C7D7A8B4 59B33D17 2EB40D81 B7BD5C3B C0BA6CAD EDB88320 ', '9ABFB3B6 03B6E20C 74B1D29A EAD54739 9DD277AF 04DB2615 73DC1683 E3630B12 94643B84 0D6D6A3E ', '7A6A5AA8 E40ECF0B 9309FF9D 0A00AE27 7D079EB1 F00F9344 8708A3D2 1E01F268 6906C2FE F762575D ', '806567CB 196C3671 6E6B06E7 FED41B76 89D32BE0 10DA7A5A 67DD4ACC F9B9DF6F 8EBEEFF9 17B7BE43 ', '60B08ED5 D6D6A3E8 A1D1937E 38D8C2C4 4FDFF252 D1BB67F1 A6BC5767 3FB506DD 48B2364B D80D2BDA ', 'AF0A1B4C 36034AF6 41047A60 DF60EFC3 A867DF55 316E8EEF 4669BE79 CB61B38C BC66831A 256FD2A0 ', '5268E236 CC0C7795 BB0B4703 220216B9 5505262F C5BA3BBE B2BD0B28 2BB45A92 5CB36A04 C2D7FFA7 ', 'B5D0CF31 2CD99E8B 5BDEAE1D 9B64C2B0 EC63F226 756AA39C 026D930A 9C0906A9 EB0E363F 72076785 ', '05005713 95BF4A82 E2B87A14 7BB12BAE 0CB61B38 92D28E9B E5D5BE0D 7CDCEFB7 0BDBDF21 86D3D2D4 ', 'F1D4E242 68DDB3F8 1FDA836E 81BE16CD F6B9265B 6FB077E1 18B74777 88085AE6 FF0F6A70 66063BCA ', '11010B5C 8F659EFF F862AE69 616BFFD3 166CCF45 A00AE278 D70DD2EE 4E048354 3903B3C2 A7672661 ', 'D06016F7 4969474D 3E6E77DB AED16A4A D9D65ADC 40DF0B66 37D83BF0 A9BCAE53 DEBB9EC5 47B2CF7F ', '30B5FFE9 BDBDF21C CABAC28A 53B39330 24B4A3A6 BAD03605 CDD70693 54DE5729 23D967BF B3667A2E ', 'C4614AB8 5D681B02 2A6F2B94 B40BBE37 C30C8EA1 5A05DF1B 2D02EF8D'].join('');
92646                 crc = crc ^ -1;
92647
92648                 for (i = 0, iTop = str.length; i < iTop; i += 1) {
92649                   y = (crc ^ str.charCodeAt(i)) & 0xFF;
92650                   x = '0x' + table.substr(y * 9, 8);
92651                   crc = crc >>> 8 ^ x;
92652                 } // always return a positive number (that's what >>> 0 does)
92653
92654
92655                 return (crc ^ -1) >>> 0;
92656               },
92657
92658               /**
92659                * @member Hashes
92660                * @class MD5
92661                * @constructor
92662                * @param {Object} [config]
92663                *
92664                * A JavaScript implementation of the RSA Data Security, Inc. MD5 Message
92665                * Digest Algorithm, as defined in RFC 1321.
92666                * Version 2.2 Copyright (C) Paul Johnston 1999 - 2009
92667                * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
92668                * See <http://pajhome.org.uk/crypt/md5> for more infHashes.
92669                */
92670               MD5: function MD5(options) {
92671                 /**
92672                  * Private config properties. You may need to tweak these to be compatible with
92673                  * the server-side, but the defaults work in most cases.
92674                  * See {@link Hashes.MD5#method-setUpperCase} and {@link Hashes.SHA1#method-setUpperCase}
92675                  */
92676                 var hexcase = options && typeof options.uppercase === 'boolean' ? options.uppercase : false,
92677                     // hexadecimal output case format. false - lowercase; true - uppercase
92678                 b64pad = options && typeof options.pad === 'string' ? options.pad : '=',
92679                     // base-64 pad character. Defaults to '=' for strict RFC compliance
92680                 utf8 = options && typeof options.utf8 === 'boolean' ? options.utf8 : true; // enable/disable utf8 encoding
92681                 // privileged (public) methods
92682
92683                 this.hex = function (s) {
92684                   return rstr2hex(rstr(s), hexcase);
92685                 };
92686
92687                 this.b64 = function (s) {
92688                   return rstr2b64(rstr(s), b64pad);
92689                 };
92690
92691                 this.any = function (s, e) {
92692                   return rstr2any(rstr(s), e);
92693                 };
92694
92695                 this.raw = function (s) {
92696                   return rstr(s);
92697                 };
92698
92699                 this.hex_hmac = function (k, d) {
92700                   return rstr2hex(rstr_hmac(k, d), hexcase);
92701                 };
92702
92703                 this.b64_hmac = function (k, d) {
92704                   return rstr2b64(rstr_hmac(k, d), b64pad);
92705                 };
92706
92707                 this.any_hmac = function (k, d, e) {
92708                   return rstr2any(rstr_hmac(k, d), e);
92709                 };
92710                 /**
92711                  * Perform a simple self-test to see if the VM is working
92712                  * @return {String} Hexadecimal hash sample
92713                  */
92714
92715
92716                 this.vm_test = function () {
92717                   return hex('abc').toLowerCase() === '900150983cd24fb0d6963f7d28e17f72';
92718                 };
92719                 /**
92720                  * Enable/disable uppercase hexadecimal returned string
92721                  * @param {Boolean}
92722                  * @return {Object} this
92723                  */
92724
92725
92726                 this.setUpperCase = function (a) {
92727                   if (typeof a === 'boolean') {
92728                     hexcase = a;
92729                   }
92730
92731                   return this;
92732                 };
92733                 /**
92734                  * Defines a base64 pad string
92735                  * @param {String} Pad
92736                  * @return {Object} this
92737                  */
92738
92739
92740                 this.setPad = function (a) {
92741                   b64pad = a || b64pad;
92742                   return this;
92743                 };
92744                 /**
92745                  * Defines a base64 pad string
92746                  * @param {Boolean}
92747                  * @return {Object} [this]
92748                  */
92749
92750
92751                 this.setUTF8 = function (a) {
92752                   if (typeof a === 'boolean') {
92753                     utf8 = a;
92754                   }
92755
92756                   return this;
92757                 }; // private methods
92758
92759                 /**
92760                  * Calculate the MD5 of a raw string
92761                  */
92762
92763
92764                 function rstr(s) {
92765                   s = utf8 ? utf8Encode(s) : s;
92766                   return binl2rstr(binl(rstr2binl(s), s.length * 8));
92767                 }
92768                 /**
92769                  * Calculate the HMAC-MD5, of a key and some data (raw strings)
92770                  */
92771
92772
92773                 function rstr_hmac(key, data) {
92774                   var bkey, ipad, opad, hash, i;
92775                   key = utf8 ? utf8Encode(key) : key;
92776                   data = utf8 ? utf8Encode(data) : data;
92777                   bkey = rstr2binl(key);
92778
92779                   if (bkey.length > 16) {
92780                     bkey = binl(bkey, key.length * 8);
92781                   }
92782
92783                   ipad = Array(16), opad = Array(16);
92784
92785                   for (i = 0; i < 16; i += 1) {
92786                     ipad[i] = bkey[i] ^ 0x36363636;
92787                     opad[i] = bkey[i] ^ 0x5C5C5C5C;
92788                   }
92789
92790                   hash = binl(ipad.concat(rstr2binl(data)), 512 + data.length * 8);
92791                   return binl2rstr(binl(opad.concat(hash), 512 + 128));
92792                 }
92793                 /**
92794                  * Calculate the MD5 of an array of little-endian words, and a bit length.
92795                  */
92796
92797
92798                 function binl(x, len) {
92799                   var i,
92800                       olda,
92801                       oldb,
92802                       oldc,
92803                       oldd,
92804                       a = 1732584193,
92805                       b = -271733879,
92806                       c = -1732584194,
92807                       d = 271733878;
92808                   /* append padding */
92809
92810                   x[len >> 5] |= 0x80 << len % 32;
92811                   x[(len + 64 >>> 9 << 4) + 14] = len;
92812
92813                   for (i = 0; i < x.length; i += 16) {
92814                     olda = a;
92815                     oldb = b;
92816                     oldc = c;
92817                     oldd = d;
92818                     a = md5_ff(a, b, c, d, x[i + 0], 7, -680876936);
92819                     d = md5_ff(d, a, b, c, x[i + 1], 12, -389564586);
92820                     c = md5_ff(c, d, a, b, x[i + 2], 17, 606105819);
92821                     b = md5_ff(b, c, d, a, x[i + 3], 22, -1044525330);
92822                     a = md5_ff(a, b, c, d, x[i + 4], 7, -176418897);
92823                     d = md5_ff(d, a, b, c, x[i + 5], 12, 1200080426);
92824                     c = md5_ff(c, d, a, b, x[i + 6], 17, -1473231341);
92825                     b = md5_ff(b, c, d, a, x[i + 7], 22, -45705983);
92826                     a = md5_ff(a, b, c, d, x[i + 8], 7, 1770035416);
92827                     d = md5_ff(d, a, b, c, x[i + 9], 12, -1958414417);
92828                     c = md5_ff(c, d, a, b, x[i + 10], 17, -42063);
92829                     b = md5_ff(b, c, d, a, x[i + 11], 22, -1990404162);
92830                     a = md5_ff(a, b, c, d, x[i + 12], 7, 1804603682);
92831                     d = md5_ff(d, a, b, c, x[i + 13], 12, -40341101);
92832                     c = md5_ff(c, d, a, b, x[i + 14], 17, -1502002290);
92833                     b = md5_ff(b, c, d, a, x[i + 15], 22, 1236535329);
92834                     a = md5_gg(a, b, c, d, x[i + 1], 5, -165796510);
92835                     d = md5_gg(d, a, b, c, x[i + 6], 9, -1069501632);
92836                     c = md5_gg(c, d, a, b, x[i + 11], 14, 643717713);
92837                     b = md5_gg(b, c, d, a, x[i + 0], 20, -373897302);
92838                     a = md5_gg(a, b, c, d, x[i + 5], 5, -701558691);
92839                     d = md5_gg(d, a, b, c, x[i + 10], 9, 38016083);
92840                     c = md5_gg(c, d, a, b, x[i + 15], 14, -660478335);
92841                     b = md5_gg(b, c, d, a, x[i + 4], 20, -405537848);
92842                     a = md5_gg(a, b, c, d, x[i + 9], 5, 568446438);
92843                     d = md5_gg(d, a, b, c, x[i + 14], 9, -1019803690);
92844                     c = md5_gg(c, d, a, b, x[i + 3], 14, -187363961);
92845                     b = md5_gg(b, c, d, a, x[i + 8], 20, 1163531501);
92846                     a = md5_gg(a, b, c, d, x[i + 13], 5, -1444681467);
92847                     d = md5_gg(d, a, b, c, x[i + 2], 9, -51403784);
92848                     c = md5_gg(c, d, a, b, x[i + 7], 14, 1735328473);
92849                     b = md5_gg(b, c, d, a, x[i + 12], 20, -1926607734);
92850                     a = md5_hh(a, b, c, d, x[i + 5], 4, -378558);
92851                     d = md5_hh(d, a, b, c, x[i + 8], 11, -2022574463);
92852                     c = md5_hh(c, d, a, b, x[i + 11], 16, 1839030562);
92853                     b = md5_hh(b, c, d, a, x[i + 14], 23, -35309556);
92854                     a = md5_hh(a, b, c, d, x[i + 1], 4, -1530992060);
92855                     d = md5_hh(d, a, b, c, x[i + 4], 11, 1272893353);
92856                     c = md5_hh(c, d, a, b, x[i + 7], 16, -155497632);
92857                     b = md5_hh(b, c, d, a, x[i + 10], 23, -1094730640);
92858                     a = md5_hh(a, b, c, d, x[i + 13], 4, 681279174);
92859                     d = md5_hh(d, a, b, c, x[i + 0], 11, -358537222);
92860                     c = md5_hh(c, d, a, b, x[i + 3], 16, -722521979);
92861                     b = md5_hh(b, c, d, a, x[i + 6], 23, 76029189);
92862                     a = md5_hh(a, b, c, d, x[i + 9], 4, -640364487);
92863                     d = md5_hh(d, a, b, c, x[i + 12], 11, -421815835);
92864                     c = md5_hh(c, d, a, b, x[i + 15], 16, 530742520);
92865                     b = md5_hh(b, c, d, a, x[i + 2], 23, -995338651);
92866                     a = md5_ii(a, b, c, d, x[i + 0], 6, -198630844);
92867                     d = md5_ii(d, a, b, c, x[i + 7], 10, 1126891415);
92868                     c = md5_ii(c, d, a, b, x[i + 14], 15, -1416354905);
92869                     b = md5_ii(b, c, d, a, x[i + 5], 21, -57434055);
92870                     a = md5_ii(a, b, c, d, x[i + 12], 6, 1700485571);
92871                     d = md5_ii(d, a, b, c, x[i + 3], 10, -1894986606);
92872                     c = md5_ii(c, d, a, b, x[i + 10], 15, -1051523);
92873                     b = md5_ii(b, c, d, a, x[i + 1], 21, -2054922799);
92874                     a = md5_ii(a, b, c, d, x[i + 8], 6, 1873313359);
92875                     d = md5_ii(d, a, b, c, x[i + 15], 10, -30611744);
92876                     c = md5_ii(c, d, a, b, x[i + 6], 15, -1560198380);
92877                     b = md5_ii(b, c, d, a, x[i + 13], 21, 1309151649);
92878                     a = md5_ii(a, b, c, d, x[i + 4], 6, -145523070);
92879                     d = md5_ii(d, a, b, c, x[i + 11], 10, -1120210379);
92880                     c = md5_ii(c, d, a, b, x[i + 2], 15, 718787259);
92881                     b = md5_ii(b, c, d, a, x[i + 9], 21, -343485551);
92882                     a = safe_add(a, olda);
92883                     b = safe_add(b, oldb);
92884                     c = safe_add(c, oldc);
92885                     d = safe_add(d, oldd);
92886                   }
92887
92888                   return Array(a, b, c, d);
92889                 }
92890                 /**
92891                  * These functions implement the four basic operations the algorithm uses.
92892                  */
92893
92894
92895                 function md5_cmn(q, a, b, x, s, t) {
92896                   return safe_add(bit_rol(safe_add(safe_add(a, q), safe_add(x, t)), s), b);
92897                 }
92898
92899                 function md5_ff(a, b, c, d, x, s, t) {
92900                   return md5_cmn(b & c | ~b & d, a, b, x, s, t);
92901                 }
92902
92903                 function md5_gg(a, b, c, d, x, s, t) {
92904                   return md5_cmn(b & d | c & ~d, a, b, x, s, t);
92905                 }
92906
92907                 function md5_hh(a, b, c, d, x, s, t) {
92908                   return md5_cmn(b ^ c ^ d, a, b, x, s, t);
92909                 }
92910
92911                 function md5_ii(a, b, c, d, x, s, t) {
92912                   return md5_cmn(c ^ (b | ~d), a, b, x, s, t);
92913                 }
92914               },
92915
92916               /**
92917                * @member Hashes
92918                * @class Hashes.SHA1
92919                * @param {Object} [config]
92920                * @constructor
92921                *
92922                * A JavaScript implementation of the Secure Hash Algorithm, SHA-1, as defined in FIPS 180-1
92923                * Version 2.2 Copyright Paul Johnston 2000 - 2009.
92924                * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
92925                * See http://pajhome.org.uk/crypt/md5 for details.
92926                */
92927               SHA1: function SHA1(options) {
92928                 /**
92929                  * Private config properties. You may need to tweak these to be compatible with
92930                  * the server-side, but the defaults work in most cases.
92931                  * See {@link Hashes.MD5#method-setUpperCase} and {@link Hashes.SHA1#method-setUpperCase}
92932                  */
92933                 var hexcase = options && typeof options.uppercase === 'boolean' ? options.uppercase : false,
92934                     // hexadecimal output case format. false - lowercase; true - uppercase
92935                 b64pad = options && typeof options.pad === 'string' ? options.pad : '=',
92936                     // base-64 pad character. Defaults to '=' for strict RFC compliance
92937                 utf8 = options && typeof options.utf8 === 'boolean' ? options.utf8 : true; // enable/disable utf8 encoding
92938                 // public methods
92939
92940                 this.hex = function (s) {
92941                   return rstr2hex(rstr(s), hexcase);
92942                 };
92943
92944                 this.b64 = function (s) {
92945                   return rstr2b64(rstr(s), b64pad);
92946                 };
92947
92948                 this.any = function (s, e) {
92949                   return rstr2any(rstr(s), e);
92950                 };
92951
92952                 this.raw = function (s) {
92953                   return rstr(s);
92954                 };
92955
92956                 this.hex_hmac = function (k, d) {
92957                   return rstr2hex(rstr_hmac(k, d));
92958                 };
92959
92960                 this.b64_hmac = function (k, d) {
92961                   return rstr2b64(rstr_hmac(k, d), b64pad);
92962                 };
92963
92964                 this.any_hmac = function (k, d, e) {
92965                   return rstr2any(rstr_hmac(k, d), e);
92966                 };
92967                 /**
92968                  * Perform a simple self-test to see if the VM is working
92969                  * @return {String} Hexadecimal hash sample
92970                  * @public
92971                  */
92972
92973
92974                 this.vm_test = function () {
92975                   return hex('abc').toLowerCase() === '900150983cd24fb0d6963f7d28e17f72';
92976                 };
92977                 /**
92978                  * @description Enable/disable uppercase hexadecimal returned string
92979                  * @param {boolean}
92980                  * @return {Object} this
92981                  * @public
92982                  */
92983
92984
92985                 this.setUpperCase = function (a) {
92986                   if (typeof a === 'boolean') {
92987                     hexcase = a;
92988                   }
92989
92990                   return this;
92991                 };
92992                 /**
92993                  * @description Defines a base64 pad string
92994                  * @param {string} Pad
92995                  * @return {Object} this
92996                  * @public
92997                  */
92998
92999
93000                 this.setPad = function (a) {
93001                   b64pad = a || b64pad;
93002                   return this;
93003                 };
93004                 /**
93005                  * @description Defines a base64 pad string
93006                  * @param {boolean}
93007                  * @return {Object} this
93008                  * @public
93009                  */
93010
93011
93012                 this.setUTF8 = function (a) {
93013                   if (typeof a === 'boolean') {
93014                     utf8 = a;
93015                   }
93016
93017                   return this;
93018                 }; // private methods
93019
93020                 /**
93021                  * Calculate the SHA-512 of a raw string
93022                  */
93023
93024
93025                 function rstr(s) {
93026                   s = utf8 ? utf8Encode(s) : s;
93027                   return binb2rstr(binb(rstr2binb(s), s.length * 8));
93028                 }
93029                 /**
93030                  * Calculate the HMAC-SHA1 of a key and some data (raw strings)
93031                  */
93032
93033
93034                 function rstr_hmac(key, data) {
93035                   var bkey, ipad, opad, i, hash;
93036                   key = utf8 ? utf8Encode(key) : key;
93037                   data = utf8 ? utf8Encode(data) : data;
93038                   bkey = rstr2binb(key);
93039
93040                   if (bkey.length > 16) {
93041                     bkey = binb(bkey, key.length * 8);
93042                   }
93043
93044                   ipad = Array(16), opad = Array(16);
93045
93046                   for (i = 0; i < 16; i += 1) {
93047                     ipad[i] = bkey[i] ^ 0x36363636;
93048                     opad[i] = bkey[i] ^ 0x5C5C5C5C;
93049                   }
93050
93051                   hash = binb(ipad.concat(rstr2binb(data)), 512 + data.length * 8);
93052                   return binb2rstr(binb(opad.concat(hash), 512 + 160));
93053                 }
93054                 /**
93055                  * Calculate the SHA-1 of an array of big-endian words, and a bit length
93056                  */
93057
93058
93059                 function binb(x, len) {
93060                   var i,
93061                       j,
93062                       t,
93063                       olda,
93064                       oldb,
93065                       oldc,
93066                       oldd,
93067                       olde,
93068                       w = Array(80),
93069                       a = 1732584193,
93070                       b = -271733879,
93071                       c = -1732584194,
93072                       d = 271733878,
93073                       e = -1009589776;
93074                   /* append padding */
93075
93076                   x[len >> 5] |= 0x80 << 24 - len % 32;
93077                   x[(len + 64 >> 9 << 4) + 15] = len;
93078
93079                   for (i = 0; i < x.length; i += 16) {
93080                     olda = a;
93081                     oldb = b;
93082                     oldc = c;
93083                     oldd = d;
93084                     olde = e;
93085
93086                     for (j = 0; j < 80; j += 1) {
93087                       if (j < 16) {
93088                         w[j] = x[i + j];
93089                       } else {
93090                         w[j] = bit_rol(w[j - 3] ^ w[j - 8] ^ w[j - 14] ^ w[j - 16], 1);
93091                       }
93092
93093                       t = safe_add(safe_add(bit_rol(a, 5), sha1_ft(j, b, c, d)), safe_add(safe_add(e, w[j]), sha1_kt(j)));
93094                       e = d;
93095                       d = c;
93096                       c = bit_rol(b, 30);
93097                       b = a;
93098                       a = t;
93099                     }
93100
93101                     a = safe_add(a, olda);
93102                     b = safe_add(b, oldb);
93103                     c = safe_add(c, oldc);
93104                     d = safe_add(d, oldd);
93105                     e = safe_add(e, olde);
93106                   }
93107
93108                   return Array(a, b, c, d, e);
93109                 }
93110                 /**
93111                  * Perform the appropriate triplet combination function for the current
93112                  * iteration
93113                  */
93114
93115
93116                 function sha1_ft(t, b, c, d) {
93117                   if (t < 20) {
93118                     return b & c | ~b & d;
93119                   }
93120
93121                   if (t < 40) {
93122                     return b ^ c ^ d;
93123                   }
93124
93125                   if (t < 60) {
93126                     return b & c | b & d | c & d;
93127                   }
93128
93129                   return b ^ c ^ d;
93130                 }
93131                 /**
93132                  * Determine the appropriate additive constant for the current iteration
93133                  */
93134
93135
93136                 function sha1_kt(t) {
93137                   return t < 20 ? 1518500249 : t < 40 ? 1859775393 : t < 60 ? -1894007588 : -899497514;
93138                 }
93139               },
93140
93141               /**
93142                * @class Hashes.SHA256
93143                * @param {config}
93144                *
93145                * A JavaScript implementation of the Secure Hash Algorithm, SHA-256, as defined in FIPS 180-2
93146                * Version 2.2 Copyright Angel Marin, Paul Johnston 2000 - 2009.
93147                * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
93148                * See http://pajhome.org.uk/crypt/md5 for details.
93149                * Also http://anmar.eu.org/projects/jssha2/
93150                */
93151               SHA256: function SHA256(options) {
93152                 /**
93153                  * Private properties configuration variables. You may need to tweak these to be compatible with
93154                  * the server-side, but the defaults work in most cases.
93155                  * @see this.setUpperCase() method
93156                  * @see this.setPad() method
93157                  */
93158                 options && typeof options.uppercase === 'boolean' ? options.uppercase : false;
93159                     var // hexadecimal output case format. false - lowercase; true - uppercase  */
93160                 b64pad = options && typeof options.pad === 'string' ? options.pad : '=',
93161
93162                 /* base-64 pad character. Default '=' for strict RFC compliance   */
93163                 utf8 = options && typeof options.utf8 === 'boolean' ? options.utf8 : true,
93164
93165                 /* enable/disable utf8 encoding */
93166                 sha256_K;
93167                 /* privileged (public) methods */
93168
93169                 this.hex = function (s) {
93170                   return rstr2hex(rstr(s, utf8));
93171                 };
93172
93173                 this.b64 = function (s) {
93174                   return rstr2b64(rstr(s, utf8), b64pad);
93175                 };
93176
93177                 this.any = function (s, e) {
93178                   return rstr2any(rstr(s, utf8), e);
93179                 };
93180
93181                 this.raw = function (s) {
93182                   return rstr(s, utf8);
93183                 };
93184
93185                 this.hex_hmac = function (k, d) {
93186                   return rstr2hex(rstr_hmac(k, d));
93187                 };
93188
93189                 this.b64_hmac = function (k, d) {
93190                   return rstr2b64(rstr_hmac(k, d), b64pad);
93191                 };
93192
93193                 this.any_hmac = function (k, d, e) {
93194                   return rstr2any(rstr_hmac(k, d), e);
93195                 };
93196                 /**
93197                  * Perform a simple self-test to see if the VM is working
93198                  * @return {String} Hexadecimal hash sample
93199                  * @public
93200                  */
93201
93202
93203                 this.vm_test = function () {
93204                   return hex('abc').toLowerCase() === '900150983cd24fb0d6963f7d28e17f72';
93205                 };
93206                 /**
93207                  * Enable/disable uppercase hexadecimal returned string
93208                  * @param {boolean}
93209                  * @return {Object} this
93210                  * @public
93211                  */
93212
93213
93214                 this.setUpperCase = function (a) {
93215
93216                   return this;
93217                 };
93218                 /**
93219                  * @description Defines a base64 pad string
93220                  * @param {string} Pad
93221                  * @return {Object} this
93222                  * @public
93223                  */
93224
93225
93226                 this.setPad = function (a) {
93227                   b64pad = a || b64pad;
93228                   return this;
93229                 };
93230                 /**
93231                  * Defines a base64 pad string
93232                  * @param {boolean}
93233                  * @return {Object} this
93234                  * @public
93235                  */
93236
93237
93238                 this.setUTF8 = function (a) {
93239                   if (typeof a === 'boolean') {
93240                     utf8 = a;
93241                   }
93242
93243                   return this;
93244                 }; // private methods
93245
93246                 /**
93247                  * Calculate the SHA-512 of a raw string
93248                  */
93249
93250
93251                 function rstr(s, utf8) {
93252                   s = utf8 ? utf8Encode(s) : s;
93253                   return binb2rstr(binb(rstr2binb(s), s.length * 8));
93254                 }
93255                 /**
93256                  * Calculate the HMAC-sha256 of a key and some data (raw strings)
93257                  */
93258
93259
93260                 function rstr_hmac(key, data) {
93261                   key = utf8 ? utf8Encode(key) : key;
93262                   data = utf8 ? utf8Encode(data) : data;
93263                   var hash,
93264                       i = 0,
93265                       bkey = rstr2binb(key),
93266                       ipad = Array(16),
93267                       opad = Array(16);
93268
93269                   if (bkey.length > 16) {
93270                     bkey = binb(bkey, key.length * 8);
93271                   }
93272
93273                   for (; i < 16; i += 1) {
93274                     ipad[i] = bkey[i] ^ 0x36363636;
93275                     opad[i] = bkey[i] ^ 0x5C5C5C5C;
93276                   }
93277
93278                   hash = binb(ipad.concat(rstr2binb(data)), 512 + data.length * 8);
93279                   return binb2rstr(binb(opad.concat(hash), 512 + 256));
93280                 }
93281                 /*
93282                  * Main sha256 function, with its support functions
93283                  */
93284
93285
93286                 function sha256_S(X, n) {
93287                   return X >>> n | X << 32 - n;
93288                 }
93289
93290                 function sha256_R(X, n) {
93291                   return X >>> n;
93292                 }
93293
93294                 function sha256_Ch(x, y, z) {
93295                   return x & y ^ ~x & z;
93296                 }
93297
93298                 function sha256_Maj(x, y, z) {
93299                   return x & y ^ x & z ^ y & z;
93300                 }
93301
93302                 function sha256_Sigma0256(x) {
93303                   return sha256_S(x, 2) ^ sha256_S(x, 13) ^ sha256_S(x, 22);
93304                 }
93305
93306                 function sha256_Sigma1256(x) {
93307                   return sha256_S(x, 6) ^ sha256_S(x, 11) ^ sha256_S(x, 25);
93308                 }
93309
93310                 function sha256_Gamma0256(x) {
93311                   return sha256_S(x, 7) ^ sha256_S(x, 18) ^ sha256_R(x, 3);
93312                 }
93313
93314                 function sha256_Gamma1256(x) {
93315                   return sha256_S(x, 17) ^ sha256_S(x, 19) ^ sha256_R(x, 10);
93316                 }
93317
93318                 sha256_K = [1116352408, 1899447441, -1245643825, -373957723, 961987163, 1508970993, -1841331548, -1424204075, -670586216, 310598401, 607225278, 1426881987, 1925078388, -2132889090, -1680079193, -1046744716, -459576895, -272742522, 264347078, 604807628, 770255983, 1249150122, 1555081692, 1996064986, -1740746414, -1473132947, -1341970488, -1084653625, -958395405, -710438585, 113926993, 338241895, 666307205, 773529912, 1294757372, 1396182291, 1695183700, 1986661051, -2117940946, -1838011259, -1564481375, -1474664885, -1035236496, -949202525, -778901479, -694614492, -200395387, 275423344, 430227734, 506948616, 659060556, 883997877, 958139571, 1322822218, 1537002063, 1747873779, 1955562222, 2024104815, -2067236844, -1933114872, -1866530822, -1538233109, -1090935817, -965641998];
93319
93320                 function binb(m, l) {
93321                   var HASH = [1779033703, -1150833019, 1013904242, -1521486534, 1359893119, -1694144372, 528734635, 1541459225];
93322                   var W = new Array(64);
93323                   var a, b, c, d, e, f, g, h;
93324                   var i, j, T1, T2;
93325                   /* append padding */
93326
93327                   m[l >> 5] |= 0x80 << 24 - l % 32;
93328                   m[(l + 64 >> 9 << 4) + 15] = l;
93329
93330                   for (i = 0; i < m.length; i += 16) {
93331                     a = HASH[0];
93332                     b = HASH[1];
93333                     c = HASH[2];
93334                     d = HASH[3];
93335                     e = HASH[4];
93336                     f = HASH[5];
93337                     g = HASH[6];
93338                     h = HASH[7];
93339
93340                     for (j = 0; j < 64; j += 1) {
93341                       if (j < 16) {
93342                         W[j] = m[j + i];
93343                       } else {
93344                         W[j] = safe_add(safe_add(safe_add(sha256_Gamma1256(W[j - 2]), W[j - 7]), sha256_Gamma0256(W[j - 15])), W[j - 16]);
93345                       }
93346
93347                       T1 = safe_add(safe_add(safe_add(safe_add(h, sha256_Sigma1256(e)), sha256_Ch(e, f, g)), sha256_K[j]), W[j]);
93348                       T2 = safe_add(sha256_Sigma0256(a), sha256_Maj(a, b, c));
93349                       h = g;
93350                       g = f;
93351                       f = e;
93352                       e = safe_add(d, T1);
93353                       d = c;
93354                       c = b;
93355                       b = a;
93356                       a = safe_add(T1, T2);
93357                     }
93358
93359                     HASH[0] = safe_add(a, HASH[0]);
93360                     HASH[1] = safe_add(b, HASH[1]);
93361                     HASH[2] = safe_add(c, HASH[2]);
93362                     HASH[3] = safe_add(d, HASH[3]);
93363                     HASH[4] = safe_add(e, HASH[4]);
93364                     HASH[5] = safe_add(f, HASH[5]);
93365                     HASH[6] = safe_add(g, HASH[6]);
93366                     HASH[7] = safe_add(h, HASH[7]);
93367                   }
93368
93369                   return HASH;
93370                 }
93371               },
93372
93373               /**
93374                * @class Hashes.SHA512
93375                * @param {config}
93376                *
93377                * A JavaScript implementation of the Secure Hash Algorithm, SHA-512, as defined in FIPS 180-2
93378                * Version 2.2 Copyright Anonymous Contributor, Paul Johnston 2000 - 2009.
93379                * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
93380                * See http://pajhome.org.uk/crypt/md5 for details.
93381                */
93382               SHA512: function SHA512(options) {
93383                 /**
93384                  * Private properties configuration variables. You may need to tweak these to be compatible with
93385                  * the server-side, but the defaults work in most cases.
93386                  * @see this.setUpperCase() method
93387                  * @see this.setPad() method
93388                  */
93389                 options && typeof options.uppercase === 'boolean' ? options.uppercase : false;
93390
93391                 var /* hexadecimal output case format. false - lowercase; true - uppercase  */
93392                 b64pad = options && typeof options.pad === 'string' ? options.pad : '=',
93393
93394                 /* base-64 pad character. Default '=' for strict RFC compliance   */
93395                 utf8 = options && typeof options.utf8 === 'boolean' ? options.utf8 : true,
93396
93397                 /* enable/disable utf8 encoding */
93398                 sha512_k;
93399                 /* privileged (public) methods */
93400
93401                 this.hex = function (s) {
93402                   return rstr2hex(rstr(s));
93403                 };
93404
93405                 this.b64 = function (s) {
93406                   return rstr2b64(rstr(s), b64pad);
93407                 };
93408
93409                 this.any = function (s, e) {
93410                   return rstr2any(rstr(s), e);
93411                 };
93412
93413                 this.raw = function (s) {
93414                   return rstr(s);
93415                 };
93416
93417                 this.hex_hmac = function (k, d) {
93418                   return rstr2hex(rstr_hmac(k, d));
93419                 };
93420
93421                 this.b64_hmac = function (k, d) {
93422                   return rstr2b64(rstr_hmac(k, d), b64pad);
93423                 };
93424
93425                 this.any_hmac = function (k, d, e) {
93426                   return rstr2any(rstr_hmac(k, d), e);
93427                 };
93428                 /**
93429                  * Perform a simple self-test to see if the VM is working
93430                  * @return {String} Hexadecimal hash sample
93431                  * @public
93432                  */
93433
93434
93435                 this.vm_test = function () {
93436                   return hex('abc').toLowerCase() === '900150983cd24fb0d6963f7d28e17f72';
93437                 };
93438                 /**
93439                  * @description Enable/disable uppercase hexadecimal returned string
93440                  * @param {boolean}
93441                  * @return {Object} this
93442                  * @public
93443                  */
93444
93445
93446                 this.setUpperCase = function (a) {
93447
93448                   return this;
93449                 };
93450                 /**
93451                  * @description Defines a base64 pad string
93452                  * @param {string} Pad
93453                  * @return {Object} this
93454                  * @public
93455                  */
93456
93457
93458                 this.setPad = function (a) {
93459                   b64pad = a || b64pad;
93460                   return this;
93461                 };
93462                 /**
93463                  * @description Defines a base64 pad string
93464                  * @param {boolean}
93465                  * @return {Object} this
93466                  * @public
93467                  */
93468
93469
93470                 this.setUTF8 = function (a) {
93471                   if (typeof a === 'boolean') {
93472                     utf8 = a;
93473                   }
93474
93475                   return this;
93476                 };
93477                 /* private methods */
93478
93479                 /**
93480                  * Calculate the SHA-512 of a raw string
93481                  */
93482
93483
93484                 function rstr(s) {
93485                   s = utf8 ? utf8Encode(s) : s;
93486                   return binb2rstr(binb(rstr2binb(s), s.length * 8));
93487                 }
93488                 /*
93489                  * Calculate the HMAC-SHA-512 of a key and some data (raw strings)
93490                  */
93491
93492
93493                 function rstr_hmac(key, data) {
93494                   key = utf8 ? utf8Encode(key) : key;
93495                   data = utf8 ? utf8Encode(data) : data;
93496                   var hash,
93497                       i = 0,
93498                       bkey = rstr2binb(key),
93499                       ipad = Array(32),
93500                       opad = Array(32);
93501
93502                   if (bkey.length > 32) {
93503                     bkey = binb(bkey, key.length * 8);
93504                   }
93505
93506                   for (; i < 32; i += 1) {
93507                     ipad[i] = bkey[i] ^ 0x36363636;
93508                     opad[i] = bkey[i] ^ 0x5C5C5C5C;
93509                   }
93510
93511                   hash = binb(ipad.concat(rstr2binb(data)), 1024 + data.length * 8);
93512                   return binb2rstr(binb(opad.concat(hash), 1024 + 512));
93513                 }
93514                 /**
93515                  * Calculate the SHA-512 of an array of big-endian dwords, and a bit length
93516                  */
93517
93518
93519                 function binb(x, len) {
93520                   var j,
93521                       i,
93522                       l,
93523                       W = new Array(80),
93524                       hash = new Array(16),
93525                       //Initial hash values
93526                   H = [new int64(0x6a09e667, -205731576), new int64(-1150833019, -2067093701), new int64(0x3c6ef372, -23791573), new int64(-1521486534, 0x5f1d36f1), new int64(0x510e527f, -1377402159), new int64(-1694144372, 0x2b3e6c1f), new int64(0x1f83d9ab, -79577749), new int64(0x5be0cd19, 0x137e2179)],
93527                       T1 = new int64(0, 0),
93528                       T2 = new int64(0, 0),
93529                       a = new int64(0, 0),
93530                       b = new int64(0, 0),
93531                       c = new int64(0, 0),
93532                       d = new int64(0, 0),
93533                       e = new int64(0, 0),
93534                       f = new int64(0, 0),
93535                       g = new int64(0, 0),
93536                       h = new int64(0, 0),
93537                       //Temporary variables not specified by the document
93538                   s0 = new int64(0, 0),
93539                       s1 = new int64(0, 0),
93540                       Ch = new int64(0, 0),
93541                       Maj = new int64(0, 0),
93542                       r1 = new int64(0, 0),
93543                       r2 = new int64(0, 0),
93544                       r3 = new int64(0, 0);
93545
93546                   if (sha512_k === undefined) {
93547                     //SHA512 constants
93548                     sha512_k = [new int64(0x428a2f98, -685199838), new int64(0x71374491, 0x23ef65cd), new int64(-1245643825, -330482897), new int64(-373957723, -2121671748), new int64(0x3956c25b, -213338824), new int64(0x59f111f1, -1241133031), new int64(-1841331548, -1357295717), new int64(-1424204075, -630357736), new int64(-670586216, -1560083902), new int64(0x12835b01, 0x45706fbe), new int64(0x243185be, 0x4ee4b28c), new int64(0x550c7dc3, -704662302), new int64(0x72be5d74, -226784913), new int64(-2132889090, 0x3b1696b1), new int64(-1680079193, 0x25c71235), new int64(-1046744716, -815192428), new int64(-459576895, -1628353838), new int64(-272742522, 0x384f25e3), new int64(0xfc19dc6, -1953704523), new int64(0x240ca1cc, 0x77ac9c65), new int64(0x2de92c6f, 0x592b0275), new int64(0x4a7484aa, 0x6ea6e483), new int64(0x5cb0a9dc, -1119749164), new int64(0x76f988da, -2096016459), new int64(-1740746414, -295247957), new int64(-1473132947, 0x2db43210), new int64(-1341970488, -1728372417), new int64(-1084653625, -1091629340), new int64(-958395405, 0x3da88fc2), new int64(-710438585, -1828018395), new int64(0x6ca6351, -536640913), new int64(0x14292967, 0xa0e6e70), new int64(0x27b70a85, 0x46d22ffc), new int64(0x2e1b2138, 0x5c26c926), new int64(0x4d2c6dfc, 0x5ac42aed), new int64(0x53380d13, -1651133473), new int64(0x650a7354, -1951439906), new int64(0x766a0abb, 0x3c77b2a8), new int64(-2117940946, 0x47edaee6), new int64(-1838011259, 0x1482353b), new int64(-1564481375, 0x4cf10364), new int64(-1474664885, -1136513023), new int64(-1035236496, -789014639), new int64(-949202525, 0x654be30), new int64(-778901479, -688958952), new int64(-694614492, 0x5565a910), new int64(-200395387, 0x5771202a), new int64(0x106aa070, 0x32bbd1b8), new int64(0x19a4c116, -1194143544), new int64(0x1e376c08, 0x5141ab53), new int64(0x2748774c, -544281703), new int64(0x34b0bcb5, -509917016), new int64(0x391c0cb3, -976659869), new int64(0x4ed8aa4a, -482243893), new int64(0x5b9cca4f, 0x7763e373), new int64(0x682e6ff3, -692930397), new int64(0x748f82ee, 0x5defb2fc), new int64(0x78a5636f, 0x43172f60), new int64(-2067236844, -1578062990), new int64(-1933114872, 0x1a6439ec), new int64(-1866530822, 0x23631e28), new int64(-1538233109, -561857047), new int64(-1090935817, -1295615723), new int64(-965641998, -479046869), new int64(-903397682, -366583396), new int64(-779700025, 0x21c0c207), new int64(-354779690, -840897762), new int64(-176337025, -294727304), new int64(0x6f067aa, 0x72176fba), new int64(0xa637dc5, -1563912026), new int64(0x113f9804, -1090974290), new int64(0x1b710b35, 0x131c471b), new int64(0x28db77f5, 0x23047d84), new int64(0x32caab7b, 0x40c72493), new int64(0x3c9ebe0a, 0x15c9bebc), new int64(0x431d67c4, -1676669620), new int64(0x4cc5d4be, -885112138), new int64(0x597f299c, -60457430), new int64(0x5fcb6fab, 0x3ad6faec), new int64(0x6c44198c, 0x4a475817)];
93549                   }
93550
93551                   for (i = 0; i < 80; i += 1) {
93552                     W[i] = new int64(0, 0);
93553                   } // append padding to the source string. The format is described in the FIPS.
93554
93555
93556                   x[len >> 5] |= 0x80 << 24 - (len & 0x1f);
93557                   x[(len + 128 >> 10 << 5) + 31] = len;
93558                   l = x.length;
93559
93560                   for (i = 0; i < l; i += 32) {
93561                     //32 dwords is the block size
93562                     int64copy(a, H[0]);
93563                     int64copy(b, H[1]);
93564                     int64copy(c, H[2]);
93565                     int64copy(d, H[3]);
93566                     int64copy(e, H[4]);
93567                     int64copy(f, H[5]);
93568                     int64copy(g, H[6]);
93569                     int64copy(h, H[7]);
93570
93571                     for (j = 0; j < 16; j += 1) {
93572                       W[j].h = x[i + 2 * j];
93573                       W[j].l = x[i + 2 * j + 1];
93574                     }
93575
93576                     for (j = 16; j < 80; j += 1) {
93577                       //sigma1
93578                       int64rrot(r1, W[j - 2], 19);
93579                       int64revrrot(r2, W[j - 2], 29);
93580                       int64shr(r3, W[j - 2], 6);
93581                       s1.l = r1.l ^ r2.l ^ r3.l;
93582                       s1.h = r1.h ^ r2.h ^ r3.h; //sigma0
93583
93584                       int64rrot(r1, W[j - 15], 1);
93585                       int64rrot(r2, W[j - 15], 8);
93586                       int64shr(r3, W[j - 15], 7);
93587                       s0.l = r1.l ^ r2.l ^ r3.l;
93588                       s0.h = r1.h ^ r2.h ^ r3.h;
93589                       int64add4(W[j], s1, W[j - 7], s0, W[j - 16]);
93590                     }
93591
93592                     for (j = 0; j < 80; j += 1) {
93593                       //Ch
93594                       Ch.l = e.l & f.l ^ ~e.l & g.l;
93595                       Ch.h = e.h & f.h ^ ~e.h & g.h; //Sigma1
93596
93597                       int64rrot(r1, e, 14);
93598                       int64rrot(r2, e, 18);
93599                       int64revrrot(r3, e, 9);
93600                       s1.l = r1.l ^ r2.l ^ r3.l;
93601                       s1.h = r1.h ^ r2.h ^ r3.h; //Sigma0
93602
93603                       int64rrot(r1, a, 28);
93604                       int64revrrot(r2, a, 2);
93605                       int64revrrot(r3, a, 7);
93606                       s0.l = r1.l ^ r2.l ^ r3.l;
93607                       s0.h = r1.h ^ r2.h ^ r3.h; //Maj
93608
93609                       Maj.l = a.l & b.l ^ a.l & c.l ^ b.l & c.l;
93610                       Maj.h = a.h & b.h ^ a.h & c.h ^ b.h & c.h;
93611                       int64add5(T1, h, s1, Ch, sha512_k[j], W[j]);
93612                       int64add(T2, s0, Maj);
93613                       int64copy(h, g);
93614                       int64copy(g, f);
93615                       int64copy(f, e);
93616                       int64add(e, d, T1);
93617                       int64copy(d, c);
93618                       int64copy(c, b);
93619                       int64copy(b, a);
93620                       int64add(a, T1, T2);
93621                     }
93622
93623                     int64add(H[0], H[0], a);
93624                     int64add(H[1], H[1], b);
93625                     int64add(H[2], H[2], c);
93626                     int64add(H[3], H[3], d);
93627                     int64add(H[4], H[4], e);
93628                     int64add(H[5], H[5], f);
93629                     int64add(H[6], H[6], g);
93630                     int64add(H[7], H[7], h);
93631                   } //represent the hash as an array of 32-bit dwords
93632
93633
93634                   for (i = 0; i < 8; i += 1) {
93635                     hash[2 * i] = H[i].h;
93636                     hash[2 * i + 1] = H[i].l;
93637                   }
93638
93639                   return hash;
93640                 } //A constructor for 64-bit numbers
93641
93642
93643                 function int64(h, l) {
93644                   this.h = h;
93645                   this.l = l; //this.toString = int64toString;
93646                 } //Copies src into dst, assuming both are 64-bit numbers
93647
93648
93649                 function int64copy(dst, src) {
93650                   dst.h = src.h;
93651                   dst.l = src.l;
93652                 } //Right-rotates a 64-bit number by shift
93653                 //Won't handle cases of shift>=32
93654                 //The function revrrot() is for that
93655
93656
93657                 function int64rrot(dst, x, shift) {
93658                   dst.l = x.l >>> shift | x.h << 32 - shift;
93659                   dst.h = x.h >>> shift | x.l << 32 - shift;
93660                 } //Reverses the dwords of the source and then rotates right by shift.
93661                 //This is equivalent to rotation by 32+shift
93662
93663
93664                 function int64revrrot(dst, x, shift) {
93665                   dst.l = x.h >>> shift | x.l << 32 - shift;
93666                   dst.h = x.l >>> shift | x.h << 32 - shift;
93667                 } //Bitwise-shifts right a 64-bit number by shift
93668                 //Won't handle shift>=32, but it's never needed in SHA512
93669
93670
93671                 function int64shr(dst, x, shift) {
93672                   dst.l = x.l >>> shift | x.h << 32 - shift;
93673                   dst.h = x.h >>> shift;
93674                 } //Adds two 64-bit numbers
93675                 //Like the original implementation, does not rely on 32-bit operations
93676
93677
93678                 function int64add(dst, x, y) {
93679                   var w0 = (x.l & 0xffff) + (y.l & 0xffff);
93680                   var w1 = (x.l >>> 16) + (y.l >>> 16) + (w0 >>> 16);
93681                   var w2 = (x.h & 0xffff) + (y.h & 0xffff) + (w1 >>> 16);
93682                   var w3 = (x.h >>> 16) + (y.h >>> 16) + (w2 >>> 16);
93683                   dst.l = w0 & 0xffff | w1 << 16;
93684                   dst.h = w2 & 0xffff | w3 << 16;
93685                 } //Same, except with 4 addends. Works faster than adding them one by one.
93686
93687
93688                 function int64add4(dst, a, b, c, d) {
93689                   var w0 = (a.l & 0xffff) + (b.l & 0xffff) + (c.l & 0xffff) + (d.l & 0xffff);
93690                   var w1 = (a.l >>> 16) + (b.l >>> 16) + (c.l >>> 16) + (d.l >>> 16) + (w0 >>> 16);
93691                   var w2 = (a.h & 0xffff) + (b.h & 0xffff) + (c.h & 0xffff) + (d.h & 0xffff) + (w1 >>> 16);
93692                   var w3 = (a.h >>> 16) + (b.h >>> 16) + (c.h >>> 16) + (d.h >>> 16) + (w2 >>> 16);
93693                   dst.l = w0 & 0xffff | w1 << 16;
93694                   dst.h = w2 & 0xffff | w3 << 16;
93695                 } //Same, except with 5 addends
93696
93697
93698                 function int64add5(dst, a, b, c, d, e) {
93699                   var w0 = (a.l & 0xffff) + (b.l & 0xffff) + (c.l & 0xffff) + (d.l & 0xffff) + (e.l & 0xffff),
93700                       w1 = (a.l >>> 16) + (b.l >>> 16) + (c.l >>> 16) + (d.l >>> 16) + (e.l >>> 16) + (w0 >>> 16),
93701                       w2 = (a.h & 0xffff) + (b.h & 0xffff) + (c.h & 0xffff) + (d.h & 0xffff) + (e.h & 0xffff) + (w1 >>> 16),
93702                       w3 = (a.h >>> 16) + (b.h >>> 16) + (c.h >>> 16) + (d.h >>> 16) + (e.h >>> 16) + (w2 >>> 16);
93703                   dst.l = w0 & 0xffff | w1 << 16;
93704                   dst.h = w2 & 0xffff | w3 << 16;
93705                 }
93706               },
93707
93708               /**
93709                * @class Hashes.RMD160
93710                * @constructor
93711                * @param {Object} [config]
93712                *
93713                * A JavaScript implementation of the RIPEMD-160 Algorithm
93714                * Version 2.2 Copyright Jeremy Lin, Paul Johnston 2000 - 2009.
93715                * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
93716                * See http://pajhome.org.uk/crypt/md5 for details.
93717                * Also http://www.ocf.berkeley.edu/~jjlin/jsotp/
93718                */
93719               RMD160: function RMD160(options) {
93720                 /**
93721                  * Private properties configuration variables. You may need to tweak these to be compatible with
93722                  * the server-side, but the defaults work in most cases.
93723                  * @see this.setUpperCase() method
93724                  * @see this.setPad() method
93725                  */
93726                 options && typeof options.uppercase === 'boolean' ? options.uppercase : false;
93727
93728                 var /* hexadecimal output case format. false - lowercase; true - uppercase  */
93729                 b64pad = options && typeof options.pad === 'string' ? options.pa : '=',
93730
93731                 /* base-64 pad character. Default '=' for strict RFC compliance   */
93732                 utf8 = options && typeof options.utf8 === 'boolean' ? options.utf8 : true,
93733
93734                 /* enable/disable utf8 encoding */
93735                 rmd160_r1 = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 7, 4, 13, 1, 10, 6, 15, 3, 12, 0, 9, 5, 2, 14, 11, 8, 3, 10, 14, 4, 9, 15, 8, 1, 2, 7, 0, 6, 13, 11, 5, 12, 1, 9, 11, 10, 0, 8, 12, 4, 13, 3, 7, 15, 14, 5, 6, 2, 4, 0, 5, 9, 7, 12, 2, 10, 14, 1, 3, 8, 11, 6, 15, 13],
93736                     rmd160_r2 = [5, 14, 7, 0, 9, 2, 11, 4, 13, 6, 15, 8, 1, 10, 3, 12, 6, 11, 3, 7, 0, 13, 5, 10, 14, 15, 8, 12, 4, 9, 1, 2, 15, 5, 1, 3, 7, 14, 6, 9, 11, 8, 12, 2, 10, 0, 4, 13, 8, 6, 4, 1, 3, 11, 15, 0, 5, 12, 2, 13, 9, 7, 10, 14, 12, 15, 10, 4, 1, 5, 8, 7, 6, 2, 13, 14, 0, 3, 9, 11],
93737                     rmd160_s1 = [11, 14, 15, 12, 5, 8, 7, 9, 11, 13, 14, 15, 6, 7, 9, 8, 7, 6, 8, 13, 11, 9, 7, 15, 7, 12, 15, 9, 11, 7, 13, 12, 11, 13, 6, 7, 14, 9, 13, 15, 14, 8, 13, 6, 5, 12, 7, 5, 11, 12, 14, 15, 14, 15, 9, 8, 9, 14, 5, 6, 8, 6, 5, 12, 9, 15, 5, 11, 6, 8, 13, 12, 5, 12, 13, 14, 11, 8, 5, 6],
93738                     rmd160_s2 = [8, 9, 9, 11, 13, 15, 15, 5, 7, 7, 8, 11, 14, 14, 12, 6, 9, 13, 15, 7, 12, 8, 9, 11, 7, 7, 12, 7, 6, 15, 13, 11, 9, 7, 15, 11, 8, 6, 6, 14, 12, 13, 5, 14, 13, 13, 7, 5, 15, 5, 8, 11, 14, 14, 6, 14, 6, 9, 12, 9, 12, 5, 15, 8, 8, 5, 12, 9, 12, 5, 14, 6, 8, 13, 6, 5, 15, 13, 11, 11];
93739                 /* privileged (public) methods */
93740
93741                 this.hex = function (s) {
93742                   return rstr2hex(rstr(s));
93743                 };
93744
93745                 this.b64 = function (s) {
93746                   return rstr2b64(rstr(s), b64pad);
93747                 };
93748
93749                 this.any = function (s, e) {
93750                   return rstr2any(rstr(s), e);
93751                 };
93752
93753                 this.raw = function (s) {
93754                   return rstr(s);
93755                 };
93756
93757                 this.hex_hmac = function (k, d) {
93758                   return rstr2hex(rstr_hmac(k, d));
93759                 };
93760
93761                 this.b64_hmac = function (k, d) {
93762                   return rstr2b64(rstr_hmac(k, d), b64pad);
93763                 };
93764
93765                 this.any_hmac = function (k, d, e) {
93766                   return rstr2any(rstr_hmac(k, d), e);
93767                 };
93768                 /**
93769                  * Perform a simple self-test to see if the VM is working
93770                  * @return {String} Hexadecimal hash sample
93771                  * @public
93772                  */
93773
93774
93775                 this.vm_test = function () {
93776                   return hex('abc').toLowerCase() === '900150983cd24fb0d6963f7d28e17f72';
93777                 };
93778                 /**
93779                  * @description Enable/disable uppercase hexadecimal returned string
93780                  * @param {boolean}
93781                  * @return {Object} this
93782                  * @public
93783                  */
93784
93785
93786                 this.setUpperCase = function (a) {
93787
93788                   return this;
93789                 };
93790                 /**
93791                  * @description Defines a base64 pad string
93792                  * @param {string} Pad
93793                  * @return {Object} this
93794                  * @public
93795                  */
93796
93797
93798                 this.setPad = function (a) {
93799                   if (typeof a !== 'undefined') {
93800                     b64pad = a;
93801                   }
93802
93803                   return this;
93804                 };
93805                 /**
93806                  * @description Defines a base64 pad string
93807                  * @param {boolean}
93808                  * @return {Object} this
93809                  * @public
93810                  */
93811
93812
93813                 this.setUTF8 = function (a) {
93814                   if (typeof a === 'boolean') {
93815                     utf8 = a;
93816                   }
93817
93818                   return this;
93819                 };
93820                 /* private methods */
93821
93822                 /**
93823                  * Calculate the rmd160 of a raw string
93824                  */
93825
93826
93827                 function rstr(s) {
93828                   s = utf8 ? utf8Encode(s) : s;
93829                   return binl2rstr(binl(rstr2binl(s), s.length * 8));
93830                 }
93831                 /**
93832                  * Calculate the HMAC-rmd160 of a key and some data (raw strings)
93833                  */
93834
93835
93836                 function rstr_hmac(key, data) {
93837                   key = utf8 ? utf8Encode(key) : key;
93838                   data = utf8 ? utf8Encode(data) : data;
93839                   var i,
93840                       hash,
93841                       bkey = rstr2binl(key),
93842                       ipad = Array(16),
93843                       opad = Array(16);
93844
93845                   if (bkey.length > 16) {
93846                     bkey = binl(bkey, key.length * 8);
93847                   }
93848
93849                   for (i = 0; i < 16; i += 1) {
93850                     ipad[i] = bkey[i] ^ 0x36363636;
93851                     opad[i] = bkey[i] ^ 0x5C5C5C5C;
93852                   }
93853
93854                   hash = binl(ipad.concat(rstr2binl(data)), 512 + data.length * 8);
93855                   return binl2rstr(binl(opad.concat(hash), 512 + 160));
93856                 }
93857                 /**
93858                  * Convert an array of little-endian words to a string
93859                  */
93860
93861
93862                 function binl2rstr(input) {
93863                   var i,
93864                       output = '',
93865                       l = input.length * 32;
93866
93867                   for (i = 0; i < l; i += 8) {
93868                     output += String.fromCharCode(input[i >> 5] >>> i % 32 & 0xFF);
93869                   }
93870
93871                   return output;
93872                 }
93873                 /**
93874                  * Calculate the RIPE-MD160 of an array of little-endian words, and a bit length.
93875                  */
93876
93877
93878                 function binl(x, len) {
93879                   var T,
93880                       j,
93881                       i,
93882                       l,
93883                       h0 = 0x67452301,
93884                       h1 = 0xefcdab89,
93885                       h2 = 0x98badcfe,
93886                       h3 = 0x10325476,
93887                       h4 = 0xc3d2e1f0,
93888                       A1,
93889                       B1,
93890                       C1,
93891                       D1,
93892                       E1,
93893                       A2,
93894                       B2,
93895                       C2,
93896                       D2,
93897                       E2;
93898                   /* append padding */
93899
93900                   x[len >> 5] |= 0x80 << len % 32;
93901                   x[(len + 64 >>> 9 << 4) + 14] = len;
93902                   l = x.length;
93903
93904                   for (i = 0; i < l; i += 16) {
93905                     A1 = A2 = h0;
93906                     B1 = B2 = h1;
93907                     C1 = C2 = h2;
93908                     D1 = D2 = h3;
93909                     E1 = E2 = h4;
93910
93911                     for (j = 0; j <= 79; j += 1) {
93912                       T = safe_add(A1, rmd160_f(j, B1, C1, D1));
93913                       T = safe_add(T, x[i + rmd160_r1[j]]);
93914                       T = safe_add(T, rmd160_K1(j));
93915                       T = safe_add(bit_rol(T, rmd160_s1[j]), E1);
93916                       A1 = E1;
93917                       E1 = D1;
93918                       D1 = bit_rol(C1, 10);
93919                       C1 = B1;
93920                       B1 = T;
93921                       T = safe_add(A2, rmd160_f(79 - j, B2, C2, D2));
93922                       T = safe_add(T, x[i + rmd160_r2[j]]);
93923                       T = safe_add(T, rmd160_K2(j));
93924                       T = safe_add(bit_rol(T, rmd160_s2[j]), E2);
93925                       A2 = E2;
93926                       E2 = D2;
93927                       D2 = bit_rol(C2, 10);
93928                       C2 = B2;
93929                       B2 = T;
93930                     }
93931
93932                     T = safe_add(h1, safe_add(C1, D2));
93933                     h1 = safe_add(h2, safe_add(D1, E2));
93934                     h2 = safe_add(h3, safe_add(E1, A2));
93935                     h3 = safe_add(h4, safe_add(A1, B2));
93936                     h4 = safe_add(h0, safe_add(B1, C2));
93937                     h0 = T;
93938                   }
93939
93940                   return [h0, h1, h2, h3, h4];
93941                 } // specific algorithm methods
93942
93943
93944                 function rmd160_f(j, x, y, z) {
93945                   return 0 <= j && j <= 15 ? x ^ y ^ z : 16 <= j && j <= 31 ? x & y | ~x & z : 32 <= j && j <= 47 ? (x | ~y) ^ z : 48 <= j && j <= 63 ? x & z | y & ~z : 64 <= j && j <= 79 ? x ^ (y | ~z) : 'rmd160_f: j out of range';
93946                 }
93947
93948                 function rmd160_K1(j) {
93949                   return 0 <= j && j <= 15 ? 0x00000000 : 16 <= j && j <= 31 ? 0x5a827999 : 32 <= j && j <= 47 ? 0x6ed9eba1 : 48 <= j && j <= 63 ? 0x8f1bbcdc : 64 <= j && j <= 79 ? 0xa953fd4e : 'rmd160_K1: j out of range';
93950                 }
93951
93952                 function rmd160_K2(j) {
93953                   return 0 <= j && j <= 15 ? 0x50a28be6 : 16 <= j && j <= 31 ? 0x5c4dd124 : 32 <= j && j <= 47 ? 0x6d703ef3 : 48 <= j && j <= 63 ? 0x7a6d76e9 : 64 <= j && j <= 79 ? 0x00000000 : 'rmd160_K2: j out of range';
93954                 }
93955               }
93956             }; // exposes Hashes
93957
93958             (function (window, undefined$1) {
93959               var freeExports = false;
93960
93961               {
93962                 freeExports = exports;
93963
93964                 if (exports && _typeof(commonjsGlobal) === 'object' && commonjsGlobal && commonjsGlobal === commonjsGlobal.global) {
93965                   window = commonjsGlobal;
93966                 }
93967               }
93968
93969               if (typeof undefined$1 === 'function' && _typeof(undefined$1.amd) === 'object' && undefined$1.amd) {
93970                 // define as an anonymous module, so, through path mapping, it can be aliased
93971                 undefined$1(function () {
93972                   return Hashes;
93973                 });
93974               } else if (freeExports) {
93975                 // in Node.js or RingoJS v0.8.0+
93976                 if (module && module.exports === freeExports) {
93977                   module.exports = Hashes;
93978                 } // in Narwhal or RingoJS v0.7.0-
93979                 else {
93980                     freeExports.Hashes = Hashes;
93981                   }
93982               } else {
93983                 // in a browser or Rhino
93984                 window.Hashes = Hashes;
93985               }
93986             })(this);
93987           })(); // IIFE
93988
93989         });
93990
93991         var sha1 = new hashes.SHA1(); // # xtend
93992
93993         var hasOwnProperty$1 = Object.prototype.hasOwnProperty;
93994
93995         function xtend() {
93996           var target = {};
93997
93998           for (var i = 0; i < arguments.length; i++) {
93999             var source = arguments[i];
94000
94001             for (var key in source) {
94002               if (hasOwnProperty$1.call(source, key)) {
94003                 target[key] = source[key];
94004               }
94005             }
94006           }
94007
94008           return target;
94009         }
94010
94011         var ohauth = {};
94012
94013         ohauth.qsString = function (obj) {
94014           return Object.keys(obj).sort().map(function (key) {
94015             return ohauth.percentEncode(key) + '=' + ohauth.percentEncode(obj[key]);
94016           }).join('&');
94017         };
94018
94019         ohauth.stringQs = function (str) {
94020           return str.split('&').filter(function (pair) {
94021             return pair !== '';
94022           }).reduce(function (obj, pair) {
94023             var parts = pair.split('=');
94024             obj[decodeURIComponent(parts[0])] = null === parts[1] ? '' : decodeURIComponent(parts[1]);
94025             return obj;
94026           }, {});
94027         };
94028
94029         ohauth.rawxhr = function (method, url, data, headers, callback) {
94030           var xhr = new XMLHttpRequest(),
94031               twoHundred = /^20\d$/;
94032
94033           xhr.onreadystatechange = function () {
94034             if (4 === xhr.readyState && 0 !== xhr.status) {
94035               if (twoHundred.test(xhr.status)) callback(null, xhr);else return callback(xhr, null);
94036             }
94037           };
94038
94039           xhr.onerror = function (e) {
94040             return callback(e, null);
94041           };
94042
94043           xhr.open(method, url, true);
94044
94045           for (var h in headers) {
94046             xhr.setRequestHeader(h, headers[h]);
94047           }
94048
94049           xhr.send(data);
94050           return xhr;
94051         };
94052
94053         ohauth.xhr = function (method, url, auth, data, options, callback) {
94054           var headers = options && options.header || {
94055             'Content-Type': 'application/x-www-form-urlencoded'
94056           };
94057           headers.Authorization = 'OAuth ' + ohauth.authHeader(auth);
94058           return ohauth.rawxhr(method, url, data, headers, callback);
94059         };
94060
94061         ohauth.nonce = function () {
94062           for (var o = ''; o.length < 6;) {
94063             o += '0123456789ABCDEFGHIJKLMNOPQRSTUVWXTZabcdefghiklmnopqrstuvwxyz'[Math.floor(Math.random() * 61)];
94064           }
94065
94066           return o;
94067         };
94068
94069         ohauth.authHeader = function (obj) {
94070           return Object.keys(obj).sort().map(function (key) {
94071             return encodeURIComponent(key) + '="' + encodeURIComponent(obj[key]) + '"';
94072           }).join(', ');
94073         };
94074
94075         ohauth.timestamp = function () {
94076           return ~~(+new Date() / 1000);
94077         };
94078
94079         ohauth.percentEncode = function (s) {
94080           return encodeURIComponent(s).replace(/\!/g, '%21').replace(/\'/g, '%27').replace(/\*/g, '%2A').replace(/\(/g, '%28').replace(/\)/g, '%29');
94081         };
94082
94083         ohauth.baseString = function (method, url, params) {
94084           if (params.oauth_signature) delete params.oauth_signature;
94085           return [method, ohauth.percentEncode(url), ohauth.percentEncode(ohauth.qsString(params))].join('&');
94086         };
94087
94088         ohauth.signature = function (oauth_secret, token_secret, baseString) {
94089           return sha1.b64_hmac(ohauth.percentEncode(oauth_secret) + '&' + ohauth.percentEncode(token_secret), baseString);
94090         };
94091         /**
94092          * Takes an options object for configuration (consumer_key,
94093          * consumer_secret, version, signature_method, token, token_secret)
94094          * and returns a function that generates the Authorization header
94095          * for given data.
94096          *
94097          * The returned function takes these parameters:
94098          * - method: GET/POST/...
94099          * - uri: full URI with protocol, port, path and query string
94100          * - extra_params: any extra parameters (that are passed in the POST data),
94101          *   can be an object or a from-urlencoded string.
94102          *
94103          * Returned function returns full OAuth header with "OAuth" string in it.
94104          */
94105
94106
94107         ohauth.headerGenerator = function (options) {
94108           options = options || {};
94109           var consumer_key = options.consumer_key || '',
94110               consumer_secret = options.consumer_secret || '',
94111               signature_method = options.signature_method || 'HMAC-SHA1',
94112               version = options.version || '1.0',
94113               token = options.token || '',
94114               token_secret = options.token_secret || '';
94115           return function (method, uri, extra_params) {
94116             method = method.toUpperCase();
94117
94118             if (typeof extra_params === 'string' && extra_params.length > 0) {
94119               extra_params = ohauth.stringQs(extra_params);
94120             }
94121
94122             var uri_parts = uri.split('?', 2),
94123                 base_uri = uri_parts[0];
94124             var query_params = uri_parts.length === 2 ? ohauth.stringQs(uri_parts[1]) : {};
94125             var oauth_params = {
94126               oauth_consumer_key: consumer_key,
94127               oauth_signature_method: signature_method,
94128               oauth_version: version,
94129               oauth_timestamp: ohauth.timestamp(),
94130               oauth_nonce: ohauth.nonce()
94131             };
94132             if (token) oauth_params.oauth_token = token;
94133             var all_params = xtend({}, oauth_params, query_params, extra_params),
94134                 base_str = ohauth.baseString(method, base_uri, all_params);
94135             oauth_params.oauth_signature = ohauth.signature(consumer_secret, token_secret, base_str);
94136             return 'OAuth ' + ohauth.authHeader(oauth_params);
94137           };
94138         };
94139
94140         var ohauth_1 = ohauth;
94141
94142         var resolveUrl = createCommonjsModule(function (module, exports) {
94143           // Copyright 2014 Simon Lydell
94144           // X11 (“MIT”) Licensed. (See LICENSE.)
94145           void function (root, factory) {
94146             {
94147               module.exports = factory();
94148             }
94149           }(commonjsGlobal, function () {
94150             function resolveUrl()
94151             /* ...urls */
94152             {
94153               var numUrls = arguments.length;
94154
94155               if (numUrls === 0) {
94156                 throw new Error("resolveUrl requires at least one argument; got none.");
94157               }
94158
94159               var base = document.createElement("base");
94160               base.href = arguments[0];
94161
94162               if (numUrls === 1) {
94163                 return base.href;
94164               }
94165
94166               var head = document.getElementsByTagName("head")[0];
94167               head.insertBefore(base, head.firstChild);
94168               var a = document.createElement("a");
94169               var resolved;
94170
94171               for (var index = 1; index < numUrls; index++) {
94172                 a.href = arguments[index];
94173                 resolved = a.href;
94174                 base.href = resolved;
94175               }
94176
94177               head.removeChild(base);
94178               return resolved;
94179             }
94180
94181             return resolveUrl;
94182           });
94183         });
94184
94185         var assign = make_assign();
94186         var create$1 = make_create();
94187         var trim$1 = make_trim();
94188         var Global$5 = typeof window !== 'undefined' ? window : commonjsGlobal;
94189         var util = {
94190           assign: assign,
94191           create: create$1,
94192           trim: trim$1,
94193           bind: bind$1,
94194           slice: slice$1,
94195           each: each$7,
94196           map: map,
94197           pluck: pluck$1,
94198           isList: isList$1,
94199           isFunction: isFunction$1,
94200           isObject: isObject$1,
94201           Global: Global$5
94202         };
94203
94204         function make_assign() {
94205           if (Object.assign) {
94206             return Object.assign;
94207           } else {
94208             return function shimAssign(obj, props1, props2, etc) {
94209               for (var i = 1; i < arguments.length; i++) {
94210                 each$7(Object(arguments[i]), function (val, key) {
94211                   obj[key] = val;
94212                 });
94213               }
94214
94215               return obj;
94216             };
94217           }
94218         }
94219
94220         function make_create() {
94221           if (Object.create) {
94222             return function create(obj, assignProps1, assignProps2, etc) {
94223               var assignArgsList = slice$1(arguments, 1);
94224               return assign.apply(this, [Object.create(obj)].concat(assignArgsList));
94225             };
94226           } else {
94227             var F = function F() {}; // eslint-disable-line no-inner-declarations
94228
94229
94230             return function create(obj, assignProps1, assignProps2, etc) {
94231               var assignArgsList = slice$1(arguments, 1);
94232               F.prototype = obj;
94233               return assign.apply(this, [new F()].concat(assignArgsList));
94234             };
94235           }
94236         }
94237
94238         function make_trim() {
94239           if (String.prototype.trim) {
94240             return function trim(str) {
94241               return String.prototype.trim.call(str);
94242             };
94243           } else {
94244             return function trim(str) {
94245               return str.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, '');
94246             };
94247           }
94248         }
94249
94250         function bind$1(obj, fn) {
94251           return function () {
94252             return fn.apply(obj, Array.prototype.slice.call(arguments, 0));
94253           };
94254         }
94255
94256         function slice$1(arr, index) {
94257           return Array.prototype.slice.call(arr, index || 0);
94258         }
94259
94260         function each$7(obj, fn) {
94261           pluck$1(obj, function (val, key) {
94262             fn(val, key);
94263             return false;
94264           });
94265         }
94266
94267         function map(obj, fn) {
94268           var res = isList$1(obj) ? [] : {};
94269           pluck$1(obj, function (v, k) {
94270             res[k] = fn(v, k);
94271             return false;
94272           });
94273           return res;
94274         }
94275
94276         function pluck$1(obj, fn) {
94277           if (isList$1(obj)) {
94278             for (var i = 0; i < obj.length; i++) {
94279               if (fn(obj[i], i)) {
94280                 return obj[i];
94281               }
94282             }
94283           } else {
94284             for (var key in obj) {
94285               if (obj.hasOwnProperty(key)) {
94286                 if (fn(obj[key], key)) {
94287                   return obj[key];
94288                 }
94289               }
94290             }
94291           }
94292         }
94293
94294         function isList$1(val) {
94295           return val != null && typeof val != 'function' && typeof val.length == 'number';
94296         }
94297
94298         function isFunction$1(val) {
94299           return val && {}.toString.call(val) === '[object Function]';
94300         }
94301
94302         function isObject$1(val) {
94303           return val && {}.toString.call(val) === '[object Object]';
94304         }
94305
94306         var slice = util.slice;
94307         var pluck = util.pluck;
94308         var each$6 = util.each;
94309         var bind = util.bind;
94310         var create = util.create;
94311         var isList = util.isList;
94312         var isFunction = util.isFunction;
94313         var isObject = util.isObject;
94314         var storeEngine = {
94315           createStore: _createStore
94316         };
94317         var storeAPI = {
94318           version: '2.0.12',
94319           enabled: false,
94320           // get returns the value of the given key. If that value
94321           // is undefined, it returns optionalDefaultValue instead.
94322           get: function get(key, optionalDefaultValue) {
94323             var data = this.storage.read(this._namespacePrefix + key);
94324             return this._deserialize(data, optionalDefaultValue);
94325           },
94326           // set will store the given value at key and returns value.
94327           // Calling set with value === undefined is equivalent to calling remove.
94328           set: function set(key, value) {
94329             if (value === undefined) {
94330               return this.remove(key);
94331             }
94332
94333             this.storage.write(this._namespacePrefix + key, this._serialize(value));
94334             return value;
94335           },
94336           // remove deletes the key and value stored at the given key.
94337           remove: function remove(key) {
94338             this.storage.remove(this._namespacePrefix + key);
94339           },
94340           // each will call the given callback once for each key-value pair
94341           // in this store.
94342           each: function each(callback) {
94343             var self = this;
94344             this.storage.each(function (val, namespacedKey) {
94345               callback.call(self, self._deserialize(val), (namespacedKey || '').replace(self._namespaceRegexp, ''));
94346             });
94347           },
94348           // clearAll will remove all the stored key-value pairs in this store.
94349           clearAll: function clearAll() {
94350             this.storage.clearAll();
94351           },
94352           // additional functionality that can't live in plugins
94353           // ---------------------------------------------------
94354           // hasNamespace returns true if this store instance has the given namespace.
94355           hasNamespace: function hasNamespace(namespace) {
94356             return this._namespacePrefix == '__storejs_' + namespace + '_';
94357           },
94358           // createStore creates a store.js instance with the first
94359           // functioning storage in the list of storage candidates,
94360           // and applies the the given mixins to the instance.
94361           createStore: function createStore() {
94362             return _createStore.apply(this, arguments);
94363           },
94364           addPlugin: function addPlugin(plugin) {
94365             this._addPlugin(plugin);
94366           },
94367           namespace: function namespace(_namespace) {
94368             return _createStore(this.storage, this.plugins, _namespace);
94369           }
94370         };
94371
94372         function _warn() {
94373           var _console = typeof console == 'undefined' ? null : console;
94374
94375           if (!_console) {
94376             return;
94377           }
94378
94379           var fn = _console.warn ? _console.warn : _console.log;
94380           fn.apply(_console, arguments);
94381         }
94382
94383         function _createStore(storages, plugins, namespace) {
94384           if (!namespace) {
94385             namespace = '';
94386           }
94387
94388           if (storages && !isList(storages)) {
94389             storages = [storages];
94390           }
94391
94392           if (plugins && !isList(plugins)) {
94393             plugins = [plugins];
94394           }
94395
94396           var namespacePrefix = namespace ? '__storejs_' + namespace + '_' : '';
94397           var namespaceRegexp = namespace ? new RegExp('^' + namespacePrefix) : null;
94398           var legalNamespaces = /^[a-zA-Z0-9_\-]*$/; // alpha-numeric + underscore and dash
94399
94400           if (!legalNamespaces.test(namespace)) {
94401             throw new Error('store.js namespaces can only have alphanumerics + underscores and dashes');
94402           }
94403
94404           var _privateStoreProps = {
94405             _namespacePrefix: namespacePrefix,
94406             _namespaceRegexp: namespaceRegexp,
94407             _testStorage: function _testStorage(storage) {
94408               try {
94409                 var testStr = '__storejs__test__';
94410                 storage.write(testStr, testStr);
94411                 var ok = storage.read(testStr) === testStr;
94412                 storage.remove(testStr);
94413                 return ok;
94414               } catch (e) {
94415                 return false;
94416               }
94417             },
94418             _assignPluginFnProp: function _assignPluginFnProp(pluginFnProp, propName) {
94419               var oldFn = this[propName];
94420
94421               this[propName] = function pluginFn() {
94422                 var args = slice(arguments, 0);
94423                 var self = this; // super_fn calls the old function which was overwritten by
94424                 // this mixin.
94425
94426                 function super_fn() {
94427                   if (!oldFn) {
94428                     return;
94429                   }
94430
94431                   each$6(arguments, function (arg, i) {
94432                     args[i] = arg;
94433                   });
94434                   return oldFn.apply(self, args);
94435                 } // Give mixing function access to super_fn by prefixing all mixin function
94436                 // arguments with super_fn.
94437
94438
94439                 var newFnArgs = [super_fn].concat(args);
94440                 return pluginFnProp.apply(self, newFnArgs);
94441               };
94442             },
94443             _serialize: function _serialize(obj) {
94444               return JSON.stringify(obj);
94445             },
94446             _deserialize: function _deserialize(strVal, defaultVal) {
94447               if (!strVal) {
94448                 return defaultVal;
94449               } // It is possible that a raw string value has been previously stored
94450               // in a storage without using store.js, meaning it will be a raw
94451               // string value instead of a JSON serialized string. By defaulting
94452               // to the raw string value in case of a JSON parse error, we allow
94453               // for past stored values to be forwards-compatible with store.js
94454
94455
94456               var val = '';
94457
94458               try {
94459                 val = JSON.parse(strVal);
94460               } catch (e) {
94461                 val = strVal;
94462               }
94463
94464               return val !== undefined ? val : defaultVal;
94465             },
94466             _addStorage: function _addStorage(storage) {
94467               if (this.enabled) {
94468                 return;
94469               }
94470
94471               if (this._testStorage(storage)) {
94472                 this.storage = storage;
94473                 this.enabled = true;
94474               }
94475             },
94476             _addPlugin: function _addPlugin(plugin) {
94477               var self = this; // If the plugin is an array, then add all plugins in the array.
94478               // This allows for a plugin to depend on other plugins.
94479
94480               if (isList(plugin)) {
94481                 each$6(plugin, function (plugin) {
94482                   self._addPlugin(plugin);
94483                 });
94484                 return;
94485               } // Keep track of all plugins we've seen so far, so that we
94486               // don't add any of them twice.
94487
94488
94489               var seenPlugin = pluck(this.plugins, function (seenPlugin) {
94490                 return plugin === seenPlugin;
94491               });
94492
94493               if (seenPlugin) {
94494                 return;
94495               }
94496
94497               this.plugins.push(plugin); // Check that the plugin is properly formed
94498
94499               if (!isFunction(plugin)) {
94500                 throw new Error('Plugins must be function values that return objects');
94501               }
94502
94503               var pluginProperties = plugin.call(this);
94504
94505               if (!isObject(pluginProperties)) {
94506                 throw new Error('Plugins must return an object of function properties');
94507               } // Add the plugin function properties to this store instance.
94508
94509
94510               each$6(pluginProperties, function (pluginFnProp, propName) {
94511                 if (!isFunction(pluginFnProp)) {
94512                   throw new Error('Bad plugin property: ' + propName + ' from plugin ' + plugin.name + '. Plugins should only return functions.');
94513                 }
94514
94515                 self._assignPluginFnProp(pluginFnProp, propName);
94516               });
94517             },
94518             // Put deprecated properties in the private API, so as to not expose it to accidential
94519             // discovery through inspection of the store object.
94520             // Deprecated: addStorage
94521             addStorage: function addStorage(storage) {
94522               _warn('store.addStorage(storage) is deprecated. Use createStore([storages])');
94523
94524               this._addStorage(storage);
94525             }
94526           };
94527           var store = create(_privateStoreProps, storeAPI, {
94528             plugins: []
94529           });
94530           store.raw = {};
94531           each$6(store, function (prop, propName) {
94532             if (isFunction(prop)) {
94533               store.raw[propName] = bind(store, prop);
94534             }
94535           });
94536           each$6(storages, function (storage) {
94537             store._addStorage(storage);
94538           });
94539           each$6(plugins, function (plugin) {
94540             store._addPlugin(plugin);
94541           });
94542           return store;
94543         }
94544
94545         var Global$4 = util.Global;
94546         var localStorage_1 = {
94547           name: 'localStorage',
94548           read: read$5,
94549           write: write$5,
94550           each: each$5,
94551           remove: remove$5,
94552           clearAll: clearAll$5
94553         };
94554
94555         function localStorage$1() {
94556           return Global$4.localStorage;
94557         }
94558
94559         function read$5(key) {
94560           return localStorage$1().getItem(key);
94561         }
94562
94563         function write$5(key, data) {
94564           return localStorage$1().setItem(key, data);
94565         }
94566
94567         function each$5(fn) {
94568           for (var i = localStorage$1().length - 1; i >= 0; i--) {
94569             var key = localStorage$1().key(i);
94570             fn(read$5(key), key);
94571           }
94572         }
94573
94574         function remove$5(key) {
94575           return localStorage$1().removeItem(key);
94576         }
94577
94578         function clearAll$5() {
94579           return localStorage$1().clear();
94580         }
94581
94582         // versions 6 and 7, where no localStorage, etc
94583         // is available.
94584
94585         var Global$3 = util.Global;
94586         var oldFFGlobalStorage = {
94587           name: 'oldFF-globalStorage',
94588           read: read$4,
94589           write: write$4,
94590           each: each$4,
94591           remove: remove$4,
94592           clearAll: clearAll$4
94593         };
94594         var globalStorage = Global$3.globalStorage;
94595
94596         function read$4(key) {
94597           return globalStorage[key];
94598         }
94599
94600         function write$4(key, data) {
94601           globalStorage[key] = data;
94602         }
94603
94604         function each$4(fn) {
94605           for (var i = globalStorage.length - 1; i >= 0; i--) {
94606             var key = globalStorage.key(i);
94607             fn(globalStorage[key], key);
94608           }
94609         }
94610
94611         function remove$4(key) {
94612           return globalStorage.removeItem(key);
94613         }
94614
94615         function clearAll$4() {
94616           each$4(function (key, _) {
94617             delete globalStorage[key];
94618           });
94619         }
94620
94621         // versions 6 and 7, where no localStorage, sessionStorage, etc
94622         // is available.
94623
94624         var Global$2 = util.Global;
94625         var oldIEUserDataStorage = {
94626           name: 'oldIE-userDataStorage',
94627           write: write$3,
94628           read: read$3,
94629           each: each$3,
94630           remove: remove$3,
94631           clearAll: clearAll$3
94632         };
94633         var storageName = 'storejs';
94634         var doc$1 = Global$2.document;
94635
94636         var _withStorageEl = _makeIEStorageElFunction();
94637
94638         var disable = (Global$2.navigator ? Global$2.navigator.userAgent : '').match(/ (MSIE 8|MSIE 9|MSIE 10)\./); // MSIE 9.x, MSIE 10.x
94639
94640         function write$3(unfixedKey, data) {
94641           if (disable) {
94642             return;
94643           }
94644
94645           var fixedKey = fixKey(unfixedKey);
94646
94647           _withStorageEl(function (storageEl) {
94648             storageEl.setAttribute(fixedKey, data);
94649             storageEl.save(storageName);
94650           });
94651         }
94652
94653         function read$3(unfixedKey) {
94654           if (disable) {
94655             return;
94656           }
94657
94658           var fixedKey = fixKey(unfixedKey);
94659           var res = null;
94660
94661           _withStorageEl(function (storageEl) {
94662             res = storageEl.getAttribute(fixedKey);
94663           });
94664
94665           return res;
94666         }
94667
94668         function each$3(callback) {
94669           _withStorageEl(function (storageEl) {
94670             var attributes = storageEl.XMLDocument.documentElement.attributes;
94671
94672             for (var i = attributes.length - 1; i >= 0; i--) {
94673               var attr = attributes[i];
94674               callback(storageEl.getAttribute(attr.name), attr.name);
94675             }
94676           });
94677         }
94678
94679         function remove$3(unfixedKey) {
94680           var fixedKey = fixKey(unfixedKey);
94681
94682           _withStorageEl(function (storageEl) {
94683             storageEl.removeAttribute(fixedKey);
94684             storageEl.save(storageName);
94685           });
94686         }
94687
94688         function clearAll$3() {
94689           _withStorageEl(function (storageEl) {
94690             var attributes = storageEl.XMLDocument.documentElement.attributes;
94691             storageEl.load(storageName);
94692
94693             for (var i = attributes.length - 1; i >= 0; i--) {
94694               storageEl.removeAttribute(attributes[i].name);
94695             }
94696
94697             storageEl.save(storageName);
94698           });
94699         } // Helpers
94700         //////////
94701         // In IE7, keys cannot start with a digit or contain certain chars.
94702         // See https://github.com/marcuswestin/store.js/issues/40
94703         // See https://github.com/marcuswestin/store.js/issues/83
94704
94705
94706         var forbiddenCharsRegex = new RegExp("[!\"#$%&'()*+,/\\\\:;<=>?@[\\]^`{|}~]", "g");
94707
94708         function fixKey(key) {
94709           return key.replace(/^\d/, '___$&').replace(forbiddenCharsRegex, '___');
94710         }
94711
94712         function _makeIEStorageElFunction() {
94713           if (!doc$1 || !doc$1.documentElement || !doc$1.documentElement.addBehavior) {
94714             return null;
94715           }
94716
94717           var scriptTag = 'script',
94718               storageOwner,
94719               storageContainer,
94720               storageEl; // Since #userData storage applies only to specific paths, we need to
94721           // somehow link our data to a specific path.  We choose /favicon.ico
94722           // as a pretty safe option, since all browsers already make a request to
94723           // this URL anyway and being a 404 will not hurt us here.  We wrap an
94724           // iframe pointing to the favicon in an ActiveXObject(htmlfile) object
94725           // (see: http://msdn.microsoft.com/en-us/library/aa752574(v=VS.85).aspx)
94726           // since the iframe access rules appear to allow direct access and
94727           // manipulation of the document element, even for a 404 page.  This
94728           // document can be used instead of the current document (which would
94729           // have been limited to the current path) to perform #userData storage.
94730
94731           try {
94732             /* global ActiveXObject */
94733             storageContainer = new ActiveXObject('htmlfile');
94734             storageContainer.open();
94735             storageContainer.write('<' + scriptTag + '>document.w=window</' + scriptTag + '><iframe src="/favicon.ico"></iframe>');
94736             storageContainer.close();
94737             storageOwner = storageContainer.w.frames[0].document;
94738             storageEl = storageOwner.createElement('div');
94739           } catch (e) {
94740             // somehow ActiveXObject instantiation failed (perhaps some special
94741             // security settings or otherwse), fall back to per-path storage
94742             storageEl = doc$1.createElement('div');
94743             storageOwner = doc$1.body;
94744           }
94745
94746           return function (storeFunction) {
94747             var args = [].slice.call(arguments, 0);
94748             args.unshift(storageEl); // See http://msdn.microsoft.com/en-us/library/ms531081(v=VS.85).aspx
94749             // and http://msdn.microsoft.com/en-us/library/ms531424(v=VS.85).aspx
94750
94751             storageOwner.appendChild(storageEl);
94752             storageEl.addBehavior('#default#userData');
94753             storageEl.load(storageName);
94754             storeFunction.apply(this, args);
94755             storageOwner.removeChild(storageEl);
94756             return;
94757           };
94758         }
94759
94760         // doesn't work but cookies do. This implementation is adopted from
94761         // https://developer.mozilla.org/en-US/docs/Web/API/Storage/LocalStorage
94762
94763         var Global$1 = util.Global;
94764         var trim = util.trim;
94765         var cookieStorage = {
94766           name: 'cookieStorage',
94767           read: read$2,
94768           write: write$2,
94769           each: each$2,
94770           remove: remove$2,
94771           clearAll: clearAll$2
94772         };
94773         var doc = Global$1.document;
94774
94775         function read$2(key) {
94776           if (!key || !_has(key)) {
94777             return null;
94778           }
94779
94780           var regexpStr = "(?:^|.*;\\s*)" + escape(key).replace(/[\-\.\+\*]/g, "\\$&") + "\\s*\\=\\s*((?:[^;](?!;))*[^;]?).*";
94781           return unescape(doc.cookie.replace(new RegExp(regexpStr), "$1"));
94782         }
94783
94784         function each$2(callback) {
94785           var cookies = doc.cookie.split(/; ?/g);
94786
94787           for (var i = cookies.length - 1; i >= 0; i--) {
94788             if (!trim(cookies[i])) {
94789               continue;
94790             }
94791
94792             var kvp = cookies[i].split('=');
94793             var key = unescape(kvp[0]);
94794             var val = unescape(kvp[1]);
94795             callback(val, key);
94796           }
94797         }
94798
94799         function write$2(key, data) {
94800           if (!key) {
94801             return;
94802           }
94803
94804           doc.cookie = escape(key) + "=" + escape(data) + "; expires=Tue, 19 Jan 2038 03:14:07 GMT; path=/";
94805         }
94806
94807         function remove$2(key) {
94808           if (!key || !_has(key)) {
94809             return;
94810           }
94811
94812           doc.cookie = escape(key) + "=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/";
94813         }
94814
94815         function clearAll$2() {
94816           each$2(function (_, key) {
94817             remove$2(key);
94818           });
94819         }
94820
94821         function _has(key) {
94822           return new RegExp("(?:^|;\\s*)" + escape(key).replace(/[\-\.\+\*]/g, "\\$&") + "\\s*\\=").test(doc.cookie);
94823         }
94824
94825         var Global = util.Global;
94826         var sessionStorage_1 = {
94827           name: 'sessionStorage',
94828           read: read$1,
94829           write: write$1,
94830           each: each$1,
94831           remove: remove$1,
94832           clearAll: clearAll$1
94833         };
94834
94835         function sessionStorage() {
94836           return Global.sessionStorage;
94837         }
94838
94839         function read$1(key) {
94840           return sessionStorage().getItem(key);
94841         }
94842
94843         function write$1(key, data) {
94844           return sessionStorage().setItem(key, data);
94845         }
94846
94847         function each$1(fn) {
94848           for (var i = sessionStorage().length - 1; i >= 0; i--) {
94849             var key = sessionStorage().key(i);
94850             fn(read$1(key), key);
94851           }
94852         }
94853
94854         function remove$1(key) {
94855           return sessionStorage().removeItem(key);
94856         }
94857
94858         function clearAll$1() {
94859           return sessionStorage().clear();
94860         }
94861
94862         // memoryStorage is a useful last fallback to ensure that the store
94863         // is functions (meaning store.get(), store.set(), etc will all function).
94864         // However, stored values will not persist when the browser navigates to
94865         // a new page or reloads the current page.
94866         var memoryStorage_1 = {
94867           name: 'memoryStorage',
94868           read: read,
94869           write: write,
94870           each: each,
94871           remove: remove,
94872           clearAll: clearAll
94873         };
94874         var memoryStorage = {};
94875
94876         function read(key) {
94877           return memoryStorage[key];
94878         }
94879
94880         function write(key, data) {
94881           memoryStorage[key] = data;
94882         }
94883
94884         function each(callback) {
94885           for (var key in memoryStorage) {
94886             if (memoryStorage.hasOwnProperty(key)) {
94887               callback(memoryStorage[key], key);
94888             }
94889           }
94890         }
94891
94892         function remove(key) {
94893           delete memoryStorage[key];
94894         }
94895
94896         function clearAll(key) {
94897           memoryStorage = {};
94898         }
94899
94900         var all = [// Listed in order of usage preference
94901         localStorage_1, oldFFGlobalStorage, oldIEUserDataStorage, cookieStorage, sessionStorage_1, memoryStorage_1];
94902
94903         /* eslint-disable */
94904         //  json2.js
94905         //  2016-10-28
94906         //  Public Domain.
94907         //  NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.
94908         //  See http://www.JSON.org/js.html
94909         //  This code should be minified before deployment.
94910         //  See http://javascript.crockford.com/jsmin.html
94911         //  USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO
94912         //  NOT CONTROL.
94913         //  This file creates a global JSON object containing two methods: stringify
94914         //  and parse. This file provides the ES5 JSON capability to ES3 systems.
94915         //  If a project might run on IE8 or earlier, then this file should be included.
94916         //  This file does nothing on ES5 systems.
94917         //      JSON.stringify(value, replacer, space)
94918         //          value       any JavaScript value, usually an object or array.
94919         //          replacer    an optional parameter that determines how object
94920         //                      values are stringified for objects. It can be a
94921         //                      function or an array of strings.
94922         //          space       an optional parameter that specifies the indentation
94923         //                      of nested structures. If it is omitted, the text will
94924         //                      be packed without extra whitespace. If it is a number,
94925         //                      it will specify the number of spaces to indent at each
94926         //                      level. If it is a string (such as "\t" or "&nbsp;"),
94927         //                      it contains the characters used to indent at each level.
94928         //          This method produces a JSON text from a JavaScript value.
94929         //          When an object value is found, if the object contains a toJSON
94930         //          method, its toJSON method will be called and the result will be
94931         //          stringified. A toJSON method does not serialize: it returns the
94932         //          value represented by the name/value pair that should be serialized,
94933         //          or undefined if nothing should be serialized. The toJSON method
94934         //          will be passed the key associated with the value, and this will be
94935         //          bound to the value.
94936         //          For example, this would serialize Dates as ISO strings.
94937         //              Date.prototype.toJSON = function (key) {
94938         //                  function f(n) {
94939         //                      // Format integers to have at least two digits.
94940         //                      return (n < 10)
94941         //                          ? "0" + n
94942         //                          : n;
94943         //                  }
94944         //                  return this.getUTCFullYear()   + "-" +
94945         //                       f(this.getUTCMonth() + 1) + "-" +
94946         //                       f(this.getUTCDate())      + "T" +
94947         //                       f(this.getUTCHours())     + ":" +
94948         //                       f(this.getUTCMinutes())   + ":" +
94949         //                       f(this.getUTCSeconds())   + "Z";
94950         //              };
94951         //          You can provide an optional replacer method. It will be passed the
94952         //          key and value of each member, with this bound to the containing
94953         //          object. The value that is returned from your method will be
94954         //          serialized. If your method returns undefined, then the member will
94955         //          be excluded from the serialization.
94956         //          If the replacer parameter is an array of strings, then it will be
94957         //          used to select the members to be serialized. It filters the results
94958         //          such that only members with keys listed in the replacer array are
94959         //          stringified.
94960         //          Values that do not have JSON representations, such as undefined or
94961         //          functions, will not be serialized. Such values in objects will be
94962         //          dropped; in arrays they will be replaced with null. You can use
94963         //          a replacer function to replace those with JSON values.
94964         //          JSON.stringify(undefined) returns undefined.
94965         //          The optional space parameter produces a stringification of the
94966         //          value that is filled with line breaks and indentation to make it
94967         //          easier to read.
94968         //          If the space parameter is a non-empty string, then that string will
94969         //          be used for indentation. If the space parameter is a number, then
94970         //          the indentation will be that many spaces.
94971         //          Example:
94972         //          text = JSON.stringify(["e", {pluribus: "unum"}]);
94973         //          // text is '["e",{"pluribus":"unum"}]'
94974         //          text = JSON.stringify(["e", {pluribus: "unum"}], null, "\t");
94975         //          // text is '[\n\t"e",\n\t{\n\t\t"pluribus": "unum"\n\t}\n]'
94976         //          text = JSON.stringify([new Date()], function (key, value) {
94977         //              return this[key] instanceof Date
94978         //                  ? "Date(" + this[key] + ")"
94979         //                  : value;
94980         //          });
94981         //          // text is '["Date(---current time---)"]'
94982         //      JSON.parse(text, reviver)
94983         //          This method parses a JSON text to produce an object or array.
94984         //          It can throw a SyntaxError exception.
94985         //          The optional reviver parameter is a function that can filter and
94986         //          transform the results. It receives each of the keys and values,
94987         //          and its return value is used instead of the original value.
94988         //          If it returns what it received, then the structure is not modified.
94989         //          If it returns undefined then the member is deleted.
94990         //          Example:
94991         //          // Parse the text. Values that look like ISO date strings will
94992         //          // be converted to Date objects.
94993         //          myData = JSON.parse(text, function (key, value) {
94994         //              var a;
94995         //              if (typeof value === "string") {
94996         //                  a =
94997         //   /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value);
94998         //                  if (a) {
94999         //                      return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4],
95000         //                          +a[5], +a[6]));
95001         //                  }
95002         //              }
95003         //              return value;
95004         //          });
95005         //          myData = JSON.parse('["Date(09/09/2001)"]', function (key, value) {
95006         //              var d;
95007         //              if (typeof value === "string" &&
95008         //                      value.slice(0, 5) === "Date(" &&
95009         //                      value.slice(-1) === ")") {
95010         //                  d = new Date(value.slice(5, -1));
95011         //                  if (d) {
95012         //                      return d;
95013         //                  }
95014         //              }
95015         //              return value;
95016         //          });
95017         //  This is a reference implementation. You are free to copy, modify, or
95018         //  redistribute.
95019
95020         /*jslint
95021             eval, for, this
95022         */
95023
95024         /*property
95025             JSON, apply, call, charCodeAt, getUTCDate, getUTCFullYear, getUTCHours,
95026             getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join,
95027             lastIndex, length, parse, prototype, push, replace, slice, stringify,
95028             test, toJSON, toString, valueOf
95029         */
95030         // Create a JSON object only if one does not already exist. We create the
95031         // methods in a closure to avoid creating global variables.
95032         if ((typeof JSON === "undefined" ? "undefined" : _typeof(JSON)) !== "object") {
95033           JSON = {};
95034         }
95035
95036         (function () {
95037
95038           var rx_one = /^[\],:{}\s]*$/;
95039           var rx_two = /\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g;
95040           var rx_three = /"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g;
95041           var rx_four = /(?:^|:|,)(?:\s*\[)+/g;
95042           var rx_escapable = /[\\"\u0000-\u001f\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g;
95043           var rx_dangerous = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g;
95044
95045           function f(n) {
95046             // Format integers to have at least two digits.
95047             return n < 10 ? "0" + n : n;
95048           }
95049
95050           function this_value() {
95051             return this.valueOf();
95052           }
95053
95054           if (typeof Date.prototype.toJSON !== "function") {
95055             Date.prototype.toJSON = function () {
95056               return isFinite(this.valueOf()) ? this.getUTCFullYear() + "-" + f(this.getUTCMonth() + 1) + "-" + f(this.getUTCDate()) + "T" + f(this.getUTCHours()) + ":" + f(this.getUTCMinutes()) + ":" + f(this.getUTCSeconds()) + "Z" : null;
95057             };
95058
95059             Boolean.prototype.toJSON = this_value;
95060             Number.prototype.toJSON = this_value;
95061             String.prototype.toJSON = this_value;
95062           }
95063
95064           var gap;
95065           var indent;
95066           var meta;
95067           var rep;
95068
95069           function quote(string) {
95070             // If the string contains no control characters, no quote characters, and no
95071             // backslash characters, then we can safely slap some quotes around it.
95072             // Otherwise we must also replace the offending characters with safe escape
95073             // sequences.
95074             rx_escapable.lastIndex = 0;
95075             return rx_escapable.test(string) ? "\"" + string.replace(rx_escapable, function (a) {
95076               var c = meta[a];
95077               return typeof c === "string" ? c : "\\u" + ("0000" + a.charCodeAt(0).toString(16)).slice(-4);
95078             }) + "\"" : "\"" + string + "\"";
95079           }
95080
95081           function str(key, holder) {
95082             // Produce a string from holder[key].
95083             var i; // The loop counter.
95084
95085             var k; // The member key.
95086
95087             var v; // The member value.
95088
95089             var length;
95090             var mind = gap;
95091             var partial;
95092             var value = holder[key]; // If the value has a toJSON method, call it to obtain a replacement value.
95093
95094             if (value && _typeof(value) === "object" && typeof value.toJSON === "function") {
95095               value = value.toJSON(key);
95096             } // If we were called with a replacer function, then call the replacer to
95097             // obtain a replacement value.
95098
95099
95100             if (typeof rep === "function") {
95101               value = rep.call(holder, key, value);
95102             } // What happens next depends on the value's type.
95103
95104
95105             switch (_typeof(value)) {
95106               case "string":
95107                 return quote(value);
95108
95109               case "number":
95110                 // JSON numbers must be finite. Encode non-finite numbers as null.
95111                 return isFinite(value) ? String(value) : "null";
95112
95113               case "boolean":
95114               case "null":
95115                 // If the value is a boolean or null, convert it to a string. Note:
95116                 // typeof null does not produce "null". The case is included here in
95117                 // the remote chance that this gets fixed someday.
95118                 return String(value);
95119               // If the type is "object", we might be dealing with an object or an array or
95120               // null.
95121
95122               case "object":
95123                 // Due to a specification blunder in ECMAScript, typeof null is "object",
95124                 // so watch out for that case.
95125                 if (!value) {
95126                   return "null";
95127                 } // Make an array to hold the partial results of stringifying this object value.
95128
95129
95130                 gap += indent;
95131                 partial = []; // Is the value an array?
95132
95133                 if (Object.prototype.toString.apply(value) === "[object Array]") {
95134                   // The value is an array. Stringify every element. Use null as a placeholder
95135                   // for non-JSON values.
95136                   length = value.length;
95137
95138                   for (i = 0; i < length; i += 1) {
95139                     partial[i] = str(i, value) || "null";
95140                   } // Join all of the elements together, separated with commas, and wrap them in
95141                   // brackets.
95142
95143
95144                   v = partial.length === 0 ? "[]" : gap ? "[\n" + gap + partial.join(",\n" + gap) + "\n" + mind + "]" : "[" + partial.join(",") + "]";
95145                   gap = mind;
95146                   return v;
95147                 } // If the replacer is an array, use it to select the members to be stringified.
95148
95149
95150                 if (rep && _typeof(rep) === "object") {
95151                   length = rep.length;
95152
95153                   for (i = 0; i < length; i += 1) {
95154                     if (typeof rep[i] === "string") {
95155                       k = rep[i];
95156                       v = str(k, value);
95157
95158                       if (v) {
95159                         partial.push(quote(k) + (gap ? ": " : ":") + v);
95160                       }
95161                     }
95162                   }
95163                 } else {
95164                   // Otherwise, iterate through all of the keys in the object.
95165                   for (k in value) {
95166                     if (Object.prototype.hasOwnProperty.call(value, k)) {
95167                       v = str(k, value);
95168
95169                       if (v) {
95170                         partial.push(quote(k) + (gap ? ": " : ":") + v);
95171                       }
95172                     }
95173                   }
95174                 } // Join all of the member texts together, separated with commas,
95175                 // and wrap them in braces.
95176
95177
95178                 v = partial.length === 0 ? "{}" : gap ? "{\n" + gap + partial.join(",\n" + gap) + "\n" + mind + "}" : "{" + partial.join(",") + "}";
95179                 gap = mind;
95180                 return v;
95181             }
95182           } // If the JSON object does not yet have a stringify method, give it one.
95183
95184
95185           if (typeof JSON.stringify !== "function") {
95186             meta = {
95187               // table of character substitutions
95188               "\b": "\\b",
95189               "\t": "\\t",
95190               "\n": "\\n",
95191               "\f": "\\f",
95192               "\r": "\\r",
95193               "\"": "\\\"",
95194               "\\": "\\\\"
95195             };
95196
95197             JSON.stringify = function (value, replacer, space) {
95198               // The stringify method takes a value and an optional replacer, and an optional
95199               // space parameter, and returns a JSON text. The replacer can be a function
95200               // that can replace values, or an array of strings that will select the keys.
95201               // A default replacer method can be provided. Use of the space parameter can
95202               // produce text that is more easily readable.
95203               var i;
95204               gap = "";
95205               indent = ""; // If the space parameter is a number, make an indent string containing that
95206               // many spaces.
95207
95208               if (typeof space === "number") {
95209                 for (i = 0; i < space; i += 1) {
95210                   indent += " ";
95211                 } // If the space parameter is a string, it will be used as the indent string.
95212
95213               } else if (typeof space === "string") {
95214                 indent = space;
95215               } // If there is a replacer, it must be a function or an array.
95216               // Otherwise, throw an error.
95217
95218
95219               rep = replacer;
95220
95221               if (replacer && typeof replacer !== "function" && (_typeof(replacer) !== "object" || typeof replacer.length !== "number")) {
95222                 throw new Error("JSON.stringify");
95223               } // Make a fake root object containing our value under the key of "".
95224               // Return the result of stringifying the value.
95225
95226
95227               return str("", {
95228                 "": value
95229               });
95230             };
95231           } // If the JSON object does not yet have a parse method, give it one.
95232
95233
95234           if (typeof JSON.parse !== "function") {
95235             JSON.parse = function (text, reviver) {
95236               // The parse method takes a text and an optional reviver function, and returns
95237               // a JavaScript value if the text is a valid JSON text.
95238               var j;
95239
95240               function walk(holder, key) {
95241                 // The walk method is used to recursively walk the resulting structure so
95242                 // that modifications can be made.
95243                 var k;
95244                 var v;
95245                 var value = holder[key];
95246
95247                 if (value && _typeof(value) === "object") {
95248                   for (k in value) {
95249                     if (Object.prototype.hasOwnProperty.call(value, k)) {
95250                       v = walk(value, k);
95251
95252                       if (v !== undefined) {
95253                         value[k] = v;
95254                       } else {
95255                         delete value[k];
95256                       }
95257                     }
95258                   }
95259                 }
95260
95261                 return reviver.call(holder, key, value);
95262               } // Parsing happens in four stages. In the first stage, we replace certain
95263               // Unicode characters with escape sequences. JavaScript handles many characters
95264               // incorrectly, either silently deleting them, or treating them as line endings.
95265
95266
95267               text = String(text);
95268               rx_dangerous.lastIndex = 0;
95269
95270               if (rx_dangerous.test(text)) {
95271                 text = text.replace(rx_dangerous, function (a) {
95272                   return "\\u" + ("0000" + a.charCodeAt(0).toString(16)).slice(-4);
95273                 });
95274               } // In the second stage, we run the text against regular expressions that look
95275               // for non-JSON patterns. We are especially concerned with "()" and "new"
95276               // because they can cause invocation, and "=" because it can cause mutation.
95277               // But just to be safe, we want to reject all unexpected forms.
95278               // We split the second stage into 4 regexp operations in order to work around
95279               // crippling inefficiencies in IE's and Safari's regexp engines. First we
95280               // replace the JSON backslash pairs with "@" (a non-JSON character). Second, we
95281               // replace all simple value tokens with "]" characters. Third, we delete all
95282               // open brackets that follow a colon or comma or that begin the text. Finally,
95283               // we look to see that the remaining characters are only whitespace or "]" or
95284               // "," or ":" or "{" or "}". If that is so, then the text is safe for eval.
95285
95286
95287               if (rx_one.test(text.replace(rx_two, "@").replace(rx_three, "]").replace(rx_four, ""))) {
95288                 // In the third stage we use the eval function to compile the text into a
95289                 // JavaScript structure. The "{" operator is subject to a syntactic ambiguity
95290                 // in JavaScript: it can begin a block or an object literal. We wrap the text
95291                 // in parens to eliminate the ambiguity.
95292                 j = eval("(" + text + ")"); // In the optional fourth stage, we recursively walk the new structure, passing
95293                 // each name/value pair to a reviver function for possible transformation.
95294
95295                 return typeof reviver === "function" ? walk({
95296                   "": j
95297                 }, "") : j;
95298               } // If the text is not JSON parseable, then a SyntaxError is thrown.
95299
95300
95301               throw new SyntaxError("JSON.parse");
95302             };
95303           }
95304         })();
95305
95306         var json2 = json2Plugin;
95307
95308         function json2Plugin() {
95309           return {};
95310         }
95311
95312         var plugins = [json2];
95313         var store_legacy = storeEngine.createStore(all, plugins);
95314
95315         var immutable = extend;
95316         var hasOwnProperty = Object.prototype.hasOwnProperty;
95317
95318         function extend() {
95319           var target = {};
95320
95321           for (var i = 0; i < arguments.length; i++) {
95322             var source = arguments[i];
95323
95324             for (var key in source) {
95325               if (hasOwnProperty.call(source, key)) {
95326                 target[key] = source[key];
95327               }
95328             }
95329           }
95330
95331           return target;
95332         }
95333
95334         //
95335         // This code is only compatible with IE10+ because the [XDomainRequest](http://bit.ly/LfO7xo)
95336         // object, IE<10's idea of [CORS](http://en.wikipedia.org/wiki/Cross-origin_resource_sharing),
95337         // does not support custom headers, which this uses everywhere.
95338
95339
95340         var osmAuth = function osmAuth(o) {
95341           var oauth = {}; // authenticated users will also have a request token secret, but it's
95342           // not used in transactions with the server
95343
95344           oauth.authenticated = function () {
95345             return !!(token('oauth_token') && token('oauth_token_secret'));
95346           };
95347
95348           oauth.logout = function () {
95349             token('oauth_token', '');
95350             token('oauth_token_secret', '');
95351             token('oauth_request_token_secret', '');
95352             return oauth;
95353           }; // TODO: detect lack of click event
95354
95355
95356           oauth.authenticate = function (callback) {
95357             if (oauth.authenticated()) return callback();
95358             oauth.logout(); // ## Getting a request token
95359
95360             var params = timenonce(getAuth(o)),
95361                 url = o.url + '/oauth/request_token';
95362             params.oauth_signature = ohauth_1.signature(o.oauth_secret, '', ohauth_1.baseString('POST', url, params));
95363
95364             if (!o.singlepage) {
95365               // Create a 600x550 popup window in the center of the screen
95366               var w = 600,
95367                   h = 550,
95368                   settings = [['width', w], ['height', h], ['left', screen.width / 2 - w / 2], ['top', screen.height / 2 - h / 2]].map(function (x) {
95369                 return x.join('=');
95370               }).join(','),
95371                   popup = window.open('about:blank', 'oauth_window', settings);
95372               oauth.popupWindow = popup;
95373
95374               if (!popup) {
95375                 var error = new Error('Popup was blocked');
95376                 error.status = 'popup-blocked';
95377                 throw error;
95378               }
95379             } // Request a request token. When this is complete, the popup
95380             // window is redirected to OSM's authorization page.
95381
95382
95383             ohauth_1.xhr('POST', url, params, null, {}, reqTokenDone);
95384             o.loading();
95385
95386             function reqTokenDone(err, xhr) {
95387               o.done();
95388               if (err) return callback(err);
95389               var resp = ohauth_1.stringQs(xhr.response);
95390               token('oauth_request_token_secret', resp.oauth_token_secret);
95391               var authorize_url = o.url + '/oauth/authorize?' + ohauth_1.qsString({
95392                 oauth_token: resp.oauth_token,
95393                 oauth_callback: resolveUrl(o.landing)
95394               });
95395
95396               if (o.singlepage) {
95397                 location.href = authorize_url;
95398               } else {
95399                 popup.location = authorize_url;
95400               }
95401             } // Called by a function in a landing page, in the popup window. The
95402             // window closes itself.
95403
95404
95405             window.authComplete = function (token) {
95406               var oauth_token = ohauth_1.stringQs(token.split('?')[1]);
95407               get_access_token(oauth_token.oauth_token);
95408               delete window.authComplete;
95409             }; // ## Getting an request token
95410             //
95411             // At this point we have an `oauth_token`, brought in from a function
95412             // call on a landing page popup.
95413
95414
95415             function get_access_token(oauth_token) {
95416               var url = o.url + '/oauth/access_token',
95417                   params = timenonce(getAuth(o)),
95418                   request_token_secret = token('oauth_request_token_secret');
95419               params.oauth_token = oauth_token;
95420               params.oauth_signature = ohauth_1.signature(o.oauth_secret, request_token_secret, ohauth_1.baseString('POST', url, params)); // ## Getting an access token
95421               //
95422               // The final token required for authentication. At this point
95423               // we have a `request token secret`
95424
95425               ohauth_1.xhr('POST', url, params, null, {}, accessTokenDone);
95426               o.loading();
95427             }
95428
95429             function accessTokenDone(err, xhr) {
95430               o.done();
95431               if (err) return callback(err);
95432               var access_token = ohauth_1.stringQs(xhr.response);
95433               token('oauth_token', access_token.oauth_token);
95434               token('oauth_token_secret', access_token.oauth_token_secret);
95435               callback(null, oauth);
95436             }
95437           };
95438
95439           oauth.bringPopupWindowToFront = function () {
95440             var brougtPopupToFront = false;
95441
95442             try {
95443               // This may cause a cross-origin error:
95444               // `DOMException: Blocked a frame with origin "..." from accessing a cross-origin frame.`
95445               if (oauth.popupWindow && !oauth.popupWindow.closed) {
95446                 oauth.popupWindow.focus();
95447                 brougtPopupToFront = true;
95448               }
95449             } catch (err) {// Bringing popup window to front failed (probably because of the cross-origin error mentioned above)
95450             }
95451
95452             return brougtPopupToFront;
95453           };
95454
95455           oauth.bootstrapToken = function (oauth_token, callback) {
95456             // ## Getting an request token
95457             // At this point we have an `oauth_token`, brought in from a function
95458             // call on a landing page popup.
95459             function get_access_token(oauth_token) {
95460               var url = o.url + '/oauth/access_token',
95461                   params = timenonce(getAuth(o)),
95462                   request_token_secret = token('oauth_request_token_secret');
95463               params.oauth_token = oauth_token;
95464               params.oauth_signature = ohauth_1.signature(o.oauth_secret, request_token_secret, ohauth_1.baseString('POST', url, params)); // ## Getting an access token
95465               // The final token required for authentication. At this point
95466               // we have a `request token secret`
95467
95468               ohauth_1.xhr('POST', url, params, null, {}, accessTokenDone);
95469               o.loading();
95470             }
95471
95472             function accessTokenDone(err, xhr) {
95473               o.done();
95474               if (err) return callback(err);
95475               var access_token = ohauth_1.stringQs(xhr.response);
95476               token('oauth_token', access_token.oauth_token);
95477               token('oauth_token_secret', access_token.oauth_token_secret);
95478               callback(null, oauth);
95479             }
95480
95481             get_access_token(oauth_token);
95482           }; // # xhr
95483           //
95484           // A single XMLHttpRequest wrapper that does authenticated calls if the
95485           // user has logged in.
95486
95487
95488           oauth.xhr = function (options, callback) {
95489             if (!oauth.authenticated()) {
95490               if (o.auto) {
95491                 return oauth.authenticate(run);
95492               } else {
95493                 callback('not authenticated', null);
95494                 return;
95495               }
95496             } else {
95497               return run();
95498             }
95499
95500             function run() {
95501               var params = timenonce(getAuth(o)),
95502                   oauth_token_secret = token('oauth_token_secret'),
95503                   url = options.prefix !== false ? o.url + options.path : options.path,
95504                   url_parts = url.replace(/#.*$/, '').split('?', 2),
95505                   base_url = url_parts[0],
95506                   query = url_parts.length === 2 ? url_parts[1] : ''; // https://tools.ietf.org/html/rfc5849#section-3.4.1.3.1
95507
95508               if ((!options.options || !options.options.header || options.options.header['Content-Type'] === 'application/x-www-form-urlencoded') && options.content) {
95509                 params = immutable(params, ohauth_1.stringQs(options.content));
95510               }
95511
95512               params.oauth_token = token('oauth_token');
95513               params.oauth_signature = ohauth_1.signature(o.oauth_secret, oauth_token_secret, ohauth_1.baseString(options.method, base_url, immutable(params, ohauth_1.stringQs(query))));
95514               return ohauth_1.xhr(options.method, url, params, options.content, options.options, done);
95515             }
95516
95517             function done(err, xhr) {
95518               if (err) return callback(err);else if (xhr.responseXML) return callback(err, xhr.responseXML);else return callback(err, xhr.response);
95519             }
95520           }; // pre-authorize this object, if we can just get a token and token_secret
95521           // from the start
95522
95523
95524           oauth.preauth = function (c) {
95525             if (!c) return;
95526             if (c.oauth_token) token('oauth_token', c.oauth_token);
95527             if (c.oauth_token_secret) token('oauth_token_secret', c.oauth_token_secret);
95528             return oauth;
95529           };
95530
95531           oauth.options = function (_) {
95532             if (!arguments.length) return o;
95533             o = _;
95534             o.url = o.url || 'https://www.openstreetmap.org';
95535             o.landing = o.landing || 'land.html';
95536             o.singlepage = o.singlepage || false; // Optional loading and loading-done functions for nice UI feedback.
95537             // by default, no-ops
95538
95539             o.loading = o.loading || function () {};
95540
95541             o.done = o.done || function () {};
95542
95543             return oauth.preauth(o);
95544           }; // 'stamp' an authentication object from `getAuth()`
95545           // with a [nonce](http://en.wikipedia.org/wiki/Cryptographic_nonce)
95546           // and timestamp
95547
95548
95549           function timenonce(o) {
95550             o.oauth_timestamp = ohauth_1.timestamp();
95551             o.oauth_nonce = ohauth_1.nonce();
95552             return o;
95553           } // get/set tokens. These are prefixed with the base URL so that `osm-auth`
95554           // can be used with multiple APIs and the keys in `localStorage`
95555           // will not clash
95556
95557
95558           var token;
95559
95560           if (store_legacy.enabled) {
95561             token = function token(x, y) {
95562               if (arguments.length === 1) return store_legacy.get(o.url + x);else if (arguments.length === 2) return store_legacy.set(o.url + x, y);
95563             };
95564           } else {
95565             var storage = {};
95566
95567             token = function token(x, y) {
95568               if (arguments.length === 1) return storage[o.url + x];else if (arguments.length === 2) return storage[o.url + x] = y;
95569             };
95570           } // Get an authentication object. If you just add and remove properties
95571           // from a single object, you'll need to use `delete` to make sure that
95572           // it doesn't contain undesired properties for authentication
95573
95574
95575           function getAuth(o) {
95576             return {
95577               oauth_consumer_key: o.oauth_consumer_key,
95578               oauth_signature_method: 'HMAC-SHA1'
95579             };
95580           } // potentially pre-authorize
95581
95582
95583           oauth.options(o);
95584           return oauth;
95585         };
95586
95587         var tiler$2 = utilTiler();
95588         var dispatch$2 = dispatch$8('apiStatusChange', 'authLoading', 'authDone', 'change', 'loading', 'loaded', 'loadedNotes');
95589         var urlroot = 'https://www.openstreetmap.org';
95590         var oauth = osmAuth({
95591           url: urlroot,
95592           oauth_consumer_key: '5A043yRSEugj4DJ5TljuapfnrflWDte8jTOcWLlT',
95593           oauth_secret: 'aB3jKq1TRsCOUrfOIZ6oQMEDmv2ptV76PA54NGLL',
95594           loading: authLoading,
95595           done: authDone
95596         }); // hardcode default block of Google Maps
95597
95598         var _imageryBlocklists = [/.*\.google(apis)?\..*\/(vt|kh)[\?\/].*([xyz]=.*){3}.*/];
95599         var _tileCache = {
95600           toLoad: {},
95601           loaded: {},
95602           inflight: {},
95603           seen: {},
95604           rtree: new RBush()
95605         };
95606         var _noteCache = {
95607           toLoad: {},
95608           loaded: {},
95609           inflight: {},
95610           inflightPost: {},
95611           note: {},
95612           closed: {},
95613           rtree: new RBush()
95614         };
95615         var _userCache = {
95616           toLoad: {},
95617           user: {}
95618         };
95619
95620         var _cachedApiStatus;
95621
95622         var _changeset = {};
95623
95624         var _deferred = new Set();
95625
95626         var _connectionID = 1;
95627         var _tileZoom = 16;
95628         var _noteZoom = 12;
95629
95630         var _rateLimitError;
95631
95632         var _userChangesets;
95633
95634         var _userDetails;
95635
95636         var _off; // set a default but also load this from the API status
95637
95638
95639         var _maxWayNodes = 2000;
95640
95641         function authLoading() {
95642           dispatch$2.call('authLoading');
95643         }
95644
95645         function authDone() {
95646           dispatch$2.call('authDone');
95647         }
95648
95649         function abortRequest$2(controllerOrXHR) {
95650           if (controllerOrXHR) {
95651             controllerOrXHR.abort();
95652           }
95653         }
95654
95655         function hasInflightRequests(cache) {
95656           return Object.keys(cache.inflight).length;
95657         }
95658
95659         function abortUnwantedRequests(cache, visibleTiles) {
95660           Object.keys(cache.inflight).forEach(function (k) {
95661             if (cache.toLoad[k]) return;
95662             if (visibleTiles.find(function (tile) {
95663               return k === tile.id;
95664             })) return;
95665             abortRequest$2(cache.inflight[k]);
95666             delete cache.inflight[k];
95667           });
95668         }
95669
95670         function getLoc(attrs) {
95671           var lon = attrs.lon && attrs.lon.value;
95672           var lat = attrs.lat && attrs.lat.value;
95673           return [parseFloat(lon), parseFloat(lat)];
95674         }
95675
95676         function getNodes(obj) {
95677           var elems = obj.getElementsByTagName('nd');
95678           var nodes = new Array(elems.length);
95679
95680           for (var i = 0, l = elems.length; i < l; i++) {
95681             nodes[i] = 'n' + elems[i].attributes.ref.value;
95682           }
95683
95684           return nodes;
95685         }
95686
95687         function getNodesJSON(obj) {
95688           var elems = obj.nodes;
95689           var nodes = new Array(elems.length);
95690
95691           for (var i = 0, l = elems.length; i < l; i++) {
95692             nodes[i] = 'n' + elems[i];
95693           }
95694
95695           return nodes;
95696         }
95697
95698         function getTags(obj) {
95699           var elems = obj.getElementsByTagName('tag');
95700           var tags = {};
95701
95702           for (var i = 0, l = elems.length; i < l; i++) {
95703             var attrs = elems[i].attributes;
95704             tags[attrs.k.value] = attrs.v.value;
95705           }
95706
95707           return tags;
95708         }
95709
95710         function getMembers(obj) {
95711           var elems = obj.getElementsByTagName('member');
95712           var members = new Array(elems.length);
95713
95714           for (var i = 0, l = elems.length; i < l; i++) {
95715             var attrs = elems[i].attributes;
95716             members[i] = {
95717               id: attrs.type.value[0] + attrs.ref.value,
95718               type: attrs.type.value,
95719               role: attrs.role.value
95720             };
95721           }
95722
95723           return members;
95724         }
95725
95726         function getMembersJSON(obj) {
95727           var elems = obj.members;
95728           var members = new Array(elems.length);
95729
95730           for (var i = 0, l = elems.length; i < l; i++) {
95731             var attrs = elems[i];
95732             members[i] = {
95733               id: attrs.type[0] + attrs.ref,
95734               type: attrs.type,
95735               role: attrs.role
95736             };
95737           }
95738
95739           return members;
95740         }
95741
95742         function getVisible(attrs) {
95743           return !attrs.visible || attrs.visible.value !== 'false';
95744         }
95745
95746         function parseComments(comments) {
95747           var parsedComments = []; // for each comment
95748
95749           for (var i = 0; i < comments.length; i++) {
95750             var comment = comments[i];
95751
95752             if (comment.nodeName === 'comment') {
95753               var childNodes = comment.childNodes;
95754               var parsedComment = {};
95755
95756               for (var j = 0; j < childNodes.length; j++) {
95757                 var node = childNodes[j];
95758                 var nodeName = node.nodeName;
95759                 if (nodeName === '#text') continue;
95760                 parsedComment[nodeName] = node.textContent;
95761
95762                 if (nodeName === 'uid') {
95763                   var uid = node.textContent;
95764
95765                   if (uid && !_userCache.user[uid]) {
95766                     _userCache.toLoad[uid] = true;
95767                   }
95768                 }
95769               }
95770
95771               if (parsedComment) {
95772                 parsedComments.push(parsedComment);
95773               }
95774             }
95775           }
95776
95777           return parsedComments;
95778         }
95779
95780         function encodeNoteRtree(note) {
95781           return {
95782             minX: note.loc[0],
95783             minY: note.loc[1],
95784             maxX: note.loc[0],
95785             maxY: note.loc[1],
95786             data: note
95787           };
95788         }
95789
95790         var jsonparsers = {
95791           node: function nodeData(obj, uid) {
95792             return new osmNode({
95793               id: uid,
95794               visible: typeof obj.visible === 'boolean' ? obj.visible : true,
95795               version: obj.version && obj.version.toString(),
95796               changeset: obj.changeset && obj.changeset.toString(),
95797               timestamp: obj.timestamp,
95798               user: obj.user,
95799               uid: obj.uid && obj.uid.toString(),
95800               loc: [parseFloat(obj.lon), parseFloat(obj.lat)],
95801               tags: obj.tags
95802             });
95803           },
95804           way: function wayData(obj, uid) {
95805             return new osmWay({
95806               id: uid,
95807               visible: typeof obj.visible === 'boolean' ? obj.visible : true,
95808               version: obj.version && obj.version.toString(),
95809               changeset: obj.changeset && obj.changeset.toString(),
95810               timestamp: obj.timestamp,
95811               user: obj.user,
95812               uid: obj.uid && obj.uid.toString(),
95813               tags: obj.tags,
95814               nodes: getNodesJSON(obj)
95815             });
95816           },
95817           relation: function relationData(obj, uid) {
95818             return new osmRelation({
95819               id: uid,
95820               visible: typeof obj.visible === 'boolean' ? obj.visible : true,
95821               version: obj.version && obj.version.toString(),
95822               changeset: obj.changeset && obj.changeset.toString(),
95823               timestamp: obj.timestamp,
95824               user: obj.user,
95825               uid: obj.uid && obj.uid.toString(),
95826               tags: obj.tags,
95827               members: getMembersJSON(obj)
95828             });
95829           },
95830           user: function parseUser(obj, uid) {
95831             return {
95832               id: uid,
95833               display_name: obj.display_name,
95834               account_created: obj.account_created,
95835               image_url: obj.img && obj.img.href,
95836               changesets_count: obj.changesets && obj.changesets.count && obj.changesets.count.toString() || '0',
95837               active_blocks: obj.blocks && obj.blocks.received && obj.blocks.received.active && obj.blocks.received.active.toString() || '0'
95838             };
95839           }
95840         };
95841
95842         function parseJSON(payload, callback, options) {
95843           options = Object.assign({
95844             skipSeen: true
95845           }, options);
95846
95847           if (!payload) {
95848             return callback({
95849               message: 'No JSON',
95850               status: -1
95851             });
95852           }
95853
95854           var json = payload;
95855           if (_typeof(json) !== 'object') json = JSON.parse(payload);
95856           if (!json.elements) return callback({
95857             message: 'No JSON',
95858             status: -1
95859           });
95860           var children = json.elements;
95861           var handle = window.requestIdleCallback(function () {
95862             _deferred["delete"](handle);
95863
95864             var results = [];
95865             var result;
95866
95867             for (var i = 0; i < children.length; i++) {
95868               result = parseChild(children[i]);
95869               if (result) results.push(result);
95870             }
95871
95872             callback(null, results);
95873           });
95874
95875           _deferred.add(handle);
95876
95877           function parseChild(child) {
95878             var parser = jsonparsers[child.type];
95879             if (!parser) return null;
95880             var uid;
95881             uid = osmEntity.id.fromOSM(child.type, child.id);
95882
95883             if (options.skipSeen) {
95884               if (_tileCache.seen[uid]) return null; // avoid reparsing a "seen" entity
95885
95886               _tileCache.seen[uid] = true;
95887             }
95888
95889             return parser(child, uid);
95890           }
95891         }
95892
95893         function parseUserJSON(payload, callback, options) {
95894           options = Object.assign({
95895             skipSeen: true
95896           }, options);
95897
95898           if (!payload) {
95899             return callback({
95900               message: 'No JSON',
95901               status: -1
95902             });
95903           }
95904
95905           var json = payload;
95906           if (_typeof(json) !== 'object') json = JSON.parse(payload);
95907           if (!json.users && !json.user) return callback({
95908             message: 'No JSON',
95909             status: -1
95910           });
95911           var objs = json.users || [json];
95912           var handle = window.requestIdleCallback(function () {
95913             _deferred["delete"](handle);
95914
95915             var results = [];
95916             var result;
95917
95918             for (var i = 0; i < objs.length; i++) {
95919               result = parseObj(objs[i]);
95920               if (result) results.push(result);
95921             }
95922
95923             callback(null, results);
95924           });
95925
95926           _deferred.add(handle);
95927
95928           function parseObj(obj) {
95929             var uid = obj.user.id && obj.user.id.toString();
95930
95931             if (options.skipSeen && _userCache.user[uid]) {
95932               delete _userCache.toLoad[uid];
95933               return null;
95934             }
95935
95936             var user = jsonparsers.user(obj.user, uid);
95937             _userCache.user[uid] = user;
95938             delete _userCache.toLoad[uid];
95939             return user;
95940           }
95941         }
95942
95943         var parsers = {
95944           node: function nodeData(obj, uid) {
95945             var attrs = obj.attributes;
95946             return new osmNode({
95947               id: uid,
95948               visible: getVisible(attrs),
95949               version: attrs.version.value,
95950               changeset: attrs.changeset && attrs.changeset.value,
95951               timestamp: attrs.timestamp && attrs.timestamp.value,
95952               user: attrs.user && attrs.user.value,
95953               uid: attrs.uid && attrs.uid.value,
95954               loc: getLoc(attrs),
95955               tags: getTags(obj)
95956             });
95957           },
95958           way: function wayData(obj, uid) {
95959             var attrs = obj.attributes;
95960             return new osmWay({
95961               id: uid,
95962               visible: getVisible(attrs),
95963               version: attrs.version.value,
95964               changeset: attrs.changeset && attrs.changeset.value,
95965               timestamp: attrs.timestamp && attrs.timestamp.value,
95966               user: attrs.user && attrs.user.value,
95967               uid: attrs.uid && attrs.uid.value,
95968               tags: getTags(obj),
95969               nodes: getNodes(obj)
95970             });
95971           },
95972           relation: function relationData(obj, uid) {
95973             var attrs = obj.attributes;
95974             return new osmRelation({
95975               id: uid,
95976               visible: getVisible(attrs),
95977               version: attrs.version.value,
95978               changeset: attrs.changeset && attrs.changeset.value,
95979               timestamp: attrs.timestamp && attrs.timestamp.value,
95980               user: attrs.user && attrs.user.value,
95981               uid: attrs.uid && attrs.uid.value,
95982               tags: getTags(obj),
95983               members: getMembers(obj)
95984             });
95985           },
95986           note: function parseNote(obj, uid) {
95987             var attrs = obj.attributes;
95988             var childNodes = obj.childNodes;
95989             var props = {};
95990             props.id = uid;
95991             props.loc = getLoc(attrs); // if notes are coincident, move them apart slightly
95992
95993             var coincident = false;
95994             var epsilon = 0.00001;
95995
95996             do {
95997               if (coincident) {
95998                 props.loc = geoVecAdd(props.loc, [epsilon, epsilon]);
95999               }
96000
96001               var bbox = geoExtent(props.loc).bbox();
96002               coincident = _noteCache.rtree.search(bbox).length;
96003             } while (coincident); // parse note contents
96004
96005
96006             for (var i = 0; i < childNodes.length; i++) {
96007               var node = childNodes[i];
96008               var nodeName = node.nodeName;
96009               if (nodeName === '#text') continue; // if the element is comments, parse the comments
96010
96011               if (nodeName === 'comments') {
96012                 props[nodeName] = parseComments(node.childNodes);
96013               } else {
96014                 props[nodeName] = node.textContent;
96015               }
96016             }
96017
96018             var note = new osmNote(props);
96019             var item = encodeNoteRtree(note);
96020             _noteCache.note[note.id] = note;
96021
96022             _noteCache.rtree.insert(item);
96023
96024             return note;
96025           },
96026           user: function parseUser(obj, uid) {
96027             var attrs = obj.attributes;
96028             var user = {
96029               id: uid,
96030               display_name: attrs.display_name && attrs.display_name.value,
96031               account_created: attrs.account_created && attrs.account_created.value,
96032               changesets_count: '0',
96033               active_blocks: '0'
96034             };
96035             var img = obj.getElementsByTagName('img');
96036
96037             if (img && img[0] && img[0].getAttribute('href')) {
96038               user.image_url = img[0].getAttribute('href');
96039             }
96040
96041             var changesets = obj.getElementsByTagName('changesets');
96042
96043             if (changesets && changesets[0] && changesets[0].getAttribute('count')) {
96044               user.changesets_count = changesets[0].getAttribute('count');
96045             }
96046
96047             var blocks = obj.getElementsByTagName('blocks');
96048
96049             if (blocks && blocks[0]) {
96050               var received = blocks[0].getElementsByTagName('received');
96051
96052               if (received && received[0] && received[0].getAttribute('active')) {
96053                 user.active_blocks = received[0].getAttribute('active');
96054               }
96055             }
96056
96057             _userCache.user[uid] = user;
96058             delete _userCache.toLoad[uid];
96059             return user;
96060           }
96061         };
96062
96063         function parseXML(xml, callback, options) {
96064           options = Object.assign({
96065             skipSeen: true
96066           }, options);
96067
96068           if (!xml || !xml.childNodes) {
96069             return callback({
96070               message: 'No XML',
96071               status: -1
96072             });
96073           }
96074
96075           var root = xml.childNodes[0];
96076           var children = root.childNodes;
96077           var handle = window.requestIdleCallback(function () {
96078             _deferred["delete"](handle);
96079
96080             var results = [];
96081             var result;
96082
96083             for (var i = 0; i < children.length; i++) {
96084               result = parseChild(children[i]);
96085               if (result) results.push(result);
96086             }
96087
96088             callback(null, results);
96089           });
96090
96091           _deferred.add(handle);
96092
96093           function parseChild(child) {
96094             var parser = parsers[child.nodeName];
96095             if (!parser) return null;
96096             var uid;
96097
96098             if (child.nodeName === 'user') {
96099               uid = child.attributes.id.value;
96100
96101               if (options.skipSeen && _userCache.user[uid]) {
96102                 delete _userCache.toLoad[uid];
96103                 return null;
96104               }
96105             } else if (child.nodeName === 'note') {
96106               uid = child.getElementsByTagName('id')[0].textContent;
96107             } else {
96108               uid = osmEntity.id.fromOSM(child.nodeName, child.attributes.id.value);
96109
96110               if (options.skipSeen) {
96111                 if (_tileCache.seen[uid]) return null; // avoid reparsing a "seen" entity
96112
96113                 _tileCache.seen[uid] = true;
96114               }
96115             }
96116
96117             return parser(child, uid);
96118           }
96119         } // replace or remove note from rtree
96120
96121
96122         function updateRtree(item, replace) {
96123           _noteCache.rtree.remove(item, function isEql(a, b) {
96124             return a.data.id === b.data.id;
96125           });
96126
96127           if (replace) {
96128             _noteCache.rtree.insert(item);
96129           }
96130         }
96131
96132         function wrapcb(thisArg, callback, cid) {
96133           return function (err, result) {
96134             if (err) {
96135               // 400 Bad Request, 401 Unauthorized, 403 Forbidden..
96136               if (err.status === 400 || err.status === 401 || err.status === 403) {
96137                 thisArg.logout();
96138               }
96139
96140               return callback.call(thisArg, err);
96141             } else if (thisArg.getConnectionId() !== cid) {
96142               return callback.call(thisArg, {
96143                 message: 'Connection Switched',
96144                 status: -1
96145               });
96146             } else {
96147               return callback.call(thisArg, err, result);
96148             }
96149           };
96150         }
96151
96152         var serviceOsm = {
96153           init: function init() {
96154             utilRebind(this, dispatch$2, 'on');
96155           },
96156           reset: function reset() {
96157             Array.from(_deferred).forEach(function (handle) {
96158               window.cancelIdleCallback(handle);
96159
96160               _deferred["delete"](handle);
96161             });
96162             _connectionID++;
96163             _userChangesets = undefined;
96164             _userDetails = undefined;
96165             _rateLimitError = undefined;
96166             Object.values(_tileCache.inflight).forEach(abortRequest$2);
96167             Object.values(_noteCache.inflight).forEach(abortRequest$2);
96168             Object.values(_noteCache.inflightPost).forEach(abortRequest$2);
96169             if (_changeset.inflight) abortRequest$2(_changeset.inflight);
96170             _tileCache = {
96171               toLoad: {},
96172               loaded: {},
96173               inflight: {},
96174               seen: {},
96175               rtree: new RBush()
96176             };
96177             _noteCache = {
96178               toLoad: {},
96179               loaded: {},
96180               inflight: {},
96181               inflightPost: {},
96182               note: {},
96183               closed: {},
96184               rtree: new RBush()
96185             };
96186             _userCache = {
96187               toLoad: {},
96188               user: {}
96189             };
96190             _cachedApiStatus = undefined;
96191             _changeset = {};
96192             return this;
96193           },
96194           getConnectionId: function getConnectionId() {
96195             return _connectionID;
96196           },
96197           changesetURL: function changesetURL(changesetID) {
96198             return urlroot + '/changeset/' + changesetID;
96199           },
96200           changesetsURL: function changesetsURL(center, zoom) {
96201             var precision = Math.max(0, Math.ceil(Math.log(zoom) / Math.LN2));
96202             return urlroot + '/history#map=' + Math.floor(zoom) + '/' + center[1].toFixed(precision) + '/' + center[0].toFixed(precision);
96203           },
96204           entityURL: function entityURL(entity) {
96205             return urlroot + '/' + entity.type + '/' + entity.osmId();
96206           },
96207           historyURL: function historyURL(entity) {
96208             return urlroot + '/' + entity.type + '/' + entity.osmId() + '/history';
96209           },
96210           userURL: function userURL(username) {
96211             return urlroot + '/user/' + username;
96212           },
96213           noteURL: function noteURL(note) {
96214             return urlroot + '/note/' + note.id;
96215           },
96216           noteReportURL: function noteReportURL(note) {
96217             return urlroot + '/reports/new?reportable_type=Note&reportable_id=' + note.id;
96218           },
96219           // Generic method to load data from the OSM API
96220           // Can handle either auth or unauth calls.
96221           loadFromAPI: function loadFromAPI(path, callback, options) {
96222             options = Object.assign({
96223               skipSeen: true
96224             }, options);
96225             var that = this;
96226             var cid = _connectionID;
96227
96228             function done(err, payload) {
96229               if (that.getConnectionId() !== cid) {
96230                 if (callback) callback({
96231                   message: 'Connection Switched',
96232                   status: -1
96233                 });
96234                 return;
96235               }
96236
96237               var isAuthenticated = that.authenticated(); // 400 Bad Request, 401 Unauthorized, 403 Forbidden
96238               // Logout and retry the request..
96239
96240               if (isAuthenticated && err && err.status && (err.status === 400 || err.status === 401 || err.status === 403)) {
96241                 that.logout();
96242                 that.loadFromAPI(path, callback, options); // else, no retry..
96243               } else {
96244                 // 509 Bandwidth Limit Exceeded, 429 Too Many Requests
96245                 // Set the rateLimitError flag and trigger a warning..
96246                 if (!isAuthenticated && !_rateLimitError && err && err.status && (err.status === 509 || err.status === 429)) {
96247                   _rateLimitError = err;
96248                   dispatch$2.call('change');
96249                   that.reloadApiStatus();
96250                 } else if (err && _cachedApiStatus === 'online' || !err && _cachedApiStatus !== 'online') {
96251                   // If the response's error state doesn't match the status,
96252                   // it's likely we lost or gained the connection so reload the status
96253                   that.reloadApiStatus();
96254                 }
96255
96256                 if (callback) {
96257                   if (err) {
96258                     return callback(err);
96259                   } else {
96260                     if (path.indexOf('.json') !== -1) {
96261                       return parseJSON(payload, callback, options);
96262                     } else {
96263                       return parseXML(payload, callback, options);
96264                     }
96265                   }
96266                 }
96267               }
96268             }
96269
96270             if (this.authenticated()) {
96271               return oauth.xhr({
96272                 method: 'GET',
96273                 path: path
96274               }, done);
96275             } else {
96276               var url = urlroot + path;
96277               var controller = new AbortController();
96278               var fn;
96279
96280               if (path.indexOf('.json') !== -1) {
96281                 fn = d3_json;
96282               } else {
96283                 fn = d3_xml;
96284               }
96285
96286               fn(url, {
96287                 signal: controller.signal
96288               }).then(function (data) {
96289                 done(null, data);
96290               })["catch"](function (err) {
96291                 if (err.name === 'AbortError') return; // d3-fetch includes status in the error message,
96292                 // but we can't access the response itself
96293                 // https://github.com/d3/d3-fetch/issues/27
96294
96295                 var match = err.message.match(/^\d{3}/);
96296
96297                 if (match) {
96298                   done({
96299                     status: +match[0],
96300                     statusText: err.message
96301                   });
96302                 } else {
96303                   done(err.message);
96304                 }
96305               });
96306               return controller;
96307             }
96308           },
96309           // Load a single entity by id (ways and relations use the `/full` call to include
96310           // nodes and members). Parent relations are not included, see `loadEntityRelations`.
96311           // GET /api/0.6/node/#id
96312           // GET /api/0.6/[way|relation]/#id/full
96313           loadEntity: function loadEntity(id, callback) {
96314             var type = osmEntity.id.type(id);
96315             var osmID = osmEntity.id.toOSM(id);
96316             var options = {
96317               skipSeen: false
96318             };
96319             this.loadFromAPI('/api/0.6/' + type + '/' + osmID + (type !== 'node' ? '/full' : '') + '.json', function (err, entities) {
96320               if (callback) callback(err, {
96321                 data: entities
96322               });
96323             }, options);
96324           },
96325           // Load a single entity with a specific version
96326           // GET /api/0.6/[node|way|relation]/#id/#version
96327           loadEntityVersion: function loadEntityVersion(id, version, callback) {
96328             var type = osmEntity.id.type(id);
96329             var osmID = osmEntity.id.toOSM(id);
96330             var options = {
96331               skipSeen: false
96332             };
96333             this.loadFromAPI('/api/0.6/' + type + '/' + osmID + '/' + version + '.json', function (err, entities) {
96334               if (callback) callback(err, {
96335                 data: entities
96336               });
96337             }, options);
96338           },
96339           // Load the relations of a single entity with the given.
96340           // GET /api/0.6/[node|way|relation]/#id/relations
96341           loadEntityRelations: function loadEntityRelations(id, callback) {
96342             var type = osmEntity.id.type(id);
96343             var osmID = osmEntity.id.toOSM(id);
96344             var options = {
96345               skipSeen: false
96346             };
96347             this.loadFromAPI('/api/0.6/' + type + '/' + osmID + '/relations.json', function (err, entities) {
96348               if (callback) callback(err, {
96349                 data: entities
96350               });
96351             }, options);
96352           },
96353           // Load multiple entities in chunks
96354           // (note: callback may be called multiple times)
96355           // Unlike `loadEntity`, child nodes and members are not fetched
96356           // GET /api/0.6/[nodes|ways|relations]?#parameters
96357           loadMultiple: function loadMultiple(ids, callback) {
96358             var that = this;
96359             var groups = utilArrayGroupBy(utilArrayUniq(ids), osmEntity.id.type);
96360             Object.keys(groups).forEach(function (k) {
96361               var type = k + 's'; // nodes, ways, relations
96362
96363               var osmIDs = groups[k].map(function (id) {
96364                 return osmEntity.id.toOSM(id);
96365               });
96366               var options = {
96367                 skipSeen: false
96368               };
96369               utilArrayChunk(osmIDs, 150).forEach(function (arr) {
96370                 that.loadFromAPI('/api/0.6/' + type + '.json?' + type + '=' + arr.join(), function (err, entities) {
96371                   if (callback) callback(err, {
96372                     data: entities
96373                   });
96374                 }, options);
96375               });
96376             });
96377           },
96378           // Create, upload, and close a changeset
96379           // PUT /api/0.6/changeset/create
96380           // POST /api/0.6/changeset/#id/upload
96381           // PUT /api/0.6/changeset/#id/close
96382           putChangeset: function putChangeset(changeset, changes, callback) {
96383             var cid = _connectionID;
96384
96385             if (_changeset.inflight) {
96386               return callback({
96387                 message: 'Changeset already inflight',
96388                 status: -2
96389               }, changeset);
96390             } else if (_changeset.open) {
96391               // reuse existing open changeset..
96392               return createdChangeset.call(this, null, _changeset.open);
96393             } else {
96394               // Open a new changeset..
96395               var options = {
96396                 method: 'PUT',
96397                 path: '/api/0.6/changeset/create',
96398                 options: {
96399                   header: {
96400                     'Content-Type': 'text/xml'
96401                   }
96402                 },
96403                 content: JXON.stringify(changeset.asJXON())
96404               };
96405               _changeset.inflight = oauth.xhr(options, wrapcb(this, createdChangeset, cid));
96406             }
96407
96408             function createdChangeset(err, changesetID) {
96409               _changeset.inflight = null;
96410
96411               if (err) {
96412                 return callback(err, changeset);
96413               }
96414
96415               _changeset.open = changesetID;
96416               changeset = changeset.update({
96417                 id: changesetID
96418               }); // Upload the changeset..
96419
96420               var options = {
96421                 method: 'POST',
96422                 path: '/api/0.6/changeset/' + changesetID + '/upload',
96423                 options: {
96424                   header: {
96425                     'Content-Type': 'text/xml'
96426                   }
96427                 },
96428                 content: JXON.stringify(changeset.osmChangeJXON(changes))
96429               };
96430               _changeset.inflight = oauth.xhr(options, wrapcb(this, uploadedChangeset, cid));
96431             }
96432
96433             function uploadedChangeset(err) {
96434               _changeset.inflight = null;
96435               if (err) return callback(err, changeset); // Upload was successful, safe to call the callback.
96436               // Add delay to allow for postgres replication #1646 #2678
96437
96438               window.setTimeout(function () {
96439                 callback(null, changeset);
96440               }, 2500);
96441               _changeset.open = null; // At this point, we don't really care if the connection was switched..
96442               // Only try to close the changeset if we're still talking to the same server.
96443
96444               if (this.getConnectionId() === cid) {
96445                 // Still attempt to close changeset, but ignore response because #2667
96446                 oauth.xhr({
96447                   method: 'PUT',
96448                   path: '/api/0.6/changeset/' + changeset.id + '/close',
96449                   options: {
96450                     header: {
96451                       'Content-Type': 'text/xml'
96452                     }
96453                   }
96454                 }, function () {
96455                   return true;
96456                 });
96457               }
96458             }
96459           },
96460           // Load multiple users in chunks
96461           // (note: callback may be called multiple times)
96462           // GET /api/0.6/users?users=#id1,#id2,...,#idn
96463           loadUsers: function loadUsers(uids, callback) {
96464             var toLoad = [];
96465             var cached = [];
96466             utilArrayUniq(uids).forEach(function (uid) {
96467               if (_userCache.user[uid]) {
96468                 delete _userCache.toLoad[uid];
96469                 cached.push(_userCache.user[uid]);
96470               } else {
96471                 toLoad.push(uid);
96472               }
96473             });
96474
96475             if (cached.length || !this.authenticated()) {
96476               callback(undefined, cached);
96477               if (!this.authenticated()) return; // require auth
96478             }
96479
96480             utilArrayChunk(toLoad, 150).forEach(function (arr) {
96481               oauth.xhr({
96482                 method: 'GET',
96483                 path: '/api/0.6/users.json?users=' + arr.join()
96484               }, wrapcb(this, done, _connectionID));
96485             }.bind(this));
96486
96487             function done(err, payload) {
96488               if (err) return callback(err);
96489               var options = {
96490                 skipSeen: true
96491               };
96492               return parseUserJSON(payload, function (err, results) {
96493                 if (err) return callback(err);
96494                 return callback(undefined, results);
96495               }, options);
96496             }
96497           },
96498           // Load a given user by id
96499           // GET /api/0.6/user/#id
96500           loadUser: function loadUser(uid, callback) {
96501             if (_userCache.user[uid] || !this.authenticated()) {
96502               // require auth
96503               delete _userCache.toLoad[uid];
96504               return callback(undefined, _userCache.user[uid]);
96505             }
96506
96507             oauth.xhr({
96508               method: 'GET',
96509               path: '/api/0.6/user/' + uid + '.json'
96510             }, wrapcb(this, done, _connectionID));
96511
96512             function done(err, payload) {
96513               if (err) return callback(err);
96514               var options = {
96515                 skipSeen: true
96516               };
96517               return parseUserJSON(payload, function (err, results) {
96518                 if (err) return callback(err);
96519                 return callback(undefined, results[0]);
96520               }, options);
96521             }
96522           },
96523           // Load the details of the logged-in user
96524           // GET /api/0.6/user/details
96525           userDetails: function userDetails(callback) {
96526             if (_userDetails) {
96527               // retrieve cached
96528               return callback(undefined, _userDetails);
96529             }
96530
96531             oauth.xhr({
96532               method: 'GET',
96533               path: '/api/0.6/user/details.json'
96534             }, wrapcb(this, done, _connectionID));
96535
96536             function done(err, payload) {
96537               if (err) return callback(err);
96538               var options = {
96539                 skipSeen: false
96540               };
96541               return parseUserJSON(payload, function (err, results) {
96542                 if (err) return callback(err);
96543                 _userDetails = results[0];
96544                 return callback(undefined, _userDetails);
96545               }, options);
96546             }
96547           },
96548           // Load previous changesets for the logged in user
96549           // GET /api/0.6/changesets?user=#id
96550           userChangesets: function userChangesets(callback) {
96551             if (_userChangesets) {
96552               // retrieve cached
96553               return callback(undefined, _userChangesets);
96554             }
96555
96556             this.userDetails(wrapcb(this, gotDetails, _connectionID));
96557
96558             function gotDetails(err, user) {
96559               if (err) {
96560                 return callback(err);
96561               }
96562
96563               oauth.xhr({
96564                 method: 'GET',
96565                 path: '/api/0.6/changesets?user=' + user.id
96566               }, wrapcb(this, done, _connectionID));
96567             }
96568
96569             function done(err, xml) {
96570               if (err) {
96571                 return callback(err);
96572               }
96573
96574               _userChangesets = Array.prototype.map.call(xml.getElementsByTagName('changeset'), function (changeset) {
96575                 return {
96576                   tags: getTags(changeset)
96577                 };
96578               }).filter(function (changeset) {
96579                 var comment = changeset.tags.comment;
96580                 return comment && comment !== '';
96581               });
96582               return callback(undefined, _userChangesets);
96583             }
96584           },
96585           // Fetch the status of the OSM API
96586           // GET /api/capabilities
96587           status: function status(callback) {
96588             var url = urlroot + '/api/capabilities';
96589             var errback = wrapcb(this, done, _connectionID);
96590             d3_xml(url).then(function (data) {
96591               errback(null, data);
96592             })["catch"](function (err) {
96593               errback(err.message);
96594             });
96595
96596             function done(err, xml) {
96597               if (err) {
96598                 // the status is null if no response could be retrieved
96599                 return callback(err, null);
96600               } // update blocklists
96601
96602
96603               var elements = xml.getElementsByTagName('blacklist');
96604               var regexes = [];
96605
96606               for (var i = 0; i < elements.length; i++) {
96607                 var regexString = elements[i].getAttribute('regex'); // needs unencode?
96608
96609                 if (regexString) {
96610                   try {
96611                     var regex = new RegExp(regexString);
96612                     regexes.push(regex);
96613                   } catch (e) {
96614                     /* noop */
96615                   }
96616                 }
96617               }
96618
96619               if (regexes.length) {
96620                 _imageryBlocklists = regexes;
96621               }
96622
96623               if (_rateLimitError) {
96624                 return callback(_rateLimitError, 'rateLimited');
96625               } else {
96626                 var waynodes = xml.getElementsByTagName('waynodes');
96627                 var maxWayNodes = waynodes.length && parseInt(waynodes[0].getAttribute('maximum'), 10);
96628                 if (maxWayNodes && isFinite(maxWayNodes)) _maxWayNodes = maxWayNodes;
96629                 var apiStatus = xml.getElementsByTagName('status');
96630                 var val = apiStatus[0].getAttribute('api');
96631                 return callback(undefined, val);
96632               }
96633             }
96634           },
96635           // Calls `status` and dispatches an `apiStatusChange` event if the returned
96636           // status differs from the cached status.
96637           reloadApiStatus: function reloadApiStatus() {
96638             // throttle to avoid unnecessary API calls
96639             if (!this.throttledReloadApiStatus) {
96640               var that = this;
96641               this.throttledReloadApiStatus = throttle(function () {
96642                 that.status(function (err, status) {
96643                   if (status !== _cachedApiStatus) {
96644                     _cachedApiStatus = status;
96645                     dispatch$2.call('apiStatusChange', that, err, status);
96646                   }
96647                 });
96648               }, 500);
96649             }
96650
96651             this.throttledReloadApiStatus();
96652           },
96653           // Returns the maximum number of nodes a single way can have
96654           maxWayNodes: function maxWayNodes() {
96655             return _maxWayNodes;
96656           },
96657           // Load data (entities) from the API in tiles
96658           // GET /api/0.6/map?bbox=
96659           loadTiles: function loadTiles(projection, callback) {
96660             if (_off) return; // determine the needed tiles to cover the view
96661
96662             var tiles = tiler$2.zoomExtent([_tileZoom, _tileZoom]).getTiles(projection); // abort inflight requests that are no longer needed
96663
96664             var hadRequests = hasInflightRequests(_tileCache);
96665             abortUnwantedRequests(_tileCache, tiles);
96666
96667             if (hadRequests && !hasInflightRequests(_tileCache)) {
96668               dispatch$2.call('loaded'); // stop the spinner
96669             } // issue new requests..
96670
96671
96672             tiles.forEach(function (tile) {
96673               this.loadTile(tile, callback);
96674             }, this);
96675           },
96676           // Load a single data tile
96677           // GET /api/0.6/map?bbox=
96678           loadTile: function loadTile(tile, callback) {
96679             if (_off) return;
96680             if (_tileCache.loaded[tile.id] || _tileCache.inflight[tile.id]) return;
96681
96682             if (!hasInflightRequests(_tileCache)) {
96683               dispatch$2.call('loading'); // start the spinner
96684             }
96685
96686             var path = '/api/0.6/map.json?bbox=';
96687             var options = {
96688               skipSeen: true
96689             };
96690             _tileCache.inflight[tile.id] = this.loadFromAPI(path + tile.extent.toParam(), tileCallback, options);
96691
96692             function tileCallback(err, parsed) {
96693               delete _tileCache.inflight[tile.id];
96694
96695               if (!err) {
96696                 delete _tileCache.toLoad[tile.id];
96697                 _tileCache.loaded[tile.id] = true;
96698                 var bbox = tile.extent.bbox();
96699                 bbox.id = tile.id;
96700
96701                 _tileCache.rtree.insert(bbox);
96702               }
96703
96704               if (callback) {
96705                 callback(err, Object.assign({
96706                   data: parsed
96707                 }, tile));
96708               }
96709
96710               if (!hasInflightRequests(_tileCache)) {
96711                 dispatch$2.call('loaded'); // stop the spinner
96712               }
96713             }
96714           },
96715           isDataLoaded: function isDataLoaded(loc) {
96716             var bbox = {
96717               minX: loc[0],
96718               minY: loc[1],
96719               maxX: loc[0],
96720               maxY: loc[1]
96721             };
96722             return _tileCache.rtree.collides(bbox);
96723           },
96724           // load the tile that covers the given `loc`
96725           loadTileAtLoc: function loadTileAtLoc(loc, callback) {
96726             // Back off if the toLoad queue is filling up.. re #6417
96727             // (Currently `loadTileAtLoc` requests are considered low priority - used by operations to
96728             // let users safely edit geometries which extend to unloaded tiles.  We can drop some.)
96729             if (Object.keys(_tileCache.toLoad).length > 50) return;
96730             var k = geoZoomToScale(_tileZoom + 1);
96731             var offset = geoRawMercator().scale(k)(loc);
96732             var projection = geoRawMercator().transform({
96733               k: k,
96734               x: -offset[0],
96735               y: -offset[1]
96736             });
96737             var tiles = tiler$2.zoomExtent([_tileZoom, _tileZoom]).getTiles(projection);
96738             tiles.forEach(function (tile) {
96739               if (_tileCache.toLoad[tile.id] || _tileCache.loaded[tile.id] || _tileCache.inflight[tile.id]) return;
96740               _tileCache.toLoad[tile.id] = true;
96741               this.loadTile(tile, callback);
96742             }, this);
96743           },
96744           // Load notes from the API in tiles
96745           // GET /api/0.6/notes?bbox=
96746           loadNotes: function loadNotes(projection, noteOptions) {
96747             noteOptions = Object.assign({
96748               limit: 10000,
96749               closed: 7
96750             }, noteOptions);
96751             if (_off) return;
96752             var that = this;
96753             var path = '/api/0.6/notes?limit=' + noteOptions.limit + '&closed=' + noteOptions.closed + '&bbox=';
96754
96755             var throttleLoadUsers = throttle(function () {
96756               var uids = Object.keys(_userCache.toLoad);
96757               if (!uids.length) return;
96758               that.loadUsers(uids, function () {}); // eagerly load user details
96759             }, 750); // determine the needed tiles to cover the view
96760
96761
96762             var tiles = tiler$2.zoomExtent([_noteZoom, _noteZoom]).getTiles(projection); // abort inflight requests that are no longer needed
96763
96764             abortUnwantedRequests(_noteCache, tiles); // issue new requests..
96765
96766             tiles.forEach(function (tile) {
96767               if (_noteCache.loaded[tile.id] || _noteCache.inflight[tile.id]) return;
96768               var options = {
96769                 skipSeen: false
96770               };
96771               _noteCache.inflight[tile.id] = that.loadFromAPI(path + tile.extent.toParam(), function (err) {
96772                 delete _noteCache.inflight[tile.id];
96773
96774                 if (!err) {
96775                   _noteCache.loaded[tile.id] = true;
96776                 }
96777
96778                 throttleLoadUsers();
96779                 dispatch$2.call('loadedNotes');
96780               }, options);
96781             });
96782           },
96783           // Create a note
96784           // POST /api/0.6/notes?params
96785           postNoteCreate: function postNoteCreate(note, callback) {
96786             if (!this.authenticated()) {
96787               return callback({
96788                 message: 'Not Authenticated',
96789                 status: -3
96790               }, note);
96791             }
96792
96793             if (_noteCache.inflightPost[note.id]) {
96794               return callback({
96795                 message: 'Note update already inflight',
96796                 status: -2
96797               }, note);
96798             }
96799
96800             if (!note.loc[0] || !note.loc[1] || !note.newComment) return; // location & description required
96801
96802             var comment = note.newComment;
96803
96804             if (note.newCategory && note.newCategory !== 'None') {
96805               comment += ' #' + note.newCategory;
96806             }
96807
96808             var path = '/api/0.6/notes?' + utilQsString({
96809               lon: note.loc[0],
96810               lat: note.loc[1],
96811               text: comment
96812             });
96813             _noteCache.inflightPost[note.id] = oauth.xhr({
96814               method: 'POST',
96815               path: path
96816             }, wrapcb(this, done, _connectionID));
96817
96818             function done(err, xml) {
96819               delete _noteCache.inflightPost[note.id];
96820
96821               if (err) {
96822                 return callback(err);
96823               } // we get the updated note back, remove from caches and reparse..
96824
96825
96826               this.removeNote(note);
96827               var options = {
96828                 skipSeen: false
96829               };
96830               return parseXML(xml, function (err, results) {
96831                 if (err) {
96832                   return callback(err);
96833                 } else {
96834                   return callback(undefined, results[0]);
96835                 }
96836               }, options);
96837             }
96838           },
96839           // Update a note
96840           // POST /api/0.6/notes/#id/comment?text=comment
96841           // POST /api/0.6/notes/#id/close?text=comment
96842           // POST /api/0.6/notes/#id/reopen?text=comment
96843           postNoteUpdate: function postNoteUpdate(note, newStatus, callback) {
96844             if (!this.authenticated()) {
96845               return callback({
96846                 message: 'Not Authenticated',
96847                 status: -3
96848               }, note);
96849             }
96850
96851             if (_noteCache.inflightPost[note.id]) {
96852               return callback({
96853                 message: 'Note update already inflight',
96854                 status: -2
96855               }, note);
96856             }
96857
96858             var action;
96859
96860             if (note.status !== 'closed' && newStatus === 'closed') {
96861               action = 'close';
96862             } else if (note.status !== 'open' && newStatus === 'open') {
96863               action = 'reopen';
96864             } else {
96865               action = 'comment';
96866               if (!note.newComment) return; // when commenting, comment required
96867             }
96868
96869             var path = '/api/0.6/notes/' + note.id + '/' + action;
96870
96871             if (note.newComment) {
96872               path += '?' + utilQsString({
96873                 text: note.newComment
96874               });
96875             }
96876
96877             _noteCache.inflightPost[note.id] = oauth.xhr({
96878               method: 'POST',
96879               path: path
96880             }, wrapcb(this, done, _connectionID));
96881
96882             function done(err, xml) {
96883               delete _noteCache.inflightPost[note.id];
96884
96885               if (err) {
96886                 return callback(err);
96887               } // we get the updated note back, remove from caches and reparse..
96888
96889
96890               this.removeNote(note); // update closed note cache - used to populate `closed:note` changeset tag
96891
96892               if (action === 'close') {
96893                 _noteCache.closed[note.id] = true;
96894               } else if (action === 'reopen') {
96895                 delete _noteCache.closed[note.id];
96896               }
96897
96898               var options = {
96899                 skipSeen: false
96900               };
96901               return parseXML(xml, function (err, results) {
96902                 if (err) {
96903                   return callback(err);
96904                 } else {
96905                   return callback(undefined, results[0]);
96906                 }
96907               }, options);
96908             }
96909           },
96910           "switch": function _switch(options) {
96911             urlroot = options.urlroot;
96912             oauth.options(Object.assign({
96913               url: urlroot,
96914               loading: authLoading,
96915               done: authDone
96916             }, options));
96917             this.reset();
96918             this.userChangesets(function () {}); // eagerly load user details/changesets
96919
96920             dispatch$2.call('change');
96921             return this;
96922           },
96923           toggle: function toggle(val) {
96924             _off = !val;
96925             return this;
96926           },
96927           isChangesetInflight: function isChangesetInflight() {
96928             return !!_changeset.inflight;
96929           },
96930           // get/set cached data
96931           // This is used to save/restore the state when entering/exiting the walkthrough
96932           // Also used for testing purposes.
96933           caches: function caches(obj) {
96934             function cloneCache(source) {
96935               var target = {};
96936               Object.keys(source).forEach(function (k) {
96937                 if (k === 'rtree') {
96938                   target.rtree = new RBush().fromJSON(source.rtree.toJSON()); // clone rbush
96939                 } else if (k === 'note') {
96940                   target.note = {};
96941                   Object.keys(source.note).forEach(function (id) {
96942                     target.note[id] = osmNote(source.note[id]); // copy notes
96943                   });
96944                 } else {
96945                   target[k] = JSON.parse(JSON.stringify(source[k])); // clone deep
96946                 }
96947               });
96948               return target;
96949             }
96950
96951             if (!arguments.length) {
96952               return {
96953                 tile: cloneCache(_tileCache),
96954                 note: cloneCache(_noteCache),
96955                 user: cloneCache(_userCache)
96956               };
96957             } // access caches directly for testing (e.g., loading notes rtree)
96958
96959
96960             if (obj === 'get') {
96961               return {
96962                 tile: _tileCache,
96963                 note: _noteCache,
96964                 user: _userCache
96965               };
96966             }
96967
96968             if (obj.tile) {
96969               _tileCache = obj.tile;
96970               _tileCache.inflight = {};
96971             }
96972
96973             if (obj.note) {
96974               _noteCache = obj.note;
96975               _noteCache.inflight = {};
96976               _noteCache.inflightPost = {};
96977             }
96978
96979             if (obj.user) {
96980               _userCache = obj.user;
96981             }
96982
96983             return this;
96984           },
96985           logout: function logout() {
96986             _userChangesets = undefined;
96987             _userDetails = undefined;
96988             oauth.logout();
96989             dispatch$2.call('change');
96990             return this;
96991           },
96992           authenticated: function authenticated() {
96993             return oauth.authenticated();
96994           },
96995           authenticate: function authenticate(callback) {
96996             var that = this;
96997             var cid = _connectionID;
96998             _userChangesets = undefined;
96999             _userDetails = undefined;
97000
97001             function done(err, res) {
97002               if (err) {
97003                 if (callback) callback(err);
97004                 return;
97005               }
97006
97007               if (that.getConnectionId() !== cid) {
97008                 if (callback) callback({
97009                   message: 'Connection Switched',
97010                   status: -1
97011                 });
97012                 return;
97013               }
97014
97015               _rateLimitError = undefined;
97016               dispatch$2.call('change');
97017               if (callback) callback(err, res);
97018               that.userChangesets(function () {}); // eagerly load user details/changesets
97019             }
97020
97021             return oauth.authenticate(done);
97022           },
97023           imageryBlocklists: function imageryBlocklists() {
97024             return _imageryBlocklists;
97025           },
97026           tileZoom: function tileZoom(val) {
97027             if (!arguments.length) return _tileZoom;
97028             _tileZoom = val;
97029             return this;
97030           },
97031           // get all cached notes covering the viewport
97032           notes: function notes(projection) {
97033             var viewport = projection.clipExtent();
97034             var min = [viewport[0][0], viewport[1][1]];
97035             var max = [viewport[1][0], viewport[0][1]];
97036             var bbox = geoExtent(projection.invert(min), projection.invert(max)).bbox();
97037             return _noteCache.rtree.search(bbox).map(function (d) {
97038               return d.data;
97039             });
97040           },
97041           // get a single note from the cache
97042           getNote: function getNote(id) {
97043             return _noteCache.note[id];
97044           },
97045           // remove a single note from the cache
97046           removeNote: function removeNote(note) {
97047             if (!(note instanceof osmNote) || !note.id) return;
97048             delete _noteCache.note[note.id];
97049             updateRtree(encodeNoteRtree(note), false); // false = remove
97050           },
97051           // replace a single note in the cache
97052           replaceNote: function replaceNote(note) {
97053             if (!(note instanceof osmNote) || !note.id) return;
97054             _noteCache.note[note.id] = note;
97055             updateRtree(encodeNoteRtree(note), true); // true = replace
97056
97057             return note;
97058           },
97059           // Get an array of note IDs closed during this session.
97060           // Used to populate `closed:note` changeset tag
97061           getClosedIDs: function getClosedIDs() {
97062             return Object.keys(_noteCache.closed).sort();
97063           }
97064         };
97065
97066         var _apibase$1 = 'https://wiki.openstreetmap.org/w/api.php';
97067         var _inflight$1 = {};
97068         var _wikibaseCache = {};
97069         var _localeIDs = {
97070           en: false
97071         };
97072
97073         var debouncedRequest$1 = debounce(request$1, 500, {
97074           leading: false
97075         });
97076
97077         function request$1(url, callback) {
97078           if (_inflight$1[url]) return;
97079           var controller = new AbortController();
97080           _inflight$1[url] = controller;
97081           d3_json(url, {
97082             signal: controller.signal
97083           }).then(function (result) {
97084             delete _inflight$1[url];
97085             if (callback) callback(null, result);
97086           })["catch"](function (err) {
97087             delete _inflight$1[url];
97088             if (err.name === 'AbortError') return;
97089             if (callback) callback(err.message);
97090           });
97091         }
97092
97093         var serviceOsmWikibase = {
97094           init: function init() {
97095             _inflight$1 = {};
97096             _wikibaseCache = {};
97097             _localeIDs = {};
97098           },
97099           reset: function reset() {
97100             Object.values(_inflight$1).forEach(function (controller) {
97101               controller.abort();
97102             });
97103             _inflight$1 = {};
97104           },
97105
97106           /**
97107            * Get the best value for the property, or undefined if not found
97108            * @param entity object from wikibase
97109            * @param property string e.g. 'P4' for image
97110            * @param langCode string e.g. 'fr' for French
97111            */
97112           claimToValue: function claimToValue(entity, property, langCode) {
97113             if (!entity.claims[property]) return undefined;
97114             var locale = _localeIDs[langCode];
97115             var preferredPick, localePick;
97116             entity.claims[property].forEach(function (stmt) {
97117               // If exists, use value limited to the needed language (has a qualifier P26 = locale)
97118               // Or if not found, use the first value with the "preferred" rank
97119               if (!preferredPick && stmt.rank === 'preferred') {
97120                 preferredPick = stmt;
97121               }
97122
97123               if (locale && stmt.qualifiers && stmt.qualifiers.P26 && stmt.qualifiers.P26[0].datavalue.value.id === locale) {
97124                 localePick = stmt;
97125               }
97126             });
97127             var result = localePick || preferredPick;
97128
97129             if (result) {
97130               var datavalue = result.mainsnak.datavalue;
97131               return datavalue.type === 'wikibase-entityid' ? datavalue.value.id : datavalue.value;
97132             } else {
97133               return undefined;
97134             }
97135           },
97136
97137           /**
97138            * Convert monolingual property into a key-value object (language -> value)
97139            * @param entity object from wikibase
97140            * @param property string e.g. 'P31' for monolingual wiki page title
97141            */
97142           monolingualClaimToValueObj: function monolingualClaimToValueObj(entity, property) {
97143             if (!entity || !entity.claims[property]) return undefined;
97144             return entity.claims[property].reduce(function (acc, obj) {
97145               var value = obj.mainsnak.datavalue.value;
97146               acc[value.language] = value.text;
97147               return acc;
97148             }, {});
97149           },
97150           toSitelink: function toSitelink(key, value) {
97151             var result = value ? 'Tag:' + key + '=' + value : 'Key:' + key;
97152             return result.replace(/_/g, ' ').trim();
97153           },
97154           //
97155           // Pass params object of the form:
97156           // {
97157           //   key: 'string',
97158           //   value: 'string',
97159           //   langCode: 'string'
97160           // }
97161           //
97162           getEntity: function getEntity(params, callback) {
97163             var doRequest = params.debounce ? debouncedRequest$1 : request$1;
97164             var that = this;
97165             var titles = [];
97166             var result = {};
97167             var rtypeSitelink = params.key === 'type' && params.value ? ('Relation:' + params.value).replace(/_/g, ' ').trim() : false;
97168             var keySitelink = params.key ? this.toSitelink(params.key) : false;
97169             var tagSitelink = params.key && params.value ? this.toSitelink(params.key, params.value) : false;
97170             var localeSitelink;
97171
97172             if (params.langCodes) {
97173               params.langCodes.forEach(function (langCode) {
97174                 if (_localeIDs[langCode] === undefined) {
97175                   // If this is the first time we are asking about this locale,
97176                   // fetch corresponding entity (if it exists), and cache it.
97177                   // If there is no such entry, cache `false` value to avoid re-requesting it.
97178                   localeSitelink = ('Locale:' + langCode).replace(/_/g, ' ').trim();
97179                   titles.push(localeSitelink);
97180                 }
97181               });
97182             }
97183
97184             if (rtypeSitelink) {
97185               if (_wikibaseCache[rtypeSitelink]) {
97186                 result.rtype = _wikibaseCache[rtypeSitelink];
97187               } else {
97188                 titles.push(rtypeSitelink);
97189               }
97190             }
97191
97192             if (keySitelink) {
97193               if (_wikibaseCache[keySitelink]) {
97194                 result.key = _wikibaseCache[keySitelink];
97195               } else {
97196                 titles.push(keySitelink);
97197               }
97198             }
97199
97200             if (tagSitelink) {
97201               if (_wikibaseCache[tagSitelink]) {
97202                 result.tag = _wikibaseCache[tagSitelink];
97203               } else {
97204                 titles.push(tagSitelink);
97205               }
97206             }
97207
97208             if (!titles.length) {
97209               // Nothing to do, we already had everything in the cache
97210               return callback(null, result);
97211             } // Requesting just the user language code
97212             // If backend recognizes the code, it will perform proper fallbacks,
97213             // and the result will contain the requested code. If not, all values are returned:
97214             // {"zh-tw":{"value":"...","language":"zh-tw","source-language":"zh-hant"}
97215             // {"pt-br":{"value":"...","language":"pt","for-language":"pt-br"}}
97216
97217
97218             var obj = {
97219               action: 'wbgetentities',
97220               sites: 'wiki',
97221               titles: titles.join('|'),
97222               languages: params.langCodes.join('|'),
97223               languagefallback: 1,
97224               origin: '*',
97225               format: 'json' // There is an MW Wikibase API bug https://phabricator.wikimedia.org/T212069
97226               // We shouldn't use v1 until it gets fixed, but should switch to it afterwards
97227               // formatversion: 2,
97228
97229             };
97230             var url = _apibase$1 + '?' + utilQsString(obj);
97231             doRequest(url, function (err, d) {
97232               if (err) {
97233                 callback(err);
97234               } else if (!d.success || d.error) {
97235                 callback(d.error.messages.map(function (v) {
97236                   return v.html['*'];
97237                 }).join('<br>'));
97238               } else {
97239                 var localeID = false;
97240                 Object.values(d.entities).forEach(function (res) {
97241                   if (res.missing !== '') {
97242                     var title = res.sitelinks.wiki.title;
97243
97244                     if (title === rtypeSitelink) {
97245                       _wikibaseCache[rtypeSitelink] = res;
97246                       result.rtype = res;
97247                     } else if (title === keySitelink) {
97248                       _wikibaseCache[keySitelink] = res;
97249                       result.key = res;
97250                     } else if (title === tagSitelink) {
97251                       _wikibaseCache[tagSitelink] = res;
97252                       result.tag = res;
97253                     } else if (title === localeSitelink) {
97254                       localeID = res.id;
97255                     } else {
97256                       console.log('Unexpected title ' + title); // eslint-disable-line no-console
97257                     }
97258                   }
97259                 });
97260
97261                 if (localeSitelink) {
97262                   // If locale ID is not found, store false to prevent repeated queries
97263                   that.addLocale(params.langCodes[0], localeID);
97264                 }
97265
97266                 callback(null, result);
97267               }
97268             });
97269           },
97270           //
97271           // Pass params object of the form:
97272           // {
97273           //   key: 'string',     // required
97274           //   value: 'string'    // optional
97275           // }
97276           //
97277           // Get an result object used to display tag documentation
97278           // {
97279           //   title:        'string',
97280           //   description:  'string',
97281           //   editURL:      'string',
97282           //   imageURL:     'string',
97283           //   wiki:         { title: 'string', text: 'string', url: 'string' }
97284           // }
97285           //
97286           getDocs: function getDocs(params, callback) {
97287             var that = this;
97288             var langCodes = _mainLocalizer.localeCodes().map(function (code) {
97289               return code.toLowerCase();
97290             });
97291             params.langCodes = langCodes;
97292             this.getEntity(params, function (err, data) {
97293               if (err) {
97294                 callback(err);
97295                 return;
97296               }
97297
97298               var entity = data.rtype || data.tag || data.key;
97299
97300               if (!entity) {
97301                 callback('No entity');
97302                 return;
97303               }
97304
97305               var i;
97306               var description;
97307
97308               for (i in langCodes) {
97309                 var _code = langCodes[i];
97310
97311                 if (entity.descriptions[_code] && entity.descriptions[_code].language === _code) {
97312                   description = entity.descriptions[_code];
97313                   break;
97314                 }
97315               }
97316
97317               if (!description && Object.values(entity.descriptions).length) description = Object.values(entity.descriptions)[0]; // prepare result
97318
97319               var result = {
97320                 title: entity.title,
97321                 description: description ? description.value : '',
97322                 descriptionLocaleCode: description ? description.language : '',
97323                 editURL: 'https://wiki.openstreetmap.org/wiki/' + entity.title
97324               }; // add image
97325
97326               if (entity.claims) {
97327                 var imageroot;
97328                 var image = that.claimToValue(entity, 'P4', langCodes[0]);
97329
97330                 if (image) {
97331                   imageroot = 'https://commons.wikimedia.org/w/index.php';
97332                 } else {
97333                   image = that.claimToValue(entity, 'P28', langCodes[0]);
97334
97335                   if (image) {
97336                     imageroot = 'https://wiki.openstreetmap.org/w/index.php';
97337                   }
97338                 }
97339
97340                 if (imageroot && image) {
97341                   result.imageURL = imageroot + '?' + utilQsString({
97342                     title: 'Special:Redirect/file/' + image,
97343                     width: 400
97344                   });
97345                 }
97346               } // Try to get a wiki page from tag data item first, followed by the corresponding key data item.
97347               // If neither tag nor key data item contain a wiki page in the needed language nor English,
97348               // get the first found wiki page from either the tag or the key item.
97349
97350
97351               var rtypeWiki = that.monolingualClaimToValueObj(data.rtype, 'P31');
97352               var tagWiki = that.monolingualClaimToValueObj(data.tag, 'P31');
97353               var keyWiki = that.monolingualClaimToValueObj(data.key, 'P31');
97354               var wikis = [rtypeWiki, tagWiki, keyWiki];
97355
97356               for (i in wikis) {
97357                 var wiki = wikis[i];
97358
97359                 for (var j in langCodes) {
97360                   var code = langCodes[j];
97361                   var referenceId = langCodes[0].split('-')[0] !== 'en' && code.split('-')[0] === 'en' ? 'inspector.wiki_en_reference' : 'inspector.wiki_reference';
97362                   var info = getWikiInfo(wiki, code, referenceId);
97363
97364                   if (info) {
97365                     result.wiki = info;
97366                     break;
97367                   }
97368                 }
97369
97370                 if (result.wiki) break;
97371               }
97372
97373               callback(null, result); // Helper method to get wiki info if a given language exists
97374
97375               function getWikiInfo(wiki, langCode, tKey) {
97376                 if (wiki && wiki[langCode]) {
97377                   return {
97378                     title: wiki[langCode],
97379                     text: tKey,
97380                     url: 'https://wiki.openstreetmap.org/wiki/' + wiki[langCode]
97381                   };
97382                 }
97383               }
97384             });
97385           },
97386           addLocale: function addLocale(langCode, qid) {
97387             // Makes it easier to unit test
97388             _localeIDs[langCode] = qid;
97389           },
97390           apibase: function apibase(val) {
97391             if (!arguments.length) return _apibase$1;
97392             _apibase$1 = val;
97393             return this;
97394           }
97395         };
97396
97397         var jsonpCache = {};
97398         window.jsonpCache = jsonpCache;
97399         function jsonpRequest(url, callback) {
97400           var request = {
97401             abort: function abort() {}
97402           };
97403
97404           if (window.JSONP_FIX) {
97405             if (window.JSONP_DELAY === 0) {
97406               callback(window.JSONP_FIX);
97407             } else {
97408               var t = window.setTimeout(function () {
97409                 callback(window.JSONP_FIX);
97410               }, window.JSONP_DELAY || 0);
97411
97412               request.abort = function () {
97413                 window.clearTimeout(t);
97414               };
97415             }
97416
97417             return request;
97418           }
97419
97420           function rand() {
97421             var chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
97422             var c = '';
97423             var i = -1;
97424
97425             while (++i < 15) {
97426               c += chars.charAt(Math.floor(Math.random() * 52));
97427             }
97428
97429             return c;
97430           }
97431
97432           function create(url) {
97433             var e = url.match(/callback=(\w+)/);
97434             var c = e ? e[1] : rand();
97435
97436             jsonpCache[c] = function (data) {
97437               if (jsonpCache[c]) {
97438                 callback(data);
97439               }
97440
97441               finalize();
97442             };
97443
97444             function finalize() {
97445               delete jsonpCache[c];
97446               script.remove();
97447             }
97448
97449             request.abort = finalize;
97450             return 'jsonpCache.' + c;
97451           }
97452
97453           var cb = create(url);
97454           var script = select('head').append('script').attr('type', 'text/javascript').attr('src', url.replace(/(\{|%7B)callback(\}|%7D)/, cb));
97455           return request;
97456         }
97457
97458         var bubbleApi = 'https://dev.virtualearth.net/mapcontrol/HumanScaleServices/GetBubbles.ashx?';
97459         var streetsideImagesApi = 'https://t.ssl.ak.tiles.virtualearth.net/tiles/';
97460         var bubbleAppKey = 'AuftgJsO0Xs8Ts4M1xZUQJQXJNsvmh3IV8DkNieCiy3tCwCUMq76-WpkrBtNAuEm';
97461         var pannellumViewerCSS = 'pannellum-streetside/pannellum.css';
97462         var pannellumViewerJS = 'pannellum-streetside/pannellum.js';
97463         var maxResults = 2000;
97464         var tileZoom = 16.5;
97465         var tiler$1 = utilTiler().zoomExtent([tileZoom, tileZoom]).skipNullIsland(true);
97466         var dispatch$1 = dispatch$8('loadedImages', 'viewerChanged');
97467         var minHfov = 10; // zoom in degrees:  20, 10, 5
97468
97469         var maxHfov = 90; // zoom out degrees
97470
97471         var defaultHfov = 45;
97472         var _hires = false;
97473         var _resolution = 512; // higher numbers are slower - 512, 1024, 2048, 4096
97474
97475         var _currScene = 0;
97476
97477         var _ssCache;
97478
97479         var _pannellumViewer;
97480
97481         var _sceneOptions = {
97482           showFullscreenCtrl: false,
97483           autoLoad: true,
97484           compass: true,
97485           yaw: 0,
97486           minHfov: minHfov,
97487           maxHfov: maxHfov,
97488           hfov: defaultHfov,
97489           type: 'cubemap',
97490           cubeMap: []
97491         };
97492
97493         var _loadViewerPromise;
97494         /**
97495          * abortRequest().
97496          */
97497
97498
97499         function abortRequest$1(i) {
97500           i.abort();
97501         }
97502         /**
97503          * localeTimeStamp().
97504          */
97505
97506
97507         function localeTimestamp(s) {
97508           if (!s) return null;
97509           var options = {
97510             day: 'numeric',
97511             month: 'short',
97512             year: 'numeric'
97513           };
97514           var d = new Date(s);
97515           if (isNaN(d.getTime())) return null;
97516           return d.toLocaleString(_mainLocalizer.localeCode(), options);
97517         }
97518         /**
97519          * loadTiles() wraps the process of generating tiles and then fetching image points for each tile.
97520          */
97521
97522
97523         function loadTiles(which, url, projection, margin) {
97524           var tiles = tiler$1.margin(margin).getTiles(projection); // abort inflight requests that are no longer needed
97525
97526           var cache = _ssCache[which];
97527           Object.keys(cache.inflight).forEach(function (k) {
97528             var wanted = tiles.find(function (tile) {
97529               return k.indexOf(tile.id + ',') === 0;
97530             });
97531
97532             if (!wanted) {
97533               abortRequest$1(cache.inflight[k]);
97534               delete cache.inflight[k];
97535             }
97536           });
97537           tiles.forEach(function (tile) {
97538             return loadNextTilePage(which, url, tile);
97539           });
97540         }
97541         /**
97542          * loadNextTilePage() load data for the next tile page in line.
97543          */
97544
97545
97546         function loadNextTilePage(which, url, tile) {
97547           var cache = _ssCache[which];
97548           var nextPage = cache.nextPage[tile.id] || 0;
97549           var id = tile.id + ',' + String(nextPage);
97550           if (cache.loaded[id] || cache.inflight[id]) return;
97551           cache.inflight[id] = getBubbles(url, tile, function (bubbles) {
97552             cache.loaded[id] = true;
97553             delete cache.inflight[id];
97554             if (!bubbles) return; // [].shift() removes the first element, some statistics info, not a bubble point
97555
97556             bubbles.shift();
97557             var features = bubbles.map(function (bubble) {
97558               if (cache.points[bubble.id]) return null; // skip duplicates
97559
97560               var loc = [bubble.lo, bubble.la];
97561               var d = {
97562                 loc: loc,
97563                 key: bubble.id,
97564                 ca: bubble.he,
97565                 captured_at: bubble.cd,
97566                 captured_by: 'microsoft',
97567                 // nbn: bubble.nbn,
97568                 // pbn: bubble.pbn,
97569                 // ad: bubble.ad,
97570                 // rn: bubble.rn,
97571                 pr: bubble.pr,
97572                 // previous
97573                 ne: bubble.ne,
97574                 // next
97575                 pano: true,
97576                 sequenceKey: null
97577               };
97578               cache.points[bubble.id] = d; // a sequence starts here
97579
97580               if (bubble.pr === undefined) {
97581                 cache.leaders.push(bubble.id);
97582               }
97583
97584               return {
97585                 minX: loc[0],
97586                 minY: loc[1],
97587                 maxX: loc[0],
97588                 maxY: loc[1],
97589                 data: d
97590               };
97591             }).filter(Boolean);
97592             cache.rtree.load(features);
97593             connectSequences();
97594
97595             if (which === 'bubbles') {
97596               dispatch$1.call('loadedImages');
97597             }
97598           });
97599         } // call this sometimes to connect the bubbles into sequences
97600
97601
97602         function connectSequences() {
97603           var cache = _ssCache.bubbles;
97604           var keepLeaders = [];
97605
97606           for (var i = 0; i < cache.leaders.length; i++) {
97607             var bubble = cache.points[cache.leaders[i]];
97608             var seen = {}; // try to make a sequence.. use the key of the leader bubble.
97609
97610             var sequence = {
97611               key: bubble.key,
97612               bubbles: []
97613             };
97614             var complete = false;
97615
97616             do {
97617               sequence.bubbles.push(bubble);
97618               seen[bubble.key] = true;
97619
97620               if (bubble.ne === undefined) {
97621                 complete = true;
97622               } else {
97623                 bubble = cache.points[bubble.ne]; // advance to next
97624               }
97625             } while (bubble && !seen[bubble.key] && !complete);
97626
97627             if (complete) {
97628               _ssCache.sequences[sequence.key] = sequence; // assign bubbles to the sequence
97629
97630               for (var j = 0; j < sequence.bubbles.length; j++) {
97631                 sequence.bubbles[j].sequenceKey = sequence.key;
97632               } // create a GeoJSON LineString
97633
97634
97635               sequence.geojson = {
97636                 type: 'LineString',
97637                 properties: {
97638                   captured_at: sequence.bubbles[0] ? sequence.bubbles[0].captured_at : null,
97639                   captured_by: sequence.bubbles[0] ? sequence.bubbles[0].captured_by : null,
97640                   key: sequence.key
97641                 },
97642                 coordinates: sequence.bubbles.map(function (d) {
97643                   return d.loc;
97644                 })
97645               };
97646             } else {
97647               keepLeaders.push(cache.leaders[i]);
97648             }
97649           } // couldn't complete these, save for later
97650
97651
97652           cache.leaders = keepLeaders;
97653         }
97654         /**
97655          * getBubbles() handles the request to the server for a tile extent of 'bubbles' (streetside image locations).
97656          */
97657
97658
97659         function getBubbles(url, tile, callback) {
97660           var rect = tile.extent.rectangle();
97661           var urlForRequest = url + utilQsString({
97662             n: rect[3],
97663             s: rect[1],
97664             e: rect[2],
97665             w: rect[0],
97666             c: maxResults,
97667             appkey: bubbleAppKey,
97668             jsCallback: '{callback}'
97669           });
97670           return jsonpRequest(urlForRequest, function (data) {
97671             if (!data || data.error) {
97672               callback(null);
97673             } else {
97674               callback(data);
97675             }
97676           });
97677         } // partition viewport into higher zoom tiles
97678
97679
97680         function partitionViewport(projection) {
97681           var z = geoScaleToZoom(projection.scale());
97682           var z2 = Math.ceil(z * 2) / 2 + 2.5; // round to next 0.5 and add 2.5
97683
97684           var tiler = utilTiler().zoomExtent([z2, z2]);
97685           return tiler.getTiles(projection).map(function (tile) {
97686             return tile.extent;
97687           });
97688         } // no more than `limit` results per partition.
97689
97690
97691         function searchLimited(limit, projection, rtree) {
97692           limit = limit || 5;
97693           return partitionViewport(projection).reduce(function (result, extent) {
97694             var found = rtree.search(extent.bbox()).slice(0, limit).map(function (d) {
97695               return d.data;
97696             });
97697             return found.length ? result.concat(found) : result;
97698           }, []);
97699         }
97700         /**
97701          * loadImage()
97702          */
97703
97704
97705         function loadImage(imgInfo) {
97706           return new Promise(function (resolve) {
97707             var img = new Image();
97708
97709             img.onload = function () {
97710               var canvas = document.getElementById('ideditor-canvas' + imgInfo.face);
97711               var ctx = canvas.getContext('2d');
97712               ctx.drawImage(img, imgInfo.x, imgInfo.y);
97713               resolve({
97714                 imgInfo: imgInfo,
97715                 status: 'ok'
97716               });
97717             };
97718
97719             img.onerror = function () {
97720               resolve({
97721                 data: imgInfo,
97722                 status: 'error'
97723               });
97724             };
97725
97726             img.setAttribute('crossorigin', '');
97727             img.src = imgInfo.url;
97728           });
97729         }
97730         /**
97731          * loadCanvas()
97732          */
97733
97734
97735         function loadCanvas(imageGroup) {
97736           return Promise.all(imageGroup.map(loadImage)).then(function (data) {
97737             var canvas = document.getElementById('ideditor-canvas' + data[0].imgInfo.face);
97738             var which = {
97739               '01': 0,
97740               '02': 1,
97741               '03': 2,
97742               '10': 3,
97743               '11': 4,
97744               '12': 5
97745             };
97746             var face = data[0].imgInfo.face;
97747             _sceneOptions.cubeMap[which[face]] = canvas.toDataURL('image/jpeg', 1.0);
97748             return {
97749               status: 'loadCanvas for face ' + data[0].imgInfo.face + 'ok'
97750             };
97751           });
97752         }
97753         /**
97754          * loadFaces()
97755          */
97756
97757
97758         function loadFaces(faceGroup) {
97759           return Promise.all(faceGroup.map(loadCanvas)).then(function () {
97760             return {
97761               status: 'loadFaces done'
97762             };
97763           });
97764         }
97765
97766         function setupCanvas(selection, reset) {
97767           if (reset) {
97768             selection.selectAll('#ideditor-stitcher-canvases').remove();
97769           } // Add the Streetside working canvases. These are used for 'stitching', or combining,
97770           // multiple images for each of the six faces, before passing to the Pannellum control as DataUrls
97771
97772
97773           selection.selectAll('#ideditor-stitcher-canvases').data([0]).enter().append('div').attr('id', 'ideditor-stitcher-canvases').attr('display', 'none').selectAll('canvas').data(['canvas01', 'canvas02', 'canvas03', 'canvas10', 'canvas11', 'canvas12']).enter().append('canvas').attr('id', function (d) {
97774             return 'ideditor-' + d;
97775           }).attr('width', _resolution).attr('height', _resolution);
97776         }
97777
97778         function qkToXY(qk) {
97779           var x = 0;
97780           var y = 0;
97781           var scale = 256;
97782
97783           for (var i = qk.length; i > 0; i--) {
97784             var key = qk[i - 1];
97785             x += +(key === '1' || key === '3') * scale;
97786             y += +(key === '2' || key === '3') * scale;
97787             scale *= 2;
97788           }
97789
97790           return [x, y];
97791         }
97792
97793         function getQuadKeys() {
97794           var dim = _resolution / 256;
97795           var quadKeys;
97796
97797           if (dim === 16) {
97798             quadKeys = ['0000', '0001', '0010', '0011', '0100', '0101', '0110', '0111', '1000', '1001', '1010', '1011', '1100', '1101', '1110', '1111', '0002', '0003', '0012', '0013', '0102', '0103', '0112', '0113', '1002', '1003', '1012', '1013', '1102', '1103', '1112', '1113', '0020', '0021', '0030', '0031', '0120', '0121', '0130', '0131', '1020', '1021', '1030', '1031', '1120', '1121', '1130', '1131', '0022', '0023', '0032', '0033', '0122', '0123', '0132', '0133', '1022', '1023', '1032', '1033', '1122', '1123', '1132', '1133', '0200', '0201', '0210', '0211', '0300', '0301', '0310', '0311', '1200', '1201', '1210', '1211', '1300', '1301', '1310', '1311', '0202', '0203', '0212', '0213', '0302', '0303', '0312', '0313', '1202', '1203', '1212', '1213', '1302', '1303', '1312', '1313', '0220', '0221', '0230', '0231', '0320', '0321', '0330', '0331', '1220', '1221', '1230', '1231', '1320', '1321', '1330', '1331', '0222', '0223', '0232', '0233', '0322', '0323', '0332', '0333', '1222', '1223', '1232', '1233', '1322', '1323', '1332', '1333', '2000', '2001', '2010', '2011', '2100', '2101', '2110', '2111', '3000', '3001', '3010', '3011', '3100', '3101', '3110', '3111', '2002', '2003', '2012', '2013', '2102', '2103', '2112', '2113', '3002', '3003', '3012', '3013', '3102', '3103', '3112', '3113', '2020', '2021', '2030', '2031', '2120', '2121', '2130', '2131', '3020', '3021', '3030', '3031', '3120', '3121', '3130', '3131', '2022', '2023', '2032', '2033', '2122', '2123', '2132', '2133', '3022', '3023', '3032', '3033', '3122', '3123', '3132', '3133', '2200', '2201', '2210', '2211', '2300', '2301', '2310', '2311', '3200', '3201', '3210', '3211', '3300', '3301', '3310', '3311', '2202', '2203', '2212', '2213', '2302', '2303', '2312', '2313', '3202', '3203', '3212', '3213', '3302', '3303', '3312', '3313', '2220', '2221', '2230', '2231', '2320', '2321', '2330', '2331', '3220', '3221', '3230', '3231', '3320', '3321', '3330', '3331', '2222', '2223', '2232', '2233', '2322', '2323', '2332', '2333', '3222', '3223', '3232', '3233', '3322', '3323', '3332', '3333'];
97799           } else if (dim === 8) {
97800             quadKeys = ['000', '001', '010', '011', '100', '101', '110', '111', '002', '003', '012', '013', '102', '103', '112', '113', '020', '021', '030', '031', '120', '121', '130', '131', '022', '023', '032', '033', '122', '123', '132', '133', '200', '201', '210', '211', '300', '301', '310', '311', '202', '203', '212', '213', '302', '303', '312', '313', '220', '221', '230', '231', '320', '321', '330', '331', '222', '223', '232', '233', '322', '323', '332', '333'];
97801           } else if (dim === 4) {
97802             quadKeys = ['00', '01', '10', '11', '02', '03', '12', '13', '20', '21', '30', '31', '22', '23', '32', '33'];
97803           } else {
97804             // dim === 2
97805             quadKeys = ['0', '1', '2', '3'];
97806           }
97807
97808           return quadKeys;
97809         }
97810
97811         var serviceStreetside = {
97812           /**
97813            * init() initialize streetside.
97814            */
97815           init: function init() {
97816             if (!_ssCache) {
97817               this.reset();
97818             }
97819
97820             this.event = utilRebind(this, dispatch$1, 'on');
97821           },
97822
97823           /**
97824            * reset() reset the cache.
97825            */
97826           reset: function reset() {
97827             if (_ssCache) {
97828               Object.values(_ssCache.bubbles.inflight).forEach(abortRequest$1);
97829             }
97830
97831             _ssCache = {
97832               bubbles: {
97833                 inflight: {},
97834                 loaded: {},
97835                 nextPage: {},
97836                 rtree: new RBush(),
97837                 points: {},
97838                 leaders: []
97839               },
97840               sequences: {}
97841             };
97842           },
97843
97844           /**
97845            * bubbles()
97846            */
97847           bubbles: function bubbles(projection) {
97848             var limit = 5;
97849             return searchLimited(limit, projection, _ssCache.bubbles.rtree);
97850           },
97851           cachedImage: function cachedImage(imageKey) {
97852             return _ssCache.bubbles.points[imageKey];
97853           },
97854           sequences: function sequences(projection) {
97855             var viewport = projection.clipExtent();
97856             var min = [viewport[0][0], viewport[1][1]];
97857             var max = [viewport[1][0], viewport[0][1]];
97858             var bbox = geoExtent(projection.invert(min), projection.invert(max)).bbox();
97859             var seen = {};
97860             var results = []; // all sequences for bubbles in viewport
97861
97862             _ssCache.bubbles.rtree.search(bbox).forEach(function (d) {
97863               var key = d.data.sequenceKey;
97864
97865               if (key && !seen[key]) {
97866                 seen[key] = true;
97867                 results.push(_ssCache.sequences[key].geojson);
97868               }
97869             });
97870
97871             return results;
97872           },
97873
97874           /**
97875            * loadBubbles()
97876            */
97877           loadBubbles: function loadBubbles(projection, margin) {
97878             // by default: request 2 nearby tiles so we can connect sequences.
97879             if (margin === undefined) margin = 2;
97880             loadTiles('bubbles', bubbleApi, projection, margin);
97881           },
97882           viewer: function viewer() {
97883             return _pannellumViewer;
97884           },
97885           initViewer: function initViewer() {
97886             if (!window.pannellum) return;
97887             if (_pannellumViewer) return;
97888             _currScene += 1;
97889
97890             var sceneID = _currScene.toString();
97891
97892             var options = {
97893               'default': {
97894                 firstScene: sceneID
97895               },
97896               scenes: {}
97897             };
97898             options.scenes[sceneID] = _sceneOptions;
97899             _pannellumViewer = window.pannellum.viewer('ideditor-viewer-streetside', options);
97900           },
97901           ensureViewerLoaded: function ensureViewerLoaded(context) {
97902             if (_loadViewerPromise) return _loadViewerPromise; // create ms-wrapper, a photo wrapper class
97903
97904             var wrap = context.container().select('.photoviewer').selectAll('.ms-wrapper').data([0]); // inject ms-wrapper into the photoviewer div
97905             // (used by all to house each custom photo viewer)
97906
97907             var wrapEnter = wrap.enter().append('div').attr('class', 'photo-wrapper ms-wrapper').classed('hide', true);
97908             var that = this;
97909             var pointerPrefix = 'PointerEvent' in window ? 'pointer' : 'mouse'; // inject div to support streetside viewer (pannellum) and attribution line
97910
97911             wrapEnter.append('div').attr('id', 'ideditor-viewer-streetside').on(pointerPrefix + 'down.streetside', function () {
97912               select(window).on(pointerPrefix + 'move.streetside', function () {
97913                 dispatch$1.call('viewerChanged');
97914               }, true);
97915             }).on(pointerPrefix + 'up.streetside pointercancel.streetside', function () {
97916               select(window).on(pointerPrefix + 'move.streetside', null); // continue dispatching events for a few seconds, in case viewer has inertia.
97917
97918               var t = timer(function (elapsed) {
97919                 dispatch$1.call('viewerChanged');
97920
97921                 if (elapsed > 2000) {
97922                   t.stop();
97923                 }
97924               });
97925             }).append('div').attr('class', 'photo-attribution fillD');
97926             var controlsEnter = wrapEnter.append('div').attr('class', 'photo-controls-wrap').append('div').attr('class', 'photo-controls');
97927             controlsEnter.append('button').on('click.back', step(-1)).html('◄');
97928             controlsEnter.append('button').on('click.forward', step(1)).html('►'); // create working canvas for stitching together images
97929
97930             wrap = wrap.merge(wrapEnter).call(setupCanvas, true); // Register viewer resize handler
97931
97932             context.ui().photoviewer.on('resize.streetside', function () {
97933               if (_pannellumViewer) {
97934                 _pannellumViewer.resize();
97935               }
97936             });
97937             _loadViewerPromise = new Promise(function (resolve, reject) {
97938               var loadedCount = 0;
97939
97940               function loaded() {
97941                 loadedCount += 1; // wait until both files are loaded
97942
97943                 if (loadedCount === 2) resolve();
97944               }
97945
97946               var head = select('head'); // load streetside pannellum viewer css
97947
97948               head.selectAll('#ideditor-streetside-viewercss').data([0]).enter().append('link').attr('id', 'ideditor-streetside-viewercss').attr('rel', 'stylesheet').attr('crossorigin', 'anonymous').attr('href', context.asset(pannellumViewerCSS)).on('load.serviceStreetside', loaded).on('error.serviceStreetside', function () {
97949                 reject();
97950               }); // load streetside pannellum viewer js
97951
97952               head.selectAll('#ideditor-streetside-viewerjs').data([0]).enter().append('script').attr('id', 'ideditor-streetside-viewerjs').attr('crossorigin', 'anonymous').attr('src', context.asset(pannellumViewerJS)).on('load.serviceStreetside', loaded).on('error.serviceStreetside', function () {
97953                 reject();
97954               });
97955             })["catch"](function () {
97956               _loadViewerPromise = null;
97957             });
97958             return _loadViewerPromise;
97959
97960             function step(stepBy) {
97961               return function () {
97962                 var viewer = context.container().select('.photoviewer');
97963                 var selected = viewer.empty() ? undefined : viewer.datum();
97964                 if (!selected) return;
97965                 var nextID = stepBy === 1 ? selected.ne : selected.pr;
97966
97967                 var yaw = _pannellumViewer.getYaw();
97968
97969                 var ca = selected.ca + yaw;
97970                 var origin = selected.loc; // construct a search trapezoid pointing out from current bubble
97971
97972                 var meters = 35;
97973                 var p1 = [origin[0] + geoMetersToLon(meters / 5, origin[1]), origin[1]];
97974                 var p2 = [origin[0] + geoMetersToLon(meters / 2, origin[1]), origin[1] + geoMetersToLat(meters)];
97975                 var p3 = [origin[0] - geoMetersToLon(meters / 2, origin[1]), origin[1] + geoMetersToLat(meters)];
97976                 var p4 = [origin[0] - geoMetersToLon(meters / 5, origin[1]), origin[1]];
97977                 var poly = [p1, p2, p3, p4, p1]; // rotate it to face forward/backward
97978
97979                 var angle = (stepBy === 1 ? ca : ca + 180) * (Math.PI / 180);
97980                 poly = geoRotate(poly, -angle, origin);
97981                 var extent = poly.reduce(function (extent, point) {
97982                   return extent.extend(geoExtent(point));
97983                 }, geoExtent()); // find nearest other bubble in the search polygon
97984
97985                 var minDist = Infinity;
97986
97987                 _ssCache.bubbles.rtree.search(extent.bbox()).forEach(function (d) {
97988                   if (d.data.key === selected.key) return;
97989                   if (!geoPointInPolygon(d.data.loc, poly)) return;
97990                   var dist = geoVecLength(d.data.loc, selected.loc);
97991                   var theta = selected.ca - d.data.ca;
97992                   var minTheta = Math.min(Math.abs(theta), 360 - Math.abs(theta));
97993
97994                   if (minTheta > 20) {
97995                     dist += 5; // penalize distance if camera angles don't match
97996                   }
97997
97998                   if (dist < minDist) {
97999                     nextID = d.data.key;
98000                     minDist = dist;
98001                   }
98002                 });
98003
98004                 var nextBubble = nextID && that.cachedImage(nextID);
98005                 if (!nextBubble) return;
98006                 context.map().centerEase(nextBubble.loc);
98007                 that.selectImage(context, nextBubble.key).yaw(yaw).showViewer(context);
98008               };
98009             }
98010           },
98011           yaw: function yaw(_yaw) {
98012             if (typeof _yaw !== 'number') return _yaw;
98013             _sceneOptions.yaw = _yaw;
98014             return this;
98015           },
98016
98017           /**
98018            * showViewer()
98019            */
98020           showViewer: function showViewer(context) {
98021             var wrap = context.container().select('.photoviewer').classed('hide', false);
98022             var isHidden = wrap.selectAll('.photo-wrapper.ms-wrapper.hide').size();
98023
98024             if (isHidden) {
98025               wrap.selectAll('.photo-wrapper:not(.ms-wrapper)').classed('hide', true);
98026               wrap.selectAll('.photo-wrapper.ms-wrapper').classed('hide', false);
98027             }
98028
98029             return this;
98030           },
98031
98032           /**
98033            * hideViewer()
98034            */
98035           hideViewer: function hideViewer(context) {
98036             var viewer = context.container().select('.photoviewer');
98037             if (!viewer.empty()) viewer.datum(null);
98038             viewer.classed('hide', true).selectAll('.photo-wrapper').classed('hide', true);
98039             context.container().selectAll('.viewfield-group, .sequence, .icon-sign').classed('currentView', false);
98040             this.updateUrlImage(null);
98041             return this.setStyles(context, null, true);
98042           },
98043
98044           /**
98045            * selectImage().
98046            */
98047           selectImage: function selectImage(context, key) {
98048             var that = this;
98049             var d = this.cachedImage(key);
98050             var viewer = context.container().select('.photoviewer');
98051             if (!viewer.empty()) viewer.datum(d);
98052             this.setStyles(context, null, true);
98053             var wrap = context.container().select('.photoviewer .ms-wrapper');
98054             var attribution = wrap.selectAll('.photo-attribution').html('');
98055             wrap.selectAll('.pnlm-load-box') // display "loading.."
98056             .style('display', 'block');
98057             if (!d) return this;
98058             this.updateUrlImage(key);
98059             _sceneOptions.northOffset = d.ca;
98060             var line1 = attribution.append('div').attr('class', 'attribution-row');
98061             var hiresDomId = utilUniqueDomId('streetside-hires'); // Add hires checkbox
98062
98063             var label = line1.append('label').attr('for', hiresDomId).attr('class', 'streetside-hires');
98064             label.append('input').attr('type', 'checkbox').attr('id', hiresDomId).property('checked', _hires).on('click', function (d3_event) {
98065               d3_event.stopPropagation();
98066               _hires = !_hires;
98067               _resolution = _hires ? 1024 : 512;
98068               wrap.call(setupCanvas, true);
98069               var viewstate = {
98070                 yaw: _pannellumViewer.getYaw(),
98071                 pitch: _pannellumViewer.getPitch(),
98072                 hfov: _pannellumViewer.getHfov()
98073               };
98074               _sceneOptions = Object.assign(_sceneOptions, viewstate);
98075               that.selectImage(context, d.key).showViewer(context);
98076             });
98077             label.append('span').html(_t.html('streetside.hires'));
98078             var captureInfo = line1.append('div').attr('class', 'attribution-capture-info'); // Add capture date
98079
98080             if (d.captured_by) {
98081               var yyyy = new Date().getFullYear();
98082               captureInfo.append('a').attr('class', 'captured_by').attr('target', '_blank').attr('href', 'https://www.microsoft.com/en-us/maps/streetside').html('©' + yyyy + ' Microsoft');
98083               captureInfo.append('span').html('|');
98084             }
98085
98086             if (d.captured_at) {
98087               captureInfo.append('span').attr('class', 'captured_at').html(localeTimestamp(d.captured_at));
98088             } // Add image links
98089
98090
98091             var line2 = attribution.append('div').attr('class', 'attribution-row');
98092             line2.append('a').attr('class', 'image-view-link').attr('target', '_blank').attr('href', 'https://www.bing.com/maps?cp=' + d.loc[1] + '~' + d.loc[0] + '&lvl=17&dir=' + d.ca + '&style=x&v=2&sV=1').html(_t.html('streetside.view_on_bing'));
98093             line2.append('a').attr('class', 'image-report-link').attr('target', '_blank').attr('href', 'https://www.bing.com/maps/privacyreport/streetsideprivacyreport?bubbleid=' + encodeURIComponent(d.key) + '&focus=photo&lat=' + d.loc[1] + '&lng=' + d.loc[0] + '&z=17').html(_t.html('streetside.report'));
98094             var bubbleIdQuadKey = d.key.toString(4);
98095             var paddingNeeded = 16 - bubbleIdQuadKey.length;
98096
98097             for (var i = 0; i < paddingNeeded; i++) {
98098               bubbleIdQuadKey = '0' + bubbleIdQuadKey;
98099             }
98100
98101             var imgUrlPrefix = streetsideImagesApi + 'hs' + bubbleIdQuadKey;
98102             var imgUrlSuffix = '.jpg?g=6338&n=z'; // Cubemap face code order matters here: front=01, right=02, back=03, left=10, up=11, down=12
98103
98104             var faceKeys = ['01', '02', '03', '10', '11', '12']; // Map images to cube faces
98105
98106             var quadKeys = getQuadKeys();
98107             var faces = faceKeys.map(function (faceKey) {
98108               return quadKeys.map(function (quadKey) {
98109                 var xy = qkToXY(quadKey);
98110                 return {
98111                   face: faceKey,
98112                   url: imgUrlPrefix + faceKey + quadKey + imgUrlSuffix,
98113                   x: xy[0],
98114                   y: xy[1]
98115                 };
98116               });
98117             });
98118             loadFaces(faces).then(function () {
98119               if (!_pannellumViewer) {
98120                 that.initViewer();
98121               } else {
98122                 // make a new scene
98123                 _currScene += 1;
98124
98125                 var sceneID = _currScene.toString();
98126
98127                 _pannellumViewer.addScene(sceneID, _sceneOptions).loadScene(sceneID); // remove previous scene
98128
98129
98130                 if (_currScene > 2) {
98131                   sceneID = (_currScene - 1).toString();
98132
98133                   _pannellumViewer.removeScene(sceneID);
98134                 }
98135               }
98136             });
98137             return this;
98138           },
98139           getSequenceKeyForBubble: function getSequenceKeyForBubble(d) {
98140             return d && d.sequenceKey;
98141           },
98142           // Updates the currently highlighted sequence and selected bubble.
98143           // Reset is only necessary when interacting with the viewport because
98144           // this implicitly changes the currently selected bubble/sequence
98145           setStyles: function setStyles(context, hovered, reset) {
98146             if (reset) {
98147               // reset all layers
98148               context.container().selectAll('.viewfield-group').classed('highlighted', false).classed('hovered', false).classed('currentView', false);
98149               context.container().selectAll('.sequence').classed('highlighted', false).classed('currentView', false);
98150             }
98151
98152             var hoveredBubbleKey = hovered && hovered.key;
98153             var hoveredSequenceKey = this.getSequenceKeyForBubble(hovered);
98154             var hoveredSequence = hoveredSequenceKey && _ssCache.sequences[hoveredSequenceKey];
98155             var hoveredBubbleKeys = hoveredSequence && hoveredSequence.bubbles.map(function (d) {
98156               return d.key;
98157             }) || [];
98158             var viewer = context.container().select('.photoviewer');
98159             var selected = viewer.empty() ? undefined : viewer.datum();
98160             var selectedBubbleKey = selected && selected.key;
98161             var selectedSequenceKey = this.getSequenceKeyForBubble(selected);
98162             var selectedSequence = selectedSequenceKey && _ssCache.sequences[selectedSequenceKey];
98163             var selectedBubbleKeys = selectedSequence && selectedSequence.bubbles.map(function (d) {
98164               return d.key;
98165             }) || []; // highlight sibling viewfields on either the selected or the hovered sequences
98166
98167             var highlightedBubbleKeys = utilArrayUnion(hoveredBubbleKeys, selectedBubbleKeys);
98168             context.container().selectAll('.layer-streetside-images .viewfield-group').classed('highlighted', function (d) {
98169               return highlightedBubbleKeys.indexOf(d.key) !== -1;
98170             }).classed('hovered', function (d) {
98171               return d.key === hoveredBubbleKey;
98172             }).classed('currentView', function (d) {
98173               return d.key === selectedBubbleKey;
98174             });
98175             context.container().selectAll('.layer-streetside-images .sequence').classed('highlighted', function (d) {
98176               return d.properties.key === hoveredSequenceKey;
98177             }).classed('currentView', function (d) {
98178               return d.properties.key === selectedSequenceKey;
98179             }); // update viewfields if needed
98180
98181             context.container().selectAll('.layer-streetside-images .viewfield-group .viewfield').attr('d', viewfieldPath);
98182
98183             function viewfieldPath() {
98184               var d = this.parentNode.__data__;
98185
98186               if (d.pano && d.key !== selectedBubbleKey) {
98187                 return 'M 8,13 m -10,0 a 10,10 0 1,0 20,0 a 10,10 0 1,0 -20,0';
98188               } else {
98189                 return 'M 6,9 C 8,8.4 8,8.4 10,9 L 16,-2 C 12,-5 4,-5 0,-2 z';
98190               }
98191             }
98192
98193             return this;
98194           },
98195           updateUrlImage: function updateUrlImage(imageKey) {
98196             if (!window.mocha) {
98197               var hash = utilStringQs(window.location.hash);
98198
98199               if (imageKey) {
98200                 hash.photo = 'streetside/' + imageKey;
98201               } else {
98202                 delete hash.photo;
98203               }
98204
98205               window.location.replace('#' + utilQsString(hash, true));
98206             }
98207           },
98208
98209           /**
98210            * cache().
98211            */
98212           cache: function cache() {
98213             return _ssCache;
98214           }
98215         };
98216
98217         var _apibase = 'https://taginfo.openstreetmap.org/api/4/';
98218         var _inflight = {};
98219         var _popularKeys = {};
98220         var _taginfoCache = {};
98221         var tag_sorts = {
98222           point: 'count_nodes',
98223           vertex: 'count_nodes',
98224           area: 'count_ways',
98225           line: 'count_ways'
98226         };
98227         var tag_sort_members = {
98228           point: 'count_node_members',
98229           vertex: 'count_node_members',
98230           area: 'count_way_members',
98231           line: 'count_way_members',
98232           relation: 'count_relation_members'
98233         };
98234         var tag_filters = {
98235           point: 'nodes',
98236           vertex: 'nodes',
98237           area: 'ways',
98238           line: 'ways'
98239         };
98240         var tag_members_fractions = {
98241           point: 'count_node_members_fraction',
98242           vertex: 'count_node_members_fraction',
98243           area: 'count_way_members_fraction',
98244           line: 'count_way_members_fraction',
98245           relation: 'count_relation_members_fraction'
98246         };
98247
98248         function sets(params, n, o) {
98249           if (params.geometry && o[params.geometry]) {
98250             params[n] = o[params.geometry];
98251           }
98252
98253           return params;
98254         }
98255
98256         function setFilter(params) {
98257           return sets(params, 'filter', tag_filters);
98258         }
98259
98260         function setSort(params) {
98261           return sets(params, 'sortname', tag_sorts);
98262         }
98263
98264         function setSortMembers(params) {
98265           return sets(params, 'sortname', tag_sort_members);
98266         }
98267
98268         function clean(params) {
98269           return utilObjectOmit(params, ['geometry', 'debounce']);
98270         }
98271
98272         function filterKeys(type) {
98273           var count_type = type ? 'count_' + type : 'count_all';
98274           return function (d) {
98275             return parseFloat(d[count_type]) > 2500 || d.in_wiki;
98276           };
98277         }
98278
98279         function filterMultikeys(prefix) {
98280           return function (d) {
98281             // d.key begins with prefix, and d.key contains no additional ':'s
98282             var re = new RegExp('^' + prefix + '(.*)$');
98283             var matches = d.key.match(re) || [];
98284             return matches.length === 2 && matches[1].indexOf(':') === -1;
98285           };
98286         }
98287
98288         function filterValues(allowUpperCase) {
98289           return function (d) {
98290             if (d.value.match(/[;,]/) !== null) return false; // exclude some punctuation
98291
98292             if (!allowUpperCase && d.value.match(/[A-Z*]/) !== null) return false; // exclude uppercase letters
98293
98294             return parseFloat(d.fraction) > 0.0;
98295           };
98296         }
98297
98298         function filterRoles(geometry) {
98299           return function (d) {
98300             if (d.role === '') return false; // exclude empty role
98301
98302             if (d.role.match(/[A-Z*;,]/) !== null) return false; // exclude uppercase letters and some punctuation
98303
98304             return parseFloat(d[tag_members_fractions[geometry]]) > 0.0;
98305           };
98306         }
98307
98308         function valKey(d) {
98309           return {
98310             value: d.key,
98311             title: d.key
98312           };
98313         }
98314
98315         function valKeyDescription(d) {
98316           var obj = {
98317             value: d.value,
98318             title: d.description || d.value
98319           };
98320
98321           if (d.count) {
98322             obj.count = d.count;
98323           }
98324
98325           return obj;
98326         }
98327
98328         function roleKey(d) {
98329           return {
98330             value: d.role,
98331             title: d.role
98332           };
98333         } // sort keys with ':' lower than keys without ':'
98334
98335
98336         function sortKeys(a, b) {
98337           return a.key.indexOf(':') === -1 && b.key.indexOf(':') !== -1 ? -1 : a.key.indexOf(':') !== -1 && b.key.indexOf(':') === -1 ? 1 : 0;
98338         }
98339
98340         var debouncedRequest = debounce(request, 300, {
98341           leading: false
98342         });
98343
98344         function request(url, params, exactMatch, callback, loaded) {
98345           if (_inflight[url]) return;
98346           if (checkCache(url, params, exactMatch, callback)) return;
98347           var controller = new AbortController();
98348           _inflight[url] = controller;
98349           d3_json(url, {
98350             signal: controller.signal
98351           }).then(function (result) {
98352             delete _inflight[url];
98353             if (loaded) loaded(null, result);
98354           })["catch"](function (err) {
98355             delete _inflight[url];
98356             if (err.name === 'AbortError') return;
98357             if (loaded) loaded(err.message);
98358           });
98359         }
98360
98361         function checkCache(url, params, exactMatch, callback) {
98362           var rp = params.rp || 25;
98363           var testQuery = params.query || '';
98364           var testUrl = url;
98365
98366           do {
98367             var hit = _taginfoCache[testUrl]; // exact match, or shorter match yielding fewer than max results (rp)
98368
98369             if (hit && (url === testUrl || hit.length < rp)) {
98370               callback(null, hit);
98371               return true;
98372             } // don't try to shorten the query
98373
98374
98375             if (exactMatch || !testQuery.length) return false; // do shorten the query to see if we already have a cached result
98376             // that has returned fewer than max results (rp)
98377
98378             testQuery = testQuery.slice(0, -1);
98379             testUrl = url.replace(/&query=(.*?)&/, '&query=' + testQuery + '&');
98380           } while (testQuery.length >= 0);
98381
98382           return false;
98383         }
98384
98385         var serviceTaginfo = {
98386           init: function init() {
98387             _inflight = {};
98388             _taginfoCache = {};
98389             _popularKeys = {
98390               // manually exclude some keys – #5377, #7485
98391               postal_code: true,
98392               full_name: true,
98393               loc_name: true,
98394               reg_name: true,
98395               short_name: true,
98396               sorting_name: true,
98397               artist_name: true,
98398               nat_name: true,
98399               long_name: true,
98400               'bridge:name': true
98401             }; // Fetch popular keys.  We'll exclude these from `values`
98402             // lookups because they stress taginfo, and they aren't likely
98403             // to yield meaningful autocomplete results.. see #3955
98404
98405             var params = {
98406               rp: 100,
98407               sortname: 'values_all',
98408               sortorder: 'desc',
98409               page: 1,
98410               debounce: false,
98411               lang: _mainLocalizer.languageCode()
98412             };
98413             this.keys(params, function (err, data) {
98414               if (err) return;
98415               data.forEach(function (d) {
98416                 if (d.value === 'opening_hours') return; // exception
98417
98418                 _popularKeys[d.value] = true;
98419               });
98420             });
98421           },
98422           reset: function reset() {
98423             Object.values(_inflight).forEach(function (controller) {
98424               controller.abort();
98425             });
98426             _inflight = {};
98427           },
98428           keys: function keys(params, callback) {
98429             var doRequest = params.debounce ? debouncedRequest : request;
98430             params = clean(setSort(params));
98431             params = Object.assign({
98432               rp: 10,
98433               sortname: 'count_all',
98434               sortorder: 'desc',
98435               page: 1,
98436               lang: _mainLocalizer.languageCode()
98437             }, params);
98438             var url = _apibase + 'keys/all?' + utilQsString(params);
98439             doRequest(url, params, false, callback, function (err, d) {
98440               if (err) {
98441                 callback(err);
98442               } else {
98443                 var f = filterKeys(params.filter);
98444                 var result = d.data.filter(f).sort(sortKeys).map(valKey);
98445                 _taginfoCache[url] = result;
98446                 callback(null, result);
98447               }
98448             });
98449           },
98450           multikeys: function multikeys(params, callback) {
98451             var doRequest = params.debounce ? debouncedRequest : request;
98452             params = clean(setSort(params));
98453             params = Object.assign({
98454               rp: 25,
98455               sortname: 'count_all',
98456               sortorder: 'desc',
98457               page: 1,
98458               lang: _mainLocalizer.languageCode()
98459             }, params);
98460             var prefix = params.query;
98461             var url = _apibase + 'keys/all?' + utilQsString(params);
98462             doRequest(url, params, true, callback, function (err, d) {
98463               if (err) {
98464                 callback(err);
98465               } else {
98466                 var f = filterMultikeys(prefix);
98467                 var result = d.data.filter(f).map(valKey);
98468                 _taginfoCache[url] = result;
98469                 callback(null, result);
98470               }
98471             });
98472           },
98473           values: function values(params, callback) {
98474             // Exclude popular keys from values lookups.. see #3955
98475             var key = params.key;
98476
98477             if (key && _popularKeys[key]) {
98478               callback(null, []);
98479               return;
98480             }
98481
98482             var doRequest = params.debounce ? debouncedRequest : request;
98483             params = clean(setSort(setFilter(params)));
98484             params = Object.assign({
98485               rp: 25,
98486               sortname: 'count_all',
98487               sortorder: 'desc',
98488               page: 1,
98489               lang: _mainLocalizer.languageCode()
98490             }, params);
98491             var url = _apibase + 'key/values?' + utilQsString(params);
98492             doRequest(url, params, false, callback, function (err, d) {
98493               if (err) {
98494                 callback(err);
98495               } else {
98496                 // In most cases we prefer taginfo value results with lowercase letters.
98497                 // A few OSM keys expect values to contain uppercase values (see #3377).
98498                 // This is not an exhaustive list (e.g. `name` also has uppercase values)
98499                 // but these are the fields where taginfo value lookup is most useful.
98500                 var re = /network|taxon|genus|species|brand|grape_variety|royal_cypher|listed_status|booth|rating|stars|:output|_hours|_times|_ref|manufacturer|country|target|brewery/;
98501                 var allowUpperCase = re.test(params.key);
98502                 var f = filterValues(allowUpperCase);
98503                 var result = d.data.filter(f).map(valKeyDescription);
98504                 _taginfoCache[url] = result;
98505                 callback(null, result);
98506               }
98507             });
98508           },
98509           roles: function roles(params, callback) {
98510             var doRequest = params.debounce ? debouncedRequest : request;
98511             var geometry = params.geometry;
98512             params = clean(setSortMembers(params));
98513             params = Object.assign({
98514               rp: 25,
98515               sortname: 'count_all_members',
98516               sortorder: 'desc',
98517               page: 1,
98518               lang: _mainLocalizer.languageCode()
98519             }, params);
98520             var url = _apibase + 'relation/roles?' + utilQsString(params);
98521             doRequest(url, params, true, callback, function (err, d) {
98522               if (err) {
98523                 callback(err);
98524               } else {
98525                 var f = filterRoles(geometry);
98526                 var result = d.data.filter(f).map(roleKey);
98527                 _taginfoCache[url] = result;
98528                 callback(null, result);
98529               }
98530             });
98531           },
98532           docs: function docs(params, callback) {
98533             var doRequest = params.debounce ? debouncedRequest : request;
98534             params = clean(setSort(params));
98535             var path = 'key/wiki_pages?';
98536
98537             if (params.value) {
98538               path = 'tag/wiki_pages?';
98539             } else if (params.rtype) {
98540               path = 'relation/wiki_pages?';
98541             }
98542
98543             var url = _apibase + path + utilQsString(params);
98544             doRequest(url, params, true, callback, function (err, d) {
98545               if (err) {
98546                 callback(err);
98547               } else {
98548                 _taginfoCache[url] = d.data;
98549                 callback(null, d.data);
98550               }
98551             });
98552           },
98553           apibase: function apibase(_) {
98554             if (!arguments.length) return _apibase;
98555             _apibase = _;
98556             return this;
98557           }
98558         };
98559
98560         /**
98561          * Wraps a GeoJSON {@link Geometry} in a GeoJSON {@link Feature}.
98562          *
98563          * @name feature
98564          * @param {Geometry} geometry input geometry
98565          * @param {Object} [properties={}] an Object of key-value pairs to add as properties
98566          * @param {Object} [options={}] Optional Parameters
98567          * @param {Array<number>} [options.bbox] Bounding Box Array [west, south, east, north] associated with the Feature
98568          * @param {string|number} [options.id] Identifier associated with the Feature
98569          * @returns {Feature} a GeoJSON Feature
98570          * @example
98571          * var geometry = {
98572          *   "type": "Point",
98573          *   "coordinates": [110, 50]
98574          * };
98575          *
98576          * var feature = turf.feature(geometry);
98577          *
98578          * //=feature
98579          */
98580
98581         function feature(geom, properties, options) {
98582           if (options === void 0) {
98583             options = {};
98584           }
98585
98586           var feat = {
98587             type: "Feature"
98588           };
98589
98590           if (options.id === 0 || options.id) {
98591             feat.id = options.id;
98592           }
98593
98594           if (options.bbox) {
98595             feat.bbox = options.bbox;
98596           }
98597
98598           feat.properties = properties || {};
98599           feat.geometry = geom;
98600           return feat;
98601         }
98602         /**
98603          * Creates a {@link Polygon} {@link Feature} from an Array of LinearRings.
98604          *
98605          * @name polygon
98606          * @param {Array<Array<Array<number>>>} coordinates an array of LinearRings
98607          * @param {Object} [properties={}] an Object of key-value pairs to add as properties
98608          * @param {Object} [options={}] Optional Parameters
98609          * @param {Array<number>} [options.bbox] Bounding Box Array [west, south, east, north] associated with the Feature
98610          * @param {string|number} [options.id] Identifier associated with the Feature
98611          * @returns {Feature<Polygon>} Polygon Feature
98612          * @example
98613          * var polygon = turf.polygon([[[-5, 52], [-4, 56], [-2, 51], [-7, 54], [-5, 52]]], { name: 'poly1' });
98614          *
98615          * //=polygon
98616          */
98617
98618         function polygon(coordinates, properties, options) {
98619           if (options === void 0) {
98620             options = {};
98621           }
98622
98623           for (var _i = 0, coordinates_1 = coordinates; _i < coordinates_1.length; _i++) {
98624             var ring = coordinates_1[_i];
98625
98626             if (ring.length < 4) {
98627               throw new Error("Each LinearRing of a Polygon must have 4 or more Positions.");
98628             }
98629
98630             for (var j = 0; j < ring[ring.length - 1].length; j++) {
98631               // Check if first point of Polygon contains two numbers
98632               if (ring[ring.length - 1][j] !== ring[0][j]) {
98633                 throw new Error("First and last Position are not equivalent.");
98634               }
98635             }
98636           }
98637
98638           var geom = {
98639             type: "Polygon",
98640             coordinates: coordinates
98641           };
98642           return feature(geom, properties, options);
98643         }
98644         /**
98645          * Creates a {@link LineString} {@link Feature} from an Array of Positions.
98646          *
98647          * @name lineString
98648          * @param {Array<Array<number>>} coordinates an array of Positions
98649          * @param {Object} [properties={}] an Object of key-value pairs to add as properties
98650          * @param {Object} [options={}] Optional Parameters
98651          * @param {Array<number>} [options.bbox] Bounding Box Array [west, south, east, north] associated with the Feature
98652          * @param {string|number} [options.id] Identifier associated with the Feature
98653          * @returns {Feature<LineString>} LineString Feature
98654          * @example
98655          * var linestring1 = turf.lineString([[-24, 63], [-23, 60], [-25, 65], [-20, 69]], {name: 'line 1'});
98656          * var linestring2 = turf.lineString([[-14, 43], [-13, 40], [-15, 45], [-10, 49]], {name: 'line 2'});
98657          *
98658          * //=linestring1
98659          * //=linestring2
98660          */
98661
98662         function lineString(coordinates, properties, options) {
98663           if (options === void 0) {
98664             options = {};
98665           }
98666
98667           if (coordinates.length < 2) {
98668             throw new Error("coordinates must be an array of two or more positions");
98669           }
98670
98671           var geom = {
98672             type: "LineString",
98673             coordinates: coordinates
98674           };
98675           return feature(geom, properties, options);
98676         }
98677         /**
98678          * Creates a {@link Feature<MultiLineString>} based on a
98679          * coordinate array. Properties can be added optionally.
98680          *
98681          * @name multiLineString
98682          * @param {Array<Array<Array<number>>>} coordinates an array of LineStrings
98683          * @param {Object} [properties={}] an Object of key-value pairs to add as properties
98684          * @param {Object} [options={}] Optional Parameters
98685          * @param {Array<number>} [options.bbox] Bounding Box Array [west, south, east, north] associated with the Feature
98686          * @param {string|number} [options.id] Identifier associated with the Feature
98687          * @returns {Feature<MultiLineString>} a MultiLineString feature
98688          * @throws {Error} if no coordinates are passed
98689          * @example
98690          * var multiLine = turf.multiLineString([[[0,0],[10,10]]]);
98691          *
98692          * //=multiLine
98693          */
98694
98695         function multiLineString(coordinates, properties, options) {
98696           if (options === void 0) {
98697             options = {};
98698           }
98699
98700           var geom = {
98701             type: "MultiLineString",
98702             coordinates: coordinates
98703           };
98704           return feature(geom, properties, options);
98705         }
98706         /**
98707          * Creates a {@link Feature<MultiPolygon>} based on a
98708          * coordinate array. Properties can be added optionally.
98709          *
98710          * @name multiPolygon
98711          * @param {Array<Array<Array<Array<number>>>>} coordinates an array of Polygons
98712          * @param {Object} [properties={}] an Object of key-value pairs to add as properties
98713          * @param {Object} [options={}] Optional Parameters
98714          * @param {Array<number>} [options.bbox] Bounding Box Array [west, south, east, north] associated with the Feature
98715          * @param {string|number} [options.id] Identifier associated with the Feature
98716          * @returns {Feature<MultiPolygon>} a multipolygon feature
98717          * @throws {Error} if no coordinates are passed
98718          * @example
98719          * var multiPoly = turf.multiPolygon([[[[0,0],[0,10],[10,10],[10,0],[0,0]]]]);
98720          *
98721          * //=multiPoly
98722          *
98723          */
98724
98725         function multiPolygon(coordinates, properties, options) {
98726           if (options === void 0) {
98727             options = {};
98728           }
98729
98730           var geom = {
98731             type: "MultiPolygon",
98732             coordinates: coordinates
98733           };
98734           return feature(geom, properties, options);
98735         }
98736
98737         /**
98738          * Get Geometry from Feature or Geometry Object
98739          *
98740          * @param {Feature|Geometry} geojson GeoJSON Feature or Geometry Object
98741          * @returns {Geometry|null} GeoJSON Geometry Object
98742          * @throws {Error} if geojson is not a Feature or Geometry Object
98743          * @example
98744          * var point = {
98745          *   "type": "Feature",
98746          *   "properties": {},
98747          *   "geometry": {
98748          *     "type": "Point",
98749          *     "coordinates": [110, 40]
98750          *   }
98751          * }
98752          * var geom = turf.getGeom(point)
98753          * //={"type": "Point", "coordinates": [110, 40]}
98754          */
98755
98756         function getGeom(geojson) {
98757           if (geojson.type === "Feature") {
98758             return geojson.geometry;
98759           }
98760
98761           return geojson;
98762         }
98763
98764         // Cohen-Sutherland line clipping algorithm, adapted to efficiently
98765         // handle polylines rather than just segments
98766         function lineclip(points, bbox, result) {
98767           var len = points.length,
98768               codeA = bitCode(points[0], bbox),
98769               part = [],
98770               i,
98771               codeB,
98772               lastCode;
98773           var a;
98774           var b;
98775           if (!result) result = [];
98776
98777           for (i = 1; i < len; i++) {
98778             a = points[i - 1];
98779             b = points[i];
98780             codeB = lastCode = bitCode(b, bbox);
98781
98782             while (true) {
98783               if (!(codeA | codeB)) {
98784                 // accept
98785                 part.push(a);
98786
98787                 if (codeB !== lastCode) {
98788                   // segment went outside
98789                   part.push(b);
98790
98791                   if (i < len - 1) {
98792                     // start a new line
98793                     result.push(part);
98794                     part = [];
98795                   }
98796                 } else if (i === len - 1) {
98797                   part.push(b);
98798                 }
98799
98800                 break;
98801               } else if (codeA & codeB) {
98802                 // trivial reject
98803                 break;
98804               } else if (codeA) {
98805                 // a outside, intersect with clip edge
98806                 a = intersect(a, b, codeA, bbox);
98807                 codeA = bitCode(a, bbox);
98808               } else {
98809                 // b outside
98810                 b = intersect(a, b, codeB, bbox);
98811                 codeB = bitCode(b, bbox);
98812               }
98813             }
98814
98815             codeA = lastCode;
98816           }
98817
98818           if (part.length) result.push(part);
98819           return result;
98820         } // Sutherland-Hodgeman polygon clipping algorithm
98821
98822         function polygonclip(points, bbox) {
98823           var result, edge, prev, prevInside, i, p, inside; // clip against each side of the clip rectangle
98824
98825           for (edge = 1; edge <= 8; edge *= 2) {
98826             result = [];
98827             prev = points[points.length - 1];
98828             prevInside = !(bitCode(prev, bbox) & edge);
98829
98830             for (i = 0; i < points.length; i++) {
98831               p = points[i];
98832               inside = !(bitCode(p, bbox) & edge); // if segment goes through the clip window, add an intersection
98833
98834               if (inside !== prevInside) result.push(intersect(prev, p, edge, bbox));
98835               if (inside) result.push(p); // add a point if it's inside
98836
98837               prev = p;
98838               prevInside = inside;
98839             }
98840
98841             points = result;
98842             if (!points.length) break;
98843           }
98844
98845           return result;
98846         } // intersect a segment against one of the 4 lines that make up the bbox
98847
98848         function intersect(a, b, edge, bbox) {
98849           return edge & 8 ? [a[0] + (b[0] - a[0]) * (bbox[3] - a[1]) / (b[1] - a[1]), bbox[3]] // top
98850           : edge & 4 ? [a[0] + (b[0] - a[0]) * (bbox[1] - a[1]) / (b[1] - a[1]), bbox[1]] // bottom
98851           : edge & 2 ? [bbox[2], a[1] + (b[1] - a[1]) * (bbox[2] - a[0]) / (b[0] - a[0])] // right
98852           : edge & 1 ? [bbox[0], a[1] + (b[1] - a[1]) * (bbox[0] - a[0]) / (b[0] - a[0])] // left
98853           : null;
98854         } // bit code reflects the point position relative to the bbox:
98855         //         left  mid  right
98856         //    top  1001  1000  1010
98857         //    mid  0001  0000  0010
98858         // bottom  0101  0100  0110
98859
98860
98861         function bitCode(p, bbox) {
98862           var code = 0;
98863           if (p[0] < bbox[0]) code |= 1; // left
98864           else if (p[0] > bbox[2]) code |= 2; // right
98865
98866           if (p[1] < bbox[1]) code |= 4; // bottom
98867           else if (p[1] > bbox[3]) code |= 8; // top
98868
98869           return code;
98870         }
98871
98872         /**
98873          * Takes a {@link Feature} and a bbox and clips the feature to the bbox using
98874          * [lineclip](https://github.com/mapbox/lineclip).
98875          * May result in degenerate edges when clipping Polygons.
98876          *
98877          * @name bboxClip
98878          * @param {Feature<LineString|MultiLineString|Polygon|MultiPolygon>} feature feature to clip to the bbox
98879          * @param {BBox} bbox extent in [minX, minY, maxX, maxY] order
98880          * @returns {Feature<LineString|MultiLineString|Polygon|MultiPolygon>} clipped Feature
98881          * @example
98882          * var bbox = [0, 0, 10, 10];
98883          * var poly = turf.polygon([[[2, 2], [8, 4], [12, 8], [3, 7], [2, 2]]]);
98884          *
98885          * var clipped = turf.bboxClip(poly, bbox);
98886          *
98887          * //addToMap
98888          * var addToMap = [bbox, poly, clipped]
98889          */
98890
98891         function bboxClip(feature, bbox) {
98892           var geom = getGeom(feature);
98893           var type = geom.type;
98894           var properties = feature.type === "Feature" ? feature.properties : {};
98895           var coords = geom.coordinates;
98896
98897           switch (type) {
98898             case "LineString":
98899             case "MultiLineString":
98900               var lines_1 = [];
98901
98902               if (type === "LineString") {
98903                 coords = [coords];
98904               }
98905
98906               coords.forEach(function (line) {
98907                 lineclip(line, bbox, lines_1);
98908               });
98909
98910               if (lines_1.length === 1) {
98911                 return lineString(lines_1[0], properties);
98912               }
98913
98914               return multiLineString(lines_1, properties);
98915
98916             case "Polygon":
98917               return polygon(clipPolygon(coords, bbox), properties);
98918
98919             case "MultiPolygon":
98920               return multiPolygon(coords.map(function (poly) {
98921                 return clipPolygon(poly, bbox);
98922               }), properties);
98923
98924             default:
98925               throw new Error("geometry " + type + " not supported");
98926           }
98927         }
98928
98929         function clipPolygon(rings, bbox) {
98930           var outRings = [];
98931
98932           for (var _i = 0, rings_1 = rings; _i < rings_1.length; _i++) {
98933             var ring = rings_1[_i];
98934             var clipped = polygonclip(ring, bbox);
98935
98936             if (clipped.length > 0) {
98937               if (clipped[0][0] !== clipped[clipped.length - 1][0] || clipped[0][1] !== clipped[clipped.length - 1][1]) {
98938                 clipped.push(clipped[0]);
98939               }
98940
98941               if (clipped.length >= 4) {
98942                 outRings.push(clipped);
98943               }
98944             }
98945           }
98946
98947           return outRings;
98948         }
98949
98950         var tiler = utilTiler().tileSize(512).margin(1);
98951         var dispatch = dispatch$8('loadedData');
98952
98953         var _vtCache;
98954
98955         function abortRequest(controller) {
98956           controller.abort();
98957         }
98958
98959         function vtToGeoJSON(data, tile, mergeCache) {
98960           var vectorTile$1 = new vectorTile.VectorTile(new pbf(data));
98961           var layers = Object.keys(vectorTile$1.layers);
98962
98963           if (!Array.isArray(layers)) {
98964             layers = [layers];
98965           }
98966
98967           var features = [];
98968           layers.forEach(function (layerID) {
98969             var layer = vectorTile$1.layers[layerID];
98970
98971             if (layer) {
98972               for (var i = 0; i < layer.length; i++) {
98973                 var feature = layer.feature(i).toGeoJSON(tile.xyz[0], tile.xyz[1], tile.xyz[2]);
98974                 var geometry = feature.geometry; // Treat all Polygons as MultiPolygons
98975
98976                 if (geometry.type === 'Polygon') {
98977                   geometry.type = 'MultiPolygon';
98978                   geometry.coordinates = [geometry.coordinates];
98979                 }
98980
98981                 var isClipped = false; // Clip to tile bounds
98982
98983                 if (geometry.type === 'MultiPolygon') {
98984                   var featureClip = bboxClip(feature, tile.extent.rectangle());
98985
98986                   if (!fastDeepEqual(feature.geometry, featureClip.geometry)) {
98987                     // feature = featureClip;
98988                     isClipped = true;
98989                   }
98990
98991                   if (!feature.geometry.coordinates.length) continue; // not actually on this tile
98992
98993                   if (!feature.geometry.coordinates[0].length) continue; // not actually on this tile
98994                 } // Generate some unique IDs and add some metadata
98995
98996
98997                 var featurehash = utilHashcode(fastJsonStableStringify(feature));
98998                 var propertyhash = utilHashcode(fastJsonStableStringify(feature.properties || {}));
98999                 feature.__layerID__ = layerID.replace(/[^_a-zA-Z0-9\-]/g, '_');
99000                 feature.__featurehash__ = featurehash;
99001                 feature.__propertyhash__ = propertyhash;
99002                 features.push(feature); // Clipped Polygons at same zoom with identical properties can get merged
99003
99004                 if (isClipped && geometry.type === 'MultiPolygon') {
99005                   var merged = mergeCache[propertyhash];
99006
99007                   if (merged && merged.length) {
99008                     var other = merged[0];
99009                     var coords = index.union(feature.geometry.coordinates, other.geometry.coordinates);
99010
99011                     if (!coords || !coords.length) {
99012                       continue; // something failed in polygon union
99013                     }
99014
99015                     merged.push(feature);
99016
99017                     for (var j = 0; j < merged.length; j++) {
99018                       // all these features get...
99019                       merged[j].geometry.coordinates = coords; // same coords
99020
99021                       merged[j].__featurehash__ = featurehash; // same hash, so deduplication works
99022                     }
99023                   } else {
99024                     mergeCache[propertyhash] = [feature];
99025                   }
99026                 }
99027               }
99028             }
99029           });
99030           return features;
99031         }
99032
99033         function loadTile(source, tile) {
99034           if (source.loaded[tile.id] || source.inflight[tile.id]) return;
99035           var url = source.template.replace('{x}', tile.xyz[0]).replace('{y}', tile.xyz[1]) // TMS-flipped y coordinate
99036           .replace(/\{[t-]y\}/, Math.pow(2, tile.xyz[2]) - tile.xyz[1] - 1).replace(/\{z(oom)?\}/, tile.xyz[2]).replace(/\{switch:([^}]+)\}/, function (s, r) {
99037             var subdomains = r.split(',');
99038             return subdomains[(tile.xyz[0] + tile.xyz[1]) % subdomains.length];
99039           });
99040           var controller = new AbortController();
99041           source.inflight[tile.id] = controller;
99042           fetch(url, {
99043             signal: controller.signal
99044           }).then(function (response) {
99045             if (!response.ok) {
99046               throw new Error(response.status + ' ' + response.statusText);
99047             }
99048
99049             source.loaded[tile.id] = [];
99050             delete source.inflight[tile.id];
99051             return response.arrayBuffer();
99052           }).then(function (data) {
99053             if (!data) {
99054               throw new Error('No Data');
99055             }
99056
99057             var z = tile.xyz[2];
99058
99059             if (!source.canMerge[z]) {
99060               source.canMerge[z] = {}; // initialize mergeCache
99061             }
99062
99063             source.loaded[tile.id] = vtToGeoJSON(data, tile, source.canMerge[z]);
99064             dispatch.call('loadedData');
99065           })["catch"](function () {
99066             source.loaded[tile.id] = [];
99067             delete source.inflight[tile.id];
99068           });
99069         }
99070
99071         var serviceVectorTile = {
99072           init: function init() {
99073             if (!_vtCache) {
99074               this.reset();
99075             }
99076
99077             this.event = utilRebind(this, dispatch, 'on');
99078           },
99079           reset: function reset() {
99080             for (var sourceID in _vtCache) {
99081               var source = _vtCache[sourceID];
99082
99083               if (source && source.inflight) {
99084                 Object.values(source.inflight).forEach(abortRequest);
99085               }
99086             }
99087
99088             _vtCache = {};
99089           },
99090           addSource: function addSource(sourceID, template) {
99091             _vtCache[sourceID] = {
99092               template: template,
99093               inflight: {},
99094               loaded: {},
99095               canMerge: {}
99096             };
99097             return _vtCache[sourceID];
99098           },
99099           data: function data(sourceID, projection) {
99100             var source = _vtCache[sourceID];
99101             if (!source) return [];
99102             var tiles = tiler.getTiles(projection);
99103             var seen = {};
99104             var results = [];
99105
99106             for (var i = 0; i < tiles.length; i++) {
99107               var features = source.loaded[tiles[i].id];
99108               if (!features || !features.length) continue;
99109
99110               for (var j = 0; j < features.length; j++) {
99111                 var feature = features[j];
99112                 var hash = feature.__featurehash__;
99113                 if (seen[hash]) continue;
99114                 seen[hash] = true; // return a shallow copy, because the hash may change
99115                 // later if this feature gets merged with another
99116
99117                 results.push(Object.assign({}, feature)); // shallow copy
99118               }
99119             }
99120
99121             return results;
99122           },
99123           loadTiles: function loadTiles(sourceID, template, projection) {
99124             var source = _vtCache[sourceID];
99125
99126             if (!source) {
99127               source = this.addSource(sourceID, template);
99128             }
99129
99130             var tiles = tiler.getTiles(projection); // abort inflight requests that are no longer needed
99131
99132             Object.keys(source.inflight).forEach(function (k) {
99133               var wanted = tiles.find(function (tile) {
99134                 return k === tile.id;
99135               });
99136
99137               if (!wanted) {
99138                 abortRequest(source.inflight[k]);
99139                 delete source.inflight[k];
99140               }
99141             });
99142             tiles.forEach(function (tile) {
99143               loadTile(source, tile);
99144             });
99145           },
99146           cache: function cache() {
99147             return _vtCache;
99148           }
99149         };
99150
99151         var apibase = 'https://www.wikidata.org/w/api.php?';
99152         var _wikidataCache = {};
99153         var serviceWikidata = {
99154           init: function init() {},
99155           reset: function reset() {
99156             _wikidataCache = {};
99157           },
99158           // Search for Wikidata items matching the query
99159           itemsForSearchQuery: function itemsForSearchQuery(query, callback) {
99160             if (!query) {
99161               if (callback) callback('No query', {});
99162               return;
99163             }
99164
99165             var lang = this.languagesToQuery()[0];
99166             var url = apibase + utilQsString({
99167               action: 'wbsearchentities',
99168               format: 'json',
99169               formatversion: 2,
99170               search: query,
99171               type: 'item',
99172               // the language to search
99173               language: lang,
99174               // the language for the label and description in the result
99175               uselang: lang,
99176               limit: 10,
99177               origin: '*'
99178             });
99179             d3_json(url).then(function (result) {
99180               if (result && result.error) {
99181                 throw new Error(result.error);
99182               }
99183
99184               if (callback) callback(null, result.search || {});
99185             })["catch"](function (err) {
99186               if (callback) callback(err.message, {});
99187             });
99188           },
99189           // Given a Wikipedia language and article title,
99190           // return an array of corresponding Wikidata entities.
99191           itemsByTitle: function itemsByTitle(lang, title, callback) {
99192             if (!title) {
99193               if (callback) callback('No title', {});
99194               return;
99195             }
99196
99197             lang = lang || 'en';
99198             var url = apibase + utilQsString({
99199               action: 'wbgetentities',
99200               format: 'json',
99201               formatversion: 2,
99202               sites: lang.replace(/-/g, '_') + 'wiki',
99203               titles: title,
99204               languages: 'en',
99205               // shrink response by filtering to one language
99206               origin: '*'
99207             });
99208             d3_json(url).then(function (result) {
99209               if (result && result.error) {
99210                 throw new Error(result.error);
99211               }
99212
99213               if (callback) callback(null, result.entities || {});
99214             })["catch"](function (err) {
99215               if (callback) callback(err.message, {});
99216             });
99217           },
99218           languagesToQuery: function languagesToQuery() {
99219             return _mainLocalizer.localeCodes().map(function (code) {
99220               return code.toLowerCase();
99221             }).filter(function (code) {
99222               // HACK: en-us isn't a wikidata language. We should really be filtering by
99223               // the languages known to be supported by wikidata.
99224               return code !== 'en-us';
99225             });
99226           },
99227           entityByQID: function entityByQID(qid, callback) {
99228             if (!qid) {
99229               callback('No qid', {});
99230               return;
99231             }
99232
99233             if (_wikidataCache[qid]) {
99234               if (callback) callback(null, _wikidataCache[qid]);
99235               return;
99236             }
99237
99238             var langs = this.languagesToQuery();
99239             var url = apibase + utilQsString({
99240               action: 'wbgetentities',
99241               format: 'json',
99242               formatversion: 2,
99243               ids: qid,
99244               props: 'labels|descriptions|claims|sitelinks',
99245               sitefilter: langs.map(function (d) {
99246                 return d + 'wiki';
99247               }).join('|'),
99248               languages: langs.join('|'),
99249               languagefallback: 1,
99250               origin: '*'
99251             });
99252             d3_json(url).then(function (result) {
99253               if (result && result.error) {
99254                 throw new Error(result.error);
99255               }
99256
99257               if (callback) callback(null, result.entities[qid] || {});
99258             })["catch"](function (err) {
99259               if (callback) callback(err.message, {});
99260             });
99261           },
99262           // Pass `params` object of the form:
99263           // {
99264           //   qid: 'string'      // brand wikidata  (e.g. 'Q37158')
99265           // }
99266           //
99267           // Get an result object used to display tag documentation
99268           // {
99269           //   title:        'string',
99270           //   description:  'string',
99271           //   editURL:      'string',
99272           //   imageURL:     'string',
99273           //   wiki:         { title: 'string', text: 'string', url: 'string' }
99274           // }
99275           //
99276           getDocs: function getDocs(params, callback) {
99277             var langs = this.languagesToQuery();
99278             this.entityByQID(params.qid, function (err, entity) {
99279               if (err || !entity) {
99280                 callback(err || 'No entity');
99281                 return;
99282               }
99283
99284               var i;
99285               var description;
99286
99287               for (i in langs) {
99288                 var code = langs[i];
99289
99290                 if (entity.descriptions[code] && entity.descriptions[code].language === code) {
99291                   description = entity.descriptions[code];
99292                   break;
99293                 }
99294               }
99295
99296               if (!description && Object.values(entity.descriptions).length) description = Object.values(entity.descriptions)[0]; // prepare result
99297
99298               var result = {
99299                 title: entity.id,
99300                 description: description ? description.value : '',
99301                 descriptionLocaleCode: description ? description.language : '',
99302                 editURL: 'https://www.wikidata.org/wiki/' + entity.id
99303               }; // add image
99304
99305               if (entity.claims) {
99306                 var imageroot = 'https://commons.wikimedia.org/w/index.php';
99307                 var props = ['P154', 'P18']; // logo image, image
99308
99309                 var prop, image;
99310
99311                 for (i = 0; i < props.length; i++) {
99312                   prop = entity.claims[props[i]];
99313
99314                   if (prop && Object.keys(prop).length > 0) {
99315                     image = prop[Object.keys(prop)[0]].mainsnak.datavalue.value;
99316
99317                     if (image) {
99318                       result.imageURL = imageroot + '?' + utilQsString({
99319                         title: 'Special:Redirect/file/' + image,
99320                         width: 400
99321                       });
99322                       break;
99323                     }
99324                   }
99325                 }
99326               }
99327
99328               if (entity.sitelinks) {
99329                 var englishLocale = _mainLocalizer.languageCode().toLowerCase() === 'en'; // must be one of these that we requested..
99330
99331                 for (i = 0; i < langs.length; i++) {
99332                   // check each, in order of preference
99333                   var w = langs[i] + 'wiki';
99334
99335                   if (entity.sitelinks[w]) {
99336                     var title = entity.sitelinks[w].title;
99337                     var tKey = 'inspector.wiki_reference';
99338
99339                     if (!englishLocale && langs[i] === 'en') {
99340                       // user's locale isn't English but
99341                       tKey = 'inspector.wiki_en_reference'; // we are sending them to enwiki anyway..
99342                     }
99343
99344                     result.wiki = {
99345                       title: title,
99346                       text: tKey,
99347                       url: 'https://' + langs[i] + '.wikipedia.org/wiki/' + title.replace(/ /g, '_')
99348                     };
99349                     break;
99350                   }
99351                 }
99352               }
99353
99354               callback(null, result);
99355             });
99356           }
99357         };
99358
99359         var endpoint = 'https://en.wikipedia.org/w/api.php?';
99360         var serviceWikipedia = {
99361           init: function init() {},
99362           reset: function reset() {},
99363           search: function search(lang, query, callback) {
99364             if (!query) {
99365               if (callback) callback('No Query', []);
99366               return;
99367             }
99368
99369             lang = lang || 'en';
99370             var url = endpoint.replace('en', lang) + utilQsString({
99371               action: 'query',
99372               list: 'search',
99373               srlimit: '10',
99374               srinfo: 'suggestion',
99375               format: 'json',
99376               origin: '*',
99377               srsearch: query
99378             });
99379             d3_json(url).then(function (result) {
99380               if (result && result.error) {
99381                 throw new Error(result.error);
99382               } else if (!result || !result.query || !result.query.search) {
99383                 throw new Error('No Results');
99384               }
99385
99386               if (callback) {
99387                 var titles = result.query.search.map(function (d) {
99388                   return d.title;
99389                 });
99390                 callback(null, titles);
99391               }
99392             })["catch"](function (err) {
99393               if (callback) callback(err, []);
99394             });
99395           },
99396           suggestions: function suggestions(lang, query, callback) {
99397             if (!query) {
99398               if (callback) callback('', []);
99399               return;
99400             }
99401
99402             lang = lang || 'en';
99403             var url = endpoint.replace('en', lang) + utilQsString({
99404               action: 'opensearch',
99405               namespace: 0,
99406               suggest: '',
99407               format: 'json',
99408               origin: '*',
99409               search: query
99410             });
99411             d3_json(url).then(function (result) {
99412               if (result && result.error) {
99413                 throw new Error(result.error);
99414               } else if (!result || result.length < 2) {
99415                 throw new Error('No Results');
99416               }
99417
99418               if (callback) callback(null, result[1] || []);
99419             })["catch"](function (err) {
99420               if (callback) callback(err.message, []);
99421             });
99422           },
99423           translations: function translations(lang, title, callback) {
99424             if (!title) {
99425               if (callback) callback('No Title');
99426               return;
99427             }
99428
99429             var url = endpoint.replace('en', lang) + utilQsString({
99430               action: 'query',
99431               prop: 'langlinks',
99432               format: 'json',
99433               origin: '*',
99434               lllimit: 500,
99435               titles: title
99436             });
99437             d3_json(url).then(function (result) {
99438               if (result && result.error) {
99439                 throw new Error(result.error);
99440               } else if (!result || !result.query || !result.query.pages) {
99441                 throw new Error('No Results');
99442               }
99443
99444               if (callback) {
99445                 var list = result.query.pages[Object.keys(result.query.pages)[0]];
99446                 var translations = {};
99447
99448                 if (list && list.langlinks) {
99449                   list.langlinks.forEach(function (d) {
99450                     translations[d.lang] = d['*'];
99451                   });
99452                 }
99453
99454                 callback(null, translations);
99455               }
99456             })["catch"](function (err) {
99457               if (callback) callback(err.message);
99458             });
99459           }
99460         };
99461
99462         var services = {
99463           geocoder: serviceNominatim,
99464           keepRight: serviceKeepRight,
99465           improveOSM: serviceImproveOSM,
99466           osmose: serviceOsmose,
99467           mapillary: serviceMapillary,
99468           nsi: serviceNsi,
99469           openstreetcam: serviceOpenstreetcam,
99470           osm: serviceOsm,
99471           osmWikibase: serviceOsmWikibase,
99472           maprules: serviceMapRules,
99473           streetside: serviceStreetside,
99474           taginfo: serviceTaginfo,
99475           vectorTile: serviceVectorTile,
99476           wikidata: serviceWikidata,
99477           wikipedia: serviceWikipedia
99478         };
99479
99480         function modeDragNote(context) {
99481           var mode = {
99482             id: 'drag-note',
99483             button: 'browse'
99484           };
99485           var edit = behaviorEdit(context);
99486
99487           var _nudgeInterval;
99488
99489           var _lastLoc;
99490
99491           var _note; // most current note.. dragged note may have stale datum.
99492
99493
99494           function startNudge(d3_event, nudge) {
99495             if (_nudgeInterval) window.clearInterval(_nudgeInterval);
99496             _nudgeInterval = window.setInterval(function () {
99497               context.map().pan(nudge);
99498               doMove(d3_event, nudge);
99499             }, 50);
99500           }
99501
99502           function stopNudge() {
99503             if (_nudgeInterval) {
99504               window.clearInterval(_nudgeInterval);
99505               _nudgeInterval = null;
99506             }
99507           }
99508
99509           function origin(note) {
99510             return context.projection(note.loc);
99511           }
99512
99513           function start(d3_event, note) {
99514             _note = note;
99515             var osm = services.osm;
99516
99517             if (osm) {
99518               // Get latest note from cache.. The marker may have a stale datum bound to it
99519               // and dragging it around can sometimes delete the users note comment.
99520               _note = osm.getNote(_note.id);
99521             }
99522
99523             context.surface().selectAll('.note-' + _note.id).classed('active', true);
99524             context.perform(actionNoop());
99525             context.enter(mode);
99526             context.selectedNoteID(_note.id);
99527           }
99528
99529           function move(d3_event, entity, point) {
99530             d3_event.stopPropagation();
99531             _lastLoc = context.projection.invert(point);
99532             doMove(d3_event);
99533             var nudge = geoViewportEdge(point, context.map().dimensions());
99534
99535             if (nudge) {
99536               startNudge(d3_event, nudge);
99537             } else {
99538               stopNudge();
99539             }
99540           }
99541
99542           function doMove(d3_event, nudge) {
99543             nudge = nudge || [0, 0];
99544             var currPoint = d3_event && d3_event.point || context.projection(_lastLoc);
99545             var currMouse = geoVecSubtract(currPoint, nudge);
99546             var loc = context.projection.invert(currMouse);
99547             _note = _note.move(loc);
99548             var osm = services.osm;
99549
99550             if (osm) {
99551               osm.replaceNote(_note); // update note cache
99552             }
99553
99554             context.replace(actionNoop()); // trigger redraw
99555           }
99556
99557           function end() {
99558             context.replace(actionNoop()); // trigger redraw
99559
99560             context.selectedNoteID(_note.id).enter(modeSelectNote(context, _note.id));
99561           }
99562
99563           var drag = behaviorDrag().selector('.layer-touch.markers .target.note.new').surface(context.container().select('.main-map').node()).origin(origin).on('start', start).on('move', move).on('end', end);
99564
99565           mode.enter = function () {
99566             context.install(edit);
99567           };
99568
99569           mode.exit = function () {
99570             context.ui().sidebar.hover.cancel();
99571             context.uninstall(edit);
99572             context.surface().selectAll('.active').classed('active', false);
99573             stopNudge();
99574           };
99575
99576           mode.behavior = drag;
99577           return mode;
99578         }
99579
99580         function modeSelectData(context, selectedDatum) {
99581           var mode = {
99582             id: 'select-data',
99583             button: 'browse'
99584           };
99585           var keybinding = utilKeybinding('select-data');
99586           var dataEditor = uiDataEditor(context);
99587           var behaviors = [behaviorBreathe(), behaviorHover(context), behaviorSelect(context), behaviorLasso(context), modeDragNode(context).behavior, modeDragNote(context).behavior]; // class the data as selected, or return to browse mode if the data is gone
99588
99589           function selectData(d3_event, drawn) {
99590             var selection = context.surface().selectAll('.layer-mapdata .data' + selectedDatum.__featurehash__);
99591
99592             if (selection.empty()) {
99593               // Return to browse mode if selected DOM elements have
99594               // disappeared because the user moved them out of view..
99595               var source = d3_event && d3_event.type === 'zoom' && d3_event.sourceEvent;
99596
99597               if (drawn && source && (source.type === 'pointermove' || source.type === 'mousemove' || source.type === 'touchmove')) {
99598                 context.enter(modeBrowse(context));
99599               }
99600             } else {
99601               selection.classed('selected', true);
99602             }
99603           }
99604
99605           function esc() {
99606             if (context.container().select('.combobox').size()) return;
99607             context.enter(modeBrowse(context));
99608           }
99609
99610           mode.zoomToSelected = function () {
99611             var extent = geoExtent(d3_geoBounds(selectedDatum));
99612             context.map().centerZoomEase(extent.center(), context.map().trimmedExtentZoom(extent));
99613           };
99614
99615           mode.enter = function () {
99616             behaviors.forEach(context.install);
99617             keybinding.on(_t('inspector.zoom_to.key'), mode.zoomToSelected).on('⎋', esc, true);
99618             select(document).call(keybinding);
99619             selectData();
99620             var sidebar = context.ui().sidebar;
99621             sidebar.show(dataEditor.datum(selectedDatum)); // expand the sidebar, avoid obscuring the data if needed
99622
99623             var extent = geoExtent(d3_geoBounds(selectedDatum));
99624             sidebar.expand(sidebar.intersects(extent));
99625             context.map().on('drawn.select-data', selectData);
99626           };
99627
99628           mode.exit = function () {
99629             behaviors.forEach(context.uninstall);
99630             select(document).call(keybinding.unbind);
99631             context.surface().selectAll('.layer-mapdata .selected').classed('selected hover', false);
99632             context.map().on('drawn.select-data', null);
99633             context.ui().sidebar.hide();
99634           };
99635
99636           return mode;
99637         }
99638
99639         function behaviorSelect(context) {
99640           var _tolerancePx = 4; // see also behaviorDrag
99641
99642           var _lastMouseEvent = null;
99643           var _showMenu = false;
99644           var _downPointers = {};
99645           var _longPressTimeout = null;
99646           var _lastInteractionType = null; // the id of the down pointer that's enabling multiselection while down
99647
99648           var _multiselectionPointerId = null; // use pointer events on supported platforms; fallback to mouse events
99649
99650           var _pointerPrefix = 'PointerEvent' in window ? 'pointer' : 'mouse';
99651
99652           function keydown(d3_event) {
99653             if (d3_event.keyCode === 32) {
99654               // don't react to spacebar events during text input
99655               var activeNode = document.activeElement;
99656               if (activeNode && new Set(['INPUT', 'TEXTAREA']).has(activeNode.nodeName)) return;
99657             }
99658
99659             if (d3_event.keyCode === 93 || // context menu key
99660             d3_event.keyCode === 32) {
99661               // spacebar
99662               d3_event.preventDefault();
99663             }
99664
99665             if (d3_event.repeat) return; // ignore repeated events for held keys
99666             // if any key is pressed the user is probably doing something other than long-pressing
99667
99668             cancelLongPress();
99669
99670             if (d3_event.shiftKey) {
99671               context.surface().classed('behavior-multiselect', true);
99672             }
99673
99674             if (d3_event.keyCode === 32) {
99675               // spacebar
99676               if (!_downPointers.spacebar && _lastMouseEvent) {
99677                 cancelLongPress();
99678                 _longPressTimeout = window.setTimeout(didLongPress, 500, 'spacebar', 'spacebar');
99679                 _downPointers.spacebar = {
99680                   firstEvent: _lastMouseEvent,
99681                   lastEvent: _lastMouseEvent
99682                 };
99683               }
99684             }
99685           }
99686
99687           function keyup(d3_event) {
99688             cancelLongPress();
99689
99690             if (!d3_event.shiftKey) {
99691               context.surface().classed('behavior-multiselect', false);
99692             }
99693
99694             if (d3_event.keyCode === 93) {
99695               // context menu key
99696               d3_event.preventDefault();
99697               _lastInteractionType = 'menukey';
99698               contextmenu(d3_event);
99699             } else if (d3_event.keyCode === 32) {
99700               // spacebar
99701               var pointer = _downPointers.spacebar;
99702
99703               if (pointer) {
99704                 delete _downPointers.spacebar;
99705                 if (pointer.done) return;
99706                 d3_event.preventDefault();
99707                 _lastInteractionType = 'spacebar';
99708                 click(pointer.firstEvent, pointer.lastEvent, 'spacebar');
99709               }
99710             }
99711           }
99712
99713           function pointerdown(d3_event) {
99714             var id = (d3_event.pointerId || 'mouse').toString();
99715             cancelLongPress();
99716             if (d3_event.buttons && d3_event.buttons !== 1) return;
99717             context.ui().closeEditMenu();
99718             _longPressTimeout = window.setTimeout(didLongPress, 500, id, 'longdown-' + (d3_event.pointerType || 'mouse'));
99719             _downPointers[id] = {
99720               firstEvent: d3_event,
99721               lastEvent: d3_event
99722             };
99723           }
99724
99725           function didLongPress(id, interactionType) {
99726             var pointer = _downPointers[id];
99727             if (!pointer) return;
99728
99729             for (var i in _downPointers) {
99730               // don't allow this or any currently down pointer to trigger another click
99731               _downPointers[i].done = true;
99732             } // treat long presses like right-clicks
99733
99734
99735             _longPressTimeout = null;
99736             _lastInteractionType = interactionType;
99737             _showMenu = true;
99738             click(pointer.firstEvent, pointer.lastEvent, id);
99739           }
99740
99741           function pointermove(d3_event) {
99742             var id = (d3_event.pointerId || 'mouse').toString();
99743
99744             if (_downPointers[id]) {
99745               _downPointers[id].lastEvent = d3_event;
99746             }
99747
99748             if (!d3_event.pointerType || d3_event.pointerType === 'mouse') {
99749               _lastMouseEvent = d3_event;
99750
99751               if (_downPointers.spacebar) {
99752                 _downPointers.spacebar.lastEvent = d3_event;
99753               }
99754             }
99755           }
99756
99757           function pointerup(d3_event) {
99758             var id = (d3_event.pointerId || 'mouse').toString();
99759             var pointer = _downPointers[id];
99760             if (!pointer) return;
99761             delete _downPointers[id];
99762
99763             if (_multiselectionPointerId === id) {
99764               _multiselectionPointerId = null;
99765             }
99766
99767             if (pointer.done) return;
99768             click(pointer.firstEvent, d3_event, id);
99769           }
99770
99771           function pointercancel(d3_event) {
99772             var id = (d3_event.pointerId || 'mouse').toString();
99773             if (!_downPointers[id]) return;
99774             delete _downPointers[id];
99775
99776             if (_multiselectionPointerId === id) {
99777               _multiselectionPointerId = null;
99778             }
99779           }
99780
99781           function contextmenu(d3_event) {
99782             d3_event.preventDefault();
99783
99784             if (!+d3_event.clientX && !+d3_event.clientY) {
99785               if (_lastMouseEvent) {
99786                 d3_event.sourceEvent = _lastMouseEvent;
99787               } else {
99788                 return;
99789               }
99790             } else {
99791               _lastMouseEvent = d3_event;
99792               _lastInteractionType = 'rightclick';
99793             }
99794
99795             _showMenu = true;
99796             click(d3_event, d3_event);
99797           }
99798
99799           function click(firstEvent, lastEvent, pointerId) {
99800             cancelLongPress();
99801             var mapNode = context.container().select('.main-map').node(); // Use the `main-map` coordinate system since the surface and supersurface
99802             // are transformed when drag-panning.
99803
99804             var pointGetter = utilFastMouse(mapNode);
99805             var p1 = pointGetter(firstEvent);
99806             var p2 = pointGetter(lastEvent);
99807             var dist = geoVecLength(p1, p2);
99808
99809             if (dist > _tolerancePx || !mapContains(lastEvent)) {
99810               resetProperties();
99811               return;
99812             }
99813
99814             var targetDatum = lastEvent.target.__data__;
99815             var multiselectEntityId;
99816
99817             if (!_multiselectionPointerId) {
99818               // If a different pointer than the one triggering this click is down on a
99819               // feature, treat this and all future clicks as multiselection until that
99820               // pointer is raised.
99821               var selectPointerInfo = pointerDownOnSelection(pointerId);
99822
99823               if (selectPointerInfo) {
99824                 _multiselectionPointerId = selectPointerInfo.pointerId; // if the other feature isn't selected yet, make sure we select it
99825
99826                 multiselectEntityId = !selectPointerInfo.selected && selectPointerInfo.entityId;
99827                 _downPointers[selectPointerInfo.pointerId].done = true;
99828               }
99829             } // support multiselect if data is already selected
99830
99831
99832             var isMultiselect = context.mode().id === 'select' && ( // and shift key is down
99833             lastEvent && lastEvent.shiftKey || // or we're lasso-selecting
99834             context.surface().select('.lasso').node() || // or a pointer is down over a selected feature
99835             _multiselectionPointerId && !multiselectEntityId);
99836
99837             processClick(targetDatum, isMultiselect, p2, multiselectEntityId);
99838
99839             function mapContains(event) {
99840               var rect = mapNode.getBoundingClientRect();
99841               return event.clientX >= rect.left && event.clientX <= rect.right && event.clientY >= rect.top && event.clientY <= rect.bottom;
99842             }
99843
99844             function pointerDownOnSelection(skipPointerId) {
99845               var mode = context.mode();
99846               var selectedIDs = mode.id === 'select' ? mode.selectedIDs() : [];
99847
99848               for (var pointerId in _downPointers) {
99849                 if (pointerId === 'spacebar' || pointerId === skipPointerId) continue;
99850                 var pointerInfo = _downPointers[pointerId];
99851                 var p1 = pointGetter(pointerInfo.firstEvent);
99852                 var p2 = pointGetter(pointerInfo.lastEvent);
99853                 if (geoVecLength(p1, p2) > _tolerancePx) continue;
99854                 var datum = pointerInfo.firstEvent.target.__data__;
99855                 var entity = datum && datum.properties && datum.properties.entity || datum;
99856
99857                 if (context.graph().hasEntity(entity.id)) {
99858                   return {
99859                     pointerId: pointerId,
99860                     entityId: entity.id,
99861                     selected: selectedIDs.indexOf(entity.id) !== -1
99862                   };
99863                 }
99864               }
99865
99866               return null;
99867             }
99868           }
99869
99870           function processClick(datum, isMultiselect, point, alsoSelectId) {
99871             var mode = context.mode();
99872             var showMenu = _showMenu;
99873             var interactionType = _lastInteractionType;
99874             var entity = datum && datum.properties && datum.properties.entity;
99875             if (entity) datum = entity;
99876
99877             if (datum && datum.type === 'midpoint') {
99878               // treat targeting midpoints as if targeting the parent way
99879               datum = datum.parents[0];
99880             }
99881
99882             var newMode;
99883
99884             if (datum instanceof osmEntity) {
99885               // targeting an entity
99886               var selectedIDs = context.selectedIDs();
99887               context.selectedNoteID(null);
99888               context.selectedErrorID(null);
99889
99890               if (!isMultiselect) {
99891                 // don't change the selection if we're toggling the menu atop a multiselection
99892                 if (!showMenu || selectedIDs.length <= 1 || selectedIDs.indexOf(datum.id) === -1) {
99893                   if (alsoSelectId === datum.id) alsoSelectId = null;
99894                   selectedIDs = (alsoSelectId ? [alsoSelectId] : []).concat([datum.id]); // always enter modeSelect even if the entity is already
99895                   // selected since listeners may expect `context.enter` events,
99896                   // e.g. in the walkthrough
99897
99898                   newMode = mode.id === 'select' ? mode.selectedIDs(selectedIDs) : modeSelect(context, selectedIDs).selectBehavior(behavior);
99899                   context.enter(newMode);
99900                 }
99901               } else {
99902                 if (selectedIDs.indexOf(datum.id) !== -1) {
99903                   // clicked entity is already in the selectedIDs list..
99904                   if (!showMenu) {
99905                     // deselect clicked entity, then reenter select mode or return to browse mode..
99906                     selectedIDs = selectedIDs.filter(function (id) {
99907                       return id !== datum.id;
99908                     });
99909                     newMode = selectedIDs.length ? mode.selectedIDs(selectedIDs) : modeBrowse(context).selectBehavior(behavior);
99910                     context.enter(newMode);
99911                   }
99912                 } else {
99913                   // clicked entity is not in the selected list, add it..
99914                   selectedIDs = selectedIDs.concat([datum.id]);
99915                   newMode = mode.selectedIDs(selectedIDs);
99916                   context.enter(newMode);
99917                 }
99918               }
99919             } else if (datum && datum.__featurehash__ && !isMultiselect) {
99920               // targeting custom data
99921               context.selectedNoteID(null).enter(modeSelectData(context, datum));
99922             } else if (datum instanceof osmNote && !isMultiselect) {
99923               // targeting a note
99924               context.selectedNoteID(datum.id).enter(modeSelectNote(context, datum.id));
99925             } else if (datum instanceof QAItem & !isMultiselect) {
99926               // targeting an external QA issue
99927               context.selectedErrorID(datum.id).enter(modeSelectError(context, datum.id, datum.service));
99928             } else {
99929               // targeting nothing
99930               context.selectedNoteID(null);
99931               context.selectedErrorID(null);
99932
99933               if (!isMultiselect && mode.id !== 'browse') {
99934                 context.enter(modeBrowse(context));
99935               }
99936             }
99937
99938             context.ui().closeEditMenu(); // always request to show the edit menu in case the mode needs it
99939
99940             if (showMenu) context.ui().showEditMenu(point, interactionType);
99941             resetProperties();
99942           }
99943
99944           function cancelLongPress() {
99945             if (_longPressTimeout) window.clearTimeout(_longPressTimeout);
99946             _longPressTimeout = null;
99947           }
99948
99949           function resetProperties() {
99950             cancelLongPress();
99951             _showMenu = false;
99952             _lastInteractionType = null; // don't reset _lastMouseEvent since it might still be useful
99953           }
99954
99955           function behavior(selection) {
99956             resetProperties();
99957             _lastMouseEvent = context.map().lastPointerEvent();
99958             select(window).on('keydown.select', keydown).on('keyup.select', keyup).on(_pointerPrefix + 'move.select', pointermove, true).on(_pointerPrefix + 'up.select', pointerup, true).on('pointercancel.select', pointercancel, true).on('contextmenu.select-window', function (d3_event) {
99959               // Edge and IE really like to show the contextmenu on the
99960               // menubar when user presses a keyboard menu button
99961               // even after we've already preventdefaulted the key event.
99962               var e = d3_event;
99963
99964               if (+e.clientX === 0 && +e.clientY === 0) {
99965                 d3_event.preventDefault();
99966               }
99967             });
99968             selection.on(_pointerPrefix + 'down.select', pointerdown).on('contextmenu.select', contextmenu);
99969             /*if (d3_event && d3_event.shiftKey) {
99970                 context.surface()
99971                     .classed('behavior-multiselect', true);
99972             }*/
99973           }
99974
99975           behavior.off = function (selection) {
99976             cancelLongPress();
99977             select(window).on('keydown.select', null).on('keyup.select', null).on('contextmenu.select-window', null).on(_pointerPrefix + 'move.select', null, true).on(_pointerPrefix + 'up.select', null, true).on('pointercancel.select', null, true);
99978             selection.on(_pointerPrefix + 'down.select', null).on('contextmenu.select', null);
99979             context.surface().classed('behavior-multiselect', false);
99980           };
99981
99982           return behavior;
99983         }
99984
99985         function operationContinue(context, selectedIDs) {
99986           var _entities = selectedIDs.map(function (id) {
99987             return context.graph().entity(id);
99988           });
99989
99990           var _geometries = Object.assign({
99991             line: [],
99992             vertex: []
99993           }, utilArrayGroupBy(_entities, function (entity) {
99994             return entity.geometry(context.graph());
99995           }));
99996
99997           var _vertex = _geometries.vertex.length && _geometries.vertex[0];
99998
99999           function candidateWays() {
100000             return _vertex ? context.graph().parentWays(_vertex).filter(function (parent) {
100001               return parent.geometry(context.graph()) === 'line' && !parent.isClosed() && parent.affix(_vertex.id) && (_geometries.line.length === 0 || _geometries.line[0] === parent);
100002             }) : [];
100003           }
100004
100005           var _candidates = candidateWays();
100006
100007           var operation = function operation() {
100008             var candidate = _candidates[0];
100009             context.enter(modeDrawLine(context, candidate.id, context.graph(), 'line', candidate.affix(_vertex.id), true));
100010           };
100011
100012           operation.relatedEntityIds = function () {
100013             return _candidates.length ? [_candidates[0].id] : [];
100014           };
100015
100016           operation.available = function () {
100017             return _geometries.vertex.length === 1 && _geometries.line.length <= 1 && !context.features().hasHiddenConnections(_vertex, context.graph());
100018           };
100019
100020           operation.disabled = function () {
100021             if (_candidates.length === 0) {
100022               return 'not_eligible';
100023             } else if (_candidates.length > 1) {
100024               return 'multiple';
100025             }
100026
100027             return false;
100028           };
100029
100030           operation.tooltip = function () {
100031             var disable = operation.disabled();
100032             return disable ? _t('operations.continue.' + disable) : _t('operations.continue.description');
100033           };
100034
100035           operation.annotation = function () {
100036             return _t('operations.continue.annotation.line');
100037           };
100038
100039           operation.id = 'continue';
100040           operation.keys = [_t('operations.continue.key')];
100041           operation.title = _t('operations.continue.title');
100042           operation.behavior = behaviorOperation(context).which(operation);
100043           return operation;
100044         }
100045
100046         function operationCopy(context, selectedIDs) {
100047           function getFilteredIdsToCopy() {
100048             return selectedIDs.filter(function (selectedID) {
100049               var entity = context.graph().hasEntity(selectedID); // don't copy untagged vertices separately from ways
100050
100051               return entity.hasInterestingTags() || entity.geometry(context.graph()) !== 'vertex';
100052             });
100053           }
100054
100055           var operation = function operation() {
100056             var graph = context.graph();
100057             var selected = groupEntities(getFilteredIdsToCopy(), graph);
100058             var canCopy = [];
100059             var skip = {};
100060             var entity;
100061             var i;
100062
100063             for (i = 0; i < selected.relation.length; i++) {
100064               entity = selected.relation[i];
100065
100066               if (!skip[entity.id] && entity.isComplete(graph)) {
100067                 canCopy.push(entity.id);
100068                 skip = getDescendants(entity.id, graph, skip);
100069               }
100070             }
100071
100072             for (i = 0; i < selected.way.length; i++) {
100073               entity = selected.way[i];
100074
100075               if (!skip[entity.id]) {
100076                 canCopy.push(entity.id);
100077                 skip = getDescendants(entity.id, graph, skip);
100078               }
100079             }
100080
100081             for (i = 0; i < selected.node.length; i++) {
100082               entity = selected.node[i];
100083
100084               if (!skip[entity.id]) {
100085                 canCopy.push(entity.id);
100086               }
100087             }
100088
100089             context.copyIDs(canCopy);
100090
100091             if (_point && (canCopy.length !== 1 || graph.entity(canCopy[0]).type !== 'node')) {
100092               // store the anchor coordinates if copying more than a single node
100093               context.copyLonLat(context.projection.invert(_point));
100094             } else {
100095               context.copyLonLat(null);
100096             }
100097           };
100098
100099           function groupEntities(ids, graph) {
100100             var entities = ids.map(function (id) {
100101               return graph.entity(id);
100102             });
100103             return Object.assign({
100104               relation: [],
100105               way: [],
100106               node: []
100107             }, utilArrayGroupBy(entities, 'type'));
100108           }
100109
100110           function getDescendants(id, graph, descendants) {
100111             var entity = graph.entity(id);
100112             var children;
100113             descendants = descendants || {};
100114
100115             if (entity.type === 'relation') {
100116               children = entity.members.map(function (m) {
100117                 return m.id;
100118               });
100119             } else if (entity.type === 'way') {
100120               children = entity.nodes;
100121             } else {
100122               children = [];
100123             }
100124
100125             for (var i = 0; i < children.length; i++) {
100126               if (!descendants[children[i]]) {
100127                 descendants[children[i]] = true;
100128                 descendants = getDescendants(children[i], graph, descendants);
100129               }
100130             }
100131
100132             return descendants;
100133           }
100134
100135           operation.available = function () {
100136             return getFilteredIdsToCopy().length > 0;
100137           };
100138
100139           operation.disabled = function () {
100140             var extent = utilTotalExtent(getFilteredIdsToCopy(), context.graph());
100141
100142             if (extent.percentContainedIn(context.map().extent()) < 0.8) {
100143               return 'too_large';
100144             }
100145
100146             return false;
100147           };
100148
100149           operation.availableForKeypress = function () {
100150             var selection = window.getSelection && window.getSelection(); // if the user has text selected then let them copy that, not the selected feature
100151
100152             return !selection || !selection.toString();
100153           };
100154
100155           operation.tooltip = function () {
100156             var disable = operation.disabled();
100157             return disable ? _t('operations.copy.' + disable, {
100158               n: selectedIDs.length
100159             }) : _t('operations.copy.description', {
100160               n: selectedIDs.length
100161             });
100162           };
100163
100164           operation.annotation = function () {
100165             return _t('operations.copy.annotation', {
100166               n: selectedIDs.length
100167             });
100168           };
100169
100170           var _point;
100171
100172           operation.point = function (val) {
100173             _point = val;
100174             return operation;
100175           };
100176
100177           operation.id = 'copy';
100178           operation.keys = [uiCmd('⌘C')];
100179           operation.title = _t('operations.copy.title');
100180           operation.behavior = behaviorOperation(context).which(operation);
100181           return operation;
100182         }
100183
100184         function operationDisconnect(context, selectedIDs) {
100185           var _vertexIDs = [];
100186           var _wayIDs = [];
100187           var _otherIDs = [];
100188           var _actions = [];
100189           selectedIDs.forEach(function (id) {
100190             var entity = context.entity(id);
100191
100192             if (entity.type === 'way') {
100193               _wayIDs.push(id);
100194             } else if (entity.geometry(context.graph()) === 'vertex') {
100195               _vertexIDs.push(id);
100196             } else {
100197               _otherIDs.push(id);
100198             }
100199           });
100200
100201           var _coords,
100202               _descriptionID = '',
100203               _annotationID = 'features';
100204
100205           var _disconnectingVertexIds = [];
100206           var _disconnectingWayIds = [];
100207
100208           if (_vertexIDs.length > 0) {
100209             // At the selected vertices, disconnect the selected ways, if any, else
100210             // disconnect all connected ways
100211             _disconnectingVertexIds = _vertexIDs;
100212
100213             _vertexIDs.forEach(function (vertexID) {
100214               var action = actionDisconnect(vertexID);
100215
100216               if (_wayIDs.length > 0) {
100217                 var waysIDsForVertex = _wayIDs.filter(function (wayID) {
100218                   var way = context.entity(wayID);
100219                   return way.nodes.indexOf(vertexID) !== -1;
100220                 });
100221
100222                 action.limitWays(waysIDsForVertex);
100223               }
100224
100225               _actions.push(action);
100226
100227               _disconnectingWayIds = _disconnectingWayIds.concat(context.graph().parentWays(context.graph().entity(vertexID)).map(function (d) {
100228                 return d.id;
100229               }));
100230             });
100231
100232             _disconnectingWayIds = utilArrayUniq(_disconnectingWayIds).filter(function (id) {
100233               return _wayIDs.indexOf(id) === -1;
100234             });
100235             _descriptionID += _actions.length === 1 ? 'single_point.' : 'multiple_points.';
100236
100237             if (_wayIDs.length === 1) {
100238               _descriptionID += 'single_way.' + context.graph().geometry(_wayIDs[0]);
100239             } else {
100240               _descriptionID += _wayIDs.length === 0 ? 'no_ways' : 'multiple_ways';
100241             }
100242           } else if (_wayIDs.length > 0) {
100243             // Disconnect the selected ways from each other, if they're connected,
100244             // else disconnect them from all connected ways
100245             var ways = _wayIDs.map(function (id) {
100246               return context.entity(id);
100247             });
100248
100249             var nodes = utilGetAllNodes(_wayIDs, context.graph());
100250             _coords = nodes.map(function (n) {
100251               return n.loc;
100252             }); // actions for connected nodes shared by at least two selected ways
100253
100254             var sharedActions = [];
100255             var sharedNodes = []; // actions for connected nodes
100256
100257             var unsharedActions = [];
100258             var unsharedNodes = [];
100259             nodes.forEach(function (node) {
100260               var action = actionDisconnect(node.id).limitWays(_wayIDs);
100261
100262               if (action.disabled(context.graph()) !== 'not_connected') {
100263                 var count = 0;
100264
100265                 for (var i in ways) {
100266                   var way = ways[i];
100267
100268                   if (way.nodes.indexOf(node.id) !== -1) {
100269                     count += 1;
100270                   }
100271
100272                   if (count > 1) break;
100273                 }
100274
100275                 if (count > 1) {
100276                   sharedActions.push(action);
100277                   sharedNodes.push(node);
100278                 } else {
100279                   unsharedActions.push(action);
100280                   unsharedNodes.push(node);
100281                 }
100282               }
100283             });
100284             _descriptionID += 'no_points.';
100285             _descriptionID += _wayIDs.length === 1 ? 'single_way.' : 'multiple_ways.';
100286
100287             if (sharedActions.length) {
100288               // if any nodes are shared, only disconnect the selected ways from each other
100289               _actions = sharedActions;
100290               _disconnectingVertexIds = sharedNodes.map(function (node) {
100291                 return node.id;
100292               });
100293               _descriptionID += 'conjoined';
100294               _annotationID = 'from_each_other';
100295             } else {
100296               // if no nodes are shared, disconnect the selected ways from all connected ways
100297               _actions = unsharedActions;
100298               _disconnectingVertexIds = unsharedNodes.map(function (node) {
100299                 return node.id;
100300               });
100301
100302               if (_wayIDs.length === 1) {
100303                 _descriptionID += context.graph().geometry(_wayIDs[0]);
100304               } else {
100305                 _descriptionID += 'separate';
100306               }
100307             }
100308           }
100309
100310           var _extent = utilTotalExtent(_disconnectingVertexIds, context.graph());
100311
100312           var operation = function operation() {
100313             context.perform(function (graph) {
100314               return _actions.reduce(function (graph, action) {
100315                 return action(graph);
100316               }, graph);
100317             }, operation.annotation());
100318             context.validator().validate();
100319           };
100320
100321           operation.relatedEntityIds = function () {
100322             if (_vertexIDs.length) {
100323               return _disconnectingWayIds;
100324             }
100325
100326             return _disconnectingVertexIds;
100327           };
100328
100329           operation.available = function () {
100330             if (_actions.length === 0) return false;
100331             if (_otherIDs.length !== 0) return false;
100332             if (_vertexIDs.length !== 0 && _wayIDs.length !== 0 && !_wayIDs.every(function (wayID) {
100333               return _vertexIDs.some(function (vertexID) {
100334                 var way = context.entity(wayID);
100335                 return way.nodes.indexOf(vertexID) !== -1;
100336               });
100337             })) return false;
100338             return true;
100339           };
100340
100341           operation.disabled = function () {
100342             var reason;
100343
100344             for (var actionIndex in _actions) {
100345               reason = _actions[actionIndex].disabled(context.graph());
100346               if (reason) return reason;
100347             }
100348
100349             if (_extent && _extent.percentContainedIn(context.map().extent()) < 0.8) {
100350               return 'too_large.' + ((_vertexIDs.length ? _vertexIDs : _wayIDs).length === 1 ? 'single' : 'multiple');
100351             } else if (_coords && someMissing()) {
100352               return 'not_downloaded';
100353             } else if (selectedIDs.some(context.hasHiddenConnections)) {
100354               return 'connected_to_hidden';
100355             }
100356
100357             return false;
100358
100359             function someMissing() {
100360               if (context.inIntro()) return false;
100361               var osm = context.connection();
100362
100363               if (osm) {
100364                 var missing = _coords.filter(function (loc) {
100365                   return !osm.isDataLoaded(loc);
100366                 });
100367
100368                 if (missing.length) {
100369                   missing.forEach(function (loc) {
100370                     context.loadTileAtLoc(loc);
100371                   });
100372                   return true;
100373                 }
100374               }
100375
100376               return false;
100377             }
100378           };
100379
100380           operation.tooltip = function () {
100381             var disable = operation.disabled();
100382
100383             if (disable) {
100384               return _t('operations.disconnect.' + disable);
100385             }
100386
100387             return _t('operations.disconnect.description.' + _descriptionID);
100388           };
100389
100390           operation.annotation = function () {
100391             return _t('operations.disconnect.annotation.' + _annotationID);
100392           };
100393
100394           operation.id = 'disconnect';
100395           operation.keys = [_t('operations.disconnect.key')];
100396           operation.title = _t('operations.disconnect.title');
100397           operation.behavior = behaviorOperation(context).which(operation);
100398           return operation;
100399         }
100400
100401         function operationDowngrade(context, selectedIDs) {
100402           var _affectedFeatureCount = 0;
100403
100404           var _downgradeType = downgradeTypeForEntityIDs(selectedIDs);
100405
100406           var _multi = _affectedFeatureCount === 1 ? 'single' : 'multiple';
100407
100408           function downgradeTypeForEntityIDs(entityIds) {
100409             var downgradeType;
100410             _affectedFeatureCount = 0;
100411
100412             for (var i in entityIds) {
100413               var entityID = entityIds[i];
100414               var type = downgradeTypeForEntityID(entityID);
100415
100416               if (type) {
100417                 _affectedFeatureCount += 1;
100418
100419                 if (downgradeType && type !== downgradeType) {
100420                   if (downgradeType !== 'generic' && type !== 'generic') {
100421                     downgradeType = 'building_address';
100422                   } else {
100423                     downgradeType = 'generic';
100424                   }
100425                 } else {
100426                   downgradeType = type;
100427                 }
100428               }
100429             }
100430
100431             return downgradeType;
100432           }
100433
100434           function downgradeTypeForEntityID(entityID) {
100435             var graph = context.graph();
100436             var entity = graph.entity(entityID);
100437             var preset = _mainPresetIndex.match(entity, graph);
100438             if (!preset || preset.isFallback()) return null;
100439
100440             if (entity.type === 'node' && preset.id !== 'address' && Object.keys(entity.tags).some(function (key) {
100441               return key.match(/^addr:.{1,}/);
100442             })) {
100443               return 'address';
100444             }
100445
100446             var geometry = entity.geometry(graph);
100447
100448             if (geometry === 'area' && entity.tags.building && !preset.tags.building) {
100449               return 'building';
100450             }
100451
100452             if (geometry === 'vertex' && Object.keys(entity.tags).length) {
100453               return 'generic';
100454             }
100455
100456             return null;
100457           }
100458
100459           var buildingKeysToKeep = ['architect', 'building', 'height', 'layer', 'source', 'type', 'wheelchair'];
100460           var addressKeysToKeep = ['source'];
100461
100462           var operation = function operation() {
100463             context.perform(function (graph) {
100464               for (var i in selectedIDs) {
100465                 var entityID = selectedIDs[i];
100466                 var type = downgradeTypeForEntityID(entityID);
100467                 if (!type) continue;
100468                 var tags = Object.assign({}, graph.entity(entityID).tags); // shallow copy
100469
100470                 for (var key in tags) {
100471                   if (type === 'address' && addressKeysToKeep.indexOf(key) !== -1) continue;
100472
100473                   if (type === 'building') {
100474                     if (buildingKeysToKeep.indexOf(key) !== -1 || key.match(/^building:.{1,}/) || key.match(/^roof:.{1,}/)) continue;
100475                   }
100476
100477                   if (type !== 'generic') {
100478                     if (key.match(/^addr:.{1,}/) || key.match(/^source:.{1,}/)) continue;
100479                   }
100480
100481                   delete tags[key];
100482                 }
100483
100484                 graph = actionChangeTags(entityID, tags)(graph);
100485               }
100486
100487               return graph;
100488             }, operation.annotation());
100489             context.validator().validate(); // refresh the select mode to enable the delete operation
100490
100491             context.enter(modeSelect(context, selectedIDs));
100492           };
100493
100494           operation.available = function () {
100495             return _downgradeType;
100496           };
100497
100498           operation.disabled = function () {
100499             if (selectedIDs.some(hasWikidataTag)) {
100500               return 'has_wikidata_tag';
100501             }
100502
100503             return false;
100504
100505             function hasWikidataTag(id) {
100506               var entity = context.entity(id);
100507               return entity.tags.wikidata && entity.tags.wikidata.trim().length > 0;
100508             }
100509           };
100510
100511           operation.tooltip = function () {
100512             var disable = operation.disabled();
100513             return disable ? _t('operations.downgrade.' + disable + '.' + _multi) : _t('operations.downgrade.description.' + _downgradeType);
100514           };
100515
100516           operation.annotation = function () {
100517             var suffix;
100518
100519             if (_downgradeType === 'building_address') {
100520               suffix = 'generic';
100521             } else {
100522               suffix = _downgradeType;
100523             }
100524
100525             return _t('operations.downgrade.annotation.' + suffix, {
100526               n: _affectedFeatureCount
100527             });
100528           };
100529
100530           operation.id = 'downgrade';
100531           operation.keys = [uiCmd('⌫')];
100532           operation.title = _t('operations.downgrade.title');
100533           operation.behavior = behaviorOperation(context).which(operation);
100534           return operation;
100535         }
100536
100537         function operationExtract(context, selectedIDs) {
100538           var _amount = selectedIDs.length === 1 ? 'single' : 'multiple';
100539
100540           var _geometries = utilArrayUniq(selectedIDs.map(function (entityID) {
100541             return context.graph().hasEntity(entityID) && context.graph().geometry(entityID);
100542           }).filter(Boolean));
100543
100544           var _geometryID = _geometries.length === 1 ? _geometries[0] : 'feature';
100545
100546           var _extent;
100547
100548           var _actions = selectedIDs.map(function (entityID) {
100549             var graph = context.graph();
100550             var entity = graph.hasEntity(entityID);
100551             if (!entity || !entity.hasInterestingTags()) return null;
100552             if (entity.type === 'node' && graph.parentWays(entity).length === 0) return null;
100553
100554             if (entity.type !== 'node') {
100555               var preset = _mainPresetIndex.match(entity, graph); // only allow extraction from ways/relations if the preset supports points
100556
100557               if (preset.geometry.indexOf('point') === -1) return null;
100558             }
100559
100560             _extent = _extent ? _extent.extend(entity.extent(graph)) : entity.extent(graph);
100561             return actionExtract(entityID, context.projection);
100562           }).filter(Boolean);
100563
100564           var operation = function operation() {
100565             var combinedAction = function combinedAction(graph) {
100566               _actions.forEach(function (action) {
100567                 graph = action(graph);
100568               });
100569
100570               return graph;
100571             };
100572
100573             context.perform(combinedAction, operation.annotation()); // do the extract
100574
100575             var extractedNodeIDs = _actions.map(function (action) {
100576               return action.getExtractedNodeID();
100577             });
100578
100579             context.enter(modeSelect(context, extractedNodeIDs));
100580           };
100581
100582           operation.available = function () {
100583             return _actions.length && selectedIDs.length === _actions.length;
100584           };
100585
100586           operation.disabled = function () {
100587             if (_extent && _extent.percentContainedIn(context.map().extent()) < 0.8) {
100588               return 'too_large';
100589             } else if (selectedIDs.some(function (entityID) {
100590               return context.graph().geometry(entityID) === 'vertex' && context.hasHiddenConnections(entityID);
100591             })) {
100592               return 'connected_to_hidden';
100593             }
100594
100595             return false;
100596           };
100597
100598           operation.tooltip = function () {
100599             var disableReason = operation.disabled();
100600
100601             if (disableReason) {
100602               return _t('operations.extract.' + disableReason + '.' + _amount);
100603             } else {
100604               return _t('operations.extract.description.' + _geometryID + '.' + _amount);
100605             }
100606           };
100607
100608           operation.annotation = function () {
100609             return _t('operations.extract.annotation', {
100610               n: selectedIDs.length
100611             });
100612           };
100613
100614           operation.id = 'extract';
100615           operation.keys = [_t('operations.extract.key')];
100616           operation.title = _t('operations.extract.title');
100617           operation.behavior = behaviorOperation(context).which(operation);
100618           return operation;
100619         }
100620
100621         function operationMerge(context, selectedIDs) {
100622           var _action = getAction();
100623
100624           function getAction() {
100625             // prefer a non-disabled action first
100626             var join = actionJoin(selectedIDs);
100627             if (!join.disabled(context.graph())) return join;
100628             var merge = actionMerge(selectedIDs);
100629             if (!merge.disabled(context.graph())) return merge;
100630             var mergePolygon = actionMergePolygon(selectedIDs);
100631             if (!mergePolygon.disabled(context.graph())) return mergePolygon;
100632             var mergeNodes = actionMergeNodes(selectedIDs);
100633             if (!mergeNodes.disabled(context.graph())) return mergeNodes; // otherwise prefer an action with an interesting disabled reason
100634
100635             if (join.disabled(context.graph()) !== 'not_eligible') return join;
100636             if (merge.disabled(context.graph()) !== 'not_eligible') return merge;
100637             if (mergePolygon.disabled(context.graph()) !== 'not_eligible') return mergePolygon;
100638             return mergeNodes;
100639           }
100640
100641           var operation = function operation() {
100642             if (operation.disabled()) return;
100643             context.perform(_action, operation.annotation());
100644             context.validator().validate();
100645             var resultIDs = selectedIDs.filter(context.hasEntity);
100646
100647             if (resultIDs.length > 1) {
100648               var interestingIDs = resultIDs.filter(function (id) {
100649                 return context.entity(id).hasInterestingTags();
100650               });
100651               if (interestingIDs.length) resultIDs = interestingIDs;
100652             }
100653
100654             context.enter(modeSelect(context, resultIDs));
100655           };
100656
100657           operation.available = function () {
100658             return selectedIDs.length >= 2;
100659           };
100660
100661           operation.disabled = function () {
100662             var actionDisabled = _action.disabled(context.graph());
100663
100664             if (actionDisabled) return actionDisabled;
100665             var osm = context.connection();
100666
100667             if (osm && _action.resultingWayNodesLength && _action.resultingWayNodesLength(context.graph()) > osm.maxWayNodes()) {
100668               return 'too_many_vertices';
100669             }
100670
100671             return false;
100672           };
100673
100674           operation.tooltip = function () {
100675             var disabled = operation.disabled();
100676
100677             if (disabled) {
100678               if (disabled === 'restriction') {
100679                 return _t('operations.merge.restriction', {
100680                   relation: _mainPresetIndex.item('type/restriction').name()
100681                 });
100682               }
100683
100684               return _t('operations.merge.' + disabled);
100685             }
100686
100687             return _t('operations.merge.description');
100688           };
100689
100690           operation.annotation = function () {
100691             return _t('operations.merge.annotation', {
100692               n: selectedIDs.length
100693             });
100694           };
100695
100696           operation.id = 'merge';
100697           operation.keys = [_t('operations.merge.key')];
100698           operation.title = _t('operations.merge.title');
100699           operation.behavior = behaviorOperation(context).which(operation);
100700           return operation;
100701         }
100702
100703         function operationPaste(context) {
100704           var _pastePoint;
100705
100706           var operation = function operation() {
100707             if (!_pastePoint) return;
100708             var oldIDs = context.copyIDs();
100709             if (!oldIDs.length) return;
100710             var projection = context.projection;
100711             var extent = geoExtent();
100712             var oldGraph = context.copyGraph();
100713             var newIDs = [];
100714             var action = actionCopyEntities(oldIDs, oldGraph);
100715             context.perform(action);
100716             var copies = action.copies();
100717             var originals = new Set();
100718             Object.values(copies).forEach(function (entity) {
100719               originals.add(entity.id);
100720             });
100721
100722             for (var id in copies) {
100723               var oldEntity = oldGraph.entity(id);
100724               var newEntity = copies[id];
100725
100726               extent._extend(oldEntity.extent(oldGraph)); // Exclude child nodes from newIDs if their parent way was also copied.
100727
100728
100729               var parents = context.graph().parentWays(newEntity);
100730               var parentCopied = parents.some(function (parent) {
100731                 return originals.has(parent.id);
100732               });
100733
100734               if (!parentCopied) {
100735                 newIDs.push(newEntity.id);
100736               }
100737             } // Use the location of the copy operation to offset the paste location,
100738             // or else use the center of the pasted extent
100739
100740
100741             var copyPoint = context.copyLonLat() && projection(context.copyLonLat()) || projection(extent.center());
100742             var delta = geoVecSubtract(_pastePoint, copyPoint); // Move the pasted objects to be anchored at the paste location
100743
100744             context.replace(actionMove(newIDs, delta, projection), operation.annotation());
100745             context.enter(modeSelect(context, newIDs));
100746           };
100747
100748           operation.point = function (val) {
100749             _pastePoint = val;
100750             return operation;
100751           };
100752
100753           operation.available = function () {
100754             return context.mode().id === 'browse';
100755           };
100756
100757           operation.disabled = function () {
100758             return !context.copyIDs().length;
100759           };
100760
100761           operation.tooltip = function () {
100762             var oldGraph = context.copyGraph();
100763             var ids = context.copyIDs();
100764
100765             if (!ids.length) {
100766               return _t('operations.paste.nothing_copied');
100767             }
100768
100769             return _t('operations.paste.description', {
100770               feature: utilDisplayLabel(oldGraph.entity(ids[0]), oldGraph),
100771               n: ids.length
100772             });
100773           };
100774
100775           operation.annotation = function () {
100776             var ids = context.copyIDs();
100777             return _t('operations.paste.annotation', {
100778               n: ids.length
100779             });
100780           };
100781
100782           operation.id = 'paste';
100783           operation.keys = [uiCmd('⌘V')];
100784           operation.title = _t('operations.paste.title');
100785           return operation;
100786         }
100787
100788         function operationReverse(context, selectedIDs) {
100789           var operation = function operation() {
100790             context.perform(function combinedReverseAction(graph) {
100791               actions().forEach(function (action) {
100792                 graph = action(graph);
100793               });
100794               return graph;
100795             }, operation.annotation());
100796             context.validator().validate();
100797           };
100798
100799           function actions(situation) {
100800             return selectedIDs.map(function (entityID) {
100801               var entity = context.hasEntity(entityID);
100802               if (!entity) return null;
100803
100804               if (situation === 'toolbar') {
100805                 if (entity.type === 'way' && !entity.isOneWay() && !entity.isSided()) return null;
100806               }
100807
100808               var geometry = entity.geometry(context.graph());
100809               if (entity.type !== 'node' && geometry !== 'line') return null;
100810               var action = actionReverse(entityID);
100811               if (action.disabled(context.graph())) return null;
100812               return action;
100813             }).filter(Boolean);
100814           }
100815
100816           function reverseTypeID() {
100817             var acts = actions();
100818             var nodeActionCount = acts.filter(function (act) {
100819               var entity = context.hasEntity(act.entityID());
100820               return entity && entity.type === 'node';
100821             }).length;
100822             if (nodeActionCount === 0) return 'line';
100823             if (nodeActionCount === acts.length) return 'point';
100824             return 'feature';
100825           }
100826
100827           operation.available = function (situation) {
100828             return actions(situation).length > 0;
100829           };
100830
100831           operation.disabled = function () {
100832             return false;
100833           };
100834
100835           operation.tooltip = function () {
100836             return _t('operations.reverse.description.' + reverseTypeID());
100837           };
100838
100839           operation.annotation = function () {
100840             var acts = actions();
100841             return _t('operations.reverse.annotation.' + reverseTypeID(), {
100842               n: acts.length
100843             });
100844           };
100845
100846           operation.id = 'reverse';
100847           operation.keys = [_t('operations.reverse.key')];
100848           operation.title = _t('operations.reverse.title');
100849           operation.behavior = behaviorOperation(context).which(operation);
100850           return operation;
100851         }
100852
100853         function operationSplit(context, selectedIDs) {
100854           var _vertexIds = selectedIDs.filter(function (id) {
100855             return context.graph().geometry(id) === 'vertex';
100856           });
100857
100858           var _selectedWayIds = selectedIDs.filter(function (id) {
100859             var entity = context.graph().hasEntity(id);
100860             return entity && entity.type === 'way';
100861           });
100862
100863           var _isAvailable = _vertexIds.length > 0 && _vertexIds.length + _selectedWayIds.length === selectedIDs.length;
100864
100865           var _action = actionSplit(_vertexIds);
100866
100867           var _ways = [];
100868           var _geometry = 'feature';
100869           var _waysAmount = 'single';
100870
100871           var _nodesAmount = _vertexIds.length === 1 ? 'single' : 'multiple';
100872
100873           if (_isAvailable) {
100874             if (_selectedWayIds.length) _action.limitWays(_selectedWayIds);
100875             _ways = _action.ways(context.graph());
100876             var geometries = {};
100877
100878             _ways.forEach(function (way) {
100879               geometries[way.geometry(context.graph())] = true;
100880             });
100881
100882             if (Object.keys(geometries).length === 1) {
100883               _geometry = Object.keys(geometries)[0];
100884             }
100885
100886             _waysAmount = _ways.length === 1 ? 'single' : 'multiple';
100887           }
100888
100889           var operation = function operation() {
100890             var difference = context.perform(_action, operation.annotation()); // select both the nodes and the ways so the mapper can immediately disconnect them if desired
100891
100892             var idsToSelect = _vertexIds.concat(difference.extantIDs().filter(function (id) {
100893               // filter out relations that may have had member additions
100894               return context.entity(id).type === 'way';
100895             }));
100896
100897             context.enter(modeSelect(context, idsToSelect));
100898           };
100899
100900           operation.relatedEntityIds = function () {
100901             return _selectedWayIds.length ? [] : _ways.map(function (way) {
100902               return way.id;
100903             });
100904           };
100905
100906           operation.available = function () {
100907             return _isAvailable;
100908           };
100909
100910           operation.disabled = function () {
100911             var reason = _action.disabled(context.graph());
100912
100913             if (reason) {
100914               return reason;
100915             } else if (selectedIDs.some(context.hasHiddenConnections)) {
100916               return 'connected_to_hidden';
100917             }
100918
100919             return false;
100920           };
100921
100922           operation.tooltip = function () {
100923             var disable = operation.disabled();
100924             if (disable) return _t('operations.split.' + disable);
100925             return _t('operations.split.description.' + _geometry + '.' + _waysAmount + '.' + _nodesAmount + '_node');
100926           };
100927
100928           operation.annotation = function () {
100929             return _t('operations.split.annotation.' + _geometry, {
100930               n: _ways.length
100931             });
100932           };
100933
100934           operation.id = 'split';
100935           operation.keys = [_t('operations.split.key')];
100936           operation.title = _t('operations.split.title');
100937           operation.behavior = behaviorOperation(context).which(operation);
100938           return operation;
100939         }
100940
100941         function operationStraighten(context, selectedIDs) {
100942           var _wayIDs = selectedIDs.filter(function (id) {
100943             return id.charAt(0) === 'w';
100944           });
100945
100946           var _nodeIDs = selectedIDs.filter(function (id) {
100947             return id.charAt(0) === 'n';
100948           });
100949
100950           var _amount = (_wayIDs.length ? _wayIDs : _nodeIDs).length === 1 ? 'single' : 'multiple';
100951
100952           var _nodes = utilGetAllNodes(selectedIDs, context.graph());
100953
100954           var _coords = _nodes.map(function (n) {
100955             return n.loc;
100956           });
100957
100958           var _extent = utilTotalExtent(selectedIDs, context.graph());
100959
100960           var _action = chooseAction();
100961
100962           var _geometry;
100963
100964           function chooseAction() {
100965             // straighten selected nodes
100966             if (_wayIDs.length === 0 && _nodeIDs.length > 2) {
100967               _geometry = 'point';
100968               return actionStraightenNodes(_nodeIDs, context.projection); // straighten selected ways (possibly between range of 2 selected nodes)
100969             } else if (_wayIDs.length > 0 && (_nodeIDs.length === 0 || _nodeIDs.length === 2)) {
100970               var startNodeIDs = [];
100971               var endNodeIDs = [];
100972
100973               for (var i = 0; i < selectedIDs.length; i++) {
100974                 var entity = context.entity(selectedIDs[i]);
100975
100976                 if (entity.type === 'node') {
100977                   continue;
100978                 } else if (entity.type !== 'way' || entity.isClosed()) {
100979                   return null; // exit early, can't straighten these
100980                 }
100981
100982                 startNodeIDs.push(entity.first());
100983                 endNodeIDs.push(entity.last());
100984               } // Remove duplicate end/startNodeIDs (duplicate nodes cannot be at the line end)
100985
100986
100987               startNodeIDs = startNodeIDs.filter(function (n) {
100988                 return startNodeIDs.indexOf(n) === startNodeIDs.lastIndexOf(n);
100989               });
100990               endNodeIDs = endNodeIDs.filter(function (n) {
100991                 return endNodeIDs.indexOf(n) === endNodeIDs.lastIndexOf(n);
100992               }); // Ensure all ways are connected (i.e. only 2 unique endpoints/startpoints)
100993
100994               if (utilArrayDifference(startNodeIDs, endNodeIDs).length + utilArrayDifference(endNodeIDs, startNodeIDs).length !== 2) return null; // Ensure path contains at least 3 unique nodes
100995
100996               var wayNodeIDs = utilGetAllNodes(_wayIDs, context.graph()).map(function (node) {
100997                 return node.id;
100998               });
100999               if (wayNodeIDs.length <= 2) return null; // If range of 2 selected nodes is supplied, ensure nodes lie on the selected path
101000
101001               if (_nodeIDs.length === 2 && (wayNodeIDs.indexOf(_nodeIDs[0]) === -1 || wayNodeIDs.indexOf(_nodeIDs[1]) === -1)) return null;
101002
101003               if (_nodeIDs.length) {
101004                 // If we're only straightenting between two points, we only need that extent visible
101005                 _extent = utilTotalExtent(_nodeIDs, context.graph());
101006               }
101007
101008               _geometry = 'line';
101009               return actionStraightenWay(selectedIDs, context.projection);
101010             }
101011
101012             return null;
101013           }
101014
101015           function operation() {
101016             if (!_action) return;
101017             context.perform(_action, operation.annotation());
101018             window.setTimeout(function () {
101019               context.validator().validate();
101020             }, 300); // after any transition
101021           }
101022
101023           operation.available = function () {
101024             return Boolean(_action);
101025           };
101026
101027           operation.disabled = function () {
101028             var reason = _action.disabled(context.graph());
101029
101030             if (reason) {
101031               return reason;
101032             } else if (_extent.percentContainedIn(context.map().extent()) < 0.8) {
101033               return 'too_large';
101034             } else if (someMissing()) {
101035               return 'not_downloaded';
101036             } else if (selectedIDs.some(context.hasHiddenConnections)) {
101037               return 'connected_to_hidden';
101038             }
101039
101040             return false;
101041
101042             function someMissing() {
101043               if (context.inIntro()) return false;
101044               var osm = context.connection();
101045
101046               if (osm) {
101047                 var missing = _coords.filter(function (loc) {
101048                   return !osm.isDataLoaded(loc);
101049                 });
101050
101051                 if (missing.length) {
101052                   missing.forEach(function (loc) {
101053                     context.loadTileAtLoc(loc);
101054                   });
101055                   return true;
101056                 }
101057               }
101058
101059               return false;
101060             }
101061           };
101062
101063           operation.tooltip = function () {
101064             var disable = operation.disabled();
101065             return disable ? _t('operations.straighten.' + disable + '.' + _amount) : _t('operations.straighten.description.' + _geometry + (_wayIDs.length === 1 ? '' : 's'));
101066           };
101067
101068           operation.annotation = function () {
101069             return _t('operations.straighten.annotation.' + _geometry, {
101070               n: _wayIDs.length ? _wayIDs.length : _nodeIDs.length
101071             });
101072           };
101073
101074           operation.id = 'straighten';
101075           operation.keys = [_t('operations.straighten.key')];
101076           operation.title = _t('operations.straighten.title');
101077           operation.behavior = behaviorOperation(context).which(operation);
101078           return operation;
101079         }
101080
101081         var Operations = /*#__PURE__*/Object.freeze({
101082                 __proto__: null,
101083                 operationCircularize: operationCircularize,
101084                 operationContinue: operationContinue,
101085                 operationCopy: operationCopy,
101086                 operationDelete: operationDelete,
101087                 operationDisconnect: operationDisconnect,
101088                 operationDowngrade: operationDowngrade,
101089                 operationExtract: operationExtract,
101090                 operationMerge: operationMerge,
101091                 operationMove: operationMove,
101092                 operationOrthogonalize: operationOrthogonalize,
101093                 operationPaste: operationPaste,
101094                 operationReflectShort: operationReflectShort,
101095                 operationReflectLong: operationReflectLong,
101096                 operationReverse: operationReverse,
101097                 operationRotate: operationRotate,
101098                 operationSplit: operationSplit,
101099                 operationStraighten: operationStraighten
101100         });
101101
101102         function modeSelect(context, selectedIDs) {
101103           var mode = {
101104             id: 'select',
101105             button: 'browse'
101106           };
101107           var keybinding = utilKeybinding('select');
101108
101109           var _breatheBehavior = behaviorBreathe();
101110
101111           var _modeDragNode = modeDragNode(context);
101112
101113           var _selectBehavior;
101114
101115           var _behaviors = [];
101116           var _operations = [];
101117           var _newFeature = false;
101118           var _follow = false; // `_focusedParentWayId` is used when we visit a vertex with multiple
101119           // parents, and we want to remember which parent line we started on.
101120
101121           var _focusedParentWayId;
101122
101123           var _focusedVertexIds;
101124
101125           function singular() {
101126             if (selectedIDs && selectedIDs.length === 1) {
101127               return context.hasEntity(selectedIDs[0]);
101128             }
101129           }
101130
101131           function selectedEntities() {
101132             return selectedIDs.map(function (id) {
101133               return context.hasEntity(id);
101134             }).filter(Boolean);
101135           }
101136
101137           function checkSelectedIDs() {
101138             var ids = [];
101139
101140             if (Array.isArray(selectedIDs)) {
101141               ids = selectedIDs.filter(function (id) {
101142                 return context.hasEntity(id);
101143               });
101144             }
101145
101146             if (!ids.length) {
101147               context.enter(modeBrowse(context));
101148               return false;
101149             } else if (selectedIDs.length > 1 && ids.length === 1 || selectedIDs.length === 1 && ids.length > 1) {
101150               // switch between single- and multi-select UI
101151               context.enter(modeSelect(context, ids));
101152               return false;
101153             }
101154
101155             selectedIDs = ids;
101156             return true;
101157           } // find the parent ways for nextVertex, previousVertex, and selectParent
101158
101159
101160           function parentWaysIdsOfSelection(onlyCommonParents) {
101161             var graph = context.graph();
101162             var parents = [];
101163
101164             for (var i = 0; i < selectedIDs.length; i++) {
101165               var entity = context.hasEntity(selectedIDs[i]);
101166
101167               if (!entity || entity.geometry(graph) !== 'vertex') {
101168                 return []; // selection includes some non-vertices
101169               }
101170
101171               var currParents = graph.parentWays(entity).map(function (w) {
101172                 return w.id;
101173               });
101174
101175               if (!parents.length) {
101176                 parents = currParents;
101177                 continue;
101178               }
101179
101180               parents = (onlyCommonParents ? utilArrayIntersection : utilArrayUnion)(parents, currParents);
101181
101182               if (!parents.length) {
101183                 return [];
101184               }
101185             }
101186
101187             return parents;
101188           } // find the child nodes for selected ways
101189
101190
101191           function childNodeIdsOfSelection(onlyCommon) {
101192             var graph = context.graph();
101193             var childs = [];
101194
101195             for (var i = 0; i < selectedIDs.length; i++) {
101196               var entity = context.hasEntity(selectedIDs[i]);
101197
101198               if (!entity || !['area', 'line'].includes(entity.geometry(graph))) {
101199                 return []; // selection includes non-area/non-line
101200               }
101201
101202               var currChilds = graph.childNodes(entity).map(function (node) {
101203                 return node.id;
101204               });
101205
101206               if (!childs.length) {
101207                 childs = currChilds;
101208                 continue;
101209               }
101210
101211               childs = (onlyCommon ? utilArrayIntersection : utilArrayUnion)(childs, currChilds);
101212
101213               if (!childs.length) {
101214                 return [];
101215               }
101216             }
101217
101218             return childs;
101219           }
101220
101221           function checkFocusedParent() {
101222             if (_focusedParentWayId) {
101223               var parents = parentWaysIdsOfSelection(true);
101224               if (parents.indexOf(_focusedParentWayId) === -1) _focusedParentWayId = null;
101225             }
101226           }
101227
101228           function parentWayIdForVertexNavigation() {
101229             var parentIds = parentWaysIdsOfSelection(true);
101230
101231             if (_focusedParentWayId && parentIds.indexOf(_focusedParentWayId) !== -1) {
101232               // prefer the previously seen parent
101233               return _focusedParentWayId;
101234             }
101235
101236             return parentIds.length ? parentIds[0] : null;
101237           }
101238
101239           mode.selectedIDs = function (val) {
101240             if (!arguments.length) return selectedIDs;
101241             selectedIDs = val;
101242             return mode;
101243           };
101244
101245           mode.zoomToSelected = function () {
101246             context.map().zoomToEase(selectedEntities());
101247           };
101248
101249           mode.newFeature = function (val) {
101250             if (!arguments.length) return _newFeature;
101251             _newFeature = val;
101252             return mode;
101253           };
101254
101255           mode.selectBehavior = function (val) {
101256             if (!arguments.length) return _selectBehavior;
101257             _selectBehavior = val;
101258             return mode;
101259           };
101260
101261           mode.follow = function (val) {
101262             if (!arguments.length) return _follow;
101263             _follow = val;
101264             return mode;
101265           };
101266
101267           function loadOperations() {
101268             _operations.forEach(function (operation) {
101269               if (operation.behavior) {
101270                 context.uninstall(operation.behavior);
101271               }
101272             });
101273
101274             _operations = Object.values(Operations).map(function (o) {
101275               return o(context, selectedIDs);
101276             }).filter(function (o) {
101277               return o.id !== 'delete' && o.id !== 'downgrade' && o.id !== 'copy';
101278             }).concat([// group copy/downgrade/delete operation together at the end of the list
101279             operationCopy(context, selectedIDs), operationDowngrade(context, selectedIDs), operationDelete(context, selectedIDs)]).filter(function (operation) {
101280               return operation.available();
101281             });
101282
101283             _operations.forEach(function (operation) {
101284               if (operation.behavior) {
101285                 context.install(operation.behavior);
101286               }
101287             }); // remove any displayed menu
101288
101289
101290             context.ui().closeEditMenu();
101291           }
101292
101293           mode.operations = function () {
101294             return _operations;
101295           };
101296
101297           mode.enter = function () {
101298             if (!checkSelectedIDs()) return;
101299             context.features().forceVisible(selectedIDs);
101300
101301             _modeDragNode.restoreSelectedIDs(selectedIDs);
101302
101303             loadOperations();
101304
101305             if (!_behaviors.length) {
101306               if (!_selectBehavior) _selectBehavior = behaviorSelect(context);
101307               _behaviors = [behaviorPaste(context), _breatheBehavior, behaviorHover(context).on('hover', context.ui().sidebar.hoverModeSelect), _selectBehavior, behaviorLasso(context), _modeDragNode.behavior, modeDragNote(context).behavior];
101308             }
101309
101310             _behaviors.forEach(context.install);
101311
101312             keybinding.on(_t('inspector.zoom_to.key'), mode.zoomToSelected).on(['[', 'pgup'], previousVertex).on([']', 'pgdown'], nextVertex).on(['{', uiCmd('⌘['), 'home'], firstVertex).on(['}', uiCmd('⌘]'), 'end'], lastVertex).on(uiCmd('⇧←'), nudgeSelection([-10, 0])).on(uiCmd('⇧↑'), nudgeSelection([0, -10])).on(uiCmd('⇧→'), nudgeSelection([10, 0])).on(uiCmd('⇧↓'), nudgeSelection([0, 10])).on(uiCmd('⇧⌥←'), nudgeSelection([-100, 0])).on(uiCmd('⇧⌥↑'), nudgeSelection([0, -100])).on(uiCmd('⇧⌥→'), nudgeSelection([100, 0])).on(uiCmd('⇧⌥↓'), nudgeSelection([0, 100])).on(utilKeybinding.plusKeys.map(function (key) {
101313               return uiCmd('⇧' + key);
101314             }), scaleSelection(1.05)).on(utilKeybinding.plusKeys.map(function (key) {
101315               return uiCmd('⇧⌥' + key);
101316             }), scaleSelection(Math.pow(1.05, 5))).on(utilKeybinding.minusKeys.map(function (key) {
101317               return uiCmd('⇧' + key);
101318             }), scaleSelection(1 / 1.05)).on(utilKeybinding.minusKeys.map(function (key) {
101319               return uiCmd('⇧⌥' + key);
101320             }), scaleSelection(1 / Math.pow(1.05, 5))).on(['\\', 'pause'], focusNextParent).on(uiCmd('⌘↑'), selectParent).on(uiCmd('⌘↓'), selectChild).on('⎋', esc, true);
101321             select(document).call(keybinding);
101322             context.ui().sidebar.select(selectedIDs, _newFeature);
101323             context.history().on('change.select', function () {
101324               loadOperations(); // reselect after change in case relation members were removed or added
101325
101326               selectElements();
101327             }).on('undone.select', checkSelectedIDs).on('redone.select', checkSelectedIDs);
101328             context.map().on('drawn.select', selectElements).on('crossEditableZoom.select', function () {
101329               selectElements();
101330
101331               _breatheBehavior.restartIfNeeded(context.surface());
101332             });
101333             context.map().doubleUpHandler().on('doubleUp.modeSelect', didDoubleUp);
101334             selectElements();
101335
101336             if (_follow) {
101337               var extent = geoExtent();
101338               var graph = context.graph();
101339               selectedIDs.forEach(function (id) {
101340                 var entity = context.entity(id);
101341
101342                 extent._extend(entity.extent(graph));
101343               });
101344               var loc = extent.center();
101345               context.map().centerEase(loc); // we could enter the mode multiple times, so reset follow for next time
101346
101347               _follow = false;
101348             }
101349
101350             function nudgeSelection(delta) {
101351               return function () {
101352                 // prevent nudging during low zoom selection
101353                 if (!context.map().withinEditableZoom()) return;
101354                 var moveOp = operationMove(context, selectedIDs);
101355
101356                 if (moveOp.disabled()) {
101357                   context.ui().flash.duration(4000).iconName('#iD-operation-' + moveOp.id).iconClass('operation disabled').label(moveOp.tooltip)();
101358                 } else {
101359                   context.perform(actionMove(selectedIDs, delta, context.projection), moveOp.annotation());
101360                   context.validator().validate();
101361                 }
101362               };
101363             }
101364
101365             function scaleSelection(factor) {
101366               return function () {
101367                 // prevent scaling during low zoom selection
101368                 if (!context.map().withinEditableZoom()) return;
101369                 var nodes = utilGetAllNodes(selectedIDs, context.graph());
101370                 var isUp = factor > 1; // can only scale if multiple nodes are selected
101371
101372                 if (nodes.length <= 1) return;
101373                 var extent = utilTotalExtent(selectedIDs, context.graph()); // These disabled checks would normally be handled by an operation
101374                 // object, but we don't want an actual scale operation at this point.
101375
101376                 function scalingDisabled() {
101377                   if (tooSmall()) {
101378                     return 'too_small';
101379                   } else if (extent.percentContainedIn(context.map().extent()) < 0.8) {
101380                     return 'too_large';
101381                   } else if (someMissing() || selectedIDs.some(incompleteRelation)) {
101382                     return 'not_downloaded';
101383                   } else if (selectedIDs.some(context.hasHiddenConnections)) {
101384                     return 'connected_to_hidden';
101385                   }
101386
101387                   return false;
101388
101389                   function tooSmall() {
101390                     if (isUp) return false;
101391                     var dLon = Math.abs(extent[1][0] - extent[0][0]);
101392                     var dLat = Math.abs(extent[1][1] - extent[0][1]);
101393                     return dLon < geoMetersToLon(1, extent[1][1]) && dLat < geoMetersToLat(1);
101394                   }
101395
101396                   function someMissing() {
101397                     if (context.inIntro()) return false;
101398                     var osm = context.connection();
101399
101400                     if (osm) {
101401                       var missing = nodes.filter(function (n) {
101402                         return !osm.isDataLoaded(n.loc);
101403                       });
101404
101405                       if (missing.length) {
101406                         missing.forEach(function (loc) {
101407                           context.loadTileAtLoc(loc);
101408                         });
101409                         return true;
101410                       }
101411                     }
101412
101413                     return false;
101414                   }
101415
101416                   function incompleteRelation(id) {
101417                     var entity = context.entity(id);
101418                     return entity.type === 'relation' && !entity.isComplete(context.graph());
101419                   }
101420                 }
101421
101422                 var disabled = scalingDisabled();
101423
101424                 if (disabled) {
101425                   var multi = selectedIDs.length === 1 ? 'single' : 'multiple';
101426                   context.ui().flash.duration(4000).iconName('#iD-icon-no').iconClass('operation disabled').label(_t('operations.scale.' + disabled + '.' + multi))();
101427                 } else {
101428                   var pivot = context.projection(extent.center());
101429                   var annotation = _t('operations.scale.annotation.' + (isUp ? 'up' : 'down') + '.feature', {
101430                     n: selectedIDs.length
101431                   });
101432                   context.perform(actionScale(selectedIDs, pivot, factor, context.projection), annotation);
101433                   context.validator().validate();
101434                 }
101435               };
101436             }
101437
101438             function didDoubleUp(d3_event, loc) {
101439               if (!context.map().withinEditableZoom()) return;
101440               var target = select(d3_event.target);
101441               var datum = target.datum();
101442               var entity = datum && datum.properties && datum.properties.entity;
101443               if (!entity) return;
101444
101445               if (entity instanceof osmWay && target.classed('target')) {
101446                 var choice = geoChooseEdge(context.graph().childNodes(entity), loc, context.projection);
101447                 var prev = entity.nodes[choice.index - 1];
101448                 var next = entity.nodes[choice.index];
101449                 context.perform(actionAddMidpoint({
101450                   loc: choice.loc,
101451                   edge: [prev, next]
101452                 }, osmNode()), _t('operations.add.annotation.vertex'));
101453               } else if (entity.type === 'midpoint') {
101454                 context.perform(actionAddMidpoint({
101455                   loc: entity.loc,
101456                   edge: entity.edge
101457                 }, osmNode()), _t('operations.add.annotation.vertex'));
101458               }
101459             }
101460
101461             function selectElements() {
101462               if (!checkSelectedIDs()) return;
101463               var surface = context.surface();
101464               surface.selectAll('.selected-member').classed('selected-member', false);
101465               surface.selectAll('.selected').classed('selected', false);
101466               surface.selectAll('.related').classed('related', false); // reload `_focusedParentWayId` based on the current selection
101467
101468               checkFocusedParent();
101469
101470               if (_focusedParentWayId) {
101471                 surface.selectAll(utilEntitySelector([_focusedParentWayId])).classed('related', true);
101472               }
101473
101474               if (context.map().withinEditableZoom()) {
101475                 // Apply selection styling if not in wide selection
101476                 surface.selectAll(utilDeepMemberSelector(selectedIDs, context.graph(), true
101477                 /* skipMultipolgonMembers */
101478                 )).classed('selected-member', true);
101479                 surface.selectAll(utilEntityOrDeepMemberSelector(selectedIDs, context.graph())).classed('selected', true);
101480               }
101481             }
101482
101483             function esc() {
101484               if (context.container().select('.combobox').size()) return;
101485               context.enter(modeBrowse(context));
101486             }
101487
101488             function firstVertex(d3_event) {
101489               d3_event.preventDefault();
101490               var entity = singular();
101491               var parentId = parentWayIdForVertexNavigation();
101492               var way;
101493
101494               if (entity && entity.type === 'way') {
101495                 way = entity;
101496               } else if (parentId) {
101497                 way = context.entity(parentId);
101498               }
101499
101500               _focusedParentWayId = way && way.id;
101501
101502               if (way) {
101503                 context.enter(mode.selectedIDs([way.first()]).follow(true));
101504               }
101505             }
101506
101507             function lastVertex(d3_event) {
101508               d3_event.preventDefault();
101509               var entity = singular();
101510               var parentId = parentWayIdForVertexNavigation();
101511               var way;
101512
101513               if (entity && entity.type === 'way') {
101514                 way = entity;
101515               } else if (parentId) {
101516                 way = context.entity(parentId);
101517               }
101518
101519               _focusedParentWayId = way && way.id;
101520
101521               if (way) {
101522                 context.enter(mode.selectedIDs([way.last()]).follow(true));
101523               }
101524             }
101525
101526             function previousVertex(d3_event) {
101527               d3_event.preventDefault();
101528               var parentId = parentWayIdForVertexNavigation();
101529               _focusedParentWayId = parentId;
101530               if (!parentId) return;
101531               var way = context.entity(parentId);
101532               var length = way.nodes.length;
101533               var curr = way.nodes.indexOf(selectedIDs[0]);
101534               var index = -1;
101535
101536               if (curr > 0) {
101537                 index = curr - 1;
101538               } else if (way.isClosed()) {
101539                 index = length - 2;
101540               }
101541
101542               if (index !== -1) {
101543                 context.enter(mode.selectedIDs([way.nodes[index]]).follow(true));
101544               }
101545             }
101546
101547             function nextVertex(d3_event) {
101548               d3_event.preventDefault();
101549               var parentId = parentWayIdForVertexNavigation();
101550               _focusedParentWayId = parentId;
101551               if (!parentId) return;
101552               var way = context.entity(parentId);
101553               var length = way.nodes.length;
101554               var curr = way.nodes.indexOf(selectedIDs[0]);
101555               var index = -1;
101556
101557               if (curr < length - 1) {
101558                 index = curr + 1;
101559               } else if (way.isClosed()) {
101560                 index = 0;
101561               }
101562
101563               if (index !== -1) {
101564                 context.enter(mode.selectedIDs([way.nodes[index]]).follow(true));
101565               }
101566             }
101567
101568             function focusNextParent(d3_event) {
101569               d3_event.preventDefault();
101570               var parents = parentWaysIdsOfSelection(true);
101571               if (!parents || parents.length < 2) return;
101572               var index = parents.indexOf(_focusedParentWayId);
101573
101574               if (index < 0 || index > parents.length - 2) {
101575                 _focusedParentWayId = parents[0];
101576               } else {
101577                 _focusedParentWayId = parents[index + 1];
101578               }
101579
101580               var surface = context.surface();
101581               surface.selectAll('.related').classed('related', false);
101582
101583               if (_focusedParentWayId) {
101584                 surface.selectAll(utilEntitySelector([_focusedParentWayId])).classed('related', true);
101585               }
101586             }
101587
101588             function selectParent(d3_event) {
101589               d3_event.preventDefault();
101590               var currentSelectedIds = mode.selectedIDs();
101591               var parentIds = _focusedParentWayId ? [_focusedParentWayId] : parentWaysIdsOfSelection(false);
101592               if (!parentIds.length) return;
101593               context.enter(mode.selectedIDs(parentIds)); // set this after re-entering the selection since we normally want it cleared on exit
101594
101595               _focusedVertexIds = currentSelectedIds;
101596             }
101597
101598             function selectChild(d3_event) {
101599               d3_event.preventDefault();
101600               var currentSelectedIds = mode.selectedIDs();
101601               var childIds = _focusedVertexIds ? _focusedVertexIds.filter(function (id) {
101602                 return context.hasEntity(id);
101603               }) : childNodeIdsOfSelection(true);
101604               if (!childIds || !childIds.length) return;
101605               if (currentSelectedIds.length === 1) _focusedParentWayId = currentSelectedIds[0];
101606               context.enter(mode.selectedIDs(childIds));
101607             }
101608           };
101609
101610           mode.exit = function () {
101611             // we could enter the mode multiple times but it's only new the first time
101612             _newFeature = false;
101613             _focusedVertexIds = null;
101614
101615             _operations.forEach(function (operation) {
101616               if (operation.behavior) {
101617                 context.uninstall(operation.behavior);
101618               }
101619             });
101620
101621             _operations = [];
101622
101623             _behaviors.forEach(context.uninstall);
101624
101625             select(document).call(keybinding.unbind);
101626             context.ui().closeEditMenu();
101627             context.history().on('change.select', null).on('undone.select', null).on('redone.select', null);
101628             var surface = context.surface();
101629             surface.selectAll('.selected-member').classed('selected-member', false);
101630             surface.selectAll('.selected').classed('selected', false);
101631             surface.selectAll('.highlighted').classed('highlighted', false);
101632             surface.selectAll('.related').classed('related', false);
101633             context.map().on('drawn.select', null);
101634             context.ui().sidebar.hide();
101635             context.features().forceVisible([]);
101636             var entity = singular();
101637
101638             if (_newFeature && entity && entity.type === 'relation' && // no tags
101639             Object.keys(entity.tags).length === 0 && // no parent relations
101640             context.graph().parentRelations(entity).length === 0 && ( // no members or one member with no role
101641             entity.members.length === 0 || entity.members.length === 1 && !entity.members[0].role)) {
101642               // the user added this relation but didn't edit it at all, so just delete it
101643               var deleteAction = actionDeleteRelation(entity.id, true
101644               /* don't delete untagged members */
101645               );
101646               context.perform(deleteAction, _t('operations.delete.annotation.relation'));
101647             }
101648           };
101649
101650           return mode;
101651         }
101652
101653         function uiLasso(context) {
101654           var group, polygon;
101655           lasso.coordinates = [];
101656
101657           function lasso(selection) {
101658             context.container().classed('lasso', true);
101659             group = selection.append('g').attr('class', 'lasso hide');
101660             polygon = group.append('path').attr('class', 'lasso-path');
101661             group.call(uiToggle(true));
101662           }
101663
101664           function draw() {
101665             if (polygon) {
101666               polygon.data([lasso.coordinates]).attr('d', function (d) {
101667                 return 'M' + d.join(' L') + ' Z';
101668               });
101669             }
101670           }
101671
101672           lasso.extent = function () {
101673             return lasso.coordinates.reduce(function (extent, point) {
101674               return extent.extend(geoExtent(point));
101675             }, geoExtent());
101676           };
101677
101678           lasso.p = function (_) {
101679             if (!arguments.length) return lasso;
101680             lasso.coordinates.push(_);
101681             draw();
101682             return lasso;
101683           };
101684
101685           lasso.close = function () {
101686             if (group) {
101687               group.call(uiToggle(false, function () {
101688                 select(this).remove();
101689               }));
101690             }
101691
101692             context.container().classed('lasso', false);
101693           };
101694
101695           return lasso;
101696         }
101697
101698         function behaviorLasso(context) {
101699           // use pointer events on supported platforms; fallback to mouse events
101700           var _pointerPrefix = 'PointerEvent' in window ? 'pointer' : 'mouse';
101701
101702           var behavior = function behavior(selection) {
101703             var lasso;
101704
101705             function pointerdown(d3_event) {
101706               var button = 0; // left
101707
101708               if (d3_event.button === button && d3_event.shiftKey === true) {
101709                 lasso = null;
101710                 select(window).on(_pointerPrefix + 'move.lasso', pointermove).on(_pointerPrefix + 'up.lasso', pointerup);
101711                 d3_event.stopPropagation();
101712               }
101713             }
101714
101715             function pointermove() {
101716               if (!lasso) {
101717                 lasso = uiLasso(context);
101718                 context.surface().call(lasso);
101719               }
101720
101721               lasso.p(context.map().mouse());
101722             }
101723
101724             function normalize(a, b) {
101725               return [[Math.min(a[0], b[0]), Math.min(a[1], b[1])], [Math.max(a[0], b[0]), Math.max(a[1], b[1])]];
101726             }
101727
101728             function lassoed() {
101729               if (!lasso) return [];
101730               var graph = context.graph();
101731               var limitToNodes;
101732
101733               if (context.map().editableDataEnabled(true
101734               /* skipZoomCheck */
101735               ) && context.map().isInWideSelection()) {
101736                 // only select from the visible nodes
101737                 limitToNodes = new Set(utilGetAllNodes(context.selectedIDs(), graph));
101738               } else if (!context.map().editableDataEnabled()) {
101739                 return [];
101740               }
101741
101742               var bounds = lasso.extent().map(context.projection.invert);
101743               var extent = geoExtent(normalize(bounds[0], bounds[1]));
101744               var intersects = context.history().intersects(extent).filter(function (entity) {
101745                 return entity.type === 'node' && (!limitToNodes || limitToNodes.has(entity)) && geoPointInPolygon(context.projection(entity.loc), lasso.coordinates) && !context.features().isHidden(entity, graph, entity.geometry(graph));
101746               }); // sort the lassoed nodes as best we can
101747
101748               intersects.sort(function (node1, node2) {
101749                 var parents1 = graph.parentWays(node1);
101750                 var parents2 = graph.parentWays(node2);
101751
101752                 if (parents1.length && parents2.length) {
101753                   // both nodes are vertices
101754                   var sharedParents = utilArrayIntersection(parents1, parents2);
101755
101756                   if (sharedParents.length) {
101757                     var sharedParentNodes = sharedParents[0].nodes; // vertices are members of the same way; sort them in their listed order
101758
101759                     return sharedParentNodes.indexOf(node1.id) - sharedParentNodes.indexOf(node2.id);
101760                   } else {
101761                     // vertices do not share a way; group them by their respective parent ways
101762                     return parseFloat(parents1[0].id.slice(1)) - parseFloat(parents2[0].id.slice(1));
101763                   }
101764                 } else if (parents1.length || parents2.length) {
101765                   // only one node is a vertex; sort standalone points before vertices
101766                   return parents1.length - parents2.length;
101767                 } // both nodes are standalone points; sort left to right
101768
101769
101770                 return node1.loc[0] - node2.loc[0];
101771               });
101772               return intersects.map(function (entity) {
101773                 return entity.id;
101774               });
101775             }
101776
101777             function pointerup() {
101778               select(window).on(_pointerPrefix + 'move.lasso', null).on(_pointerPrefix + 'up.lasso', null);
101779               if (!lasso) return;
101780               var ids = lassoed();
101781               lasso.close();
101782
101783               if (ids.length) {
101784                 context.enter(modeSelect(context, ids));
101785               }
101786             }
101787
101788             selection.on(_pointerPrefix + 'down.lasso', pointerdown);
101789           };
101790
101791           behavior.off = function (selection) {
101792             selection.on(_pointerPrefix + 'down.lasso', null);
101793           };
101794
101795           return behavior;
101796         }
101797
101798         function modeBrowse(context) {
101799           var mode = {
101800             button: 'browse',
101801             id: 'browse',
101802             title: _t('modes.browse.title'),
101803             description: _t('modes.browse.description')
101804           };
101805           var sidebar;
101806
101807           var _selectBehavior;
101808
101809           var _behaviors = [];
101810
101811           mode.selectBehavior = function (val) {
101812             if (!arguments.length) return _selectBehavior;
101813             _selectBehavior = val;
101814             return mode;
101815           };
101816
101817           mode.enter = function () {
101818             if (!_behaviors.length) {
101819               if (!_selectBehavior) _selectBehavior = behaviorSelect(context);
101820               _behaviors = [behaviorPaste(context), behaviorHover(context).on('hover', context.ui().sidebar.hover), _selectBehavior, behaviorLasso(context), modeDragNode(context).behavior, modeDragNote(context).behavior];
101821             }
101822
101823             _behaviors.forEach(context.install); // Get focus on the body.
101824
101825
101826             if (document.activeElement && document.activeElement.blur) {
101827               document.activeElement.blur();
101828             }
101829
101830             if (sidebar) {
101831               context.ui().sidebar.show(sidebar);
101832             } else {
101833               context.ui().sidebar.select(null);
101834             }
101835           };
101836
101837           mode.exit = function () {
101838             context.ui().sidebar.hover.cancel();
101839
101840             _behaviors.forEach(context.uninstall);
101841
101842             if (sidebar) {
101843               context.ui().sidebar.hide();
101844             }
101845           };
101846
101847           mode.sidebar = function (_) {
101848             if (!arguments.length) return sidebar;
101849             sidebar = _;
101850             return mode;
101851           };
101852
101853           mode.operations = function () {
101854             return [operationPaste(context)];
101855           };
101856
101857           return mode;
101858         }
101859
101860         function behaviorAddWay(context) {
101861           var dispatch = dispatch$8('start', 'startFromWay', 'startFromNode');
101862           var draw = behaviorDraw(context);
101863
101864           function behavior(surface) {
101865             draw.on('click', function () {
101866               dispatch.apply('start', this, arguments);
101867             }).on('clickWay', function () {
101868               dispatch.apply('startFromWay', this, arguments);
101869             }).on('clickNode', function () {
101870               dispatch.apply('startFromNode', this, arguments);
101871             }).on('cancel', behavior.cancel).on('finish', behavior.cancel);
101872             context.map().dblclickZoomEnable(false);
101873             surface.call(draw);
101874           }
101875
101876           behavior.off = function (surface) {
101877             surface.call(draw.off);
101878           };
101879
101880           behavior.cancel = function () {
101881             window.setTimeout(function () {
101882               context.map().dblclickZoomEnable(true);
101883             }, 1000);
101884             context.enter(modeBrowse(context));
101885           };
101886
101887           return utilRebind(behavior, dispatch, 'on');
101888         }
101889
101890         function behaviorHash(context) {
101891           // cached window.location.hash
101892           var _cachedHash = null; // allowable latitude range
101893
101894           var _latitudeLimit = 90 - 1e-8;
101895
101896           function computedHashParameters() {
101897             var map = context.map();
101898             var center = map.center();
101899             var zoom = map.zoom();
101900             var precision = Math.max(0, Math.ceil(Math.log(zoom) / Math.LN2));
101901             var oldParams = utilObjectOmit(utilStringQs(window.location.hash), ['comment', 'source', 'hashtags', 'walkthrough']);
101902             var newParams = {};
101903             delete oldParams.id;
101904             var selected = context.selectedIDs().filter(function (id) {
101905               return context.hasEntity(id);
101906             });
101907
101908             if (selected.length) {
101909               newParams.id = selected.join(',');
101910             }
101911
101912             newParams.map = zoom.toFixed(2) + '/' + center[1].toFixed(precision) + '/' + center[0].toFixed(precision);
101913             return Object.assign(oldParams, newParams);
101914           }
101915
101916           function computedHash() {
101917             return '#' + utilQsString(computedHashParameters(), true);
101918           }
101919
101920           function computedTitle(includeChangeCount) {
101921             var baseTitle = context.documentTitleBase() || 'iD';
101922             var contextual;
101923             var changeCount;
101924             var titleID;
101925             var selected = context.selectedIDs().filter(function (id) {
101926               return context.hasEntity(id);
101927             });
101928
101929             if (selected.length) {
101930               var firstLabel = utilDisplayLabel(context.entity(selected[0]), context.graph());
101931
101932               if (selected.length > 1) {
101933                 contextual = _t('title.labeled_and_more', {
101934                   labeled: firstLabel,
101935                   count: selected.length - 1
101936                 });
101937               } else {
101938                 contextual = firstLabel;
101939               }
101940
101941               titleID = 'context';
101942             }
101943
101944             if (includeChangeCount) {
101945               changeCount = context.history().difference().summary().length;
101946
101947               if (changeCount > 0) {
101948                 titleID = contextual ? 'changes_context' : 'changes';
101949               }
101950             }
101951
101952             if (titleID) {
101953               return _t('title.format.' + titleID, {
101954                 changes: changeCount,
101955                 base: baseTitle,
101956                 context: contextual
101957               });
101958             }
101959
101960             return baseTitle;
101961           }
101962
101963           function updateTitle(includeChangeCount) {
101964             if (!context.setsDocumentTitle()) return;
101965             var newTitle = computedTitle(includeChangeCount);
101966
101967             if (document.title !== newTitle) {
101968               document.title = newTitle;
101969             }
101970           }
101971
101972           function updateHashIfNeeded() {
101973             if (context.inIntro()) return;
101974             var latestHash = computedHash();
101975
101976             if (_cachedHash !== latestHash) {
101977               _cachedHash = latestHash; // Update the URL hash without affecting the browser navigation stack,
101978               // though unavoidably creating a browser history entry
101979
101980               window.history.replaceState(null, computedTitle(false
101981               /* includeChangeCount */
101982               ), latestHash); // set the title we want displayed for the browser tab/window
101983
101984               updateTitle(true
101985               /* includeChangeCount */
101986               );
101987             }
101988           }
101989
101990           var _throttledUpdate = throttle(updateHashIfNeeded, 500);
101991
101992           var _throttledUpdateTitle = throttle(function () {
101993             updateTitle(true
101994             /* includeChangeCount */
101995             );
101996           }, 500);
101997
101998           function hashchange() {
101999             // ignore spurious hashchange events
102000             if (window.location.hash === _cachedHash) return;
102001             _cachedHash = window.location.hash;
102002             var q = utilStringQs(_cachedHash);
102003             var mapArgs = (q.map || '').split('/').map(Number);
102004
102005             if (mapArgs.length < 3 || mapArgs.some(isNaN)) {
102006               // replace bogus hash
102007               updateHashIfNeeded();
102008             } else {
102009               // don't update if the new hash already reflects the state of iD
102010               if (_cachedHash === computedHash()) return;
102011               var mode = context.mode();
102012               context.map().centerZoom([mapArgs[2], Math.min(_latitudeLimit, Math.max(-_latitudeLimit, mapArgs[1]))], mapArgs[0]);
102013
102014               if (q.id && mode) {
102015                 var ids = q.id.split(',').filter(function (id) {
102016                   return context.hasEntity(id);
102017                 });
102018
102019                 if (ids.length && (mode.id === 'browse' || mode.id === 'select' && !utilArrayIdentical(mode.selectedIDs(), ids))) {
102020                   context.enter(modeSelect(context, ids));
102021                   return;
102022                 }
102023               }
102024
102025               var center = context.map().center();
102026               var dist = geoSphericalDistance(center, [mapArgs[2], mapArgs[1]]);
102027               var maxdist = 500; // Don't allow the hash location to change too much while drawing
102028               // This can happen if the user accidentally hit the back button.  #3996
102029
102030               if (mode && mode.id.match(/^draw/) !== null && dist > maxdist) {
102031                 context.enter(modeBrowse(context));
102032                 return;
102033               }
102034             }
102035           }
102036
102037           function behavior() {
102038             context.map().on('move.behaviorHash', _throttledUpdate);
102039             context.history().on('change.behaviorHash', _throttledUpdateTitle);
102040             context.on('enter.behaviorHash', _throttledUpdate);
102041             select(window).on('hashchange.behaviorHash', hashchange);
102042
102043             if (window.location.hash) {
102044               var q = utilStringQs(window.location.hash);
102045
102046               if (q.id) {
102047                 //if (!context.history().hasRestorableChanges()) {
102048                 // targeting specific features: download, select, and zoom to them
102049                 context.zoomToEntity(q.id.split(',')[0], !q.map); //}
102050               }
102051
102052               if (q.walkthrough === 'true') {
102053                 behavior.startWalkthrough = true;
102054               }
102055
102056               if (q.map) {
102057                 behavior.hadHash = true;
102058               }
102059
102060               hashchange();
102061               updateTitle(false);
102062             }
102063           }
102064
102065           behavior.off = function () {
102066             _throttledUpdate.cancel();
102067
102068             _throttledUpdateTitle.cancel();
102069
102070             context.map().on('move.behaviorHash', null);
102071             context.on('enter.behaviorHash', null);
102072             select(window).on('hashchange.behaviorHash', null);
102073             window.location.hash = '';
102074           };
102075
102076           return behavior;
102077         }
102078
102079         // This is only done in testing because of the performance penalty.
102080
102081         var debug = false; // Reexport just what our tests use, see #4379
102082         var d3 = {
102083           dispatch: dispatch$8,
102084           geoMercator: mercator,
102085           geoProjection: projection,
102086           polygonArea: d3_polygonArea,
102087           polygonCentroid: d3_polygonCentroid,
102088           select: select,
102089           selectAll: selectAll,
102090           timerFlush: timerFlush
102091         };
102092
102093         var iD = /*#__PURE__*/Object.freeze({
102094                 __proto__: null,
102095                 debug: debug,
102096                 d3: d3,
102097                 actionAddEntity: actionAddEntity,
102098                 actionAddMember: actionAddMember,
102099                 actionAddMidpoint: actionAddMidpoint,
102100                 actionAddVertex: actionAddVertex,
102101                 actionChangeMember: actionChangeMember,
102102                 actionChangePreset: actionChangePreset,
102103                 actionChangeTags: actionChangeTags,
102104                 actionCircularize: actionCircularize,
102105                 actionConnect: actionConnect,
102106                 actionCopyEntities: actionCopyEntities,
102107                 actionDeleteMember: actionDeleteMember,
102108                 actionDeleteMultiple: actionDeleteMultiple,
102109                 actionDeleteNode: actionDeleteNode,
102110                 actionDeleteRelation: actionDeleteRelation,
102111                 actionDeleteWay: actionDeleteWay,
102112                 actionDiscardTags: actionDiscardTags,
102113                 actionDisconnect: actionDisconnect,
102114                 actionExtract: actionExtract,
102115                 actionJoin: actionJoin,
102116                 actionMerge: actionMerge,
102117                 actionMergeNodes: actionMergeNodes,
102118                 actionMergePolygon: actionMergePolygon,
102119                 actionMergeRemoteChanges: actionMergeRemoteChanges,
102120                 actionMove: actionMove,
102121                 actionMoveMember: actionMoveMember,
102122                 actionMoveNode: actionMoveNode,
102123                 actionNoop: actionNoop,
102124                 actionOrthogonalize: actionOrthogonalize,
102125                 actionRestrictTurn: actionRestrictTurn,
102126                 actionReverse: actionReverse,
102127                 actionRevert: actionRevert,
102128                 actionRotate: actionRotate,
102129                 actionScale: actionScale,
102130                 actionSplit: actionSplit,
102131                 actionStraightenNodes: actionStraightenNodes,
102132                 actionStraightenWay: actionStraightenWay,
102133                 actionUnrestrictTurn: actionUnrestrictTurn,
102134                 actionReflect: actionReflect,
102135                 actionUpgradeTags: actionUpgradeTags,
102136                 behaviorAddWay: behaviorAddWay,
102137                 behaviorBreathe: behaviorBreathe,
102138                 behaviorDrag: behaviorDrag,
102139                 behaviorDrawWay: behaviorDrawWay,
102140                 behaviorDraw: behaviorDraw,
102141                 behaviorEdit: behaviorEdit,
102142                 behaviorHash: behaviorHash,
102143                 behaviorHover: behaviorHover,
102144                 behaviorLasso: behaviorLasso,
102145                 behaviorOperation: behaviorOperation,
102146                 behaviorPaste: behaviorPaste,
102147                 behaviorSelect: behaviorSelect,
102148                 coreContext: coreContext,
102149                 coreFileFetcher: coreFileFetcher,
102150                 fileFetcher: _mainFileFetcher,
102151                 coreDifference: coreDifference,
102152                 coreGraph: coreGraph,
102153                 coreHistory: coreHistory,
102154                 coreLocalizer: coreLocalizer,
102155                 t: _t,
102156                 localizer: _mainLocalizer,
102157                 coreLocations: coreLocations,
102158                 locationManager: _mainLocations,
102159                 prefs: corePreferences,
102160                 coreTree: coreTree,
102161                 coreUploader: coreUploader,
102162                 coreValidator: coreValidator,
102163                 geoExtent: geoExtent,
102164                 geoLatToMeters: geoLatToMeters,
102165                 geoLonToMeters: geoLonToMeters,
102166                 geoMetersToLat: geoMetersToLat,
102167                 geoMetersToLon: geoMetersToLon,
102168                 geoMetersToOffset: geoMetersToOffset,
102169                 geoOffsetToMeters: geoOffsetToMeters,
102170                 geoScaleToZoom: geoScaleToZoom,
102171                 geoSphericalClosestNode: geoSphericalClosestNode,
102172                 geoSphericalDistance: geoSphericalDistance,
102173                 geoZoomToScale: geoZoomToScale,
102174                 geoAngle: geoAngle,
102175                 geoChooseEdge: geoChooseEdge,
102176                 geoEdgeEqual: geoEdgeEqual,
102177                 geoGetSmallestSurroundingRectangle: geoGetSmallestSurroundingRectangle,
102178                 geoHasLineIntersections: geoHasLineIntersections,
102179                 geoHasSelfIntersections: geoHasSelfIntersections,
102180                 geoRotate: geoRotate,
102181                 geoLineIntersection: geoLineIntersection,
102182                 geoPathHasIntersections: geoPathHasIntersections,
102183                 geoPathIntersections: geoPathIntersections,
102184                 geoPathLength: geoPathLength,
102185                 geoPointInPolygon: geoPointInPolygon,
102186                 geoPolygonContainsPolygon: geoPolygonContainsPolygon,
102187                 geoPolygonIntersectsPolygon: geoPolygonIntersectsPolygon,
102188                 geoViewportEdge: geoViewportEdge,
102189                 geoRawMercator: geoRawMercator,
102190                 geoVecAdd: geoVecAdd,
102191                 geoVecAngle: geoVecAngle,
102192                 geoVecCross: geoVecCross,
102193                 geoVecDot: geoVecDot,
102194                 geoVecEqual: geoVecEqual,
102195                 geoVecFloor: geoVecFloor,
102196                 geoVecInterp: geoVecInterp,
102197                 geoVecLength: geoVecLength,
102198                 geoVecLengthSquare: geoVecLengthSquare,
102199                 geoVecNormalize: geoVecNormalize,
102200                 geoVecNormalizedDot: geoVecNormalizedDot,
102201                 geoVecProject: geoVecProject,
102202                 geoVecSubtract: geoVecSubtract,
102203                 geoVecScale: geoVecScale,
102204                 geoOrthoNormalizedDotProduct: geoOrthoNormalizedDotProduct,
102205                 geoOrthoCalcScore: geoOrthoCalcScore,
102206                 geoOrthoMaxOffsetAngle: geoOrthoMaxOffsetAngle,
102207                 geoOrthoCanOrthogonalize: geoOrthoCanOrthogonalize,
102208                 modeAddArea: modeAddArea,
102209                 modeAddLine: modeAddLine,
102210                 modeAddPoint: modeAddPoint,
102211                 modeAddNote: modeAddNote,
102212                 modeBrowse: modeBrowse,
102213                 modeDragNode: modeDragNode,
102214                 modeDragNote: modeDragNote,
102215                 modeDrawArea: modeDrawArea,
102216                 modeDrawLine: modeDrawLine,
102217                 modeMove: modeMove,
102218                 modeRotate: modeRotate,
102219                 modeSave: modeSave,
102220                 modeSelect: modeSelect,
102221                 modeSelectData: modeSelectData,
102222                 modeSelectError: modeSelectError,
102223                 modeSelectNote: modeSelectNote,
102224                 operationCircularize: operationCircularize,
102225                 operationContinue: operationContinue,
102226                 operationCopy: operationCopy,
102227                 operationDelete: operationDelete,
102228                 operationDisconnect: operationDisconnect,
102229                 operationDowngrade: operationDowngrade,
102230                 operationExtract: operationExtract,
102231                 operationMerge: operationMerge,
102232                 operationMove: operationMove,
102233                 operationOrthogonalize: operationOrthogonalize,
102234                 operationPaste: operationPaste,
102235                 operationReflectShort: operationReflectShort,
102236                 operationReflectLong: operationReflectLong,
102237                 operationReverse: operationReverse,
102238                 operationRotate: operationRotate,
102239                 operationSplit: operationSplit,
102240                 operationStraighten: operationStraighten,
102241                 osmChangeset: osmChangeset,
102242                 osmEntity: osmEntity,
102243                 osmNode: osmNode,
102244                 osmNote: osmNote,
102245                 osmRelation: osmRelation,
102246                 osmWay: osmWay,
102247                 QAItem: QAItem,
102248                 osmIntersection: osmIntersection,
102249                 osmTurn: osmTurn,
102250                 osmInferRestriction: osmInferRestriction,
102251                 osmLanes: osmLanes,
102252                 osmOldMultipolygonOuterMemberOfRelation: osmOldMultipolygonOuterMemberOfRelation,
102253                 osmIsOldMultipolygonOuterMember: osmIsOldMultipolygonOuterMember,
102254                 osmOldMultipolygonOuterMember: osmOldMultipolygonOuterMember,
102255                 osmJoinWays: osmJoinWays,
102256                 get osmAreaKeys () { return osmAreaKeys; },
102257                 osmSetAreaKeys: osmSetAreaKeys,
102258                 osmTagSuggestingArea: osmTagSuggestingArea,
102259                 get osmPointTags () { return osmPointTags; },
102260                 osmSetPointTags: osmSetPointTags,
102261                 get osmVertexTags () { return osmVertexTags; },
102262                 osmSetVertexTags: osmSetVertexTags,
102263                 osmNodeGeometriesForTags: osmNodeGeometriesForTags,
102264                 osmOneWayTags: osmOneWayTags,
102265                 osmPavedTags: osmPavedTags,
102266                 osmIsInterestingTag: osmIsInterestingTag,
102267                 osmRoutableHighwayTagValues: osmRoutableHighwayTagValues,
102268                 osmFlowingWaterwayTagValues: osmFlowingWaterwayTagValues,
102269                 osmRailwayTrackTagValues: osmRailwayTrackTagValues,
102270                 presetCategory: presetCategory,
102271                 presetCollection: presetCollection,
102272                 presetField: presetField,
102273                 presetPreset: presetPreset,
102274                 presetManager: _mainPresetIndex,
102275                 presetIndex: presetIndex,
102276                 rendererBackgroundSource: rendererBackgroundSource,
102277                 rendererBackground: rendererBackground,
102278                 rendererFeatures: rendererFeatures,
102279                 rendererMap: rendererMap,
102280                 rendererPhotos: rendererPhotos,
102281                 rendererTileLayer: rendererTileLayer,
102282                 services: services,
102283                 serviceKeepRight: serviceKeepRight,
102284                 serviceImproveOSM: serviceImproveOSM,
102285                 serviceOsmose: serviceOsmose,
102286                 serviceMapillary: serviceMapillary,
102287                 serviceMapRules: serviceMapRules,
102288                 serviceNominatim: serviceNominatim,
102289                 serviceNsi: serviceNsi,
102290                 serviceOpenstreetcam: serviceOpenstreetcam,
102291                 serviceOsm: serviceOsm,
102292                 serviceOsmWikibase: serviceOsmWikibase,
102293                 serviceStreetside: serviceStreetside,
102294                 serviceTaginfo: serviceTaginfo,
102295                 serviceVectorTile: serviceVectorTile,
102296                 serviceWikidata: serviceWikidata,
102297                 serviceWikipedia: serviceWikipedia,
102298                 svgAreas: svgAreas,
102299                 svgData: svgData,
102300                 svgDebug: svgDebug,
102301                 svgDefs: svgDefs,
102302                 svgKeepRight: svgKeepRight,
102303                 svgIcon: svgIcon,
102304                 svgGeolocate: svgGeolocate,
102305                 svgLabels: svgLabels,
102306                 svgLayers: svgLayers,
102307                 svgLines: svgLines,
102308                 svgMapillaryImages: svgMapillaryImages,
102309                 svgMapillarySigns: svgMapillarySigns,
102310                 svgMidpoints: svgMidpoints,
102311                 svgNotes: svgNotes,
102312                 svgMarkerSegments: svgMarkerSegments,
102313                 svgOpenstreetcamImages: svgOpenstreetcamImages,
102314                 svgOsm: svgOsm,
102315                 svgPassiveVertex: svgPassiveVertex,
102316                 svgPath: svgPath,
102317                 svgPointTransform: svgPointTransform,
102318                 svgPoints: svgPoints,
102319                 svgRelationMemberTags: svgRelationMemberTags,
102320                 svgSegmentWay: svgSegmentWay,
102321                 svgStreetside: svgStreetside,
102322                 svgTagClasses: svgTagClasses,
102323                 svgTagPattern: svgTagPattern,
102324                 svgTouch: svgTouch,
102325                 svgTurns: svgTurns,
102326                 svgVertices: svgVertices,
102327                 uiFieldDefaultCheck: uiFieldCheck,
102328                 uiFieldOnewayCheck: uiFieldCheck,
102329                 uiFieldCheck: uiFieldCheck,
102330                 uiFieldManyCombo: uiFieldCombo,
102331                 uiFieldMultiCombo: uiFieldCombo,
102332                 uiFieldNetworkCombo: uiFieldCombo,
102333                 uiFieldSemiCombo: uiFieldCombo,
102334                 uiFieldTypeCombo: uiFieldCombo,
102335                 uiFieldCombo: uiFieldCombo,
102336                 uiFieldUrl: uiFieldText,
102337                 uiFieldIdentifier: uiFieldText,
102338                 uiFieldNumber: uiFieldText,
102339                 uiFieldTel: uiFieldText,
102340                 uiFieldEmail: uiFieldText,
102341                 uiFieldText: uiFieldText,
102342                 uiFieldAccess: uiFieldAccess,
102343                 uiFieldAddress: uiFieldAddress,
102344                 uiFieldCycleway: uiFieldCycleway,
102345                 uiFieldLanes: uiFieldLanes,
102346                 uiFieldLocalized: uiFieldLocalized,
102347                 uiFieldRoadspeed: uiFieldRoadspeed,
102348                 uiFieldStructureRadio: uiFieldRadio,
102349                 uiFieldRadio: uiFieldRadio,
102350                 uiFieldRestrictions: uiFieldRestrictions,
102351                 uiFieldTextarea: uiFieldTextarea,
102352                 uiFieldWikidata: uiFieldWikidata,
102353                 uiFieldWikipedia: uiFieldWikipedia,
102354                 uiFields: uiFields,
102355                 uiIntro: uiIntro,
102356                 uiPanelBackground: uiPanelBackground,
102357                 uiPanelHistory: uiPanelHistory,
102358                 uiPanelLocation: uiPanelLocation,
102359                 uiPanelMeasurement: uiPanelMeasurement,
102360                 uiInfoPanels: uiInfoPanels,
102361                 uiPaneBackground: uiPaneBackground,
102362                 uiPaneHelp: uiPaneHelp,
102363                 uiPaneIssues: uiPaneIssues,
102364                 uiPaneMapData: uiPaneMapData,
102365                 uiPanePreferences: uiPanePreferences,
102366                 uiSectionBackgroundDisplayOptions: uiSectionBackgroundDisplayOptions,
102367                 uiSectionBackgroundList: uiSectionBackgroundList,
102368                 uiSectionBackgroundOffset: uiSectionBackgroundOffset,
102369                 uiSectionChanges: uiSectionChanges,
102370                 uiSectionDataLayers: uiSectionDataLayers,
102371                 uiSectionEntityIssues: uiSectionEntityIssues,
102372                 uiSectionFeatureType: uiSectionFeatureType,
102373                 uiSectionMapFeatures: uiSectionMapFeatures,
102374                 uiSectionMapStyleOptions: uiSectionMapStyleOptions,
102375                 uiSectionOverlayList: uiSectionOverlayList,
102376                 uiSectionPhotoOverlays: uiSectionPhotoOverlays,
102377                 uiSectionPresetFields: uiSectionPresetFields,
102378                 uiSectionPrivacy: uiSectionPrivacy,
102379                 uiSectionRawMemberEditor: uiSectionRawMemberEditor,
102380                 uiSectionRawMembershipEditor: uiSectionRawMembershipEditor,
102381                 uiSectionRawTagEditor: uiSectionRawTagEditor,
102382                 uiSectionSelectionList: uiSectionSelectionList,
102383                 uiSectionValidationIssues: uiSectionValidationIssues,
102384                 uiSectionValidationOptions: uiSectionValidationOptions,
102385                 uiSectionValidationRules: uiSectionValidationRules,
102386                 uiSectionValidationStatus: uiSectionValidationStatus,
102387                 uiSettingsCustomBackground: uiSettingsCustomBackground,
102388                 uiSettingsCustomData: uiSettingsCustomData,
102389                 uiInit: uiInit,
102390                 uiAccount: uiAccount,
102391                 uiAttribution: uiAttribution,
102392                 uiChangesetEditor: uiChangesetEditor,
102393                 uiCmd: uiCmd,
102394                 uiCombobox: uiCombobox,
102395                 uiCommit: uiCommit,
102396                 uiCommitWarnings: uiCommitWarnings,
102397                 uiConfirm: uiConfirm,
102398                 uiConflicts: uiConflicts,
102399                 uiContributors: uiContributors,
102400                 uiCurtain: uiCurtain,
102401                 uiDataEditor: uiDataEditor,
102402                 uiDataHeader: uiDataHeader,
102403                 uiDisclosure: uiDisclosure,
102404                 uiEditMenu: uiEditMenu,
102405                 uiEntityEditor: uiEntityEditor,
102406                 uiFeatureInfo: uiFeatureInfo,
102407                 uiFeatureList: uiFeatureList,
102408                 uiField: uiField,
102409                 uiFieldHelp: uiFieldHelp,
102410                 uiFlash: uiFlash,
102411                 uiFormFields: uiFormFields,
102412                 uiFullScreen: uiFullScreen,
102413                 uiGeolocate: uiGeolocate,
102414                 uiImproveOsmComments: uiImproveOsmComments,
102415                 uiImproveOsmDetails: uiImproveOsmDetails,
102416                 uiImproveOsmEditor: uiImproveOsmEditor,
102417                 uiImproveOsmHeader: uiImproveOsmHeader,
102418                 uiInfo: uiInfo,
102419                 uiInspector: uiInspector,
102420                 uiIssuesInfo: uiIssuesInfo,
102421                 uiKeepRightDetails: uiKeepRightDetails,
102422                 uiKeepRightEditor: uiKeepRightEditor,
102423                 uiKeepRightHeader: uiKeepRightHeader,
102424                 uiLasso: uiLasso,
102425                 uiLoading: uiLoading,
102426                 uiMapInMap: uiMapInMap,
102427                 uiModal: uiModal,
102428                 uiNotice: uiNotice,
102429                 uiNoteComments: uiNoteComments,
102430                 uiNoteEditor: uiNoteEditor,
102431                 uiNoteHeader: uiNoteHeader,
102432                 uiNoteReport: uiNoteReport,
102433                 uiPopover: uiPopover,
102434                 uiPresetIcon: uiPresetIcon,
102435                 uiPresetList: uiPresetList,
102436                 uiRestore: uiRestore,
102437                 uiScale: uiScale,
102438                 uiSidebar: uiSidebar,
102439                 uiSourceSwitch: uiSourceSwitch,
102440                 uiSpinner: uiSpinner,
102441                 uiSplash: uiSplash,
102442                 uiStatus: uiStatus,
102443                 uiSuccess: uiSuccess,
102444                 uiTagReference: uiTagReference,
102445                 uiToggle: uiToggle,
102446                 uiTooltip: uiTooltip,
102447                 uiVersion: uiVersion,
102448                 uiViewOnOSM: uiViewOnOSM,
102449                 uiViewOnKeepRight: uiViewOnKeepRight,
102450                 uiZoom: uiZoom,
102451                 utilAesEncrypt: utilAesEncrypt,
102452                 utilAesDecrypt: utilAesDecrypt,
102453                 utilArrayChunk: utilArrayChunk,
102454                 utilArrayDifference: utilArrayDifference,
102455                 utilArrayFlatten: utilArrayFlatten,
102456                 utilArrayGroupBy: utilArrayGroupBy,
102457                 utilArrayIdentical: utilArrayIdentical,
102458                 utilArrayIntersection: utilArrayIntersection,
102459                 utilArrayUnion: utilArrayUnion,
102460                 utilArrayUniq: utilArrayUniq,
102461                 utilArrayUniqBy: utilArrayUniqBy,
102462                 utilAsyncMap: utilAsyncMap,
102463                 utilCleanTags: utilCleanTags,
102464                 utilCombinedTags: utilCombinedTags,
102465                 utilDeepMemberSelector: utilDeepMemberSelector,
102466                 utilDetect: utilDetect,
102467                 utilDisplayName: utilDisplayName,
102468                 utilDisplayNameForPath: utilDisplayNameForPath,
102469                 utilDisplayType: utilDisplayType,
102470                 utilDisplayLabel: utilDisplayLabel,
102471                 utilEntityRoot: utilEntityRoot,
102472                 utilEditDistance: utilEditDistance,
102473                 utilEntityAndDeepMemberIDs: utilEntityAndDeepMemberIDs,
102474                 utilEntityOrMemberSelector: utilEntityOrMemberSelector,
102475                 utilEntityOrDeepMemberSelector: utilEntityOrDeepMemberSelector,
102476                 utilEntitySelector: utilEntitySelector,
102477                 utilFastMouse: utilFastMouse,
102478                 utilFetchJson: utilFetchJson,
102479                 utilFunctor: utilFunctor,
102480                 utilGetAllNodes: utilGetAllNodes,
102481                 utilGetSetValue: utilGetSetValue,
102482                 utilHashcode: utilHashcode,
102483                 utilHighlightEntities: utilHighlightEntities,
102484                 utilKeybinding: utilKeybinding,
102485                 utilNoAuto: utilNoAuto,
102486                 utilObjectOmit: utilObjectOmit,
102487                 utilPrefixCSSProperty: utilPrefixCSSProperty,
102488                 utilPrefixDOMProperty: utilPrefixDOMProperty,
102489                 utilQsString: utilQsString,
102490                 utilRebind: utilRebind,
102491                 utilSafeClassName: utilSafeClassName,
102492                 utilSetTransform: utilSetTransform,
102493                 utilSessionMutex: utilSessionMutex,
102494                 utilStringQs: utilStringQs,
102495                 utilTagDiff: utilTagDiff,
102496                 utilTagText: utilTagText,
102497                 utilTiler: utilTiler,
102498                 utilTotalExtent: utilTotalExtent,
102499                 utilTriggerEvent: utilTriggerEvent,
102500                 utilUnicodeCharsCount: utilUnicodeCharsCount,
102501                 utilUnicodeCharsTruncated: utilUnicodeCharsTruncated,
102502                 utilUniqueDomId: utilUniqueDomId,
102503                 utilWrap: utilWrap,
102504                 validationAlmostJunction: validationAlmostJunction,
102505                 validationCloseNodes: validationCloseNodes,
102506                 validationCrossingWays: validationCrossingWays,
102507                 validationDisconnectedWay: validationDisconnectedWay,
102508                 validationFormatting: validationFormatting,
102509                 validationHelpRequest: validationHelpRequest,
102510                 validationImpossibleOneway: validationImpossibleOneway,
102511                 validationIncompatibleSource: validationIncompatibleSource,
102512                 validationMaprules: validationMaprules,
102513                 validationMismatchedGeometry: validationMismatchedGeometry,
102514                 validationMissingRole: validationMissingRole,
102515                 validationMissingTag: validationMissingTag,
102516                 validationOutdatedTags: validationOutdatedTags,
102517                 validationPrivateData: validationPrivateData,
102518                 validationSuspiciousName: validationSuspiciousName,
102519                 validationUnsquareWay: validationUnsquareWay
102520         });
102521
102522         window.requestIdleCallback = window.requestIdleCallback || function (cb) {
102523           var start = Date.now();
102524           return window.requestAnimationFrame(function () {
102525             cb({
102526               didTimeout: false,
102527               timeRemaining: function timeRemaining() {
102528                 return Math.max(0, 50 - (Date.now() - start));
102529               }
102530             });
102531           });
102532         };
102533
102534         window.cancelIdleCallback = window.cancelIdleCallback || function (id) {
102535           window.cancelAnimationFrame(id);
102536         };
102537         window.iD = iD;
102538
102539 }());